471 lines
15 KiB
C++
471 lines
15 KiB
C++
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <ee_actions.h>
|
|
#include <sch_edit_frame.h>
|
|
#include <sch_painter.h>
|
|
#include <sch_symbol.h>
|
|
#include <sch_label.h>
|
|
#include <sch_text.h>
|
|
#include <sch_textbox.h>
|
|
#include <schematic.h>
|
|
#include <string_utils.h>
|
|
#include <tool/tool_manager.h>
|
|
#include "search_handlers.h"
|
|
|
|
|
|
void SCH_SEARCH_HANDLER::ActivateItem( long aItemRow )
|
|
{
|
|
std::vector<long> item = { aItemRow };
|
|
SelectItems( item );
|
|
|
|
m_frame->GetToolManager()->RunAction( EE_ACTIONS::properties, true );
|
|
}
|
|
|
|
|
|
void SCH_SEARCH_HANDLER::FindAll( const std::function<bool( SCH_ITEM*, SCH_SHEET_PATH* )>& aCollector )
|
|
{
|
|
SCH_SCREENS screens( m_frame->Schematic().Root() );
|
|
std::vector<SCH_SHEET_PATH*> 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, std::vector<long>* aSelection )
|
|
{
|
|
std::vector<SCH_ITEM*> selection;
|
|
|
|
for( long i = 0; i < (long) m_hitlist.size(); ++i )
|
|
{
|
|
if( alg::contains( *aSelection, i ) )
|
|
selection.push_back( m_hitlist[i].item );
|
|
}
|
|
|
|
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. ~(a<b) != (a>b)
|
|
if( aAscending )
|
|
return StrNumCmp( getResultCell( a, col ), getResultCell( b, col ), true ) < 0;
|
|
else
|
|
return StrNumCmp( getResultCell( b, col ), getResultCell( a, col ), true ) < 0;
|
|
} );
|
|
|
|
aSelection->clear();
|
|
|
|
for( long i = 0; i < (long) m_hitlist.size(); ++i )
|
|
{
|
|
if( alg::contains( selection, m_hitlist[i].item ) )
|
|
aSelection->push_back( i );
|
|
}
|
|
}
|
|
|
|
|
|
void SCH_SEARCH_HANDLER::SelectItems( std::vector<long>& aItemRows )
|
|
{
|
|
EDA_ITEMS selectedItems;
|
|
std::vector<SCH_SEARCH_HIT> 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;
|
|
} );
|
|
|
|
APP_SETTINGS_BASE::SEARCH_PANE& settings = m_frame->config()->m_SearchPane;
|
|
|
|
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<EDA_ITEMS*>( EE_ACTIONS::addItemsToSel, &selectedItems );
|
|
|
|
switch( settings.selection_zoom )
|
|
{
|
|
case APP_SETTINGS_BASE::SEARCH_PANE::SELECTION_ZOOM::PAN:
|
|
m_frame->GetToolManager()->RunAction( ACTIONS::centerSelection );
|
|
break;
|
|
case APP_SETTINGS_BASE::SEARCH_PANE::SELECTION_ZOOM::ZOOM:
|
|
m_frame->GetToolManager()->RunAction( ACTIONS::zoomFitSelection );
|
|
break;
|
|
case APP_SETTINGS_BASE::SEARCH_PANE::SELECTION_ZOOM::NONE:
|
|
break;
|
|
}
|
|
|
|
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<SCH_SYMBOL*>( 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<SCH_SYMBOL*>( 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;
|
|
}
|
|
|
|
|
|
POWER_SEARCH_HANDLER::POWER_SEARCH_HANDLER( SCH_EDIT_FRAME* aFrame ) :
|
|
SCH_SEARCH_HANDLER( _HKI( "Power" ), 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( "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 POWER_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<SCH_SYMBOL*>( 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 POWER_SEARCH_HANDLER::getResultCell( const SCH_SEARCH_HIT& aHit, int aCol )
|
|
{
|
|
SCH_SYMBOL* sym = dynamic_cast<SCH_SYMBOL*>( 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 aHit.sheetPath->GetPageNumber();
|
|
else if( aCol == 3 )
|
|
return m_frame->MessageTextFromValue( sym->GetPosition().x );
|
|
else if( aCol == 4 )
|
|
return m_frame->MessageTextFromValue( sym->GetPosition().y );
|
|
|
|
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<SCH_TEXT*>( 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<SCH_TEXTBOX*>( 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<SCH_LABEL_BASE*>( 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<SCH_LABEL_BASE*>( 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;
|
|
}
|