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();
181 
182  if( !st->pressed ) // save the drag origin on the first click only
184 
186  st->dragMaxDelta = 0;
187  st->pressed = true;
188  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DOWN, args );
189  }
190  else if( up ) // Handle mouse button release
191  {
192  st->pressed = false;
193 
194  if( st->dragging )
195  {
196  wxLongLong t = wxGetLocalTimeMillis();
197 
198  // Determine if it was just a single click or beginning of dragging
199  if( t - st->downTimestamp < DragTimeThreshold &&
201  isClick = true;
202  else
203  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_UP, args );
204  }
205  else
206  isClick = true;
207 
208  if( isClick )
209  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_CLICK, args );
210 
211  st->dragging = false;
212  }
213  else if( dblClick )
214  {
215  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DBLCLICK, args );
216  }
217 
218  if( st->pressed && aMotion )
219  {
220  st->dragging = true;
221  double dragPixelDistance =
223  st->dragMaxDelta = std::max( st->dragMaxDelta, dragPixelDistance );
224 
225  wxLongLong t = wxGetLocalTimeMillis();
226 
228  {
229  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DRAG, args );
230  evt->setMouseDragOrigin( st->dragOrigin );
231  evt->setMouseDelta( m_lastMousePos - st->dragOrigin );
232  }
233  }
234 
235  if( evt )
236  {
237  evt->SetMousePosition( isClick ? st->downPosition : m_lastMousePos );
238  m_toolMgr->ProcessEvent( *evt );
239 
240  return true;
241  }
242 
243  return false;
244 }
245 
246 
247 void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
248 {
249  bool motion = false, buttonEvents = false;
251 
252  int type = aEvent.GetEventType();
253 
254  // Mouse handling
255  if( type == wxEVT_MOTION || type == wxEVT_MOUSEWHEEL ||
256 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
257  type == wxEVT_MAGNIFY ||
258 #endif
259  type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_UP ||
260  type == wxEVT_MIDDLE_DOWN || type == wxEVT_MIDDLE_UP ||
261  type == wxEVT_RIGHT_DOWN || type == wxEVT_RIGHT_UP ||
262  type == wxEVT_LEFT_DCLICK || type == wxEVT_MIDDLE_DCLICK || type == wxEVT_RIGHT_DCLICK ||
263  // Event issued whem mouse retains position in screen coordinates,
264  // but changes in world coordinates (e.g. autopanning)
266  {
267  wxMouseEvent* me = static_cast<wxMouseEvent*>( &aEvent );
268  int mods = decodeModifiers( me );
269 
271 
272  if( pos != m_lastMousePos )
273  {
274  motion = true;
275  m_lastMousePos = pos;
276  }
277 
278  for( unsigned int i = 0; i < m_buttons.size(); i++ )
279  buttonEvents |= handleMouseButton( aEvent, i, motion );
280 
281  if( !buttonEvents && motion )
282  {
283  evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_MOTION, mods );
284  evt->SetMousePosition( pos );
285  }
286 
287 #ifdef __APPLE__
288  // TODO That's a big ugly workaround, somehow DRAWPANEL_GAL loses focus
289  // after second LMB click and currently I have no means to do better debugging
290  if( type == wxEVT_LEFT_UP )
291  static_cast<PCB_BASE_FRAME*>( m_toolMgr->GetEditFrame() )->GetGalCanvas()->SetFocus();
292 #endif /* __APPLE__ */
293  }
294 
295  // Keyboard handling
296  else if( type == wxEVT_CHAR )
297  {
298  wxKeyEvent* ke = static_cast<wxKeyEvent*>( &aEvent );
299  int key = ke->GetKeyCode();
300  int mods = decodeModifiers( ke );
301 
302  if( mods & MD_CTRL )
303  {
304 #if !wxCHECK_VERSION( 2, 9, 0 )
305  // I really look forward to the day when we will use only one version of wxWidgets..
306  const int WXK_CONTROL_A = 1;
307  const int WXK_CONTROL_Z = 26;
308 #endif
309 
310  // wxWidgets have a quirk related to Ctrl+letter hot keys handled by CHAR_EVT
311  // http://docs.wxwidgets.org/trunk/classwx_key_event.html:
312  // "char events for ASCII letters in this case carry codes corresponding to the ASCII
313  // value of Ctrl-Latter, i.e. 1 for Ctrl-A, 2 for Ctrl-B and so on until 26 for Ctrl-Z."
314  if( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z )
315  key += 'A' - 1;
316  }
317 
318  if( key == WXK_ESCAPE ) // ESC is the special key for cancelling tools
320  else
321  evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_PRESSED, key | mods );
322  }
323 
324  if( evt )
325  m_toolMgr->ProcessEvent( *evt );
326 
327  // pass the event to the GUI, it might still be interested in it
328 #ifdef __APPLE__
329  // On OS X, key events are always meant to be caught. An uncaught key event is assumed
330  // to be a user input error by OS X (as they are pressing keys in a context where nothing
331  // is there to catch the event). This annoyingly makes OS X beep and/or flash the screen
332  // in pcbnew and the footprint editor any time a hotkey is used. The correct procedure is
333  // to NOT pass key events to the GUI under OS X.
334 
335  if( type != wxEVT_CHAR )
336  aEvent.Skip();
337 #else
338  aEvent.Skip();
339 #endif
340 
341  updateUI();
342 }
343 
344 
345 void TOOL_DISPATCHER::DispatchWxCommand( wxCommandEvent& aEvent )
346 {
348 
349  if( evt )
350  m_toolMgr->ProcessEvent( *evt );
351  else
352  aEvent.Skip();
353 
354  updateUI();
355 }
356 
357 
359 {
360  // TODO I don't feel it is the right place for updating UI,
361  // but at the moment I cannot think of a better one..
362  EDA_DRAW_FRAME* frame = static_cast<EDA_DRAW_FRAME*>( m_toolMgr->GetEditFrame() );
363  frame->UpdateStatusBar();
364  //frame->UpdateMsgPanel();
365 }
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...
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()
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:669
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.