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
1205 targetLayer = m_iface->GetBoardLayerFromPNSLayer( otherEndLayers.Start() );
1206 }
1207 }
1208 else
1209 {
1210 if( currentLayer == pairTop || currentLayer == pairBottom )
1211 {
1212 // the current layer is on the defined layer pair,
1213 // swap to the other side
1214 currentLayer = pairTop;
1215 targetLayer = pairBottom;
1216 }
1217 else
1218 {
1219 // the current layer is not part of the current layer pair,
1220 // so fallback and swap to the top layer of the pair by default
1221 targetLayer = pairTop;
1222 }
1223
1224 // Do not create a broken via (i.e. a via on only one copper layer)
1225 if( currentLayer == targetLayer )
1226 {
1227 WX_INFOBAR* infobar = frame()->GetInfoBar();
1228 infobar->ShowMessageFor( _( "Via needs 2 different layers." ),
1229 2000, wxICON_ERROR,
1231 return 0;
1232 }
1233 }
1234 }
1235
1236 sizes.SetViaDiameter( bds.m_ViasMinSize );
1237 sizes.SetViaDrill( bds.m_MinThroughDrill );
1238
1239 if( bds.UseNetClassVia() || viaType == VIATYPE::MICROVIA )
1240 {
1241 PCB_VIA dummyVia( board() );
1242 dummyVia.SetViaType( viaType );
1243 dummyVia.SetLayerPair( currentLayer, targetLayer );
1244
1245 if( !m_router->GetCurrentNets().empty() )
1246 dummyVia.SetNet( static_cast<NETINFO_ITEM*>( m_router->GetCurrentNets()[0] ) );
1247
1248 DRC_CONSTRAINT constraint;
1249
1250 constraint = bds.m_DRCEngine->EvalRules( VIA_DIAMETER_CONSTRAINT, &dummyVia, nullptr,
1251 currentLayer );
1252
1253 if( !constraint.IsNull() )
1254 sizes.SetViaDiameter( constraint.m_Value.Opt() );
1255
1256 constraint = bds.m_DRCEngine->EvalRules( HOLE_SIZE_CONSTRAINT, &dummyVia, nullptr,
1257 currentLayer );
1258
1259 if( !constraint.IsNull() )
1260 sizes.SetViaDrill( constraint.m_Value.Opt() );
1261 }
1262 else
1263 {
1264 sizes.SetViaDiameter( bds.GetCurrentViaSize() );
1265 sizes.SetViaDrill( bds.GetCurrentViaDrill() );
1266 }
1267
1268 sizes.SetViaType( viaType );
1269 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( currentLayer ),
1270 m_iface->GetPNSLayerFromBoardLayer( targetLayer ) );
1271
1272 m_router->UpdateSizes( sizes );
1273
1274 if( !m_router->IsPlacingVia() )
1275 m_router->ToggleViaPlacement();
1276
1277 if( m_router->RoutingInProgress() )
1278 {
1279 updateEndItem( aEvent );
1281 }
1282 else
1283 {
1284 updateStartItem( aEvent );
1285 }
1286
1287 return 0;
1288}
1289
1290
1292{
1295 int pnsLayer = m_iface->GetPNSLayerFromBoardLayer( pcbLayer );
1296
1297 if( !::IsCopperLayer( pcbLayer ) )
1298 {
1299 editFrame->ShowInfoBarError( _( "Tracks on Copper layers only." ) );
1300 return false;
1301 }
1302
1304 editFrame->SetActiveLayer( pcbLayer );
1305
1306 if( !getView()->IsLayerVisible( pcbLayer ) )
1307 {
1308 editFrame->GetAppearancePanel()->SetLayerVisible( pcbLayer, true );
1309 editFrame->GetCanvas()->Refresh();
1310 }
1311
1312 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
1313
1314 m_iface->SetStartLayerFromPCBNew( pcbLayer );
1315
1316 frame()->GetBoard()->GetDesignSettings().m_TempOverrideTrackWidth = false;
1317 m_iface->ImportSizes( sizes, m_startItem, nullptr, aStartPosition );
1318 sizes.AddLayerPair( m_iface->GetPNSLayerFromBoardLayer( frame()->GetScreen()->m_Route_Layer_TOP ),
1319 m_iface->GetPNSLayerFromBoardLayer( frame()->GetScreen()->m_Route_Layer_BOTTOM ) );
1320
1321 m_router->UpdateSizes( sizes );
1322
1323 if( m_startItem && m_startItem->Net() )
1324 {
1326 {
1327 if( PNS::NET_HANDLE coupledNet = m_router->GetRuleResolver()->DpCoupledNet( m_startItem->Net() ) )
1328 highlightNets( true, { m_startItem->Net(), coupledNet } );
1329 }
1330 else
1331 {
1332 highlightNets( true, { m_startItem->Net() } );
1333 }
1334 }
1335
1336 controls()->SetAutoPan( true );
1337
1338 if( !m_router->StartRouting( m_startSnapPoint, m_startItem, pnsLayer ) )
1339 {
1340 // It would make more sense to leave the net highlighted as the higher-contrast mode
1341 // makes the router clearances more visible. However, since we just started routing
1342 // the conversion of the screen from low contrast to high contrast is a bit jarring and
1343 // makes the infobar coming up less noticeable.
1344 highlightNets( false );
1345
1346 frame()->ShowInfoBarError( m_router->FailureReason(), true,
1347 [&]()
1348 {
1349 m_router->ClearViewDecorations();
1350 } );
1351
1352 controls()->SetAutoPan( false );
1353 return false;
1354 }
1355
1356 m_endItem = nullptr;
1358
1360 frame()->UndoRedoBlock( true );
1361
1362 return true;
1363}
1364
1365
1367{
1368 m_router->StopRouting();
1369
1370 m_startItem = nullptr;
1371 m_endItem = nullptr;
1372
1373 frame()->SetActiveLayer( m_originalActiveLayer );
1375 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1376 controls()->SetAutoPan( false );
1377 controls()->ForceCursorPosition( false );
1378 frame()->UndoRedoBlock( false );
1379 highlightNets( false );
1380
1381 return true;
1382}
1383
1384
1386{
1387 m_router->ClearViewDecorations();
1388
1389 if( !prepareInteractive( aStartPosition ) )
1390 return;
1391
1392 auto setCursor =
1393 [&]()
1394 {
1395 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1396 };
1397
1398 auto syncRouterAndFrameLayer =
1399 [&]()
1400 {
1401 int pnsLayer = m_router->GetCurrentLayer();
1402 PCB_LAYER_ID pcbLayer = m_iface->GetBoardLayerFromPNSLayer( pnsLayer );
1404
1405 editFrame->SetActiveLayer( pcbLayer );
1406
1407 if( !getView()->IsLayerVisible( pcbLayer ) )
1408 {
1409 editFrame->GetAppearancePanel()->SetLayerVisible( pcbLayer, true );
1410 editFrame->GetCanvas()->Refresh();
1411 }
1412 };
1413
1414 // Set initial cursor
1415 setCursor();
1416
1417 while( TOOL_EVENT* evt = Wait() )
1418 {
1419 setCursor();
1420
1421 // Don't crash if we missed an operation that canceled routing.
1422 if( !m_router->RoutingInProgress() )
1423 {
1424 if( evt->IsCancelInteractive() )
1425 m_cancelled = true;
1426
1427 break;
1428 }
1429
1430 handleCommonEvents( *evt );
1431
1432 if( evt->IsMotion() )
1433 {
1434 updateEndItem( *evt );
1436 }
1437 else if( evt->IsAction( &PCB_ACTIONS::routerUndoLastSegment )
1438 || evt->IsAction( &ACTIONS::doDelete )
1439 || evt->IsAction( &ACTIONS::undo ) )
1440 {
1441 if( std::optional<VECTOR2I> last = m_router->UndoLastSegment() )
1442 {
1443 getViewControls()->WarpMouseCursor( last.value(), true );
1444 evt->SetMousePosition( last.value() );
1445 }
1446
1447 updateEndItem( *evt );
1449 }
1450 else if( evt->IsAction( &PCB_ACTIONS::routerAttemptFinish ) )
1451 {
1452 if( m_toolMgr->IsContextMenuActive() )
1453 m_toolMgr->WarpAfterContextMenu();
1454
1455 bool* autoRouted = evt->Parameter<bool*>();
1456
1457 if( m_router->Finish() )
1458 {
1459 // When we're routing a group of signals automatically we want
1460 // to break up the undo stack every time we have to manually route
1461 // so the user gets nice checkpoints. Remove the APPEND_UNDO flag.
1462 if( autoRouted != nullptr )
1463 *autoRouted = true;
1464
1465 break;
1466 }
1467 else
1468 {
1469 // This acts as check if we were called by the autorouter; we don't want
1470 // to reset APPEND_UNDO if we're auto finishing after route-other-end
1471 if( autoRouted != nullptr )
1472 {
1473 *autoRouted = false;
1474 m_iface->SetCommitFlags( 0 );
1475 }
1476
1477 // Warp the mouse so the user is at the point we managed to route to
1478 controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true );
1479 }
1480 }
1481 else if( evt->IsAction( &PCB_ACTIONS::routerContinueFromEnd ) )
1482 {
1483 bool needsAppend = m_router->Placer()->HasPlacedAnything();
1484
1485 if( m_router->ContinueFromEnd( &m_startItem ) )
1486 {
1487 syncRouterAndFrameLayer();
1488 m_startSnapPoint = m_router->Placer()->CurrentStart();
1489 updateEndItem( *evt );
1490
1491 // Warp the mouse to wherever we actually ended up routing to
1492 controls()->WarpMouseCursor( m_router->Placer()->CurrentEnd(), true, true );
1493
1494 // We want the next router commit to be one undo at the UI layer
1495 m_iface->SetCommitFlags( needsAppend ? APPEND_UNDO : 0 );
1496 }
1497 else
1498 {
1499 frame()->ShowInfoBarError( m_router->FailureReason(), true );
1500 }
1501 }
1502 else if( evt->IsClick( BUT_LEFT )
1503 || evt->IsDrag( BUT_LEFT )
1504 || evt->IsAction( &PCB_ACTIONS::routeSingleTrack ) )
1505 {
1506 updateEndItem( *evt );
1507 bool needLayerSwitch = m_router->IsPlacingVia();
1508 bool forceFinish = evt->Modifier( MD_SHIFT );
1509 bool forceCommit = false;
1510
1511 if( m_router->FixRoute( m_endSnapPoint, m_endItem, false, forceCommit ) )
1512 break;
1513
1514 if( needLayerSwitch )
1515 {
1517 }
1518 else
1519 {
1521 }
1522
1523 // Synchronize the indicated layer
1524 syncRouterAndFrameLayer();
1525
1526 updateEndItem( *evt );
1528 m_startItem = nullptr;
1529 }
1530 else if( evt->IsAction( &ACT_SwitchPosture ) )
1531 {
1532 m_router->FlipPosture();
1533 updateEndItem( *evt );
1534 m_router->Move( m_endSnapPoint, m_endItem ); // refresh
1535 }
1536 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
1537 {
1538 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1539 controls()->SetAutoPan( false );
1540 {
1541 m_toolMgr->RunAction( ACT_CustomTrackWidth );
1542 }
1543 controls()->SetAutoPan( true );
1544 setCursor();
1546 }
1547 else if( evt->IsAction( &ACTIONS::finishInteractive ) || evt->IsDblClick( BUT_LEFT ) )
1548 {
1549 // Stop current routing:
1550 bool forceFinish = true;
1551 bool forceCommit = false;
1552
1553 m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit );
1554 break;
1555 }
1556 else if( evt->IsCancelInteractive() || evt->IsActivate()
1557 || evt->IsAction( &PCB_ACTIONS::routerInlineDrag ) )
1558 {
1559 if( evt->IsCancelInteractive() && !m_router->RoutingInProgress() )
1560 m_cancelled = true;
1561
1562 if( evt->IsActivate() && !evt->IsMoveTool() )
1563 m_cancelled = true;
1564
1565 break;
1566 }
1567 else if( evt->IsUndoRedo() )
1568 {
1569 // We're in an UndoRedoBlock. If we get here, something's broken.
1570 wxFAIL;
1571 break;
1572 }
1573 else if( evt->IsClick( BUT_RIGHT ) )
1574 {
1575 m_menu->ShowContextMenu( selection() );
1576 }
1577 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
1578 // but we don't at present have that, so we just knock out some of the egregious ones.
1579 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
1580 {
1581 wxBell();
1582 }
1583 else
1584 {
1585 evt->SetPassEvent();
1586 }
1587 }
1588
1589 m_router->CommitRouting();
1590 // Reset to normal for next route
1591 m_iface->SetCommitFlags( 0 );
1592
1594}
1595
1596
1598{
1599 PNS::SIZES_SETTINGS sizes = m_router->Sizes();
1600 DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( frame(), sizes );
1601
1602 if( settingsDlg.ShowModal() == wxID_OK )
1603 {
1604 m_router->UpdateSizes( sizes );
1605 m_savedSizes = sizes;
1606
1607 BOARD_DESIGN_SETTINGS& bds = frame()->GetBoard()->GetDesignSettings();
1609 bds.SetCustomDiffPairGap( sizes.DiffPairGap() );
1611 }
1612
1613 return 0;
1614}
1615
1616
1618{
1619 DIALOG_PNS_SETTINGS settingsDlg( frame(), m_router->Settings() );
1620
1621 settingsDlg.ShowModal();
1622
1624
1625 return 0;
1626}
1627
1628
1630{
1631 PNS::PNS_MODE mode = aEvent.Parameter<PNS::PNS_MODE>();
1632 PNS::ROUTING_SETTINGS& settings = m_router->Settings();
1633
1634 settings.SetMode( mode );
1636
1637 return 0;
1638}
1639
1640
1642{
1643 PNS::ROUTING_SETTINGS& settings = m_router->Settings();
1644 PNS::PNS_MODE mode = settings.Mode();
1645
1646 switch( mode )
1647 {
1648 case PNS::RM_MarkObstacles: mode = PNS::RM_Shove; break;
1649 case PNS::RM_Shove: mode = PNS::RM_Walkaround; break;
1650 case PNS::RM_Walkaround: mode = PNS::RM_MarkObstacles; break;
1651 }
1652
1653 settings.SetMode( mode );
1655
1656 return 0;
1657}
1658
1659
1661{
1662 return m_router->Settings().Mode();
1663}
1664
1665
1667{
1668 return m_router->RoutingInProgress();
1669}
1670
1671
1673{
1674 if( !m_startItem )
1675 return;
1676
1678 m_router->BreakSegmentOrArc( m_startItem, m_startSnapPoint );
1679}
1680
1681
1683{
1687 PCB_LAYER_ID originalLayer = frame->GetActiveLayer();
1688 bool autoRoute = aEvent.Matches( PCB_ACTIONS::routerAutorouteSelected.MakeEvent() );
1689 bool otherEnd = aEvent.Matches( PCB_ACTIONS::routerRouteSelectedFromEnd.MakeEvent() );
1690
1691 if( m_router->RoutingInProgress() )
1692 return 0;
1693
1694 // Save selection then clear it for interactive routing
1695 PCB_SELECTION selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
1696
1697 if( selection.Size() == 0 )
1698 return 0;
1699
1700 m_toolMgr->RunAction( ACTIONS::selectionClear );
1701
1702 TOOL_EVENT pushedEvent = aEvent;
1703 frame->PushTool( aEvent );
1704
1705 auto setCursor =
1706 [&]()
1707 {
1708 frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1709 };
1710
1711 Activate();
1712 // Must be done after Activate() so that it gets set into the correct context
1713 controls->ShowCursor( true );
1714 controls->ForceCursorPosition( false );
1715 // Set initial cursor
1716 setCursor();
1717
1718 // Get all connected board items, adding pads for any footprints selected
1719 std::vector<BOARD_CONNECTED_ITEM*> itemList;
1720
1721 for( EDA_ITEM* item : selection.GetItemsSortedBySelectionOrder() )
1722 {
1723 if( item->Type() == PCB_FOOTPRINT_T )
1724 {
1725 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
1726 itemList.push_back( pad );
1727 }
1728 else if( dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) != nullptr )
1729 {
1730 itemList.push_back( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
1731 }
1732 }
1733
1734 std::shared_ptr<CONNECTIVITY_DATA> connectivity = frame->GetBoard()->GetConnectivity();
1735
1736 // For putting sequential tracks that successfully autoroute into one undo commit
1737 bool groupStart = true;
1738
1739 for( BOARD_CONNECTED_ITEM* item : itemList )
1740 {
1741 // This code is similar to GetRatsnestForPad() but it only adds the anchor for
1742 // the side of the connectivity on this pad. It also checks for ratsnest points
1743 // inside the pad (like a trace end) and counts them.
1744 RN_NET* net = connectivity->GetRatsnestForNet( item->GetNetCode() );
1745
1746 if( !net )
1747 continue;
1748
1749 std::vector<std::shared_ptr<const CN_ANCHOR>> anchors;
1750
1751 for( const CN_EDGE& edge : net->GetEdges() )
1752 {
1753 std::shared_ptr<const CN_ANCHOR> target = edge.GetTargetNode();
1754 std::shared_ptr<const CN_ANCHOR> source = edge.GetSourceNode();
1755
1756 if( !source || source->Dirty() || !target || target->Dirty() )
1757 continue;
1758
1759 if( source->Parent() == item )
1760 anchors.push_back( source );
1761 else if( target->Parent() == item )
1762 anchors.push_back( target );
1763 }
1764
1765 // Route them
1766 for( std::shared_ptr<const CN_ANCHOR> anchor : anchors )
1767 {
1768 if( !anchor->Valid() )
1769 continue;
1770
1771 // Try to return to the original layer as indicating the user's preferred
1772 // layer for autorouting tracks. The layer can be changed by the user to
1773 // finish tracks that can't complete automatically, but should be changed
1774 // back after.
1775 if( frame->GetActiveLayer() != originalLayer )
1776 frame->SetActiveLayer( originalLayer );
1777
1778 VECTOR2I ignore;
1779 m_startItem = m_router->GetWorld()->FindItemByParent( anchor->Parent() );
1780 m_startSnapPoint = anchor->Pos();
1781 m_router->SetMode( mode );
1782
1783 // Prime the interactive routing to attempt finish if we are autorouting
1784 bool autoRouted = false;
1785
1786 if( autoRoute )
1787 m_toolMgr->PostAction( PCB_ACTIONS::routerAttemptFinish, &autoRouted );
1788 else if( otherEnd )
1790
1791 // We want autorouted tracks to all be in one undo group except for
1792 // any tracks that need to be manually finished.
1793 // The undo appending for manually finished tracks is handled in peformRouting()
1794 if( groupStart )
1795 groupStart = false;
1796 else
1797 m_iface->SetCommitFlags( APPEND_UNDO );
1798
1799 // Start interactive routing. Will automatically finish if possible.
1801
1802 // Route didn't complete automatically, need to a new undo commit
1803 // for the next line so those can group as far as they autoroute
1804 if( !autoRouted )
1805 groupStart = true;
1806 }
1807 }
1808
1809 m_iface->SetCommitFlags( 0 );
1810 frame->PopTool( pushedEvent );
1811 return 0;
1812}
1813
1814
1816{
1817 if( m_inRouterTool )
1818 return 0;
1819
1821
1825
1826 if( m_router->RoutingInProgress() )
1827 {
1828 if( m_router->Mode() == mode )
1829 return 0;
1830 else
1831 m_router->StopRouting();
1832 }
1833
1834 // Deselect all items
1835 m_toolMgr->RunAction( ACTIONS::selectionClear );
1836
1837 TOOL_EVENT pushedEvent = aEvent;
1838 frame->PushTool( aEvent );
1839
1840 auto setCursor =
1841 [&]()
1842 {
1843 frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1844 };
1845
1846 Activate();
1847 // Must be done after Activate() so that it gets set into the correct context
1848 controls->ShowCursor( true );
1849 controls->ForceCursorPosition( false );
1850 // Set initial cursor
1851 setCursor();
1852
1853 m_router->SetMode( mode );
1854 m_cancelled = false;
1855
1856 if( aEvent.HasPosition() )
1857 m_toolMgr->PrimeTool( aEvent.Position() );
1858
1859 // Main loop: keep receiving events
1860 while( TOOL_EVENT* evt = Wait() )
1861 {
1862 if( !evt->IsDrag() )
1863 setCursor();
1864
1865 if( evt->IsCancelInteractive() )
1866 {
1867 frame->PopTool( pushedEvent );
1868 break;
1869 }
1870 else if( evt->IsActivate() )
1871 {
1872 if( evt->IsMoveTool() || evt->IsEditorTool() )
1873 {
1874 // leave ourselves on the stack so we come back after the move
1875 break;
1876 }
1877 else
1878 {
1879 frame->PopTool( pushedEvent );
1880 break;
1881 }
1882 }
1883 else if( evt->Action() == TA_UNDO_REDO_PRE )
1884 {
1885 m_router->ClearWorld();
1886 }
1887 else if( evt->Action() == TA_UNDO_REDO_POST || evt->Action() == TA_MODEL_CHANGE )
1888 {
1889 m_router->SyncWorld();
1890 }
1891 else if( evt->IsMotion() )
1892 {
1893 updateStartItem( *evt );
1894 }
1895 else if( evt->IsAction( &PCB_ACTIONS::dragFreeAngle ) )
1896 {
1897 updateStartItem( *evt, true );
1899 }
1900 else if( evt->IsAction( &PCB_ACTIONS::drag45Degree ) )
1901 {
1902 updateStartItem( *evt, true );
1904 }
1905 else if( evt->IsAction( &PCB_ACTIONS::breakTrack ) )
1906 {
1907 updateStartItem( *evt, true );
1908 breakTrack( );
1909 evt->SetPassEvent( false );
1910 }
1911 else if( evt->IsClick( BUT_LEFT )
1912 || evt->IsAction( &PCB_ACTIONS::routeSingleTrack )
1913 || evt->IsAction( &PCB_ACTIONS::routeDiffPair ) )
1914 {
1915 updateStartItem( *evt );
1916
1917 if( evt->HasPosition() )
1918 performRouting( evt->Position() );
1919 }
1920 else if( evt->IsAction( &ACT_PlaceThroughVia ) )
1921 {
1922 m_toolMgr->RunAction( PCB_ACTIONS::layerToggle );
1923 }
1924 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1925 {
1926 m_router->SwitchLayer( m_iface->GetPNSLayerFromBoardLayer( frame->GetActiveLayer() ) );
1927 updateStartItem( *evt );
1928 updateSizesAfterRouterEvent( m_iface->GetPNSLayerFromBoardLayer( frame->GetActiveLayer() ), m_startSnapPoint );
1929 }
1930 else if( evt->IsKeyPressed() )
1931 {
1932 // wxWidgets fails to correctly translate shifted keycodes on the wxEVT_CHAR_HOOK
1933 // event so we need to process the wxEVT_CHAR event that will follow as long as we
1934 // pass the event.
1935 evt->SetPassEvent();
1936 }
1937 else if( evt->IsClick( BUT_RIGHT ) )
1938 {
1939 m_menu->ShowContextMenu( selection() );
1940 }
1941 else
1942 {
1943 evt->SetPassEvent();
1944 }
1945
1946 if( m_cancelled )
1947 {
1948 frame->PopTool( pushedEvent );
1949 break;
1950 }
1951 }
1952
1953 // Store routing settings till the next invocation
1954 m_savedSizes = m_router->Sizes();
1955 m_router->ClearViewDecorations();
1956
1957 return 0;
1958}
1959
1960
1962{
1963 m_router->ClearViewDecorations();
1964
1965 view()->ClearPreview();
1966 view()->InitPreview();
1967
1969
1970 if( m_startItem && m_startItem->IsLocked() )
1971 {
1972 KIDIALOG dlg( frame(), _( "The selected item is locked." ), _( "Confirmation" ),
1973 wxOK | wxCANCEL | wxICON_WARNING );
1974 dlg.SetOKLabel( _( "Drag Anyway" ) );
1975 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
1976
1977 if( dlg.ShowModal() == wxID_CANCEL )
1978 return;
1979 }
1980
1981 // We don't support dragging arcs inside the PNS right now
1982 if( m_startItem && m_startItem->Kind() == PNS::ITEM::ARC_T )
1983 {
1984 if( m_router->RoutingInProgress() )
1985 m_router->StopRouting();
1986
1987 m_startItem = nullptr;
1988
1989 m_gridHelper->SetAuxAxes( false );
1990 ctls->ForceCursorPosition( false );
1991 highlightNets( false );
1992
1993 m_cancelled = true;
1994
1995 m_toolMgr->PostAction( PCB_ACTIONS::drag45Degree );
1996
1997 return;
1998 }
1999
2000 bool dragStarted = m_router->StartDragging( m_startSnapPoint, m_startItem, aMode );
2001
2002 if( !dragStarted )
2003 return;
2004
2005 if( m_startItem && m_startItem->Net() )
2006 highlightNets( true, { m_startItem->Net() } );
2007
2008 ctls->SetAutoPan( true );
2009 m_gridHelper->SetAuxAxes( true, m_startSnapPoint );
2010 frame()->UndoRedoBlock( true );
2011
2012 while( TOOL_EVENT* evt = Wait() )
2013 {
2014 ctls->ForceCursorPosition( false );
2015
2016 if( evt->IsMotion() )
2017 {
2018 updateEndItem( *evt );
2020
2021 if( PNS::DRAG_ALGO* dragger = m_router->GetDragger() )
2022 {
2023 bool dragStatus;
2024
2025 if( dragger->GetForceMarkObstaclesMode( &dragStatus ) )
2026 {
2027 view()->ClearPreview();
2028
2029 if( !dragStatus )
2030 {
2031 wxString hint;
2032 hint.Printf( _( "(%s to commit anyway.)" ),
2034
2036 statusItem->SetMessage( _( "Track violates DRC." ) );
2037 statusItem->SetHint( hint );
2038 statusItem->SetPosition( frame()->GetToolManager()->GetMousePosition() );
2039 view()->AddToPreview( statusItem );
2040 }
2041 }
2042 }
2043 }
2044 else if( evt->IsClick( BUT_LEFT ) )
2045 {
2046 bool forceFinish = false;
2047 bool forceCommit = evt->Modifier( MD_CTRL );
2048
2049 if( m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit ) )
2050 break;
2051 }
2052 else if( evt->IsClick( BUT_RIGHT ) )
2053 {
2054 m_menu->ShowContextMenu( selection() );
2055 }
2056 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2057 {
2058 if( evt->IsCancelInteractive() && !m_startItem )
2059 m_cancelled = true;
2060
2061 if( evt->IsActivate() && !evt->IsMoveTool() )
2062 m_cancelled = true;
2063
2064 break;
2065 }
2066 else if( evt->IsUndoRedo() )
2067 {
2068 // We're in an UndoRedoBlock. If we get here, something's broken.
2069 wxFAIL;
2070 break;
2071 }
2072 else if( evt->Category() == TC_COMMAND )
2073 {
2074 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2075 // but we don't at present have that, so we just knock out some of the egregious ones.
2076 if( evt->IsAction( &ACTIONS::cut )
2077 || evt->IsAction( &ACTIONS::copy )
2078 || evt->IsAction( &ACTIONS::paste )
2079 || evt->IsAction( &ACTIONS::pasteSpecial )
2081 {
2082 wxBell();
2083 }
2084 // treat an undo as an escape
2085 else if( evt->IsAction( &ACTIONS::undo ) )
2086 {
2087 if( m_startItem )
2088 break;
2089 else
2090 wxBell();
2091 }
2092 else
2093 {
2094 evt->SetPassEvent();
2095 }
2096 }
2097 else
2098 {
2099 evt->SetPassEvent();
2100 }
2101
2102 handleCommonEvents( *evt );
2103 }
2104
2105 view()->ClearPreview();
2106 view()->ShowPreview( false );
2107
2108 if( m_router->RoutingInProgress() )
2109 m_router->StopRouting();
2110
2111 m_startItem = nullptr;
2112
2113 m_gridHelper->SetAuxAxes( false );
2114 frame()->UndoRedoBlock( false );
2115 ctls->SetAutoPan( false );
2116 ctls->ForceCursorPosition( false );
2117 highlightNets( false );
2118}
2119
2120
2122 PCB_SELECTION_TOOL* aSelTool )
2123{
2124 /*
2125 * If the collection contains a trivial line corner (two connected segments)
2126 * or a non-fanout-via (a via with no more than two connected segments), then
2127 * trim the collection down to a single item (which one won't matter since
2128 * they're all connected).
2129 */
2130
2131 // First make sure we've got something that *might* match.
2132 int vias = aCollector.CountType( PCB_VIA_T );
2133 int traces = aCollector.CountType( PCB_TRACE_T );
2134 int arcs = aCollector.CountType( PCB_ARC_T );
2135
2136 // We eliminate arcs because they are not supported in the inline drag code.
2137 if( arcs > 0 )
2138 return;
2139
2140 // We need to have at least 1 via or track
2141 if( vias + traces == 0 )
2142 return;
2143
2144 // We cannot drag more than one via at a time
2145 if( vias > 1 )
2146 return;
2147
2148 // We cannot drag more than two track segments at a time
2149 if( traces > 2 )
2150 return;
2151
2152 // Fetch first PCB_TRACK (via or trace) as our reference
2153 PCB_TRACK* reference = nullptr;
2154
2155 for( int i = 0; !reference && i < aCollector.GetCount(); i++ )
2156 reference = dynamic_cast<PCB_TRACK*>( aCollector[i] );
2157
2158 // This should never happen, but just in case...
2159 if( !reference )
2160 return;
2161
2162 int refNet = reference->GetNetCode();
2163
2164 VECTOR2I refPoint( aPt.x, aPt.y );
2165 EDA_ITEM_FLAGS flags = reference->IsPointOnEnds( refPoint, -1 );
2166
2167 if( flags & STARTPOINT )
2168 refPoint = reference->GetStart();
2169 else if( flags & ENDPOINT )
2170 refPoint = reference->GetEnd();
2171
2172 // Check all items to ensure that any TRACKs are co-terminus with the reference and on
2173 // the same net.
2174 for( int i = 0; i < aCollector.GetCount(); i++ )
2175 {
2176 PCB_TRACK* neighbor = dynamic_cast<PCB_TRACK*>( aCollector[i] );
2177
2178 if( neighbor && neighbor != reference )
2179 {
2180 if( neighbor->GetNetCode() != refNet )
2181 return;
2182
2183 if( neighbor->GetStart() != refPoint && neighbor->GetEnd() != refPoint )
2184 return;
2185 }
2186 }
2187
2188 // Selection meets criteria; trim it to the reference item.
2189 aCollector.Empty();
2190 aCollector.Append( reference );
2191}
2192
2193
2194bool ROUTER_TOOL::CanInlineDrag( int aDragMode )
2195{
2197 const PCB_SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2198
2199 if( selection.Size() == 1 )
2200 {
2201 return selection.Front()->IsType( GENERAL_COLLECTOR::DraggableItems );
2202 }
2203 else if( selection.CountType( PCB_FOOTPRINT_T ) == selection.Size() )
2204 {
2205 // Footprints cannot be dragged freely.
2206 return !( aDragMode & PNS::DM_FREE_ANGLE );
2207 }
2208 else if( selection.CountType( PCB_TRACE_T ) == selection.Size() )
2209 {
2210 return true;
2211 }
2212
2213 return false;
2214}
2215
2216
2217void ROUTER_TOOL::restoreSelection( const PCB_SELECTION& aOriginalSelection )
2218{
2219 EDA_ITEMS selItems;
2220 std::copy( aOriginalSelection.Items().begin(), aOriginalSelection.Items().end(), std::back_inserter( selItems ) );
2221 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &selItems );
2222}
2223
2224
2226{
2227 const PCB_SELECTION selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2228
2229 if( selection.Empty() )
2231
2232 if( selection.Empty() || !selection.Front()->IsBOARD_ITEM() )
2233 return 0;
2234
2235 // selection gets cleared in the next action, we need a copy of the selected items.
2236 std::deque<EDA_ITEM*> selectedItems = selection.GetItems();
2237
2238 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
2239
2240 if( item->Type() != PCB_TRACE_T
2241 && item->Type() != PCB_VIA_T
2242 && item->Type() != PCB_ARC_T
2243 && item->Type() != PCB_FOOTPRINT_T )
2244 {
2245 return 0;
2246 }
2247
2248 std::set<FOOTPRINT*> footprints;
2249
2250 if( item->Type() == PCB_FOOTPRINT_T )
2251 footprints.insert( static_cast<FOOTPRINT*>( item ) );
2252
2253 // We can drag multiple footprints, but not a grab-bag of items
2254 if( selection.Size() > 1 && item->Type() == PCB_FOOTPRINT_T )
2255 {
2256 for( int idx = 1; idx < selection.Size(); ++idx )
2257 {
2258 if( !selection.GetItem( idx )->IsBOARD_ITEM() )
2259 return 0;
2260
2261 if( static_cast<BOARD_ITEM*>( selection.GetItem( idx ) )->Type() != PCB_FOOTPRINT_T )
2262 return 0;
2263
2264 footprints.insert( static_cast<FOOTPRINT*>( selection.GetItem( idx ) ) );
2265 }
2266 }
2267
2268 // If we overrode locks, we want to clear the flag from the source item before SyncWorld is
2269 // called so that virtual vias are not generated for the (now unlocked) track segment. Note in
2270 // this case the lock can't be reliably re-applied, because there is no guarantee that the end
2271 // state of the drag results in the same number of segments so it's not clear which segment to
2272 // apply the lock state to.
2273 bool wasLocked = false;
2274
2275 if( item->IsLocked() )
2276 {
2277 wasLocked = true;
2278 item->SetLocked( false );
2279 }
2280
2281 m_toolMgr->RunAction( ACTIONS::selectionClear );
2282
2283 TOOL_EVENT pushedEvent = aEvent;
2284 frame()->PushTool( aEvent );
2285 Activate();
2286
2287 m_startItem = nullptr;
2288
2289 PNS::ITEM* startItem = nullptr;
2290 PNS::ITEM_SET itemsToDrag;
2291
2292 bool showCourtyardConflicts = frame()->GetPcbNewSettings()->m_ShowCourtyardCollisions;
2293
2294 std::shared_ptr<DRC_ENGINE> drcEngine = m_toolMgr->GetTool<DRC_TOOL>()->GetDRCEngine();
2295 DRC_INTERACTIVE_COURTYARD_CLEARANCE courtyardClearanceDRC( drcEngine );
2296
2297 std::shared_ptr<CONNECTIVITY_DATA> connectivityData = board()->GetConnectivity();
2298 std::vector<BOARD_ITEM*> dynamicItems;
2299 std::unique_ptr<CONNECTIVITY_DATA> dynamicData = nullptr;
2300 VECTOR2I lastOffset;
2301 std::vector<PNS::ITEM*> leaderSegments;
2302 bool singleFootprintDrag = false;
2303
2304 if( !footprints.empty() )
2305 {
2306 if( footprints.size() == 1 )
2307 singleFootprintDrag = true;
2308
2309 if( showCourtyardConflicts )
2310 courtyardClearanceDRC.Init( board() );
2311
2312 for( FOOTPRINT* footprint : footprints )
2313 {
2314 for( PAD* pad : footprint->Pads() )
2315 {
2316 PNS::ITEM* solid = m_router->GetWorld()->FindItemByParent( pad );
2317
2318 if( solid )
2319 itemsToDrag.Add( solid );
2320
2321 if( pad->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
2322 {
2323 if( connectivityData->GetRatsnestForPad( pad ).size() > 0 )
2324 dynamicItems.push_back( pad );
2325 }
2326 }
2327
2328 for( ZONE* zone : footprint->Zones() )
2329 {
2330 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( zone ) )
2331 itemsToDrag.Add( solid );
2332 }
2333
2334 for( BOARD_ITEM* shape : footprint->GraphicalItems() )
2335 {
2336 if( shape->GetLayer() == Edge_Cuts
2337 || shape->GetLayer() == Margin
2338 || IsCopperLayer( shape->GetLayer() ) )
2339 {
2340 for( PNS::ITEM* solid : m_router->GetWorld()->FindItemsByParent( shape ) )
2341 itemsToDrag.Add( solid );
2342 }
2343 }
2344
2345 if( showCourtyardConflicts )
2346 courtyardClearanceDRC.m_FpInMove.push_back( footprint );
2347 }
2348
2349 dynamicData = std::make_unique<CONNECTIVITY_DATA>( board()->GetConnectivity(),
2350 dynamicItems, true );
2351 connectivityData->BlockRatsnestItems( dynamicItems );
2352 }
2353 else
2354 {
2355 for( const EDA_ITEM* selItem : selectedItems )
2356 {
2357 if( !selItem->IsBOARD_ITEM() )
2358 continue;
2359
2360 const BOARD_ITEM* boardItem = static_cast<const BOARD_ITEM*>( selItem );
2361 PNS::ITEM* pnsItem = m_router->GetWorld()->FindItemByParent( boardItem );
2362
2363 if( !pnsItem )
2364 continue;
2365
2366 if( pnsItem->OfKind( PNS::ITEM::SEGMENT_T )
2367 || pnsItem->OfKind( PNS::ITEM::VIA_T )
2368 || pnsItem->OfKind( PNS::ITEM::ARC_T ) )
2369 {
2370 itemsToDrag.Add( pnsItem );
2371 }
2372 }
2373 }
2374
2375 GAL* gal = m_toolMgr->GetView()->GetGAL();
2376 VECTOR2I p0 = GetClampedCoords( controls()->GetCursorPosition( false ), COORDS_PADDING );
2377 VECTOR2I p = p0;
2378
2379 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
2380 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
2381
2382 if( itemsToDrag.Count() >= 1 )
2383 {
2384 // Snap to closest item
2385 int layer = m_iface->GetPNSLayerFromBoardLayer( m_originalActiveLayer );
2386 PNS::ITEM* closestItem = nullptr;
2387 SEG::ecoord closestDistSq = std::numeric_limits<SEG::ecoord>::max();
2388
2389 for( PNS::ITEM* pitem : itemsToDrag.Items() )
2390 {
2391 SEG::ecoord distSq = pitem->Shape( layer )->SquaredDistance( p0, 0 );
2392
2393 if( distSq < closestDistSq )
2394 {
2395 closestDistSq = distSq;
2396 closestItem = pitem;
2397 }
2398 }
2399
2400 if( closestItem )
2401 {
2402 p = snapToItem( closestItem, p0 );
2403
2404 m_startItem = closestItem;
2405
2406 if( closestItem->Net() )
2407 highlightNets( true, { closestItem->Net() } );
2408 }
2409 }
2410
2411 if( !footprints.empty() && singleFootprintDrag )
2412 {
2413 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
2414
2415 // The mouse is going to be moved on grid before dragging begins.
2416 VECTOR2I tweakedMousePos;
2418
2419 // Check if user wants to warp the mouse to origin of moved object
2420
2421 if( editFrame->GetMoveWarpsCursor() )
2422 tweakedMousePos = footprint->GetPosition(); // Use footprint anchor to warp mouse
2423 else
2424 tweakedMousePos = GetClampedCoords( controls()->GetCursorPosition(),
2425 COORDS_PADDING ); // Just use current mouse pos
2426
2427 // We tweak the mouse position using the value from above, and then use that as the
2428 // start position to prevent the footprint from jumping when we start dragging.
2429 // First we move the visual cross hair cursor...
2430 controls()->ForceCursorPosition( true, tweakedMousePos );
2431 controls()->SetCursorPosition( tweakedMousePos ); // ...then the mouse pointer
2432
2433 // Now that the mouse is in the right position, get a copy of the position to use later
2434 p = controls()->GetCursorPosition();
2435 }
2436
2437 int dragMode = aEvent.Parameter<int> ();
2438
2439 bool dragStarted = m_router->StartDragging( p, itemsToDrag, dragMode );
2440
2441 if( !dragStarted )
2442 {
2443 if( wasLocked )
2444 item->SetLocked( true );
2445
2446 if( !footprints.empty() )
2447 connectivityData->ClearLocalRatsnest();
2448
2449 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
2450 courtyardClearanceDRC.ClearConflicts( getView() );
2451
2453 controls()->ForceCursorPosition( false );
2454 frame()->PopTool( pushedEvent );
2455 highlightNets( false );
2456 return 0;
2457 }
2458
2459 m_gridHelper->SetAuxAxes( true, p );
2460 controls()->ShowCursor( true );
2461 controls()->SetAutoPan( true );
2462 frame()->UndoRedoBlock( true );
2463
2464 view()->ClearPreview();
2465 view()->InitPreview();
2466
2467 auto setCursor =
2468 [&]()
2469 {
2470 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2471 };
2472
2473 // Set initial cursor
2474 setCursor();
2475
2476 // Set the initial visible area
2477 BOX2D viewAreaD = getView()->GetGAL()->GetVisibleWorldExtents();
2478 m_router->SetVisibleViewArea( BOX2ISafe( viewAreaD ) );
2479
2480 // Send an initial movement to prime the collision detection
2481 m_router->Move( p, nullptr );
2482
2483 bool hasMouseMoved = false;
2484 bool hasMultidragCancelled = false;
2485
2486 while( TOOL_EVENT* evt = Wait() )
2487 {
2488 setCursor();
2489
2490 if( evt->IsCancelInteractive() || evt->IsActivate() )
2491 {
2492 if( wasLocked )
2493 item->SetLocked( true );
2494
2495 hasMultidragCancelled = true;
2496
2497 break;
2498 }
2499 else if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
2500 {
2501 hasMouseMoved = true;
2502 updateEndItem( *evt );
2504
2505 view()->ClearPreview();
2506
2507 if( !footprints.empty() )
2508 {
2509 VECTOR2I offset = m_endSnapPoint - p;
2510 BOARD_ITEM* previewItem;
2511
2512 for( FOOTPRINT* footprint : footprints )
2513 {
2514 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
2515 {
2516 previewItem = static_cast<BOARD_ITEM*>( drawing->Clone() );
2517 previewItem->Move( offset );
2518
2519 view()->AddToPreview( previewItem );
2520 view()->Hide( drawing, true );
2521 }
2522
2523 for( PAD* pad : footprint->Pads() )
2524 {
2525 if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none()
2526 && pad->GetDrillSize().x == 0 )
2527 {
2528 previewItem = static_cast<BOARD_ITEM*>( pad->Clone() );
2529 previewItem->Move( offset );
2530
2531 view()->AddToPreview( previewItem );
2532 }
2533 else
2534 {
2535 // Pads with copper or holes are handled by the router
2536 }
2537
2538 view()->Hide( pad, true );
2539 }
2540
2541 previewItem = static_cast<BOARD_ITEM*>( footprint->Reference().Clone() );
2542 previewItem->Move( offset );
2543 view()->AddToPreview( previewItem );
2544 view()->Hide( &footprint->Reference() );
2545
2546 previewItem = static_cast<BOARD_ITEM*>( footprint->Value().Clone() );
2547 previewItem->Move( offset );
2548 view()->AddToPreview( previewItem );
2549 view()->Hide( &footprint->Value() );
2550
2551 if( showCourtyardConflicts )
2552 footprint->Move( offset );
2553 }
2554
2555 if( showCourtyardConflicts )
2556 {
2557 courtyardClearanceDRC.Run();
2558 courtyardClearanceDRC.UpdateConflicts( getView(), false );
2559
2560 for( FOOTPRINT* footprint : footprints )
2561 footprint->Move( -offset );
2562 }
2563
2564 // Update ratsnest
2565 dynamicData->Move( offset - lastOffset );
2566 lastOffset = offset;
2567 connectivityData->ComputeLocalRatsnest( dynamicItems, dynamicData.get(), offset );
2568 }
2569
2570 if( PNS::DRAG_ALGO* dragger = m_router->GetDragger() )
2571 {
2572 bool dragStatus;
2573
2574 if( dragger->GetForceMarkObstaclesMode( &dragStatus ) )
2575 {
2576 if( !dragStatus )
2577 {
2578 wxString hint;
2579 hint.Printf( _( "(%s to commit anyway.)" ),
2581
2583 statusItem->SetMessage( _( "Track violates DRC." ) );
2584 statusItem->SetHint( hint );
2585 statusItem->SetPosition( frame()->GetToolManager()->GetMousePosition() );
2586 view()->AddToPreview( statusItem );
2587 }
2588 }
2589 }
2590 }
2591 else if( hasMouseMoved && ( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) ) )
2592 {
2593 bool forceFinish = false;
2594 bool forceCommit = evt->Modifier( MD_CTRL );
2595
2596 updateEndItem( *evt );
2597 m_router->FixRoute( m_endSnapPoint, m_endItem, forceFinish, forceCommit );
2598 leaderSegments = m_router->GetLastCommittedLeaderSegments();
2599
2600 break;
2601 }
2602 else if( evt->IsUndoRedo() )
2603 {
2604 // We're in an UndoRedoBlock. If we get here, something's broken.
2605 wxFAIL;
2606 break;
2607 }
2608 else if( evt->Category() == TC_COMMAND )
2609 {
2610 // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
2611 // but we don't at present have that, so we just knock out some of the egregious ones.
2612 if( evt->IsAction( &ACTIONS::cut )
2613 || evt->IsAction( &ACTIONS::copy )
2614 || evt->IsAction( &ACTIONS::paste )
2615 || evt->IsAction( &ACTIONS::pasteSpecial )
2617 {
2618 wxBell();
2619 }
2620 // treat an undo as an escape
2621 else if( evt->IsAction( &ACTIONS::undo ) )
2622 {
2623 if( wasLocked )
2624 item->SetLocked( true );
2625
2626 break;
2627 }
2628 else
2629 {
2630 evt->SetPassEvent();
2631 }
2632 }
2633 else
2634 {
2635 evt->SetPassEvent();
2636 }
2637
2638 handleCommonEvents( *evt );
2639 }
2640
2641 if( !footprints.empty() )
2642 {
2643 for( FOOTPRINT* footprint : footprints )
2644 {
2645 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
2646 view()->Hide( drawing, false );
2647
2648 view()->Hide( &footprint->Reference(), false );
2649 view()->Hide( &footprint->Value(), false );
2650
2651 for( PAD* pad : footprint->Pads() )
2652 view()->Hide( pad, false );
2653 }
2654
2655 view()->ClearPreview();
2656 view()->ShowPreview( false );
2657
2658 connectivityData->ClearLocalRatsnest();
2659 }
2660
2661 // Clear temporary COURTYARD_CONFLICT flag and ensure the conflict shadow is cleared
2662 courtyardClearanceDRC.ClearConflicts( getView() );
2663
2664 if( m_router->RoutingInProgress() )
2665 m_router->StopRouting();
2666
2667
2668 if( itemsToDrag.Size() && hasMultidragCancelled )
2669 {
2671 }
2672 else if( leaderSegments.size() )
2673 {
2674 std::vector<EDA_ITEM*> newItems;
2675
2676 for( auto lseg : leaderSegments )
2677 newItems.push_back( lseg->Parent() );
2678
2679 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &newItems );
2680 }
2681
2682 m_gridHelper->SetAuxAxes( false );
2683 controls()->SetAutoPan( false );
2684 controls()->ForceCursorPosition( false );
2685 frame()->UndoRedoBlock( false );
2686 frame()->PopTool( pushedEvent );
2687 highlightNets( false );
2688 view()->ClearPreview();
2689 view()->ShowPreview( false );
2690
2691 return 0;
2692}
2693
2694
2696{
2697 const SELECTION& selection = m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
2698
2699 if( selection.Size() != 1 )
2700 return 0;
2701
2702 const BOARD_CONNECTED_ITEM* item =
2703 static_cast<const BOARD_CONNECTED_ITEM*>( selection.Front() );
2704
2705 if( item->Type() != PCB_TRACE_T && item->Type() != PCB_ARC_T )
2706 return 0;
2707
2708 m_toolMgr->RunAction( ACTIONS::selectionClear );
2709
2710 Activate();
2711
2712 m_startItem = m_router->GetWorld()->FindItemByParent( item );
2713
2714 TOOL_MANAGER* toolManager = frame()->GetToolManager();
2715 GAL* gal = toolManager->GetView()->GetGAL();
2716
2717 m_gridHelper->SetUseGrid( gal->GetGridSnapping() && !aEvent.DisableGridSnapping() );
2718 m_gridHelper->SetSnap( !aEvent.Modifier( MD_SHIFT ) );
2719
2720 controls()->ForceCursorPosition( false );
2721
2722 if( toolManager->IsContextMenuActive() )
2723 {
2724 // If we're here from a context menu then we need to get the position of the
2725 // cursor when the context menu was invoked. This is used to figure out the
2726 // break point on the track.
2728 }
2729 else
2730 {
2731 // If we're here from a hotkey, then get the current mouse position so we know
2732 // where to break the track.
2733 m_startSnapPoint = snapToItem( m_startItem, controls()->GetCursorPosition() );
2734 }
2735
2736 if( m_startItem && m_startItem->IsLocked() )
2737 {
2738 KIDIALOG dlg( frame(), _( "The selected item is locked." ), _( "Confirmation" ),
2739 wxOK | wxCANCEL | wxICON_WARNING );
2740 dlg.SetOKLabel( _( "Break Track" ) );
2741 dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
2742
2743 if( dlg.ShowModal() == wxID_CANCEL )
2744 return 0;
2745 }
2746
2747 frame()->UndoRedoBlock( true );
2748 breakTrack();
2749
2750 if( m_router->RoutingInProgress() )
2751 m_router->StopRouting();
2752
2753 frame()->UndoRedoBlock( false );
2754
2755 return 0;
2756}
2757
2758
2760{
2762 DIALOG_TRACK_VIA_SIZE sizeDlg( frame(), bds );
2763
2764 if( sizeDlg.ShowModal() == wxID_OK )
2765 {
2766 bds.m_TempOverrideTrackWidth = true;
2767 bds.UseCustomTrackViaSize( true );
2768
2771 }
2772
2773 return 0;
2774}
2775
2776
2778{
2779 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
2780
2781 if( !m_router->GetCurrentNets().empty() )
2782 m_iface->ImportSizes( sizes, m_startItem, m_router->GetCurrentNets()[0], VECTOR2D() );
2783
2784 m_router->UpdateSizes( sizes );
2785
2786 // Changing the track width can affect the placement, so call the
2787 // move routine without changing the destination
2788 // Update end item first to avoid moving to an invalid/missing item
2789 updateEndItem( aEvent );
2791
2793
2794 return 0;
2795}
2796
2797
2799{
2800 std::vector<MSG_PANEL_ITEM> items;
2801
2802 if( m_router->GetState() == PNS::ROUTER::ROUTE_TRACK )
2803 {
2804 PNS::SIZES_SETTINGS sizes( m_router->Sizes() );
2805 PNS::RULE_RESOLVER* resolver = m_iface->GetRuleResolver();
2806 PNS::CONSTRAINT constraint;
2807 std::vector<PNS::NET_HANDLE> nets = m_router->GetCurrentNets();
2808 wxString description;
2809 wxString secondary;
2810 wxString mode;
2811
2813 {
2814 wxASSERT( nets.size() >= 2 );
2815
2816 NETINFO_ITEM* netA = static_cast<NETINFO_ITEM*>( nets[0] );
2817 NETINFO_ITEM* netB = static_cast<NETINFO_ITEM*>( nets[1] );
2818 wxASSERT( netA );
2819 wxASSERT( netB );
2820
2821 description = wxString::Format( _( "Routing Diff Pair: %s" ),
2822 netA->GetNetname() + wxT( ", " ) + netB->GetNetname() );
2823
2824 wxString netclass;
2825 NETCLASS* netclassA = netA->GetNetClass();
2826 NETCLASS* netclassB = netB->GetNetClass();
2827
2828 if( *netclassA == *netclassB )
2829 netclass = netclassA->GetHumanReadableName();
2830 else
2831 netclass = netclassA->GetHumanReadableName() + wxT( ", " )
2832 + netclassB->GetHumanReadableName();
2833
2834 secondary = wxString::Format( _( "Resolved Netclass: %s" ),
2835 UnescapeString( netclass ) );
2836 }
2837 else if( !nets.empty() && nets[0] )
2838 {
2839 NETINFO_ITEM* net = static_cast<NETINFO_ITEM*>( nets[0] );
2840
2841 description = wxString::Format( _( "Routing Track: %s" ),
2842 net->GetNetname() );
2843
2844 secondary = wxString::Format(
2845 _( "Resolved Netclass: %s" ),
2847 }
2848 else
2849 {
2850 description = _( "Routing Track" );
2851 secondary = _( "(no net)" );
2852 }
2853
2854 items.emplace_back( description, secondary );
2855
2856 wxString cornerMode;
2857
2858 if( m_router->Settings().GetFreeAngleMode() )
2859 {
2860 cornerMode = _( "Free-angle" );
2861 }
2862 else
2863 {
2864 switch( m_router->Settings().GetCornerMode() )
2865 {
2866 case DIRECTION_45::CORNER_MODE::MITERED_45: cornerMode = _( "45-degree" ); break;
2867 case DIRECTION_45::CORNER_MODE::ROUNDED_45: cornerMode = _( "45-degree rounded" ); break;
2868 case DIRECTION_45::CORNER_MODE::MITERED_90: cornerMode = _( "90-degree" ); break;
2869 case DIRECTION_45::CORNER_MODE::ROUNDED_90: cornerMode = _( "90-degree rounded" ); break;
2870 default: break;
2871 }
2872 }
2873
2874 items.emplace_back( _( "Corner Style" ), cornerMode );
2875
2876 switch( m_router->Settings().Mode() )
2877 {
2878 case PNS::PNS_MODE::RM_MarkObstacles: mode = _( "Highlight collisions" ); break;
2879 case PNS::PNS_MODE::RM_Walkaround: mode = _( "Walk around" ); break;
2880 case PNS::PNS_MODE::RM_Shove: mode = _( "Shove" ); break;
2881 default: break;
2882 }
2883
2884 items.emplace_back( _( "Mode" ), mode );
2885
2886#define FORMAT_VALUE( x ) frame()->MessageTextFromValue( x )
2887
2889 {
2890 items.emplace_back( wxString::Format( _( "Track Width: %s" ),
2891 FORMAT_VALUE( sizes.DiffPairWidth() ) ),
2892 wxString::Format( _( "(from %s)" ),
2893 sizes.GetDiffPairWidthSource() ) );
2894
2895 items.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
2896 FORMAT_VALUE( sizes.Clearance() ) ),
2897 wxString::Format( _( "(from %s)" ),
2898 sizes.GetClearanceSource() ) );
2899
2900 items.emplace_back( wxString::Format( _( "Diff Pair Gap: %s" ),
2901 FORMAT_VALUE( sizes.DiffPairGap() ) ),
2902 wxString::Format( _( "(from %s)" ),
2903 sizes.GetDiffPairGapSource() ) );
2904
2905 const PNS::ITEM_SET& traces = m_router->Placer()->Traces();
2906 wxASSERT( traces.Count() == 2 );
2907
2908 if( resolver->QueryConstraint( PNS::CONSTRAINT_TYPE::CT_MAX_UNCOUPLED, traces[0],
2909 traces[1], m_router->GetCurrentLayer(), &constraint ) )
2910 {
2911 items.emplace_back( wxString::Format( _( "DP Max Uncoupled-length: %s" ),
2912 FORMAT_VALUE( constraint.m_Value.Max() ) ),
2913 wxString::Format( _( "(from %s)" ),
2914 constraint.m_RuleName ) );
2915 }
2916 }
2917 else
2918 {
2919 items.emplace_back( wxString::Format( _( "Track Width: %s" ),
2920 FORMAT_VALUE( sizes.TrackWidth() ) ),
2921 wxString::Format( _( "(from %s)" ),
2922 sizes.GetWidthSource() ) );
2923
2924 items.emplace_back( wxString::Format( _( "Min Clearance: %s" ),
2925 FORMAT_VALUE( sizes.Clearance() ) ),
2926 wxString::Format( _( "(from %s)" ),
2927 sizes.GetClearanceSource() ) );
2928 }
2929
2930#undef FORMAT_VALUE
2931
2932 frame()->SetMsgPanel( items );
2933 }
2934 else
2935 {
2936 frame()->SetMsgPanel( board() );
2937 return;
2938 }
2939}
2940
2941
2943{
2945
2959
2966
3002
3005
3010}
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:930
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1041
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:288
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:65
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
Definition project.h:210
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:204
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:169
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:676
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.