From e294fe2074b000f6b37f449f236dba04d7afd2aa Mon Sep 17 00:00:00 2001 From: Jon Evans Date: Sun, 28 Aug 2022 19:02:12 -0400 Subject: [PATCH] ADDED: Dynamic field columns in symbol chooser CHANGED: Symbol chooser search now considers custom symbol fields Visible columns can be controlled in database libraries. In standard KiCad libraries, we show columns for all custom fields for now. Customizable column visibility will be added in the future. Fixes https://gitlab.com/kicad/code/kicad/-/issues/11946 --- common/lib_tree_model.cpp | 4 + common/lib_tree_model_adapter.cpp | 183 +++++++++++++----- common/settings/app_settings.cpp | 43 +++- eeschema/eeschema_settings.cpp | 9 +- eeschema/lib_field.cpp | 3 + eeschema/lib_field.h | 4 + eeschema/lib_symbol.cpp | 19 ++ eeschema/lib_symbol.h | 4 +- eeschema/sch_io_mgr.h | 28 +++ .../database/sch_database_plugin.cpp | 18 ++ .../database/sch_database_plugin.h | 8 + .../sch_plugins/kicad/sch_sexpr_plugin.cpp | 35 ++++ eeschema/sch_plugins/kicad/sch_sexpr_plugin.h | 3 + .../symbol_editor/symbol_editor_settings.cpp | 9 +- eeschema/symbol_lib_table.h | 18 ++ eeschema/symbol_tree_model_adapter.cpp | 21 ++ include/lib_tree_item.h | 8 +- include/lib_tree_model.h | 3 + include/lib_tree_model_adapter.h | 25 ++- include/settings/app_settings.h | 8 +- pcbnew/footprint_editor_settings.cpp | 9 +- pcbnew/pcbnew_settings.cpp | 9 +- 22 files changed, 410 insertions(+), 61 deletions(-) diff --git a/common/lib_tree_model.cpp b/common/lib_tree_model.cpp index 6c8f64e4b0..f7691a855b 100644 --- a/common/lib_tree_model.cpp +++ b/common/lib_tree_model.cpp @@ -175,6 +175,8 @@ LIB_TREE_NODE_LIB_ID::LIB_TREE_NODE_LIB_ID( LIB_TREE_NODE* aParent, LIB_TREE_ITE m_Desc = aItem->GetDescription(); m_Footprint = aItem->GetFootprint(); + aItem->GetChooserFields( m_Fields ); + m_MatchName = aItem->GetName(); m_SearchText = aItem->GetSearchText(); m_Normalized = false; @@ -206,6 +208,8 @@ void LIB_TREE_NODE_LIB_ID::Update( LIB_TREE_ITEM* aItem ) m_Desc = aItem->GetDescription(); m_MatchName = aItem->GetName(); + aItem->GetChooserFields( m_Fields ); + m_SearchText = aItem->GetSearchText(); m_Normalized = false; diff --git a/common/lib_tree_model_adapter.cpp b/common/lib_tree_model_adapter.cpp index aa5a0ab19f..265b0b9d38 100644 --- a/common/lib_tree_model_adapter.cpp +++ b/common/lib_tree_model_adapter.cpp @@ -74,16 +74,16 @@ LIB_TREE_MODEL_ADAPTER::LIB_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, m_show_units( true ), m_preselect_unit( 0 ), m_freeze( 0 ), - m_col_part( nullptr ), - m_col_desc( nullptr ), m_widget( nullptr ) { - // Default column widths - m_colWidths[NAME_COL] = 300; - m_colWidths[DESC_COL] = 2000; + // Default column widths. Do not translate these names. + m_colWidths[ wxT( "Item" ) ] = 300; + m_colWidths[ wxT( "Description" ) ] = 600; APP_SETTINGS_BASE* cfg = Kiface().KifaceSettings(); - m_colWidths[NAME_COL] = cfg->m_LibTree.column_width; + + for( const std::pair& pair : cfg->m_LibTree.column_widths ) + m_colWidths[pair.first] = pair.second; } @@ -96,7 +96,16 @@ void LIB_TREE_MODEL_ADAPTER::SaveColWidths() if( m_widget ) { APP_SETTINGS_BASE* cfg = Kiface().KifaceSettings(); - cfg->m_LibTree.column_width = m_widget->GetColumn( NAME_COL )->GetWidth(); + + cfg->m_LibTree.columns.clear(); + cfg->m_LibTree.column_widths.clear(); + + // TODO(JE) ordering? + for( const std::pair& pair : m_colNameMap ) + { + cfg->m_LibTree.columns.emplace_back( pair.first ); + cfg->m_LibTree.column_widths[pair.first] = pair.second->GetWidth(); + } } } @@ -236,30 +245,59 @@ void LIB_TREE_MODEL_ADAPTER::UpdateSearchString( const wxString& aSearch, bool a void LIB_TREE_MODEL_ADAPTER::AttachTo( wxDataViewCtrl* aDataViewCtrl ) { - wxString itemHead = _( "Item" ); - wxString descHead = _( "Description" ); - - // The extent of the text doesn't take into account the space on either side - // in the header, so artificially pad it - wxSize itemHeadMinWidth = KIUI::GetTextSize( itemHead + wxT( "MMM" ), aDataViewCtrl ); - wxSize descHeadMinWidth = KIUI::GetTextSize( descHead + wxT( "MMM" ), aDataViewCtrl ); - - // Ensure the part column is wider than the smallest allowable width - if( m_colWidths[NAME_COL] < itemHeadMinWidth.x ) - m_colWidths[NAME_COL] = itemHeadMinWidth.x; - m_widget = aDataViewCtrl; aDataViewCtrl->SetIndent( kDataViewIndent ); aDataViewCtrl->AssociateModel( this ); aDataViewCtrl->ClearColumns(); - m_col_part = aDataViewCtrl->AppendTextColumn( itemHead, NAME_COL, wxDATAVIEW_CELL_INERT, - m_colWidths[NAME_COL] ); - m_col_desc = aDataViewCtrl->AppendTextColumn( descHead, DESC_COL, wxDATAVIEW_CELL_INERT, - m_colWidths[DESC_COL] ); + // These two columns are always added; other columns may be added by specific libraries. + // Do not use translated names here. + doAddColumn( wxT( "Item" ) ); - m_col_part->SetMinWidth( itemHeadMinWidth.x ); - m_col_desc->SetMinWidth( descHeadMinWidth.x ); + // TODO(JE) make Description optional + doAddColumn( wxT( "Description" ) ); + + for( auto& it : m_colNameMap ) + { + if( !it.second ) + doAddColumn( it.first, false ); + } +} + + +wxDataViewColumn* LIB_TREE_MODEL_ADAPTER::doAddColumn( const wxString& aHeader, bool aTranslate ) +{ + wxString translatedHeader = aTranslate ? wxGetTranslation( aHeader ) : aHeader; + + // The extent of the text doesn't take into account the space on either side + // in the header, so artificially pad it + wxSize headerMinWidth = KIUI::GetTextSize( translatedHeader + wxT( "MMM" ), m_widget ); + + if( !m_colWidths.count( aHeader ) || m_colWidths[aHeader] < headerMinWidth.x ) + m_colWidths[aHeader] = headerMinWidth.x; + + int index = m_columns.size(); + + wxDataViewColumn* ret = m_widget->AppendTextColumn( translatedHeader, index, + wxDATAVIEW_CELL_INERT, + m_colWidths[aHeader] ); + ret->SetMinWidth( headerMinWidth.x ); + + m_columns.emplace_back( ret ); + m_colNameMap[aHeader] = ret; + m_colIdxMap[m_columns.size() - 1] = aHeader; + + return ret; +} + + +void LIB_TREE_MODEL_ADAPTER::addColumnIfNecessary( const wxString& aHeader ) +{ + if( m_colNameMap.count( aHeader ) ) + return; + + // Columns will be created later + m_colNameMap[aHeader] = nullptr; } @@ -352,19 +390,46 @@ unsigned int LIB_TREE_MODEL_ADAPTER::GetChildren( const wxDataViewItem& aItem, void LIB_TREE_MODEL_ADAPTER::FinishTreeInitialization() { - m_col_part->SetWidth( m_colWidths[NAME_COL] ); - m_col_desc->SetWidth( m_colWidths[DESC_COL] ); + wxDataViewColumn* col = nullptr; + size_t idx = 0; + int totalWidth = 0; + wxString header; + + for( ; idx < m_columns.size() - 1; idx++ ) + { + col = m_columns[idx]; + header = col->GetTitle(); + + wxASSERT( m_colWidths.count( header ) ); + + col->SetWidth( m_colWidths[header] ); + totalWidth += col->GetWidth(); + } + + int remainingWidth = m_widget->GetSize().x - totalWidth; + header = m_columns[idx]->GetTitle(); + + m_columns[idx]->SetWidth( std::max( m_colWidths[header], remainingWidth ) ); } void LIB_TREE_MODEL_ADAPTER::OnSize( wxSizeEvent& aEvent ) { - // On GTK, this value in not immediately available, so don't - // set it to zero just because we haven't fully initialized - if( m_col_part->GetWidth() > 0 ) - m_colWidths[NAME_COL] = m_col_part->GetWidth(); + for( auto& it : m_colNameMap ) + { + if( it.second == m_columns[0] ) + { + // On GTK, this value in not immediately available, so don't + // set it to zero just because we haven't fully initialized + if( it.second->GetWidth() > 0 ) + m_colWidths[it.first] = it.second->GetWidth(); - m_col_desc->SetWidth( m_colWidths[DESC_COL] ); + continue; + } + + wxASSERT( m_colWidths.count( it.first ) ); + it.second->SetWidth( m_colWidths[it.first] ); + } // Mandatory in any wxSizeEvent handler: aEvent.Skip(); @@ -378,22 +443,40 @@ void LIB_TREE_MODEL_ADAPTER::RefreshTree() // user's scroll position (which re-attaching or deleting/re-inserting columns does). static int walk = 1; - int partWidth = m_col_part->GetWidth(); - int descWidth = m_col_desc->GetWidth(); + std::vector widths; + + for( const wxDataViewColumn* col : m_columns ) + widths.emplace_back( col->GetWidth() ); + + wxASSERT( widths.size() ); // Only use the widths read back if they are non-zero. // GTK returns the displayed width of the column, which is not calculated immediately - if( descWidth > 0 ) + if( widths[0] > 0 ) { - m_colWidths[NAME_COL] = partWidth; - m_colWidths[DESC_COL] = descWidth; + size_t i = 0; + + for( auto& it : m_colNameMap ) + m_colWidths[it.first] = widths[i++]; } - m_colWidths[NAME_COL] += walk; - m_colWidths[DESC_COL] -= walk; + auto colIt = m_colWidths.begin(); + + colIt->second += walk; + colIt++; + + if( colIt != m_colWidths.end() ) + colIt->second -= walk; + + for( auto& it : m_colNameMap ) + { + if( it.second == m_columns[0] ) + continue; + + wxASSERT( m_colWidths.count( it.first ) ); + it.second->SetWidth( m_colWidths[it.first] ); + } - m_col_part->SetWidth( m_colWidths[NAME_COL] ); - m_col_desc->SetWidth( m_colWidths[DESC_COL] ); walk = -walk; } @@ -443,13 +526,25 @@ void LIB_TREE_MODEL_ADAPTER::GetValue( wxVariant& aVariant, switch( aCol ) { - default: // column == -1 is used for default Compare function - case 0: + case NAME_COL: aVariant = UnescapeString( node->m_Name ); break; - case 1: + + case DESC_COL: aVariant = node->m_Desc; break; + + default: + { + wxCHECK_RET( m_colIdxMap.count( aCol ), wxT( "Invalid column in LIB_TREE_MODEL_ADAPTER" ) ); + + if( node->m_Fields.count( m_colIdxMap.at( aCol ) ) ) + aVariant = node->m_Fields[m_colIdxMap.at( aCol )]; + else + aVariant = wxEmptyString; + + break; + } } } diff --git a/common/settings/app_settings.cpp b/common/settings/app_settings.cpp index 5dadb5738f..914564eb32 100644 --- a/common/settings/app_settings.cpp +++ b/common/settings/app_settings.cpp @@ -42,7 +42,6 @@ APP_SETTINGS_BASE::APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaV m_appSettingsSchemaVersion( aSchemaVersion ) { // Make Coverity happy: - m_LibTree.column_width = 300; m_Graphics.canvas_type = EDA_DRAW_PANEL_GAL::GAL_FALLBACK; // Build parameters list: @@ -79,8 +78,32 @@ APP_SETTINGS_BASE::APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaV m_params.emplace_back( new PARAM( "color_picker.default_tab", &m_ColorPicker.default_tab, 0 ) ); - m_params.emplace_back( new PARAM( "lib_tree.column_width", - &m_LibTree.column_width, 300 ) ); + m_params.emplace_back( new PARAM_LAMBDA( "lib_tree.column_widths", + [&]() -> nlohmann::json + { + nlohmann::json ret = {}; + + for( const std::pair& pair : m_LibTree.column_widths ) + ret[std::string( pair.first.ToUTF8() )] = pair.second; + + return ret; + }, + [&]( const nlohmann::json& aJson ) + { + if( !aJson.is_object() ) + return; + + m_LibTree.column_widths.clear(); + + for( const auto& entry : aJson.items() ) + { + if( !entry.value().is_number_integer() ) + continue; + + m_LibTree.column_widths[ entry.key() ] = entry.value().get(); + } + }, + {} ) ); m_params.emplace_back( new PARAM( "printing.background", &m_Printing.background, false ) ); @@ -363,3 +386,17 @@ const std::vector APP_SETTINGS_BASE::DefaultGridSizeList() const "0.025 mm", "0.01 mm" }; } + + +bool APP_SETTINGS_BASE::migrateLibTreeWidth() +{ + // We used to store only the width of the first column, because there were only + // two possible columns. + if( std::optional optWidth = Get( "lib_tree.column_width" ) ) + { + Set( "lib_tree.column_widths", { { "Item", *optWidth } } ); + At( "lib_tree" ).erase( "column_width" ); + } + + return true; +} diff --git a/eeschema/eeschema_settings.cpp b/eeschema/eeschema_settings.cpp index cdecd00d60..a23e7b4bab 100644 --- a/eeschema/eeschema_settings.cpp +++ b/eeschema/eeschema_settings.cpp @@ -40,7 +40,7 @@ using namespace T_BOMCFG_T; // for the BOM_CFG_PARSER parser and its keywords ///! Update the schema version whenever a migration is required -const int eeschemaSchemaVersion = 2; +const int eeschemaSchemaVersion = 3; /// Default value for bom.plugins const nlohmann::json defaultBomPlugins = @@ -475,6 +475,13 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() : return true; } ); + + registerMigration( 2, 3, + [&]() -> bool + { + // This is actually a migration for APP_SETTINGS_BASE::m_LibTree + return migrateLibTreeWidth(); + } ); } diff --git a/eeschema/lib_field.cpp b/eeschema/lib_field.cpp index 05a2d0abf3..76eae1c16b 100644 --- a/eeschema/lib_field.cpp +++ b/eeschema/lib_field.cpp @@ -77,6 +77,7 @@ LIB_FIELD& LIB_FIELD::operator=( const LIB_FIELD& field ) m_autoAdded = field.m_autoAdded; m_showName = field.m_showName; m_allowAutoPlace = field.m_allowAutoPlace; + m_showInChooser = field.m_showInChooser; SetText( field.GetText() ); SetAttributes( field ); @@ -105,6 +106,7 @@ void LIB_FIELD::Init( int aId ) m_autoAdded = false; m_showName = false; m_allowAutoPlace = true; + m_showInChooser = true; } @@ -199,6 +201,7 @@ void LIB_FIELD::Copy( LIB_FIELD* aTarget ) const aTarget->m_name = m_name; aTarget->m_showName = m_showName; aTarget->m_allowAutoPlace = m_allowAutoPlace; + aTarget->m_showInChooser = m_showInChooser; aTarget->CopyText( *this ); aTarget->SetAttributes( *this ); diff --git a/eeschema/lib_field.h b/eeschema/lib_field.h index ad8e7f1b8f..964d7d4c62 100644 --- a/eeschema/lib_field.h +++ b/eeschema/lib_field.h @@ -184,6 +184,9 @@ public: bool CanAutoplace() const { return m_allowAutoPlace; } void SetCanAutoplace( bool aCanPlace ) { m_allowAutoPlace = aCanPlace; } + bool ShowInChooser() const { return m_showInChooser; } + void SetShowInChooser( bool aShow = true ) { m_showInChooser = aShow; } + private: /** @@ -223,6 +226,7 @@ private: bool m_autoAdded; ///< Was this field automatically added to a LIB_SYMBOL? bool m_showName; ///< Render the field's name in addition to its value bool m_allowAutoPlace; ///< This field can be autoplaced when converted to a SCH_FIELD + bool m_showInChooser; ///< This field is available as a data column for the chooser }; #endif // CLASS_LIBENTRY_FIELDS_H diff --git a/eeschema/lib_symbol.cpp b/eeschema/lib_symbol.cpp index 270cc464dc..cdfea0c3ff 100644 --- a/eeschema/lib_symbol.cpp +++ b/eeschema/lib_symbol.cpp @@ -59,10 +59,29 @@ wxString LIB_SYMBOL::GetSearchText() text += discount + footprint; } + // TODO(JE) rework this later so we can highlight matches in their column + std::map fields; + GetChooserFields( fields ); + + for( const auto& it : fields ) + text += discount + it.second; + return text; } +void LIB_SYMBOL::GetChooserFields( std::map& aColumnMap ) +{ + for( LIB_ITEM& item : m_drawings[ LIB_FIELD_T ] ) + { + LIB_FIELD* field = static_cast( &item ); + + if( field->ShowInChooser() ) + aColumnMap[field->GetName()] = field->EDA_TEXT::GetShownText(); + } +} + + bool operator<( const LIB_SYMBOL& aItem1, const LIB_SYMBOL& aItem2 ) { return aItem1.GetName() < aItem2.GetName(); diff --git a/eeschema/lib_symbol.h b/eeschema/lib_symbol.h index eeb23db2c4..a01c2909bf 100644 --- a/eeschema/lib_symbol.h +++ b/eeschema/lib_symbol.h @@ -174,7 +174,9 @@ public: return GetFootprintField().GetText(); } - /** + void GetChooserFields( std::map& aColumnMap ) override; + + /** * For symbols derived from other symbols, IsRoot() indicates no derivation. */ bool IsRoot() const override { return m_parent.use_count() == 0; } diff --git a/eeschema/sch_io_mgr.h b/eeschema/sch_io_mgr.h index a945681ad9..ff5da3b35d 100644 --- a/eeschema/sch_io_mgr.h +++ b/eeschema/sch_io_mgr.h @@ -462,6 +462,34 @@ public: */ virtual void GetSubLibraryNames( std::vector& aNames ) {} + /** + * Retrieves a list of (custom) field names that are present on symbols in this library. + * The plugin is responsible for guaranteeing that this list contains the set of unique + * custom field names present on any symbols contained in the library. + * + * The required KiCad fields are not included in this list. + * + * @param aNames will be filled with any custom fields present in this library. + */ + virtual void GetAvailableSymbolFields( std::vector& aNames ) {} + + /** + * Retrieves a list of (custom) field names that should be shown by default for this library + * in the symbol chooser. This list should be a subset of the result returned by + * GetAvailableSymbolFields(). + * + * The preference for which fields to hide and show for a given library is stored on a + * per-library basis in a user's preferences (or in the project local settings for a project- + * local library). The set of fields returned by GetDefaultSymbolFields() will be used if this + * preference is missing. + * + * @param aNames will be filled with the custom field names that should be shown by default + */ + virtual void GetDefaultSymbolFields( std::vector& aNames ) + { + return GetAvailableSymbolFields( aNames ); + } + /** * Return true if the first line in @a aFileName begins with the expected header. * diff --git a/eeschema/sch_plugins/database/sch_database_plugin.cpp b/eeschema/sch_plugins/database/sch_database_plugin.cpp index 339658e05f..1189811951 100644 --- a/eeschema/sch_plugins/database/sch_database_plugin.cpp +++ b/eeschema/sch_plugins/database/sch_database_plugin.cpp @@ -153,6 +153,19 @@ void SCH_DATABASE_PLUGIN::GetSubLibraryNames( std::vector& aNames ) } +void SCH_DATABASE_PLUGIN::GetAvailableSymbolFields( std::vector& aNames ) +{ + std::copy( m_customFields.begin(), m_customFields.end(), std::back_inserter( aNames ) ); +} + + +void SCH_DATABASE_PLUGIN::GetDefaultSymbolFields( std::vector& aNames ) +{ + std::copy( m_defaultShownFields.begin(), m_defaultShownFields.end(), + std::back_inserter( aNames ) ); +} + + bool SCH_DATABASE_PLUGIN::CheckHeader( const wxString& aFileName ) { // TODO: Implement this sometime; but CheckHeader isn't even called... @@ -352,6 +365,11 @@ LIB_SYMBOL* SCH_DATABASE_PLUGIN::loadSymbolFromRow( const wxString& aSymbolName, field->SetNameShown( mapping.show_name ); symbol->AddField( field ); + + m_customFields.insert( mapping.name ); + + if( mapping.visible_in_chooser ) + m_defaultShownFields.insert( mapping.name ); } return symbol; diff --git a/eeschema/sch_plugins/database/sch_database_plugin.h b/eeschema/sch_plugins/database/sch_database_plugin.h index da16cc0a8d..b542e8dad3 100644 --- a/eeschema/sch_plugins/database/sch_database_plugin.h +++ b/eeschema/sch_plugins/database/sch_database_plugin.h @@ -77,6 +77,10 @@ public: void GetSubLibraryNames( std::vector& aNames ) override; + void GetAvailableSymbolFields( std::vector& aNames ) override; + + void GetDefaultSymbolFields( std::vector& aNames ) override; + bool CheckHeader( const wxString& aFileName ) override; // Database libraries can never be written using the symbol editing API @@ -105,6 +109,10 @@ private: std::unique_ptr m_conn; + std::set m_customFields; + + std::set m_defaultShownFields; + }; #endif //KICAD_SCH_DATABASE_PLUGIN_H diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp index 2cca1d7570..8535dae5cf 100644 --- a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp +++ b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.cpp @@ -1433,6 +1433,41 @@ bool SCH_SEXPR_PLUGIN::IsSymbolLibWritable( const wxString& aLibraryPath ) } +void SCH_SEXPR_PLUGIN::GetAvailableSymbolFields( std::vector& aNames ) +{ + if( !m_cache ) + return; + + const LIB_SYMBOL_MAP& symbols = m_cache->m_symbols; + + std::set fieldNames; + + for( LIB_SYMBOL_MAP::const_iterator it = symbols.begin(); it != symbols.end(); ++it ) + { + std::vector fields; + it->second->GetFields( fields ); + + for( LIB_FIELD* field : fields ) + { + if( field->IsMandatory() ) + continue; + + // TODO(JE): enable configurability of this outside database libraries? + // if( field->ShowInChooser() ) + fieldNames.insert( field->GetName() ); + } + } + + std::copy( fieldNames.begin(), fieldNames.end(), std::back_inserter( aNames ) ); +} + + +void SCH_SEXPR_PLUGIN::GetDefaultSymbolFields( std::vector& aNames ) +{ + GetAvailableSymbolFields( aNames ); +} + + LIB_SYMBOL* SCH_SEXPR_PLUGIN::ParseLibSymbol( LINE_READER& aReader, int aFileVersion ) { LOCALE_IO toggle; // toggles on, then off, the C locale. diff --git a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.h b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.h index bd9ec54071..5b31d92b69 100644 --- a/eeschema/sch_plugins/kicad/sch_sexpr_plugin.h +++ b/eeschema/sch_plugins/kicad/sch_sexpr_plugin.h @@ -133,6 +133,9 @@ public: bool CheckHeader( const wxString& aFileName ) override; bool IsSymbolLibWritable( const wxString& aLibraryPath ) override; + void GetAvailableSymbolFields( std::vector& aNames ) override; + void GetDefaultSymbolFields( std::vector& aNames ) override; + const wxString& GetError() const override { return m_error; } static LIB_SYMBOL* ParseLibSymbol( LINE_READER& aReader, diff --git a/eeschema/symbol_editor/symbol_editor_settings.cpp b/eeschema/symbol_editor/symbol_editor_settings.cpp index fadfe0305f..a976fc3828 100644 --- a/eeschema/symbol_editor/symbol_editor_settings.cpp +++ b/eeschema/symbol_editor/symbol_editor_settings.cpp @@ -29,7 +29,7 @@ ///! Update the schema version whenever a migration is required -const int libeditSchemaVersion = 0; +const int libeditSchemaVersion = 1; SYMBOL_EDITOR_SETTINGS::SYMBOL_EDITOR_SETTINGS() : @@ -81,6 +81,13 @@ SYMBOL_EDITOR_SETTINGS::SYMBOL_EDITOR_SETTINGS() : m_params.emplace_back( new PARAM( "use_eeschema_color_settings", &m_UseEeschemaColorSettings, true ) ); + + registerMigration( 0, 1, + [&]() -> bool + { + // This is actually a migration for APP_SETTINGS_BASE::m_LibTree + return migrateLibTreeWidth(); + } ); } diff --git a/eeschema/symbol_lib_table.h b/eeschema/symbol_lib_table.h index a7a46f2d9b..64df33d81c 100644 --- a/eeschema/symbol_lib_table.h +++ b/eeschema/symbol_lib_table.h @@ -84,6 +84,24 @@ public: void GetSubLibraryNames( std::vector& aNames ) const; + /** + * @see SCH_PLUGIN::GetAvailableSymbolFields + */ + void GetAvailableSymbolFields( std::vector& aNames ) const + { + if( plugin ) + plugin->GetAvailableSymbolFields( aNames ); + } + + /** + * @see SCH_PLUGIN::GetDefaultSymbolFields + */ + void GetDefaultSymbolFields( std::vector& aNames ) const + { + if( plugin ) + plugin->GetDefaultSymbolFields( aNames ); + } + protected: SYMBOL_LIB_TABLE_ROW( const SYMBOL_LIB_TABLE_ROW& aRow ) : LIB_TABLE_ROW( aRow ), diff --git a/eeschema/symbol_tree_model_adapter.cpp b/eeschema/symbol_tree_model_adapter.cpp index 26a68665f2..b558bd83ad 100644 --- a/eeschema/symbol_tree_model_adapter.cpp +++ b/eeschema/symbol_tree_model_adapter.cpp @@ -136,6 +136,12 @@ bool SYMBOL_TREE_MODEL_ADAPTER::AddLibraries( const std::vector& aNick if( !row->GetIsVisible() ) continue; + std::vector additionalColumns; + row->GetAvailableSymbolFields( additionalColumns ); + + for( const wxString& column : additionalColumns ) + addColumnIfNecessary( column ); + if( row->SupportsSubLibraries() ) { std::vector subLibraries; @@ -248,6 +254,21 @@ void SYMBOL_TREE_MODEL_ADAPTER::GetValue( wxVariant& aVariant, wxDataViewItem co case DESC_COL: aVariant = node->m_Desc; break; + + default: + { + if( m_colIdxMap.count( aCol ) ) + { + const wxString& key = m_colIdxMap.at( aCol ); + + if( node->m_Fields.count( key ) ) + aVariant = node->m_Fields.at( key ); + else + aVariant = wxEmptyString; + } + + break; + } } } diff --git a/include/lib_tree_item.h b/include/lib_tree_item.h index e476a1e85c..58e26f2f5e 100644 --- a/include/lib_tree_item.h +++ b/include/lib_tree_item.h @@ -25,6 +25,7 @@ #ifndef LIB_TREE_ITEM_H #define LIB_TREE_ITEM_H +#include #include #include @@ -46,9 +47,14 @@ public: virtual wxString GetName() const = 0; virtual wxString GetLibNickname() const = 0; - virtual wxString GetDescription() = 0; + /** + * Retrieves a key/value map of the fields on this item that should be exposed to the library + * browser/chooser for displaying in columns, searching, etc + */ + virtual void GetChooserFields( std::map& aColumnMap ) {} + virtual wxString GetSearchText() { return wxEmptyString; } /** diff --git a/include/lib_tree_model.h b/include/lib_tree_model.h index bcb31d328b..fa9be1eff4 100644 --- a/include/lib_tree_model.h +++ b/include/lib_tree_model.h @@ -23,6 +23,7 @@ #define LIB_TREE_MODEL_H #include +#include #include #include #include @@ -135,6 +136,8 @@ public: wxString m_SearchText; // Descriptive text to search bool m_Normalized; // Support for lazy normalization. + /// @see LIB_TREE_ITEMS::GetChooserFields + std::map m_Fields; LIB_ID m_LibId; // LIB_ID determined by the parent library nickname and alias name. int m_Unit; // Actual unit, or zero diff --git a/include/lib_tree_model_adapter.h b/include/lib_tree_model_adapter.h index f436b00010..3efdbb2157 100644 --- a/include/lib_tree_model_adapter.h +++ b/include/lib_tree_model_adapter.h @@ -30,6 +30,7 @@ #include #include #include +#include /** * Adapter class in the symbol selector Model-View-Adapter (mediated MVC) @@ -123,15 +124,16 @@ public: }; /** - * This enum defines the order of the columns in the tree view + * This enum defines the order of the default columns in the tree view */ enum TREE_COLS { NAME_COL = 0, ///< Library or library item name column DESC_COL, ///< Library or library description column - NUM_COLS ///< The number of tree columns + NUM_COLS ///< The number of default tree columns }; + /** * Save the column widths to the config file. This requires the tree view to still be * valid. @@ -179,6 +181,10 @@ public: const std::vector& aItemList, bool pinned, bool presorted ); + void AddColumn( const wxString& aHeader ) + { + doAddColumn( aHeader, false ); + } /** * Sort the tree and assign ranks after adding libraries. @@ -328,7 +334,7 @@ protected: */ wxDataViewItem GetParent( const wxDataViewItem& aItem ) const override; - unsigned int GetColumnCount() const override { return NUM_COLS; } + unsigned int GetColumnCount() const override { return m_columns.size(); } /** * Return the type of data stored in the column as indicated by wxVariant::GetType() @@ -389,8 +395,13 @@ private: */ LIB_TREE_NODE* ShowSingleLibrary(); + wxDataViewColumn* doAddColumn( const wxString& aHeader, bool aTranslate = true ); + protected: - LIB_TREE_NODE_ROOT m_tree; + void addColumnIfNecessary( const wxString& aHeader ); + + LIB_TREE_NODE_ROOT m_tree; + std::map m_colIdxMap; private: [[maybe_unused]] EDA_BASE_FRAME* m_parent; @@ -401,11 +412,11 @@ private: int m_preselect_unit; int m_freeze; - wxDataViewColumn* m_col_part; - wxDataViewColumn* m_col_desc; wxDataViewCtrl* m_widget; - int m_colWidths[NUM_COLS]; + std::vector m_columns; + std::map m_colNameMap; + std::map m_colWidths; }; #endif // LIB_TREE_MODEL_ADAPTER_H diff --git a/include/settings/app_settings.h b/include/settings/app_settings.h index 5209d04125..9e087e641b 100644 --- a/include/settings/app_settings.h +++ b/include/settings/app_settings.h @@ -127,7 +127,8 @@ public: struct LIB_TREE { - int column_width; + std::vector columns; ///< Ordered list of visible columns in the tree + std::map column_widths; ///< Column widths, keyed by header name }; struct PRINTING @@ -211,6 +212,11 @@ protected: * @param aJsonPath is the path to read parameters from */ void addParamsForWindow( WINDOW_SETTINGS* aWindow, const std::string& aJsonPath ); + + /** + * Migrates the library tree width setting from a single column (Item) to multi-column + */ + bool migrateLibTreeWidth(); }; #endif diff --git a/pcbnew/footprint_editor_settings.cpp b/pcbnew/footprint_editor_settings.cpp index a306351ff3..fb7f664f15 100644 --- a/pcbnew/footprint_editor_settings.cpp +++ b/pcbnew/footprint_editor_settings.cpp @@ -34,7 +34,7 @@ ///! Update the schema version whenever a migration is required -const int fpEditSchemaVersion = 1; +const int fpEditSchemaVersion = 2; FOOTPRINT_EDITOR_SETTINGS::FOOTPRINT_EDITOR_SETTINGS() : @@ -292,6 +292,13 @@ FOOTPRINT_EDITOR_SETTINGS::FOOTPRINT_EDITOR_SETTINGS() : } ) ); registerMigration( 0, 1, std::bind( &FOOTPRINT_EDITOR_SETTINGS::migrateSchema0to1, this ) ); + + registerMigration( 1, 2, + [&]() -> bool + { + // This is actually a migration for APP_SETTINGS_BASE::m_LibTree + return migrateLibTreeWidth(); + } ); } diff --git a/pcbnew/pcbnew_settings.cpp b/pcbnew/pcbnew_settings.cpp index d726c526ba..566c6e9183 100644 --- a/pcbnew/pcbnew_settings.cpp +++ b/pcbnew/pcbnew_settings.cpp @@ -43,7 +43,7 @@ ///! Update the schema version whenever a migration is required -const int pcbnewSchemaVersion = 3; +const int pcbnewSchemaVersion = 4; PCBNEW_SETTINGS::PCBNEW_SETTINGS() @@ -595,6 +595,13 @@ PCBNEW_SETTINGS::PCBNEW_SETTINGS() return true; } ); + + registerMigration( 3, 4, + [&]() -> bool + { + // This is actually a migration for APP_SETTINGS_BASE::m_LibTree + return migrateLibTreeWidth(); + } ); }