KiCad PCB EDA Suite
Loading...
Searching...
No Matches
router_tool.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Tomasz Wlostowski <[email protected]>
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#include <wx/filedlg.h>
24#include <wx/hyperlink.h>
25#include <advanced_config.h>
26
27#include <functional>
28#include <iomanip>
29#include <utility>
30#include <sstream>
31
32using namespace std::placeholders;
33#include <board.h>
35#include <board_item.h>
36#include <footprint.h>
38#include <pad.h>
39#include <zone.h>
40#include <pcb_edit_frame.h>
41#include <pcbnew_id.h>
45#include <math/vector2wx.h>
46#include <paths.h>
47#include <confirm.h>
48#include <kidialog.h>
49#include <widgets/wx_infobar.h>
54#include <view/view_controls.h>
55#include <bitmaps.h>
56#include <string_utils.h>
57#include <gal/painter.h>
58#include <tool/tool_action.h>
59#include <tool/action_menu.h>
60#include <tool/tool_manager.h>
61#include <tool/tool_menu.h>
62#include <tools/pcb_actions.h>
65#include <tools/drc_tool.h>
68
69#include <project.h>
72
73#include "router_tool.h"
75#include "pns_router.h"
76#include "pns_itemset.h"
77#include "pns_logger.h"
78#include "pns_placement_algo.h"
79#include "pns_drag_algo.h"
80
81#include "pns_kicad_iface.h"
82
84
86
87using namespace KIGFX;
88
93{
94 // Via type
95 VIA_MASK = 0x07,
96 VIA = 0x00,
97 BLIND_VIA = 0x01,
98 BURIED_VIA = 0x02,
99 MICROVIA = 0x04,
100
101 // Select layer
103};
104
105
106// Actions, being statically-defined, require specialized I18N handling. We continue to
107// use the _() macro so that string harvesting by the I18N framework doesn't have to be
108// specialized, but we don't translate on initialization and instead do it in the getters.
109
110#undef _
111#define _(s) s
112
113// Pass all the parameters as int to allow combining flags
115 .Name( "pcbnew.InteractiveRouter.PlaceVia" )
116 .Scope( AS_CONTEXT )
117 .DefaultHotkey( 'V' )
118 .LegacyHotkeyName( "Add Through Via" )
119 .FriendlyName( _( "Place Through Via" ) )
120 .Tooltip( _( "Adds a through-hole via at the end of currently routed track." ) )
121 .Icon( BITMAPS::via )
122 .Flags( AF_NONE )
123 .Parameter<int>( VIA_ACTION_FLAGS::VIA ) );
124
126 .Name( "pcbnew.InteractiveRouter.PlaceBlindVia" )
127 .Scope( AS_CONTEXT )
128 .DefaultHotkey( MD_ALT + MD_SHIFT + 'V' )
129 .LegacyHotkeyName( "Add Blind/Buried Via" )
130 .FriendlyName( _( "Place Blind/Buried Via" ) )
131 .Tooltip( _( "Adds a blind or buried via at the end of currently routed track.") )
132 .Icon( BITMAPS::via_buried )
133 .Flags( AF_NONE )
134 .Parameter<int>( VIA_ACTION_FLAGS::BLIND_VIA ) );
135
137 .Name( "pcbnew.InteractiveRouter.PlaceMicroVia" )
138 .Scope( AS_CONTEXT )
139 .DefaultHotkey( MD_CTRL + 'V' )
140 .LegacyHotkeyName( "Add MicroVia" )
141 .FriendlyName( _( "Place Microvia" ) )
142 .Tooltip( _( "Adds a microvia at the end of currently routed track." ) )
143 .Icon( BITMAPS::via_microvia )
144 .Flags( AF_NONE )
145 .Parameter<int>( VIA_ACTION_FLAGS::MICROVIA ) );
146
148 .Name( "pcbnew.InteractiveRouter.SelLayerAndPlaceVia" )
149 .Scope( AS_CONTEXT )
150 .DefaultHotkey( '<' )
151 .LegacyHotkeyName( "Select Layer and Add Through Via" )
152 .FriendlyName( _( "Select Layer and Place Through Via..." ) )
153 .Tooltip( _( "Select a layer, then add a through-hole via at the end of currently routed track." ) )
155 .Flags( AF_NONE )
157
159 .Name( "pcbnew.InteractiveRouter.SelLayerAndPlaceBlindVia" )
160 .Scope( AS_CONTEXT )
161 .DefaultHotkey( MD_ALT + '<' )
162 .LegacyHotkeyName( "Select Layer and Add Blind/Buried Via" )
163 .FriendlyName( _( "Select Layer and Place Blind/Buried Via..." ) )
164 .Tooltip( _( "Select a layer, then add a blind or buried via at the end of currently routed track." ) )
166 .Flags( AF_NONE )
168
170 .Name( "pcbnew.InteractiveRouter.SelLayerAndPlaceMicroVia" )
171 .Scope( AS_CONTEXT )
172 .FriendlyName( _( "Select Layer and Place Micro Via..." ) )
173 .Tooltip( _( "Select a layer, then add a micro via at the end of currently routed track." ) )
175 .Flags( AF_NONE )
177
179 .Name( "pcbnew.InteractiveRouter.CustomTrackViaSize" )
180 .Scope( AS_CONTEXT )
181 .DefaultHotkey( 'Q' )
182 .LegacyHotkeyName( "Custom Track/Via Size" )
183 .FriendlyName( _( "Custom Track/Via Size..." ) )
184 .Tooltip( _( "Shows a dialog for changing the track width and via size." ) )
185 .Icon( BITMAPS::width_track ) );
186
188 .Name( "pcbnew.InteractiveRouter.SwitchPosture" )
189 .Scope( AS_CONTEXT )
190 .DefaultHotkey( '/' )
191 .LegacyHotkeyName( "Switch Track Posture" )
192 .FriendlyName( _( "Switch Track Posture" ) )
193 .Tooltip( _( "Switches posture of the currently routed track." ) )
195
196 // This old command ( track corner switch mode) is now moved to a submenu with other corner mode options
198 .Name( "pcbnew.InteractiveRouter.SwitchRoundingToNext" )
199 .Scope( AS_CONTEXT )
200 .DefaultHotkey( MD_CTRL + '/' )
201 .FriendlyName( _( "Track Corner Mode Switch" ) )
202 .Tooltip( _( "Switches between sharp/rounded and 45°/90° corners when routing tracks." ) )
204
205// hotkeys W and Shift+W are used to switch to track width changes
207 .Name( "pcbnew.InteractiveRouter.SwitchRounding45" )
208 .Scope( AS_CONTEXT )
209 .DefaultHotkey( MD_CTRL + 'W' )
210 .FriendlyName( _( "Track Corner Mode 45" ) )
211 .Tooltip( _( "Switch to 45° corner when routing tracks." ) ) );
212
214 .Name( "pcbnew.InteractiveRouter.SwitchRounding90" )
215 .Scope( AS_CONTEXT )
216 .DefaultHotkey( MD_CTRL + MD_ALT + 'W' )
217 .FriendlyName( _( "Track Corner Mode 90" ) )
218 .Tooltip( _( "Switch to 90° corner when routing tracks." ) ) );
219
221 .Name( "pcbnew.InteractiveRouter.SwitchRoundingArc45" )
222 .Scope( AS_CONTEXT )
223 .DefaultHotkey( MD_CTRL + MD_SHIFT + 'W' )
224 .FriendlyName( _( "Track Corner Mode Arc 45" ) )
225 .Tooltip( _( "Switch to arc 45° corner when routing tracks." ) ) );
226
228 .Name( "pcbnew.InteractiveRouter.SwitchRoundingArc90" )
229 .Scope( AS_CONTEXT )
230 .DefaultHotkey( MD_ALT + 'W' )
231 .FriendlyName( _( "Track Corner Mode Arc 90" ) )
232 .Tooltip( _( "Switch to arc 90° corner when routing tracks." ) ) );
233
234#undef _
235#define _(s) wxGetTranslation((s))
236
237
239 TOOL_BASE( "pcbnew.InteractiveRouter" ),
242 m_inRouterTool( false )
243{
244}
245
246
248{
249public:
251 ACTION_MENU( true ),
252 m_frame( aFrame )
253 {
255 SetTitle( _( "Select Track/Via Width" ) );
256 }
257
258protected:
259 ACTION_MENU* create() const override
260 {
261 return new TRACK_WIDTH_MENU( m_frame );
262 }
263
264 void update() override
265 {
266 BOARD_DESIGN_SETTINGS& bds = m_frame.GetBoard()->GetDesignSettings();
267 bool useIndex = !bds.m_UseConnectedTrackWidth &&
269 wxString msg;
270
271 Clear();
272
273 Append( ID_POPUP_PCB_SELECT_AUTO_WIDTH, _( "Use Starting Track Width" ),
274 _( "Route using the width of the starting track." ), wxITEM_CHECK );
277
278 Append( ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES, _( "Use Net Class Values" ),
279 _( "Use track and via sizes from the net class" ), wxITEM_CHECK );
281 useIndex && bds.GetTrackWidthIndex() == 0 && bds.GetViaSizeIndex() == 0 );
282
283 Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
284 _( "Specify custom track and via sizes" ), wxITEM_CHECK );
286
287 AppendSeparator();
288
289 // Append the list of tracks & via sizes
290 for( unsigned i = 0; i < bds.m_TrackWidthList.size(); i++ )
291 {
292 int width = bds.m_TrackWidthList[i];
293
294 if( i == 0 )
295 msg = _( "Track netclass width" );
296 else
297 msg.Printf( _( "Track %s" ), m_frame.MessageTextFromValue( width ) );
298
299 int menuIdx = ID_POPUP_PCB_SELECT_WIDTH1 + i;
300 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
301 Check( menuIdx, useIndex && bds.GetTrackWidthIndex() == i );
302 }
303
304 AppendSeparator();
305
306 for( unsigned i = 0; i < bds.m_ViasDimensionsList.size(); i++ )
307 {
309
310 if( i == 0 )
311 msg = _( "Via netclass values" );
312 else
313 {
314 if( via.m_Drill > 0 )
315 {
316 msg.Printf( _("Via %s, hole %s" ),
317 m_frame.MessageTextFromValue( via.m_Diameter ),
318 m_frame.MessageTextFromValue( via.m_Drill ) );
319 }
320 else
321 {
322 msg.Printf( _( "Via %s" ),
323 m_frame.MessageTextFromValue( via.m_Diameter ) );
324 }
325 }
326
327 int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
328 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
329 Check( menuIdx, useIndex && bds.GetViaSizeIndex() == i );
330 }
331 }
332
333 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
334 {
335 BOARD_DESIGN_SETTINGS &bds = m_frame.GetBoard()->GetDesignSettings();
336 int id = aEvent.GetId();
337
338 // On Windows, this handler can be called with an event ID not existing in any
339 // menuitem, so only set flags when we have an ID match.
340
342 {
343 bds.UseCustomTrackViaSize( true );
344 bds.m_TempOverrideTrackWidth = true;
345 m_frame.GetToolManager()->RunAction( ACT_CustomTrackWidth );
346 }
347 else if( id == ID_POPUP_PCB_SELECT_AUTO_WIDTH )
348 {
349 bds.UseCustomTrackViaSize( false );
350 bds.m_UseConnectedTrackWidth = true;
351 bds.m_TempOverrideTrackWidth = false;
352 }
354 {
355 bds.UseCustomTrackViaSize( false );
356 bds.m_UseConnectedTrackWidth = false;
357 bds.SetViaSizeIndex( 0 );
358 bds.SetTrackWidthIndex( 0 );
359 }
361 {
362 bds.UseCustomTrackViaSize( false );
364 }
366 {
367 bds.UseCustomTrackViaSize( false );
368 bds.m_TempOverrideTrackWidth = true;
370 }
371
373 }
374
375private:
377};
378
379
381{
382public:
384 ACTION_MENU( true ),
385 m_frame( aFrame )
386 {
388 SetTitle( _( "Select Differential Pair Dimensions" ) );
389 }
390
391protected:
392 ACTION_MENU* create() const override
393 {
394 return new DIFF_PAIR_MENU( m_frame );
395 }
396
397 void update() override
398 {
399 const BOARD_DESIGN_SETTINGS& bds = m_frame.GetBoard()->GetDesignSettings();
400
401 Clear();
402
403 Append( ID_POPUP_PCB_SELECT_USE_NETCLASS_DIFFPAIR, _( "Use Net Class Values" ),
404 _( "Use differential pair dimensions from the net class" ), wxITEM_CHECK );
406 !bds.UseCustomDiffPairDimensions() && bds.GetDiffPairIndex() == 0 );
407
408 Append( ID_POPUP_PCB_SELECT_CUSTOM_DIFFPAIR, _( "Use Custom Values..." ),
409 _( "Specify custom differential pair dimensions" ), wxITEM_CHECK );
411
412 AppendSeparator();
413
414 // Append the list of differential pair dimensions
415
416 // Drop index 0 which is the current netclass dimensions (which are handled above)
417 for( unsigned i = 1; i < bds.m_DiffPairDimensionsList.size(); ++i )
418 {
420 wxString msg;
421
422 if( diffPair.m_Gap <= 0 )
423 {
424 if( diffPair.m_ViaGap <= 0 )
425 {
426 msg.Printf( _( "Width %s" ),
427 m_frame.MessageTextFromValue( diffPair.m_Width ) );
428 }
429 else
430 {
431 msg.Printf( _( "Width %s, via gap %s" ),
432 m_frame.MessageTextFromValue( diffPair.m_Width ),
433 m_frame.MessageTextFromValue( diffPair.m_ViaGap ) );
434 }
435 }
436 else
437 {
438 if( diffPair.m_ViaGap <= 0 )
439 {
440 msg.Printf( _( "Width %s, gap %s" ),
441 m_frame.MessageTextFromValue( diffPair.m_Width ),
442 m_frame.MessageTextFromValue( diffPair.m_Gap ) );
443 }
444 else
445 {
446 msg.Printf( _( "Width %s, gap %s, via gap %s" ),
447 m_frame.MessageTextFromValue( diffPair.m_Width ),
448 m_frame.MessageTextFromValue( diffPair.m_Gap ),
449 m_frame.MessageTextFromValue( diffPair.m_ViaGap ) );
450 }
451 }
452
453 int menuIdx = ID_POPUP_PCB_SELECT_DIFFPAIR1 + i - 1;
454 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
455 Check( menuIdx, !bds.UseCustomDiffPairDimensions() && bds.GetDiffPairIndex() == i );
456 }
457 }
458
459 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
460 {
461 BOARD_DESIGN_SETTINGS &bds = m_frame.GetBoard()->GetDesignSettings();
462 int id = aEvent.GetId();
463
464 // On Windows, this handler can be called with an event ID not existing in any
465 // menuitem, so only set flags when we have an ID match.
466
468 {
469 bds.UseCustomDiffPairDimensions( true );
470 TOOL_MANAGER* toolManager = m_frame.GetToolManager();
472 }
474 {
475 bds.UseCustomDiffPairDimensions( false );
476 bds.SetDiffPairIndex( 0 );
477 }
479 {
480 bds.UseCustomDiffPairDimensions( false );
481 // remember that the menu doesn't contain index 0 (which is the netclass values)
483 }
484
486 }
487
488private:
490};
491
492
496
497
499{
501
503
504 wxASSERT( frame );
505
506 auto& menu = m_menu->GetMenu();
507 menu.SetUntranslatedTitle( _HKI( "Interactive Router" ) );
508
509 m_trackViaMenu = std::make_shared<TRACK_WIDTH_MENU>( *frame );
510 m_trackViaMenu->SetTool( this );
511 m_menu->RegisterSubMenu( m_trackViaMenu );
512
513 m_diffPairMenu = std::make_shared<DIFF_PAIR_MENU>( *frame );
514 m_diffPairMenu->SetTool( this );
515 m_menu->RegisterSubMenu( m_diffPairMenu );
516
517 ACTION_MANAGER* mgr = frame->GetToolManager()->GetActionManager();
518
519 auto haveHighlight =
520 [this]( const SELECTION& sel )
521 {
522 KIGFX::RENDER_SETTINGS* cfg = m_toolMgr->GetView()->GetPainter()->GetSettings();
523
524 return !cfg->GetHighlightNetCodes().empty();
525 };
526
527 auto notRoutingCond =
528 [this]( const SELECTION& )
529 {
530 return !m_router->RoutingInProgress();
531 };
532
533 auto hasOtherEnd =
534 [this]( const SELECTION& )
535 {
536 std::vector<PNS::NET_HANDLE> currentNets = m_router->GetCurrentNets();
537
538 if( currentNets.empty() || currentNets[0] == nullptr )
539 return false;
540
541 // Need to have something unconnected to finish to
542 NETINFO_ITEM* netInfo = static_cast<NETINFO_ITEM*>( currentNets[0] );
543 int currentNet = netInfo->GetNetCode();
545 RN_NET* ratsnest = board->GetConnectivity()->GetRatsnestForNet( currentNet );
546
547 return ratsnest && !ratsnest->GetEdges().empty();
548 };
549
551 menu.AddSeparator( 1 );
552
553 menu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 2 );
554 menu.AddSeparator( haveHighlight, 2 );
555
556 menu.AddItem( PCB_ACTIONS::routeSingleTrack, notRoutingCond );
557 menu.AddItem( PCB_ACTIONS::routeDiffPair, notRoutingCond );
560 menu.AddItem( PCB_ACTIONS::routerContinueFromEnd, hasOtherEnd );
561 menu.AddItem( PCB_ACTIONS::routerAttemptFinish, hasOtherEnd );
562 menu.AddItem( PCB_ACTIONS::routerAutorouteSelected, notRoutingCond
564 menu.AddItem( PCB_ACTIONS::breakTrack, notRoutingCond );
565
566 menu.AddItem( PCB_ACTIONS::drag45Degree, notRoutingCond );
567 menu.AddItem( PCB_ACTIONS::dragFreeAngle, notRoutingCond );
568
576
577 // Add submenu for track corner mode handling
578 CONDITIONAL_MENU* submenuCornerMode = new CONDITIONAL_MENU( this );
579 submenuCornerMode->SetTitle( _( "Track Corner Mode" ) );
581
583 submenuCornerMode->AddSeparator( 1 );
588
589 menu.AddMenu( submenuCornerMode );
590
591 // Manage check/uncheck marks in this submenu items
592 auto cornerMode45Cond =
593 [this]( const SELECTION& )
594 {
595 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::MITERED_45;
596 };
597
598 auto cornerMode90Cond =
599 [this]( const SELECTION& )
600 {
601 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::MITERED_90;
602 };
603
604 auto cornerModeArc45Cond =
605 [this]( const SELECTION& )
606 {
607 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::ROUNDED_45;
608 };
609
610 auto cornerModeArc90Cond =
611 [this]( const SELECTION& )
612 {
613 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::ROUNDED_90;
614 };
615
616#define CHECK( x ) ACTION_CONDITIONS().Check( x )
617 mgr->SetConditions( ACT_SwitchCornerMode45, CHECK( cornerMode45Cond ) );
618 mgr->SetConditions( ACT_SwitchCornerMode90, CHECK( cornerMode90Cond ) );
619 mgr->SetConditions( ACT_SwitchCornerModeArc45, CHECK( cornerModeArc45Cond ) );
620 mgr->SetConditions( ACT_SwitchCornerModeArc90, CHECK( cornerModeArc90Cond ) );
621
622 auto diffPairCond =
623 [this]( const SELECTION& )
624 {
625 return m_router->Mode() == PNS::PNS_MODE_ROUTE_DIFF_PAIR;
626 };
627
628 menu.AddSeparator();
629
631 menu.AddMenu( m_diffPairMenu.get(), diffPairCond );
632
634
635 menu.AddSeparator();
636
637 frame->AddStandardSubMenus( *m_menu.get() );
638
639 return true;
640}
641
642
644{
645 if( aReason == RUN )
646 TOOL_BASE::Reset( aReason );
647}
648
649// Saves the complete event log and the dump of the PCB, allowing us to
650// recreate hard-to-find P&S quirks and bugs.
651
653{
654 static wxString mruPath = PATHS::GetDefaultUserProjectsPath();
655 static size_t lastLoggerSize = 0;
656
657 auto logger = m_router->Logger();
658
659 if( !logger || logger->GetEvents().size() == 0
660 || logger->GetEvents().size() == lastLoggerSize )
661 {
662 return;
663 }
664
665 wxFileDialog dlg( frame(), _( "Save router log" ), mruPath, "pns.log",
666 "PNS log files" + AddFileExtListToFilter( { "log" } ),
667 wxFD_OVERWRITE_PROMPT | wxFD_SAVE );
668
669 if( dlg.ShowModal() != wxID_OK )
670 {
671 lastLoggerSize = logger->GetEvents().size(); // prevent re-entry
672 return;
673 }
674
675 wxFileName fname_log( dlg.GetPath() );
676 mruPath = fname_log.GetPath();
677
678 wxFileName fname_dump( fname_log );
679 fname_dump.SetExt( "dump" );
680
681 wxFileName fname_settings( fname_log );
682 fname_settings.SetExt( "settings" );
683
684 FILE* settings_f = wxFopen( fname_settings.GetAbsolutePath(), "wb" );
685 std::string settingsStr = m_router->Settings().FormatAsString();
686 fprintf( settings_f, "%s\n", settingsStr.c_str() );
687 fclose( settings_f );
688
689 // Export as *.kicad_pcb format, using a strategy which is specifically chosen
690 // as an example on how it could also be used to send it to the system clipboard.
691
692 PCB_IO_KICAD_SEXPR pcb_io;
693
694 pcb_io.SaveBoard( fname_dump.GetAbsolutePath(), m_iface->GetBoard(), nullptr );
695
696 PROJECT* prj = m_iface->GetBoard()->GetProject();
697 prj->GetProjectFile().SaveAs( fname_dump.GetPath(), fname_dump.GetName() );
698 prj->GetLocalSettings().SaveAs( fname_dump.GetPath(), fname_dump.GetName() );
699
700 // Build log file:
701 std::vector<PNS::ITEM*> added, removed, heads;
702 m_router->GetUpdatedItems( removed, added, heads );
703
704 std::set<KIID> removedKIIDs;
705
706 for( auto item : removed )
707 {
708 wxASSERT_MSG( item->Parent() != nullptr, "removed an item with no parent uuid?" );
709
710 if( item->Parent() )
711 removedKIIDs.insert( item->Parent()->m_Uuid );
712 }
713
714 FILE* log_f = wxFopen( fname_log.GetAbsolutePath(), "wb" );
715 wxString logString = PNS::LOGGER::FormatLogFileAsString( m_router->Mode(),
716 added, removedKIIDs, heads,
717 logger->GetEvents() );
718
719 if( !log_f )
720 {
721 DisplayError( frame(), wxString::Format( _( "Unable to write '%s'." ),
722 fname_log.GetAbsolutePath() ) );
723 return;
724 }
725
726 fprintf( log_f, "%s\n", logString.c_str().AsChar() );
727 fclose( log_f );
728
729 logger->Clear(); // prevent re-entry
730 lastLoggerSize = 0;
731}
732
733
735{
736 if( aEvent.Category() == TC_VIEW || aEvent.Category() == TC_MOUSE )
737 {
738 BOX2D viewAreaD = getView()->GetGAL()->GetVisibleWorldExtents();
739 m_router->SetVisibleViewArea( BOX2ISafe( viewAreaD ) );
740 }
741
742 if( !ADVANCED_CFG::GetCfg().m_EnableRouterDump )
743 return;
744
745 if( !aEvent.IsKeyPressed() )
746 return;
747
748 switch( aEvent.KeyCode() )
749 {
750 case '0':
752 aEvent.SetPassEvent( false );
753 break;
754
755 default:
756 break;
757 }
758}
759
761{
762 bool asChanged = false;
763
764 if( aEvent.IsAction( &ACT_SwitchCornerModeToNext ) )
765 {
766 DIRECTION_45::CORNER_MODE curr_mode = m_router->Settings().GetCornerMode();
767
769 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_45 );
770 else if( curr_mode == DIRECTION_45::CORNER_MODE::ROUNDED_45 )
771 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_90 );
772 else if( curr_mode == DIRECTION_45::CORNER_MODE::MITERED_90 )
773 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_90 );
774 else if( curr_mode == DIRECTION_45::CORNER_MODE::ROUNDED_90 )
775 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_45 );
776
777 asChanged = true;
778 }
779 else if( aEvent.IsAction( &ACT_SwitchCornerMode45 ) )
780 {
781 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_45 );
782 asChanged = true;
783 }
784 else if( aEvent.IsAction( &ACT_SwitchCornerModeArc45 ) )
785 {
786 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_45 );
787 asChanged = true;
788 }
789 else if( aEvent.IsAction( &ACT_SwitchCornerMode90 ) )
790 {
791 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_90 );
792 asChanged = true;
793 }
794 else if( aEvent.IsAction( &ACT_SwitchCornerModeArc90 ) )
795 {
796 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_90 );
797 asChanged = true;
798 }
799
800 if( asChanged )
801 {
803 updateEndItem( aEvent );
804 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
805 }
806
807 return 0;
808}
809
810
812{
813 PCB_LAYER_ID tl = static_cast<PCB_LAYER_ID>( getView()->GetTopLayer() );
814
815 if( m_startItem )
816 {
817 int startLayer = m_iface->GetPNSLayerFromBoardLayer( tl );
818 const PNS_LAYER_RANGE& ls = m_startItem->Layers();
819
820 if( ls.Overlaps( startLayer ) )
821 return tl;
822 else
823 return m_iface->GetBoardLayerFromPNSLayer( ls.Start() );
824 }
825
826 return tl;
827}
828
829
831{
832 int activeLayer = m_iface->GetPNSLayerFromBoardLayer( frame()->GetActiveLayer() );
833 int currentLayer = m_router->GetCurrentLayer();
834
835 if( currentLayer != activeLayer )
836 m_router->SwitchLayer( activeLayer );
837
838 std::optional<int> newLayer = m_router->Sizes().PairedLayer( currentLayer );
839
840 if( !newLayer )
841 newLayer = m_router->Sizes().GetLayerTop();
842
843 m_router->SwitchLayer( *newLayer );
844 m_lastTargetLayer = m_iface->GetBoardLayerFromPNSLayer( *newLayer );
845
848}
849
850
851// N.B. aTargetLayer is a PNS layer, not a PCB_LAYER_ID
852void ROUTER_TOOL::updateSizesAfterRouterEvent( int aTargetLayer, const VECTOR2I& aPos )
853{
854 std::vector<PNS::NET_HANDLE> nets = m_router->GetCurrentNets();
855
856 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
858 std::shared_ptr<DRC_ENGINE>& drcEngine = bds.m_DRCEngine;
859 DRC_CONSTRAINT constraint;
860 PCB_LAYER_ID targetLayer = m_iface->GetBoardLayerFromPNSLayer( aTargetLayer );
861
862 PCB_TRACK dummyTrack( board() );
863 dummyTrack.SetFlags( ROUTER_TRANSIENT );
864 dummyTrack.SetLayer( targetLayer );
865 dummyTrack.SetNet( nets.empty() ? nullptr: static_cast<NETINFO_ITEM*>( nets[0] ) );
866 dummyTrack.SetStart( aPos );
867 dummyTrack.SetEnd( dummyTrack.GetStart() );
868
869 constraint = drcEngine->EvalRules( CLEARANCE_CONSTRAINT, &dummyTrack, nullptr, targetLayer );
870
871 if( constraint.m_Value.Min() >= bds.m_MinClearance )
872 {
873 sizes.SetClearance( constraint.m_Value.Min() );
874 sizes.SetClearanceSource( constraint.GetName() );
875 }
876 else
877 {
878 sizes.SetClearance( bds.m_MinClearance );
879 sizes.SetClearanceSource( _( "board minimum clearance" ) );
880 }
881
882 if( bds.UseNetClassTrack() || !sizes.TrackWidthIsExplicit() )
883 {
884 constraint = drcEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, &dummyTrack, nullptr,
885 targetLayer );
886
887 if( !constraint.IsNull() )
888 {
889 int width = sizes.TrackWidth();
890
891 // Only change the size if we're explicitly using the net class, or we're out of range
892 // for our new constraints. Otherwise, just leave the track width alone so we don't
893 // change for no reason.
894 if( bds.UseNetClassTrack()
895 || ( width < bds.m_TrackMinWidth )
896 || ( width < constraint.m_Value.Min() )
897 || ( width > constraint.m_Value.Max() ) )
898 {
899 sizes.SetTrackWidth( std::max( bds.m_TrackMinWidth, constraint.m_Value.Opt() ) );
900 }
901
902 if( sizes.TrackWidth() == constraint.m_Value.Opt() )
903 sizes.SetWidthSource( constraint.GetName() );
904 else if( sizes.TrackWidth() == bds.m_TrackMinWidth )
905 sizes.SetWidthSource( _( "board minimum track width" ) );
906 else
907 sizes.SetWidthSource( _( "existing track" ) );
908 }
909 }
910
911 if( nets.size() >= 2 && ( bds.UseNetClassDiffPair() || !sizes.TrackWidthIsExplicit() ) )
912 {
913 PCB_TRACK dummyTrackB( board() );
914 dummyTrackB.SetFlags( ROUTER_TRANSIENT );
915 dummyTrackB.SetLayer( targetLayer );
916 dummyTrackB.SetNet( static_cast<NETINFO_ITEM*>( nets[1] ) );
917 dummyTrackB.SetStart( aPos );
918 dummyTrackB.SetEnd( dummyTrackB.GetStart() );
919
920 constraint = drcEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, &dummyTrack, &dummyTrackB,
921 targetLayer );
922
923 if( !constraint.IsNull() )
924 {
925 if( bds.UseNetClassDiffPair()
926 || ( sizes.DiffPairWidth() < bds.m_TrackMinWidth )
927 || ( sizes.DiffPairWidth() < constraint.m_Value.Min() )
928 || ( sizes.DiffPairWidth() > constraint.m_Value.Max() ) )
929 {
930 sizes.SetDiffPairWidth( std::max( bds.m_TrackMinWidth, constraint.m_Value.Opt() ) );
931 }
932
933 if( sizes.DiffPairWidth() == constraint.m_Value.Opt() )
934 sizes.SetDiffPairWidthSource( constraint.GetName() );
935 else
936 sizes.SetDiffPairWidthSource( _( "board minimum track width" ) );
937 }
938
939 constraint = drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, &dummyTrack, &dummyTrackB,
940 targetLayer );
941
942 if( !constraint.IsNull() )
943 {
944 if( bds.UseNetClassDiffPair()
945 || ( sizes.DiffPairGap() < bds.m_MinClearance )
946 || ( sizes.DiffPairGap() < constraint.m_Value.Min() )
947 || ( sizes.DiffPairGap() > constraint.m_Value.Max() ) )
948 {
949 sizes.SetDiffPairGap( std::max( bds.m_MinClearance, constraint.m_Value.Opt() ) );
950 }
951
952 if( sizes.DiffPairGap() == constraint.m_Value.Opt() )
953 sizes.SetDiffPairGapSource( constraint.GetName() );
954 else
955 sizes.SetDiffPairGapSource( _( "board minimum clearance" ) );
956 }
957 }
958
959 m_router->UpdateSizes( sizes );
960}
961
962
963static VIATYPE getViaTypeFromFlags( int aFlags )
964{
965 switch( aFlags & VIA_ACTION_FLAGS::VIA_MASK )
966 {
968 return VIATYPE::THROUGH;
970 return VIATYPE::BLIND;
972 return VIATYPE::BURIED;
974 return VIATYPE::MICROVIA;
975 default:
976 wxASSERT_MSG( false, wxT( "Unhandled via type" ) );
977 return VIATYPE::THROUGH;
978 }
979}
980
981
983{
984 handleLayerSwitch( aEvent, false );
986
987 return 0;
988}
989
990
992{
993 if( !m_router->IsPlacingVia() )
994 {
995 return handleLayerSwitch( aEvent, true );
996 }
997 else
998 {
999 m_router->ToggleViaPlacement();
1000 frame()->SetActiveLayer(
1001 m_iface->GetBoardLayerFromPNSLayer( m_router->GetCurrentLayer() ) );
1002 updateEndItem( aEvent );
1004 }
1005
1007 return 0;
1008}
1009
1010
1011int ROUTER_TOOL::handleLayerSwitch( const TOOL_EVENT& aEvent, bool aForceVia )
1012{
1013 wxCHECK( m_router, 0 );
1014
1015 if( !IsToolActive() )
1016 return 0;
1017
1018 // Ensure PNS_KICAD_IFACE (m_iface) m_board member is up to date
1019 // For some reason, this is not always the case
1020 m_iface->SetBoard( board() );
1021
1022 // First see if this is one of the switch layer commands
1023 BOARD* brd = board();
1024 LSET enabledLayers = LSET::AllCuMask( brd->GetDesignSettings().GetCopperLayerCount() );
1025 LSEQ layers = enabledLayers.UIOrder();
1026
1027 // These layers are in Board Layer UI order not PNS layer order
1028 PCB_LAYER_ID currentLayer = m_iface->GetBoardLayerFromPNSLayer( m_router->GetCurrentLayer() );
1029 PCB_LAYER_ID targetLayer = UNDEFINED_LAYER;
1030
1031 if( aEvent.IsAction( &PCB_ACTIONS::layerNext ) )
1032 {
1033 size_t idx = 0;
1034 size_t target_idx = 0;
1035 PCB_LAYER_ID lastTargetLayer = m_lastTargetLayer;
1036
1037 for( size_t i = 0; i < layers.size(); i++ )
1038 {
1039 if( layers[i] == currentLayer )
1040 {
1041 idx = i;
1042 break;
1043 }
1044 }
1045
1046 target_idx = ( idx + 1 ) % layers.size();
1047 // issue: #14480
1048 // idx + 1 layer may be invisible, switches to next visible layer
1049 for( size_t i = 0; i < layers.size() - 1; i++ )
1050 {
1051 if( brd->IsLayerVisible( layers[target_idx] ) )
1052 {
1053 targetLayer = layers[target_idx];
1054 break;
1055 }
1056 target_idx += 1;
1057
1058 if( target_idx >= layers.size() )
1059 {
1060 target_idx = 0;
1061 }
1062 }
1063
1064 if( targetLayer == UNDEFINED_LAYER )
1065 {
1066 // if there is no visible layers
1067 return 0;
1068 }
1069 }
1070 else if( aEvent.IsAction( &PCB_ACTIONS::layerPrev ) )
1071 {
1072 size_t idx = 0;
1073 size_t target_idx = 0;
1074
1075 for( size_t i = 0; i < layers.size(); i++ )
1076 {
1077 if( layers[i] == currentLayer )
1078 {
1079 idx = i;
1080 break;
1081 }
1082 }
1083
1084 target_idx = ( idx > 0 ) ? ( idx - 1 ) : ( layers.size() - 1 );
1085
1086 for( size_t i = 0; i < layers.size() - 1; i++ )
1087 {
1088 if( brd->IsLayerVisible( layers[target_idx] ) )
1089 {
1090 targetLayer = layers[target_idx];
1091 break;
1092 }
1093
1094 if( target_idx > 0 )
1095 target_idx -= 1;
1096 else
1097 target_idx = layers.size() - 1;
1098 }
1099
1100 if( targetLayer == UNDEFINED_LAYER )
1101 {
1102 // if there is no visible layers
1103 return 0;
1104 }
1105 }
1106 else if( aEvent.IsAction( &PCB_ACTIONS::layerToggle ) )
1107 {
1108 PCB_SCREEN* screen = frame()->GetScreen();
1109
1110 if( currentLayer == screen->m_Route_Layer_TOP )
1111 targetLayer = screen->m_Route_Layer_BOTTOM;
1112 else
1113 targetLayer = screen->m_Route_Layer_TOP;
1114 }
1116 {
1117 targetLayer = aEvent.Parameter<PCB_LAYER_ID>();
1118
1119 if( !enabledLayers.test( targetLayer ) )
1120 return 0;
1121 }
1122
1123 if( targetLayer != UNDEFINED_LAYER )
1124 {
1125 if( targetLayer == currentLayer )
1126 return 0;
1127
1128 if( !aForceVia && m_router && m_router->SwitchLayer( m_iface->GetPNSLayerFromBoardLayer( targetLayer ) ) )
1129 {
1130 updateEndItem( aEvent );
1131 updateSizesAfterRouterEvent( m_iface->GetPNSLayerFromBoardLayer( targetLayer ), m_endSnapPoint );
1132 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
1133 return 0;
1134 }
1135 }
1136
1138 const int layerCount = bds.GetCopperLayerCount();
1139
1140 PCB_LAYER_ID pairTop = frame()->GetScreen()->m_Route_Layer_TOP;
1141 PCB_LAYER_ID pairBottom = frame()->GetScreen()->m_Route_Layer_BOTTOM;
1142
1143 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
1144
1145 VIATYPE viaType = VIATYPE::THROUGH;
1146 bool selectLayer = false;
1147
1148 // Otherwise it is one of the router-specific via commands
1149 if( targetLayer == UNDEFINED_LAYER )
1150 {
1151 const int actViaFlags = aEvent.Parameter<int>();
1152 selectLayer = actViaFlags & VIA_ACTION_FLAGS::SELECT_LAYER;
1153
1154 viaType = getViaTypeFromFlags( actViaFlags );
1155
1156 // ask the user for a target layer
1157 if( selectLayer )
1158 {
1159 // When the currentLayer is undefined, trying to place a via does not work
1160 // because it means there is no track in progress, and some other variables
1161 // values are not defined like m_endSnapPoint. So do not continue.
1162 if( currentLayer == UNDEFINED_LAYER )
1163 return 0;
1164
1165 wxPoint endPoint = ToWxPoint( view()->ToScreen( m_endSnapPoint ) );
1166 endPoint = frame()->GetCanvas()->ClientToScreen( endPoint );
1167
1168 // Build the list of not allowed layer for the target layer
1169 LSET not_allowed_ly = LSET::AllNonCuMask();
1170
1171 if( viaType != VIATYPE::THROUGH )
1172 not_allowed_ly.set( currentLayer );
1173
1174 targetLayer = frame()->SelectOneLayer( static_cast<PCB_LAYER_ID>( currentLayer ),
1175 not_allowed_ly, endPoint );
1176
1177 // Reset the cursor to the end of the track
1179
1180 if( targetLayer == UNDEFINED_LAYER ) // canceled by user
1181 return 0;
1182
1183 // One cannot place a blind/buried via on only one layer:
1184 if( viaType != VIATYPE::THROUGH )
1185 {
1186 if( currentLayer == targetLayer )
1187 return 0;
1188 }
1189 }
1190 }
1191
1192 // fixme: P&S supports more than one fixed layer pair. Update the dialog?
1193 sizes.ClearLayerPairs();
1194
1195 // Convert blind/buried via to a through hole one, if it goes through all layers
1196 if( viaType != VIATYPE::THROUGH
1197 && ( ( targetLayer == B_Cu && currentLayer == F_Cu )
1198 || ( targetLayer == F_Cu && currentLayer == B_Cu ) ) )
1199 {
1200 viaType = VIATYPE::THROUGH;
1201 }
1202
1203 if( targetLayer == UNDEFINED_LAYER )
1204 {
1205 // Implicit layer selection
1206 if( viaType == VIATYPE::THROUGH )
1207 {
1208 // Try to switch to the nearest ratnest item's layer if we have one
1209 VECTOR2I otherEnd;
1210 PNS_LAYER_RANGE otherEndLayers;
1211 PNS::ITEM* otherEndItem = nullptr;
1212
1213 if( !m_router->GetNearestRatnestAnchor( otherEnd, otherEndLayers, otherEndItem ) )
1214 {
1215 // use the default layer pair
1216 currentLayer = pairTop;
1217 targetLayer = pairBottom;
1218 }
1219 else
1220 {
1221 // use the layer of the other end, unless it is the same layer as the currently active layer, in which
1222 // case use the layer pair (if applicable)
1223 PCB_LAYER_ID otherEndLayerPcbId = m_iface->GetBoardLayerFromPNSLayer( otherEndLayers.Start() );
1224 const std::optional<int> pairedLayerPns = m_router->Sizes().PairedLayer( m_router->GetCurrentLayer() );
1225
1226 if( currentLayer == otherEndLayerPcbId && pairedLayerPns.has_value() )
1227 {
1228 // Closest ratsnest layer is the same as the active layer - assume the via is being placed for
1229 // other routing reasons and switch the layer
1230 targetLayer = m_iface->GetBoardLayerFromPNSLayer( *pairedLayerPns );
1231 }
1232 else
1233 {
1234 targetLayer = m_iface->GetBoardLayerFromPNSLayer( otherEndLayers.Start() );
1235 }
1236 }
1237 }
1238 else
1239 {
1240 if( currentLayer == pairTop || currentLayer == pairBottom )
1241 {
1242 // the current layer is on the defined layer pair,
1243 // swap to the other side
1244 currentLayer = pairTop;
1245 targetLayer = pairBottom;
1246 }
1247 else
1248 {
1249 // the current layer is not part of the current layer pair,
1250 // so fallback and swap to the top layer of the pair by default
1251 targetLayer = pairTop;
1252 }
1253
1254 // Do not create a broken via (i.e. a via on only one copper layer)
1255 if( currentLayer == targetLayer )
1256 {
1257 WX_INFOBAR* infobar = frame()->GetInfoBar();
1258 infobar->ShowMessageFor( _( "Via needs 2 different layers." ),
1259 2000, wxICON_ERROR,
1261 return 0;
1262 }
1263 }
1264 }
1265
1266 sizes.SetViaDiameter( bds.m_ViasMinSize );
1267 sizes.SetViaDrill( bds.m_MinThroughDrill );
1268
1269 if( bds.UseNetClassVia() || viaType == VIATYPE::MICROVIA )
1270 {
1271 PCB_VIA dummyVia( board() );
1272 dummyVia.SetViaType( viaType );
1273 dummyVia.SetLayerPair( currentLayer, targetLayer );
1274
1275 if( !m_router->GetCurrentNets().empty() )
1276 dummyVia.SetNet( static_cast<NETINFO_ITEM*>( m_router->GetCurrentNets()[0] ) );
1277
1278 DRC_CONSTRAINT constraint;
1279
1280 constraint = bds.m_DRCEngine->EvalRules( VIA_DIAMETER_CONSTRAINT, &dummyVia, nullptr,
1281 currentLayer );
1282
1283 if( !constraint.IsNull() )
1284 sizes.SetViaDiameter( constraint.m_Value.Opt() );
1285
1286 constraint = bds.m_DRCEngine->EvalRules( HOLE_SIZE_CONSTRAINT, &dummyVia, nullptr,
1287 currentLayer );
1288
1289 if( !constraint.IsNull() )
1290 sizes.SetViaDrill( constraint.m_Value.Opt() );
1291 }
1292 else
1293 {
1294 sizes.SetViaDiameter( bds.GetCurrentViaSize() );
1295 sizes.SetViaDrill( bds.GetCurrentViaDrill() );
1296 }
1297
1298 sizes.SetViaType( viaType );
1299 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( currentLayer ),
1300 m_iface->GetPNSLayerFromBoardLayer( targetLayer ) );
1301
1302 m_router->UpdateSizes( sizes );
1303
1304 if( !m_router->IsPlacingVia() )
1305 m_router->ToggleViaPlacement();
1306
1307 if( m_router->RoutingInProgress() )
1308 {
1309 updateEndItem( aEvent );
1311 }
1312 else
1313 {
1314 updateStartItem( aEvent );
1315 }
1316
1317 return 0;
1318}
1319
1320
1322{
1325 int pnsLayer = m_iface->GetPNSLayerFromBoardLayer( pcbLayer );
1326
1327 if( !::IsCopperLayer( pcbLayer ) )
1328 {
1329 editFrame->ShowInfoBarError( _( "Tracks on Copper layers only." ) );
1330 return false;
1331 }
1332
1334 editFrame->SetActiveLayer( pcbLayer );
1335
1336 if( !getView()->IsLayerVisible( pcbLayer ) )
1337 {
1338 editFrame->GetAppearancePanel()->SetLayerVisible( pcbLayer, true );
1339 editFrame->GetCanvas()->Refresh();
1340 }
1341
1342 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
1343
1344 m_iface->SetStartLayerFromPCBNew( pcbLayer );
1345
1346 frame()->GetBoard()->GetDesignSettings().m_TempOverrideTrackWidth = false;
1347 m_iface->ImportSizes( sizes, m_startItem, nullptr, aStartPosition );
1348 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( frame()->GetScreen()->m_Route_Layer_TOP ),
1349 m_iface->GetPNSLayerFromBoardLayer( frame()->GetScreen()->m_Route_Layer_BOTTOM ) );
1350
1351 m_router->UpdateSizes( sizes );
1352
1353 if( m_startItem && m_startItem->Net() )
1354 {
1356 {
1357 if( PNS::NET_HANDLE coupledNet = m_router->GetRuleResolver()->DpCoupledNet( m_startItem->Net() ) )
1358 highlightNets( true, { m_startItem->Net(), coupledNet } );
1359 }
1360 else
1361 {
1362 highlightNets( true, { m_startItem->Net() } );
1363 }
1364 }
1365
1366 controls()->SetAutoPan( true );
1367
1368 if( !m_router->StartRouting( m_startSnapPoint, m_startItem, pnsLayer ) )
1369 {
1370 // It would make more sense to leave the net highlighted as the higher-contrast mode
1371 // makes the router clearances more visible. However, since we just started routing
1372 // the conversion of the screen from low contrast to high contrast is a bit jarring and
1373 // makes the infobar coming up less noticeable.
1374 highlightNets( false );
1375
1376 frame()->ShowInfoBarError( m_router->FailureReason(), true,
1377 [&]()
1378 {
1379 m_router->ClearViewDecorations();
1380 } );
1381
1382 controls()->SetAutoPan( false );
1383 return false;
1384 }
1385
1386 m_endItem = nullptr;
1388
1390 frame()->UndoRedoBlock( true );
1391
1392 return true;
1393}
1394
1395
1397{
1398 m_router->StopRouting();
1399
1400 m_startItem = nullptr;
1401 m_endItem = nullptr;
1402
1403 frame()->SetActiveLayer( m_originalActiveLayer );
1405 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1406 controls()->SetAutoPan( false );
1407 controls()->ForceCursorPosition( false );
1408 frame()->UndoRedoBlock( false );
1409 highlightNets( false );
1410
1411 return true;
1412}
1413
1414
1416{
1417 m_router->ClearViewDecorations();
1418
1419 if( !prepareInteractive( aStartPosition ) )
1420 return;
1421
1422 auto setCursor =
1423 [&]()
1424 {
1425 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1426 };
1427
1428 auto syncRouterAndFrameLayer =
1429 [&]()
1430 {
1431 int pnsLayer = m_router->GetCurrentLayer();
1432 PCB_LAYER_ID pcbLayer = m_iface->GetBoardLayerFromPNSLayer( pnsLayer );
1434
1435 editFrame->SetActiveLayer( pcbLayer );
1436
1437 if( !getView()->IsLayerVisible( pcbLayer ) )
1438 {
1439 editFrame->GetAppearancePanel()->SetLayerVisible( pcbLayer, true );
1440 editFrame->GetCanvas()->Refresh();
1441 }
1442 };
1443
1444 // Set initial cursor
1445 setCursor();
1446
1447 while( TOOL_EVENT* evt = Wait() )
1448 {
1449 setCursor();
1450
1451 // Don't crash if we missed an operation that canceled routing.
1452 if( !m_router->RoutingInProgress() )
1453 {
1454 if( evt->IsCancelInteractive() )
1455 m_cancelled = true;
1456
1457 break;
1458 }
1459
1460 handleCommonEvents( *evt );
1461
1462 if( evt->IsMotion() )
1463 {
1464 updateEndItem( *evt );
1466 }
1467 else if( evt->IsAction( &PCB_ACTIONS::routerUndoLastSegment )
1468 || evt->IsAction( &ACTIONS::doDelete )
1469 || evt->IsAction( &ACTIONS::undo ) )
1470 {
1471 if( std::optional<VECTOR2I> last = m_router->UndoLastSegment() )
1472 {
1473 getViewControls()->WarpMouseCursor( last.value(), true );
1474 evt->SetMousePosition( last.value() );
1475 }
1476
1477 updateEndItem( *evt );
1479 }
1480 else if( evt->IsAction( &PCB_ACTIONS::routerAttemptFinish ) )
1481 {
1482 if( m_toolMgr->IsContextMenuActive() )
1483 m_toolMgr->WarpAfterContextMenu();
1484
1485 bool* autoRouted = evt->Parameter<bool*>();
1486
1487 if( m_router->Finish() )
1488 {
1489 // When we're routing a group of signals automatically we want
1490 // to break up the undo stack every time we have to manually route
1491 // so the user gets nice checkpoints. Remove the APPEND_UNDO flag.
1492 if( autoRouted != nullptr )
1493 *autoRouted = true;
1494
1495 break;
1496 }
1497 else
1498 {
1499 // This acts as check if we were called by the autorouter; we don't want
1500 // to reset APPEND_UNDO if we're auto finishing after route-other-end
1501 if( autoRouted != nullptr )
1502 {
1503 *autoRouted = false;
1504 m_iface->SetCommitFlags( 0 );
1505 }
1506
1507 // Warp the mouse so the user is at the point we managed to route to
1508 controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true );
1509 }
1510 }
1511 else if( evt->IsAction( &PCB_ACTIONS::routerContinueFromEnd ) )
1512 {
1513 bool needsAppend = m_router->Placer()->HasPlacedAnything();
1514
1515 if( m_router->ContinueFromEnd( &m_startItem ) )
1516 {
1517 syncRouterAndFrameLayer();
1518 m_startSnapPoint = m_router->Placer()->CurrentStart();
1519 updateEndItem( *evt );
1520
1521 // Warp the mouse to wherever we actually ended up routing to
1522 controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true );
1523
1524 // We want the next router commit to be one undo at the UI layer
1525 m_iface->SetCommitFlags( needsAppend ? APPEND_UNDO : 0 );
1526 }
1527 else
1528 {
1529 frame()->ShowInfoBarError( m_router->FailureReason(), true );
1530 }
1531 }
1532 else if( evt->IsClick( BUT_LEFT )
1533 || evt->IsDrag( BUT_LEFT )
1534 || evt->IsAction( &PCB_ACTIONS::routeSingleTrack ) )
1535 {
1536 updateEndItem( *evt );
1537 bool needLayerSwitch = m_router->IsPlacingVia();
1538 bool forceFinish = evt->Modifier( MD_SHIFT );
1539 bool forceCommit = false;
1540
1541 if( m_router->FixRoute( m_endSnapPoint, m_endItem, false, forceCommit ) )
1542 break;
1543
1544 if( needLayerSwitch )
1545 {
1547 }
1548 else
1549 {
1551 }
1552
1553 // Synchronize the indicated layer
1554 syncRouterAndFrameLayer();
1555
1556 updateEndItem( *evt );
1558 m_startItem = nullptr;
1559 }
1560 else if( evt->IsAction( &ACT_SwitchPosture ) )
1561 {
1562 m_router->FlipPosture();
1563 updateEndItem( *evt );
1564 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
1565 }
1566 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
1567 {
1568 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1569 controls()->SetAutoPan( false );
1570 {
1571 m_toolMgr->RunAction( ACT_CustomTrackWidth );
1572 }
1573 controls()->SetAutoPan( true );
1574 setCursor();
1576 }
1577 else if( evt->IsAction( &ACTIONS::finishInteractive ) || evt->IsDblClick( BUT_LEFT ) )
1578 {
1579 // Stop current routing:
1580 bool forceFinish = true;
1581 bool forceCommit = false;
1582
1583 m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit );
1584 break;
1585 }
1586 else if( evt->IsCancelInteractive() || evt->IsActivate()
1587 || evt->IsAction( &PCB_ACTIONS::routerInlineDrag ) )
1588 {
1589 if( evt->IsCancelInteractive() && !m_router->RoutingInProgress() )
1590 m_cancelled = true;
1591
1592 if( evt->IsActivate() && !evt->IsMoveTool() )
1593 m_cancelled = true;
1594
1595 break;
1596 }
1597 else if( evt->IsUndoRedo() )
1598 {
1599 // We're in an UndoRedoBlock. If we get here, something's broken.
1600 wxFAIL;
1601 break;
1602 }
1603 else if( evt->IsClick( BUT_RIGHT ) )
1604 {
1605 m_menu->ShowContextMenu( selection() );
1606 }
1607 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
1608 // but we don't at present have that, so we just knock out some of the egregious ones.
1609 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
1610 {
1611 wxBell();
1612 }
1613 else
1614 {
1615 evt->SetPassEvent();
1616 }
1617 }
1618
1619 m_router->CommitRouting();
1620 // Reset to normal for next route
1621 m_iface->SetCommitFlags( 0 );
1622
1624}
1625
1626
1628{
1629 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
1630 DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( frame(), sizes );
1631
1632 if( settingsDlg.ShowModal() == wxID_OK )
1633 {
1634 m_router->UpdateSizes( sizes );
1635 m_savedSizes = sizes;
1636
1637 BOARD_DESIGN_SETTINGS& bds = frame()->GetBoard()->GetDesignSettings();
1639 bds.SetCustomDiffPairGap( sizes.DiffPairGap() );
1641 }
1642
1643 return 0;
1644}
1645
1646
1648{
1649 DIALOG_PNS_SETTINGS settingsDlg( frame(), m_router->Settings() );
1650
1651 settingsDlg.ShowModal();
1652
1654
1655 return 0;
1656}
1657
1658
1660{
1661 PNS::PNS_MODE mode = aEvent.Parameter<PNS::PNS_MODE>();
1662 PNS::ROUTING_SETTINGS& settings = m_router->Settings();
1663
1664 settings.SetMode( mode );
1666
1667 return 0;
1668}
1669
1670
1672{
1673 PNS::ROUTING_SETTINGS& settings = m_router->Settings();
1674 PNS::PNS_MODE mode = settings.Mode();
1675
1676 switch( mode )
1677 {
1678 case PNS::RM_MarkObstacles: mode = PNS::RM_Shove; break;
1679 case PNS::RM_Shove: mode = PNS::RM_Walkaround; break;
1680 case PNS::RM_Walkaround: mode = PNS::RM_MarkObstacles; break;
1681 }
1682
1683 settings.SetMode( mode );
1685
1686 return 0;
1687}
1688
1689
1691{
1692 return m_router->Settings().Mode();
1693}
1694
1695
1697{
1698 return m_router->RoutingInProgress();
1699}
1700
1701
1703{
1704 if( !m_startItem )
1705 return;
1706
1708 m_router->BreakSegmentOrArc( m_startItem, m_startSnapPoint );
1709}
1710
1711
1713{
1717 PCB_LAYER_ID originalLayer = frame->GetActiveLayer();
1718 bool autoRoute = aEvent.Matches( PCB_ACTIONS::routerAutorouteSelected.MakeEvent() );
1719 bool otherEnd = aEvent.Matches( PCB_ACTIONS::routerRouteSelectedFromEnd.MakeEvent() );
1720
1721 if( m_router->RoutingInProgress() )
1722 return 0;
1723
1724 // Save selection then clear it for interactive routing
1725 PCB_SELECTION selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
1726
1727 if( selection.Size() == 0 )
1728 return 0;
1729
1730 m_toolMgr->RunAction( ACTIONS::selectionClear );
1731
1732 TOOL_EVENT pushedEvent = aEvent;
1733 frame->PushTool( aEvent );
1734
1735 auto setCursor =
1736 [&]()
1737 {
1738 frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1739 };
1740
1741 Activate();
1742 // Must be done after Activate() so that it gets set into the correct context
1743 controls->ShowCursor( true );
1744 controls->ForceCursorPosition( false );
1745 // Set initial cursor
1746 setCursor();
1747
1748 // Get all connected board items, adding pads for any footprints selected
1749 std::vector<BOARD_CONNECTED_ITEM*> itemList;
1750
1751 for( EDA_ITEM* item : selection.GetItemsSortedBySelectionOrder() )
1752 {
1753 if( item->Type() == PCB_FOOTPRINT_T )
1754 {
1755 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
1756 itemList.push_back( pad );
1757 }
1758 else if( dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) != nullptr )
1759 {
1760 itemList.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1761 }
1762 }
1763
1764 std::shared_ptr<CONNECTIVITY_DATA> connectivity = frame->GetBoard()->GetConnectivity();
1765
1766 // For putting sequential tracks that successfully autoroute into one undo commit
1767 bool groupStart = true;
1768
1769 for( BOARD_CONNECTED_ITEM* item : itemList )
1770 {
1771 // This code is similar to GetRatsnestForPad() but it only adds the anchor for
1772 // the side of the connectivity on this pad. It also checks for ratsnest points
1773 // inside the pad (like a trace end) and counts them.
1774 RN_NET* net = connectivity->GetRatsnestForNet( item->GetNetCode() );
1775
1776 if( !net )
1777 continue;
1778
1779 std::vector<std::shared_ptr<const CN_ANCHOR>> anchors;
1780
1781 for( const CN_EDGE& edge : net->GetEdges() )
1782 {
1783 std::shared_ptr<const CN_ANCHOR> target = edge.GetTargetNode();
1784 std::shared_ptr<const CN_ANCHOR> source = edge.GetSourceNode();
1785
1786 if( !source || source->Dirty() || !target || target->Dirty() )
1787 continue;
1788
1789 if( source->Parent() == item )
1790 anchors.push_back( source );
1791 else if( target->Parent() == item )
1792 anchors.push_back( target );
1793 }
1794
1795 // Route them
1796 for( std::shared_ptr<const CN_ANCHOR> anchor : anchors )
1797 {
1798 if( !anchor->Valid() )
1799 continue;
1800
1801 // Try to return to the original layer as indicating the user's preferred
1802 // layer for autorouting tracks. The layer can be changed by the user to
1803 // finish tracks that can't complete automatically, but should be changed
1804 // back after.
1805 if( frame->GetActiveLayer() != originalLayer )
1806 frame->SetActiveLayer( originalLayer );
1807
1808 VECTOR2I ignore;
1809 m_startItem = m_router->GetWorld()->FindItemByParent( anchor->Parent() );
1810 m_startSnapPoint = anchor->Pos();
1811 m_router->SetMode( mode );
1812
1813 // Prime the interactive routing to attempt finish if we are autorouting
1814 bool autoRouted = false;
1815
1816 if( autoRoute )
1817 m_toolMgr->PostAction( PCB_ACTIONS::routerAttemptFinish, &autoRouted );
1818 else if( otherEnd )
1820
1821 // We want autorouted tracks to all be in one undo group except for
1822 // any tracks that need to be manually finished.
1823 // The undo appending for manually finished tracks is handled in peformRouting()
1824 if( groupStart )
1825 groupStart = false;
1826 else
1827 m_iface->SetCommitFlags( APPEND_UNDO );
1828
1829 // Start interactive routing. Will automatically finish if possible.
1831
1832 // Route didn't complete automatically, need to a new undo commit
1833 // for the next line so those can group as far as they autoroute
1834 if( !autoRouted )
1835 groupStart = true;
1836 }
1837 }
1838
1839 m_iface->SetCommitFlags( 0 );
1840 frame->PopTool( pushedEvent );
1841 return 0;
1842}
1843
1844
1846{
1847 if( m_inRouterTool )
1848 return 0;
1849
1851
1855
1856 if( m_router->RoutingInProgress() )
1857 {
1858 if( m_router->Mode() == mode )
1859 return 0;
1860 else
1861 m_router->StopRouting();
1862 }
1863
1864 // Deselect all items
1865 m_toolMgr->RunAction( ACTIONS::selectionClear );
1866
1867 TOOL_EVENT pushedEvent = aEvent;
1868 frame->PushTool( aEvent );
1869
1870 auto setCursor =
1871 [&]()
1872 {
1873 frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1874 };
1875
1876 Activate();
1877 // Must be done after Activate() so that it gets set into the correct context
1878 controls->ShowCursor( true );
1879 controls->ForceCursorPosition( false );
1880 // Set initial cursor
1881 setCursor();
1882
1883 m_router->SetMode( mode );
1884 m_cancelled = false;
1885
1886 if( aEvent.HasPosition() )
1887 m_toolMgr->PrimeTool( aEvent.Position() );
1888
1889 // Main loop: keep receiving events
1890 while( TOOL_EVENT* evt = Wait() )
1891 {
1892 if( !evt->IsDrag() )
1893 setCursor();
1894
1895 if( evt->IsCancelInteractive() )
1896 {
1897 frame->PopTool( pushedEvent );
1898 break;
1899 }
1900 else if( evt->IsActivate() )
1901 {
1902 if( evt->IsMoveTool() || evt->IsEditorTool() )
1903 {
1904 // leave ourselves on the stack so we come back after the move
1905 break;
1906 }
1907 else
1908 {
1909 frame->PopTool( pushedEvent );
1910 break;
1911 }
1912 }
1913 else if( evt->Action() == TA_UNDO_REDO_PRE )
1914 {
1915 m_router->ClearWorld();
1916 }
1917 else if( evt->Action() == TA_UNDO_REDO_POST || evt->Action() == TA_MODEL_CHANGE )
1918 {
1919 m_router->SyncWorld();
1920 }
1921 else if( evt->IsMotion() )
1922 {
1923 updateStartItem( *evt );
1924 }
1925 else if( evt->IsAction( &PCB_ACTIONS::dragFreeAngle ) )
1926 {
1927 updateStartItem( *evt, true );
1929 }
1930 else if( evt->IsAction( &PCB_ACTIONS::drag45Degree ) )
1931 {
1932 updateStartItem( *evt, true );
1934 }
1935 else if( evt->IsAction( &PCB_ACTIONS::breakTrack ) )
1936 {
1937 updateStartItem( *evt, true );
1938 breakTrack( );
1939 evt->SetPassEvent( false );
1940 }
1941 else if( evt->IsClick( BUT_LEFT )
1942 || evt->IsAction( &PCB_ACTIONS::routeSingleTrack )
1943 || evt->IsAction( &PCB_ACTIONS::routeDiffPair ) )
1944 {
1945 updateStartItem( *evt );
1946
1947 if( evt->HasPosition() )
1948 performRouting( evt->Position() );
1949 }
1950 else if( evt->IsAction( &ACT_PlaceThroughVia ) )
1951 {
1952 m_toolMgr->RunAction( PCB_ACTIONS::layerToggle );
1953 }
1954 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1955 {
1956 m_router->SwitchLayer( m_iface->GetPNSLayerFromBoardLayer( frame->GetActiveLayer() ) );
1957 updateStartItem( *evt );
1958 updateSizesAfterRouterEvent( m_iface->GetPNSLayerFromBoardLayer( frame->GetActiveLayer() ), m_startSnapPoint );
1959 }
1960 else if( evt->IsKeyPressed() )
1961 {
1962 // wxWidgets fails to correctly translate shifted keycodes on the wxEVT_CHAR_HOOK
1963 // event so we need to process the wxEVT_CHAR event that will follow as long as we
1964 // pass the event.
1965 evt->SetPassEvent();
1966 }
1967 else if( evt->IsClick( BUT_RIGHT ) )
1968 {
1969 m_menu->ShowContextMenu( selection() );
1970 }
1971 else
1972 {
1973 evt->SetPassEvent();
1974 }
1975
1976 if( m_cancelled )
1977 {
1978 frame->PopTool( pushedEvent );
1979 break;
1980 }
1981 }
1982
1983 // Store routing settings till the next invocation
1984 m_savedSizes = m_router->Sizes();
1985 m_router->ClearViewDecorations();
1986
1987 return 0;
1988}
1989
1990
1992{
1993 m_router->ClearViewDecorations();
1994
1995 view()->ClearPreview();
1996 view()->InitPreview();
1997
1999
2000 if( m_startItem && m_startItem->IsLocked() )
2001 {
2002 KIDIALOG dlg( frame(), _( "The selected item is locked." ), _( "Confirmation" ),
2003 wxOK | wxCANCEL | wxICON_WARNING );
2004 dlg.SetOKLabel( _( "Drag Anyway" ) );
2005 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
2006
2007 if( dlg.ShowModal() == wxID_CANCEL )
2008 return;
2009 }
2010
2011 // We don't support dragging arcs inside the PNS right now
2012 if( m_startItem && m_startItem->Kind() == PNS::ITEM::ARC_T )
2013 {
2014 if( m_router->RoutingInProgress() )
2015 m_router->StopRouting();
2016
2017 m_startItem = nullptr;
2018
2019 m_gridHelper->SetAuxAxes( false );
2020 ctls->ForceCursorPosition( false );
2021 highlightNets( false );
2022
2023 m_cancelled = true;
2024
2025 m_toolMgr->PostAction( PCB_ACTIONS::drag45Degree );
2026
2027 return;
2028 }
2029
2030 bool dragStarted = m_router->StartDragging( m_startSnapPoint, m_startItem, aMode );
2031
2032 if( !dragStarted )
2033 return;
2034
2035 if( m_startItem && m_startItem->Net() )
2036 highlightNets( true, { m_startItem->Net() } );
2037
2038 ctls->SetAutoPan( true );
2039 m_gridHelper->SetAuxAxes( true, m_startSnapPoint );
2040 frame()->UndoRedoBlock( true );
2041
2042 while( TOOL_EVENT* evt = Wait() )
2043 {
2044 ctls->ForceCursorPosition( false );
2045
2046 if( evt->IsMotion() )
2047 {
2048 updateEndItem( *evt );
2050
2051 if( PNS::DRAG_ALGO* dragger = m_router->GetDragger() )
2052 {
2053 bool dragStatus;
2054
2055 if( dragger->GetForceMarkObstaclesMode( &dragStatus ) )
2056 {
2057 view()->ClearPreview();
2058
2059 if( !dragStatus )
2060 {
2061 wxString hint;
2062 hint.Printf( _( "(%s to commit anyway.)" ),
2064
2066 statusItem->SetMessage( _( "Track violates DRC." ) );
2067 statusItem->SetHint( hint );
2068 statusItem->SetPosition( frame()->GetToolManager()->GetMousePosition() );
2069 view()->AddToPreview( statusItem );
2070 }
2071 }
2072 }
2073 }
2074 else if( evt->IsClick( BUT_LEFT ) )
2075 {
2076 bool forceFinish = false;
2077 bool forceCommit = evt->Modifier( MD_CTRL );
2078
2079 if( m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit ) )
2080 break;
2081 }
2082 else if( evt->IsClick( BUT_RIGHT ) )
2083 {
2084 m_menu->ShowContextMenu( selection() );
2085 }
2086 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2087 {
2088 if( evt->IsCancelInteractive() && !m_startItem )
2089 m_cancelled = true;
2090
2091 if( evt->IsActivate() && !evt->IsMoveTool() )
2092 m_cancelled = true;
2093
2094 break;
2095 }
2096 else if( evt->IsUndoRedo() )
2097 {
2098 // We're in an UndoRedoBlock. If we get here, something's broken.
2099 wxFAIL;
2100 break;
2101 }
2102 else if( evt->Category() == TC_COMMAND )
2103 {
2104 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2105 // but we don't at present have that, so we just knock out some of the egregious ones.
2106 if( evt->IsAction( &ACTIONS::cut )
2107 || evt->IsAction( &ACTIONS::copy )
2108 || evt->IsAction( &ACTIONS::paste )
2109 || evt->IsAction( &ACTIONS::pasteSpecial )
2111 {
2112 wxBell();
2113 }
2114 // treat an undo as an escape
2115 else if( evt->IsAction( &ACTIONS::undo ) )
2116 {
2117 if( m_startItem )
2118 break;
2119 else
2120 wxBell();
2121 }
2122 else
2123 {
2124 evt->SetPassEvent();
2125 }
2126 }
2127 else
2128 {
2129 evt->SetPassEvent();
2130 }
2131
2132 handleCommonEvents( *evt );
2133 }
2134
2135 view()->ClearPreview();
2136 view()->ShowPreview( false );
2137
2138 if( m_router->RoutingInProgress() )
2139 m_router->StopRouting();
2140
2141 m_startItem = nullptr;
2142
2143 m_gridHelper->SetAuxAxes( false );
2144 frame()->UndoRedoBlock( false );
2145 ctls->SetAutoPan( false );
2146 ctls->ForceCursorPosition( false );
2147 highlightNets( false );
2148}
2149
2150
2152 PCB_SELECTION_TOOL* aSelTool )
2153{
2154 /*
2155 * If the collection contains a trivial line corner (two connected segments)
2156 * or a non-fanout-via (a via with no more than two connected segments), then
2157 * trim the collection down to a single item (which one won't matter since
2158 * they're all connected).
2159 */
2160
2161 // First make sure we've got something that *might* match.
2162 int vias = aCollector.CountType( PCB_VIA_T );
2163 int traces = aCollector.CountType( PCB_TRACE_T );
2164 int arcs = aCollector.CountType( PCB_ARC_T );
2165
2166 // We eliminate arcs because they are not supported in the inline drag code.
2167 if( arcs > 0 )
2168 return;
2169
2170 // We need to have at least 1 via or track
2171 if( vias + traces == 0 )
2172 return;
2173
2174 // We cannot drag more than one via at a time
2175 if( vias > 1 )
2176 return;
2177
2178 // We cannot drag more than two track segments at a time
2179 if( traces > 2 )
2180 return;
2181
2182 // Fetch first PCB_TRACK (via or trace) as our reference
2183 PCB_TRACK* reference = nullptr;
2184
2185 for( int i = 0; !reference && i < aCollector.GetCount(); i++ )
2186 reference = dynamic_cast<PCB_TRACK*>( aCollector[i] );
2187
2188 // This should never happen, but just in case...
2189 if( !reference )
2190 return;
2191
2192 int refNet = reference->GetNetCode();
2193
2194 VECTOR2I refPoint( aPt.x, aPt.y );
2195 EDA_ITEM_FLAGS flags = reference->IsPointOnEnds( refPoint, -1 );
2196
2197 if( flags & STARTPOINT )
2198 refPoint = reference->GetStart();
2199 else if( flags & ENDPOINT )
2200 refPoint = reference->GetEnd();
2201
2202 // Check all items to ensure that any TRACKs are co-terminus with the reference and on
2203 // the same net.
2204 for( int i = 0; i < aCollector.GetCount(); i++ )
2205 {
2206 PCB_TRACK* neighbor = dynamic_cast<PCB_TRACK*>( aCollector[i] );
2207
2208 if( neighbor && neighbor != reference )
2209 {
2210 if( neighbor->GetNetCode() != refNet )
2211 return;
2212
2213 if( neighbor->GetStart() != refPoint && neighbor->GetEnd() != refPoint )
2214 return;
2215 }
2216 }
2217
2218 // Selection meets criteria; trim it to the reference item.
2219 aCollector.Empty();
2220 aCollector.Append( reference );
2221}
2222
2223
2224bool ROUTER_TOOL::CanInlineDrag( int aDragMode )
2225{
2227 const PCB_SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2228
2229 if( selection.Size() == 1 )
2230 {
2231 return selection.Front()->IsType( GENERAL_COLLECTOR::DraggableItems );
2232 }
2233 else if( selection.CountType( PCB_FOOTPRINT_T ) == selection.Size() )
2234 {
2235 // Footprints cannot be dragged freely.
2236 return !( aDragMode & PNS::DM_FREE_ANGLE );
2237 }
2238 else if( selection.CountType( PCB_TRACE_T ) == selection.Size() )
2239 {
2240 return true;
2241 }
2242
2243 return false;
2244}
2245
2246
2247void ROUTER_TOOL::restoreSelection( const PCB_SELECTION& aOriginalSelection )
2248{
2249 EDA_ITEMS selItems;
2250 std::copy( aOriginalSelection.Items().begin(), aOriginalSelection.Items().end(), std::back_inserter( selItems ) );
2251 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &selItems );
2252}
2253
2254
2256{
2257 const PCB_SELECTION selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2258
2259 if( selection.Empty() )
2261
2262 if( selection.Empty() || !selection.Front()->IsBOARD_ITEM() )
2263 return 0;
2264
2265 // selection gets cleared in the next action, we need a copy of the selected items.
2266 std::deque<EDA_ITEM*> selectedItems = selection.GetItems();
2267
2268 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
2269
2270 if( item->Type() != PCB_TRACE_T
2271 && item->Type() != PCB_VIA_T
2272 && item->Type() != PCB_ARC_T
2273 && item->Type() != PCB_FOOTPRINT_T )
2274 {
2275 return 0;
2276 }
2277
2278 std::set<FOOTPRINT*> footprints;
2279
2280 if( item->Type() == PCB_FOOTPRINT_T )
2281 footprints.insert( static_cast<FOOTPRINT*>( item ) );
2282
2283 // We can drag multiple footprints, but not a grab-bag of items
2284 if( selection.Size() > 1 && item->Type() == PCB_FOOTPRINT_T )
2285 {
2286 for( int idx = 1; idx < selection.Size(); ++idx )
2287 {
2288 if( !selection.GetItem( idx )->IsBOARD_ITEM() )
2289 return 0;
2290
2291 if( static_cast<BOARD_ITEM*>( selection.GetItem( idx ) )->Type() != PCB_FOOTPRINT_T )
2292 return 0;
2293
2294 footprints.insert( static_cast<FOOTPRINT*>( selection.GetItem( idx ) ) );
2295 }
2296 }
2297
2298 // If we overrode locks, we want to clear the flag from the source item before SyncWorld is
2299 // called so that virtual vias are not generated for the (now unlocked) track segment. Note in
2300 // this case the lock can't be reliably re-applied, because there is no guarantee that the end
2301 // state of the drag results in the same number of segments so it's not clear which segment to
2302 // apply the lock state to.
2303 bool wasLocked = false;
2304
2305 if( item->IsLocked() )
2306 {
2307 wasLocked = true;
2308 item->SetLocked( false );
2309 }
2310
2311 m_toolMgr->RunAction( ACTIONS::selectionClear );
2312
2313 TOOL_EVENT pushedEvent = aEvent;
2314 frame()->PushTool( aEvent );
2315 Activate();
2316
2317 m_startItem = nullptr;
2318
2319 PNS::ITEM* startItem = nullptr;
2320 PNS::ITEM_SET itemsToDrag;
2321
2322 bool showCourtyardConflicts = frame()->GetPcbNewSettings()->m_ShowCourtyardCollisions;
2323
2324 std::shared_ptr<DRC_ENGINE> drcEngine = m_toolMgr->GetTool<DRC_TOOL>()->GetDRCEngine();
2325 DRC_INTERACTIVE_COURTYARD_CLEARANCE courtyardClearanceDRC( drcEngine );
2326
2327 std::shared_ptr<CONNECTIVITY_DATA> connectivityData = board()->GetConnectivity();
2328 std::vector<BOARD_ITEM*> dynamicItems;
2329 std::unique_ptr<CONNECTIVITY_DATA> dynamicData = nullptr;
2330 VECTOR2I lastOffset;
2331 std::vector<PNS::ITEM*> leaderSegments;
2332 bool singleFootprintDrag = false;
2333
2334 if( !footprints.empty() )
2335 {
2336 if( footprints.size() == 1 )
2337 singleFootprintDrag = true;
2338
2339 if( showCourtyardConflicts )
2340 courtyardClearanceDRC.Init( board() );
2341
2342 for( FOOTPRINT* footprint : footprints )
2343 {
2344 for( PAD* pad : footprint->Pads() )
2345 {
2346 PNS::ITEM* solid = m_router->GetWorld()->FindItemByParent( pad );
2347
2348 if( solid )
2349 itemsToDrag.Add( solid );
2350
2351 if( pad->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
2352 {
2353 if( connectivityData->GetRatsnestForPad( pad ).size() > 0 )
2354 dynamicItems.push_back( pad );
2355 }
2356 }
2357
2358 for( ZONE* zone : footprint->Zones() )
2359 {
2360 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( zone ) )
2361 itemsToDrag.Add( solid );
2362 }
2363
2364 for( BOARD_ITEM* shape : footprint->GraphicalItems() )
2365 {
2366 if( shape->GetLayer() == Edge_Cuts
2367 || shape->GetLayer() == Margin
2368 || IsCopperLayer( shape->GetLayer() ) )
2369 {
2370 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( shape ) )
2371 itemsToDrag.Add( solid );
2372 }
2373 }
2374
2375 if( showCourtyardConflicts )
2376 courtyardClearanceDRC.m_FpInMove.push_back( footprint );
2377 }
2378
2379 dynamicData = std::make_unique<CONNECTIVITY_DATA>( board()->GetConnectivity(),
2380 dynamicItems, true );
2381 connectivityData->BlockRatsnestItems( dynamicItems );
2382 }
2383 else
2384 {
2385 for( const EDA_ITEM* selItem : selectedItems )
2386 {
2387 if( !selItem->IsBOARD_ITEM() )
2388 continue;
2389
2390 const BOARD_ITEM* boardItem = static_cast<const BOARD_ITEM*>( selItem );
2391 PNS::ITEM* pnsItem = m_router->GetWorld()->FindItemByParent( boardItem );
2392
2393 if( !pnsItem )
2394 continue;
2395
2396 if( pnsItem->OfKind( PNS::ITEM::SEGMENT_T )
2397 || pnsItem->OfKind( PNS::ITEM::VIA_T )
2398 || pnsItem->OfKind( PNS::ITEM::ARC_T ) )
2399 {
2400 itemsToDrag.Add( pnsItem );
2401 }
2402 }
2403 }
2404
2405 GAL* gal = m_toolMgr->GetView()->GetGAL();
2406 VECTOR2I p0 = GetClampedCoords( controls()->GetCursorPosition( false ), COORDS_PADDING );
2407 VECTOR2I p = p0;
2408
2409 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
2410 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
2411
2412 if( itemsToDrag.Count() >= 1 )
2413 {
2414 // Snap to closest item
2415 int layer = m_iface->GetPNSLayerFromBoardLayer( m_originalActiveLayer );
2416 PNS::ITEM* closestItem = nullptr;
2417 SEG::ecoord closestDistSq = std::numeric_limits<SEG::ecoord>::max();
2418
2419 for( PNS::ITEM* pitem : itemsToDrag.Items() )
2420 {
2421 SEG::ecoord distSq = pitem->Shape( layer )->SquaredDistance( p0, 0 );
2422
2423 if( distSq < closestDistSq )
2424 {
2425 closestDistSq = distSq;
2426 closestItem = pitem;
2427 }
2428 }
2429
2430 if( closestItem )
2431 {
2432 p = snapToItem( closestItem, p0 );
2433
2434 m_startItem = closestItem;
2435
2436 if( closestItem->Net() )
2437 highlightNets( true, { closestItem->Net() } );
2438 }
2439 }
2440
2441 if( !footprints.empty() && singleFootprintDrag )
2442 {
2443 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
2444
2445 // The mouse is going to be moved on grid before dragging begins.
2446 VECTOR2I tweakedMousePos;
2448
2449 // Check if user wants to warp the mouse to origin of moved object
2450
2451 if( editFrame->GetMoveWarpsCursor() )
2452 tweakedMousePos = footprint->GetPosition(); // Use footprint anchor to warp mouse
2453 else
2454 tweakedMousePos = GetClampedCoords( controls()->GetCursorPosition(),
2455 COORDS_PADDING ); // Just use current mouse pos
2456
2457 // We tweak the mouse position using the value from above, and then use that as the
2458 // start position to prevent the footprint from jumping when we start dragging.
2459 // First we move the visual cross hair cursor...
2460 controls()->ForceCursorPosition( true, tweakedMousePos );
2461 controls()->SetCursorPosition( tweakedMousePos ); // ...then the mouse pointer
2462
2463 // Now that the mouse is in the right position, get a copy of the position to use later
2464 p = controls()->GetCursorPosition();
2465 }
2466
2467 int dragMode = aEvent.Parameter<int> ();
2468
2469 bool dragStarted = m_router->StartDragging( p, itemsToDrag, dragMode );
2470
2471 if( !dragStarted )
2472 {
2473 if( wasLocked )
2474 item->SetLocked( true );
2475
2476 if( !footprints.empty() )
2477 connectivityData->ClearLocalRatsnest();
2478
2479 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
2480 courtyardClearanceDRC.ClearConflicts( getView() );
2481
2483 controls()->ForceCursorPosition( false );
2484 frame()->PopTool( pushedEvent );
2485 highlightNets( false );
2486 return 0;
2487 }
2488
2489 m_gridHelper->SetAuxAxes( true, p );
2490 controls()->ShowCursor( true );
2491 controls()->SetAutoPan( true );
2492 frame()->UndoRedoBlock( true );
2493
2494 view()->ClearPreview();
2495 view()->InitPreview();
2496
2497 auto setCursor =
2498 [&]()
2499 {
2500 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2501 };
2502
2503 // Set initial cursor
2504 setCursor();
2505
2506 // Set the initial visible area
2507 BOX2D viewAreaD = getView()->GetGAL()->GetVisibleWorldExtents();
2508 m_router->SetVisibleViewArea( BOX2ISafe( viewAreaD ) );
2509
2510 // Send an initial movement to prime the collision detection
2511 m_router->Move( p, nullptr );
2512
2513 bool hasMouseMoved = false;
2514 bool hasMultidragCancelled = false;
2515
2516 while( TOOL_EVENT* evt = Wait() )
2517 {
2518 setCursor();
2519
2520 if( evt->IsCancelInteractive() || evt->IsActivate() )
2521 {
2522 if( wasLocked )
2523 item->SetLocked( true );
2524
2525 hasMultidragCancelled = true;
2526
2527 break;
2528 }
2529 else if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
2530 {
2531 hasMouseMoved = true;
2532 updateEndItem( *evt );
2534
2535 view()->ClearPreview();
2536
2537 if( !footprints.empty() )
2538 {
2539 VECTOR2I offset = m_endSnapPoint - p;
2540 BOARD_ITEM* previewItem;
2541
2542 for( FOOTPRINT* footprint : footprints )
2543 {
2544 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
2545 {
2546 previewItem = static_cast<BOARD_ITEM*>( drawing->Clone() );
2547 previewItem->Move( offset );
2548
2549 view()->AddToPreview( previewItem );
2550 view()->Hide( drawing, true );
2551 }
2552
2553 for( PAD* pad : footprint->Pads() )
2554 {
2555 if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none()
2556 && pad->GetDrillSize().x == 0 )
2557 {
2558 previewItem = static_cast<BOARD_ITEM*>( pad->Clone() );
2559 previewItem->Move( offset );
2560
2561 view()->AddToPreview( previewItem );
2562 }
2563 else
2564 {
2565 // Pads with copper or holes are handled by the router
2566 }
2567
2568 view()->Hide( pad, true );
2569 }
2570
2571 previewItem = static_cast<BOARD_ITEM*>( footprint->Reference().Clone() );
2572 previewItem->Move( offset );
2573 view()->AddToPreview( previewItem );
2574 view()->Hide( &footprint->Reference() );
2575
2576 previewItem = static_cast<BOARD_ITEM*>( footprint->Value().Clone() );
2577 previewItem->Move( offset );
2578 view()->AddToPreview( previewItem );
2579 view()->Hide( &footprint->Value() );
2580
2581 if( showCourtyardConflicts )
2582 footprint->Move( offset );
2583 }
2584
2585 if( showCourtyardConflicts )
2586 {
2587 courtyardClearanceDRC.Run();
2588 courtyardClearanceDRC.UpdateConflicts( getView(), false );
2589
2590 for( FOOTPRINT* footprint : footprints )
2591 footprint->Move( -offset );
2592 }
2593
2594 // Update ratsnest
2595 dynamicData->Move( offset - lastOffset );
2596 lastOffset = offset;
2597 connectivityData->ComputeLocalRatsnest( dynamicItems, dynamicData.get(), offset );
2598 }
2599
2600 if( PNS::DRAG_ALGO* dragger = m_router->GetDragger() )
2601 {
2602 bool dragStatus;
2603
2604 if( dragger->GetForceMarkObstaclesMode( &dragStatus ) )
2605 {
2606 if( !dragStatus )
2607 {
2608 wxString hint;
2609 hint.Printf( _( "(%s to commit anyway.)" ),
2611
2613 statusItem->SetMessage( _( "Track violates DRC." ) );
2614 statusItem->SetHint( hint );
2615 statusItem->SetPosition( frame()->GetToolManager()->GetMousePosition() );
2616 view()->AddToPreview( statusItem );
2617 }
2618 }
2619 }
2620 }
2621 else if( hasMouseMoved && ( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
2622 {
2623 bool forceFinish = false;
2624 bool forceCommit = evt->Modifier( MD_CTRL );
2625
2626 updateEndItem( *evt );
2627 m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit );
2628 leaderSegments = m_router->GetLastCommittedLeaderSegments();
2629
2630 break;
2631 }
2632 else if( evt->IsUndoRedo() )
2633 {
2634 // We're in an UndoRedoBlock. If we get here, something's broken.
2635 wxFAIL;
2636 break;
2637 }
2638 else if( evt->Category() == TC_COMMAND )
2639 {
2640 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2641 // but we don't at present have that, so we just knock out some of the egregious ones.
2642 if( evt->IsAction( &ACTIONS::cut )
2643 || evt->IsAction( &ACTIONS::copy )
2644 || evt->IsAction( &ACTIONS::paste )
2645 || evt->IsAction( &ACTIONS::pasteSpecial )
2647 {
2648 wxBell();
2649 }
2650 // treat an undo as an escape
2651 else if( evt->IsAction( &ACTIONS::undo ) )
2652 {
2653 if( wasLocked )
2654 item->SetLocked( true );
2655
2656 break;
2657 }
2658 else
2659 {
2660 evt->SetPassEvent();
2661 }
2662 }
2663 else
2664 {
2665 evt->SetPassEvent();
2666 }
2667
2668 handleCommonEvents( *evt );
2669 }
2670
2671 if( !footprints.empty() )
2672 {
2673 for( FOOTPRINT* footprint : footprints )
2674 {
2675 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
2676 view()->Hide( drawing, false );
2677
2678 view()->Hide( &footprint->Reference(), false );
2679 view()->Hide( &footprint->Value(), false );
2680
2681 for( PAD* pad : footprint->Pads() )
2682 view()->Hide( pad, false );
2683 }
2684
2685 view()->ClearPreview();
2686 view()->ShowPreview( false );
2687
2688 connectivityData->ClearLocalRatsnest();
2689 }
2690
2691 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
2692 courtyardClearanceDRC.ClearConflicts( getView() );
2693
2694 if( m_router->RoutingInProgress() )
2695 m_router->StopRouting();
2696
2697
2698 if( itemsToDrag.Size() && hasMultidragCancelled )
2699 {
2701 }
2702 else if( leaderSegments.size() )
2703 {
2704 std::vector<EDA_ITEM*> newItems;
2705
2706 for( auto lseg : leaderSegments )
2707 newItems.push_back( lseg->Parent() );
2708
2709 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &newItems );
2710 }
2711
2712 m_gridHelper->SetAuxAxes( false );
2713 controls()->SetAutoPan( false );
2714 controls()->ForceCursorPosition( false );
2715 frame()->UndoRedoBlock( false );
2716 frame()->PopTool( pushedEvent );
2717 highlightNets( false );
2718 view()->ClearPreview();
2719 view()->ShowPreview( false );
2720
2721 return 0;
2722}
2723
2724
2726{
2727 const SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2728
2729 if( selection.Size() != 1 )
2730 return 0;
2731
2732 const BOARD_CONNECTED_ITEM* item =
2733 static_cast<const BOARD_CONNECTED_ITEM*>( selection.Front() );
2734
2735 if( item->Type() != PCB_TRACE_T && item->Type() != PCB_ARC_T )
2736 return 0;
2737
2738 m_toolMgr->RunAction( ACTIONS::selectionClear );
2739
2740 Activate();
2741
2742 m_startItem = m_router->GetWorld()->FindItemByParent( item );
2743
2744 TOOL_MANAGER* toolManager = frame()->GetToolManager();
2745 GAL* gal = toolManager->GetView()->GetGAL();
2746
2747 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
2748 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
2749
2750 controls()->ForceCursorPosition( false );
2751
2752 if( toolManager->IsContextMenuActive() )
2753 {
2754 // If we're here from a context menu then we need to get the position of the
2755 // cursor when the context menu was invoked. This is used to figure out the
2756 // break point on the track.
2758 }
2759 else
2760 {
2761 // If we're here from a hotkey, then get the current mouse position so we know
2762 // where to break the track.
2763 m_startSnapPoint = snapToItem( m_startItem, controls()->GetCursorPosition() );
2764 }
2765
2766 if( m_startItem && m_startItem->IsLocked() )
2767 {
2768 KIDIALOG dlg( frame(), _( "The selected item is locked." ), _( "Confirmation" ),
2769 wxOK | wxCANCEL | wxICON_WARNING );
2770 dlg.SetOKLabel( _( "Break Track" ) );
2771 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
2772
2773 if( dlg.ShowModal() == wxID_CANCEL )
2774 return 0;
2775 }
2776
2777 frame()->UndoRedoBlock( true );
2778 breakTrack();
2779
2780 if( m_router->RoutingInProgress() )
2781 m_router->StopRouting();
2782
2783 frame()->UndoRedoBlock( false );
2784
2785 return 0;
2786}
2787
2788
2790{
2792 DIALOG_TRACK_VIA_SIZE sizeDlg( frame(), bds );
2793
2794 if( sizeDlg.ShowModal() == wxID_OK )
2795 {
2796 bds.m_TempOverrideTrackWidth = true;
2797 bds.UseCustomTrackViaSize( true );
2798
2801 }
2802
2803 return 0;
2804}
2805
2806
2808{
2809 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
2810
2811 if( !m_router->GetCurrentNets().empty() )
2812 m_iface->ImportSizes( sizes, m_startItem, m_router->GetCurrentNets()[0], VECTOR2D() );
2813
2814 m_router->UpdateSizes( sizes );
2815
2816 // Changing the track width can affect the placement, so call the
2817 // move routine without changing the destination
2818 // Update end item first to avoid moving to an invalid/missing item
2819 updateEndItem( aEvent );
2821
2823
2824 return 0;
2825}
2826
2827
2829{
2830 std::vector<MSG_PANEL_ITEM> items;
2831
2832 if( m_router->GetState() == PNS::ROUTER::ROUTE_TRACK )
2833 {
2834 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
2835 PNS::RULE_RESOLVER* resolver = m_iface->GetRuleResolver();
2836 PNS::CONSTRAINT constraint;
2837 std::vector<PNS::NET_HANDLE> nets = m_router->GetCurrentNets();
2838 wxString description;
2839 wxString secondary;
2840 wxString mode;
2841
2843 {
2844 wxASSERT( nets.size() >= 2 );
2845
2846 NETINFO_ITEM* netA = static_cast<NETINFO_ITEM*>( nets[0] );
2847 NETINFO_ITEM* netB = static_cast<NETINFO_ITEM*>( nets[1] );
2848 wxASSERT( netA );
2849 wxASSERT( netB );
2850
2851 description = wxString::Format( _( "Routing Diff Pair: %s" ),
2852 netA->GetNetname() + wxT( ", " ) + netB->GetNetname() );
2853
2854 wxString netclass;
2855 NETCLASS* netclassA = netA->GetNetClass();
2856 NETCLASS* netclassB = netB->GetNetClass();
2857
2858 if( *netclassA == *netclassB )
2859 netclass = netclassA->GetHumanReadableName();
2860 else
2861 netclass = netclassA->GetHumanReadableName() + wxT( ", " )
2862 + netclassB->GetHumanReadableName();
2863
2864 secondary = wxString::Format( _( "Resolved Netclass: %s" ),
2865 UnescapeString( netclass ) );
2866 }
2867 else if( !nets.empty() && nets[0] )
2868 {
2869 NETINFO_ITEM* net = static_cast<NETINFO_ITEM*>( nets[0] );
2870
2871 description = wxString::Format( _( "Routing Track: %s" ),
2872 net->GetNetname() );
2873
2874 secondary = wxString::Format(
2875 _( "Resolved Netclass: %s" ),
2877 }
2878 else
2879 {
2880 description = _( "Routing Track" );
2881 secondary = _( "(no net)" );
2882 }
2883
2884 items.emplace_back( description, secondary );
2885
2886 wxString cornerMode;
2887
2888 if( m_router->Settings().GetFreeAngleMode() )
2889 {
2890 cornerMode = _( "Free-angle" );
2891 }
2892 else
2893 {
2894 switch( m_router->Settings().GetCornerMode() )
2895 {
2896 case DIRECTION_45::CORNER_MODE::MITERED_45: cornerMode = _( "45-degree" ); break;
2897 case DIRECTION_45::CORNER_MODE::ROUNDED_45: cornerMode = _( "45-degree rounded" ); break;
2898 case DIRECTION_45::CORNER_MODE::MITERED_90: cornerMode = _( "90-degree" ); break;
2899 case DIRECTION_45::CORNER_MODE::ROUNDED_90: cornerMode = _( "90-degree rounded" ); break;
2900 default: break;
2901 }
2902 }
2903
2904 items.emplace_back( _( "Corner Style" ), cornerMode );
2905
2906 switch( m_router->Settings().Mode() )
2907 {
2908 case PNS::PNS_MODE::RM_MarkObstacles: mode = _( "Highlight collisions" ); break;
2909 case PNS::PNS_MODE::RM_Walkaround: mode = _( "Walk around" ); break;
2910 case PNS::PNS_MODE::RM_Shove: mode = _( "Shove" ); break;
2911 default: break;
2912 }
2913
2914 items.emplace_back( _( "Mode" ), mode );
2915
2916#define FORMAT_VALUE( x ) frame()->MessageTextFromValue( x )
2917
2919 {
2920 items.emplace_back( wxString::Format( _( "Track Width: %s" ),
2921 FORMAT_VALUE( sizes.DiffPairWidth() ) ),
2922 wxString::Format( _( "(from %s)" ),
2923 sizes.GetDiffPairWidthSource() ) );
2924
2925 items.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
2926 FORMAT_VALUE( sizes.Clearance() ) ),
2927 wxString::Format( _( "(from %s)" ),
2928 sizes.GetClearanceSource() ) );
2929
2930 items.emplace_back( wxString::Format( _( "Diff Pair Gap: %s" ),
2931 FORMAT_VALUE( sizes.DiffPairGap() ) ),
2932 wxString::Format( _( "(from %s)" ),
2933 sizes.GetDiffPairGapSource() ) );
2934
2935 const PNS::ITEM_SET& traces = m_router->Placer()->Traces();
2936 wxASSERT( traces.Count() == 2 );
2937
2938 if( resolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_MAX_UNCOUPLED, traces[0],
2939 traces[1], m_router->GetCurrentLayer(), &constraint ) )
2940 {
2941 items.emplace_back( wxString::Format( _( "DP Max Uncoupled-length: %s" ),
2942 FORMAT_VALUE( constraint.m_Value.Max() ) ),
2943 wxString::Format( _( "(from %s)" ),
2944 constraint.m_RuleName ) );
2945 }
2946 }
2947 else
2948 {
2949 items.emplace_back( wxString::Format( _( "Track Width: %s" ),
2950 FORMAT_VALUE( sizes.TrackWidth() ) ),
2951 wxString::Format( _( "(from %s)" ),
2952 sizes.GetWidthSource() ) );
2953
2954 items.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
2955 FORMAT_VALUE( sizes.Clearance() ) ),
2956 wxString::Format( _( "(from %s)" ),
2957 sizes.GetClearanceSource() ) );
2958 }
2959
2960#undef FORMAT_VALUE
2961
2962 frame()->SetMsgPanel( items );
2963 }
2964 else
2965 {
2966 frame()->SetMsgPanel( board() );
2967 return;
2968 }
2969}
2970
2971
2973{
2975
2989
2996
3032
3035
3041}
std::function< void(const VECTOR2I &, GENERAL_COLLECTOR &, PCB_SELECTION_TOOL *)> CLIENT_SELECTION_FILTER
Definition actions.h:37
@ width_track_via
@ change_entry_orient
@ switch_corner_rounding_shape
constexpr BOX2I BOX2ISafe(const BOX2D &aInput)
Definition box2.h:929
BOX2< VECTOR2D > BOX2D
Definition box2.h:923
static TOOL_ACTION paste
Definition actions.h:80
static TOOL_ACTION cancelInteractive
Definition actions.h:72
static TOOL_ACTION copy
Definition actions.h:78
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition actions.h:217
static TOOL_ACTION pasteSpecial
Definition actions.h:81
static TOOL_ACTION undo
Definition actions.h:75
static TOOL_ACTION doDelete
Definition actions.h:85
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:224
static TOOL_ACTION cut
Definition actions.h:77
static TOOL_ACTION finishInteractive
Definition actions.h:73
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:232
Manage TOOL_ACTION objects.
void SetConditions(const TOOL_ACTION &aAction, const ACTION_CONDITIONS &aConditions)
Set the conditions the UI elements for activating a specific tool action should use for determining t...
ACTION_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool=nullptr)
Default constructor.
void Clear()
Remove all the entries from the menu (as well as its title).
void SetTitle(const wxString &aTitle) override
Set title for the menu.
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
void SetLayerVisible(int aLayer, bool isVisible)
BASE_SET & set(size_t pos)
Definition base_set.h:116
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
Container for design settings for a BOARD object.
void UseCustomTrackViaSize(bool aEnabled)
Enables/disables custom track/via size settings.
void SetCustomDiffPairWidth(int aWidth)
Sets custom track width for differential pairs (i.e.
void SetViaSizeIndex(int aIndex)
Set the current via size list index to aIndex.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
std::vector< DIFF_PAIR_DIMENSION > m_DiffPairDimensionsList
void SetCustomDiffPairGap(int aGap)
Sets custom gap for differential pairs (i.e.
bool UseNetClassVia() const
Return true if netclass values should be used to obtain appropriate via size.
bool UseNetClassTrack() const
Return true if netclass values should be used to obtain appropriate track width.
bool UseNetClassDiffPair() const
Return true if netclass values should be used to obtain appropriate diff pair dimensions.
void SetTrackWidthIndex(int aIndex)
Set the current track width list index to aIndex.
void UseCustomDiffPairDimensions(bool aEnabled)
Enables/disables custom differential pair dimensions.
std::vector< int > m_TrackWidthList
std::vector< VIA_DIMENSION > m_ViasDimensionsList
void SetCustomDiffPairViaGap(int aGap)
Sets custom via gap for differential pairs (i.e.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
void SetLocked(bool aLocked) override
Definition board_item.h:327
bool IsLocked() const override
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:343
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:284
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
bool IsLayerVisible(PCB_LAYER_ID aLayer) const
A proxy function that calls the correspondent function in m_BoardSettings tests whether a given layer...
Definition board.cpp:960
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1069
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:538
CN_EDGE represents a point-to-point connection, whether realized or unrealized (ie: tracks etc.
void Empty()
Clear the list.
Definition collector.h:91
int GetCount() const
Return the number of objects in the list.
Definition collector.h:83
int CountType(KICAD_T aType)
Count the number of items matching aType.
Definition collector.h:223
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition collector.h:101
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
void AddCheckItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a checked menu entry to run a TOOL_ACTION on selected items.
int ShowModal() override
Implementing DIALOG_TRACK_VIA_SIZE_BASE.
PCB_EDIT_FRAME & m_frame
OPT_TOOL_EVENT eventHandler(const wxMenuEvent &aEvent) override
Event handler stub.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
DIFF_PAIR_MENU(PCB_EDIT_FRAME &aFrame)
void update() override
Update menu state stub.
CORNER_MODE
Corner modes.
Definition direction45.h:67
@ ROUNDED_90
H/V with filleted corners.
Definition direction45.h:71
@ MITERED_90
H/V only (90-degree corners)
Definition direction45.h:70
@ ROUNDED_45
H/V/45 with filleted corners.
Definition direction45.h:69
@ MITERED_45
H/V/45 with mitered corners (default)
Definition direction45.h:68
wxString GetName() const
Definition drc_rule.h:194
MINOPTMAX< int > m_Value
Definition drc_rule.h:228
bool IsNull() const
Definition drc_rule.h:181
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
void UpdateConflicts(KIGFX::VIEW *aView, bool aHighlightMoved)
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, WX_INFOBAR::MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:142
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:207
static const std::vector< KICAD_T > DraggableItems
A scan list for items that can be dragged.
Definition collectors.h:145
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition kidialog.h:42
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
Definition kidialog.cpp:55
int ShowModal() override
Definition kidialog.cpp:93
Abstract interface for drawing on a 2D-surface.
BOX2D GetVisibleWorldExtents() const
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const std::set< int > & GetHighlightNetCodes() const
Return the netcode of currently highlighted net.
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
virtual void WarpMouseCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
If enabled (.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual void SetCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true, bool aTriggeredByArrows=false, long aArrowCommand=0)=0
Move cursor to the requested position expressed in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
void ShowPreview(bool aShow=true)
Definition view.cpp:1743
virtual int GetTopLayer() const
Definition view.cpp:830
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:202
void InitPreview()
Definition view.cpp:1722
void ClearPreview()
Definition view.cpp:1707
void Hide(VIEW_ITEM *aItem, bool aHide=true, bool aHideOverlay=false)
Temporarily hide the item in the view (e.g.
Definition view.cpp:1633
void AddToPreview(VIEW_ITEM *aItem, bool aTakeOwnership=true)
Definition view.cpp:1729
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
LSEQ UIOrder() const
Return the copper, technical and user layers in the order shown in layer widget.
Definition lset.cpp:726
static LSET AllNonCuMask()
Return a mask holding all layer minus CU layers.
Definition lset.cpp:610
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:582
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:591
T Min() const
Definition minoptmax.h:33
T Max() const
Definition minoptmax.h:34
T Opt() const
Definition minoptmax.h:35
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:45
const wxString GetHumanReadableName() const
Gets the consolidated name of this netclass (which may be an aggregate).
Definition netclass.cpp:294
Handle the data for a net.
Definition netinfo.h:54
const wxString & GetNetname() const
Definition netinfo.h:112
NETCLASS * GetNetClass()
Definition netinfo.h:99
int GetNetCode() const
Definition netinfo.h:106
Definition pad.h:55
static wxString GetDefaultUserProjectsPath()
Gets the default path we point users to create projects.
Definition paths.cpp:137
static TOOL_ACTION_GROUP layerDirectSwitchActions()
static TOOL_ACTION layerToggle
static TOOL_ACTION drag45Degree
static TOOL_ACTION layerInner12
static TOOL_ACTION routerUndoLastSegment
static TOOL_ACTION layerInner8
static TOOL_ACTION layerInner3
static TOOL_ACTION layerPrev
static TOOL_ACTION routerSettingsDialog
Activation of the Push and Shove settings dialogs.
static TOOL_ACTION layerInner2
static TOOL_ACTION routerAttemptFinish
static TOOL_ACTION routeDiffPair
Activation of the Push and Shove router (differential pair mode)
static TOOL_ACTION trackViaSizeChanged
static TOOL_ACTION layerChanged
static TOOL_ACTION layerInner25
static TOOL_ACTION breakTrack
Break a single track into two segments at the cursor.
static TOOL_ACTION routerRouteSelectedFromEnd
static TOOL_ACTION routerHighlightMode
Actions to enable switching modes via hotkey assignments.
static TOOL_ACTION routerWalkaroundMode
static TOOL_ACTION routerShoveMode
static TOOL_ACTION layerInner24
static TOOL_ACTION properties
Activation of the edit tool.
static TOOL_ACTION layerInner29
static TOOL_ACTION routerAutorouteSelected
static TOOL_ACTION layerInner11
static TOOL_ACTION routerDiffPairDialog
static TOOL_ACTION routerContinueFromEnd
static TOOL_ACTION layerInner16
static TOOL_ACTION layerInner26
static TOOL_ACTION layerInner18
static TOOL_ACTION layerInner14
static TOOL_ACTION selectLayerPair
static TOOL_ACTION layerInner6
static TOOL_ACTION dragFreeAngle
static TOOL_ACTION clearHighlight
static TOOL_ACTION layerInner22
static TOOL_ACTION layerInner5
static TOOL_ACTION layerInner20
static TOOL_ACTION layerInner7
static TOOL_ACTION layerInner27
static TOOL_ACTION layerInner1
static TOOL_ACTION layerInner10
static TOOL_ACTION layerInner15
static TOOL_ACTION layerInner17
static TOOL_ACTION layerBottom
static TOOL_ACTION layerInner19
static TOOL_ACTION layerInner9
static TOOL_ACTION routerInlineDrag
Activation of the Push and Shove router (inline dragging mode)
static TOOL_ACTION layerInner30
static TOOL_ACTION layerTop
static TOOL_ACTION cycleRouterMode
static TOOL_ACTION layerInner4
static TOOL_ACTION routeSingleTrack
Activation of the Push and Shove router.
static TOOL_ACTION layerInner13
static TOOL_ACTION layerInner21
static TOOL_ACTION layerNext
static TOOL_ACTION routerRouteSelected
static TOOL_ACTION layerInner23
static TOOL_ACTION layerInner28
Common, abstract interface for edit frames.
APPEARANCE_CONTROLS * GetAppearancePanel()
virtual PCB_LAYER_ID GetActiveLayer() const
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
The main frame for Pcbnew.
void SetActiveLayer(PCB_LAYER_ID aLayer) override
Change the currently active layer to aLayer and also update the APPEARANCE_CONTROLS.
A #PLUGIN derivation for saving and loading Pcbnew s-expression formatted files.
void SaveBoard(const wxString &aFileName, BOARD *aBoard, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aBoard to a storage file in a format that this PCB_IO implementation knows about or it can be u...
PCB_LAYER_ID m_Route_Layer_TOP
Definition pcb_screen.h:43
PCB_LAYER_ID m_Route_Layer_BOTTOM
Definition pcb_screen.h:44
The selection tool: currently supports:
T * frame() const
KIGFX::PCB_VIEW * view() const
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
PCBNEW_SETTINGS::DISPLAY_OPTIONS & displayOptions() const
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:150
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:153
const VECTOR2I & GetStart() const
Definition pcb_track.h:154
const VECTOR2I & GetEnd() const
Definition pcb_track.h:151
EDA_ITEM_FLAGS IsPointOnEnds(const VECTOR2I &point, int min_dist=0) const
Return STARTPOINT if point if near (dist = min_dist) start point, ENDPOINT if point if near (dist = m...
void SetLayerPair(PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer)
For a via m_layer contains the top layer, the other layer is in m_bottomLayer/.
void SetViaType(VIATYPE aViaType)
Definition pcb_track.h:456
int Size() const
int Count(int aKindMask=-1) const
Definition pns_itemset.h:66
void Add(const LINE &aLine)
std::vector< ITEM * > & Items()
Definition pns_itemset.h:87
Base class for PNS router board items.
Definition pns_item.h:98
virtual NET_HANDLE Net() const
Definition pns_item.h:210
bool OfKind(int aKindMask) const
Definition pns_item.h:181
static wxString FormatLogFileAsString(int aMode, const std::vector< ITEM * > &aAddedItems, const std::set< KIID > &aRemovedItems, const std::vector< ITEM * > &aHeads, const std::vector< EVENT_ENTRY > &aEvents)
Contain all persistent settings of the router, such as the mode, optimization effort,...
void SetMode(PNS_MODE aMode)
Return the optimizer effort. Bigger means cleaner traces, but slower routing.
PNS_MODE Mode() const
Set the routing mode.
void SetViaType(VIATYPE aViaType)
void SetTrackWidth(int aWidth)
void SetDiffPairWidth(int aWidth)
void SetDiffPairWidthSource(const wxString &aSource)
void SetDiffPairGapSource(const wxString &aSource)
void SetDiffPairGap(int aGap)
void SetViaDrill(int aDrill)
wxString GetClearanceSource() const
wxString GetDiffPairGapSource() const
wxString GetDiffPairWidthSource() const
void AddLayerPair(int aL1, int aL2)
void SetClearance(int aClearance)
bool TrackWidthIsExplicit() const
void SetViaDiameter(int aDiameter)
void SetClearanceSource(const wxString &aSource)
wxString GetWidthSource() const
void SetWidthSource(const wxString &aSource)
virtual void updateStartItem(const TOOL_EVENT &aEvent, bool aIgnorePads=false)
const VECTOR2I snapToItem(ITEM *aSnapToItem, const VECTOR2I &aP)
virtual void highlightNets(bool aEnabled, std::set< NET_HANDLE > aNetcodes={})
SIZES_SETTINGS m_savedSizes
PNS_KICAD_IFACE * m_iface
virtual void updateEndItem(const TOOL_EVENT &aEvent)
TOOL_BASE(const std::string &aToolName)
static const unsigned int COORDS_PADDING
ROUTER * m_router
VECTOR2I m_endSnapPoint
PCB_GRID_HELPER * m_gridHelper
VECTOR2I m_startSnapPoint
Represent a contiguous set of PCB layers.
int Start() const
bool Overlaps(const PNS_LAYER_RANGE &aOther) const
bool SaveAs(const wxString &aDirectory, const wxString &aFile)
bool SaveAs(const wxString &aDirectory, const wxString &aFile)
Container for project specific data.
Definition project.h:65
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
Definition project.h:205
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:199
Describe ratsnest for a single net.
const std::vector< CN_EDGE > & GetEdges() const
void SetMessage(const wxString &aStatus)
void SetHint(const wxString &aHint)
void SetPosition(const VECTOR2I &aPos) override
int onViaCommand(const TOOL_EVENT &aEvent)
int InlineDrag(const TOOL_EVENT &aEvent)
std::shared_ptr< ACTION_MENU > m_trackViaMenu
Definition router_tool.h:98
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
int onTrackViaSizeChanged(const TOOL_EVENT &aEvent)
int CustomTrackWidthDialog(const TOOL_EVENT &aEvent)
int handlePnSCornerModeChange(const TOOL_EVENT &aEvent)
static void NeighboringSegmentFilter(const VECTOR2I &aPt, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *aSelTool)
PNS::PNS_MODE GetRouterMode()
void saveRouterDebugLog()
void performDragging(int aMode=PNS::DM_ANY)
PCB_LAYER_ID m_originalActiveLayer
int onLayerCommand(const TOOL_EVENT &aEvent)
PCB_LAYER_ID getStartLayer(const PNS::ITEM *aItem)
int CycleRouterMode(const TOOL_EVENT &aEvent)
void updateSizesAfterRouterEvent(int targetLayer, const VECTOR2I &aPos)
bool m_inRouterTool
int handleLayerSwitch(const TOOL_EVENT &aEvent, bool aForceVia)
void switchLayerOnViaPlacement()
int RouteSelected(const TOOL_EVENT &aEvent)
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
bool finishInteractive()
int ChangeRouterMode(const TOOL_EVENT &aEvent)
std::shared_ptr< ACTION_MENU > m_diffPairMenu
Definition router_tool.h:97
PCB_LAYER_ID m_lastTargetLayer
void handleCommonEvents(TOOL_EVENT &evt)
void performRouting(VECTOR2D aStartPosition)
bool Init() override
Init() is called once upon a registration of the tool.
int InlineBreakTrack(const TOOL_EVENT &aEvent)
bool prepareInteractive(VECTOR2D aStartPosition)
void restoreSelection(const PCB_SELECTION &aOriginalSelection)
bool RoutingInProgress()
Returns whether routing is currently active.
void UpdateMessagePanel()
int MainLoop(const TOOL_EVENT &aEvent)
bool CanInlineDrag(int aDragMode)
int SettingsDialog(const TOOL_EVENT &aEvent)
int DpDimensionsDialog(const TOOL_EVENT &aEvent)
int SelectCopperLayerPair(const TOOL_EVENT &aEvent)
VECTOR2I::extended_type ecoord
Definition seg.h:44
static bool NotEmpty(const SELECTION &aSelection)
Test if there are any items selected.
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
std::deque< EDA_ITEM * > & Items()
Definition selection.h:182
bool GetMoveWarpsCursor() const
Indicate that a move operation should warp the mouse pointer to the origin of the move object.
Build up the properties of a TOOL_ACTION in an incremental manner that is static-construction safe.
Represent a single user action.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:186
virtual void Reset(RESET_REASON aReason)=0
Bring the tool to a known, initial state.
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:44
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition tool_base.cpp:38
bool IsToolActive() const
Definition tool_base.cpp:32
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:78
@ RUN
Tool is invoked after being inactive.
Definition tool_base.h:79
Generic, UI-independent tool event.
Definition tool_event.h:171
bool HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition tool_event.h:260
bool DisableGridSnapping() const
Definition tool_event.h:371
int KeyCode() const
Definition tool_event.h:376
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition tool_event.h:392
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:293
bool IsKeyPressed() const
Definition tool_event.h:381
TOOL_EVENT_CATEGORY Category() const
Return the category (eg. mouse/keyboard/action) of an event.
Definition tool_event.h:247
int Modifier(int aMask=MD_MODIFIER_MASK) const
Return information about key modifiers state (Ctrl, Alt, etc.).
Definition tool_event.h:366
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
bool IsActionInGroup(const TOOL_ACTION_GROUP &aGroup) const
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:473
void SetPassEvent(bool aPass=true)
Definition tool_event.h:256
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
friend class TOOL_MANAGER
std::unique_ptr< TOOL_MENU > m_menu
The functions below are not yet implemented - their interface may change.
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
Master controller class:
VECTOR2D GetMenuCursorPos() const
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
bool IsContextMenuActive() const
True while processing a context menu.
KIGFX::VIEW * GetView() const
OPT_TOOL_EVENT eventHandler(const wxMenuEvent &aEvent) override
Event handler stub.
TRACK_WIDTH_MENU(PCB_EDIT_FRAME &aFrame)
void update() override
Update menu state stub.
PCB_EDIT_FRAME & m_frame
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
A modified version of the wxInfoBar class that allows us to:
Definition wx_infobar.h:76
void ShowMessageFor(const wxString &aMessage, int aTime, int aFlags=wxICON_INFORMATION, MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the infobar with the provided message and icon for a specific period of time.
static bool IsZoneFillAction(const TOOL_EVENT *aEvent)
Handle a list of polygons defining a copper zone.
Definition zone.h:74
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:177
This file is part of the common library.
@ ARROW
Definition cursors.h:46
@ PENCIL
Definition cursors.h:52
#define CHECK(x)
@ VIA_DIAMETER_CONSTRAINT
Definition drc_rule.h:70
@ DIFF_PAIR_GAP_CONSTRAINT
Definition drc_rule.h:73
@ TRACK_WIDTH_CONSTRAINT
Definition drc_rule.h:59
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:49
@ HOLE_SIZE_CONSTRAINT
Definition drc_rule.h:54
#define _(s)
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition eda_item.h:566
#define ROUTER_TRANSIENT
transient items that should NOT be cached
#define ENDPOINT
ends. (Used to support dragging.)
std::uint32_t EDA_ITEM_FLAGS
#define STARTPOINT
When a line is selected, these flags indicate which.
static FILENAME_RESOLVER * resolver
a few functions useful in geometry calculations.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamps a vector to values that can be negated, respecting numeric limits of coordinates data type wit...
wxString KeyNameFromKeyCode(int aKeycode, bool *aIsFound)
Return the key name from the key code.
#define PSEUDO_WXK_CLICK
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ Edge_Cuts
Definition layer_ids.h:112
@ B_Cu
Definition layer_ids.h:65
@ Margin
Definition layer_ids.h:113
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ F_Cu
Definition layer_ids.h:64
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:33
PNS_MODE
< Routing modes
@ RM_MarkObstacles
Ignore collisions, mark obstacles.
@ RM_Walkaround
Only walk around.
@ RM_Shove
Only shove.
void * NET_HANDLE
Definition pns_item.h:55
ROUTER_MODE
Definition pns_router.h:62
@ PNS_MODE_ROUTE_DIFF_PAIR
Definition pns_router.h:64
@ DM_ANY
Definition pns_router.h:77
@ DM_FREE_ANGLE
Definition pns_router.h:75
#define _HKI(x)
Definition page_info.cpp:44
VIATYPE
Definition pcb_track.h:67
@ THROUGH
Definition pcb_track.h:68
@ MICROVIA
Definition pcb_track.h:71
@ ID_POPUP_PCB_SELECT_WIDTH1
Definition pcbnew_id.h:23
@ ID_POPUP_PCB_SELECT_DIFFPAIR16
Definition pcbnew_id.h:75
@ ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES
Definition pcbnew_id.h:22
@ ID_POPUP_PCB_SELECT_WIDTH16
Definition pcbnew_id.h:38
@ ID_POPUP_PCB_SELECT_AUTO_WIDTH
Definition pcbnew_id.h:21
@ ID_POPUP_PCB_SELECT_CUSTOM_WIDTH
Definition pcbnew_id.h:20
@ ID_POPUP_PCB_SELECT_DIFFPAIR1
Definition pcbnew_id.h:60
@ ID_POPUP_PCB_SELECT_USE_NETCLASS_DIFFPAIR
Definition pcbnew_id.h:59
@ ID_POPUP_PCB_SELECT_VIASIZE1
Definition pcbnew_id.h:39
@ ID_POPUP_PCB_SELECT_CUSTOM_DIFFPAIR
Definition pcbnew_id.h:58
@ ID_POPUP_PCB_SELECT_VIASIZE16
Definition pcbnew_id.h:54
Class that computes missing connections on a PCB.
static const TOOL_ACTION ACT_SwitchCornerModeToNext(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRoundingToNext") .Scope(AS_CONTEXT) .DefaultHotkey(MD_CTRL+'/') .FriendlyName(_("Track Corner Mode Switch")) .Tooltip(_("Switches between sharp/rounded and 45°/90° corners when routing tracks.")) .Icon(BITMAPS::switch_corner_rounding_shape))
#define FORMAT_VALUE(x)
static const TOOL_ACTION ACT_SwitchCornerMode45(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRounding45") .Scope(AS_CONTEXT) .DefaultHotkey(MD_CTRL+ 'W') .FriendlyName(_("Track Corner Mode 45")) .Tooltip(_("Switch to 45° corner when routing tracks.")))
static const TOOL_ACTION ACT_PlaceBlindVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.PlaceBlindVia") .Scope(AS_CONTEXT) .DefaultHotkey(MD_ALT+MD_SHIFT+ 'V') .LegacyHotkeyName("Add Blind/Buried Via") .FriendlyName(_("Place Blind/Buried Via")) .Tooltip(_("Adds a blind or buried via at the end of currently routed track.")) .Icon(BITMAPS::via_buried) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::BLIND_VIA))
static VIATYPE getViaTypeFromFlags(int aFlags)
static const TOOL_ACTION ACT_SwitchCornerModeArc90(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRoundingArc90") .Scope(AS_CONTEXT) .DefaultHotkey(MD_ALT+ 'W') .FriendlyName(_("Track Corner Mode Arc 90")) .Tooltip(_("Switch to arc 90° corner when routing tracks.")))
static const TOOL_ACTION ACT_SwitchCornerMode90(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRounding90") .Scope(AS_CONTEXT) .DefaultHotkey(MD_CTRL+MD_ALT+ 'W') .FriendlyName(_("Track Corner Mode 90")) .Tooltip(_("Switch to 90° corner when routing tracks.")))
static const TOOL_ACTION ACT_PlaceMicroVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.PlaceMicroVia") .Scope(AS_CONTEXT) .DefaultHotkey(MD_CTRL+ 'V') .LegacyHotkeyName("Add MicroVia") .FriendlyName(_("Place Microvia")) .Tooltip(_("Adds a microvia at the end of currently routed track.")) .Icon(BITMAPS::via_microvia) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::MICROVIA))
static const TOOL_ACTION ACT_SelLayerAndPlaceBlindVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SelLayerAndPlaceBlindVia") .Scope(AS_CONTEXT) .DefaultHotkey(MD_ALT+'<') .LegacyHotkeyName("Select Layer and Add Blind/Buried Via") .FriendlyName(_("Select Layer and Place Blind/Buried Via...")) .Tooltip(_("Select a layer, then add a blind or buried via at the end of currently routed track.")) .Icon(BITMAPS::select_w_layer) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::BLIND_VIA|VIA_ACTION_FLAGS::SELECT_LAYER))
static const TOOL_ACTION ACT_SwitchPosture(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchPosture") .Scope(AS_CONTEXT) .DefaultHotkey('/') .LegacyHotkeyName("Switch Track Posture") .FriendlyName(_("Switch Track Posture")) .Tooltip(_("Switches posture of the currently routed track.")) .Icon(BITMAPS::change_entry_orient))
VIA_ACTION_FLAGS
Flags used by via tool actions.
@ BLIND_VIA
blind via
@ BURIED_VIA
buried via
@ SELECT_LAYER
Ask user to select layer before adding via.
@ MICROVIA
Microvia.
@ VIA_MASK
@ VIA
Normal via.
static const TOOL_ACTION ACT_SelLayerAndPlaceThroughVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SelLayerAndPlaceVia") .Scope(AS_CONTEXT) .DefaultHotkey('<') .LegacyHotkeyName("Select Layer and Add Through Via") .FriendlyName(_("Select Layer and Place Through Via...")) .Tooltip(_("Select a layer, then add a through-hole via at the end of currently routed track.")) .Icon(BITMAPS::select_w_layer) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::VIA|VIA_ACTION_FLAGS::SELECT_LAYER))
static const TOOL_ACTION ACT_SwitchCornerModeArc45(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRoundingArc45") .Scope(AS_CONTEXT) .DefaultHotkey(MD_CTRL+MD_SHIFT+ 'W') .FriendlyName(_("Track Corner Mode Arc 45")) .Tooltip(_("Switch to arc 45° corner when routing tracks.")))
static const TOOL_ACTION ACT_SelLayerAndPlaceMicroVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SelLayerAndPlaceMicroVia") .Scope(AS_CONTEXT) .FriendlyName(_("Select Layer and Place Micro Via...")) .Tooltip(_("Select a layer, then add a micro via at the end of currently routed track.")) .Icon(BITMAPS::select_w_layer) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::MICROVIA|VIA_ACTION_FLAGS::SELECT_LAYER))
static const TOOL_ACTION ACT_PlaceThroughVia(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.PlaceVia") .Scope(AS_CONTEXT) .DefaultHotkey( 'V') .LegacyHotkeyName("Add Through Via") .FriendlyName(_("Place Through Via")) .Tooltip(_("Adds a through-hole via at the end of currently routed track.")) .Icon(BITMAPS::via) .Flags(AF_NONE) .Parameter< int >(VIA_ACTION_FLAGS::VIA))
#define _(s)
static const TOOL_ACTION ACT_CustomTrackWidth(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.CustomTrackViaSize") .Scope(AS_CONTEXT) .DefaultHotkey( 'Q') .LegacyHotkeyName("Custom Track/Via Size") .FriendlyName(_("Custom Track/Via Size...")) .Tooltip(_("Shows a dialog for changing the track width and via size.")) .Icon(BITMAPS::width_track))
#define APPEND_UNDO
Definition sch_commit.h:41
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
Container to handle a stock of specific differential pairs each with unique track width,...
An abstract function object, returning a design rule (clearance, diff pair gap, etc) required between...
Definition pns_node.h:73
wxString m_RuleName
Definition pns_node.h:77
MINOPTMAX< int > m_Value
Definition pns_node.h:75
Container to handle a stock of specific vias each with unique diameter and drill sizes in the BOARD c...
@ AS_CONTEXT
Action belongs to a particular tool (i.e. a part of a pop-up menu)
Definition tool_action.h:47
@ AF_NONE
Definition tool_action.h:55
@ TA_MODEL_CHANGE
Model has changed (partial update).
Definition tool_event.h:121
@ TA_UNDO_REDO_PRE
This event is sent before undo/redo command is performed.
Definition tool_event.h:106
@ TA_UNDO_REDO_POST
This event is sent after undo/redo command is performed.
Definition tool_event.h:109
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition tool_event.h:641
@ MD_ALT
Definition tool_event.h:145
@ MD_CTRL
Definition tool_event.h:144
@ MD_SHIFT
Definition tool_event.h:143
@ TC_COMMAND
Definition tool_event.h:57
@ TC_MOUSE
Definition tool_event.h:55
@ TC_VIEW
Definition tool_event.h:59
@ BUT_LEFT
Definition tool_event.h:132
@ BUT_RIGHT
Definition tool_event.h:133
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:86
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694
wxPoint ToWxPoint(const VECTOR2I &aSize)
Definition vector2wx.h:50
wxString AddFileExtListToFilter(const std::vector< std::string > &aExts)
Build the wildcard extension file dialog wildcard filter to add to the base message dialog.