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  * Last changez: 2017
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <wxPcbStruct.h>
27 #include <wxBasePcbFrame.h>
28 
29 #include <tool/tool_manager.h>
30 #include <tool/tool_dispatcher.h>
31 #include <tool/actions.h>
32 #include <view/view.h>
33 #include <view/wx_view_controls.h>
34 
35 #include <class_draw_panel_gal.h>
36 #include <pcbnew_id.h>
37 
38 #include <boost/optional.hpp>
39 
42 {
43  BUTTON_STATE( TOOL_MOUSE_BUTTONS aButton, const wxEventType& aDownEvent,
44  const wxEventType& aUpEvent, const wxEventType& aDblClickEvent ) :
45  dragging( false ),
46  pressed( false ),
47  dragMaxDelta( 0.0f ),
48  button( aButton ),
49  downEvent( aDownEvent ),
50  upEvent( aUpEvent ),
51  dblClickEvent( aDblClickEvent )
52  {};
53 
55  bool dragging;
56 
58  bool pressed;
59 
62 
65 
68  double dragMaxDelta;
69 
72 
74  wxEventType downEvent;
75 
77  wxEventType upEvent;
78 
80  wxEventType dblClickEvent;
81 
83  wxLongLong downTimestamp;
84 
86  void Reset()
87  {
88  dragging = false;
89  pressed = false;
90  }
91 
93  bool GetState() const
94  {
95  wxMouseState mouseState = wxGetMouseState();
96 
97  switch( button )
98  {
99  case BUT_LEFT:
100  return mouseState.LeftIsDown();
101 
102  case BUT_MIDDLE:
103  return mouseState.MiddleIsDown();
104 
105  case BUT_RIGHT:
106  return mouseState.RightIsDown();
107 
108  default:
109  assert( false );
110  break;
111  }
112 
113  return false;
114  }
115 };
116 
117 
119  m_toolMgr( aToolMgr ),
120  m_actions( aActions )
121 {
122  m_buttons.push_back( new BUTTON_STATE( BUT_LEFT, wxEVT_LEFT_DOWN,
123  wxEVT_LEFT_UP, wxEVT_LEFT_DCLICK ) );
124  m_buttons.push_back( new BUTTON_STATE( BUT_RIGHT, wxEVT_RIGHT_DOWN,
125  wxEVT_RIGHT_UP, wxEVT_RIGHT_DCLICK ) );
126  m_buttons.push_back( new BUTTON_STATE( BUT_MIDDLE, wxEVT_MIDDLE_DOWN,
127  wxEVT_MIDDLE_UP, wxEVT_MIDDLE_DCLICK ) );
128 
129  ResetState();
130 }
131 
132 
134 {
135  for( BUTTON_STATE* st : m_buttons )
136  delete st;
137 }
138 
139 
141 {
142  for( BUTTON_STATE* st : m_buttons )
143  st->Reset();
144 }
145 
146 
148 {
149  return static_cast<EDA_DRAW_FRAME*>( m_toolMgr->GetEditFrame() )->GetGalCanvas()->GetView();
150 }
151 
152 
153 bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion )
154 {
155  BUTTON_STATE* st = m_buttons[aIndex];
156  wxEventType type = aEvent.GetEventType();
158  bool isClick = false;
159 
160 // bool up = type == st->upEvent;
161 // bool down = type == st->downEvent;
162  bool up = false, down = false;
163  bool dblClick = type == st->dblClickEvent;
164  bool state = st->GetState();
165 
166  if( !dblClick )
167  {
168  // Sometimes the dispatcher does not receive mouse button up event, so it stays
169  // in the dragging mode even if the mouse button is not held anymore
170  if( st->pressed && !state )
171  up = true;
172  else if( !st->pressed && state )
173  down = true;
174  }
175 
176  int mods = decodeModifiers( static_cast<wxMouseEvent*>( &aEvent ) );
177  int args = st->button | mods;
178 
179  if( down ) // Handle mouse button press
180  {
181  st->downTimestamp = wxGetLocalTimeMillis();
182 
183  if( !st->pressed ) // save the drag origin on the first click only
185 
187  st->dragMaxDelta = 0;
188  st->pressed = true;
189  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DOWN, args );
190  }
191  else if( up ) // Handle mouse button release
192  {
193  st->pressed = false;
194 
195  if( st->dragging )
196  {
197  wxLongLong t = wxGetLocalTimeMillis();
198 
199  // Determine if it was just a single click or beginning of dragging
200  if( t - st->downTimestamp < DragTimeThreshold &&
202  isClick = true;
203  else
204  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_UP, args );
205  }
206  else
207  isClick = true;
208 
209  if( isClick )
210  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_CLICK, args );
211 
212  st->dragging = false;
213  }
214  else if( dblClick )
215  {
216  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DBLCLICK, args );
217  }
218 
219  if( st->pressed && aMotion )
220  {
221  st->dragging = true;
222  double dragPixelDistance =
224  st->dragMaxDelta = std::max( st->dragMaxDelta, dragPixelDistance );
225 
226  wxLongLong t = wxGetLocalTimeMillis();
227 
229  {
230  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DRAG, args );
231  evt->setMouseDragOrigin( st->dragOrigin );
232  evt->setMouseDelta( m_lastMousePos - st->dragOrigin );
233  }
234  }
235 
236  if( evt )
237  {
238  evt->SetMousePosition( isClick ? st->downPosition : m_lastMousePos );
239  m_toolMgr->ProcessEvent( *evt );
240 
241  return true;
242  }
243 
244  return false;
245 }
246 
247 
248 // Helper function to know if a special key ( see key list ) should be captured
249 // or if the event can be skipped
250 // on Linux, the event must be passed to the GUI if they are not used by Kicad,
251 // especially the wxEVENT_CHAR_HOOK, if it is not handled
252 // unfortunately, m_toolMgr->ProcessEvent( const TOOL_EVENT& aEvent)
253 // does not return info about that. So the event is filtered before passed to
254 // the GUI. These key codes are known to be used in pcbnew to move the cursor
255 // or change active layer, and have a default action (moving scrollbar button) if
256 // the event is skipped
257 bool isKeySpecialCode( int aKeyCode )
258 {
259  const enum wxKeyCode special_keys[] =
260  {
261  WXK_UP, WXK_DOWN, WXK_LEFT, WXK_RIGHT,
262  WXK_PAGEUP, WXK_PAGEDOWN,
263  WXK_NUMPAD_UP, WXK_NUMPAD_DOWN, WXK_NUMPAD_LEFT, WXK_NUMPAD_RIGHT,
264  WXK_NUMPAD_PAGEUP, WXK_NUMPAD_PAGEDOWN
265  };
266 
267  bool isInList = false;
268 
269  for( unsigned ii = 0; ii < DIM( special_keys ) && !isInList; ii++ )
270  {
271  if( special_keys[ii] == aKeyCode )
272  isInList = true;
273  }
274 
275  return isInList;
276 }
277 
278 /* aHelper class that convert some special key codes to an equivalent.
279  * WXK_NUMPAD_UP to WXK_UP,
280  * WXK_NUMPAD_DOWN to WXK_DOWN,
281  * WXK_NUMPAD_LEFT to WXK_LEFT,
282  * WXK_NUMPAD_RIGHT,
283  * WXK_NUMPAD_PAGEUP,
284  * WXK_NUMPAD_PAGEDOWN
285  * note:
286  * wxEVT_CHAR_HOOK does this conversion when it is skipped by fireing a wxEVT_CHAR
287  * with this converted code, but we do not skip these key events because they also
288  * have default action (scroll the panel)
289  */
290 int translateSpecialCode( int aKeyCode )
291 {
292  switch( aKeyCode )
293  {
294  case WXK_NUMPAD_UP: return WXK_UP;
295  case WXK_NUMPAD_DOWN: return WXK_DOWN;
296  case WXK_NUMPAD_LEFT: return WXK_LEFT;
297  case WXK_NUMPAD_RIGHT: return WXK_RIGHT;
298  case WXK_NUMPAD_PAGEUP: return WXK_PAGEUP;
299  case WXK_NUMPAD_PAGEDOWN: return WXK_PAGEDOWN;
300  default: break;
301  };
302 
303  return aKeyCode;
304 }
305 
306 
307 void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
308 {
309  bool motion = false, buttonEvents = false;
311  int key = 0; // key = 0 if the event is not a key event
312  bool keyIsSpecial = false; // True if the key is a special key code
313 
314  int type = aEvent.GetEventType();
315 
316  // Mouse handling
317  // Note: wxEVT_LEFT_DOWN event must always be skipped.
318  if( type == wxEVT_MOTION || type == wxEVT_MOUSEWHEEL ||
319 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
320  type == wxEVT_MAGNIFY ||
321 #endif
322  type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_UP ||
323  type == wxEVT_MIDDLE_DOWN || type == wxEVT_MIDDLE_UP ||
324  type == wxEVT_RIGHT_DOWN || type == wxEVT_RIGHT_UP ||
325  type == wxEVT_LEFT_DCLICK || type == wxEVT_MIDDLE_DCLICK || type == wxEVT_RIGHT_DCLICK ||
326  // Event issued whem mouse retains position in screen coordinates,
327  // but changes in world coordinates (e.g. autopanning)
329  {
330  wxMouseEvent* me = static_cast<wxMouseEvent*>( &aEvent );
331  int mods = decodeModifiers( me );
332 
334 
335  if( pos != m_lastMousePos )
336  {
337  motion = true;
338  m_lastMousePos = pos;
339  }
340 
341  for( unsigned int i = 0; i < m_buttons.size(); i++ )
342  buttonEvents |= handleMouseButton( aEvent, i, motion );
343 
344  if( !buttonEvents && motion )
345  {
346  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_MOTION, mods );
347  evt->SetMousePosition( pos );
348  }
349 
350 #ifdef __APPLE__
351  // TODO That's a big ugly workaround, somehow DRAWPANEL_GAL loses focus
352  // after second LMB click and currently I have no means to do better debugging
353  if( type == wxEVT_LEFT_UP )
354  static_cast<PCB_BASE_FRAME*>( m_toolMgr->GetEditFrame() )->GetGalCanvas()->SetFocus();
355 #endif /* __APPLE__ */
356  }
357  else if( type == wxEVT_CHAR_HOOK || type == wxEVT_CHAR )
358  {
359  wxKeyEvent* ke = static_cast<wxKeyEvent*>( &aEvent );
360  key = ke->GetKeyCode();
361  keyIsSpecial = isKeySpecialCode( key );
362 
363  // if the key event must be skipped, skip it here if the event is a wxEVT_CHAR_HOOK
364  // and do nothing.
365  // a wxEVT_CHAR will be fired by wxWidgets later for this key.
366  if( type == wxEVT_CHAR_HOOK )
367  {
368  if( !keyIsSpecial )
369  {
370  aEvent.Skip();
371  return;
372  }
373  else
374  key = translateSpecialCode( key );
375  }
376 
377  int mods = decodeModifiers( ke );
378 
379  // wxLogMessage( "key %d evt type %d", key, type );
380 
381  if( mods & MD_CTRL )
382  {
383  // wxWidgets maps key codes related to Ctrl+letter handled by CHAR_EVT
384  // (http://docs.wxwidgets.org/trunk/classwx_key_event.html):
385  // char events for ASCII letters in this case carry codes corresponding to the ASCII
386  // value of Ctrl-Latter, i.e. 1 for Ctrl-A, 2 for Ctrl-B and so on until 26 for Ctrl-Z.
387  // They are remapped here to be more easy to handle in code
388  if( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
389  key += 'A' - 1;
390  }
391 
392  if( key == WXK_ESCAPE ) // ESC is the special key for cancelling tools
394  else
395  evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_PRESSED, key | mods );
396  }
397 
398  if( evt )
399  m_toolMgr->ProcessEvent( *evt );
400 
401  // pass the event to the GUI, it might still be interested in it
402  // Note wxEVT_CHAR_HOOK event is already skipped for special keys not used by kicad
403  // and wxEVT_LEFT_DOWN must be always Skipped.
404  //
405  // On OS X, key events are always meant to be caught. An uncaught key event is assumed
406  // to be a user input error by OS X (as they are pressing keys in a context where nothing
407  // is there to catch the event). This annoyingly makes OS X beep and/or flash the screen
408  // in pcbnew and the footprint editor any time a hotkey is used. The correct procedure is
409  // to NOT pass wxEVT_CHAR events to the GUI under OS X.
410  //
411  // On Windows, avoid to call wxEvent::Skip for special keys because some keys (ARROWS, PAGE_UP, PAGE_DOWN
412  // have predefined actions (like move thumbtrack cursor), and we do not want these
413  // actions executed (most are handled by Kicad)
414 
415  if( !evt || type == wxEVT_LEFT_DOWN )
416  aEvent.Skip();
417 
418  // The suitable Skip is already called, but the wxEVT_CHAR
419  // must be Skipped (sent to GUI).
420  // Otherwise accelerators and shortcuts in main menu or toolbars are not seen.
421 #ifndef __APPLE__
422  if( type == wxEVT_CHAR && !keyIsSpecial )
423  aEvent.Skip();
424 #endif
425 
426  updateUI();
427 }
428 
429 
430 void TOOL_DISPATCHER::DispatchWxCommand( wxCommandEvent& aEvent )
431 {
433 
434  if( evt )
435  m_toolMgr->ProcessEvent( *evt );
436  else
437  aEvent.Skip();
438 
439  updateUI();
440 }
441 
442 
444 {
445  // TODO I don't feel it is the right place for updating UI,
446  // but at the moment I cannot think of a better one..
447  EDA_DRAW_FRAME* frame = static_cast<EDA_DRAW_FRAME*>( m_toolMgr->GetEditFrame() );
448  frame->UpdateStatusBar();
449  //frame->UpdateMsgPanel();
450 }
#define DIM(x)
of elements in an array
Definition: macros.h:98
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)
virtual VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Function GetMousePosition() Returns the current mouse pointer position.
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...
int translateSpecialCode(int aKeyCode)
wxWindow * GetEditFrame() const
Definition: tool_manager.h:266
static const int DragDistanceThreshold
The distance threshold for mouse cursor that disinguishes between a single mouse click and a beginnin...
virtual ~TOOL_DISPATCHER()
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:54
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:256
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...
bool isKeySpecialCode(int aKeyCode)
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:460
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:670
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.