/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2022-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 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 #include #include #include #include #include "search_handlers.h" void SCH_SEARCH_HANDLER::ActivateItem( long aItemRow ) { std::vector item = { aItemRow }; SelectItems( item ); m_frame->GetToolManager()->RunAction( EE_ACTIONS::properties, true ); } void SCH_SEARCH_HANDLER::FindAll( const std::function& aCollector ) { SCH_SCREENS screens( m_frame->Schematic().Root() ); std::vector paths; m_hitlist.clear(); screens.BuildClientSheetPathList(); for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() ) { for( SCH_SHEET_PATH& sheet : screen->GetClientSheetPaths() ) paths.push_back( &sheet ); } for( SCH_SHEET_PATH* sheet : paths ) { for( SCH_ITEM* item : sheet->LastScreen()->Items() ) { if( aCollector( item, sheet ) ) m_hitlist.push_back( { item, sheet } ); } } } void SCH_SEARCH_HANDLER::Sort( int aCol, bool aAscending ) { int col = std::max( 0, aCol ); // Provide a stable order by sorting on first column if no // sort column provided. std::sort( m_hitlist.begin(), m_hitlist.end(), [&]( const SCH_SEARCH_HIT& a, const SCH_SEARCH_HIT& b ) -> bool { // N.B. To meet the iterator sort conditions, we cannot simply invert the truth // to get the opposite sort. i.e. ~(ab) if( aAscending ) return StrNumCmp( getResultCell( a, col ), getResultCell( b, col ), true ) < 0; else return StrNumCmp( getResultCell( b, col ), getResultCell( a, col ), true ) < 0; } ); } void SCH_SEARCH_HANDLER::SelectItems( std::vector& aItemRows ) { EDA_ITEMS selectedItems; std::vector selectedHits; m_frame->GetToolManager()->RunAction( EE_ACTIONS::clearSelection ); for( long row : aItemRows ) { if( row >= 0 && row < (long) m_hitlist.size() ) { selectedHits.emplace_back( m_hitlist[row] ); selectedItems.emplace_back( m_hitlist[row].item ); } } if( selectedHits.empty() ) return; bool allHitsOnSamePage = std::all_of( selectedHits.begin() + 1, selectedHits.end(), [&]( const SCH_SEARCH_HIT& r ) { return r.sheetPath == selectedHits.front().sheetPath; } ); if( allHitsOnSamePage && !selectedHits.empty() ) { if( m_frame->GetCurrentSheet() != *selectedHits.front().sheetPath ) { m_frame->SetCurrentSheet( *selectedHits.front().sheetPath ); m_frame->DisplayCurrentSheet(); } if( selectedItems.size() ) m_frame->GetToolManager()->RunAction( EE_ACTIONS::addItemsToSel, &selectedItems ); m_frame->GetCanvas()->Refresh( false ); } } SYMBOL_SEARCH_HANDLER::SYMBOL_SEARCH_HANDLER( SCH_EDIT_FRAME* aFrame ) : SCH_SEARCH_HANDLER( _HKI( "Symbols" ), aFrame ) { m_columns.emplace_back( _HKI( "Reference" ), 2, wxLIST_FORMAT_LEFT ); m_columns.emplace_back( _HKI( "Value" ), 6, wxLIST_FORMAT_LEFT ); m_columns.emplace_back( _HKI( "Footprint" ), 10, wxLIST_FORMAT_LEFT ); m_columns.emplace_back( _HKI( "Page" ), 1, wxLIST_FORMAT_CENTER ); m_columns.emplace_back( wxT( "X" ), 3, wxLIST_FORMAT_CENTER ); m_columns.emplace_back( wxT( "Y" ), 3, wxLIST_FORMAT_CENTER ); m_columns.emplace_back( _HKI( "Excl. sim" ), 2, wxLIST_FORMAT_CENTER ); m_columns.emplace_back( _HKI( "Excl. BOM" ), 2, wxLIST_FORMAT_CENTER ); m_columns.emplace_back( _HKI( "Excl. board" ), 2, wxLIST_FORMAT_CENTER ); m_columns.emplace_back( _HKI( "DNP" ), 2, wxLIST_FORMAT_CENTER ); } int SYMBOL_SEARCH_HANDLER::Search( const wxString& aQuery ) { m_hitlist.clear(); SCH_SEARCH_DATA frp; frp.findString = aQuery; // Try to handle whatever the user throws at us (substring, wildcards, regex, etc.) frp.matchMode = EDA_SEARCH_MATCH_MODE::PERMISSIVE; frp.searchCurrentSheetOnly = false; auto search = [frp]( SCH_ITEM* item, SCH_SHEET_PATH* sheet ) { if( item && item->Type() == SCH_SYMBOL_T ) { SCH_SYMBOL* sym = static_cast( item ); // IsPower depends on non-missing lib symbol association if( !sym->IsMissingLibSymbol() && sym->IsPower() ) return false; for( SCH_FIELD& field : sym->GetFields() ) { if( frp.findString.IsEmpty() || field.Matches( frp, sheet ) ) return true; } } return false; }; FindAll( search ); return (int) m_hitlist.size(); } wxString SYMBOL_SEARCH_HANDLER::getResultCell( const SCH_SEARCH_HIT& aHit, int aCol ) { SCH_SYMBOL*sym = dynamic_cast( aHit.item ); if( !sym ) return wxEmptyString; if( aCol == 0 ) return sym->GetRef( aHit.sheetPath, true ); else if( aCol == 1 ) return sym->GetField( VALUE_FIELD )->GetShownText( aHit.sheetPath, false ); else if( aCol == 2 ) return sym->GetField( FOOTPRINT_FIELD )->GetShownText( aHit.sheetPath, false ); else if( aCol == 3 ) return aHit.sheetPath->GetPageNumber(); else if( aCol == 4 ) return m_frame->MessageTextFromValue( sym->GetPosition().x ); else if( aCol == 5 ) return m_frame->MessageTextFromValue( sym->GetPosition().y ); else if( aCol == 6 ) return sym->GetExcludedFromSim() ? wxS( "X" ) : wxS( " " ); else if( aCol == 7 ) return sym->GetExcludedFromBOM() ? wxS( "X" ) : wxS( " " ); else if( aCol == 8 ) return sym->GetExcludedFromBoard() ? wxS( "X" ) : wxS( " " ); else if( aCol == 9 ) return sym->GetDNP() ? wxS( "X" ) : wxS( " " ); return wxEmptyString; } TEXT_SEARCH_HANDLER::TEXT_SEARCH_HANDLER( SCH_EDIT_FRAME* aFrame ) : SCH_SEARCH_HANDLER( _HKI( "Text" ), aFrame ) { m_columns.emplace_back( _HKI( "Type" ), 2, wxLIST_FORMAT_LEFT ); m_columns.emplace_back( _HKI( "Text" ), 12, wxLIST_FORMAT_LEFT ); m_columns.emplace_back( _HKI( "Page" ), 1, wxLIST_FORMAT_CENTER ); m_columns.emplace_back( wxT( "X" ), 3, wxLIST_FORMAT_CENTER ); m_columns.emplace_back( wxT( "Y" ), 3, wxLIST_FORMAT_CENTER ); } int TEXT_SEARCH_HANDLER::Search( const wxString& aQuery ) { m_hitlist.clear(); SCH_SEARCH_DATA frp; frp.findString = aQuery; // Try to handle whatever the user throws at us (substring, wildcards, regex, etc.) frp.matchMode = EDA_SEARCH_MATCH_MODE::PERMISSIVE; frp.searchCurrentSheetOnly = false; auto search = [frp]( SCH_ITEM* item, SCH_SHEET_PATH* sheet ) { if( item->Type() == SCH_TEXT_T || item->Type() == SCH_TEXTBOX_T ) { if( frp.findString.IsEmpty() || item->Matches( frp, sheet ) ) return true; } return false; }; FindAll( search ); return (int) m_hitlist.size(); } wxString TEXT_SEARCH_HANDLER::getResultCell( const SCH_SEARCH_HIT& aHit, int aCol ) { if( aHit.item->Type() == SCH_TEXT_T ) { SCH_TEXT* txt = dynamic_cast( aHit.item ); if( !txt ) return wxEmptyString; if( aCol == 0 ) return _( "Text" ); else if( aCol == 1 ) return txt->GetShownText( false ); else if( aCol == 2 ) return aHit.sheetPath->GetPageNumber(); else if( aCol == 3 ) return m_frame->MessageTextFromValue( txt->GetPosition().x ); else if( aCol == 4 ) return m_frame->MessageTextFromValue( txt->GetPosition().y ); } else if( aHit.item->Type() == SCH_TEXTBOX_T ) { SCH_TEXTBOX* txt = dynamic_cast( aHit.item ); if( !txt ) return wxEmptyString; if( aCol == 0 ) return _( "Text Box" ); else if( aCol == 1 ) return txt->GetShownText( false ); else if( aCol == 2 ) return aHit.sheetPath->GetPageNumber(); else if( aCol == 3 ) return m_frame->MessageTextFromValue( txt->GetPosition().x ); else if( aCol == 4 ) return m_frame->MessageTextFromValue( txt->GetPosition().y ); } return wxEmptyString; } LABEL_SEARCH_HANDLER::LABEL_SEARCH_HANDLER( SCH_EDIT_FRAME* aFrame ) : SCH_SEARCH_HANDLER( _HKI( "Labels" ), aFrame ) { m_columns.emplace_back( _HKI( "Type" ), 2, wxLIST_FORMAT_LEFT ); m_columns.emplace_back( _HKI( "Name" ), 6, wxLIST_FORMAT_LEFT ); m_columns.emplace_back( _HKI( "Page" ), 2, wxLIST_FORMAT_CENTER ); m_columns.emplace_back( wxT( "X" ), 3, wxLIST_FORMAT_CENTER ); m_columns.emplace_back( wxT( "Y" ), 3 , wxLIST_FORMAT_CENTER); } int LABEL_SEARCH_HANDLER::Search( const wxString& aQuery ) { m_hitlist.clear(); SCH_SEARCH_DATA frp; frp.findString = aQuery; // Try to handle whatever the user throws at us (substring, wildcards, regex, etc.) frp.matchMode = EDA_SEARCH_MATCH_MODE::PERMISSIVE; frp.searchCurrentSheetOnly = false; auto search = [frp]( SCH_ITEM* item, SCH_SHEET_PATH* sheet ) { if( item->IsType( { SCH_LABEL_LOCATE_ANY_T } ) ) { SCH_LABEL_BASE* lbl = dynamic_cast( item ); wxCHECK( lbl, false ); if( frp.findString.IsEmpty() || lbl->Matches( frp, sheet ) ) return true; } return false; }; FindAll( search ); return (int) m_hitlist.size(); } wxString LABEL_SEARCH_HANDLER::getResultCell( const SCH_SEARCH_HIT& aHit, int aCol ) { SCH_LABEL_BASE* lbl = dynamic_cast( aHit.item ); if( !lbl ) return wxEmptyString; if (aCol == 0) { if(lbl->Type() == SCH_LABEL_T) return _HKI( "Local" ); else if( lbl->Type() == SCH_GLOBAL_LABEL_T ) return _HKI( "Global" ); else if( lbl->Type() == SCH_HIER_LABEL_T ) return _HKI( "Hierarchical" ); else if( lbl->Type() == SCH_DIRECTIVE_LABEL_T ) return _HKI( "Directive" ); } else if( aCol == 1 ) return lbl->GetShownText( false ); else if( aCol == 2 ) return aHit.sheetPath->GetPageNumber(); else if( aCol == 3 ) return m_frame->MessageTextFromValue( lbl->GetPosition().x ); else if( aCol == 4 ) return m_frame->MessageTextFromValue( lbl->GetPosition().y ); return wxEmptyString; }