KiCad PCB EDA Suite
draw_panel.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) 2009 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr
5  * Copyright (C) 2007-2011 Wayne Stambaugh <stambaughw@verizon.net>
6  * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
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 
30 #include <fctsys.h>
31 #include <pgm_base.h>
32 #include <kiface_i.h>
33 #include <gr_basic.h>
34 #include <common.h>
35 #include <macros.h>
36 #include <id.h>
37 #include <class_drawpanel.h>
38 #include <class_draw_panel_gal.h>
39 #include <class_base_screen.h>
40 #include <draw_frame.h>
41 #include <view/view_controls.h>
43 
44 #include <kicad_device_context.h>
45 
46 static const int CURSOR_SIZE = 12;
47 
48 #define CLIP_BOX_PADDING 2
49 
52 
53 #define ENBL_ZOOM_NO_CENTER_KEY wxT( "ZoomNoCenter" )
54 #define ENBL_MOUSEWHEEL_PAN_KEY wxT( "MousewheelPAN" )
55 #define ENBL_MIDDLE_BUTT_PAN_KEY wxT( "MiddleButtonPAN" )
56 #define MIDDLE_BUTT_PAN_LIMITED_KEY wxT( "MiddleBtnPANLimited" )
57 #define ENBL_AUTO_PAN_KEY wxT( "AutoPAN" )
58 
60 
61 
62 // Definitions for enabling and disabling debugging features in drawpanel.cpp.
63 // Please don't forget to turn these off before making any commits to Launchpad.
64 #define DEBUG_SHOW_CLIP_RECT 0 // Set to 1 to draw clipping rectangle.
65 
66 
73 #define KICAD_TRACE_COORDS wxT( "kicad_dump_coords" )
74 
75 
76 // Events used by EDA_DRAW_PANEL
77 BEGIN_EVENT_TABLE( EDA_DRAW_PANEL, wxScrolledWindow )
78  EVT_LEAVE_WINDOW( EDA_DRAW_PANEL::OnMouseLeaving )
79  EVT_ENTER_WINDOW( EDA_DRAW_PANEL::OnMouseEntering )
80  EVT_MOUSEWHEEL( EDA_DRAW_PANEL::OnMouseWheel )
81 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
82  EVT_MAGNIFY( EDA_DRAW_PANEL::OnMagnify )
83 #endif
84  EVT_MOUSE_EVENTS( EDA_DRAW_PANEL::OnMouseEvent )
85  EVT_CHAR( EDA_DRAW_PANEL::OnKeyEvent )
86  EVT_CHAR_HOOK( EDA_DRAW_PANEL::OnCharHook )
87  EVT_PAINT( EDA_DRAW_PANEL::OnPaint )
88  EVT_ERASE_BACKGROUND( EDA_DRAW_PANEL::OnEraseBackground )
89  EVT_SCROLLWIN( EDA_DRAW_PANEL::OnScroll )
90  EVT_ACTIVATE( EDA_DRAW_PANEL::OnActivate )
92 END_EVENT_TABLE()
93 
94 
95 /***********************************************************************/
96 /* EDA_DRAW_PANEL base functions (EDA_DRAW_PANEL is the main panel)*/
97 /***********************************************************************/
98 
100  const wxPoint& pos, const wxSize& size ) :
101  wxScrolledWindow( parent, id, pos, size, wxBORDER | wxHSCROLL | wxVSCROLL )
102 {
103  wxASSERT( parent );
104 
105  ShowScrollbars( wxSHOW_SB_ALWAYS, wxSHOW_SB_ALWAYS );
106  DisableKeyboardScrolling();
107 
108  m_scrollIncrementX = std::min( size.x / 8, 10 );
109  m_scrollIncrementY = std::min( size.y / 8, 10 );
110 
111  SetLayoutDirection( wxLayout_LeftToRight );
112 
113  SetBackgroundColour( parent->GetDrawBgColor().ToColour() );
114 
115 #if KICAD_USE_BUFFERED_DC || KICAD_USE_BUFFERED_PAINTDC
116  SetBackgroundStyle( wxBG_STYLE_CUSTOM );
117 #endif
118 
119  m_ClipBox.SetSize( size );
120  m_ClipBox.SetX( 0 );
121  m_ClipBox.SetY( 0 );
122  m_canStartBlock = -1; // Command block can start if >= 0
123  m_abortRequest = false;
124  m_enableMousewheelPan = false;
125  m_enableZoomNoCenter = false;
126  m_enableAutoPan = true;
127  m_ignoreMouseEvents = false;
128  // Be sure a mouse release button event will be ignored when creating the canvas
129  // if the mouse click was not made inside the canvas (can happen sometimes, when
130  // launching an editor from a double click made in an other frame)
132 
133  m_mouseCaptureCallback = NULL;
135 
136  wxConfigBase* cfg = Kiface().KifaceSettings();
137 
138  if( cfg )
139  {
140  cfg->Read( ENBL_MOUSEWHEEL_PAN_KEY, &m_enableMousewheelPan, false );
141  cfg->Read( ENBL_ZOOM_NO_CENTER_KEY, &m_enableZoomNoCenter, false );
142  cfg->Read( ENBL_AUTO_PAN_KEY, &m_enableAutoPan, true );
143  }
144 
145  m_requestAutoPan = false;
146  m_enableBlockCommands = false;
148 
149 #ifdef __WXMAC__
150  m_defaultCursor = m_currentCursor = wxCURSOR_CROSS;
151  m_showCrossHair = false;
152 #else
153  m_defaultCursor = m_currentCursor = wxCURSOR_ARROW;
154  m_showCrossHair = true;
155 #endif
156 
157  m_cursorLevel = 0;
158  m_PrintIsMirrored = false;
159 }
160 
161 
163 {
164  wxConfigBase* cfg = Kiface().KifaceSettings();
165 
166  if( cfg )
167  {
170  cfg->Write( ENBL_AUTO_PAN_KEY, m_enableAutoPan );
171  }
172 }
173 
174 
176 {
177  wxWindow* mom = wxScrolledWindow::GetParent();
178  return (EDA_DRAW_FRAME*) mom;
179 }
180 
181 
183 {
184  return GetParent()->GetDisplayOptions();
185 }
186 
187 
189 {
190  EDA_DRAW_FRAME* parentFrame = GetParent();
191 
192  return parentFrame->GetScreen();
193 }
194 
195 
197 {
198  wxPoint ret;
199  INSTALL_UNBUFFERED_DC( dc, this );
200  ret.x = dc.LogicalToDeviceX( pos.x );
201  ret.y = dc.LogicalToDeviceY( pos.y );
202  return ret;
203 }
204 
205 
207 {
208  wxPoint ret;
209  INSTALL_UNBUFFERED_DC( dc, this );
210  ret.x = dc.DeviceToLogicalX( pos.x );
211  ret.y = dc.DeviceToLogicalY( pos.y );
212  return ret;
213 }
214 
215 
216 void EDA_DRAW_PANEL::DrawCrossHair( wxDC* aDC, COLOR4D aColor )
217 {
218  if( m_cursorLevel != 0 || aDC == NULL || !m_showCrossHair )
219  return;
220 
221  wxPoint cursor = GetParent()->GetCrossHairPosition();
222 
223  GRSetDrawMode( aDC, GR_XOR );
224 
225  if( GetParent()->GetGalDisplayOptions().m_fullscreenCursor )
226  {
227  wxSize clientSize = GetClientSize();
228 
229  // Y axis
230  wxPoint lineStart( cursor.x, aDC->DeviceToLogicalY( 0 ) );
231  wxPoint lineEnd( cursor.x, aDC->DeviceToLogicalY( clientSize.y ) );
232 
233  GRLine( &m_ClipBox, aDC, lineStart, lineEnd, 0, aColor );
234 
235  // X axis
236  lineStart = wxPoint( aDC->DeviceToLogicalX( 0 ), cursor.y );
237  lineEnd = wxPoint( aDC->DeviceToLogicalX( clientSize.x ), cursor.y );
238 
239  GRLine( &m_ClipBox, aDC, lineStart, lineEnd, 0, aColor );
240  }
241  else
242  {
243  int len = aDC->DeviceToLogicalXRel( CURSOR_SIZE );
244 
245  GRLine( &m_ClipBox, aDC, cursor.x - len, cursor.y,
246  cursor.x + len, cursor.y, 0, aColor );
247  GRLine( &m_ClipBox, aDC, cursor.x, cursor.y - len,
248  cursor.x, cursor.y + len, 0, aColor );
249  }
250 }
251 
252 
254 {
255  DrawCrossHair( DC );
256  --m_cursorLevel;
257 }
258 
259 
261 {
262  ++m_cursorLevel;
263  DrawCrossHair( DC );
264 
265  if( m_cursorLevel > 0 ) // Shouldn't happen, but just in case ..
266  m_cursorLevel = 0;
267 }
268 
269 
271 {
272  return GetScreen()->GetZoom();
273 }
274 
275 
276 void EDA_DRAW_PANEL::SetZoom( double zoom )
277 {
278  GetScreen()->SetZoom( zoom );
279 }
280 
281 
283 {
284  return GetScreen()->GetGridSize();
285 }
286 
287 
289 {
290  wxPoint pos;
291  EDA_RECT display_rect;
292 
293  INSTALL_UNBUFFERED_DC( dc, this ); // Refresh the clip box to the entire screen size.
294  SetClipBox( dc );
295 
296  display_rect = m_ClipBox;
297 
298  // Slightly decreased the size of the useful screen area to avoid drawing limits.
299  #define PIXEL_MARGIN 8
300  display_rect.Inflate( -PIXEL_MARGIN );
301 
302  return display_rect.Contains( aPosition );
303 }
304 
305 
306 void EDA_DRAW_PANEL::RefreshDrawingRect( const EDA_RECT& aRect, bool aEraseBackground )
307 {
308  INSTALL_UNBUFFERED_DC( dc, this );
309 
310  wxRect rect = aRect;
311 
312  rect.x = dc.LogicalToDeviceX( rect.x );
313  rect.y = dc.LogicalToDeviceY( rect.y );
314  rect.width = dc.LogicalToDeviceXRel( rect.width );
315  rect.height = dc.LogicalToDeviceYRel( rect.height );
316 
317  wxLogTrace( KICAD_TRACE_COORDS,
318  wxT( "Refresh area: drawing (%d, %d, %d, %d), device (%d, %d, %d, %d)" ),
319  aRect.GetX(), aRect.GetY(), aRect.GetWidth(), aRect.GetHeight(),
320  rect.x, rect.y, rect.width, rect.height );
321 
322  RefreshRect( rect, aEraseBackground );
323 }
324 
325 
326 void EDA_DRAW_PANEL::Refresh( bool eraseBackground, const wxRect* rect )
327 {
328  if( GetParent()->IsGalCanvasActive() )
329  {
331  }
332  else
333  {
334  wxScrolledWindow::Refresh( eraseBackground, rect );
335  }
336 }
337 
338 
340 {
341  wxSize size = GetClientSize() / 2;
342  INSTALL_UNBUFFERED_DC( dc, this );
343 
344  return wxPoint( dc.DeviceToLogicalX( size.x ), dc.DeviceToLogicalY( size.y ) );
345 }
346 
347 
349 {
350  MoveCursor( GetParent()->GetCrossHairPosition() );
351 }
352 
353 
354 void EDA_DRAW_PANEL::MoveCursor( const wxPoint& aPosition )
355 {
356  if( GetParent()->IsGalCanvasActive() )
357  return;
358 
359  int x, y, xPpu, yPpu;
360  wxPoint screenPos, drawingPos;
361  wxRect clientRect( wxPoint( 0, 0 ), GetClientSize() );
362 
363  INSTALL_UNBUFFERED_DC( dc, this );
364  screenPos.x = dc.LogicalToDeviceX( aPosition.x );
365  screenPos.y = dc.LogicalToDeviceY( aPosition.y );
366 
367  // Scroll if the requested mouse position cursor is outside the drawing area.
368  if( !clientRect.Contains( screenPos ) )
369  {
370  GetViewStart( &x, &y );
371  GetScrollPixelsPerUnit( &xPpu, &yPpu );
372  CalcUnscrolledPosition( screenPos.x, screenPos.y, &drawingPos.x, &drawingPos.y );
373 
374  wxLogTrace( KICAD_TRACE_COORDS,
375  wxT( "MoveCursor() initial screen position(%d, %d) " ) \
376  wxT( "rectangle(%d, %d, %d, %d) view(%d, %d)" ),
377  screenPos.x, screenPos.y, clientRect.x, clientRect.y,
378  clientRect.width, clientRect.height, x, y );
379 
380  if( screenPos.y < clientRect.GetTop() )
381  y -= m_scrollIncrementY * yPpu;
382  else if( screenPos.y > clientRect.GetBottom() )
383  y += m_scrollIncrementY * yPpu;
384  else if( clientRect.GetRight() < screenPos.x )
385  x += m_scrollIncrementX * xPpu;
386  else
387  x -= m_scrollIncrementX * xPpu;
388 
389  Scroll( x, y );
390  CalcScrolledPosition( drawingPos.x, drawingPos.y, &screenPos.x, &screenPos.y );
391 
392  wxLogTrace( KICAD_TRACE_COORDS,
393  wxT( "MoveCursor() scrolled screen position(%d, %d) view(%d, %d)" ),
394  screenPos.x, screenPos.y, x, y );
395  }
396 
397  WarpPointer( screenPos.x, screenPos.y );
398 }
399 
400 
401 void EDA_DRAW_PANEL::OnActivate( wxActivateEvent& event )
402 {
403  m_canStartBlock = -1; // Block Command can't start
404  event.Skip();
405 }
406 
407 
408 void EDA_DRAW_PANEL::OnScroll( wxScrollWinEvent& event )
409 {
410  int id = event.GetEventType();
411  int x, y;
412  int ppux, ppuy;
413  int csizeX, csizeY;
414  int unitsX, unitsY;
415 
416  GetViewStart( &x, &y );
417  GetScrollPixelsPerUnit( &ppux, &ppuy );
418  GetClientSize( &csizeX, &csizeY );
419  GetVirtualSize( &unitsX, &unitsY );
420 
421  int tmpX = x;
422  int tmpY = y;
423 
424  csizeX /= ppux;
425  csizeY /= ppuy;
426 
427  unitsX /= ppux;
428  unitsY /= ppuy;
429 
430  int dir = event.GetOrientation(); // wxHORIZONTAL or wxVERTICAL
431 
432  // On windows and on wxWidgets >= 2.9.5 and < 3.1,
433  // there is a bug in mousewheel event which always generates 2 scroll events
434  // (should be the case only for the default mousewheel event)
435  // with id = wxEVT_SCROLLWIN_LINEUP or wxEVT_SCROLLWIN_LINEDOWN
436  // so we skip these events.
437  // Note they are here just in case, because they are not actually used
438  // in Kicad
439 #if wxCHECK_VERSION( 3, 1, 0 ) || !wxCHECK_VERSION( 2, 9, 5 ) || ( !defined (__WINDOWS__) && !defined (__WXMAC__) )
440  int maxX = unitsX - csizeX;
441  int maxY = unitsY - csizeY;
442 
443  if( id == wxEVT_SCROLLWIN_LINEUP )
444  {
445  if( dir == wxHORIZONTAL )
446  {
447  x -= m_scrollIncrementX;
448 
449  if( x < 0 )
450  x = 0;
451  }
452  else
453  {
454  y -= m_scrollIncrementY;
455 
456  if( y < 0 )
457  y = 0;
458  }
459  }
460  else if( id == wxEVT_SCROLLWIN_LINEDOWN )
461  {
462  if( dir == wxHORIZONTAL )
463  {
464  x += m_scrollIncrementX;
465  if( x > maxX )
466  x = maxX;
467  }
468  else
469  {
470  y += m_scrollIncrementY;
471 
472  if( y > maxY )
473  y = maxY;
474  }
475  }
476  else
477 #endif
478  if( id == wxEVT_SCROLLWIN_THUMBTRACK )
479  {
480  if( dir == wxHORIZONTAL )
481  x = event.GetPosition();
482  else
483  y = event.GetPosition();
484  }
485  else
486  {
487  event.Skip();
488  return;
489  }
490 
491  wxLogTrace( KICAD_TRACE_COORDS,
492  wxT( "Setting scroll bars ppuX=%d, ppuY=%d, unitsX=%d, unitsY=%d, posX=%d, posY=%d" ),
493  ppux, ppuy, unitsX, unitsY, x, y );
494 
495  double scale = GetParent()->GetScreen()->GetScalingFactor();
496 
498  center.x += KiROUND( (double) ( x - tmpX ) / scale );
499  center.y += KiROUND( (double) ( y - tmpY ) / scale );
500  GetParent()->SetScrollCenterPosition( center );
501 
502  Scroll( x, y );
503  event.Skip();
504 }
505 
506 
507 void EDA_DRAW_PANEL::SetClipBox( wxDC& aDC, const wxRect* aRect )
508 {
509  wxRect clipBox;
510 
511  // Use the entire visible device area if no clip area was defined.
512  if( aRect == NULL )
513  {
514  BASE_SCREEN* Screen = GetScreen();
515 
516  if( !Screen )
517  return;
518 
519  Screen->m_StartVisu = CalcUnscrolledPosition( wxPoint( 0, 0 ) );
520  clipBox.SetSize( GetClientSize() );
521 
522  int scrollX, scrollY;
523 
524  double scalar = Screen->GetScalingFactor();
525  scrollX = KiROUND( Screen->GetGridSize().x * scalar );
526  scrollY = KiROUND( Screen->GetGridSize().y * scalar );
527 
528  m_scrollIncrementX = std::max( GetClientSize().x / 8, scrollX );
529  m_scrollIncrementY = std::max( GetClientSize().y / 8, scrollY );
530  Screen->m_ScrollbarPos.x = GetScrollPos( wxHORIZONTAL );
531  Screen->m_ScrollbarPos.y = GetScrollPos( wxVERTICAL );
532  }
533  else
534  {
535  clipBox = *aRect;
536  }
537 
538  // Pad clip box in device units.
539  clipBox.Inflate( CLIP_BOX_PADDING );
540 
541  // Convert from device units to drawing units.
542  m_ClipBox.SetOrigin( wxPoint( aDC.DeviceToLogicalX( clipBox.x ),
543  aDC.DeviceToLogicalY( clipBox.y ) ) );
544  m_ClipBox.SetSize( wxSize( aDC.DeviceToLogicalXRel( clipBox.width ),
545  aDC.DeviceToLogicalYRel( clipBox.height ) ) );
546 
547  wxLogTrace( KICAD_TRACE_COORDS,
548  wxT( "Device clip box=(%d, %d, %d, %d), Logical clip box=(%d, %d, %d, %d)" ),
549  clipBox.x, clipBox.y, clipBox.width, clipBox.height,
551 }
552 
553 
555 {
556  GRSetDrawMode( DC, GR_COPY );
557 
558  COLOR4D bgColor = GetParent()->GetDrawBgColor();
559 
560  GRSFilledRect( NULL, DC, m_ClipBox.GetX(), m_ClipBox.GetY(),
562  0, bgColor, bgColor );
563 
564  // Set to one (1) to draw bounding box validate bounding box calculation.
565 #if DEBUG_SHOW_CLIP_RECT
566  EDA_RECT bBox = m_ClipBox;
567  GRRect( NULL, DC, bBox.GetOrigin().x, bBox.GetOrigin().y,
568  bBox.GetEnd().x, bBox.GetEnd().y, 0, LIGHTMAGENTA );
569 #endif
570 }
571 
572 
574 {
575  wxScrolledWindow::DoPrepareDC( dc );
576 
577  if( GetScreen() != NULL )
578  {
579  double scale = GetScreen()->GetScalingFactor();
580  dc.SetUserScale( scale, scale );
581 
582  wxPoint pt = GetScreen()->m_DrawOrg;
583  dc.SetLogicalOrigin( pt.x, pt.y );
584  }
585 
586  SetClipBox( dc ); // Reset the clip box to the entire screen.
587  GRResetPenAndBrush( &dc );
588  dc.SetBackgroundMode( wxTRANSPARENT );
589 }
590 
591 
592 void EDA_DRAW_PANEL::OnPaint( wxPaintEvent& event )
593 {
594  if( GetScreen() == NULL )
595  {
596  event.Skip();
597  return;
598  }
599 
600  INSTALL_PAINTDC( paintDC, this );
601 
602  wxRect region = GetUpdateRegion().GetBox();
603  SetClipBox( paintDC, &region );
604  ReDraw( &paintDC, true );
605 }
606 
607 
608 void EDA_DRAW_PANEL::ReDraw( wxDC* DC, bool erasebg )
609 {
610  BASE_SCREEN* Screen = GetScreen();
611 
612  if( Screen == NULL )
613  return;
614 
615  COLOR4D bgColor = GetParent()->GetDrawBgColor();
616 
617  // TODO(JE): Is this correct?
618  if( bgColor.GetBrightness() > 0.5 )
619  {
620  g_XorMode = GR_NXOR;
622  }
623  else
624  {
625  g_XorMode = GR_XOR;
627  }
628 
629  GRResetPenAndBrush( DC );
630 
631  DC->SetBackground( wxBrush( bgColor.ToColour() ) );
632  DC->SetBackgroundMode( wxSOLID );
633 
634  if( erasebg )
635  EraseScreen( DC );
636 
637  GetParent()->RedrawActiveWindow( DC, erasebg );
638 
639  // Verfies that the clipping is working correctly. If these two sets of numbers are
640  // not the same or really close. The clipping algorithms are broken.
641  wxLogTrace( KICAD_TRACE_COORDS,
642  wxT( "Clip box: (%d, %d, %d, %d), Draw extents (%d, %d, %d, %d)" ),
644  DC->MinX(), DC->MinY(), DC->MaxX(), DC->MaxY() );
645 }
646 
647 
649 {
650  m_enableMousewheelPan = aEnable;
651 
652  if( GetParent()->IsGalCanvasActive() )
654 }
655 
656 
658 {
659  m_enableZoomNoCenter = aEnable;
660 
661  if( GetParent()->IsGalCanvasActive() )
663 }
664 
665 
667 {
668  COLOR4D axis_color = COLOR4D( BLUE );
669 
670  GRSetDrawMode( DC, GR_COPY );
671 
672  if( GetParent()->IsGridVisible() )
673  DrawGrid( DC );
674 
675  // Draw axis
676  if( GetParent()->m_showAxis )
677  {
678  wxSize pageSize = GetParent()->GetPageSizeIU();
679 
680  // Draw the Y axis
681  GRLine( &m_ClipBox, DC, 0, -pageSize.y, 0, pageSize.y, 0, axis_color );
682 
683  // Draw the X axis
684  GRLine( &m_ClipBox, DC, -pageSize.x, 0, pageSize.x, 0, 0, axis_color );
685  }
686 
687  if( GetParent()->m_showOriginAxis )
688  DrawAuxiliaryAxis( DC, GR_COPY );
689 
690  if( GetParent()->m_showGridAxis )
691  DrawGridAxis( DC, GR_COPY, GetParent()->GetGridOrigin() );
692 }
693 
694 
695 void EDA_DRAW_PANEL::DrawGrid( wxDC* aDC )
696 {
697  #define MIN_GRID_SIZE 10 // min grid size in pixels to allow drawing
698  BASE_SCREEN* screen = GetScreen();
699  wxRealPoint gridSize;
700  wxSize screenSize;
701  wxPoint org;
702  wxRealPoint screenGridSize;
703 
704  /* The grid must be visible. this is possible only is grid value
705  * and zoom value are sufficient
706  */
707  gridSize = screen->GetGridSize();
708  screen->m_StartVisu = CalcUnscrolledPosition( wxPoint( 0, 0 ) );
709  screenSize = GetClientSize();
710 
711  screenGridSize.x = aDC->LogicalToDeviceXRel( KiROUND( gridSize.x ) );
712  screenGridSize.y = aDC->LogicalToDeviceYRel( KiROUND( gridSize.y ) );
713 
714  org = m_ClipBox.GetPosition();
715 
716  if( screenGridSize.x < MIN_GRID_SIZE || screenGridSize.y < MIN_GRID_SIZE )
717  {
718  screenGridSize.x *= 2.0;
719  screenGridSize.y *= 2.0;
720  gridSize.x *= 2.0;
721  gridSize.y *= 2.0;
722  }
723 
724  if( screenGridSize.x < MIN_GRID_SIZE || screenGridSize.y < MIN_GRID_SIZE )
725  return;
726 
727  org = GetParent()->GetNearestGridPosition( org, &gridSize );
728 
729  // Setting the nearest grid position can select grid points outside the clip box.
730  // Incrementing the start point by one grid step should prevent drawing grid points
731  // outside the clip box.
732  if( org.x < m_ClipBox.GetX() )
733  org.x += KiROUND( gridSize.x );
734 
735  if( org.y < m_ClipBox.GetY() )
736  org.y += KiROUND( gridSize.y );
737 
738  // Use a pixel based draw to display grid. There are a lot of calls, so the cost is
739  // high and grid is slowly drawn on some platforms. An other way using blit transfert was used,
740  // a long time ago, but it did not give very good results.
741  // The better way is highly dependent on the platform and the graphic card.
742  int xpos;
743  double right = ( double ) m_ClipBox.GetRight();
744  double bottom = ( double ) m_ClipBox.GetBottom();
745 
746 #if defined( __WXMAC__ ) && defined( USE_WX_GRAPHICS_CONTEXT )
747  wxGCDC *gcdc = wxDynamicCast( aDC, wxGCDC );
748  if( gcdc )
749  {
750  wxGraphicsContext *gc = gcdc->GetGraphicsContext();
751 
752  // Grid point size
753  const int gsz = 1;
754  const double w = aDC->DeviceToLogicalXRel( gsz );
755  const double h = aDC->DeviceToLogicalYRel( gsz );
756 
757  // Use our own pen
758  wxPen pen( GetParent()->GetGridColor().ToColour(), h );
759  pen.SetCap( wxCAP_BUTT );
760  gc->SetPen( pen );
761 
762  // draw grid
763  wxGraphicsPath path = gc->CreatePath();
764  for( double x = (double) org.x - w/2.0; x <= right - w/2.0; x += gridSize.x )
765  {
766  for( double y = (double) org.y; y <= bottom; y += gridSize.y )
767  {
768  path.MoveToPoint( x, y );
769  path.AddLineToPoint( x+w, y );
770  }
771  }
772  gc->StrokePath( path );
773  }
774  else
775 #endif
776  {
777  GRSetColorPen( aDC, GetParent()->GetGridColor() );
778 
779  for( double x = (double) org.x; x <= right; x += gridSize.x )
780  {
781  xpos = KiROUND( x );
782 
783  for( double y = (double) org.y; y <= bottom; y += gridSize.y )
784  {
785  aDC->DrawPoint( xpos, KiROUND( y ) );
786  }
787  }
788  }
789 }
790 
791 // Set to 1 to draw auxirilary axis as lines, 0 to draw as target (circle with cross)
792 #define DRAW_AXIS_AS_LINES 0
793 // Size in pixels of the target shape
794 #define AXIS_SIZE_IN_PIXELS 15
795 
796 void EDA_DRAW_PANEL::DrawAuxiliaryAxis( wxDC* aDC, GR_DRAWMODE aDrawMode )
797 {
798  wxPoint origin = GetParent()->GetAuxOrigin();
799 
800  if( origin == wxPoint( 0, 0 ) )
801  return;
802 
803  COLOR4D color = COLOR4D( RED );
804 
805  GRSetDrawMode( aDC, aDrawMode );
806 
807 #if DRAW_AXIS_AS_LINES
808  wxSize pageSize = GetParent()->GetPageSizeIU();
809  // Draw the Y axis
810  GRLine( &m_ClipBox, aDC, origin.x, -pageSize.y,
811  origin.x, pageSize.y, 0, color );
812 
813  // Draw the X axis
814  GRLine( &m_ClipBox, aDC, -pageSize.x, origin.y,
815  pageSize.x, origin.y, 0, color );
816 #else
817  int radius = aDC->DeviceToLogicalXRel( AXIS_SIZE_IN_PIXELS );
818  int linewidth = aDC->DeviceToLogicalXRel( 1 );
819 
820  GRSetColorPen( aDC, color, linewidth );
821 
822  GRLine( &m_ClipBox, aDC, origin.x, origin.y-radius,
823  origin.x, origin.y+radius, 0, color );
824 
825  // Draw the + shape
826  GRLine( &m_ClipBox, aDC, origin.x-radius, origin.y,
827  origin.x+radius, origin.y, 0, color );
828 
829  GRCircle( &m_ClipBox, aDC, origin, radius, linewidth, color );
830 #endif
831 }
832 
833 
834 void EDA_DRAW_PANEL::DrawGridAxis( wxDC* aDC, GR_DRAWMODE aDrawMode, const wxPoint& aGridOrigin )
835 {
836  if( !GetParent()->m_showGridAxis || ( !aGridOrigin.x && !aGridOrigin.y ) )
837  return;
838 
840 
841  GRSetDrawMode( aDC, aDrawMode );
842 
843 #if DRAW_AXIS_AS_LINES
844  wxSize pageSize = GetParent()->GetPageSizeIU();
845  // Draw the Y axis
846  GRLine( &m_ClipBox, aDC, aGridOrigin.x, -pageSize.y,
847  aGridOrigin.x, pageSize.y, 0, color );
848 
849  // Draw the X axis
850  GRLine( &m_ClipBox, aDC, -pageSize.x, aGridOrigin.y,
851  pageSize.x, aGridOrigin.y, 0, color );
852 #else
853  int radius = aDC->DeviceToLogicalXRel( AXIS_SIZE_IN_PIXELS );
854  int linewidth = aDC->DeviceToLogicalXRel( 1 );
855 
856  GRSetColorPen( aDC, GetParent()->GetGridColor(), linewidth );
857 
858  GRLine( &m_ClipBox, aDC, aGridOrigin.x-radius, aGridOrigin.y-radius,
859  aGridOrigin.x+radius, aGridOrigin.y+radius, 0, color );
860 
861  // Draw the X shape
862  GRLine( &m_ClipBox, aDC, aGridOrigin.x+radius, aGridOrigin.y-radius,
863  aGridOrigin.x-radius, aGridOrigin.y+radius, 0, color );
864 
865  GRCircle( &m_ClipBox, aDC, aGridOrigin, radius, linewidth, color );
866 #endif
867 }
868 
869 
870 bool EDA_DRAW_PANEL::OnRightClick( wxMouseEvent& event )
871 {
872  wxPoint pos;
873  wxMenu MasterMenu;
874 
875  INSTALL_UNBUFFERED_DC( dc, this );
876 
877  pos = event.GetLogicalPosition( dc );
878 
879  if( !GetParent()->OnRightClick( pos, &MasterMenu ) )
880  return false;
881 
882  GetParent()->AddMenuZoomAndGrid( &MasterMenu );
883 
884  pos = event.GetPosition();
885  m_ignoreMouseEvents = true;
886  PopupMenu( &MasterMenu, pos );
887  // here, we are waiting for popup menu closing.
888  // Among different ways, it can be closed by clicking on the left mouse button.
889  // The expected behavior is to move the mouse cursor to its initial
890  // location, where the right click was made.
891  // However there is a case where the move cursor does not work as expected:
892  // when the user left clicks on the caption frame: the entire window is moved.
893  // Calling wxSafeYield avoid this behavior because it allows the left click
894  // to be proceeded before moving the mouse
895  wxSafeYield();
896 
897  // Move the mouse cursor to its initial position:
899  m_ignoreMouseEvents = false;
900 
901  return true;
902 }
903 
904 
905 void EDA_DRAW_PANEL::OnMouseEntering( wxMouseEvent& aEvent )
906 {
907  // This is an ugly hack that fixes some cross hair display bugs when the mouse leaves the
908  // canvas area during middle button mouse panning.
909  if( m_cursorLevel != 0 )
910  m_cursorLevel = 0;
911 
912  aEvent.Skip();
913 }
914 
915 
916 void EDA_DRAW_PANEL::OnMouseLeaving( wxMouseEvent& event )
917 {
918  if( m_mouseCaptureCallback == NULL ) // No command in progress.
919  m_requestAutoPan = false;
920 
922  return;
923 
924  // Auto pan when mouse has left the client window
925  // Ensure the cross_hair position is updated,
926  // because it will be used to center the screen.
927  // We use a position inside the client window
928  wxRect area( wxPoint( 0, 0 ), GetClientSize() );
929  wxPoint cross_hair_pos = event.GetPosition();
930 
931  // Certain window managers (e.g. awesome wm) incorrectly trigger "on leave" event,
932  // therefore test if the cursor has really left the panel area
933  if( !area.Contains( cross_hair_pos ) )
934  {
935  INSTALL_UNBUFFERED_DC( dc, this );
936  cross_hair_pos.x = dc.DeviceToLogicalX( cross_hair_pos.x );
937  cross_hair_pos.y = dc.DeviceToLogicalY( cross_hair_pos.y );
938 
939  GetParent()->SetCrossHairPosition( cross_hair_pos );
940 
941  wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_POPUP_ZOOM_CENTER );
942  cmd.SetEventObject( this );
943  GetEventHandler()->ProcessEvent( cmd );
944  }
945 
946  event.Skip();
947 }
948 
949 
950 void EDA_DRAW_PANEL::OnMouseWheel( wxMouseEvent& event )
951 {
952  if( m_ignoreMouseEvents )
953  return;
954 
955  wxRect rect = wxRect( wxPoint( 0, 0 ), GetClientSize() );
956 
957  // Ignore scroll events if the cursor is outside the drawing area.
958  if( event.GetWheelRotation() == 0 || !GetParent()->IsEnabled()
959  || !rect.Contains( event.GetPosition() ) )
960  {
961  wxLogTrace( KICAD_TRACE_COORDS,
962  wxT( "OnMouseWheel() position(%d, %d) rectangle(%d, %d, %d, %d)" ),
963  event.GetPosition().x, event.GetPosition().y,
964  rect.x, rect.y, rect.width, rect.height );
965  event.Skip();
966  return;
967  }
968 
969  INSTALL_UNBUFFERED_DC( dc, this );
970  GetParent()->SetCrossHairPosition( event.GetLogicalPosition( dc ) );
971 
972  wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED );
973  cmd.SetEventObject( this );
974 
975  bool offCenterReq = event.ControlDown() && event.ShiftDown();
976  offCenterReq = offCenterReq || m_enableZoomNoCenter;
977 
978  int axis = event.GetWheelAxis();
979  int wheelRotation = event.GetWheelRotation();
980 
982  {
983  // MousewheelPAN + Ctrl = zooming
984  if( event.ControlDown() && !event.ShiftDown() )
985  {
986  if( wheelRotation > 0 )
987  cmd.SetId( ID_POPUP_ZOOM_IN );
988  else if( wheelRotation < 0)
989  cmd.SetId( ID_POPUP_ZOOM_OUT );
990  }
991  // MousewheelPAN + Shift = horizontal scrolling
992  else if( event.ShiftDown() && !event.ControlDown() )
993  {
994  if( wheelRotation > 0 )
995  cmd.SetId( ID_PAN_LEFT );
996  else if( wheelRotation < 0)
997  cmd.SetId( ID_PAN_RIGHT );
998  }
999  // Without modifiers MousewheelPAN - just pan
1000  else
1001  {
1002  wxPoint newStart = GetViewStart();
1003  if( axis == wxMOUSE_WHEEL_HORIZONTAL )
1004  newStart.x += wheelRotation;
1005  else
1006  newStart.y -= wheelRotation;
1007 
1009  GetParent()->SetScrollCenterPosition( center );
1010  Scroll( newStart );
1011  }
1012  }
1013  else if( wheelRotation > 0 )
1014  {
1015  if( event.ShiftDown() && !event.ControlDown() )
1016  cmd.SetId( ID_PAN_UP );
1017  else if( event.ControlDown() && !event.ShiftDown() )
1018  cmd.SetId( ID_PAN_LEFT );
1019  else if( offCenterReq )
1020  cmd.SetId( ID_OFFCENTER_ZOOM_IN );
1021  else
1022  cmd.SetId( ID_POPUP_ZOOM_IN );
1023  }
1024  else if( wheelRotation < 0 )
1025  {
1026  if( event.ShiftDown() && !event.ControlDown() )
1027  cmd.SetId( ID_PAN_DOWN );
1028  else if( event.ControlDown() && !event.ShiftDown() )
1029  cmd.SetId( ID_PAN_RIGHT );
1030  else if( offCenterReq )
1031  cmd.SetId( ID_OFFCENTER_ZOOM_OUT );
1032  else
1033  cmd.SetId( ID_POPUP_ZOOM_OUT );
1034  }
1035 
1036  if( cmd.GetId() )
1037  GetEventHandler()->ProcessEvent( cmd );
1038  event.Skip();
1039 }
1040 
1041 
1042 #if wxCHECK_VERSION( 3, 1, 0 ) || defined( USE_OSX_MAGNIFY_EVENT )
1043 void EDA_DRAW_PANEL::OnMagnify( wxMouseEvent& event )
1044 {
1045  // Scale the panel around our cursor position.
1046  bool warpCursor = false;
1047 
1048  wxPoint cursorPosition = GetParent()->GetCursorPosition( false );
1049  wxPoint centerPosition = GetParent()->GetScrollCenterPosition();
1050  wxPoint vector = centerPosition - cursorPosition;
1051 
1052  double magnification = ( event.GetMagnification() + 1.0f );
1053  double scaleFactor = GetZoom() / magnification;
1054 
1055  // Scale the vector between the cursor and center point
1056  vector.x /= magnification;
1057  vector.y /= magnification;
1058 
1059  SetZoom(scaleFactor);
1060 
1061  // Recenter the window along our scaled vector such that the
1062  // cursor becomes our scale axis, remaining fixed on screen
1063  GetParent()->RedrawScreen( cursorPosition + vector, warpCursor );
1064 
1065  event.Skip();
1066 }
1067 #endif
1068 
1069 
1070 void EDA_DRAW_PANEL::OnMouseEvent( wxMouseEvent& event )
1071 {
1072  int localbutt = 0;
1073  BASE_SCREEN* screen = GetScreen();
1074 
1075  if( !screen )
1076  return;
1077 
1078  /* Adjust value to filter mouse displacement before consider the drag
1079  * mouse is really a drag command, not just a movement while click
1080  */
1081 #define MIN_DRAG_COUNT_FOR_START_BLOCK_COMMAND 5
1082 
1083  if( event.Leaving() )
1084  m_canStartBlock = -1;
1085 
1086  if( !IsMouseCaptured() ) // No mouse capture in progress.
1087  m_requestAutoPan = false;
1088 
1089  if( GetParent()->IsActive() )
1090  SetFocus();
1091  else
1092  return;
1093 
1094  if( !event.IsButton() && !event.Moving() && !event.Dragging() )
1095  return;
1096 
1097  if( event.RightDown() )
1098  {
1099  OnRightClick( event );
1100  return;
1101  }
1102 
1103  if( m_ignoreMouseEvents )
1104  return;
1105 
1106  if( event.LeftDown() )
1107  localbutt = GR_M_LEFT_DOWN;
1108 
1109  if( event.ButtonDClick( 1 ) )
1110  localbutt = GR_M_LEFT_DOWN | GR_M_DCLICK;
1111 
1112  if( event.MiddleDown() )
1113  localbutt = GR_M_MIDDLE_DOWN;
1114 
1115  INSTALL_UNBUFFERED_DC( DC, this );
1116  DC.SetBackground( *wxBLACK_BRUSH );
1117 
1118  // Compute the cursor position in drawing (logical) units.
1119  GetParent()->SetMousePosition( event.GetLogicalPosition( DC ) );
1120 
1121  int kbstat = 0;
1122 
1123  if( event.ShiftDown() )
1124  kbstat |= GR_KB_SHIFT;
1125 
1126  if( event.ControlDown() )
1127  kbstat |= GR_KB_CTRL;
1128 
1129  if( event.AltDown() )
1130  kbstat |= GR_KB_ALT;
1131 
1132  // Calling Double Click and Click functions :
1133  if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) )
1134  {
1135  GetParent()->OnLeftDClick( &DC, GetParent()->RefPos( true ) );
1136 
1137  // inhibit a response to the mouse left button release,
1138  // because we have a double click, and we do not want a new
1139  // OnLeftClick command at end of this Double Click
1141  }
1142  else if( event.LeftUp() )
1143  {
1144  // A block command is in progress: a left up is the end of block
1145  // or this is the end of a double click, already seen
1146  // Note also m_ignoreNextLeftButtonRelease can be set by
1147  // the call to OnLeftClick(), so do not change it after calling OnLeftClick
1148  bool ignoreEvt = m_ignoreNextLeftButtonRelease;
1150 
1151  if( screen->m_BlockLocate.GetState() == STATE_NO_BLOCK && !ignoreEvt )
1152  GetParent()->OnLeftClick( &DC, GetParent()->RefPos( true ) );
1153 
1154  }
1155  else if( !event.LeftIsDown() )
1156  {
1157  /* be sure there is a response to a left button release command
1158  * even when a LeftUp event is not seen. This happens when a
1159  * double click opens a dialog box, and the release mouse button
1160  * is made when the dialog box is opened.
1161  */
1163  }
1164 
1165  if( event.ButtonDown( wxMOUSE_BTN_MIDDLE ) )
1166  {
1168  m_PanStartEventPosition = event.GetPosition();
1169 
1170  INSTALL_UNBUFFERED_DC( dc, this );
1171  CrossHairOff( &dc );
1172  SetCursor( wxCURSOR_SIZING );
1173  }
1174 
1175  if( event.ButtonUp( wxMOUSE_BTN_MIDDLE ) )
1176  {
1177  INSTALL_UNBUFFERED_DC( dc, this );
1178  CrossHairOn( &dc );
1179  SetCursor( (wxStockCursor) m_currentCursor );
1180  }
1181 
1182  if( event.MiddleIsDown() )
1183  {
1184  wxPoint currentPosition = event.GetPosition();
1185 
1186  double scale = GetParent()->GetScreen()->GetScalingFactor();
1187  int x = m_PanStartCenter.x +
1188  KiROUND( (double) ( m_PanStartEventPosition.x - currentPosition.x ) / scale );
1189  int y = m_PanStartCenter.y +
1190  KiROUND( (double) ( m_PanStartEventPosition.y - currentPosition.y ) / scale );
1191 
1192  GetParent()->RedrawScreen( wxPoint( x, y ), false );
1193  }
1194 
1195  // Calling the general function on mouse changes (and pseudo key commands)
1196  GetParent()->GeneralControl( &DC, event.GetLogicalPosition( DC ), 0 );
1197 
1198  /*******************************/
1199  /* Control of block commands : */
1200  /*******************************/
1201 
1202  // Command block can't start if mouse is dragging a new panel
1203  static EDA_DRAW_PANEL* lastPanel;
1204  if( lastPanel != this )
1205  {
1206  m_minDragEventCount = 0;
1207  m_canStartBlock = -1;
1208  }
1209 
1210  /* A new command block can start after a release buttons
1211  * and if the drag is enough
1212  * This is to avoid a false start block when a dialog box is dismissed,
1213  * or when changing panels in hierarchy navigation
1214  * or when clicking while and moving mouse
1215  */
1216  if( !event.LeftIsDown() && !event.MiddleIsDown() )
1217  {
1218  m_minDragEventCount = 0;
1219  m_canStartBlock = 0;
1220 
1221  /* Remember the last cursor position when a drag mouse starts
1222  * this is the last position ** before ** clicking a button
1223  * this is useful to start a block command from the point where the
1224  * mouse was clicked first
1225  * (a filter creates a delay for the real block command start, and
1226  * we must remember this point)
1227  */
1229  }
1230 
1231  if( m_enableBlockCommands && !(localbutt & GR_M_DCLICK) )
1232  {
1233  if( !screen->IsBlockActive() )
1234  {
1236  }
1237 
1238  if( event.LeftDown() )
1239  {
1240  if( screen->m_BlockLocate.GetState() == STATE_BLOCK_MOVE )
1241  {
1242  m_requestAutoPan = false;
1243  GetParent()->HandleBlockPlace( &DC );
1245  }
1246  }
1247  else if( ( m_canStartBlock >= 0 ) && event.LeftIsDown() && !IsMouseCaptured() )
1248  {
1249  // Mouse is dragging: if no block in progress, start a block command.
1250  if( screen->m_BlockLocate.GetState() == STATE_NO_BLOCK )
1251  {
1252  // Start a block command
1253  int cmd_type = kbstat;
1254 
1255  // A block command is started if the drag is enough. A small
1256  // drag is ignored (it is certainly a little mouse move when
1257  // clicking) not really a drag mouse
1260  else
1261  {
1262  auto cmd = (GetParent()->GetToolId() == ID_ZOOM_SELECTION) ? BLOCK_ZOOM : 0;
1263 
1264  if( !GetParent()->HandleBlockBegin( &DC, cmd_type, m_CursorStartPos, cmd ) )
1265  {
1266  // should not occur: error
1268  wxT( "EDA_DRAW_PANEL::OnMouseEvent() Block Error" ) );
1269  }
1270  else
1271  {
1272  m_requestAutoPan = true;
1273  SetCursor( wxCURSOR_SIZING );
1274  }
1275  }
1276  }
1277  }
1278 
1279  if( event.ButtonUp( wxMOUSE_BTN_LEFT ) )
1280  {
1281  /* Release the mouse button: end of block.
1282  * The command can finish (DELETE) or have a next command (MOVE,
1283  * COPY). However the block command is canceled if the block
1284  * size is small because a block command filtering is already
1285  * made, this case happens, but only when the on grid cursor has
1286  * not moved.
1287  */
1288  #define BLOCK_MINSIZE_LIMIT 1
1289  bool BlockIsSmall =
1291  && ( std::abs( screen->m_BlockLocate.GetHeight() ) < BLOCK_MINSIZE_LIMIT );
1292 
1293  if( (screen->m_BlockLocate.GetState() != STATE_NO_BLOCK) && BlockIsSmall )
1294  {
1296  {
1297  m_endMouseCaptureCallback( this, &DC );
1298  m_requestAutoPan = false;
1299  }
1300 
1301  SetCursor( (wxStockCursor) m_currentCursor );
1302  }
1303  else if( screen->m_BlockLocate.GetState() == STATE_BLOCK_END )
1304  {
1305  m_requestAutoPan = false;
1306  GetParent()->HandleBlockEnd( &DC );
1307  SetCursor( (wxStockCursor) m_currentCursor );
1308  if( screen->m_BlockLocate.GetState() == STATE_BLOCK_MOVE )
1309  {
1310  m_requestAutoPan = true;
1311  SetCursor( wxCURSOR_HAND );
1312  }
1313  }
1314  }
1315  }
1316 
1317  // End of block command on a double click
1318  // To avoid an unwanted block move command if the mouse is moved while double clicking
1319  if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) )
1320  {
1321  if( !screen->IsBlockActive() && IsMouseCaptured() )
1322  {
1323  m_endMouseCaptureCallback( this, &DC );
1324  }
1325  }
1326 
1327 #if 0
1328  wxString msg_debug;
1329  msg_debug.Printf( " block state %d, cmd %d",
1330  screen->m_BlockLocate.GetState(),
1331  screen->m_BlockLocate.GetCommand() );
1332  GetParent()->PrintMsg( msg_debug );
1333 #endif
1334 
1335  lastPanel = this;
1336 }
1337 
1338 
1339 
1340 void EDA_DRAW_PANEL::OnCharHook( wxKeyEvent& event )
1341 {
1342  event.Skip();
1343 }
1344 
1345 void EDA_DRAW_PANEL::OnKeyEvent( wxKeyEvent& event )
1346 {
1347  int localkey;
1348  wxPoint pos;
1349 
1350  localkey = event.GetKeyCode();
1351 
1352  switch( localkey )
1353  {
1354  default:
1355  break;
1356 
1357  case WXK_ESCAPE:
1358  m_abortRequest = true;
1359 
1360  if( IsMouseCaptured() )
1361  EndMouseCapture();
1362  else
1364  break;
1365  }
1366 
1367  /* Normalize keys code to easily handle keys from Ctrl+A to Ctrl+Z
1368  * They have an ascii code from 1 to 27 remapped
1369  * to GR_KB_CTRL + 'A' to GR_KB_CTRL + 'Z'
1370  */
1371  if( event.ControlDown() && localkey >= WXK_CONTROL_A && localkey <= WXK_CONTROL_Z )
1372  localkey += 'A' - 1;
1373 
1374  /* Disallow shift for keys that have two keycodes on them (e.g. number and
1375  * punctuation keys) leaving only the "letter keys" of A-Z.
1376  * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout)
1377  * and Ctrl-( and Ctrl-5 (FR layout).
1378  * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout
1379  */
1380  bool keyIsLetter = ( localkey >= 'A' && localkey <= 'Z' ) ||
1381  ( localkey >= 'a' && localkey <= 'z' );
1382 
1383  if( event.ShiftDown() && ( keyIsLetter || localkey > 256 ) )
1384  localkey |= GR_KB_SHIFT;
1385 
1386  if( event.ControlDown() )
1387  localkey |= GR_KB_CTRL;
1388 
1389  if( event.AltDown() )
1390  localkey |= GR_KB_ALT;
1391 
1392  INSTALL_UNBUFFERED_DC( DC, this );
1393 
1394  // Some key commands use the current mouse position: refresh it.
1395  pos = wxGetMousePosition() - GetScreenPosition();
1396 
1397  // Compute the cursor position in drawing units. Also known as logical units to wxDC.
1398  pos = wxPoint( DC.DeviceToLogicalX( pos.x ), DC.DeviceToLogicalY( pos.y ) );
1399 
1400  GetParent()->SetMousePosition( pos );
1401 
1402  if( !GetParent()->GeneralControl( &DC, pos, localkey ) )
1403  event.Skip();
1404 }
1405 
1406 
1407 void EDA_DRAW_PANEL::OnPan( wxCommandEvent& event )
1408 {
1409  int x, y;
1410  int ppux, ppuy;
1411  int unitsX, unitsY;
1412  int maxX, maxY;
1413  int tmpX, tmpY;
1414 
1415  GetViewStart( &x, &y );
1416  GetScrollPixelsPerUnit( &ppux, &ppuy );
1417  GetVirtualSize( &unitsX, &unitsY );
1418  tmpX = x;
1419  tmpY = y;
1420  maxX = unitsX;
1421  maxY = unitsY;
1422  unitsX /= ppux;
1423  unitsY /= ppuy;
1424 
1425  wxLogTrace( KICAD_TRACE_COORDS,
1426  wxT( "Scroll center position before pan: (%d, %d)" ), tmpX, tmpY );
1427 
1428  switch( event.GetId() )
1429  {
1430  case ID_PAN_UP:
1431  y -= m_scrollIncrementY;
1432  break;
1433 
1434  case ID_PAN_DOWN:
1435  y += m_scrollIncrementY;
1436  break;
1437 
1438  case ID_PAN_LEFT:
1439  x -= m_scrollIncrementX;
1440  break;
1441 
1442  case ID_PAN_RIGHT:
1443  x += m_scrollIncrementX;
1444  break;
1445 
1446  default:
1447  wxLogDebug( wxT( "Unknown ID %d in EDA_DRAW_PANEL::OnPan()." ), event.GetId() );
1448  }
1449 
1450  bool updateCenterScrollPos = true;
1451 
1452  if( x < 0 )
1453  {
1454  x = 0;
1455  updateCenterScrollPos = false;
1456  }
1457 
1458  if( y < 0 )
1459  {
1460  y = 0;
1461  updateCenterScrollPos = false;
1462  }
1463 
1464  if( x > maxX )
1465  {
1466  x = maxX;
1467  updateCenterScrollPos = false;
1468  }
1469 
1470  if( y > maxY )
1471  {
1472  y = maxY;
1473  updateCenterScrollPos = false;
1474  }
1475 
1476  // Don't update the scroll position beyond the scroll limits.
1477  if( updateCenterScrollPos )
1478  {
1479  double scale = GetParent()->GetScreen()->GetScalingFactor();
1480 
1482  center.x += KiROUND( (double) ( x - tmpX ) / scale );
1483  center.y += KiROUND( (double) ( y - tmpY ) / scale );
1484  GetParent()->SetScrollCenterPosition( center );
1485 
1486  wxLogTrace( KICAD_TRACE_COORDS,
1487  wxT( "Scroll center position after pan: (%d, %d)" ), center.x, center.y );
1488  }
1489 
1490  Scroll( x/ppux, y/ppuy );
1491 }
1492 
1493 
1494 void EDA_DRAW_PANEL::EndMouseCapture( int id, int cursor, const wxString& title,
1495  bool aCallEndFunc )
1496 {
1497  if( m_mouseCaptureCallback && m_endMouseCaptureCallback && aCallEndFunc )
1498  {
1499  INSTALL_UNBUFFERED_DC( dc, this );
1500  m_endMouseCaptureCallback( this, &dc );
1501  }
1502 
1503  m_mouseCaptureCallback = NULL;
1505  m_requestAutoPan = false;
1506 
1507  if( id != -1 && cursor != -1 )
1508  {
1509  wxASSERT( cursor > wxCURSOR_NONE && cursor < wxCURSOR_MAX );
1510  GetParent()->SetToolID( id, cursor, title );
1511  }
1512 }
1513 
1514 
1515 void EDA_DRAW_PANEL::CallMouseCapture( wxDC* aDC, const wxPoint& aPosition, bool aErase )
1516 {
1517  wxCHECK_RET( aDC != NULL, wxT( "Invalid device context." ) );
1518  wxCHECK_RET( m_mouseCaptureCallback != NULL, wxT( "Mouse capture callback not set." ) );
1519 
1520  m_mouseCaptureCallback( this, aDC, aPosition, aErase );
1521 }
1522 
1523 
1525 {
1526  wxCHECK_RET( aDC != NULL, wxT( "Invalid device context." ) );
1527 
1528  // CallEndMouseCapture is sometimes called with m_endMouseCaptureCallback == NULL
1529  // for instance after an ABORT in block paste.
1531  m_endMouseCaptureCallback( this, aDC );
1532 }
Definition: colors.h:57
GR_DRAWMODE g_XorMode
Definition: gr_basic.cpp:51
#define GR_M_DCLICK
Definition: gr_basic.h:81
void PrintMsg(const wxString &text)
Definition: basicframe.cpp:382
void GRResetPenAndBrush(wxDC *DC)
Definition: gr_basic.cpp:196
bool m_abortRequest
Flag used to abort long commands.
double GetZoom()
Definition: draw_panel.cpp:270
int m_canStartBlock
useful to avoid false start block in certain cases (like switch from a sheet to an other sheet >= 0 (...
#define AXIS_SIZE_IN_PIXELS
Definition: draw_panel.cpp:794
wxPoint GetScreenCenterLogicalPosition()
Function GetScreenCenterLogicalPosition.
Definition: draw_panel.cpp:339
BLOCK_SELECTOR m_BlockLocate
Block description for block commands.
#define GR_M_MIDDLE_DOWN
Definition: gr_basic.h:80
#define PIXEL_MARGIN
void GRSetDrawMode(wxDC *DC, GR_DRAWMODE draw_mode)
Definition: gr_basic.cpp:290
static int KiROUND(double v)
KiROUND rounds a floating point number to an int using "round halfway cases away from zero"...
Definition: common.h:107
KICAD_MANAGER_FRAME::Process_Config EVT_MENU_RANGE(ID_KICAD_SELECT_ICONS_OPTIONS, ID_KICAD_SELECT_ICON_OPTIONS_END, KICAD_MANAGER_FRAME::OnChangeIconsOptions) EVT_UPDATE_UI(ID_SELECT_PREFERED_PDF_BROWSER
virtual void HandleBlockPlace(wxDC *DC)
Function HandleBlockPlace( ) Called after HandleBlockEnd, when a block command needs to be executed a...
Definition: draw_frame.cpp:639
virtual void Refresh(bool eraseBackground=true, const wxRect *rect=NULL) override
Definition: draw_panel.cpp:326
wxPoint ToDeviceXY(const wxPoint &pos)
Function ToDeviceXY transforms logical to device coordinates.
Definition: draw_panel.cpp:196
double GetScalingFactor() const
Function GetScalingFactor returns the inverse of the current scale used to draw items on screen...
Definition: base_screen.cpp:92
void GRSFilledRect(EDA_RECT *aClipBox, wxDC *aDC, int x1, int y1, int x2, int y2, int aWidth, COLOR4D aColor, COLOR4D aBgColor)
Definition: gr_basic.cpp:1152
wxRealPoint GetGrid()
Definition: draw_panel.cpp:282
void SetEnableZoomNoCenter(bool aEnable)
Definition: draw_panel.cpp:657
bool m_enableZoomNoCenter
True to enable zooming around the crosshair instead of the center.
int m_scrollIncrementX
X axis scroll increment in pixels per unit.
bool Contains(const wxPoint &aPoint) const
Function Contains.
VIEW_CONTROLS class definition.
void GRLine(EDA_RECT *ClipBox, wxDC *DC, int x1, int y1, int x2, int y2, int width, COLOR4D Color)
Definition: gr_basic.cpp:352
bool m_requestAutoPan
true to request an auto pan. Valid only when m_enableAutoPan = true.
EDA_RECT m_ClipBox
The drawing area used to redraw the screen which is usually the visible area of the drawing in intern...
double GetZoom() const
Function GetZoom returns the current "zoom factor", which is a measure of "internal units per device ...
void SetScrollCenterPosition(const wxPoint &aPoint)
void DrawCrossHair(wxDC *aDC, COLOR4D aColor=COLOR4D::WHITE)
Function DrawCrossHair draws the user cross hair.
Definition: draw_panel.cpp:216
bool m_PrintIsMirrored
True when drawing in mirror mode.
void DrawGrid(wxDC *aDC)
Function DrawGrid draws a grid to aDC.
Definition: draw_panel.cpp:695
int GetHeight() const
END_MOUSE_CAPTURE_CALLBACK m_endMouseCaptureCallback
Abort mouse capture callback function.
virtual void EnableMousewheelPan(bool aEnable)
Function EnableMousewheelPan() Enables or disables mousewheel panning.
void EndMouseCapture(int aId=-1, int aCursorId=-1, const wxString &aTitle=wxEmptyString, bool aCallEndFunc=true)
Function EndMouseCapture ends mouse a capture.
void OnKeyEvent(wxKeyEvent &event)
wxPoint GetCursorPosition(bool aOnGrid, wxRealPoint *aGridSize=NULL) const
Function GetCursorPosition returns the current cursor position in logical (drawing) units...
void SetOrigin(const wxPoint &pos)
const wxPoint & GetScrollCenterPosition() const
#define INSTALL_PAINTDC(name, parent)
void RefreshDrawingRect(const EDA_RECT &aRect, bool aEraseBackground=true)
Function RefreshDrawingRect redraws the contents of aRect in drawing units.
Definition: draw_panel.cpp:306
wxPoint m_PanStartEventPosition
Initial position of mouse event when pan started.
void MoveCursor(const wxPoint &aPosition)
Function MoveCursor moves the mouse pointer to aPosition in logical (drawing) units.
Definition: draw_panel.cpp:354
void OnPan(wxCommandEvent &event)
Class EDA_DRAW_FRAME is the base class for create windows for drawing purpose.
Definition: draw_frame.h:53
void MoveCursorToCrossHair()
Function MoveCursorToCrossHair warps the cursor to the current cross hair position.
Definition: draw_panel.cpp:348
bool m_enableBlockCommands
True enables block commands.
#define abs(a)
Definition: auxiliary.h:84
#define GR_KB_ALT
Definition: common.h:65
void CallEndMouseCapture(wxDC *aDC)
Function CallEndMouseCapture calls the end mouse capture callback.
virtual const wxSize GetPageSizeIU() const =0
Function GetPageSizeIU works off of GetPageSettings() to return the size of the paper page in the int...
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
Definition: kicad.cpp:52
void OnMouseEvent(wxMouseEvent &event)
#define ENBL_ZOOM_NO_CENTER_KEY
Definition: draw_panel.cpp:53
void SetClipBox(const EDA_RECT &aRect)
This file contains miscellaneous commonly used macros and functions.
MOUSE_CAPTURE_CALLBACK m_mouseCaptureCallback
Mouse capture move callback function.
void DrawGridAxis(wxDC *aDC, GR_DRAWMODE aDrawMode, const wxPoint &aGridOrigin)
Function DrawGridAxis Draw on auxiliary axis, used in Pcbnew to show grid origin, when the grid origi...
Definition: draw_panel.cpp:834
wxPoint ToLogicalXY(const wxPoint &pos)
Function ToLogicalXY transforms device to logical coordinates.
Definition: draw_panel.cpp:206
virtual bool HandleBlockBegin(wxDC *aDC, EDA_KEY aKey, const wxPoint &aPosition, int aExplicitCommand=0)
Function HandleBlockBegin initializes the block command including the command type, initial position, and other variables.
Definition: draw_frame.cpp:804
void EnableCursorWarping(bool aEnable)
Function EnableCursorWarping() Enables or disables warping the cursor.
wxPoint m_StartVisu
Coordinates in drawing units of the current view position (upper left corner of device) ...
bool IsBlockActive() const
void OnPaint(wxPaintEvent &event)
Definition: draw_panel.cpp:592
bool IsPointOnDisplay(const wxPoint &aPosition)
Function IsPointOnDisplay.
Definition: draw_panel.cpp:288
void GRRect(EDA_RECT *aClipBox, wxDC *aDC, int x1, int y1, int x2, int y2, COLOR4D aColor)
Definition: gr_basic.cpp:1077
#define ENBL_AUTO_PAN_KEY
Definition: draw_panel.cpp:57
virtual BASE_SCREEN * GetScreen() const
Function GetScreen returns a pointer to a BASE_SCREEN or one of its derivatives.
Definition: draw_frame.h:309
a helper to handle the real device context used in KiCad
virtual COLOR4D GetDrawBgColor() const
Definition: draw_frame.h:286
void OnEraseBackground(wxEraseEvent &event)
void Refresh()
#define GR_KB_SHIFT
Definition: common.h:66
GR_DRAWMODE
Drawmode. Compositing mode plus a flag or two.
Definition: gr_basic.h:41
virtual void OnLeftClick(wxDC *DC, const wxPoint &MousePos)=0
void OnMouseLeaving(wxMouseEvent &event)
Definition: draw_panel.cpp:916
bool IsMouseCaptured() const
Class BASE_SCREEN handles how to draw a screen (a board, a schematic ...)
#define BLOCK_MINSIZE_LIMIT
double GetBrightness() const
Function GetBrightness Returns the brightness value of the color ranged from 0.0 to 1...
Definition: color4d.h:254
bool m_ignoreMouseEvents
Ignore mouse events when true.
void CallMouseCapture(wxDC *aDC, const wxPoint &aPosition, bool aErase)
Function CallMouseCapture calls the mouse capture callback.
void SetZoom(double mode)
Definition: draw_panel.cpp:276
#define ENBL_MOUSEWHEEL_PAN_KEY
Definition: draw_panel.cpp:54
wxConfigBase * KifaceSettings() const
Definition: kiface_i.h:101
void SetX(int val)
#define MIN_GRID_SIZE
Definition: colors.h:60
EDA_DRAW_FRAME * GetParent() const
Definition: draw_panel.cpp:175
int GetBottom() const
bool SetZoom(double iu_per_du)
Function SetZoom adjusts the current zoom factor.
virtual void SetToolID(int aId, int aCursor, const wxString &aToolMsg)
Function SetToolID sets the tool command ID to aId and sets the cursor to aCursor.
Definition: draw_frame.cpp:535
int GetRight() const
#define CLIP_BOX_PADDING
Definition: draw_panel.cpp:48
void OnMouseEntering(wxMouseEvent &aEvent)
Definition: draw_panel.cpp:905
#define MIN_DRAG_COUNT_FOR_START_BLOCK_COMMAND
void CrossHairOff(wxDC *DC)
Definition: draw_panel.cpp:253
bool m_ignoreNextLeftButtonRelease
Ignore the next mouse left button release when true.
const wxPoint & GetPosition() const
void Refresh(bool aEraseBackground=true, const wxRect *aRect=NULL) override
wxPoint m_ScrollbarPos
Current scroll bar position in scroll units.
void SetY(int val)
void OnMouseWheel(wxMouseEvent &event)
Function OnMouseWheel handles mouse wheel events.
Definition: draw_panel.cpp:950
virtual const wxPoint & GetAuxOrigin() const =0
Function GetAuxOrigin returns the origin of the axis used for plotting and various exports...
virtual bool HandleBlockEnd(wxDC *DC)
Function HandleBlockEnd( ) Handle the "end" of a block command, i.e.
Definition: draw_frame.cpp:644
wxPoint m_PanStartCenter
Initial scroll center position when pan started.
virtual void RedrawActiveWindow(wxDC *DC, bool EraseBg)=0
bool m_showCrossHair
Indicate if cross hair is to be shown.
void RedrawScreen(const wxPoint &aCenterPoint, bool aWarpPointer)
Function RedrawScreen redraws the entire screen area by updating the scroll bars and mouse pointer in...
Definition: zoom.cpp:46
#define GR_M_LEFT_DOWN
Definition: gr_basic.h:78
void SetSize(const wxSize &size)
void CrossHairOn(wxDC *DC)
Definition: draw_panel.cpp:260
wxPoint GetNearestGridPosition(const wxPoint &aPosition, wxRealPoint *aGridSize=NULL) const
Function GetNearestGridPosition returns the nearest aGridSize location to aPosition.
const int scale
void DisplayToolMsg(const wxString &msg)
Definition: draw_frame.cpp:497
see class PGM_BASE
BASE_SCREEN class implementation.
void SetMousePosition(const wxPoint &aPosition)
void ReDraw(wxDC *aDC, bool aEraseBackground=true)
Definition: draw_panel.cpp:608
void OnCharHook(wxKeyEvent &event)
BLOCK_STATE_T GetState() const
#define max(a, b)
Definition: auxiliary.h:86
#define INSTALL_UNBUFFERED_DC(name, parent)
bool m_enableMousewheelPan
True to enable mousewheel panning by default.
void OnActivate(wxActivateEvent &event)
Function OnActivate handles window activation events.
Definition: draw_panel.cpp:401
int m_currentCursor
Current mouse cursor shape id.
int GetToolId() const
Definition: draw_frame.h:406
int m_defaultCursor
The default mouse cursor shape id.
void GRCircle(EDA_RECT *ClipBox, wxDC *DC, int xc, int yc, int r, int width, COLOR4D Color)
Definition: gr_basic.cpp:791
const wxRealPoint & GetGridSize() const
Return the grid size of the currently selected grid.
void GRSetColorPen(wxDC *DC, COLOR4D Color, int width, wxPenStyle style)
Function GRSetColorPen sets a pen style, width, color, and alpha into the given device context...
Definition: gr_basic.cpp:209
Class EDA_RECT handles the component boundary box.
wxPoint m_DrawOrg
offsets for drawing the circuit on the screen
void SetEnableMousewheelPan(bool aEnable)
Definition: draw_panel.cpp:648
int GetX() const
BASE_SCREEN * GetScreen()
Definition: draw_panel.cpp:188
The common library.
int GetWidth() const
#define KICAD_TRACE_COORDS
Trace mask used to enable or disable the trace output of coordinates during drawing functions...
Definition: draw_panel.cpp:73
virtual COLOR4D GetGridColor() const
Function GetGridColor() , virtual.
Definition: draw_frame.h:435
bool OnRightClick(wxMouseEvent &event)
Function OnRightClick builds and displays a context menu on a right mouse button click.
Definition: draw_panel.cpp:870
static const int CURSOR_SIZE
Cursor size in pixels.
Definition: draw_panel.cpp:46
Definition: colors.h:49
int GetY() const
void DrawAuxiliaryAxis(wxDC *aDC, GR_DRAWMODE aDrawMode)
Function DrawAuxiliaryAxis Draw the Auxiliary Axis, used in Pcbnew which as origin coordinates for ge...
Definition: draw_panel.cpp:796
KIGFX::VIEW_CONTROLS * GetViewControls() const
Function GetViewControls() Returns a pointer to the VIEW_CONTROLS instance used in the panel...
int m_minDragEventCount
Count the drag events.
BLOCK_COMMAND_T GetCommand() const
wxPoint m_CursorStartPos
Used for testing the cursor movement.
Definition: id.h:246
virtual bool GeneralControl(wxDC *aDC, const wxPoint &aPosition, EDA_KEY aHotKey=0)
Function GeneralControl performs application specific control using aDC at aPosition in logical units...
Definition: draw_frame.h:523
EDA_DRAW_PANEL_GAL * GetGalCanvas() const
Function GetGalCanvas returns a pointer to GAL-based canvas of given EDA draw frame.
Definition: draw_frame.h:803
virtual void AddMenuZoomAndGrid(wxMenu *aMasterMenu)
Function AddMenuZoomAndGrid (virtual) Add standard zoom commands and submenu zoom and grid selection ...
Definition: zoom.cpp:239
void DrawBackGround(wxDC *DC)
Function DrawBackGround.
Definition: draw_panel.cpp:666
bool m_enableAutoPan
True to enable automatic panning.
Definition: colors.h:45
void * GetDisplayOptions()
Function GetDisplayOptions A way to pass info to draw functions.
Definition: draw_panel.cpp:182
void SetCrossHairPosition(const wxPoint &aPosition, bool aSnapToGrid=true)
Function SetCrossHairPosition sets the screen cross hair position to aPosition in logical (drawing) u...
void EraseScreen(wxDC *DC)
Definition: draw_panel.cpp:554
virtual void OnLeftDClick(wxDC *DC, const wxPoint &MousePos)
Definition: draw_frame.cpp:492
#define GR_KB_CTRL
Definition: common.h:64
wxPoint GetCrossHairPosition(bool aInvertY=false) const
Function GetCrossHairPosition return the current cross hair position in logical (drawing) coordinates...
int m_cursorLevel
Index for cursor redraw in XOR mode.
virtual void DoPrepareDC(wxDC &aDC) override
Function DoPrepareDC sets up the device context aDC for drawing.
Definition: draw_panel.cpp:573
virtual void * GetDisplayOptions()
Function GetDisplayOptions A way to pass info to draw functions.
Definition: draw_frame.h:818
void OnScroll(wxScrollWinEvent &event)
Definition: draw_panel.cpp:408
COLOR4D g_GhostColor
Draw color for moving objects.
Definition: common.cpp:57
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Function Inflate inflates the rectangle horizontally by dx and vertically by dy.
int m_scrollIncrementY
Y axis scroll increment in pixels per unit.
#define min(a, b)
Definition: auxiliary.h:85
Class COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:39