/* * KiRouter - a push-and-(sometimes-)shove PCB router * * Copyright (C) 2013-2017 CERN * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors. * Author: Tomasz Wlostowski * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include #include #include #include #include #include "pns_router.h" #include "pns_meander_placer.h" // fixme: move settings to separate header #include "pns_tune_status_popup.h" #include "length_tuner_tool.h" #include #include using namespace KIGFX; // Actions, being statically-defined, require specialized I18N handling. We continue to // use the _() macro so that string harvesting by the I18N framework doesn't have to be // specialized, but we don't translate on initialization and instead do it in the getters. #undef _ #define _(s) s static TOOL_ACTION ACT_StartTuning( "pcbnew.LengthTuner.StartTuning", AS_CONTEXT, 'X', LEGACY_HK_NAME( "Add New Track" ), _( "New Track" ), _( "Starts laying a new track." ) ); static TOOL_ACTION ACT_EndTuning( "pcbnew.LengthTuner.EndTuning", AS_CONTEXT, WXK_END, LEGACY_HK_NAME( "Stop laying the current track." ), _( "End Track" ), _( "Stops laying the current meander." ) ); static TOOL_ACTION ACT_SpacingIncrease( "pcbnew.LengthTuner.SpacingIncrease", AS_CONTEXT, '1', LEGACY_HK_NAME( "Increase meander spacing by one step." ), _( "Increase Spacing" ), _( "Increase meander spacing by one step." ), BITMAPS::router_len_tuner_dist_incr ); static TOOL_ACTION ACT_SpacingDecrease( "pcbnew.LengthTuner.SpacingDecrease", AS_CONTEXT, '2', LEGACY_HK_NAME( "Decrease meander spacing by one step." ), _( "Decrease Spacing" ), _( "Decrease meander spacing by one step." ), BITMAPS::router_len_tuner_dist_decr ); static TOOL_ACTION ACT_AmplIncrease( "pcbnew.LengthTuner.AmplIncrease", AS_CONTEXT, '3', LEGACY_HK_NAME( "Increase meander amplitude by one step." ), _( "Increase Amplitude" ), _( "Increase meander amplitude by one step." ), BITMAPS::router_len_tuner_amplitude_incr ); static TOOL_ACTION ACT_AmplDecrease( "pcbnew.LengthTuner.AmplDecrease", AS_CONTEXT, '4', LEGACY_HK_NAME( "Decrease meander amplitude by one step." ), _( "Decrease Amplitude" ), _( "Decrease meander amplitude by one step." ), BITMAPS::router_len_tuner_amplitude_decr ); #undef _ #define _(s) wxGetTranslation((s)) LENGTH_TUNER_TOOL::LENGTH_TUNER_TOOL() : TOOL_BASE( "pcbnew.LengthTuner" ), m_inLengthTuner( false ) { // set the initial tune mode for the settings dialog, // in case the dialog is opened before the tool is activated the first time m_lastTuneMode = PNS::ROUTER_MODE::PNS_MODE_TUNE_SINGLE; m_inLengthTuner = false; } LENGTH_TUNER_TOOL::~LENGTH_TUNER_TOOL() { } bool LENGTH_TUNER_TOOL::Init() { m_inLengthTuner = false; auto& menu = m_menu.GetMenu(); menu.SetTitle( _( "Length Tuner" ) ); menu.SetIcon( BITMAPS::router_len_tuner ); menu.DisplayTitle( true ); menu.AddItem( ACTIONS::cancelInteractive, SELECTION_CONDITIONS::ShowAlways ); menu.AddSeparator(); menu.AddItem( ACT_SpacingIncrease, SELECTION_CONDITIONS::ShowAlways ); menu.AddItem( ACT_SpacingDecrease, SELECTION_CONDITIONS::ShowAlways ); menu.AddItem( ACT_AmplIncrease, SELECTION_CONDITIONS::ShowAlways ); menu.AddItem( ACT_AmplDecrease, SELECTION_CONDITIONS::ShowAlways ); menu.AddItem( PCB_ACTIONS::lengthTunerSettingsDialog, SELECTION_CONDITIONS::ShowAlways ); return true; } void LENGTH_TUNER_TOOL::Reset( RESET_REASON aReason ) { if( aReason == RUN ) TOOL_BASE::Reset( aReason ); } void LENGTH_TUNER_TOOL::updateStatusPopup( PNS_TUNE_STATUS_POPUP& aPopup ) { // fixme: wx code not allowed inside tools! wxPoint p = wxGetMousePosition(); p.x += 20; p.y += 20; aPopup.UpdateStatus( m_router ); aPopup.Move( p ); } void LENGTH_TUNER_TOOL::performTuning() { if( m_startItem ) { frame()->SetActiveLayer( ToLAYER_ID ( m_startItem->Layers().Start() ) ); if( m_startItem->Net() >= 0 ) highlightNets( true, { m_startItem->Net() } ); } controls()->ForceCursorPosition( false ); controls()->SetAutoPan( true ); int layer = m_startItem ? m_startItem->Layer() : static_cast( frame()->GetActiveLayer() ); if( !m_router->StartRouting( m_startSnapPoint, m_startItem, layer ) ) { frame()->ShowInfoBarMsg( m_router->FailureReason() ); highlightNets( false ); return; } auto placer = static_cast( m_router->Placer() ); placer->UpdateSettings( m_savedMeanderSettings ); frame()->UndoRedoBlock( true ); VECTOR2I end = getViewControls()->GetMousePosition(); // Create an instance of PNS_TUNE_STATUS_POPUP. PNS_TUNE_STATUS_POPUP statusPopup( frame() ); statusPopup.Popup(); canvas()->SetStatusPopup( statusPopup.GetPanel() ); m_router->Move( end, nullptr ); updateStatusPopup( statusPopup ); auto setCursor = [&]() { frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW ); }; // Set initial cursor setCursor(); while( TOOL_EVENT* evt = Wait() ) { setCursor(); if( evt->IsCancelInteractive() || evt->IsActivate() ) { break; } else if( evt->IsMotion() ) { end = evt->Position(); m_router->Move( end, nullptr ); updateStatusPopup( statusPopup ); } else if( evt->IsClick( BUT_LEFT ) ) { if( m_router->FixRoute( evt->Position(), nullptr ) ) break; } else if( evt->IsClick( BUT_RIGHT ) ) { m_menu.ShowContextMenu( selection() ); } else if( evt->IsAction( &ACT_EndTuning ) ) { if( m_router->FixRoute( end, nullptr ) ) break; } else if( evt->IsAction( &ACT_AmplDecrease ) ) { placer->AmplitudeStep( -1 ); m_router->Move( end, nullptr ); updateStatusPopup( statusPopup ); } else if( evt->IsAction( &ACT_AmplIncrease ) ) { placer->AmplitudeStep( 1 ); m_router->Move( end, nullptr ); updateStatusPopup( statusPopup ); } else if(evt->IsAction( &ACT_SpacingDecrease ) ) { placer->SpacingStep( -1 ); m_router->Move( end, nullptr ); updateStatusPopup( statusPopup ); } else if( evt->IsAction( &ACT_SpacingIncrease ) ) { placer->SpacingStep( 1 ); m_router->Move( end, nullptr ); updateStatusPopup( statusPopup ); } else if( evt->IsAction( &PCB_ACTIONS::lengthTunerSettingsDialog ) ) { statusPopup.Hide(); TOOL_EVENT dummy; meanderSettingsDialog( dummy ); statusPopup.Show(); } // TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions", // but we don't at present have that, so we just knock out some of the egregious ones. else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) ) { wxBell(); } else { evt->SetPassEvent(); } } m_router->StopRouting(); frame()->UndoRedoBlock( false ); canvas()->SetStatusPopup( nullptr ); controls()->SetAutoPan( false ); controls()->ForceCursorPosition( false ); frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW ); highlightNets( false ); } void LENGTH_TUNER_TOOL::setTransitions() { Go( &LENGTH_TUNER_TOOL::MainLoop, PCB_ACTIONS::routerTuneSingleTrace.MakeEvent() ); Go( &LENGTH_TUNER_TOOL::MainLoop, PCB_ACTIONS::routerTuneDiffPair.MakeEvent() ); Go( &LENGTH_TUNER_TOOL::MainLoop, PCB_ACTIONS::routerTuneDiffPairSkew.MakeEvent() ); // in case tool is inactive, otherwise the event is handled in the tool loop Go( &LENGTH_TUNER_TOOL::meanderSettingsDialog, PCB_ACTIONS::lengthTunerSettingsDialog.MakeEvent() ); } int LENGTH_TUNER_TOOL::MainLoop( const TOOL_EVENT& aEvent ) { if( m_inLengthTuner ) return 0; REENTRANCY_GUARD guard( &m_inLengthTuner ); // Deselect all items m_toolMgr->RunAction( PCB_ACTIONS::selectionClear ); frame()->PushTool( aEvent ); auto setCursor = [&]() { frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW ); }; Activate(); // Must be done after Activate() so that it gets set into the correct context controls()->ShowCursor( true ); // Set initial cursor setCursor(); // Router mode must be set after Activate() m_lastTuneMode = aEvent.Parameter(); m_router->SetMode( m_lastTuneMode ); // Main loop: keep receiving events while( TOOL_EVENT* evt = Wait() ) { setCursor(); if( evt->IsCancelInteractive() || evt->IsActivate() ) { break; // Finish } else if( evt->Action() == TA_UNDO_REDO_PRE ) { m_router->ClearWorld(); } else if( evt->Action() == TA_UNDO_REDO_POST || evt->Action() == TA_MODEL_CHANGE ) { m_router->SyncWorld(); } else if( evt->IsMotion() ) { updateStartItem( *evt ); } else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACT_StartTuning ) ) { updateStartItem( *evt ); performTuning(); } else if( evt->IsAction( &PCB_ACTIONS::lengthTunerSettingsDialog ) ) { TOOL_EVENT dummy; meanderSettingsDialog( dummy ); } else if( evt->IsClick( BUT_RIGHT ) ) { m_menu.ShowContextMenu( selection() ); } else { evt->SetPassEvent(); } } // Store routing settings till the next invocation m_savedSizes = m_router->Sizes(); frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW ); frame()->PopTool( aEvent ); return 0; } int LENGTH_TUNER_TOOL::meanderSettingsDialog( const TOOL_EVENT& aEvent ) { PNS::MEANDER_PLACER_BASE* placer = static_cast( m_router->Placer() ); PNS::MEANDER_SETTINGS settings = placer ? placer->MeanderSettings() : m_savedMeanderSettings; DIALOG_PNS_LENGTH_TUNING_SETTINGS settingsDlg( frame(), settings, m_lastTuneMode ); if( settingsDlg.ShowModal() == wxID_OK ) { if( placer ) placer->UpdateSettings( settings ); m_savedMeanderSettings = settings; } return 0; }