draw_panel.cpp 43.2 KB
Newer Older
1 2 3
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
4
 * Copyright (C) 2009 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr
Wayne Stambaugh's avatar
Wayne Stambaugh committed
5
 * Copyright (C) 2007-2011 Wayne Stambaugh <stambaughw@verizon.net>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 * Copyright (C) 1992-2011 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
 */

26 27 28
/**
 * @file drawpanel.cpp
 */
29

30
#include <fctsys.h>
31 32
#include <pgm_base.h>
#include <kiface_i.h>
33 34 35 36 37
#include <gr_basic.h>
#include <common.h>
#include <macros.h>
#include <id.h>
#include <class_drawpanel.h>
38
#include <class_drawpanel_gal.h>
39
#include <class_base_screen.h>
40
#include <draw_frame.h>
41

42
#include <kicad_device_context.h>
plyatov's avatar
plyatov committed
43

44
static const int CURSOR_SIZE = 12; ///< Cursor size in pixels
plyatov's avatar
plyatov committed
45

46
#define CLIP_BOX_PADDING 2
charras's avatar
charras committed
47

48
// keys to store options in config:
49 50 51 52
#define ENBL_ZOOM_NO_CENTER_KEY         wxT( "ZoomNoCenter" )
#define ENBL_MIDDLE_BUTT_PAN_KEY        wxT( "MiddleButtonPAN" )
#define MIDDLE_BUTT_PAN_LIMITED_KEY     wxT( "MiddleBtnPANLimited" )
#define ENBL_AUTO_PAN_KEY               wxT( "AutoPAN" )
53

54 55 56

// Definitions for enabling and disabling debugging features in drawpanel.cpp.
// Please don't forget to turn these off before making any commits to Launchpad.
57
#define DEBUG_SHOW_CLIP_RECT       0  // Set to 1 to draw clipping rectangle.
58

59

60 61 62 63 64 65 66
/**
 * 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" )
charras's avatar
charras committed
67 68


69 70 71 72 73 74
// Events used by EDA_DRAW_PANEL
BEGIN_EVENT_TABLE( EDA_DRAW_PANEL, wxScrolledWindow )
    EVT_LEAVE_WINDOW( EDA_DRAW_PANEL::OnMouseLeaving )
    EVT_MOUSEWHEEL( EDA_DRAW_PANEL::OnMouseWheel )
    EVT_MOUSE_EVENTS( EDA_DRAW_PANEL::OnMouseEvent )
    EVT_CHAR( EDA_DRAW_PANEL::OnKeyEvent )
75
    EVT_CHAR_HOOK( EDA_DRAW_PANEL::OnCharHook )
76
    EVT_PAINT( EDA_DRAW_PANEL::OnPaint )
77
    EVT_ERASE_BACKGROUND( EDA_DRAW_PANEL::OnEraseBackground )
78 79 80
    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 )
plyatov's avatar
plyatov committed
81 82
END_EVENT_TABLE()

83

84
/***********************************************************************/
85
/* EDA_DRAW_PANEL base functions (EDA_DRAW_PANEL is the main panel)*/
86
/***********************************************************************/
plyatov's avatar
plyatov committed
87

88 89
EDA_DRAW_PANEL::EDA_DRAW_PANEL( EDA_DRAW_FRAME* parent, int id,
                                const wxPoint& pos, const wxSize& size ) :
90 91 92
#if wxCHECK_VERSION( 2, 9, 5 )
    wxScrolledWindow( parent, id, pos, size, wxBORDER | wxHSCROLL | wxVSCROLL )
#else
93
    wxScrolledWindow( parent, id, pos, size, wxBORDER | wxHSCROLL | wxVSCROLL | wxALWAYS_SHOW_SB )
94
#endif
plyatov's avatar
plyatov committed
95
{
96
    wxASSERT( parent );
97

98 99
#if wxCHECK_VERSION( 2, 9, 5 )
    ShowScrollbars( wxSHOW_SB_ALWAYS, wxSHOW_SB_ALWAYS );
100
    DisableKeyboardScrolling();
101
#endif
102

103 104
    m_scrollIncrementX = std::min( size.x / 8, 10 );
    m_scrollIncrementY = std::min( size.y / 8, 10 );
105

106
    SetBackgroundColour( MakeColour( g_DrawBgColor ) );
107 108

#if KICAD_USE_BUFFERED_DC || KICAD_USE_BUFFERED_PAINTDC
109
    SetBackgroundStyle( wxBG_STYLE_CUSTOM );
110
#endif
111

112 113 114
    m_ClipBox.SetSize( size );
    m_ClipBox.SetX( 0 );
    m_ClipBox.SetY( 0 );
115 116
    m_canStartBlock = -1;       // Command block can start if >= 0
    m_abortRequest = false;
117
    m_enableMiddleButtonPan = false;
118
    m_enableZoomNoCenter = false;
119
    m_panScrollbarLimits = false;
120 121
    m_enableAutoPan = true;
    m_ignoreMouseEvents = false;
122
    m_ignoreNextLeftButtonRelease = false;
123

124 125
    m_mouseCaptureCallback = NULL;
    m_endMouseCaptureCallback = NULL;
126

127 128 129
    wxConfigBase* cfg = Kiface().KifaceSettings();

    if( cfg )
130
    {
131 132 133 134
        cfg->Read( ENBL_MIDDLE_BUTT_PAN_KEY, &m_enableMiddleButtonPan, false );
        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 );
135
    }
136

137 138
    m_requestAutoPan = false;
    m_enableBlockCommands = false;
139
    m_minDragEventCount = 0;
140 141

#ifdef __WXMAC__
142
    m_defaultCursor = m_currentCursor = wxCURSOR_CROSS;
143 144
    m_showCrossHair = false;
#else
145
    m_defaultCursor = m_currentCursor = wxCURSOR_ARROW;
146 147
    m_showCrossHair = true;
#endif
148

149
    m_cursorLevel = 0;
150
    m_PrintIsMirrored = false;
plyatov's avatar
plyatov committed
151 152
}

dickelbeck's avatar
dickelbeck committed
153

154
EDA_DRAW_PANEL::~EDA_DRAW_PANEL()
155
{
156 157 158 159 160 161 162 163 164
    wxConfigBase* cfg = Kiface().KifaceSettings();

    if( cfg )
    {
        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 );
    }
165 166
}

167

168 169
EDA_DRAW_FRAME* EDA_DRAW_PANEL::GetParent()
{
170 171
    wxWindow* mom = wxScrolledWindow::GetParent();
    return (EDA_DRAW_FRAME*) mom;
172 173 174
}


175
BASE_SCREEN* EDA_DRAW_PANEL::GetScreen()
176
{
177
    EDA_DRAW_FRAME* parentFrame = GetParent();
178

179
    return parentFrame->GetScreen();
180 181
}

182 183
wxPoint EDA_DRAW_PANEL::ToDeviceXY( const wxPoint& pos )
{
184 185 186 187 188
    wxPoint ret;
    INSTALL_UNBUFFERED_DC( dc, this );
    ret.x = dc.LogicalToDeviceX( pos.x );
    ret.y = dc.LogicalToDeviceY( pos.y );
    return ret;
189 190 191 192
}

wxPoint EDA_DRAW_PANEL::ToLogicalXY( const wxPoint& pos )
{
193 194 195 196 197
    wxPoint ret;
    INSTALL_UNBUFFERED_DC( dc, this );
    ret.x = dc.DeviceToLogicalX( pos.x );
    ret.y = dc.DeviceToLogicalY( pos.y );
    return ret;
198
}
199

200
void EDA_DRAW_PANEL::DrawCrossHair( wxDC* aDC, EDA_COLOR_T aColor )
plyatov's avatar
plyatov committed
201
{
202
    if( m_cursorLevel != 0 || aDC == NULL || !m_showCrossHair )
203 204
        return;

205
    wxPoint cursor = GetParent()->GetCrossHairPosition();
206

207
    GRSetDrawMode( aDC, GR_XOR );
stambaughw's avatar
stambaughw committed
208

209
    if( GetParent()->m_cursorShape != 0 )    // Draws full screen crosshair.
210
    {
211 212 213 214 215 216 217 218 219 220 221 222 223
        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 );
224 225 226
    }
    else
    {
227
        int len = aDC->DeviceToLogicalXRel( CURSOR_SIZE );
228

229 230 231 232
        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 );
233
    }
plyatov's avatar
plyatov committed
234 235
}

dickelbeck's avatar
dickelbeck committed
236

237
void EDA_DRAW_PANEL::CrossHairOff( wxDC* DC )
plyatov's avatar
plyatov committed
238
{
239
    DrawCrossHair( DC );
240
    --m_cursorLevel;
plyatov's avatar
plyatov committed
241 242
}

dickelbeck's avatar
dickelbeck committed
243

244
void EDA_DRAW_PANEL::CrossHairOn( wxDC* DC )
plyatov's avatar
plyatov committed
245
{
246
    ++m_cursorLevel;
247
    DrawCrossHair( DC );
plyatov's avatar
plyatov committed
248

249 250
    if( m_cursorLevel > 0 )  // Shouldn't happen, but just in case ..
        m_cursorLevel = 0;
plyatov's avatar
plyatov committed
251 252 253
}


254
double EDA_DRAW_PANEL::GetZoom()
plyatov's avatar
plyatov committed
255
{
256
    return GetScreen()->GetZoom();
plyatov's avatar
plyatov committed
257 258
}

dickelbeck's avatar
dickelbeck committed
259

260
void EDA_DRAW_PANEL::SetZoom( double zoom )
plyatov's avatar
plyatov committed
261
{
262
    GetScreen()->SetZoom( zoom );
plyatov's avatar
plyatov committed
263 264
}

dickelbeck's avatar
dickelbeck committed
265

266
wxRealPoint EDA_DRAW_PANEL::GetGrid()
plyatov's avatar
plyatov committed
267
{
268
    return GetScreen()->GetGridSize();
plyatov's avatar
plyatov committed
269 270 271
}


272
bool EDA_DRAW_PANEL::IsPointOnDisplay( const wxPoint& aPosition )
plyatov's avatar
plyatov committed
273
{
274
    wxPoint  pos;
275
    EDA_RECT display_rect;
276

277
    INSTALL_UNBUFFERED_DC( dc, this );  // Refresh the clip box to the entire screen size.
278
    SetClipBox( dc );
279

280 281
    display_rect = m_ClipBox;

282
    // Slightly decreased the size of the useful screen area to avoid drawing limits.
283
    #define PIXEL_MARGIN 8
284
    display_rect.Inflate( -PIXEL_MARGIN );
285

286
    return display_rect.Contains( aPosition );
plyatov's avatar
plyatov committed
287 288 289
}


290
void EDA_DRAW_PANEL::RefreshDrawingRect( const EDA_RECT& aRect, bool aEraseBackground )
291
{
292
    INSTALL_UNBUFFERED_DC( dc, this );
293

294
    wxRect rect = aRect;
295

296 297 298 299
    rect.x = dc.LogicalToDeviceX( rect.x );
    rect.y = dc.LogicalToDeviceY( rect.y );
    rect.width = dc.LogicalToDeviceXRel( rect.width );
    rect.height = dc.LogicalToDeviceYRel( rect.height );
300

301 302 303 304
    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 );
dickelbeck's avatar
dickelbeck committed
305

306
    RefreshRect( rect, aEraseBackground );
307 308 309
}


310 311 312 313 314 315 316 317 318 319 320 321 322
void EDA_DRAW_PANEL::Refresh( bool eraseBackground, const wxRect* rect )
{
    if( GetParent()->IsGalCanvasActive() )
    {
        GetParent()->GetGalCanvas()->Refresh();
    }
    else
    {
        wxScrolledWindow::Refresh( eraseBackground, rect );
    }
}


323
wxPoint EDA_DRAW_PANEL::GetScreenCenterLogicalPosition()
plyatov's avatar
plyatov committed
324
{
325 326
    wxSize size = GetClientSize() / 2;
    INSTALL_UNBUFFERED_DC( dc, this );
327

328
    return wxPoint( dc.DeviceToLogicalX( size.x ), dc.DeviceToLogicalY( size.y ) );
plyatov's avatar
plyatov committed
329 330 331
}


332
void EDA_DRAW_PANEL::MoveCursorToCrossHair()
plyatov's avatar
plyatov committed
333
{
334
    MoveCursor( GetParent()->GetCrossHairPosition() );
plyatov's avatar
plyatov committed
335 336
}

dickelbeck's avatar
dickelbeck committed
337

338
void EDA_DRAW_PANEL::MoveCursor( const wxPoint& aPosition )
plyatov's avatar
plyatov committed
339
{
340 341 342 343
    int     x, y, xPpu, yPpu;
    wxPoint screenPos, drawingPos;
    wxRect  clientRect( wxPoint( 0, 0 ), GetClientSize() );

344 345 346
    INSTALL_UNBUFFERED_DC( dc, this );
    screenPos.x = dc.LogicalToDeviceX( aPosition.x );
    screenPos.y = dc.LogicalToDeviceY( aPosition.y );
347

348
    // Scroll if the requested mouse position cursor is outside the drawing area.
349 350 351 352
    if( !clientRect.Contains( screenPos ) )
    {
        GetViewStart( &x, &y );
        GetScrollPixelsPerUnit( &xPpu, &yPpu );
353
        CalcUnscrolledPosition( screenPos.x, screenPos.y, &drawingPos.x, &drawingPos.y );
354

355 356
        wxLogTrace( KICAD_TRACE_COORDS,
                    wxT( "MoveCursor() initial screen position(%d, %d) " ) \
357
                    wxT( "rectangle(%d, %d, %d, %d) view(%d, %d)" ),
358 359 360 361
                    screenPos.x, screenPos.y, clientRect.x, clientRect.y,
                    clientRect.width, clientRect.height, x, y );

        if( screenPos.y < clientRect.GetTop() )
362
            y -= m_scrollIncrementY * yPpu;
363
        else if( screenPos.y > clientRect.GetBottom() )
364
            y += m_scrollIncrementY * yPpu;
365
        else if( clientRect.GetRight() < screenPos.x )
366
            x += m_scrollIncrementX * xPpu;
367
        else
368
            x -= m_scrollIncrementX * xPpu;
369 370

        Scroll( x, y );
371
        CalcScrolledPosition( drawingPos.x, drawingPos.y, &screenPos.x, &screenPos.y );
372

373 374
        wxLogTrace( KICAD_TRACE_COORDS,
                    wxT( "MoveCursor() scrolled screen position(%d, %d) view(%d, %d)" ),
375
                    screenPos.x, screenPos.y, x, y );
376 377 378
    }

    WarpPointer( screenPos.x, screenPos.y );
plyatov's avatar
plyatov committed
379 380
}

381

382
void EDA_DRAW_PANEL::OnActivate( wxActivateEvent& event )
plyatov's avatar
plyatov committed
383
{
384
    m_canStartBlock = -1;   // Block Command can't start
385
    event.Skip();
plyatov's avatar
plyatov committed
386 387
}

388

389
void EDA_DRAW_PANEL::OnScroll( wxScrollWinEvent& event )
plyatov's avatar
plyatov committed
390
{
391
    int id = event.GetEventType();
392
    int dir;
393
    int x, y;
394
    int ppux, ppuy;
395
    int csizeX, csizeY;
396 397
    int unitsX, unitsY;
    int maxX, maxY;
398 399

    GetViewStart( &x, &y );
400
    GetScrollPixelsPerUnit( &ppux, &ppuy );
401
    GetClientSize( &csizeX, &csizeY );
402
    GetVirtualSize( &unitsX, &unitsY );
403

404 405 406
    int tmpX = x;
    int tmpY = y;

407 408
    csizeX /= ppux;
    csizeY /= ppuy;
409 410 411 412

    unitsX /= ppux;
    unitsY /= ppuy;

413 414 415
    maxX = unitsX - csizeX;
    maxY = unitsY - csizeY;

416
    dir = event.GetOrientation();   // wxHORIZONTAL or wxVERTICAL
417 418

    if( id == wxEVT_SCROLLWIN_LINEUP )
419 420 421 422
    {
        if( dir == wxHORIZONTAL )
        {
            x -= m_scrollIncrementX;
423

424 425 426 427 428 429
            if( x < 0 )
                x = 0;
        }
        else
        {
            y -= m_scrollIncrementY;
430

431 432 433 434
            if( y < 0 )
                y = 0;
        }
    }
435
    else if( id == wxEVT_SCROLLWIN_LINEDOWN )
436 437 438 439 440 441 442 443 444 445
    {
        if( dir == wxHORIZONTAL )
        {
            x += m_scrollIncrementX;
            if( x > maxX )
                x = maxX;
        }
        else
        {
            y += m_scrollIncrementY;
446

447 448 449 450
            if( y > maxY )
                y = maxY;
        }
    }
451 452 453
    else if( id == wxEVT_SCROLLWIN_THUMBTRACK )
    {
        if( dir == wxHORIZONTAL )
454
            x = event.GetPosition();
455
        else
456
            y = event.GetPosition();
457 458 459 460 461 462 463
    }
    else
    {
        event.Skip();
        return;
    }

464
    wxLogTrace( KICAD_TRACE_COORDS,
465 466
                wxT( "Setting scroll bars ppuX=%d, ppuY=%d, unitsX=%d, unitsY=%d, posX=%d, posY=%d" ),
                ppux, ppuy, unitsX, unitsY, x, y );
stambaughw's avatar
stambaughw committed
467

468 469
    double scale = GetParent()->GetScreen()->GetScalingFactor();

470
    wxPoint center = GetParent()->GetScrollCenterPosition();
471 472
    center.x += KiROUND( (double) ( x - tmpX ) / scale );
    center.y += KiROUND( (double) ( y - tmpY ) / scale );
473
    GetParent()->SetScrollCenterPosition( center );
474

475
    Scroll( x, y );
476
    event.Skip();
plyatov's avatar
plyatov committed
477 478
}

479

480
void EDA_DRAW_PANEL::SetClipBox( wxDC& aDC, const wxRect* aRect )
plyatov's avatar
plyatov committed
481
{
482 483 484 485 486 487
    wxRect clipBox;

    // Use the entire visible device area if no clip area was defined.
    if( aRect == NULL )
    {
        BASE_SCREEN* Screen = GetScreen();
488

489 490
        if( !Screen )
            return;
491

492 493
        Screen->m_StartVisu = CalcUnscrolledPosition( wxPoint( 0, 0 ) );
        clipBox.SetSize( GetClientSize() );
plyatov's avatar
plyatov committed
494

495
        int scrollX, scrollY;
plyatov's avatar
plyatov committed
496

497
        double scalar = Screen->GetScalingFactor();
498 499
        scrollX = KiROUND( Screen->GetGridSize().x * scalar );
        scrollY = KiROUND( Screen->GetGridSize().y * scalar );
500

501 502
        m_scrollIncrementX = std::max( GetClientSize().x / 8, scrollX );
        m_scrollIncrementY = std::max( GetClientSize().y / 8, scrollY );
503 504 505 506 507 508 509
        Screen->m_ScrollbarPos.x = GetScrollPos( wxHORIZONTAL );
        Screen->m_ScrollbarPos.y = GetScrollPos( wxVERTICAL );
    }
    else
    {
        clipBox = *aRect;
    }
plyatov's avatar
plyatov committed
510

511 512
    // Pad clip box in device units.
    clipBox.Inflate( CLIP_BOX_PADDING );
513

514
    // Convert from device units to drawing units.
515 516 517 518
    m_ClipBox.SetOrigin( wxPoint( aDC.DeviceToLogicalX( clipBox.x ),
                                  aDC.DeviceToLogicalY( clipBox.y ) ) );
    m_ClipBox.SetSize( wxSize( aDC.DeviceToLogicalXRel( clipBox.width ),
                               aDC.DeviceToLogicalYRel( clipBox.height ) ) );
519

520 521
    wxLogTrace( KICAD_TRACE_COORDS,
                wxT( "Device clip box=(%d, %d, %d, %d), Logical clip box=(%d, %d, %d, %d)" ),
522
                clipBox.x, clipBox.y, clipBox.width, clipBox.height,
523
                m_ClipBox.GetX(), m_ClipBox.GetY(), m_ClipBox.GetWidth(), m_ClipBox.GetHeight() );
plyatov's avatar
plyatov committed
524 525 526
}


527
void EDA_DRAW_PANEL::EraseScreen( wxDC* DC )
plyatov's avatar
plyatov committed
528
{
529
    GRSetDrawMode( DC, GR_COPY );
dickelbeck's avatar
dickelbeck committed
530

531
    GRSFilledRect( NULL, DC, m_ClipBox.GetX(), m_ClipBox.GetY(),
532
                   m_ClipBox.GetRight(), m_ClipBox.GetBottom(),
533
                   0, g_DrawBgColor, g_DrawBgColor );
534

535
    // Set to one (1) to draw bounding box validate bounding box calculation.
536
#if DEBUG_SHOW_CLIP_RECT
537
    EDA_RECT bBox = m_ClipBox;
538 539 540
    GRRect( NULL, DC, bBox.GetOrigin().x, bBox.GetOrigin().y,
            bBox.GetEnd().x, bBox.GetEnd().y, 0, LIGHTMAGENTA );
#endif
plyatov's avatar
plyatov committed
541 542
}

543

544
void EDA_DRAW_PANEL::DoPrepareDC( wxDC& dc )
545
{
546 547
    wxScrolledWindow::DoPrepareDC( dc );

548 549 550
    if( GetScreen() != NULL )
    {
        double scale = GetScreen()->GetScalingFactor();
551
        dc.SetUserScale( scale, scale );
552

553
        wxPoint pt = GetScreen()->m_DrawOrg;
554
        dc.SetLogicalOrigin( pt.x, pt.y );
555
    }
556

557
    SetClipBox( dc );                         // Reset the clip box to the entire screen.
558 559 560 561
    GRResetPenAndBrush( &dc );
    dc.SetBackgroundMode( wxTRANSPARENT );
}

562

563
void EDA_DRAW_PANEL::OnPaint( wxPaintEvent& event )
dickelbeck's avatar
dickelbeck committed
564
{
565 566 567 568 569 570
    if( GetScreen() == NULL )
    {
        event.Skip();
        return;
    }

571
    INSTALL_PAINTDC( paintDC, this );
572

573 574
    wxRect region = GetUpdateRegion().GetBox();
    SetClipBox( paintDC, &region );
575
    ReDraw( &paintDC, true );
dickelbeck's avatar
dickelbeck committed
576 577
}

578

579
void EDA_DRAW_PANEL::ReDraw( wxDC* DC, bool erasebg )
plyatov's avatar
plyatov committed
580
{
581
    BASE_SCREEN* Screen = GetScreen();
plyatov's avatar
plyatov committed
582

583
    if( Screen == NULL )
dickelbeck's avatar
dickelbeck committed
584
        return;
plyatov's avatar
plyatov committed
585

586
    if( ( g_DrawBgColor != WHITE ) && ( g_DrawBgColor != BLACK ) )
dickelbeck's avatar
dickelbeck committed
587
        g_DrawBgColor = BLACK;
588 589 590 591 592 593 594 595 596 597 598

    if( g_DrawBgColor == WHITE )
    {
        g_XorMode    = GR_NXOR;
        g_GhostColor = BLACK;
    }
    else
    {
        g_XorMode    = GR_XOR;
        g_GhostColor = WHITE;
    }
plyatov's avatar
plyatov committed
599

600 601
    GRResetPenAndBrush( DC );

602
    DC->SetBackground( g_DrawBgColor == BLACK ? *wxBLACK_BRUSH : *wxWHITE_BRUSH );
603
    DC->SetBackgroundMode( wxSOLID );
604 605 606 607

    if( erasebg )
        EraseScreen( DC );

608
    GetParent()->RedrawActiveWindow( DC, erasebg );
609 610 611 612 613 614 615

    // 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() );
plyatov's avatar
plyatov committed
616 617
}

dickelbeck's avatar
dickelbeck committed
618

619
void EDA_DRAW_PANEL::DrawBackGround( wxDC* DC )
620
{
621
    EDA_COLOR_T axis_color = BLUE;
622 623 624

    GRSetDrawMode( DC, GR_COPY );

625
    if( GetParent()->IsGridVisible() )
626 627
        DrawGrid( DC );

628
    // Draw axis
629
    if( GetParent()->m_showAxis )
630
    {
631
        wxSize  pageSize = GetParent()->GetPageSizeIU();
632

633 634 635 636 637 638 639
        // Draw the Y axis
        GRDashedLine( &m_ClipBox, DC, 0, -pageSize.y,
                      0, pageSize.y, 0, axis_color );

        // Draw the X axis
        GRDashedLine( &m_ClipBox, DC, -pageSize.x, 0,
                      pageSize.x, 0, 0, axis_color );
640 641
    }

642
    if( GetParent()->m_showOriginAxis )
643
        DrawAuxiliaryAxis( DC, GR_COPY );
644

645
    if( GetParent()->m_showGridAxis )
646
        DrawGridAxis( DC, GR_COPY, GetParent()->GetGridOrigin() );
647 648 649
}


650
void EDA_DRAW_PANEL::DrawGrid( wxDC* aDC )
plyatov's avatar
plyatov committed
651
{
652
    #define MIN_GRID_SIZE 10        // min grid size in pixels to allow drawing
653
    BASE_SCREEN* screen = GetScreen();
654 655
    wxRealPoint  gridSize;
    wxSize       screenSize;
656
    wxPoint      org;
657
    wxRealPoint  screenGridSize;
658

659 660 661
    /* The grid must be visible. this is possible only is grid value
     * and zoom value are sufficient
     */
662 663 664
    gridSize = screen->GetGridSize();
    screen->m_StartVisu = CalcUnscrolledPosition( wxPoint( 0, 0 ) );
    screenSize = GetClientSize();
665

666 667
    screenGridSize.x = aDC->LogicalToDeviceXRel( KiROUND( gridSize.x ) );
    screenGridSize.y = aDC->LogicalToDeviceYRel( KiROUND( gridSize.y ) );
668

669
    org = m_ClipBox.GetPosition();
670

671
    if( screenGridSize.x < MIN_GRID_SIZE || screenGridSize.y < MIN_GRID_SIZE )
672
    {
673 674 675 676
        screenGridSize.x *= 2.0;
        screenGridSize.y *= 2.0;
        gridSize.x *= 2.0;
        gridSize.y *= 2.0;
677
    }
678

679 680
    if( screenGridSize.x < MIN_GRID_SIZE || screenGridSize.y < MIN_GRID_SIZE )
        return;
681

682
    org = GetParent()->GetNearestGridPosition( org, &gridSize );
683

684 685 686 687
    // 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() )
688
        org.x += KiROUND( gridSize.x );
689 690

    if( org.y < m_ClipBox.GetY() )
691
        org.y += KiROUND( gridSize.y );
692

693
#if ( defined( __WXMAC__ ) || 1 )
694 695 696
    // 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.
697
#ifndef __WXMAC__
698
    GRSetColorPen( aDC, GetParent()->GetGridColor() );
699 700 701 702
#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
703

704 705 706
    int xpos;
    double right = ( double ) m_ClipBox.GetRight();
    double bottom = ( double ) m_ClipBox.GetBottom();
707

708
    for( double x = (double) org.x; x <= right; x += gridSize.x )
709
    {
710
        xpos = KiROUND( x );
711

712
        for( double y = (double) org.y; y <= bottom; y += gridSize.y )
713
        {
714
            aDC->DrawPoint( xpos, KiROUND( y )  );
715
        }
716
    }
717
#else
718
    /* This is fast only if the Blit function is fast.  Not true on all platforms.
719 720 721
     *
     * A first grid column is drawn in a temporary bitmap, and after is duplicated using
     * the Blit function (copy from a screen area to an other screen area).
722
     */
charras's avatar
charras committed
723
    wxMemoryDC tmpDC;
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
    wxBitmap tmpBM( 1, aDC->LogicalToDeviceYRel( m_ClipBox.GetHeight() ) );
    tmpDC.SelectObject( tmpBM );
    tmpDC.SetLogicalFunction( wxCOPY );
    tmpDC.SetBackground( wxBrush( GetBackgroundColour() ) );
    tmpDC.Clear();
    tmpDC.SetPen( MakeColour( GetParent()->GetGridColor() ) );

    double usx, usy;
    int lox, loy, dox, doy;

    aDC->GetUserScale( &usx, &usy );
    aDC->GetLogicalOrigin( &lox, &loy );
    aDC->GetDeviceOrigin( &dox, &doy );

    // Create a dummy DC for coordinate translation because the actual DC scale and origin
    // must be reset in order to work correctly.
    wxBitmap tmpBitmap( 1, 1 );
    wxMemoryDC scaleDC( tmpBitmap );
    scaleDC.SetUserScale( usx, usy );
    scaleDC.SetLogicalOrigin( lox, loy );
    scaleDC.SetDeviceOrigin( dox, doy );
745 746 747

    double bottom = ( double ) m_ClipBox.GetBottom();

748 749
    // Draw a column of grid points.
    for( double y = (double) org.y; y <= bottom; y += gridSize.y )
750
    {
751
        tmpDC.DrawPoint( 0, scaleDC.LogicalToDeviceY( KiROUND( y ) ) );
752
    }
753

754 755 756 757
    // Reset the device context scale and origin and restore on exit.
    EDA_BLIT_NORMALIZER blitNorm( aDC );

    // Mask of everything but the grid points.
758
    tmpDC.SelectObject( wxNullBitmap );
759
    tmpBM.SetMask( new wxMask( tmpBM, GetBackgroundColour() ) );
760 761
    tmpDC.SelectObject( tmpBM );

762 763
    double right = m_ClipBox.GetRight();

764
    // Blit the column for each row of the damaged region.
765
    for( double x = (double) org.x; x <= right; x += gridSize.x )
766
    {
767
        aDC->Blit( scaleDC.LogicalToDeviceX( KiROUND( x ) ),
768
                   scaleDC.LogicalToDeviceY( m_ClipBox.GetY() ),
769
                   1, tmpBM.GetHeight(), &tmpDC, 0, 0, wxCOPY, true );
770
    }
771
#endif
plyatov's avatar
plyatov committed
772 773
}

774

775
void EDA_DRAW_PANEL::DrawAuxiliaryAxis( wxDC* aDC, GR_DRAWMODE aDrawMode )
plyatov's avatar
plyatov committed
776
{
777
    wxPoint origin = GetParent()->GetAuxOrigin();
778 779

    if( origin == wxPoint( 0, 0 ) )
780 781
        return;

782
    EDA_COLOR_T color = DARKRED;
783
    wxSize  pageSize = GetParent()->GetPageSizeIU();
784

785
    GRSetDrawMode( aDC, aDrawMode );
786

787
    // Draw the Y axis
788
    GRDashedLine( &m_ClipBox, aDC,
789
                  origin.x,
790
                  -pageSize.y,
791
                  origin.x,
792 793
                  pageSize.y,
                  0, color );
794

795
    // Draw the X axis
796
    GRDashedLine( &m_ClipBox, aDC,
797
                  -pageSize.x,
798
                  origin.y,
799
                  pageSize.x,
800
                  origin.y,
801
                  0, color );
plyatov's avatar
plyatov committed
802 803
}

804

805
void EDA_DRAW_PANEL::DrawGridAxis( wxDC* aDC, GR_DRAWMODE aDrawMode, const wxPoint& aGridOrigin )
806
{
807
    if( !GetParent()->m_showGridAxis || ( !aGridOrigin.x && !aGridOrigin.y ) )
808 809
        return;

810 811
    EDA_COLOR_T color    = GetParent()->GetGridColor();
    wxSize      pageSize = GetParent()->GetPageSizeIU();
812

813
    GRSetDrawMode( aDC, aDrawMode );
814

815
    // Draw the Y axis
816
    GRDashedLine( &m_ClipBox, aDC,
817
                  aGridOrigin.x,
818
                  -pageSize.y,
819
                  aGridOrigin.x,
820 821
                  pageSize.y,
                  0, color );
822

823
    // Draw the X axis
824
    GRDashedLine( &m_ClipBox, aDC,
825
                  -pageSize.x,
826
                  aGridOrigin.y,
827
                  pageSize.x,
828
                  aGridOrigin.y,
829
                  0, color );
830
}
dickelbeck's avatar
dickelbeck committed
831

832 833

bool EDA_DRAW_PANEL::OnRightClick( wxMouseEvent& event )
plyatov's avatar
plyatov committed
834
{
835 836 837
    wxPoint pos;
    wxMenu  MasterMenu;

838 839 840
    INSTALL_UNBUFFERED_DC( dc, this );

    pos = event.GetLogicalPosition( dc );
841

842
    if( !GetParent()->OnRightClick( pos, &MasterMenu ) )
843 844
        return false;

845
    GetParent()->AddMenuZoomAndGrid( &MasterMenu );
846

847
    pos = event.GetPosition();
848
    m_ignoreMouseEvents = true;
849
    PopupMenu( &MasterMenu, pos );
850
    MoveCursorToCrossHair();
851
    m_ignoreMouseEvents = false;
852 853

    return true;
plyatov's avatar
plyatov committed
854 855 856
}


857
void EDA_DRAW_PANEL::OnMouseLeaving( wxMouseEvent& event )
plyatov's avatar
plyatov committed
858
{
859
    if( m_mouseCaptureCallback == NULL )          // No command in progress.
860
        m_requestAutoPan = false;
861

862
    if( !m_enableAutoPan || !m_requestAutoPan || m_ignoreMouseEvents )
863 864
        return;

865 866 867 868
    // 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
869
    wxSize size = GetClientSize();
870 871 872 873 874
    wxPoint cross_hair_pos = event.GetPosition();
    cross_hair_pos.x = std::min( cross_hair_pos.x, size.x );
    cross_hair_pos.y = std::min( cross_hair_pos.y, size.x );
    cross_hair_pos.x = std::max( cross_hair_pos.x, 0 );
    cross_hair_pos.y = std::max( cross_hair_pos.y, 0 );
875

876 877 878 879
    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 );

880
    GetParent()->SetCrossHairPosition( cross_hair_pos );
881 882 883 884

    wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_POPUP_ZOOM_CENTER );
    cmd.SetEventObject( this );
    GetEventHandler()->ProcessEvent( cmd );
885 886

    event.Skip();
plyatov's avatar
plyatov committed
887 888 889
}


890
void EDA_DRAW_PANEL::OnMouseWheel( wxMouseEvent& event )
891
{
892
    if( m_ignoreMouseEvents )
893 894
        return;

895
    wxRect rect = wxRect( wxPoint( 0, 0 ), GetClientSize() );
896

897
    // Ignore scroll events if the cursor is outside the drawing area.
898
    if( event.GetWheelRotation() == 0 || !GetParent()->IsEnabled()
899
       || !rect.Contains( event.GetPosition() ) )
900
    {
901 902
        wxLogTrace( KICAD_TRACE_COORDS,
                    wxT( "OnMouseWheel() position(%d, %d) rectangle(%d, %d, %d, %d)" ),
903 904
                    event.GetPosition().x, event.GetPosition().y,
                    rect.x, rect.y, rect.width, rect.height );
905 906 907 908
        event.Skip();
        return;
    }

909
    INSTALL_UNBUFFERED_DC( dc, this );
910
    GetParent()->SetCrossHairPosition( event.GetLogicalPosition( dc ) );
911

912
    wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED );
913 914
    cmd.SetEventObject( this );

915 916 917
    bool offCenterReq = event.ControlDown() && event.ShiftDown();
    offCenterReq = offCenterReq || m_enableZoomNoCenter;

918 919 920 921 922 923
#if wxMAJOR_VERSION >= 2 && wxMINOR_VERSION >= 9
    int axis = event.GetWheelAxis();
#else
    const int axis = 0;
#endif

924
    // This is a zoom in or out command
925 926 927
    if( event.GetWheelRotation() > 0 )
    {
        if( event.ShiftDown() && !event.ControlDown() )
928 929 930 931 932 933
        {
            if( axis == 0 )
                cmd.SetId( ID_PAN_UP );
            else
                cmd.SetId( ID_PAN_RIGHT );
        }
934 935
        else if( event.ControlDown() && !event.ShiftDown() )
            cmd.SetId( ID_PAN_LEFT );
936
        else if( offCenterReq )
937
            cmd.SetId( ID_OFFCENTER_ZOOM_IN );
938 939 940
        else
            cmd.SetId( ID_POPUP_ZOOM_IN );
    }
941
    else if( event.GetWheelRotation() < 0 )
942 943
    {
        if( event.ShiftDown() && !event.ControlDown() )
944 945 946 947 948 949
        {
            if( axis == 0 )
                cmd.SetId( ID_PAN_DOWN );
            else
                cmd.SetId( ID_PAN_LEFT );
        }
950 951
        else if( event.ControlDown() && !event.ShiftDown() )
            cmd.SetId( ID_PAN_RIGHT );
952
        else if( offCenterReq )
953
            cmd.SetId( ID_OFFCENTER_ZOOM_OUT );
954 955 956 957 958
        else
            cmd.SetId( ID_POPUP_ZOOM_OUT );
    }

    GetEventHandler()->ProcessEvent( cmd );
959
    event.Skip();
960 961
}

962

963
void EDA_DRAW_PANEL::OnMouseEvent( wxMouseEvent& event )
plyatov's avatar
plyatov committed
964
{
965 966
    int          localrealbutt = 0, localbutt = 0;
    BASE_SCREEN* screen = GetScreen();
967

968 969
    if( !screen )
        return;
970 971 972 973 974 975

    /* 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

976
    if( event.Leaving() )
977
        m_canStartBlock = -1;
978

979
    if( !IsMouseCaptured() )          // No mouse capture in progress.
980
        m_requestAutoPan = false;
981

982
    if( GetParent()->IsActive() )
983 984 985 986
        SetFocus();
    else
        return;

987
    if( !event.IsButton() && !event.Moving() && !event.Dragging() )
988 989 990 991
        return;

    if( event.RightDown() )
    {
992
        OnRightClick( event );
dickelbeck's avatar
dickelbeck committed
993
        return;
994 995
    }

996
    if( m_ignoreMouseEvents )
997 998 999 1000
        return;

    if( event.LeftIsDown() )
        localrealbutt |= GR_M_LEFT_DOWN;
1001

1002 1003 1004 1005 1006 1007 1008 1009
    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;
1010

1011 1012
    if( event.MiddleDown() )
        localbutt = GR_M_MIDDLE_DOWN;
1013

1014
    localrealbutt |= localbutt;     // compensation default wxGTK
1015

1016 1017 1018
    INSTALL_UNBUFFERED_DC( DC, this );
    DC.SetBackground( *wxBLACK_BRUSH );

1019
    // Compute the cursor position in drawing (logical) units.
1020
    GetParent()->SetMousePosition( event.GetLogicalPosition( DC ) );
charras's avatar
charras committed
1021

1022
    int kbstat = 0;
1023 1024 1025

    if( event.ShiftDown() )
        kbstat |= GR_KB_SHIFT;
1026

1027 1028
    if( event.ControlDown() )
        kbstat |= GR_KB_CTRL;
1029

1030 1031 1032
    if( event.AltDown() )
        kbstat |= GR_KB_ALT;

1033
    // Calling Double Click and Click functions :
1034
    if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) )
1035
    {
1036
        GetParent()->OnLeftDClick( &DC, GetParent()->RefPos( true ) );
1037 1038

        // inhibit a response to the mouse left button release,
1039 1040
        // because we have a double click, and we do not want a new
        // OnLeftClick command at end of this Double Click
1041
        m_ignoreNextLeftButtonRelease = true;
1042
    }
dickelbeck's avatar
dickelbeck committed
1043 1044
    else if( event.LeftUp() )
    {
1045 1046
        // 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
1047 1048 1049 1050 1051 1052
        // 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 )
1053
            GetParent()->OnLeftClick( &DC, GetParent()->RefPos( true ) );
1054

1055
    }
1056
    else if( !event.LeftIsDown() )
1057 1058
    {
        /* be sure there is a response to a left button release command
1059 1060
         * even when a LeftUp event is not seen.  This happens when a
         * double click opens a dialog box, and the release mouse button
1061
         * is made when the dialog box is opened.
1062
         */
1063
        m_ignoreNextLeftButtonRelease = false;
dickelbeck's avatar
dickelbeck committed
1064
    }
1065

1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
    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
1077
            m_PanStartCenter = GetParent()->GetScrollCenterPosition();
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096

        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;
1097
            int tmpX, tmpY;
1098 1099 1100 1101 1102
            int ppux, ppuy;
            int maxX, maxY;
            int vsizeX, vsizeY;
            int csizeX, csizeY;

1103
            GetViewStart( &tmpX, &tmpY );
1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148
            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 );

1149 1150
            double scale = GetParent()->GetScreen()->GetScalingFactor();

1151
            wxPoint center = GetParent()->GetScrollCenterPosition();
1152 1153
            center.x += KiROUND( (double) ( x - tmpX ) / scale ) / ppux;
            center.y += KiROUND( (double) ( y - tmpY ) / scale ) / ppuy;
1154
            GetParent()->SetScrollCenterPosition( center );
1155

1156 1157 1158 1159 1160 1161 1162
            Refresh();
            Update();
        }
        else
        {
            double scale = GetParent()->GetScreen()->GetScalingFactor();
            int x = m_PanStartCenter.x +
1163
                    KiROUND( (double) ( m_PanStartEventPosition.x - currentPosition.x ) / scale );
1164
            int y = m_PanStartCenter.y +
1165
                    KiROUND( (double) ( m_PanStartEventPosition.y - currentPosition.y ) / scale );
1166 1167 1168 1169 1170 1171 1172

            GetParent()->RedrawScreen( wxPoint( x, y ), false );
        }
    }

    if( event.ButtonUp( wxMOUSE_BTN_MIDDLE ) && !m_enableMiddleButtonPan &&
        (screen->m_BlockLocate.GetState() == STATE_NO_BLOCK) )
1173
    {
1174
        // The middle button has been released, with no block command:
1175
        // We use it for a zoom center at cursor position command
1176
        wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_POPUP_ZOOM_CENTER );
1177 1178
        cmd.SetEventObject( this );
        GetEventHandler()->ProcessEvent( cmd );
1179 1180
    }

1181
    // Calling the general function on mouse changes (and pseudo key commands)
1182
    GetParent()->GeneralControl( &DC, event.GetLogicalPosition( DC ), 0 );
1183 1184 1185 1186 1187 1188

    /*******************************/
    /* Control of block commands : */
    /*******************************/

    // Command block can't start if mouse is dragging a new panel
1189 1190
    static EDA_DRAW_PANEL* lastPanel;
    if( lastPanel != this )
1191
    {
1192
        m_minDragEventCount = 0;
1193
        m_canStartBlock   = -1;
1194
    }
1195

1196
    /* A new command block can start after a release buttons
1197
     * and if the drag is enough
1198
     * This is to avoid a false start block when a dialog box is dismissed,
1199
     * or when changing panels in hierarchy navigation
1200 1201
     * or when clicking while and moving mouse
     */
1202 1203
    if( !event.LeftIsDown() && !event.MiddleIsDown() )
    {
1204
        m_minDragEventCount = 0;
1205
        m_canStartBlock   = 0;
1206

1207
        /* Remember the last cursor position when a drag mouse starts
1208
         * this is the last position ** before ** clicking a button
1209 1210 1211 1212
         * 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)
1213
         */
1214
        m_CursorStartPos = GetParent()->GetCrossHairPosition();
1215 1216
    }

1217
    if( m_enableBlockCommands && !(localbutt & GR_M_DCLICK) )
1218
    {
1219
        if( !screen->IsBlockActive() )
1220
        {
1221
            screen->m_BlockLocate.SetOrigin( m_CursorStartPos );
1222
        }
1223

1224
        if( event.LeftDown() || ( !m_enableMiddleButtonPan && event.MiddleDown() ) )
1225
        {
1226
            if( screen->m_BlockLocate.GetState() == STATE_BLOCK_MOVE )
1227
            {
1228
                m_requestAutoPan = false;
1229
                GetParent()->HandleBlockPlace( &DC );
1230
                m_ignoreNextLeftButtonRelease = true;
1231 1232
            }
        }
1233
        else if( ( m_canStartBlock >= 0 )
1234
                && ( event.LeftIsDown() || ( !m_enableMiddleButtonPan && event.MiddleIsDown() ) )
1235
                && !IsMouseCaptured() )
1236
        {
1237
            // Mouse is dragging: if no block in progress,  start a block command.
1238
            if( screen->m_BlockLocate.GetState() == STATE_NO_BLOCK )
1239 1240
            {
                //  Start a block command
1241
                int cmd_type = kbstat;
1242

1243
                if( !m_enableMiddleButtonPan && event.MiddleIsDown() )
1244
                    cmd_type |= MOUSE_MIDDLE;
1245

1246 1247 1248
                // 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
1249 1250
                if( m_minDragEventCount < MIN_DRAG_COUNT_FOR_START_BLOCK_COMMAND )
                    m_minDragEventCount++;
1251 1252
                else
                {
1253
                    if( !GetParent()->HandleBlockBegin( &DC, cmd_type, m_CursorStartPos ) )
1254
                    {
1255
                        // should not occur: error
1256
                        GetParent()->DisplayToolMsg(
1257
                            wxT( "EDA_DRAW_PANEL::OnMouseEvent() Block Error" ) );
1258 1259 1260
                    }
                    else
                    {
1261
                        m_requestAutoPan = true;
1262
                        SetCursor( wxCURSOR_SIZING );
1263
                    }
1264 1265 1266 1267
                }
            }
        }

1268 1269
        if( event.ButtonUp( wxMOUSE_BTN_LEFT ) ||
            ( !m_enableMiddleButtonPan && event.ButtonUp( wxMOUSE_BTN_MIDDLE ) ) )
1270 1271
        {
            /* Release the mouse button: end of block.
1272 1273 1274 1275 1276
             * 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.
1277
             */
dickelbeck's avatar
dickelbeck committed
1278
            #define BLOCK_MINSIZE_LIMIT 1
1279
            bool BlockIsSmall =
1280 1281
                ( std::abs( screen->m_BlockLocate.GetWidth() ) < BLOCK_MINSIZE_LIMIT )
                && ( std::abs( screen->m_BlockLocate.GetHeight() ) < BLOCK_MINSIZE_LIMIT );
1282

1283
            if( (screen->m_BlockLocate.GetState() != STATE_NO_BLOCK) && BlockIsSmall )
1284
            {
1285
                if( m_endMouseCaptureCallback )
1286
                {
1287
                    m_endMouseCaptureCallback( this, &DC );
1288
                    m_requestAutoPan = false;
1289
                }
1290

1291
                SetCursor( (wxStockCursor) m_currentCursor );
1292
           }
1293
            else if( screen->m_BlockLocate.GetState() == STATE_BLOCK_END )
1294
            {
1295
                m_requestAutoPan = false;
1296
                GetParent()->HandleBlockEnd( &DC );
1297
                SetCursor( (wxStockCursor) m_currentCursor );
1298
                if( screen->m_BlockLocate.GetState() == STATE_BLOCK_MOVE )
1299
                {
1300
                    m_requestAutoPan = true;
1301
                    SetCursor( wxCURSOR_HAND );
1302
                }
1303
           }
1304 1305 1306
        }
    }

1307
    // End of block command on a double click
1308
    // To avoid an unwanted block move command if the mouse is moved while double clicking
1309
    if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) )
1310
    {
1311
        if( !screen->IsBlockActive() && IsMouseCaptured() )
1312
        {
1313
            m_endMouseCaptureCallback( this, &DC );
1314 1315
        }
    }
plyatov's avatar
plyatov committed
1316 1317

#if 0
1318 1319
    wxString msg_debug;
    msg_debug.Printf( " block state %d, cmd %d",
1320 1321
                      screen->m_BlockLocate.GetState(),
                      screen->m_BlockLocate.GetCommand() );
1322
    GetParent()->PrintMsg( msg_debug );
plyatov's avatar
plyatov committed
1323 1324
#endif

1325
    lastPanel = this;
plyatov's avatar
plyatov committed
1326 1327
}

1328

1329 1330 1331 1332 1333 1334

void EDA_DRAW_PANEL::OnCharHook( wxKeyEvent& event )
{
    event.Skip();
}

1335
void EDA_DRAW_PANEL::OnKeyEvent( wxKeyEvent& event )
plyatov's avatar
plyatov committed
1336
{
1337
    int localkey;
1338
    wxPoint pos;
1339

1340
    localkey = event.GetKeyCode();
1341 1342 1343

    switch( localkey )
    {
1344 1345
    default:
        break;
1346 1347

    case WXK_ESCAPE:
1348
        m_abortRequest = true;
1349

1350
        if( IsMouseCaptured() )
1351
            EndMouseCapture();
1352
        else
1353
            EndMouseCapture( ID_NO_TOOL_SELECTED, m_defaultCursor, wxEmptyString );
1354 1355 1356 1357 1358 1359 1360
        break;
    }

    if( event.ControlDown() )
        localkey |= GR_KB_CTRL;
    if( event.AltDown() )
        localkey |= GR_KB_ALT;
1361
    if( event.ShiftDown() && (event.GetKeyCode() > 256) )
1362 1363
        localkey |= GR_KB_SHIFT;

charras's avatar
charras committed
1364 1365
    /* Normalize keys code to easily handle keys from Ctrl+A to Ctrl+Z
     * They have an ascii code from 1 to 27 remapped
1366
     * to GR_KB_CTRL + 'A' to GR_KB_CTRL + 'Z'
charras's avatar
charras committed
1367
     */
1368
    if( (localkey > GR_KB_CTRL) && (localkey <= GR_KB_CTRL+26) )
charras's avatar
charras committed
1369 1370
        localkey += 'A' - 1;

1371
    INSTALL_UNBUFFERED_DC( DC, this );
charras's avatar
charras committed
1372

1373
    // Some key commands use the current mouse position: refresh it.
1374
    pos = wxGetMousePosition() - GetScreenPosition();
1375

1376
    // Compute the cursor position in drawing units.  Also known as logical units to wxDC.
1377
    pos = wxPoint( DC.DeviceToLogicalX( pos.x ), DC.DeviceToLogicalY( pos.y ) );
1378

1379
    GetParent()->SetMousePosition( pos );
1380
    GetParent()->GeneralControl( &DC, pos, localkey );
plyatov's avatar
plyatov committed
1381
}
1382

1383

1384
void EDA_DRAW_PANEL::OnPan( wxCommandEvent& event )
1385
{
1386
    int x, y;
1387 1388 1389
    int ppux, ppuy;
    int unitsX, unitsY;
    int maxX, maxY;
1390

1391 1392 1393 1394 1395 1396 1397
    GetViewStart( &x, &y );
    GetScrollPixelsPerUnit( &ppux, &ppuy );
    GetVirtualSize( &unitsX, &unitsY );
    maxX = unitsX;
    maxY = unitsY;
    unitsX /= ppux;
    unitsY /= ppuy;
1398 1399 1400 1401

    switch( event.GetId() )
    {
    case ID_PAN_UP:
1402
        y -= m_scrollIncrementY;
1403 1404 1405
        break;

    case ID_PAN_DOWN:
1406
        y += m_scrollIncrementY;
1407 1408 1409
        break;

    case ID_PAN_LEFT:
1410
        x -= m_scrollIncrementX;
1411 1412 1413
        break;

    case ID_PAN_RIGHT:
1414
        x += m_scrollIncrementX;
1415 1416 1417
        break;

    default:
1418
        wxLogDebug( wxT( "Unknown ID %d in EDA_DRAW_PANEL::OnPan()." ), event.GetId() );
1419 1420
    }

1421 1422
    if( x < 0 )
        x = 0;
1423

1424 1425
    if( y < 0 )
        y = 0;
1426

1427 1428
    if( x > maxX )
        x = maxX;
1429

1430 1431 1432
    if( y > maxY )
        y = maxY;

1433
    Scroll( x/ppux, y/ppuy );
1434
}
1435 1436


1437 1438
void EDA_DRAW_PANEL::EndMouseCapture( int id, int cursor, const wxString& title,
                                      bool aCallEndFunc )
1439
{
1440
    if( m_mouseCaptureCallback && m_endMouseCaptureCallback && aCallEndFunc )
1441
    {
1442
        INSTALL_UNBUFFERED_DC( dc, this );
1443
        m_endMouseCaptureCallback( this, &dc );
charras's avatar
charras committed
1444
    }
1445

jean-pierre charras's avatar
jean-pierre charras committed
1446 1447
    m_mouseCaptureCallback = NULL;
    m_endMouseCaptureCallback = NULL;
1448
    m_requestAutoPan = false;
1449

charras's avatar
charras committed
1450 1451 1452
    if( id != -1 && cursor != -1 )
    {
        wxASSERT( cursor > wxCURSOR_NONE && cursor < wxCURSOR_MAX );
1453
        GetParent()->SetToolID( id, cursor, title );
1454 1455
    }
}
1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473


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." ) );
    wxCHECK_RET( m_endMouseCaptureCallback != NULL, wxT( "End mouse capture callback not set." ) );

    m_endMouseCaptureCallback( this, aDC );
}