Added a sort-order dropdown to lib-tree filters.

Also simplifies the scoring algorithm so that it only
differentiates between exact-match, match-at-start and
any-match.  The rest of the position-based matching
stuff is gone, as is the knowledge of the name vs the
keywords vs the description.  All that is left to the
provider of the weighted search terms array.
This commit is contained in:
Jeff Young 2023-05-01 21:26:29 +01:00
parent 04a53ea40d
commit df7d62fdd3
29 changed files with 273 additions and 203 deletions

View File

@ -445,6 +445,38 @@ bool EDA_COMBINED_MATCHER::StartsWith( const wxString& aTerm )
} }
int EDA_COMBINED_MATCHER::ScoreTerms( std::vector<SEARCH_TERM>& aWeightedTerms )
{
int score = 0;
for( SEARCH_TERM& term : aWeightedTerms )
{
if( !term.Normalized )
{
term.Text = term.Text.MakeLower().Trim( false ).Trim( true );
term.Normalized = true;
}
int found_pos = EDA_PATTERN_NOT_FOUND;
int matchers_fired = 0;
if( GetPattern() == term.Text )
{
score += 8 * term.Score;
}
else if( Find( term.Text, matchers_fired, found_pos ) )
{
if( found_pos == 0 )
score += 2 * term.Score;
else
score += term.Score;
}
}
return score;
}
wxString const& EDA_COMBINED_MATCHER::GetPattern() const wxString const& EDA_COMBINED_MATCHER::GetPattern() const
{ {
return m_pattern; return m_pattern;

View File

@ -82,15 +82,13 @@ void FOOTPRINT_FILTER_IT::increment()
if( ( filter_type & FOOTPRINT_FILTER::FILTERING_BY_TEXT_PATTERN ) ) if( ( filter_type & FOOTPRINT_FILTER::FILTERING_BY_TEXT_PATTERN ) )
{ {
wxString searchStr = wxString::Format( wxT( "%s:%s %s" ),
candidate.GetLibNickname(),
candidate.GetFootprintName(),
candidate.GetSearchText() );
bool exclude = false; bool exclude = false;
for( std::unique_ptr<EDA_COMBINED_MATCHER>& matcher : m_filter->m_pattern_filters ) for( std::unique_ptr<EDA_COMBINED_MATCHER>& matcher : m_filter->m_pattern_filters )
{ {
if( !matcher->Find( searchStr.Lower() ) ) std::vector<SEARCH_TERM> searchTerms = candidate.GetSearchTerms();
if( !matcher->ScoreTerms( searchTerms ) )
{ {
exclude = true; exclude = true;
break; break;

View File

@ -37,6 +37,7 @@
#include <lib_id.h> #include <lib_id.h>
#include <thread> #include <thread>
#include <utility> #include <utility>
#include <wx/tokenzr.h>
#include <kiface_base.h> #include <kiface_base.h>
FOOTPRINT_INFO* FOOTPRINT_LIST::GetFootprintInfo( const wxString& aLibNickname, FOOTPRINT_INFO* FOOTPRINT_LIST::GetFootprintInfo( const wxString& aLibNickname,
@ -69,6 +70,25 @@ FOOTPRINT_INFO* FOOTPRINT_LIST::GetFootprintInfo( const wxString& aFootprintName
} }
std::vector<SEARCH_TERM> FOOTPRINT_INFO::GetSearchTerms()
{
std::vector<SEARCH_TERM> terms;
terms.emplace_back( SEARCH_TERM( GetName(), 8 ) );
wxStringTokenizer keywordTokenizer( GetKeywords(), wxS( " " ), wxTOKEN_STRTOK );
while( keywordTokenizer.HasMoreTokens() )
terms.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
// Also include keywords as one long string, just in case
terms.emplace_back( SEARCH_TERM( GetKeywords(), 1 ) );
terms.emplace_back( SEARCH_TERM( GetDescription(), 1 ) );
return terms;
}
bool FOOTPRINT_INFO::InLibrary( const wxString& aLibrary ) const bool FOOTPRINT_INFO::InLibrary( const wxString& aLibrary ) const
{ {
return aLibrary == m_nickname; return aLibrary == m_nickname;

View File

@ -34,20 +34,6 @@
static const unsigned kLowestDefaultScore = 1; static const unsigned kLowestDefaultScore = 1;
// Creates a score depending on the position of a string match. If the position
// is 0 (= prefix match), this returns the maximum score. This degrades until
// pos == max, which returns a score of 0; Evertyhing else beyond that is just
// 0. Only values >= 0 allowed for position and max.
//
// @param aPosition is the position a string has been found in a substring.
// @param aMaximum is the maximum score this function returns.
// @return position dependent score.
static int matchPosScore(int aPosition, int aMaximum)
{
return ( aPosition < aMaximum ) ? aMaximum - aPosition : 0;
}
void LIB_TREE_NODE::ResetScore() void LIB_TREE_NODE::ResetScore()
{ {
for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children ) for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
@ -85,20 +71,21 @@ void LIB_TREE_NODE::AssignIntrinsicRanks( bool presorted )
} }
void LIB_TREE_NODE::SortNodes() void LIB_TREE_NODE::SortNodes( bool aUseScores )
{ {
std::sort( m_Children.begin(), m_Children.end(), std::sort( m_Children.begin(), m_Children.end(),
[]( std::unique_ptr<LIB_TREE_NODE>& a, std::unique_ptr<LIB_TREE_NODE>& b ) [&]( std::unique_ptr<LIB_TREE_NODE>& a, std::unique_ptr<LIB_TREE_NODE>& b )
{ {
return Compare( *a, *b ); return Compare( *a, *b, aUseScores );
} ); } );
for( std::unique_ptr<LIB_TREE_NODE>& node: m_Children ) for( std::unique_ptr<LIB_TREE_NODE>& node: m_Children )
node->SortNodes(); node->SortNodes( aUseScores );
} }
bool LIB_TREE_NODE::Compare( LIB_TREE_NODE const& aNode1, LIB_TREE_NODE const& aNode2 ) bool LIB_TREE_NODE::Compare( LIB_TREE_NODE const& aNode1, LIB_TREE_NODE const& aNode2,
bool aUseScores )
{ {
if( aNode1.m_Type != aNode2.m_Type ) if( aNode1.m_Type != aNode2.m_Type )
return aNode1.m_Type < aNode2.m_Type; return aNode1.m_Type < aNode2.m_Type;
@ -126,6 +113,9 @@ bool LIB_TREE_NODE::Compare( LIB_TREE_NODE const& aNode1, LIB_TREE_NODE const& a
else if( aNode2.m_Pinned && !aNode1.m_Pinned ) else if( aNode2.m_Pinned && !aNode1.m_Pinned )
return false; return false;
if( aUseScores && aNode1.m_Score != aNode2.m_Score )
return aNode1.m_Score > aNode2.m_Score;
if( aNode1.m_IntrinsicRank != aNode2.m_IntrinsicRank ) if( aNode1.m_IntrinsicRank != aNode2.m_IntrinsicRank )
return aNode1.m_IntrinsicRank > aNode2.m_IntrinsicRank; return aNode1.m_IntrinsicRank > aNode2.m_IntrinsicRank;
@ -139,7 +129,6 @@ LIB_TREE_NODE::LIB_TREE_NODE()
m_IntrinsicRank( 0 ), m_IntrinsicRank( 0 ),
m_Score( kLowestDefaultScore ), m_Score( kLowestDefaultScore ),
m_Pinned( false ), m_Pinned( false ),
m_Normalized( false ),
m_Unit( 0 ), m_Unit( 0 ),
m_IsRoot( false ) m_IsRoot( false )
{} {}
@ -167,15 +156,9 @@ LIB_TREE_NODE_UNIT::LIB_TREE_NODE_UNIT( LIB_TREE_NODE* aParent, LIB_TREE_ITEM* a
m_Name = namePrefix + " " + aItem->GetUnitReference( aUnit ); m_Name = namePrefix + " " + aItem->GetUnitReference( aUnit );
if( aItem->HasUnitDisplayName( aUnit ) ) if( aItem->HasUnitDisplayName( aUnit ) )
{
m_Desc = aItem->GetUnitDisplayName( aUnit ); m_Desc = aItem->GetUnitDisplayName( aUnit );
}
else else
{
m_Desc = wxEmptyString; m_Desc = wxEmptyString;
}
m_MatchName = wxEmptyString;
m_IntrinsicRank = -aUnit; m_IntrinsicRank = -aUnit;
} }
@ -195,9 +178,7 @@ LIB_TREE_NODE_LIB_ID::LIB_TREE_NODE_LIB_ID( LIB_TREE_NODE* aParent, LIB_TREE_ITE
aItem->GetChooserFields( m_Fields ); aItem->GetChooserFields( m_Fields );
m_MatchName = aItem->GetName(); m_SearchTerms = aItem->GetSearchTerms();
m_SearchText = aItem->GetSearchText();
m_Normalized = false;
m_IsRoot = aItem->IsRoot(); m_IsRoot = aItem->IsRoot();
@ -224,12 +205,10 @@ void LIB_TREE_NODE_LIB_ID::Update( LIB_TREE_ITEM* aItem )
m_Name = aItem->GetName(); m_Name = aItem->GetName();
m_Desc = aItem->GetDescription(); m_Desc = aItem->GetDescription();
m_MatchName = aItem->GetName();
aItem->GetChooserFields( m_Fields ); aItem->GetChooserFields( m_Fields );
m_SearchText = aItem->GetSearchText(); m_SearchTerms = aItem->GetSearchTerms();
m_Normalized = false;
m_IsRoot = aItem->IsRoot(); m_IsRoot = aItem->IsRoot();
m_Children.clear(); m_Children.clear();
@ -244,59 +223,13 @@ void LIB_TREE_NODE_LIB_ID::UpdateScore( EDA_COMBINED_MATCHER& aMatcher, const wx
if( m_Score <= 0 ) if( m_Score <= 0 )
return; // Leaf nodes without scores are out of the game. return; // Leaf nodes without scores are out of the game.
if( !m_Normalized ) if( !aLib.IsEmpty() && m_Parent->m_Name.Lower() != aLib )
{
m_MatchName = UnescapeString( m_MatchName ).Lower();
m_SearchText = m_SearchText.Lower();
m_Normalized = true;
}
if( !aLib.IsEmpty() && m_Parent->m_MatchName != aLib )
{ {
m_Score = 0; m_Score = 0;
return; return;
} }
// Keywords and description we only count if the match string is at m_Score = aMatcher.ScoreTerms( m_SearchTerms );
// least two characters long. That avoids spurious, low quality
// matches. Most abbreviations are at three characters long.
int found_pos = EDA_PATTERN_NOT_FOUND;
int matchers_fired = 0;
if( aMatcher.GetPattern() == m_MatchName )
{
m_Score += 1000; // exact match. High score :)
}
else if( aMatcher.Find( m_MatchName, matchers_fired, found_pos ) )
{
// Substring match. The earlier in the string the better.
m_Score += matchPosScore( found_pos, 20 ) + 20;
}
else if( aMatcher.Find( m_Parent->m_MatchName, matchers_fired, found_pos ) )
{
m_Score += 19; // parent name matches. score += 19
}
else if( aMatcher.Find( m_SearchText, matchers_fired, found_pos ) )
{
// If we have a very short search term (like one or two letters),
// we don't want to accumulate scores if they just happen to be in
// keywords or description as almost any one or two-letter
// combination shows up in there.
if( aMatcher.GetPattern().length() >= 2 )
{
// For longer terms, we add scores 1..18 for positional match
// (higher in the front, where the keywords are).
m_Score += matchPosScore( found_pos, 17 ) + 1;
}
}
else
{
// No match. That's it for this item.
m_Score = 0;
}
// More matchers = better match
m_Score += 2 * matchers_fired;
} }
@ -305,7 +238,6 @@ LIB_TREE_NODE_LIB::LIB_TREE_NODE_LIB( LIB_TREE_NODE* aParent, wxString const& aN
{ {
m_Type = LIB; m_Type = LIB;
m_Name = aName; m_Name = aName;
m_MatchName = aName.Lower();
m_Desc = aDesc; m_Desc = aDesc;
m_Parent = aParent; m_Parent = aParent;
m_LibId.SetLibNickname( aName ); m_LibId.SetLibNickname( aName );
@ -338,27 +270,7 @@ void LIB_TREE_NODE_LIB::UpdateScore( EDA_COMBINED_MATCHER& aMatcher, const wxStr
{ {
// No children; we are a leaf. // No children; we are a leaf.
if( !aLib.IsEmpty() ) m_Score = aMatcher.ScoreTerms( m_SearchTerms );
{
m_Score = m_MatchName == aLib ? 1000 : 0;
return;
}
int found_pos = EDA_PATTERN_NOT_FOUND;
int matchers_fired = 0;
if( aMatcher.GetPattern() == m_MatchName )
{
m_Score += 1000; // exact match. High score :)
}
else if( aMatcher.Find( m_MatchName, matchers_fired, found_pos ) )
{
// Substring match. The earlier in the string the better.
m_Score += matchPosScore( found_pos, 20 ) + 20;
}
// More matchers = better match
m_Score += 2 * matchers_fired;
} }
} }

View File

@ -68,6 +68,7 @@ LIB_TREE_MODEL_ADAPTER::LIB_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent,
const wxString& aPinnedKey ) : const wxString& aPinnedKey ) :
m_parent( aParent ), m_parent( aParent ),
m_filter( SYM_FILTER_NONE ), m_filter( SYM_FILTER_NONE ),
m_sort_mode( BEST_MATCH ),
m_show_units( true ), m_show_units( true ),
m_preselect_unit( 0 ), m_preselect_unit( 0 ),
m_freeze( 0 ), m_freeze( 0 ),
@ -208,7 +209,7 @@ void LIB_TREE_MODEL_ADAPTER::UpdateSearchString( const wxString& aSearch, bool a
m_tree.UpdateScore( matcher, lib ); m_tree.UpdateScore( matcher, lib );
} }
m_tree.SortNodes(); m_tree.SortNodes( m_sort_mode == BEST_MATCH );
AfterReset(); AfterReset();
Thaw(); Thaw();
} }
@ -273,7 +274,7 @@ void LIB_TREE_MODEL_ADAPTER::resortTree()
Freeze(); Freeze();
BeforeReset(); BeforeReset();
m_tree.SortNodes(); m_tree.SortNodes( m_sort_mode == BEST_MATCH );
AfterReset(); AfterReset();
Thaw(); Thaw();

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2014 Henner Zeller <h.zeller@acm.org> * Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
* Copyright (C) 2014-2022 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2014-2023 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -23,8 +23,10 @@
*/ */
#include <widgets/lib_tree.h> #include <widgets/lib_tree.h>
#include <widgets/std_bitmap_button.h>
#include <core/kicad_algo.h> #include <core/kicad_algo.h>
#include <macros.h> #include <macros.h>
#include <bitmaps.h>
#include <dialogs/eda_reorderable_list_dialog.h> #include <dialogs/eda_reorderable_list_dialog.h>
#include <tool/tool_interactive.h> #include <tool/tool_interactive.h>
#include <tool/tool_manager.h> #include <tool/tool_manager.h>
@ -46,7 +48,9 @@ LIB_TREE::LIB_TREE( wxWindow* aParent, const wxString& aRecentSearchesKey, LIB_T
HTML_WINDOW* aDetails ) : HTML_WINDOW* aDetails ) :
wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxWANTS_CHARS | wxTAB_TRAVERSAL | wxNO_BORDER ), wxWANTS_CHARS | wxTAB_TRAVERSAL | wxNO_BORDER ),
m_lib_table( aLibTable ), m_adapter( aAdapter ), m_query_ctrl( nullptr ), m_adapter( aAdapter ),
m_query_ctrl( nullptr ),
m_sort_ctrl( nullptr ),
m_details_ctrl( nullptr ), m_details_ctrl( nullptr ),
m_inTimerEvent( false ), m_inTimerEvent( false ),
m_recentSearchesKey( aRecentSearchesKey ), m_recentSearchesKey( aRecentSearchesKey ),
@ -73,6 +77,36 @@ LIB_TREE::LIB_TREE( wxWindow* aParent, const wxString& aRecentSearchesKey, LIB_T
search_sizer->Add( m_query_ctrl, 1, wxEXPAND, 5 ); search_sizer->Add( m_query_ctrl, 1, wxEXPAND, 5 );
m_sort_ctrl = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition,
wxDefaultSize, wxBU_AUTODRAW|0 );
m_sort_ctrl->SetBitmap( KiBitmap( BITMAPS::small_sort_desc ) );
m_sort_ctrl->Bind( wxEVT_LEFT_DOWN,
[&]( wxMouseEvent& aEvent )
{
wxMenu menu;
menu.Append( 4201, _( "Sort by Best Match" ), wxEmptyString, wxITEM_CHECK );
menu.Append( 4202, _( "Sort Alphabetically" ), wxEmptyString, wxITEM_CHECK );
if( m_adapter->GetSortMode() == LIB_TREE_MODEL_ADAPTER::BEST_MATCH )
menu.Check( 4201, true );
else
menu.Check( 4202, true );
if( m_sort_ctrl->GetPopupMenuSelectionFromUser( menu ) == 0 )
{
m_adapter->SetSortMode( LIB_TREE_MODEL_ADAPTER::BEST_MATCH );
Regenerate( true );
}
else
{
m_adapter->SetSortMode( LIB_TREE_MODEL_ADAPTER::ALPHABETIC );
Regenerate( true );
}
} );
search_sizer->Add( m_sort_ctrl, 0, wxEXPAND|wxALL, 1 );
sizer->Add( search_sizer, 0, wxEXPAND, 5 ); sizer->Add( search_sizer, 0, wxEXPAND, 5 );
m_query_ctrl->Bind( wxEVT_TEXT, &LIB_TREE::onQueryText, this ); m_query_ctrl->Bind( wxEVT_TEXT, &LIB_TREE::onQueryText, this );

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2014 Henner Zeller <h.zeller@acm.org> * Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
* Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -203,6 +203,8 @@ DIALOG_CHOOSE_SYMBOL::DIALOG_CHOOSE_SYMBOL( SCH_BASE_FRAME* aParent, const wxStr
wxSize dlgSize( panelCfg.width > 0 ? panelCfg.width : horizPixelsFromDU( 390 ), wxSize dlgSize( panelCfg.width > 0 ? panelCfg.width : horizPixelsFromDU( 390 ),
panelCfg.height > 0 ? panelCfg.height : vertPixelsFromDU( 300 ) ); panelCfg.height > 0 ? panelCfg.height : vertPixelsFromDU( 300 ) );
SetSize( dlgSize ); SetSize( dlgSize );
aAdapter->SetSortMode( (LIB_TREE_MODEL_ADAPTER::SORT_MODE) cfg->m_SymChooserPanel.sort_mode );
} }
SetInitialFocus( m_tree->GetFocusTarget() ); SetInitialFocus( m_tree->GetFocusTarget() );
@ -280,6 +282,8 @@ DIALOG_CHOOSE_SYMBOL::~DIALOG_CHOOSE_SYMBOL()
if( m_vsplitter ) if( m_vsplitter )
cfg->m_SymChooserPanel.sash_pos_v = m_vsplitter->GetSashPosition(); cfg->m_SymChooserPanel.sash_pos_v = m_vsplitter->GetSashPosition();
cfg->m_SymChooserPanel.sort_mode = m_tree->GetSortMode();
} }
} }

View File

@ -446,6 +446,9 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() :
m_params.emplace_back( new PARAM<int>( "symbol_chooser.height", m_params.emplace_back( new PARAM<int>( "symbol_chooser.height",
&m_SymChooserPanel.height, -1 ) ); &m_SymChooserPanel.height, -1 ) );
m_params.emplace_back( new PARAM<int>( "symbol_chooser.sort_mode",
&m_SymChooserPanel.sort_mode, 0 ) );
m_params.emplace_back( new PARAM<bool>( "symbol_chooser.keep_symbol", m_params.emplace_back( new PARAM<bool>( "symbol_chooser.keep_symbol",
&m_SymChooserPanel.keep_symbol, false ) ); &m_SymChooserPanel.keep_symbol, false ) );

View File

@ -234,6 +234,7 @@ public:
int sash_pos_v; int sash_pos_v;
int width; int width;
int height; int height;
int sort_mode;
bool keep_symbol; bool keep_symbol;
bool place_all_units; bool place_all_units;
}; };

View File

@ -45,28 +45,34 @@ int LIB_SYMBOL::m_subpartIdSeparator = 0;
int LIB_SYMBOL::m_subpartFirstId = 'A'; int LIB_SYMBOL::m_subpartFirstId = 'A';
wxString LIB_SYMBOL::GetSearchText() std::vector<SEARCH_TERM> LIB_SYMBOL::GetSearchTerms()
{ {
// Matches are scored by offset from front of string, so inclusion of this spacer std::vector<SEARCH_TERM> terms;
// discounts matches found after it.
static const wxString discount( wxT( " " ) );
wxString text = GetKeyWords() + discount + GetDescription(); terms.emplace_back( SEARCH_TERM( GetName(), 8 ) );
wxString footprint = GetFootprintField().GetText();
if( !footprint.IsEmpty() ) wxStringTokenizer keywordTokenizer( GetKeyWords(), wxS( " " ), wxTOKEN_STRTOK );
{
text += discount + footprint; while( keywordTokenizer.HasMoreTokens() )
} terms.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
// TODO(JE) rework this later so we can highlight matches in their column // TODO(JE) rework this later so we can highlight matches in their column
std::map<wxString, wxString> fields; std::map<wxString, wxString> fields;
GetChooserFields( fields ); GetChooserFields( fields );
for( const auto& it : fields ) for( const auto& [ name, text ] : fields )
text += discount + it.second; terms.emplace_back( SEARCH_TERM( text, 4 ) );
return text; // Also include keywords as one long string, just in case
terms.emplace_back( SEARCH_TERM( GetKeyWords(), 1 ) );
terms.emplace_back( SEARCH_TERM( GetDescription(), 1 ) );
wxString footprint = GetFootprintField().GetText();
if( !footprint.IsEmpty() )
terms.emplace_back( SEARCH_TERM( GetFootprintField().GetText(), 1 ) );
return terms;
} }
@ -77,7 +83,7 @@ void LIB_SYMBOL::GetChooserFields( std::map<wxString , wxString>& aColumnMap )
LIB_FIELD* field = static_cast<LIB_FIELD*>( &item ); LIB_FIELD* field = static_cast<LIB_FIELD*>( &item );
if( field->ShowInChooser() ) if( field->ShowInChooser() )
aColumnMap[field->GetName()] = field->EDA_TEXT::GetShownText(); aColumnMap[field->GetName()] = field->EDA_TEXT::GetShownText( false );
} }
} }

View File

@ -177,7 +177,7 @@ public:
return m_keyWords; return m_keyWords;
} }
wxString GetSearchText() override; std::vector<SEARCH_TERM> GetSearchTerms() override;
wxString GetFootprint() override wxString GetFootprint() override
{ {

View File

@ -152,7 +152,7 @@ SCH_SHEET* SCH_LEGACY_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchem
if( m_path.IsEmpty() ) if( m_path.IsEmpty() )
m_path = aSchematic->Prj().GetProjectPath(); m_path = aSchematic->Prj().GetProjectPath();
wxLogTrace( traceSchLegacyPlugin, "m_Normalized append path \"%s\".", m_path ); wxLogTrace( traceSchLegacyPlugin, "Normalized append path \"%s\".", m_path );
} }
else else
{ {

View File

@ -142,6 +142,7 @@ SYMBOL_EDIT_FRAME::SYMBOL_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
SyncLibraries( false, loadingCancelled ); SyncLibraries( false, loadingCancelled );
m_treePane = new SYMBOL_TREE_PANE( this, m_libMgr ); m_treePane = new SYMBOL_TREE_PANE( this, m_libMgr );
m_treePane->GetLibTree()->SetSortMode( (LIB_TREE_MODEL_ADAPTER::SORT_MODE) m_settings->m_LibrarySortMode );
resolveCanvasType(); resolveCanvasType();
SwitchCanvas( m_canvasType ); SwitchCanvas( m_canvasType );
@ -304,6 +305,8 @@ void SYMBOL_EDIT_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
m_settings->m_ShowPinElectricalType = GetRenderSettings()->m_ShowPinsElectricalType; m_settings->m_ShowPinElectricalType = GetRenderSettings()->m_ShowPinsElectricalType;
m_settings->m_LibWidth = m_treePane->GetSize().x; m_settings->m_LibWidth = m_treePane->GetSize().x;
m_settings->m_LibrarySortMode = m_treePane->GetLibTree()->GetSortMode();
} }

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -73,6 +73,9 @@ SYMBOL_EDITOR_SETTINGS::SYMBOL_EDITOR_SETTINGS() :
m_params.emplace_back( new PARAM<int>( "lib_table_width", m_params.emplace_back( new PARAM<int>( "lib_table_width",
&m_LibWidth, 250 ) ); &m_LibWidth, 250 ) );
m_params.emplace_back( new PARAM<int>( "library.sort_mode",
&m_LibrarySortMode, 0 ) );
m_params.emplace_back( new PARAM<wxString>( "edit_symbol_visible_columns", m_params.emplace_back( new PARAM<wxString>( "edit_symbol_visible_columns",
&m_EditSymbolVisibleColumns, "0 1 2 3 4 5 6 7" ) ); &m_EditSymbolVisibleColumns, "0 1 2 3 4 5 6 7" ) );

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -60,6 +60,8 @@ public:
int m_LibWidth; int m_LibWidth;
int m_LibrarySortMode;
wxString m_EditSymbolVisibleColumns; wxString m_EditSymbolVisibleColumns;
wxString m_PinTableVisibleColumns; wxString m_PinTableVisibleColumns;

View File

@ -795,16 +795,16 @@ bool SYMBOL_VIEWER_FRAME::ReCreateSymbolList()
while( tokenizer.HasMoreTokens() ) while( tokenizer.HasMoreTokens() )
{ {
const wxString term = tokenizer.GetNextToken().Lower(); const wxString filterTerm = tokenizer.GetNextToken().Lower();
EDA_COMBINED_MATCHER matcher( term, CTX_LIBITEM ); EDA_COMBINED_MATCHER matcher( filterTerm, CTX_LIBITEM );
for( LIB_SYMBOL* symbol : symbols ) for( LIB_SYMBOL* symbol : symbols )
{ {
wxString search = symbol->GetName() + wxS( " " ) + symbol->GetSearchText(); std::vector<SEARCH_TERM> searchTerms = symbol->GetSearchTerms();
bool matched = matcher.Find( search.Lower() ); int matched = matcher.ScoreTerms( searchTerms );
if( !matched && term.IsNumber() ) if( filterTerm.IsNumber() && wxAtoi( filterTerm ) == (int)symbol->GetPinCount() )
matched = ( wxAtoi( term ) == (int)symbol->GetPinCount() ); matched++;
if( !matched ) if( !matched )
excludes.insert( symbol->GetName() ); excludes.insert( symbol->GetName() );

View File

@ -37,6 +37,26 @@
static const int EDA_PATTERN_NOT_FOUND = wxNOT_FOUND; static const int EDA_PATTERN_NOT_FOUND = wxNOT_FOUND;
/*
* A structure for storing weighted search terms.
*
* NOTE: an exact match is scored at 8 * Score while a match at the start of the text is scored
* at 2 * Score.
*/
struct SEARCH_TERM
{
SEARCH_TERM( const wxString& aText, int aScore ) :
Text( aText ),
Score( aScore ),
Normalized( false )
{}
wxString Text;
int Score;
bool Normalized;
};
/* /*
* Interface for a pattern matcher, for which there are several implementations * Interface for a pattern matcher, for which there are several implementations
*/ */
@ -205,6 +225,8 @@ public:
const wxString& GetPattern() const; const wxString& GetPattern() const;
int ScoreTerms( std::vector<SEARCH_TERM>& aWeightedTerms );
private: private:
// Add matcher if it can compile the pattern. // Add matcher if it can compile the pattern.
void AddMatcher( const wxString& aPattern, std::unique_ptr<EDA_PATTERN_MATCH> aMatcher ); void AddMatcher( const wxString& aPattern, std::unique_ptr<EDA_PATTERN_MATCH> aMatcher );

View File

@ -64,6 +64,8 @@ public:
AUI_PANELS m_AuiPanels; AUI_PANELS m_AuiPanels;
int m_LibrarySortMode;
USER_GRID m_UserGrid; USER_GRID m_UserGrid;
bool m_PolarCoords; bool m_PolarCoords;

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2011 Jean-Pierre Charras, <jp.charras@wanadoo.fr> * Copyright (C) 2011 Jean-Pierre Charras, <jp.charras@wanadoo.fr>
* Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -89,14 +89,7 @@ public:
return m_keywords; return m_keywords;
} }
wxString GetSearchText() override std::vector<SEARCH_TERM> GetSearchTerms() override;
{
// Matches are scored by offset from front of string, so inclusion of this spacer
// discounts matches found after it.
static const wxString discount( wxT( " " ) );
return GetKeywords() + discount + GetDescription();
}
unsigned GetPadCount() unsigned GetPadCount()
{ {

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2018 KiCad Developers, see change_log.txt for contributors. * Copyright (C) 2018-2023 KiCad Developers, see change_log.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -28,6 +28,7 @@
#include <map> #include <map>
#include <lib_id.h> #include <lib_id.h>
#include <import_export.h> #include <import_export.h>
#include <eda_pattern_match.h>
/** /**
* A mix-in to provide polymorphism between items stored in libraries (symbols, aliases * A mix-in to provide polymorphism between items stored in libraries (symbols, aliases
@ -55,7 +56,7 @@ public:
*/ */
virtual void GetChooserFields( std::map<wxString , wxString>& aColumnMap ) {} virtual void GetChooserFields( std::map<wxString , wxString>& aColumnMap ) {}
virtual wxString GetSearchText() { return wxEmptyString; } virtual std::vector<SEARCH_TERM> GetSearchTerms() { return std::vector<SEARCH_TERM>(); }
/** /**
* For items having aliases, IsRoot() indicates the principal item. * For items having aliases, IsRoot() indicates the principal item.

View File

@ -3,7 +3,7 @@
* *
* Copyright (C) 2017 Chris Pavlina <pavlina.chris@gmail.com> * Copyright (C) 2017 Chris Pavlina <pavlina.chris@gmail.com>
* Copyright (C) 2014 Henner Zeller <h.zeller@acm.org> * Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
* Copyright (C) 2014-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2014-2023 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the
@ -26,12 +26,10 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <wx/string.h> #include <wx/string.h>
#include <eda_pattern_match.h>
#include <lib_tree_item.h> #include <lib_tree_item.h>
class EDA_COMBINED_MATCHER;
/** /**
* Model class in the component selector Model-View-Adapter (mediated MVC) * Model class in the component selector Model-View-Adapter (mediated MVC)
* architecture. The other pieces are in: * architecture. The other pieces are in:
@ -98,18 +96,24 @@ public:
/** /**
* Sort child nodes quickly and recursively (IntrinsicRanks must have been set). * Sort child nodes quickly and recursively (IntrinsicRanks must have been set).
*/ */
void SortNodes(); void SortNodes( bool aUseScores );
/** /**
* Compare two nodes. Returns true if aNode1 < aNode2. * Compare two nodes. Returns true if aNode1 < aNode2.
*/ */
static bool Compare( LIB_TREE_NODE const& aNode1, LIB_TREE_NODE const& aNode2 ); static bool Compare( LIB_TREE_NODE const& aNode1, LIB_TREE_NODE const& aNode2,
bool aUseScores );
LIB_TREE_NODE(); LIB_TREE_NODE();
virtual ~LIB_TREE_NODE() {} virtual ~LIB_TREE_NODE() {}
enum TYPE { enum TYPE
ROOT, LIB, LIBID, UNIT, INVALID {
ROOT,
LIB,
LIBID,
UNIT,
INVALID
}; };
typedef std::vector<std::unique_ptr<LIB_TREE_NODE>> PTR_VECTOR; typedef std::vector<std::unique_ptr<LIB_TREE_NODE>> PTR_VECTOR;
@ -131,12 +135,9 @@ public:
wxString m_Name; // Actual name of the part wxString m_Name; // Actual name of the part
wxString m_Desc; // Description to be displayed wxString m_Desc; // Description to be displayed
wxString m_Footprint; // Footprint ID as a string (ie: the footprint field text) wxString m_Footprint; // Footprint ID as a string (ie: the footprint field text)
wxString m_MatchName; // Normalized name for matching
wxString m_SearchText; // Descriptive text to search
bool m_Normalized; // Support for lazy normalization.
/// @see LIB_TREE_ITEMS::GetChooserFields std::vector<SEARCH_TERM> m_SearchTerms; /// List of weighted search terms
std::map<wxString, wxString> m_Fields; std::map<wxString, wxString> m_Fields; /// @see LIB_TREE_ITEMS::GetChooserFields
LIB_ID m_LibId; // LIB_ID determined by the parent library nickname and alias name. LIB_ID m_LibId; // LIB_ID determined by the parent library nickname and alias name.
int m_Unit; // Actual unit, or zero int m_Unit; // Actual unit, or zero

View File

@ -3,7 +3,7 @@
* *
* Copyright (C) 2017 Chris Pavlina <pavlina.chris@gmail.com> * Copyright (C) 2017 Chris Pavlina <pavlina.chris@gmail.com>
* Copyright (C) 2014 Henner Zeller <h.zeller@acm.org> * Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
* Copyright (C) 2014-2021 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2014-2023 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the
@ -133,6 +133,11 @@ public:
NUM_COLS ///< The number of default tree columns NUM_COLS ///< The number of default tree columns
}; };
enum SORT_MODE
{
BEST_MATCH = 0,
ALPHABETIC
};
/** /**
* Save the column widths to the config file. This requires the tree view to still be * Save the column widths to the config file. This requires the tree view to still be
@ -152,6 +157,9 @@ public:
*/ */
SYM_FILTER_TYPE GetFilter() const { return m_filter; } SYM_FILTER_TYPE GetFilter() const { return m_filter; }
void SetSortMode( SORT_MODE aMode ) { m_sort_mode = aMode; }
SORT_MODE GetSortMode() const { return m_sort_mode; }
/** /**
* Whether or not to show units. May be set at any time; updates at the next * Whether or not to show units. May be set at any time; updates at the next
* UpdateSearchString() * UpdateSearchString()
@ -415,6 +423,7 @@ private:
EDA_BASE_FRAME* m_parent; EDA_BASE_FRAME* m_parent;
SYM_FILTER_TYPE m_filter; SYM_FILTER_TYPE m_filter;
SORT_MODE m_sort_mode;
bool m_show_units; bool m_show_units;
LIB_ID m_preselect_lib_id; LIB_ID m_preselect_lib_id;
int m_preselect_unit; int m_preselect_unit;

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2014 Henner Zeller <h.zeller@acm.org> * Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
* Copyright (C) 2014-2022 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2014-2023 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -35,6 +35,7 @@ class wxHtmlLinkEvent;
class wxSearchCtrl; class wxSearchCtrl;
class wxTimer; class wxTimer;
class wxTimerEvent; class wxTimerEvent;
class STD_BITMAP_BUTTON;
class ACTION_MENU; class ACTION_MENU;
class LIB_ID; class LIB_ID;
class LIB_TABLE; class LIB_TABLE;
@ -127,6 +128,12 @@ public:
void SetSearchString( const wxString& aSearchString ); void SetSearchString( const wxString& aSearchString );
wxString GetSearchString() const; wxString GetSearchString() const;
/**
* Save/restore the sorting mode.
*/
void SetSortMode( LIB_TREE_MODEL_ADAPTER::SORT_MODE aMode ) { m_adapter->SetSortMode( aMode ); }
LIB_TREE_MODEL_ADAPTER::SORT_MODE GetSortMode() const { return m_adapter->GetSortMode(); }
/** /**
* Regenerate the tree. * Regenerate the tree.
*/ */
@ -214,11 +221,10 @@ protected:
void onDebounceTimer( wxTimerEvent& aEvent ); void onDebounceTimer( wxTimerEvent& aEvent );
protected: protected:
LIB_TABLE* m_lib_table;
wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER> m_adapter; wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER> m_adapter;
wxSearchCtrl* m_query_ctrl; wxSearchCtrl* m_query_ctrl;
STD_BITMAP_BUTTON* m_sort_ctrl;
WX_DATAVIEWCTRL* m_tree_ctrl; WX_DATAVIEWCTRL* m_tree_ctrl;
HTML_WINDOW* m_details_ctrl; HTML_WINDOW* m_details_ctrl;
wxTimer* m_debounceTimer; wxTimer* m_debounceTimer;

View File

@ -119,8 +119,8 @@ DIALOG_CHOOSE_FOOTPRINT::DIALOG_CHOOSE_FOOTPRINT( PCB_BASE_FRAME* aParent,
Layout(); Layout();
auto cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>(); if( PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>() )
{
// We specify the width of the right window (m_symbol_view_panel), because specify // We specify the width of the right window (m_symbol_view_panel), because specify
// the width of the left window does not work as expected when SetSashGravity() is called // the width of the left window does not work as expected when SetSashGravity() is called
if( cfg->m_FootprintChooser.sash_h < 0 ) if( cfg->m_FootprintChooser.sash_h < 0 )
@ -140,6 +140,9 @@ DIALOG_CHOOSE_FOOTPRINT::DIALOG_CHOOSE_FOOTPRINT( PCB_BASE_FRAME* aParent,
horizPixelsFromDU( 340 ) : cfg->m_FootprintChooser.height; horizPixelsFromDU( 340 ) : cfg->m_FootprintChooser.height;
SetSize( wxSize( w, h ) ); SetSize( wxSize( w, h ) );
aAdapter->SetSortMode( (LIB_TREE_MODEL_ADAPTER::SORT_MODE) cfg->m_FootprintChooser.sort_mode );
}
SetInitialFocus( m_tree->GetFocusTarget() ); SetInitialFocus( m_tree->GetFocusTarget() );
} }
@ -157,14 +160,17 @@ DIALOG_CHOOSE_FOOTPRINT::~DIALOG_CHOOSE_FOOTPRINT()
m_dbl_click_timer->Stop(); m_dbl_click_timer->Stop();
delete m_dbl_click_timer; delete m_dbl_click_timer;
auto cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>(); if( PCBNEW_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>() )
{
cfg->m_FootprintChooser.width = GetSize().x; cfg->m_FootprintChooser.width = GetSize().x;
cfg->m_FootprintChooser.height = GetSize().y; cfg->m_FootprintChooser.height = GetSize().y;
cfg->m_FootprintChooser.sash_h = m_hsplitter->GetSashPosition(); cfg->m_FootprintChooser.sash_h = m_hsplitter->GetSashPosition();
if( m_vsplitter ) if( m_vsplitter )
cfg->m_FootprintChooser.sash_v = m_vsplitter->GetSashPosition(); cfg->m_FootprintChooser.sash_v = m_vsplitter->GetSashPosition();
cfg->m_FootprintChooser.sort_mode = m_tree->GetSortMode();
}
} }

View File

@ -626,6 +626,8 @@ void FOOTPRINT_EDIT_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
GetToolManager()->GetTool<PCB_SELECTION_TOOL>()->GetFilter() = cfg->m_SelectionFilter; GetToolManager()->GetTool<PCB_SELECTION_TOOL>()->GetFilter() = cfg->m_SelectionFilter;
m_selectionFilterPanel->SetCheckboxesFromFilter( cfg->m_SelectionFilter ); m_selectionFilterPanel->SetCheckboxesFromFilter( cfg->m_SelectionFilter );
m_treePane->GetLibTree()->SetSortMode( (LIB_TREE_MODEL_ADAPTER::SORT_MODE) cfg->m_LibrarySortMode );
} }
} }
@ -658,6 +660,8 @@ void FOOTPRINT_EDIT_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
cfg->m_AuiPanels.properties_splitter_proportion = cfg->m_AuiPanels.properties_splitter_proportion =
m_propertiesPanel->SplitterProportion(); m_propertiesPanel->SplitterProportion();
cfg->m_LibrarySortMode = m_treePane->GetLibTree()->GetSortMode();
} }
} }

View File

@ -80,6 +80,9 @@ FOOTPRINT_EDITOR_SETTINGS::FOOTPRINT_EDITOR_SETTINGS() :
m_params.emplace_back( new PARAM<bool>( "aui.show_properties", m_params.emplace_back( new PARAM<bool>( "aui.show_properties",
&m_AuiPanels.show_properties, false ) ); &m_AuiPanels.show_properties, false ) );
m_params.emplace_back( new PARAM<int>( "library.sort_mode",
&m_LibrarySortMode, 0 ) );
m_params.emplace_back( new PARAM<wxString>( "system.last_import_export_path", m_params.emplace_back( new PARAM<wxString>( "system.last_import_export_path",
&m_LastImportExportPath, "" ) ); &m_LastImportExportPath, "" ) );

View File

@ -567,16 +567,16 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList()
while( tokenizer.HasMoreTokens() ) while( tokenizer.HasMoreTokens() )
{ {
const wxString term = tokenizer.GetNextToken().Lower(); const wxString filterTerm = tokenizer.GetNextToken().Lower();
EDA_COMBINED_MATCHER matcher( term, CTX_LIBITEM ); EDA_COMBINED_MATCHER matcher( filterTerm, CTX_LIBITEM );
for( const std::unique_ptr<FOOTPRINT_INFO>& footprint : fp_info_list->GetList() ) for( const std::unique_ptr<FOOTPRINT_INFO>& footprint : fp_info_list->GetList() )
{ {
wxString search = footprint->GetFootprintName() + wxS( " " ) + footprint->GetSearchText(); std::vector<SEARCH_TERM> searchTerms = footprint->GetSearchTerms();
bool matched = matcher.Find( search.Lower() ); int matched = matcher.ScoreTerms( searchTerms );
if( !matched && term.IsNumber() ) if( filterTerm.IsNumber() && wxAtoi( filterTerm ) == (int)footprint->GetPadCount() )
matched = ( wxAtoi( term ) == (int)footprint->GetPadCount() ); matched++;
if( !matched ) if( !matched )
excludes.insert( footprint->GetFootprintName() ); excludes.insert( footprint->GetFootprintName() );

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -130,6 +130,9 @@ PCBNEW_SETTINGS::PCBNEW_SETTINGS()
m_params.emplace_back( new PARAM<int>( "footprint_chooser.sash_v", m_params.emplace_back( new PARAM<int>( "footprint_chooser.sash_v",
&m_FootprintChooser.sash_v, -1 ) ); &m_FootprintChooser.sash_v, -1 ) );
m_params.emplace_back( new PARAM<int>( "footprint_chooser.sort_mode",
&m_FootprintChooser.sort_mode, 0 ) );
m_params.emplace_back( new PARAM<bool>( "editing.flip_left_right", m_params.emplace_back( new PARAM<bool>( "editing.flip_left_right",
&m_FlipLeftRight, true ) ); &m_FlipLeftRight, true ) );

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 2020 Jon Evans <jon@craftyjon.com> * Copyright (C) 2020 Jon Evans <jon@craftyjon.com>
* Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software: you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the
@ -296,6 +296,7 @@ public:
int height; int height;
int sash_h; int sash_h;
int sash_v; int sash_v;
int sort_mode;
}; };
struct ZONES struct ZONES