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