Update schematic variants

This commit is contained in:
Seth Hillbrand
2026-01-05 17:45:04 -08:00
parent a7ec3bb7cc
commit c385767cd3
76 changed files with 83456 additions and 170 deletions
+3
View File
@@ -138,6 +138,9 @@ i18n_status.csv
qa/data/**/*.csv
qa/data/**/*.spice
# Unignore variant test comparison files
!qa/data/cli/variants/*.bom.csv
# Don't actually ignore any of these files, since we need them in the tree
!resources/linux/icons/hicolor/**/**/*
!resources/linux/icons-nightly/hicolor/**/**/*
+5 -2
View File
@@ -40,7 +40,8 @@ JOB_EXPORT_SCH_BOM::JOB_EXPORT_SCH_BOM() :
m_sortField(),
m_sortAsc( true ),
m_filterString(),
m_excludeDNP( false )
m_excludeDNP( false ),
m_variantNames()
{
m_params.emplace_back( new JOB_PARAM<wxString>( "field_delimiter",
&m_fieldDelimiter,
@@ -79,7 +80,9 @@ JOB_EXPORT_SCH_BOM::JOB_EXPORT_SCH_BOM() :
&m_bomFmtPresetName,
m_bomFmtPresetName ) );
m_params.emplace_back( new JOB_PARAM<wxString>( "variant", &m_variant, m_variant ) );
m_params.emplace_back( new JOB_PARAM_LIST<wxString>( "variant_names",
&m_variantNames,
m_variantNames ) );
}
+3 -1
View File
@@ -55,7 +55,9 @@ public:
bool m_sortAsc;
wxString m_filterString;
bool m_excludeDNP;
wxString m_variant;
// Variant names to export. Empty vector means default variant only.
std::vector<wxString> m_variantNames;
};
#endif
+2 -1
View File
@@ -55,7 +55,8 @@ JOB_EXPORT_SCH_NETLIST::JOB_EXPORT_SCH_NETLIST() :
m_spiceSaveAllVoltages( false ),
m_spiceSaveAllCurrents( false ),
m_spiceSaveAllDissipations( false ),
m_spiceSaveAllEvents( false )
m_spiceSaveAllEvents( false ),
m_variantNames()
{
m_params.emplace_back( new JOB_PARAM<FORMAT>( "format", &format, format ) );
m_params.emplace_back( new JOB_PARAM<bool>( "spice.save_all_voltages", &m_spiceSaveAllVoltages,
+4
View File
@@ -22,6 +22,7 @@
#define JOB_EXPORT_SCH_NETLIST_H
#include <kicommon.h>
#include <vector>
#include "job.h"
class KICOMMON_API JOB_EXPORT_SCH_NETLIST : public JOB
@@ -54,6 +55,9 @@ public:
bool m_spiceSaveAllCurrents;
bool m_spiceSaveAllDissipations;
bool m_spiceSaveAllEvents;
// Variant names to export. Empty vector means default variant only.
std::vector<wxString> m_variantNames;
};
#endif
+2 -1
View File
@@ -53,7 +53,8 @@ JOB_EXPORT_SCH_PLOT::JOB_EXPORT_SCH_PLOT( bool aOutputIsDirectory ) :
m_PDFPropertyPopups( true ),
m_PDFHierarchicalLinks( true ),
m_PDFMetadata( true ),
m_theme()
m_theme(),
m_variantNames()
{
m_params.emplace_back( new JOB_PARAM<SCH_PLOT_FORMAT>( "format",
&m_plotFormat, m_plotFormat ) );
+3
View File
@@ -68,6 +68,9 @@ public:
bool m_PDFHierarchicalLinks;
bool m_PDFMetadata;
wxString m_theme;
// Variant names to export. Empty vector means default variant only.
std::vector<wxString> m_variantNames;
};
@@ -339,6 +339,7 @@ bool DIALOG_LIB_SYMBOL_PROPERTIES::TransferDataToWindow()
m_excludeFromSimCheckBox->SetValue( m_libEntry->GetExcludedFromSim() );
m_excludeFromBomCheckBox->SetValue( m_libEntry->GetExcludedFromBOM() );
m_excludeFromBoardCheckBox->SetValue( m_libEntry->GetExcludedFromBoard() );
m_excludeFromPosFilesCheckBox->SetValue( m_libEntry->GetExcludedFromPosFiles() );
m_ShowPinNumButt->SetValue( m_libEntry->GetShowPinNumbers() );
m_ShowPinNameButt->SetValue( m_libEntry->GetShowPinNames() );
@@ -685,6 +686,7 @@ bool DIALOG_LIB_SYMBOL_PROPERTIES::TransferDataFromWindow()
m_libEntry->SetExcludedFromSim( m_excludeFromSimCheckBox->GetValue() );
m_libEntry->SetExcludedFromBOM( m_excludeFromBomCheckBox->GetValue() );
m_libEntry->SetExcludedFromBoard( m_excludeFromBoardCheckBox->GetValue() );
m_libEntry->SetExcludedFromPosFiles( m_excludeFromPosFilesCheckBox->GetValue() );
m_libEntry->SetShowPinNumbers( m_ShowPinNumButt->GetValue() );
m_libEntry->SetShowPinNames( m_ShowPinNameButt->GetValue() );
@@ -1185,9 +1187,11 @@ void DIALOG_LIB_SYMBOL_PROPERTIES::onPowerCheckBox( wxCommandEvent& aEvent )
m_excludeFromSimCheckBox->SetValue( true );
m_excludeFromBomCheckBox->SetValue( true );
m_excludeFromBoardCheckBox->SetValue( true );
m_excludeFromPosFilesCheckBox->SetValue( true );
m_excludeFromBomCheckBox->Enable( false );
m_excludeFromBoardCheckBox->Enable( false );
m_excludeFromSimCheckBox->Enable( false );
m_excludeFromPosFilesCheckBox->Enable( false );
m_spiceFieldsButton->Show( false );
m_OptionLocalPower->Enable( true );
}
@@ -1196,6 +1200,7 @@ void DIALOG_LIB_SYMBOL_PROPERTIES::onPowerCheckBox( wxCommandEvent& aEvent )
m_excludeFromBomCheckBox->Enable( true );
m_excludeFromBoardCheckBox->Enable( true );
m_excludeFromSimCheckBox->Enable( true );
m_excludeFromPosFilesCheckBox->Enable( true );
m_spiceFieldsButton->Show( true );
m_OptionLocalPower->Enable( false );
}
@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a-dirty)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@@ -262,6 +262,9 @@ DIALOG_LIB_SYMBOL_PROPERTIES_BASE::DIALOG_LIB_SYMBOL_PROPERTIES_BASE( wxWindow*
m_excludeFromBoardCheckBox = new wxCheckBox( sbSizerAttributes->GetStaticBox(), wxID_ANY, _("Exclude from board"), wxDefaultPosition, wxDefaultSize, 0 );
sbSizerAttributes->Add( m_excludeFromBoardCheckBox, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 );
m_excludeFromPosFilesCheckBox = new wxCheckBox( sbSizerAttributes->GetStaticBox(), wxID_ANY, _("Exclude from position files"), wxDefaultPosition, wxDefaultSize, 0 );
sbSizerAttributes->Add( m_excludeFromPosFilesCheckBox, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 );
bSizerRightCol->Add( sbSizerAttributes, 1, wxEXPAND|wxALL, 5 );
@@ -606,6 +609,7 @@ DIALOG_LIB_SYMBOL_PROPERTIES_BASE::DIALOG_LIB_SYMBOL_PROPERTIES_BASE( wxWindow*
m_excludeFromSimCheckBox->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_excludeFromBomCheckBox->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_excludeFromBoardCheckBox->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_excludeFromPosFilesCheckBox->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_unitSpinCtrl->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnUnitSpinCtrlKillFocus ), NULL, this );
m_unitSpinCtrl->Connect( wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnUnitSpinCtrl ), NULL, this );
m_unitSpinCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnUnitSpinCtrlText ), NULL, this );
@@ -651,6 +655,7 @@ DIALOG_LIB_SYMBOL_PROPERTIES_BASE::~DIALOG_LIB_SYMBOL_PROPERTIES_BASE()
m_excludeFromSimCheckBox->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_excludeFromBomCheckBox->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_excludeFromBoardCheckBox->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_excludeFromPosFilesCheckBox->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_unitSpinCtrl->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnUnitSpinCtrlKillFocus ), NULL, this );
m_unitSpinCtrl->Disconnect( wxEVT_COMMAND_SPINCTRL_UPDATED, wxSpinEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnUnitSpinCtrl ), NULL, this );
m_unitSpinCtrl->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_LIB_SYMBOL_PROPERTIES_BASE::OnUnitSpinCtrlText ), NULL, this );
@@ -1894,6 +1894,72 @@
<event name="OnCheckBox">OnCheckBox</event>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxLEFT|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="false">
<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="checked">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">Exclude from position files</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_excludeFromPosFilesCheckBox</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>
<event name="OnCheckBox">OnCheckBox</event>
</object>
</object>
</object>
</object>
</object>
@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a-dirty)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@@ -75,6 +75,7 @@ class DIALOG_LIB_SYMBOL_PROPERTIES_BASE : public DIALOG_SHIM
wxCheckBox* m_excludeFromSimCheckBox;
wxCheckBox* m_excludeFromBomCheckBox;
wxCheckBox* m_excludeFromBoardCheckBox;
wxCheckBox* m_excludeFromPosFilesCheckBox;
wxPanel* m_PanelUnitsAndBodyStyles;
wxStaticText* m_staticTextNbUnits;
wxSpinCtrl* m_unitSpinCtrl;
+170 -38
View File
@@ -210,6 +210,7 @@ DIALOG_SYMBOL_FIELDS_TABLE::DIALOG_SYMBOL_FIELDS_TABLE( SCH_EDIT_FRAME* parent,
m_addVariantButton->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
m_deleteVariantButton->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
m_renameVariantButton->SetBitmap( KiBitmapBundle( BITMAPS::small_edit ) );
m_copyVariantButton->SetBitmap( KiBitmapBundle( BITMAPS::copy ) );
m_sidebarButton->SetBitmap( KiBitmapBundle( BITMAPS::left ) );
@@ -275,6 +276,8 @@ DIALOG_SYMBOL_FIELDS_TABLE::DIALOG_SYMBOL_FIELDS_TABLE( SCH_EDIT_FRAME* parent,
m_variantListBox->SetSelection( 0 );
}
updateVariantButtonStates();
if( !ADVANCED_CFG::GetCfg().m_EnableVariantsUI )
m_splitter_left->Unsplit( m_variantsPanel );
@@ -285,6 +288,9 @@ DIALOG_SYMBOL_FIELDS_TABLE::DIALOG_SYMBOL_FIELDS_TABLE( SCH_EDIT_FRAME* parent,
// non-job versions (which have different sizes).
m_hash_key = TO_UTF8( GetTitle() );
// Set the current variant for highlighting variant-specific field values
m_dataModel->SetCurrentVariant( m_parent->Schematic().GetCurrentVariant() );
SetInitialFocus( m_grid );
m_grid->ClearSelection();
@@ -1559,7 +1565,10 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnOk( wxCommandEvent& aEvent )
m_job->m_fieldsGroupBy.emplace_back( modelField.name );
}
m_job->m_variant = getSelectedVariant();
wxString selectedVariant = getSelectedVariant();
if( !selectedVariant.IsEmpty() )
m_job->m_variantNames.push_back( selectedVariant );
EndModal( wxID_OK );
}
@@ -2614,24 +2623,26 @@ SCH_REFERENCE_LIST DIALOG_SYMBOL_FIELDS_TABLE::getSheetSymbolReferences( SCH_SHE
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 )
if( !m_parent->ShowAddVariantDialog() )
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;
ctrlContents.Add( GetDefaultVariantName() );
wxArrayString ctrlContents = m_variantListBox->GetStrings();
for( const wxString& variant : m_parent->Schematic().GetVariantNames() )
ctrlContents.Add( variant );
ctrlContents.Add( dlg.GetValue() );
ctrlContents.Sort( SortVariantNames );
m_variantListBox->Set( ctrlContents );
wxString currentVariant = m_parent->Schematic().GetCurrentVariant();
int newSelection = m_variantListBox->FindString(
currentVariant.IsEmpty() ? GetDefaultVariantName() : currentVariant );
if( newSelection != wxNOT_FOUND )
m_variantListBox->SetSelection( newSelection );
updateVariantButtonStates();
}
@@ -2642,49 +2653,152 @@ void DIALOG_SYMBOL_FIELDS_TABLE::onDeleteVariant( wxCommandEvent& aEvent )
// An empty or default selection cannot be deleted.
if( ( selection == wxNOT_FOUND ) || ( selection == 0 ) )
{
wxBell();
m_parent->GetInfoBar()->ShowMessageFor( _( "Cannot delete the default variant." ),
10000, wxICON_ERROR );
return;
}
wxString variantName = m_variantListBox->GetString( selection );
m_variantListBox->SetSelection( selection - 1 );
m_variantListBox->Delete( selection );
m_parent->Schematic().DeleteVariant( variantName );
int newSelection = std::max( 0, selection - 1 );
m_variantListBox->SetSelection( newSelection );
wxString selectedVariant = getSelectedVariant();
m_parent->SetCurrentVariant( selectedVariant );
if( m_grid->CommitPendingChanges( true ) )
{
m_dataModel->SetCurrentVariant( selectedVariant );
m_dataModel->UpdateReferences( m_dataModel->GetReferenceList(), selectedVariant );
m_dataModel->RebuildRows();
if( m_nbPages->GetSelection() == 1 )
PreviewRefresh();
else
m_grid->ForceRefresh();
}
updateVariantButtonStates();
m_parent->UpdateVariantSelectionCtrl( m_parent->Schematic().GetVariantNamesForUI() );
}
void DIALOG_SYMBOL_FIELDS_TABLE::onRenameVariant( wxCommandEvent& aEvent )
{
// wxArrayInt selections;
int selection = m_variantListBox->GetSelection();
// // Only allow renaming a single selection that is not the default.
// if( ( m_variantListBox->GetSelections( selections ) != 1 ) || ( selections[0] == 0 ) )
// {
// wxBell();
// return;
// }
// An empty or default selection cannot be renamed.
if( ( selection == wxNOT_FOUND ) || ( selection == 0 ) )
{
m_parent->GetInfoBar()->ShowMessageFor( _( "Cannot rename the default variant." ),
10000, wxICON_ERROR );
return;
}
// wxArrayString ctrlContents = m_variantListBox->GetStrings();
// wxString oldVariantName = ctrlContents[selections[0]];
wxString oldVariantName = m_variantListBox->GetString( selection );
// wxTextEntryDialog dlg( this, _( "Add new variant name:" ), _( "New Variant" ), oldVariantName,
// wxOK | wxCANCEL | wxCENTER );
wxTextEntryDialog dlg( this, _( "Enter new variant name:" ), _( "Rename Variant" ),
oldVariantName, wxOK | wxCANCEL | wxCENTER );
// if( dlg.ShowModal() == wxID_CANCEL )
// return;
if( dlg.ShowModal() == wxID_CANCEL )
return;
// wxString newVariantName = dlg.GetValue();
wxString newVariantName = dlg.GetValue().Trim().Trim( false );
// if( newVariantName.IsEmpty() || ( newVariantName == oldVariantName ) || ( newVariantName == ctrlContents[0] ) )
// {
// wxBell();
// return;
// }
// Empty name is not allowed.
if( newVariantName.IsEmpty() )
{
m_parent->GetInfoBar()->ShowMessageFor( _( "Variant name cannot be empty." ),
10000, wxICON_ERROR );
return;
}
// ctrlContents.Remove( m_variantListBox->GetString( selections[0] ) );
// ctrlContents.Add( newVariantName );
// ctrlContents.Sort( SortVariantNames );
// m_variantListBox->Set( ctrlContents );
// Same name - nothing to do
if( newVariantName == oldVariantName )
return;
// Duplicate name is not allowed.
if( m_variantListBox->FindString( newVariantName ) != wxNOT_FOUND )
{
m_parent->GetInfoBar()->ShowMessageFor( wxString::Format( _( "Variant '%s' already exists." ),
newVariantName ),
10000, wxICON_ERROR );
return;
}
m_parent->Schematic().RenameVariant( oldVariantName, newVariantName );
wxArrayString ctrlContents = m_variantListBox->GetStrings();
ctrlContents.Remove( oldVariantName );
ctrlContents.Add( newVariantName );
ctrlContents.Sort( SortVariantNames );
m_variantListBox->Set( ctrlContents );
int newSelection = m_variantListBox->FindString( newVariantName );
if( newSelection != wxNOT_FOUND )
m_variantListBox->SetSelection( newSelection );
updateVariantButtonStates();
m_parent->UpdateVariantSelectionCtrl( m_parent->Schematic().GetVariantNamesForUI() );
}
void DIALOG_SYMBOL_FIELDS_TABLE::onCopyVariant( wxCommandEvent& aEvent )
{
int selection = m_variantListBox->GetSelection();
// An empty or default selection cannot be copied.
if( ( selection == wxNOT_FOUND ) || ( selection == 0 ) )
{
m_parent->GetInfoBar()->ShowMessageFor( _( "Cannot copy the default variant." ),
10000, wxICON_ERROR );
return;
}
wxString sourceVariantName = m_variantListBox->GetString( selection );
wxTextEntryDialog dlg( this, _( "Enter name for the copied variant:" ), _( "Copy Variant" ),
sourceVariantName + wxS( "_copy" ), wxOK | wxCANCEL | wxCENTER );
if( dlg.ShowModal() == wxID_CANCEL )
return;
wxString newVariantName = dlg.GetValue().Trim().Trim( false );
// Empty name is not allowed.
if( newVariantName.IsEmpty() )
{
m_parent->GetInfoBar()->ShowMessageFor( _( "Variant name cannot be empty." ),
10000, wxICON_ERROR );
return;
}
// Duplicate name is not allowed.
if( m_variantListBox->FindString( newVariantName ) != wxNOT_FOUND )
{
m_parent->GetInfoBar()->ShowMessageFor( wxString::Format( _( "Variant '%s' already exists." ),
newVariantName ),
10000, wxICON_ERROR );
return;
}
m_parent->Schematic().CopyVariant( sourceVariantName, newVariantName );
wxArrayString ctrlContents = m_variantListBox->GetStrings();
ctrlContents.Add( newVariantName );
ctrlContents.Sort( SortVariantNames );
m_variantListBox->Set( ctrlContents );
int newSelection = m_variantListBox->FindString( newVariantName );
if( newSelection != wxNOT_FOUND )
m_variantListBox->SetSelection( newSelection );
updateVariantButtonStates();
m_parent->UpdateVariantSelectionCtrl( m_parent->Schematic().GetVariantNamesForUI() );
}
@@ -2693,6 +2807,8 @@ void DIALOG_SYMBOL_FIELDS_TABLE::onVariantSelectionChange( wxCommandEvent& aEven
wxString currentVariant;
wxString selectedVariant = getSelectedVariant();
updateVariantButtonStates();
if( m_parent )
{
currentVariant = m_parent->Schematic().GetCurrentVariant();
@@ -2705,6 +2821,9 @@ void DIALOG_SYMBOL_FIELDS_TABLE::onVariantSelectionChange( wxCommandEvent& aEven
{
if( m_grid->CommitPendingChanges( true ) )
{
// Update the data model's current variant for field highlighting
m_dataModel->SetCurrentVariant( selectedVariant );
m_dataModel->UpdateReferences( m_dataModel->GetReferenceList(), selectedVariant );
m_dataModel->RebuildRows();
@@ -2722,6 +2841,19 @@ void DIALOG_SYMBOL_FIELDS_TABLE::onVariantSelectionChange( wxCommandEvent& aEven
}
void DIALOG_SYMBOL_FIELDS_TABLE::updateVariantButtonStates()
{
int selection = m_variantListBox->GetSelection();
// Copy, rename, and delete are only enabled for non-default variant selections
bool canModify = ( selection != wxNOT_FOUND ) && ( selection != 0 );
m_copyVariantButton->Enable( canModify );
m_renameVariantButton->Enable( canModify );
m_deleteVariantButton->Enable( canModify );
}
wxString DIALOG_SYMBOL_FIELDS_TABLE::getSelectedVariant() const
{
wxString retv;
@@ -142,8 +142,11 @@ private:
void onAddVariant( wxCommandEvent& aEvent ) override;
void onDeleteVariant( wxCommandEvent& aEvent ) override;
void onRenameVariant( wxCommandEvent& aEvent ) override;
void onCopyVariant( wxCommandEvent& aEvent ) override;
void onVariantSelectionChange( wxCommandEvent& aEvent ) override;
void updateVariantButtonStates();
wxString getSelectedVariant() const;
private:
@@ -150,6 +150,9 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::DIALOG_SYMBOL_FIELDS_TABLE_BASE( wxWindow* pare
m_renameVariantButton = new STD_BITMAP_BUTTON( m_variantsPanel, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
bSizer14->Add( m_renameVariantButton, 0, 0, 5 );
m_copyVariantButton = new STD_BITMAP_BUTTON( m_variantsPanel, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW|0 );
bSizer14->Add( m_copyVariantButton, 0, 0, 5 );
bSizer14->Add( 15, 0, 0, 0, 5 );
@@ -441,6 +444,7 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::DIALOG_SYMBOL_FIELDS_TABLE_BASE( wxWindow* pare
m_variantListBox->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::onVariantSelectionChange ), 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_copyVariantButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::onCopyVariant ), 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 );
@@ -480,6 +484,7 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::~DIALOG_SYMBOL_FIELDS_TABLE_BASE()
m_variantListBox->Disconnect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::onVariantSelectionChange ), 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_copyVariantButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::onCopyVariant ), 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 );
@@ -1226,6 +1226,81 @@
<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="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_copyVariantButton</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">STD_BITMAP_BUTTON; widgets/std_bitmap_button.h; 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">onCopyVariant</event>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag"></property>
@@ -67,6 +67,7 @@ class DIALOG_SYMBOL_FIELDS_TABLE_BASE : public DIALOG_SHIM
wxListBox* m_variantListBox;
STD_BITMAP_BUTTON* m_addVariantButton;
STD_BITMAP_BUTTON* m_renameVariantButton;
STD_BITMAP_BUTTON* m_copyVariantButton;
STD_BITMAP_BUTTON* m_deleteVariantButton;
wxPanel* m_rightPanel;
wxNotebook* m_nbPages;
@@ -117,6 +118,7 @@ class DIALOG_SYMBOL_FIELDS_TABLE_BASE : public DIALOG_SHIM
virtual void onVariantSelectionChange( wxCommandEvent& event ) { event.Skip(); }
virtual void onAddVariant( wxCommandEvent& event ) { event.Skip(); }
virtual void onRenameVariant( wxCommandEvent& event ) { event.Skip(); }
virtual void onCopyVariant( 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(); }
@@ -550,6 +550,7 @@ bool DIALOG_SYMBOL_PROPERTIES::TransferDataToWindow()
m_cbExcludeFromSim->SetValue( m_symbol->GetExcludedFromSim( &sheetPath, variantName ) );
m_cbExcludeFromBom->SetValue( m_symbol->GetExcludedFromBOM( &sheetPath, variantName ) );
m_cbExcludeFromBoard->SetValue( m_symbol->GetExcludedFromBoard() );
m_cbExcludeFromPosFiles->SetValue( m_symbol->GetExcludedFromPosFiles() );
m_cbDNP->SetValue( m_symbol->GetDNP( &sheetPath, variantName ) );
if( m_part )
@@ -784,8 +785,8 @@ bool DIALOG_SYMBOL_PROPERTIES::TransferDataFromWindow()
// Restore the default field text for existing fields.
tmp->SetText( defaultText, &currentSheet );
tmp->SetText( m_symbol->Schematic()->ConvertRefsToKIIDs( field.GetText() ),
&currentSheet, currentVariant );
wxString variantText = m_symbol->Schematic()->ConvertRefsToKIIDs( field.GetText() );
tmp->SetText( variantText, &currentSheet, currentVariant );
}
}
@@ -796,6 +797,7 @@ bool DIALOG_SYMBOL_PROPERTIES::TransferDataFromWindow()
m_symbol->SetExcludedFromSim( m_cbExcludeFromSim->IsChecked(), &currentSheet, currentVariant );
m_symbol->SetExcludedFromBOM( m_cbExcludeFromBom->IsChecked(), &currentSheet, currentVariant );
m_symbol->SetExcludedFromBoard( m_cbExcludeFromBoard->IsChecked() );
m_symbol->SetExcludedFromPosFiles( m_cbExcludeFromPosFiles->IsChecked() );
m_symbol->SetDNP( m_cbDNP->IsChecked(), &currentSheet, currentVariant );
// Update any assignments
@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a-dirty)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@@ -218,6 +218,11 @@ DIALOG_SYMBOL_PROPERTIES_BASE::DIALOG_SYMBOL_PROPERTIES_BASE( wxWindow* parent,
sbAttributes->Add( m_cbExcludeFromBoard, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
m_cbExcludeFromPosFiles = new wxCheckBox( sbAttributes->GetStaticBox(), wxID_ANY, _("Exclude from position files"), wxDefaultPosition, wxDefaultSize, 0 );
m_cbExcludeFromPosFiles->SetToolTip( _("This is useful for adding symbols that should not be included in the \nexported position files used for pick and place machines") );
sbAttributes->Add( m_cbExcludeFromPosFiles, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
m_cbDNP = new wxCheckBox( sbAttributes->GetStaticBox(), wxID_ANY, _("Do not populate"), wxDefaultPosition, wxDefaultSize, 0 );
sbAttributes->Add( m_cbDNP, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 );
@@ -361,6 +366,7 @@ DIALOG_SYMBOL_PROPERTIES_BASE::DIALOG_SYMBOL_PROPERTIES_BASE( wxWindow* parent,
m_cbExcludeFromSim->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_cbExcludeFromBom->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_cbExcludeFromBoard->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_cbExcludeFromPosFiles->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_updateSymbolBtn->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnUpdateSymbol ), NULL, this );
m_changeSymbolBtn->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnExchangeSymbol ), NULL, this );
m_editSchematicSymbolBtn->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnEditSymbol ), NULL, this );
@@ -393,6 +399,7 @@ DIALOG_SYMBOL_PROPERTIES_BASE::~DIALOG_SYMBOL_PROPERTIES_BASE()
m_cbExcludeFromSim->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_cbExcludeFromBom->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_cbExcludeFromBoard->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_cbExcludeFromPosFiles->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnCheckBox ), NULL, this );
m_updateSymbolBtn->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnUpdateSymbol ), NULL, this );
m_changeSymbolBtn->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnExchangeSymbol ), NULL, this );
m_editSchematicSymbolBtn->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_PROPERTIES_BASE::OnEditSymbol ), NULL, this );
@@ -1569,6 +1569,72 @@
<event name="OnCheckBox">OnCheckBox</event>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="false">
<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="checked">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">Exclude from position files</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_cbExcludeFromPosFiles</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">This is useful for adding symbols that should not be included in the &#x0A;exported position files used for pick and place machines</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="OnCheckBox">OnCheckBox</event>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxLEFT|wxRIGHT</property>
@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a-dirty)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@@ -66,6 +66,7 @@ class DIALOG_SYMBOL_PROPERTIES_BASE : public DIALOG_SHIM
wxCheckBox* m_cbExcludeFromSim;
wxCheckBox* m_cbExcludeFromBom;
wxCheckBox* m_cbExcludeFromBoard;
wxCheckBox* m_cbExcludeFromPosFiles;
wxCheckBox* m_cbDNP;
wxButton* m_updateSymbolBtn;
wxButton* m_changeSymbolBtn;
+107 -43
View File
@@ -284,6 +284,16 @@ int EESCHEMA_JOBS_HANDLER::JobExportPlot( JOB* aJob )
aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
// Apply variant if specified
if( !aPlotJob->m_variantNames.empty() )
{
// For plot export, we use the first variant name from the set
wxString variantName = *aPlotJob->m_variantNames.begin();
if( variantName != wxS( "all" ) )
sch->SetCurrentVariant( variantName );
}
std::unique_ptr<SCH_RENDER_SETTINGS> renderSettings = std::make_unique<SCH_RENDER_SETTINGS>();
InitRenderSettings( renderSettings.get(), aPlotJob->m_theme, sch, aPlotJob->m_drawingSheet );
@@ -352,12 +362,14 @@ int EESCHEMA_JOBS_HANDLER::JobExportPlot( JOB* aJob )
plotOpts.m_theme = aPlotJob->m_theme;
plotOpts.m_useBackgroundColor = aPlotJob->m_useBackgroundColor;
plotOpts.m_plotHopOver = aPlotJob->m_show_hop_over;
plotOpts.m_variant = aPlotJob->m_variant;
// Use variant from m_variantNames if specified, otherwise use the schematic's current variant
if( !aPlotJob->m_variantNames.empty() )
plotOpts.m_variant = aPlotJob->m_variantNames.front();
// Always export dxf in mm by kicad-cli (similar to Pcbnew)
plotOpts.m_DXF_File_Unit = DXF_UNITS::MM;
sch->SetCurrentVariant( plotOpts.m_variant );
schPlotter->Plot( format, plotOpts, renderSettings.get(), m_reporter );
if( m_reporter->HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
@@ -381,6 +393,16 @@ int EESCHEMA_JOBS_HANDLER::JobExportNetlist( JOB* aJob )
aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
// Apply variant if specified
if( !aNetJob->m_variantNames.empty() )
{
// For netlist export, we use the first variant name from the set
wxString variantName = *aNetJob->m_variantNames.begin();
if( variantName != wxS( "all" ) )
sch->SetCurrentVariant( variantName );
}
// Annotation warning check
SCH_REFERENCE_LIST referenceList;
sch->Hierarchy().GetSymbols( referenceList );
@@ -498,7 +520,16 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
sch->SetCurrentVariant( aBomJob->m_variant );
wxString currentVariant;
if( !aBomJob->m_variantNames.empty() )
{
currentVariant = aBomJob->m_variantNames.front();
if( currentVariant != wxS( "all" ) )
sch->SetCurrentVariant( currentVariant );
}
// Annotation warning check
SCH_REFERENCE_LIST referenceList;
@@ -536,16 +567,16 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
for( FIELD_T fieldId : MANDATORY_FIELDS )
{
dataModel.AddColumn( GetCanonicalFieldName( fieldId ),
GetDefaultFieldName( fieldId, DO_TRANSLATE ), false, aBomJob->m_variant );
GetDefaultFieldName( fieldId, DO_TRANSLATE ), false, currentVariant );
}
// Generated/virtual fields (e.g. ${QUANTITY}, ${ITEM_NUMBER}) present only in the fields table
dataModel.AddColumn( FIELDS_EDITOR_GRID_DATA_MODEL::QUANTITY_VARIABLE,
GetGeneratedFieldDisplayName( FIELDS_EDITOR_GRID_DATA_MODEL::QUANTITY_VARIABLE ),
false, aBomJob->m_variant );
false, currentVariant );
dataModel.AddColumn( FIELDS_EDITOR_GRID_DATA_MODEL::ITEM_NUMBER_VARIABLE,
GetGeneratedFieldDisplayName( FIELDS_EDITOR_GRID_DATA_MODEL::ITEM_NUMBER_VARIABLE ),
false, aBomJob->m_variant );
false, currentVariant );
// Attribute fields (boolean flags on symbols)
dataModel.AddColumn( wxS( "${DNP}" ), GetGeneratedFieldDisplayName( wxS( "${DNP}" ) ),
@@ -572,7 +603,7 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
}
for( const wxString& fieldName : userFieldNames )
dataModel.AddColumn( fieldName, GetGeneratedFieldDisplayName( fieldName ), true, aBomJob->m_variant );
dataModel.AddColumn( fieldName, GetGeneratedFieldDisplayName( fieldName ), true, currentVariant );
// Add any templateFieldNames which aren't already present in the userFieldNames
for( const TEMPLATE_FIELDNAME& templateFieldname :
@@ -581,7 +612,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, aBomJob->m_variant );
false, currentVariant );
}
}
@@ -694,41 +725,11 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
preset.excludeDNP = aBomJob->m_excludeDNP;
}
dataModel.ApplyBomPreset( preset, aBomJob->m_variant );
if( aBomJob->GetConfiguredOutputPath().IsEmpty() )
{
wxFileName fn = sch->GetFileName();
fn.SetName( fn.GetName() );
fn.SetExt( FILEEXT::CsvFileExtension );
aBomJob->SetConfiguredOutputPath( fn.GetFullName() );
}
wxString outPath = aBomJob->GetFullOutputPath( &sch->Project() );
if( !PATHS::EnsurePathExists( outPath, true ) )
{
m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
return CLI::EXIT_CODES::ERR_INVALID_OUTPUT_CONFLICT;
}
wxFile f;
if( !f.Open( outPath, wxFile::write ) )
{
m_reporter->Report( wxString::Format( _( "Unable to open destination '%s'" ), outPath ),
RPT_SEVERITY_ERROR );
return CLI::EXIT_CODES::ERR_INVALID_INPUT_FILE;
}
BOM_FMT_PRESET fmt;
// Load a format preset if one is specified
if( !aBomJob->m_bomFmtPresetName.IsEmpty() )
{
// Find the preset
std::optional<BOM_FMT_PRESET> schFmtPreset;
for( const BOM_FMT_PRESET& p : BOM_FMT_PRESET::BuiltInPresets() )
@@ -770,13 +771,76 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
fmt.keepLineBreaks = aBomJob->m_keepLineBreaks;
}
bool res = f.Write( dataModel.Export( fmt ) );
if( aBomJob->GetConfiguredOutputPath().IsEmpty() )
{
wxFileName fn = sch->GetFileName();
fn.SetName( fn.GetName() );
fn.SetExt( FILEEXT::CsvFileExtension );
if( !res )
return CLI::EXIT_CODES::ERR_UNKNOWN;
aBomJob->SetConfiguredOutputPath( fn.GetFullName() );
}
m_reporter->Report( wxString::Format( _( "Wrote bill of materials to '%s'." ), outPath ),
RPT_SEVERITY_ACTION );
wxString configuredPath = aBomJob->GetConfiguredOutputPath();
bool hasVariantPlaceholder = configuredPath.Contains( wxS( "${VARIANT}" ) );
// Determine which variants to process
std::vector<wxString> variantsToProcess;
if( aBomJob->m_variantNames.size() > 1 && hasVariantPlaceholder )
{
variantsToProcess = aBomJob->m_variantNames;
}
else
{
variantsToProcess.push_back( currentVariant );
}
for( const wxString& variantName : variantsToProcess )
{
std::vector<wxString> singleVariant = { variantName };
dataModel.SetVariantNames( singleVariant );
dataModel.SetCurrentVariant( variantName );
dataModel.ApplyBomPreset( preset, variantName );
wxString outPath;
if( hasVariantPlaceholder )
{
wxString variantPath = configuredPath;
variantPath.Replace( wxS( "${VARIANT}" ), variantName );
aBomJob->SetConfiguredOutputPath( variantPath );
outPath = aBomJob->GetFullOutputPath( &sch->Project() );
aBomJob->SetConfiguredOutputPath( configuredPath );
}
else
{
outPath = aBomJob->GetFullOutputPath( &sch->Project() );
}
if( !PATHS::EnsurePathExists( outPath, true ) )
{
m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
return CLI::EXIT_CODES::ERR_INVALID_OUTPUT_CONFLICT;
}
wxFile f;
if( !f.Open( outPath, wxFile::write ) )
{
m_reporter->Report( wxString::Format( _( "Unable to open destination '%s'" ), outPath ),
RPT_SEVERITY_ERROR );
return CLI::EXIT_CODES::ERR_INVALID_INPUT_FILE;
}
bool res = f.Write( dataModel.Export( fmt ) );
if( !res )
return CLI::EXIT_CODES::ERR_UNKNOWN;
m_reporter->Report( wxString::Format( _( "Wrote bill of materials to '%s'." ), outPath ),
RPT_SEVERITY_ACTION );
}
return CLI::EXIT_CODES::OK;
}
+151 -7
View File
@@ -354,16 +354,87 @@ wxString FIELDS_EDITOR_GRID_DATA_MODEL::GetValue( int aRow, int aCol )
wxGridCellAttr* FIELDS_EDITOR_GRID_DATA_MODEL::GetAttr( int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind )
{
wxGridCellAttr* attr = nullptr;
if( GetColFieldName( aCol ) == GetCanonicalFieldName( FIELD_T::DATASHEET )
|| IsURL( GetValue( m_rows[aRow], aCol ) ) )
{
if( m_urlEditor )
{
m_urlEditor->IncRef();
return enhanceAttr( m_urlEditor, aRow, aCol, aKind );
attr = m_urlEditor;
}
}
// Highlight cells that differ from default when viewing a variant
if( !m_currentVariant.IsEmpty() && aRow >= 0 && aRow < (int) m_rows.size()
&& aCol >= 0 && aCol < (int) m_cols.size() )
{
const wxString& fieldName = m_cols[aCol].m_fieldName;
// Skip Reference and generated fields (like ${QUANTITY}) for highlighting
if( !ColIsReference( aCol ) && !ColIsQuantity( aCol ) && !ColIsItemNumber( aCol ) )
{
const DATA_MODEL_ROW& row = m_rows[aRow];
// Check if any symbol in this row has a variant-specific value
bool hasVariantDifference = false;
for( const SCH_REFERENCE& ref : row.m_Refs )
{
wxString defaultValue = getDefaultFieldValue( ref, fieldName );
KIID_PATH symbolKey = KIID_PATH();
if( const SCH_SYMBOL* symbol = ref.GetSymbol() )
{
symbolKey = ref.GetSheetPath().Path();
symbolKey.push_back( symbol->m_Uuid );
}
// Get the current value from the data store
wxString currentValue;
if( m_dataStore.contains( symbolKey ) && m_dataStore[symbolKey].contains( fieldName ) )
currentValue = m_dataStore[symbolKey][fieldName];
if( currentValue != defaultValue )
{
hasVariantDifference = true;
break;
}
}
if( hasVariantDifference )
{
if( !attr )
{
attr = new wxGridCellAttr();
}
else
{
// Clone so we don't modify the shared URL editor attr
wxGridCellAttr* newAttr = attr->Clone();
attr->DecRef();
attr = newAttr;
}
// Use a subtle highlight color that works in both light and dark themes
// Light yellow in light mode, darker yellow-ish in dark mode
wxColour bg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
bool isDark = ( bg.Red() + bg.Green() + bg.Blue() ) < 384;
if( isDark )
attr->SetBackgroundColour( wxColour( 80, 80, 40 ) ); // Dark gold/brown
else
attr->SetBackgroundColour( wxColour( 255, 255, 200 ) ); // Light yellow
}
}
}
if( attr )
return enhanceAttr( attr, aRow, aCol, aKind );
return WX_GRID_TABLE_BASE::GetAttr( aRow, aCol, aKind );
}
@@ -784,6 +855,38 @@ wxString FIELDS_EDITOR_GRID_DATA_MODEL::getAttributeValue( const SCH_REFERENCE&
}
wxString FIELDS_EDITOR_GRID_DATA_MODEL::getDefaultFieldValue( const SCH_REFERENCE& aRef,
const wxString& aFieldName )
{
const SCH_SYMBOL* symbol = aRef.GetSymbol();
if( !symbol )
return wxEmptyString;
// For attributes, get the default (non-variant) value
if( isAttribute( aFieldName ) )
return getAttributeValue( aRef, aFieldName, wxEmptyString );
// For regular fields, get the text without variant override
if( const SCH_FIELD* field = symbol->GetField( aFieldName ) )
{
if( field->IsPrivate() )
return wxEmptyString;
// Get the field text with empty variant name (default value)
wxString value = symbol->Schematic()->ConvertKIIDsToRefs(
field->GetText( &aRef.GetSheetPath(), wxEmptyString ) );
return value;
}
// For generated fields, return the field name itself
if( IsGeneratedField( aFieldName ) )
return aFieldName;
return wxEmptyString;
}
bool FIELDS_EDITOR_GRID_DATA_MODEL::setAttributeValue( SCH_REFERENCE& aRef,
const wxString& aAttributeName,
const wxString& aValue,
@@ -855,16 +958,56 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::RebuildRows()
if( !m_filter.IsEmpty() && !matcher.Find( ref.GetFullRef().Lower() ) )
continue;
if( m_excludeDNP && ( ref.GetSymbol()->GetDNP( &ref.GetSheetPath() )
|| ref.GetSheetPath().GetDNP() ) )
if( m_excludeDNP )
{
continue;
bool isDNP = false;
if( !m_variantNames.empty() )
{
for( const wxString& variantName : m_variantNames )
{
if( ref.GetSymbol()->GetDNP( &ref.GetSheetPath(), variantName )
|| ref.GetSheetPath().GetDNP( variantName ) )
{
isDNP = true;
break;
}
}
}
else
{
isDNP = ref.GetSymbol()->GetDNP( &ref.GetSheetPath(), m_currentVariant )
|| ref.GetSheetPath().GetDNP( m_currentVariant );
}
if( isDNP )
continue;
}
if( !m_includeExcluded && ( ref.GetSymbol()->GetExcludedFromBOM()
|| ref.GetSheetPath().GetExcludedFromBOM() ) )
if( !m_includeExcluded )
{
continue;
bool isExcluded = false;
if( !m_variantNames.empty() )
{
for( const wxString& variantName : m_variantNames )
{
if( ref.GetSymbol()->GetExcludedFromBOM( &ref.GetSheetPath(), variantName )
|| ref.GetSheetPath().GetExcludedFromBOM( variantName ) )
{
isExcluded = true;
break;
}
}
}
else
{
isExcluded = ref.GetSymbol()->GetExcludedFromBOM( &ref.GetSheetPath(), m_currentVariant )
|| ref.GetSheetPath().GetExcludedFromBOM( m_currentVariant );
}
if( isExcluded )
continue;
}
// Check if the symbol if on the current sheet or, in the sheet path somewhere
@@ -1210,6 +1353,7 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::ApplyBomPreset( const BOM_PRESET& aPreset, c
SetFilter( aPreset.filterString );
SetExcludeDNP( aPreset.excludeDNP );
SetIncludeExcludedFromBOM( aPreset.includeExcludedFromBOM );
SetCurrentVariant( aVariantName );
RebuildRows();
}
+27
View File
@@ -332,6 +332,20 @@ public:
const SCH_REFERENCE_LIST& GetReferenceList() const { return m_symbolsList; }
/**
* Set the current variant name for highlighting purposes.
*
* When a variant is set, cells that differ from the default (non-variant) value
* will be highlighted.
*
* @param aVariantName The name of the current variant, or empty string for default.
*/
void SetCurrentVariant( const wxString& aVariantName ) { m_currentVariant = aVariantName; }
const wxString& GetCurrentVariant() const { return m_currentVariant; }
void SetVariantNames( const std::vector<wxString>& aVariantNames ) { m_variantNames = aVariantNames; }
const std::vector<wxString>& GetVariantNames() const { return m_variantNames; }
private:
static bool cmp( const DATA_MODEL_ROW& lhGroup, const DATA_MODEL_ROW& rhGroup,
FIELDS_EDITOR_GRID_DATA_MODEL* dataModel, int sortCol, bool ascending );
@@ -345,6 +359,17 @@ private:
wxString getAttributeValue( const SCH_REFERENCE& aRef, const wxString& aAttributeName,
const wxString& aVariantNames );
/**
* Get the default (non-variant) value for a field.
*
* This retrieves the field value as it would appear without any variant override.
*
* @param aRef The symbol reference.
* @param aFieldName The name of the field.
* @return The default field value.
*/
wxString getDefaultFieldValue( const SCH_REFERENCE& aRef, const wxString& aFieldName );
/**
* Set the attribute value.
*
@@ -388,6 +413,8 @@ protected:
bool m_includeExcluded;
bool m_rebuildsEnabled;
wxGridCellAttr* m_urlEditor;
wxString m_currentVariant; ///< Current variant name for highlighting
std::vector<wxString> m_variantNames; ///< Variant names for multi-variant DNP filtering
std::vector<DATA_MODEL_COL> m_cols;
std::vector<DATA_MODEL_ROW> m_rows;
+19 -2
View File
@@ -2129,6 +2129,15 @@ int LIB_SYMBOL::Compare( const LIB_SYMBOL& aRhs, int aCompareFlags, REPORTER* aR
if( !aReporter )
return retv;
}
if( m_excludedFromPosFiles != aRhs.m_excludedFromPosFiles )
{
retv = ( m_excludedFromPosFiles ) ? -1 : 1;
REPORT( _( "Exclude from position files settings differ." ) );
if( !aReporter )
return retv;
}
}
if( !aReporter )
@@ -2223,6 +2232,9 @@ double LIB_SYMBOL::Similarity( const SCH_ITEM& aOther ) const
if( m_excludedFromSim != other.m_excludedFromSim )
similarity *= 0.9;
if( m_excludedFromPosFiles != other.m_excludedFromPosFiles )
similarity *= 0.9;
if( m_flags != other.m_flags )
similarity *= 0.9;
@@ -2396,13 +2408,18 @@ static struct LIB_SYMBOL_DESC
&LIB_SYMBOL::SetExcludedFromSimProp,
&LIB_SYMBOL::GetExcludedFromSimProp ),
groupAttributes );
propMgr.AddProperty( new PROPERTY<SYMBOL, bool>( _HKI( "Exclude from Board" ), &SYMBOL::SetExcludedFromBoard,
&SYMBOL::GetExcludedFromBoard ),
propMgr.AddProperty( new PROPERTY<LIB_SYMBOL, bool>( _HKI( "Exclude from Board" ),
&LIB_SYMBOL::SetExcludedFromBoardProp,
&LIB_SYMBOL::GetExcludedFromBoardProp ),
groupAttributes );
propMgr.AddProperty( new PROPERTY<LIB_SYMBOL, bool>( _HKI( "Exclude from Bill of Materials" ),
&LIB_SYMBOL::SetExcludedFromBOMProp,
&LIB_SYMBOL::GetExcludedFromBOMProp ),
groupAttributes );
propMgr.AddProperty( new PROPERTY<LIB_SYMBOL, bool>( _HKI( "Exclude from Position Files" ),
&LIB_SYMBOL::SetExcludedFromPosFilesProp,
&LIB_SYMBOL::GetExcludedFromPosFilesProp ),
groupAttributes );
const wxString groupUnits = _HKI( "Units and Body Styles" );
+13
View File
@@ -507,6 +507,19 @@ public:
SetExcludedFromBOM( aExclude );
}
bool GetExcludedFromBoardProp() const
{
return GetExcludedFromBoard();
}
void SetExcludedFromBoardProp( bool aExclude )
{
SetExcludedFromBoard( aExclude );
}
bool GetExcludedFromPosFilesProp() const { return GetExcludedFromPosFiles(); }
void SetExcludedFromPosFilesProp( bool aExclude ) { SetExcludedFromPosFiles( aExclude ); }
std::set<KIFONT::OUTLINE_FONT*> GetFonts() const override;
EMBEDDED_FILES* GetEmbeddedFiles() override;
@@ -396,6 +396,12 @@ XNODE* NETLIST_EXPORTER_XML::makeSymbols( unsigned aCtl )
xproperty->AddAttribute( wxT( "name" ), wxT( "exclude_from_board" ) );
}
if( symbol->ResolveExcludedFromPosFiles() )
{
xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
xproperty->AddAttribute( wxT( "name" ), wxT( "exclude_from_pos_files" ) );
}
if( symbol->ResolveDNP() || sheet.GetDNP() )
{
xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
+7
View File
@@ -267,6 +267,13 @@ public:
void SetCurrentVariant( const wxString& aVariantName );
/**
* Show a dialog to create a new variant with name and description.
*
* @return true if a variant was created, false if cancelled or invalid input
*/
bool ShowAddVariantDialog();
void onVariantSelected( wxCommandEvent& aEvent );
/**
-1
View File
@@ -1089,7 +1089,6 @@ void SCH_FIELD::SetText( const wxString& aText, const SCH_SHEET_PATH* aPath, con
{
wxCHECK( aPath && m_parent, /* void */ );
// Don't allow modification of text value of generated fields.
if( m_isGeneratedField )
return;
+2 -1
View File
@@ -133,4 +133,5 @@
//#define SEXPR_SCHEMATIC_FILE_VERSION 20250901 // Stacked Pin notation
//#define SEXPR_SCHEMATIC_FILE_VERSION 20250922 // Schematic variants.
//#define SEXPR_SCHEMATIC_FILE_VERSION 20251012 // Flat schematic hierarchy support
#define SEXPR_SCHEMATIC_FILE_VERSION 20251028 // Updated properties formatting (do_not_autoplace, show_name)
//#define SEXPR_SCHEMATIC_FILE_VERSION 20251028 // Updated properties formatting (do_not_autoplace, show_name)
#define SEXPR_SCHEMATIC_FILE_VERSION 20260101 // PCB variants
@@ -736,6 +736,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, "in_pos_files", !aSymbol->GetExcludedFromPosFiles() );
KICAD_FORMAT::FormatBool( m_out, "dnp", aSymbol->GetDNP() );
AUTOPLACE_ALGO fieldsAutoplaced = aSymbol->GetFieldsAutoplaced();
@@ -909,6 +910,12 @@ void SCH_IO_KICAD_SEXPR::saveSymbol( SCH_SYMBOL* aSymbol, const SCHEMATIC& aSche
if( variant.m_ExcludedFromBOM != aSymbol->GetExcludedFromBOM() )
KICAD_FORMAT::FormatBool( m_out, "in_bom", variant.m_ExcludedFromBOM );
if( variant.m_ExcludedFromBoard != aSymbol->GetExcludedFromBoard() )
KICAD_FORMAT::FormatBool( m_out, "on_board", !variant.m_ExcludedFromBoard );
if( variant.m_ExcludedFromPosFiles != aSymbol->GetExcludedFromPosFiles() )
KICAD_FORMAT::FormatBool( m_out, "in_pos_files", !variant.m_ExcludedFromPosFiles );
for( const auto&[fname, fvalue] : variant.m_Fields )
{
m_out->Print( "(field (name %s) (value %s))",
@@ -330,6 +330,7 @@ void SCH_IO_KICAD_SEXPR_LIB_CACHE::SaveSymbol( LIB_SYMBOL* aSymbol, OUTPUTFORMAT
KICAD_FORMAT::FormatBool( &aFormatter, "exclude_from_sim", aSymbol->GetExcludedFromSim() );
KICAD_FORMAT::FormatBool( &aFormatter, "in_bom", !aSymbol->GetExcludedFromBOM() );
KICAD_FORMAT::FormatBool( &aFormatter, "on_board", !aSymbol->GetExcludedFromBoard() );
KICAD_FORMAT::FormatBool( &aFormatter, "in_pos_files", !aSymbol->GetExcludedFromPosFiles() );
KICAD_FORMAT::FormatBool( &aFormatter, "duplicate_pin_numbers_are_jumpers",
aSymbol->GetDuplicatePinNumbersAreJumpers() );
@@ -417,6 +417,11 @@ LIB_SYMBOL* SCH_IO_KICAD_SEXPR_PARSER::parseLibSymbol( LIB_SYMBOL_MAP& aSymbolLi
NeedRIGHT();
break;
case T_in_pos_files:
symbol->SetExcludedFromPosFiles( !parseBool() );
NeedRIGHT();
break;
case T_duplicate_pin_numbers_are_jumpers:
symbol->SetDuplicatePinNumbersAreJumpers( parseBool() );
NeedRIGHT();
@@ -3211,6 +3216,11 @@ SCH_SYMBOL* SCH_IO_KICAD_SEXPR_PARSER::parseSchematicSymbol()
NeedRIGHT();
break;
case T_in_pos_files:
symbol->SetExcludedFromPosFiles( !parseBool() );
NeedRIGHT();
break;
case T_dnp:
symbol->SetDNP( parseBool() );
NeedRIGHT();
@@ -3392,6 +3402,16 @@ SCH_SYMBOL* SCH_IO_KICAD_SEXPR_PARSER::parseSchematicSymbol()
NeedRIGHT();
break;
case T_on_board:
variant.m_ExcludedFromBoard = !parseBool();
NeedRIGHT();
break;
case T_in_pos_files:
variant.m_ExcludedFromPosFiles = !parseBool();
NeedRIGHT();
break;
case T_field:
{
wxString fieldName;
@@ -3428,7 +3448,7 @@ SCH_SYMBOL* SCH_IO_KICAD_SEXPR_PARSER::parseSchematicSymbol()
}
default:
Expecting( "dnp, exclude_from_sim, field, in_bom, or name" );
Expecting( "dnp, exclude_from_sim, field, in_bom, in_pos_files, name, or on_board" );
}
instance.m_Variants[variant.m_Name] = variant;
@@ -3843,6 +3863,16 @@ SCH_SHEET* SCH_IO_KICAD_SEXPR_PARSER::parseSheet()
NeedRIGHT();
break;
case T_on_board:
variant.m_ExcludedFromBoard = !parseBool();
NeedRIGHT();
break;
case T_in_pos_files:
variant.m_ExcludedFromPosFiles = !parseBool();
NeedRIGHT();
break;
case T_field:
{
wxString fieldName;
@@ -3879,7 +3909,7 @@ SCH_SHEET* SCH_IO_KICAD_SEXPR_PARSER::parseSheet()
}
default:
Expecting( "dnp, exclude_from_sim, field, in_bom, or name" );
Expecting( "dnp, exclude_from_sim, field, in_bom, in_pos_files, name, or on_board" );
}
instance.m_Variants[variant.m_Name] = variant;
+20 -3
View File
@@ -306,14 +306,31 @@ bool SCH_ITEM::ResolveExcludedFromBOM( const SCH_SHEET_PATH* aInstance,
}
bool SCH_ITEM::ResolveExcludedFromBoard() const
bool SCH_ITEM::ResolveExcludedFromBoard( const SCH_SHEET_PATH* aInstance,
const wxString& aVariantName ) const
{
if( GetExcludedFromBoard() )
if( GetExcludedFromBoard( aInstance, aVariantName ) )
return true;
for( SCH_RULE_AREA* area : m_rule_areas_cache )
{
if( area->GetExcludedFromBoard() )
if( area->GetExcludedFromBoard( aInstance, aVariantName ) )
return true;
}
return false;
}
bool SCH_ITEM::ResolveExcludedFromPosFiles( const SCH_SHEET_PATH* aInstance,
const wxString& aVariantName ) const
{
if( GetExcludedFromPosFiles( aInstance, aVariantName ) )
return true;
for( SCH_RULE_AREA* area : m_rule_areas_cache )
{
if( area->GetExcludedFromPosFiles( aInstance, aVariantName ) )
return true;
}
+13 -3
View File
@@ -266,9 +266,19 @@ public:
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 SetExcludedFromBoard( bool aExclude, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) { }
virtual bool GetExcludedFromBoard( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const { return false; }
bool ResolveExcludedFromBoard( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const;
virtual void SetExcludedFromPosFiles( bool aExclude, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) { }
virtual bool GetExcludedFromPosFiles( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const { return false; }
bool ResolveExcludedFromPosFiles( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const;
virtual void SetDNP( bool aDNP, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) { }
+11 -2
View File
@@ -91,8 +91,17 @@ public:
/**
* Set or clear exclude from board netlist flag.
*/
void SetExcludedFromBoard( bool aExcludeFromBoard ) override { m_excludedFromBoard = aExcludeFromBoard; }
bool GetExcludedFromBoard() const override { return m_excludedFromBoard; }
void SetExcludedFromBoard( bool aExclude, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override
{
m_excludedFromBoard = aExclude;
}
bool GetExcludedFromBoard( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override
{
return m_excludedFromBoard;
}
/**
* Set or clear the 'Do Not Populate' flag.
+114
View File
@@ -1904,6 +1904,100 @@ void SCH_SCREEN::DeleteVariant( const wxString& aVariantName, SCH_COMMIT* aCommi
}
void SCH_SCREEN::RenameVariant( const wxString& aOldName, const wxString& aNewName,
SCH_COMMIT* aCommit )
{
wxCHECK( !aOldName.IsEmpty() && !aNewName.IsEmpty(), /* void */ );
for( SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) )
{
SCH_SYMBOL* symbol = static_cast<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( aOldName ) )
{
if( aCommit )
aCommit->Modify( item, this );
symbol->RenameVariant( instance.m_Path, aOldName, aNewName );
}
}
}
for( SCH_ITEM* item : Items().OfType( SCH_SHEET_T ) )
{
SCH_SHEET* sheet = static_cast<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( aOldName ) )
{
if( aCommit )
aCommit->Modify( item, this );
sheet->RenameVariant( instance.m_Path, aOldName, aNewName );
}
}
}
}
void SCH_SCREEN::CopyVariant( const wxString& aSourceVariant, const wxString& aNewVariant,
SCH_COMMIT* aCommit )
{
wxCHECK( !aSourceVariant.IsEmpty() && !aNewVariant.IsEmpty(), /* void */ );
for( SCH_ITEM* item : Items().OfType( SCH_SYMBOL_T ) )
{
SCH_SYMBOL* symbol = static_cast<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( aSourceVariant ) )
{
if( aCommit )
aCommit->Modify( item, this );
symbol->CopyVariant( instance.m_Path, aSourceVariant, aNewVariant );
}
}
}
for( SCH_ITEM* item : Items().OfType( SCH_SHEET_T ) )
{
SCH_SHEET* sheet = static_cast<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( aSourceVariant ) )
{
if( aCommit )
aCommit->Modify( item, this );
sheet->CopyVariant( instance.m_Path, aSourceVariant, aNewVariant );
}
}
}
}
#if defined(DEBUG)
void SCH_SCREEN::Show( int nestLevel, std::ostream& os ) const
{
@@ -2365,3 +2459,23 @@ void SCH_SCREENS::DeleteVariant( const wxString& aVariantName, SCH_COMMIT* aComm
for( SCH_SCREEN* screen : m_screens )
screen->DeleteVariant( aVariantName, aCommit );
}
void SCH_SCREENS::RenameVariant( const wxString& aOldName, const wxString& aNewName,
SCH_COMMIT* aCommit )
{
wxCHECK( !aOldName.IsEmpty() && !aNewName.IsEmpty(), /* void */ );
for( SCH_SCREEN* screen : m_screens )
screen->RenameVariant( aOldName, aNewName, aCommit );
}
void SCH_SCREENS::CopyVariant( const wxString& aSourceVariant, const wxString& aNewVariant,
SCH_COMMIT* aCommit )
{
wxCHECK( !aSourceVariant.IsEmpty() && !aNewVariant.IsEmpty(), /* void */ );
for( SCH_SCREEN* screen : m_screens )
screen->CopyVariant( aSourceVariant, aNewVariant, aCommit );
}
+12
View File
@@ -631,6 +631,12 @@ public:
void DeleteVariant( const wxString& aVariantName, SCH_COMMIT* aCommit = nullptr );
void RenameVariant( const wxString& aOldName, const wxString& aNewName,
SCH_COMMIT* aCommit = nullptr );
void CopyVariant( const wxString& aSourceVariant, const wxString& aNewVariant,
SCH_COMMIT* aCommit = nullptr );
private:
friend SCH_EDIT_FRAME; // Only to populate m_symbolInstances.
friend SCH_IO_KICAD_SEXPR_PARSER; // Only to load instance information from schematic file.
@@ -872,6 +878,12 @@ public:
void DeleteVariant( const wxString& aVariantName, SCH_COMMIT* aCommit = nullptr );
void RenameVariant( const wxString& aOldName, const wxString& aNewName,
SCH_COMMIT* aCommit = nullptr );
void CopyVariant( const wxString& aSourceVariant, const wxString& aNewVariant,
SCH_COMMIT* aCommit = nullptr );
private:
void addScreenToList( SCH_SCREEN* aScreen, SCH_SHEET* aSheet );
void buildScreenList( SCH_SHEET* aSheet);
+34 -1
View File
@@ -1851,6 +1851,39 @@ void SCH_SHEET::DeleteVariant( const KIID_PATH& aPath, const wxString& aVariantN
}
void SCH_SHEET::RenameVariant( const KIID_PATH& aPath, const wxString& aOldName,
const wxString& aNewName )
{
SCH_SHEET_INSTANCE* instance = getInstance( aPath );
// The instance path must already exist and contain the old variant.
if( !instance || !instance->m_Variants.contains( aOldName ) )
return;
// Get the variant data, update the name, and re-insert with new key
SCH_SHEET_VARIANT variant = instance->m_Variants[aOldName];
variant.m_Name = aNewName;
instance->m_Variants.erase( aOldName );
instance->m_Variants.insert( std::make_pair( aNewName, variant ) );
}
void SCH_SHEET::CopyVariant( const KIID_PATH& aPath, const wxString& aSourceVariant,
const wxString& aNewVariant )
{
SCH_SHEET_INSTANCE* instance = getInstance( aPath );
// The instance path must already exist and contain the source variant.
if( !instance || !instance->m_Variants.contains( aSourceVariant ) )
return;
// Copy the variant data with a new name
SCH_SHEET_VARIANT variant = instance->m_Variants[aSourceVariant];
variant.m_Name = aNewVariant;
instance->m_Variants.insert( std::make_pair( aNewVariant, variant ) );
}
void SCH_SHEET::SetDNP( bool aEnable, const SCH_SHEET_PATH* aInstance, const wxString& aVariantName )
{
if( !aInstance || aVariantName.IsEmpty() )
@@ -2114,7 +2147,7 @@ static struct SCH_SHEET_DESC
const wxString groupAttributes = _HKI( "Attributes" );
propMgr.AddProperty( new PROPERTY<SCH_SHEET, bool>( _HKI( "Exclude From Board" ),
&SCH_SHEET::SetExcludedFromBoard, &SCH_SHEET::GetExcludedFromBoard ),
&SCH_SHEET::SetExcludedFromBoardProp, &SCH_SHEET::GetExcludedFromBoardProp ),
groupAttributes );
propMgr.AddProperty( new PROPERTY<SCH_SHEET, bool>( _HKI( "Exclude From Simulation" ),
&SCH_SHEET::SetExcludedFromSimProp, &SCH_SHEET::GetExcludedFromSimProp ),
+31 -2
View File
@@ -458,8 +458,20 @@ public:
/**
* Set or clear exclude from board netlist flag.
*/
void SetExcludedFromBoard( bool aExcludeFromBoard ) override { m_excludedFromBoard = aExcludeFromBoard; }
bool GetExcludedFromBoard() const override { return m_excludedFromBoard; }
void SetExcludedFromBoard( bool aExclude, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override
{
m_excludedFromBoard = aExclude;
}
bool GetExcludedFromBoard( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override
{
return m_excludedFromBoard;
}
bool GetExcludedFromBoardProp() const { return GetExcludedFromBoard(); }
void SetExcludedFromBoardProp( bool aExclude ) { SetExcludedFromBoard( aExclude ); }
/**
* Set or clear the 'Do Not Populate' flags
@@ -525,12 +537,29 @@ public:
void DeleteVariant( const KIID_PATH& aPath, const wxString& aVariantName );
void RenameVariant( const KIID_PATH& aPath, const wxString& aOldName, const wxString& aNewName );
void CopyVariant( const KIID_PATH& aPath, const wxString& aSourceVariant,
const wxString& aNewVariant );
void AddVariant( const SCH_SHEET_PATH& aInstance, const SCH_SHEET_VARIANT& aVariant );
void DeleteVariant( const SCH_SHEET_PATH& aInstance, const wxString& aVariantName )
{
DeleteVariant( aInstance.Path(), aVariantName );
}
void RenameVariant( const SCH_SHEET_PATH& aInstance, const wxString& aOldName,
const wxString& aNewName )
{
RenameVariant( aInstance.Path(), aOldName, aNewName );
}
void CopyVariant( const SCH_SHEET_PATH& aInstance, const wxString& aSourceVariant,
const wxString& aNewVariant )
{
CopyVariant( aInstance.Path(), aSourceVariant, aNewVariant );
}
/**
* Check if the instance data of this sheet has any changes compared to \a aOther.
*
+49
View File
@@ -100,6 +100,8 @@ void SCH_SYMBOL_VARIANT::InitializeAttributes( const SCH_SYMBOL& aSymbol )
m_DNP = aSymbol.GetDNP();
m_ExcludedFromBOM = aSymbol.GetExcludedFromBOM();
m_ExcludedFromSim = aSymbol.GetExcludedFromSim();
m_ExcludedFromBoard = aSymbol.GetExcludedFromBoard();
m_ExcludedFromPosFiles = aSymbol.GetExcludedFromPosFiles();
}
@@ -108,6 +110,8 @@ void SCH_SHEET_VARIANT::InitializeAttributes( const SCH_SHEET& aSheet )
m_DNP = aSheet.GetDNP();
m_ExcludedFromBOM = aSheet.GetExcludedFromBOM();
m_ExcludedFromSim = aSheet.GetExcludedFromSim();
m_ExcludedFromBoard = aSheet.GetExcludedFromBoard();
m_ExcludedFromPosFiles = false; // Sheets don't have position files exclusion
}
@@ -299,6 +303,21 @@ bool SCH_SHEET_PATH::GetExcludedFromSim() const
}
bool SCH_SHEET_PATH::GetExcludedFromSim( const wxString& aVariantName ) const
{
if( aVariantName.IsEmpty() )
return GetExcludedFromSim();
for( SCH_SHEET* sheet : m_sheets )
{
if( sheet->GetExcludedFromSim( this, aVariantName ) )
return true;
}
return false;
}
bool SCH_SHEET_PATH::GetExcludedFromBOM() const
{
for( SCH_SHEET* sheet : m_sheets )
@@ -311,6 +330,21 @@ bool SCH_SHEET_PATH::GetExcludedFromBOM() const
}
bool SCH_SHEET_PATH::GetExcludedFromBOM( const wxString& aVariantName ) const
{
if( aVariantName.IsEmpty() )
return GetExcludedFromBOM();
for( SCH_SHEET* sheet : m_sheets )
{
if( sheet->GetExcludedFromBOM( this, aVariantName ) )
return true;
}
return false;
}
bool SCH_SHEET_PATH::GetExcludedFromBoard() const
{
for( SCH_SHEET* sheet : m_sheets )
@@ -335,6 +369,21 @@ bool SCH_SHEET_PATH::GetDNP() const
}
bool SCH_SHEET_PATH::GetDNP( const wxString& aVariantName ) const
{
if( aVariantName.IsEmpty() )
return GetDNP();
for( SCH_SHEET* sheet : m_sheets )
{
if( sheet->GetDNP( this, aVariantName ) )
return true;
}
return false;
}
wxString SCH_SHEET_PATH::PathAsString() const
{
wxString s;
+13 -3
View File
@@ -51,6 +51,8 @@ public:
m_Name( aName ),
m_ExcludedFromSim( false ),
m_ExcludedFromBOM( false ),
m_ExcludedFromBoard( false ),
m_ExcludedFromPosFiles( false ),
m_DNP( false )
{
}
@@ -58,8 +60,11 @@ public:
virtual ~VARIANT() = default;
wxString m_Name;
wxString m_Description;
bool m_ExcludedFromSim;
bool m_ExcludedFromBOM;
bool m_ExcludedFromBoard;
bool m_ExcludedFromPosFiles;
bool m_DNP;
std::map<wxString, wxString> m_Fields;
};
@@ -71,9 +76,7 @@ public:
* 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.
* @note The #REFERENCE field is immutable across variants. Changing it would effectively be a new board.
*/
class SCH_SYMBOL_VARIANT : public VARIANT
{
@@ -110,6 +113,8 @@ struct SCH_SYMBOL_INSTANCE
bool m_DNP = false;
bool m_ExcludedFromBOM = false;
bool m_ExcludedFromSim = false;
bool m_ExcludedFromBoard = false;
bool m_ExcludedFromPosFiles = false;
/// A list of symbol variants.
std::map<wxString, SCH_SYMBOL_VARIANT> m_Variants;
@@ -154,6 +159,8 @@ struct SCH_SHEET_INSTANCE
bool m_DNP = false;
bool m_ExcludedFromBOM = false;
bool m_ExcludedFromSim = false;
bool m_ExcludedFromBoard = false;
bool m_ExcludedFromPosFiles = false;
/// A list of sheet variants.
std::map<wxString, SCH_SHEET_VARIANT> m_Variants;
@@ -354,9 +361,12 @@ public:
SCH_SCREEN* LastScreen() const;
bool GetExcludedFromSim() const;
bool GetExcludedFromSim( const wxString& aVariantName ) const;
bool GetExcludedFromBOM() const;
bool GetExcludedFromBOM( const wxString& aVariantName ) const;
bool GetExcludedFromBoard() const;
bool GetDNP() const;
bool GetDNP( const wxString& aVariantName ) const;
/**
* Fetch a SCH_ITEM by ID.
+209 -15
View File
@@ -677,6 +677,13 @@ void SCH_SYMBOL::SetRefProp( const wxString& aRef )
}
void SCH_SYMBOL::SetValueProp( const wxString& aValue )
{
wxString currentVariant = Schematic()->GetCurrentVariant();
SetValueFieldText( aValue, &Schematic()->CurrentSheet(), currentVariant );
}
void SCH_SYMBOL::SetRef( const SCH_SHEET_PATH* sheet, const wxString& ref )
{
KIID_PATH path = sheet->Path();
@@ -734,7 +741,10 @@ void SCH_SYMBOL::SetFieldText( const wxString& aFieldName, const wxString& aFiel
break;
default:
if( aFieldText != field->GetText( aPath ) ) // Do not set the variant unless it's different than the default.
{
wxString defaultText = field->GetText( aPath );
if( aFieldText != defaultText )
{
if( aVariantName.IsEmpty() )
{
@@ -763,6 +773,7 @@ void SCH_SYMBOL::SetFieldText( const wxString& aFieldName, const wxString& aFiel
break;
}
}
}
@@ -1061,6 +1072,124 @@ bool SCH_SYMBOL::GetExcludedFromSim( const SCH_SHEET_PATH* aInstance, const wxSt
}
void SCH_SYMBOL::SetExcludedFromBoard( bool aEnable, const SCH_SHEET_PATH* aInstance,
const wxString& aVariantName )
{
if( !aInstance || aVariantName.IsEmpty() )
{
m_excludedFromBoard = aEnable;
return;
}
SCH_SYMBOL_INSTANCE* instance = getInstance( *aInstance );
wxCHECK_MSG( instance, /* void */,
wxString::Format( wxS( "Cannot set exclude from board for invalid sheet path '%s'." ),
aInstance->PathHumanReadable() ) );
if( aVariantName.IsEmpty() )
{
m_excludedFromBoard = aEnable;
}
else
{
if( instance->m_Variants.contains( aVariantName )
&& ( aEnable != instance->m_Variants[aVariantName].m_ExcludedFromBoard ) )
{
instance->m_Variants[aVariantName].m_ExcludedFromBoard = aEnable;
}
else
{
SCH_SYMBOL_VARIANT variant( aVariantName );
variant.InitializeAttributes( *this );
variant.m_ExcludedFromBoard = aEnable;
AddVariant( *aInstance, variant );
}
}
}
bool SCH_SYMBOL::GetExcludedFromBoard( const SCH_SHEET_PATH* aInstance,
const wxString& aVariantName ) const
{
if( !aInstance || aVariantName.IsEmpty() )
return m_excludedFromBoard;
SCH_SYMBOL_INSTANCE instance;
if( !GetInstance( instance, aInstance->Path() ) )
return m_excludedFromBoard;
if( aVariantName.IsEmpty() )
return m_excludedFromBoard;
else if( instance.m_Variants.contains( aVariantName ) )
return instance.m_Variants[aVariantName].m_ExcludedFromBoard;
// If variant is not defined yet, return default exclude from board setting.
return m_excludedFromBoard;
}
void SCH_SYMBOL::SetExcludedFromPosFiles( bool aEnable, const SCH_SHEET_PATH* aInstance,
const wxString& aVariantName )
{
if( !aInstance || aVariantName.IsEmpty() )
{
m_excludedFromPosFiles = aEnable;
return;
}
SCH_SYMBOL_INSTANCE* instance = getInstance( *aInstance );
wxCHECK_MSG( instance, /* void */,
wxString::Format( wxS( "Cannot set exclude from pos files for invalid sheet path '%s'." ),
aInstance->PathHumanReadable() ) );
if( aVariantName.IsEmpty() )
{
m_excludedFromPosFiles = aEnable;
}
else
{
if( instance->m_Variants.contains( aVariantName )
&& ( aEnable != instance->m_Variants[aVariantName].m_ExcludedFromPosFiles ) )
{
instance->m_Variants[aVariantName].m_ExcludedFromPosFiles = aEnable;
}
else
{
SCH_SYMBOL_VARIANT variant( aVariantName );
variant.InitializeAttributes( *this );
variant.m_ExcludedFromPosFiles = aEnable;
AddVariant( *aInstance, variant );
}
}
}
bool SCH_SYMBOL::GetExcludedFromPosFiles( const SCH_SHEET_PATH* aInstance,
const wxString& aVariantName ) const
{
if( !aInstance || aVariantName.IsEmpty() )
return m_excludedFromPosFiles;
SCH_SYMBOL_INSTANCE instance;
if( !GetInstance( instance, aInstance->Path() ) )
return m_excludedFromPosFiles;
if( aVariantName.IsEmpty() )
return m_excludedFromPosFiles;
else if( instance.m_Variants.contains( aVariantName ) )
return instance.m_Variants[aVariantName].m_ExcludedFromPosFiles;
// If variant is not defined yet, return default exclude from position files setting.
return m_excludedFromPosFiles;
}
void SCH_SYMBOL::SetUnitSelection( int aUnitSelection )
{
for( SCH_SYMBOL_INSTANCE& instance : m_instanceReferences )
@@ -1084,7 +1213,11 @@ const wxString SCH_SYMBOL::GetValue( bool aResolve, const SCH_SHEET_PATH* aInsta
if( variant && variant->m_Fields.contains( GetField( FIELD_T::VALUE )->GetName() ) )
return variant->m_Fields[GetField( FIELD_T::VALUE )->GetName()];
return wxEmptyString;
// Fall back to default value when variant doesn't have an override
if( aResolve )
return GetField( FIELD_T::VALUE )->GetShownText( aInstance, aAllowExtraText );
return GetField( FIELD_T::VALUE )->GetText();
}
@@ -1097,18 +1230,23 @@ void SCH_SYMBOL::SetValueFieldText( const wxString& aValue, const SCH_SHEET_PATH
return;
}
std::optional variant = GetVariant( *aInstance, aVariantName );
SCH_SYMBOL_INSTANCE* instance = getInstance( *aInstance );
if( variant )
wxCHECK( instance, /* void */ );
wxString fieldName = GetField( FIELD_T::VALUE )->GetName();
if( instance->m_Variants.contains( aVariantName ) )
{
variant->m_Fields[GetField( FIELD_T::VALUE )->GetName()] = aValue;
instance->m_Variants[aVariantName].m_Fields[fieldName] = aValue;
}
else
{
SCH_SYMBOL_VARIANT newVariant( aVariantName );
newVariant.m_Fields[GetField( FIELD_T::VALUE )->GetName()] = aValue;
AddVariant( *aInstance, newVariant );
newVariant.InitializeAttributes( *this );
newVariant.m_Fields[fieldName] = aValue;
instance->m_Variants.insert( std::make_pair( aVariantName, newVariant ) );
}
}
@@ -1591,6 +1729,13 @@ void SCH_SYMBOL::GetContextualTextVars( wxArrayString* aVars ) const
bool SCH_SYMBOL::ResolveTextVar( const SCH_SHEET_PATH* aPath, wxString* token, int aDepth ) const
{
return ResolveTextVar( aPath, token, wxEmptyString, aDepth );
}
bool SCH_SYMBOL::ResolveTextVar( const SCH_SHEET_PATH* aPath, wxString* token,
const wxString& aVariantName, int aDepth ) const
{
static wxRegEx operatingPoint( wxT( "^"
"OP"
@@ -1697,9 +1842,23 @@ bool SCH_SYMBOL::ResolveTextVar( const SCH_SHEET_PATH* aPath, wxString* token, i
if( token->IsSameAs( fieldName, false ) )
{
if( field.GetId() == FIELD_T::REFERENCE )
{
*token = GetRef( aPath, true );
}
else if( !aVariantName.IsEmpty() )
{
// Check for variant-specific field value
std::optional<SCH_SYMBOL_VARIANT> variant = GetVariant( *aPath, aVariantName );
if( variant && variant->m_Fields.contains( fieldName ) )
*token = variant->m_Fields.at( fieldName );
else
*token = field.GetShownText( aPath, false, aDepth + 1 );
}
else
{
*token = field.GetShownText( aPath, false, aDepth + 1 );
}
return true;
}
@@ -3422,6 +3581,39 @@ void SCH_SYMBOL::DeleteVariant( const KIID_PATH& aPath, const wxString& aVariant
}
void SCH_SYMBOL::RenameVariant( const KIID_PATH& aPath, const wxString& aOldName,
const wxString& aNewName )
{
SCH_SYMBOL_INSTANCE* instance = getInstance( aPath );
// The instance path must already exist and contain the old variant.
if( !instance || !instance->m_Variants.contains( aOldName ) )
return;
// Get the variant data, update the name, and re-insert with new key
SCH_SYMBOL_VARIANT variant = instance->m_Variants[aOldName];
variant.m_Name = aNewName;
instance->m_Variants.erase( aOldName );
instance->m_Variants.insert( std::make_pair( aNewName, variant ) );
}
void SCH_SYMBOL::CopyVariant( const KIID_PATH& aPath, const wxString& aSourceVariant,
const wxString& aNewVariant )
{
SCH_SYMBOL_INSTANCE* instance = getInstance( aPath );
// The instance path must already exist and contain the source variant.
if( !instance || !instance->m_Variants.contains( aSourceVariant ) )
return;
// Copy the variant data with a new name
SCH_SYMBOL_VARIANT variant = instance->m_Variants[aSourceVariant];
variant.m_Name = aNewVariant;
instance->m_Variants.insert( std::make_pair( aNewVariant, variant ) );
}
bool SCH_SYMBOL::operator==( const SCH_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
@@ -3668,13 +3860,6 @@ static struct SCH_SYMBOL_DESC
const wxString groupAttributes = _HKI( "Attributes" );
// This property is created in lib_symbol.cpp as SYMBOL property, so do not recreate it
#if 0
propMgr.AddProperty( new PROPERTY<SYMBOL, bool>( _HKI( "Exclude From Board" ),
&SYMBOL::SetExcludedFromBoard,
&SYMBOL::GetExcludedFromBoard ),
groupAttributes );
#endif
propMgr.AddProperty( new PROPERTY<SCH_SYMBOL, bool>( _HKI( "Exclude From Simulation" ),
&SCH_SYMBOL::SetExcludedFromSimProp,
&SCH_SYMBOL::GetExcludedFromSimProp ),
@@ -3683,7 +3868,16 @@ static struct SCH_SYMBOL_DESC
&SCH_SYMBOL::SetExcludedFromBOMProp,
&SCH_SYMBOL::GetExcludedFromBOMProp ),
groupAttributes );
propMgr.AddProperty( new PROPERTY<SCH_SYMBOL, bool>( _HKI( "Do not Populate" ), &SCH_SYMBOL::SetDNPProp,
propMgr.AddProperty( new PROPERTY<SCH_SYMBOL, bool>( _HKI( "Exclude From Board" ),
&SCH_SYMBOL::SetExcludedFromBoardProp,
&SCH_SYMBOL::GetExcludedFromBoardProp ),
groupAttributes );
propMgr.AddProperty( new PROPERTY<SCH_SYMBOL, bool>( _HKI( "Exclude From Position Files" ),
&SCH_SYMBOL::SetExcludedFromPosFilesProp,
&SCH_SYMBOL::GetExcludedFromPosFilesProp ),
groupAttributes );
propMgr.AddProperty( new PROPERTY<SCH_SYMBOL, bool>( _HKI( "Do not Populate" ),
&SCH_SYMBOL::SetDNPProp,
&SCH_SYMBOL::GetDNPProp ),
groupAttributes );
}
+61 -4
View File
@@ -361,10 +361,23 @@ public:
/**
* Resolve any references to system tokens supported by the symbol.
*
* @param aPath the sheet path for context.
* @param token the token to resolve (modified in place with the result).
* @param aDepth a counter to limit recursion and circular references.
*/
bool ResolveTextVar( const SCH_SHEET_PATH* aPath, wxString* token, int aDepth = 0 ) const;
/**
* Resolve any references to system tokens supported by the symbol with variant support.
*
* @param aPath the sheet path for context.
* @param token the token to resolve (modified in place with the result).
* @param aVariantName optional variant name to resolve field values for a specific variant.
* @param aDepth a counter to limit recursion and circular references.
*/
bool ResolveTextVar( const SCH_SHEET_PATH* aPath, wxString* token,
const wxString& aVariantName, int aDepth = 0 ) const;
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
/**
@@ -500,10 +513,7 @@ public:
return GetValue( false, &Schematic()->CurrentSheet(), false, Schematic()->GetCurrentVariant() );
}
void SetValueProp( const wxString& aValue )
{
SetValueFieldText( aValue, &Schematic()->CurrentSheet(), Schematic()->GetCurrentVariant() );
}
void SetValueProp( const wxString& aValue ); // Implemented in sch_symbol.cpp for tracing
int GetUnitProp() const
{
@@ -721,6 +731,36 @@ public:
SetExcludedFromSim( aEnable, &Schematic()->CurrentSheet(), Schematic()->GetCurrentVariant() );
}
void SetExcludedFromBoard( bool aEnable, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override;
bool GetExcludedFromBoard( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override;
bool GetExcludedFromBoardProp() const
{
return GetExcludedFromBoard( &Schematic()->CurrentSheet(), Schematic()->GetCurrentVariant() );
}
void SetExcludedFromBoardProp( bool aEnable )
{
SetExcludedFromBoard( aEnable, &Schematic()->CurrentSheet(), Schematic()->GetCurrentVariant() );
}
void SetExcludedFromPosFiles( bool aEnable, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override;
bool GetExcludedFromPosFiles( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override;
bool GetExcludedFromPosFilesProp() const
{
return GetExcludedFromPosFiles( &Schematic()->CurrentSheet(), Schematic()->GetCurrentVariant() );
}
void SetExcludedFromPosFilesProp( bool aEnable )
{
SetExcludedFromPosFiles( aEnable, &Schematic()->CurrentSheet(), Schematic()->GetCurrentVariant() );
}
/**
* SCH_SYMBOLs don't currently support embedded files, but their LIB_SYMBOL counterparts
* do.
@@ -901,6 +941,11 @@ public:
void DeleteVariant( const KIID_PATH& aPath, const wxString& aVariantName );
void RenameVariant( const KIID_PATH& aPath, const wxString& aOldName, const wxString& aNewName );
void CopyVariant( const KIID_PATH& aPath, const wxString& aSourceVariant,
const wxString& aNewVariant );
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 );
@@ -909,6 +954,18 @@ public:
DeleteVariant( aInstance.Path(), aVariantName );
}
void RenameVariant( const SCH_SHEET_PATH& aInstance, const wxString& aOldName,
const wxString& aNewName )
{
RenameVariant( aInstance.Path(), aOldName, aNewName );
}
void CopyVariant( const SCH_SHEET_PATH& aInstance, const wxString& aSourceVariant,
const wxString& aNewVariant )
{
CopyVariant( aInstance.Path(), aSourceVariant, aNewVariant );
}
bool operator==( const SCH_ITEM& aOther ) const override;
protected:
+117 -8
View File
@@ -418,6 +418,8 @@ void SCHEMATIC::GetContextualTextVars( wxArrayString* aVars ) const
add( wxT( "FILENAME" ) );
add( wxT( "FILEPATH" ) );
add( wxT( "PROJECTNAME" ) );
add( wxT( "VARIANT" ) );
add( wxT( "VARIANT_DESC" ) );
if( !CurrentSheet().empty() )
CurrentSheet().LastScreen()->GetTitleBlock().GetContextualTextVars( aVars );
@@ -468,11 +470,16 @@ bool SCHEMATIC::ResolveTextVar( const SCH_SHEET_PATH* aSheetPath, wxString* toke
*token = m_project->GetProjectName();
return true;
}
else if( token->IsSameAs( wxT( "VARIANTNAME" ) ) )
else if( token->IsSameAs( wxT( "VARIANTNAME" ) ) || token->IsSameAs( wxT( "VARIANT" ) ) )
{
*token = m_currentVariant;
return true;
}
else if( token->IsSameAs( wxT( "VARIANT_DESC" ) ) )
{
*token = GetVariantDescription( m_currentVariant );
return true;
}
// aSheetPath->LastScreen() can be null during schematic loading
if( aSheetPath->LastScreen() && aSheetPath->LastScreen()->GetTitleBlock().TextVarResolver( token, m_project ) )
@@ -737,6 +744,18 @@ bool SCHEMATIC::ResolveCrossReference( wxString* token, int aDepth ) const
sheetPath = Hierarchy().GetSheetPathByKIIDPath( path ).value_or( sheetPath );
}
// Parse optional variant name from syntax ${REF:FIELD:VARIANT}
// remainder is "FIELD" or "FIELD:VARIANT"
wxString variantName;
wxString fieldName = remainder;
int colonPos = remainder.Find( ':' );
if( colonPos != wxNOT_FOUND )
{
fieldName = remainder.Left( colonPos );
variantName = remainder.Mid( colonPos + 1 );
}
// Note: We don't expand nested variables or evaluate math expressions here.
// The multi-pass loop in GetShownText handles all variable and expression resolution
// before cross-references are resolved. This ensures table cell variables like ${ROW}
@@ -746,17 +765,16 @@ bool SCHEMATIC::ResolveCrossReference( wxString* token, int aDepth ) const
{
SCH_SYMBOL* refSymbol = static_cast<SCH_SYMBOL*>( refItem );
bool resolved = refSymbol->ResolveTextVar( &sheetPath, &remainder, aDepth + 1 );
bool resolved = refSymbol->ResolveTextVar( &sheetPath, &fieldName, variantName, aDepth + 1 );
if( resolved )
{
*token = std::move( remainder );
*token = std::move( fieldName );
}
else
{
// Field/function not found on symbol
*token =
wxString::Format( wxT( "<Unresolved: %s:%s>" ), refSymbol->GetRef( &sheetPath, false ), remainder );
*token = wxString::Format( wxT( "<Unresolved: %s:%s>" ), refSymbol->GetRef( &sheetPath, false ), fieldName );
}
return true;
@@ -818,17 +836,17 @@ bool SCHEMATIC::ResolveCrossReference( wxString* token, int aDepth ) const
if( foundSymbol )
{
bool resolved = foundSymbol->ResolveTextVar( &foundPath, &remainder, aDepth + 1 );
bool resolved = foundSymbol->ResolveTextVar( &foundPath, &fieldName, variantName, aDepth + 1 );
if( resolved )
{
*token = std::move( remainder );
*token = std::move( fieldName );
}
else
{
// Field/function not found on symbol
*token = wxString::Format( wxT( "<Unresolved: %s:%s>" ), foundSymbol->GetRef( &foundPath, false ),
remainder );
fieldName );
}
return true;
@@ -2128,6 +2146,18 @@ void SCHEMATIC::SetCurrentVariant( const wxString& aVariantName )
}
void SCHEMATIC::AddVariant( const wxString& aVariantName )
{
m_variantNames.emplace( aVariantName );
// Ensure the variant is registered in the project file
auto& descriptions = Settings().m_VariantDescriptions;
if( descriptions.find( aVariantName ) == descriptions.end() )
descriptions[aVariantName] = wxEmptyString;
}
void SCHEMATIC::DeleteVariant( const wxString& aVariantName, SCH_COMMIT* aCommit )
{
wxCHECK( m_rootSheet, /* void */ );
@@ -2137,6 +2167,76 @@ void SCHEMATIC::DeleteVariant( const wxString& aVariantName, SCH_COMMIT* aCommit
allScreens.DeleteVariant( aVariantName, aCommit );
m_variantNames.erase( aVariantName );
Settings().m_VariantDescriptions.erase( aVariantName );
}
void SCHEMATIC::RenameVariant( const wxString& aOldName, const wxString& aNewName,
SCH_COMMIT* aCommit )
{
wxCHECK( m_rootSheet, /* void */ );
wxCHECK( !aOldName.IsEmpty() && !aNewName.IsEmpty(), /* void */ );
wxCHECK( m_variantNames.contains( aOldName ), /* void */ );
m_variantNames.erase( aOldName );
m_variantNames.insert( aNewName );
auto& descriptions = Settings().m_VariantDescriptions;
if( descriptions.count( aOldName ) )
{
descriptions[aNewName] = descriptions[aOldName];
descriptions.erase( aOldName );
}
if( m_currentVariant == aOldName )
m_currentVariant = aNewName;
SCH_SCREENS allScreens( m_rootSheet );
allScreens.RenameVariant( aOldName, aNewName, aCommit );
}
void SCHEMATIC::CopyVariant( const wxString& aSourceVariant, const wxString& aNewVariant,
SCH_COMMIT* aCommit )
{
wxCHECK( m_rootSheet, /* void */ );
wxCHECK( !aSourceVariant.IsEmpty() && !aNewVariant.IsEmpty(), /* void */ );
wxCHECK( m_variantNames.contains( aSourceVariant ), /* void */ );
wxCHECK( !m_variantNames.contains( aNewVariant ), /* void */ );
AddVariant( aNewVariant );
auto& descriptions = Settings().m_VariantDescriptions;
if( descriptions.count( aSourceVariant ) )
descriptions[aNewVariant] = descriptions[aSourceVariant];
SCH_SCREENS allScreens( m_rootSheet );
allScreens.CopyVariant( aSourceVariant, aNewVariant, aCommit );
}
wxString SCHEMATIC::GetVariantDescription( const wxString& aVariantName ) const
{
const auto& descriptions = Settings().m_VariantDescriptions;
auto it = descriptions.find( aVariantName );
if( it != descriptions.end() )
return it->second;
return wxEmptyString;
}
void SCHEMATIC::SetVariantDescription( const wxString& aVariantName, const wxString& aDescription )
{
auto& descriptions = Settings().m_VariantDescriptions;
if( aDescription.IsEmpty() )
descriptions.erase( aVariantName );
else
descriptions[aVariantName] = aDescription;
}
@@ -2147,6 +2247,15 @@ void SCHEMATIC::LoadVariants()
SCH_SCREENS screens( m_rootSheet );
std::set<wxString> variantNames = screens.GetVariantNames();
m_variantNames.insert( variantNames.begin(), variantNames.end() );
// Register any unknown variants to the project file with empty descriptions
auto& descriptions = Settings().m_VariantDescriptions;
for( const wxString& name : variantNames )
{
if( descriptions.find( name ) == descriptions.end() )
descriptions[name] = wxEmptyString;
}
}
}
+47 -1
View File
@@ -465,7 +465,53 @@ public:
*/
void DeleteVariant( const wxString& aVariantName, SCH_COMMIT* aCommit = nullptr );
void AddVariant( const wxString& aVariantName ) { m_variantNames.emplace( aVariantName ); }
void AddVariant( const wxString& aVariantName );
/**
* Rename a variant from @a aOldName to @a aNewName.
*
* This updates the variant name in all symbols and sheets throughout the schematic.
*
* @param aOldName is the current name of the variant to rename.
* @param aNewName is the new name for the variant.
* @param aCommit is an optional SCH_COMMIT for undo/redo support.
*/
void RenameVariant( const wxString& aOldName, const wxString& aNewName,
SCH_COMMIT* aCommit = nullptr );
/**
* Copy a variant from @a aSourceVariant to @a aNewVariant.
*
* This creates a new variant with data copied from the source variant for all
* symbols and sheets throughout the schematic.
*
* @param aSourceVariant is the name of the variant to copy from.
* @param aNewVariant is the name of the new variant to create.
* @param aCommit is an optional SCH_COMMIT for undo/redo support.
*/
void CopyVariant( const wxString& aSourceVariant, const wxString& aNewVariant,
SCH_COMMIT* aCommit = nullptr );
/**
* Return the set of variant names (without the default placeholder).
*/
const std::set<wxString>& GetVariantNames() const { return m_variantNames; }
/**
* Return the description for a variant.
*
* @param aVariantName is the name of the variant.
* @return the description or an empty string if the variant has no description.
*/
wxString GetVariantDescription( const wxString& aVariantName ) const;
/**
* Set the description for a variant.
*
* @param aVariantName is the name of the variant.
* @param aDescription is the description to set.
*/
void SetVariantDescription( const wxString& aVariantName, const wxString& aDescription );
/**
* This is a throw away method for variant testing.
+1
View File
@@ -79,6 +79,7 @@ instances
inverted
inverted_clock
in_bom
in_pos_files
iref
italic
jumper_pin_groups
+40
View File
@@ -262,6 +262,46 @@ SCHEMATIC_SETTINGS::SCHEMATIC_SETTINGS( JSON_SETTINGS* aParent, const std::strin
m_refDesTracker->Deserialize( aData );
}, {} ) );
m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "variants",
[&]() -> nlohmann::json
{
nlohmann::json ret = nlohmann::json::array();
for( const auto& [name, description] : m_VariantDescriptions )
{
nlohmann::json entry;
entry["name"] = name;
if( !description.IsEmpty() )
entry["description"] = description;
ret.push_back( entry );
}
return ret;
},
[&]( const nlohmann::json& aJson )
{
m_VariantDescriptions.clear();
if( aJson.is_array() )
{
for( const auto& entry : aJson )
{
if( entry.contains( "name" ) )
{
wxString name = entry["name"].get<wxString>();
wxString desc;
if( entry.contains( "description" ) )
desc = entry["description"].get<wxString>();
m_VariantDescriptions[name] = desc;
}
}
}
}, nlohmann::json::array() ) );
registerMigration( 0, 1,
[&]() -> bool
{
+6
View File
@@ -122,6 +122,12 @@ public:
* This is used to avoid reusing designators in the same project.
*/
std::shared_ptr<REFDES_TRACKER> m_refDesTracker;
/**
* A map of variant names to their descriptions.
* This is stored in the project file and is the authoritative source for variant metadata.
*/
std::map<wxString, wxString> m_VariantDescriptions;
};
#endif
+37 -9
View File
@@ -74,6 +74,7 @@ public:
m_excludedFromSim( false ),
m_excludedFromBOM( false ),
m_excludedFromBoard( false ),
m_excludedFromPosFiles( false ),
m_DNP( false )
{ }
@@ -85,6 +86,7 @@ public:
m_excludedFromSim( base.m_excludedFromSim ),
m_excludedFromBOM( base.m_excludedFromBOM ),
m_excludedFromBoard( base.m_excludedFromBoard ),
m_excludedFromPosFiles( base.m_excludedFromPosFiles ),
m_DNP( base.m_DNP )
{ }
@@ -92,14 +94,15 @@ public:
{
SCH_ITEM::operator=( aItem );
m_pinNameOffset = aItem.m_pinNameOffset;
m_showPinNames = aItem.m_showPinNames;
m_showPinNumbers = aItem.m_showPinNumbers;
m_pinNameOffset = aItem.m_pinNameOffset;
m_showPinNames = aItem.m_showPinNames;
m_showPinNumbers = aItem.m_showPinNumbers;
m_excludedFromSim = aItem.m_excludedFromSim;
m_excludedFromBOM = aItem.m_excludedFromBOM;
m_excludedFromBoard = aItem.m_excludedFromBoard;
m_DNP = aItem.m_DNP;
m_excludedFromSim = aItem.m_excludedFromSim;
m_excludedFromBOM = aItem.m_excludedFromBOM;
m_excludedFromBoard = aItem.m_excludedFromBoard;
m_excludedFromPosFiles = aItem.m_excludedFromPosFiles;
m_DNP = aItem.m_DNP;
return *this;
};
@@ -204,8 +207,32 @@ public:
/**
* Set or clear exclude from board netlist flag.
*/
void SetExcludedFromBoard( bool aExcludeFromBoard ) override { m_excludedFromBoard = aExcludeFromBoard; }
bool GetExcludedFromBoard() const override { return m_excludedFromBoard; }
void SetExcludedFromBoard( bool aExclude, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override
{
m_excludedFromBoard = aExclude;
}
bool GetExcludedFromBoard( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override
{
return m_excludedFromBoard;
}
/**
* Set or clear exclude from position files flag.
*/
void SetExcludedFromPosFiles( bool aExclude, const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) override
{
m_excludedFromPosFiles = aExclude;
}
bool GetExcludedFromPosFiles( const SCH_SHEET_PATH* aInstance = nullptr,
const wxString& aVariantName = wxEmptyString ) const override
{
return m_excludedFromPosFiles;
}
/**
* Set or clear the 'Do Not Populate' flag.
@@ -247,6 +274,7 @@ protected:
bool m_excludedFromSim;
bool m_excludedFromBOM;
bool m_excludedFromBoard;
bool m_excludedFromPosFiles;
bool m_DNP; ///< True if symbol is set to 'Do Not Populate'.
int m_previewUnit = 1;
+112 -3
View File
@@ -300,7 +300,12 @@ void SCH_EDIT_FRAME::UpdateVariantSelectionCtrl( const wxArrayString& aVariantNa
if( selectionIndex != wxNOT_FOUND )
currentSelection = m_currentVariantCtrl->GetString( selectionIndex );
m_currentVariantCtrl->Set( aVariantNames );
// Add all variant names, a separator, and "Add New Variant..." at the end
wxArrayString contents = aVariantNames;
contents.Add( wxS( "---" ) );
contents.Add( _( "Add New Variant..." ) );
m_currentVariantCtrl->Set( contents );
selectionIndex = m_currentVariantCtrl->FindString( currentSelection );
@@ -317,11 +322,44 @@ void SCH_EDIT_FRAME::onVariantSelected( wxCommandEvent& aEvent )
return;
int selection = m_currentVariantCtrl->GetSelection();
int count = m_currentVariantCtrl->GetCount();
if( selection == wxNOT_FOUND || count == 0 )
return;
// "Add New Variant..." is always the last item, separator is second-to-last
if( selection == count - 1 )
{
// Restore previous selection before showing dialog
int previousSelection = m_currentVariantCtrl->FindString(
Schematic().GetCurrentVariant().IsEmpty() ? GetDefaultVariantName()
: Schematic().GetCurrentVariant() );
if( previousSelection != wxNOT_FOUND )
m_currentVariantCtrl->SetSelection( previousSelection );
ShowAddVariantDialog();
return;
}
// Separator line - ignore selection
if( selection == count - 2 )
{
int previousSelection = m_currentVariantCtrl->FindString(
Schematic().GetCurrentVariant().IsEmpty() ? GetDefaultVariantName()
: Schematic().GetCurrentVariant() );
if( previousSelection != wxNOT_FOUND )
m_currentVariantCtrl->SetSelection( previousSelection );
return;
}
wxString selectedString = m_currentVariantCtrl->GetString( selection );
wxString selectedVariant;
if( ( selection != wxNOT_FOUND ) && ( m_currentVariantCtrl->GetString( selection ) != GetDefaultVariantName() ) )
selectedVariant = m_currentVariantCtrl->GetString( selection );
if( selectedString != GetDefaultVariantName() )
selectedVariant = selectedString;
Schematic().SetCurrentVariant( selectedVariant );
UpdateProperties();
@@ -329,6 +367,77 @@ void SCH_EDIT_FRAME::onVariantSelected( wxCommandEvent& aEvent )
}
bool SCH_EDIT_FRAME::ShowAddVariantDialog()
{
// Create a dialog with both name and description fields
wxDialog dlg( this, wxID_ANY, _( "New Variant" ), wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER );
wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
// Name field
wxStaticText* nameLabel = new wxStaticText( &dlg, wxID_ANY, _( "Variant name:" ) );
mainSizer->Add( nameLabel, 0, wxALL | wxEXPAND, 5 );
wxTextCtrl* nameCtrl = new wxTextCtrl( &dlg, wxID_ANY );
mainSizer->Add( nameCtrl, 0, wxALL | wxEXPAND, 5 );
// Description field
wxStaticText* descLabel = new wxStaticText( &dlg, wxID_ANY, _( "Description (optional):" ) );
mainSizer->Add( descLabel, 0, wxALL | wxEXPAND, 5 );
wxTextCtrl* descCtrl = new wxTextCtrl( &dlg, wxID_ANY, wxEmptyString, wxDefaultPosition,
wxSize( 300, 60 ), wxTE_MULTILINE );
mainSizer->Add( descCtrl, 1, wxALL | wxEXPAND, 5 );
// Buttons
wxStdDialogButtonSizer* btnSizer = new wxStdDialogButtonSizer();
btnSizer->AddButton( new wxButton( &dlg, wxID_OK ) );
btnSizer->AddButton( new wxButton( &dlg, wxID_CANCEL ) );
btnSizer->Realize();
mainSizer->Add( btnSizer, 0, wxALL | wxALIGN_RIGHT, 5 );
dlg.SetSizer( mainSizer );
dlg.Fit();
dlg.Centre();
if( dlg.ShowModal() == wxID_CANCEL )
return false;
wxString variantName = nameCtrl->GetValue().Trim().Trim( false );
wxString variantDesc = descCtrl->GetValue().Trim().Trim( false );
// Empty strings and duplicate variant names are not allowed.
if( variantName.IsEmpty() )
{
GetInfoBar()->ShowMessageFor( _( "Variant name cannot be empty." ),
10000, wxICON_ERROR );
return false;
}
if( Schematic().GetVariantNames().contains( variantName ) )
{
GetInfoBar()->ShowMessageFor( wxString::Format( _( "Variant '%s' already exists." ),
variantName ),
10000, wxICON_ERROR );
return false;
}
// Add variant to the schematic
Schematic().AddVariant( variantName );
if( !variantDesc.IsEmpty() )
Schematic().SetVariantDescription( variantName, variantDesc );
// Update the variant selector and select the new variant
UpdateVariantSelectionCtrl( Schematic().GetVariantNamesForUI() );
SetCurrentVariant( variantName );
UpdateProperties();
HardRedraw();
return true;
}
void SCH_EDIT_FRAME::SetCurrentVariant( const wxString& aVariantName )
{
if( !m_currentVariantCtrl )
+22 -5
View File
@@ -98,10 +98,23 @@ int CLI::COMMAND::Perform( KIWAY& aKiway )
}
}
if( m_hasVariantArg )
if( m_hasVariantArg && m_argParser.is_used( ARG_VARIANT ) )
{
auto variantName = m_argParser.get<std::string>( ARG_VARIANT );
m_argVariantName = From_UTF8( variantName );
auto variantNames = m_argParser.get<std::vector<std::string>>( ARG_VARIANT );
for( const auto& name : variantNames )
m_argVariantNames.push_back( From_UTF8( name ) );
if( m_argVariantNames.size() > 1 && m_hasOutputArg && !m_argOutput.IsEmpty() )
{
if( !m_argOutput.Contains( wxS( "${VARIANT}" ) ) )
{
wxFprintf( stderr,
_( "When specifying multiple variants, the output path must contain "
"${VARIANT} to generate separate output files for each variant.\n" ) );
return EXIT_CODES::ERR_ARGS;
}
}
}
return doPerform( aKiway );
@@ -190,8 +203,12 @@ void CLI::COMMAND::addVariantsArg()
m_hasVariantArg = true;
m_argParser.add_argument( ARG_VARIANT )
.default_value( std::string() )
.default_value( std::vector<std::string>() )
.append()
.help( UTF8STDSTR(
_( "The variant name to output.\n"
_( "The variant name(s) to output, can be used multiple times to specify "
"multiple variants.\n"
"When specifying multiple variants, use ${VARIANT} in the output path to "
"generate separate files for each variant.\n"
"When no --variant argument is provided the default variant is output." ) ) );
}
+3 -3
View File
@@ -156,11 +156,11 @@ protected:
bool m_hasVariantArg;
/**
* The name of the variant to output.
* The list of variant names to output.
*
* An empty string indicates the default variant.
* An empty vector indicates the default variant.
*/
wxString m_argVariantName;
std::vector<wxString> m_argVariantNames;
};
}
+1 -1
View File
@@ -139,6 +139,7 @@ int CLI::SCH_EXPORT_BOM_COMMAND::doPerform( KIWAY& aKiway )
// Basic options
bomJob->m_filename = m_argInput;
bomJob->SetConfiguredOutputPath( m_argOutput );
bomJob->m_variantNames = m_argVariantNames;
bomJob->m_bomPresetName = From_UTF8( m_argParser.get<std::string>( ARG_PRESET ).c_str() );
bomJob->m_bomFmtPresetName = From_UTF8( m_argParser.get<std::string>( ARG_FMT_PRESET ).c_str() );
@@ -166,7 +167,6 @@ int CLI::SCH_EXPORT_BOM_COMMAND::doPerform( KIWAY& aKiway )
bomJob->m_sortAsc = m_argParser.get<bool>( ARG_SORT_ASC );
bomJob->m_filterString = From_UTF8( m_argParser.get<std::string>( ARG_FILTER ).c_str() );
bomJob->m_excludeDNP = m_argParser.get<bool>( ARG_EXCLUDE_DNP );
bomJob->m_variant = From_UTF8( m_argParser.get<std::string>( ARG_VARIANT ).c_str() );
if( m_argParser.get<bool>( DEPRECATED_ARG_INCLUDE_EXCLUDED_FROM_BOM ) )
wxFprintf( stdout, DEPRECATED_ARG_INCLUDE_EXCLUDED_FROM_BOM_WARNING );
+2
View File
@@ -34,6 +34,7 @@ CLI::SCH_EXPORT_NETLIST_COMMAND::SCH_EXPORT_NETLIST_COMMAND() : COMMAND( "netlis
{
m_argParser.add_description( UTF8STDSTR( _( "Export a netlist" ) ) );
addCommonArgs( true, true, false, false );
addVariantsArg();
m_argParser.add_argument( ARG_FORMAT )
.default_value( std::string( "kicadsexpr" ) )
@@ -50,6 +51,7 @@ int CLI::SCH_EXPORT_NETLIST_COMMAND::doPerform( KIWAY& aKiway )
netJob->m_filename = m_argInput;
netJob->SetConfiguredOutputPath( m_argOutput );
netJob->m_variantNames = m_argVariantNames;
if( !wxFile::Exists( netJob->m_filename ) )
{
+1 -1
View File
@@ -182,6 +182,7 @@ int CLI::SCH_EXPORT_PLOT_COMMAND::doPerform( KIWAY& aKiway )
plotJob->m_drawingSheet = m_argDrawingSheet;
plotJob->SetVarOverrides( m_argDefineVars );
plotJob->m_variantNames = m_argVariantNames;
// PDF local options
if( m_plotFormat == SCH_PLOT_FORMAT::PDF )
@@ -191,7 +192,6 @@ int CLI::SCH_EXPORT_PLOT_COMMAND::doPerform( KIWAY& aKiway )
plotJob->m_PDFMetadata = !m_argParser.get<bool>( ARG_EXCLUDE_PDF_METADATA );
}
plotJob->m_variant = From_UTF8( m_argParser.get<std::string>( ARG_VARIANT ).c_str() );
int exitCode = aKiway.ProcessJob( KIWAY::FACE_SCH, plotJob.get() );
return exitCode;
@@ -763,6 +763,46 @@ bool BOARD_NETLIST_UPDATER::updateFootprintParameters( FOOTPRINT* aPcbFootprint,
m_reporter->Report( msg, RPT_SEVERITY_ACTION );
}
if( ( aNetlistComponent->GetProperties().count( wxT( "exclude_from_pos_files" ) ) > 0 )
!= ( ( aPcbFootprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES ) > 0 ) )
{
if( m_isDryRun )
{
if( aNetlistComponent->GetProperties().count( wxT( "exclude_from_pos_files" ) ) )
{
msg.Printf( _( "Add %s 'exclude from position files' fabrication attribute." ),
aPcbFootprint->GetReference() );
}
else
{
msg.Printf( _( "Remove %s 'exclude from position files' fabrication attribute." ),
aPcbFootprint->GetReference() );
}
}
else
{
int attributes = aPcbFootprint->GetAttributes();
if( aNetlistComponent->GetProperties().count( wxT( "exclude_from_pos_files" ) ) )
{
attributes |= FP_EXCLUDE_FROM_POS_FILES;
msg.Printf( _( "Added %s 'exclude from position files' fabrication attribute." ),
aPcbFootprint->GetReference() );
}
else
{
attributes &= ~FP_EXCLUDE_FROM_POS_FILES;
msg.Printf( _( "Removed %s 'exclude from position files' fabrication attribute." ),
aPcbFootprint->GetReference() );
}
changed = true;
aPcbFootprint->SetAttributes( attributes );
}
m_reporter->Report( msg, RPT_SEVERITY_ACTION );
}
if( aNetlistComponent->GetDuplicatePadNumbersAreJumpers()
!= aPcbFootprint->GetDuplicatePadNumbersAreJumpers() )
{
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4
View File
@@ -0,0 +1,4 @@
(sym_lib_table
(version 7)
(lib (name "pic_programmer") (type "KiCad") (uri "${KIPRJMOD}/pic_programmer.kicad_sym") (options "") (descr ""))
)
+744
View File
@@ -0,0 +1,744 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_fields": false,
"apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.381,
"copper_line_width": 0.381,
"copper_text_italic": false,
"copper_text_size_h": 1.524,
"copper_text_size_v": 2.032,
"copper_text_thickness": 0.3048,
"copper_text_upright": true,
"courtyard_line_width": 0.05,
"dimension_precision": 1,
"dimension_units": 0,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": false,
"text_position": 0,
"units_format": 1
},
"fab_line_width": 0.1,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.1,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": true,
"pads": {
"drill": 0.75,
"height": 1.3,
"width": 1.3
},
"silk_line_width": 0.381,
"silk_text_italic": false,
"silk_text_size_h": 1.524,
"silk_text_size_v": 1.524,
"silk_text_thickness": 0.3048,
"silk_text_upright": true,
"zones": {
"45_degree_only": false,
"min_clearance": 0.508
}
},
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [],
"meta": {
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "error",
"copper_sliver": "warning",
"courtyards_overlap": "error",
"creepage": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint": "error",
"footprint_filters_mismatch": "ignore",
"footprint_symbol_mismatch": "warning",
"footprint_type_mismatch": "warning",
"hole_clearance": "error",
"hole_near_hole": "error",
"hole_to_hole": "error",
"holes_co_located": "warning",
"invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"lib_footprint_issues": "warning",
"lib_footprint_mismatch": "warning",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"mirrored_text_on_front_layer": "warning",
"missing_courtyard": "warning",
"missing_footprint": "warning",
"net_conflict": "warning",
"nonmirrored_text_on_back_layer": "warning",
"npth_inside_courtyard": "warning",
"overlapping_pads": "warning",
"padstack": "error",
"pth_inside_courtyard": "warning",
"shorting_items": "error",
"silk_edge_clearance": "ignore",
"silk_over_copper": "error",
"silk_overlap": "error",
"skew_out_of_range": "error",
"solder_mask_bridge": "error",
"starved_thermal": "error",
"text_height": "warning",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_angle": "error",
"track_dangling": "warning",
"track_segment_length": "error",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rule_severitieslegacy_courtyards_overlap": true,
"rule_severitieslegacy_no_courtyard_defined": false,
"rules": {
"allow_blind_buried_vias": false,
"allow_microvias": false,
"max_error": 0.01,
"min_clearance": 0.0,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.01,
"min_groove_width": 0.0,
"min_hole_clearance": 0.0,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.508,
"min_microvia_drill": 0.127,
"min_resolved_spokes": 1,
"min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.12,
"min_through_hole_diameter": 0.5,
"min_track_width": 0.25,
"min_via_annular_width": 0.05,
"min_via_diameter": 0.9,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpthpad": true,
"td_onroundshapesonly": false,
"td_onsmdpad": true,
"td_ontrackend": false,
"td_onvia": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [
0.5,
0.4,
0.5
],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 100,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.1,
"single_sided": false,
"spacing": 0.6
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 100,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.1,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 100,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.1,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [
{
"diameter": 1.6,
"drill": 0.6
}
],
"zones_allow_external_fillets": false,
"zones_use_no_outline": true
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_pairs": [],
"layer_presets": [],
"viewports": []
},
"boards": [],
"component_class_settings": {
"assignments": [],
"meta": {
"version": 0
},
"sheet_component_classes": {
"enabled": false
}
},
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_label_syntax": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"conflicting_netclasses": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"footprint_filter": "ignore",
"footprint_link_issues": "warning",
"four_way_junction": "ignore",
"global_label_dangling": "warning",
"ground_pin_not_ground": "warning",
"hier_label_mismatch": "error",
"isolated_pin_label": "warning",
"label_dangling": "error",
"label_multiple_wires": "warning",
"lib_symbol_issues": "warning",
"lib_symbol_mismatch": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "error",
"no_connect_dangling": "error",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "error",
"power_pin_not_driven": "error",
"same_local_global_label": "warning",
"similar_label_and_power": "warning",
"similar_labels": "warning",
"similar_power": "warning",
"simulation_model_issue": "ignore",
"single_global_label": "warning",
"stacked_pin_name": "warning",
"unannotated": "error",
"unconnected_wire_endpoint": "warning",
"undefined_netclass": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"legacy": {
"common": {
"NetDir": ""
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "variants.kicad_pro",
"version": 3
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.25,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.25,
"line_style": 0,
"microvia_diameter": 0.508,
"microvia_drill": 0.127,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": 2147483647,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.5,
"tuning_profile": "",
"via_diameter": 1.6,
"via_drill": 0.6,
"wire_width": 6
},
{
"bus_width": 12,
"clearance": 0.28,
"diff_pair_gap": 0.28,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.28,
"line_style": 0,
"microvia_diameter": 0.508,
"microvia_drill": 0.127,
"name": "POWER",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": 0,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.8,
"tuning_profile": "",
"via_diameter": 1.6,
"via_drill": 0.6,
"wire_width": 6
}
],
"meta": {
"version": 5
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": [
{
"netclass": "POWER",
"pattern": "GND"
},
{
"netclass": "POWER",
"pattern": "VCC"
}
]
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "variants.net",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "pic_programmer.step",
"svg": "",
"vmrl": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"annotation": {
"method": 0,
"sort_order": 0
},
"bom_export_filename": "${PROJECTNAME}.csv",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "Quantity",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": false
},
{
"group_by": false,
"label": "#",
"name": "${ITEM_NUMBER}",
"show": false
},
{
"group_by": false,
"label": "Champ4",
"name": "Champ4",
"show": false
},
{
"group_by": false,
"label": "Description",
"name": "Description",
"show": false
}
],
"filter_string": "",
"group_symbols": true,
"include_excluded_from_bom": false,
"name": "",
"sort_asc": true,
"sort_field": "Reference"
},
"bus_aliases": {},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_bus_thickness": 12.0,
"default_junction_size": 36.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"default_wire_thickness": 6.0,
"field_names": [],
"hop_over_size_choice": 0,
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.2,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.2
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"ngspice": {
"fix_include_paths": true,
"fix_passive_vals": false,
"meta": {
"version": 0
},
"model_mode": 0,
"workbook_filename": ""
},
"page_layout_descr_file": "",
"plot_directory": "",
"reuse_designators": true,
"space_save_all_events": true,
"spice_adjust_passive_values": false,
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0,
"top_level_sheets": [
{
"filename": "variants.kicad_sch",
"name": "pic_programmer",
"uuid": "2e45d1d2-c73f-46e5-98d6-3a9dc360bff5"
}
],
"used_designators": "",
"variants": [
{
"description": "testing variant 1 description",
"name": "Variant 1"
},
{
"description": "Test of variant 2 desc",
"name": "Variant2"
}
]
},
"sheets": [
[
"2e45d1d2-c73f-46e5-98d6-3a9dc360bff5",
"pic_programmer"
],
[
"00000000-0000-0000-0000-00004804a5e2",
"pic_sockets"
]
],
"text_variables": {},
"tuning_profiles": {
"meta": {
"version": 0
},
"tuning_profiles_impedance_geometric": []
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,57 @@
"Refs","Value"
"C1","100µF"
"C2","220uF"
"C3","22uF/25V"
"C4","0"
"C5","10nF"
"C6","100nF"
"C7","100nF"
"C9","220nF"
"D1","1N4004"
"D2","BAT43"
"D3","BAT43"
"D4","BAT43"
"D5","BAT43"
"D6","BAT43"
"D7","BAT43"
"D8","RED-LED"
"D9","GREEN-LED"
"D10","SCHOTTKY"
"D11","BAT43"
"D12","YELLOW-LED"
"J1","DB9-FEMAL"
"L1","22uH"
"P1","CONN_2"
"P2","SUPP28"
"P3","SUPP40"
"Q1","BC237"
"Q2","BC307"
"Q3","BC307"
"R1","10K"
"R2","10K"
"R3","10K"
"R4","10K"
"R5","10K"
"R6","10K"
"R7","10K"
"R8","1K"
"R9","2.2K"
"R10","5,1K"
"R11","22K"
"R12","2k"
"R13","4k"
"R14","470"
"R15","6.2K"
"R16","62K"
"R17","22K"
"R18","220"
"R19","2.2K"
"R20","2.2K"
"R21","470"
"RV1","1K"
"U1","24Cxx"
"U2","74HC125"
"U3","7805"
"U4","LT1373"
"U5","PIC_18_PINS"
"U6","PIC_8_PINS"
1 Refs Value
2 C1 100µF
3 C2 220uF
4 C3 22uF/25V
5 C4 0
6 C5 10nF
7 C6 100nF
8 C7 100nF
9 C9 220nF
10 D1 1N4004
11 D2 BAT43
12 D3 BAT43
13 D4 BAT43
14 D5 BAT43
15 D6 BAT43
16 D7 BAT43
17 D8 RED-LED
18 D9 GREEN-LED
19 D10 SCHOTTKY
20 D11 BAT43
21 D12 YELLOW-LED
22 J1 DB9-FEMAL
23 L1 22uH
24 P1 CONN_2
25 P2 SUPP28
26 P3 SUPP40
27 Q1 BC237
28 Q2 BC307
29 Q3 BC307
30 R1 10K
31 R2 10K
32 R3 10K
33 R4 10K
34 R5 10K
35 R6 10K
36 R7 10K
37 R8 1K
38 R9 2.2K
39 R10 5,1K
40 R11 22K
41 R12 2k
42 R13 4k
43 R14 470
44 R15 6.2K
45 R16 62K
46 R17 22K
47 R18 220
48 R19 2.2K
49 R20 2.2K
50 R21 470
51 RV1 1K
52 U1 24Cxx
53 U2 74HC125
54 U3 7805
55 U4 LT1373
56 U5 PIC_18_PINS
57 U6 PIC_8_PINS
+56
View File
@@ -0,0 +1,56 @@
"Refs","Value"
"C1","100µF"
"C2","220uF"
"C3","22uF/25V"
"C4","0"
"C5","10nF"
"C6","100nF"
"C7","100nF"
"C9","220nF"
"D1","1N4004"
"D2","BAT43"
"D3","BAT43"
"D4","BAT43"
"D5","BAT43"
"D6","BAT43"
"D7","BAT43"
"D8","RED-LED"
"D9","GREEN-LED"
"D10","SCHOTTKY"
"D11","BAT43"
"D12","YELLOW-LED"
"L1","22uH"
"P1","CONN_2"
"P2","SUPP28"
"P3","SUPP40"
"Q1","BC237"
"Q2","BC307"
"Q3","BC307"
"R1","10K"
"R2","10K"
"R3","10K"
"R4","10K"
"R5","10K"
"R6","10K"
"R7","10K"
"R8","1K"
"R9","2.2K"
"R10","5,1K"
"R11","22K"
"R12","2k"
"R13","4k"
"R14","470"
"R15","6.2K"
"R16","62K"
"R17","22K"
"R18","220"
"R19","2.2K"
"R20","2.2K"
"R21","470"
"RV1","1K"
"U1","24Cxx"
"U2","74HC125"
"U3","7805"
"U4","LT1373"
"U5","PIC_18_PINS"
"U6","PIC_8_PINS"
1 Refs Value
2 C1 100µF
3 C2 220uF
4 C3 22uF/25V
5 C4 0
6 C5 10nF
7 C6 100nF
8 C7 100nF
9 C9 220nF
10 D1 1N4004
11 D2 BAT43
12 D3 BAT43
13 D4 BAT43
14 D5 BAT43
15 D6 BAT43
16 D7 BAT43
17 D8 RED-LED
18 D9 GREEN-LED
19 D10 SCHOTTKY
20 D11 BAT43
21 D12 YELLOW-LED
22 L1 22uH
23 P1 CONN_2
24 P2 SUPP28
25 P3 SUPP40
26 Q1 BC237
27 Q2 BC307
28 Q3 BC307
29 R1 10K
30 R2 10K
31 R3 10K
32 R4 10K
33 R5 10K
34 R6 10K
35 R7 10K
36 R8 1K
37 R9 2.2K
38 R10 5,1K
39 R11 22K
40 R12 2k
41 R13 4k
42 R14 470
43 R15 6.2K
44 R16 62K
45 R17 22K
46 R18 220
47 R19 2.2K
48 R20 2.2K
49 R21 470
50 RV1 1K
51 U1 24Cxx
52 U2 74HC125
53 U3 7805
54 U4 LT1373
55 U5 PIC_18_PINS
56 U6 PIC_8_PINS
+56
View File
@@ -0,0 +1,56 @@
"Refs","Value"
"C1","100µF"
"C2","220uF"
"C3","22uF/25V"
"C4","0"
"C5","10nF"
"C6","100nF"
"C7","100nF"
"C9","220nF"
"D1","1N4004"
"D2","BAT43"
"D3","BAT43"
"D4","BAT43"
"D5","BAT43"
"D6","BAT43"
"D7","BAT43"
"D8","RED-LED"
"D9","GREEN-LED"
"D10","SCHOTTKY"
"D11","BAT43"
"D12","YELLOW-LED"
"J1","DB9-FEMAL"
"L1","22uH"
"P1","CONN_2"
"P2","SUPP28"
"P3","SUPP40"
"Q1","BC237"
"Q2","BC307"
"Q3","BC307"
"R1","10K"
"R2","10K"
"R3","10K"
"R4","10K"
"R5","10K"
"R6","10K"
"R7","10K"
"R8","1K"
"R9","2.2K"
"R10","5,1K"
"R11","22K"
"R12","2k"
"R13","4k"
"R15","6.2K"
"R16","62K"
"R17","22K"
"R18","220"
"R19","2.2K"
"R20","2.2K"
"R21","470"
"RV1","1K"
"U1","24Cxx"
"U2","74HC125"
"U3","7805"
"U4","LT1373"
"U5","PIC_18_PINS"
"U6","PIC_8_PINS"
1 Refs Value
2 C1 100µF
3 C2 220uF
4 C3 22uF/25V
5 C4 0
6 C5 10nF
7 C6 100nF
8 C7 100nF
9 C9 220nF
10 D1 1N4004
11 D2 BAT43
12 D3 BAT43
13 D4 BAT43
14 D5 BAT43
15 D6 BAT43
16 D7 BAT43
17 D8 RED-LED
18 D9 GREEN-LED
19 D10 SCHOTTKY
20 D11 BAT43
21 D12 YELLOW-LED
22 J1 DB9-FEMAL
23 L1 22uH
24 P1 CONN_2
25 P2 SUPP28
26 P3 SUPP40
27 Q1 BC237
28 Q2 BC307
29 Q3 BC307
30 R1 10K
31 R2 10K
32 R3 10K
33 R4 10K
34 R5 10K
35 R6 10K
36 R7 10K
37 R8 1K
38 R9 2.2K
39 R10 5,1K
40 R11 22K
41 R12 2k
42 R13 4k
43 R15 6.2K
44 R16 62K
45 R17 22K
46 R18 220
47 R19 2.2K
48 R20 2.2K
49 R21 470
50 RV1 1K
51 U1 24Cxx
52 U2 74HC125
53 U3 7805
54 U4 LT1373
55 U5 PIC_18_PINS
56 U6 PIC_8_PINS
+3 -1
View File
@@ -1,7 +1,8 @@
{
"ERC": {
"crossprobe": true,
"scroll_on_crossprobe": true
"scroll_on_crossprobe": true,
"show_all_errors": false
},
"annotation": {
"automatic": true,
@@ -174,6 +175,7 @@
"scope": 0,
"selection_mode": 0,
"sidebar_collapsed": false,
"variant_sash_pos": 500,
"width": 0
},
"find_replace": {
+1
View File
@@ -98,6 +98,7 @@
"units": 1
},
"template": {
"last_used": "",
"window": {
"pos": {
"x": -1,
+4
View File
@@ -0,0 +1,4 @@
(fp_lib_table
(version 7)
(lib (name "footprints") (type "KiCad") (uri "${KIPRJMOD}/libs/pic_programmer_fp.pretty") (options "") (descr "project specific"))
)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4
View File
@@ -0,0 +1,4 @@
(sym_lib_table
(version 7)
(lib (name "pic_programmer") (type "KiCad") (uri "${KIPRJMOD}/pic_programmer.kicad_sym") (options "") (descr ""))
)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,744 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_fields": false,
"apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.381,
"copper_line_width": 0.381,
"copper_text_italic": false,
"copper_text_size_h": 1.524,
"copper_text_size_v": 2.032,
"copper_text_thickness": 0.3048,
"copper_text_upright": true,
"courtyard_line_width": 0.05,
"dimension_precision": 1,
"dimension_units": 0,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": false,
"text_position": 0,
"units_format": 1
},
"fab_line_width": 0.1,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.1,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": true,
"pads": {
"drill": 0.75,
"height": 1.3,
"width": 1.3
},
"silk_line_width": 0.381,
"silk_text_italic": false,
"silk_text_size_h": 1.524,
"silk_text_size_v": 1.524,
"silk_text_thickness": 0.3048,
"silk_text_upright": true,
"zones": {
"45_degree_only": false,
"min_clearance": 0.508
}
},
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [],
"meta": {
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "error",
"copper_sliver": "warning",
"courtyards_overlap": "error",
"creepage": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint": "error",
"footprint_filters_mismatch": "ignore",
"footprint_symbol_mismatch": "warning",
"footprint_type_mismatch": "warning",
"hole_clearance": "error",
"hole_near_hole": "error",
"hole_to_hole": "error",
"holes_co_located": "warning",
"invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"lib_footprint_issues": "warning",
"lib_footprint_mismatch": "warning",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"mirrored_text_on_front_layer": "warning",
"missing_courtyard": "warning",
"missing_footprint": "warning",
"net_conflict": "warning",
"nonmirrored_text_on_back_layer": "warning",
"npth_inside_courtyard": "warning",
"overlapping_pads": "warning",
"padstack": "error",
"pth_inside_courtyard": "warning",
"shorting_items": "error",
"silk_edge_clearance": "ignore",
"silk_over_copper": "error",
"silk_overlap": "error",
"skew_out_of_range": "error",
"solder_mask_bridge": "error",
"starved_thermal": "error",
"text_height": "warning",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_angle": "error",
"track_dangling": "warning",
"track_segment_length": "error",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rule_severitieslegacy_courtyards_overlap": true,
"rule_severitieslegacy_no_courtyard_defined": false,
"rules": {
"allow_blind_buried_vias": false,
"allow_microvias": false,
"max_error": 0.01,
"min_clearance": 0.0,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.01,
"min_groove_width": 0.0,
"min_hole_clearance": 0.0,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.508,
"min_microvia_drill": 0.127,
"min_resolved_spokes": 1,
"min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.12,
"min_through_hole_diameter": 0.5,
"min_track_width": 0.25,
"min_via_annular_width": 0.05,
"min_via_diameter": 0.9,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpthpad": true,
"td_onroundshapesonly": false,
"td_onsmdpad": true,
"td_ontrackend": false,
"td_onvia": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [
0.5,
0.4,
0.5
],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 100,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.1,
"single_sided": false,
"spacing": 0.6
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 100,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.1,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 100,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.1,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [
{
"diameter": 1.6,
"drill": 0.6
}
],
"zones_allow_external_fillets": false,
"zones_use_no_outline": true
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_pairs": [],
"layer_presets": [],
"viewports": []
},
"boards": [],
"component_class_settings": {
"assignments": [],
"meta": {
"version": 0
},
"sheet_component_classes": {
"enabled": false
}
},
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_label_syntax": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"conflicting_netclasses": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"footprint_filter": "ignore",
"footprint_link_issues": "warning",
"four_way_junction": "ignore",
"global_label_dangling": "warning",
"ground_pin_not_ground": "warning",
"hier_label_mismatch": "error",
"isolated_pin_label": "warning",
"label_dangling": "error",
"label_multiple_wires": "warning",
"lib_symbol_issues": "warning",
"lib_symbol_mismatch": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "error",
"no_connect_dangling": "error",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "error",
"power_pin_not_driven": "error",
"same_local_global_label": "warning",
"similar_label_and_power": "warning",
"similar_labels": "warning",
"similar_power": "warning",
"simulation_model_issue": "ignore",
"single_global_label": "warning",
"stacked_pin_name": "warning",
"unannotated": "error",
"unconnected_wire_endpoint": "warning",
"undefined_netclass": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"legacy": {
"common": {
"NetDir": ""
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "variants.kicad_pro",
"version": 3
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.25,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.25,
"line_style": 0,
"microvia_diameter": 0.508,
"microvia_drill": 0.127,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": 2147483647,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.5,
"tuning_profile": "",
"via_diameter": 1.6,
"via_drill": 0.6,
"wire_width": 6
},
{
"bus_width": 12,
"clearance": 0.28,
"diff_pair_gap": 0.28,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.28,
"line_style": 0,
"microvia_diameter": 0.508,
"microvia_drill": 0.127,
"name": "POWER",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": 0,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.8,
"tuning_profile": "",
"via_diameter": 1.6,
"via_drill": 0.6,
"wire_width": 6
}
],
"meta": {
"version": 5
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": [
{
"netclass": "POWER",
"pattern": "GND"
},
{
"netclass": "POWER",
"pattern": "VCC"
}
]
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "variants.net",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "pic_programmer.step",
"svg": "",
"vmrl": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"annotation": {
"method": 0,
"sort_order": 0
},
"bom_export_filename": "${PROJECTNAME}.csv",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "Quantity",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": false
},
{
"group_by": false,
"label": "#",
"name": "${ITEM_NUMBER}",
"show": false
},
{
"group_by": false,
"label": "Champ4",
"name": "Champ4",
"show": false
},
{
"group_by": false,
"label": "Description",
"name": "Description",
"show": false
}
],
"filter_string": "",
"group_symbols": true,
"include_excluded_from_bom": false,
"name": "",
"sort_asc": true,
"sort_field": "Reference"
},
"bus_aliases": {},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_bus_thickness": 12.0,
"default_junction_size": 36.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"default_wire_thickness": 6.0,
"field_names": [],
"hop_over_size_choice": 0,
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.2,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.2
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"ngspice": {
"fix_include_paths": true,
"fix_passive_vals": false,
"meta": {
"version": 0
},
"model_mode": 0,
"workbook_filename": ""
},
"page_layout_descr_file": "",
"plot_directory": "",
"reuse_designators": true,
"space_save_all_events": true,
"spice_adjust_passive_values": false,
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0,
"top_level_sheets": [
{
"filename": "variants.kicad_sch",
"name": "pic_programmer",
"uuid": "2e45d1d2-c73f-46e5-98d6-3a9dc360bff5"
}
],
"used_designators": "",
"variants": [
{
"description": "testing variant 1 description",
"name": "Variant 1"
},
{
"description": "Test of variant 2 desc",
"name": "Variant2"
}
]
},
"sheets": [
[
"2e45d1d2-c73f-46e5-98d6-3a9dc360bff5",
"pic_programmer"
],
[
"00000000-0000-0000-0000-00004804a5e2",
"pic_sockets"
]
],
"text_variables": {},
"tuning_profiles": {
"meta": {
"version": 0
},
"tuning_profiles_impedance_geometric": []
}
}
File diff suppressed because it is too large Load Diff
+90
View File
@@ -140,6 +140,96 @@ def test_sch_export_pdf( kitest,
kitest.add_attachment( str( output_filepath ) )
@pytest.mark.parametrize("test_file,output_fn,compare_fn,line_skip_count,cli_args",
[("cli/variants/variants.kicad_sch", "variants_default.bom.csv", "cli/variants/variants_default.bom.csv", 0,
["--exclude-dnp", "--fields", "Reference,Value", "--labels", "Refs,Value"]),
("cli/variants/variants.kicad_sch", "variants_v1.bom.csv", "cli/variants/variants_v1.bom.csv", 0,
["--variant", "Variant 1", "--exclude-dnp", "--fields", "Reference,Value", "--labels", "Refs,Value"]),
("cli/variants/variants.kicad_sch", "variants_v2.bom.csv", "cli/variants/variants_v2.bom.csv", 0,
["--variant", "Variant2", "--exclude-dnp", "--fields", "Reference,Value", "--labels", "Refs,Value"]),
])
def test_sch_export_bom_variants( kitest,
test_file: str,
output_fn: str,
compare_fn: str,
line_skip_count: int,
cli_args: List[str] ):
"""Test BOM export with variant support and DNP exclusion"""
input_file = kitest.get_data_file_path( test_file )
compare_filepath = kitest.get_data_file_path( compare_fn )
output_filepath = kitest.get_output_path( "cli/" ).joinpath( output_fn )
command = [utils.kicad_cli(), "sch", "export", "bom"]
command.extend( cli_args )
command.append( "-o" )
command.append( str( output_filepath ) )
command.append( input_file )
stdout, stderr, exitcode = utils.run_and_capture( command )
assert exitcode == 0
assert stderr == ''
assert utils.textdiff_files( compare_filepath, str( output_filepath ), line_skip_count )
kitest.add_attachment( str( output_filepath ) )
def test_sch_export_bom_multi_variant_requires_placeholder( kitest ):
"""Test that multiple variants require ${VARIANT} in output path"""
input_file = kitest.get_data_file_path( "cli/variants/variants.kicad_sch" )
output_filepath = kitest.get_output_path( "cli/" ).joinpath( "multi_variant_fail.csv" )
command = [utils.kicad_cli(), "sch", "export", "bom"]
command.extend( ["--variant", "Variant 1", "--variant", "Variant2"] )
command.extend( ["--exclude-dnp", "--fields", "Reference,Value", "--labels", "Refs,Value"] )
command.append( "-o" )
command.append( str( output_filepath ) )
command.append( input_file )
stdout, stderr, exitcode = utils.run_and_capture( command )
assert exitcode == 1
assert "VARIANT" in stderr
def test_sch_export_bom_multi_variant_with_placeholder( kitest ):
"""Test BOM export with multiple variants using ${VARIANT} placeholder"""
input_file = kitest.get_data_file_path( "cli/variants/variants.kicad_sch" )
output_dir = kitest.get_output_path( "cli/" )
output_pattern = str( output_dir.joinpath( "bom_${VARIANT}.csv" ) )
command = [utils.kicad_cli(), "sch", "export", "bom"]
command.extend( ["--variant", "Variant 1", "--variant", "Variant2"] )
command.extend( ["--exclude-dnp", "--fields", "Reference,Value", "--labels", "Refs,Value"] )
command.append( "-o" )
command.append( output_pattern )
command.append( input_file )
stdout, stderr, exitcode = utils.run_and_capture( command )
assert exitcode == 0
assert stderr == ''
v1_path = output_dir.joinpath( "bom_Variant 1.csv" )
v2_path = output_dir.joinpath( "bom_Variant2.csv" )
assert v1_path.exists(), f"Expected output file {v1_path} not found"
assert v2_path.exists(), f"Expected output file {v2_path} not found"
v1_compare = kitest.get_data_file_path( "cli/variants/variants_v1.bom.csv" )
v2_compare = kitest.get_data_file_path( "cli/variants/variants_v2.bom.csv" )
assert utils.textdiff_files( v1_compare, str( v1_path ), 0 )
assert utils.textdiff_files( v2_compare, str( v2_path ), 0 )
kitest.add_attachment( str( v1_path ) )
kitest.add_attachment( str( v2_path ) )
@pytest.mark.parametrize("test_file,output_fn,line_skip_count,cli_args",
[("cli/basic_test/basic_test.kicad_sch", "basic_test.pythonbom", 6, [])
])
+8
View File
@@ -486,6 +486,14 @@ BOOST_AUTO_TEST_CASE( Compare )
BOOST_CHECK( m_part_no_data.Compare( testPart, SCH_ITEM::COMPARE_FLAGS::EQUALITY ) < 0 );
m_part_no_data.SetExcludedFromBoard( false );
// Include in position files support tests.
testPart.SetExcludedFromPosFiles( true );
BOOST_CHECK( m_part_no_data.Compare( testPart, SCH_ITEM::COMPARE_FLAGS::EQUALITY ) > 0 );
testPart.SetExcludedFromPosFiles( false );
m_part_no_data.SetExcludedFromPosFiles( true );
BOOST_CHECK( m_part_no_data.Compare( testPart, SCH_ITEM::COMPARE_FLAGS::EQUALITY ) < 0 );
m_part_no_data.SetExcludedFromPosFiles( false );
// Show pin names flag comparison tests.
m_part_no_data.SetShowPinNames( false );
BOOST_CHECK( m_part_no_data.Compare( testPart, SCH_ITEM::COMPARE_FLAGS::EQUALITY ) < 0 );
+563
View File
@@ -132,6 +132,16 @@ BOOST_AUTO_TEST_CASE( SchSymbolVariantTest )
symbol->SetExcludedFromSim( true, &m_schematic->Hierarchy()[0], variantName );
BOOST_CHECK( symbol->GetExcludedFromSim( &m_schematic->Hierarchy()[0], variantName ) );
// Test exclude from board property variant.
BOOST_CHECK( !symbol->GetExcludedFromBoard() );
symbol->SetExcludedFromBoard( true, &m_schematic->Hierarchy()[0], variantName );
BOOST_CHECK( symbol->GetExcludedFromBoard( &m_schematic->Hierarchy()[0], variantName ) );
// Test exclude from position files property variant.
BOOST_CHECK( !symbol->GetExcludedFromPosFiles() );
symbol->SetExcludedFromPosFiles( true, &m_schematic->Hierarchy()[0], variantName );
BOOST_CHECK( symbol->GetExcludedFromPosFiles( &m_schematic->Hierarchy()[0], variantName ) );
// Test a value field variant change.
BOOST_CHECK( symbol->GetField( FIELD_T::VALUE )->GetShownText( &m_schematic->Hierarchy()[0],
false, 0 ) == wxS( "1K" ) );
@@ -185,4 +195,557 @@ BOOST_AUTO_TEST_CASE( FieldAdditionByName )
}
/**
* Test variant description methods.
*/
BOOST_AUTO_TEST_CASE( VariantDescription )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "variant_test" ) );
fn.SetName( wxS( "variant_test" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
BOOST_REQUIRE( m_schematic );
wxString variantName = wxS( "TestVariant" );
wxString description = wxS( "This is a test variant description" );
// Add a variant
m_schematic->AddVariant( variantName );
BOOST_CHECK( m_schematic->GetVariantNames().contains( variantName ) );
// Set description
m_schematic->SetVariantDescription( variantName, description );
BOOST_CHECK_EQUAL( m_schematic->GetVariantDescription( variantName ), description );
// Empty description for non-existent variant
BOOST_CHECK( m_schematic->GetVariantDescription( wxS( "NonExistent" ) ).IsEmpty() );
// Clear description
m_schematic->SetVariantDescription( variantName, wxEmptyString );
BOOST_CHECK( m_schematic->GetVariantDescription( variantName ).IsEmpty() );
}
/**
* Test ${VARIANT} text variable resolution.
*/
BOOST_AUTO_TEST_CASE( TextVariableVARIANT )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "variant_test" ) );
fn.SetName( wxS( "variant_test" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
BOOST_REQUIRE( m_schematic );
wxString variantName = wxS( "MilitaryGrade" );
// Add a variant and set it as current
m_schematic->AddVariant( variantName );
m_schematic->SetCurrentVariant( variantName );
// Test that ${VARIANT} resolves to the current variant name
wxString token = wxS( "VARIANT" );
bool resolved = m_schematic->ResolveTextVar( &m_schematic->Hierarchy()[0], &token, 0 );
BOOST_CHECK( resolved );
BOOST_CHECK_EQUAL( token, variantName );
// Also test VARIANTNAME (legacy/alias)
token = wxS( "VARIANTNAME" );
resolved = m_schematic->ResolveTextVar( &m_schematic->Hierarchy()[0], &token, 0 );
BOOST_CHECK( resolved );
BOOST_CHECK_EQUAL( token, variantName );
// Empty variant
m_schematic->SetCurrentVariant( wxEmptyString );
token = wxS( "VARIANT" );
resolved = m_schematic->ResolveTextVar( &m_schematic->Hierarchy()[0], &token, 0 );
BOOST_CHECK( resolved );
BOOST_CHECK( token.IsEmpty() );
}
/**
* Test ${VARIANT_DESC} text variable resolution.
*/
BOOST_AUTO_TEST_CASE( TextVariableVARIANT_DESC )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "variant_test" ) );
fn.SetName( wxS( "variant_test" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
BOOST_REQUIRE( m_schematic );
wxString variantName = wxS( "IndustrialVariant" );
wxString description = wxS( "Industrial temperature range components" );
// Add a variant with description and set it as current
m_schematic->AddVariant( variantName );
m_schematic->SetVariantDescription( variantName, description );
m_schematic->SetCurrentVariant( variantName );
// Test that ${VARIANT_DESC} resolves to the variant description
wxString token = wxS( "VARIANT_DESC" );
bool resolved = m_schematic->ResolveTextVar( &m_schematic->Hierarchy()[0], &token, 0 );
BOOST_CHECK( resolved );
BOOST_CHECK_EQUAL( token, description );
// Test with no description set
m_schematic->SetVariantDescription( variantName, wxEmptyString );
token = wxS( "VARIANT_DESC" );
resolved = m_schematic->ResolveTextVar( &m_schematic->Hierarchy()[0], &token, 0 );
BOOST_CHECK( resolved );
BOOST_CHECK( token.IsEmpty() );
}
/**
* Test RenameVariant preserves data.
*/
BOOST_AUTO_TEST_CASE( RenameVariantPreservesData )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "variant_test" ) );
fn.SetName( wxS( "variant_test" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
BOOST_REQUIRE( m_schematic );
SCH_SYMBOL* symbol = GetFirstSymbol();
BOOST_REQUIRE( symbol );
wxString oldName = wxS( "OriginalVariant" );
wxString newName = wxS( "RenamedVariant" );
wxString description = wxS( "Original description" );
// Set up the variant with data
m_schematic->AddVariant( oldName );
m_schematic->SetVariantDescription( oldName, description );
m_schematic->SetCurrentVariant( oldName );
// Set symbol variant properties
symbol->SetDNP( true, &m_schematic->Hierarchy()[0], oldName );
symbol->GetField( FIELD_T::VALUE )->SetText( wxS( "100K" ), &m_schematic->Hierarchy()[0], oldName );
// Verify the data is set
BOOST_CHECK( symbol->GetDNP( &m_schematic->Hierarchy()[0], oldName ) );
BOOST_CHECK_EQUAL( symbol->GetField( FIELD_T::VALUE )->GetShownText( &m_schematic->Hierarchy()[0],
false, 0, oldName ), wxS( "100K" ) );
// Rename the variant
m_schematic->RenameVariant( oldName, newName );
// Verify old name is gone
BOOST_CHECK( !m_schematic->GetVariantNames().contains( oldName ) );
// Verify new name exists
BOOST_CHECK( m_schematic->GetVariantNames().contains( newName ) );
// Verify description was preserved
BOOST_CHECK_EQUAL( m_schematic->GetVariantDescription( newName ), description );
// Verify current variant was updated
BOOST_CHECK_EQUAL( m_schematic->GetCurrentVariant(), newName );
// Verify symbol variant data was preserved
BOOST_CHECK( symbol->GetDNP( &m_schematic->Hierarchy()[0], newName ) );
BOOST_CHECK_EQUAL( symbol->GetField( FIELD_T::VALUE )->GetShownText( &m_schematic->Hierarchy()[0],
false, 0, newName ), wxS( "100K" ) );
}
/**
* Test CopyVariant creates an independent copy.
*/
BOOST_AUTO_TEST_CASE( CopyVariantCreatesIndependentCopy )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "variant_test" ) );
fn.SetName( wxS( "variant_test" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
BOOST_REQUIRE( m_schematic );
SCH_SYMBOL* symbol = GetFirstSymbol();
BOOST_REQUIRE( symbol );
wxString sourceVariant = wxS( "SourceVariant" );
wxString copyVariant = wxS( "CopiedVariant" );
wxString description = wxS( "Source description" );
// Set up the source variant
m_schematic->AddVariant( sourceVariant );
m_schematic->SetVariantDescription( sourceVariant, description );
// Set symbol variant properties for source
symbol->SetDNP( true, &m_schematic->Hierarchy()[0], sourceVariant );
symbol->SetExcludedFromBOM( true, &m_schematic->Hierarchy()[0], sourceVariant );
symbol->SetExcludedFromBoard( true, &m_schematic->Hierarchy()[0], sourceVariant );
symbol->SetExcludedFromPosFiles( true, &m_schematic->Hierarchy()[0], sourceVariant );
symbol->GetField( FIELD_T::VALUE )->SetText( wxS( "47K" ), &m_schematic->Hierarchy()[0], sourceVariant );
// Copy the variant
m_schematic->CopyVariant( sourceVariant, copyVariant );
// Verify both variants exist
BOOST_CHECK( m_schematic->GetVariantNames().contains( sourceVariant ) );
BOOST_CHECK( m_schematic->GetVariantNames().contains( copyVariant ) );
// Verify description was copied
BOOST_CHECK_EQUAL( m_schematic->GetVariantDescription( copyVariant ), description );
// Verify symbol properties were copied
BOOST_CHECK( symbol->GetDNP( &m_schematic->Hierarchy()[0], copyVariant ) );
BOOST_CHECK( symbol->GetExcludedFromBOM( &m_schematic->Hierarchy()[0], copyVariant ) );
BOOST_CHECK( symbol->GetExcludedFromBoard( &m_schematic->Hierarchy()[0], copyVariant ) );
BOOST_CHECK( symbol->GetExcludedFromPosFiles( &m_schematic->Hierarchy()[0], copyVariant ) );
BOOST_CHECK_EQUAL( symbol->GetField( FIELD_T::VALUE )->GetShownText( &m_schematic->Hierarchy()[0],
false, 0, copyVariant ), wxS( "47K" ) );
// Modify the copy and verify source is unchanged
symbol->SetDNP( false, &m_schematic->Hierarchy()[0], copyVariant );
symbol->GetField( FIELD_T::VALUE )->SetText( wxS( "100K" ), &m_schematic->Hierarchy()[0], copyVariant );
// Source should still have original values
BOOST_CHECK( symbol->GetDNP( &m_schematic->Hierarchy()[0], sourceVariant ) );
BOOST_CHECK_EQUAL( symbol->GetField( FIELD_T::VALUE )->GetShownText( &m_schematic->Hierarchy()[0],
false, 0, sourceVariant ), wxS( "47K" ) );
// Copy should have modified values
BOOST_CHECK( !symbol->GetDNP( &m_schematic->Hierarchy()[0], copyVariant ) );
BOOST_CHECK_EQUAL( symbol->GetField( FIELD_T::VALUE )->GetShownText( &m_schematic->Hierarchy()[0],
false, 0, copyVariant ), wxS( "100K" ) );
}
/**
* Test variant field value difference detection.
*/
BOOST_AUTO_TEST_CASE( VariantFieldDifferenceDetection )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "variant_test" ) );
fn.SetName( wxS( "variant_test" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
BOOST_REQUIRE( m_schematic );
SCH_SYMBOL* symbol = GetFirstSymbol();
BOOST_REQUIRE( symbol );
wxString variantName1 = wxS( "DiffTestVariant1" );
wxString variantName2 = wxS( "DiffTestVariant2" );
// Get the default value
wxString defaultValue = symbol->GetField( FIELD_T::VALUE )->GetShownText( &m_schematic->Hierarchy()[0],
false, 0 );
BOOST_CHECK_EQUAL( defaultValue, wxS( "1K" ) );
// Add a variant and set a different value
m_schematic->AddVariant( variantName1 );
symbol->GetField( FIELD_T::VALUE )->SetText( wxS( "2.2K" ), &m_schematic->Hierarchy()[0], variantName1 );
// Get variant value
wxString variantValue = symbol->GetField( FIELD_T::VALUE )->GetShownText( &m_schematic->Hierarchy()[0],
false, 0, variantName1 );
BOOST_CHECK_EQUAL( variantValue, wxS( "2.2K" ) );
// Verify values differ
BOOST_CHECK( defaultValue != variantValue );
// Add another variant with same value as default to verify equality detection
m_schematic->AddVariant( variantName2 );
symbol->GetField( FIELD_T::VALUE )->SetText( wxS( "1K" ), &m_schematic->Hierarchy()[0], variantName2 );
wxString variantValue2 = symbol->GetField( FIELD_T::VALUE )->GetShownText( &m_schematic->Hierarchy()[0],
false, 0, variantName2 );
// Second variant should have the same value as default
BOOST_CHECK_EQUAL( defaultValue, variantValue2 );
}
/**
* Test variant DNP filtering for BOM.
*/
BOOST_AUTO_TEST_CASE( VariantDNPFiltering )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "variant_test" ) );
fn.SetName( wxS( "variant_test" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
BOOST_REQUIRE( m_schematic );
SCH_SYMBOL* symbol = GetFirstSymbol();
BOOST_REQUIRE( symbol );
wxString variantName = wxS( "DNPVariant" );
// By default, symbol should not be DNP
BOOST_CHECK( !symbol->GetDNP() );
BOOST_CHECK( !symbol->GetDNP( &m_schematic->Hierarchy()[0], wxEmptyString ) );
// Add a variant where symbol is DNP
m_schematic->AddVariant( variantName );
symbol->SetDNP( true, &m_schematic->Hierarchy()[0], variantName );
// Default should still not be DNP
BOOST_CHECK( !symbol->GetDNP() );
BOOST_CHECK( !symbol->GetDNP( &m_schematic->Hierarchy()[0], wxEmptyString ) );
// Variant should be DNP
BOOST_CHECK( symbol->GetDNP( &m_schematic->Hierarchy()[0], variantName ) );
// Test exclude from BOM as well
BOOST_CHECK( !symbol->GetExcludedFromBOM() );
symbol->SetExcludedFromBOM( true, &m_schematic->Hierarchy()[0], variantName );
BOOST_CHECK( symbol->GetExcludedFromBOM( &m_schematic->Hierarchy()[0], variantName ) );
BOOST_CHECK( !symbol->GetExcludedFromBOM( &m_schematic->Hierarchy()[0], wxEmptyString ) );
// Test exclude from board as well
BOOST_CHECK( !symbol->GetExcludedFromBoard() );
symbol->SetExcludedFromBoard( true, &m_schematic->Hierarchy()[0], variantName );
BOOST_CHECK( symbol->GetExcludedFromBoard( &m_schematic->Hierarchy()[0], variantName ) );
BOOST_CHECK( !symbol->GetExcludedFromBoard( &m_schematic->Hierarchy()[0], wxEmptyString ) );
// Test exclude from position files as well
BOOST_CHECK( !symbol->GetExcludedFromPosFiles() );
symbol->SetExcludedFromPosFiles( true, &m_schematic->Hierarchy()[0], variantName );
BOOST_CHECK( symbol->GetExcludedFromPosFiles( &m_schematic->Hierarchy()[0], variantName ) );
BOOST_CHECK( !symbol->GetExcludedFromPosFiles( &m_schematic->Hierarchy()[0], wxEmptyString ) );
}
/**
* Test GetVariantNamesForUI returns properly formatted array.
*/
BOOST_AUTO_TEST_CASE( GetVariantNamesForUI )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "variant_test" ) );
fn.SetName( wxS( "variant_test" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
BOOST_REQUIRE( m_schematic );
// Add some variants
m_schematic->AddVariant( wxS( "Zebra" ) );
m_schematic->AddVariant( wxS( "Alpha" ) );
m_schematic->AddVariant( wxS( "Beta" ) );
wxArrayString variantNames = m_schematic->GetVariantNamesForUI();
// Should have at least 4 items (default + 3 variants)
BOOST_CHECK( variantNames.GetCount() >= 4 );
// First item should be the default placeholder
// The actual string may vary but should represent "default"
BOOST_CHECK( !variantNames[0].IsEmpty() );
// Remaining items should be sorted
// Alpha, Beta, Zebra
bool foundAlpha = false;
bool foundBeta = false;
bool foundZebra = false;
for( size_t i = 1; i < variantNames.GetCount(); i++ )
{
if( variantNames[i] == wxS( "Alpha" ) )
foundAlpha = true;
else if( variantNames[i] == wxS( "Beta" ) )
foundBeta = true;
else if( variantNames[i] == wxS( "Zebra" ) )
foundZebra = true;
}
BOOST_CHECK( foundAlpha );
BOOST_CHECK( foundBeta );
BOOST_CHECK( foundZebra );
}
/**
* Test that SetValueFieldText correctly persists variant field values.
* This tests the fix for a bug where variant values were not saved because
* GetVariant returned a copy instead of modifying the instance directly.
*/
BOOST_AUTO_TEST_CASE( SetValueFieldTextPersistsVariantValue )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "variant_test" ) );
fn.SetName( wxS( "variant_test" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
BOOST_REQUIRE( m_schematic );
SCH_SYMBOL* symbol = GetFirstSymbol();
BOOST_REQUIRE( symbol );
wxString variantName = wxS( "PersistenceTest" );
wxString newValue = wxS( "4.7K" );
wxString defaultValue = symbol->GetField( FIELD_T::VALUE )->GetText();
// Add the variant
m_schematic->AddVariant( variantName );
// Set value using SetValueFieldText (the function that had the bug)
symbol->SetValueFieldText( newValue, &m_schematic->Hierarchy()[0], variantName );
// Verify that the variant value was actually saved by reading it back
std::optional<SCH_SYMBOL_VARIANT> variant = symbol->GetVariant( m_schematic->Hierarchy()[0], variantName );
BOOST_REQUIRE( variant.has_value() );
wxString fieldName = symbol->GetField( FIELD_T::VALUE )->GetName();
BOOST_CHECK( variant->m_Fields.contains( fieldName ) );
BOOST_CHECK_EQUAL( variant->m_Fields.at( fieldName ), newValue );
// Verify through GetValue method as well
wxString retrievedValue = symbol->GetValue( false, &m_schematic->Hierarchy()[0], false, variantName );
BOOST_CHECK_EQUAL( retrievedValue, newValue );
// Verify default value is unchanged
wxString retrievedDefault = symbol->GetValue( false, &m_schematic->Hierarchy()[0], false, wxEmptyString );
BOOST_CHECK_EQUAL( retrievedDefault, defaultValue );
}
/**
* Test that SetFieldText works consistently with SetValueFieldText for the VALUE field.
*/
BOOST_AUTO_TEST_CASE( SetFieldTextAndSetValueFieldTextConsistency )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "variant_test" ) );
fn.SetName( wxS( "variant_test" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
BOOST_REQUIRE( m_schematic );
SCH_SYMBOL* symbol = GetFirstSymbol();
BOOST_REQUIRE( symbol );
wxString variantName1 = wxS( "MethodTest1" );
wxString variantName2 = wxS( "MethodTest2" );
wxString value1 = wxS( "10K" );
wxString value2 = wxS( "22K" );
wxString fieldName = symbol->GetField( FIELD_T::VALUE )->GetName();
m_schematic->AddVariant( variantName1 );
m_schematic->AddVariant( variantName2 );
// Set variant 1 using SetValueFieldText
symbol->SetValueFieldText( value1, &m_schematic->Hierarchy()[0], variantName1 );
// Set variant 2 using SetFieldText
symbol->SetFieldText( fieldName, value2, &m_schematic->Hierarchy()[0], variantName2 );
// Both methods should produce the same result structure
std::optional<SCH_SYMBOL_VARIANT> variant1 = symbol->GetVariant( m_schematic->Hierarchy()[0], variantName1 );
std::optional<SCH_SYMBOL_VARIANT> variant2 = symbol->GetVariant( m_schematic->Hierarchy()[0], variantName2 );
BOOST_REQUIRE( variant1.has_value() );
BOOST_REQUIRE( variant2.has_value() );
BOOST_CHECK( variant1->m_Fields.contains( fieldName ) );
BOOST_CHECK( variant2->m_Fields.contains( fieldName ) );
BOOST_CHECK_EQUAL( variant1->m_Fields.at( fieldName ), value1 );
BOOST_CHECK_EQUAL( variant2->m_Fields.at( fieldName ), value2 );
// Verify through GetValue
BOOST_CHECK_EQUAL( symbol->GetValue( false, &m_schematic->Hierarchy()[0], false, variantName1 ), value1 );
BOOST_CHECK_EQUAL( symbol->GetValue( false, &m_schematic->Hierarchy()[0], false, variantName2 ), value2 );
// Verify through GetFieldText
BOOST_CHECK_EQUAL( symbol->GetFieldText( fieldName, &m_schematic->Hierarchy()[0], variantName1 ), value1 );
BOOST_CHECK_EQUAL( symbol->GetFieldText( fieldName, &m_schematic->Hierarchy()[0], variantName2 ), value2 );
}
/**
* Test variant-specific field dereferencing via ${REF:FIELD:VARIANT} syntax.
*/
BOOST_AUTO_TEST_CASE( VariantSpecificFieldDereferencing )
{
wxFileName fn;
fn.SetPath( KI_TEST::GetEeschemaTestDataDir() );
fn.AppendDir( wxS( "variant_test" ) );
fn.SetName( wxS( "variant_test" ) );
fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
LoadSchematic( fn.GetFullPath() );
BOOST_REQUIRE( m_schematic );
SCH_SYMBOL* symbol = GetFirstSymbol();
BOOST_REQUIRE( symbol );
wxString variantName = wxS( "MilitaryGrade" );
wxString variantValue = wxS( "1K-MIL" );
wxString defaultValue = wxS( "1K" );
// Create a variant with a different value field
m_schematic->AddVariant( variantName );
symbol->GetField( FIELD_T::VALUE )->SetText( variantValue, &m_schematic->Hierarchy()[0], variantName );
// Verify default value
BOOST_CHECK_EQUAL( symbol->GetField( FIELD_T::VALUE )->GetShownText( &m_schematic->Hierarchy()[0], false, 0 ),
defaultValue );
// Get the symbol's reference (R1) for cross-reference testing
wxString symbolRef = symbol->GetRef( &m_schematic->Hierarchy()[0], false );
// Test 1: ResolveCrossReference with variant syntax - should return variant-specific value
wxString token = symbolRef + wxS( ":VALUE:" ) + variantName;
bool resolved = m_schematic->ResolveCrossReference( &token, 0 );
BOOST_CHECK( resolved );
BOOST_CHECK_EQUAL( token, variantValue );
// Test 2: ResolveCrossReference without variant - should return default value
token = symbolRef + wxS( ":VALUE" );
resolved = m_schematic->ResolveCrossReference( &token, 0 );
BOOST_CHECK( resolved );
BOOST_CHECK_EQUAL( token, defaultValue );
// Test 3: ResolveCrossReference with non-existent variant - should return default value
token = symbolRef + wxS( ":VALUE:NonExistentVariant" );
resolved = m_schematic->ResolveCrossReference( &token, 0 );
BOOST_CHECK( resolved );
BOOST_CHECK_EQUAL( token, defaultValue );
// Test 4: ResolveTextVar with variant parameter directly
token = wxS( "VALUE" );
resolved = symbol->ResolveTextVar( &m_schematic->Hierarchy()[0], &token, variantName, 0 );
BOOST_CHECK( resolved );
BOOST_CHECK_EQUAL( token, variantValue );
// Test 5: ResolveTextVar with empty variant - should return default
token = wxS( "VALUE" );
resolved = symbol->ResolveTextVar( &m_schematic->Hierarchy()[0], &token, wxEmptyString, 0 );
BOOST_CHECK( resolved );
BOOST_CHECK_EQUAL( token, defaultValue );
}
BOOST_AUTO_TEST_SUITE_END()