draw_panel.cpp 43.3 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_draw_panel_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( parent->GetDrawBgColor() ) );
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 = true;
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
        cfg->Read( ENBL_MIDDLE_BUTT_PAN_KEY, &m_enableMiddleButtonPan, true );
132 133 134
        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
EDA_DRAW_FRAME* EDA_DRAW_PANEL::GetParent() const
169
{
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 532
    EDA_COLOR_T bgColor = GetParent()->GetDrawBgColor();

533
    GRSFilledRect( NULL, DC, m_ClipBox.GetX(), m_ClipBox.GetY(),
534
                   m_ClipBox.GetRight(), m_ClipBox.GetBottom(),
535
                   0, bgColor, bgColor );
536

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

545

546
void EDA_DRAW_PANEL::DoPrepareDC( wxDC& dc )
547
{
548 549
    wxScrolledWindow::DoPrepareDC( dc );

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

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

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

564

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

573
    INSTALL_PAINTDC( paintDC, this );
574

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

580

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

585
    if( Screen == NULL )
dickelbeck's avatar
dickelbeck committed
586
        return;
plyatov's avatar
plyatov committed
587

588 589 590 591
    EDA_COLOR_T bgColor = GetParent()->GetDrawBgColor();

    if( ( bgColor != WHITE ) && ( bgColor != BLACK ) )
        bgColor = BLACK;
592

593
    if( bgColor == WHITE )
594 595 596 597 598 599 600 601 602
    {
        g_XorMode    = GR_NXOR;
        g_GhostColor = BLACK;
    }
    else
    {
        g_XorMode    = GR_XOR;
        g_GhostColor = WHITE;
    }
plyatov's avatar
plyatov committed
603

604 605
    GRResetPenAndBrush( DC );

606
    DC->SetBackground( bgColor == BLACK ? *wxBLACK_BRUSH : *wxWHITE_BRUSH );
607
    DC->SetBackgroundMode( wxSOLID );
608 609 610 611

    if( erasebg )
        EraseScreen( DC );

612
    GetParent()->RedrawActiveWindow( DC, erasebg );
613 614 615 616 617 618 619

    // 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
620 621
}

dickelbeck's avatar
dickelbeck committed
622

623
void EDA_DRAW_PANEL::DrawBackGround( wxDC* DC )
624
{
625
    EDA_COLOR_T axis_color = BLUE;
626 627 628

    GRSetDrawMode( DC, GR_COPY );

629
    if( GetParent()->IsGridVisible() )
630 631
        DrawGrid( DC );

632
    // Draw axis
633
    if( GetParent()->m_showAxis )
634
    {
635
        wxSize  pageSize = GetParent()->GetPageSizeIU();
636

637 638 639 640 641 642 643
        // 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 );
644 645
    }

646
    if( GetParent()->m_showOriginAxis )
647
        DrawAuxiliaryAxis( DC, GR_COPY );
648

649
    if( GetParent()->m_showGridAxis )
650
        DrawGridAxis( DC, GR_COPY, GetParent()->GetGridOrigin() );
651 652 653
}


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

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

670 671
    screenGridSize.x = aDC->LogicalToDeviceXRel( KiROUND( gridSize.x ) );
    screenGridSize.y = aDC->LogicalToDeviceYRel( KiROUND( gridSize.y ) );
672

673
    org = m_ClipBox.GetPosition();
674

675
    if( screenGridSize.x < MIN_GRID_SIZE || screenGridSize.y < MIN_GRID_SIZE )
676
    {
677 678 679 680
        screenGridSize.x *= 2.0;
        screenGridSize.y *= 2.0;
        gridSize.x *= 2.0;
        gridSize.y *= 2.0;
681
    }
682

683 684
    if( screenGridSize.x < MIN_GRID_SIZE || screenGridSize.y < MIN_GRID_SIZE )
        return;
685

686
    org = GetParent()->GetNearestGridPosition( org, &gridSize );
687

688 689 690 691
    // 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() )
692
        org.x += KiROUND( gridSize.x );
693 694

    if( org.y < m_ClipBox.GetY() )
695
        org.y += KiROUND( gridSize.y );
696

697
#if ( defined( __WXMAC__ ) || 1 )
698 699 700
    // 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.
701
#ifndef __WXMAC__
702
    GRSetColorPen( aDC, GetParent()->GetGridColor() );
703 704 705 706
#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
707

708 709 710
    int xpos;
    double right = ( double ) m_ClipBox.GetRight();
    double bottom = ( double ) m_ClipBox.GetBottom();
711

712
    for( double x = (double) org.x; x <= right; x += gridSize.x )
713
    {
714
        xpos = KiROUND( x );
715

716
        for( double y = (double) org.y; y <= bottom; y += gridSize.y )
717
        {
718
            aDC->DrawPoint( xpos, KiROUND( y )  );
719
        }
720
    }
721
#else
722
    /* This is fast only if the Blit function is fast.  Not true on all platforms.
723 724 725
     *
     * 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).
726
     */
charras's avatar
charras committed
727
    wxMemoryDC tmpDC;
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
    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 );
749 750 751

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

752 753
    // Draw a column of grid points.
    for( double y = (double) org.y; y <= bottom; y += gridSize.y )
754
    {
755
        tmpDC.DrawPoint( 0, scaleDC.LogicalToDeviceY( KiROUND( y ) ) );
756
    }
757

758 759 760 761
    // Reset the device context scale and origin and restore on exit.
    EDA_BLIT_NORMALIZER blitNorm( aDC );

    // Mask of everything but the grid points.
762
    tmpDC.SelectObject( wxNullBitmap );
763
    tmpBM.SetMask( new wxMask( tmpBM, GetBackgroundColour() ) );
764 765
    tmpDC.SelectObject( tmpBM );

766 767
    double right = m_ClipBox.GetRight();

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

778

779
void EDA_DRAW_PANEL::DrawAuxiliaryAxis( wxDC* aDC, GR_DRAWMODE aDrawMode )
plyatov's avatar
plyatov committed
780
{
781
    wxPoint origin = GetParent()->GetAuxOrigin();
782 783

    if( origin == wxPoint( 0, 0 ) )
784 785
        return;

786
    EDA_COLOR_T color = DARKRED;
787
    wxSize  pageSize = GetParent()->GetPageSizeIU();
788

789
    GRSetDrawMode( aDC, aDrawMode );
790

791
    // Draw the Y axis
792
    GRDashedLine( &m_ClipBox, aDC,
793
                  origin.x,
794
                  -pageSize.y,
795
                  origin.x,
796 797
                  pageSize.y,
                  0, color );
798

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

808

809
void EDA_DRAW_PANEL::DrawGridAxis( wxDC* aDC, GR_DRAWMODE aDrawMode, const wxPoint& aGridOrigin )
810
{
811
    if( !GetParent()->m_showGridAxis || ( !aGridOrigin.x && !aGridOrigin.y ) )
812 813
        return;

814 815
    EDA_COLOR_T color    = GetParent()->GetGridColor();
    wxSize      pageSize = GetParent()->GetPageSizeIU();
816

817
    GRSetDrawMode( aDC, aDrawMode );
818

819
    // Draw the Y axis
820
    GRDashedLine( &m_ClipBox, aDC,
821
                  aGridOrigin.x,
822
                  -pageSize.y,
823
                  aGridOrigin.x,
824 825
                  pageSize.y,
                  0, color );
826

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

836 837

bool EDA_DRAW_PANEL::OnRightClick( wxMouseEvent& event )
plyatov's avatar
plyatov committed
838
{
839 840 841
    wxPoint pos;
    wxMenu  MasterMenu;

842 843 844
    INSTALL_UNBUFFERED_DC( dc, this );

    pos = event.GetLogicalPosition( dc );
845

846
    if( !GetParent()->OnRightClick( pos, &MasterMenu ) )
847 848
        return false;

849
    GetParent()->AddMenuZoomAndGrid( &MasterMenu );
850

851
    pos = event.GetPosition();
852
    m_ignoreMouseEvents = true;
853
    PopupMenu( &MasterMenu, pos );
854
    MoveCursorToCrossHair();
855
    m_ignoreMouseEvents = false;
856 857

    return true;
plyatov's avatar
plyatov committed
858 859 860
}


861
void EDA_DRAW_PANEL::OnMouseLeaving( wxMouseEvent& event )
plyatov's avatar
plyatov committed
862
{
863
    if( m_mouseCaptureCallback == NULL )          // No command in progress.
864
        m_requestAutoPan = false;
865

866
    if( !m_enableAutoPan || !m_requestAutoPan || m_ignoreMouseEvents )
867 868
        return;

869 870 871 872
    // 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
873
    wxSize size = GetClientSize();
874 875 876 877 878
    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 );
879

880 881 882 883
    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 );

884
    GetParent()->SetCrossHairPosition( cross_hair_pos );
885 886 887 888

    wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED, ID_POPUP_ZOOM_CENTER );
    cmd.SetEventObject( this );
    GetEventHandler()->ProcessEvent( cmd );
889 890

    event.Skip();
plyatov's avatar
plyatov committed
891 892 893
}


894
void EDA_DRAW_PANEL::OnMouseWheel( wxMouseEvent& event )
895
{
896
    if( m_ignoreMouseEvents )
897 898
        return;

899
    wxRect rect = wxRect( wxPoint( 0, 0 ), GetClientSize() );
900

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

913
    INSTALL_UNBUFFERED_DC( dc, this );
914
    GetParent()->SetCrossHairPosition( event.GetLogicalPosition( dc ) );
915

916
    wxCommandEvent cmd( wxEVT_COMMAND_MENU_SELECTED );
917 918
    cmd.SetEventObject( this );

919 920 921
    bool offCenterReq = event.ControlDown() && event.ShiftDown();
    offCenterReq = offCenterReq || m_enableZoomNoCenter;

922 923 924 925 926 927
#if wxMAJOR_VERSION >= 2 && wxMINOR_VERSION >= 9
    int axis = event.GetWheelAxis();
#else
    const int axis = 0;
#endif

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

    GetEventHandler()->ProcessEvent( cmd );
963
    event.Skip();
964 965
}

966

967
void EDA_DRAW_PANEL::OnMouseEvent( wxMouseEvent& event )
plyatov's avatar
plyatov committed
968
{
969 970
    int          localrealbutt = 0, localbutt = 0;
    BASE_SCREEN* screen = GetScreen();
971

972 973
    if( !screen )
        return;
974 975 976 977 978 979

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

980
    if( event.Leaving() )
981
        m_canStartBlock = -1;
982

983
    if( !IsMouseCaptured() )          // No mouse capture in progress.
984
        m_requestAutoPan = false;
985

986
    if( GetParent()->IsActive() )
987 988 989 990
        SetFocus();
    else
        return;

991
    if( !event.IsButton() && !event.Moving() && !event.Dragging() )
992 993 994 995
        return;

    if( event.RightDown() )
    {
996
        OnRightClick( event );
dickelbeck's avatar
dickelbeck committed
997
        return;
998 999
    }

1000
    if( m_ignoreMouseEvents )
1001 1002 1003 1004
        return;

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

1006 1007 1008 1009 1010 1011 1012 1013
    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;
1014

1015 1016
    if( event.MiddleDown() )
        localbutt = GR_M_MIDDLE_DOWN;
1017

1018
    localrealbutt |= localbutt;     // compensation default wxGTK
1019

1020 1021 1022
    INSTALL_UNBUFFERED_DC( DC, this );
    DC.SetBackground( *wxBLACK_BRUSH );

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

1026
    int kbstat = 0;
1027 1028 1029

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

1031 1032
    if( event.ControlDown() )
        kbstat |= GR_KB_CTRL;
1033

1034 1035 1036
    if( event.AltDown() )
        kbstat |= GR_KB_ALT;

1037
    // Calling Double Click and Click functions :
1038
    if( localbutt == (int) ( GR_M_LEFT_DOWN | GR_M_DCLICK ) )
1039
    {
1040
        GetParent()->OnLeftDClick( &DC, GetParent()->RefPos( true ) );
1041 1042

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

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

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

        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;
1101
            int tmpX, tmpY;
1102 1103 1104 1105 1106
            int ppux, ppuy;
            int maxX, maxY;
            int vsizeX, vsizeY;
            int csizeX, csizeY;

1107
            GetViewStart( &tmpX, &tmpY );
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 1149 1150 1151 1152
            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 );

1153 1154
            double scale = GetParent()->GetScreen()->GetScalingFactor();

1155
            wxPoint center = GetParent()->GetScrollCenterPosition();
1156 1157
            center.x += KiROUND( (double) ( x - tmpX ) / scale ) / ppux;
            center.y += KiROUND( (double) ( y - tmpY ) / scale ) / ppuy;
1158
            GetParent()->SetScrollCenterPosition( center );
1159

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

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

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

1185
    // Calling the general function on mouse changes (and pseudo key commands)
1186
    GetParent()->GeneralControl( &DC, event.GetLogicalPosition( DC ), 0 );
1187 1188 1189 1190 1191 1192

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

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

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

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

1221
    if( m_enableBlockCommands && !(localbutt & GR_M_DCLICK) )
1222
    {
1223
        if( !screen->IsBlockActive() )
1224
        {
1225
            screen->m_BlockLocate.SetOrigin( m_CursorStartPos );
1226
        }
1227

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

1247
                if( !m_enableMiddleButtonPan && event.MiddleIsDown() )
1248
                    cmd_type |= MOUSE_MIDDLE;
1249

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

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

1287
            if( (screen->m_BlockLocate.GetState() != STATE_NO_BLOCK) && BlockIsSmall )
1288
            {
1289
                if( m_endMouseCaptureCallback )
1290
                {
1291
                    m_endMouseCaptureCallback( this, &DC );
1292
                    m_requestAutoPan = false;
1293
                }
1294

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

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

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

1329
    lastPanel = this;
plyatov's avatar
plyatov committed
1330 1331
}

1332

1333 1334 1335 1336 1337 1338

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

1339
void EDA_DRAW_PANEL::OnKeyEvent( wxKeyEvent& event )
plyatov's avatar
plyatov committed
1340
{
1341
    int localkey;
1342
    wxPoint pos;
1343

1344
    localkey = event.GetKeyCode();
1345 1346 1347

    switch( localkey )
    {
1348 1349
    default:
        break;
1350 1351

    case WXK_ESCAPE:
1352
        m_abortRequest = true;
1353

1354
        if( IsMouseCaptured() )
1355
            EndMouseCapture();
1356
        else
1357
            EndMouseCapture( ID_NO_TOOL_SELECTED, m_defaultCursor, wxEmptyString );
1358 1359 1360 1361 1362 1363 1364
        break;
    }

    if( event.ControlDown() )
        localkey |= GR_KB_CTRL;
    if( event.AltDown() )
        localkey |= GR_KB_ALT;
1365
    if( event.ShiftDown() && (event.GetKeyCode() > 256) )
1366 1367
        localkey |= GR_KB_SHIFT;

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

1375
    INSTALL_UNBUFFERED_DC( DC, this );
charras's avatar
charras committed
1376

1377
    // Some key commands use the current mouse position: refresh it.
1378
    pos = wxGetMousePosition() - GetScreenPosition();
1379

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

1383
    GetParent()->SetMousePosition( pos );
1384
    GetParent()->GeneralControl( &DC, pos, localkey );
plyatov's avatar
plyatov committed
1385
}
1386

1387

1388
void EDA_DRAW_PANEL::OnPan( wxCommandEvent& event )
1389
{
1390
    int x, y;
1391 1392 1393
    int ppux, ppuy;
    int unitsX, unitsY;
    int maxX, maxY;
1394

1395 1396 1397 1398 1399 1400 1401
    GetViewStart( &x, &y );
    GetScrollPixelsPerUnit( &ppux, &ppuy );
    GetVirtualSize( &unitsX, &unitsY );
    maxX = unitsX;
    maxY = unitsY;
    unitsX /= ppux;
    unitsY /= ppuy;
1402 1403 1404 1405

    switch( event.GetId() )
    {
    case ID_PAN_UP:
1406
        y -= m_scrollIncrementY;
1407 1408 1409
        break;

    case ID_PAN_DOWN:
1410
        y += m_scrollIncrementY;
1411 1412 1413
        break;

    case ID_PAN_LEFT:
1414
        x -= m_scrollIncrementX;
1415 1416 1417
        break;

    case ID_PAN_RIGHT:
1418
        x += m_scrollIncrementX;
1419 1420 1421
        break;

    default:
1422
        wxLogDebug( wxT( "Unknown ID %d in EDA_DRAW_PANEL::OnPan()." ), event.GetId() );
1423 1424
    }

1425 1426
    if( x < 0 )
        x = 0;
1427

1428 1429
    if( y < 0 )
        y = 0;
1430

1431 1432
    if( x > maxX )
        x = maxX;
1433

1434 1435 1436
    if( y > maxY )
        y = maxY;

1437
    Scroll( x/ppux, y/ppuy );
1438
}
1439 1440


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

jean-pierre charras's avatar
jean-pierre charras committed
1450 1451
    m_mouseCaptureCallback = NULL;
    m_endMouseCaptureCallback = NULL;
1452
    m_requestAutoPan = false;
1453

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


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 );
}