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