KiCad PCB EDA Suite
tool_dispatcher.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2013 CERN
5  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <wxPcbStruct.h>
26 #include <wxBasePcbFrame.h>
27 
28 #include <tool/tool_manager.h>
29 #include <tool/tool_dispatcher.h>
30 #include <tool/actions.h>
31 #include <view/view.h>
32 #include <view/wx_view_controls.h>
33 
34 #include <class_draw_panel_gal.h>
35 #include <pcbnew_id.h>
36 
37 #include <boost/optional.hpp>
38 
41 {
42  BUTTON_STATE( TOOL_MOUSE_BUTTONS aButton, const wxEventType& aDownEvent,
43  const wxEventType& aUpEvent, const wxEventType& aDblClickEvent ) :
44  dragging( false ),
45  pressed( false ),
46  dragMaxDelta( 0.0f ),
47  button( aButton ),
48  downEvent( aDownEvent ),
49  upEvent( aUpEvent ),
50  dblClickEvent( aDblClickEvent )
51  {};
52 
54  bool dragging;
55 
57  bool pressed;
58 
61 
64 
67  double dragMaxDelta;
68 
71 
73  wxEventType downEvent;
74 
76  wxEventType upEvent;
77 
79  wxEventType dblClickEvent;
80 
82  wxLongLong downTimestamp;
83 
85  void Reset()
86  {
87  dragging = false;
88  pressed = false;
89  }
90 
92  bool GetState() const
93  {
94  wxMouseState mouseState = wxGetMouseState();
95 
96  switch( button )
97  {
98  case BUT_LEFT:
99  return mouseState.LeftIsDown();
100 
101  case BUT_MIDDLE:
102  return mouseState.MiddleIsDown();
103 
104  case BUT_RIGHT:
105  return mouseState.RightIsDown();
106 
107  default:
108  assert( false );
109  break;
110  }
111 
112  return false;
113  }
114 };
115 
116 
118  m_toolMgr( aToolMgr ),
119  m_actions( aActions )
120 {
121  m_buttons.push_back( new BUTTON_STATE( BUT_LEFT, wxEVT_LEFT_DOWN,
122  wxEVT_LEFT_UP, wxEVT_LEFT_DCLICK ) );
123  m_buttons.push_back( new BUTTON_STATE( BUT_RIGHT, wxEVT_RIGHT_DOWN,
124  wxEVT_RIGHT_UP, wxEVT_RIGHT_DCLICK ) );
125  m_buttons.push_back( new BUTTON_STATE( BUT_MIDDLE, wxEVT_MIDDLE_DOWN,
126  wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DCLICK ) );
127 
128  ResetState();
129 }
130 
131 
133 {
134  for( BUTTON_STATE* st : m_buttons )
135  delete st;
136 }
137 
138 
140 {
141  for( BUTTON_STATE* st : m_buttons )
142  st->Reset();
143 }
144 
145 
147 {
148  return static_cast<EDA_DRAW_FRAME*>( m_toolMgr->GetEditFrame() )->GetGalCanvas()->GetView();
149 }
150 
151 
152 bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion )
153 {
154  BUTTON_STATE* st = m_buttons[aIndex];
155  wxEventType type = aEvent.GetEventType();
157  bool isClick = false;
158 
159 // bool up = type == st->upEvent;
160 // bool down = type == st->downEvent;
161  bool up = false, down = false;
162  bool dblClick = type == st->dblClickEvent;
163  bool state = st->GetState();
164 
165  if( !dblClick )
166  {
167  // Sometimes the dispatcher does not receive mouse button up event, so it stays
168  // in the dragging mode even if the mouse button is not held anymore
169  if( st->pressed && !state )
170  up = true;
171  else if( !st->pressed && state )
172  down = true;
173  }
174 
175  int mods = decodeModifiers( static_cast<wxMouseEvent*>( &aEvent ) );
176  int args = st->button | mods;
177 
178  if( down ) // Handle mouse button press
179  {
180  st->downTimestamp = wxGetLocalTimeMillis();
183  st->dragMaxDelta = 0;
184  st->pressed = true;
185  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DOWN, args );
186  }
187  else if( up ) // Handle mouse button release
188  {
189  st->pressed = false;
190 
191  if( st->dragging )
192  {
193  wxLongLong t = wxGetLocalTimeMillis();
194 
195  // Determine if it was just a single click or beginning of dragging
196  if( t - st->downTimestamp < DragTimeThreshold &&
198  isClick = true;
199  else
200  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_UP, args );
201  }
202  else
203  isClick = true;
204 
205  if( isClick )
206  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_CLICK, args );
207 
208  st->dragging = false;
209  }
210  else if( dblClick )
211  {
212  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DBLCLICK, args );
213  }
214 
215  if( st->pressed && aMotion )
216  {
217  st->dragging = true;
218  double dragPixelDistance =
220  st->dragMaxDelta = std::max( st->dragMaxDelta, dragPixelDistance );
221 
222  wxLongLong t = wxGetLocalTimeMillis();
223 
225  {
226  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DRAG, args );
227  evt->setMouseDragOrigin( st->dragOrigin );
228  evt->setMouseDelta( m_lastMousePos - st->dragOrigin );
229  }
230  }
231 
232  if( evt )
233  {
234  evt->SetMousePosition( isClick ? st->downPosition : m_lastMousePos );
235  m_toolMgr->ProcessEvent( *evt );
236 
237  return true;
238  }
239 
240  return false;
241 }
242 
243 
244 void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
245 {
246  bool motion = false, buttonEvents = false;
248 
249  int type = aEvent.GetEventType();
250 
251  // Mouse handling
252  if( type == wxEVT_MOTION || type == wxEVT_MOUSEWHEEL ||
253 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
254  type == wxEVT_MAGNIFY ||
255 #endif
256  type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_UP ||
257  type == wxEVT_MIDDLE_DOWN || type == wxEVT_MIDDLE_UP ||
258  type == wxEVT_RIGHT_DOWN || type == wxEVT_RIGHT_UP ||
259  type == wxEVT_LEFT_DCLICK || type == wxEVT_MIDDLE_DCLICK || type == wxEVT_RIGHT_DCLICK ||
260  // Event issued whem mouse retains position in screen coordinates,
261  // but changes in world coordinates (e.g. autopanning)
263  {
264  wxMouseEvent* me = static_cast<wxMouseEvent*>( &aEvent );
265  int mods = decodeModifiers( me );
266 
268  VECTOR2D pos = getView()->ToWorld( screenPos );
269 
270  if( pos != m_lastMousePos )
271  {
272  motion = true;
273  m_lastMousePos = pos;
274  }
275 
276  for( unsigned int i = 0; i < m_buttons.size(); i++ )
277  buttonEvents |= handleMouseButton( aEvent, i, motion );
278 
279  if( !buttonEvents && motion )
280  {
281  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_MOTION, mods );
282  evt->SetMousePosition( pos );
283  }
284 
285 #ifdef __APPLE__
286  // TODO That's a big ugly workaround, somehow DRAWPANEL_GAL loses focus
287  // after second LMB click and currently I have no means to do better debugging
288  if( type == wxEVT_LEFT_UP )
289  static_cast<PCB_BASE_FRAME*>( m_toolMgr->GetEditFrame() )->GetGalCanvas()->SetFocus();
290 #endif /* __APPLE__ */
291  }
292 
293  // Keyboard handling
294  else if( type == wxEVT_CHAR )
295  {
296  wxKeyEvent* ke = static_cast<wxKeyEvent*>( &aEvent );
297  int key = ke->GetKeyCode();
298  int mods = decodeModifiers( ke );
299 
300  if( mods & MD_CTRL )
301  {
302 #if !wxCHECK_VERSION( 2, 9, 0 )
303  // I really look forward to the day when we will use only one version of wxWidgets..
304  const int WXK_CONTROL_A = 1;
305  const int WXK_CONTROL_Z = 26;
306 #endif
307 
308  // wxWidgets have a quirk related to Ctrl+letter hot keys handled by CHAR_EVT
309  // http://docs.wxwidgets.org/trunk/classwx_key_event.html:
310  // "char events for ASCII letters in this case carry codes corresponding to the ASCII
311  // value of Ctrl-Latter, i.e. 1 for Ctrl-A, 2 for Ctrl-B and so on until 26 for Ctrl-Z."
312  if( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
313  key += 'A' - 1;
314  }
315 
316  if( key == WXK_ESCAPE ) // ESC is the special key for cancelling tools
318  else
319  evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_PRESSED, key | mods );
320  }
321 
322  if( evt )
323  m_toolMgr->ProcessEvent( *evt );
324 
325  // pass the event to the GUI, it might still be interested in it
326 #ifdef __APPLE__
327  // On OS X, key events are always meant to be caught. An uncaught key event is assumed
328  // to be a user input error by OS X (as they are pressing keys in a context where nothing
329  // is there to catch the event). This annoyingly makes OS X beep and/or flash the screen
330  // in pcbnew and the footprint editor any time a hotkey is used. The correct procedure is
331  // to NOT pass key events to the GUI under OS X.
332 
333  if( type != wxEVT_CHAR )
334  aEvent.Skip();
335 #else
336  aEvent.Skip();
337 #endif
338 
339  updateUI();
340 }
341 
342 
343 void TOOL_DISPATCHER::DispatchWxCommand( wxCommandEvent& aEvent )
344 {
346 
347  if( evt )
348  m_toolMgr->ProcessEvent( *evt );
349  else
350  aEvent.Skip();
351 
352  updateUI();
353 }
354 
355 
357 {
358  // TODO I don't feel it is the right place for updating UI,
359  // but at the moment I cannot think of a better one..
360  EDA_DRAW_FRAME* frame = static_cast<EDA_DRAW_FRAME*>( m_toolMgr->GetEditFrame() );
361  frame->UpdateStatusBar();
362  //frame->UpdateMsgPanel();
363 }
VECTOR2D downPosition
Point where click event has occurred.
static int decodeModifiers(const wxKeyboardState *aState)
Saves the state of key modifiers (Alt, Ctrl and so on).
ACTIONS * m_actions
Instance of an actions list that handles legacy action translation
double dragMaxDelta
Difference between drag origin point and current mouse position (expressed as distance in pixels)...
BUTTON_STATE(TOOL_MOUSE_BUTTONS aButton, const wxEventType &aDownEvent, const wxEventType &aUpEvent, const wxEventType &aDblClickEvent)
static const wxEventType EVT_REFRESH_MOUSE
Event that forces mouse move event in the dispatcher (eg.
virtual boost::optional< TOOL_EVENT > TranslateLegacyId(int aId)=0
Function TranslateLegacyId() Translates legacy tool ids to the corresponding TOOL_ACTION name...
wxWindow * GetEditFrame() const
Definition: tool_manager.h:258
static const int DragDistanceThreshold
The distance threshold for mouse cursor that disinguishes between a single mouse click and a beginnin...
virtual ~TOOL_DISPATCHER()
VECTOR2D ToWorld(const VECTOR2D &aCoord, bool aAbsolute=true) const
Function ToWorld() Converts a screen space point/vector to a point/vector in world space coordinates...
Definition: view.cpp:440
virtual void ResetState()
Function ResetState() Brings the dispatcher to its initial state.
bool dragging
Flag indicating that dragging is active for the given button.
VECTOR2D dragOrigin
Point where dragging has started (in world coordinates).
bool GetState() const
Checks the current state of the button.
Class EDA_DRAW_FRAME is the base class for create windows for drawing purpose.
Definition: draw_frame.h:53
TOOL_DISPATCHER(TOOL_MANAGER *aToolMgr, ACTIONS *aActions)
Constructor.
WX_VIEW_CONTROLS class definition.
void Reset()
Restores initial state.
KIGFX::VIEW_CONTROLS * GetViewControls() const
Definition: tool_manager.h:248
Class TOOL_MANAGER.
Definition: tool_manager.h:49
Stores information about a mouse button state
Classes used in Pcbnew, CvPcb and GerbView.
T EuclideanNorm() const
Destructor.
Definition: vector2d.h:295
wxEventType dblClickEvent
The type of wxEvent that determines mouse button double click.
wxEventType downEvent
The type of wxEvent that determines mouse button press.
virtual void DispatchWxEvent(wxEvent &aEvent)
Function DispatchWxEvent() Processes wxEvents (mostly UI events), translates them to TOOL_EVENTs...
Class TOOL_EVENT.
Definition: tool_event.h:162
VECTOR2D m_lastMousePos
The last mouse cursor position (in world coordinates).
void updateUI()
Redraws the status bar and message panel.
std::vector< BUTTON_STATE * > m_buttons
State of mouse buttons.
TOOL_MOUSE_BUTTONS
Definition: tool_event.h:115
#define max(a, b)
Definition: auxiliary.h:86
virtual void DispatchWxCommand(wxCommandEvent &aEvent)
Function DispatchWxCommand() Processes wxCommands (mostly menu related events) and runs appropriate a...
bool pressed
Flag indicating that the given button is pressed.
VECTOR2D ToScreen(const VECTOR2D &aCoord, bool aAbsolute=true) const
Function ToScreen() Converts a world space point/vector to a point/vector in screen space coordinates...
Definition: view.cpp:459
Class ACTIONS.
Definition: actions.h:41
wxEventType upEvent
The type of wxEvent that determines mouse button release.
wxLongLong downTimestamp
Time stamp for the last mouse button press event.
void ProcessEvent(const TOOL_EVENT &aEvent)
Propagates an event to tools that requested events of matching type(s).
static const int DragTimeThreshold
The time threshold for a mouse button press that distinguishes between a single mouse click and a beg...
Class VIEW.
Definition: view.h:58
TOOL_MOUSE_BUTTONS button
Determines the mouse button for which information are stored.
virtual void UpdateStatusBar()
Function UpdateStatusBar updates the status bar information.
Definition: draw_frame.cpp:650
bool handleMouseButton(wxEvent &aEvent, int aIndex, bool aMotion)
Handles mouse related events (click, motion, dragging).
class PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer...
KIGFX::VIEW * getView()
Returns the instance of VIEW, used by the application.
TOOL_MANAGER * m_toolMgr
Instance of tool manager that cooperates with the dispatcher.
virtual VECTOR2I GetMousePosition() const =0
Function GetMousePosition() Returns the current mouse pointer position in screen coordinates.