From c89925578d473e88581e97139a3b9d4709811f1d Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Wed, 11 Jan 2023 14:17:36 +0000 Subject: [PATCH] Move find/replace to own tool so it doesn't rob events from cross-probing. --- eeschema/CMakeLists.txt | 5 +- eeschema/dialogs/dialog_schematic_find.cpp | 16 +- eeschema/dialogs/dialog_schematic_find.h | 29 +- eeschema/sch_edit_frame.cpp | 2 + eeschema/sch_edit_frame.h | 1 + eeschema/sch_plugins/kicad/sch_sexpr_plugin.h | 2 +- eeschema/tools/sch_editor_control.cpp | 433 ---------------- eeschema/tools/sch_editor_control.h | 27 +- eeschema/tools/sch_find_replace_tool.cpp | 466 ++++++++++++++++++ eeschema/tools/sch_find_replace_tool.h | 93 ++++ 10 files changed, 584 insertions(+), 490 deletions(-) create mode 100644 eeschema/tools/sch_find_replace_tool.cpp create mode 100644 eeschema/tools/sch_find_replace_tool.h diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index 9ddbbfc095..917e28003d 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -67,6 +67,8 @@ set( EESCHEMA_DLGS dialogs/dialog_eeschema_page_settings.cpp dialogs/dialog_erc.cpp dialogs/dialog_erc_base.cpp + dialogs/dialog_export_netlist.cpp + dialogs/dialog_export_netlist_base.cpp dialogs/dialog_field_properties.cpp dialogs/dialog_field_properties_base.cpp dialogs/dialog_global_edit_text_and_graphics.cpp @@ -96,8 +98,6 @@ set( EESCHEMA_DLGS dialogs/dialog_line_properties_base.cpp dialogs/dialog_migrate_buses.cpp dialogs/dialog_migrate_buses_base.cpp - dialogs/dialog_export_netlist.cpp - dialogs/dialog_export_netlist_base.cpp dialogs/dialog_pin_properties.cpp dialogs/dialog_pin_properties_base.cpp dialogs/dialog_plot_schematic.cpp @@ -325,6 +325,7 @@ set( EESCHEMA_SRCS tools/sch_edit_tool.cpp tools/sch_editor_control.cpp tools/sch_editor_conditions.cpp + tools/sch_find_replace_tool.cpp tools/sch_line_wire_bus_tool.cpp tools/sch_move_tool.cpp tools/sch_navigate_tool.cpp diff --git a/eeschema/dialogs/dialog_schematic_find.cpp b/eeschema/dialogs/dialog_schematic_find.cpp index b909d39ad1..620d781d11 100644 --- a/eeschema/dialogs/dialog_schematic_find.cpp +++ b/eeschema/dialogs/dialog_schematic_find.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include DIALOG_SCH_FIND::DIALOG_SCH_FIND( SCH_EDIT_FRAME* aParent, SCH_SEARCH_DATA* aData, @@ -33,7 +33,7 @@ DIALOG_SCH_FIND::DIALOG_SCH_FIND( SCH_EDIT_FRAME* aParent, SCH_SEARCH_DATA* aDat DIALOG_SCH_FIND_BASE( aParent, wxID_ANY, _( "Find" ), aPosition, aSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | aStyle ), m_frame( aParent ), - m_editorControl( m_frame->GetToolManager()->GetTool() ), + m_findReplaceTool( m_frame->GetToolManager()->GetTool() ), m_findReplaceData( aData ), m_findDirty( true ) { @@ -96,7 +96,7 @@ void DIALOG_SCH_FIND::OnIdle( wxIdleEvent& aEvent ) { if( m_findDirty ) { - m_editorControl->UpdateFind( ACTIONS::updateFind.MakeEvent() ); + m_findReplaceTool->UpdateFind( ACTIONS::updateFind.MakeEvent() ); m_findDirty = false; } } @@ -112,7 +112,7 @@ void DIALOG_SCH_FIND::OnCancel( wxCommandEvent& aEvent ) void DIALOG_SCH_FIND::OnUpdateReplaceUI( wxUpdateUIEvent& aEvent ) { aEvent.Enable( HasFlag( wxFR_REPLACEDIALOG ) && !m_comboFind->GetValue().empty() && - m_editorControl->HasMatch() ); + m_findReplaceTool->HasMatch() ); } @@ -152,7 +152,7 @@ void DIALOG_SCH_FIND::OnSearchForSelect( wxCommandEvent& aEvent ) m_comboFind->SetSelection( 0 ); } - m_editorControl->UpdateFind( ACTIONS::updateFind.MakeEvent() ); + m_findReplaceTool->UpdateFind( ACTIONS::updateFind.MakeEvent() ); } @@ -252,7 +252,7 @@ void DIALOG_SCH_FIND::OnFind( wxCommandEvent& aEvent ) m_comboFind->SetSelection( 0 ); } - m_editorControl->FindNext( ACTIONS::findNext.MakeEvent() ); + m_findReplaceTool->FindNext( ACTIONS::findNext.MakeEvent() ); } @@ -276,9 +276,9 @@ void DIALOG_SCH_FIND::OnReplace( wxCommandEvent& aEvent ) } if( aEvent.GetId() == wxID_REPLACE ) - m_editorControl->ReplaceAndFindNext( ACTIONS::replaceAndFindNext.MakeEvent() ); + m_findReplaceTool->ReplaceAndFindNext( ACTIONS::replaceAndFindNext.MakeEvent() ); else if( aEvent.GetId() == wxID_REPLACE_ALL ) - m_editorControl->ReplaceAll( ACTIONS::replaceAll.MakeEvent() ); + m_findReplaceTool->ReplaceAll( ACTIONS::replaceAll.MakeEvent() ); } diff --git a/eeschema/dialogs/dialog_schematic_find.h b/eeschema/dialogs/dialog_schematic_find.h index 95fbde1386..57ac450703 100644 --- a/eeschema/dialogs/dialog_schematic_find.h +++ b/eeschema/dialogs/dialog_schematic_find.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2010 Wayne Stambaugh - * Copyright (C) 2010-2021 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2010-2023 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 @@ -22,26 +22,15 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/** - * @file - * - * Subclass of DIALOG_SCH_FIND_BASE, which is generated by wxFormBuilder. - * - * This dialog is used to define the search criteria used to search for items - * in the current schematic. What is searched depends on the schematic item - * type. Check the Matches() method for each item derived from SCH_ITEM() to - * find out how matching is performed against that item. - */ - -#ifndef __dialog_schematic_find__ -#define __dialog_schematic_find__ +#ifndef DIALOG_SCH_FIND_H +#define DIALOG_SCH_FIND_H #include "dialog_schematic_find_base.h" #include // Use the wxFindReplaceDialog events, data, and enums. class SCH_EDIT_FRAME; -class SCH_EDITOR_CONTROL; +class SCH_FIND_REPLACE_TOOL; class DIALOG_SCH_FIND : public DIALOG_SCH_FIND_BASE @@ -78,13 +67,13 @@ protected: // Rebuild the search flags from dialog settings void updateFlags(); - SCH_EDIT_FRAME* m_frame; - SCH_EDITOR_CONTROL* m_editorControl; - SCH_SEARCH_DATA* m_findReplaceData; - bool m_findDirty; + SCH_EDIT_FRAME* m_frame; + SCH_FIND_REPLACE_TOOL* m_findReplaceTool; + SCH_SEARCH_DATA* m_findReplaceData; + bool m_findDirty; DECLARE_NO_COPY_CLASS( DIALOG_SCH_FIND ) }; -#endif // __dialog_schematic_find__ +#endif // DIALOG_SCH_FIND_H diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index 7e4d645844..d1e4c63efc 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -76,6 +76,7 @@ #include #include #include +#include #include #include #include @@ -382,6 +383,7 @@ void SCH_EDIT_FRAME::setupTools() m_toolManager->RegisterTool( new SCH_EDIT_TOOL ); m_toolManager->RegisterTool( new EE_INSPECTION_TOOL ); m_toolManager->RegisterTool( new SCH_EDITOR_CONTROL ); + m_toolManager->RegisterTool( new SCH_FIND_REPLACE_TOOL ); m_toolManager->RegisterTool( new EE_POINT_EDITOR ); m_toolManager->RegisterTool( new SCH_NAVIGATE_TOOL ); m_toolManager->InitTools(); diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h index 7e1daee416..ec60fd4ff4 100644 --- a/eeschema/sch_edit_frame.h +++ b/eeschema/sch_edit_frame.h @@ -919,6 +919,7 @@ private: // The schematic editor control class should be able to access some internal // functions of the editor frame. friend class SCH_EDITOR_CONTROL; + friend class SCH_FIND_REPLACE_TOOL; SCHEMATIC* m_schematic; ///< The currently loaded schematic const SCH_CONNECTION* m_highlightedConn; ///< The highlighted net or bus, or nullptr diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.h b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.h index 3118481419..c85ac5643d 100644 --- a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.h +++ b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.h @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -34,7 +35,6 @@ class LINE_READER; class SCH_SCREEN; class SCH_SHEET; struct SCH_SHEET_INSTANCE; -class SCH_SHEET_PATH; class SCH_BITMAP; class SCH_JUNCTION; class SCH_NO_CONNECT; diff --git a/eeschema/tools/sch_editor_control.cpp b/eeschema/tools/sch_editor_control.cpp index 8314c6932a..6cf61be251 100644 --- a/eeschema/tools/sch_editor_control.cpp +++ b/eeschema/tools/sch_editor_control.cpp @@ -314,428 +314,6 @@ int SCH_EDITOR_CONTROL::Quit( const TOOL_EVENT& aEvent ) } -int SCH_EDITOR_CONTROL::FindAndReplace( const TOOL_EVENT& aEvent ) -{ - m_frame->ShowFindReplaceDialog( aEvent.IsAction( &ACTIONS::findAndReplace ) ); - return UpdateFind( aEvent ); -} - - -int SCH_EDITOR_CONTROL::UpdateFind( const TOOL_EVENT& aEvent ) -{ - EDA_SEARCH_DATA& data = m_frame->GetFindReplaceData(); - - auto visit = - [&]( EDA_ITEM* aItem, SCH_SHEET_PATH* aSheet ) - { - // We may get triggered when the dialog is not opened due to binding - // SelectedItemsModified we also get triggered when the find dialog is - // closed....so we need to double check the dialog is open. - if( m_frame->m_findReplaceDialog != nullptr - && !data.findString.IsEmpty() - && aItem->Matches( data, aSheet ) ) - { - aItem->SetForceVisible( true ); - m_selectionTool->BrightenItem( aItem ); - m_foundItemHighlighted = true; - } - else if( aItem->IsBrightened() ) - { - aItem->SetForceVisible( false ); - m_selectionTool->UnbrightenItem( aItem ); - } - }; - - if( aEvent.IsAction( &ACTIONS::find ) || aEvent.IsAction( &ACTIONS::findAndReplace ) - || aEvent.IsAction( &ACTIONS::updateFind ) ) - { - m_foundItemHighlighted = false; - m_selectionTool->ClearSelection(); - - for( SCH_ITEM* item : m_frame->GetScreen()->Items() ) - { - visit( item, &m_frame->GetCurrentSheet() ); - - item->RunOnChildren( - [&]( SCH_ITEM* aChild ) - { - visit( aChild, &m_frame->GetCurrentSheet() ); - } ); - } - } - else if( aEvent.Matches( EVENTS::SelectedItemsModified ) ) - { - for( EDA_ITEM* item : m_selectionTool->GetSelection() ) - visit( item, &m_frame->GetCurrentSheet() ); - } - else if( m_foundItemHighlighted ) - { - m_foundItemHighlighted = false; - - for( SCH_ITEM* item : m_frame->GetScreen()->Items() ) - { - visit( item, &m_frame->GetCurrentSheet() ); - - item->RunOnChildren( - [&]( SCH_ITEM* aChild ) - { - visit( aChild, &m_frame->GetCurrentSheet() ); - } ); - } - } - - getView()->UpdateItems(); - m_frame->GetCanvas()->Refresh(); - m_frame->UpdateTitle(); - - return 0; -} - - -SCH_ITEM* SCH_EDITOR_CONTROL::nextMatch( SCH_SCREEN* aScreen, SCH_SHEET_PATH* aSheet, - SCH_ITEM* aAfter, EDA_SEARCH_DATA& aData ) -{ - bool past_item = true; - - if( aAfter != nullptr ) - { - past_item = false; - - if( aAfter->Type() == SCH_PIN_T || aAfter->Type() == SCH_FIELD_T ) - aAfter = static_cast( aAfter->GetParent() ); - } - - std::vector sorted_items; - - for( SCH_ITEM* item : aScreen->Items() ) - sorted_items.push_back( item ); - - std::sort( sorted_items.begin(), sorted_items.end(), - [&]( SCH_ITEM* a, SCH_ITEM* b ) - { - if( a->GetPosition().x == b->GetPosition().x ) - { - // Ensure deterministic sort - if( a->GetPosition().y == b->GetPosition().y ) - return a->m_Uuid < b->m_Uuid; - - return a->GetPosition().y < b->GetPosition().y; - } - else - return a->GetPosition().x < b->GetPosition().x; - } ); - - for( SCH_ITEM* item : sorted_items ) - { - if( item == aAfter ) - { - past_item = true; - } - else if( past_item ) - { - if( aData.markersOnly && item->Type() == SCH_MARKER_T ) - return item; - - if( item->Matches( aData, aSheet ) ) - return item; - - if( item->Type() == SCH_SYMBOL_T ) - { - SCH_SYMBOL* cmp = static_cast( item ); - - for( SCH_FIELD& field : cmp->GetFields() ) - { - if( field.Matches( aData, aSheet ) ) - return &field; - } - - for( SCH_PIN* pin : cmp->GetPins() ) - { - if( pin->Matches( aData, aSheet ) ) - return pin; - } - } - - if( item->Type() == SCH_SHEET_T ) - { - SCH_SHEET* sheet = static_cast( item ); - - for( SCH_FIELD& field : sheet->GetFields() ) - { - if( field.Matches( aData, aSheet ) ) - return &field; - } - - for( SCH_SHEET_PIN* pin : sheet->GetPins() ) - { - if( pin->Matches( aData, aSheet ) ) - return pin; - } - } - } - } - - return nullptr; -} - - -int SCH_EDITOR_CONTROL::FindNext( const TOOL_EVENT& aEvent ) -{ - EDA_SEARCH_DATA& data = m_frame->GetFindReplaceData(); - bool searchAllSheets = false; - - try - { - const SCH_SEARCH_DATA& schSearchData = dynamic_cast( data ); - searchAllSheets = !( schSearchData.searchCurrentSheetOnly ); - } - catch( const std::bad_cast& ) - { - } - - if( aEvent.IsAction( &ACTIONS::findNextMarker ) ) - data.markersOnly = true; - else if( data.findString.IsEmpty() ) - return FindAndReplace( ACTIONS::find.MakeEvent() ); - - EE_SELECTION& selection = m_selectionTool->GetSelection(); - SCH_ITEM* afterItem = dynamic_cast( selection.Front() ); - SCH_ITEM* item = nullptr; - - SCH_SHEET_PATH* afterSheet = &m_frame->GetCurrentSheet(); - - if( m_wrapAroundTimer.IsRunning() ) - { - afterSheet = nullptr; - afterItem = nullptr; - m_wrapAroundTimer.Stop(); - m_frame->ClearFindReplaceStatus(); - } - - m_selectionTool->ClearSelection(); - - if( afterSheet || !searchAllSheets ) - item = nextMatch( m_frame->GetScreen(), &m_frame->GetCurrentSheet(), afterItem, data ); - - if( !item && searchAllSheets ) - { - SCH_SCREENS screens( m_frame->Schematic().Root() ); - std::vector paths; - - screens.BuildClientSheetPathList(); - - for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() ) - { - for( SCH_SHEET_PATH& sheet : screen->GetClientSheetPaths() ) - paths.push_back( &sheet ); - } - - std::sort( paths.begin(), paths.end(), [] ( const SCH_SHEET_PATH* lhs, - const SCH_SHEET_PATH* rhs ) -> bool - { - int retval = lhs->ComparePageNum( *rhs ); - - if( retval < 0 ) - return true; - else if( retval > 0 ) - return false; - else /// Enforce strict ordering. If the page numbers are the same, use UUIDs - return lhs->GetCurrentHash() < rhs->GetCurrentHash(); - } ); - - for( SCH_SHEET_PATH* sheet : paths ) - { - if( afterSheet ) - { - if( afterSheet->GetCurrentHash() == sheet->GetCurrentHash() ) - afterSheet = nullptr; - - continue; - } - - item = nextMatch( sheet->LastScreen(), sheet, nullptr, data ); - - if( item ) - { - m_frame->Schematic().SetCurrentSheet( *sheet ); - m_frame->DisplayCurrentSheet(); - - break; - } - } - } - - if( item ) - { - if( !item->IsBrightened() ) - { - // Clear any previous brightening - UpdateFind( aEvent ); - - // Brighten (and show) found object - item->SetForceVisible( true ); - m_selectionTool->BrightenItem( item ); - m_foundItemHighlighted = true; - } - - m_selectionTool->AddItemToSel( item ); - m_frame->FocusOnLocation( item->GetBoundingBox().GetCenter() ); - m_frame->GetCanvas()->Refresh(); - } - else - { - wxString msg = searchAllSheets ? _( "Reached end of schematic." ) - : _( "Reached end of sheet." ); - - // Show the popup during the time period the user can wrap the search - m_frame->ShowFindReplaceStatus( msg + wxS( " " ) + - _( "Find again to wrap around to the start." ), 4000 ); - m_wrapAroundTimer.StartOnce( 4000 ); - } - - return 0; -} - - -bool SCH_EDITOR_CONTROL::HasMatch() -{ - EDA_SEARCH_DATA& data = m_frame->GetFindReplaceData(); - EDA_ITEM* item = m_selectionTool->GetSelection().Front(); - - return item && item->Matches( data, &m_frame->GetCurrentSheet() ); -} - - -int SCH_EDITOR_CONTROL::ReplaceAndFindNext( const TOOL_EVENT& aEvent ) -{ - EDA_SEARCH_DATA& data = m_frame->GetFindReplaceData(); - EDA_ITEM* item = m_selectionTool->GetSelection().Front(); - SCH_SHEET_PATH* sheet = &m_frame->GetCurrentSheet(); - - if( data.findString.IsEmpty() ) - return FindAndReplace( ACTIONS::find.MakeEvent() ); - - if( item && item->Matches( data, sheet ) ) - { - SCH_ITEM* sch_item = static_cast( item ); - - m_frame->SaveCopyInUndoList( sheet->LastScreen(), sch_item, UNDO_REDO::CHANGED, false ); - - if( item->Replace( data, sheet ) ) - { - m_frame->UpdateItem( item, false, true ); - m_frame->GetCurrentSheet().UpdateAllScreenReferences(); - m_frame->OnModify(); - } - - FindNext( ACTIONS::findNext.MakeEvent() ); - } - - return 0; -} - - -int SCH_EDITOR_CONTROL::ReplaceAll( const TOOL_EVENT& aEvent ) -{ - EDA_SEARCH_DATA& data = m_frame->GetFindReplaceData(); - bool currentSheetOnly = false; - - try - { - const SCH_SEARCH_DATA& schSearchData = dynamic_cast( data ); - currentSheetOnly = schSearchData.searchCurrentSheetOnly; - } - catch( const std::bad_cast& ) - { - } - - bool modified = false; - - if( data.findString.IsEmpty() ) - return FindAndReplace( ACTIONS::find.MakeEvent() ); - - auto doReplace = - [&]( SCH_ITEM* aItem, SCH_SHEET_PATH* aSheet, EDA_SEARCH_DATA& aData ) - { - m_frame->SaveCopyInUndoList( aSheet->LastScreen(), aItem, UNDO_REDO::CHANGED, - modified ); - - if( aItem->Replace( aData, aSheet ) ) - { - m_frame->UpdateItem( aItem, false, true ); - modified = true; - } - }; - - if( currentSheetOnly ) - { - SCH_SHEET_PATH* currentSheet = &m_frame->GetCurrentSheet(); - - SCH_ITEM* item = nextMatch( m_frame->GetScreen(), currentSheet, nullptr, data ); - - while( item ) - { - doReplace( item, currentSheet, data ); - item = nextMatch( m_frame->GetScreen(), currentSheet, item, data ); - } - } - else - { - SCH_SHEET_LIST allSheets = m_frame->Schematic().GetSheets(); - SCH_SCREENS screens( m_frame->Schematic().Root() ); - - for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() ) - { - SCH_SHEET_LIST sheets = allSheets.FindAllSheetsForScreen( screen ); - - for( unsigned ii = 0; ii < sheets.size(); ++ii ) - { - SCH_ITEM* item = nextMatch( screen, &sheets[ii], nullptr, data ); - - while( item ) - { - if( ii == 0 ) - { - doReplace( item, &sheets[0], data ); - } - else if( item->Type() == SCH_FIELD_T ) - { - SCH_FIELD* field = static_cast( item ); - - if( field->GetParent() && field->GetParent()->Type() == SCH_SYMBOL_T ) - { - switch( field->GetId() ) - { - case REFERENCE_FIELD: - case VALUE_FIELD: - case FOOTPRINT_FIELD: - // must be handled for each distinct sheet - doReplace( field, &sheets[ii], data ); - break; - - default: - // handled in first iteration - break; - } - } - } - - item = nextMatch( screen, &sheets[ii], item, data ); - } - } - } - } - - if( modified ) - { - m_frame->GetCurrentSheet().UpdateAllScreenReferences(); - m_frame->OnModify(); - } - - return 0; -} - - int SCH_EDITOR_CONTROL::CrossProbeToPcb( const TOOL_EVENT& aEvent ) { doCrossProbeSchToPcb( aEvent, false ); @@ -2635,17 +2213,6 @@ void SCH_EDITOR_CONTROL::setTransitions() Go( &SCH_EDITOR_CONTROL::RescueSymbols, EE_ACTIONS::rescueSymbols.MakeEvent() ); Go( &SCH_EDITOR_CONTROL::RemapSymbols, EE_ACTIONS::remapSymbols.MakeEvent() ); - Go( &SCH_EDITOR_CONTROL::FindAndReplace, ACTIONS::find.MakeEvent() ); - Go( &SCH_EDITOR_CONTROL::FindAndReplace, ACTIONS::findAndReplace.MakeEvent() ); - Go( &SCH_EDITOR_CONTROL::FindNext, ACTIONS::findNext.MakeEvent() ); - Go( &SCH_EDITOR_CONTROL::FindNext, ACTIONS::findNextMarker.MakeEvent() ); - Go( &SCH_EDITOR_CONTROL::ReplaceAndFindNext, ACTIONS::replaceAndFindNext.MakeEvent() ); - Go( &SCH_EDITOR_CONTROL::ReplaceAll, ACTIONS::replaceAll.MakeEvent() ); - Go( &SCH_EDITOR_CONTROL::UpdateFind, ACTIONS::updateFind.MakeEvent() ); - Go( &SCH_EDITOR_CONTROL::UpdateFind, EVENTS::SelectedItemsModified ); - Go( &SCH_EDITOR_CONTROL::UpdateFind, EVENTS::PointSelectedEvent ); - Go( &SCH_EDITOR_CONTROL::UpdateFind, EVENTS::SelectedEvent ); - Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::PointSelectedEvent ); Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::SelectedEvent ); Go( &SCH_EDITOR_CONTROL::CrossProbeToPcb, EVENTS::UnselectedEvent ); diff --git a/eeschema/tools/sch_editor_control.h b/eeschema/tools/sch_editor_control.h index 27cf02cec9..9af5be103d 100644 --- a/eeschema/tools/sch_editor_control.h +++ b/eeschema/tools/sch_editor_control.h @@ -42,8 +42,7 @@ public: EE_TOOL_BASE( "eeschema.EditorControl" ), m_probingPcbToSch( false ), m_pickerItem( nullptr ), - m_duplicateIsHoverSelection( false ), - m_foundItemHighlighted( false ) + m_duplicateIsHoverSelection( false ) { } ~SCH_EDITOR_CONTROL() { } @@ -77,15 +76,6 @@ public: bool RescueLegacyProject( bool aRunningOnDemand ); bool RescueSymbolLibTableProject( bool aRunningOnDemand ); - int FindAndReplace( const TOOL_EVENT& aEvent ); - - int FindNext( const TOOL_EVENT& aEvent ); - bool HasMatch(); - int ReplaceAndFindNext( const TOOL_EVENT& aEvent ); - int ReplaceAll( const TOOL_EVENT& aEvent ); - - int UpdateFind( const TOOL_EVENT& aEvent ); - ///< Notifies pcbnew about the selected item. int CrossProbeToPcb( const TOOL_EVENT& aEvent ); @@ -214,17 +204,6 @@ private: ///< Set up handlers for various events. void setTransitions() override; - /** - * Advance the search and returns the next matching item after \a aAfter. - * - * @param aScreen Pointer to the screen used for searching - * @param aAfter Starting match to compare - * @param aData Search data to compare against or NULL to match the first item found - * @return pointer to the next search item found or NULL if nothing found - */ - SCH_ITEM* nextMatch( SCH_SCREEN* aScreen, SCH_SHEET_PATH* aSheet, SCH_ITEM* aAfter, - EDA_SEARCH_DATA& aData ); - private: bool m_probingPcbToSch; // Recursion guard for PCB to schematic cross-probing EDA_ITEM* m_pickerItem; // Current item for picker highlighting. @@ -232,10 +211,6 @@ private: std::string m_duplicateClipboard; // Temporary storage for Duplicate action bool m_duplicateIsHoverSelection; - bool m_foundItemHighlighted; - wxTimer m_wrapAroundTimer; // A timer during which a subsequent FindNext will - // result in a wrap-around - // A map of sheet filename --> screens for the clipboard contents. We use these to hook up // cut/paste operations for unsaved sheet content. std::map m_supplementaryClipboard; diff --git a/eeschema/tools/sch_find_replace_tool.cpp b/eeschema/tools/sch_find_replace_tool.cpp new file mode 100644 index 0000000000..5fdd9a87b5 --- /dev/null +++ b/eeschema/tools/sch_find_replace_tool.cpp @@ -0,0 +1,466 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 CERN + * Copyright (C) 1992-2023 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 + + +int SCH_FIND_REPLACE_TOOL::FindAndReplace( const TOOL_EVENT& aEvent ) +{ + m_frame->ShowFindReplaceDialog( aEvent.IsAction( &ACTIONS::findAndReplace ) ); + return UpdateFind( aEvent ); +} + + +int SCH_FIND_REPLACE_TOOL::UpdateFind( const TOOL_EVENT& aEvent ) +{ + EDA_SEARCH_DATA& data = m_frame->GetFindReplaceData(); + + auto visit = + [&]( EDA_ITEM* aItem, SCH_SHEET_PATH* aSheet ) + { + // We may get triggered when the dialog is not opened due to binding + // SelectedItemsModified we also get triggered when the find dialog is + // closed....so we need to double check the dialog is open. + if( m_frame->m_findReplaceDialog != nullptr + && !data.findString.IsEmpty() + && aItem->Matches( data, aSheet ) ) + { + aItem->SetForceVisible( true ); + m_selectionTool->BrightenItem( aItem ); + m_foundItemHighlighted = true; + } + else if( aItem->IsBrightened() ) + { + aItem->SetForceVisible( false ); + m_selectionTool->UnbrightenItem( aItem ); + } + }; + + if( aEvent.IsAction( &ACTIONS::find ) || aEvent.IsAction( &ACTIONS::findAndReplace ) + || aEvent.IsAction( &ACTIONS::updateFind ) ) + { + m_foundItemHighlighted = false; + m_selectionTool->ClearSelection(); + + for( SCH_ITEM* item : m_frame->GetScreen()->Items() ) + { + visit( item, &m_frame->GetCurrentSheet() ); + + item->RunOnChildren( + [&]( SCH_ITEM* aChild ) + { + visit( aChild, &m_frame->GetCurrentSheet() ); + } ); + } + } + else if( aEvent.Matches( EVENTS::SelectedItemsModified ) ) + { + for( EDA_ITEM* item : m_selectionTool->GetSelection() ) + visit( item, &m_frame->GetCurrentSheet() ); + } + else if( m_foundItemHighlighted ) + { + m_foundItemHighlighted = false; + + for( SCH_ITEM* item : m_frame->GetScreen()->Items() ) + { + visit( item, &m_frame->GetCurrentSheet() ); + + item->RunOnChildren( + [&]( SCH_ITEM* aChild ) + { + visit( aChild, &m_frame->GetCurrentSheet() ); + } ); + } + } + + getView()->UpdateItems(); + m_frame->GetCanvas()->Refresh(); + m_frame->UpdateTitle(); + + return 0; +} + + +SCH_ITEM* SCH_FIND_REPLACE_TOOL::nextMatch( SCH_SCREEN* aScreen, SCH_SHEET_PATH* aSheet, + SCH_ITEM* aAfter, EDA_SEARCH_DATA& aData ) +{ + bool past_item = true; + + if( aAfter != nullptr ) + { + past_item = false; + + if( aAfter->Type() == SCH_PIN_T || aAfter->Type() == SCH_FIELD_T ) + aAfter = static_cast( aAfter->GetParent() ); + } + + std::vector sorted_items; + + for( SCH_ITEM* item : aScreen->Items() ) + sorted_items.push_back( item ); + + std::sort( sorted_items.begin(), sorted_items.end(), + [&]( SCH_ITEM* a, SCH_ITEM* b ) + { + if( a->GetPosition().x == b->GetPosition().x ) + { + // Ensure deterministic sort + if( a->GetPosition().y == b->GetPosition().y ) + return a->m_Uuid < b->m_Uuid; + + return a->GetPosition().y < b->GetPosition().y; + } + else + return a->GetPosition().x < b->GetPosition().x; + } ); + + for( SCH_ITEM* item : sorted_items ) + { + if( item == aAfter ) + { + past_item = true; + } + else if( past_item ) + { + if( aData.markersOnly && item->Type() == SCH_MARKER_T ) + return item; + + if( item->Matches( aData, aSheet ) ) + return item; + + if( item->Type() == SCH_SYMBOL_T ) + { + SCH_SYMBOL* cmp = static_cast( item ); + + for( SCH_FIELD& field : cmp->GetFields() ) + { + if( field.Matches( aData, aSheet ) ) + return &field; + } + + for( SCH_PIN* pin : cmp->GetPins() ) + { + if( pin->Matches( aData, aSheet ) ) + return pin; + } + } + + if( item->Type() == SCH_SHEET_T ) + { + SCH_SHEET* sheet = static_cast( item ); + + for( SCH_FIELD& field : sheet->GetFields() ) + { + if( field.Matches( aData, aSheet ) ) + return &field; + } + + for( SCH_SHEET_PIN* pin : sheet->GetPins() ) + { + if( pin->Matches( aData, aSheet ) ) + return pin; + } + } + } + } + + return nullptr; +} + + +int SCH_FIND_REPLACE_TOOL::FindNext( const TOOL_EVENT& aEvent ) +{ + EDA_SEARCH_DATA& data = m_frame->GetFindReplaceData(); + bool searchAllSheets = false; + + try + { + const SCH_SEARCH_DATA& schSearchData = dynamic_cast( data ); + searchAllSheets = !( schSearchData.searchCurrentSheetOnly ); + } + catch( const std::bad_cast& ) + { + } + + if( aEvent.IsAction( &ACTIONS::findNextMarker ) ) + data.markersOnly = true; + else if( data.findString.IsEmpty() ) + return FindAndReplace( ACTIONS::find.MakeEvent() ); + + EE_SELECTION& selection = m_selectionTool->GetSelection(); + SCH_ITEM* afterItem = dynamic_cast( selection.Front() ); + SCH_ITEM* item = nullptr; + + SCH_SHEET_PATH* afterSheet = &m_frame->GetCurrentSheet(); + + if( m_wrapAroundTimer.IsRunning() ) + { + afterSheet = nullptr; + afterItem = nullptr; + m_wrapAroundTimer.Stop(); + m_frame->ClearFindReplaceStatus(); + } + + m_selectionTool->ClearSelection(); + + if( afterSheet || !searchAllSheets ) + item = nextMatch( m_frame->GetScreen(), &m_frame->GetCurrentSheet(), afterItem, data ); + + if( !item && searchAllSheets ) + { + SCH_SCREENS screens( m_frame->Schematic().Root() ); + std::vector paths; + + screens.BuildClientSheetPathList(); + + for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() ) + { + for( SCH_SHEET_PATH& sheet : screen->GetClientSheetPaths() ) + paths.push_back( &sheet ); + } + + std::sort( paths.begin(), paths.end(), [] ( const SCH_SHEET_PATH* lhs, + const SCH_SHEET_PATH* rhs ) -> bool + { + int retval = lhs->ComparePageNum( *rhs ); + + if( retval < 0 ) + return true; + else if( retval > 0 ) + return false; + else /// Enforce strict ordering. If the page numbers are the same, use UUIDs + return lhs->GetCurrentHash() < rhs->GetCurrentHash(); + } ); + + for( SCH_SHEET_PATH* sheet : paths ) + { + if( afterSheet ) + { + if( afterSheet->GetCurrentHash() == sheet->GetCurrentHash() ) + afterSheet = nullptr; + + continue; + } + + item = nextMatch( sheet->LastScreen(), sheet, nullptr, data ); + + if( item ) + { + m_frame->Schematic().SetCurrentSheet( *sheet ); + m_frame->DisplayCurrentSheet(); + + break; + } + } + } + + if( item ) + { + if( !item->IsBrightened() ) + { + // Clear any previous brightening + UpdateFind( aEvent ); + + // Brighten (and show) found object + item->SetForceVisible( true ); + m_selectionTool->BrightenItem( item ); + m_foundItemHighlighted = true; + } + + m_selectionTool->AddItemToSel( item ); + m_frame->FocusOnLocation( item->GetBoundingBox().GetCenter() ); + m_frame->GetCanvas()->Refresh(); + } + else + { + wxString msg = searchAllSheets ? _( "Reached end of schematic." ) + : _( "Reached end of sheet." ); + + // Show the popup during the time period the user can wrap the search + m_frame->ShowFindReplaceStatus( msg + wxS( " " ) + + _( "Find again to wrap around to the start." ), 4000 ); + m_wrapAroundTimer.StartOnce( 4000 ); + } + + return 0; +} + + +bool SCH_FIND_REPLACE_TOOL::HasMatch() +{ + EDA_SEARCH_DATA& data = m_frame->GetFindReplaceData(); + EDA_ITEM* item = m_selectionTool->GetSelection().Front(); + + return item && item->Matches( data, &m_frame->GetCurrentSheet() ); +} + + +int SCH_FIND_REPLACE_TOOL::ReplaceAndFindNext( const TOOL_EVENT& aEvent ) +{ + EDA_SEARCH_DATA& data = m_frame->GetFindReplaceData(); + EDA_ITEM* item = m_selectionTool->GetSelection().Front(); + SCH_SHEET_PATH* sheet = &m_frame->GetCurrentSheet(); + + if( data.findString.IsEmpty() ) + return FindAndReplace( ACTIONS::find.MakeEvent() ); + + if( item && item->Matches( data, sheet ) ) + { + SCH_ITEM* sch_item = static_cast( item ); + + m_frame->SaveCopyInUndoList( sheet->LastScreen(), sch_item, UNDO_REDO::CHANGED, false ); + + if( item->Replace( data, sheet ) ) + { + m_frame->UpdateItem( item, false, true ); + m_frame->GetCurrentSheet().UpdateAllScreenReferences(); + m_frame->OnModify(); + } + + FindNext( ACTIONS::findNext.MakeEvent() ); + } + + return 0; +} + + +int SCH_FIND_REPLACE_TOOL::ReplaceAll( const TOOL_EVENT& aEvent ) +{ + EDA_SEARCH_DATA& data = m_frame->GetFindReplaceData(); + bool currentSheetOnly = false; + + try + { + const SCH_SEARCH_DATA& schSearchData = dynamic_cast( data ); + currentSheetOnly = schSearchData.searchCurrentSheetOnly; + } + catch( const std::bad_cast& ) + { + } + + bool modified = false; + + if( data.findString.IsEmpty() ) + return FindAndReplace( ACTIONS::find.MakeEvent() ); + + auto doReplace = + [&]( SCH_ITEM* aItem, SCH_SHEET_PATH* aSheet, EDA_SEARCH_DATA& aData ) + { + m_frame->SaveCopyInUndoList( aSheet->LastScreen(), aItem, UNDO_REDO::CHANGED, + modified ); + + if( aItem->Replace( aData, aSheet ) ) + { + m_frame->UpdateItem( aItem, false, true ); + modified = true; + } + }; + + if( currentSheetOnly ) + { + SCH_SHEET_PATH* currentSheet = &m_frame->GetCurrentSheet(); + + SCH_ITEM* item = nextMatch( m_frame->GetScreen(), currentSheet, nullptr, data ); + + while( item ) + { + doReplace( item, currentSheet, data ); + item = nextMatch( m_frame->GetScreen(), currentSheet, item, data ); + } + } + else + { + SCH_SHEET_LIST allSheets = m_frame->Schematic().GetSheets(); + SCH_SCREENS screens( m_frame->Schematic().Root() ); + + for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() ) + { + SCH_SHEET_LIST sheets = allSheets.FindAllSheetsForScreen( screen ); + + for( unsigned ii = 0; ii < sheets.size(); ++ii ) + { + SCH_ITEM* item = nextMatch( screen, &sheets[ii], nullptr, data ); + + while( item ) + { + if( ii == 0 ) + { + doReplace( item, &sheets[0], data ); + } + else if( item->Type() == SCH_FIELD_T ) + { + SCH_FIELD* field = static_cast( item ); + + if( field->GetParent() && field->GetParent()->Type() == SCH_SYMBOL_T ) + { + switch( field->GetId() ) + { + case REFERENCE_FIELD: + case VALUE_FIELD: + case FOOTPRINT_FIELD: + // must be handled for each distinct sheet + doReplace( field, &sheets[ii], data ); + break; + + default: + // handled in first iteration + break; + } + } + } + + item = nextMatch( screen, &sheets[ii], item, data ); + } + } + } + } + + if( modified ) + { + m_frame->GetCurrentSheet().UpdateAllScreenReferences(); + m_frame->OnModify(); + } + + return 0; +} + + +void SCH_FIND_REPLACE_TOOL::setTransitions() +{ + Go( &SCH_FIND_REPLACE_TOOL::FindAndReplace, ACTIONS::find.MakeEvent() ); + Go( &SCH_FIND_REPLACE_TOOL::FindAndReplace, ACTIONS::findAndReplace.MakeEvent() ); + Go( &SCH_FIND_REPLACE_TOOL::FindNext, ACTIONS::findNext.MakeEvent() ); + Go( &SCH_FIND_REPLACE_TOOL::FindNext, ACTIONS::findNextMarker.MakeEvent() ); + Go( &SCH_FIND_REPLACE_TOOL::ReplaceAndFindNext, ACTIONS::replaceAndFindNext.MakeEvent() ); + Go( &SCH_FIND_REPLACE_TOOL::ReplaceAll, ACTIONS::replaceAll.MakeEvent() ); + Go( &SCH_FIND_REPLACE_TOOL::UpdateFind, ACTIONS::updateFind.MakeEvent() ); + Go( &SCH_FIND_REPLACE_TOOL::UpdateFind, EVENTS::SelectedItemsModified ); + Go( &SCH_FIND_REPLACE_TOOL::UpdateFind, EVENTS::PointSelectedEvent ); + Go( &SCH_FIND_REPLACE_TOOL::UpdateFind, EVENTS::SelectedEvent ); +} diff --git a/eeschema/tools/sch_find_replace_tool.h b/eeschema/tools/sch_find_replace_tool.h new file mode 100644 index 0000000000..9da7111108 --- /dev/null +++ b/eeschema/tools/sch_find_replace_tool.h @@ -0,0 +1,93 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 CERN + * Copyright (C) 2019-2023 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 + */ + + +#ifndef SCH_FIND_REPLACE_TOOL_H +#define SCH_FIND_REPLACE_TOOL_H + +#include +#include + +class SCH_EDIT_FRAME; + +/** + * Handle actions specific to the schematic editor. + */ +class SCH_FIND_REPLACE_TOOL : public wxEvtHandler, public EE_TOOL_BASE +{ +public: + SCH_FIND_REPLACE_TOOL() : + EE_TOOL_BASE( "eeschema.FindReplace" ), + m_foundItemHighlighted( false ) + { } + + ~SCH_FIND_REPLACE_TOOL() { } + + int FindAndReplace( const TOOL_EVENT& aEvent ); + + int FindNext( const TOOL_EVENT& aEvent ); + bool HasMatch(); + int ReplaceAndFindNext( const TOOL_EVENT& aEvent ); + int ReplaceAll( const TOOL_EVENT& aEvent ); + + int UpdateFind( const TOOL_EVENT& aEvent ); + + /** + * Find a symbol in the schematic and an item in this symbol and select it. + * + * @param aPath The symbol path to find. Pass nullptr to search by aReference. + * @param aReference The symbol reference designator to find, or to display in + * status bar if aPath is specified + * @param aSearchHierarchy If false, search the current sheet only. Otherwise, + * the entire hierarchy + * @param aSearchType A #SCH_SEARCH_T value used to determine what to search for. + * @param aSearchText The text to search for, either in value, reference or elsewhere. + */ + SCH_ITEM* FindSymbolAndItem( const wxString* aPath, const wxString* aReference, + bool aSearchHierarchy, SCH_SEARCH_T aSearchType, + const wxString& aSearchText ); + +private: + ///< Set up handlers for various events. + void setTransitions() override; + + /** + * Advance the search and returns the next matching item after \a aAfter. + * + * @param aScreen Pointer to the screen used for searching + * @param aAfter Starting match to compare + * @param aData Search data to compare against or NULL to match the first item found + * @return pointer to the next search item found or NULL if nothing found + */ + SCH_ITEM* nextMatch( SCH_SCREEN* aScreen, SCH_SHEET_PATH* aSheet, SCH_ITEM* aAfter, + EDA_SEARCH_DATA& aData ); + +private: + bool m_foundItemHighlighted; + wxTimer m_wrapAroundTimer; // A timer during which a subsequent FindNext will + // result in a wrap-around +}; + + +#endif // SCH_FIND_REPLACE_TOOL_H