KiCad PCB EDA Suite
wx_view_controls.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) 2012 Torsten Hueter, torstenhtr <at> gmx.de
5  * Copyright (C) 2013-2015 CERN
6  * Copyright (C) 2012-2021 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
9  * @author Maciej Suminski <maciej.suminski@cern.ch>
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, you may find one here:
23  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24  * or you may search the http://www.gnu.org website for the version 2 license,
25  * or you may write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27  */
28 
29 #include <pgm_base.h>
30 #include <view/view.h>
31 #include <view/wx_view_controls.h>
32 #include <view/zoom_controller.h>
34 #include <tool/tool_dispatcher.h>
35 #include <trace_helpers.h>
37 #include <math/util.h> // for KiROUND
38 #include <widgets/ui_common.h>
39 #include <class_draw_panel_gal.h>
40 #include <kiplatform/ui.h>
41 #include <wx/log.h>
42 
43 
44 #if defined __WXMSW__
45  #define USE_MOUSE_CAPTURE
46 #endif
47 
48 using namespace KIGFX;
49 
50 const wxEventType WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE = wxNewEventType();
51 
52 
53 static std::unique_ptr<ZOOM_CONTROLLER> GetZoomControllerForPlatform( bool aAcceleration )
54 {
55 #ifdef __WXMAC__
56  // On Apple pointer devices, wheel events occur frequently and with
57  // smaller rotation values. For those devices, let's handle zoom
58  // based on the rotation amount rather than the time difference.
59  return std::make_unique<CONSTANT_ZOOM_CONTROLLER>( CONSTANT_ZOOM_CONTROLLER::MAC_SCALE );
60 #elif __WXGTK3__
61  // GTK3 is similar, but the scale constant is smaller
62  return std::make_unique<CONSTANT_ZOOM_CONTROLLER>( CONSTANT_ZOOM_CONTROLLER::GTK3_SCALE );
63 #else
64  if( aAcceleration )
65  return std::make_unique<ACCELERATING_ZOOM_CONTROLLER>();
66  else
67  return std::make_unique<CONSTANT_ZOOM_CONTROLLER>( CONSTANT_ZOOM_CONTROLLER::MSW_SCALE );
68 #endif
69 }
70 
71 
73  VIEW_CONTROLS( aView ),
74  m_state( IDLE ),
75  m_parentPanel( aParentPanel ),
76  m_scrollScale( 1.0, 1.0 ),
77  m_initialZoomScale( 0.0 ),
78 #ifdef __WXGTK3__
79  m_lastTimestamp( 0 ),
80 #endif
81  m_cursorPos( 0, 0 ),
82  m_updateCursor( true )
83 {
84  LoadSettings();
85 
86  m_parentPanel->Connect( wxEVT_MOTION,
87  wxMouseEventHandler( WX_VIEW_CONTROLS::onMotion ), NULL, this );
88 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
89  m_parentPanel->Connect( wxEVT_MAGNIFY,
90  wxMouseEventHandler( WX_VIEW_CONTROLS::onMagnify ), NULL, this );
91 #endif
92  m_parentPanel->Connect( wxEVT_MOUSEWHEEL,
93  wxMouseEventHandler( WX_VIEW_CONTROLS::onWheel ), NULL, this );
94  m_parentPanel->Connect( wxEVT_MIDDLE_UP,
95  wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), NULL, this );
96  m_parentPanel->Connect( wxEVT_MIDDLE_DOWN,
97  wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), NULL, this );
98  m_parentPanel->Connect( wxEVT_LEFT_UP,
99  wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), NULL, this );
100  m_parentPanel->Connect( wxEVT_LEFT_DOWN,
101  wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), NULL, this );
102  m_parentPanel->Connect( wxEVT_RIGHT_UP,
103  wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), NULL, this );
104  m_parentPanel->Connect( wxEVT_RIGHT_DOWN,
105  wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), NULL, this );
106 #if defined __WXMSW__
107  m_parentPanel->Connect( wxEVT_ENTER_WINDOW,
108  wxMouseEventHandler( WX_VIEW_CONTROLS::onEnter ), NULL, this );
109 #endif
110  m_parentPanel->Connect( wxEVT_LEAVE_WINDOW,
111  wxMouseEventHandler( WX_VIEW_CONTROLS::onLeave ), NULL, this );
112  m_parentPanel->Connect( wxEVT_SCROLLWIN_THUMBTRACK,
113  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this );
114  m_parentPanel->Connect( wxEVT_SCROLLWIN_PAGEUP,
115  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this );
116  m_parentPanel->Connect( wxEVT_SCROLLWIN_PAGEDOWN,
117  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this );
118 
119  m_parentPanel->Connect( wxEVT_SCROLLWIN_BOTTOM,
120  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this );
121  m_parentPanel->Connect( wxEVT_SCROLLWIN_TOP,
122  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this );
123  m_parentPanel->Connect( wxEVT_SCROLLWIN_LINEUP,
124  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this );
125  m_parentPanel->Connect( wxEVT_SCROLLWIN_LINEDOWN,
126  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), NULL, this );
127 #if defined USE_MOUSE_CAPTURE
128  m_parentPanel->Connect( wxEVT_MOUSE_CAPTURE_LOST,
129  wxMouseEventHandler( WX_VIEW_CONTROLS::onCaptureLost ), NULL, this );
130 #endif
131 
132  m_cursorWarped = false;
133 
134  m_panTimer.SetOwner( this );
135  this->Connect( wxEVT_TIMER, wxTimerEventHandler( WX_VIEW_CONTROLS::onTimer ), NULL, this );
136 
140 }
141 
142 
144 {
145 #if defined USE_MOUSE_CAPTURE
146  if( m_parentPanel->HasCapture() )
147  m_parentPanel->ReleaseMouse();
148 #endif
149 }
150 
151 
153 {
154  COMMON_SETTINGS* cfg = Pgm().GetCommonSettings();
155 
169 
170  m_zoomController.reset();
171 
172  if( cfg->m_Input.zoom_speed_auto )
173  {
175  }
176  else
177  {
178  if( cfg->m_Input.zoom_acceleration )
179  {
181  std::make_unique<ACCELERATING_ZOOM_CONTROLLER>( cfg->m_Input.zoom_speed );
182  }
183  else
184  {
186 
187  m_zoomController = std::make_unique<CONSTANT_ZOOM_CONTROLLER>( scale );
188  }
189  }
190 }
191 
192 
193 void WX_VIEW_CONTROLS::onMotion( wxMouseEvent& aEvent )
194 {
195  bool isAutoPanning = false;
196  int x = aEvent.GetX();
197  int y = aEvent.GetY();
198  VECTOR2D mousePos( x, y );
199 
201  handleCursorCapture( x, y );
202 
204  isAutoPanning = handleAutoPanning( aEvent );
205 
206  if( !isAutoPanning && aEvent.Dragging() )
207  {
208  if( m_state == DRAG_PANNING )
209  {
210  static bool justWarped = false;
211  int warpX = 0;
212  int warpY = 0;
213  wxSize parentSize = m_parentPanel->GetClientSize();
214 
215  if( x < 0 )
216  {
217  warpX = parentSize.x;
218  }
219  else if(x >= parentSize.x )
220  {
221  warpX = -parentSize.x;
222  }
223 
224  if( y < 0 )
225  {
226  warpY = parentSize.y;
227  }
228  else if( y >= parentSize.y )
229  {
230  warpY = -parentSize.y;
231  }
232 
233  if( !justWarped )
234  {
235  VECTOR2D d = m_dragStartPoint - mousePos;
236  VECTOR2D delta = m_view->ToWorld( d, false );
237  m_view->SetCenter( m_lookStartPoint + delta );
238  aEvent.StopPropagation();
239  }
240 
241  if( warpX || warpY )
242  {
243  if( !justWarped )
244  {
245  m_parentPanel->WarpPointer( x + warpX, y + warpY );
246  m_dragStartPoint += VECTOR2D( warpX, warpY );
247  justWarped = true;
248  }
249  else
250  justWarped = false;
251  }
252  else
253  justWarped = false;
254  }
255  else if( m_state == DRAG_ZOOMING )
256  {
257  static bool justWarped = false;
258  int warpY = 0;
259  wxSize parentSize = m_parentPanel->GetClientSize();
260 
261  if( y < 0 )
262  {
263  warpY = parentSize.y;
264  }
265  else if( y >= parentSize.y )
266  {
267  warpY = -parentSize.y;
268  }
269 
270  if( !justWarped )
271  {
272  VECTOR2D d = m_dragStartPoint - mousePos;
273  double scale = exp( d.y * m_settings.m_zoomSpeed * 0.001 );
274 
275  wxLogTrace( traceZoomScroll, wxString::Format( "dy: %f scale: %f", d.y, scale ) );
276 
278  aEvent.StopPropagation();
279  }
280 
281  if( warpY )
282  {
283  if( !justWarped )
284  {
285  m_parentPanel->WarpPointer( x, y + warpY );
286  m_dragStartPoint += VECTOR2D( 0, warpY );
287  justWarped = true;
288  }
289  else
290  justWarped = false;
291  }
292  else
293  justWarped = false;
294  }
295  }
296 
297  if( m_updateCursor ) // do not update the cursor position if it was explicitly set
298  m_cursorPos = m_view->ToWorld( mousePos );
299  else
300  m_updateCursor = true;
301 
302  aEvent.Skip();
303 }
304 
305 
306 void WX_VIEW_CONTROLS::onWheel( wxMouseEvent& aEvent )
307 {
308 #ifdef __WXGTK3__
309  if( aEvent.GetTimestamp() == m_lastTimestamp )
310  {
311  aEvent.Skip( false );
312  return;
313  }
314 
315  m_lastTimestamp = aEvent.GetTimestamp();
316 #endif
317 
318  const double wheelPanSpeed = 0.001;
319  const int axis = aEvent.GetWheelAxis();
320 
321  if( axis == wxMOUSE_WHEEL_HORIZONTAL && !m_settings.m_horizontalPan )
322  return;
323 
324  // Pick the modifier, if any. Shift beats control beats alt, we don't support more than one.
325  int modifiers =
326  aEvent.ShiftDown() ? WXK_SHIFT :
327  ( aEvent.ControlDown() ? WXK_CONTROL : ( aEvent.AltDown() ? WXK_ALT : 0 ) );
328 
329  // Restrict zoom handling to the vertical axis, otherwise horizontal
330  // scrolling events (e.g. touchpads and some mice) end up interpreted
331  // as vertical scroll events and confuse the user.
332  if( axis == wxMOUSE_WHEEL_VERTICAL && modifiers == m_settings.m_scrollModifierZoom )
333  {
334  const int rotation = aEvent.GetWheelRotation();
335  const double zoomScale = m_zoomController->GetScaleForRotation( rotation );
336 
337  if( IsCursorWarpingEnabled() )
338  {
339  CenterOnCursor();
340  m_view->SetScale( m_view->GetScale() * zoomScale );
341  }
342  else
343  {
344  const VECTOR2D anchor = m_view->ToWorld( VECTOR2D( aEvent.GetX(), aEvent.GetY() ) );
345  m_view->SetScale( m_view->GetScale() * zoomScale, anchor );
346  }
347 
348  // Refresh the zoom level and mouse position on message panel
349  // (mouse position has not changed, only the zoom level has changed):
350  refreshMouse();
351  }
352  else
353  {
354  // Scrolling
355  VECTOR2D scrollVec = m_view->ToWorld( m_view->GetScreenPixelSize(), false ) *
356  ( (double) aEvent.GetWheelRotation() * wheelPanSpeed );
357  double scrollX = 0.0;
358  double scrollY = 0.0;
359 
360  if( axis == wxMOUSE_WHEEL_HORIZONTAL || modifiers == m_settings.m_scrollModifierPanH )
361  scrollX = scrollVec.x;
362  else
363  scrollY = -scrollVec.y;
364 
365  VECTOR2D delta( scrollX, scrollY );
366 
367  m_view->SetCenter( m_view->GetCenter() + delta );
368  refreshMouse();
369  }
370 
371  // Do not skip this event, otherwise wxWidgets will fire
372  // 3 wxEVT_SCROLLWIN_LINEUP or wxEVT_SCROLLWIN_LINEDOWN (normal wxWidgets behavior)
373  // and we do not want that.
375 }
376 
377 
378 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
379 void WX_VIEW_CONTROLS::onMagnify( wxMouseEvent& aEvent )
380 {
381  // Scale based on the magnification from our underlying magnification event.
382  VECTOR2D anchor = m_view->ToWorld( VECTOR2D( aEvent.GetX(), aEvent.GetY() ) );
383  m_view->SetScale( m_view->GetScale() * ( aEvent.GetMagnification() + 1.0f ), anchor );
384 
385  aEvent.Skip();
386 }
387 #endif
388 
389 
390 void WX_VIEW_CONTROLS::onButton( wxMouseEvent& aEvent )
391 {
392  switch( m_state )
393  {
394  case IDLE:
395  case AUTO_PANNING:
396  if( ( aEvent.MiddleDown() && m_settings.m_dragMiddle == MOUSE_DRAG_ACTION::PAN ) ||
397  ( aEvent.RightDown() && m_settings.m_dragRight == MOUSE_DRAG_ACTION::PAN ) )
398  {
399  m_dragStartPoint = VECTOR2D( aEvent.GetX(), aEvent.GetY() );
402 #if defined USE_MOUSE_CAPTURE
403  if( !m_parentPanel->HasCapture() )
404  m_parentPanel->CaptureMouse();
405 #endif
406  }
407  else if( ( aEvent.MiddleDown() && m_settings.m_dragMiddle == MOUSE_DRAG_ACTION::ZOOM ) ||
408  ( aEvent.RightDown() && m_settings.m_dragRight == MOUSE_DRAG_ACTION::ZOOM ) )
409  {
410  m_dragStartPoint = VECTOR2D( aEvent.GetX(), aEvent.GetY() );
414 #if defined USE_MOUSE_CAPTURE
415  if( !m_parentPanel->HasCapture() )
416  m_parentPanel->CaptureMouse();
417 #endif
418  }
419 
420  if( aEvent.LeftUp() )
421  m_state = IDLE; // Stop autopanning when user release left mouse button
422 
423  break;
424 
425  case DRAG_ZOOMING:
426  case DRAG_PANNING:
427  if( aEvent.MiddleUp() || aEvent.LeftUp() || aEvent.RightUp() )
428  {
429  m_state = IDLE;
430 #if defined USE_MOUSE_CAPTURE
431  if( !m_settings.m_cursorCaptured && m_parentPanel->HasCapture() )
432  m_parentPanel->ReleaseMouse();
433 #endif
434  }
435 
436  break;
437  }
438 
439  aEvent.Skip();
440 }
441 
442 
443 void WX_VIEW_CONTROLS::onEnter( wxMouseEvent& aEvent )
444 {
445  // Avoid stealing focus from text controls
446  // This is particularly important for users using On-Screen-Keyboards
447  // They may move the mouse over the canvas to reach the keyboard
449  {
450  return;
451  }
452 
453 #if defined( _WIN32 )
454  // Win32 transmits mouse move and wheel events to all controls below the mouse regardless of focus
455  // Forcing the focus here will cause the EDA FRAMES to immediately become the top level active window
456  if( m_parentPanel->GetParent() != nullptr )
457  {
458  // this assumes the parent panel's parent is the eda window
459  if( KIPLATFORM::UI::IsWindowActive( m_parentPanel->GetParent() ) )
460  {
462  }
463  }
464 #else
466 #endif
467 }
468 
469 
470 void WX_VIEW_CONTROLS::onLeave( wxMouseEvent& aEvent )
471 {
472 #if !defined USE_MOUSE_CAPTURE
473  onMotion( aEvent );
474 #endif
475 }
476 
477 void WX_VIEW_CONTROLS::onCaptureLost( wxMouseEvent& aEvent )
478 {
479  // This method must be present to suppress the capture-lost assertion
480 
481  // Set the flag to allow calling m_parentPanel->CaptureMouse()
482  // Nots: One cannot call m_parentPanel->CaptureMouse() twice, thit is not accepted
483  // by wxWidgets (MSW specific) so we need this guard
485 }
486 
487 void WX_VIEW_CONTROLS::onTimer( wxTimerEvent& aEvent )
488 {
489  switch( m_state )
490  {
491  case AUTO_PANNING:
492  {
494  {
495  m_state = IDLE;
496  return;
497  }
498 
499  if( !m_parentPanel->HasFocus() )
500  break;
501 
502  double borderSize = std::min( m_settings.m_autoPanMargin * m_view->GetScreenPixelSize().x,
504 
505  // When the mouse cursor is outside the area with no pan,
506  // m_panDirection is the dist to this area limit ( in pixels )
507  // It will be used also as pan value (the pan speed depends on this dist).
508  VECTOR2D dir( m_panDirection );
509 
510  // When the mouse cursor is outside the area with no pan, the pan value
511  // is accelerated depending on the dist between the area and the cursor
512  float accel = 0.5f + ( m_settings.m_autoPanAcceleration / 5.0f );
513 
514  // For a small mouse cursor dist to area, just use the distance.
515  // But for a dist > borderSize / 2, use an accelerated pan value
516 
517  if( dir.EuclideanNorm() >= borderSize ) // far from area limits
518  dir = dir.Resize( borderSize * accel );
519  else if( dir.EuclideanNorm() > borderSize / 2 ) // Near from area limits
520  dir = dir.Resize( borderSize );
521 
522  dir = m_view->ToWorld( dir, false );
523  m_view->SetCenter( m_view->GetCenter() + dir );
524 
525  refreshMouse();
526  }
527  break;
528 
529  case IDLE: // Just remove unnecessary warnings
530  case DRAG_PANNING:
531  case DRAG_ZOOMING:
532  break;
533  }
534 }
535 
536 
537 void WX_VIEW_CONTROLS::onScroll( wxScrollWinEvent& aEvent )
538 {
539  const double linePanDelta = 0.05;
540  const double pagePanDelta = 0.5;
541 
542  int type = aEvent.GetEventType();
543  int dir = aEvent.GetOrientation();
544 
545  if( type == wxEVT_SCROLLWIN_THUMBTRACK )
546  {
547  auto center = m_view->GetCenter();
548  const auto& boundary = m_view->GetBoundary();
549 
550  // Flip scroll direction in flipped view
551  const double xstart = ( m_view->IsMirroredX() ?
552  boundary.GetRight() : boundary.GetLeft() );
553  const double xdelta = ( m_view->IsMirroredX() ? -1 : 1 );
554 
555  if( dir == wxHORIZONTAL )
556  center.x = xstart + xdelta * ( aEvent.GetPosition() / m_scrollScale.x );
557  else
558  center.y = boundary.GetTop() + aEvent.GetPosition() / m_scrollScale.y;
559 
560  m_view->SetCenter( center );
561  }
562  else
563  {
564  double dist = 0;
565 
566  if( type == wxEVT_SCROLLWIN_PAGEUP )
567  dist = pagePanDelta;
568  else if( type == wxEVT_SCROLLWIN_PAGEDOWN )
569  dist = -pagePanDelta;
570  else if( type == wxEVT_SCROLLWIN_LINEUP )
571  dist = linePanDelta;
572  else if( type == wxEVT_SCROLLWIN_LINEDOWN )
573  dist = -linePanDelta;
574  else
575  {
576  wxASSERT( "Unhandled event type" );
577  return;
578  }
579 
580  VECTOR2D scroll = m_view->ToWorld( m_view->GetScreenPixelSize(), false ) * dist;
581 
582  double scrollX = 0.0;
583  double scrollY = 0.0;
584 
585  if ( dir == wxHORIZONTAL )
586  scrollX = -scroll.x;
587  else
588  scrollY = -scroll.y;
589 
590  VECTOR2D delta( scrollX, scrollY );
591 
592  m_view->SetCenter( m_view->GetCenter() + delta );
593  }
594 
596 }
597 
598 
599 void WX_VIEW_CONTROLS::CaptureCursor( bool aEnabled )
600 {
601 #if defined USE_MOUSE_CAPTURE
602  // Note: for some reason, m_parentPanel->HasCapture() can be false even if CaptureMouse()
603  // was called (i.e. mouse was captured, so when need to test m_MouseCapturedLost to be
604  // sure a wxEVT_MOUSE_CAPTURE_LOST event was fired before. Otherwise wxMSW complains
605  if( aEnabled && !m_parentPanel->HasCapture() && m_parentPanel->m_MouseCapturedLost )
606  {
607  m_parentPanel->CaptureMouse();
608 
609  // Clear the flag to allow calling m_parentPanel->CaptureMouse()
610  // Calling it without calling ReleaseMouse() is not accepted by wxWidgets (MSW specific)
612  }
613  else if( !aEnabled && m_parentPanel->HasCapture()
615  {
616  m_parentPanel->ReleaseMouse();
617 
618  // Mouse is released, calling CaptureMouse() is allowed now:
620  }
621 #endif
622  VIEW_CONTROLS::CaptureCursor( aEnabled );
623 }
624 
625 
627 {
629  {
630  m_state = IDLE;
631 #if defined USE_MOUSE_CAPTURE
632  if( !m_settings.m_cursorCaptured && m_parentPanel->HasCapture() )
633  m_parentPanel->ReleaseMouse();
634 #endif
635  }
636 }
637 
638 
639 VECTOR2D WX_VIEW_CONTROLS::GetMousePosition( bool aWorldCoordinates ) const
640 {
641  wxPoint msp = getMouseScreenPosition();
642  VECTOR2D screenPos( msp.x, msp.y );
643 
644  return aWorldCoordinates ? m_view->ToWorld( screenPos ) : screenPos;
645 }
646 
647 
649 {
650  GAL* gal = m_view->GetGAL();
651 
652  if( aEnableSnapping && gal->GetGridSnapping() )
653  {
654  return gal->GetGridPoint( m_cursorPos );
655  }
656  else
657  {
658  return m_cursorPos;
659  }
660 }
661 
662 
663 VECTOR2D WX_VIEW_CONTROLS::GetCursorPosition( bool aEnableSnapping ) const
664 {
666  {
668  }
669  else
670  {
671  return GetRawCursorPosition( aEnableSnapping );
672  }
673 }
674 
675 
676 void WX_VIEW_CONTROLS::SetCursorPosition( const VECTOR2D& aPosition, bool aWarpView,
677  bool aTriggeredByArrows, long aArrowCommand )
678 {
679  m_updateCursor = false;
680 
681  if( aTriggeredByArrows )
682  {
685  m_settings.m_lastKeyboardCursorCommand = aArrowCommand;
686  m_cursorWarped = false;
687  }
688  else
689  {
693  m_cursorWarped = true;
694  }
695 
696  WarpCursor( aPosition, true, aWarpView );
697  m_cursorPos = aPosition;
698 }
699 
700 
701 void WX_VIEW_CONTROLS::SetCrossHairCursorPosition( const VECTOR2D& aPosition, bool aWarpView = true )
702 {
703  m_updateCursor = false;
704 
705  const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
706  BOX2I screen( VECTOR2I( 0, 0 ), screenSize );
707  VECTOR2D screenPos = m_view->ToScreen( aPosition );
708 
709  if( aWarpView && !screen.Contains( screenPos ) )
710  m_view->SetCenter( aPosition );
711 
712  m_cursorPos = aPosition;
713 }
714 
715 
716 void WX_VIEW_CONTROLS::WarpCursor( const VECTOR2D& aPosition, bool aWorldCoordinates,
717  bool aWarpView )
718 {
719  if( aWorldCoordinates )
720  {
721  const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
722  BOX2I screen( VECTOR2I( 0, 0 ), screenSize );
723  VECTOR2D screenPos = m_view->ToScreen( aPosition );
724 
725  if( !screen.Contains( screenPos ) )
726  {
727  if( aWarpView )
728  {
729  m_view->SetCenter( aPosition );
730  m_parentPanel->WarpPointer( screenSize.x / 2, screenSize.y / 2 );
731  }
732  }
733  else
734  {
735  m_parentPanel->WarpPointer( screenPos.x, screenPos.y );
736  }
737  }
738  else
739  {
740  m_parentPanel->WarpPointer( aPosition.x, aPosition.y );
741  }
742 
743  refreshMouse();
744 }
745 
746 
748 {
749  const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
750  VECTOR2I screenCenter( screenSize / 2 );
751 
752  if( GetMousePosition( false ) != screenCenter )
753  {
755  m_parentPanel->WarpPointer( KiROUND( screenSize.x / 2 ), KiROUND( screenSize.y / 2 ) );
756  }
757 }
758 
759 
760 bool WX_VIEW_CONTROLS::handleAutoPanning( const wxMouseEvent& aEvent )
761 {
762  VECTOR2I p( aEvent.GetX(), aEvent.GetY() );
764 
766  {
767  // last cursor move event came from keyboard cursor control. If auto-panning is enabled and
768  // the next position is inside the autopan zone, check if it really came from a mouse event, otherwise
769  // disable autopan temporarily. Also temporarily disable autopan if the cursor is in the autopan zone
770  // because the application warped the cursor.
771 
772  m_cursorWarped = false;
773  return true;
774  }
775 
776  m_cursorWarped = false;
777 
778  // Compute areas where autopanning is active
779  int borderStart = std::min( m_settings.m_autoPanMargin * m_view->GetScreenPixelSize().x,
781  borderStart = std::max( borderStart, 2 );
782  int borderEndX = m_view->GetScreenPixelSize().x - borderStart;
783  int borderEndY = m_view->GetScreenPixelSize().y - borderStart;
784 
785  if( p.x < borderStart )
786  m_panDirection.x = -( borderStart - p.x );
787  else if( p.x > borderEndX )
788  m_panDirection.x = ( p.x - borderEndX );
789  else
790  m_panDirection.x = 0;
791 
792  if( p.y < borderStart )
793  m_panDirection.y = -( borderStart - p.y );
794  else if( p.y > borderEndY )
795  m_panDirection.y = ( p.y - borderEndY );
796  else
797  m_panDirection.y = 0;
798 
799  bool borderHit = ( m_panDirection.x != 0 || m_panDirection.y != 0 );
800 
801  switch( m_state )
802  {
803  case AUTO_PANNING:
804  if( !borderHit )
805  {
806  m_panTimer.Stop();
807  m_state = IDLE;
808 
809  return false;
810  }
811 
812  return true;
813  break;
814 
815  case IDLE:
816  if( borderHit )
817  {
819  m_panTimer.Start( (int) ( 250.0 / 60.0 ) );
820 
821  return true;
822  }
823 
824  return false;
825  break;
826 
827  case DRAG_PANNING:
828  case DRAG_ZOOMING:
829  return false;
830  }
831 
832  wxASSERT_MSG( false, wxT( "This line should never be reached" ) );
833  return false; // Should not be reached, just avoid the compiler warnings..
834 }
835 
836 
838 {
840  {
841  bool warp = false;
842  wxSize parentSize = m_parentPanel->GetClientSize();
843 
844  if( x < 0 )
845  {
846  x = 0;
847  warp = true;
848  }
849  else if( x >= parentSize.x )
850  {
851  x = parentSize.x - 1;
852  warp = true;
853  }
854 
855  if( y < 0 )
856  {
857  y = 0;
858  warp = true;
859  }
860  else if( y >= parentSize.y )
861  {
862  y = parentSize.y - 1;
863  warp = true;
864  }
865 
866  if( warp )
867  m_parentPanel->WarpPointer( x, y );
868  }
869 }
870 
871 
873 {
874  // Notify tools that the cursor position has changed in the world coordinates
875  wxMouseEvent moveEvent( EVT_REFRESH_MOUSE );
876  wxPoint msp = getMouseScreenPosition();
877  moveEvent.SetX( msp.x );
878  moveEvent.SetY( msp.y );
879 
880  // Set the modifiers state
881  moveEvent.SetControlDown( wxGetKeyState( WXK_CONTROL ) );
882  moveEvent.SetShiftDown( wxGetKeyState( WXK_SHIFT ) );
883  moveEvent.SetAltDown( wxGetKeyState( WXK_ALT ) );
884 
885  m_cursorPos = m_view->ToWorld( VECTOR2D( msp.x, msp.y ) );
886  wxPostEvent( m_parentPanel, moveEvent );
887 }
888 
889 
891 {
892  wxPoint msp = wxGetMousePosition();
893  m_parentPanel->ScreenToClient( &msp.x, &msp.y );
894  return msp;
895 }
896 
897 
899 {
900  const BOX2D viewport = m_view->GetViewport();
901  const BOX2D& boundary = m_view->GetBoundary();
902 
903  m_scrollScale.x = 2e3 / viewport.GetWidth(); // TODO it does not have to be updated so often
904  m_scrollScale.y = 2e3 / viewport.GetHeight();
905  VECTOR2I newScroll( ( viewport.Centre().x - boundary.GetLeft() ) * m_scrollScale.x,
906  ( viewport.Centre().y - boundary.GetTop() ) * m_scrollScale.y );
907 
908  // We add the width of the scroll bar thumb to the range because the scroll range is given by
909  // the full bar while the position is given by the left/top position of the thumb
910  VECTOR2I newRange( m_scrollScale.x * boundary.GetWidth() + m_parentPanel->GetScrollThumb( wxSB_HORIZONTAL ),
911  m_scrollScale.y * boundary.GetHeight() + m_parentPanel->GetScrollThumb( wxSB_VERTICAL ) );
912 
913  // Flip scroll direction in flipped view
914  if( m_view->IsMirroredX() )
915  newScroll.x = ( boundary.GetRight() - viewport.Centre().x ) * m_scrollScale.x;
916 
917  // Adjust scrollbars only if it is needed. Otherwise there are cases when canvas is continuously
918  // refreshed (Windows)
919  if( m_scrollPos != newScroll || newRange.x != m_parentPanel->GetScrollRange( wxSB_HORIZONTAL )
920  || newRange.y != m_parentPanel->GetScrollRange( wxSB_VERTICAL ) )
921  {
922  m_parentPanel->SetScrollbars( 1, 1, newRange.x, newRange.y, newScroll.x, newScroll.y, true );
923  m_scrollPos = newScroll;
924 
925 #if !defined( __APPLE__ ) && !defined( WIN32 )
926  // Trigger a mouse refresh to get the canvas update in GTK (re-draws the scrollbars).
927  // Note that this causes an infinite loop on OSX and Windows (in certain cases) as it generates a paint event.
928  refreshMouse();
929 #endif
930  }
931 }
932 
933 void WX_VIEW_CONTROLS::ForceCursorPosition( bool aEnabled, const VECTOR2D& aPosition )
934 {
936  m_settings.m_forcedPosition = aPosition;
937 }
VECTOR2D m_lookStartPoint
Current direction of panning (only autopanning mode).
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition: view.cpp:514
const wxChar *const traceZoomScroll
Flag to enable debug output of zoom-scrolling calculations in KIGFX::ZOOM_CONTROLLER and derivatives.
void LoadSettings() override
Event that forces mouse move event in the dispatcher (eg.
MOUSE_DRAG_ACTION m_dragRight
Is last cursor motion event coming from keyboard arrow cursor motion action.
EDA_DRAW_PANEL_GAL * m_parentPanel
Store information about point where dragging has started.
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:194
const BOX2D & GetBoundary() const
Definition: view.h:292
Panning with mouse button pressed.
static const wxEventType EVT_REFRESH_MOUSE
static constexpr double MSW_SCALE
A suitable (magic) scale factor for Windows systems.
VECTOR2D ToWorld(const VECTOR2D &aCoord, bool aAbsolute=true) const
Converts a screen space point/vector to a point/vector in world space coordinates.
Definition: view.cpp:451
coord_type GetTop() const
Definition: box2.h:194
MOUSE_DRAG_ACTION drag_left
const VECTOR2D & GetCenter() const
Return the center point of this VIEW (in world space coordinates).
Definition: view.h:333
wxPoint getMouseScreenPosition() const
Get the cursor position in the screen coordinates.
VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const override
Return the current mouse pointer position.
std::unique_ptr< ZOOM_CONTROLLER > m_zoomController
void onButton(wxMouseEvent &aEvent)
VC_SETTINGS m_settings
static std::unique_ptr< ZOOM_CONTROLLER > GetZoomControllerForPlatform(bool aAcceleration)
VECTOR2D m_forcedPosition
Is the forced cursor position enabled.
Definition: view_controls.h:55
coord_type GetRight() const
Definition: box2.h:189
GAL * GetGAL() const
Return the #GAL this view is using to draw graphical primitives.
Definition: view.h:189
VIEW * m_view
< Pointer to controlled VIEW.
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
VECTOR2D m_scrollScale
Current scrollbar position.
WX_VIEW_CONTROLS class definition.
static constexpr double GTK3_SCALE
A suitable (magic) scale factor for Mac systems.
bool IsInputControlFocused()
Checks if a input control has focus.
Definition: ui_common.cpp:138
MOUSE_DRAG_ACTION drag_right
void UpdateScrollbars()
End any mouse drag action still in progress.
void onCaptureLost(wxMouseEvent &WXUNUSED(aEvent))
VECTOR2D GetGridPoint(const VECTOR2D &aPoint) const
For a given point it returns the nearest point belonging to the grid in world coordinates.
Zooming with mouse button pressed.
const VECTOR2I & GetScreenPixelSize() const
Return GAL canvas size in pixels.
int m_scrollModifierPanH
What modifier key to enable vertical with the (vertical) scroll wheel.
VECTOR2D m_lastKeyboardCursorPosition
bool GetGridSnapping() const
VECTOR2D GetRawCursorPosition(bool aSnappingEnabled=true) const override
Return the current cursor position in world coordinates ignoring the cursorUp position force mode.
bool m_autoPanSettingEnabled
Distance from cursor to VIEW edge when panning is active.
Definition: view_controls.h:73
void onWheel(wxMouseEvent &aEvent)
Handler functions.
MOUSE_DRAG_ACTION m_dragLeft
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition: view.cpp:580
bool IsWindowActive(wxWindow *aWindow)
Check to see if the given window is the currently active window (e.g.
Definition: gtk/ui.cpp:50
Panning on approaching borders of the frame.
bool m_forceCursorPosition
Should the cursor be locked within the parent window area.
Definition: view_controls.h:58
#define NULL
VECTOR2< double > VECTOR2D
Definition: vector2d.h:622
MOUSE_DRAG_ACTION m_dragMiddle
bool m_zoomSpeedAuto
What modifier key to enable zoom with the (vertical) scroll wheel.
Definition: view_controls.h:97
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
coord_type GetWidth() const
Definition: box2.h:187
int m_scrollModifierZoom
What modifier key to enable horizontal pan with the (vertical) scroll wheel.
bool Contains(const Vec &aPoint) const
Function Contains.
Definition: box2.h:141
void SetCrossHairCursorPosition(const VECTOR2D &aPosition, bool aWarpView) override
void CaptureCursor(bool aEnabled) override
Force the cursor to stay within the drawing panel area.
float m_autoPanAcceleration
If the cursor is allowed to be warped.
Definition: view_controls.h:82
int m_zoomSpeed
When true, ignore zoom_speed and pick a platform-specific default.
Definition: view_controls.h:94
const VECTOR2I & GetScreenPixelSize() const
Return the size of the our rendering area in pixels.
Definition: view.cpp:1158
Functions to provide common constants and other functions to assist in making a consistent UI.
bool IsCursorWarpingEnabled() const
An interface for classes handling user events controlling the view behavior such as zooming,...
bool m_warpCursor
Enable horizontal panning with the horizontal scroll/trackpad input.
Definition: view_controls.h:85
static constexpr double MAC_SCALE
wxLogTrace helper definitions.
virtual void SetFocus() override
float m_autoPanMargin
How fast is panning when in auto mode.
Definition: view_controls.h:76
bool m_horizontalPan
Enable the accelerating zoom controller.
Definition: view_controls.h:88
bool m_cursorWarped
Current VIEW_CONTROLS settings.
void refreshMouse()
Send an event to refresh mouse position.
VECTOR2D ToScreen(const VECTOR2D &aCoord, bool aAbsolute=true) const
Convert a world space point/vector to a point/vector in screen space coordinates.
Definition: view.cpp:470
bool m_autoPanEnabled
Flag for turning on autopanning.
Definition: view_controls.h:70
void handleCursorCapture(int x, int y)
Limit the cursor position to within the canvas by warping it.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=NULL) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
bool m_zoomAcceleration
Zoom speed for the non-accelerating zoom controller.
Definition: view_controls.h:91
void onMotion(wxMouseEvent &aEvent)
void WarpCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false) override
Set the viewport center to the current cursor position and warps the cursor to the screen center.
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition: view.h:237
void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0)) override
Applies VIEW_CONTROLS settings from the program COMMON_SETTINGS.
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition: view.cpp:554
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
VECTOR2D m_zoomStartPoint
Current cursor position (world coordinates).
void onTimer(wxTimerEvent &WXUNUSED(aEvent))
bool m_MouseCapturedLost
used on wxMSW: true after a wxEVT_MOUSE_CAPTURE_LOST was received false after the mouse is recaptured...
const int scale
see class PGM_BASE
Vec Centre() const
Definition: box2.h:69
VECTOR2D m_panDirection
Timer responsible for handling autopanning.
long m_lastKeyboardCursorCommand
Position of the above event.
static constexpr double MANUAL_SCALE_FACTOR
bool m_updateCursor
A #ZOOM_CONTROLLER that determines zoom steps. This is platform-specific.
bool m_lastKeyboardCursorPositionValid
ACTIONS::CURSOR_UP, ACTIONS::CURSOR_DOWN, etc.
double m_initialZoomScale
The mouse position when a drag zoom started.
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:70
VECTOR2I m_scrollPos
The zoom scale when a drag zoom started.
coord_type GetHeight() const
Definition: box2.h:188
bool handleAutoPanning(const wxMouseEvent &aEvent)
Compute new viewport settings while in autopanning mode.
void onEnter(wxMouseEvent &WXUNUSED(aEvent))
VECTOR2D m_dragStartPoint
Stores information about the center of viewport when dragging has started.
void SetCursorPosition(const VECTOR2D &aPosition, bool warpView, bool aTriggeredByArrows, long aArrowCommand) override
Move the graphic crosshair cursor to the requested position expressed in world coordinates.
STATE m_state
Panel that is affected by VIEW_CONTROLS.
coord_type GetLeft() const
Definition: box2.h:193
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:67
double GetScale() const
Definition: view.h:263
void CenterOnCursor() const override
Adjusts the scrollbars position to match the current viewport.
bool m_cursorCaptured
Should the cursor snap to grid or move freely.
Definition: view_controls.h:61
void onScroll(wxScrollWinEvent &aEvent)
WX_VIEW_CONTROLS(VIEW *aView, EDA_DRAW_PANEL_GAL *aParentPanel)
MOUSE_DRAG_ACTION drag_middle
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
Abstract interface for drawing on a 2D-surface.
void onLeave(wxMouseEvent &WXUNUSED(aEvent))
VECTOR2D m_cursorPos
Flag deciding whether the cursor position should be calculated using the mouse position.
wxTimer m_panTimer
Ratio used for scaling world coordinates to scrollbar position.