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