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#if 0 // Old track corner command replaced by a submenu
197static const TOOL_ACTION ACT_SwitchCornerMode( TOOL_ACTION_ARGS()
198 .Name( "pcbnew.InteractiveRouter.SwitchRounding" )
199 .Scope( AS_CONTEXT )
200 .DefaultHotkey( MD_CTRL + '/' )
201 .FriendlyName( _( "Track Corner Mode" ) )
202 .Tooltip( _( "Switches between sharp/rounded and 45°/90° corners when routing tracks." ) )
204#endif
205
207 .Name( "pcbnew.InteractiveRouter.SwitchRounding45" )
208 .Scope( AS_CONTEXT )
209 .DefaultHotkey( '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_SHIFT + '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 + '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
586
587 menu.AddMenu( submenuCornerMode );
588
589 // Manage check/uncheck marks in this submenu items
590 auto cornerMode45Cond =
591 [this]( const SELECTION& )
592 {
593 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::MITERED_45;
594 };
595
596 auto cornerMode90Cond =
597 [this]( const SELECTION& )
598 {
599 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::MITERED_90;
600 };
601
602 auto cornerModeArc45Cond =
603 [this]( const SELECTION& )
604 {
605 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::ROUNDED_45;
606 };
607
608 auto cornerModeArc90Cond =
609 [this]( const SELECTION& )
610 {
611 return m_router->Settings().GetCornerMode() == DIRECTION_45::CORNER_MODE::ROUNDED_90;
612 };
613
614#define CHECK( x ) ACTION_CONDITIONS().Check( x )
615 mgr->SetConditions( ACT_SwitchCornerMode45, CHECK( cornerMode45Cond ) );
616 mgr->SetConditions( ACT_SwitchCornerMode90, CHECK( cornerMode90Cond ) );
617 mgr->SetConditions( ACT_SwitchCornerModeArc45, CHECK( cornerModeArc45Cond ) );
618 mgr->SetConditions( ACT_SwitchCornerModeArc90, CHECK( cornerModeArc90Cond ) );
619
620 auto diffPairCond =
621 [this]( const SELECTION& )
622 {
623 return m_router->Mode() == PNS::PNS_MODE_ROUTE_DIFF_PAIR;
624 };
625
626 menu.AddSeparator();
627
629 menu.AddMenu( m_diffPairMenu.get(), diffPairCond );
630
632
633 menu.AddSeparator();
634
635 frame->AddStandardSubMenus( *m_menu.get() );
636
637 return true;
638}
639
640
642{
643 if( aReason == RUN )
644 TOOL_BASE::Reset( aReason );
645}
646
647// Saves the complete event log and the dump of the PCB, allowing us to
648// recreate hard-to-find P&S quirks and bugs.
649
651{
652 static wxString mruPath = PATHS::GetDefaultUserProjectsPath();
653 static size_t lastLoggerSize = 0;
654
655 auto logger = m_router->Logger();
656
657 if( !logger || logger->GetEvents().size() == 0
658 || logger->GetEvents().size() == lastLoggerSize )
659 {
660 return;
661 }
662
663 wxFileDialog dlg( frame(), _( "Save router log" ), mruPath, "pns.log",
664 "PNS log files" + AddFileExtListToFilter( { "log" } ),
665 wxFD_OVERWRITE_PROMPT | wxFD_SAVE );
666
667 if( dlg.ShowModal() != wxID_OK )
668 {
669 lastLoggerSize = logger->GetEvents().size(); // prevent re-entry
670 return;
671 }
672
673 wxFileName fname_log( dlg.GetPath() );
674 mruPath = fname_log.GetPath();
675
676 wxFileName fname_dump( fname_log );
677 fname_dump.SetExt( "dump" );
678
679 wxFileName fname_settings( fname_log );
680 fname_settings.SetExt( "settings" );
681
682 FILE* settings_f = wxFopen( fname_settings.GetAbsolutePath(), "wb" );
683 std::string settingsStr = m_router->Settings().FormatAsString();
684 fprintf( settings_f, "%s\n", settingsStr.c_str() );
685 fclose( settings_f );
686
687 // Export as *.kicad_pcb format, using a strategy which is specifically chosen
688 // as an example on how it could also be used to send it to the system clipboard.
689
690 PCB_IO_KICAD_SEXPR pcb_io;
691
692 pcb_io.SaveBoard( fname_dump.GetAbsolutePath(), m_iface->GetBoard(), nullptr );
693
694 PROJECT* prj = m_iface->GetBoard()->GetProject();
695 prj->GetProjectFile().SaveAs( fname_dump.GetPath(), fname_dump.GetName() );
696 prj->GetLocalSettings().SaveAs( fname_dump.GetPath(), fname_dump.GetName() );
697
698 // Build log file:
699 std::vector<PNS::ITEM*> added, removed, heads;
700 m_router->GetUpdatedItems( removed, added, heads );
701
702 std::set<KIID> removedKIIDs;
703
704 for( auto item : removed )
705 {
706 wxASSERT_MSG( item->Parent() != nullptr, "removed an item with no parent uuid?" );
707
708 if( item->Parent() )
709 removedKIIDs.insert( item->Parent()->m_Uuid );
710 }
711
712 FILE* log_f = wxFopen( fname_log.GetAbsolutePath(), "wb" );
713 wxString logString = PNS::LOGGER::FormatLogFileAsString( m_router->Mode(),
714 added, removedKIIDs, heads,
715 logger->GetEvents() );
716
717 if( !log_f )
718 {
719 DisplayError( frame(), wxString::Format( _( "Unable to write '%s'." ),
720 fname_log.GetAbsolutePath() ) );
721 return;
722 }
723
724 fprintf( log_f, "%s\n", logString.c_str().AsChar() );
725 fclose( log_f );
726
727 logger->Clear(); // prevent re-entry
728 lastLoggerSize = 0;
729}
730
731
733{
734 if( aEvent.Category() == TC_VIEW || aEvent.Category() == TC_MOUSE )
735 {
736 BOX2D viewAreaD = getView()->GetGAL()->GetVisibleWorldExtents();
737 m_router->SetVisibleViewArea( BOX2ISafe( viewAreaD ) );
738 }
739
740 if( !ADVANCED_CFG::GetCfg().m_EnableRouterDump )
741 return;
742
743 if( !aEvent.IsKeyPressed() )
744 return;
745
746 switch( aEvent.KeyCode() )
747 {
748 case '0':
750 aEvent.SetPassEvent( false );
751 break;
752
753 default:
754 break;
755 }
756}
757
759{
760 bool asChanged = false;
761
762 if( aEvent.IsAction( &ACT_SwitchCornerMode45 ) )
763 {
764 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_45 );
765 asChanged = true;
766 }
767 else if( aEvent.IsAction( &ACT_SwitchCornerModeArc45 ) )
768 {
769 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_45 );
770 asChanged = true;
771 }
772 else if( aEvent.IsAction( &ACT_SwitchCornerMode90 ) )
773 {
774 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::MITERED_90 );
775 asChanged = true;
776 }
777 else if( aEvent.IsAction( &ACT_SwitchCornerModeArc90 ) )
778 {
779 m_router->Settings().SetCornerMode( DIRECTION_45::CORNER_MODE::ROUNDED_90 );
780 asChanged = true;
781 }
782
783 if( asChanged )
784 {
786 updateEndItem( aEvent );
787 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
788 }
789
790 return 0;
791}
792
793
795{
796 PCB_LAYER_ID tl = static_cast<PCB_LAYER_ID>( getView()->GetTopLayer() );
797
798 if( m_startItem )
799 {
800 int startLayer = m_iface->GetPNSLayerFromBoardLayer( tl );
801 const PNS_LAYER_RANGE& ls = m_startItem->Layers();
802
803 if( ls.Overlaps( startLayer ) )
804 return tl;
805 else
806 return m_iface->GetBoardLayerFromPNSLayer( ls.Start() );
807 }
808
809 return tl;
810}
811
812
814{
815 int activeLayer = m_iface->GetPNSLayerFromBoardLayer( frame()->GetActiveLayer() );
816 int currentLayer = m_router->GetCurrentLayer();
817
818 if( currentLayer != activeLayer )
819 m_router->SwitchLayer( activeLayer );
820
821 std::optional<int> newLayer = m_router->Sizes().PairedLayer( currentLayer );
822
823 if( !newLayer )
824 newLayer = m_router->Sizes().GetLayerTop();
825
826 m_router->SwitchLayer( *newLayer );
827 m_lastTargetLayer = m_iface->GetBoardLayerFromPNSLayer( *newLayer );
828
831}
832
833
834// N.B. aTargetLayer is a PNS layer, not a PCB_LAYER_ID
835void ROUTER_TOOL::updateSizesAfterRouterEvent( int aTargetLayer, const VECTOR2I& aPos )
836{
837 std::vector<PNS::NET_HANDLE> nets = m_router->GetCurrentNets();
838
839 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
841 std::shared_ptr<DRC_ENGINE>& drcEngine = bds.m_DRCEngine;
842 DRC_CONSTRAINT constraint;
843 PCB_LAYER_ID targetLayer = m_iface->GetBoardLayerFromPNSLayer( aTargetLayer );
844
845 PCB_TRACK dummyTrack( board() );
846 dummyTrack.SetFlags( ROUTER_TRANSIENT );
847 dummyTrack.SetLayer( targetLayer );
848 dummyTrack.SetNet( nets.empty() ? nullptr: static_cast<NETINFO_ITEM*>( nets[0] ) );
849 dummyTrack.SetStart( aPos );
850 dummyTrack.SetEnd( dummyTrack.GetStart() );
851
852 constraint = drcEngine->EvalRules( CLEARANCE_CONSTRAINT, &dummyTrack, nullptr, targetLayer );
853
854 if( constraint.m_Value.Min() >= bds.m_MinClearance )
855 {
856 sizes.SetClearance( constraint.m_Value.Min() );
857 sizes.SetClearanceSource( constraint.GetName() );
858 }
859 else
860 {
861 sizes.SetClearance( bds.m_MinClearance );
862 sizes.SetClearanceSource( _( "board minimum clearance" ) );
863 }
864
865 if( bds.UseNetClassTrack() || !sizes.TrackWidthIsExplicit() )
866 {
867 constraint = drcEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, &dummyTrack, nullptr,
868 targetLayer );
869
870 if( !constraint.IsNull() )
871 {
872 int width = sizes.TrackWidth();
873
874 // Only change the size if we're explicitly using the net class, or we're out of range
875 // for our new constraints. Otherwise, just leave the track width alone so we don't
876 // change for no reason.
877 if( bds.UseNetClassTrack()
878 || ( width < bds.m_TrackMinWidth )
879 || ( width < constraint.m_Value.Min() )
880 || ( width > constraint.m_Value.Max() ) )
881 {
882 sizes.SetTrackWidth( std::max( bds.m_TrackMinWidth, constraint.m_Value.Opt() ) );
883 }
884
885 if( sizes.TrackWidth() == constraint.m_Value.Opt() )
886 sizes.SetWidthSource( constraint.GetName() );
887 else if( sizes.TrackWidth() == bds.m_TrackMinWidth )
888 sizes.SetWidthSource( _( "board minimum track width" ) );
889 else
890 sizes.SetWidthSource( _( "existing track" ) );
891 }
892 }
893
894 if( nets.size() >= 2 && ( bds.UseNetClassDiffPair() || !sizes.TrackWidthIsExplicit() ) )
895 {
896 PCB_TRACK dummyTrackB( board() );
897 dummyTrackB.SetFlags( ROUTER_TRANSIENT );
898 dummyTrackB.SetLayer( targetLayer );
899 dummyTrackB.SetNet( static_cast<NETINFO_ITEM*>( nets[1] ) );
900 dummyTrackB.SetStart( aPos );
901 dummyTrackB.SetEnd( dummyTrackB.GetStart() );
902
903 constraint = drcEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, &dummyTrack, &dummyTrackB,
904 targetLayer );
905
906 if( !constraint.IsNull() )
907 {
908 if( bds.UseNetClassDiffPair()
909 || ( sizes.DiffPairWidth() < bds.m_TrackMinWidth )
910 || ( sizes.DiffPairWidth() < constraint.m_Value.Min() )
911 || ( sizes.DiffPairWidth() > constraint.m_Value.Max() ) )
912 {
913 sizes.SetDiffPairWidth( std::max( bds.m_TrackMinWidth, constraint.m_Value.Opt() ) );
914 }
915
916 if( sizes.DiffPairWidth() == constraint.m_Value.Opt() )
917 sizes.SetDiffPairWidthSource( constraint.GetName() );
918 else
919 sizes.SetDiffPairWidthSource( _( "board minimum track width" ) );
920 }
921
922 constraint = drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, &dummyTrack, &dummyTrackB,
923 targetLayer );
924
925 if( !constraint.IsNull() )
926 {
927 if( bds.UseNetClassDiffPair()
928 || ( sizes.DiffPairGap() < bds.m_MinClearance )
929 || ( sizes.DiffPairGap() < constraint.m_Value.Min() )
930 || ( sizes.DiffPairGap() > constraint.m_Value.Max() ) )
931 {
932 sizes.SetDiffPairGap( std::max( bds.m_MinClearance, constraint.m_Value.Opt() ) );
933 }
934
935 if( sizes.DiffPairGap() == constraint.m_Value.Opt() )
936 sizes.SetDiffPairGapSource( constraint.GetName() );
937 else
938 sizes.SetDiffPairGapSource( _( "board minimum clearance" ) );
939 }
940 }
941
942 m_router->UpdateSizes( sizes );
943}
944
945
946static VIATYPE getViaTypeFromFlags( int aFlags )
947{
948 switch( aFlags & VIA_ACTION_FLAGS::VIA_MASK )
949 {
951 return VIATYPE::THROUGH;
953 return VIATYPE::BLIND;
955 return VIATYPE::BURIED;
957 return VIATYPE::MICROVIA;
958 default:
959 wxASSERT_MSG( false, wxT( "Unhandled via type" ) );
960 return VIATYPE::THROUGH;
961 }
962}
963
964
966{
967 handleLayerSwitch( aEvent, false );
969
970 return 0;
971}
972
973
975{
976 if( !m_router->IsPlacingVia() )
977 {
978 return handleLayerSwitch( aEvent, true );
979 }
980 else
981 {
982 m_router->ToggleViaPlacement();
983 frame()->SetActiveLayer(
984 m_iface->GetBoardLayerFromPNSLayer( m_router->GetCurrentLayer() ) );
985 updateEndItem( aEvent );
987 }
988
990 return 0;
991}
992
993
994int ROUTER_TOOL::handleLayerSwitch( const TOOL_EVENT& aEvent, bool aForceVia )
995{
996 wxCHECK( m_router, 0 );
997
998 if( !IsToolActive() )
999 return 0;
1000
1001 // Ensure PNS_KICAD_IFACE (m_iface) m_board member is up to date
1002 // For some reason, this is not always the case
1003 m_iface->SetBoard( board() );
1004
1005 // First see if this is one of the switch layer commands
1006 BOARD* brd = board();
1007 LSET enabledLayers = LSET::AllCuMask( brd->GetDesignSettings().GetCopperLayerCount() );
1008 LSEQ layers = enabledLayers.UIOrder();
1009
1010 // These layers are in Board Layer UI order not PNS layer order
1011 PCB_LAYER_ID currentLayer = m_iface->GetBoardLayerFromPNSLayer( m_router->GetCurrentLayer() );
1012 PCB_LAYER_ID targetLayer = UNDEFINED_LAYER;
1013
1014 if( aEvent.IsAction( &PCB_ACTIONS::layerNext ) )
1015 {
1016 size_t idx = 0;
1017 size_t target_idx = 0;
1018 PCB_LAYER_ID lastTargetLayer = m_lastTargetLayer;
1019
1020 for( size_t i = 0; i < layers.size(); i++ )
1021 {
1022 if( layers[i] == currentLayer )
1023 {
1024 idx = i;
1025 break;
1026 }
1027 }
1028
1029 target_idx = ( idx + 1 ) % layers.size();
1030 // issue: #14480
1031 // idx + 1 layer may be invisible, switches to next visible layer
1032 for( size_t i = 0; i < layers.size() - 1; i++ )
1033 {
1034 if( brd->IsLayerVisible( layers[target_idx] ) )
1035 {
1036 targetLayer = layers[target_idx];
1037 break;
1038 }
1039 target_idx += 1;
1040
1041 if( target_idx >= layers.size() )
1042 {
1043 target_idx = 0;
1044 }
1045 }
1046
1047 if( targetLayer == UNDEFINED_LAYER )
1048 {
1049 // if there is no visible layers
1050 return 0;
1051 }
1052 }
1053 else if( aEvent.IsAction( &PCB_ACTIONS::layerPrev ) )
1054 {
1055 size_t idx = 0;
1056 size_t target_idx = 0;
1057
1058 for( size_t i = 0; i < layers.size(); i++ )
1059 {
1060 if( layers[i] == currentLayer )
1061 {
1062 idx = i;
1063 break;
1064 }
1065 }
1066
1067 target_idx = ( idx > 0 ) ? ( idx - 1 ) : ( layers.size() - 1 );
1068
1069 for( size_t i = 0; i < layers.size() - 1; i++ )
1070 {
1071 if( brd->IsLayerVisible( layers[target_idx] ) )
1072 {
1073 targetLayer = layers[target_idx];
1074 break;
1075 }
1076
1077 if( target_idx > 0 )
1078 target_idx -= 1;
1079 else
1080 target_idx = layers.size() - 1;
1081 }
1082
1083 if( targetLayer == UNDEFINED_LAYER )
1084 {
1085 // if there is no visible layers
1086 return 0;
1087 }
1088 }
1089 else if( aEvent.IsAction( &PCB_ACTIONS::layerToggle ) )
1090 {
1091 PCB_SCREEN* screen = frame()->GetScreen();
1092
1093 if( currentLayer == screen->m_Route_Layer_TOP )
1094 targetLayer = screen->m_Route_Layer_BOTTOM;
1095 else
1096 targetLayer = screen->m_Route_Layer_TOP;
1097 }
1099 {
1100 targetLayer = aEvent.Parameter<PCB_LAYER_ID>();
1101
1102 if( !enabledLayers.test( targetLayer ) )
1103 return 0;
1104 }
1105
1106 if( targetLayer != UNDEFINED_LAYER )
1107 {
1108 if( targetLayer == currentLayer )
1109 return 0;
1110
1111 if( !aForceVia && m_router && m_router->SwitchLayer( m_iface->GetPNSLayerFromBoardLayer( targetLayer ) ) )
1112 {
1113 updateEndItem( aEvent );
1114 updateSizesAfterRouterEvent( m_iface->GetPNSLayerFromBoardLayer( targetLayer ), m_endSnapPoint );
1115 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
1116 return 0;
1117 }
1118 }
1119
1121 const int layerCount = bds.GetCopperLayerCount();
1122
1123 PCB_LAYER_ID pairTop = frame()->GetScreen()->m_Route_Layer_TOP;
1124 PCB_LAYER_ID pairBottom = frame()->GetScreen()->m_Route_Layer_BOTTOM;
1125
1126 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
1127
1128 VIATYPE viaType = VIATYPE::THROUGH;
1129 bool selectLayer = false;
1130
1131 // Otherwise it is one of the router-specific via commands
1132 if( targetLayer == UNDEFINED_LAYER )
1133 {
1134 const int actViaFlags = aEvent.Parameter<int>();
1135 selectLayer = actViaFlags & VIA_ACTION_FLAGS::SELECT_LAYER;
1136
1137 viaType = getViaTypeFromFlags( actViaFlags );
1138
1139 // ask the user for a target layer
1140 if( selectLayer )
1141 {
1142 // When the currentLayer is undefined, trying to place a via does not work
1143 // because it means there is no track in progress, and some other variables
1144 // values are not defined like m_endSnapPoint. So do not continue.
1145 if( currentLayer == UNDEFINED_LAYER )
1146 return 0;
1147
1148 wxPoint endPoint = ToWxPoint( view()->ToScreen( m_endSnapPoint ) );
1149 endPoint = frame()->GetCanvas()->ClientToScreen( endPoint );
1150
1151 // Build the list of not allowed layer for the target layer
1152 LSET not_allowed_ly = LSET::AllNonCuMask();
1153
1154 if( viaType != VIATYPE::THROUGH )
1155 not_allowed_ly.set( currentLayer );
1156
1157 targetLayer = frame()->SelectOneLayer( static_cast<PCB_LAYER_ID>( currentLayer ),
1158 not_allowed_ly, endPoint );
1159
1160 // Reset the cursor to the end of the track
1162
1163 if( targetLayer == UNDEFINED_LAYER ) // canceled by user
1164 return 0;
1165
1166 // One cannot place a blind/buried via on only one layer:
1167 if( viaType != VIATYPE::THROUGH )
1168 {
1169 if( currentLayer == targetLayer )
1170 return 0;
1171 }
1172 }
1173 }
1174
1175 // fixme: P&S supports more than one fixed layer pair. Update the dialog?
1176 sizes.ClearLayerPairs();
1177
1178 // Convert blind/buried via to a through hole one, if it goes through all layers
1179 if( viaType != VIATYPE::THROUGH
1180 && ( ( targetLayer == B_Cu && currentLayer == F_Cu )
1181 || ( targetLayer == F_Cu && currentLayer == B_Cu ) ) )
1182 {
1183 viaType = VIATYPE::THROUGH;
1184 }
1185
1186 if( targetLayer == UNDEFINED_LAYER )
1187 {
1188 // Implicit layer selection
1189 if( viaType == VIATYPE::THROUGH )
1190 {
1191 // Try to switch to the nearest ratnest item's layer if we have one
1192 VECTOR2I otherEnd;
1193 PNS_LAYER_RANGE otherEndLayers;
1194 PNS::ITEM* otherEndItem = nullptr;
1195
1196 if( !m_router->GetNearestRatnestAnchor( otherEnd, otherEndLayers, otherEndItem ) )
1197 {
1198 // use the default layer pair
1199 currentLayer = pairTop;
1200 targetLayer = pairBottom;
1201 }
1202 else
1203 {
1204 // use the layer of the other end, unless it is the same layer as the currently active layer, in which
1205 // case use the layer pair (if applicable)
1206 PCB_LAYER_ID otherEndLayerPcbId = m_iface->GetBoardLayerFromPNSLayer( otherEndLayers.Start() );
1207 const std::optional<int> pairedLayerPns = m_router->Sizes().PairedLayer( m_router->GetCurrentLayer() );
1208
1209 if( currentLayer == otherEndLayerPcbId && pairedLayerPns.has_value() )
1210 {
1211 // Closest ratsnest layer is the same as the active layer - assume the via is being placed for
1212 // other routing reasons and switch the layer
1213 targetLayer = m_iface->GetBoardLayerFromPNSLayer( *pairedLayerPns );
1214 }
1215 else
1216 {
1217 targetLayer = m_iface->GetBoardLayerFromPNSLayer( otherEndLayers.Start() );
1218 }
1219 }
1220 }
1221 else
1222 {
1223 if( currentLayer == pairTop || currentLayer == pairBottom )
1224 {
1225 // the current layer is on the defined layer pair,
1226 // swap to the other side
1227 currentLayer = pairTop;
1228 targetLayer = pairBottom;
1229 }
1230 else
1231 {
1232 // the current layer is not part of the current layer pair,
1233 // so fallback and swap to the top layer of the pair by default
1234 targetLayer = pairTop;
1235 }
1236
1237 // Do not create a broken via (i.e. a via on only one copper layer)
1238 if( currentLayer == targetLayer )
1239 {
1240 WX_INFOBAR* infobar = frame()->GetInfoBar();
1241 infobar->ShowMessageFor( _( "Via needs 2 different layers." ),
1242 2000, wxICON_ERROR,
1244 return 0;
1245 }
1246 }
1247 }
1248
1249 sizes.SetViaDiameter( bds.m_ViasMinSize );
1250 sizes.SetViaDrill( bds.m_MinThroughDrill );
1251
1252 if( bds.UseNetClassVia() || viaType == VIATYPE::MICROVIA )
1253 {
1254 PCB_VIA dummyVia( board() );
1255 dummyVia.SetViaType( viaType );
1256 dummyVia.SetLayerPair( currentLayer, targetLayer );
1257
1258 if( !m_router->GetCurrentNets().empty() )
1259 dummyVia.SetNet( static_cast<NETINFO_ITEM*>( m_router->GetCurrentNets()[0] ) );
1260
1261 DRC_CONSTRAINT constraint;
1262
1263 constraint = bds.m_DRCEngine->EvalRules( VIA_DIAMETER_CONSTRAINT, &dummyVia, nullptr,
1264 currentLayer );
1265
1266 if( !constraint.IsNull() )
1267 sizes.SetViaDiameter( constraint.m_Value.Opt() );
1268
1269 constraint = bds.m_DRCEngine->EvalRules( HOLE_SIZE_CONSTRAINT, &dummyVia, nullptr,
1270 currentLayer );
1271
1272 if( !constraint.IsNull() )
1273 sizes.SetViaDrill( constraint.m_Value.Opt() );
1274 }
1275 else
1276 {
1277 sizes.SetViaDiameter( bds.GetCurrentViaSize() );
1278 sizes.SetViaDrill( bds.GetCurrentViaDrill() );
1279 }
1280
1281 sizes.SetViaType( viaType );
1282 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( currentLayer ),
1283 m_iface->GetPNSLayerFromBoardLayer( targetLayer ) );
1284
1285 m_router->UpdateSizes( sizes );
1286
1287 if( !m_router->IsPlacingVia() )
1288 m_router->ToggleViaPlacement();
1289
1290 if( m_router->RoutingInProgress() )
1291 {
1292 updateEndItem( aEvent );
1294 }
1295 else
1296 {
1297 updateStartItem( aEvent );
1298 }
1299
1300 return 0;
1301}
1302
1303
1305{
1308 int pnsLayer = m_iface->GetPNSLayerFromBoardLayer( pcbLayer );
1309
1310 if( !::IsCopperLayer( pcbLayer ) )
1311 {
1312 editFrame->ShowInfoBarError( _( "Tracks on Copper layers only." ) );
1313 return false;
1314 }
1315
1317 editFrame->SetActiveLayer( pcbLayer );
1318
1319 if( !getView()->IsLayerVisible( pcbLayer ) )
1320 {
1321 editFrame->GetAppearancePanel()->SetLayerVisible( pcbLayer, true );
1322 editFrame->GetCanvas()->Refresh();
1323 }
1324
1325 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
1326
1327 m_iface->SetStartLayerFromPCBNew( pcbLayer );
1328
1329 frame()->GetBoard()->GetDesignSettings().m_TempOverrideTrackWidth = false;
1330 m_iface->ImportSizes( sizes, m_startItem, nullptr, aStartPosition );
1331 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( frame()->GetScreen()->m_Route_Layer_TOP ),
1332 m_iface->GetPNSLayerFromBoardLayer( frame()->GetScreen()->m_Route_Layer_BOTTOM ) );
1333
1334 m_router->UpdateSizes( sizes );
1335
1336 if( m_startItem && m_startItem->Net() )
1337 {
1339 {
1340 if( PNS::NET_HANDLE coupledNet = m_router->GetRuleResolver()->DpCoupledNet( m_startItem->Net() ) )
1341 highlightNets( true, { m_startItem->Net(), coupledNet } );
1342 }
1343 else
1344 {
1345 highlightNets( true, { m_startItem->Net() } );
1346 }
1347 }
1348
1349 controls()->SetAutoPan( true );
1350
1351 if( !m_router->StartRouting( m_startSnapPoint, m_startItem, pnsLayer ) )
1352 {
1353 // It would make more sense to leave the net highlighted as the higher-contrast mode
1354 // makes the router clearances more visible. However, since we just started routing
1355 // the conversion of the screen from low contrast to high contrast is a bit jarring and
1356 // makes the infobar coming up less noticeable.
1357 highlightNets( false );
1358
1359 frame()->ShowInfoBarError( m_router->FailureReason(), true,
1360 [&]()
1361 {
1362 m_router->ClearViewDecorations();
1363 } );
1364
1365 controls()->SetAutoPan( false );
1366 return false;
1367 }
1368
1369 m_endItem = nullptr;
1371
1373 frame()->UndoRedoBlock( true );
1374
1375 return true;
1376}
1377
1378
1380{
1381 m_router->StopRouting();
1382
1383 m_startItem = nullptr;
1384 m_endItem = nullptr;
1385
1386 frame()->SetActiveLayer( m_originalActiveLayer );
1388 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1389 controls()->SetAutoPan( false );
1390 controls()->ForceCursorPosition( false );
1391 frame()->UndoRedoBlock( false );
1392 highlightNets( false );
1393
1394 return true;
1395}
1396
1397
1399{
1400 m_router->ClearViewDecorations();
1401
1402 if( !prepareInteractive( aStartPosition ) )
1403 return;
1404
1405 auto setCursor =
1406 [&]()
1407 {
1408 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1409 };
1410
1411 auto syncRouterAndFrameLayer =
1412 [&]()
1413 {
1414 int pnsLayer = m_router->GetCurrentLayer();
1415 PCB_LAYER_ID pcbLayer = m_iface->GetBoardLayerFromPNSLayer( pnsLayer );
1417
1418 editFrame->SetActiveLayer( pcbLayer );
1419
1420 if( !getView()->IsLayerVisible( pcbLayer ) )
1421 {
1422 editFrame->GetAppearancePanel()->SetLayerVisible( pcbLayer, true );
1423 editFrame->GetCanvas()->Refresh();
1424 }
1425 };
1426
1427 // Set initial cursor
1428 setCursor();
1429
1430 while( TOOL_EVENT* evt = Wait() )
1431 {
1432 setCursor();
1433
1434 // Don't crash if we missed an operation that canceled routing.
1435 if( !m_router->RoutingInProgress() )
1436 {
1437 if( evt->IsCancelInteractive() )
1438 m_cancelled = true;
1439
1440 break;
1441 }
1442
1443 handleCommonEvents( *evt );
1444
1445 if( evt->IsMotion() )
1446 {
1447 updateEndItem( *evt );
1449 }
1450 else if( evt->IsAction( &PCB_ACTIONS::routerUndoLastSegment )
1451 || evt->IsAction( &ACTIONS::doDelete )
1452 || evt->IsAction( &ACTIONS::undo ) )
1453 {
1454 if( std::optional<VECTOR2I> last = m_router->UndoLastSegment() )
1455 {
1456 getViewControls()->WarpMouseCursor( last.value(), true );
1457 evt->SetMousePosition( last.value() );
1458 }
1459
1460 updateEndItem( *evt );
1462 }
1463 else if( evt->IsAction( &PCB_ACTIONS::routerAttemptFinish ) )
1464 {
1465 if( m_toolMgr->IsContextMenuActive() )
1466 m_toolMgr->WarpAfterContextMenu();
1467
1468 bool* autoRouted = evt->Parameter<bool*>();
1469
1470 if( m_router->Finish() )
1471 {
1472 // When we're routing a group of signals automatically we want
1473 // to break up the undo stack every time we have to manually route
1474 // so the user gets nice checkpoints. Remove the APPEND_UNDO flag.
1475 if( autoRouted != nullptr )
1476 *autoRouted = true;
1477
1478 break;
1479 }
1480 else
1481 {
1482 // This acts as check if we were called by the autorouter; we don't want
1483 // to reset APPEND_UNDO if we're auto finishing after route-other-end
1484 if( autoRouted != nullptr )
1485 {
1486 *autoRouted = false;
1487 m_iface->SetCommitFlags( 0 );
1488 }
1489
1490 // Warp the mouse so the user is at the point we managed to route to
1491 controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true );
1492 }
1493 }
1494 else if( evt->IsAction( &PCB_ACTIONS::routerContinueFromEnd ) )
1495 {
1496 bool needsAppend = m_router->Placer()->HasPlacedAnything();
1497
1498 if( m_router->ContinueFromEnd( &m_startItem ) )
1499 {
1500 syncRouterAndFrameLayer();
1501 m_startSnapPoint = m_router->Placer()->CurrentStart();
1502 updateEndItem( *evt );
1503
1504 // Warp the mouse to wherever we actually ended up routing to
1505 controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true );
1506
1507 // We want the next router commit to be one undo at the UI layer
1508 m_iface->SetCommitFlags( needsAppend ? APPEND_UNDO : 0 );
1509 }
1510 else
1511 {
1512 frame()->ShowInfoBarError( m_router->FailureReason(), true );
1513 }
1514 }
1515 else if( evt->IsClick( BUT_LEFT )
1516 || evt->IsDrag( BUT_LEFT )
1517 || evt->IsAction( &PCB_ACTIONS::routeSingleTrack ) )
1518 {
1519 updateEndItem( *evt );
1520 bool needLayerSwitch = m_router->IsPlacingVia();
1521 bool forceFinish = evt->Modifier( MD_SHIFT );
1522 bool forceCommit = false;
1523
1524 if( m_router->FixRoute( m_endSnapPoint, m_endItem, false, forceCommit ) )
1525 break;
1526
1527 if( needLayerSwitch )
1528 {
1530 }
1531 else
1532 {
1534 }
1535
1536 // Synchronize the indicated layer
1537 syncRouterAndFrameLayer();
1538
1539 updateEndItem( *evt );
1541 m_startItem = nullptr;
1542 }
1543 else if( evt->IsAction( &ACT_SwitchPosture ) )
1544 {
1545 m_router->FlipPosture();
1546 updateEndItem( *evt );
1547 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
1548 }
1549 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
1550 {
1551 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1552 controls()->SetAutoPan( false );
1553 {
1554 m_toolMgr->RunAction( ACT_CustomTrackWidth );
1555 }
1556 controls()->SetAutoPan( true );
1557 setCursor();
1559 }
1560 else if( evt->IsAction( &ACTIONS::finishInteractive ) || evt->IsDblClick( BUT_LEFT ) )
1561 {
1562 // Stop current routing:
1563 bool forceFinish = true;
1564 bool forceCommit = false;
1565
1566 m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit );
1567 break;
1568 }
1569 else if( evt->IsCancelInteractive() || evt->IsActivate()
1570 || evt->IsAction( &PCB_ACTIONS::routerInlineDrag ) )
1571 {
1572 if( evt->IsCancelInteractive() && !m_router->RoutingInProgress() )
1573 m_cancelled = true;
1574
1575 if( evt->IsActivate() && !evt->IsMoveTool() )
1576 m_cancelled = true;
1577
1578 break;
1579 }
1580 else if( evt->IsUndoRedo() )
1581 {
1582 // We're in an UndoRedoBlock. If we get here, something's broken.
1583 wxFAIL;
1584 break;
1585 }
1586 else if( evt->IsClick( BUT_RIGHT ) )
1587 {
1588 m_menu->ShowContextMenu( selection() );
1589 }
1590 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
1591 // but we don't at present have that, so we just knock out some of the egregious ones.
1592 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
1593 {
1594 wxBell();
1595 }
1596 else
1597 {
1598 evt->SetPassEvent();
1599 }
1600 }
1601
1602 m_router->CommitRouting();
1603 // Reset to normal for next route
1604 m_iface->SetCommitFlags( 0 );
1605
1607}
1608
1609
1611{
1612 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
1613 DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( frame(), sizes );
1614
1615 if( settingsDlg.ShowModal() == wxID_OK )
1616 {
1617 m_router->UpdateSizes( sizes );
1618 m_savedSizes = sizes;
1619
1620 BOARD_DESIGN_SETTINGS& bds = frame()->GetBoard()->GetDesignSettings();
1622 bds.SetCustomDiffPairGap( sizes.DiffPairGap() );
1624 }
1625
1626 return 0;
1627}
1628
1629
1631{
1632 DIALOG_PNS_SETTINGS settingsDlg( frame(), m_router->Settings() );
1633
1634 settingsDlg.ShowModal();
1635
1637
1638 return 0;
1639}
1640
1641
1643{
1644 PNS::PNS_MODE mode = aEvent.Parameter<PNS::PNS_MODE>();
1645 PNS::ROUTING_SETTINGS& settings = m_router->Settings();
1646
1647 settings.SetMode( mode );
1649
1650 return 0;
1651}
1652
1653
1655{
1656 PNS::ROUTING_SETTINGS& settings = m_router->Settings();
1657 PNS::PNS_MODE mode = settings.Mode();
1658
1659 switch( mode )
1660 {
1661 case PNS::RM_MarkObstacles: mode = PNS::RM_Shove; break;
1662 case PNS::RM_Shove: mode = PNS::RM_Walkaround; break;
1663 case PNS::RM_Walkaround: mode = PNS::RM_MarkObstacles; break;
1664 }
1665
1666 settings.SetMode( mode );
1668
1669 return 0;
1670}
1671
1672
1674{
1675 return m_router->Settings().Mode();
1676}
1677
1678
1680{
1681 return m_router->RoutingInProgress();
1682}
1683
1684
1686{
1687 if( !m_startItem )
1688 return;
1689
1691 m_router->BreakSegmentOrArc( m_startItem, m_startSnapPoint );
1692}
1693
1694
1696{
1700 PCB_LAYER_ID originalLayer = frame->GetActiveLayer();
1701 bool autoRoute = aEvent.Matches( PCB_ACTIONS::routerAutorouteSelected.MakeEvent() );
1702 bool otherEnd = aEvent.Matches( PCB_ACTIONS::routerRouteSelectedFromEnd.MakeEvent() );
1703
1704 if( m_router->RoutingInProgress() )
1705 return 0;
1706
1707 // Save selection then clear it for interactive routing
1708 PCB_SELECTION selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
1709
1710 if( selection.Size() == 0 )
1711 return 0;
1712
1713 m_toolMgr->RunAction( ACTIONS::selectionClear );
1714
1715 TOOL_EVENT pushedEvent = aEvent;
1716 frame->PushTool( aEvent );
1717
1718 auto setCursor =
1719 [&]()
1720 {
1721 frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1722 };
1723
1724 Activate();
1725 // Must be done after Activate() so that it gets set into the correct context
1726 controls->ShowCursor( true );
1727 controls->ForceCursorPosition( false );
1728 // Set initial cursor
1729 setCursor();
1730
1731 // Get all connected board items, adding pads for any footprints selected
1732 std::vector<BOARD_CONNECTED_ITEM*> itemList;
1733
1734 for( EDA_ITEM* item : selection.GetItemsSortedBySelectionOrder() )
1735 {
1736 if( item->Type() == PCB_FOOTPRINT_T )
1737 {
1738 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
1739 itemList.push_back( pad );
1740 }
1741 else if( dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) != nullptr )
1742 {
1743 itemList.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1744 }
1745 }
1746
1747 std::shared_ptr<CONNECTIVITY_DATA> connectivity = frame->GetBoard()->GetConnectivity();
1748
1749 // For putting sequential tracks that successfully autoroute into one undo commit
1750 bool groupStart = true;
1751
1752 for( BOARD_CONNECTED_ITEM* item : itemList )
1753 {
1754 // This code is similar to GetRatsnestForPad() but it only adds the anchor for
1755 // the side of the connectivity on this pad. It also checks for ratsnest points
1756 // inside the pad (like a trace end) and counts them.
1757 RN_NET* net = connectivity->GetRatsnestForNet( item->GetNetCode() );
1758
1759 if( !net )
1760 continue;
1761
1762 std::vector<std::shared_ptr<const CN_ANCHOR>> anchors;
1763
1764 for( const CN_EDGE& edge : net->GetEdges() )
1765 {
1766 std::shared_ptr<const CN_ANCHOR> target = edge.GetTargetNode();
1767 std::shared_ptr<const CN_ANCHOR> source = edge.GetSourceNode();
1768
1769 if( !source || source->Dirty() || !target || target->Dirty() )
1770 continue;
1771
1772 if( source->Parent() == item )
1773 anchors.push_back( source );
1774 else if( target->Parent() == item )
1775 anchors.push_back( target );
1776 }
1777
1778 // Route them
1779 for( std::shared_ptr<const CN_ANCHOR> anchor : anchors )
1780 {
1781 if( !anchor->Valid() )
1782 continue;
1783
1784 // Try to return to the original layer as indicating the user's preferred
1785 // layer for autorouting tracks. The layer can be changed by the user to
1786 // finish tracks that can't complete automatically, but should be changed
1787 // back after.
1788 if( frame->GetActiveLayer() != originalLayer )
1789 frame->SetActiveLayer( originalLayer );
1790
1791 VECTOR2I ignore;
1792 m_startItem = m_router->GetWorld()->FindItemByParent( anchor->Parent() );
1793 m_startSnapPoint = anchor->Pos();
1794 m_router->SetMode( mode );
1795
1796 // Prime the interactive routing to attempt finish if we are autorouting
1797 bool autoRouted = false;
1798
1799 if( autoRoute )
1800 m_toolMgr->PostAction( PCB_ACTIONS::routerAttemptFinish, &autoRouted );
1801 else if( otherEnd )
1803
1804 // We want autorouted tracks to all be in one undo group except for
1805 // any tracks that need to be manually finished.
1806 // The undo appending for manually finished tracks is handled in peformRouting()
1807 if( groupStart )
1808 groupStart = false;
1809 else
1810 m_iface->SetCommitFlags( APPEND_UNDO );
1811
1812 // Start interactive routing. Will automatically finish if possible.
1814
1815 // Route didn't complete automatically, need to a new undo commit
1816 // for the next line so those can group as far as they autoroute
1817 if( !autoRouted )
1818 groupStart = true;
1819 }
1820 }
1821
1822 m_iface->SetCommitFlags( 0 );
1823 frame->PopTool( pushedEvent );
1824 return 0;
1825}
1826
1827
1829{
1830 if( m_inRouterTool )
1831 return 0;
1832
1834
1838
1839 if( m_router->RoutingInProgress() )
1840 {
1841 if( m_router->Mode() == mode )
1842 return 0;
1843 else
1844 m_router->StopRouting();
1845 }
1846
1847 // Deselect all items
1848 m_toolMgr->RunAction( ACTIONS::selectionClear );
1849
1850 TOOL_EVENT pushedEvent = aEvent;
1851 frame->PushTool( aEvent );
1852
1853 auto setCursor =
1854 [&]()
1855 {
1856 frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1857 };
1858
1859 Activate();
1860 // Must be done after Activate() so that it gets set into the correct context
1861 controls->ShowCursor( true );
1862 controls->ForceCursorPosition( false );
1863 // Set initial cursor
1864 setCursor();
1865
1866 m_router->SetMode( mode );
1867 m_cancelled = false;
1868
1869 if( aEvent.HasPosition() )
1870 m_toolMgr->PrimeTool( aEvent.Position() );
1871
1872 // Main loop: keep receiving events
1873 while( TOOL_EVENT* evt = Wait() )
1874 {
1875 if( !evt->IsDrag() )
1876 setCursor();
1877
1878 if( evt->IsCancelInteractive() )
1879 {
1880 frame->PopTool( pushedEvent );
1881 break;
1882 }
1883 else if( evt->IsActivate() )
1884 {
1885 if( evt->IsMoveTool() || evt->IsEditorTool() )
1886 {
1887 // leave ourselves on the stack so we come back after the move
1888 break;
1889 }
1890 else
1891 {
1892 frame->PopTool( pushedEvent );
1893 break;
1894 }
1895 }
1896 else if( evt->Action() == TA_UNDO_REDO_PRE )
1897 {
1898 m_router->ClearWorld();
1899 }
1900 else if( evt->Action() == TA_UNDO_REDO_POST || evt->Action() == TA_MODEL_CHANGE )
1901 {
1902 m_router->SyncWorld();
1903 }
1904 else if( evt->IsMotion() )
1905 {
1906 updateStartItem( *evt );
1907 }
1908 else if( evt->IsAction( &PCB_ACTIONS::dragFreeAngle ) )
1909 {
1910 updateStartItem( *evt, true );
1912 }
1913 else if( evt->IsAction( &PCB_ACTIONS::drag45Degree ) )
1914 {
1915 updateStartItem( *evt, true );
1917 }
1918 else if( evt->IsAction( &PCB_ACTIONS::breakTrack ) )
1919 {
1920 updateStartItem( *evt, true );
1921 breakTrack( );
1922 evt->SetPassEvent( false );
1923 }
1924 else if( evt->IsClick( BUT_LEFT )
1925 || evt->IsAction( &PCB_ACTIONS::routeSingleTrack )
1926 || evt->IsAction( &PCB_ACTIONS::routeDiffPair ) )
1927 {
1928 updateStartItem( *evt );
1929
1930 if( evt->HasPosition() )
1931 performRouting( evt->Position() );
1932 }
1933 else if( evt->IsAction( &ACT_PlaceThroughVia ) )
1934 {
1935 m_toolMgr->RunAction( PCB_ACTIONS::layerToggle );
1936 }
1937 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1938 {
1939 m_router->SwitchLayer( m_iface->GetPNSLayerFromBoardLayer( frame->GetActiveLayer() ) );
1940 updateStartItem( *evt );
1941 updateSizesAfterRouterEvent( m_iface->GetPNSLayerFromBoardLayer( frame->GetActiveLayer() ), m_startSnapPoint );
1942 }
1943 else if( evt->IsKeyPressed() )
1944 {
1945 // wxWidgets fails to correctly translate shifted keycodes on the wxEVT_CHAR_HOOK
1946 // event so we need to process the wxEVT_CHAR event that will follow as long as we
1947 // pass the event.
1948 evt->SetPassEvent();
1949 }
1950 else if( evt->IsClick( BUT_RIGHT ) )
1951 {
1952 m_menu->ShowContextMenu( selection() );
1953 }
1954 else
1955 {
1956 evt->SetPassEvent();
1957 }
1958
1959 if( m_cancelled )
1960 {
1961 frame->PopTool( pushedEvent );
1962 break;
1963 }
1964 }
1965
1966 // Store routing settings till the next invocation
1967 m_savedSizes = m_router->Sizes();
1968 m_router->ClearViewDecorations();
1969
1970 return 0;
1971}
1972
1973
1975{
1976 m_router->ClearViewDecorations();
1977
1978 view()->ClearPreview();
1979 view()->InitPreview();
1980
1982
1983 if( m_startItem && m_startItem->IsLocked() )
1984 {
1985 KIDIALOG dlg( frame(), _( "The selected item is locked." ), _( "Confirmation" ),
1986 wxOK | wxCANCEL | wxICON_WARNING );
1987 dlg.SetOKLabel( _( "Drag Anyway" ) );
1988 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
1989
1990 if( dlg.ShowModal() == wxID_CANCEL )
1991 return;
1992 }
1993
1994 // We don't support dragging arcs inside the PNS right now
1995 if( m_startItem && m_startItem->Kind() == PNS::ITEM::ARC_T )
1996 {
1997 if( m_router->RoutingInProgress() )
1998 m_router->StopRouting();
1999
2000 m_startItem = nullptr;
2001
2002 m_gridHelper->SetAuxAxes( false );
2003 ctls->ForceCursorPosition( false );
2004 highlightNets( false );
2005
2006 m_cancelled = true;
2007
2008 m_toolMgr->PostAction( PCB_ACTIONS::drag45Degree );
2009
2010 return;
2011 }
2012
2013 bool dragStarted = m_router->StartDragging( m_startSnapPoint, m_startItem, aMode );
2014
2015 if( !dragStarted )
2016 return;
2017
2018 if( m_startItem && m_startItem->Net() )
2019 highlightNets( true, { m_startItem->Net() } );
2020
2021 ctls->SetAutoPan( true );
2022 m_gridHelper->SetAuxAxes( true, m_startSnapPoint );
2023 frame()->UndoRedoBlock( true );
2024
2025 while( TOOL_EVENT* evt = Wait() )
2026 {
2027 ctls->ForceCursorPosition( false );
2028
2029 if( evt->IsMotion() )
2030 {
2031 updateEndItem( *evt );
2033
2034 if( PNS::DRAG_ALGO* dragger = m_router->GetDragger() )
2035 {
2036 bool dragStatus;
2037
2038 if( dragger->GetForceMarkObstaclesMode( &dragStatus ) )
2039 {
2040 view()->ClearPreview();
2041
2042 if( !dragStatus )
2043 {
2044 wxString hint;
2045 hint.Printf( _( "(%s to commit anyway.)" ),
2047
2049 statusItem->SetMessage( _( "Track violates DRC." ) );
2050 statusItem->SetHint( hint );
2051 statusItem->SetPosition( frame()->GetToolManager()->GetMousePosition() );
2052 view()->AddToPreview( statusItem );
2053 }
2054 }
2055 }
2056 }
2057 else if( evt->IsClick( BUT_LEFT ) )
2058 {
2059 bool forceFinish = false;
2060 bool forceCommit = evt->Modifier( MD_CTRL );
2061
2062 if( m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit ) )
2063 break;
2064 }
2065 else if( evt->IsClick( BUT_RIGHT ) )
2066 {
2067 m_menu->ShowContextMenu( selection() );
2068 }
2069 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2070 {
2071 if( evt->IsCancelInteractive() && !m_startItem )
2072 m_cancelled = true;
2073
2074 if( evt->IsActivate() && !evt->IsMoveTool() )
2075 m_cancelled = true;
2076
2077 break;
2078 }
2079 else if( evt->IsUndoRedo() )
2080 {
2081 // We're in an UndoRedoBlock. If we get here, something's broken.
2082 wxFAIL;
2083 break;
2084 }
2085 else if( evt->Category() == TC_COMMAND )
2086 {
2087 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2088 // but we don't at present have that, so we just knock out some of the egregious ones.
2089 if( evt->IsAction( &ACTIONS::cut )
2090 || evt->IsAction( &ACTIONS::copy )
2091 || evt->IsAction( &ACTIONS::paste )
2092 || evt->IsAction( &ACTIONS::pasteSpecial )
2094 {
2095 wxBell();
2096 }
2097 // treat an undo as an escape
2098 else if( evt->IsAction( &ACTIONS::undo ) )
2099 {
2100 if( m_startItem )
2101 break;
2102 else
2103 wxBell();
2104 }
2105 else
2106 {
2107 evt->SetPassEvent();
2108 }
2109 }
2110 else
2111 {
2112 evt->SetPassEvent();
2113 }
2114
2115 handleCommonEvents( *evt );
2116 }
2117
2118 view()->ClearPreview();
2119 view()->ShowPreview( false );
2120
2121 if( m_router->RoutingInProgress() )
2122 m_router->StopRouting();
2123
2124 m_startItem = nullptr;
2125
2126 m_gridHelper->SetAuxAxes( false );
2127 frame()->UndoRedoBlock( false );
2128 ctls->SetAutoPan( false );
2129 ctls->ForceCursorPosition( false );
2130 highlightNets( false );
2131}
2132
2133
2135 PCB_SELECTION_TOOL* aSelTool )
2136{
2137 /*
2138 * If the collection contains a trivial line corner (two connected segments)
2139 * or a non-fanout-via (a via with no more than two connected segments), then
2140 * trim the collection down to a single item (which one won't matter since
2141 * they're all connected).
2142 */
2143
2144 // First make sure we've got something that *might* match.
2145 int vias = aCollector.CountType( PCB_VIA_T );
2146 int traces = aCollector.CountType( PCB_TRACE_T );
2147 int arcs = aCollector.CountType( PCB_ARC_T );
2148
2149 // We eliminate arcs because they are not supported in the inline drag code.
2150 if( arcs > 0 )
2151 return;
2152
2153 // We need to have at least 1 via or track
2154 if( vias + traces == 0 )
2155 return;
2156
2157 // We cannot drag more than one via at a time
2158 if( vias > 1 )
2159 return;
2160
2161 // We cannot drag more than two track segments at a time
2162 if( traces > 2 )
2163 return;
2164
2165 // Fetch first PCB_TRACK (via or trace) as our reference
2166 PCB_TRACK* reference = nullptr;
2167
2168 for( int i = 0; !reference && i < aCollector.GetCount(); i++ )
2169 reference = dynamic_cast<PCB_TRACK*>( aCollector[i] );
2170
2171 // This should never happen, but just in case...
2172 if( !reference )
2173 return;
2174
2175 int refNet = reference->GetNetCode();
2176
2177 VECTOR2I refPoint( aPt.x, aPt.y );
2178 EDA_ITEM_FLAGS flags = reference->IsPointOnEnds( refPoint, -1 );
2179
2180 if( flags & STARTPOINT )
2181 refPoint = reference->GetStart();
2182 else if( flags & ENDPOINT )
2183 refPoint = reference->GetEnd();
2184
2185 // Check all items to ensure that any TRACKs are co-terminus with the reference and on
2186 // the same net.
2187 for( int i = 0; i < aCollector.GetCount(); i++ )
2188 {
2189 PCB_TRACK* neighbor = dynamic_cast<PCB_TRACK*>( aCollector[i] );
2190
2191 if( neighbor && neighbor != reference )
2192 {
2193 if( neighbor->GetNetCode() != refNet )
2194 return;
2195
2196 if( neighbor->GetStart() != refPoint && neighbor->GetEnd() != refPoint )
2197 return;
2198 }
2199 }
2200
2201 // Selection meets criteria; trim it to the reference item.
2202 aCollector.Empty();
2203 aCollector.Append( reference );
2204}
2205
2206
2207bool ROUTER_TOOL::CanInlineDrag( int aDragMode )
2208{
2210 const PCB_SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2211
2212 if( selection.Size() == 1 )
2213 {
2214 return selection.Front()->IsType( GENERAL_COLLECTOR::DraggableItems );
2215 }
2216 else if( selection.CountType( PCB_FOOTPRINT_T ) == selection.Size() )
2217 {
2218 // Footprints cannot be dragged freely.
2219 return !( aDragMode & PNS::DM_FREE_ANGLE );
2220 }
2221 else if( selection.CountType( PCB_TRACE_T ) == selection.Size() )
2222 {
2223 return true;
2224 }
2225
2226 return false;
2227}
2228
2229
2230void ROUTER_TOOL::restoreSelection( const PCB_SELECTION& aOriginalSelection )
2231{
2232 EDA_ITEMS selItems;
2233 std::copy( aOriginalSelection.Items().begin(), aOriginalSelection.Items().end(), std::back_inserter( selItems ) );
2234 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &selItems );
2235}
2236
2237
2239{
2240 const PCB_SELECTION selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2241
2242 if( selection.Empty() )
2244
2245 if( selection.Empty() || !selection.Front()->IsBOARD_ITEM() )
2246 return 0;
2247
2248 // selection gets cleared in the next action, we need a copy of the selected items.
2249 std::deque<EDA_ITEM*> selectedItems = selection.GetItems();
2250
2251 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
2252
2253 if( item->Type() != PCB_TRACE_T
2254 && item->Type() != PCB_VIA_T
2255 && item->Type() != PCB_ARC_T
2256 && item->Type() != PCB_FOOTPRINT_T )
2257 {
2258 return 0;
2259 }
2260
2261 std::set<FOOTPRINT*> footprints;
2262
2263 if( item->Type() == PCB_FOOTPRINT_T )
2264 footprints.insert( static_cast<FOOTPRINT*>( item ) );
2265
2266 // We can drag multiple footprints, but not a grab-bag of items
2267 if( selection.Size() > 1 && item->Type() == PCB_FOOTPRINT_T )
2268 {
2269 for( int idx = 1; idx < selection.Size(); ++idx )
2270 {
2271 if( !selection.GetItem( idx )->IsBOARD_ITEM() )
2272 return 0;
2273
2274 if( static_cast<BOARD_ITEM*>( selection.GetItem( idx ) )->Type() != PCB_FOOTPRINT_T )
2275 return 0;
2276
2277 footprints.insert( static_cast<FOOTPRINT*>( selection.GetItem( idx ) ) );
2278 }
2279 }
2280
2281 // If we overrode locks, we want to clear the flag from the source item before SyncWorld is
2282 // called so that virtual vias are not generated for the (now unlocked) track segment. Note in
2283 // this case the lock can't be reliably re-applied, because there is no guarantee that the end
2284 // state of the drag results in the same number of segments so it's not clear which segment to
2285 // apply the lock state to.
2286 bool wasLocked = false;
2287
2288 if( item->IsLocked() )
2289 {
2290 wasLocked = true;
2291 item->SetLocked( false );
2292 }
2293
2294 m_toolMgr->RunAction( ACTIONS::selectionClear );
2295
2296 TOOL_EVENT pushedEvent = aEvent;
2297 frame()->PushTool( aEvent );
2298 Activate();
2299
2300 m_startItem = nullptr;
2301
2302 PNS::ITEM* startItem = nullptr;
2303 PNS::ITEM_SET itemsToDrag;
2304
2305 bool showCourtyardConflicts = frame()->GetPcbNewSettings()->m_ShowCourtyardCollisions;
2306
2307 std::shared_ptr<DRC_ENGINE> drcEngine = m_toolMgr->GetTool<DRC_TOOL>()->GetDRCEngine();
2308 DRC_INTERACTIVE_COURTYARD_CLEARANCE courtyardClearanceDRC( drcEngine );
2309
2310 std::shared_ptr<CONNECTIVITY_DATA> connectivityData = board()->GetConnectivity();
2311 std::vector<BOARD_ITEM*> dynamicItems;
2312 std::unique_ptr<CONNECTIVITY_DATA> dynamicData = nullptr;
2313 VECTOR2I lastOffset;
2314 std::vector<PNS::ITEM*> leaderSegments;
2315 bool singleFootprintDrag = false;
2316
2317 if( !footprints.empty() )
2318 {
2319 if( footprints.size() == 1 )
2320 singleFootprintDrag = true;
2321
2322 if( showCourtyardConflicts )
2323 courtyardClearanceDRC.Init( board() );
2324
2325 for( FOOTPRINT* footprint : footprints )
2326 {
2327 for( PAD* pad : footprint->Pads() )
2328 {
2329 PNS::ITEM* solid = m_router->GetWorld()->FindItemByParent( pad );
2330
2331 if( solid )
2332 itemsToDrag.Add( solid );
2333
2334 if( pad->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
2335 {
2336 if( connectivityData->GetRatsnestForPad( pad ).size() > 0 )
2337 dynamicItems.push_back( pad );
2338 }
2339 }
2340
2341 for( ZONE* zone : footprint->Zones() )
2342 {
2343 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( zone ) )
2344 itemsToDrag.Add( solid );
2345 }
2346
2347 for( BOARD_ITEM* shape : footprint->GraphicalItems() )
2348 {
2349 if( shape->GetLayer() == Edge_Cuts
2350 || shape->GetLayer() == Margin
2351 || IsCopperLayer( shape->GetLayer() ) )
2352 {
2353 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( shape ) )
2354 itemsToDrag.Add( solid );
2355 }
2356 }
2357
2358 if( showCourtyardConflicts )
2359 courtyardClearanceDRC.m_FpInMove.push_back( footprint );
2360 }
2361
2362 dynamicData = std::make_unique<CONNECTIVITY_DATA>( board()->GetConnectivity(),
2363 dynamicItems, true );
2364 connectivityData->BlockRatsnestItems( dynamicItems );
2365 }
2366 else
2367 {
2368 for( const EDA_ITEM* selItem : selectedItems )
2369 {
2370 if( !selItem->IsBOARD_ITEM() )
2371 continue;
2372
2373 const BOARD_ITEM* boardItem = static_cast<const BOARD_ITEM*>( selItem );
2374 PNS::ITEM* pnsItem = m_router->GetWorld()->FindItemByParent( boardItem );
2375
2376 if( !pnsItem )
2377 continue;
2378
2379 if( pnsItem->OfKind( PNS::ITEM::SEGMENT_T )
2380 || pnsItem->OfKind( PNS::ITEM::VIA_T )
2381 || pnsItem->OfKind( PNS::ITEM::ARC_T ) )
2382 {
2383 itemsToDrag.Add( pnsItem );
2384 }
2385 }
2386 }
2387
2388 GAL* gal = m_toolMgr->GetView()->GetGAL();
2389 VECTOR2I p0 = GetClampedCoords( controls()->GetCursorPosition( false ), COORDS_PADDING );
2390 VECTOR2I p = p0;
2391
2392 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
2393 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
2394
2395 if( itemsToDrag.Count() >= 1 )
2396 {
2397 // Snap to closest item
2398 int layer = m_iface->GetPNSLayerFromBoardLayer( m_originalActiveLayer );
2399 PNS::ITEM* closestItem = nullptr;
2400 SEG::ecoord closestDistSq = std::numeric_limits<SEG::ecoord>::max();
2401
2402 for( PNS::ITEM* pitem : itemsToDrag.Items() )
2403 {
2404 SEG::ecoord distSq = pitem->Shape( layer )->SquaredDistance( p0, 0 );
2405
2406 if( distSq < closestDistSq )
2407 {
2408 closestDistSq = distSq;
2409 closestItem = pitem;
2410 }
2411 }
2412
2413 if( closestItem )
2414 {
2415 p = snapToItem( closestItem, p0 );
2416
2417 m_startItem = closestItem;
2418
2419 if( closestItem->Net() )
2420 highlightNets( true, { closestItem->Net() } );
2421 }
2422 }
2423
2424 if( !footprints.empty() && singleFootprintDrag )
2425 {
2426 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
2427
2428 // The mouse is going to be moved on grid before dragging begins.
2429 VECTOR2I tweakedMousePos;
2431
2432 // Check if user wants to warp the mouse to origin of moved object
2433
2434 if( editFrame->GetMoveWarpsCursor() )
2435 tweakedMousePos = footprint->GetPosition(); // Use footprint anchor to warp mouse
2436 else
2437 tweakedMousePos = GetClampedCoords( controls()->GetCursorPosition(),
2438 COORDS_PADDING ); // Just use current mouse pos
2439
2440 // We tweak the mouse position using the value from above, and then use that as the
2441 // start position to prevent the footprint from jumping when we start dragging.
2442 // First we move the visual cross hair cursor...
2443 controls()->ForceCursorPosition( true, tweakedMousePos );
2444 controls()->SetCursorPosition( tweakedMousePos ); // ...then the mouse pointer
2445
2446 // Now that the mouse is in the right position, get a copy of the position to use later
2447 p = controls()->GetCursorPosition();
2448 }
2449
2450 int dragMode = aEvent.Parameter<int> ();
2451
2452 bool dragStarted = m_router->StartDragging( p, itemsToDrag, dragMode );
2453
2454 if( !dragStarted )
2455 {
2456 if( wasLocked )
2457 item->SetLocked( true );
2458
2459 if( !footprints.empty() )
2460 connectivityData->ClearLocalRatsnest();
2461
2462 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
2463 courtyardClearanceDRC.ClearConflicts( getView() );
2464
2466 controls()->ForceCursorPosition( false );
2467 frame()->PopTool( pushedEvent );
2468 highlightNets( false );
2469 return 0;
2470 }
2471
2472 m_gridHelper->SetAuxAxes( true, p );
2473 controls()->ShowCursor( true );
2474 controls()->SetAutoPan( true );
2475 frame()->UndoRedoBlock( true );
2476
2477 view()->ClearPreview();
2478 view()->InitPreview();
2479
2480 auto setCursor =
2481 [&]()
2482 {
2483 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2484 };
2485
2486 // Set initial cursor
2487 setCursor();
2488
2489 // Set the initial visible area
2490 BOX2D viewAreaD = getView()->GetGAL()->GetVisibleWorldExtents();
2491 m_router->SetVisibleViewArea( BOX2ISafe( viewAreaD ) );
2492
2493 // Send an initial movement to prime the collision detection
2494 m_router->Move( p, nullptr );
2495
2496 bool hasMouseMoved = false;
2497 bool hasMultidragCancelled = false;
2498
2499 while( TOOL_EVENT* evt = Wait() )
2500 {
2501 setCursor();
2502
2503 if( evt->IsCancelInteractive() || evt->IsActivate() )
2504 {
2505 if( wasLocked )
2506 item->SetLocked( true );
2507
2508 hasMultidragCancelled = true;
2509
2510 break;
2511 }
2512 else if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
2513 {
2514 hasMouseMoved = true;
2515 updateEndItem( *evt );
2517
2518 view()->ClearPreview();
2519
2520 if( !footprints.empty() )
2521 {
2522 VECTOR2I offset = m_endSnapPoint - p;
2523 BOARD_ITEM* previewItem;
2524
2525 for( FOOTPRINT* footprint : footprints )
2526 {
2527 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
2528 {
2529 previewItem = static_cast<BOARD_ITEM*>( drawing->Clone() );
2530 previewItem->Move( offset );
2531
2532 view()->AddToPreview( previewItem );
2533 view()->Hide( drawing, true );
2534 }
2535
2536 for( PAD* pad : footprint->Pads() )
2537 {
2538 if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none()
2539 && pad->GetDrillSize().x == 0 )
2540 {
2541 previewItem = static_cast<BOARD_ITEM*>( pad->Clone() );
2542 previewItem->Move( offset );
2543
2544 view()->AddToPreview( previewItem );
2545 }
2546 else
2547 {
2548 // Pads with copper or holes are handled by the router
2549 }
2550
2551 view()->Hide( pad, true );
2552 }
2553
2554 previewItem = static_cast<BOARD_ITEM*>( footprint->Reference().Clone() );
2555 previewItem->Move( offset );
2556 view()->AddToPreview( previewItem );
2557 view()->Hide( &footprint->Reference() );
2558
2559 previewItem = static_cast<BOARD_ITEM*>( footprint->Value().Clone() );
2560 previewItem->Move( offset );
2561 view()->AddToPreview( previewItem );
2562 view()->Hide( &footprint->Value() );
2563
2564 if( showCourtyardConflicts )
2565 footprint->Move( offset );
2566 }
2567
2568 if( showCourtyardConflicts )
2569 {
2570 courtyardClearanceDRC.Run();
2571 courtyardClearanceDRC.UpdateConflicts( getView(), false );
2572
2573 for( FOOTPRINT* footprint : footprints )
2574 footprint->Move( -offset );
2575 }
2576
2577 // Update ratsnest
2578 dynamicData->Move( offset - lastOffset );
2579 lastOffset = offset;
2580 connectivityData->ComputeLocalRatsnest( dynamicItems, dynamicData.get(), offset );
2581 }
2582
2583 if( PNS::DRAG_ALGO* dragger = m_router->GetDragger() )
2584 {
2585 bool dragStatus;
2586
2587 if( dragger->GetForceMarkObstaclesMode( &dragStatus ) )
2588 {
2589 if( !dragStatus )
2590 {
2591 wxString hint;
2592 hint.Printf( _( "(%s to commit anyway.)" ),
2594
2596 statusItem->SetMessage( _( "Track violates DRC." ) );
2597 statusItem->SetHint( hint );
2598 statusItem->SetPosition( frame()->GetToolManager()->GetMousePosition() );
2599 view()->AddToPreview( statusItem );
2600 }
2601 }
2602 }
2603 }
2604 else if( hasMouseMoved && ( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
2605 {
2606 bool forceFinish = false;
2607 bool forceCommit = evt->Modifier( MD_CTRL );
2608
2609 updateEndItem( *evt );
2610 m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit );
2611 leaderSegments = m_router->GetLastCommittedLeaderSegments();
2612
2613 break;
2614 }
2615 else if( evt->IsUndoRedo() )
2616 {
2617 // We're in an UndoRedoBlock. If we get here, something's broken.
2618 wxFAIL;
2619 break;
2620 }
2621 else if( evt->Category() == TC_COMMAND )
2622 {
2623 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2624 // but we don't at present have that, so we just knock out some of the egregious ones.
2625 if( evt->IsAction( &ACTIONS::cut )
2626 || evt->IsAction( &ACTIONS::copy )
2627 || evt->IsAction( &ACTIONS::paste )
2628 || evt->IsAction( &ACTIONS::pasteSpecial )
2630 {
2631 wxBell();
2632 }
2633 // treat an undo as an escape
2634 else if( evt->IsAction( &ACTIONS::undo ) )
2635 {
2636 if( wasLocked )
2637 item->SetLocked( true );
2638
2639 break;
2640 }
2641 else
2642 {
2643 evt->SetPassEvent();
2644 }
2645 }
2646 else
2647 {
2648 evt->SetPassEvent();
2649 }
2650
2651 handleCommonEvents( *evt );
2652 }
2653
2654 if( !footprints.empty() )
2655 {
2656 for( FOOTPRINT* footprint : footprints )
2657 {
2658 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
2659 view()->Hide( drawing, false );
2660
2661 view()->Hide( &footprint->Reference(), false );
2662 view()->Hide( &footprint->Value(), false );
2663
2664 for( PAD* pad : footprint->Pads() )
2665 view()->Hide( pad, false );
2666 }
2667
2668 view()->ClearPreview();
2669 view()->ShowPreview( false );
2670
2671 connectivityData->ClearLocalRatsnest();
2672 }
2673
2674 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
2675 courtyardClearanceDRC.ClearConflicts( getView() );
2676
2677 if( m_router->RoutingInProgress() )
2678 m_router->StopRouting();
2679
2680
2681 if( itemsToDrag.Size() && hasMultidragCancelled )
2682 {
2684 }
2685 else if( leaderSegments.size() )
2686 {
2687 std::vector<EDA_ITEM*> newItems;
2688
2689 for( auto lseg : leaderSegments )
2690 newItems.push_back( lseg->Parent() );
2691
2692 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &newItems );
2693 }
2694
2695 m_gridHelper->SetAuxAxes( false );
2696 controls()->SetAutoPan( false );
2697 controls()->ForceCursorPosition( false );
2698 frame()->UndoRedoBlock( false );
2699 frame()->PopTool( pushedEvent );
2700 highlightNets( false );
2701 view()->ClearPreview();
2702 view()->ShowPreview( false );
2703
2704 return 0;
2705}
2706
2707
2709{
2710 const SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2711
2712 if( selection.Size() != 1 )
2713 return 0;
2714
2715 const BOARD_CONNECTED_ITEM* item =
2716 static_cast<const BOARD_CONNECTED_ITEM*>( selection.Front() );
2717
2718 if( item->Type() != PCB_TRACE_T && item->Type() != PCB_ARC_T )
2719 return 0;
2720
2721 m_toolMgr->RunAction( ACTIONS::selectionClear );
2722
2723 Activate();
2724
2725 m_startItem = m_router->GetWorld()->FindItemByParent( item );
2726
2727 TOOL_MANAGER* toolManager = frame()->GetToolManager();
2728 GAL* gal = toolManager->GetView()->GetGAL();
2729
2730 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
2731 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
2732
2733 controls()->ForceCursorPosition( false );
2734
2735 if( toolManager->IsContextMenuActive() )
2736 {
2737 // If we're here from a context menu then we need to get the position of the
2738 // cursor when the context menu was invoked. This is used to figure out the
2739 // break point on the track.
2741 }
2742 else
2743 {
2744 // If we're here from a hotkey, then get the current mouse position so we know
2745 // where to break the track.
2746 m_startSnapPoint = snapToItem( m_startItem, controls()->GetCursorPosition() );
2747 }
2748
2749 if( m_startItem && m_startItem->IsLocked() )
2750 {
2751 KIDIALOG dlg( frame(), _( "The selected item is locked." ), _( "Confirmation" ),
2752 wxOK | wxCANCEL | wxICON_WARNING );
2753 dlg.SetOKLabel( _( "Break Track" ) );
2754 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
2755
2756 if( dlg.ShowModal() == wxID_CANCEL )
2757 return 0;
2758 }
2759
2760 frame()->UndoRedoBlock( true );
2761 breakTrack();
2762
2763 if( m_router->RoutingInProgress() )
2764 m_router->StopRouting();
2765
2766 frame()->UndoRedoBlock( false );
2767
2768 return 0;
2769}
2770
2771
2773{
2775 DIALOG_TRACK_VIA_SIZE sizeDlg( frame(), bds );
2776
2777 if( sizeDlg.ShowModal() == wxID_OK )
2778 {
2779 bds.m_TempOverrideTrackWidth = true;
2780 bds.UseCustomTrackViaSize( true );
2781
2784 }
2785
2786 return 0;
2787}
2788
2789
2791{
2792 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
2793
2794 if( !m_router->GetCurrentNets().empty() )
2795 m_iface->ImportSizes( sizes, m_startItem, m_router->GetCurrentNets()[0], VECTOR2D() );
2796
2797 m_router->UpdateSizes( sizes );
2798
2799 // Changing the track width can affect the placement, so call the
2800 // move routine without changing the destination
2801 // Update end item first to avoid moving to an invalid/missing item
2802 updateEndItem( aEvent );
2804
2806
2807 return 0;
2808}
2809
2810
2812{
2813 std::vector<MSG_PANEL_ITEM> items;
2814
2815 if( m_router->GetState() == PNS::ROUTER::ROUTE_TRACK )
2816 {
2817 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
2818 PNS::RULE_RESOLVER* resolver = m_iface->GetRuleResolver();
2819 PNS::CONSTRAINT constraint;
2820 std::vector<PNS::NET_HANDLE> nets = m_router->GetCurrentNets();
2821 wxString description;
2822 wxString secondary;
2823 wxString mode;
2824
2826 {
2827 wxASSERT( nets.size() >= 2 );
2828
2829 NETINFO_ITEM* netA = static_cast<NETINFO_ITEM*>( nets[0] );
2830 NETINFO_ITEM* netB = static_cast<NETINFO_ITEM*>( nets[1] );
2831 wxASSERT( netA );
2832 wxASSERT( netB );
2833
2834 description = wxString::Format( _( "Routing Diff Pair: %s" ),
2835 netA->GetNetname() + wxT( ", " ) + netB->GetNetname() );
2836
2837 wxString netclass;
2838 NETCLASS* netclassA = netA->GetNetClass();
2839 NETCLASS* netclassB = netB->GetNetClass();
2840
2841 if( *netclassA == *netclassB )
2842 netclass = netclassA->GetHumanReadableName();
2843 else
2844 netclass = netclassA->GetHumanReadableName() + wxT( ", " )
2845 + netclassB->GetHumanReadableName();
2846
2847 secondary = wxString::Format( _( "Resolved Netclass: %s" ),
2848 UnescapeString( netclass ) );
2849 }
2850 else if( !nets.empty() && nets[0] )
2851 {
2852 NETINFO_ITEM* net = static_cast<NETINFO_ITEM*>( nets[0] );
2853
2854 description = wxString::Format( _( "Routing Track: %s" ),
2855 net->GetNetname() );
2856
2857 secondary = wxString::Format(
2858 _( "Resolved Netclass: %s" ),
2860 }
2861 else
2862 {
2863 description = _( "Routing Track" );
2864 secondary = _( "(no net)" );
2865 }
2866
2867 items.emplace_back( description, secondary );
2868
2869 wxString cornerMode;
2870
2871 if( m_router->Settings().GetFreeAngleMode() )
2872 {
2873 cornerMode = _( "Free-angle" );
2874 }
2875 else
2876 {
2877 switch( m_router->Settings().GetCornerMode() )
2878 {
2879 case DIRECTION_45::CORNER_MODE::MITERED_45: cornerMode = _( "45-degree" ); break;
2880 case DIRECTION_45::CORNER_MODE::ROUNDED_45: cornerMode = _( "45-degree rounded" ); break;
2881 case DIRECTION_45::CORNER_MODE::MITERED_90: cornerMode = _( "90-degree" ); break;
2882 case DIRECTION_45::CORNER_MODE::ROUNDED_90: cornerMode = _( "90-degree rounded" ); break;
2883 default: break;
2884 }
2885 }
2886
2887 items.emplace_back( _( "Corner Style" ), cornerMode );
2888
2889 switch( m_router->Settings().Mode() )
2890 {
2891 case PNS::PNS_MODE::RM_MarkObstacles: mode = _( "Highlight collisions" ); break;
2892 case PNS::PNS_MODE::RM_Walkaround: mode = _( "Walk around" ); break;
2893 case PNS::PNS_MODE::RM_Shove: mode = _( "Shove" ); break;
2894 default: break;
2895 }
2896
2897 items.emplace_back( _( "Mode" ), mode );
2898
2899#define FORMAT_VALUE( x ) frame()->MessageTextFromValue( x )
2900
2902 {
2903 items.emplace_back( wxString::Format( _( "Track Width: %s" ),
2904 FORMAT_VALUE( sizes.DiffPairWidth() ) ),
2905 wxString::Format( _( "(from %s)" ),
2906 sizes.GetDiffPairWidthSource() ) );
2907
2908 items.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
2909 FORMAT_VALUE( sizes.Clearance() ) ),
2910 wxString::Format( _( "(from %s)" ),
2911 sizes.GetClearanceSource() ) );
2912
2913 items.emplace_back( wxString::Format( _( "Diff Pair Gap: %s" ),
2914 FORMAT_VALUE( sizes.DiffPairGap() ) ),
2915 wxString::Format( _( "(from %s)" ),
2916 sizes.GetDiffPairGapSource() ) );
2917
2918 const PNS::ITEM_SET& traces = m_router->Placer()->Traces();
2919 wxASSERT( traces.Count() == 2 );
2920
2921 if( resolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_MAX_UNCOUPLED, traces[0],
2922 traces[1], m_router->GetCurrentLayer(), &constraint ) )
2923 {
2924 items.emplace_back( wxString::Format( _( "DP Max Uncoupled-length: %s" ),
2925 FORMAT_VALUE( constraint.m_Value.Max() ) ),
2926 wxString::Format( _( "(from %s)" ),
2927 constraint.m_RuleName ) );
2928 }
2929 }
2930 else
2931 {
2932 items.emplace_back( wxString::Format( _( "Track Width: %s" ),
2933 FORMAT_VALUE( sizes.TrackWidth() ) ),
2934 wxString::Format( _( "(from %s)" ),
2935 sizes.GetWidthSource() ) );
2936
2937 items.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
2938 FORMAT_VALUE( sizes.Clearance() ) ),
2939 wxString::Format( _( "(from %s)" ),
2940 sizes.GetClearanceSource() ) );
2941 }
2942
2943#undef FORMAT_VALUE
2944
2945 frame()->SetMsgPanel( items );
2946 }
2947 else
2948 {
2949 frame()->SetMsgPanel( board() );
2950 return;
2951 }
2952}
2953
2954
2956{
2958
2972
2979
3015
3018
3023}
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:216
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:223
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:231
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:79
void SetLocked(bool aLocked) override
Definition board_item.h:323
bool IsLocked() const override
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:339
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:280
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:933
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1044
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 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.
@ 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:174
MINOPTMAX< int > m_Value
Definition drc_rule.h:208
bool IsNull() const
Definition drc_rule.h:161
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:54
static wxString GetDefaultUserProjectsPath()
Gets the default path we point users to create projects.
Definition paths.cpp:136
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:66
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
Definition project.h:211
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:205
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_SwitchCornerMode90(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRounding90") .Scope(AS_CONTEXT) .DefaultHotkey(MD_SHIFT+ 'W') .FriendlyName(_("Track Corner Mode 90")) .Tooltip(_("Switch to 90° corner when routing tracks.")))
#define FORMAT_VALUE(x)
static const TOOL_ACTION ACT_SwitchCornerMode45(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRounding45") .Scope(AS_CONTEXT) .DefaultHotkey( '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_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_SwitchCornerModeArc45(TOOL_ACTION_ARGS() .Name("pcbnew.InteractiveRouter.SwitchRoundingArc45") .Scope(AS_CONTEXT) .DefaultHotkey(MD_CTRL+ 'W') .FriendlyName(_("Track Corner Mode Arc 45")) .Tooltip(_("Switch to arc 45° corner when routing tracks.")))
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_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
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition tool_event.h:641
@ 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
@ TC_COMMAND
Definition tool_event.h:57
@ TC_MOUSE
Definition tool_event.h:55
@ TC_VIEW
Definition tool_event.h:59
@ MD_ALT
Definition tool_event.h:145
@ MD_CTRL
Definition tool_event.h:144
@ MD_SHIFT
Definition tool_event.h:143
@ 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.