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 ), nullptr, 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 ), nullptr, this );
91 #endif
92  m_parentPanel->Connect( wxEVT_MOUSEWHEEL,
93  wxMouseEventHandler( WX_VIEW_CONTROLS::onWheel ), nullptr, this );
94  m_parentPanel->Connect( wxEVT_MIDDLE_UP,
95  wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
96  m_parentPanel->Connect( wxEVT_MIDDLE_DOWN,
97  wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
98  m_parentPanel->Connect( wxEVT_LEFT_UP,
99  wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
100  m_parentPanel->Connect( wxEVT_LEFT_DOWN,
101  wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
102  m_parentPanel->Connect( wxEVT_RIGHT_UP,
103  wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
104  m_parentPanel->Connect( wxEVT_RIGHT_DOWN,
105  wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
106 #if defined __WXMSW__
107  m_parentPanel->Connect( wxEVT_ENTER_WINDOW,
108  wxMouseEventHandler( WX_VIEW_CONTROLS::onEnter ), nullptr, this );
109 #endif
110  m_parentPanel->Connect( wxEVT_LEAVE_WINDOW,
111  wxMouseEventHandler( WX_VIEW_CONTROLS::onLeave ), nullptr, this );
112  m_parentPanel->Connect( wxEVT_SCROLLWIN_THUMBTRACK,
113  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
114  m_parentPanel->Connect( wxEVT_SCROLLWIN_PAGEUP,
115  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
116  m_parentPanel->Connect( wxEVT_SCROLLWIN_PAGEDOWN,
117  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
118 
119  m_parentPanel->Connect( wxEVT_SCROLLWIN_BOTTOM,
120  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
121  m_parentPanel->Connect( wxEVT_SCROLLWIN_TOP,
122  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
123  m_parentPanel->Connect( wxEVT_SCROLLWIN_LINEUP,
124  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
125  m_parentPanel->Connect( wxEVT_SCROLLWIN_LINEDOWN,
126  wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
127 #if defined USE_MOUSE_CAPTURE
128  m_parentPanel->Connect( wxEVT_MOUSE_CAPTURE_LOST,
129  wxMouseEventHandler( WX_VIEW_CONTROLS::onCaptureLost ), nullptr, this );
130 #endif
131 
132  m_cursorWarped = false;
133 
134  m_panTimer.SetOwner( this );
135  this->Connect( wxEVT_TIMER, wxTimerEventHandler( WX_VIEW_CONTROLS::onTimer ), nullptr, 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 );
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 
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 
403 #if defined USE_MOUSE_CAPTURE
404  if( !m_parentPanel->HasCapture() )
405  m_parentPanel->CaptureMouse();
406 #endif
407  }
408  else if( ( aEvent.MiddleDown() && m_settings.m_dragMiddle == MOUSE_DRAG_ACTION::ZOOM ) ||
409  ( aEvent.RightDown() && m_settings.m_dragRight == MOUSE_DRAG_ACTION::ZOOM ) )
410  {
411  m_dragStartPoint = VECTOR2D( aEvent.GetX(), aEvent.GetY() );
415 
416 #if defined USE_MOUSE_CAPTURE
417  if( !m_parentPanel->HasCapture() )
418  m_parentPanel->CaptureMouse();
419 #endif
420  }
421 
422  if( aEvent.LeftUp() )
423  m_state = IDLE; // Stop autopanning when user release left mouse button
424 
425  break;
426 
427  case DRAG_ZOOMING:
428  case DRAG_PANNING:
429  if( aEvent.MiddleUp() || aEvent.LeftUp() || aEvent.RightUp() )
430  {
431  m_state = IDLE;
432 
433 #if defined USE_MOUSE_CAPTURE
434  if( !m_settings.m_cursorCaptured && m_parentPanel->HasCapture() )
435  m_parentPanel->ReleaseMouse();
436 #endif
437  }
438 
439  break;
440  }
441 
442  aEvent.Skip();
443 }
444 
445 
446 void WX_VIEW_CONTROLS::onEnter( wxMouseEvent& aEvent )
447 {
448  // Avoid stealing focus from text controls
449  // This is particularly important for users using On-Screen-Keyboards
450  // They may move the mouse over the canvas to reach the keyboard
452  {
453  return;
454  }
455 
456 #if defined( _WIN32 )
457  // Win32 transmits mouse move and wheel events to all controls below the mouse regardless
458  // of focus. Forcing the focus here will cause the EDA FRAMES to immediately become the
459  // top level active window.
460  if( m_parentPanel->GetParent() != nullptr )
461  {
462  // this assumes the parent panel's parent is the eda window
463  if( KIPLATFORM::UI::IsWindowActive( m_parentPanel->GetParent() ) )
464  {
466  }
467  }
468 #else
470 #endif
471 }
472 
473 
474 void WX_VIEW_CONTROLS::onLeave( wxMouseEvent& aEvent )
475 {
476 #if !defined USE_MOUSE_CAPTURE
477  onMotion( aEvent );
478 #endif
479 }
480 
481 void WX_VIEW_CONTROLS::onCaptureLost( wxMouseEvent& aEvent )
482 {
483  // This method must be present to suppress the capture-lost assertion
484 
485  // Set the flag to allow calling m_parentPanel->CaptureMouse()
486  // Note: One cannot call m_parentPanel->CaptureMouse() twice, this is not accepted
487  // by wxWidgets (MSW specific) so we need this guard
489 }
490 
491 
492 void WX_VIEW_CONTROLS::onTimer( wxTimerEvent& aEvent )
493 {
494  switch( m_state )
495  {
496  case AUTO_PANNING:
497  {
499  {
500  m_state = IDLE;
501  return;
502  }
503 
504  if( !m_parentPanel->HasFocus() )
505  break;
506 
507  double borderSize = std::min( m_settings.m_autoPanMargin * m_view->GetScreenPixelSize().x,
509 
510  // When the mouse cursor is outside the area with no pan,
511  // m_panDirection is the dist to this area limit ( in pixels )
512  // It will be used also as pan value (the pan speed depends on this dist).
513  VECTOR2D dir( m_panDirection );
514 
515  // When the mouse cursor is outside the area with no pan, the pan value
516  // is accelerated depending on the dist between the area and the cursor
517  float accel = 0.5f + ( m_settings.m_autoPanAcceleration / 5.0f );
518 
519  // For a small mouse cursor dist to area, just use the distance.
520  // But for a dist > borderSize / 2, use an accelerated pan value
521 
522  if( dir.EuclideanNorm() >= borderSize ) // far from area limits
523  dir = dir.Resize( borderSize * accel );
524  else if( dir.EuclideanNorm() > borderSize / 2 ) // Near from area limits
525  dir = dir.Resize( borderSize );
526 
527  dir = m_view->ToWorld( dir, false );
528  m_view->SetCenter( m_view->GetCenter() + dir );
529 
530  refreshMouse();
531  }
532  break;
533 
534  case IDLE: // Just remove unnecessary warnings
535  case DRAG_PANNING:
536  case DRAG_ZOOMING:
537  break;
538  }
539 }
540 
541 
542 void WX_VIEW_CONTROLS::onScroll( wxScrollWinEvent& aEvent )
543 {
544  const double linePanDelta = 0.05;
545  const double pagePanDelta = 0.5;
546 
547  int type = aEvent.GetEventType();
548  int dir = aEvent.GetOrientation();
549 
550  if( type == wxEVT_SCROLLWIN_THUMBTRACK )
551  {
552  auto center = m_view->GetCenter();
553  const auto& boundary = m_view->GetBoundary();
554 
555  // Flip scroll direction in flipped view
556  const double xstart = ( m_view->IsMirroredX() ?
557  boundary.GetRight() : boundary.GetLeft() );
558  const double xdelta = ( m_view->IsMirroredX() ? -1 : 1 );
559 
560  if( dir == wxHORIZONTAL )
561  center.x = xstart + xdelta * ( aEvent.GetPosition() / m_scrollScale.x );
562  else
563  center.y = boundary.GetTop() + aEvent.GetPosition() / m_scrollScale.y;
564 
565  m_view->SetCenter( center );
566  }
567  else
568  {
569  double dist = 0;
570 
571  if( type == wxEVT_SCROLLWIN_PAGEUP )
572  dist = pagePanDelta;
573  else if( type == wxEVT_SCROLLWIN_PAGEDOWN )
574  dist = -pagePanDelta;
575  else if( type == wxEVT_SCROLLWIN_LINEUP )
576  dist = linePanDelta;
577  else if( type == wxEVT_SCROLLWIN_LINEDOWN )
578  dist = -linePanDelta;
579  else
580  wxCHECK_MSG( false, /* void */, wxT( "Unhandled event type" ) );
581 
582  VECTOR2D scroll = m_view->ToWorld( m_view->GetScreenPixelSize(), false ) * dist;
583 
584  double scrollX = 0.0;
585  double scrollY = 0.0;
586 
587  if ( dir == wxHORIZONTAL )
588  scrollX = -scroll.x;
589  else
590  scrollY = -scroll.y;
591 
592  VECTOR2D delta( scrollX, scrollY );
593 
595  }
596 
598 }
599 
600 
601 void WX_VIEW_CONTROLS::CaptureCursor( bool aEnabled )
602 {
603 #if defined USE_MOUSE_CAPTURE
604  // Note: for some reason, m_parentPanel->HasCapture() can be false even if CaptureMouse()
605  // was called (i.e. mouse was captured, so when need to test m_MouseCapturedLost to be
606  // sure a wxEVT_MOUSE_CAPTURE_LOST event was fired before. Otherwise wxMSW complains
607  if( aEnabled && !m_parentPanel->HasCapture() && m_parentPanel->m_MouseCapturedLost )
608  {
609  m_parentPanel->CaptureMouse();
610 
611  // Clear the flag to allow calling m_parentPanel->CaptureMouse()
612  // Calling it without calling ReleaseMouse() is not accepted by wxWidgets (MSW specific)
614  }
615  else if( !aEnabled && m_parentPanel->HasCapture()
617  {
618  m_parentPanel->ReleaseMouse();
619 
620  // Mouse is released, calling CaptureMouse() is allowed now:
622  }
623 #endif
624  VIEW_CONTROLS::CaptureCursor( aEnabled );
625 }
626 
627 
629 {
631  {
632  m_state = IDLE;
633 #if defined USE_MOUSE_CAPTURE
634  if( !m_settings.m_cursorCaptured && m_parentPanel->HasCapture() )
635  m_parentPanel->ReleaseMouse();
636 #endif
637  }
638 }
639 
640 
641 VECTOR2D WX_VIEW_CONTROLS::GetMousePosition( bool aWorldCoordinates ) const
642 {
643  wxPoint msp = getMouseScreenPosition();
644  VECTOR2D screenPos( msp.x, msp.y );
645 
646  return aWorldCoordinates ? m_view->ToWorld( screenPos ) : screenPos;
647 }
648 
649 
651 {
652  GAL* gal = m_view->GetGAL();
653 
654  if( aEnableSnapping && gal->GetGridSnapping() )
655  {
656  return gal->GetGridPoint( m_cursorPos );
657  }
658  else
659  {
660  return m_cursorPos;
661  }
662 }
663 
664 
665 VECTOR2D WX_VIEW_CONTROLS::GetCursorPosition( bool aEnableSnapping ) const
666 {
668  {
670  }
671  else
672  {
673  return GetRawCursorPosition( aEnableSnapping );
674  }
675 }
676 
677 
678 void WX_VIEW_CONTROLS::SetCursorPosition( const VECTOR2D& aPosition, bool aWarpView,
679  bool aTriggeredByArrows, long aArrowCommand )
680 {
681  m_updateCursor = false;
682 
683  if( aTriggeredByArrows )
684  {
687  m_settings.m_lastKeyboardCursorCommand = aArrowCommand;
688  m_cursorWarped = false;
689  }
690  else
691  {
695  m_cursorWarped = true;
696  }
697 
698  WarpCursor( aPosition, true, aWarpView );
699  m_cursorPos = aPosition;
700 }
701 
702 
704  bool aWarpView = true )
705 {
706  m_updateCursor = false;
707 
708  const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
709  BOX2I screen( VECTOR2I( 0, 0 ), screenSize );
710  VECTOR2D screenPos = m_view->ToScreen( aPosition );
711 
712  if( aWarpView && !screen.Contains( screenPos ) )
713  m_view->SetCenter( aPosition );
714 
715  m_cursorPos = aPosition;
716 }
717 
718 
719 void WX_VIEW_CONTROLS::WarpCursor( const VECTOR2D& aPosition, bool aWorldCoordinates,
720  bool aWarpView )
721 {
722  if( aWorldCoordinates )
723  {
724  const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
725  BOX2I screen( VECTOR2I( 0, 0 ), screenSize );
726  VECTOR2D screenPos = m_view->ToScreen( aPosition );
727 
728  if( !screen.Contains( screenPos ) )
729  {
730  if( aWarpView )
731  {
732  m_view->SetCenter( aPosition );
733  m_parentPanel->WarpPointer( screenSize.x / 2, screenSize.y / 2 );
734  }
735  }
736  else
737  {
738  m_parentPanel->WarpPointer( screenPos.x, screenPos.y );
739  }
740  }
741  else
742  {
743  m_parentPanel->WarpPointer( aPosition.x, aPosition.y );
744  }
745 
746  refreshMouse();
747 }
748 
749 
751 {
752  const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
753  VECTOR2I screenCenter( screenSize / 2 );
754 
755  if( GetMousePosition( false ) != screenCenter )
756  {
758  m_parentPanel->WarpPointer( KiROUND( screenSize.x / 2 ), KiROUND( screenSize.y / 2 ) );
759  }
760 }
761 
762 
763 bool WX_VIEW_CONTROLS::handleAutoPanning( const wxMouseEvent& aEvent )
764 {
765  VECTOR2I p( aEvent.GetX(), aEvent.GetY() );
767 
769  {
770  // last cursor move event came from keyboard cursor control. If auto-panning is enabled
771  // and the next position is inside the autopan zone, check if it really came from a mouse
772  // event, otherwise disable autopan temporarily. Also temporarily disable autopan if the
773  // cursor is in the autopan zone because the application warped the cursor.
774 
775  m_cursorWarped = false;
776  return true;
777  }
778 
779  m_cursorWarped = false;
780 
781  // Compute areas where autopanning is active
782  int borderStart = std::min( m_settings.m_autoPanMargin * m_view->GetScreenPixelSize().x,
784  borderStart = std::max( borderStart, 2 );
785  int borderEndX = m_view->GetScreenPixelSize().x - borderStart;
786  int borderEndY = m_view->GetScreenPixelSize().y - borderStart;
787 
788  if( p.x < borderStart )
789  m_panDirection.x = -( borderStart - p.x );
790  else if( p.x > borderEndX )
791  m_panDirection.x = ( p.x - borderEndX );
792  else
793  m_panDirection.x = 0;
794 
795  if( p.y < borderStart )
796  m_panDirection.y = -( borderStart - p.y );
797  else if( p.y > borderEndY )
798  m_panDirection.y = ( p.y - borderEndY );
799  else
800  m_panDirection.y = 0;
801 
802  bool borderHit = ( m_panDirection.x != 0 || m_panDirection.y != 0 );
803 
804  switch( m_state )
805  {
806  case AUTO_PANNING:
807  if( !borderHit )
808  {
809  m_panTimer.Stop();
810  m_state = IDLE;
811 
812  return false;
813  }
814 
815  return true;
816  break;
817 
818  case IDLE:
819  if( borderHit )
820  {
822  m_panTimer.Start( (int) ( 250.0 / 60.0 ) );
823 
824  return true;
825  }
826 
827  return false;
828  break;
829 
830  case DRAG_PANNING:
831  case DRAG_ZOOMING:
832  return false;
833  }
834 
835  wxCHECK_MSG( false, false, wxT( "This line should never be reached" ) );
836 }
837 
838 
840 {
842  {
843  bool warp = false;
844  wxSize parentSize = m_parentPanel->GetClientSize();
845 
846  if( x < 0 )
847  {
848  x = 0;
849  warp = true;
850  }
851  else if( x >= parentSize.x )
852  {
853  x = parentSize.x - 1;
854  warp = true;
855  }
856 
857  if( y < 0 )
858  {
859  y = 0;
860  warp = true;
861  }
862  else if( y >= parentSize.y )
863  {
864  y = parentSize.y - 1;
865  warp = true;
866  }
867 
868  if( warp )
869  m_parentPanel->WarpPointer( x, y );
870  }
871 }
872 
873 
875 {
876  // Notify tools that the cursor position has changed in the world coordinates
877  wxMouseEvent moveEvent( EVT_REFRESH_MOUSE );
878  wxPoint msp = getMouseScreenPosition();
879  moveEvent.SetX( msp.x );
880  moveEvent.SetY( msp.y );
881 
882  // Set the modifiers state
883  moveEvent.SetControlDown( wxGetKeyState( WXK_CONTROL ) );
884  moveEvent.SetShiftDown( wxGetKeyState( WXK_SHIFT ) );
885  moveEvent.SetAltDown( wxGetKeyState( WXK_ALT ) );
886 
887  m_cursorPos = m_view->ToWorld( VECTOR2D( msp.x, msp.y ) );
888  wxPostEvent( m_parentPanel, moveEvent );
889 }
890 
891 
893 {
894  wxPoint msp = wxGetMousePosition();
895  m_parentPanel->ScreenToClient( &msp.x, &msp.y );
896  return msp;
897 }
898 
899 
901 {
902  const BOX2D viewport = m_view->GetViewport();
903  const BOX2D& boundary = m_view->GetBoundary();
904 
905  m_scrollScale.x = 2e3 / viewport.GetWidth(); // TODO it does not have to be updated so often
906  m_scrollScale.y = 2e3 / viewport.GetHeight();
907  VECTOR2I newScroll( ( viewport.Centre().x - boundary.GetLeft() ) * m_scrollScale.x,
908  ( viewport.Centre().y - boundary.GetTop() ) * m_scrollScale.y );
909 
910  // We add the width of the scroll bar thumb to the range because the scroll range is given by
911  // the full bar while the position is given by the left/top position of the thumb
912  VECTOR2I newRange( m_scrollScale.x * boundary.GetWidth() +
913  m_parentPanel->GetScrollThumb( wxSB_HORIZONTAL ),
914  m_scrollScale.y * boundary.GetHeight() +
915  m_parentPanel->GetScrollThumb( wxSB_VERTICAL ) );
916 
917  // Flip scroll direction in flipped view
918  if( m_view->IsMirroredX() )
919  newScroll.x = ( boundary.GetRight() - viewport.Centre().x ) * m_scrollScale.x;
920 
921  // Adjust scrollbars only if it is needed. Otherwise there are cases when canvas is continuously
922  // refreshed (Windows)
923  if( m_scrollPos != newScroll || newRange.x != m_parentPanel->GetScrollRange( wxSB_HORIZONTAL )
924  || newRange.y != m_parentPanel->GetScrollRange( wxSB_VERTICAL ) )
925  {
926  m_parentPanel->SetScrollbars( 1, 1, newRange.x, newRange.y, newScroll.x, newScroll.y,
927  true );
928  m_scrollPos = newScroll;
929 
930 #if !defined( __APPLE__ ) && !defined( WIN32 )
931  // Trigger a mouse refresh to get the canvas update in GTK (re-draws the scrollbars).
932  // Note that this causes an infinite loop on OSX and Windows (in certain cases) as it
933  // generates a paint event.
934  refreshMouse();
935 #endif
936  }
937 }
938 
939 void WX_VIEW_CONTROLS::ForceCursorPosition( bool aEnabled, const VECTOR2D& aPosition )
940 {
942  m_settings.m_forcedPosition = aPosition;
943 }
VECTOR2D m_lookStartPoint
Current direction of panning (only autopanning mode).
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition: view.cpp:510
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:236
const BOX2D & GetBoundary() const
Definition: view.h:293
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:447
coord_type GetTop() const
Definition: box2.h:187
MOUSE_DRAG_ACTION drag_left
const VECTOR2D & GetCenter() const
Return the center point of this VIEW (in world space coordinates).
Definition: view.h:334
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:182
GAL * GetGAL() const
Return the #GAL this view is using to draw graphical primitives.
Definition: view.h:190
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.
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
bool IsInputControlFocused(wxWindow *aFocus=nullptr)
Check if a input control has focus.
Definition: ui_common.cpp:220
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:576
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
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:180
int m_scrollModifierZoom
What modifier key to enable horizontal pan with the (vertical) scroll wheel.
bool Contains(const Vec &aPoint) const
Definition: box2.h:134
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:1171
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:466
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.
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:238
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:550
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:63
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.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
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:73
VECTOR2I m_scrollPos
The zoom scale when a drag zoom started.
coord_type GetHeight() const
Definition: box2.h:181
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.
constexpr int delta
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:186
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:68
double GetScale() const
Definition: view.h:264
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.