1 /*
2  * KiRouter - a push-and-(sometimes-)shove PCB router
3  *
4  * Copyright (C) 2013-2017 CERN
5  * Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
7  *
8  * This program is free software: you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation, either version 3 of the License, or (at your
11  * option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "class_draw_panel_gal.h"
23 #include <dialogs/dialog_pns_length_tuning_settings.h>
24 #include <tool/tool_manager.h>
25 #include <tools/pcb_actions.h>
26 #include "pns_router.h"
27 #include "pns_meander_placer.h" // fixme: move settings to separate header
28 #include "pns_tune_status_popup.h"
29 
30 #include "length_tuner_tool.h"
31 #include <bitmaps.h>
32 #include <tools/tool_event_utils.h>
33 
34 using namespace KIGFX;
35 
36 // Actions, being statically-defined, require specialized I18N handling.  We continue to
37 // use the _() macro so that string harvesting by the I18N framework doesn't have to be
38 // specialized, but we don't translate on initialization and instead do it in the getters.
39 
40 #undef _
41 #define _(s) s
42 
43 static TOOL_ACTION ACT_StartTuning( "pcbnew.LengthTuner.StartTuning",
44         AS_CONTEXT,
45         'X', LEGACY_HK_NAME( "Add New Track" ),
46         _( "New Track" ), _( "Starts laying a new track." ) );
47 
48 static TOOL_ACTION ACT_EndTuning( "pcbnew.LengthTuner.EndTuning",
49         AS_CONTEXT,
50         WXK_END, LEGACY_HK_NAME( "Stop laying the current track." ),
51         _( "End Track" ), _( "Stops laying the current meander." ) );
52 
53 static TOOL_ACTION ACT_SpacingIncrease( "pcbnew.LengthTuner.SpacingIncrease",
54         AS_CONTEXT,
55         '1', LEGACY_HK_NAME( "Increase meander spacing by one step." ),
56         _( "Increase Spacing" ), _( "Increase meander spacing by one step." ),
57         BITMAPS::router_len_tuner_dist_incr );
58 
59 static TOOL_ACTION ACT_SpacingDecrease( "pcbnew.LengthTuner.SpacingDecrease",
60         AS_CONTEXT,
61         '2', LEGACY_HK_NAME( "Decrease meander spacing by one step." ),
62         _( "Decrease Spacing" ), _( "Decrease meander spacing by one step." ),
63         BITMAPS::router_len_tuner_dist_decr );
64 
65 static TOOL_ACTION ACT_AmplIncrease( "pcbnew.LengthTuner.AmplIncrease",
66         AS_CONTEXT,
67         '3', LEGACY_HK_NAME( "Increase meander amplitude by one step." ),
68         _( "Increase Amplitude" ), _( "Increase meander amplitude by one step." ),
69         BITMAPS::router_len_tuner_amplitude_incr );
70 
71 static TOOL_ACTION ACT_AmplDecrease( "pcbnew.LengthTuner.AmplDecrease",
72         AS_CONTEXT,
73         '4', LEGACY_HK_NAME( "Decrease meander amplitude by one step." ),
74         _( "Decrease Amplitude" ), _( "Decrease meander amplitude by one step." ),
75         BITMAPS::router_len_tuner_amplitude_decr );
76 
77 #undef _
78 #define _(s) wxGetTranslation((s))
79 
80 
LENGTH_TUNER_TOOL()81 LENGTH_TUNER_TOOL::LENGTH_TUNER_TOOL() :
82     TOOL_BASE( "pcbnew.LengthTuner" )
83 {
84     // set the initial tune mode for the settings dialog,
85     // in case the dialog is opened before the tool is activated the first time
86     m_lastTuneMode = PNS::ROUTER_MODE::PNS_MODE_TUNE_SINGLE;
87 }
88 
89 
~LENGTH_TUNER_TOOL()90 LENGTH_TUNER_TOOL::~LENGTH_TUNER_TOOL()
91 {
92 }
93 
94 
Init()95 bool LENGTH_TUNER_TOOL::Init()
96 {
97     auto& menu = m_menu.GetMenu();
98 
99     menu.SetTitle( _( "Length Tuner" ) );
100     menu.SetIcon( BITMAPS::router_len_tuner );
101     menu.DisplayTitle( true );
102 
103     menu.AddItem( ACTIONS::cancelInteractive,             SELECTION_CONDITIONS::ShowAlways );
104 
105     menu.AddSeparator();
106 
107     menu.AddItem( ACT_SpacingIncrease,                    SELECTION_CONDITIONS::ShowAlways );
108     menu.AddItem( ACT_SpacingDecrease,                    SELECTION_CONDITIONS::ShowAlways );
109     menu.AddItem( ACT_AmplIncrease,                       SELECTION_CONDITIONS::ShowAlways );
110     menu.AddItem( ACT_AmplDecrease,                       SELECTION_CONDITIONS::ShowAlways );
111     menu.AddItem( PCB_ACTIONS::lengthTunerSettingsDialog, SELECTION_CONDITIONS::ShowAlways );
112 
113     return true;
114 }
115 
116 
Reset(RESET_REASON aReason)117 void LENGTH_TUNER_TOOL::Reset( RESET_REASON aReason )
118 {
119     if( aReason == RUN )
120         TOOL_BASE::Reset( aReason );
121 }
122 
123 
updateStatusPopup(PNS_TUNE_STATUS_POPUP & aPopup)124 void LENGTH_TUNER_TOOL::updateStatusPopup( PNS_TUNE_STATUS_POPUP& aPopup )
125 {
126     // fixme: wx code not allowed inside tools!
127     wxPoint p = wxGetMousePosition();
128 
129     p.x += 20;
130     p.y += 20;
131 
132     aPopup.UpdateStatus( m_router );
133     aPopup.Move( p );
134 }
135 
136 
performTuning()137 void LENGTH_TUNER_TOOL::performTuning()
138 {
139     if( m_startItem )
140     {
141         frame()->SetActiveLayer( ToLAYER_ID ( m_startItem->Layers().Start() ) );
142 
143         if( m_startItem->Net() >= 0 )
144             highlightNet( true, m_startItem->Net() );
145     }
146 
147     controls()->ForceCursorPosition( false );
148     controls()->SetAutoPan( true );
149 
150     int layer = m_startItem ? m_startItem->Layer() : static_cast<int>( frame()->GetActiveLayer() );
151 
152     if( !m_router->StartRouting( m_startSnapPoint, m_startItem, layer ) )
153     {
154         frame()->ShowInfoBarMsg( m_router->FailureReason() );
155         highlightNet( false );
156         return;
157     }
158 
159     auto placer = static_cast<PNS::MEANDER_PLACER_BASE*>( m_router->Placer() );
160 
161     placer->UpdateSettings( m_savedMeanderSettings );
162     frame()->UndoRedoBlock( true );
163 
164     VECTOR2I end = getViewControls()->GetMousePosition();
165 
166     // Create an instance of PNS_TUNE_STATUS_POPUP.
167     PNS_TUNE_STATUS_POPUP statusPopup( frame() );
168     statusPopup.Popup();
169 
170     m_router->Move( end, nullptr );
171     updateStatusPopup( statusPopup );
172 
173     auto setCursor =
174             [&]()
175             {
176                 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
177             };
178 
179     // Set initial cursor
180     setCursor();
181 
182     while( TOOL_EVENT* evt = Wait() )
183     {
184         setCursor();
185 
186         if( evt->IsCancelInteractive() || evt->IsActivate() )
187         {
188             break;
189         }
190         else if( evt->IsMotion() )
191         {
192             end = evt->Position();
193             m_router->Move( end, nullptr );
194             updateStatusPopup( statusPopup );
195         }
196         else if( evt->IsClick( BUT_LEFT ) )
197         {
198             if( m_router->FixRoute( evt->Position(), nullptr ) )
199                 break;
200         }
201         else if( evt->IsClick( BUT_RIGHT ) )
202         {
203             m_menu.ShowContextMenu( selection() );
204         }
205         else if( evt->IsAction( &ACT_EndTuning ) )
206         {
207             if( m_router->FixRoute( end, nullptr ) )
208                 break;
209         }
210         else if( evt->IsAction( &ACT_AmplDecrease ) )
211         {
212             placer->AmplitudeStep( -1 );
213             m_router->Move( end, nullptr );
214             updateStatusPopup( statusPopup );
215         }
216         else if( evt->IsAction( &ACT_AmplIncrease ) )
217         {
218             placer->AmplitudeStep( 1 );
219             m_router->Move( end, nullptr );
220             updateStatusPopup( statusPopup );
221         }
222         else if(evt->IsAction( &ACT_SpacingDecrease ) )
223         {
224             placer->SpacingStep( -1 );
225             m_router->Move( end, nullptr );
226             updateStatusPopup( statusPopup );
227         }
228         else if( evt->IsAction( &ACT_SpacingIncrease ) )
229         {
230             placer->SpacingStep( 1 );
231             m_router->Move( end, nullptr );
232             updateStatusPopup( statusPopup );
233         }
234         else if( evt->IsAction( &PCB_ACTIONS::lengthTunerSettingsDialog ) )
235         {
236             statusPopup.Hide();
237             TOOL_EVENT dummy;
238             meanderSettingsDialog( dummy );
239             statusPopup.Show();
240         }
241     }
242 
243     m_router->StopRouting();
244     frame()->UndoRedoBlock( false );
245 
246     controls()->SetAutoPan( false );
247     controls()->ForceCursorPosition( false );
248     frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
249     highlightNet( false );
250 }
251 
252 
setTransitions()253 void LENGTH_TUNER_TOOL::setTransitions()
254 {
255     Go( &LENGTH_TUNER_TOOL::MainLoop,              PCB_ACTIONS::routerTuneSingleTrace.MakeEvent() );
256     Go( &LENGTH_TUNER_TOOL::MainLoop,              PCB_ACTIONS::routerTuneDiffPair.MakeEvent() );
257     Go( &LENGTH_TUNER_TOOL::MainLoop,
258         PCB_ACTIONS::routerTuneDiffPairSkew.MakeEvent() );
259 
260     // in case tool is inactive, otherwise the event is handled in the tool loop
261     Go( &LENGTH_TUNER_TOOL::meanderSettingsDialog,
262         PCB_ACTIONS::lengthTunerSettingsDialog.MakeEvent() );
263 }
264 
265 
MainLoop(const TOOL_EVENT & aEvent)266 int LENGTH_TUNER_TOOL::MainLoop( const TOOL_EVENT& aEvent )
267 {
268     // Deselect all items
269     m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
270 
271     std::string tool = aEvent.GetCommandStr().get();
272     frame()->PushTool( tool );
273 
274     auto setCursor =
275             [&]()
276             {
277                 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
278             };
279 
280     Activate();
281     // Must be done after Activate() so that it gets set into the correct context
282     controls()->ShowCursor( true );
283     // Set initial cursor
284     setCursor();
285 
286     // Router mode must be set after Activate()
287     m_lastTuneMode = aEvent.Parameter<PNS::ROUTER_MODE>();
288     m_router->SetMode( m_lastTuneMode );
289 
290     // Main loop: keep receiving events
291     while( TOOL_EVENT* evt = Wait() )
292     {
293         setCursor();
294 
295         if( evt->IsCancelInteractive() || evt->IsActivate() )
296         {
297             break; // Finish
298         }
299         else if( evt->Action() == TA_UNDO_REDO_PRE )
300         {
301             m_router->ClearWorld();
302         }
303         else if( evt->Action() == TA_UNDO_REDO_POST || evt->Action() == TA_MODEL_CHANGE )
304         {
305             m_router->SyncWorld();
306         }
307         else if( evt->IsMotion() )
308         {
309             updateStartItem( *evt );
310         }
311         else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACT_StartTuning ) )
312         {
313             updateStartItem( *evt );
314             performTuning();
315         }
316         else if( evt->IsAction( &PCB_ACTIONS::lengthTunerSettingsDialog ) )
317         {
318             TOOL_EVENT dummy;
319             meanderSettingsDialog( dummy );
320         }
321         else if( evt->IsClick( BUT_RIGHT ) )
322         {
323             m_menu.ShowContextMenu( selection() );
324         }
325         else
326         {
327             evt->SetPassEvent();
328         }
329     }
330 
331     // Store routing settings till the next invocation
332     m_savedSizes = m_router->Sizes();
333 
334     frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
335     frame()->PopTool( tool );
336     return 0;
337 }
338 
339 
meanderSettingsDialog(const TOOL_EVENT & aEvent)340 int LENGTH_TUNER_TOOL::meanderSettingsDialog( const TOOL_EVENT& aEvent )
341 {
342     PNS::MEANDER_PLACER_BASE* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( m_router->Placer() );
343 
344     PNS::MEANDER_SETTINGS settings = placer ? placer->MeanderSettings() : m_savedMeanderSettings;
345     DIALOG_PNS_LENGTH_TUNING_SETTINGS settingsDlg( frame(), settings, m_lastTuneMode );
346 
347     if( settingsDlg.ShowModal() == wxID_OK )
348     {
349         if( placer )
350             placer->UpdateSettings( settings );
351 
352         m_savedMeanderSettings = settings;
353     }
354 
355     return 0;
356 }
357