router_tool.cpp 21.8 KB
Newer Older
1 2 3 4 5
/*
 * KiRouter - a push-and-(sometimes-)shove PCB router
 *
 * Copyright (C) 2013  CERN
 * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
6
 *
7 8 9 10
 * 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 3 of the License, or (at your
 * option) any later version.
11
 *
12 13 14 15
 * 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.
16
 *
17
 * You should have received a copy of the GNU General Public License along
18
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
19 20
 */

Dick Hollenbeck's avatar
Dick Hollenbeck committed
21
#include <wx/numdlg.h>
22

23 24
#include <boost/foreach.hpp>
#include <boost/optional.hpp>
25
#include <boost/bind.hpp>
26

27
#include "class_draw_panel_gal.h"
28 29 30
#include "class_board.h"

#include <wxPcbStruct.h>
31
#include <id.h>
32
#include <macros.h>
33
#include <pcbnew_id.h>
34 35 36
#include <view/view_controls.h>
#include <pcbcommon.h>
#include <pcb_painter.h>
37 38 39
#include <dialogs/dialog_pns_settings.h>
#include <dialogs/dialog_track_via_size.h>
#include <base_units.h>
40 41

#include <tool/context_menu.h>
42
#include <tools/common_actions.h>
43

44 45
#include <ratsnest_data.h>

46 47 48 49 50
#include "router_tool.h"
#include "pns_segment.h"
#include "pns_router.h"
#include "trace.h"

51
using namespace KIGFX;
52 53
using boost::optional;

54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
static TOOL_ACTION ACT_NewTrack( "pcbnew.InteractiveRouter.NewTrack",
                                 AS_CONTEXT, 'X',
                                 "New Track", "Starts laying a new track.");
static TOOL_ACTION ACT_EndTrack( "pcbnew.InteractiveRouter.EndTrack",
                                 AS_CONTEXT, WXK_END,
                                 "End Track", "Stops laying the current track.");
static TOOL_ACTION ACT_AutoEndRoute( "pcbnew.InteractiveRouter.AutoEndRoute",
                                     AS_CONTEXT, 'F',
                                     "Auto-end Track", "Automagically finishes currently routed track." );
static TOOL_ACTION ACT_Drag( "pcbnew.InteractiveRouter.Drag",
                                     AS_CONTEXT, 'G',
                                     "Drag Track/Via", "Drags a track or a via." );
static TOOL_ACTION ACT_PlaceThroughVia( "pcbnew.InteractiveRouter.PlaceVia",
                                 AS_CONTEXT, 'V',
                                 "Place Through Via", "Adds a through-hole via at the end of currently routed track." );
static TOOL_ACTION ACT_CustomTrackWidth( "pcbnew.InteractiveRouter.CustomTrackWidth",
                                      AS_CONTEXT, 'W',
                                      "Custom Track Width", "Shows a dialog for changing the track width and via size.");
static TOOL_ACTION ACT_RouterOptions( "pcbnew.InteractiveRouter.RouterOptions",
                                      AS_CONTEXT, 'E',
                                      "Routing Options...", "Shows a dialog containing router options.");
static TOOL_ACTION ACT_SwitchPosture( "pcbnew.InteractiveRouter.SwitchPosture",
                                      AS_CONTEXT, '/',
                                      "Switch Track Posture", "Switches posture of the currenly routed track.");
78 79

ROUTER_TOOL::ROUTER_TOOL() :
80
    TOOL_INTERACTIVE( "pcbnew.InteractiveRouter" )
81
{
82
    m_router = NULL;
83
}
84

85

86 87 88 89 90
class CONTEXT_TRACK_WIDTH_MENU: public CONTEXT_MENU
{
public:
    CONTEXT_TRACK_WIDTH_MENU()
    {
91 92
        setCustomEventHandler( boost::bind( &CONTEXT_TRACK_WIDTH_MENU::handleCustomEvent,
                                            this, _1 ) );
93
    }
94

95 96
    void SetBoard( BOARD* aBoard )
    {
97
        BOARD_DESIGN_SETTINGS& bds = aBoard->GetDesignSettings();
Dick Hollenbeck's avatar
Dick Hollenbeck committed
98

99 100
        wxString msg;
        m_board = aBoard;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
101

102 103
        Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Custom size" ),
                wxEmptyString, wxITEM_CHECK );
104 105

        Append( ID_POPUP_PCB_SELECT_AUTO_WIDTH, _( "Use the starting track width" ),
106
                _( "Route using the width of the starting track." ), wxITEM_CHECK );
107

108 109
        Append( ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES, _( "Use netclass values" ),
                _( "Use track and via sizes from the net class" ), wxITEM_CHECK );
110 111 112

        for( unsigned i = 0; i < bds.m_TrackWidthList.size(); i++ )
        {
113
            msg = _( "Track ");
Dick Hollenbeck's avatar
Dick Hollenbeck committed
114 115
            msg << StringFromValue( g_UserUnit, bds.m_TrackWidthList[i], true );

116 117 118 119 120 121 122 123 124 125
            if( i == 0 )
                msg << _( " (from netclass)" );

            Append( ID_POPUP_PCB_SELECT_WIDTH1 + i, msg, wxEmptyString, wxITEM_CHECK );
        }

        AppendSeparator();

        for( unsigned i = 0; i < bds.m_ViasDimensionsList.size(); i++ )
        {
126 127
            msg = _("Via ");
            msg << StringFromValue( g_UserUnit, bds.m_ViasDimensionsList[i].m_Diameter, true );
128
            wxString drill = StringFromValue( g_UserUnit,
129 130
                                              bds.m_ViasDimensionsList[i].m_Drill,
                                              true );
131 132 133 134

            if( bds.m_ViasDimensionsList[i].m_Drill <= 0 )
            {
                msg << _ (", drill: default");
135 136 137
            }
            else
            {
138 139 140 141 142 143 144 145 146 147 148 149 150 151
                msg << _ (", drill: ") << drill;
            }

            if( i == 0 )
                msg << _( " (from netclass)" );

            Append( ID_POPUP_PCB_SELECT_VIASIZE1 + i, msg, wxEmptyString, wxITEM_CHECK );
        }
    }

protected:
    OPT_TOOL_EVENT handleCustomEvent( const wxEvent& aEvent )
    {
#if ID_POPUP_PCB_SELECT_VIASIZE1 < ID_POPUP_PCB_SELECT_WIDTH1
152
#error You have changed event ids order, it breaks code. Check the source code for more details.
153 154 155 156 157 158 159
// Recognising type of event (track width/via size) is based on comparison if the event id is
// within a specific range. If ranges of event ids changes, then the following is not valid anymore.
#endif
        BOARD_DESIGN_SETTINGS &bds = m_board->GetDesignSettings();

        int id = aEvent.GetId();

160
        // Initial settings, to be modified below
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
        bds.m_UseConnectedTrackWidth = false;
        bds.UseCustomTrackViaSize( false );

        if( id == ID_POPUP_PCB_SELECT_CUSTOM_WIDTH )
        {
            bds.UseCustomTrackViaSize( true );
        }

        else if( id == ID_POPUP_PCB_SELECT_AUTO_WIDTH )
        {
            bds.m_UseConnectedTrackWidth = true;
        }

        else if( id == ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES )
        {
            bds.SetViaSizeIndex( 0 );
            bds.SetTrackWidthIndex( 0 );
        }

        else if( id > ID_POPUP_PCB_SELECT_VIASIZE1 )     // via size has changed
        {
            assert( id < ID_POPUP_PCB_SELECT_WIDTH_END_RANGE );

            bds.SetViaSizeIndex( id - ID_POPUP_PCB_SELECT_VIASIZE1 );
        }

        else    // track width has changed
        {
            assert( id >= ID_POPUP_PCB_SELECT_WIDTH1 );
            assert( id < ID_POPUP_PCB_SELECT_VIASIZE );

            bds.SetTrackWidthIndex( id - ID_POPUP_PCB_SELECT_WIDTH1 );
        }

        return OPT_TOOL_EVENT( COMMON_ACTIONS::trackViaSizeChanged.MakeEvent() );
    }

    BOARD* m_board;
};
200 201


202 203
class ROUTER_TOOL_MENU: public CONTEXT_MENU
{
204
public:
205
    ROUTER_TOOL_MENU( BOARD* aBoard )
206 207 208 209 210 211 212 213 214
    {
        SetTitle( wxT( "Interactive Router" ) );
        Add( ACT_NewTrack );
        Add( ACT_EndTrack );
//        Add( ACT_AutoEndRoute );  // fixme: not implemented yet. Sorry.
        Add( ACT_Drag );
        Add( ACT_PlaceThroughVia );
        Add( ACT_SwitchPosture );

215 216
        AppendSeparator();
        
217 218 219 220 221 222
        CONTEXT_TRACK_WIDTH_MENU* trackMenu = new CONTEXT_TRACK_WIDTH_MENU;
        trackMenu->SetBoard( aBoard );
        AppendSubMenu( trackMenu, wxT( "Select Track Width" ) );

        Add( ACT_CustomTrackWidth );

223
        AppendSeparator();
224 225 226 227
        Add( ACT_RouterOptions );
    }
};

228

229 230
ROUTER_TOOL::~ROUTER_TOOL()
{
231
    delete m_router;
232 233 234
}


235
void ROUTER_TOOL::Reset( RESET_REASON aReason )
236
{
237
    if( m_router )
238 239 240 241
        delete m_router;

    m_router = new PNS_ROUTER;

242
    TRACEn( 0, "Reset" );
243
    m_router->ClearWorld();
244
    m_router->SetBoard( getModel<BOARD>() );
245
    m_router->SyncWorld();
246
    m_router->LoadSettings( m_settings );
247
    m_needsSync = false;
248

249
    if( getView() )
250 251
        m_router->SetView( getView() );

252
    Go( &ROUTER_TOOL::Main, COMMON_ACTIONS::routerActivate.MakeEvent() );
253 254
}

255

256 257 258
int ROUTER_TOOL::getDefaultWidth( int aNetCode )
{
    int w, d1, d2;
259 260

    getNetclassDimensions( aNetCode, w, d1, d2 );
261

262 263 264
    return w;
}

265

266
void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth,
Maciej Suminski's avatar
Maciej Suminski committed
267
                                         int& aViaDiameter, int& aViaDrill )
268
{
269
    BOARD* board = getModel<BOARD>();
270
    BOARD_DESIGN_SETTINGS &bds = board->GetDesignSettings();
271

272
    NETCLASSPTR netClass;
273 274 275
    NETINFO_ITEM* ni = board->FindNet( aNetCode );

    if( ni )
276 277
    {
        wxString netClassName = ni->GetClassName();
278
        netClass = bds.m_NetClasses.Find( netClassName );
279 280 281
    }

    if( !netClass )
282
        netClass = bds.GetDefault();
283

284 285 286 287 288 289
    aWidth = netClass->GetTrackWidth();
    aViaDiameter = netClass->GetViaDiameter();
    aViaDrill = netClass->GetViaDrill();
}


290
PNS_ITEM* ROUTER_TOOL::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLayer )
291 292 293
{
    int tl = getView()->GetTopLayer();

294
    if( aLayer > 0 )
295 296
        tl = aLayer;

297 298 299 300
    PNS_ITEM* prioritized[4];

    for(int i = 0; i < 4; i++)
        prioritized[i] = 0;
Dick Hollenbeck's avatar
Dick Hollenbeck committed
301

302
    PNS_ITEMSET candidates = m_router->QueryHoverItems( aWhere );
303

304
    BOOST_FOREACH( PNS_ITEM* item, candidates.Items() )
305
    {
306
        if( !IsCopperLayer( item->Layers().Start() ) )
307 308
            continue;

309 310 311
        // fixme: this causes flicker with live loop removal...
        //if( item->Parent() && !item->Parent()->ViewIsVisible() )
        //    continue;
312

313
        if( aNet < 0 || item->Net() == aNet )
314
        {
315 316
            if( item->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ) )
            {
317 318
                if( !prioritized[2] )
                    prioritized[2] = item;
319
                if( item->Layers().Overlaps( tl ) )
320
                    prioritized[0] = item;
321 322
            }
            else
323
            {
324
                if( !prioritized[3] )
325
                    prioritized[3] = item;
326
                if( item->Layers().Overlaps( tl ) )
327
                    prioritized[1] = item;
328 329 330 331
            }
        }
    }

332 333
    PNS_ITEM* rv = NULL;
    for( int i = 0; i < 4; i++ )
334
    {
335
        PNS_ITEM* item = prioritized[i];
Dick Hollenbeck's avatar
Dick Hollenbeck committed
336

337 338 339
        if( DisplayOpt.ContrastModeDisplay )
            if( item && !item->Layers().Overlaps( tl ) )
                item = NULL;
340

341
        if( item )
342 343 344 345 346
        {
            rv = item;
            break;
        }
    }
347

348
    if( rv && aLayer >= 0 && !rv->Layers().Overlaps( aLayer ) )
349 350
        rv = NULL;

351
    if( rv )
352
        TRACE( 0, "%s, layer : %d, tl: %d", rv->KindStr().c_str() % rv->Layers().Start() % tl );
353 354 355 356

    return rv;
}

357

358
void ROUTER_TOOL::highlightNet( bool aEnabled, int aNetcode )
359
{
360 361 362 363 364 365
    RENDER_SETTINGS* rs = getView()->GetPainter()->GetSettings();

    if( aNetcode >= 0 && aEnabled )
        rs->SetHighlight( true, aNetcode );
    else
        rs->SetHighlight( false );
366 367 368 369

    getView()->UpdateAllLayersColor();
}

370 371

void ROUTER_TOOL::handleCommonEvents( TOOL_EVENT& aEvent )
372 373
{
#ifdef DEBUG
374 375 376 377 378 379 380 381
    if( aEvent.IsKeyPressed() )
    {
        switch( aEvent.KeyCode() )
        {
            case 'S':
                TRACEn( 2, "saving drag/route log...\n" );
                m_router->DumpLog();
                break;
382
        }
383
    }
Dick Hollenbeck's avatar
Dick Hollenbeck committed
384
    else
385
#endif
386
    if( aEvent.IsAction( &ACT_RouterOptions ) )
387
    {
Maciej Suminski's avatar
Maciej Suminski committed
388
        DIALOG_PNS_SETTINGS settingsDlg( getEditFrame<PCB_EDIT_FRAME>(), m_router->Settings() );
389 390 391 392 393

        if( settingsDlg.ShowModal() )
            m_router->ApplySettings();
    }

394
    else if( aEvent.IsAction( &ACT_CustomTrackWidth ) )
395
    {
Maciej Suminski's avatar
Maciej Suminski committed
396
        DIALOG_TRACK_VIA_SIZE sizeDlg( getEditFrame<PCB_EDIT_FRAME>(), m_router->Settings() );
397
        BOARD_DESIGN_SETTINGS& bds = getModel<BOARD>()->GetDesignSettings();
398 399 400 401 402 403 404 405 406

        sizeDlg.ShowModal();

        // TODO it should be changed, router settings won't keep track & via sizes in the future
        bds.SetCustomTrackWidth( m_router->Settings().GetTrackWidth() );
        bds.SetCustomViaSize( m_router->Settings().GetViaDiameter() );
        bds.SetCustomViaDrill( m_router->Settings().GetViaDrill() );
        bds.UseCustomTrackViaSize( true );

407
        m_toolMgr->RunAction( COMMON_ACTIONS::trackViaSizeChanged );
408 409
    }

410
    else if( aEvent.IsAction( &COMMON_ACTIONS::trackViaSizeChanged ) )
411
    {
412
        BOARD_DESIGN_SETTINGS& bds = getModel<BOARD>()->GetDesignSettings();
413 414 415 416 417 418 419

        m_router->Settings().SetTrackWidth( bds.GetCurrentTrackWidth() );
        m_router->Settings().SetViaDiameter( bds.GetCurrentViaSize() );
        m_router->Settings().SetViaDrill( bds.GetCurrentViaDrill() );
        m_router->ApplySettings();
    }
}
420

421

422 423
void ROUTER_TOOL::updateStartItem( TOOL_EVENT& aEvent )
{
424
    VIEW_CONTROLS* ctls = getViewControls();
425
    int tl = getView()->GetTopLayer();
426
    VECTOR2I cp = ctls->GetCursorPosition();
427
    PNS_ITEM* startItem = NULL;
428 429 430 431

    if( aEvent.IsMotion() || aEvent.IsClick() )
    {
        VECTOR2I p = aEvent.Position();
432
        startItem = pickSingleItem( p );
433 434 435
        bool snapEnabled = !aEvent.Modifier(MD_SHIFT);
        m_router->EnableSnapping ( snapEnabled );

436
        if( !snapEnabled && startItem && !startItem->Layers().Overlaps( tl ) )
437 438 439
            startItem = NULL;

        if( startItem && startItem->Net() >= 0 )
440 441
        {
            bool dummy;
442
            VECTOR2I psnap = m_router->SnapToItem( startItem, p, dummy );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
443

444 445
            if( snapEnabled )
            {
446 447
                m_startSnapPoint = psnap;
                ctls->ForceCursorPosition( true, psnap );
448 449 450
            }
            else
            {
451 452 453
                m_startSnapPoint = cp;
                ctls->ForceCursorPosition( false );
            }
454

455
            if( startItem->Layers().IsMultilayer() )
456 457
                m_startLayer = tl;
            else
458
                m_startLayer = startItem->Layers().Start();
459

460
            m_startItem = startItem;
461 462 463
        }
        else
        {
464
            m_startItem = NULL;
465
            m_startSnapPoint = cp;
466
            m_startLayer = tl;
467
            ctls->ForceCursorPosition( false );
468 469 470 471
        }
    }
}

472

473 474
void ROUTER_TOOL::updateEndItem( TOOL_EVENT& aEvent )
{
475
    VIEW_CONTROLS* ctls = getViewControls();
476 477
    VECTOR2I p = getView()->ToWorld( ctls->GetMousePosition() );
    VECTOR2I cp = ctls->GetCursorPosition();
478
    int layer;
479
    bool snapEnabled = !aEvent.Modifier( MD_SHIFT );
480

481
    m_router->EnableSnapping( snapEnabled );
482 483

    if( !snapEnabled || m_router->GetCurrentNet() < 0 || !m_startItem )
484 485
    {
        m_endItem = NULL;
486
        m_endSnapPoint = cp;
487 488 489 490 491
        return;
    }

    bool dummy;

492
    if( m_router->IsPlacingVia() )
493
        layer = -1;
494
    else
495 496
        layer = m_router->GetCurrentLayer();

497
    PNS_ITEM* endItem = pickSingleItem( p, m_startItem->Net(), layer );
498 499

    if( endItem )
500
    {
501 502
        VECTOR2I cursorPos = m_router->SnapToItem( endItem, p, dummy );
        ctls->ForceCursorPosition( true, cursorPos );
503 504
        m_endItem = endItem;
        m_endSnapPoint = cursorPos;
505 506 507
    }
    else
    {
508
        m_endItem = NULL;
509
        m_endSnapPoint = cp;
510
        ctls->ForceCursorPosition( false );
511
    }
512 513

    if( m_endItem )
514
        TRACE( 0, "%s, layer : %d", m_endItem->KindStr().c_str() % m_endItem->Layers().Start() );
515 516
}

517

518
void ROUTER_TOOL::performRouting()
519
{
520
    PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
521
    bool saveUndoBuffer = true;
522 523
    VIEW_CONTROLS* ctls = getViewControls();

524
    if( getModel<BOARD>()->GetDesignSettings().m_UseConnectedTrackWidth )
525 526 527 528 529
    {
        int width = getDefaultWidth( m_startItem ? m_startItem->Net() : -1 );

        if( m_startItem && m_startItem->OfKind( PNS_ITEM::SEGMENT ) )
            width = static_cast<PNS_SEGMENT*>( m_startItem )->Width();
530

531 532
        m_router->Settings().SetTrackWidth( width );
    }
533

534
    m_router->SwitchLayer( m_startLayer );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
535
    frame->SetActiveLayer( ToLAYER_ID( m_startLayer ) );
536

537 538
    if( m_startItem && m_startItem->Net() >= 0 )
        highlightNet( true, m_startItem->Net() );
539 540 541

    ctls->ForceCursorPosition( false );
    ctls->SetAutoPan( true );
542 543 544 545 546 547 548 549

    m_router->StartRouting( m_startSnapPoint, m_startItem );

    m_endItem = NULL;
    m_endSnapPoint = m_startSnapPoint;

    while( OPT_TOOL_EVENT evt = Wait() )
    {
550
        if( evt->IsCancel() || evt->IsActivate() )
551
            break;
552 553 554 555 556
        else if( evt->Action() == TA_UNDO_REDO )
        {
            saveUndoBuffer = false;
            break;
        }
557 558
        else if( evt->IsMotion() )
        {
559
            updateEndItem( *evt );
560 561
            m_router->Move( m_endSnapPoint, m_endItem );
        }
562
        else if( evt->IsClick( BUT_LEFT ) )
563
        {
564
            updateEndItem( *evt );
565 566

            if( m_router->FixRoute( m_endSnapPoint, m_endItem ) )
567
                break;
568

569
            // Synchronize the indicated layer
570
            frame->SetActiveLayer( ToLAYER_ID( m_router->GetCurrentLayer() ) );
571

572
            m_router->Move( m_endSnapPoint, m_endItem );
573 574
        }
        else if( evt->IsAction( &ACT_PlaceThroughVia ) )
575
        {
576 577
            m_router->Settings().SetLayerPair( frame->GetScreen()->m_Route_Layer_TOP,
                                               frame->GetScreen()->m_Route_Layer_BOTTOM );
578
            m_router->ToggleViaPlacement();
579
            m_router->Move( m_endSnapPoint, m_endItem );        // refresh
580
        }
581
        else if( evt->IsAction( &ACT_SwitchPosture ) )
582
        {
583
            m_router->FlipPosture();
584
            m_router->Move( m_endSnapPoint, m_endItem );        // refresh
585
        }
586
        else if( evt->IsAction( &COMMON_ACTIONS::layerChanged ) )
587 588
        {
            updateEndItem( *evt );
589 590
            m_router->SwitchLayer( frame->GetActiveLayer() );
            m_router->Move( m_endSnapPoint, m_endItem );        // refresh
591 592 593 594
        }
        else if( evt->IsAction( &ACT_EndTrack ) )
        {
            if( m_router->FixRoute( m_endSnapPoint, m_endItem ) )
595 596
                break;
        }
597 598
    
        handleCommonEvents( *evt );
599 600
    }

601
    m_router->StopRouting();
602

603 604 605
    if( saveUndoBuffer )
    {
        // Save the recent changes in the undo buffer
606 607 608
        frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED );
        m_router->ClearUndoBuffer();
        frame->OnModify();
609 610 611 612
    }
    else
    {
        // It was interrupted by TA_UNDO_REDO event, so we have to sync the world now
613
        m_needsSync = true;
614
    }
615

616 617 618
    ctls->SetAutoPan( false );
    ctls->ForceCursorPosition( false );
    highlightNet( false );
619 620 621 622 623
}


int ROUTER_TOOL::Main( TOOL_EVENT& aEvent )
{
624
    VIEW_CONTROLS* ctls = getViewControls();
625
    BOARD* board = getModel<BOARD>();
626
    BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
627

628
    // Deselect all items
629
    m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
630

631 632
    getEditFrame<PCB_EDIT_FRAME>()->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL,
                                               _( "Interactive Router" ) );
633

634
    ctls->SetSnapping( true );
635 636
    ctls->ShowCursor( true );

637 638 639 640 641
    // Set current track widths & via size
    m_router->Settings().SetTrackWidth( bds.GetCurrentTrackWidth() );
    m_router->Settings().SetViaDiameter( bds.GetCurrentViaSize() );
    m_router->Settings().SetViaDrill( bds.GetCurrentViaDrill() );

642
    ROUTER_TOOL_MENU* ctxMenu = new ROUTER_TOOL_MENU( board );
643 644

    SetContextMenu ( ctxMenu );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
645

646 647 648
    // Main loop: keep receiving events
    while( OPT_TOOL_EVENT evt = Wait() )
    {
649 650 651
        if( m_needsSync )
        {
            m_router->SyncWorld();
652
            m_router->SetView( getView() );
653 654 655
            m_needsSync = false;
        }

656
        if( evt->IsCancel() || evt->IsActivate() )
657
            break; // Finish
658
        else if( evt->Action() == TA_UNDO_REDO )
659
            m_needsSync = true;
660
        else if( evt->IsMotion() )
661
            updateStartItem( *evt );
662
        else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACT_NewTrack ) )
663 664 665
        {
            updateStartItem( *evt );

666 667 668 669
            if( evt->Modifier( MD_CTRL ) )
                performDragging();
            else
                performRouting();
670 671
        }
        else if ( evt->IsAction( &ACT_Drag ) )
672 673
            performDragging();

674
        handleCommonEvents( *evt );
675
    }
676 677 678 679

    // Restore the default settings
    ctls->SetAutoPan( false );
    ctls->ShowCursor( false );
680 681
    getEditFrame<PCB_EDIT_FRAME>()->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );

682 683
    // Store routing settings till the next invocation
    m_settings = m_router->Settings();
684 685
    delete ctxMenu;

686 687
    return 0;
}
688 689 690 691


void ROUTER_TOOL::performDragging()
{
692 693
    PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
    bool saveUndoBuffer = true;
694 695 696
    VIEW_CONTROLS* ctls = getViewControls();

    bool dragStarted = m_router->StartDragging( m_startSnapPoint, m_startItem );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
697

698
    if( !dragStarted )
699 700 701 702 703 704 705
        return;

    if( m_startItem && m_startItem->Net() >= 0 )
        highlightNet( true, m_startItem->Net() );

    ctls->ForceCursorPosition( false );
    ctls->SetAutoPan( true );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
706

707 708
    while( OPT_TOOL_EVENT evt = Wait() )
    {
709
        if( evt->IsCancel() || evt->IsActivate() )
710
            break;
711 712 713 714 715
        else if( evt->Action() == TA_UNDO_REDO )
        {
            saveUndoBuffer = false;
            break;
        }
716 717 718 719 720 721 722 723 724 725
        else if( evt->IsMotion() )
        {
            updateEndItem( *evt );
            m_router->Move( m_endSnapPoint, m_endItem );
        }
        else if( evt->IsClick( BUT_LEFT ) )
        {
            if( m_router->FixRoute( m_endSnapPoint, m_endItem ) )
                break;
        }
726 727

        handleCommonEvents( *evt );
728 729 730 731 732
    }

    if( m_router->RoutingInProgress() )
        m_router->StopRouting();

733 734 735 736 737 738 739 740 741 742 743 744 745
    if( saveUndoBuffer )
    {
        // Save the recent changes in the undo buffer
        frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED );
        m_router->ClearUndoBuffer();
        frame->OnModify();
    }
    else
    {
        // It was interrupted by TA_UNDO_REDO event, so we have to sync the world now
        m_needsSync = true;
    }

746 747 748 749
    ctls->SetAutoPan( false );
    ctls->ForceCursorPosition( false );
    highlightNet( false );
}