Browse Source

Work in progress variant code commit.

Please do not attempt to use this yet as it is a work in progress.  Given
large number of merge conflicts, I pushed this partial commit to save time
rebasing.

All of the user interface is hidden behind the "EnableVariantUI" advanced
configuration flag until variants are ready.
master
Wayne Stambaugh 2 weeks ago
parent
commit
33ead34ea5
  1. 6
      common/advanced_config.cpp
  2. 21
      common/string_utils.cpp
  3. 135
      eeschema/dialogs/dialog_symbol_fields_table.cpp
  4. 6
      eeschema/dialogs/dialog_symbol_fields_table.h
  5. 50
      eeschema/dialogs/dialog_symbol_fields_table_base.cpp
  6. 454
      eeschema/dialogs/dialog_symbol_fields_table_base.fbp
  7. 12
      eeschema/dialogs/dialog_symbol_fields_table_base.h
  8. 4
      eeschema/dialogs/dialog_symbol_properties.cpp
  9. 4
      eeschema/eeschema_id.h
  10. 8
      eeschema/eeschema_jobs_handler.cpp
  11. 180
      eeschema/fields_data_model.cpp
  12. 19
      eeschema/fields_data_model.h
  13. 7
      eeschema/menubar.cpp
  14. 11
      eeschema/sch_edit_frame.h
  15. 46
      eeschema/sch_field.cpp
  16. 6
      eeschema/sch_field.h
  17. 3
      eeschema/sch_file_versions.h
  18. 66
      eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr.cpp
  19. 123
      eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.cpp
  20. 20
      eeschema/sch_item.cpp
  21. 29
      eeschema/sch_item.h
  22. 11
      eeschema/sch_painter.cpp
  23. 32
      eeschema/sch_rule_area.h
  24. 104
      eeschema/sch_screen.cpp
  25. 8
      eeschema/sch_screen.h
  26. 44
      eeschema/sch_sheet.cpp
  27. 40
      eeschema/sch_sheet.h
  28. 14
      eeschema/sch_sheet_path.cpp
  29. 90
      eeschema/sch_sheet_path.h
  30. 268
      eeschema/sch_symbol.cpp
  31. 54
      eeschema/sch_symbol.h
  32. 13
      eeschema/sch_text.h
  33. 13
      eeschema/sch_textbox.h
  34. 52
      eeschema/schematic.cpp
  35. 30
      eeschema/schematic.h
  36. 2
      eeschema/schematic.keywords
  37. 28
      eeschema/symbol.h
  38. 54
      eeschema/toolbars_sch_editor.cpp
  39. 11
      eeschema/toolbars_sch_editor.h
  40. 12
      eeschema/tools/sch_actions.cpp
  41. 4
      eeschema/tools/sch_actions.h
  42. 9
      include/advanced_config.h
  43. 4
      include/string_utils.h
  44. 29
      kicad/cli/command.cpp
  45. 18
      kicad/cli/command.h
  46. 20
      qa/tests/common/test_kicad_string.cpp

6
common/advanced_config.cpp

@ -138,6 +138,7 @@ static const wxChar MaxPastedTextLength[] = wxT( "MaxPastedTextLength" );
static const wxChar PNSProcessClusterTimeout[] = wxT( "PNSProcessClusterTimeout" );
static const wxChar ImportSkipComponentBodies[] = wxT( "ImportSkipComponentBodies" );
static const wxChar ScreenDPI[] = wxT( "ScreenDPI" );
static const wxChar EnableVariantsUI[] = wxT( "EnableVariantsUI" );
} // namespace KEYS
@ -335,6 +336,8 @@ ADVANCED_CFG::ADVANCED_CFG()
m_ScreenDPI = 91;
m_EnableVariantsUI = false;
loadFromConfigFile();
}
@ -672,6 +675,9 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg )
&m_ScreenDPI, m_ScreenDPI,
50, 500 ) );
m_entries.push_back( std::make_unique<PARAM_CFG_BOOL>( true, AC_KEYS::EnableVariantsUI, &m_EnableVariantsUI,
m_EnableVariantsUI ) );
// Special case for trace mask setting...we just grab them and set them immediately
// Because we even use wxLogTrace inside of advanced config
m_entries.push_back( std::make_unique<PARAM_CFG_WXSTRING>( true, AC_KEYS::TraceMasks, &m_traceMasks,

21
common/string_utils.cpp

@ -49,6 +49,8 @@
*/
static const char illegalFileNameChars[] = "\\/:\"<>|*?";
static const wxChar defaultVariantName[] = wxT( "< Default >" );
// Checks if a full filename is valid, i.e. does not contains illegal chars
bool IsFullFileNameValid( const wxString& aFullFilename )
@ -1625,3 +1627,22 @@ std::vector<wxString> ExpandStackedPinNotation( const wxString& aPinName, bool*
return expanded;
}
wxString GetDefaultVariantName()
{
return wxString( defaultVariantName );
}
int SortVariantNames( const wxString& aLhs, const wxString& aRhs )
{
if( ( aLhs == defaultVariantName ) && ( aRhs != defaultVariantName ) )
return -1;
if( ( aLhs != defaultVariantName ) && ( aRhs == defaultVariantName ) )
return 1;
return StrNumCmp( aLhs, aRhs );
}

135
eeschema/dialogs/dialog_symbol_fields_table.cpp

@ -22,7 +22,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <advanced_config.h>
#include <common.h>
#include <base_units.h>
#include <bitmaps.h>
@ -35,7 +35,6 @@
#include <grid_tricks.h>
#include <string_utils.h>
#include <kiface_base.h>
#include <sch_commit.h>
#include <sch_edit_frame.h>
#include <sch_reference_list.h>
#include <tools/sch_editor_control.h>
@ -208,6 +207,10 @@ DIALOG_SYMBOL_FIELDS_TABLE::DIALOG_SYMBOL_FIELDS_TABLE( SCH_EDIT_FRAME* parent,
m_removeFieldButton->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
m_renameFieldButton->SetBitmap( KiBitmapBundle( BITMAPS::small_edit ) );
m_addVariantButton->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
m_deleteVariantButton->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
m_renameVariantButton->SetBitmap( KiBitmapBundle( BITMAPS::small_edit ) );
m_sidebarButton->SetBitmap( KiBitmapBundle( BITMAPS::left ) );
m_viewControlsDataModel = new VIEW_CONTROLS_GRID_DATA_MODEL( true );
@ -258,6 +261,11 @@ DIALOG_SYMBOL_FIELDS_TABLE::DIALOG_SYMBOL_FIELDS_TABLE( SCH_EDIT_FRAME* parent,
m_grid->PushEventHandler( new FIELDS_EDITOR_GRID_TRICKS( this, m_grid, m_viewControlsDataModel, m_dataModel,
&m_parent->Schematic() ) );
m_variantListBox->Set( parent->Schematic().GetVariantNamesForUI() );
if( !ADVANCED_CFG::GetCfg().m_EnableVariantsUI )
bLeftSizer->Hide( variantSizer, true );
// Load our BOM view presets
SetUserBomPresets( m_schSettings.m_BomPresets );
@ -650,20 +658,23 @@ bool DIALOG_SYMBOL_FIELDS_TABLE::TransferDataFromWindow()
return true;
}
std::set<wxString> selectedVariantNames;
SCH_COMMIT commit( m_parent );
SCH_SHEET_PATH currentSheet = m_parent->GetCurrentSheet();
m_dataModel->ApplyData( commit, m_schSettings.m_TemplateFieldNames );
m_dataModel->ApplyData( commit, m_schSettings.m_TemplateFieldNames, selectedVariantNames );
commit.Push( wxS( "Symbol Fields Table Edit" ) );
if( !commit.Empty() )
{
commit.Push( wxS( "Symbol Fields Table Edit" ) ); // Push clears the commit buffer.
m_parent->OnModify();
}
// Reset the view to where we left the user
m_parent->SetCurrentSheet( currentSheet );
m_parent->SyncView();
m_parent->Refresh();
m_parent->OnModify();
return true;
}
@ -679,7 +690,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::AddField( const wxString& aFieldName, const wxS
return;
}
m_dataModel->AddColumn( aFieldName, aLabelValue, addedByUser );
m_dataModel->AddColumn( aFieldName, aLabelValue, addedByUser, getSelectedVariants() );
wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_APPENDED, 1 );
m_grid->ProcessTableMessage( msg );
@ -1177,7 +1188,6 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnViewControlsCellChanged( wxGridEvent& aEvent
void DIALOG_SYMBOL_FIELDS_TABLE::OnTableValueChanged( wxGridEvent& aEvent )
{
m_grid->ForceRefresh();
OnModify();
}
@ -1949,7 +1959,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::doApplyBomPreset( const BOM_PRESET& aPreset )
// Basically, we apply the BOM preset to the data model and then
// update our UI to reflect resulting the data model state, not the preset.
m_dataModel->ApplyBomPreset( aPreset );
m_dataModel->ApplyBomPreset( aPreset, getSelectedVariants() );
// BOM Presets can add, but not remove, columns, so make sure the view controls
// grid has all of them before starting
@ -2493,7 +2503,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnSchItemsChanged( SCHEMATIC& aSch
for( SCH_FIELD& field : symbol->GetFields() )
AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
m_dataModel->UpdateReferences( getSymbolReferences( symbol, allRefs ) );
m_dataModel->UpdateReferences( getSymbolReferences( symbol, allRefs ), getSelectedVariants() );
}
else if( item->Type() == SCH_SHEET_T )
{
@ -2510,7 +2520,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnSchItemsChanged( SCHEMATIC& aSch
AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
}
m_dataModel->UpdateReferences( refs );
m_dataModel->UpdateReferences( refs, getSelectedVariants() );
}
}
@ -2604,3 +2614,106 @@ SCH_REFERENCE_LIST DIALOG_SYMBOL_FIELDS_TABLE::getSheetSymbolReferences( SCH_SHE
return sheetRefs;
}
void DIALOG_SYMBOL_FIELDS_TABLE::onAddVariant( wxCommandEvent& aEvent )
{
wxTextEntryDialog dlg( this, _( "Add new variant name:" ), _( "New Variant" ), wxEmptyString,
wxOK | wxCANCEL | wxCENTER );
if( dlg.ShowModal() == wxID_CANCEL )
return;
// Empty strings and duplicate variant names are not allowed.
if( dlg.GetValue().IsEmpty() || ( m_variantListBox->FindString( dlg.GetValue() ) != wxNOT_FOUND ) )
{
wxBell();
return;
}
wxArrayString ctrlContents = m_variantListBox->GetStrings();
ctrlContents.Add( dlg.GetValue() );
ctrlContents.Sort( SortVariantNames );
m_variantListBox->Set( ctrlContents );
}
void DIALOG_SYMBOL_FIELDS_TABLE::onDeleteVariant( wxCommandEvent& aEvent )
{
wxArrayInt selections;
// An empty or default selection cannot be deleted.
if( ( m_variantListBox->GetSelections( selections ) == 0 ) || ( selections[0] == 0 ) )
{
wxBell();
return;
}
wxArrayString ctrlContents = m_variantListBox->GetStrings();
for( int selection : selections )
{
wxString variantName = m_variantListBox->GetString( selection );
ctrlContents.Remove( variantName );
m_parent->Schematic().DeleteVariant( variantName );
}
m_variantListBox->Set( ctrlContents );
}
void DIALOG_SYMBOL_FIELDS_TABLE::onRenameVariant( wxCommandEvent& aEvent )
{
wxArrayInt selections;
// Only allow renaming a single selection that is not the default.
if( ( m_variantListBox->GetSelections( selections ) != 1 ) || ( selections[0] == 0 ) )
{
wxBell();
return;
}
wxArrayString ctrlContents = m_variantListBox->GetStrings();
wxString oldVariantName = ctrlContents[selections[0]];
wxTextEntryDialog dlg( this, _( "Add new variant name:" ), _( "New Variant" ), oldVariantName,
wxOK | wxCANCEL | wxCENTER );
if( dlg.ShowModal() == wxID_CANCEL )
return;
wxString newVariantName = dlg.GetValue();
if( newVariantName.IsEmpty() || ( newVariantName == oldVariantName ) || ( newVariantName == ctrlContents[0] ) )
{
wxBell();
return;
}
ctrlContents.Remove( m_variantListBox->GetString( selections[0] ) );
ctrlContents.Add( newVariantName );
ctrlContents.Sort( SortVariantNames );
m_variantListBox->Set( ctrlContents );
}
std::set<wxString> DIALOG_SYMBOL_FIELDS_TABLE::getSelectedVariants() const
{
std::set<wxString> retv;
wxArrayInt selections;
if( m_variantListBox->GetSelections( selections ) )
{
for( int selection : selections )
{
if( selection == 0 )
continue;
retv.emplace( m_variantListBox->GetString( selection ) );
}
}
return retv;
}

6
eeschema/dialogs/dialog_symbol_fields_table.h

@ -139,6 +139,12 @@ private:
void savePresetsToSchematic();
void onAddVariant( wxCommandEvent& aEvent ) override;
void onDeleteVariant( wxCommandEvent& aEvent ) override;
void onRenameVariant( wxCommandEvent& aEvent ) override;
std::set<wxString> getSelectedVariants() const;
private:
std::map<wxString, BOM_PRESET> m_bomPresets;
BOM_PRESET* m_currentBomPreset;

50
eeschema/dialogs/dialog_symbol_fields_table_base.cpp

@ -23,7 +23,6 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::DIALOG_SYMBOL_FIELDS_TABLE_BASE( wxWindow* pare
m_splitterMainWindow->SetMinimumPaneSize( 120 );
m_leftPanel = new wxPanel( m_splitterMainWindow, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bLeftSizer;
bLeftSizer = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* bMargins;
@ -71,7 +70,7 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::DIALOG_SYMBOL_FIELDS_TABLE_BASE( wxWindow* pare
m_addFieldButton = new STD_BITMAP_BUTTON( m_leftPanel, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
m_addFieldButton->SetToolTip( _("Add a new field") );
bFieldsButtons->Add( m_addFieldButton, 0, wxBOTTOM|wxLEFT, 5 );
bFieldsButtons->Add( m_addFieldButton, 0, wxBOTTOM, 5 );
m_renameFieldButton = new STD_BITMAP_BUTTON( m_leftPanel, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
m_renameFieldButton->SetToolTip( _("Rename selected field") );
@ -89,15 +88,48 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::DIALOG_SYMBOL_FIELDS_TABLE_BASE( wxWindow* pare
bMargins->Add( bFieldsButtons, 0, wxEXPAND|wxTOP, 5 );
variantSizer = new wxBoxSizer( wxVERTICAL );
m_staticline6 = new wxStaticLine( m_leftPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
variantSizer->Add( m_staticline6, 0, wxEXPAND|wxTOP, 5 );
m_staticText9 = new wxStaticText( m_leftPanel, wxID_ANY, _("Variants:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText9->Wrap( -1 );
variantSizer->Add( m_staticText9, 0, wxBOTTOM|wxTOP, 5 );
m_variantListBox = new wxListBox( m_leftPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
variantSizer->Add( m_variantListBox, 1, wxBOTTOM|wxEXPAND, 5 );
wxBoxSizer* bSizer14;
bSizer14 = new wxBoxSizer( wxHORIZONTAL );
m_addVariantButton = new wxBitmapButton( m_leftPanel, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
bSizer14->Add( m_addVariantButton, 0, wxRIGHT, 5 );
m_renameVariantButton = new wxBitmapButton( m_leftPanel, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
bSizer14->Add( m_renameVariantButton, 0, 0, 5 );
bSizer14->Add( 15, 0, 0, 0, 5 );
m_deleteVariantButton = new wxBitmapButton( m_leftPanel, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
bSizer14->Add( m_deleteVariantButton, 0, wxLEFT, 5 );
variantSizer->Add( bSizer14, 0, wxEXPAND, 5 );
bMargins->Add( variantSizer, 1, wxEXPAND, 5 );
wxBoxSizer* bPresets;
bPresets = new wxBoxSizer( wxVERTICAL );
m_staticline11 = new wxStaticLine( m_leftPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bPresets->Add( m_staticline11, 0, wxEXPAND|wxBOTTOM, 5 );
bPresets->Add( m_staticline11, 0, wxBOTTOM|wxEXPAND|wxTOP, 5 );
m_bomPresetsLabel = new wxStaticText( m_leftPanel, wxID_ANY, _("View presets:"), wxDefaultPosition, wxDefaultSize, 0 );
m_bomPresetsLabel->Wrap( -1 );
bPresets->Add( m_bomPresetsLabel, 0, wxLEFT, 5 );
bPresets->Add( m_bomPresetsLabel, 0, 0, 5 );
bPresets->Add( 0, 2, 0, 0, 5 );
@ -106,7 +138,7 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::DIALOG_SYMBOL_FIELDS_TABLE_BASE( wxWindow* pare
int m_cbBomPresetsNChoices = sizeof( m_cbBomPresetsChoices ) / sizeof( wxString );
m_cbBomPresets = new wxChoice( m_leftPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_cbBomPresetsNChoices, m_cbBomPresetsChoices, 0 );
m_cbBomPresets->SetSelection( 0 );
bPresets->Add( m_cbBomPresets, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
bPresets->Add( m_cbBomPresets, 0, wxEXPAND, 5 );
bPresets->Add( 0, 2, 0, wxEXPAND, 5 );
@ -115,7 +147,7 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::DIALOG_SYMBOL_FIELDS_TABLE_BASE( wxWindow* pare
bMargins->Add( bPresets, 0, wxEXPAND|wxTOP|wxBOTTOM, 5 );
bLeftSizer->Add( bMargins, 1, wxEXPAND, 5 );
bLeftSizer->Add( bMargins, 1, wxEXPAND|wxLEFT|wxRIGHT|wxTOP, 5 );
m_leftPanel->SetSizer( bLeftSizer );
@ -386,6 +418,9 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::DIALOG_SYMBOL_FIELDS_TABLE_BASE( wxWindow* pare
m_addFieldButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnAddField ), NULL, this );
m_renameFieldButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnRenameField ), NULL, this );
m_removeFieldButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnRemoveField ), NULL, this );
m_addVariantButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::onAddVariant ), NULL, this );
m_renameVariantButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::onRenameVariant ), NULL, this );
m_deleteVariantButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::onDeleteVariant ), NULL, this );
m_nbPages->Connect( wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, wxNotebookEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPageChanged ), NULL, this );
m_filter->Connect( wxEVT_MOTION, wxMouseEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnFilterMouseMoved ), NULL, this );
m_filter->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnFilterText ), NULL, this );
@ -421,6 +456,9 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::~DIALOG_SYMBOL_FIELDS_TABLE_BASE()
m_addFieldButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnAddField ), NULL, this );
m_renameFieldButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnRenameField ), NULL, this );
m_removeFieldButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnRemoveField ), NULL, this );
m_addVariantButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::onAddVariant ), NULL, this );
m_renameVariantButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::onRenameVariant ), NULL, this );
m_deleteVariantButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::onDeleteVariant ), NULL, this );
m_nbPages->Disconnect( wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED, wxNotebookEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPageChanged ), NULL, this );
m_filter->Disconnect( wxEVT_MOTION, wxMouseEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnFilterMouseMoved ), NULL, this );
m_filter->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnFilterText ), NULL, this );

454
eeschema/dialogs/dialog_symbol_fields_table_base.fbp

@ -184,10 +184,10 @@
<property name="minimum_size"></property>
<property name="name">bLeftSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<property name="permission">protected</property>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="flag">wxEXPAND|wxLEFT|wxRIGHT|wxTOP</property>
<property name="proportion">1</property>
<object class="wxBoxSizer" expanded="true">
<property name="minimum_size"></property>
@ -296,7 +296,7 @@
<property name="permission">none</property>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxLEFT</property>
<property name="flag">wxBOTTOM</property>
<property name="proportion">0</property>
<object class="wxBitmapButton" expanded="true">
<property name="BottomDockable">1</property>
@ -531,6 +531,448 @@
</object>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxBoxSizer" expanded="true">
<property name="minimum_size"></property>
<property name="name">variantSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">protected</property>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxTOP</property>
<property name="proportion">0</property>
<object class="wxStaticLine" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticline6</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxLI_HORIZONTAL</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxTOP</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Variants:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticText9</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxListBox" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="choices"></property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_variantListBox</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="true">
<property name="minimum_size"></property>
<property name="name">bSizer14</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxBitmapButton" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="auth_needed">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">MyButton</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_addVariantButton</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">onAddVariant</event>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag"></property>
<property name="proportion">0</property>
<object class="wxBitmapButton" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="auth_needed">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">MyButton</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_renameVariantButton</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">onRenameVariant</event>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag"></property>
<property name="proportion">0</property>
<object class="spacer" expanded="true">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">15</property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxLEFT</property>
<property name="proportion">0</property>
<object class="wxBitmapButton" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="auth_needed">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">MyButton</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_deleteVariantButton</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">onDeleteVariant</event>
</object>
</object>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxTOP|wxBOTTOM</property>
@ -542,7 +984,7 @@
<property name="permission">none</property>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxBOTTOM</property>
<property name="flag">wxBOTTOM|wxEXPAND|wxTOP</property>
<property name="proportion">0</property>
<object class="wxStaticLine" expanded="true">
<property name="BottomDockable">1</property>
@ -601,7 +1043,7 @@
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxLEFT</property>
<property name="flag"></property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="true">
<property name="BottomDockable">1</property>
@ -673,7 +1115,7 @@
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxChoice" expanded="true">
<property name="BottomDockable">1</property>

12
eeschema/dialogs/dialog_symbol_fields_table_base.h

@ -28,6 +28,7 @@ class WX_GRID;
#include <wx/sizer.h>
#include <wx/statline.h>
#include <wx/stattext.h>
#include <wx/listbox.h>
#include <wx/choice.h>
#include <wx/panel.h>
#include <wx/srchctrl.h>
@ -50,10 +51,18 @@ class DIALOG_SYMBOL_FIELDS_TABLE_BASE : public DIALOG_SHIM
protected:
wxSplitterWindow* m_splitterMainWindow;
wxPanel* m_leftPanel;
wxBoxSizer* bLeftSizer;
WX_GRID* m_viewControlsGrid;
STD_BITMAP_BUTTON* m_addFieldButton;
STD_BITMAP_BUTTON* m_renameFieldButton;
STD_BITMAP_BUTTON* m_removeFieldButton;
wxBoxSizer* variantSizer;
wxStaticLine* m_staticline6;
wxStaticText* m_staticText9;
wxListBox* m_variantListBox;
wxBitmapButton* m_addVariantButton;
wxBitmapButton* m_renameVariantButton;
wxBitmapButton* m_deleteVariantButton;
wxStaticLine* m_staticline11;
wxStaticText* m_bomPresetsLabel;
wxChoice* m_cbBomPresets;
@ -103,6 +112,9 @@ class DIALOG_SYMBOL_FIELDS_TABLE_BASE : public DIALOG_SHIM
virtual void OnAddField( wxCommandEvent& event ) { event.Skip(); }
virtual void OnRenameField( wxCommandEvent& event ) { event.Skip(); }
virtual void OnRemoveField( wxCommandEvent& event ) { event.Skip(); }
virtual void onAddVariant( wxCommandEvent& event ) { event.Skip(); }
virtual void onRenameVariant( wxCommandEvent& event ) { event.Skip(); }
virtual void onDeleteVariant( wxCommandEvent& event ) { event.Skip(); }
virtual void OnPageChanged( wxNotebookEvent& event ) { event.Skip(); }
virtual void OnFilterMouseMoved( wxMouseEvent& event ) { event.Skip(); }
virtual void OnFilterText( wxCommandEvent& event ) { event.Skip(); }

4
eeschema/dialogs/dialog_symbol_properties.cpp

@ -526,7 +526,7 @@ bool DIALOG_SYMBOL_PROPERTIES::TransferDataToWindow()
m_cbExcludeFromSim->SetValue( m_symbol->GetExcludedFromSim() );
m_cbExcludeFromBom->SetValue( m_symbol->GetExcludedFromBOM() );
m_cbExcludeFromBoard->SetValue( m_symbol->GetExcludedFromBoard() );
m_cbDNP->SetValue( m_symbol->GetDNP() );
m_cbDNP->SetValue( m_symbol->GetDNP( &GetParent()->GetCurrentSheet() ) );
if( m_part )
{
@ -759,7 +759,7 @@ bool DIALOG_SYMBOL_PROPERTIES::TransferDataFromWindow()
m_symbol->SetExcludedFromSim( m_cbExcludeFromSim->IsChecked() );
m_symbol->SetExcludedFromBOM( m_cbExcludeFromBom->IsChecked() );
m_symbol->SetExcludedFromBoard( m_cbExcludeFromBoard->IsChecked() );
m_symbol->SetDNP( m_cbDNP->IsChecked() );
m_symbol->SetDNP( m_cbDNP->IsChecked(), &GetParent()->GetCurrentSheet() );
// Update any assignments
if( m_dataModel )

4
eeschema/eeschema_id.h

@ -109,7 +109,9 @@ enum id_eeschema_frm
ID_POPUP_SCH_PIN_TRICKS_END = ID_POPUP_SCH_PIN_TRICKS_GLOBAL_LABEL,
ID_POPUP_SCH_ALT_PIN_FUNCTION,
ID_POPUP_SCH_ALT_PIN_FUNCTION_END = ID_POPUP_SCH_ALT_PIN_FUNCTION + MAX_ALT_PIN_FUNCTION_ITEMS
ID_POPUP_SCH_ALT_PIN_FUNCTION_END = ID_POPUP_SCH_ALT_PIN_FUNCTION + MAX_ALT_PIN_FUNCTION_ITEMS,
ID_TOOLBAR_SCH_SELECT_VARAIANT
};

8
eeschema/eeschema_jobs_handler.cpp

@ -530,7 +530,7 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
for( FIELD_T fieldId : MANDATORY_FIELDS )
{
dataModel.AddColumn( GetCanonicalFieldName( fieldId ),
GetDefaultFieldName( fieldId, DO_TRANSLATE ), false );
GetDefaultFieldName( fieldId, DO_TRANSLATE ), false, {} );
}
// User field names in symbols second
@ -548,7 +548,7 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
}
for( const wxString& fieldName : userFieldNames )
dataModel.AddColumn( fieldName, GetGeneratedFieldDisplayName( fieldName ), true );
dataModel.AddColumn( fieldName, GetGeneratedFieldDisplayName( fieldName ), true, {} );
// Add any templateFieldNames which aren't already present in the userFieldNames
for( const TEMPLATE_FIELDNAME& templateFieldname :
@ -557,7 +557,7 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
if( userFieldNames.count( templateFieldname.m_Name ) == 0 )
{
dataModel.AddColumn( templateFieldname.m_Name, GetGeneratedFieldDisplayName( templateFieldname.m_Name ),
false );
false, {} );
}
}
@ -670,7 +670,7 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
preset.excludeDNP = aBomJob->m_excludeDNP;
}
dataModel.ApplyBomPreset( preset );
dataModel.ApplyBomPreset( preset, {} );
if( aBomJob->GetConfiguredOutputPath().IsEmpty() )
{

180
eeschema/fields_data_model.cpp

@ -30,6 +30,9 @@
#include "fields_data_model.h"
static wxString multipleValues = wxS( "<...>" );
wxString VIEW_CONTROLS_GRID_DATA_MODEL::GetColLabelValue( int aCol )
{
switch( aCol )
@ -182,7 +185,7 @@ const wxString FIELDS_EDITOR_GRID_DATA_MODEL::ITEM_NUMBER_VARIABLE = wxS( "${ITE
void FIELDS_EDITOR_GRID_DATA_MODEL::AddColumn( const wxString& aFieldName, const wxString& aLabel,
bool aAddedByUser )
bool aAddedByUser, const std::set<wxString>& aVariantNames )
{
// Don't add a field twice
if( GetFieldNameCol( aFieldName ) != -1 )
@ -191,40 +194,61 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::AddColumn( const wxString& aFieldName, const
m_cols.push_back( { aFieldName, aLabel, aAddedByUser, false, false } );
for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
{
if( SCH_SYMBOL* symbol = m_symbolsList[i].GetSymbol() )
updateDataStoreSymbolField( *symbol, aFieldName );
}
updateDataStoreSymbolField( m_symbolsList[i], aFieldName, aVariantNames );
}
void FIELDS_EDITOR_GRID_DATA_MODEL::updateDataStoreSymbolField( const SCH_SYMBOL& aSymbol,
const wxString& aFieldName )
void FIELDS_EDITOR_GRID_DATA_MODEL::updateDataStoreSymbolField( const SCH_REFERENCE& aSymbolRef,
const wxString& aFieldName,
const std::set<wxString>& aVariantNames )
{
const SCH_SYMBOL* symbol = aSymbolRef.GetSymbol();
if( !symbol )
return;
if( isAttribute( aFieldName ) )
{
m_dataStore[aSymbol.m_Uuid][aFieldName] = getAttributeValue( aSymbol, aFieldName );
m_dataStore[symbol->m_Uuid][aFieldName] = getAttributeValue( *symbol, aFieldName, aVariantNames );
}
else if( const SCH_FIELD* field = aSymbol.GetField( aFieldName ) )
else if( const SCH_FIELD* field = symbol->GetField( aFieldName ) )
{
if( field->IsPrivate() )
{
m_dataStore[aSymbol.m_Uuid][aFieldName] = wxEmptyString;
m_dataStore[symbol->m_Uuid][aFieldName] = wxEmptyString;
return;
}
wxString value = aSymbol.Schematic()->ConvertKIIDsToRefs( field->GetText() );
m_dataStore[aSymbol.m_Uuid][aFieldName] = value;
wxString value = symbol->Schematic()->ConvertKIIDsToRefs( field->GetText() );
for( const wxString& variantName : aVariantNames )
{
std::optional<SCH_SYMBOL_VARIANT> variant = symbol->GetVariant( aSymbolRef.GetSheetPath(), variantName );
if( !variant || !variant->m_Fields.contains( aFieldName ) )
continue;
if( value.IsEmpty() )
{
value = variant->m_Fields[aFieldName];
continue;
}
if( value != variant->m_Fields[aFieldName] )
value = multipleValues;
}
m_dataStore[symbol->m_Uuid][aFieldName] = value;
}
else if( IsGeneratedField( aFieldName ) )
{
// Handle generated fields with variables as names (e.g. ${QUANTITY}) that are not present in
// the symbol by giving them the correct value
m_dataStore[aSymbol.m_Uuid][aFieldName] = aFieldName;
m_dataStore[symbol->m_Uuid][aFieldName] = aFieldName;
}
else
{
m_dataStore[aSymbol.m_Uuid][aFieldName] = wxEmptyString;
m_dataStore[symbol->m_Uuid][aFieldName] = wxEmptyString;
}
}
@ -703,35 +727,66 @@ bool FIELDS_EDITOR_GRID_DATA_MODEL::isAttribute( const wxString& aFieldName )
}
wxString FIELDS_EDITOR_GRID_DATA_MODEL::getAttributeValue( const SCH_SYMBOL& aSymbol, const wxString& aAttributeName )
wxString FIELDS_EDITOR_GRID_DATA_MODEL::getAttributeValue( const SCH_SYMBOL& aSymbol, const wxString& aAttributeName,
const std::set<wxString>& aVariantNames )
{
if( aAttributeName == wxS( "${DNP}" ) )
return aSymbol.GetDNP() ? wxS( "1" ) : wxS( "0" );
wxString retv;
if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
return aSymbol.GetExcludedFromBoard() ? wxS( "1" ) : wxS( "0" );
auto getAttrString = [&]( const wxString aVariantName = wxEmptyString )->wxString
{
if( aAttributeName == wxS( "${DNP}" ) )
return aSymbol.GetDNP( nullptr, aVariantName ) ? wxS( "1" ) : wxS( "0" );
if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
return aSymbol.GetExcludedFromBoard() ? wxS( "1" ) : wxS( "0" );
if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
return aSymbol.GetExcludedFromBOM( nullptr, aVariantName ) ? wxS( "1" ) : wxS( "0" );
if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
return aSymbol.GetExcludedFromSim( nullptr, aVariantName ) ? wxS( "1" ) : wxS( "0" );
if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
return aSymbol.GetExcludedFromBOM() ? wxS( "1" ) : wxS( "0" );
return wxS( "0" );
};
if( aVariantNames.empty() )
{
retv = getAttrString();
}
else
{
for( const wxString& variantName : aVariantNames )
{
if( retv.IsEmpty() )
{
retv = getAttrString( variantName );
continue;
}
if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
return aSymbol.GetExcludedFromSim() ? wxS( "1" ) : wxS( "0" );
if( retv != getAttrString( variantName ) )
retv = multipleValues;
}
}
return wxS( "0" );
return retv;
}
bool FIELDS_EDITOR_GRID_DATA_MODEL::setAttributeValue( SCH_SYMBOL& aSymbol,
const wxString& aAttributeName,
const wxString& aValue )
const wxString& aValue,
const wxString& aVariantName )
{
if( aValue == multipleValues )
return false;
bool attrChanged = false;
bool newValue = aValue == wxS( "1" );
if( aAttributeName == wxS( "${DNP}" ) )
{
attrChanged = aSymbol.GetDNP() != newValue;
aSymbol.SetDNP( newValue );
attrChanged = aSymbol.GetDNP( nullptr, aVariantName ) != newValue;
aSymbol.SetDNP( newValue, nullptr, aVariantName );
}
else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
{
@ -740,13 +795,13 @@ bool FIELDS_EDITOR_GRID_DATA_MODEL::setAttributeValue( SCH_SYMBOL& aSymbol,
}
else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
{
attrChanged = aSymbol.GetExcludedFromBOM() != newValue;
aSymbol.SetExcludedFromBOM( newValue );
attrChanged = aSymbol.GetExcludedFromBOM( nullptr, aVariantName ) != newValue;
aSymbol.SetExcludedFromBOM( newValue, nullptr, aVariantName );
}
else if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
{
attrChanged = aSymbol.GetExcludedFromSim() != newValue;
aSymbol.SetExcludedFromSim( newValue );
attrChanged = aSymbol.GetExcludedFromSim( nullptr, aVariantName ) != newValue;
aSymbol.SetExcludedFromSim( newValue, nullptr, aVariantName );
}
return attrChanged;
@ -791,7 +846,7 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::RebuildRows()
if( !m_filter.IsEmpty() && !matcher.Find( ref.GetFullRef().Lower() ) )
continue;
if( m_excludeDNP && ( ref.GetSymbol()->GetDNP()
if( m_excludeDNP && ( ref.GetSymbol()->GetDNP( &ref.GetSheetPath() )
|| ref.GetSheetPath().GetDNP() ) )
{
continue;
@ -952,7 +1007,8 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::ExpandAfterSort()
}
void FIELDS_EDITOR_GRID_DATA_MODEL::ApplyData( SCH_COMMIT& aCommit, TEMPLATES& aTemplateFieldnames )
void FIELDS_EDITOR_GRID_DATA_MODEL::ApplyData( SCH_COMMIT& aCommit, TEMPLATES& aTemplateFieldnames,
std::set<wxString>& aVariantNames )
{
bool symbolModified = false;
std::unique_ptr<SCH_SYMBOL> symbolCopy;
@ -975,7 +1031,16 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::ApplyData( SCH_COMMIT& aCommit, TEMPLATES& a
// Attributes bypass the field logic, so handle them first
if( isAttribute( srcName ) )
{
symbolModified |= setAttributeValue( *symbol, srcName, srcValue );
if( aVariantNames.empty() )
{
symbolModified |= setAttributeValue( *symbol, srcName, srcValue );
}
else
{
for( const wxString& name : aVariantNames )
symbolModified |= setAttributeValue( *symbol, srcName, srcValue, name );
}
continue;
}
@ -984,6 +1049,10 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::ApplyData( SCH_COMMIT& aCommit, TEMPLATES& a
if( IsGeneratedField( srcName ) )
continue;
// Don't change values in the case of multiple variants selected.
if( srcValue == multipleValues )
continue;
SCH_FIELD* destField = symbol->GetField( srcName );
if( destField && destField->IsPrivate() )
@ -1017,15 +1086,40 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::ApplyData( SCH_COMMIT& aCommit, TEMPLATES& a
if( !destField )
continue;
// Reference is not editable from this dialog
if( destField->GetId() == FIELD_T::REFERENCE )
{
// Reference is not editable from this dialog
continue;
}
wxString previousValue = destField->GetText();
destField->SetText( symbol->Schematic()->ConvertRefsToKIIDs( srcValue ) );
if( aVariantNames.empty() )
{
destField->SetText( symbol->Schematic()->ConvertRefsToKIIDs( srcValue ) );
}
else
{
for( const wxString& variantName : aVariantNames )
{
std::optional<SCH_SYMBOL_VARIANT> variant = symbol->GetVariant( m_symbolsList[i].GetSheetPath(),
variantName );
if( !variant )
{
SCH_SYMBOL_VARIANT newVariant( variantName );
newVariant.m_Fields[srcName] = srcValue;
symbol->AddVariant( m_symbolsList[i].GetSheetPath(), newVariant );
symbolModified |= true;
}
else if( !variant->m_Fields.contains( srcName )
|| ( variant->m_Fields[srcName] != srcValue ) )
{
variant->m_Fields[srcName] = srcValue;
symbol->AddVariant( m_symbolsList[i].GetSheetPath(), *variant );
symbolModified |= true;
}
}
}
if( !createField && ( previousValue != srcValue ) )
symbolModified = true;
@ -1088,7 +1182,8 @@ int FIELDS_EDITOR_GRID_DATA_MODEL::GetDataWidth( int aCol )
}
void FIELDS_EDITOR_GRID_DATA_MODEL::ApplyBomPreset( const BOM_PRESET& aPreset )
void FIELDS_EDITOR_GRID_DATA_MODEL::ApplyBomPreset( const BOM_PRESET& aPreset,
const std::set<wxString>& aVariantNames )
{
// Hide and un-group everything by default
for( size_t i = 0; i < m_cols.size(); i++ )
@ -1116,7 +1211,7 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::ApplyBomPreset( const BOM_PRESET& aPreset )
// they won't be saved to the symbols anyway
if( col == -1 )
{
AddColumn( field.name, field.label, true );
AddColumn( field.name, field.label, true, aVariantNames );
col = GetFieldNameCol( field.name );
}
else
@ -1312,7 +1407,8 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::RemoveReferences( const SCH_REFERENCE_LIST&
}
void FIELDS_EDITOR_GRID_DATA_MODEL::UpdateReferences( const SCH_REFERENCE_LIST& aRefs )
void FIELDS_EDITOR_GRID_DATA_MODEL::UpdateReferences( const SCH_REFERENCE_LIST& aRefs,
const std::set<wxString>& aVariantNames )
{
bool refListChanged = false;
@ -1322,7 +1418,7 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::UpdateReferences( const SCH_REFERENCE_LIST&
// columns; we must have all fields in the symbol added to the data model at this point,
// and some of the data model columns may be variables that are not present in the symbol
for( const DATA_MODEL_COL& col : m_cols )
updateDataStoreSymbolField( *ref.GetSymbol(), col.m_fieldName );
updateDataStoreSymbolField( ref, col.m_fieldName, aVariantNames );
if( !m_symbolsList.Contains( ref ) )
{

19
eeschema/fields_data_model.h

@ -162,7 +162,8 @@ public:
static const wxString QUANTITY_VARIABLE;
static const wxString ITEM_NUMBER_VARIABLE;
void AddColumn( const wxString& aFieldName, const wxString& aLabel, bool aAddedByUser );
void AddColumn( const wxString& aFieldName, const wxString& aLabel, bool aAddedByUser,
const std::set<wxString>& aVariantNames );
void RemoveColumn( int aCol );
void RenameColumn( int aCol, const wxString& newName );
@ -270,7 +271,7 @@ public:
void CollapseForSort();
void ExpandAfterSort();
void ApplyData( SCH_COMMIT& aCommit, TEMPLATES& aTemplateFieldnames );
void ApplyData( SCH_COMMIT& aCommit, TEMPLATES& aTemplateFieldnames, std::set<wxString>& aVariantNames );
bool IsEdited() { return m_edited; }
@ -322,14 +323,14 @@ public:
return m_cols[aCol].m_show;
}
void ApplyBomPreset( const BOM_PRESET& preset );
void ApplyBomPreset( const BOM_PRESET& preset, const std::set<wxString>& aVariantNames );
BOM_PRESET GetBomSettings();
wxString Export( const BOM_FMT_PRESET& settings );
void AddReferences( const SCH_REFERENCE_LIST& aRefs );
void RemoveReferences( const SCH_REFERENCE_LIST& aRefs );
void RemoveSymbol( const SCH_SYMBOL& aSymbol );
void UpdateReferences( const SCH_REFERENCE_LIST& aRefs );
void UpdateReferences( const SCH_REFERENCE_LIST& aRefs, const std::set<wxString>& aVariantNames );
private:
static bool cmp( const DATA_MODEL_ROW& lhGroup, const DATA_MODEL_ROW& rhGroup,
@ -341,7 +342,8 @@ private:
// Helper functions to deal with translating wxGrid values to and from
// named field values like ${DNP}
bool isAttribute( const wxString& aFieldName );
wxString getAttributeValue( const SCH_SYMBOL&, const wxString& aAttributeName );
wxString getAttributeValue( const SCH_SYMBOL&, const wxString& aAttributeName,
const std::set<wxString>& aVariantNames );
/**
* Set the attribute value.
@ -349,10 +351,12 @@ private:
* @param aSymbol is the symbol to set the attribute.
* @param aAttributeName is the name of the symbol attribute.
* @param aValue is the value to set the attribute.
* @param aVariantName is an optional variant name to set the variant attribute.
* @retval true if the symbol attribute value has changed.
* @retval false if the symbol attribute has **not** changed.
*/
bool setAttributeValue( SCH_SYMBOL& aSymbol, const wxString& aAttributeName, const wxString& aValue );
bool setAttributeValue( SCH_SYMBOL& aSymbol, const wxString& aAttributeName, const wxString& aValue,
const wxString& aVariantName = wxEmptyString );
/* Helper function to get the resolved field value.
* Handles symbols that are missing fields that would have a variable
@ -362,7 +366,8 @@ private:
void Sort();
void updateDataStoreSymbolField( const SCH_SYMBOL& aSymbol, const wxString& aFieldName );
void updateDataStoreSymbolField( const SCH_REFERENCE& aSymbolRef, const wxString& aFieldName,
const std::set<wxString>& aVariantNames );
protected:
/**

7
eeschema/menubar.cpp

@ -320,6 +320,13 @@ void SCH_EDIT_FRAME::doReCreateMenuBar()
toolsMenu->AppendSeparator();
toolsMenu->Add( ACTIONS::updateSchematicFromPcb )->Enable( !Kiface().IsSingle() );
toolsMenu->AppendSeparator();
ACTION_MENU* submenuVariants = new ACTION_MENU( false, selTool );
submenuVariants->SetTitle( _( "Variants" ) );
submenuVariants->Add( SCH_ACTIONS::addVariant );
submenuVariants->Add( SCH_ACTIONS::removeVariant );
toolsMenu->Add( submenuVariants );
#ifdef KICAD_IPC_API
toolsMenu->AppendSeparator();
toolsMenu->Add( ACTIONS::pluginsReload );

11
eeschema/sch_edit_frame.h

@ -248,6 +248,15 @@ public:
*/
void UpdateDesignBlockOptions();
/**
* Update the variant name control on the main toolbar.
*
* Updating will attempt to maintain the current selection if it's available. If the current
* selection is no longer available, the default (no variant) will be selected.
*
*/
void UpdateVariantSelectionCtrl( const wxArrayString& aVariantNames );
/**
* Test all of the connectable objects in the schematic for unused connection points.
*
@ -1031,6 +1040,8 @@ private:
std::vector<LIB_ID> m_designBlockHistoryList;
SCH_DESIGN_BLOCK_PANE* m_designBlocksPane;
wxChoice* m_currentVariantCtrl;
#ifdef KICAD_IPC_API
std::unique_ptr<API_HANDLER_SCH> m_apiHandler;
#endif

46
eeschema/sch_field.cpp

@ -189,8 +189,7 @@ wxString SCH_FIELD::GetShownName() const
}
wxString SCH_FIELD::GetShownText( const SCH_SHEET_PATH* aPath, bool aAllowExtraText,
int aDepth ) const
wxString SCH_FIELD::GetShownText( const SCH_SHEET_PATH* aPath, bool aAllowExtraText, int aDepth ) const
{
std::function<bool( wxString* )> libSymbolResolver =
[&]( wxString* token ) -> bool
@ -248,7 +247,7 @@ wxString SCH_FIELD::GetShownText( const SCH_SHEET_PATH* aPath, bool aAllowExtraT
return label->ResolveTextVar( aPath, token, aDepth + 1 );
};
wxString text = EDA_TEXT::GetShownText( aAllowExtraText, aDepth );
wxString text = getUnescapedText( aPath );
if( IsNameShown() && aAllowExtraText )
text = GetShownName() << wxS( ": " ) << text;
@ -273,16 +272,6 @@ wxString SCH_FIELD::GetShownText( const SCH_SHEET_PATH* aPath, bool aAllowExtraT
}
}
if( m_id == FIELD_T::REFERENCE && aPath )
{
SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
// For more than one part per package, we must add the part selection
// A, B, ... or 1, 2, .. to the reference.
if( parentSymbol && parentSymbol->GetUnitCount() > 1 )
text << parentSymbol->SubReference( parentSymbol->GetUnitSelection( aPath ) );
}
if( m_id == FIELD_T::SHEET_FILENAME && aAllowExtraText && !IsNameShown() )
text = _( "File:" ) + wxS( " " ) + text;
@ -1598,6 +1587,37 @@ int SCH_FIELD::compare( const SCH_ITEM& aOther, int aCompareFlags ) const
}
wxString SCH_FIELD::getUnescapedText( const SCH_SHEET_PATH* aPath, const wxString& aVariantName ) const
{
wxString retv = EDA_TEXT::GetShownText( false );
// Special handling for parent object field instance and variant information.
if( m_parent && aPath )
{
switch( m_parent->Type() )
{
case SCH_SYMBOL_T:
{
const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( m_parent );
if( m_id == FIELD_T::REFERENCE )
retv = symbol->GetRef( aPath, true );
break;
}
case SCH_SHEET_T:
break;
default:
break;
}
}
return retv;
}
static struct SCH_FIELD_DESC
{
SCH_FIELD_DESC()

6
eeschema/sch_field.h

@ -131,8 +131,7 @@ public:
* with the ${} stripped.
*/
wxString GetShownName() const;
wxString GetShownText( const SCH_SHEET_PATH* aPath, bool aAllowExtraText,
int aDepth = 0 ) const;
wxString GetShownText( const SCH_SHEET_PATH* aPath, bool aAllowExtraText, int aDepth = 0 ) const;
wxString GetShownText( bool aAllowExtraText, int aDepth = 0 ) const override;
@ -325,6 +324,9 @@ protected:
void setId( FIELD_T aId );
wxString getUnescapedText( const SCH_SHEET_PATH* aPath = nullptr,
const wxString& aVariantName = wxEmptyString ) const;
private:
FIELD_T m_id; ///< Field id, @see enum FIELD_T
int m_ordinal; ///< Sort order for non-mandatory fields

3
eeschema/sch_file_versions.h

@ -129,4 +129,5 @@
//#define SEXPR_SCHEMATIC_FILE_VERSION 20250610 // DNP, etc. flags for rule areas
//#define SEXPR_SCHEMATIC_FILE_VERSION 20250827 // Custom body styles
//#define SEXPR_SCHEMATIC_FILE_VERSION 20250829 // Rounded Rectangles
#define SEXPR_SCHEMATIC_FILE_VERSION 20250901 // Stacked Pin notation
//#define SEXPR_SCHEMATIC_FILE_VERSION 20250901 // Stacked Pin notation
#define SEXPR_SCHEMATIC_FILE_VERSION 20250922 // Schematic variants.

66
eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr.cpp

@ -735,7 +735,7 @@ void SCH_IO_KICAD_SEXPR::saveSymbol( SCH_SYMBOL* aSymbol, const SCHEMATIC& aSche
KICAD_FORMAT::FormatBool( m_out, "exclude_from_sim", aSymbol->GetExcludedFromSim() );
KICAD_FORMAT::FormatBool( m_out, "in_bom", !aSymbol->GetExcludedFromBOM() );
KICAD_FORMAT::FormatBool( m_out, "on_board", !aSymbol->GetExcludedFromBoard() );
KICAD_FORMAT::FormatBool( m_out, "dnp", aSymbol->GetDNP() );
KICAD_FORMAT::FormatBool( m_out, "dnp", ordinalInstance.m_DNP );
AUTOPLACE_ALGO fieldsAutoplaced = aSymbol->GetFieldsAutoplaced();
@ -861,10 +861,37 @@ void SCH_IO_KICAD_SEXPR::saveSymbol( SCH_SYMBOL* aSymbol, const SCHEMATIC& aSche
path = tmp.AsString();
m_out->Print( "(path %s (reference %s) (unit %d))",
m_out->Print( "(path %s (reference %s) (unit %d)",
m_out->Quotew( path ).c_str(),
m_out->Quotew( instance.m_Reference ).c_str(),
instance.m_Unit );
if( !instance.m_Variants.empty() )
{
for( const auto&[name, variant] : instance.m_Variants )
{
m_out->Print( "(variant (name %s)", m_out->Quotew( name ).c_str() );
if( variant.m_DNP != aSymbol->GetDNP() )
KICAD_FORMAT::FormatBool( m_out, "dnp", variant.m_DNP );
if( variant.m_ExcludedFromSim != aSymbol->GetExcludedFromSim() )
KICAD_FORMAT::FormatBool( m_out, "exclude_from_sim", variant.m_ExcludedFromSim );
if( variant.m_ExcludedFromBOM != aSymbol->GetExcludedFromBOM() )
KICAD_FORMAT::FormatBool( m_out, "in_bom", variant.m_ExcludedFromBOM );
for( const auto&[fname, fvalue] : variant.m_Fields )
{
m_out->Print( "(field (name %s) (value %s))",
m_out->Quotew( fname ).c_str(), m_out->Quotew( fvalue ).c_str() );
}
m_out->Print( ")" ); // Closes `variant` token.
}
}
m_out->Print( ")" ); // Closes `path` token.
}
m_out->Print( ")" ); // Closes `project`.
@ -873,7 +900,7 @@ void SCH_IO_KICAD_SEXPR::saveSymbol( SCH_SYMBOL* aSymbol, const SCHEMATIC& aSche
m_out->Print( ")" ); // Closes `instances`.
}
m_out->Print( ")" ); // Closes `symbol`.
m_out->Print( ")" ); // Closes `symbol`.
}
@ -1070,10 +1097,37 @@ void SCH_IO_KICAD_SEXPR::saveSheet( SCH_SHEET* aSheet, const SCH_SHEET_LIST& aSh
wxString path = sheetInstances[i].m_Path.AsString();
m_out->Print( "(path %s (page %s))",
m_out->Print( "(path %s (page %s)",
m_out->Quotew( path ).c_str(),
m_out->Quotew( sheetInstances[i].m_PageNumber ).c_str() );
if( !sheetInstances[i].m_Variants.empty() )
{
for( const auto&[name, variant] : sheetInstances[i].m_Variants )
{
m_out->Print( "(variant (name %s)", m_out->Quotew( name ).c_str() );
if( variant.m_DNP != aSheet->GetDNP() )
KICAD_FORMAT::FormatBool( m_out, "dnp", variant.m_DNP );
if( variant.m_ExcludedFromSim != aSheet->GetExcludedFromSim() )
KICAD_FORMAT::FormatBool( m_out, "exclude_from_sim", variant.m_ExcludedFromSim );
if( variant.m_ExcludedFromBOM != aSheet->GetExcludedFromBOM() )
KICAD_FORMAT::FormatBool( m_out, "in_bom", variant.m_ExcludedFromBOM );
for( const auto&[fname, fvalue] : variant.m_Fields )
{
m_out->Print( "(field (name %s) (value %s))",
m_out->Quotew( fname ).c_str(), m_out->Quotew( fvalue ).c_str() );
}
m_out->Print( ")" ); // Closes `variant` token.
}
}
m_out->Print( ")" ); // Closes `path` token.
if( inProjectClause && ( ( i + 1 == sheetInstances.size() )
|| lastProjectUuid != sheetInstances[i+1].m_Path[0] ) )
{
@ -1082,10 +1136,10 @@ void SCH_IO_KICAD_SEXPR::saveSheet( SCH_SHEET* aSheet, const SCH_SHEET_LIST& aSh
}
}
m_out->Print( ")" ); // Closes `instances` token.
m_out->Print( ")" ); // Closes `instances` token.
}
m_out->Print( ")" ); // Closes sheet token.
m_out->Print( ")" ); // Closes sheet token.
}

123
eeschema/sch_io/kicad_sexpr/sch_io_kicad_sexpr_parser.cpp

@ -3349,12 +3349,70 @@ SCH_SYMBOL* SCH_IO_KICAD_SEXPR_PARSER::parseSchematicSymbol()
NeedRIGHT();
break;
default:
Expecting( "reference, unit, value or footprint" );
case T_variant:
{
SCH_SYMBOL_VARIANT variant;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_name:
NeedSYMBOL();
variant.m_Name = FromUTF8();
NeedRIGHT();
break;
case T_dnp:
variant.m_DNP = parseBool();
NeedRIGHT();
break;
case T_exclude_from_sim:
variant.m_ExcludedFromSim = parseBool();
NeedRIGHT();
break;
case T_in_bom:
variant.m_ExcludedFromBOM = parseBool();
NeedRIGHT();
break;
case T_field:
{
NeedSYMBOL();
wxString fieldName = FromUTF8();
NeedRIGHT();
NeedSYMBOL();
wxString fieldValue = FromUTF8();
NeedRIGHT();
variant.m_Fields[fieldName] = fieldValue;
break;
}
default:
Expecting( "dnp, exclude_from_sim, field, in_bom, or name" );
}
instance.m_Variants[variant.m_Name] = variant;
}
NeedRIGHT();
break;
}
symbol->AddHierarchicalReference( instance );
default:
Expecting( "reference, unit, value, footprint, or variant" );
}
}
symbol->AddHierarchicalReference( instance );
}
}
@ -3721,8 +3779,65 @@ SCH_SHEET* SCH_IO_KICAD_SEXPR_PARSER::parseSheet()
break;
}
case T_variant:
{
SCH_SHEET_VARIANT variant;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_name:
NeedSYMBOL();
variant.m_Name = FromUTF8();
NeedRIGHT();
break;
case T_dnp:
variant.m_DNP = parseBool();
NeedRIGHT();
break;
case T_exclude_from_sim:
variant.m_ExcludedFromSim = parseBool();
NeedRIGHT();
break;
case T_in_bom:
variant.m_ExcludedFromBOM = parseBool();
NeedRIGHT();
break;
case T_field:
{
NeedSYMBOL();
wxString fieldName = FromUTF8();
NeedRIGHT();
NeedSYMBOL();
wxString fieldValue = FromUTF8();
NeedRIGHT();
variant.m_Fields[fieldName] = fieldValue;
break;
}
default:
Expecting( "dnp, exclude_from_sim, field, in_bom, or name" );
}
instance.m_Variants[variant.m_Name] = variant;
}
NeedRIGHT();
break;
}
default:
Expecting( "page" );
Expecting( "page or variant" );
}
}

20
eeschema/sch_item.cpp

@ -244,14 +244,15 @@ SYMBOL* SCH_ITEM::GetParentSymbol()
}
bool SCH_ITEM::ResolveExcludedFromSim() const
bool SCH_ITEM::ResolveExcludedFromSim( const SCH_SHEET_PATH* aInstance,
const wxString& aVariantName ) const
{
if( GetExcludedFromSim() )
if( GetExcludedFromSim( aInstance, aVariantName ) )
return true;
for( SCH_RULE_AREA* area : m_rule_areas_cache )
{
if( area->GetExcludedFromSim() )
if( area->GetExcludedFromSim( aInstance, aVariantName ) )
return true;
}
@ -259,14 +260,15 @@ bool SCH_ITEM::ResolveExcludedFromSim() const
}
bool SCH_ITEM::ResolveExcludedFromBOM() const
bool SCH_ITEM::ResolveExcludedFromBOM( const SCH_SHEET_PATH* aInstance,
const wxString& aVariantName ) const
{
if( GetExcludedFromBOM() )
if( GetExcludedFromBOM( aInstance, aVariantName ) )
return true;
for( SCH_RULE_AREA* area : m_rule_areas_cache )
{
if( area->GetExcludedFromBOM() )
if( area->GetExcludedFromBOM( aInstance, aVariantName ) )
return true;
}
@ -289,14 +291,14 @@ bool SCH_ITEM::ResolveExcludedFromBoard() const
}
bool SCH_ITEM::ResolveDNP() const
bool SCH_ITEM::ResolveDNP( const SCH_SHEET_PATH* aInstance, const wxString& aVariantName ) const
{
if( GetDNP() )
if( GetDNP( aInstance, aVariantName ) )
return true;
for( SCH_RULE_AREA* area : m_rule_areas_cache )
{
if( area->GetDNP() )
if( area->GetDNP( aInstance, aVariantName ) )
return true;
}

29
eeschema/sch_item.h

@ -249,21 +249,30 @@ public:
void SetPrivate( bool aPrivate ) { m_private = aPrivate; }
bool IsPrivate() const { return m_private; }
virtual void SetExcludedFromSim( bool aExclude ) { }
virtual bool GetExcludedFromSim() const { return false; }
bool ResolveExcludedFromSim() const;
virtual void SetExcludedFromBOM( bool aExcludeFromBOM ) { }
virtual bool GetExcludedFromBOM() const { return false; }
bool ResolveExcludedFromBOM() const;
virtual void SetExcludedFromSim( bool aExclude, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) { }
virtual bool GetExcludedFromSim( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const { return false; }
bool ResolveExcludedFromSim( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const;
virtual void SetExcludedFromBOM( bool aExcludeFromBOM, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) { }
virtual bool GetExcludedFromBOM( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const { return false; }
bool ResolveExcludedFromBOM( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const;
virtual void SetExcludedFromBoard( bool aExcludeFromBoard ) { }
virtual bool GetExcludedFromBoard() const { return false; }
bool ResolveExcludedFromBoard() const;
virtual void SetDNP( bool aDNP ) { }
virtual bool GetDNP() const { return false; }
bool ResolveDNP() const;
virtual void SetDNP( bool aDNP, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) { }
virtual bool GetDNP( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const { return false; }
bool ResolveDNP( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const;
/**
* Check if object is movable from the anchor point.

11
eeschema/sch_painter.cpp

@ -2670,9 +2670,14 @@ wxString SCH_PAINTER::expandLibItemTextVars( const wxString& aSourceText,
void SCH_PAINTER::draw( const SCH_SYMBOL* aSymbol, int aLayer )
{
bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
bool DNP = aSymbol->GetDNP();
bool markExclusion = eeconfig()->m_Appearance.mark_sim_exclusions
&& aSymbol->GetExcludedFromSim();
std::optional<SCH_SHEET_PATH> optSheetPath;
if( m_schematic )
optSheetPath = m_schematic->CurrentSheet();
bool DNP = aSymbol->GetDNP( nullptr );
bool markExclusion = eeconfig()->m_Appearance.mark_sim_exclusions && aSymbol->GetExcludedFromSim( nullptr );
if( m_schSettings.IsPrinting() && drawingShadows )
return;

32
eeschema/sch_rule_area.h

@ -61,14 +61,32 @@ public:
/**
* Set or clear the exclude from simulation flag.
*/
void SetExcludedFromSim( bool aExcludeFromSim ) override { m_excludedFromSim = aExcludeFromSim; }
bool GetExcludedFromSim() const override { return m_excludedFromSim; }
void SetExcludedFromSim( bool aExcludeFromSim, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override
{
m_excludedFromSim = aExcludeFromSim;
}
bool GetExcludedFromSim( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override
{
return m_excludedFromSim;
}
/**
* Set or clear the exclude from schematic bill of materials flag.
*/
void SetExcludedFromBOM( bool aExcludeFromBOM ) override { m_excludedFromBOM = aExcludeFromBOM; }
bool GetExcludedFromBOM() const override { return m_excludedFromBOM; }
void SetExcludedFromBOM( bool aExcludeFromBOM, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override
{
m_excludedFromBOM = aExcludeFromBOM;
}
bool GetExcludedFromBOM( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override
{
return m_excludedFromBOM;
}
/**
* Set or clear exclude from board netlist flag.
@ -79,8 +97,10 @@ public:
/**
* Set or clear the 'Do Not Populate' flag.
*/
bool GetDNP() const override { return m_DNP; }
void SetDNP( bool aDNP ) override { m_DNP = aDNP; }
bool GetDNP( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override { return m_DNP; }
void SetDNP( bool aDNP, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override { m_DNP = aDNP; }
std::vector<int> ViewGetLayers() const override;

104
eeschema/sch_screen.cpp

@ -977,12 +977,12 @@ void SCH_SCREEN::Plot( PLOTTER* aPlotter, const SCH_PLOT_OPTS& aPlotOpts ) const
{
field.ClearRenderCache();
field.Plot( aPlotter, false, aPlotOpts, sym->GetUnit(), sym->GetBodyStyle(), { 0, 0 },
sym->GetDNP() );
static_cast<const SYMBOL*>( sym )->GetDNP() );
}
sym->PlotPins( aPlotter );
if( sym->GetDNP() )
if( static_cast<const SYMBOL*>( sym )->GetDNP() )
sym->PlotDNP( aPlotter );
}
@ -1755,6 +1755,80 @@ bool SCH_SCREEN::InProjectPath() const
}
std::set<wxString> SCH_SCREEN::GetVariantNames() const
{
std::set<wxString> variantNames;
for( const SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) )
{
const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( item );
wxCHECK2( symbol, continue );
const std::vector<SCH_SYMBOL_INSTANCE> symbolInstances = symbol->GetInstances();
for( const SCH_SYMBOL_INSTANCE& instance : symbolInstances )
{
for( const auto& [name, variant] : instance.m_Variants )
variantNames.emplace( name );
}
}
for( const SCH_ITEM* item : Items().OfType( SCH_SHEET_T ) )
{
const SCH_SHEET* sheet = static_cast<const SCH_SHEET*>( item );
wxCHECK2( sheet, continue );
const std::vector<SCH_SHEET_INSTANCE> sheetInstances = sheet->GetInstances();
for( const SCH_SHEET_INSTANCE& instance : sheetInstances )
{
for( const auto& [name, variant] : instance.m_Variants )
variantNames.emplace( name );
}
}
return variantNames;
}
void SCH_SCREEN::DeleteVariant( const wxString& aVariantName )
{
wxCHECK( !aVariantName.IsEmpty(), /* void */ );
for( const SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) )
{
const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( item );
wxCHECK2( symbol, continue );
std::vector<SCH_SYMBOL_INSTANCE> symbolInstances = symbol->GetInstances();
for( SCH_SYMBOL_INSTANCE& instance : symbolInstances )
{
if( instance.m_Variants.contains( aVariantName ) )
instance.m_Variants.erase( aVariantName );
}
}
for( const SCH_ITEM* item : Items().OfType( SCH_SHEET_T ) )
{
const SCH_SHEET* sheet = static_cast<const SCH_SHEET*>( item );
wxCHECK2( sheet, continue );
std::vector<SCH_SHEET_INSTANCE> sheetInstances = sheet->GetInstances();
for( SCH_SHEET_INSTANCE& instance : sheetInstances )
{
if( instance.m_Variants.contains( aVariantName ) )
instance.m_Variants.erase( aVariantName );
}
}
}
#if defined(DEBUG)
void SCH_SCREEN::Show( int nestLevel, std::ostream& os ) const
{
@ -1841,7 +1915,8 @@ void SCH_SCREENS::buildScreenList( SCH_SHEET* aSheet )
{
SCH_SCREEN* screen = aSheet->GetScreen();
wxCHECK_RET( screen, "No screen for aSheet" );
if( !screen )
return;
addScreenToList( screen, aSheet );
@ -2185,3 +2260,26 @@ bool SCH_SCREENS::HasSymbolFieldNamesWithWhiteSpace() const
return false;
}
std::set<wxString> SCH_SCREENS::GetVariantNames() const
{
std::set<wxString> variantNames;
for( const SCH_SCREEN* screen : m_screens )
{
for( const wxString& variantName : screen->GetVariantNames() )
variantNames.emplace( variantName );
}
return variantNames;
}
void SCH_SCREENS::DeleteVariant( const wxString& aVariantName )
{
wxCHECK( !aVariantName.IsEmpty(), /* void */ );
for( SCH_SCREEN* screen : m_screens )
screen->DeleteVariant( aVariantName );
}

8
eeschema/sch_screen.h

@ -619,6 +619,10 @@ public:
*/
wxString GroupsSanityCheckInternal( bool repair );
std::set<wxString> GetVariantNames() const;
void DeleteVariant( const wxString& aVariantName );
private:
friend SCH_EDIT_FRAME; // Only to populate m_symbolInstances.
friend SCH_IO_KICAD_SEXPR_PARSER; // Only to load instance information from schematic file.
@ -854,6 +858,10 @@ public:
bool HasSymbolFieldNamesWithWhiteSpace() const;
std::set<wxString> GetVariantNames() const;
void DeleteVariant( const wxString& aVariantName );
private:
void addScreenToList( SCH_SCREEN* aScreen, SCH_SHEET* aSheet );
void buildScreenList( SCH_SHEET* aSheet);

44
eeschema/sch_sheet.cpp

@ -1631,6 +1631,42 @@ double SCH_SHEET::Similarity( const SCH_ITEM& aOther ) const
}
bool SCH_SHEET::GetDNPProp() const
{
return GetDNP( nullptr, Schematic()->GetCurrentVariant() );
}
void SCH_SHEET::SetDNPProp( bool aEnable )
{
SetDNP( aEnable, nullptr, Schematic()->GetCurrentVariant() );
}
bool SCH_SHEET::GetExcludedFromSimProp() const
{
return GetExcludedFromSim( nullptr, Schematic()->GetCurrentVariant() );
}
void SCH_SHEET::SetExcludedFromSimProp( bool aEnable )
{
SetExcludedFromSim( aEnable, nullptr, Schematic()->GetCurrentVariant() );
}
bool SCH_SHEET::GetExcludedFromBOMProp() const
{
return GetExcludedFromBOM( nullptr, Schematic()->GetCurrentVariant() );
}
void SCH_SHEET::SetExcludedFromBOMProp( bool aEnable )
{
SetExcludedFromBOM( aEnable, nullptr, Schematic()->GetCurrentVariant() );
}
#if defined(DEBUG)
void SCH_SHEET::Show( int nestLevel, std::ostream& os ) const
@ -1691,15 +1727,15 @@ static struct SCH_SHEET_DESC
&SCH_SHEET::SetExcludedFromBoard, &SCH_SHEET::GetExcludedFromBoard ),
groupAttributes );
propMgr.AddProperty( new PROPERTY<SCH_SHEET, bool>( _HKI( "Exclude From Simulation" ),
&SCH_SHEET::SetExcludedFromSim, &SCH_SHEET::GetExcludedFromSim ),
&SCH_SHEET::SetExcludedFromSimProp, &SCH_SHEET::GetExcludedFromSimProp ),
groupAttributes );
propMgr.AddProperty(
new PROPERTY<SCH_SHEET, bool>( _HKI( "Exclude From Bill of Materials" ),
&SCH_SHEET::SetExcludedFromBOM,
&SCH_SHEET::GetExcludedFromBOM ),
&SCH_SHEET::SetExcludedFromBOMProp,
&SCH_SHEET::GetExcludedFromBOMProp ),
groupAttributes );
propMgr.AddProperty( new PROPERTY<SCH_SHEET, bool>( _HKI( "Do not Populate" ),
&SCH_SHEET::SetDNP, &SCH_SHEET::GetDNP ),
&SCH_SHEET::SetDNPProp, &SCH_SHEET::GetDNPProp ),
groupAttributes );
}
} _SCH_SHEET_DESC;

40
eeschema/sch_sheet.h

@ -381,14 +381,38 @@ public:
/**
* Set or clear the exclude from simulation flag.
*/
void SetExcludedFromSim( bool aExcludeFromSim ) override { m_excludedFromSim = aExcludeFromSim; }
bool GetExcludedFromSim() const override { return m_excludedFromSim; }
void SetExcludedFromSim( bool aExcludeFromSim, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override
{
m_excludedFromSim = aExcludeFromSim;
}
bool GetExcludedFromSim( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override
{
return m_excludedFromSim;
}
bool GetExcludedFromSimProp() const;
void SetExcludedFromSimProp( bool aEnable );
/**
* Set or clear the exclude from schematic bill of materials flag.
*/
void SetExcludedFromBOM( bool aExcludeFromBOM ) override { m_excludedFromBOM = aExcludeFromBOM; }
bool GetExcludedFromBOM() const override { return m_excludedFromBOM; }
void SetExcludedFromBOM( bool aExcludeFromBOM, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override
{
m_excludedFromBOM = aExcludeFromBOM;
}
bool GetExcludedFromBOM( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override
{
return m_excludedFromBOM;
}
bool GetExcludedFromBOMProp() const;
void SetExcludedFromBOMProp( bool aEnable );
/**
* Set or clear exclude from board netlist flag.
@ -399,8 +423,12 @@ public:
/**
* Set or clear the 'Do Not Populate' flags
*/
bool GetDNP() const override { return m_DNP; }
void SetDNP( bool aDNP ) override { m_DNP = aDNP; }
bool GetDNP( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override { return m_DNP; }
void SetDNP( bool aDNP, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override { m_DNP = aDNP; }
bool GetDNPProp() const;
void SetDNPProp( bool aEnable );
wxString GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const override;

14
eeschema/sch_sheet_path.cpp

@ -96,6 +96,14 @@ public:
};
void SCH_SYMBOL_VARIANT::InitializeAttributes( const SCH_SYMBOL& aSymbol )
{
m_DNP = aSymbol.GetDNP();
m_ExcludedFromBOM = aSymbol.GetExcludedFromBOM();
m_ExcludedFromSim = aSymbol.GetExcludedFromSim();
}
namespace std
{
size_t hash<SCH_SHEET_PATH>::operator()( const SCH_SHEET_PATH& path ) const
@ -400,6 +408,12 @@ void SCH_SHEET_PATH::UpdateAllScreenReferences() const
|| aItem->Type() == SCH_SHAPE_T );
} );
std::optional<wxString> variantName;
const SCHEMATIC* schematic = LastScreen()->Schematic();
if( schematic )
variantName = schematic->GetCurrentVariant();
for( SCH_ITEM* item : items )
{
if( item->Type() == SCH_SYMBOL_T )

90
eeschema/sch_sheet_path.h

@ -32,11 +32,62 @@
#define CLASS_DRAWSHEET_PATH_H
#include <map>
#include <memory>
#include <optional>
#include <kiid.h>
#include <wx/string.h>
class SCH_SYMBOL;
class SCH_SHEET;
/**
* Object to store and handle common variant information.
*/
class VARIANT
{
public:
VARIANT( const wxString& aName = wxEmptyString ) :
m_Name( aName ),
m_ExcludedFromSim( false ),
m_ExcludedFromBOM( false ),
m_DNP( false )
{
}
virtual ~VARIANT() = default;
wxString m_Name;
bool m_ExcludedFromSim;
bool m_ExcludedFromBOM;
bool m_DNP;
std::map<wxString, wxString> m_Fields;
};
/**
* Variant information for a schematic symbol.
*
* Schematic symbol variants are a set of field and/or properties differentials against the default symbol
* values. Each symbol instance may contain 0 or more variants.
*
* @note The two exceptions to this are the #REFERENCE field and the #SYMBOL::m_excludeFromBoard property.
* Changing either of these would effectively be a new board. They are immutable and will always
* be the symbol default value.
*/
class SCH_SYMBOL_VARIANT : public VARIANT
{
public:
SCH_SYMBOL_VARIANT( const wxString& aName = wxEmptyString ) :
VARIANT( aName )
{}
void InitializeAttributes( const SCH_SYMBOL& aSymbol );
virtual ~SCH_SYMBOL_VARIANT() = default;
};
/**
* A simple container for schematic symbol instance information.
*/
@ -49,12 +100,42 @@ struct SCH_SYMBOL_INSTANCE
int m_Unit = 1;
// Do not use. This is left over from the dubious decision to instantiate symbol value
// and footprint fields.
// and footprint fields. This is now handle by variants.
wxString m_Value;
wxString m_Footprint;
// The project name associated with this instance.
wxString m_ProjectName;
bool m_DNP = false;
bool m_ExcludedFromBOM = false;
bool m_ExcludedFromSim = false;
/// A list of symbol variants.
std::map<wxString, SCH_SYMBOL_VARIANT> m_Variants;
};
/**
* Variant information for a schematic sheet.
*
* Schematic sheet variants are a set of field and/or properties differentials against the default sheet
* values. Each sheet instance may contain 0 or more variants.
*
* @note The exceptions to this are the #SHEET_NAME and #SHEET_FILENAME fields and the #SCH_SHEET::m_excludeFromBoard
* property. Changing any of these would effectively be a new board. They are immutable and will always be
* the sheet default value.
*/
class SCH_SHEET_VARIANT : public VARIANT
{
public:
SCH_SHEET_VARIANT() :
VARIANT()
{}
virtual ~SCH_SHEET_VARIANT() = default;
void InitializeAttributes( const SCH_SHEET& aSymbol );
};
@ -69,6 +150,13 @@ struct SCH_SHEET_INSTANCE
// The project name associated with this instance.
wxString m_ProjectName;
bool m_DNP = false;
bool m_ExcludedFromBOM = false;
bool m_ExcludedFromSim = false;
/// A list of sheet variants.
std::map<wxString, SCH_SHEET_VARIANT> m_Variants;
};

268
eeschema/sch_symbol.cpp

@ -767,6 +767,222 @@ void SCH_SYMBOL::SetUnitSelection( const SCH_SHEET_PATH* aSheet, int aUnitSelect
}
void SCH_SYMBOL::SetDNP(bool aEnable, const SCH_SHEET_PATH* aInstance, const wxString& aVariantName )
{
if( !aInstance || aVariantName.IsEmpty() )
{
m_DNP = aEnable;
return;
}
SCH_SYMBOL_INSTANCE instance;
wxCHECK_MSG( GetInstance( instance, aInstance->Path() ), /* void */,
wxString::Format( wxS( "Cannot get DNP attribute for invalid sheet path '%s'." ),
aInstance->PathHumanReadable() ) );
if( aVariantName.IsEmpty() )
{
instance.m_DNP = aEnable;
}
else
{
if( instance.m_Variants.contains( aVariantName ) )
{
instance.m_Variants[aVariantName].m_DNP = aEnable;
}
else
{
SCH_SYMBOL_VARIANT variant( aVariantName );
variant.InitializeAttributes( *this );
variant.m_DNP = aEnable;
AddVariant( *aInstance, variant );
}
}
}
bool SCH_SYMBOL::GetDNP( const SCH_SHEET_PATH* aInstance, const wxString& aVariantName ) const
{
if( !aInstance || aVariantName.IsEmpty() )
return m_DNP;
SCH_SYMBOL_INSTANCE instance;
wxCHECK_MSG( GetInstance( instance, aInstance->Path() ), m_DNP,
wxString::Format( wxS( "Cannot get DNP attribute for invalid sheet path '%s'." ),
aInstance->PathHumanReadable() ) );
if( aVariantName.IsEmpty() )
{
return instance.m_DNP;
}
else
{
wxCHECK_MSG( instance.m_Variants.contains( aVariantName ), false,
wxString::Format( wxS( "Cannot get DNP attribute for invalid sheet path '%s' for variant '%s'." ),
aInstance->PathHumanReadable(), aVariantName ) );
return instance.m_Variants[aVariantName].m_DNP;
}
return m_DNP;
}
void SCH_SYMBOL::SetDNP( bool aEnable, const SCH_SHEET_PATH& aInstance, const std::vector<wxString>& aVariantNames )
{
for( const wxString& variantName : aVariantNames )
SetDNP( aEnable, &aInstance, variantName );
}
void SCH_SYMBOL::SetExcludedFromBOM( bool aEnable, const SCH_SHEET_PATH* aInstance,
const wxString& aVariantName )
{
if( !aInstance || aVariantName.IsEmpty() )
{
m_excludedFromBOM = aEnable;
return;
}
SCH_SYMBOL_INSTANCE instance;
wxCHECK_MSG( GetInstance( instance, aInstance->Path() ), /* void */,
wxString::Format( wxS( "Cannot get DNP attribute for invalid sheet path '%s'." ),
aInstance->PathHumanReadable() ) );
if( aVariantName.IsEmpty() )
{
instance.m_ExcludedFromBOM = aEnable;
}
else
{
if( instance.m_Variants.contains( aVariantName ) )
{
instance.m_Variants[aVariantName].m_ExcludedFromBOM = aEnable;
}
else
{
SCH_SYMBOL_VARIANT variant( aVariantName );
variant.InitializeAttributes( *this );
variant.m_ExcludedFromBOM = aEnable;
AddVariant( *aInstance, variant );
}
}
}
bool SCH_SYMBOL::GetExcludedFromBOM( const SCH_SHEET_PATH* aInstance, const wxString& aVariantName ) const
{
if( !aInstance || aVariantName.IsEmpty() )
return m_excludedFromBOM;
SCH_SYMBOL_INSTANCE instance;
wxCHECK_MSG( GetInstance( instance, aInstance->Path() ), m_excludedFromBOM,
wxString::Format( wxS( "Cannot get DNP attribute for invalid sheet path '%s'." ),
aInstance->PathHumanReadable() ) );
if( aVariantName.IsEmpty() )
{
return instance.m_ExcludedFromBOM;
}
else
{
wxCHECK_MSG( instance.m_Variants.contains( aVariantName ), false,
wxString::Format( wxS( "Cannot get DNP attribute for invalid sheet path '%s' variant '%s'." ),
aInstance->PathHumanReadable(), aVariantName ) );
return instance.m_Variants[aVariantName].m_ExcludedFromBOM;
}
return m_excludedFromBOM;
}
void SCH_SYMBOL::SetExcludedFromBOM( bool aEnable, const SCH_SHEET_PATH& aInstance,
const std::vector<wxString>& aVariantNames )
{
for( const wxString& variantName : aVariantNames )
SetExcludedFromBOM( aEnable, &aInstance, variantName );
}
void SCH_SYMBOL::SetExcludedFromSim( bool aEnable, const SCH_SHEET_PATH* aInstance, const wxString& aVariantName )
{
if( !aInstance || aVariantName.IsEmpty() )
{
m_excludedFromSim = aEnable;
return;
}
SCH_SYMBOL_INSTANCE instance;
wxCHECK_MSG( GetInstance( instance, aInstance->Path() ), /* void */,
wxString::Format( wxS( "Cannot get DNP attribute for invalid sheet path '%s'." ),
aInstance->PathHumanReadable() ) );
if( aVariantName.IsEmpty() )
{
instance.m_ExcludedFromSim = aEnable;
}
else
{
if( instance.m_Variants.contains( aVariantName ) )
{
instance.m_Variants[aVariantName].m_ExcludedFromSim = aEnable;
}
else
{
SCH_SYMBOL_VARIANT variant( aVariantName );
variant.InitializeAttributes( *this );
variant.m_ExcludedFromSim = aEnable;
AddVariant( *aInstance, variant );
}
}
}
bool SCH_SYMBOL::GetExcludedFromSim( const SCH_SHEET_PATH* aInstance, const wxString& aVariantName ) const
{
if( !aInstance || aVariantName.IsEmpty() )
return m_excludedFromSim;
SCH_SYMBOL_INSTANCE instance;
wxCHECK_MSG( GetInstance( instance, aInstance->Path() ), m_excludedFromSim,
wxString::Format( wxS( "Cannot get DNP attribute for invalid sheet path '%s'." ),
aInstance->PathHumanReadable() ) );
if( aVariantName.IsEmpty() )
{
return instance.m_ExcludedFromSim;
}
else
{
wxCHECK_MSG( instance.m_Variants.contains( aVariantName ), false,
wxString::Format( wxS( "Cannot get DNP attribute for invalid sheet path '%s' variant '%s'." ),
aInstance->PathHumanReadable(), aVariantName ) );
return instance.m_Variants[aVariantName].m_ExcludedFromSim;
}
return m_excludedFromSim;
}
void SCH_SYMBOL::SetExcludedFromSim( bool aEnable, const SCH_SHEET_PATH& aInstance,
const std::vector<wxString>& aVariantNames )
{
for( const wxString& variantName : aVariantNames )
SetExcludedFromSim( aEnable, &aInstance, variantName );
}
void SCH_SYMBOL::SetUnitSelection( int aUnitSelection )
{
for( SCH_SYMBOL_INSTANCE& instance : m_instanceReferences )
@ -1062,7 +1278,7 @@ void SCH_SYMBOL::SyncOtherUnits( const SCH_SHEET_PATH& aSourceSheet, SCH_COMMIT&
otherUnit->SetExcludedFromBoard( m_excludedFromBoard );
if( updateDNP )
otherUnit->SetDNP( m_DNP );
otherUnit->SetDNP( GetDNP( &aSourceSheet ), &sheet );
if( updatePins )
{
@ -1921,7 +2137,7 @@ void SCH_SYMBOL::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_
if( GetExcludedFromBoard() )
msgs.Add( _( "Board" ) );
if( GetDNP() )
if( GetDNP( currentSheet ) )
msgs.Add( _( "DNP" ) );
msg = wxJoin( msgs, '|' );
@ -2850,6 +3066,42 @@ std::unordered_set<wxString> SCH_SYMBOL::GetComponentClassNames( const SCH_SHEET
}
std::optional<SCH_SYMBOL_VARIANT> SCH_SYMBOL::GetVariant( const SCH_SHEET_PATH& aInstance,
const wxString& aVariantName ) const
{
SCH_SYMBOL_INSTANCE instance;
if( !GetInstance( instance, aInstance.Path() ) || !instance.m_Variants.contains( aVariantName ) )
return std::nullopt;
return instance.m_Variants.find( aVariantName )->second;
}
void SCH_SYMBOL::AddVariant( const SCH_SHEET_PATH& aInstance, const SCH_SYMBOL_VARIANT& aVariant )
{
SCH_SYMBOL_INSTANCE instance;
// The instance path must already exist.
if( !GetInstance( instance, aInstance.Path() ) )
return;
instance.m_Variants.emplace( std::make_pair( aVariant.m_Name, aVariant ) );
}
void SCH_SYMBOL::DeleteVariant( const SCH_SHEET_PATH& aInstance, const wxString& aVariantName )
{
SCH_SYMBOL_INSTANCE instance;
// The instance path must already exist.
if( !GetInstance( instance, aInstance.Path() ) || !instance.m_Variants.contains( aVariantName ) )
return;
instance.m_Variants.erase( aVariantName );
}
bool SCH_SYMBOL::operator==( const SCH_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
@ -3023,14 +3275,14 @@ static struct SCH_SYMBOL_DESC
propMgr.AddProperty( new PROPERTY<SYMBOL, bool>( _HKI( "Exclude From Board" ),
&SYMBOL::SetExcludedFromBoard, &SYMBOL::GetExcludedFromBoard ),
groupAttributes );
propMgr.AddProperty( new PROPERTY<SYMBOL, bool>( _HKI( "Exclude From Simulation" ),
&SYMBOL::SetExcludedFromSim, &SYMBOL::GetExcludedFromSim ),
propMgr.AddProperty( new PROPERTY<SCH_SYMBOL, bool>( _HKI( "Exclude From Simulation" ),
&SCH_SYMBOL::SetExcludedFromSimProp, &SCH_SYMBOL::GetExcludedFromSimProp ),
groupAttributes );
propMgr.AddProperty( new PROPERTY<SYMBOL, bool>( _HKI( "Exclude From Bill of Materials" ),
&SYMBOL::SetExcludedFromBOM, &SYMBOL::GetExcludedFromBOM ),
propMgr.AddProperty( new PROPERTY<SCH_SYMBOL, bool>( _HKI( "Exclude From Bill of Materials" ),
&SCH_SYMBOL::SetExcludedFromBOMProp, &SCH_SYMBOL::GetExcludedFromBOMProp ),
groupAttributes );
propMgr.AddProperty( new PROPERTY<SYMBOL, bool>( _HKI( "Do not Populate" ),
&SYMBOL::SetDNP, &SYMBOL::GetDNP ),
propMgr.AddProperty( new PROPERTY<SCH_SYMBOL, bool>( _HKI( "Do not Populate" ),
&SCH_SYMBOL::SetDNPProp, &SCH_SYMBOL::GetDNPProp ),
groupAttributes );
}
} _SCH_SYMBOL_DESC;

54
eeschema/sch_symbol.h

@ -413,7 +413,11 @@ public:
const SCH_FIELD* GetField( FIELD_T aFieldNdx ) const;
/**
* Return a field in this symbol. Both versions return nullptr if the field is not found.
* Return a field in this symbol.
*
* @param aFieldName is the canonical name of the field.
*
* @return Both non-const and const versions return nullptr if the field is not found.
*/
SCH_FIELD* GetField( const wxString& aFieldName );
const SCH_FIELD* GetField( const wxString& aFieldName ) const;
@ -667,6 +671,50 @@ public:
/// Set the selected unit of this symbol for all sheets.
void SetUnitSelection( int aUnitSelection );
virtual void SetDNP( bool aEnable, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override;
virtual bool GetDNP( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override;
void SetDNP( bool aEnable, const SCH_SHEET_PATH& aInstance, const std::vector<wxString>& aVariantNames );
bool GetDNPProp() const { return GetDNP( nullptr, Schematic()->GetCurrentVariant() ); }
void SetDNPProp( bool aEnable ) { SetDNP( aEnable, nullptr, Schematic()->GetCurrentVariant() ); }
void SetExcludedFromBOM( bool aEnable, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override;
bool GetExcludedFromBOM( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override;
void SetExcludedFromBOM( bool aEnable, const SCH_SHEET_PATH& aInstance,
const std::vector<wxString>& aVariantNames );
bool GetExcludedFromBOMProp() const
{
return GetExcludedFromBOM( nullptr, Schematic()->GetCurrentVariant() );
}
void SetExcludedFromBOMProp( bool aEnable )
{
SetExcludedFromBOM( aEnable, nullptr, Schematic()->GetCurrentVariant() );
}
void SetExcludedFromSim( bool aEnable, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override;
bool GetExcludedFromSim( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override;
void SetExcludedFromSim( bool aEnable, const SCH_SHEET_PATH& aInstance,
const std::vector<wxString>& aVariantNames );
bool GetExcludedFromSimProp() const
{
return GetExcludedFromSim( nullptr, Schematic()->GetCurrentVariant() );
}
void SetExcludedFromSimProp( bool aEnable )
{
SetExcludedFromSim( aEnable, nullptr, Schematic()->GetCurrentVariant() );
}
/**
* SCH_SYMBOLs don't currently support embedded files, but their LIB_SYMBOL counterparts
* do.
@ -826,6 +874,10 @@ public:
/// Return the component classes this symbol belongs in.
std::unordered_set<wxString> GetComponentClassNames( const SCH_SHEET_PATH* aPath ) const;
std::optional<SCH_SYMBOL_VARIANT> GetVariant( const SCH_SHEET_PATH& aInstance, const wxString& aVariantName ) const;
void AddVariant( const SCH_SHEET_PATH& aInstance, const SCH_SYMBOL_VARIANT& aVariant );
void DeleteVariant( const SCH_SHEET_PATH& aInstance, const wxString& aVariantName );
bool operator==( const SCH_ITEM& aOther ) const override;
protected:

13
eeschema/sch_text.h

@ -84,8 +84,17 @@ public:
void DoHypertextAction( EDA_DRAW_FRAME* aFrame ) const override;
void SetExcludedFromSim( bool aExclude ) override { m_excludedFromSim = aExclude; }
bool GetExcludedFromSim() const override { return m_excludedFromSim; }
void SetExcludedFromSim( bool aExclude, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override
{
m_excludedFromSim = aExclude;
}
bool GetExcludedFromSim( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override
{
return m_excludedFromSim;
}
/**
* This offset depends on the orientation, the type of text, and the area required to

13
eeschema/sch_textbox.h

@ -88,8 +88,17 @@ public:
bool IsHypertext() const override;
void DoHypertextAction( EDA_DRAW_FRAME* aFrame ) const override;
void SetExcludedFromSim( bool aExclude ) override { m_excludedFromSim = aExclude; }
bool GetExcludedFromSim() const override { return m_excludedFromSim; }
void SetExcludedFromSim( bool aExclude, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override
{
m_excludedFromSim = aExclude;
}
bool GetExcludedFromSim( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override
{
return m_excludedFromSim;
}
bool operator<( const SCH_ITEM& aItem ) const override;

52
eeschema/schematic.cpp

@ -48,6 +48,7 @@
#include <sch_selection_tool.h>
#include <sim/spice_settings.h>
#include <sim/spice_value.h>
#include <string_utils.h>
#include <tool/tool_manager.h>
#include <undo_redo_container.h>
@ -240,6 +241,9 @@ void SCHEMATIC::SetRoot( SCH_SHEET* aRootSheet )
m_hierarchy = BuildSheetListSortedByPageNumbers();
m_connectionGraph->Reset();
SCH_SCREENS screens( aRootSheet );
m_variantNames = screens.GetVariantNames();
}
@ -1607,22 +1611,54 @@ void SCHEMATIC::CreateDefaultScreens()
Reset();
SCH_SHEET* rootSheet = new SCH_SHEET( this );
SetRoot( rootSheet );
SCH_SCREEN* rootScreen = new SCH_SCREEN( this );
const_cast<KIID&>( rootSheet->m_Uuid ) = rootScreen->GetUuid();
Root().SetScreen( rootScreen );
RootScreen()->SetFileName( wxEmptyString );
const_cast<KIID&>( rootSheet->m_Uuid ) = rootScreen->GetUuid();
rootSheet->SetScreen( rootScreen );
rootScreen->SetFileName( wxEmptyString );
rootScreen->SetPageNumber( wxT( "1" ) );
// Don't leave root page number empty
SCH_SHEET_PATH rootSheetPath;
rootSheetPath.push_back( rootSheet );
RootScreen()->SetPageNumber( wxT( "1" ) );
rootSheetPath.SetPageNumber( wxT( "1" ) );
SetRoot( rootSheet );
// Rehash sheetpaths in hierarchy since we changed the uuid.
RefreshHierarchy();
}
}
wxArrayString SCHEMATIC::GetVariantNamesForUI() const
{
wxArrayString variantNames;
// There is no default variant name. This is just a place holder for UI controls.
variantNames.Add( GetDefaultVariantName() );
for( const wxString& name : m_variantNames )
variantNames.Add( name );
return variantNames;
}
wxString SCHEMATIC::GetCurrentVariant() const
{
if( m_currentVariant.IsEmpty() || ( m_currentVariant == GetDefaultVariantName() ) )
return wxEmptyString;
return m_currentVariant;
}
void SCHEMATIC::DeleteVariant( const wxString& aVariantName )
{
wxCHECK( m_rootSheet, /* void */ );
SCH_SCREENS allScreens( m_rootSheet );
allScreens.DeleteVariant( aVariantName );
}

30
eeschema/schematic.h

@ -421,6 +421,32 @@ public:
void CreateDefaultScreens();
/**
* Return an array of variant names for using in wxWidgets UI controls.
*
* Most wxWidgets UI controls that support multiple entries take a wxArrayString as an argument. This
* is a convenience method so it's not necessary to convert a std::set<wxString> to a wxArrayString at
* the point of use.
*
* @note This also adds the pseudo "<default>" entry to the beginning of the array followed by all other
* variant names sorted using the #StrNumCmp function.
*/
wxArrayString GetVariantNamesForUI() const;
/**
* Return the current variant being edited.
*
* @return the name of the current variant being edited. An empty string is the default variant.
*/
wxString GetCurrentVariant() const;
/**
* Delete all information for @a aVariantName.
*
* @param aVariantName is the name of the variant to remove.
*/
void DeleteVariant( const wxString& aVariantName );
/**
* True if a SCHEMATIC exists, false if not
*/
@ -496,6 +522,10 @@ private:
void updateProjectBusAliases();
std::vector<std::shared_ptr<BUS_ALIAS>> m_busAliases;
wxString m_currentVariant;
std::set<wxString> m_variantNames;
};
#endif

2
eeschema/schematic.keywords

@ -51,6 +51,7 @@ extends
external
exclude_from_sim
face
field
fields_autoplaced
file
fill
@ -171,6 +172,7 @@ unit_name
unspecified
uuid
value
variant
version
width
wire

28
eeschema/symbol.h

@ -172,18 +172,32 @@ public:
/**
* Set or clear the exclude from simulation flag.
*/
void SetExcludedFromSim( bool aExcludeFromSim ) override
virtual void SetExcludedFromSim( bool aExcludeFromSim, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override
{
m_excludedFromSim = aExcludeFromSim;
}
bool GetExcludedFromSim() const override { return m_excludedFromSim; }
virtual bool GetExcludedFromSim( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override
{
return m_excludedFromSim;
}
/**
* Set or clear the exclude from schematic bill of materials flag.
*/
void SetExcludedFromBOM( bool aExcludeFromBOM ) override { m_excludedFromBOM = aExcludeFromBOM; }
bool GetExcludedFromBOM() const override { return m_excludedFromBOM; }
virtual void SetExcludedFromBOM( bool aExcludeFromBOM, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override
{
m_excludedFromBOM = aExcludeFromBOM;
}
virtual bool GetExcludedFromBOM( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override
{
return m_excludedFromBOM;
}
/**
* Set or clear exclude from board netlist flag.
@ -194,8 +208,10 @@ public:
/**
* Set or clear the 'Do Not Populate' flag.
*/
bool GetDNP() const override { return m_DNP; }
void SetDNP( bool aDNP ) override { m_DNP = aDNP; }
virtual bool GetDNP( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override { return m_DNP; }
virtual void SetDNP( bool aDNP, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override { m_DNP = aDNP; }
virtual int GetOrientation() const { return SYM_NORMAL; }

54
eeschema/toolbars_sch_editor.cpp

@ -45,6 +45,12 @@
#include <toolbars_sch_editor.h>
ACTION_TOOLBAR_CONTROL SCH_ACTION_TOOLBAR_CONTROLS::currentVariant(
"control.currentVariant",
_( "Current Variant" ),
_( "Control to select the current schematic varaint" ) );
std::optional<TOOLBAR_CONFIGURATION> SCH_EDIT_TOOLBAR_SETTINGS::DefaultToolbarConfig( TOOLBAR_LOC aToolbar )
{
TOOLBAR_CONFIGURATION config;
@ -196,6 +202,9 @@ std::optional<TOOLBAR_CONFIGURATION> SCH_EDIT_TOOLBAR_SETTINGS::DefaultToolbarCo
config.AppendSeparator()
.AppendAction( SCH_ACTIONS::showPcbNew );
if( ADVANCED_CFG::GetCfg().m_EnableVariantsUI )
config.AppendControl( SCH_ACTION_TOOLBAR_CONTROLS::currentVariant );
// Insert all the IPC plugins here on the toolbar
// TODO (ISM): Move this to individual actions for each script
config.AppendControl( ACTION_TOOLBAR_CONTROLS::ipcScripting );
@ -212,6 +221,27 @@ void SCH_EDIT_FRAME::configureToolbars()
{
SCH_BASE_FRAME::configureToolbars();
if( ADVANCED_CFG::GetCfg().m_EnableVariantsUI )
{
// Variant selection drop down control on main tool bar.
auto variantSelectionCtrlFactory =
[this]( ACTION_TOOLBAR* aToolbar )
{
std::optional<wxString> currentVariantName = Schematic().GetCurrentVariant();
wxString tmp = currentVariantName ? *currentVariantName : GetDefaultVariantName();
m_currentVariantCtrl = new wxChoice( aToolbar, ID_TOOLBAR_SCH_SELECT_VARAIANT, wxDefaultPosition,
wxDefaultSize, Schematic().GetVariantNamesForUI(), 0,
wxDefaultValidator, tmp );
m_currentVariantCtrl->SetToolTip( _( "Select the current variant to display and edit." ) );
aToolbar->Add( m_currentVariantCtrl );
UpdateVariantSelectionCtrl( Schematic().GetVariantNamesForUI() );
};
RegisterCustomToolbarControlFactory( SCH_ACTION_TOOLBAR_CONTROLS::currentVariant, variantSelectionCtrlFactory );
}
// IPC/Scripting plugin control
// TODO (ISM): Clean this up to make IPC actions just normal tool actions to get rid of this entire
// control
@ -239,3 +269,27 @@ void SCH_EDIT_FRAME::configureToolbars()
RegisterCustomToolbarControlFactory( ACTION_TOOLBAR_CONTROLS::ipcScripting, pluginControlFactory );
}
void SCH_EDIT_FRAME::UpdateVariantSelectionCtrl( const wxArrayString& aVariantNames )
{
if( !m_currentVariantCtrl )
return;
// Fall back to the default if nothing is currently selected.
wxString currentSelection = GetDefaultVariantName();
int selectionIndex = m_currentVariantCtrl->GetSelection();
if( selectionIndex != wxNOT_FOUND )
currentSelection = m_currentVariantCtrl->GetString( selectionIndex );
m_currentVariantCtrl->Set( aVariantNames );
selectionIndex = m_currentVariantCtrl->FindString( currentSelection );
if( ( selectionIndex == wxNOT_FOUND ) && ( m_currentVariantCtrl->GetCount() != 0 ) )
selectionIndex = 0;
m_currentVariantCtrl->SetSelection( selectionIndex );
}

11
eeschema/toolbars_sch_editor.h

@ -20,8 +20,19 @@
#ifndef TOOLBARS_SCH_EDITOR_H_
#define TOOLBARS_SCH_EDITOR_H_
#include <tool/action_toolbar.h>
#include <tool/ui/toolbar_configuration.h>
/**
* Custom toolbar controls for the schematic editor frame.
*/
class SCH_ACTION_TOOLBAR_CONTROLS : public ACTION_TOOLBAR_CONTROLS
{
public:
static ACTION_TOOLBAR_CONTROL currentVariant;
};
/**
* Toolbar configuration for the schematic editor frame.
*/

12
eeschema/tools/sch_actions.cpp

@ -1677,4 +1677,16 @@ TOOL_ACTION SCH_ACTIONS::showNetlist( TOOL_ACTION_ARGS()
.FriendlyName( _( "Show SPICE Netlist" ) )
.Icon( BITMAPS::netlist ) );
TOOL_ACTION SCH_ACTIONS::addVariant( TOOL_ACTION_ARGS()
.Name( "eeschema.EditorControl.addVariant" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Add Variant..." ) )
.Tooltip( _( "Add new variant to the schematic." ) ) );
TOOL_ACTION SCH_ACTIONS::removeVariant( TOOL_ACTION_ARGS()
.Name( "eeschema.EditorControl.removeVariant" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Remove Variant..." ) )
.Tooltip( _( "Remove an existing variant from the schematic." ) ) );
// clang-format on

4
eeschema/tools/sch_actions.h

@ -182,6 +182,10 @@ public:
static TOOL_ACTION addSymbolToSchematic;
static TOOL_ACTION exportSymbolsToLibrary;
// Variant operations
static TOOL_ACTION addVariant;
static TOOL_ACTION removeVariant;
// Attribute Toggles
static TOOL_ACTION setExcludeFromBOM;
static TOOL_ACTION setExcludeFromSimulation;

9
include/advanced_config.h

@ -872,6 +872,15 @@ public:
*/
int m_ScreenDPI;
/**
* Enable access to the variants user interface.
*
* Setting name: "EnableVariantsUI"
* Valid values: 0 or 1
* Default value: 0
*/
bool m_EnableVariantsUI;
wxString m_traceMasks; ///< Trace masks for wxLogTrace, loaded from the config file.
///@}

4
include/string_utils.h

@ -495,4 +495,8 @@ KICOMMON_API std::vector<wxString> ExpandStackedPinNotation( const wxString& aPi
bool* aValid = nullptr );
KICOMMON_API wxString GetDefaultVariantName();
KICOMMON_API int SortVariantNames( const wxString& aLhs, const wxString& aRhs );
#endif // STRING_UTILS_H

29
kicad/cli/command.cpp

@ -34,7 +34,8 @@ CLI::COMMAND::COMMAND( const std::string& aName ) :
m_hasOutputArg( false ),
m_hasDrawingSheetArg( false ),
m_hasDefineArg( false ),
m_outputArgExpectsDir( false )
m_outputArgExpectsDir( false ),
m_hasVariantsArg( false )
{
m_argParser.add_argument( ARG_HELP_SHORT, ARG_HELP )
@ -77,7 +78,6 @@ int CLI::COMMAND::Perform( KIWAY& aKiway )
m_argDrawingSheet = From_UTF8( m_argParser.get<std::string>( ARG_DRAWING_SHEET ).c_str() );
}
if( m_hasDefineArg )
{
auto defines = m_argParser.get<std::vector<std::string>>( ARG_DEFINE_VAR_LONG );
@ -99,6 +99,17 @@ int CLI::COMMAND::Perform( KIWAY& aKiway )
}
}
if( m_hasVariantsArg )
{
auto variantNames = m_argParser.get<std::vector<std::string>>( ARG_VARIANTS );
for( const std::string& variantName : variantNames )
m_argVariantNames.emplace( variantName );
if( m_argVariantNames.empty() )
return EXIT_CODES::ERR_ARGS;
}
return doPerform( aKiway );
}
@ -178,3 +189,17 @@ void CLI::COMMAND::addDefineArg()
"\nUse in the format of '--define-var key=value' or '-D key=value'" ) ) )
.metavar( "KEY=VALUE" );
}
void CLI::COMMAND::addVariantsArg()
{
m_hasVariantsArg = true;
m_argParser.add_argument( ARG_VARIANTS )
.default_value( std::set<std::string>() )
.append()
.help( UTF8STDSTR(
_( "List of variant names to output.\n"
"When no --variants arguement is provided the default variant is output.\n"
"To output all variants use '--variants=all'" ) ) );
}

18
kicad/cli/command.h

@ -35,6 +35,7 @@
#define ARG_DRAWING_SHEET "--drawing-sheet"
#define ARG_DEFINE_VAR_SHORT "-D"
#define ARG_DEFINE_VAR_LONG "--define-var"
#define ARG_VARIANTS "--variants"
namespace CLI
{
@ -84,6 +85,11 @@ protected:
*/
void addDefineArg();
/**
* Set up the list of variants to output arguement.
*/
void addVariantsArg();
/**
* The internal handler that should be overloaded to implement command specific
* processing and work.
@ -143,6 +149,18 @@ protected:
* Value of the drawing sheet arg if configured
*/
std::map<wxString, wxString> m_argDefineVars;
/**
* Whether or not the input arguement for variant names was added for parsing.
*/
bool m_hasVariantsArg;
/**
* A set of variant names to output.
*
* An empty set indicates the default variant only.
*/
std::set<wxString> m_argVariantNames;
};
}

20
qa/tests/common/test_kicad_string.cpp

@ -168,4 +168,22 @@ BOOST_AUTO_TEST_CASE( HTMLEscape )
}
}
BOOST_AUTO_TEST_SUITE_END()
/**
* Test #SortVariantNames().
*/
BOOST_AUTO_TEST_CASE( VariantNameSort )
{
std::vector<wxString> variantNames;
// Verify default variant name is always sorted to the beginning of the list.
variantNames.emplace_back( wxS( "Variant1" ) );
variantNames.emplace_back( GetDefaultVariantName() );
std::sort( variantNames.begin(), variantNames.end(), SortVariantNames );
BOOST_CHECK_EQUAL( variantNames[0], GetDefaultVariantName() );
BOOST_CHECK_EQUAL( variantNames[1], wxS( "Variant1" ) );
}
BOOST_AUTO_TEST_SUITE_END()
Loading…
Cancel
Save