summaryrefslogtreecommitdiff
path: root/common/draw_panel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/draw_panel.cpp')
-rw-r--r--common/draw_panel.cpp1619
1 files changed, 1619 insertions, 0 deletions
diff --git a/common/draw_panel.cpp b/common/draw_panel.cpp
new file mode 100644
index 0000000..3bd1103
--- /dev/null
+++ b/common/draw_panel.cpp
@@ -0,0 +1,1619 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2009 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr
+ * Copyright (C) 2007-2011 Wayne Stambaugh <stambaughw@verizon.net>
+ * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * @file draw_panel.cpp
+ */
+
+#include <fctsys.h>
+#include <pgm_base.h>
+#include <kiface_i.h>
+#include <gr_basic.h>
+#include <common.h>
+#include <macros.h>
+#include <id.h>
+#include <class_drawpanel.h>
+#include <class_draw_panel_gal.h>
+#include <class_base_screen.h>
+#include <draw_frame.h>
+#include <view/view_controls.h>
+
+#include <kicad_device_context.h>
+
+static const int CURSOR_SIZE = 12; ///< Cursor size in pixels
+
+#define CLIP_BOX_PADDING 2
+
+// keys to store options in config:
+#define ENBL_ZOOM_NO_CENTER_KEY wxT( "ZoomNoCenter" )
+#define ENBL_MOUSEWHEEL_PAN_KEY wxT( "MousewheelPAN" )
+#define ENBL_MIDDLE_BUTT_PAN_KEY wxT( "MiddleButtonPAN" )
+#define MIDDLE_BUTT_PAN_LIMITED_KEY wxT( "MiddleBtnPANLimited" )
+#define ENBL_AUTO_PAN_KEY wxT( "AutoPAN" )
+
+
+// Definitions for enabling and disabling debugging features in drawpanel.cpp.
+// Please don't forget to turn these off before making any commits to Launchpad.
+#define DEBUG_SHOW_CLIP_RECT 0 // Set to 1 to draw clipping rectangle.
+
+
+/**
+ * Trace mask used to enable or disable the trace output of coordinates during drawing
+ * functions. The coordinate dumping can be turned on by setting the WXTRACE environment
+ * variable to "kicad_dump_coords". See the wxWidgets documentation on wxLogTrace for
+ * more information.
+ */
+#define KICAD_TRACE_COORDS wxT( "kicad_dump_coords" )
+
+
+// Events used by EDA_DRAW_PANEL
+BEGIN_EVENT_TABLE( EDA_DRAW_PANEL, wxScrolledWindow )
+ EVT_LEAVE_WINDOW( EDA_DRAW_PANEL::OnMouseLeaving )
+ EVT_ENTER_WINDOW( EDA_DRAW_PANEL::OnMouseEntering )
+ EVT_MOUSEWHEEL( EDA_DRAW_PANEL::OnMouseWheel )
+#ifdef USE_OSX_MAGNIFY_EVENT
+ EVT_MAGNIFY( EDA_DRAW_PANEL::OnMagnify )
+#endif
+ EVT_MOUSE_EVENTS( EDA_DRAW_PANEL::OnMouseEvent )
+ EVT_CHAR( EDA_DRAW_PANEL::OnKeyEvent )
+ EVT_CHAR_HOOK( EDA_DRAW_PANEL::OnCharHook )
+ EVT_PAINT( EDA_DRAW_PANEL::OnPaint )
+ EVT_ERASE_BACKGROUND( EDA_DRAW_PANEL::OnEraseBackground )
+ EVT_SCROLLWIN( EDA_DRAW_PANEL::OnScroll )
+ EVT_ACTIVATE( EDA_DRAW_PANEL::OnActivate )
+ EVT_MENU_RANGE( ID_PAN_UP, ID_PAN_RIGHT, EDA_DRAW_PANEL::OnPan )
+END_EVENT_TABLE()
+
+
+/***********************************************************************/
+/* EDA_DRAW_PANEL base functions (EDA_DRAW_PANEL is the main panel)*/
+/***********************************************************************/
+
+EDA_DRAW_PANEL::EDA_DRAW_PANEL( EDA_DRAW_FRAME* parent, int id,
+ const wxPoint& pos, const wxSize& size ) :
+ wxScrolledWindow( parent, id, pos, size, wxBORDER | wxHSCROLL | wxVSCROLL )
+{
+ wxASSERT( parent );
+
+ ShowScrollbars( wxSHOW_SB_ALWAYS, wxSHOW_SB_ALWAYS );
+ DisableKeyboardScrolling();
+
+ m_scrollIncrementX = std::min( size.x / 8, 10 );
+ m_scrollIncrementY = std::min( size.y / 8, 10 );
+
+ SetLayoutDirection( wxLayout_LeftToRight );
+
+ SetBackgroundColour( MakeColour( parent->GetDrawBgColor() ) );
+
+#if KICAD_USE_BUFFERED_DC || KICAD_USE_BUFFERED_PAINTDC
+ SetBackgroundStyle( wxBG_STYLE_CUSTOM );
+#endif
+
+ m_ClipBox.SetSize( size );
+ m_ClipBox.SetX( 0 );
+ m_ClipBox.SetY( 0 );
+ m_canStartBlock = -1; // Command block can start if >= 0
+ m_abortRequest = false;
+ m_enableMousewheelPan = false;
+ m_enableMiddleButtonPan = true;
+ m_enableZoomNoCenter = false;
+ m_panScrollbarLimits = false;
+ m_enableAutoPan = true;
+ m_ignoreMouseEvents = false;
+ // Be sure a mouse release button event will be ignored when creating the canvas
+ // if the mouse click was not made inside the canvas (can happen sometimes, when
+ // launching an editor from a double click made in an other frame)
+ m_ignoreNextLeftButtonRelease = true;
+
+ m_mouseCaptureCallback = NULL;
+ m_endMouseCaptureCallback = NULL;
+
+ wxConfigBase* cfg = Kiface().KifaceSettings();
+
+ if( cfg )
+ {
+ cfg->Read( ENBL_MOUSEWHEEL_PAN_KEY, &m_enableMousewheelPan, false );
+ cfg->Read( ENBL_MIDDLE_BUTT_PAN_KEY, &m_enableMiddleButtonPan, true );
+ cfg->Read( ENBL_ZOOM_NO_CENTER_KEY, &m_enableZoomNoCenter, false );
+ cfg->Read( MIDDLE_BUTT_PAN_LIMITED_KEY, &m_panScrollbarLimits, false );
+ cfg->Read( ENBL_AUTO_PAN_KEY, &m_enableAutoPan, true );
+ }
+
+ m_requestAutoPan = false;
+ m_enableBlockCommands = false;
+ m_minDragEventCount = 0;
+
+#ifdef __WXMAC__
+ m_defaultCursor = m_currentCursor = wxCURSOR_CROSS;
+ m_showCrossHair = false;
+#else
+ m_defaultCursor = m_currentCursor = wxCURSOR_ARROW;
+ m_showCrossHair = true;
+#endif
+
+ m_cursorLevel = 0;
+ m_PrintIsMirrored = false;
+}
+
+
+EDA_DRAW_PANEL::~EDA_DRAW_PANEL()
+{
+ wxConfigBase* cfg = Kiface().KifaceSettings();
+
+ if( cfg )
+ {
+ cfg->Write( ENBL_MOUSEWHEEL_PAN_KEY, m_enableMousewheelPan );
+ cfg->Write( ENBL_MIDDLE_BUTT_PAN_KEY, m_enableMiddleButtonPan );
+ cfg->Write( ENBL_ZOOM_NO_CENTER_KEY, m_enableZoomNoCenter );
+ cfg->Write( MIDDLE_BUTT_PAN_LIMITED_KEY, m_panScrollbarLimits );
+ cfg->Write( ENBL_AUTO_PAN_KEY, m_enableAutoPan );
+ }
+}
+
+
+EDA_DRAW_FRAME* EDA_DRAW_PANEL::GetParent() const
+{
+ wxWindow* mom = wxScrolledWindow::GetParent();
+ return (EDA_DRAW_FRAME*) mom;
+}
+
+
+void* EDA_DRAW_PANEL::GetDisplayOptions()
+{
+ return GetParent()->GetDisplayOptions();
+}
+
+
+BASE_SCREEN* EDA_DRAW_PANEL::GetScreen()
+{
+ EDA_DRAW_FRAME* parentFrame = GetParent();
+
+ return parentFrame->GetScreen();
+}
+
+
+wxPoint EDA_DRAW_PANEL::ToDeviceXY( const wxPoint& pos )
+{
+ wxPoint ret;
+ INSTALL_UNBUFFERED_DC( dc, this );
+ ret.x = dc.LogicalToDeviceX( pos.x );
+ ret.y = dc.LogicalToDeviceY( pos.y );
+ return ret;
+}
+
+
+wxPoint EDA_DRAW_PANEL::ToLogicalXY( const wxPoint& pos )
+{
+ wxPoint ret;
+ INSTALL_UNBUFFERED_DC( dc, this );
+ ret.x = dc.DeviceToLogicalX( pos.x );
+ ret.y = dc.DeviceToLogicalY( pos.y );
+ return ret;
+}
+
+
+void EDA_DRAW_PANEL::DrawCrossHair( wxDC* aDC, EDA_COLOR_T aColor )
+{
+ if( m_cursorLevel != 0 || aDC == NULL || !m_showCrossHair )
+ return;
+
+ wxPoint cursor = GetParent()->GetCrossHairPosition();
+
+ GRSetDrawMode( aDC, GR_XOR );
+
+ if( GetParent()->m_cursorShape != 0 ) // Draws full screen crosshair.
+ {
+ wxSize clientSize = GetClientSize();
+
+ // Y axis
+ wxPoint lineStart( cursor.x, aDC->DeviceToLogicalY( 0 ) );
+ wxPoint lineEnd( cursor.x, aDC->DeviceToLogicalY( clientSize.y ) );
+
+ GRLine( &m_ClipBox, aDC, lineStart, lineEnd, 0, aColor );
+
+ // X axis
+ lineStart = wxPoint( aDC->DeviceToLogicalX( 0 ), cursor.y );
+ lineEnd = wxPoint( aDC->DeviceToLogicalX( clientSize.x ), cursor.y );
+
+ GRLine( &m_ClipBox, aDC, lineStart, lineEnd, 0, aColor );
+ }
+ else
+ {
+ int len = aDC->DeviceToLogicalXRel( CURSOR_SIZE );
+
+ GRLine( &m_ClipBox, aDC, cursor.x - len, cursor.y,
+ cursor.x + len, cursor.y, 0, aColor );
+ GRLine( &m_ClipBox, aDC, cursor.x, cursor.y - len,
+ cursor.x, cursor.y + len, 0, aColor );
+ }
+}
+
+
+void EDA_DRAW_PANEL::CrossHairOff( wxDC* DC )
+{
+ DrawCrossHair( DC );
+ --m_cursorLevel;
+}
+
+
+void EDA_DRAW_PANEL::CrossHairOn( wxDC* DC )
+{
+ ++m_cursorLevel;
+ DrawCrossHair( DC );
+
+ if( m_cursorLevel > 0 ) // Shouldn't happen, but just in case ..
+ m_cursorLevel = 0;
+}
+
+
+double EDA_DRAW_PANEL::GetZoom()
+{
+ return GetScreen()->GetZoom();
+}
+
+
+void EDA_DRAW_PANEL::SetZoom( double zoom )
+{
+ GetScreen()->SetZoom( zoom );
+}
+
+
+wxRealPoint EDA_DRAW_PANEL::GetGrid()
+{
+ return GetScreen()->GetGridSize();
+}
+
+
+bool EDA_DRAW_PANEL::IsPointOnDisplay( const wxPoint& aPosition )
+{
+ wxPoint pos;
+ EDA_RECT display_rect;
+
+ INSTALL_UNBUFFERED_DC( dc, this ); // Refresh the clip box to the entire screen size.
+ SetClipBox( dc );
+
+ display_rect = m_ClipBox;
+
+ // Slightly decreased the size of the useful screen area to avoid drawing limits.
+ #define PIXEL_MARGIN 8
+ display_rect.Inflate( -PIXEL_MARGIN );
+
+ return display_rect.Contains( aPosition );
+}
+
+
+void EDA_DRAW_PANEL::RefreshDrawingRect( const EDA_RECT& aRect, bool aEraseBackground )
+{
+ INSTALL_UNBUFFERED_DC( dc, this );
+
+ wxRect rect = aRect;
+
+ rect.x = dc.LogicalToDeviceX( rect.x );
+ rect.y = dc.LogicalToDeviceY( rect.y );
+ rect.width = dc.LogicalToDeviceXRel( rect.width );
+ rect.height = dc.LogicalToDeviceYRel( rect.height );
+
+ wxLogTrace( KICAD_TRACE_COORDS,
+ wxT( "Refresh area: drawing (%d, %d, %d, %d), device (%d, %d, %d, %d)" ),
+ aRect.GetX(), aRect.GetY(), aRect.GetWidth(), aRect.GetHeight(),
+ rect.x, rect.y, rect.width, rect.height );
+
+ RefreshRect( rect, aEraseBackground );
+}
+
+
+void EDA_DRAW_PANEL::Refresh( bool eraseBackground, const wxRect* rect )
+{
+ if( GetParent()->IsGalCanvasActive() )
+ {
+ GetParent()->GetGalCanvas()->Refresh();
+ }
+ else
+ {
+ wxScrolledWindow::Refresh( eraseBackground, rect );
+ }
+}
+
+
+wxPoint EDA_DRAW_PANEL::GetScreenCenterLogicalPosition()
+{
+ wxSize size = GetClientSize() / 2;
+ INSTALL_UNBUFFERED_DC( dc, this );
+
+ return wxPoint( dc.DeviceToLogicalX( size.x ), dc.DeviceToLogicalY( size.y ) );
+}
+
+
+void EDA_DRAW_PANEL::MoveCursorToCrossHair()
+{
+ MoveCursor( GetParent()->GetCrossHairPosition() );
+}
+
+
+void EDA_DRAW_PANEL::MoveCursor( const wxPoint& aPosition )
+{
+ if( GetParent()->IsGalCanvasActive() )
+ return;
+
+ int x, y, xPpu, yPpu;
+ wxPoint screenPos, drawingPos;
+ wxRect clientRect( wxPoint( 0, 0 ), GetClientSize() );
+
+ INSTALL_UNBUFFERED_DC( dc, this );
+ screenPos.x = dc.LogicalToDeviceX( aPosition.x );
+ screenPos.y = dc.LogicalToDeviceY( aPosition.y );
+
+ // Scroll if the requested mouse position cursor is outside the drawing area.
+ if( !clientRect.Contains( screenPos ) )
+ {
+ GetViewStart( &x, &y );
+ GetScrollPixelsPerUnit( &xPpu, &yPpu );
+ CalcUnscrolledPosition( screenPos.x, screenPos.y, &drawingPos.x, &drawingPos.y );
+
+ wxLogTrace( KICAD_TRACE_COORDS,
+ wxT( "MoveCursor() initial screen position(%d, %d) " ) \
+ wxT( "rectangle(%d, %d, %d, %d) view(%d, %d)" ),
+ screenPos.x, screenPos.y, clientRect.x, clientRect.y,
+ clientRect.width, clientRect.height, x, y );
+
+ if( screenPos.y < clientRect.GetTop() )
+ y -= m_scrollIncrementY * yPpu;
+ else if( screenPos.y > clientRect.GetBottom() )
+ y += m_scrollIncrementY * yPpu;
+ else if( clientRect.GetRight() < screenPos.x )
+ x += m_scrollIncrementX * xPpu;
+ else
+ x -= m_scrollIncrementX * xPpu;
+
+ Scroll( x, y );
+ CalcScrolledPosition( drawingPos.x, drawingPos.y, &screenPos.x, &screenPos.y );
+
+ wxLogTrace( KICAD_TRACE_COORDS,
+ wxT( "MoveCursor() scrolled screen position(%d, %d) view(%d, %d)" ),
+ screenPos.x, screenPos.y, x, y );
+ }
+
+ WarpPointer( screenPos.x, screenPos.y );
+}
+
+
+void EDA_DRAW_PANEL::OnActivate( wxActivateEvent& event )
+{
+ m_canStartBlock = -1; // Block Command can't start
+ event.Skip();
+}
+
+
+void EDA_DRAW_PANEL::OnScroll( wxScrollWinEvent& event )
+{
+ int id = event.GetEventType();
+ int x, y;
+ int ppux, ppuy;
+ int csizeX, csizeY;
+ int unitsX, unitsY;
+
+ GetViewStart( &x, &y );
+ GetScrollPixelsPerUnit( &ppux, &ppuy );
+ GetClientSize( &csizeX, &csizeY );
+ GetVirtualSize( &unitsX, &unitsY );
+
+ int tmpX = x;
+ int tmpY = y;
+
+ csizeX /= ppux;
+ csizeY /= ppuy;
+
+ unitsX /= ppux;
+ unitsY /= ppuy;
+
+ int dir = event.GetOrientation(); // wxHORIZONTAL or wxVERTICAL
+
+ // On windows and on wxWidgets >= 2.9.5 and < 3.1,
+ // there is a bug in mousewheel event which always generates 2 scroll events
+ // (should be the case only for the default mousewheel event)
+ // with id = wxEVT_SCROLLWIN_LINEUP or wxEVT_SCROLLWIN_LINEDOWN
+ // so we skip these events.
+ // Note they are here just in case, because they are not actually used
+ // in Kicad
+#if wxCHECK_VERSION( 3, 1, 0 ) || !wxCHECK_VERSION( 2, 9, 5 ) || ( !defined (__WINDOWS__) && !defined (__WXMAC__) )
+ int maxX = unitsX - csizeX;
+ int maxY = unitsY - csizeY;
+
+ if( id == wxEVT_SCROLLWIN_LINEUP )
+ {
+ if( dir == wxHORIZONTAL )
+ {
+ x -= m_scrollIncrementX;
+
+ if( x < 0 )
+ x = 0;
+ }
+ else
+ {
+ y -= m_scrollIncrementY;
+
+ if( y < 0 )
+ y = 0;
+ }
+ }
+ else if( id == wxEVT_SCROLLWIN_LINEDOWN )
+ {
+ if( dir == wxHORIZONTAL )
+ {
+ x += m_scrollIncrementX;
+ if( x > maxX )
+ x = maxX;
+ }
+ else
+ {
+ y += m_scrollIncrementY;
+
+ if( y > maxY )
+ y = maxY;
+ }
+ }
+ else
+#endif
+ if( id == wxEVT_SCROLLWIN_THUMBTRACK )
+ {
+ if( dir == wxHORIZONTAL )
+ x = event.GetPosition();
+ else
+ y = event.GetPosition();
+ }
+ else
+ {
+ event.Skip();
+ return;
+ }
+
+ wxLogTrace( KICAD_TRACE_COORDS,
+ wxT( "Setting scroll bars ppuX=%d, ppuY=%d, unitsX=%d, unitsY=%d, posX=%d, posY=%d" ),
+ ppux, ppuy, unitsX, unitsY, x, y );
+
+ double scale = GetParent()->GetScreen()->GetScalingFactor();
+
+ wxPoint center = GetParent()->GetScrollCenterPosition();
+ center.x += KiROUND( (double) ( x - tmpX ) / scale );
+ center.y += KiROUND( (double) ( y - tmpY ) / scale );
+ GetParent()->SetScrollCenterPosition( center );
+
+ Scroll( x, y );
+ event.Skip();
+}
+
+
+void EDA_DRAW_PANEL::SetClipBox( wxDC& aDC, const wxRect* aRect )
+{
+ wxRect clipBox;
+
+ // Use the entire visible device area if no clip area was defined.
+ if( aRect == NULL )
+ {
+ BASE_SCREEN* Screen = GetScreen();
+
+ if( !Screen )
+ return;
+
+ Screen->m_StartVisu = CalcUnscrolledPosition( wxPoint( 0, 0 ) );
+ clipBox.SetSize( GetClientSize() );
+
+ int scrollX, scrollY;
+
+ double scalar = Screen->GetScalingFactor();
+ scrollX = KiROUND( Screen->GetGridSize().x * scalar );
+ scrollY = KiROUND( Screen->GetGridSize().y * scalar );
+
+ m_scrollIncrementX = std::max( GetClientSize().x / 8, scrollX );
+ m_scrollIncrementY = std::max( GetClientSize().y / 8, scrollY );
+ Screen->m_ScrollbarPos.x = GetScrollPos( wxHORIZONTAL );
+ Screen->m_ScrollbarPos.y = GetScrollPos( wxVERTICAL );
+ }
+ else
+ {
+ clipBox = *aRect;
+ }
+
+ // Pad clip box in device units.
+ clipBox.Inflate( CLIP_BOX_PADDING );
+
+ // Convert from device units to drawing units.
+ m_ClipBox.SetOrigin( wxPoint( aDC.DeviceToLogicalX( clipBox.x ),
+ aDC.DeviceToLogicalY( clipBox.y ) ) );
+ m_ClipBox.SetSize( wxSize( aDC.DeviceToLogicalXRel( clipBox.width ),
+ aDC.DeviceToLogicalYRel( clipBox.height ) ) );
+
+ wxLogTrace( KICAD_TRACE_COORDS,
+ wxT( "Device clip box=(%d, %d, %d, %d), Logical clip box=(%d, %d, %d, %d)" ),
+ clipBox.x, clipBox.y, clipBox.width, clipBox.height,
+ m_ClipBox.GetX(), m_ClipBox.GetY(), m_ClipBox.GetWidth(), m_ClipBox.GetHeight() );
+}
+
+
+void EDA_DRAW_PANEL::EraseScreen( wxDC* DC )
+{
+ GRSetDrawMode( DC, GR_COPY );
+
+ EDA_COLOR_T bgColor = GetParent()->GetDrawBgColor();
+
+ GRSFilledRect( NULL, DC, m_ClipBox.GetX(), m_ClipBox.GetY(),
+ m_ClipBox.GetRight(), m_ClipBox.GetBottom(),
+ 0, bgColor, bgColor );
+
+ // Set to one (1) to draw bounding box validate bounding box calculation.
+#if DEBUG_SHOW_CLIP_RECT
+ EDA_RECT bBox = m_ClipBox;
+ GRRect( NULL, DC, bBox.GetOrigin().x, bBox.GetOrigin().y,
+ bBox.GetEnd().x, bBox.GetEnd().y, 0, LIGHTMAGENTA );
+#endif
+}
+
+
+void EDA_DRAW_PANEL::DoPrepareDC( wxDC& dc )
+{
+ wxScrolledWindow::DoPrepareDC( dc );
+
+ if( GetScreen() != NULL )
+ {
+ double scale = GetScreen()->GetScalingFactor();
+ dc.SetUserScale( scale, scale );
+
+ wxPoint pt = GetScreen()->m_DrawOrg;
+ dc.SetLogicalOrigin( pt.x, pt.y );
+ }
+
+ SetClipBox( dc ); // Reset the clip box to the entire screen.
+ GRResetPenAndBrush( &dc );
+ dc.SetBackgroundMode( wxTRANSPARENT );
+}
+
+
+void EDA_DRAW_PANEL::OnPaint( wxPaintEvent& event )
+{
+ if( GetScreen() == NULL )
+ {
+ event.Skip();
+ return;
+ }
+
+ INSTALL_PAINTDC( paintDC, this );
+
+ wxRect region = GetUpdateRegion().GetBox();
+ SetClipBox( paintDC, &region );
+ ReDraw( &paintDC, true );
+}
+
+
+void EDA_DRAW_PANEL::ReDraw( wxDC* DC, bool erasebg )
+{
+ BASE_SCREEN* Screen = GetScreen();
+
+ if( Screen == NULL )
+ return;
+
+ EDA_COLOR_T bgColor = GetParent()->GetDrawBgColor();
+
+ if( ( bgColor != WHITE ) && ( bgColor != BLACK ) )
+ bgColor = BLACK;
+
+ if( bgColor == WHITE )
+ {
+ g_XorMode = GR_NXOR;
+ g_GhostColor = BLACK;
+ }
+ else
+ {
+ g_XorMode = GR_XOR;
+ g_GhostColor = WHITE;
+ }
+
+ GRResetPenAndBrush( DC );
+
+ DC->SetBackground( bgColor == BLACK ? *wxBLACK_BRUSH : *wxWHITE_BRUSH );
+ DC->SetBackgroundMode( wxSOLID );
+
+ if( erasebg )
+ EraseScreen( DC );
+
+ GetParent()->RedrawActiveWindow( DC, erasebg );
+
+ // Verfies that the clipping is working correctly. If these two sets of numbers are
+ // not the same or really close. The clipping algorithms are broken.
+ wxLogTrace( KICAD_TRACE_COORDS,
+ wxT( "Clip box: (%d, %d, %d, %d), Draw extents (%d, %d, %d, %d)" ),
+ m_ClipBox.GetX(), m_ClipBox.GetY(), m_ClipBox.GetRight(), m_ClipBox.GetBottom(),
+ DC->MinX(), DC->MinY(), DC->MaxX(), DC->MaxY() );
+}
+
+
+void EDA_DRAW_PANEL::SetEnableMousewheelPan( bool aEnable )
+{
+ m_enableMousewheelPan = aEnable;
+
+ if( GetParent()->IsGalCanvasActive() )
+ GetParent()->GetGalCanvas()->GetViewControls()->EnableMousewheelPan( aEnable );
+}
+
+
+void EDA_DRAW_PANEL::SetEnableZoomNoCenter( bool aEnable )
+{
+ m_enableZoomNoCenter = aEnable;
+
+ if( GetParent()->IsGalCanvasActive() )
+ GetParent()->GetGalCanvas()->GetViewControls()->EnableCursorWarping( !aEnable );
+}
+
+
+void EDA_DRAW_PANEL::DrawBackGround( wxDC* DC )
+{
+ EDA_COLOR_T axis_color = BLUE;
+
+ GRSetDrawMode( DC, GR_COPY );
+
+ if( GetParent()->IsGridVisible() )
+ DrawGrid( DC );
+
+ // Draw axis
+ if( GetParent()->m_showAxis )
+ {
+ wxSize pageSize = GetParent()->GetPageSizeIU();
+
+ // Draw the Y axis
+ GRLine( &m_ClipBox, DC, 0, -pageSize.y, 0, pageSize.y, 0, axis_color );
+
+ // Draw the X axis
+ GRLine( &m_ClipBox, DC, -pageSize.x, 0, pageSize.x, 0, 0, axis_color );
+ }
+
+ if( GetParent()->m_showOriginAxis )
+ DrawAuxiliaryAxis( DC, GR_COPY );
+
+ if( GetParent()->m_showGridAxis )
+ DrawGridAxis( DC, GR_COPY, GetParent()->GetGridOrigin() );
+}
+
+
+void EDA_DRAW_PANEL::DrawGrid( wxDC* aDC )
+{
+ #define MIN_GRID_SIZE 10 // min grid size in pixels to allow drawing
+ BASE_SCREEN* screen = GetScreen();
+ wxRealPoint gridSize;
+ wxSize screenSize;
+ wxPoint org;
+ wxRealPoint screenGridSize;
+
+ /* The grid must be visible. this is possible only is grid value
+ * and zoom value are sufficient
+ */
+ gridSize = screen->GetGridSize();
+ screen->m_StartVisu = CalcUnscrolledPosition( wxPoint( 0, 0 ) );
+ screenSize = GetClientSize();
+
+ screenGridSize.x = aDC->LogicalToDeviceXRel( KiROUND( gridSize.x ) );
+ screenGridSize.y = aDC->LogicalToDeviceYRel( KiROUND( gridSize.y ) );
+
+ org = m_ClipBox.GetPosition();
+
+ if( screenGridSize.x < MIN_GRID_SIZE || screenGridSize.y < MIN_GRID_SIZE )
+ {
+ screenGridSize.x *= 2.0;
+ screenGridSize.y *= 2.0;
+ gridSize.x *= 2.0;
+ gridSize.y *= 2.0;
+ }
+
+ if( screenGridSize.x < MIN_GRID_SIZE || screenGridSize.y < MIN_GRID_SIZE )
+ return;
+
+ org = GetParent()->GetNearestGridPosition( org, &gridSize );
+
+ // Setting the nearest grid position can select grid points outside the clip box.
+ // Incrementing the start point by one grid step should prevent drawing grid points
+ // outside the clip box.
+ if( org.x < m_ClipBox.GetX() )
+ org.x += KiROUND( gridSize.x );
+
+ if( org.y < m_ClipBox.GetY() )
+ org.y += KiROUND( gridSize.y );
+
+ // Use a pixel based draw to display grid. There are a lot of calls, so the cost is
+ // high and grid is slowly drawn on some platforms. Please note that this should
+ // always be enabled until the bitmap based solution below is fixed.
+#ifndef __WXMAC__
+ GRSetColorPen( aDC, GetParent()->GetGridColor() );
+#else
+ // On mac (Cocoa), a point isn't a pixel and being of size 1 don't survive to antialiasing
+ GRSetColorPen( aDC, GetParent()->GetGridColor(), aDC->DeviceToLogicalXRel( 2 ) );
+#endif
+
+ int xpos;
+ double right = ( double ) m_ClipBox.GetRight();
+ double bottom = ( double ) m_ClipBox.GetBottom();
+
+#if defined( __WXMAC__ ) && defined( USE_WX_GRAPHICS_CONTEXT )
+ wxGCDC *gcdc = wxDynamicCast( aDC, wxGCDC );
+ if( gcdc )
+ {
+ wxGraphicsContext *gc = gcdc->GetGraphicsContext();
+
+ // Grid point size
+ const int gsz = 1;
+ const double w = aDC->DeviceToLogicalXRel( gsz );
+ const double h = aDC->DeviceToLogicalYRel( gsz );
+
+ // Use our own pen
+ wxPen pen( MakeColour( GetParent()->GetGridColor() ), h );
+ pen.SetCap( wxCAP_BUTT );
+ gc->SetPen( pen );
+
+ // draw grid
+ wxGraphicsPath path = gc->CreatePath();
+ for( double x = (double) org.x - w/2.0; x <= right - w/2.0; x += gridSize.x )
+ {
+ for( double y = (double) org.y; y <= bottom; y += gridSize.y )
+ {
+ path.MoveToPoint( x, y );
+ path.AddLineToPoint( x+w, y );
+ }
+ }
+ gc->StrokePath( path );
+ }
+ else
+#endif
+ {
+ GRSetColorPen( aDC, GetParent()->GetGridColor() );
+
+ for( double x = (double) org.x; x <= right; x += gridSize.x )
+ {
+ xpos = KiROUND( x );
+
+ for( double y = (double) org.y; y <= bottom; y += gridSize.y )
+ {
+ aDC->DrawPoint( xpos, KiROUND( y ) );
+ }
+ }
+ }
+}
+
+// Set to 1 to draw auxirilary axis as lines, 0 to draw as target (circle with cross)
+#define DRAW_AXIS_AS_LINES 0
+// Size in pixels of the target shape
+#define AXIS_SIZE_IN_PIXELS 15
+
+void EDA_DRAW_PANEL::DrawAuxiliaryAxis( wxDC* aDC, GR_DRAWMODE aDrawMode )
+{
+ wxPoint origin = GetParent()->GetAuxOrigin();
+
+ if( origin == wxPoint( 0, 0 ) )
+ return;
+
+ EDA_COLOR_T color = RED;
+
+ GRSetDrawMode( aDC, aDrawMode );
+
+#if DRAW_AXIS_AS_LINES
+ wxSize pageSize = GetParent()->GetPageSizeIU();
+ // Draw the Y axis
+ GRLine( &m_ClipBox, aDC, origin.x, -pageSize.y,
+ origin.x, pageSize.y, 0, color );
+
+ // Draw the X axis
+ GRLine( &m_ClipBox, aDC, -pageSize.x, origin.y,
+ pageSize.x, origin.y, 0, color );
+#else
+ int radius = aDC->DeviceToLogicalXRel( AXIS_SIZE_IN_PIXELS );
+ int linewidth = aDC->DeviceToLogicalXRel( 1 );
+
+ GRSetColorPen( aDC, color, linewidth );
+
+ GRLine( &m_ClipBox, aDC, origin.x, origin.y-radius,
+ origin.x, origin.y+radius, 0, color );
+
+ // Draw the + shape
+ GRLine( &m_ClipBox, aDC, origin.x-radius, origin.y,
+ origin.x+radius, origin.y, 0, color );
+
+ GRCircle( &m_ClipBox, aDC, origin, radius, linewidth, color );
+#endif
+}
+
+
+void EDA_DRAW_PANEL::DrawGridAxis( wxDC* aDC, GR_DRAWMODE aDrawMode, const wxPoint& aGridOrigin )
+{
+ if( !GetParent()->m_showGridAxis || ( !aGridOrigin.x && !aGridOrigin.y ) )
+ return;
+
+ EDA_COLOR_T color = GetParent()->GetGridColor();
+
+ GRSetDrawMode( aDC, aDrawMode );
+
+#if DRAW_AXIS_AS_LINES
+ wxSize pageSize = GetParent()->GetPageSizeIU();
+ // Draw the Y axis
+ GRLine( &m_ClipBox, aDC, aGridOrigin.x, -pageSize.y,
+ aGridOrigin.x, pageSize.y, 0, color );
+
+ // Draw the X axis
+ GRLine( &m_ClipBox, aDC, -pageSize.x, aGridOrigin.y,
+ pageSize.x, aGridOrigin.y, 0, color );
+#else
+ int radius = aDC->DeviceToLogicalXRel( AXIS_SIZE_IN_PIXELS );
+ int linewidth = aDC->DeviceToLogicalXRel( 1 );
+
+ GRSetColorPen( aDC, GetParent()->GetGridColor(), linewidth );
+
+ GRLine( &m_ClipBox, aDC, aGridOrigin.x-radius, aGridOrigin.y-radius,
+ aGridOrigin.x+radius, aGridOrigin.y+radius, 0, color );
+
+ // Draw the X shape
+ GRLine( &m_ClipBox, aDC, aGridOrigin.x+radius, aGridOrigin.y-radius,
+ aGridOrigin.x-radius, aGridOrigin.y+radius, 0, color );
+
+ GRCircle( &m_ClipBox, aDC, aGridOrigin, radius, linewidth, color );
+#endif
+}
+
+
+bool EDA_DRAW_PANEL::OnRightClick( wxMouseEvent& event )
+{
+ wxPoint pos;
+ wxMenu MasterMenu;
+
+ INSTALL_UNBUFFERED_DC( dc, this );
+
+ pos = event.GetLogicalPosition( dc );
+
+ if( !GetParent()->OnRightClick( pos, &MasterMenu ) )
+ return false;
+
+ GetParent()->AddMenuZoomAndGrid( &MasterMenu );
+
+ pos = event.GetPosition();
+ m_ignoreMouseEvents = true;
+ PopupMenu( &MasterMenu, pos );
+ // here, we are waiting for popup menu closing.
+ // Among different ways, it can be closed by clicking on the left mouse button.
+ // The expected behavior is to move the mouse cursor to its initial
+ // location, where the right click was made.
+ // However there is a case where the move cursor does not work as expected:
+ // when the user left clicks on the caption frame: the entire window is moved.
+ // Calling wxSafeYield avoid this behavior because it allows the left click
+ // to be proceeded before moving the mouse
+ wxSafeYield();
+
+ // Move the mouse cursor to its initial position:
+ MoveCursorToCrossHair();
+ m_ignoreMouseEvents = false;
+
+ return true;
+}
+
+
+void EDA_DRAW_PANEL::OnMouseEntering( wxMouseEvent& aEvent )
+{
+ // This is an ugly hack that fixes some cross hair display bugs when the mouse leaves the
+ // canvas area during middle button mouse panning.
+ if( m_cursorLevel != 0 )
+ m_cursorLevel = 0;
+
+ aEvent.Skip();
+}
+
+
+void EDA_DRAW_PANEL::OnMouseLeaving( wxMouseEvent& event )
+{
+ if( m_mouseCaptureCallback == NULL ) // No command in progress.
+ m_requestAutoPan = false;
+
+ if( !m_enableAutoPan || !m_requestAutoPan || m_ignoreMouseEvents )
+ return;
+
+ // Auto pan when mouse has left the client window
+ // Ensure the cross_hair position is updated,
+ // because it will be used to center the screen.
+ // We use a position inside the client window
+ wxRect area( wxPoint( 0, 0 ), GetClientSize() );
+ wxPoint cross_hair_pos = event.GetPosition();
+
+ // Certain window managers (e.g. awesome wm) incorrectly trigger "on leave" event,
+ // therefore test if the cursor has really left the panel area
+ if( !area.Contains( cross_hair_pos ) )
+ {
+ INSTALL_UNBUFFERED_DC( dc, this );
+ cross_hair_pos.x = dc.DeviceToLogicalX( cross_hair_pos.x );
+ cross_hair_pos.y = dc.DeviceToLogicalY( cross_hair_pos.y );
+
+ GetParent()->SetCrossHairPosition( cross_hair_pos );
+
+ wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_POPUP_ZOOM_CENTER );
+ cmd.SetEventObject( this );
+ GetEventHandler()->ProcessEvent( cmd );
+ }
+
+ event.Skip();
+}
+
+
+void EDA_DRAW_PANEL::OnMouseWheel( wxMouseEvent& event )
+{
+ if( m_ignoreMouseEvents )
+ return;
+
+ wxRect rect = wxRect( wxPoint( 0, 0 ), GetClientSize() );
+
+ // Ignore scroll events if the cursor is outside the drawing area.
+ if( event.GetWheelRotation() == 0 || !GetParent()->IsEnabled()
+ || !rect.Contains( event.GetPosition() ) )
+ {
+ wxLogTrace( KICAD_TRACE_COORDS,
+ wxT( "OnMouseWheel() position(%d, %d) rectangle(%d, %d, %d, %d)" ),
+ event.GetPosition().x, event.GetPosition().y,
+ rect.x, rect.y, rect.width, rect.height );
+ event.Skip();
+ return;
+ }
+
+ INSTALL_UNBUFFERED_DC( dc, this );
+ GetParent()->SetCrossHairPosition( event.GetLogicalPosition( dc ) );
+
+ wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED );
+ cmd.SetEventObject( this );
+
+ bool offCenterReq = event.ControlDown() && event.ShiftDown();
+ offCenterReq = offCenterReq || m_enableZoomNoCenter;
+
+ int axis = event.GetWheelAxis();
+ int wheelRotation = event.GetWheelRotation();
+
+ if( m_enableMousewheelPan )
+ {
+ wxPoint newStart = GetViewStart();
+ if( axis == wxMOUSE_WHEEL_HORIZONTAL )
+ newStart.x += wheelRotation;
+ else
+ newStart.y -= wheelRotation;
+
+ wxPoint center = GetScreenCenterLogicalPosition();
+ GetParent()->SetScrollCenterPosition( center );
+ Scroll( newStart );
+ }
+ else if( wheelRotation > 0 )
+ {
+ if( event.ShiftDown() && !event.ControlDown() )
+ cmd.SetId( ID_PAN_UP );
+ else if( event.ControlDown() && !event.ShiftDown() )
+ cmd.SetId( ID_PAN_LEFT );
+ else if( offCenterReq )
+ cmd.SetId( ID_OFFCENTER_ZOOM_IN );
+ else
+ cmd.SetId( ID_POPUP_ZOOM_IN );
+ }
+ else if( wheelRotation < 0 )
+ {
+ if( event.ShiftDown() && !event.ControlDown() )
+ cmd.SetId( ID_PAN_DOWN );
+ else if( event.ControlDown() && !event.ShiftDown() )
+ cmd.SetId( ID_PAN_RIGHT );
+ else if( offCenterReq )
+ cmd.SetId( ID_OFFCENTER_ZOOM_OUT );
+ else
+ cmd.SetId( ID_POPUP_ZOOM_OUT );
+ }
+
+ if( cmd.GetId() )
+ GetEventHandler()->ProcessEvent( cmd );
+ event.Skip();
+}
+
+
+#ifdef USE_OSX_MAGNIFY_EVENT
+void EDA_DRAW_PANEL::OnMagnify( wxMouseEvent& event )
+{
+ // Scale the panel around our cursor position.
+ bool warpCursor = false;
+
+ wxPoint cursorPosition = GetParent()->GetCursorPosition( false );
+ wxPoint centerPosition = GetParent()->GetScrollCenterPosition();
+ wxPoint vector = centerPosition - cursorPosition;
+
+ double magnification = ( event.GetMagnification() + 1.0f );
+ double scaleFactor = GetZoom() / magnification;
+
+ // Scale the vector between the cursor and center point
+ vector.x /= magnification;
+ vector.y /= magnification;
+
+ SetZoom(scaleFactor);
+
+ // Recenter the window along our scaled vector such that the
+ // cursor becomes our scale axis, remaining fixed on screen
+ GetParent()->RedrawScreen( cursorPosition + vector, warpCursor );
+
+ event.Skip();
+}
+#endif
+
+
+void EDA_DRAW_PANEL::OnMouseEvent( wxMouseEvent& event )
+{
+ int localrealbutt = 0, localbutt = 0;
+ BASE_SCREEN* screen = GetScreen();
+
+ if( !screen )
+ return;
+
+ /* Adjust value to filter mouse displacement before consider the drag
+ * mouse is really a drag command, not just a movement while click
+ */
+#define MIN_DRAG_COUNT_FOR_START_BLOCK_COMMAND 5
+
+ if( event.Leaving() )
+ m_canStartBlock = -1;
+
+ if( !IsMouseCaptured() ) // No mouse capture in progress.
+ m_requestAutoPan = false;
+
+ if( GetParent()->IsActive() )
+ SetFocus();
+ else
+ return;
+
+ if( !event.IsButton() && !event.Moving() && !event.Dragging() )
+ return;
+
+ if( event.RightDown() )
+ {
+ OnRightClick( event );
+ return;
+ }
+
+ if( m_ignoreMouseEvents )
+ return;
+
+ if( event.LeftIsDown() )
+ localrealbutt |= GR_M_LEFT_DOWN;
+
+ if( event.MiddleIsDown() )
+ localrealbutt |= GR_M_MIDDLE_DOWN;
+
+ if( event.LeftDown() )
+ localbutt = GR_M_LEFT_DOWN;
+
+ if( event.ButtonDClick( 1 ) )
+ localbutt = GR_M_LEFT_DOWN | GR_M_DCLICK;
+
+ if( event.MiddleDown() )
+ localbutt = GR_M_MIDDLE_DOWN;
+
+ localrealbutt |= localbutt; // compensation default wxGTK
+
+ INSTALL_UNBUFFERED_DC( DC, this );
+ DC.SetBackground( *wxBLACK_BRUSH );
+
+ // Compute the cursor position in drawing (logical) units.
+ GetParent()->SetMousePosition( event.GetLogicalPosition( DC ) );
+
+ int kbstat = 0;
+
+ if( event.ShiftDown() )
+ kbstat |= GR_KB_SHIFT;
+
+ if( event.ControlDown() )
+ kbstat |= GR_KB_CTRL;
+
+ if( event.AltDown() )
+ kbstat |= GR_KB_ALT;
+
+ // Calling Double Click and Click functions :
+ if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) )
+ {
+ GetParent()->OnLeftDClick( &DC, GetParent()->RefPos( true ) );
+
+ // inhibit a response to the mouse left button release,
+ // because we have a double click, and we do not want a new
+ // OnLeftClick command at end of this Double Click
+ m_ignoreNextLeftButtonRelease = true;
+ }
+ else if( event.LeftUp() )
+ {
+ // A block command is in progress: a left up is the end of block
+ // or this is the end of a double click, already seen
+ // Note also m_ignoreNextLeftButtonRelease can be set by
+ // the call to OnLeftClick(), so do not change it after calling OnLeftClick
+ bool ignoreEvt = m_ignoreNextLeftButtonRelease;
+ m_ignoreNextLeftButtonRelease = false;
+
+ if( screen->m_BlockLocate.GetState() == STATE_NO_BLOCK && !ignoreEvt )
+ GetParent()->OnLeftClick( &DC, GetParent()->RefPos( true ) );
+
+ }
+ else if( !event.LeftIsDown() )
+ {
+ /* be sure there is a response to a left button release command
+ * even when a LeftUp event is not seen. This happens when a
+ * double click opens a dialog box, and the release mouse button
+ * is made when the dialog box is opened.
+ */
+ m_ignoreNextLeftButtonRelease = false;
+ }
+
+ if( event.ButtonDown( wxMOUSE_BTN_MIDDLE ) && m_enableMiddleButtonPan )
+ {
+ if( m_panScrollbarLimits )
+ {
+ int ppux, ppuy;
+ GetScrollPixelsPerUnit( &ppux, &ppuy );
+ GetViewStart( &m_PanStartCenter.x, &m_PanStartCenter.y );
+ m_PanStartCenter.x *= ppux;
+ m_PanStartCenter.y *= ppuy;
+ }
+ else
+ m_PanStartCenter = GetParent()->GetScrollCenterPosition();
+
+ m_PanStartEventPosition = event.GetPosition();
+
+ INSTALL_UNBUFFERED_DC( dc, this );
+ CrossHairOff( &dc );
+ }
+
+ if( event.ButtonUp( wxMOUSE_BTN_MIDDLE ) && m_enableMiddleButtonPan )
+ {
+ INSTALL_UNBUFFERED_DC( dc, this );
+ CrossHairOn( &dc );
+ }
+
+ if( event.MiddleIsDown() && m_enableMiddleButtonPan )
+ {
+ wxPoint currentPosition = event.GetPosition();
+
+ if( m_panScrollbarLimits )
+ {
+ int x, y;
+ int tmpX, tmpY;
+ int ppux, ppuy;
+ int maxX, maxY;
+ int vsizeX, vsizeY;
+ int csizeX, csizeY;
+
+ GetViewStart( &tmpX, &tmpY );
+ GetScrollPixelsPerUnit( &ppux, &ppuy );
+ GetVirtualSize( &vsizeX, &vsizeY );
+ GetClientSize( &csizeX, &csizeY );
+
+ maxX = vsizeX - csizeX;
+ maxY = vsizeY - csizeY;
+
+ x = m_PanStartCenter.x + m_PanStartEventPosition.x - currentPosition.x;
+ y = m_PanStartCenter.y + m_PanStartEventPosition.y - currentPosition.y;
+
+ bool shouldMoveCursor = false;
+
+ if( x < 0 )
+ {
+ currentPosition.x += x;
+ x = 0;
+ shouldMoveCursor = true;
+ }
+
+ if( y < 0 )
+ {
+ currentPosition.y += y;
+ y = 0;
+ shouldMoveCursor = true;
+ }
+
+ if( x > maxX )
+ {
+ currentPosition.x += ( x - maxX );
+ x = maxX;
+ shouldMoveCursor = true;
+ }
+
+ if( y > maxY )
+ {
+ currentPosition.y += ( y - maxY );
+ y = maxY;
+ shouldMoveCursor = true;
+ }
+
+ if( shouldMoveCursor )
+ WarpPointer( currentPosition.x, currentPosition.y );
+
+ Scroll( x/ppux, y/ppuy );
+
+ double scale = GetParent()->GetScreen()->GetScalingFactor();
+
+ wxPoint center = GetParent()->GetScrollCenterPosition();
+ center.x += KiROUND( (double) ( x - tmpX ) / scale ) / ppux;
+ center.y += KiROUND( (double) ( y - tmpY ) / scale ) / ppuy;
+ GetParent()->SetScrollCenterPosition( center );
+
+ Refresh();
+ Update();
+ }
+ else
+ {
+ double scale = GetParent()->GetScreen()->GetScalingFactor();
+ int x = m_PanStartCenter.x +
+ KiROUND( (double) ( m_PanStartEventPosition.x - currentPosition.x ) / scale );
+ int y = m_PanStartCenter.y +
+ KiROUND( (double) ( m_PanStartEventPosition.y - currentPosition.y ) / scale );
+
+ GetParent()->RedrawScreen( wxPoint( x, y ), false );
+ }
+ }
+
+ if( event.ButtonUp( wxMOUSE_BTN_MIDDLE ) && !m_enableMiddleButtonPan &&
+ (screen->m_BlockLocate.GetState() == STATE_NO_BLOCK) )
+ {
+ // The middle button has been released, with no block command:
+ // We use it for a zoom center at cursor position command
+ wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_POPUP_ZOOM_CENTER );
+ cmd.SetEventObject( this );
+ GetEventHandler()->ProcessEvent( cmd );
+ }
+
+ // Calling the general function on mouse changes (and pseudo key commands)
+ GetParent()->GeneralControl( &DC, event.GetLogicalPosition( DC ), 0 );
+
+ /*******************************/
+ /* Control of block commands : */
+ /*******************************/
+
+ // Command block can't start if mouse is dragging a new panel
+ static EDA_DRAW_PANEL* lastPanel;
+ if( lastPanel != this )
+ {
+ m_minDragEventCount = 0;
+ m_canStartBlock = -1;
+ }
+
+ /* A new command block can start after a release buttons
+ * and if the drag is enough
+ * This is to avoid a false start block when a dialog box is dismissed,
+ * or when changing panels in hierarchy navigation
+ * or when clicking while and moving mouse
+ */
+ if( !event.LeftIsDown() && !event.MiddleIsDown() )
+ {
+ m_minDragEventCount = 0;
+ m_canStartBlock = 0;
+
+ /* Remember the last cursor position when a drag mouse starts
+ * this is the last position ** before ** clicking a button
+ * this is useful to start a block command from the point where the
+ * mouse was clicked first
+ * (a filter creates a delay for the real block command start, and
+ * we must remember this point)
+ */
+ m_CursorStartPos = GetParent()->GetCrossHairPosition();
+ }
+
+ if( m_enableBlockCommands && !(localbutt & GR_M_DCLICK) )
+ {
+ if( !screen->IsBlockActive() )
+ {
+ screen->m_BlockLocate.SetOrigin( m_CursorStartPos );
+ }
+
+ if( event.LeftDown() || ( !m_enableMiddleButtonPan && event.MiddleDown() ) )
+ {
+ if( screen->m_BlockLocate.GetState() == STATE_BLOCK_MOVE )
+ {
+ m_requestAutoPan = false;
+ GetParent()->HandleBlockPlace( &DC );
+ m_ignoreNextLeftButtonRelease = true;
+ }
+ }
+ else if( ( m_canStartBlock >= 0 )
+ && ( event.LeftIsDown() || ( !m_enableMiddleButtonPan && event.MiddleIsDown() ) )
+ && !IsMouseCaptured() )
+ {
+ // Mouse is dragging: if no block in progress, start a block command.
+ if( screen->m_BlockLocate.GetState() == STATE_NO_BLOCK )
+ {
+ // Start a block command
+ int cmd_type = kbstat;
+
+ if( !m_enableMiddleButtonPan && event.MiddleIsDown() )
+ cmd_type |= MOUSE_MIDDLE;
+
+ // A block command is started if the drag is enough. A small
+ // drag is ignored (it is certainly a little mouse move when
+ // clicking) not really a drag mouse
+ if( m_minDragEventCount < MIN_DRAG_COUNT_FOR_START_BLOCK_COMMAND )
+ m_minDragEventCount++;
+ else
+ {
+ if( !GetParent()->HandleBlockBegin( &DC, cmd_type, m_CursorStartPos ) )
+ {
+ // should not occur: error
+ GetParent()->DisplayToolMsg(
+ wxT( "EDA_DRAW_PANEL::OnMouseEvent() Block Error" ) );
+ }
+ else
+ {
+ m_requestAutoPan = true;
+ SetCursor( wxCURSOR_SIZING );
+ }
+ }
+ }
+ }
+
+ if( event.ButtonUp( wxMOUSE_BTN_LEFT ) ||
+ ( !m_enableMiddleButtonPan && event.ButtonUp( wxMOUSE_BTN_MIDDLE ) ) )
+ {
+ /* Release the mouse button: end of block.
+ * The command can finish (DELETE) or have a next command (MOVE,
+ * COPY). However the block command is canceled if the block
+ * size is small because a block command filtering is already
+ * made, this case happens, but only when the on grid cursor has
+ * not moved.
+ */
+ #define BLOCK_MINSIZE_LIMIT 1
+ bool BlockIsSmall =
+ ( std::abs( screen->m_BlockLocate.GetWidth() ) < BLOCK_MINSIZE_LIMIT )
+ && ( std::abs( screen->m_BlockLocate.GetHeight() ) < BLOCK_MINSIZE_LIMIT );
+
+ if( (screen->m_BlockLocate.GetState() != STATE_NO_BLOCK) && BlockIsSmall )
+ {
+ if( m_endMouseCaptureCallback )
+ {
+ m_endMouseCaptureCallback( this, &DC );
+ m_requestAutoPan = false;
+ }
+
+ SetCursor( (wxStockCursor) m_currentCursor );
+ }
+ else if( screen->m_BlockLocate.GetState() == STATE_BLOCK_END )
+ {
+ m_requestAutoPan = false;
+ GetParent()->HandleBlockEnd( &DC );
+ SetCursor( (wxStockCursor) m_currentCursor );
+ if( screen->m_BlockLocate.GetState() == STATE_BLOCK_MOVE )
+ {
+ m_requestAutoPan = true;
+ SetCursor( wxCURSOR_HAND );
+ }
+ }
+ }
+ }
+
+ // End of block command on a double click
+ // To avoid an unwanted block move command if the mouse is moved while double clicking
+ if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) )
+ {
+ if( !screen->IsBlockActive() && IsMouseCaptured() )
+ {
+ m_endMouseCaptureCallback( this, &DC );
+ }
+ }
+
+#if 0
+ wxString msg_debug;
+ msg_debug.Printf( " block state %d, cmd %d",
+ screen->m_BlockLocate.GetState(),
+ screen->m_BlockLocate.GetCommand() );
+ GetParent()->PrintMsg( msg_debug );
+#endif
+
+ lastPanel = this;
+}
+
+
+
+void EDA_DRAW_PANEL::OnCharHook( wxKeyEvent& event )
+{
+ event.Skip();
+}
+
+void EDA_DRAW_PANEL::OnKeyEvent( wxKeyEvent& event )
+{
+ int localkey;
+ wxPoint pos;
+
+ localkey = event.GetKeyCode();
+
+ switch( localkey )
+ {
+ default:
+ break;
+
+ case WXK_ESCAPE:
+ m_abortRequest = true;
+
+ if( IsMouseCaptured() )
+ EndMouseCapture();
+ else
+ EndMouseCapture( ID_NO_TOOL_SELECTED, m_defaultCursor, wxEmptyString );
+ break;
+ }
+
+ /* Normalize keys code to easily handle keys from Ctrl+A to Ctrl+Z
+ * They have an ascii code from 1 to 27 remapped
+ * to GR_KB_CTRL + 'A' to GR_KB_CTRL + 'Z'
+ */
+ if( event.ControlDown() && localkey >= WXK_CONTROL_A && localkey <= WXK_CONTROL_Z )
+ localkey += 'A' - 1;
+
+ /* Disallow shift for keys that have two keycodes on them (e.g. number and
+ * punctuation keys) leaving only the "letter keys" of A-Z.
+ * Then, you can have, e.g. Ctrl-5 and Ctrl-% (GB layout)
+ * and Ctrl-( and Ctrl-5 (FR layout).
+ * Otherwise, you'd have to have to say Ctrl-Shift-5 on a FR layout
+ */
+ bool keyIsLetter = ( localkey >= 'A' && localkey <= 'Z' ) ||
+ ( localkey >= 'a' && localkey <= 'z' );
+
+ if( event.ShiftDown() && ( keyIsLetter || localkey > 256 ) )
+ localkey |= GR_KB_SHIFT;
+
+ if( event.ControlDown() )
+ localkey |= GR_KB_CTRL;
+
+ if( event.AltDown() )
+ localkey |= GR_KB_ALT;
+
+ INSTALL_UNBUFFERED_DC( DC, this );
+
+ // Some key commands use the current mouse position: refresh it.
+ pos = wxGetMousePosition() - GetScreenPosition();
+
+ // Compute the cursor position in drawing units. Also known as logical units to wxDC.
+ pos = wxPoint( DC.DeviceToLogicalX( pos.x ), DC.DeviceToLogicalY( pos.y ) );
+
+ GetParent()->SetMousePosition( pos );
+
+ if( !GetParent()->GeneralControl( &DC, pos, localkey ) )
+ event.Skip();
+}
+
+
+void EDA_DRAW_PANEL::OnPan( wxCommandEvent& event )
+{
+ int x, y;
+ int ppux, ppuy;
+ int unitsX, unitsY;
+ int maxX, maxY;
+ int tmpX, tmpY;
+
+ GetViewStart( &x, &y );
+ GetScrollPixelsPerUnit( &ppux, &ppuy );
+ GetVirtualSize( &unitsX, &unitsY );
+ tmpX = x;
+ tmpY = y;
+ maxX = unitsX;
+ maxY = unitsY;
+ unitsX /= ppux;
+ unitsY /= ppuy;
+
+ wxLogTrace( KICAD_TRACE_COORDS,
+ wxT( "Scroll center position before pan: (%d, %d)" ), tmpX, tmpY );
+
+ switch( event.GetId() )
+ {
+ case ID_PAN_UP:
+ y -= m_scrollIncrementY;
+ break;
+
+ case ID_PAN_DOWN:
+ y += m_scrollIncrementY;
+ break;
+
+ case ID_PAN_LEFT:
+ x -= m_scrollIncrementX;
+ break;
+
+ case ID_PAN_RIGHT:
+ x += m_scrollIncrementX;
+ break;
+
+ default:
+ wxLogDebug( wxT( "Unknown ID %d in EDA_DRAW_PANEL::OnPan()." ), event.GetId() );
+ }
+
+ bool updateCenterScrollPos = true;
+
+ if( x < 0 )
+ {
+ x = 0;
+ updateCenterScrollPos = false;
+ }
+
+ if( y < 0 )
+ {
+ y = 0;
+ updateCenterScrollPos = false;
+ }
+
+ if( x > maxX )
+ {
+ x = maxX;
+ updateCenterScrollPos = false;
+ }
+
+ if( y > maxY )
+ {
+ y = maxY;
+ updateCenterScrollPos = false;
+ }
+
+ // Don't update the scroll position beyond the scroll limits.
+ if( updateCenterScrollPos )
+ {
+ double scale = GetParent()->GetScreen()->GetScalingFactor();
+
+ wxPoint center = GetParent()->GetScrollCenterPosition();
+ center.x += KiROUND( (double) ( x - tmpX ) / scale );
+ center.y += KiROUND( (double) ( y - tmpY ) / scale );
+ GetParent()->SetScrollCenterPosition( center );
+
+ wxLogTrace( KICAD_TRACE_COORDS,
+ wxT( "Scroll center position after pan: (%d, %d)" ), center.x, center.y );
+ }
+
+ Scroll( x/ppux, y/ppuy );
+}
+
+
+void EDA_DRAW_PANEL::EndMouseCapture( int id, int cursor, const wxString& title,
+ bool aCallEndFunc )
+{
+ if( m_mouseCaptureCallback && m_endMouseCaptureCallback && aCallEndFunc )
+ {
+ INSTALL_UNBUFFERED_DC( dc, this );
+ m_endMouseCaptureCallback( this, &dc );
+ }
+
+ m_mouseCaptureCallback = NULL;
+ m_endMouseCaptureCallback = NULL;
+ m_requestAutoPan = false;
+
+ if( id != -1 && cursor != -1 )
+ {
+ wxASSERT( cursor > wxCURSOR_NONE && cursor < wxCURSOR_MAX );
+ GetParent()->SetToolID( id, cursor, title );
+ }
+}
+
+
+void EDA_DRAW_PANEL::CallMouseCapture( wxDC* aDC, const wxPoint& aPosition, bool aErase )
+{
+ wxCHECK_RET( aDC != NULL, wxT( "Invalid device context." ) );
+ wxCHECK_RET( m_mouseCaptureCallback != NULL, wxT( "Mouse capture callback not set." ) );
+
+ m_mouseCaptureCallback( this, aDC, aPosition, aErase );
+}
+
+
+void EDA_DRAW_PANEL::CallEndMouseCapture( wxDC* aDC )
+{
+ wxCHECK_RET( aDC != NULL, wxT( "Invalid device context." ) );
+
+ // CallEndMouseCapture is sometimes called with m_endMouseCaptureCallback == NULL
+ // for instance after an ABORT in block paste.
+ if( m_endMouseCaptureCallback )
+ m_endMouseCaptureCallback( this, aDC );
+}