/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors. * * 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 2 * 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, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include "pcb_inspection_tool.h" PCB_INSPECTION_TOOL::PCB_INSPECTION_TOOL() : PCB_TOOL_BASE( "pcbnew.InspectionTool" ), m_frame( nullptr ) { m_probingSchToPcb = false; m_lastNetcode = -1; m_slowRatsnest = false; } class NET_CONTEXT_MENU : public ACTION_MENU { public: NET_CONTEXT_MENU() : ACTION_MENU( true ) { SetIcon( ratsnest_xpm ); SetTitle( _( "Net Tools" ) ); Add( PCB_ACTIONS::showNet ); Add( PCB_ACTIONS::hideNet ); // Add( PCB_ACTIONS::highlightNet ); } private: void update() override { const auto& selection = getToolManager()->GetTool()->GetSelection(); bool haveNetCode = false; for( EDA_ITEM* item : selection ) { if( BOARD_CONNECTED_ITEM* bci = dynamic_cast( item ) ) { if( bci->GetNetCode() > 0 ) { haveNetCode = true; break; } } } Enable( getMenuId( PCB_ACTIONS::showNet ), haveNetCode ); Enable( getMenuId( PCB_ACTIONS::hideNet ), haveNetCode ); // Enable( getMenuId( PCB_ACTIONS::highlightNet ), haveNetCode ); } ACTION_MENU* create() const override { return new NET_CONTEXT_MENU(); } }; bool PCB_INSPECTION_TOOL::Init() { SELECTION_TOOL* selectionTool = m_toolMgr->GetTool(); auto netSubMenu = std::make_shared(); netSubMenu->SetTool( this ); static KICAD_T connectedTypes[] = { PCB_TRACE_T, PCB_VIA_T, PCB_ARC_T, PCB_PAD_T, PCB_ZONE_AREA_T, EOT }; CONDITIONAL_MENU& menu = selectionTool->GetToolMenu().GetMenu(); selectionTool->GetToolMenu().AddSubMenu( netSubMenu ); menu.AddMenu( netSubMenu.get(), SELECTION_CONDITIONS::OnlyTypes( connectedTypes ) ); m_ratsnestTimer.SetOwner( this ); Connect( m_ratsnestTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler( PCB_INSPECTION_TOOL::ratsnestTimer ), NULL, this ); return true; } void PCB_INSPECTION_TOOL::Reset( RESET_REASON aReason ) { m_frame = getEditFrame(); } int PCB_INSPECTION_TOOL::ShowStatisticsDialog( const TOOL_EVENT& aEvent ) { DIALOG_BOARD_STATISTICS dialog( m_frame ); dialog.ShowModal(); return 0; } int PCB_INSPECTION_TOOL::CrossProbePcbToSch( const TOOL_EVENT& aEvent ) { // Don't get in an infinite loop PCB -> SCH -> PCB -> SCH -> ... if( m_probingSchToPcb ) return 0; SELECTION_TOOL* selTool = m_toolMgr->GetTool(); const PCBNEW_SELECTION& selection = selTool->GetSelection(); if( selection.Size() == 1 ) m_frame->SendMessageToEESCHEMA( static_cast( selection.Front() ) ); else m_frame->SendMessageToEESCHEMA( nullptr ); return 0; } int PCB_INSPECTION_TOOL::HighlightItem( const TOOL_EVENT& aEvent ) { BOARD_ITEM* item = aEvent.Parameter(); m_probingSchToPcb = true; // recursion guard { m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); if( item ) m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, (void*) item ); } m_probingSchToPcb = false; return 0; } /** * Look for a BOARD_CONNECTED_ITEM in a given spot and if one is found - it enables * highlight for its net. * * @param aPosition is the point where an item is expected (world coordinates). * @param aUseSelection is true if we should use the current selection to pick the netcode */ bool PCB_INSPECTION_TOOL::highlightNet( const VECTOR2D& aPosition, bool aUseSelection ) { BOARD* board = static_cast( m_toolMgr->GetModel() ); KIGFX::RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings(); int net = -1; bool enableHighlight = false; if( aUseSelection ) { SELECTION_TOOL* selectionTool = m_toolMgr->GetTool(); const PCBNEW_SELECTION& selection = selectionTool->GetSelection(); for( auto item : selection ) { if( auto ci = dyn_cast( item ) ) { int item_net = ci->GetNetCode(); if( net < 0 ) net = item_net; else if( net != item_net ) // more than one net selected: do nothing return false; } } enableHighlight = ( net >= 0 && !settings->GetHighlightNetCodes().count( net ) ); } // If we didn't get a net to highlight from the selection, use the cursor if( net < 0 ) { auto guide = m_frame->GetCollectorsGuide(); GENERAL_COLLECTOR collector; // Find a connected item for which we are going to highlight a net collector.Collect( board, GENERAL_COLLECTOR::PadsOrTracks, (wxPoint) aPosition, guide ); if( collector.GetCount() == 0 ) collector.Collect( board, GENERAL_COLLECTOR::Zones, (wxPoint) aPosition, guide ); // Clear the previous highlight m_frame->SendMessageToEESCHEMA( nullptr ); for( int i = 0; i < collector.GetCount(); i++ ) { if( ( collector[i]->GetLayerSet() & LSET::AllCuMask() ).none() ) collector.Remove( i ); if( collector[i]->Type() == PCB_PAD_T ) { m_frame->SendMessageToEESCHEMA( static_cast( collector[i] ) ); break; } } enableHighlight = ( collector.GetCount() > 0 ); // Obtain net code for the clicked item if( enableHighlight ) net = static_cast( collector[0] )->GetNetCode(); } auto& netcodes = settings->GetHighlightNetCodes(); // Toggle highlight when the same net was picked if( net > 0 && netcodes.count( net ) ) enableHighlight = !settings->IsHighlightEnabled(); if( enableHighlight != settings->IsHighlightEnabled() || !netcodes.count( net ) ) { if( !netcodes.empty() ) m_lastNetcode = *netcodes.begin(); settings->SetHighlight( enableHighlight, net ); m_toolMgr->GetView()->UpdateAllLayersColor(); } // Store the highlighted netcode in the current board (for dialogs for instance) if( enableHighlight && net >= 0 ) { board->SetHighLightNet( net ); board->HighLightON(); NETINFO_ITEM* netinfo = board->FindNet( net ); if( netinfo ) { MSG_PANEL_ITEMS items; netinfo->GetMsgPanelInfo( m_frame, items ); m_frame->SetMsgPanel( items ); m_frame->SendCrossProbeNetName( netinfo->GetNetname() ); } } else { board->ResetNetHighLight(); m_frame->SetMsgPanel( board ); m_frame->SendCrossProbeNetName( "" ); } return true; } int PCB_INSPECTION_TOOL::HighlightNet( const TOOL_EVENT& aEvent ) { int netcode = aEvent.Parameter(); KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings(); if( netcode > 0 ) { m_lastNetcode = *settings->GetHighlightNetCodes().begin(); settings->SetHighlight( true, netcode ); m_toolMgr->GetView()->UpdateAllLayersColor(); } else if( aEvent.IsAction( &PCB_ACTIONS::toggleLastNetHighlight ) ) { int temp = *settings->GetHighlightNetCodes().begin(); settings->SetHighlight( true, m_lastNetcode ); m_toolMgr->GetView()->UpdateAllLayersColor(); m_lastNetcode = temp; } else // Highlight the net belonging to the item under the cursor { highlightNet( getViewControls()->GetMousePosition(), false ); } return 0; } int PCB_INSPECTION_TOOL::ClearHighlight( const TOOL_EVENT& aEvent ) { BOARD* board = static_cast( m_toolMgr->GetModel() ); KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings(); board->ResetNetHighLight(); settings->SetHighlight( false ); m_toolMgr->GetView()->UpdateAllLayersColor(); m_frame->SetMsgPanel( board ); m_frame->SendCrossProbeNetName( "" ); return 0; } int PCB_INSPECTION_TOOL::HighlightNetTool( const TOOL_EVENT& aEvent ) { std::string tool = aEvent.GetCommandStr().get(); PCBNEW_PICKER_TOOL* picker = m_toolMgr->GetTool(); // Deactivate other tools; particularly important if another PICKER is currently running Activate(); // If the keyboard hotkey was triggered and we are already in the highlight tool, behave // the same as a left-click. Otherwise highlight the net of the selected item(s), or if // there is no selection, then behave like a ctrl-left-click. if( aEvent.IsAction( &PCB_ACTIONS::highlightNetSelection ) ) { bool use_selection = m_frame->IsCurrentTool( PCB_ACTIONS::highlightNetTool ); highlightNet( getViewControls()->GetMousePosition(), use_selection ); } picker->SetClickHandler( [this] ( const VECTOR2D& pt ) -> bool { highlightNet( pt, false ); return true; } ); picker->SetLayerSet( LSET::AllCuMask() ); m_toolMgr->RunAction( ACTIONS::pickerTool, true, &tool ); return 0; } int PCB_INSPECTION_TOOL::LocalRatsnestTool( const TOOL_EVENT& aEvent ) { std::string tool = aEvent.GetCommandStr().get(); PCBNEW_PICKER_TOOL* picker = m_toolMgr->GetTool(); BOARD* board = getModel(); auto& opt = displayOptions(); // Deactivate other tools; particularly important if another PICKER is currently running Activate(); picker->SetClickHandler( [this, board, opt]( const VECTOR2D& pt ) -> bool { SELECTION_TOOL* selectionTool = m_toolMgr->GetTool(); m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true, EDIT_TOOL::PadFilter ); PCBNEW_SELECTION& selection = selectionTool->GetSelection(); if( selection.Empty() ) { m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true, EDIT_TOOL::FootprintFilter ); selection = selectionTool->GetSelection(); } if( selection.Empty() ) { // Clear the previous local ratsnest if we click off all items for( MODULE* mod : board->Modules() ) { for( D_PAD* pad : mod->Pads() ) pad->SetLocalRatsnestVisible( opt.m_ShowGlobalRatsnest ); } } else { for( auto item : selection ) { if( D_PAD* pad = dyn_cast(item) ) { pad->SetLocalRatsnestVisible( !pad->GetLocalRatsnestVisible() ); } else if( MODULE* mod = dyn_cast(item) ) { if( !mod->Pads().empty() ) { bool enable = !( *( mod->Pads().begin() ) )->GetLocalRatsnestVisible(); for( auto modpad : mod->Pads() ) modpad->SetLocalRatsnestVisible( enable ); } } } } m_toolMgr->GetView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY ); return true; } ); picker->SetFinalizeHandler( [board, opt] ( int aCondition ) { if( aCondition != PCBNEW_PICKER_TOOL::END_ACTIVATE ) { for( auto mod : board->Modules() ) { for( auto pad : mod->Pads() ) pad->SetLocalRatsnestVisible( opt.m_ShowGlobalRatsnest ); } } } ); m_toolMgr->RunAction( ACTIONS::pickerTool, true, &tool ); return 0; } int PCB_INSPECTION_TOOL::UpdateSelectionRatsnest( const TOOL_EVENT& aEvent ) { auto selectionTool = m_toolMgr->GetTool(); auto& selection = selectionTool->GetSelection(); auto connectivity = getModel()->GetConnectivity(); if( selection.Empty() ) { connectivity->ClearDynamicRatsnest(); } else if( m_slowRatsnest ) { // Compute ratsnest only when user stops dragging for a moment connectivity->HideDynamicRatsnest(); m_ratsnestTimer.Start( 20 ); } else { // Check how much time doest it take to calculate ratsnest PROF_COUNTER counter; calculateSelectionRatsnest(); counter.Stop(); // If it is too slow, then switch to 'slow ratsnest' mode when // ratsnest is calculated when user stops dragging items for a moment if( counter.msecs() > 25 ) { m_slowRatsnest = true; connectivity->HideDynamicRatsnest(); } } return 0; } int PCB_INSPECTION_TOOL::HideDynamicRatsnest( const TOOL_EVENT& aEvent ) { getModel()->GetConnectivity()->HideDynamicRatsnest(); m_slowRatsnest = false; return 0; } void PCB_INSPECTION_TOOL::ratsnestTimer( wxTimerEvent& aEvent ) { auto connectivity = getModel()->GetConnectivity(); m_ratsnestTimer.Stop(); /// Check how much time does it take to calculate ratsnest PROF_COUNTER counter; calculateSelectionRatsnest(); counter.Stop(); /// If the ratsnest is fast enough, turn the slow ratsnest off if( counter.msecs() <= 25 ) m_slowRatsnest = false; m_frame->GetCanvas()->RedrawRatsnest(); m_frame->GetCanvas()->Refresh(); } void PCB_INSPECTION_TOOL::calculateSelectionRatsnest() { SELECTION_TOOL* selectionTool = m_toolMgr->GetTool(); SELECTION& selection = selectionTool->GetSelection(); std::shared_ptr connectivity = board()->GetConnectivity(); std::vector items; for( EDA_ITEM* item : selection ) { BOARD_CONNECTED_ITEM* boardItem = dyn_cast( item ); if( !boardItem ) continue; if( boardItem->Type() == PCB_MODULE_T ) { for( auto pad : static_cast( item )->Pads() ) { if( pad->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest ) items.push_back( pad ); } } else if( boardItem->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest ) { items.push_back( boardItem ); } } connectivity->ComputeDynamicRatsnest( items ); } int PCB_INSPECTION_TOOL::ListNets( const TOOL_EVENT& aEvent ) { if( m_listNetsDialog == nullptr ) { m_listNetsDialog = std::make_unique( m_frame, m_listNetsDialogSettings ); m_listNetsDialog->Connect( wxEVT_CLOSE_WINDOW, wxCommandEventHandler( PCB_INSPECTION_TOOL::onListNetsDialogClosed ), nullptr, this ); m_listNetsDialog->Connect( wxEVT_BUTTON, wxCommandEventHandler( PCB_INSPECTION_TOOL::onListNetsDialogClosed ), nullptr, this ); } m_listNetsDialog->Show( true ); return 0; } void PCB_INSPECTION_TOOL::onListNetsDialogClosed( wxCommandEvent& event ) { m_listNetsDialogSettings = m_listNetsDialog->Settings(); m_listNetsDialog->Disconnect( wxEVT_CLOSE_WINDOW, wxCommandEventHandler( PCB_INSPECTION_TOOL::onListNetsDialogClosed ), nullptr, this ); m_listNetsDialog->Disconnect( wxEVT_BUTTON, wxCommandEventHandler( PCB_INSPECTION_TOOL::onListNetsDialogClosed ), nullptr, this ); m_listNetsDialog->Destroy(); m_listNetsDialog.release(); } int PCB_INSPECTION_TOOL::HideNet( const TOOL_EVENT& aEvent ) { doHideNet( aEvent.Parameter(), true ); return 0; } int PCB_INSPECTION_TOOL::ShowNet( const TOOL_EVENT& aEvent ) { doHideNet( aEvent.Parameter(), false ); return 0; } void PCB_INSPECTION_TOOL::doHideNet( int aNetCode, bool aHide ) { KIGFX::PCB_RENDER_SETTINGS* rs = static_cast( m_toolMgr->GetView()->GetPainter()->GetSettings() ); SELECTION_TOOL* selectionTool = m_toolMgr->GetTool(); SELECTION& selection = selectionTool->GetSelection(); if( aNetCode <= 0 && !selection.Empty() ) { for( EDA_ITEM* item : selection ) { if( BOARD_CONNECTED_ITEM* bci = dynamic_cast( item ) ) { if( bci->GetNetCode() > 0 ) doHideNet( bci->GetNetCode(), aHide ); } } return; } if( aHide ) rs->GetHiddenNets().insert( aNetCode ); else rs->GetHiddenNets().erase( aNetCode ); m_frame->GetCanvas()->RedrawRatsnest(); m_frame->GetCanvas()->Refresh(); } void PCB_INSPECTION_TOOL::setTransitions() { Go( &PCB_INSPECTION_TOOL::CrossProbePcbToSch, EVENTS::SelectedEvent ); Go( &PCB_INSPECTION_TOOL::CrossProbePcbToSch, EVENTS::UnselectedEvent ); Go( &PCB_INSPECTION_TOOL::CrossProbePcbToSch, EVENTS::ClearedEvent ); Go( &PCB_INSPECTION_TOOL::LocalRatsnestTool, PCB_ACTIONS::localRatsnestTool.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::HideDynamicRatsnest, PCB_ACTIONS::hideDynamicRatsnest.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::UpdateSelectionRatsnest,PCB_ACTIONS::updateLocalRatsnest.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::ListNets, PCB_ACTIONS::listNets.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::ShowStatisticsDialog, PCB_ACTIONS::boardStatistics.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::HighlightNet, PCB_ACTIONS::highlightNet.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::HighlightNet, PCB_ACTIONS::highlightNetSelection.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::HighlightNet, PCB_ACTIONS::toggleLastNetHighlight.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::ClearHighlight, PCB_ACTIONS::clearHighlight.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::HighlightNetTool, PCB_ACTIONS::highlightNetTool.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::ClearHighlight, ACTIONS::cancelInteractive.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::HighlightItem, PCB_ACTIONS::highlightItem.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::HideNet, PCB_ACTIONS::hideNet.MakeEvent() ); Go( &PCB_INSPECTION_TOOL::ShowNet, PCB_ACTIONS::showNet.MakeEvent() ); }