From 753ae21fd43950ef64cd66a14028769e2255c2d4 Mon Sep 17 00:00:00 2001 From: Mike Williams Date: Tue, 14 Mar 2023 20:03:57 -0400 Subject: [PATCH] BOM Generator: wire up to kicad-cli --- common/jobs/job_export_sch_bom.h | 74 ++++ .../dialogs/dialog_symbol_fields_table.cpp | 320 ++++++-------- eeschema/dialogs/dialog_symbol_fields_table.h | 11 +- .../dialog_symbol_fields_table_base.cpp | 58 +-- .../dialog_symbol_fields_table_base.fbp | 396 ++++++++++++------ .../dialogs/dialog_symbol_fields_table_base.h | 14 +- eeschema/eeschema_jobs_handler.cpp | 124 ++++++ eeschema/eeschema_jobs_handler.h | 3 +- eeschema/eeschema_settings.h | 1 + eeschema/fields_data_model.cpp | 81 +++- eeschema/fields_data_model.h | 35 +- eeschema/sch_reference_list.cpp | 15 +- eeschema/sch_reference_list.h | 3 +- eeschema/schematic_settings.cpp | 31 ++ eeschema/schematic_settings.h | 75 ++-- kicad/CMakeLists.txt | 1 + kicad/cli/command_export_sch_bom.cpp | 149 +++++++ kicad/cli/command_export_sch_bom.h | 83 ++++ kicad/kicad_cli.cpp | 3 + 19 files changed, 1053 insertions(+), 424 deletions(-) create mode 100644 common/jobs/job_export_sch_bom.h create mode 100644 kicad/cli/command_export_sch_bom.cpp create mode 100644 kicad/cli/command_export_sch_bom.h diff --git a/common/jobs/job_export_sch_bom.h b/common/jobs/job_export_sch_bom.h new file mode 100644 index 0000000000..0b5387a0d1 --- /dev/null +++ b/common/jobs/job_export_sch_bom.h @@ -0,0 +1,74 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 Mike Williams + * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef JOB_EXPORT_SCH_BOM_H +#define JOB_EXPORT_SCH_BOM_H + +#include +#include "job.h" + +class JOB_EXPORT_SCH_BOM : public JOB +{ +public: + JOB_EXPORT_SCH_BOM( bool aIsCli ) : + JOB( "bom", aIsCli ), + m_filename(), + m_outputFile(), + + m_fieldDelimiter(), + m_stringDelimiter(), + m_refDelimiter(), + m_refRangeDelimiter(), + m_keepTabs( false ), + m_keepLineBreaks( false ), + + m_fieldsOrdered(), + m_fieldsLabels(), + m_fieldsGroupBy(), + m_sortField(), + m_sortAsc( true ), + m_filterString(), + m_groupSymbols( true ) + { + } + + // Basic options + wxString m_filename; + wxString m_outputFile; + + // Format options + wxString m_fieldDelimiter; + wxString m_stringDelimiter; + wxString m_refDelimiter; + wxString m_refRangeDelimiter; + bool m_keepTabs; + bool m_keepLineBreaks; + + // Fields options + std::vector m_fieldsOrdered; + std::vector m_fieldsLabels; + std::vector m_fieldsGroupBy; + wxString m_sortField; + bool m_sortAsc; + wxString m_filterString; + bool m_groupSymbols; +}; + +#endif diff --git a/eeschema/dialogs/dialog_symbol_fields_table.cpp b/eeschema/dialogs/dialog_symbol_fields_table.cpp index 374e56142c..da1fbe13ce 100644 --- a/eeschema/dialogs/dialog_symbol_fields_table.cpp +++ b/eeschema/dialogs/dialog_symbol_fields_table.cpp @@ -141,56 +141,6 @@ protected: }; -BOM_PRESET DIALOG_SYMBOL_FIELDS_TABLE::bomPresetGroupedByValue( - _HKI( "Grouped By Value" ), - std::map( { - std::pair( "Reference", true ), - std::pair( "Value", true ), - std::pair( "Datasheet", true ), - std::pair( "Footprint", true ), - std::pair( "Quantity", true ), - } ), - std::map( { - std::pair( "Reference", false ), - std::pair( "Value", true ), - std::pair( "Datasheet", false ), - std::pair( "Footprint", false ), - std::pair( "Quantity", false ), - } ), - std::map(), std::vector(), _( "Reference" ), true, _HKI( "" ), - true ); - - -BOM_PRESET DIALOG_SYMBOL_FIELDS_TABLE::bomPresetGroupedByValueFootprint( - _HKI( "Grouped By Value and Footprint" ), - std::map( { - std::pair( "Reference", true ), - std::pair( "Value", true ), - std::pair( "Datasheet", true ), - std::pair( "Footprint", true ), - std::pair( "Quantity", true ), - } ), - std::map( { - std::pair( "Reference", false ), - std::pair( "Value", true ), - std::pair( "Datasheet", false ), - std::pair( "Footprint", true ), - std::pair( "Quantity", false ), - } ), - std::map(), std::vector(), _( "Reference" ), true, _HKI( "" ), - true ); - - -BOM_FMT_PRESET DIALOG_SYMBOL_FIELDS_TABLE::bomFmtPresetCSV - ( _HKI("CSV"), wxS( "," ), wxT( "\"" ), false, true, true); - -BOM_FMT_PRESET DIALOG_SYMBOL_FIELDS_TABLE::bomFmtPresetTSV - ( _HKI("TSV"), wxS( "\t" ), wxT(""), false, true, true); - -BOM_FMT_PRESET DIALOG_SYMBOL_FIELDS_TABLE::bomFmtPresetSemicolons - ( _HKI("Semicolons"), wxS( ";" ), wxT("'"), false, true, true); - - DIALOG_SYMBOL_FIELDS_TABLE::DIALOG_SYMBOL_FIELDS_TABLE( SCH_EDIT_FRAME* parent ) : DIALOG_SYMBOL_FIELDS_TABLE_BASE( parent ), m_currentBomPreset( nullptr ), m_lastSelectedBomPreset( nullptr ), m_parent( parent ), @@ -241,7 +191,7 @@ DIALOG_SYMBOL_FIELDS_TABLE::DIALOG_SYMBOL_FIELDS_TABLE( SCH_EDIT_FRAME* parent ) m_fieldsCtrl->SetIndent( 0 ); m_filter->SetDescriptiveText( _( "Filter" ) ); - m_dataModel = new FIELDS_EDITOR_GRID_DATA_MODEL( m_parent, m_symbolsList ); + m_dataModel = new FIELDS_EDITOR_GRID_DATA_MODEL( m_symbolsList ); LoadFieldNames(); // loads rows into m_fieldsCtrl and columns into m_dataModel @@ -274,13 +224,6 @@ DIALOG_SYMBOL_FIELDS_TABLE::DIALOG_SYMBOL_FIELDS_TABLE( SCH_EDIT_FRAME* parent ) m_splitterMainWindow->SetMinimumPaneSize( fieldsMinWidth ); m_splitterMainWindow->SetSashPosition( fieldsMinWidth + 40 ); - m_cbBomPresets->SetToolTip( wxString::Format( - _( "Save and restore layer visibility combinations.\n" - "Use %s+Tab to activate selector.\n" - "Successive Tabs while holding %s down will " - "cycle through presets in the popup." ), - KeyNameFromKeyCode( PRESET_SWITCH_KEY ), KeyNameFromKeyCode( PRESET_SWITCH_KEY ) ) ); - m_grid->UseNativeColHeader( true ); m_grid->SetTable( m_dataModel, true ); @@ -295,12 +238,12 @@ DIALOG_SYMBOL_FIELDS_TABLE::DIALOG_SYMBOL_FIELDS_TABLE( SCH_EDIT_FRAME* parent ) // Load our BOM view presets SetUserBomPresets( m_schSettings.m_BomPresets ); - ApplyBomPreset( bomPresetGroupedByValueFootprint ); + ApplyBomPreset( SCHEMATIC_SETTINGS::bomPresetGroupedByValueFootprint ); syncBomPresetSelection(); // Load BOM export format presets SetUserBomFmtPresets( m_schSettings.m_BomFmtPresets ); - ApplyBomFmtPreset( bomFmtPresetCSV ); + ApplyBomFmtPreset( SCHEMATIC_SETTINGS::bomFmtPresetCSV ); syncBomFmtPresetSelection(); m_grid->SelectRow( 0 ); @@ -377,11 +320,12 @@ void DIALOG_SYMBOL_FIELDS_TABLE::SetupColumnProperties() // become unhidden. if( m_grid->IsColShown( col ) ) { + EESCHEMA_SETTINGS* cfg = static_cast( Kiface().KifaceSettings() ); std::string key( m_dataModel->GetColFieldName( col ).ToUTF8() ); - if( m_schSettings.m_BomSettings.column_widths.count( key ) ) + if( cfg->m_FieldEditorPanel.field_widths.count( key ) ) { - int width = m_schSettings.m_BomSettings.column_widths.at( key ); + int width = cfg->m_FieldEditorPanel.field_widths.at( key ); m_grid->SetColSize( col, width ); } else @@ -396,10 +340,10 @@ void DIALOG_SYMBOL_FIELDS_TABLE::SetupColumnProperties() } } - if( m_schSettings.m_BomSettings.sort_field == m_dataModel->GetColFieldName( col ) ) + if( m_schSettings.m_BomSettings.sortField == m_dataModel->GetColFieldName( col ) ) { sortCol = col; - sortAscending = m_schSettings.m_BomSettings.sort_asc; + sortAscending = m_schSettings.m_BomSettings.sortAsc; } } @@ -500,7 +444,13 @@ bool DIALOG_SYMBOL_FIELDS_TABLE::TransferDataFromWindow() SCH_SHEET_PATH currentSheet = m_parent->GetCurrentSheet(); - m_dataModel->ApplyData(); + std::function changeHandler = + [this]( SCH_SYMBOL& aSymbol, SCH_SHEET_PATH& aPath ) -> void + { + m_parent->SaveCopyInUndoList( aPath.LastScreen(), &aSymbol, UNDO_REDO::CHANGED, true ); + }; + + m_dataModel->ApplyData( changeHandler ); // Reset the view to where we left the user m_parent->SetCurrentSheet( currentSheet ); @@ -513,29 +463,20 @@ bool DIALOG_SYMBOL_FIELDS_TABLE::TransferDataFromWindow() } -void DIALOG_SYMBOL_FIELDS_TABLE::AddField( const wxString& aFieldName, - const wxString& aLabelValue, - bool defaultShow, bool defaultSortBy, bool addedByUser ) +void DIALOG_SYMBOL_FIELDS_TABLE::AddField( const wxString& aFieldName, const wxString& aLabelValue, + bool show, bool groupBy, bool addedByUser ) { m_dataModel->AddColumn( aFieldName, aLabelValue, addedByUser ); wxVector fieldsCtrlRow; - bool show = defaultShow; - bool sort_by = defaultSortBy; std::string key( aFieldName.ToUTF8() ); - if( m_schSettings.m_BomSettings.fields_show.count( key ) ) - show = m_schSettings.m_BomSettings.fields_show.at( key ); - - if( m_schSettings.m_BomSettings.fields_group_by.count( key ) ) - sort_by = m_schSettings.m_BomSettings.fields_group_by.at( key ); - // Don't change these to emplace_back: some versions of wxWidgets don't support it fieldsCtrlRow.push_back( wxVariant( aFieldName ) ); fieldsCtrlRow.push_back( wxVariant( aLabelValue ) ); fieldsCtrlRow.push_back( wxVariant( show ) ); - fieldsCtrlRow.push_back( wxVariant( sort_by ) ); + fieldsCtrlRow.push_back( wxVariant( groupBy ) ); fieldsCtrlRow.push_back( wxVariant( aFieldName ) ); m_fieldsCtrl->AppendItem( fieldsCtrlRow ); @@ -622,7 +563,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnAddField( wxCommandEvent& event ) std::string key( fieldName.ToUTF8() ); - m_parent->Schematic().Settings().m_BomSettings.fields_show[key] = true; + m_parent->Schematic().Settings().m_BomSettings.fieldsOrdered.emplace_back( key ); AddField( fieldName, fieldName, true, false, true ); wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_APPENDED, 1 ); @@ -740,21 +681,13 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnRenameField( wxCommandEvent& event ) m_dataModel->RenameColumn( col, newFieldName ); m_fieldsCtrl->SetTextValue( newFieldName, col, 0 ); - std::string oldKey( fieldName.ToUTF8() ); - std::string newKey( newFieldName.ToUTF8() ); - - //In-place rename map key - auto node = m_schSettings.m_BomSettings.fields_show.extract( oldKey ); - node.key() = newKey; - m_schSettings.m_BomSettings.fields_show.insert( std::move( node ) ); - syncBomPresetSelection(); } void DIALOG_SYMBOL_FIELDS_TABLE::OnFilterText( wxCommandEvent& aEvent ) { - m_schSettings.m_BomSettings.filter_string = m_filter->GetValue(); + m_schSettings.m_BomSettings.filterString = m_filter->GetValue(); m_dataModel->SetFilter( m_filter->GetValue() ); m_dataModel->RebuildRows(); m_grid->ForceRefresh(); @@ -804,11 +737,9 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnColumnItemToggled( wxDataViewEvent& event ) case SHOW_FIELD_COLUMN: { bool value = m_fieldsCtrl->GetToggleValue( row, col ); + int dataCol = m_dataModel->GetFieldNameCol( + m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN ) ); - std::string fieldName( m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN ).ToUTF8() ); - m_schSettings.m_BomSettings.fields_show[fieldName] = value; - - int dataCol = m_dataModel->GetFieldNameCol( fieldName ); m_dataModel->SetShowColumn( dataCol, value ); if( dataCol != -1 ) @@ -834,9 +765,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnColumnItemToggled( wxDataViewEvent& event ) m_fieldsCtrl->SetToggleValue( value, row, col ); } - wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN ); - std::string fieldNameStr( fieldName.ToUTF8() ); - m_schSettings.m_BomSettings.fields_group_by[fieldNameStr] = value; + wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN ); m_dataModel->SetGroupColumn( m_dataModel->GetFieldNameCol( fieldName ), value ); m_dataModel->RebuildRows(); @@ -854,7 +783,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnColumnItemToggled( wxDataViewEvent& event ) void DIALOG_SYMBOL_FIELDS_TABLE::OnGroupSymbolsToggled( wxCommandEvent& event ) { - m_schSettings.m_BomSettings.group_symbols = m_groupSymbolsBox->GetValue(); + m_schSettings.m_BomSettings.groupSymbols = m_groupSymbolsBox->GetValue(); m_dataModel->SetGroupingEnabled( m_groupSymbolsBox->GetValue() ); m_dataModel->RebuildRows(); m_grid->ForceRefresh(); @@ -883,8 +812,8 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnColSort( wxGridEvent& aEvent ) } // We only support sorting on one column at this time - m_schSettings.m_BomSettings.sort_field = m_dataModel->GetColFieldName( sortCol ); - m_schSettings.m_BomSettings.sort_asc = ascending; + m_schSettings.m_BomSettings.sortField = m_dataModel->GetColFieldName( sortCol ); + m_schSettings.m_BomSettings.sortAsc = ascending; m_dataModel->SetSorting( sortCol, ascending ); m_dataModel->RebuildRows(); @@ -905,7 +834,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnColMove( wxGridEvent& aEvent ) m_dataModel->MoveColumn( origPos, newPos ); - m_schSettings.m_BomSettings.column_order = m_dataModel->GetFieldsOrder(); + m_schSettings.m_BomSettings.fieldsOrdered = m_dataModel->GetFieldsOrder(); // "Unmove" the column since we've moved the column internally m_grid->ResetColPos(); @@ -949,9 +878,6 @@ void DIALOG_SYMBOL_FIELDS_TABLE::OnTableColSize( wxGridSizeEvent& aEvent ) int col = aEvent.GetRowOrCol(); std::string key( m_dataModel->GetColFieldName( col ).ToUTF8() ); - if( m_grid->GetColSize( col ) ) - m_schSettings.m_BomSettings.column_widths[key] = m_grid->GetColSize( col ); - aEvent.Skip(); m_grid->ForceRefresh(); @@ -1074,9 +1000,10 @@ void DIALOG_SYMBOL_FIELDS_TABLE::PreviewRefresh() current.fieldDelimiter = m_textFieldDelimiter->GetValue(); current.stringDelimiter = m_textStringDelimiter->GetValue(); - current.spacedRefs = m_checkSpacedRefs->GetValue(); - current.removeTabs = m_checkRemoveTabs->GetValue(); - current.removeLineBreaks = m_checkRemoveLineBreaks->GetValue(); + current.refDelimiter = m_textRefDelimiter->GetValue(); + current.refRangeDelimiter = m_textRefRangeDelimiter->GetValue(); + current.keepTabs = m_checkKeepTabs->GetValue(); + current.keepLineBreaks = m_checkKeepLineBreaks->GetValue(); m_textOutput->SetValue( m_dataModel->Export( current ) ); } @@ -1274,7 +1201,8 @@ void DIALOG_SYMBOL_FIELDS_TABLE::loadDefaultBomPresets() m_bomPresetMRU.clear(); // Load the read-only defaults - for( const BOM_PRESET& preset : { bomPresetGroupedByValue, bomPresetGroupedByValueFootprint } ) + for( const BOM_PRESET& preset : { SCHEMATIC_SETTINGS::bomPresetGroupedByValue, + SCHEMATIC_SETTINGS::bomPresetGroupedByValueFootprint } ) { m_bomPresets[preset.name] = preset; m_bomPresets[preset.name].readOnly = true; @@ -1298,7 +1226,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::rebuildBomPresetsWidget() m_cbBomPresets->Append( wxGetTranslation( pair.first ), static_cast( &pair.second ) ); - if( pair.first == bomPresetGroupedByValueFootprint.name ) + if( pair.first == SCHEMATIC_SETTINGS::bomPresetGroupedByValueFootprint.name ) default_idx = idx; idx++; @@ -1324,13 +1252,15 @@ void DIALOG_SYMBOL_FIELDS_TABLE::syncBomPresetSelection() auto it = std::find_if( m_bomPresets.begin(), m_bomPresets.end(), [&]( const std::pair& aPair ) { - return ( aPair.second.fields_show == current.fields_show - && aPair.second.fields_group_by == current.fields_group_by - && aPair.second.sort_field == current.sort_field - && aPair.second.sort_asc == current.sort_asc - && aPair.second.column_order == current.column_order - && aPair.second.filter_string == current.filter_string - && aPair.second.group_symbols == current.group_symbols ); + return ( aPair.second.fieldsOrdered == current.fieldsOrdered + && aPair.second.fieldsLabels == current.fieldsLabels + && aPair.second.fieldsShow == current.fieldsShow + && aPair.second.fieldsGroupBy == current.fieldsGroupBy + && aPair.second.sortField == current.sortField + && aPair.second.sortAsc == current.sortAsc + && aPair.second.fieldsOrdered == current.fieldsOrdered + && aPair.second.filterString == current.filterString + && aPair.second.groupSymbols == current.groupSymbols ); } ); if( it != m_bomPresets.end() ) @@ -1426,14 +1356,14 @@ void DIALOG_SYMBOL_FIELDS_TABLE::onBomPresetChanged( wxCommandEvent& aEvent ) if( !exists ) { - m_bomPresets[name] = BOM_PRESET( name, m_schSettings.m_BomSettings.fields_show, - m_schSettings.m_BomSettings.fields_group_by, - m_schSettings.m_BomSettings.column_widths, - m_schSettings.m_BomSettings.column_order, - m_schSettings.m_BomSettings.sort_field, - m_schSettings.m_BomSettings.sort_asc, - m_schSettings.m_BomSettings.filter_string, - m_schSettings.m_BomSettings.group_symbols ); + m_bomPresets[name] = BOM_PRESET( name, m_schSettings.m_BomSettings.fieldsOrdered, + m_schSettings.m_BomSettings.fieldsLabels, + m_schSettings.m_BomSettings.fieldsShow, + m_schSettings.m_BomSettings.fieldsGroupBy, + m_schSettings.m_BomSettings.sortField, + m_schSettings.m_BomSettings.sortAsc, + m_schSettings.m_BomSettings.filterString, + m_schSettings.m_BomSettings.groupSymbols ); } BOM_PRESET* preset = &m_bomPresets[name]; @@ -1445,14 +1375,14 @@ void DIALOG_SYMBOL_FIELDS_TABLE::onBomPresetChanged( wxCommandEvent& aEvent ) } else { - preset->fields_show = m_schSettings.m_BomSettings.fields_show; - preset->fields_group_by = m_schSettings.m_BomSettings.fields_group_by; - preset->column_widths = m_schSettings.m_BomSettings.column_widths; - preset->column_order = m_schSettings.m_BomSettings.column_order; - preset->sort_field = m_schSettings.m_BomSettings.sort_field; - preset->sort_asc = m_schSettings.m_BomSettings.sort_asc; - preset->filter_string = m_schSettings.m_BomSettings.filter_string; - preset->group_symbols = m_schSettings.m_BomSettings.group_symbols; + preset->fieldsOrdered = m_schSettings.m_BomSettings.fieldsOrdered; + preset->fieldsLabels = m_schSettings.m_BomSettings.fieldsLabels; + preset->fieldsShow = m_schSettings.m_BomSettings.fieldsShow; + preset->fieldsGroupBy = m_schSettings.m_BomSettings.fieldsGroupBy; + preset->sortField = m_schSettings.m_BomSettings.sortField; + preset->sortAsc = m_schSettings.m_BomSettings.sortAsc; + preset->filterString = m_schSettings.m_BomSettings.filterString; + preset->groupSymbols = m_schSettings.m_BomSettings.groupSymbols; index = m_cbBomPresets->FindString( name ); m_bomPresetMRU.Remove( name ); @@ -1526,71 +1456,79 @@ void DIALOG_SYMBOL_FIELDS_TABLE::onBomPresetChanged( wxCommandEvent& aEvent ) void DIALOG_SYMBOL_FIELDS_TABLE::doApplyBomPreset( const BOM_PRESET& aPreset ) { - // Set a good default sort - int refCol = m_dataModel->GetFieldNameCol( - TEMPLATE_FIELDNAME::GetDefaultFieldName( REFERENCE_FIELD ) ); - m_grid->SetSortingColumn( refCol, false ); + // Basically, we apply the BOM preset to the data model and then + // update our UI to reflect resulting the data model state, not the preset. + m_dataModel->ApplyBomPreset( aPreset ); + // BOM Presets can add, but not remove, columns, so make sure the field control + // grid has all of them before starting + for( int i = 0; i < m_dataModel->GetColsCount(); i++ ) + { + const wxString& fieldName( m_dataModel->GetColFieldName( i ) ); + bool found = false; + + for( int j = 0; i < m_fieldsCtrl->GetItemCount(); j++ ) + { + if( m_fieldsCtrl->GetTextValue( j, FIELD_NAME_COLUMN ) == fieldName ) + { + found = true; + break; + } + } + + // Properties like label, etc. will be added in the next loop + if( !found ) + AddField( fieldName, fieldName, false, false ); + } + + // Sync all fields for( int i = 0; i < m_fieldsCtrl->GetItemCount(); i++ ) { - const std::string fieldName( m_fieldsCtrl->GetTextValue( i, FIELD_NAME_COLUMN ).ToUTF8() ); - int col = m_dataModel->GetFieldNameCol( fieldName ); + const wxString& fieldName( m_fieldsCtrl->GetTextValue( i, FIELD_NAME_COLUMN ) ); + int col = m_dataModel->GetFieldNameCol( fieldName ); if( col == -1 ) + { + wxASSERT_MSG( true, "Fields control has a field not found in the data model." ); continue; + } - bool show = aPreset.fields_show.count( fieldName ) && aPreset.fields_show.at( fieldName ); - bool groupBy = aPreset.fields_group_by.count( fieldName ) - && aPreset.fields_group_by.at( fieldName ); - int width = aPreset.column_widths.count( fieldName ) ? aPreset.column_widths.at( fieldName ) - : -1; + EESCHEMA_SETTINGS* cfg = static_cast( Kiface().KifaceSettings() ); + std::string fieldNameStr( fieldName.ToUTF8() ); + + if( cfg->m_FieldEditorPanel.field_widths.count( fieldNameStr ) ) + m_grid->SetColMinimalWidth( col, + cfg->m_FieldEditorPanel.field_widths.at( fieldNameStr ) ); // Set shown colums + bool show = m_dataModel->GetShowColumn( col ); m_fieldsCtrl->SetToggleValue( show, i, SHOW_FIELD_COLUMN ); - m_dataModel->SetShowColumn( col, show ); if( show ) + { m_grid->ShowCol( col ); + //m_grid->SetColSize( col, schSettings. ); + } else m_grid->HideCol( col ); // Set grouped columns + bool groupBy = m_dataModel->GetGroupColumn( col ); m_fieldsCtrl->SetToggleValue( groupBy, i, GROUP_BY_COLUMN ); - m_dataModel->SetGroupColumn( col, groupBy ); - - // Set sorting - if( aPreset.sort_field == fieldName ) - { - m_dataModel->SetSorting( col, aPreset.sort_asc ); - m_grid->SetSortingColumn( col, aPreset.sort_asc ); - } - - // Set grid column sizes - if( width != -1 ) - m_grid->SetColSize( col, width ); } - m_dataModel->SetGroupingEnabled( aPreset.group_symbols ); - m_groupSymbolsBox->SetValue( aPreset.group_symbols ); - - m_dataModel->SetFieldsOrder( aPreset.column_order ); - - m_dataModel->SetFilter( aPreset.filter_string ); - m_filter->ChangeValue( aPreset.filter_string ); + m_grid->SetSortingColumn( m_dataModel->GetSortCol(), m_dataModel->GetSortAsc() ); + m_groupSymbolsBox->SetValue( m_dataModel->GetGroupingEnabled() ); + m_filter->ChangeValue( m_dataModel->GetFilter() ); SetupColumnProperties(); + // This will rebuild all rows and columns in the model such that the order + // and labels are right, then we refresh the shown grid data to match m_dataModel->RebuildRows(); m_grid->ForceRefresh(); - m_schSettings.m_BomSettings.fields_show = aPreset.fields_show; - m_schSettings.m_BomSettings.fields_group_by = aPreset.fields_group_by; - m_schSettings.m_BomSettings.column_widths = aPreset.column_widths; - m_schSettings.m_BomSettings.sort_field = aPreset.sort_field; - m_schSettings.m_BomSettings.sort_asc = aPreset.sort_asc; - m_schSettings.m_BomSettings.column_order = aPreset.column_order; - m_schSettings.m_BomSettings.filter_string = aPreset.filter_string; - m_schSettings.m_BomSettings.group_symbols = aPreset.group_symbols; + m_schSettings.m_BomSettings = aPreset; } @@ -1659,7 +1597,8 @@ void DIALOG_SYMBOL_FIELDS_TABLE::loadDefaultBomFmtPresets() // Load the read-only defaults for( const BOM_FMT_PRESET& preset : - { bomFmtPresetCSV, bomFmtPresetSemicolons, bomFmtPresetTSV } ) + { SCHEMATIC_SETTINGS::bomFmtPresetCSV, SCHEMATIC_SETTINGS::bomFmtPresetSemicolons, + SCHEMATIC_SETTINGS::bomFmtPresetTSV } ) { m_bomFmtPresets[preset.name] = preset; m_bomFmtPresets[preset.name].readOnly = true; @@ -1683,7 +1622,7 @@ void DIALOG_SYMBOL_FIELDS_TABLE::rebuildBomFmtPresetsWidget() m_cbBomFmtPresets->Append( wxGetTranslation( pair.first ), static_cast( &pair.second ) ); - if( pair.first == bomFmtPresetCSV.name ) + if( pair.first == SCHEMATIC_SETTINGS::bomFmtPresetCSV.name ) default_idx = idx; idx++; @@ -1712,9 +1651,10 @@ void DIALOG_SYMBOL_FIELDS_TABLE::syncBomFmtPresetSelection() { return ( aPair.second.fieldDelimiter == current.fieldDelimiter && aPair.second.stringDelimiter == current.stringDelimiter - && aPair.second.spacedRefs == current.spacedRefs - && aPair.second.removeTabs == current.removeTabs - && aPair.second.removeLineBreaks == current.removeLineBreaks ); + && aPair.second.refDelimiter == current.refDelimiter + && aPair.second.refRangeDelimiter == current.refRangeDelimiter + && aPair.second.keepTabs == current.keepTabs + && aPair.second.keepLineBreaks == current.keepLineBreaks ); } ); if( it != m_bomFmtPresets.end() ) @@ -1811,12 +1751,13 @@ void DIALOG_SYMBOL_FIELDS_TABLE::onBomFmtPresetChanged( wxCommandEvent& aEvent ) if( !exists ) { - m_bomFmtPresets[name] = BOM_FMT_PRESET( name, - m_schSettings.m_BomFmtSettings.fieldDelimiter, - m_schSettings.m_BomFmtSettings.stringDelimiter, - m_schSettings.m_BomFmtSettings.spacedRefs, - m_schSettings.m_BomFmtSettings.removeTabs, - m_schSettings.m_BomFmtSettings.removeLineBreaks ); + m_bomFmtPresets[name] = + BOM_FMT_PRESET( name, m_schSettings.m_BomFmtSettings.fieldDelimiter, + m_schSettings.m_BomFmtSettings.stringDelimiter, + m_schSettings.m_BomFmtSettings.refDelimiter, + m_schSettings.m_BomFmtSettings.refRangeDelimiter, + m_schSettings.m_BomFmtSettings.keepTabs, + m_schSettings.m_BomFmtSettings.keepLineBreaks ); } BOM_FMT_PRESET* preset = &m_bomFmtPresets[name]; @@ -1830,9 +1771,10 @@ void DIALOG_SYMBOL_FIELDS_TABLE::onBomFmtPresetChanged( wxCommandEvent& aEvent ) { preset->fieldDelimiter = m_schSettings.m_BomFmtSettings.fieldDelimiter; preset->stringDelimiter = m_schSettings.m_BomFmtSettings.stringDelimiter; - preset->spacedRefs = m_schSettings.m_BomFmtSettings.spacedRefs; - preset->removeTabs = m_schSettings.m_BomFmtSettings.removeTabs; - preset->removeLineBreaks = m_schSettings.m_BomFmtSettings.removeLineBreaks; + preset->refDelimiter = m_schSettings.m_BomFmtSettings.refDelimiter; + preset->refRangeDelimiter = m_schSettings.m_BomFmtSettings.refRangeDelimiter; + preset->keepTabs = m_schSettings.m_BomFmtSettings.keepTabs; + preset->keepLineBreaks = m_schSettings.m_BomFmtSettings.keepLineBreaks; index = m_cbBomFmtPresets->FindString( name ); m_bomFmtPresetMRU.Remove( name ); @@ -1909,15 +1851,17 @@ void DIALOG_SYMBOL_FIELDS_TABLE::doApplyBomFmtPreset( const BOM_FMT_PRESET& aPre { m_textFieldDelimiter->ChangeValue( aPreset.fieldDelimiter ); m_textStringDelimiter->ChangeValue( aPreset.stringDelimiter ); - m_checkSpacedRefs->SetValue( aPreset.spacedRefs ); - m_checkRemoveTabs->SetValue( aPreset.removeTabs ); - m_checkRemoveLineBreaks->SetValue( aPreset.removeLineBreaks ); + m_textRefDelimiter->SetValue( aPreset.refDelimiter ); + m_textRefRangeDelimiter->SetValue( aPreset.refRangeDelimiter ); + m_checkKeepTabs->SetValue( aPreset.keepTabs ); + m_checkKeepLineBreaks->SetValue( aPreset.keepLineBreaks ); PreviewRefresh(); m_schSettings.m_BomFmtSettings.fieldDelimiter = aPreset.fieldDelimiter; m_schSettings.m_BomFmtSettings.stringDelimiter = aPreset.stringDelimiter; - m_schSettings.m_BomFmtSettings.spacedRefs = aPreset.spacedRefs; - m_schSettings.m_BomFmtSettings.removeTabs = aPreset.removeTabs; - m_schSettings.m_BomFmtSettings.removeLineBreaks = aPreset.removeLineBreaks; + m_schSettings.m_BomFmtSettings.refDelimiter = aPreset.refDelimiter; + m_schSettings.m_BomFmtSettings.refRangeDelimiter = aPreset.refRangeDelimiter; + m_schSettings.m_BomFmtSettings.keepTabs = aPreset.keepTabs; + m_schSettings.m_BomFmtSettings.keepLineBreaks = aPreset.keepLineBreaks; } diff --git a/eeschema/dialogs/dialog_symbol_fields_table.h b/eeschema/dialogs/dialog_symbol_fields_table.h index d36ab272f9..b1c71028aa 100644 --- a/eeschema/dialogs/dialog_symbol_fields_table.h +++ b/eeschema/dialogs/dialog_symbol_fields_table.h @@ -48,8 +48,8 @@ public: private: void SetupColumnProperties(); - void AddField( const wxString& displayName, const wxString& aCanonicalName, bool defaultShow, - bool defaultSortBy, bool addedByUser = false ); + void AddField( const wxString& displayName, const wxString& aCanonicalName, bool show, + bool groupBy, bool addedByUser = false ); /** * Construct the rows of m_fieldsCtrl and the columns of m_dataModel from a union of all @@ -109,9 +109,6 @@ private: BOM_PRESET* m_lastSelectedBomPreset; wxArrayString m_bomPresetMRU; - static BOM_PRESET bomPresetGroupedByValue; - static BOM_PRESET bomPresetGroupedByValueFootprint; - void syncBomFmtPresetSelection(); void rebuildBomFmtPresetsWidget(); void updateBomFmtPresetSelection( const wxString& aName ); @@ -124,10 +121,6 @@ private: BOM_FMT_PRESET* m_lastSelectedBomFmtPreset; wxArrayString m_bomFmtPresetMRU; - static BOM_FMT_PRESET bomFmtPresetCSV; - static BOM_FMT_PRESET bomFmtPresetSemicolons; - static BOM_FMT_PRESET bomFmtPresetTSV; - SCH_EDIT_FRAME* m_parent; int m_fieldNameColWidth; int m_labelColWidth; diff --git a/eeschema/dialogs/dialog_symbol_fields_table_base.cpp b/eeschema/dialogs/dialog_symbol_fields_table_base.cpp index af076eb454..989d56be3e 100644 --- a/eeschema/dialogs/dialog_symbol_fields_table_base.cpp +++ b/eeschema/dialogs/dialog_symbol_fields_table_base.cpp @@ -167,8 +167,7 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::DIALOG_SYMBOL_FIELDS_TABLE_BASE( wxWindow* pare gbExport->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); wxFlexGridSizer* fgExportOptions; - fgExportOptions = new wxFlexGridSizer( 6, 2, 0, 0 ); - fgExportOptions->AddGrowableRow( 5 ); + fgExportOptions = new wxFlexGridSizer( 7, 2, 0, 0 ); fgExportOptions->SetFlexibleDirection( wxBOTH ); fgExportOptions->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); @@ -196,28 +195,35 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::DIALOG_SYMBOL_FIELDS_TABLE_BASE( wxWindow* pare m_textStringDelimiter = new wxTextCtrl( m_panelExport, wxID_ANY, _("\""), wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB ); fgExportOptions->Add( m_textStringDelimiter, 0, wxALL|wxEXPAND, 5 ); - m_labelSpacedRefs = new wxStaticText( m_panelExport, wxID_ANY, _("Spaced References:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_labelSpacedRefs->Wrap( -1 ); - fgExportOptions->Add( m_labelSpacedRefs, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5 ); + m_labelRefDelimiter = new wxStaticText( m_panelExport, wxID_ANY, _("Reference Delimiter:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_labelRefDelimiter->Wrap( -1 ); + fgExportOptions->Add( m_labelRefDelimiter, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5 ); - m_checkSpacedRefs = new wxCheckBox( m_panelExport, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - fgExportOptions->Add( m_checkSpacedRefs, 0, wxALL, 5 ); + m_textRefDelimiter = new wxTextCtrl( m_panelExport, wxID_ANY, _(","), wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB ); + fgExportOptions->Add( m_textRefDelimiter, 0, wxALL, 5 ); - m_labelRemoveTabs = new wxStaticText( m_panelExport, wxID_ANY, _("Remove Tabs:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_labelRemoveTabs->Wrap( -1 ); - fgExportOptions->Add( m_labelRemoveTabs, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5 ); + m_labelRefRangeDelimiter = new wxStaticText( m_panelExport, wxID_ANY, _("Range Delimiter:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_labelRefRangeDelimiter->Wrap( -1 ); + fgExportOptions->Add( m_labelRefRangeDelimiter, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5 ); - m_checkRemoveTabs = new wxCheckBox( m_panelExport, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - m_checkRemoveTabs->SetValue(true); - fgExportOptions->Add( m_checkRemoveTabs, 0, wxALL, 5 ); + m_textRefRangeDelimiter = new wxTextCtrl( m_panelExport, wxID_ANY, _("-"), wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB ); + m_textRefRangeDelimiter->SetToolTip( _("Leave blank to disable ranges.") ); - m_labelRemoveLineBreaks = new wxStaticText( m_panelExport, wxID_ANY, _("Remove Line Breaks:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_labelRemoveLineBreaks->Wrap( -1 ); - fgExportOptions->Add( m_labelRemoveLineBreaks, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5 ); + fgExportOptions->Add( m_textRefRangeDelimiter, 0, wxALL, 5 ); - m_checkRemoveLineBreaks = new wxCheckBox( m_panelExport, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - m_checkRemoveLineBreaks->SetValue(true); - fgExportOptions->Add( m_checkRemoveLineBreaks, 0, wxALL, 5 ); + m_labelKeepTabs = new wxStaticText( m_panelExport, wxID_ANY, _("Keep Tabs:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_labelKeepTabs->Wrap( -1 ); + fgExportOptions->Add( m_labelKeepTabs, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5 ); + + m_checkKeepTabs = new wxCheckBox( m_panelExport, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgExportOptions->Add( m_checkKeepTabs, 0, wxALL, 5 ); + + m_labelKeepLineBreaks = new wxStaticText( m_panelExport, wxID_ANY, _("Keep Line Breaks:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_labelKeepLineBreaks->Wrap( -1 ); + fgExportOptions->Add( m_labelKeepLineBreaks, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL, 5 ); + + m_checkKeepLineBreaks = new wxCheckBox( m_panelExport, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgExportOptions->Add( m_checkKeepLineBreaks, 0, wxALL, 5 ); gbExport->Add( fgExportOptions, wxGBPosition( 0, 0 ), wxGBSpan( 3, 1 ), wxEXPAND, 5 ); @@ -326,9 +332,10 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::DIALOG_SYMBOL_FIELDS_TABLE_BASE( wxWindow* pare m_grid->Connect( wxEVT_GRID_RANGE_SELECT, wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnTableRangeSelected ), NULL, this ); m_textFieldDelimiter->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); m_textStringDelimiter->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); - m_checkSpacedRefs->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); - m_checkRemoveTabs->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); - m_checkRemoveLineBreaks->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); + m_textRefDelimiter->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); + m_textRefRangeDelimiter->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); + m_checkKeepTabs->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); + m_checkKeepLineBreaks->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); m_browseButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnOutputFileBrowseClicked ), NULL, this ); m_bRefreshPreview->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); m_buttonExport->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnExport ), NULL, this ); @@ -359,9 +366,10 @@ DIALOG_SYMBOL_FIELDS_TABLE_BASE::~DIALOG_SYMBOL_FIELDS_TABLE_BASE() m_grid->Disconnect( wxEVT_GRID_RANGE_SELECT, wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnTableRangeSelected ), NULL, this ); m_textFieldDelimiter->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); m_textStringDelimiter->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); - m_checkSpacedRefs->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); - m_checkRemoveTabs->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); - m_checkRemoveLineBreaks->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); + m_textRefDelimiter->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); + m_textRefRangeDelimiter->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); + m_checkKeepTabs->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); + m_checkKeepLineBreaks->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); m_browseButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnOutputFileBrowseClicked ), NULL, this ); m_bRefreshPreview->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnPreviewRefresh ), NULL, this ); m_buttonExport->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SYMBOL_FIELDS_TABLE_BASE::OnExport ), NULL, this ); diff --git a/eeschema/dialogs/dialog_symbol_fields_table_base.fbp b/eeschema/dialogs/dialog_symbol_fields_table_base.fbp index 8d6495e301..00ce0d3914 100644 --- a/eeschema/dialogs/dialog_symbol_fields_table_base.fbp +++ b/eeschema/dialogs/dialog_symbol_fields_table_base.fbp @@ -1436,13 +1436,13 @@ 2 wxBOTH - 5 + 0 fgExportOptions wxFLEX_GROWMODE_SPECIFIED none - 6 + 7 0 5 @@ -1853,7 +1853,7 @@ 0 0 wxID_ANY - Spaced References: + Reference Delimiter: 0 0 @@ -1862,7 +1862,259 @@ 0 1 - m_labelSpacedRefs + m_labelRefDelimiter + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_textRefDelimiter + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_PROCESS_TAB + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + , + + + + OnPreviewRefresh + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Range Delimiter: + 0 + + 0 + + + 0 + + 1 + m_labelRefRangeDelimiter + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_textRefRangeDelimiter + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_PROCESS_TAB + ; ; forward_declare + 0 + Leave blank to disable ranges. + + wxFILTER_NONE + wxDefaultValidator + + - + + + + OnPreviewRefresh + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Keep Tabs: + 0 + + 0 + + + 0 + + 1 + m_labelKeepTabs 1 @@ -1923,7 +2175,7 @@ 0 1 - m_checkSpacedRefs + m_checkKeepTabs 1 @@ -1979,7 +2231,7 @@ 0 0 wxID_ANY - Remove Tabs: + Keep Line Breaks: 0 0 @@ -1988,7 +2240,7 @@ 0 1 - m_labelRemoveTabs + m_labelKeepLineBreaks 1 @@ -2026,7 +2278,7 @@ 1 0 - 1 + 0 1 1 @@ -2049,133 +2301,7 @@ 0 1 - m_checkRemoveTabs - 1 - - - protected - 1 - - Resizable - 1 - - - ; ; forward_declare - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - OnPreviewRefresh - - - - 5 - wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - Remove Line Breaks: - 0 - - 0 - - - 0 - - 1 - m_labelRemoveLineBreaks - 1 - - - protected - 1 - - Resizable - 1 - - - ; ; forward_declare - 0 - - - - - -1 - - - - 5 - wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - - - 0 - - - 0 - - 1 - m_checkRemoveLineBreaks + m_checkKeepLineBreaks 1 diff --git a/eeschema/dialogs/dialog_symbol_fields_table_base.h b/eeschema/dialogs/dialog_symbol_fields_table_base.h index 0c098ccf1d..86e36dd34c 100644 --- a/eeschema/dialogs/dialog_symbol_fields_table_base.h +++ b/eeschema/dialogs/dialog_symbol_fields_table_base.h @@ -76,12 +76,14 @@ class DIALOG_SYMBOL_FIELDS_TABLE_BASE : public DIALOG_SHIM wxTextCtrl* m_textFieldDelimiter; wxStaticText* m_labelStringDelimiter; wxTextCtrl* m_textStringDelimiter; - wxStaticText* m_labelSpacedRefs; - wxCheckBox* m_checkSpacedRefs; - wxStaticText* m_labelRemoveTabs; - wxCheckBox* m_checkRemoveTabs; - wxStaticText* m_labelRemoveLineBreaks; - wxCheckBox* m_checkRemoveLineBreaks; + wxStaticText* m_labelRefDelimiter; + wxTextCtrl* m_textRefDelimiter; + wxStaticText* m_labelRefRangeDelimiter; + wxTextCtrl* m_textRefRangeDelimiter; + wxStaticText* m_labelKeepTabs; + wxCheckBox* m_checkKeepTabs; + wxStaticText* m_labelKeepLineBreaks; + wxCheckBox* m_checkKeepLineBreaks; wxStaticText* m_labelOutputDirectory; wxTextCtrl* m_outputFileName; wxBitmapButton* m_browseButton; diff --git a/eeschema/eeschema_jobs_handler.cpp b/eeschema/eeschema_jobs_handler.cpp index 631fab50b3..6daf9ae15b 100644 --- a/eeschema/eeschema_jobs_handler.cpp +++ b/eeschema/eeschema_jobs_handler.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -55,9 +56,13 @@ #include #include +#include + EESCHEMA_JOBS_HANDLER::EESCHEMA_JOBS_HANDLER() { + Register( "bom", + std::bind( &EESCHEMA_JOBS_HANDLER::JobExportBom, this, std::placeholders::_1 ) ); Register( "pythonbom", std::bind( &EESCHEMA_JOBS_HANDLER::JobExportPythonBom, this, std::placeholders::_1 ) ); Register( "netlist", @@ -230,6 +235,125 @@ int EESCHEMA_JOBS_HANDLER::JobExportNetlist( JOB* aJob ) } +int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob ) +{ + JOB_EXPORT_SCH_BOM* aBomJob = dynamic_cast( aJob ); + + if( !aBomJob ) + return CLI::EXIT_CODES::ERR_UNKNOWN; + + SCHEMATIC* sch = EESCHEMA_HELPERS::LoadSchematic( aBomJob->m_filename, SCH_IO_MGR::SCH_KICAD ); + + if( sch == nullptr ) + { + wxFprintf( stderr, _( "Failed to load schematic file\n" ) ); + return CLI::EXIT_CODES::ERR_INVALID_INPUT_FILE; + } + + // Annotation warning check + SCH_REFERENCE_LIST referenceList; + sch->GetSheets().GetSymbols( referenceList, false, false ); + + if( referenceList.GetCount() > 0 ) + { + SCH_REFERENCE_LIST copy = referenceList; + + // Check annotation splits references... + if( copy.CheckAnnotation( []( ERCE_T, const wxString&, SCH_REFERENCE*, SCH_REFERENCE* ) {} ) > 0 ) + { + wxPrintf( _( "Warning: schematic has annotation errors, please use the schematic " + "editor to fix them\n" ) ); + } + } + + // Test duplicate sheet names: + ERC_TESTER erc( sch ); + + if( erc.TestDuplicateSheetNames( false ) > 0 ) + { + wxPrintf( _( "Warning: duplicate sheet names.\n" ) ); + } + + + // Build our data model + FIELDS_EDITOR_GRID_DATA_MODEL dataModel( referenceList ); + + // Mandatory fields + quantity virtual field first + for( int i = 0; i < MANDATORY_FIELDS; ++i ) + dataModel.AddColumn( TEMPLATE_FIELDNAME::GetDefaultFieldName( i ), + TEMPLATE_FIELDNAME::GetDefaultFieldName( i, true ), false ); + + dataModel.AddColumn( wxS( "Quantity" ), _( "Qty" ), false ); + + // User field names in symbols second + std::set userFieldNames; + + for( size_t i = 0; i < referenceList.GetCount(); ++i ) + { + SCH_SYMBOL* symbol = referenceList[i].GetSymbol(); + + for( int j = MANDATORY_FIELDS; j < symbol->GetFieldCount(); ++j ) + userFieldNames.insert( symbol->GetFields()[j].GetName() ); + } + + for( const wxString& fieldName : userFieldNames ) + dataModel.AddColumn( fieldName, fieldName, true ); + + // Add any templateFieldNames which aren't already present in the userFieldNames + for( const TEMPLATE_FIELDNAME& templateFieldname : + sch->Settings().m_TemplateFieldNames.GetTemplateFieldNames() ) + { + if( userFieldNames.count( templateFieldname.m_Name ) == 0 ) + dataModel.AddColumn( templateFieldname.m_Name, templateFieldname.m_Name, false ); + } + + BOM_PRESET preset; + preset.fieldsOrdered = aBomJob->m_fieldsOrdered; + preset.fieldsLabels = aBomJob->m_fieldsLabels; + preset.fieldsShow = aBomJob->m_fieldsOrdered; + preset.fieldsGroupBy = aBomJob->m_fieldsGroupBy; + preset.sortAsc = aBomJob->m_sortAsc; + preset.sortField = aBomJob->m_sortField; + preset.groupSymbols = aBomJob->m_groupSymbols; + preset.filterString = aBomJob->m_filterString; + + dataModel.ApplyBomPreset( preset ); + + if( aBomJob->m_outputFile.IsEmpty() ) + { + wxFileName fn = sch->GetFileName(); + fn.SetName( fn.GetName() ); + fn.SetExt( CsvFileExtension ); + + aBomJob->m_outputFile = fn.GetFullName(); + } + + wxFile f; + if( !f.Open( aBomJob->m_outputFile, wxFile::write ) ) + { + wxFprintf( stderr, _( "Unable to open destination '%s'" ), aBomJob->m_outputFile ); + return CLI::EXIT_CODES::ERR_INVALID_INPUT_FILE; + } + + BOM_FMT_PRESET fmt; + fmt.fieldDelimiter = aBomJob->m_fieldDelimiter; + fmt.stringDelimiter = aBomJob->m_stringDelimiter; + fmt.refDelimiter = aBomJob->m_refDelimiter; + fmt.refRangeDelimiter = aBomJob->m_refRangeDelimiter; + fmt.keepTabs = aBomJob->m_keepTabs; + fmt.keepLineBreaks = aBomJob->m_keepLineBreaks; + + bool res = f.Write( dataModel.Export( fmt ) ); + + if( !res ) + { + return CLI::EXIT_CODES::ERR_UNKNOWN; + } + + return CLI::EXIT_CODES::OK; +} + + int EESCHEMA_JOBS_HANDLER::JobExportPythonBom( JOB* aJob ) { JOB_EXPORT_SCH_PYTHONBOM* aNetJob = dynamic_cast( aJob ); diff --git a/eeschema/eeschema_jobs_handler.h b/eeschema/eeschema_jobs_handler.h index bd5375b549..178699db00 100644 --- a/eeschema/eeschema_jobs_handler.h +++ b/eeschema/eeschema_jobs_handler.h @@ -41,6 +41,7 @@ class EESCHEMA_JOBS_HANDLER : public JOB_DISPATCHER, REPORTER { public: EESCHEMA_JOBS_HANDLER(); + int JobExportBom( JOB* aJob ); int JobExportPythonBom( JOB* aJob ); int JobExportNetlist( JOB* aJob ); int JobExportPlot( JOB* aJob ); @@ -73,4 +74,4 @@ public: }; -#endif \ No newline at end of file +#endif diff --git a/eeschema/eeschema_settings.h b/eeschema/eeschema_settings.h index a551e18937..8b7e3282c0 100644 --- a/eeschema/eeschema_settings.h +++ b/eeschema/eeschema_settings.h @@ -194,6 +194,7 @@ public: struct PANEL_FIELD_EDITOR { + std::map field_widths; }; struct PANEL_LIB_VIEW diff --git a/eeschema/fields_data_model.cpp b/eeschema/fields_data_model.cpp index 0975a722e2..e56fcd080e 100644 --- a/eeschema/fields_data_model.cpp +++ b/eeschema/fields_data_model.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include "string_utils.h" #include "fields_data_model.h" @@ -12,6 +12,10 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::AddColumn( const wxString& aFieldName, const wxString& aLabel, bool aAddedByUser ) { + // Don't add a field twice + if( GetFieldNameCol( aFieldName ) != -1 ) + return; + m_cols.push_back((struct DATA_MODEL_COL) { .m_fieldName = aFieldName, .m_label = aLabel, @@ -127,7 +131,8 @@ wxString FIELDS_EDITOR_GRID_DATA_MODEL::GetValue( int aRow, int aCol ) wxString FIELDS_EDITOR_GRID_DATA_MODEL::GetValue( const DATA_MODEL_ROW& group, int aCol, - bool spacedRefs ) + const wxString& refDelimiter, + const wxString& refRangeDelimiter ) { std::vector references; wxString fieldValue; @@ -183,7 +188,7 @@ wxString FIELDS_EDITOR_GRID_DATA_MODEL::GetValue( const DATA_MODEL_ROW& group, i } if( ColIsReference( aCol ) ) - fieldValue = SCH_REFERENCE_LIST::Shorthand( references, spacedRefs ); + fieldValue = SCH_REFERENCE_LIST::Shorthand( references, refDelimiter, refRangeDelimiter ); else if( ColIsQuantity( aCol ) ) fieldValue = wxString::Format( wxT( "%d" ), (int) references.size() ); @@ -493,14 +498,14 @@ void FIELDS_EDITOR_GRID_DATA_MODEL::ExpandAfterSort() } -void FIELDS_EDITOR_GRID_DATA_MODEL::ApplyData() +void FIELDS_EDITOR_GRID_DATA_MODEL::ApplyData( + std::function symbolChangeHandler ) { for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i ) { SCH_SYMBOL& symbol = *m_symbolsList[i].GetSymbol(); - SCH_SCREEN* screen = m_symbolsList[i].GetSheetPath().LastScreen(); - m_frame->SaveCopyInUndoList( screen, &symbol, UNDO_REDO::CHANGED, true ); + symbolChangeHandler( symbol, m_symbolsList[i].GetSheetPath() ); const std::map& fieldStore = m_dataStore[symbol.m_Uuid]; @@ -583,10 +588,67 @@ int FIELDS_EDITOR_GRID_DATA_MODEL::GetDataWidth( int aCol ) } +void FIELDS_EDITOR_GRID_DATA_MODEL::ApplyBomPreset( const BOM_PRESET& aPreset ) +{ + // Hide and un-group everything by default + for( size_t i = 0; i < m_cols.size(); i++ ) + { + SetShowColumn( i, false ); + SetGroupColumn( i, false ); + } + + // Set columns that are present and shown + for( size_t i = 0; i < aPreset.fieldsOrdered.size(); i++ ) + { + const wxString& fieldName = aPreset.fieldsOrdered[i]; + const wxString& label = + i < aPreset.fieldsLabels.size() ? aPreset.fieldsLabels[i] : fieldName; + + int col = GetFieldNameCol( fieldName ); + + // Add any missing fields, if the user doesn't add any data + // they won't be saved to the symbols anywa + if( col == -1 ) + AddColumn( fieldName, label, true ); + else + SetColLabelValue( col, label ); + + SetShowColumn( col, true ); + } + + // Set grouping columns + SetGroupingEnabled( aPreset.groupSymbols ); + + for( auto fieldName : aPreset.fieldsGroupBy ) + { + int col = GetFieldNameCol( fieldName ); + + if( col != -1 ) + SetGroupColumn( col, true ); + } + + SetFieldsOrder( aPreset.fieldsOrdered ); + + // Set our sorting + int sortCol = GetFieldNameCol( aPreset.sortField ); + if( sortCol != -1 ) + SetSorting( sortCol, aPreset.sortAsc ); + else + SetSorting( GetFieldNameCol( TEMPLATE_FIELDNAME::GetDefaultFieldName( REFERENCE_FIELD ) ), + aPreset.sortAsc ); + + SetFilter( aPreset.filterString ); + + RebuildRows(); +} + wxString FIELDS_EDITOR_GRID_DATA_MODEL::Export( const BOM_FMT_PRESET& settings ) { wxString out; + if( m_cols.empty() ) + return out; + size_t last_col = m_cols.size() - 1; // Find the location for the line terminator @@ -601,13 +663,13 @@ wxString FIELDS_EDITOR_GRID_DATA_MODEL::Export( const BOM_FMT_PRESET& settings ) auto formatField = [&]( wxString field, bool last ) -> wxString { - if( settings.removeLineBreaks ) + if( !settings.keepLineBreaks ) { field.Replace( wxS( "\r" ), wxS( "" ) ); field.Replace( wxS( "\n" ), wxS( "" ) ); } - if( settings.removeTabs ) + if( !settings.keepTabs ) { field.Replace( wxS( "\t" ), wxS( "" ) ); } @@ -642,7 +704,8 @@ wxString FIELDS_EDITOR_GRID_DATA_MODEL::Export( const BOM_FMT_PRESET& settings ) continue; // Get the unanottated version of the field, e.g. no "> " or "v " by - out.Append( formatField( GetRawValue( (int) row, (int) col, settings.spacedRefs ), + out.Append( formatField( GetRawValue( (int) row, (int) col, settings.refDelimiter, + settings.refRangeDelimiter ), col == last_col ) ); } } diff --git a/eeschema/fields_data_model.h b/eeschema/fields_data_model.h index 8dbe6cf030..d3453a5aff 100644 --- a/eeschema/fields_data_model.h +++ b/eeschema/fields_data_model.h @@ -10,6 +10,8 @@ // The internal field name (untranslated) #define FIELD_NAME_COLUMN 4 +struct BOM_PRESET; +struct BOM_FMT_PRESET; enum GROUP_TYPE { @@ -47,8 +49,8 @@ struct DATA_MODEL_COL class FIELDS_EDITOR_GRID_DATA_MODEL : public wxGridTableBase { public: - FIELDS_EDITOR_GRID_DATA_MODEL( SCH_EDIT_FRAME* aFrame, SCH_REFERENCE_LIST& aSymbolsList ) : - m_frame( aFrame ), m_symbolsList( aSymbolsList ), m_edited( false ), m_sortColumn( 0 ), + FIELDS_EDITOR_GRID_DATA_MODEL( SCH_REFERENCE_LIST& aSymbolsList ) : + m_symbolsList( aSymbolsList ), m_edited( false ), m_sortColumn( 0 ), m_sortAscending( false ), m_groupingEnabled( false ) { m_symbolsList.SplitReferences(); @@ -96,10 +98,13 @@ public: } wxString GetValue( int aRow, int aCol ) override; - wxString GetValue( const DATA_MODEL_ROW& group, int aCol, bool spacedRefs = true ); - wxString GetRawValue( int aRow, int aCol, bool spacedRefs ) + wxString GetValue( const DATA_MODEL_ROW& group, int aCol, + const wxString& refDelimiter = wxT( ", " ), + const wxString& refRangeDelimiter = wxT( "-" ) ); + wxString GetRawValue( int aRow, int aCol, const wxString& refDelimiter, + const wxString& refRangeDelimiter ) { - return GetValue( m_rows[aRow], aCol, spacedRefs ); + return GetValue( m_rows[aRow], aCol, refDelimiter, refRangeDelimiter ); } void SetValue( int aRow, int aCol, const wxString& aValue ) override; @@ -120,6 +125,8 @@ public: m_sortColumn = aCol; m_sortAscending = ascending; } + int GetSortCol() { return m_sortColumn; } + bool GetSortAsc() { return m_sortAscending; } void RebuildRows(); void ExpandRow( int aRow ); @@ -128,26 +135,41 @@ public: void CollapseForSort(); void ExpandAfterSort(); - void ApplyData(); + void ApplyData( std::function symbolChangeHandler ); bool IsEdited() { return m_edited; } int GetDataWidth( int aCol ); void SetFilter( const wxString& aFilter ) { m_filter = aFilter; } + const wxString& GetFilter() { return m_filter; } + void SetGroupingEnabled( bool group ) { m_groupingEnabled = group; } + bool GetGroupingEnabled() { return m_groupingEnabled; } + void SetGroupColumn( int aCol, bool group ) { wxCHECK_RET( aCol >= 0 && aCol < (int) m_cols.size(), "Invalid Column Number" ); m_cols[aCol].m_group = group; } + bool GetGroupColumn( int aCol ) + { + wxCHECK_MSG( aCol >= 0 && aCol < (int) m_cols.size(), false, "Invalid Column Number" ); + return m_cols[aCol].m_group; + } void SetShowColumn( int aCol, bool show ) { wxCHECK_RET( aCol >= 0 && aCol < (int) m_cols.size(), "Invalid Column Number" ); m_cols[aCol].m_show = show; } + bool GetShowColumn( int aCol ) + { + wxCHECK_MSG( aCol >= 0 && aCol < (int) m_cols.size(), false, "Invalid Column Number" ); + return m_cols[aCol].m_show; + } + void ApplyBomPreset( const BOM_PRESET& preset ); wxString Export( const BOM_FMT_PRESET& settings ); private: @@ -160,7 +182,6 @@ private: protected: - SCH_EDIT_FRAME* m_frame; SCH_REFERENCE_LIST m_symbolsList; bool m_edited; int m_sortColumn; diff --git a/eeschema/sch_reference_list.cpp b/eeschema/sch_reference_list.cpp index 0bdfc7ea61..47a8c03262 100644 --- a/eeschema/sch_reference_list.cpp +++ b/eeschema/sch_reference_list.cpp @@ -922,7 +922,9 @@ bool SCH_REFERENCE::IsSplitNeeded() } -wxString SCH_REFERENCE_LIST::Shorthand( std::vector aList, bool spaced ) +wxString SCH_REFERENCE_LIST::Shorthand( std::vector aList, + const wxString& refDelimiter, + const wxString& refRangeDelimiter ) { wxString retVal; size_t i = 0; @@ -939,25 +941,28 @@ wxString SCH_REFERENCE_LIST::Shorthand( std::vector aList, bool s && aList[ i + range ].m_numRef == int( numRef + range ) ) { range++; + + if( range == 2 && refRangeDelimiter.IsEmpty() ) + break; } if( !retVal.IsEmpty() ) - retVal << ( spaced ? wxT( ", " ) : wxT( "," ) ); + retVal << refDelimiter; if( range == 1 ) { retVal << ref << aList[ i ].GetRefNumber(); } - else if( range == 2 ) + else if( range == 2 || refRangeDelimiter.IsEmpty() ) { retVal << ref << aList[ i ].GetRefNumber(); - retVal << ( spaced ? wxT( ", " ) : wxT( "," ) ); + retVal << refDelimiter; retVal << ref << aList[ i + 1 ].GetRefNumber(); } else { retVal << ref << aList[ i ].GetRefNumber(); - retVal << wxT( "-" ); + retVal << refRangeDelimiter; retVal << ref << aList[ i + ( range - 1 ) ].GetRefNumber(); } diff --git a/eeschema/sch_reference_list.h b/eeschema/sch_reference_list.h index 47540c15e7..93c3c6889e 100644 --- a/eeschema/sch_reference_list.h +++ b/eeschema/sch_reference_list.h @@ -587,7 +587,8 @@ public: * "R1, R2, R4 - R7, U1" * @param spaced Add spaces between references */ - static wxString Shorthand( std::vector aList, bool spaced = true ); + static wxString Shorthand( std::vector aList, const wxString& refDelimiter, + const wxString& refRangeDelimiter ); friend class BACK_ANNOTATION; diff --git a/eeschema/schematic_settings.cpp b/eeschema/schematic_settings.cpp index fc3192276d..74dfc02e18 100644 --- a/eeschema/schematic_settings.cpp +++ b/eeschema/schematic_settings.cpp @@ -30,11 +30,42 @@ #include #include #include +#include const int schSettingsSchemaVersion = 1; +BOM_PRESET SCHEMATIC_SETTINGS::bomPresetGroupedByValue( + _HKI( "Grouped By Value and Footprint" ), + std::vector( { "Reference", "Value", "Datasheet", "Footprint", "Quantity" } ), + std::vector( { "Reference", "Value", "Datasheet", "Footprint", "Qty" } ), + std::vector( { "Reference", "Value", "Datasheet", "Footprint", "Quantity" } ), + std::vector( { "Value" } ), _( "Reference" ), true, _HKI( "" ), true ); + + +BOM_PRESET SCHEMATIC_SETTINGS::bomPresetGroupedByValueFootprint( + _HKI( "Grouped By Value and Footprint" ), + std::vector( { "Reference", "Value", "Datasheet", "Footprint", "Quantity" } ), + std::vector( { "Reference", "Value", "Datasheet", "Footprint", "Qty" } ), + std::vector( { "Reference", "Value", "Datasheet", "Footprint", "Quantity" } ), + std::vector( { "Value", "Footprint" } ), _( "Reference" ), true, _HKI( "" ), + true ); + + +BOM_FMT_PRESET SCHEMATIC_SETTINGS::bomFmtPresetCSV( _HKI( "CSV" ), wxS( "," ), wxT( "\"" ), + wxT( "," ), wxT( "" ), false, false ); + + +BOM_FMT_PRESET SCHEMATIC_SETTINGS::bomFmtPresetTSV( _HKI( "TSV" ), wxS( "\t" ), wxT( "" ), + wxT( "," ), wxT( "" ), false, false ); + + +BOM_FMT_PRESET SCHEMATIC_SETTINGS::bomFmtPresetSemicolons( _HKI( "Semicolons" ), wxS( ";" ), + wxT( "'" ), wxT( "," ), wxT( "" ), false, + false ); + + SCHEMATIC_SETTINGS::SCHEMATIC_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) : NESTED_SETTINGS( "schematic", schSettingsSchemaVersion, aParent, aPath ), m_DefaultLineWidth( DEFAULT_LINE_WIDTH_MILS * schIUScale.IU_PER_MILS ), diff --git a/eeschema/schematic_settings.h b/eeschema/schematic_settings.h index 054efd4cd0..87359f2e26 100644 --- a/eeschema/schematic_settings.h +++ b/eeschema/schematic_settings.h @@ -20,6 +20,8 @@ #ifndef KICAD_SCHEMATIC_SETTINGS_H #define KICAD_SCHEMATIC_SETTINGS_H +#include + #include #include #include @@ -30,40 +32,30 @@ class NGSPICE_SIMULATOR_SETTINGS; struct BOM_PRESET { - BOM_PRESET( const wxString& aName = wxEmptyString ) : - name( aName ), readOnly(false), sort_asc( true ), group_symbols( false) { } + BOM_PRESET( const wxString& aName = wxEmptyString ) : name( aName ) {} - BOM_PRESET( const wxString& aName, - const std::map& aFieldsShow, - const std::map& aFieldsGroupBy, - const std::map& aColumnWidths, - const std::vector& aColumnOrder, - const wxString& aSortField, - bool aSortAscending, - const wxString& aFilterString, - bool aGroupSymbols - ) : + BOM_PRESET( const wxString& aName, const std::vector& aFieldsOrdered, + const std::vector& aFieldsLabels, + const std::vector& aFieldsShow, + const std::vector& aFieldsGroupBy, const wxString& aSortField, + bool aSortAscending, const wxString& aFilterString, bool aGroupSymbols ) : name( aName ), - readOnly( false ), - fields_show( aFieldsShow ), - fields_group_by( aFieldsGroupBy ), - column_widths( aColumnWidths ), - column_order( aColumnOrder ), - filter_string( aFilterString ), - group_symbols( aGroupSymbols ) + fieldsOrdered( aFieldsOrdered ), fieldsLabels( aFieldsLabels ), + fieldsShow( aFieldsShow ), fieldsGroupBy( aFieldsGroupBy ), sortField( aSortField ), + sortAsc( aSortAscending ), filterString( aFilterString ), groupSymbols( aGroupSymbols ) { } wxString name; - bool readOnly; - std::map fields_show; - std::map fields_group_by; - std::map column_widths; - std::vector column_order; - wxString sort_field; - bool sort_asc; - wxString filter_string; - bool group_symbols; + bool readOnly = false; + std::vector fieldsOrdered; + std::vector fieldsLabels; + std::vector fieldsShow; + std::vector fieldsGroupBy; + wxString sortField; + bool sortAsc = true; + wxString filterString; + bool groupSymbols = false; }; @@ -71,21 +63,22 @@ struct BOM_FMT_PRESET { BOM_FMT_PRESET( const wxString& aName = wxEmptyString ) : name( aName ), readOnly( false ), fieldDelimiter( wxS( "\"" ) ), - stringDelimiter( wxS( "," ) ), spacedRefs( false ), removeTabs( true ), - removeLineBreaks( true ) + stringDelimiter( wxS( "\"" ) ), refDelimiter( "," ), refRangeDelimiter( "" ), + keepTabs( true ), keepLineBreaks( true ) { } BOM_FMT_PRESET( const wxString& aName, const wxString& aFieldDelimiter, - const wxString& aStringDelimiter, bool spacedRefs, bool removeTabs, - bool removeLineBreaks ) : + const wxString& aStringDelimiter, const wxString& aRefDelimiter, + const wxString& aRefRangeDelimiter, bool removeTabs, bool removeLineBreaks ) : name( aName ), readOnly( false ), fieldDelimiter( aFieldDelimiter ), stringDelimiter( aStringDelimiter ), - spacedRefs( spacedRefs ), - removeTabs( removeTabs ), - removeLineBreaks( removeLineBreaks ) + refDelimiter( aRefDelimiter ), + refRangeDelimiter( aRefRangeDelimiter ), + keepTabs( removeTabs ), + keepLineBreaks( removeLineBreaks ) { } @@ -93,9 +86,10 @@ struct BOM_FMT_PRESET bool readOnly; wxString fieldDelimiter; wxString stringDelimiter; - bool spacedRefs; - bool removeTabs; - bool removeLineBreaks; + wxString refDelimiter; + wxString refRangeDelimiter; + bool keepTabs; + bool keepLineBreaks; }; @@ -158,10 +152,15 @@ public: TEMPLATES m_TemplateFieldNames; /// List of stored BOM presets + static BOM_PRESET bomPresetGroupedByValue; + static BOM_PRESET bomPresetGroupedByValueFootprint; BOM_PRESET m_BomSettings; std::vector m_BomPresets; /// List of stored BOM format presets + static BOM_FMT_PRESET bomFmtPresetCSV; + static BOM_FMT_PRESET bomFmtPresetSemicolons; + static BOM_FMT_PRESET bomFmtPresetTSV; BOM_FMT_PRESET m_BomFmtSettings; std::vector m_BomFmtPresets; diff --git a/kicad/CMakeLists.txt b/kicad/CMakeLists.txt index 62c8ea5cb6..6879ffd0f1 100644 --- a/kicad/CMakeLists.txt +++ b/kicad/CMakeLists.txt @@ -46,6 +46,7 @@ set( KICAD_CLI_SRCS cli/command_export_pcb_svg.cpp cli/command_fp_export_svg.cpp cli/command_fp_upgrade.cpp + cli/command_export_sch_bom.cpp cli/command_export_sch_pythonbom.cpp cli/command_export_sch_netlist.cpp cli/command_export_sch_plot.cpp diff --git a/kicad/cli/command_export_sch_bom.cpp b/kicad/cli/command_export_sch_bom.cpp new file mode 100644 index 0000000000..c87602db8f --- /dev/null +++ b/kicad/cli/command_export_sch_bom.cpp @@ -0,0 +1,149 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 Mike Williams + * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "command_export_sch_bom.h" +#include +#include "jobs/job_export_sch_bom.h" +#include +#include +#include +#include + +#include + + +CLI::EXPORT_SCH_BOM_COMMAND::EXPORT_SCH_BOM_COMMAND() : EXPORT_PCB_BASE_COMMAND( "bom" ) +{ + // Field output options + m_argParser.add_argument( ARG_FIELDS ) + .help( UTF8STDSTR( _( ARG_FIELDS ) ) ) + .default_value( std::string( "Reference,Value,Footprint,Quantity" ) ); + + m_argParser.add_argument( ARG_LABELS ) + .help( UTF8STDSTR( _( ARG_LABELS_DESC ) ) ) + .default_value( std::string( "Refs,Value,Footprint,Qty" ) ); + + m_argParser.add_argument( ARG_GROUP_BY ) + .help( UTF8STDSTR( _( ARG_GROUP_BY_DESC ) ) ) + .default_value( std::string( "Value,Footprint" ) ); + + m_argParser.add_argument( ARG_SORT_FIELD ) + .help( UTF8STDSTR( _( ARG_SORT_FIELD_DESC ) ) ) + .default_value( std::string( "Reference" ) ); + + m_argParser.add_argument( ARG_SORT_ASC ) + .help( UTF8STDSTR( _( ARG_SORT_ASC_DESC ) ) ) + .implicit_value( true ) + .default_value( true ); + + m_argParser.add_argument( ARG_FILTER ) + .help( UTF8STDSTR( _( ARG_FILTER_DESC ) ) ) + .default_value( std::string( "" ) ); + + m_argParser.add_argument( ARG_GROUP_SYMBOLS ) + .help( UTF8STDSTR( _( ARG_GROUP_SYMBOLS_DESC ) ) ) + .implicit_value( true ) + .default_value( true ); + + // Output formatting options + m_argParser.add_argument( ARG_FIELD_DELIMITER ) + .help( UTF8STDSTR( _( ARG_FIELD_DELIMITER_DESC ) ) ) + .default_value( std::string( "," ) ); + + m_argParser.add_argument( ARG_STRING_DELIMITER ) + .help( UTF8STDSTR( _( ARG_STRING_DELIMITER_DESC ) ) ) + .default_value( std::string( "\"" ) ); + + m_argParser.add_argument( ARG_REF_DELIMITER ) + .help( UTF8STDSTR( _( ARG_REF_DELIMITER_DESC ) ) ) + .default_value( std::string( "," ) ); + + m_argParser.add_argument( ARG_REF_RANGE_DELIMITER ) + .help( UTF8STDSTR( _( ARG_REF_RANGE_DELIMITER_DESC ) ) ) + .default_value( std::string( "-" ) ); + + m_argParser.add_argument( ARG_KEEP_TABS ) + .help( UTF8STDSTR( _( ARG_KEEP_TABS_DESC ) ) ) + .implicit_value( true ) + .default_value( false ); + + m_argParser.add_argument( ARG_KEEP_LINE_BREAKS ) + .help( UTF8STDSTR( _( ARG_KEEP_LINE_BREAKS_DESC ) ) ) + .implicit_value( true ) + .default_value( false ); +} + + +std::vector CLI::EXPORT_SCH_BOM_COMMAND::convertStringList( const wxString& aList ) +{ + std::vector v; + + if( !aList.IsEmpty() ) + { + wxStringTokenizer layerTokens( aList, "," ); + + while( layerTokens.HasMoreTokens() ) + v.emplace_back( layerTokens.GetNextToken() ); + } + + return v; +} + +int CLI::EXPORT_SCH_BOM_COMMAND::doPerform( KIWAY& aKiway ) +{ + std::unique_ptr bomJob = std::make_unique( true ); + + // Basic options + bomJob->m_filename = FROM_UTF8( m_argParser.get( ARG_INPUT ).c_str() ); + bomJob->m_outputFile = FROM_UTF8( m_argParser.get( ARG_OUTPUT ).c_str() ); + + // Format options + bomJob->m_fieldDelimiter = + FROM_UTF8( m_argParser.get( ARG_FIELD_DELIMITER ).c_str() ); + bomJob->m_stringDelimiter = + FROM_UTF8( m_argParser.get( ARG_STRING_DELIMITER ).c_str() ); + bomJob->m_refDelimiter = FROM_UTF8( m_argParser.get( ARG_REF_DELIMITER ).c_str() ); + bomJob->m_refRangeDelimiter = + FROM_UTF8( m_argParser.get( ARG_REF_RANGE_DELIMITER ).c_str() ); + bomJob->m_keepTabs = m_argParser.get( ARG_KEEP_TABS ); + bomJob->m_keepLineBreaks = m_argParser.get( ARG_KEEP_LINE_BREAKS ); + + // Output fields options + bomJob->m_fieldsOrdered = + convertStringList( FROM_UTF8( m_argParser.get( ARG_FIELDS ).c_str() ) ); + bomJob->m_fieldsLabels = + convertStringList( FROM_UTF8( m_argParser.get( ARG_LABELS ).c_str() ) ); + bomJob->m_fieldsGroupBy = + convertStringList( FROM_UTF8( m_argParser.get( ARG_GROUP_BY ).c_str() ) ); + bomJob->m_sortField = FROM_UTF8( m_argParser.get( ARG_SORT_FIELD ).c_str() ); + bomJob->m_sortAsc = m_argParser.get( ARG_SORT_ASC ); + bomJob->m_filterString = FROM_UTF8( m_argParser.get( ARG_FILTER ).c_str() ); + bomJob->m_groupSymbols = m_argParser.get( ARG_GROUP_SYMBOLS ); + + if( !wxFile::Exists( bomJob->m_filename ) ) + { + wxFprintf( stderr, _( "Schematic file does not exist or is not accessible\n" ) ); + return EXIT_CODES::ERR_INVALID_INPUT_FILE; + } + + int exitCode = aKiway.ProcessJob( KIWAY::FACE_SCH, bomJob.get() ); + + return exitCode; +} diff --git a/kicad/cli/command_export_sch_bom.h b/kicad/cli/command_export_sch_bom.h new file mode 100644 index 0000000000..f5dfb3b063 --- /dev/null +++ b/kicad/cli/command_export_sch_bom.h @@ -0,0 +1,83 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 Mike Williams + * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef COMMAND_EXPORT_SCH_BOM_H +#define COMMAND_EXPORT_SCH_BOM_H + +#include "command_export_pcb_base.h" + +namespace CLI +{ + +// Options for setting the format of the export, e.g. CSV +#define ARG_FIELD_DELIMITER "--field-delimiter" +#define ARG_FIELD_DELIMITER_DESC "Separator between output fields/columns." + +#define ARG_STRING_DELIMITER "--string-delimiter" +#define ARG_STRING_DELIMITER_DESC "Character to surround fields with." + +#define ARG_REF_DELIMITER "--ref-delimiter" +#define ARG_REF_DELIMITER_DESC "Character to place between individual references." + +#define ARG_REF_RANGE_DELIMITER "--ref-range-delimiter" +#define ARG_REF_RANGE_DELIMITER_DESC "Character to place in ranges of references. Leave blank for no ranges." + +#define ARG_KEEP_TABS "--keep-tabs" +#define ARG_KEEP_TABS_DESC "Keep tab characters from input fields. Stripped by default." + +#define ARG_KEEP_LINE_BREAKS "--keep-newlines" +#define ARG_KEEP_LINE_BREAKS_DESC "Keep newline characters from input fields. Stripped by default." + +//Options for controlling the fields and the grouping +#define ARG_FIELDS "--fields" +#define ARG_FIELDS_DESC "An ordered list of fields to export." + +#define ARG_LABELS "--labels" +#define ARG_LABELS_DESC "An ordered list of labels to apply the exported fields." + +#define ARG_GROUP_BY "--group-by" +#define ARG_GROUP_BY_DESC "Fields to group references by when field values match." + +#define ARG_SORT_FIELD "--sort-field" +#define ARG_SORT_FIELD_DESC "Field name to sort by." + +#define ARG_SORT_ASC "--sort-asc" +#define ARG_SORT_ASC_DESC "Sort ascending (true) or descending (false)." + +#define ARG_FILTER "--filter" +#define ARG_FILTER_DESC "Filter string to remove output lines." + +#define ARG_GROUP_SYMBOLS "--group" +#define ARG_GROUP_SYMBOLS_DESC "Enable grouping of references with matching group-by fields." + +class EXPORT_SCH_BOM_COMMAND : public EXPORT_PCB_BASE_COMMAND +{ +public: + EXPORT_SCH_BOM_COMMAND(); + +protected: + int doPerform( KIWAY& aKiway ) override; + +private: + std::vector convertStringList( const wxString& aList ); +}; +} // namespace CLI + +#endif diff --git a/kicad/kicad_cli.cpp b/kicad/kicad_cli.cpp index 33bdd7a4da..7f09eac054 100644 --- a/kicad/kicad_cli.cpp +++ b/kicad/kicad_cli.cpp @@ -57,6 +57,7 @@ #include "cli/command_export_pcb_pos.h" #include "cli/command_export_pcb_svg.h" #include "cli/command_export_pcb_step.h" +#include "cli/command_export_sch_bom.h" #include "cli/command_export_sch_pythonbom.h" #include "cli/command_export_sch_netlist.h" #include "cli/command_export_sch_plot.h" @@ -137,6 +138,7 @@ static CLI::EXPORT_PCB_COMMAND exportPcbCmd{}; static CLI::PCB_COMMAND pcbCmd{}; static CLI::EXPORT_SCH_COMMAND exportSchCmd{}; static CLI::SCH_COMMAND schCmd{}; +static CLI::EXPORT_SCH_BOM_COMMAND exportSchBomCmd{}; static CLI::EXPORT_SCH_PYTHONBOM_COMMAND exportSchPythonBomCmd{}; static CLI::EXPORT_SCH_NETLIST_COMMAND exportSchNetlistCmd{}; static CLI::EXPORT_SCH_PLOT_COMMAND exportSchDxfCmd{ "dxf", PLOT_FORMAT::DXF }; @@ -199,6 +201,7 @@ static std::vector commandStack = { &exportSchNetlistCmd, &exportSchPdfCmd, &exportSchPostscriptCmd, + &exportSchBomCmd, &exportSchPythonBomCmd, &exportSchSvgCmd }