KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_symbol_fields_table.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2017 Oliver Walters
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <advanced_config.h>
22#include <common.h>
23#include <base_units.h>
24#include <bitmaps.h>
25#include <confirm.h>
26#include <eda_doc.h>
28#include <schematic_settings.h>
29#include <general.h>
30#include <grid_tricks.h>
31#include <string_utils.h>
32#include <kiface_base.h>
33#include <sch_edit_frame.h>
34#include <widgets/wx_infobar.h>
35#include <sch_reference_list.h>
37#include <kiplatform/ui.h>
42#include <widgets/wx_grid.h>
44#include <wx/debug.h>
45#include <wx/ffile.h>
46#include <wx/grid.h>
47#include <wx/textdlg.h>
48#include <wx/filedlg.h>
49#include <wx/msgdlg.h>
53#include <fields_data_model.h>
54#include <eda_list_dialog.h>
55#include <project_sch.h>
57
58wxDEFINE_EVENT( EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, wxCommandEvent );
59
60#ifdef __WXMAC__
61#define COLUMN_MARGIN 4
62#else
63#define COLUMN_MARGIN 15
64#endif
65
67
68
69enum
70{
73};
74
76{
77public:
79 GRID_TRICKS( aGrid )
80 {}
81
82protected:
83 void doPopupSelection( wxCommandEvent& event ) override
84 {
85 if( event.GetId() >= GRIDTRICKS_FIRST_SHOWHIDE )
86 m_grid->PostSizeEvent();
87
89 }
90};
91
92
94{
95public:
97 VIEW_CONTROLS_GRID_DATA_MODEL* aViewFieldsData,
98 FIELDS_EDITOR_GRID_DATA_MODEL* aDataModel, EMBEDDED_FILES* aFiles ) :
99 GRID_TRICKS( aGrid ),
100 m_dlg( aParent ),
101 m_viewControlsDataModel( aViewFieldsData ),
102 m_dataModel( aDataModel ),
103 m_files( aFiles )
104 {}
105
106protected:
107 void showPopupMenu( wxMenu& menu, wxGridEvent& aEvent ) override
108 {
109 int col = m_grid->GetGridCursorCol();
110
111 if( m_dataModel->GetColFieldName( col ) == GetCanonicalFieldName( FIELD_T::FOOTPRINT ) )
112 {
113 menu.Append( MYID_SELECT_FOOTPRINT, _( "Select Footprint..." ), _( "Browse for footprint" ) );
114 menu.AppendSeparator();
115 }
116 else if( m_dataModel->GetColFieldName( col ) == GetCanonicalFieldName( FIELD_T::DATASHEET ) )
117 {
118 menu.Append( MYID_SHOW_DATASHEET, _( "Show Datasheet" ), _( "Show datasheet in browser" ) );
119 menu.AppendSeparator();
120 }
121
122 GRID_TRICKS::showPopupMenu( menu, aEvent );
123 }
124
125 void doPopupSelection( wxCommandEvent& event ) override
126 {
127 int row = m_grid->GetGridCursorRow();
128 int col = m_grid->GetGridCursorCol();
129
130 if( event.GetId() == MYID_SELECT_FOOTPRINT )
131 {
132 // pick a footprint using the footprint picker.
133 wxString fpid = m_grid->GetCellValue( row, col );
134
135 if( KIWAY_PLAYER* frame = m_dlg->Kiway().Player( FRAME_FOOTPRINT_CHOOSER, true, m_dlg ) )
136 {
137 if( frame->ShowModal( &fpid, m_dlg ) )
138 m_grid->SetCellValue( row, col, fpid );
139
140 frame->Destroy();
141 }
142 }
143 else if (event.GetId() == MYID_SHOW_DATASHEET )
144 {
145 wxString datasheet_uri = m_grid->GetCellValue( row, col );
146 GetAssociatedDocument( m_dlg, datasheet_uri, &m_dlg->Prj(), PROJECT_SCH::SchSearchS( &m_dlg->Prj() ),
147 { m_files } );
148 }
149 else if( event.GetId() >= GRIDTRICKS_FIRST_SHOWHIDE )
150 {
151 if( !m_grid->CommitPendingChanges( false ) )
152 return;
153
154 // Pop-up column order is the order of the shown fields, not the viewControls order
155 col = event.GetId() - GRIDTRICKS_FIRST_SHOWHIDE;
156
157 bool show = !m_dataModel->GetShowColumn( col );
158
159 m_dlg->ShowHideColumn( col, show );
160
161 wxString fieldName = m_dataModel->GetColFieldName( col );
162
163 for( row = 0; row < m_viewControlsDataModel->GetNumberRows(); row++ )
164 {
165 if( m_viewControlsDataModel->GetCanonicalFieldName( row ) == fieldName )
166 m_viewControlsDataModel->SetValueAsBool( row, SHOW_FIELD_COLUMN, show );
167 }
168
169 if( m_viewControlsDataModel->GetView() )
170 m_viewControlsDataModel->GetView()->ForceRefresh();
171 }
172 else
173 {
175 }
176 }
177
178private:
183};
184
185
188 m_currentBomPreset( nullptr ),
189 m_lastSelectedBomPreset( nullptr ),
190 m_parent( parent ),
191 m_viewControlsDataModel( nullptr ),
192 m_dataModel( nullptr ),
193 m_schSettings( parent->Schematic().Settings() ),
194 m_job( aJob )
195{
196 // Get all symbols from the list of schematic sheets
197 m_parent->Schematic().Hierarchy().GetSymbols( m_symbolsList, SYMBOL_FILTER_NON_POWER );
198
199 if( auto conflicts = DetectFieldCaseConflicts( m_symbolsList ); !conflicts.empty() )
200 {
201 DIALOG_RESOLVE_FIELD_CASE_CONFLICTS resolver( this, m_parent, std::move( conflicts ) );
202
203 if( resolver.ShowModal() != wxID_OK )
204 {
205 m_aborted = true;
206 return;
207 }
208
209 m_symbolsList.Clear();
210 m_parent->Schematic().Hierarchy().GetSymbols( m_symbolsList, SYMBOL_FILTER_NON_POWER );
211 }
212
214 m_bMenu->SetBitmap( KiBitmapBundle( BITMAPS::config ) );
217
221
227
229
230 // Do not OptOut the notebook. That would also exclude its child controls such as the
231 // scope selector from being persisted. The active page is forced by the opening tool.
232
234
235 m_viewControlsGrid->UseNativeColHeader( true );
237
238 // must be done after SetTable(), which appears to re-set it
239 m_viewControlsGrid->SetSelectionMode( wxGrid::wxGridSelectCells );
240
241 // add Cut, Copy, and Paste to wxGrid
243
244 wxGridCellAttr* attr = new wxGridCellAttr;
245 attr->SetReadOnly( true );
246 m_viewControlsDataModel->SetColAttr( attr, DISPLAY_NAME_COLUMN );
247
248 attr = new wxGridCellAttr;
249 attr->SetRenderer( new wxGridCellBoolRenderer() );
250 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
251 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
252 m_viewControlsDataModel->SetColAttr( attr, SHOW_FIELD_COLUMN );
253
254 attr = new wxGridCellAttr;
255 attr->SetRenderer( new wxGridCellBoolRenderer() );
256 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
257 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
258 m_viewControlsDataModel->SetColAttr( attr, GROUP_BY_COLUMN );
259
260 // Compress the view controls grid. (We want it to look different from the fields grid.)
261 m_viewControlsGrid->SetDefaultRowSize( m_viewControlsGrid->GetDefaultRowSize() - FromDIP( 4 ) );
262
263 m_filter->SetDescriptiveText( _( "Filter" ) );
264
265 attr = new wxGridCellAttr;
266 attr->SetEditor( new GRID_CELL_URL_EDITOR( this, PROJECT_SCH::SchSearchS( &Prj() ), { &m_parent->Schematic() } ) );
268
269 m_grid->UseNativeColHeader( true );
270 m_grid->SetTable( m_dataModel, true );
271
272 // The field-list grid regroups its rows, so the dialog's position-based Ctrl+Z would shift
273 // values onto the wrong field.
275
276 // must be done after SetTable(), which appears to re-set it
277 m_grid->SetSelectionMode( wxGrid::wxGridSelectCells );
278
279 // add Cut, Copy, and Paste to wxGrid
281 &m_parent->Schematic() ) );
282
284
285 if( !m_parent->Schematic().GetCurrentVariant().IsEmpty() )
286 {
287 int toSelect = m_variantListBox->FindString( m_parent->Schematic().GetCurrentVariant() );
288
289 if( toSelect == wxNOT_FOUND )
290 m_variantListBox->SetSelection( 0 );
291 else
292 m_variantListBox->SetSelection( toSelect );
293 }
294 else
295 {
296 m_variantListBox->SetSelection( 0 );
297 }
298
300
301 if( m_job )
302 SetTitle( m_job->GetSettingsDialogTitle() );
303
304 // DIALOG_SHIM needs a unique hash_key because classname will be the same for both job and
305 // non-job versions (which have different sizes).
306 m_hash_key = TO_UTF8( GetTitle() );
307
308 // Set the current variant for highlighting variant-specific field values
309 m_dataModel->SetCurrentVariant( m_parent->Schematic().GetCurrentVariant() );
310
312 m_grid->ClearSelection();
313
315
317
318 SetSize( wxSize( horizPixelsFromDU( 600 ), vertPixelsFromDU( 300 ) ) );
319
320 EESCHEMA_SETTINGS::PANEL_SYMBOL_FIELDS_TABLE& cfg = m_parent->eeconfig()->m_FieldEditorPanel;
321
322 m_viewControlsGrid->ShowHideColumns( "0 1 2 3" );
323
324 CallAfter( [this, cfg]()
325 {
326 if( cfg.sidebar_collapsed )
328 else
329 m_splitterMainWindow->SetSashPosition( cfg.sash_pos );
330
332
333 m_splitter_left->SetSashPosition( cfg.variant_sash_pos );
334 } );
335
336 if( m_job )
337 m_outputFileName->SetValue( m_job->GetConfiguredOutputPath() );
338 else
339 m_outputFileName->SetValue( m_schSettings.m_BomExportFileName );
340
341 Center();
342
343 // Connect Events
344 m_grid->Bind( wxEVT_GRID_COL_SORT, &DIALOG_SYMBOL_FIELDS_TABLE::OnColSort, this );
345 m_grid->Bind( wxEVT_GRID_COL_MOVE, &DIALOG_SYMBOL_FIELDS_TABLE::OnColMove, this );
346 m_grid->GetGridWindow()->Bind( wxEVT_MOTION, &DIALOG_SYMBOL_FIELDS_TABLE::OnGridMouseMove, this );
349 m_viewControlsGrid->Bind( wxEVT_GRID_CELL_CHANGED, &DIALOG_SYMBOL_FIELDS_TABLE::OnViewControlsCellChanged, this );
350
351 if( !m_job )
352 {
353 // Start listening for schematic changes
354 m_parent->Schematic().AddListener( this );
355 }
356 else
357 {
358 // Don't allow editing
359 m_grid->EnableEditing( false );
360 m_buttonApply->Hide();
361 m_buttonExport->Hide();
362 }
363}
364
365
367{
369 m_schSettings.m_BomExportFileName = m_outputFileName->GetValue();
370
371 EESCHEMA_SETTINGS::PANEL_SYMBOL_FIELDS_TABLE& cfg = m_parent->eeconfig()->m_FieldEditorPanel;
372
373 if( !cfg.sidebar_collapsed )
374 cfg.sash_pos = m_splitterMainWindow->GetSashPosition();
375
376 cfg.variant_sash_pos = m_splitter_left->GetSashPosition();
377
378 for( int i = 0; i < m_grid->GetNumberCols(); i++ )
379 {
380 if( m_grid->IsColShown( i ) )
381 {
382 std::string fieldName( m_dataModel->GetColFieldName( i ).ToUTF8() );
383 cfg.field_widths[fieldName] = m_grid->GetColSize( i );
384 }
385 }
386
387 // Disconnect Events
388 m_grid->GetGridWindow()->Unbind( wxEVT_MOTION, &DIALOG_SYMBOL_FIELDS_TABLE::OnGridMouseMove, this );
389 m_grid->Unbind( wxEVT_GRID_COL_SORT, &DIALOG_SYMBOL_FIELDS_TABLE::OnColSort, this );
390 m_grid->Unbind( wxEVT_GRID_COL_SORT, &DIALOG_SYMBOL_FIELDS_TABLE::OnColMove, this );
393 m_viewControlsGrid->Unbind( wxEVT_GRID_CELL_CHANGED, &DIALOG_SYMBOL_FIELDS_TABLE::OnViewControlsCellChanged, this );
394
395 // Delete the GRID_TRICKS.
396 m_viewControlsGrid->PopEventHandler( true );
397 m_grid->PopEventHandler( true );
398
399 // we gave ownership of m_viewControlsDataModel & m_dataModel to the wxGrids...
400}
401
402
403void DIALOG_SYMBOL_FIELDS_TABLE::setSideBarButtonLook( bool aIsLeftPanelCollapsed )
404{
405 // Set bitmap and tooltip according to left panel visibility
406
407 if( aIsLeftPanelCollapsed )
408 {
410 m_sidebarButton->SetToolTip( _( "Expand left panel" ) );
411 }
412 else
413 {
415 m_sidebarButton->SetToolTip( _( "Collapse left panel" ) );
416 }
417}
418
419
421{
422 wxGridCellAttr* attr = new wxGridCellAttr;
423 attr->SetReadOnly( false );
424
425 // Set some column types to specific editors
426 if( m_dataModel->ColIsReference( aCol ) )
427 {
428 attr->SetReadOnly();
429 attr->SetRenderer( new GRID_CELL_TEXT_RENDERER() );
430 m_dataModel->SetColAttr( attr, aCol );
431 }
432 else if( m_dataModel->GetColFieldName( aCol ) == GetCanonicalFieldName( FIELD_T::FOOTPRINT ) )
433 {
434 attr->SetEditor( new GRID_CELL_FPID_EDITOR( this, wxEmptyString ) );
435 m_dataModel->SetColAttr( attr, aCol );
436 }
437 else if( m_dataModel->GetColFieldName( aCol ) == GetCanonicalFieldName( FIELD_T::DATASHEET ) )
438 {
439 // set datasheet column viewer button
440 attr->SetEditor( new GRID_CELL_URL_EDITOR( this, PROJECT_SCH::SchSearchS( &Prj() ),
441 { &m_parent->Schematic() } ) );
442 m_dataModel->SetColAttr( attr, aCol );
443 }
444 else if( m_dataModel->ColIsQuantity( aCol ) || m_dataModel->ColIsItemNumber( aCol ) )
445 {
446 attr->SetReadOnly();
447 attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_CENTER );
448 attr->SetRenderer( new wxGridCellNumberRenderer() );
449 m_dataModel->SetColAttr( attr, aCol );
450 }
451 else if( m_dataModel->ColIsAttribute( aCol ) )
452 {
453 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
454 attr->SetRenderer( new GRID_CELL_CHECKBOX_RENDERER() );
455 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
456 m_dataModel->SetColAttr( attr, aCol );
457 }
458 else if( IsGeneratedField( m_dataModel->GetColFieldName( aCol ) ) )
459 {
460 attr->SetReadOnly();
461 m_dataModel->SetColAttr( attr, aCol );
462 }
463 else
464 {
465 attr->SetRenderer( new GRID_CELL_TEXT_RENDERER() );
466 attr->SetEditor( m_grid->GetDefaultEditor() );
467 m_dataModel->SetColAttr( attr, aCol );
468 }
469}
470
471
473{
474 EESCHEMA_SETTINGS* cfg = m_parent->eeconfig();
475 wxSize defaultDlgSize = ConvertDialogToPixels( wxSize( 600, 300 ) );
476
477 // Restore column sorting order and widths
478 m_grid->AutoSizeColumns( false );
479 int sortCol = 0;
480 bool sortAscending = true;
481
482 for( int col = 0; col < m_grid->GetNumberCols(); ++col )
483 {
485
486 if( col == m_dataModel->GetSortCol() )
487 {
488 sortCol = col;
489 sortAscending = m_dataModel->GetSortAsc();
490 }
491 }
492
493 // sync m_grid's column visibilities to Show checkboxes in m_viewControlsGrid
494 for( int i = 0; i < m_viewControlsDataModel->GetNumberRows(); ++i )
495 {
496 int col = m_dataModel->GetFieldNameCol( m_viewControlsDataModel->GetCanonicalFieldName( i ) );
497
498 if( col == -1 )
499 continue;
500
501 bool show = m_viewControlsDataModel->GetValueAsBool( i, SHOW_FIELD_COLUMN );
502 m_dataModel->SetShowColumn( col, show );
503
504 if( show )
505 {
506 m_grid->ShowCol( col );
507
508 std::string key( m_dataModel->GetColFieldName( col ).ToUTF8() );
509
510 if( cfg->m_FieldEditorPanel.field_widths.count( key )
511 && ( cfg->m_FieldEditorPanel.field_widths.at( key ) > 0 ) )
512 {
513 m_grid->SetColSize( col, cfg->m_FieldEditorPanel.field_widths.at( key ) );
514 }
515 else
516 {
517 int textWidth = m_dataModel->GetDataWidth( col ) + COLUMN_MARGIN;
518 int maxWidth = defaultDlgSize.x / 3;
519
520 m_grid->SetColSize( col, std::clamp( textWidth, 100, maxWidth ) );
521 }
522 }
523 else
524 {
525 m_grid->HideCol( col );
526 }
527 }
528
529 m_dataModel->SetSorting( sortCol, sortAscending );
530 m_grid->SetSortingColumn( sortCol, sortAscending );
531}
532
533
535{
536 if( !wxDialog::TransferDataToWindow() )
537 return false;
538
539 LoadFieldNames(); // loads rows into m_viewControlsDataModel and columns into m_dataModel
540
541 // Load our BOM view presets
542 SetUserBomPresets( m_schSettings.m_BomPresets );
543
544 BOM_PRESET preset = m_schSettings.m_BomSettings;
545
546 if( m_job )
547 {
548 preset.name = m_job->m_bomPresetName;
549 preset.excludeDNP = m_job->m_excludeDNP;
550 preset.filterString = m_job->m_filterString;
551 preset.sortAsc = m_job->m_sortAsc;
552 preset.sortField = m_job->m_sortField;
553 preset.groupSymbols = m_job->m_groupSymbols;
554
555 preset.fieldsOrdered.clear();
556
557 size_t i = 0;
558
559 for( const wxString& fieldName : m_job->m_fieldsOrdered )
560 {
561 BOM_FIELD field;
562 field.name = fieldName;
563 field.show = !fieldName.StartsWith( wxT( "__" ), &field.name );
564 field.groupBy = alg::contains( m_job->m_fieldsGroupBy, field.name );
565
566 if( ( m_job->m_fieldsLabels.size() > i ) && !m_job->m_fieldsLabels[i].IsEmpty() )
567 field.label = m_job->m_fieldsLabels[i];
568 else if( IsGeneratedField( field.name ) )
569 field.label = GetGeneratedFieldDisplayName( field.name );
570 else
571 field.label = field.name;
572
573 preset.fieldsOrdered.emplace_back( field );
574 i++;
575 }
576 }
577
578 ApplyBomPreset( preset );
580
581 // Load BOM export format presets
582 SetUserBomFmtPresets( m_schSettings.m_BomFmtPresets );
583 BOM_FMT_PRESET fmtPreset = m_schSettings.m_BomFmtSettings;
584
585 if( m_job )
586 {
587 fmtPreset.name = m_job->m_bomFmtPresetName;
588 fmtPreset.fieldDelimiter = m_job->m_fieldDelimiter;
589 fmtPreset.keepLineBreaks = m_job->m_keepLineBreaks;
590 fmtPreset.keepTabs = m_job->m_keepTabs;
591 fmtPreset.refDelimiter = m_job->m_refDelimiter;
592 fmtPreset.refRangeDelimiter = m_job->m_refRangeDelimiter;
593 fmtPreset.stringDelimiter = m_job->m_stringDelimiter;
594 }
595
596 ApplyBomFmtPreset( fmtPreset );
598
599 TOOL_MANAGER* toolMgr = m_parent->GetToolManager();
600 SCH_SELECTION_TOOL* selectionTool = toolMgr->GetTool<SCH_SELECTION_TOOL>();
601 SCH_SELECTION& selection = selectionTool->GetSelection();
602 SCH_SYMBOL* symbol = nullptr;
603
604 m_dataModel->SetGroupingEnabled( m_groupSymbolsBox->GetValue() );
605
606 setScope( static_cast<SCOPE>( m_scope->GetSelection() ) );
607
608 if( selection.GetSize() == 1 )
609 {
610 EDA_ITEM* item = selection.Front();
611
612 if( item->Type() == SCH_SYMBOL_T )
613 symbol = (SCH_SYMBOL*) item;
614 else if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T )
615 symbol = (SCH_SYMBOL*) item->GetParent();
616 }
617
618 if( symbol )
619 {
620 for( int row = 0; row < m_dataModel->GetNumberRows(); ++row )
621 {
622 std::vector<SCH_REFERENCE> references = m_dataModel->GetRowReferences( row );
623 bool found = false;
624
625 for( const SCH_REFERENCE& ref : references )
626 {
627 if( ref.GetSymbol() == symbol )
628 {
629 found = true;
630 break;
631 }
632 }
633
634 if( found )
635 {
636 // Find the value column and the reference column if they're shown
637 int valueCol = -1;
638 int refCol = -1;
639 int anyCol = -1;
640
641 for( int col = 0; col < m_dataModel->GetNumberCols(); col++ )
642 {
643 if( m_dataModel->ColIsValue( col ) )
644 valueCol = col;
645 else if( m_dataModel->ColIsReference( col ) )
646 refCol = col;
647 else if( anyCol == -1 && m_dataModel->GetShowColumn( col ) )
648 anyCol = col;
649 }
650
651 if( valueCol != -1 && m_dataModel->GetShowColumn( valueCol ) )
652 m_grid->GoToCell( row, valueCol );
653 else if( refCol != -1 && m_dataModel->GetShowColumn( refCol ) )
654 m_grid->GoToCell( row, refCol );
655 else if( anyCol != -1 )
656 m_grid->GoToCell( row, anyCol );
657
658 break;
659 }
660 }
661 }
662
663 // We don't want table range selection events to happen until we've loaded the data or we
664 // we'll clear our selection as the grid is built before the code above can get the
665 // user's current selection.
667
668 return true;
669}
670
671
673{
674 if( !m_grid->CommitPendingChanges() )
675 return false;
676
677 if( !wxDialog::TransferDataFromWindow() )
678 return false;
679
680 if( m_job )
681 {
682 // and exit, don't even dream of saving changes from the data model
683 return true;
684 }
685
686 SCH_COMMIT commit( m_parent );
687 SCH_SHEET_PATH currentSheet = m_parent->GetCurrentSheet();
688 wxString currentVariant = m_parent->Schematic().GetCurrentVariant();
689
690 m_dataModel->ApplyData( commit, m_schSettings.m_TemplateFieldNames, currentVariant );
691
692 if( !commit.Empty() )
693 {
694 commit.Push( wxS( "Symbol Fields Table Edit" ) ); // Push clears the commit buffer.
695 m_parent->OnModify();
696 }
697
698 // Reset the view to where we left the user
699 m_parent->SetCurrentSheet( currentSheet );
700 m_parent->SyncView();
701 m_parent->Refresh();
702
703 return true;
704}
705
706
707void DIALOG_SYMBOL_FIELDS_TABLE::AddField( const wxString& aFieldName, const wxString& aLabelValue,
708 bool show, bool groupBy, bool addedByUser )
709{
710 // Users can add fields with variable names that match the special names in the grid,
711 // e.g. ${QUANTITY} so make sure we don't add them twice
712 for( int row = 0; row < m_viewControlsDataModel->GetNumberRows(); row++ )
713 {
714 if( m_viewControlsDataModel->GetCanonicalFieldName( row ).CmpNoCase( aFieldName ) == 0 )
715 return;
716 }
717
718 m_dataModel->AddColumn( aFieldName, aLabelValue, addedByUser, m_parent->Schematic().GetCurrentVariant() );
719
720 wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_APPENDED, 1 );
721 m_grid->ProcessTableMessage( msg );
722
723 m_viewControlsGrid->OnAddRow(
724 [&]() -> std::pair<int, int>
725 {
726 m_viewControlsDataModel->AppendRow( aFieldName, aLabelValue, show, groupBy );
727
728 return { m_viewControlsDataModel->GetNumberRows() - 1, -1 };
729 } );
730}
731
732
734{
735 auto addMandatoryField =
736 [&]( FIELD_T fieldId, bool show, bool groupBy )
737 {
738 m_mandatoryFieldListIndexes[fieldId] = m_viewControlsDataModel->GetNumberRows();
739
741 show, groupBy );
742 };
743
744 // Add mandatory fields first show groupBy
745 addMandatoryField( FIELD_T::REFERENCE, true, true );
746 addMandatoryField( FIELD_T::VALUE, true, true );
747 addMandatoryField( FIELD_T::FOOTPRINT, true, true );
748 addMandatoryField( FIELD_T::DATASHEET, true, false );
749 addMandatoryField( FIELD_T::DESCRIPTION, false, false );
750
751 // Generated fields present only in the fields table
754
755 // User fields next
756 auto caseInsensitiveLess = []( const wxString& a, const wxString& b )
757 {
758 return a.CmpNoCase( b ) < 0;
759 };
760
761 std::map<wxString, std::map<wxString, int>, decltype( caseInsensitiveLess )> userFieldGroups( caseInsensitiveLess );
762
763 for( int ii = 0; ii < (int) m_symbolsList.GetCount(); ++ii )
764 {
765 SCH_SYMBOL* symbol = m_symbolsList[ii].GetSymbol();
766
767 for( const SCH_FIELD& field : symbol->GetFields() )
768 {
769 if( !field.IsMandatory() && !field.IsPrivate() )
770 userFieldGroups[field.GetName()][field.GetName()]++;
771 }
772 }
773
774 for( const auto& [groupKey, exactCounts] : userFieldGroups )
775 {
776 wxString canonicalName;
777
778 if( const TEMPLATE_FIELDNAME* tfn = m_schSettings.m_TemplateFieldNames.GetFieldName( groupKey ) )
779 {
780 canonicalName = tfn->m_Name;
781 }
782 else
783 {
784 int bestCount = -1;
785
786 for( const auto& [name, count] : exactCounts )
787 {
788 if( count > bestCount )
789 {
790 bestCount = count;
791 canonicalName = name;
792 }
793 }
794 }
795
796 AddField( canonicalName, GetGeneratedFieldDisplayName( canonicalName ), true, false );
797 }
798
799 // Add any templateFieldNames which aren't already present.
800 for( const TEMPLATE_FIELDNAME& tfn : m_schSettings.m_TemplateFieldNames.GetTemplateFieldNames() )
801 {
802 if( userFieldGroups.count( tfn.m_Name ) == 0 )
803 AddField( tfn.m_Name, GetGeneratedFieldDisplayName( tfn.m_Name ), false, false );
804 }
805}
806
807
808void DIALOG_SYMBOL_FIELDS_TABLE::OnAddField( wxCommandEvent& event )
809{
810 wxTextEntryDialog dlg( this, _( "New field name:" ), _( "Add Field" ) );
811
812 if( dlg.ShowModal() != wxID_OK )
813 return;
814
815 wxString fieldName = dlg.GetValue();
816
817 if( fieldName.IsEmpty() )
818 {
819 DisplayError( this, _( "Field must have a name." ) );
820 return;
821 }
822
823 for( int i = 0; i < m_dataModel->GetNumberCols(); ++i )
824 {
825 if( fieldName.CmpNoCase( m_dataModel->GetColFieldName( i ) ) == 0 )
826 {
827 DisplayError( this, wxString::Format( _( "Field name '%s' already in use." ), fieldName ) );
828 return;
829 }
830 }
831
832 AddField( fieldName, GetGeneratedFieldDisplayName( fieldName ), true, false, true );
833
834 SetupColumnProperties( m_dataModel->GetColsCount() - 1 );
835
837 OnModify();
838}
839
840
841void DIALOG_SYMBOL_FIELDS_TABLE::OnRemoveField( wxCommandEvent& event )
842{
843 m_viewControlsGrid->OnDeleteRows(
844 [&]( int row )
845 {
846 for( FIELD_T id : MANDATORY_FIELDS )
847 {
848 if( m_mandatoryFieldListIndexes[id] == row )
849 {
850 DisplayError( this, wxString::Format( _( "The first %d fields are mandatory." ),
851 (int) m_mandatoryFieldListIndexes.size() ) );
852 return false;
853 }
854 }
855
856 return IsOK( this, wxString::Format( _( "Are you sure you want to remove the field '%s'?" ),
857 m_viewControlsDataModel->GetValue( row, DISPLAY_NAME_COLUMN ) ) );
858 },
859 [&]( int row )
860 {
861 wxString fieldName = m_viewControlsDataModel->GetCanonicalFieldName( row );
862 int col = m_dataModel->GetFieldNameCol( fieldName );
863
864 if( col != -1 )
865 m_dataModel->RemoveColumn( col );
866
867 m_viewControlsDataModel->DeleteRow( row );
868
870 OnModify();
871 } );
872}
873
874
875void DIALOG_SYMBOL_FIELDS_TABLE::OnRenameField( wxCommandEvent& event )
876{
877 wxArrayInt selectedRows = m_viewControlsGrid->GetSelectedRows();
878
879 if( selectedRows.empty() && m_viewControlsGrid->GetGridCursorRow() >= 0 )
880 selectedRows.push_back( m_viewControlsGrid->GetGridCursorRow() );
881
882 if( selectedRows.empty() )
883 return;
884
885 int row = selectedRows[0];
886
887 for( FIELD_T id : MANDATORY_FIELDS )
888 {
889 if( m_mandatoryFieldListIndexes[id] == row )
890 {
891 DisplayError( this, wxString::Format( _( "The first %d fields are mandatory and names cannot be changed." ),
892 (int) m_mandatoryFieldListIndexes.size() ) );
893 return;
894 }
895 }
896
897 wxString fieldName = m_viewControlsDataModel->GetCanonicalFieldName( row );
898 wxString label = m_viewControlsDataModel->GetValue( row, LABEL_COLUMN );
899 bool labelIsAutogenerated = label.IsSameAs( GetGeneratedFieldDisplayName( fieldName ) );
900
901 int col = m_dataModel->GetFieldNameCol( fieldName );
902 wxCHECK_RET( col != -1, wxS( "Existing field name missing from data model" ) );
903
904 wxTextEntryDialog dlg( this, _( "New field name:" ), _( "Rename Field" ), fieldName );
905
906 if( dlg.ShowModal() != wxID_OK )
907 return;
908
909 wxString newFieldName = dlg.GetValue();
910
911 // No change, no-op
912 if( newFieldName == fieldName )
913 return;
914
915 // New field name already exists
916 if( m_dataModel->GetFieldNameCol( newFieldName ) != -1 )
917 {
918 wxString confirm_msg = wxString::Format( _( "Field name %s already exists." ), newFieldName );
919 DisplayError( this, confirm_msg );
920 return;
921 }
922
923 m_dataModel->RenameColumn( col, newFieldName );
924 m_viewControlsDataModel->SetCanonicalFieldName( row, newFieldName );
925 m_viewControlsDataModel->SetValue( row, DISPLAY_NAME_COLUMN, newFieldName );
926
927 if( labelIsAutogenerated )
928 {
929 m_viewControlsDataModel->SetValue( row, LABEL_COLUMN, GetGeneratedFieldDisplayName( newFieldName ) );
930 wxGridEvent evt( m_viewControlsGrid->GetId(), wxEVT_GRID_CELL_CHANGED, m_viewControlsGrid, row, LABEL_COLUMN );
932 }
933
935 OnModify();
936}
937
938
939void DIALOG_SYMBOL_FIELDS_TABLE::OnFilterText( wxCommandEvent& aEvent )
940{
941 m_dataModel->SetFilter( m_filter->GetValue() );
942 m_dataModel->RebuildRows();
943 m_grid->ForceRefresh();
944
946}
947
948
950{
951#if defined( __WXOSX__ ) // Doesn't work properly on other ports
952 wxPoint pos = aEvent.GetPosition();
953 wxRect ctrlRect = m_filter->GetScreenRect();
954 int buttonWidth = ctrlRect.GetHeight(); // Presume buttons are square
955
956 // TODO: restore cursor when mouse leaves the filter field (or is it a MSW bug?)
957 if( m_filter->IsSearchButtonVisible() && pos.x < buttonWidth )
958 SetCursor( wxCURSOR_ARROW );
959 else if( m_filter->IsCancelButtonVisible() && pos.x > ctrlRect.GetWidth() - buttonWidth )
960 SetCursor( wxCURSOR_ARROW );
961 else
962 SetCursor( wxCURSOR_IBEAM );
963#endif
964}
965
966
968{
969 m_dataModel->SetPath( m_parent->GetCurrentSheet() );
970 m_dataModel->SetScope( aScope );
971 m_dataModel->RebuildRows();
972}
973
974
975void DIALOG_SYMBOL_FIELDS_TABLE::OnScope( wxCommandEvent& aEvent )
976{
977 switch( aEvent.GetSelection() )
978 {
979 case 0: setScope( SCOPE::SCOPE_ALL ); break;
980 case 1: setScope( SCOPE::SCOPE_SHEET ); break;
981 case 2: setScope( SCOPE::SCOPE_SHEET_RECURSIVE ); break;
982 }
983}
984
985
987{
988 m_dataModel->SetGroupingEnabled( m_groupSymbolsBox->GetValue() );
989 m_dataModel->RebuildRows();
990 m_grid->ForceRefresh();
991
993}
994
995
996void DIALOG_SYMBOL_FIELDS_TABLE::OnMenu( wxCommandEvent& event )
997{
998 EESCHEMA_SETTINGS::PANEL_SYMBOL_FIELDS_TABLE& cfg = m_parent->eeconfig()->m_FieldEditorPanel;
999
1000 // Build a pop menu:
1001 wxMenu menu;
1002
1003 menu.Append( 4204, _( "Include 'DNP' Symbols" ),
1004 _( "Show symbols marked 'DNP' in the table. This setting also controls whether or not 'DNP' "
1005 "symbols are included on export." ),
1006 wxITEM_CHECK );
1007 menu.Check( 4204, !m_dataModel->GetExcludeDNP() );
1008
1009 menu.Append( 4205, _( "Include 'Exclude from BOM' Symbols" ),
1010 _( "Show symbols marked 'Exclude from BOM' in the table. Symbols marked 'Exclude from BOM' "
1011 "are never included on export." ),
1012 wxITEM_CHECK );
1013 menu.Check( 4205, m_dataModel->GetIncludeExcludedFromBOM() );
1014
1015 menu.AppendSeparator();
1016
1017 menu.Append( 4206, _( "Highlight on Cross-probe" ),
1018 _( "Highlight corresponding item on canvas when it is selected in the table" ),
1019 wxITEM_CHECK );
1020 menu.Check( 4206, cfg.selection_mode == 0 );
1021
1022 menu.Append( 4207, _( "Select on Cross-probe" ),
1023 _( "Select corresponding item on canvas when it is selected in the table" ),
1024 wxITEM_CHECK );
1025 menu.Check( 4207, cfg.selection_mode == 1 );
1026
1027 // menu_id is the selected submenu id from the popup menu or wxID_NONE
1028 int menu_id = m_bMenu->GetPopupMenuSelectionFromUser( menu );
1029
1030 if( menu_id == 0 || menu_id == 4204 )
1031 {
1032 m_dataModel->SetExcludeDNP( !m_dataModel->GetExcludeDNP() );
1033 m_dataModel->RebuildRows();
1034 m_grid->ForceRefresh();
1035
1037 }
1038 else if( menu_id == 1 || menu_id == 4205 )
1039 {
1040 m_dataModel->SetIncludeExcludedFromBOM( !m_dataModel->GetIncludeExcludedFromBOM() );
1041 m_dataModel->RebuildRows();
1042 m_grid->ForceRefresh();
1043
1045 }
1046 else if( menu_id == 3 || menu_id == 4206 )
1047 {
1048 if( cfg.selection_mode != 0 )
1049 cfg.selection_mode = 0;
1050 else
1051 cfg.selection_mode = 2;
1052 }
1053 else if( menu_id == 4 || menu_id == 4207 )
1054 {
1055 if( cfg.selection_mode != 1 )
1056 cfg.selection_mode = 1;
1057 else
1058 cfg.selection_mode = 2;
1059 }
1060}
1061
1062
1063void DIALOG_SYMBOL_FIELDS_TABLE::OnColSort( wxGridEvent& aEvent )
1064{
1065 int sortCol = aEvent.GetCol();
1066 std::string key( m_dataModel->GetColFieldName( sortCol ).ToUTF8() );
1067 bool ascending;
1068
1069 // Don't sort by item number, it is generated by the sort
1070 if( m_dataModel->ColIsItemNumber( sortCol ) )
1071 {
1072 aEvent.Veto();
1073 return;
1074 }
1075
1076 // This is bonkers, but wxWidgets doesn't tell us ascending/descending in the event, and
1077 // if we ask it will give us pre-event info.
1078 if( m_grid->IsSortingBy( sortCol ) )
1079 {
1080 // same column; invert ascending
1081 ascending = !m_grid->IsSortOrderAscending();
1082 }
1083 else
1084 {
1085 // different column; start with ascending
1086 ascending = true;
1087 }
1088
1089 m_dataModel->SetSorting( sortCol, ascending );
1090 m_dataModel->RebuildRows();
1091 m_grid->ForceRefresh();
1092
1094}
1095
1096
1097void DIALOG_SYMBOL_FIELDS_TABLE::OnColMove( wxGridEvent& aEvent )
1098{
1099 int origPos = aEvent.GetCol();
1100
1101 // Save column widths since the setup function uses the saved config values
1102 EESCHEMA_SETTINGS* cfg = m_parent->eeconfig();
1103
1104 for( int i = 0; i < m_grid->GetNumberCols(); i++ )
1105 {
1106 if( m_grid->IsColShown( i ) )
1107 {
1108 std::string fieldName( m_dataModel->GetColFieldName( i ).ToUTF8() );
1109 cfg->m_FieldEditorPanel.field_widths[fieldName] = m_grid->GetColSize( i );
1110 }
1111 }
1112
1113 CallAfter(
1114 [origPos, this]()
1115 {
1116 int newPos = m_grid->GetColPos( origPos );
1117
1118#ifdef __WXMAC__
1119 if( newPos < origPos )
1120 newPos += 1;
1121#endif
1122
1123 m_dataModel->MoveColumn( origPos, newPos );
1124
1125 // "Unmove" the column since we've moved the column internally
1126 m_grid->ResetColPos();
1127
1128 // We need to reset all the column attr's to the correct column order
1130
1131 m_grid->ForceRefresh();
1132 } );
1133
1135}
1136
1137
1139{
1140 if( aShow )
1141 m_grid->ShowCol( aCol );
1142 else
1143 m_grid->HideCol( aCol );
1144
1145 m_dataModel->SetShowColumn( aCol, aShow );
1146
1148
1149 if( m_nbPages->GetSelection() == 1 )
1151 else
1152 m_grid->ForceRefresh();
1153
1154 OnModify();
1155}
1156
1157
1159{
1160 int row = aEvent.GetRow();
1161
1162 wxCHECK( row < m_viewControlsGrid->GetNumberRows(), /* void */ );
1163
1164 switch( aEvent.GetCol() )
1165 {
1166 case LABEL_COLUMN:
1167 {
1168 wxString label = m_viewControlsDataModel->GetValue( row, LABEL_COLUMN );
1169 wxString fieldName = m_viewControlsDataModel->GetCanonicalFieldName( row );
1170 int dataCol = m_dataModel->GetFieldNameCol( fieldName );
1171
1172 if( dataCol != -1 )
1173 {
1174 m_dataModel->SetColLabelValue( dataCol, label );
1175 m_grid->SetColLabelValue( dataCol, label );
1176
1177 if( m_nbPages->GetSelection() == 1 )
1179 else
1180 m_grid->ForceRefresh();
1181
1183 OnModify();
1184 }
1185
1186 break;
1187 }
1188
1189 case SHOW_FIELD_COLUMN:
1190 {
1191 wxString fieldName = m_viewControlsDataModel->GetCanonicalFieldName( row );
1192 bool value = m_viewControlsDataModel->GetValueAsBool( row, SHOW_FIELD_COLUMN );
1193 int dataCol = m_dataModel->GetFieldNameCol( fieldName );
1194
1195 if( dataCol != -1 )
1196 ShowHideColumn( dataCol, value );
1197
1198 break;
1199 }
1200
1201 case GROUP_BY_COLUMN:
1202 {
1203 wxString fieldName = m_viewControlsDataModel->GetCanonicalFieldName( row );
1204 bool value = m_viewControlsDataModel->GetValueAsBool( row, GROUP_BY_COLUMN );
1205 int dataCol = m_dataModel->GetFieldNameCol( fieldName );
1206
1207 if( m_dataModel->ColIsQuantity( dataCol ) && value )
1208 {
1209 DisplayError( this, _( "The Quantity column cannot be grouped by." ) );
1210
1211 value = false;
1212 m_viewControlsDataModel->SetValueAsBool( row, GROUP_BY_COLUMN, value );
1213 break;
1214 }
1215
1216 if( m_dataModel->ColIsItemNumber( dataCol ) && value )
1217 {
1218 DisplayError( this, _( "The Item Number column cannot be grouped by." ) );
1219
1220 value = false;
1221 m_viewControlsDataModel->SetValueAsBool( row, GROUP_BY_COLUMN, value );
1222 break;
1223 }
1224
1225 m_dataModel->SetGroupColumn( dataCol, value );
1226 m_dataModel->RebuildRows();
1227
1228 if( m_nbPages->GetSelection() == 1 )
1230 else
1231 m_grid->ForceRefresh();
1232
1234 OnModify();
1235 break;
1236 }
1237
1238 default:
1239 break;
1240 }
1241}
1242
1243
1245{
1246 m_grid->ForceRefresh();
1247}
1248
1249
1250void DIALOG_SYMBOL_FIELDS_TABLE::OnTableColSize( wxGridSizeEvent& aEvent )
1251{
1252 aEvent.Skip();
1253
1254 m_grid->ForceRefresh();
1255}
1256
1257
1259{
1260 m_dataModel->RebuildRows();
1261 m_grid->ForceRefresh();
1262}
1263
1264
1266{
1267 if( m_dataModel->IsExpanderColumn( event.GetCol() ) )
1268 {
1269 m_grid->ClearSelection();
1270
1271 m_dataModel->ExpandCollapseRow( event.GetRow() );
1272 m_grid->SetGridCursor( event.GetRow(), event.GetCol() );
1273 }
1274 else
1275 {
1276 event.Skip();
1277 }
1278}
1279
1280
1282{
1283 aEvent.Skip();
1284
1285 wxPoint pos = aEvent.GetPosition();
1286 int ux, uy;
1287 m_grid->CalcUnscrolledPosition( pos.x, pos.y, &ux, &uy );
1288 int row = m_grid->YToRow( uy );
1289 int col = m_grid->XToCol( ux );
1290
1291
1292 if( row == wxNOT_FOUND || col == wxNOT_FOUND )
1293 {
1294 m_grid->GetGridWindow()->UnsetToolTip();
1295 return;
1296 }
1297
1298 wxString rawValue = m_dataModel->GetValue( row, col );
1299
1300 if( rawValue.Contains( wxT( "${" ) ) )
1301 {
1302 m_grid->GetGridWindow()->SetToolTip( rawValue );
1303 }
1304 else
1305 {
1306 m_grid->GetGridWindow()->UnsetToolTip();
1307 }
1308}
1309
1310
1311void DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected( wxGridRangeSelectEvent& aEvent )
1312{
1313 EESCHEMA_SETTINGS::PANEL_SYMBOL_FIELDS_TABLE& cfg = m_parent->eeconfig()->m_FieldEditorPanel;
1314
1315 // Cross-probing should only work in Edit page
1316 if( m_nbPages->GetSelection() != 0 )
1317 return;
1318
1319 // Multi-select can grab the rows that are expanded child refs, and also the row
1320 // containing the list of all child refs. Make sure we add refs/symbols uniquely
1321 std::set<SCH_REFERENCE> refs;
1322 std::set<SCH_ITEM*> symbols;
1323
1324 // This handler handles selecting and deselecting
1325 if( aEvent.Selecting() )
1326 {
1327 for( int i = aEvent.GetTopRow(); i <= aEvent.GetBottomRow(); i++ )
1328 {
1329 for( const SCH_REFERENCE& ref : m_dataModel->GetRowReferences( i ) )
1330 refs.insert( ref );
1331 }
1332
1333 for( const SCH_REFERENCE& ref : refs )
1334 symbols.insert( ref.GetSymbol() );
1335 }
1336
1337 if( cfg.selection_mode == 0 )
1338 {
1339 SCH_EDITOR_CONTROL* editor = m_parent->GetToolManager()->GetTool<SCH_EDITOR_CONTROL>();
1340
1341 if( refs.size() > 0 )
1342 {
1343 // Use of full path based on UUID allows select of not yet annotated or duplicated
1344 // symbols
1345 wxString symbol_path = refs.begin()->GetFullPath();
1346
1347 // Focus only handles one item at this time
1348 editor->FindSymbolAndItem( &symbol_path, nullptr, true, HIGHLIGHT_SYMBOL, wxEmptyString );
1349 }
1350 else
1351 {
1352 m_parent->ClearFocus();
1353 }
1354 }
1355 else if( cfg.selection_mode == 1 )
1356 {
1357 SCH_SELECTION_TOOL* selTool = m_parent->GetToolManager()->GetTool<SCH_SELECTION_TOOL>();
1358 std::vector<SCH_ITEM*> items( symbols.begin(), symbols.end() );
1359
1360 if( refs.size() > 0 )
1361 selTool->SyncSelection( refs.begin()->GetSheetPath(), nullptr, items );
1362 else
1363 selTool->ClearSelection();
1364 }
1365}
1366
1367
1369{
1370 const wxString& showColLabel = m_viewControlsGrid->GetColLabelValue( SHOW_FIELD_COLUMN );
1371 const wxString& groupByColLabel = m_viewControlsGrid->GetColLabelValue( GROUP_BY_COLUMN );
1372 int showColWidth = KIUI::GetTextSize( showColLabel, m_viewControlsGrid ).x + COLUMN_MARGIN;
1373 int groupByColWidth = KIUI::GetTextSize( groupByColLabel, m_viewControlsGrid ).x + COLUMN_MARGIN;
1374 int remainingWidth = m_viewControlsGrid->GetSize().GetX() - showColWidth - groupByColWidth;
1375
1376 m_viewControlsGrid->SetColSize( showColWidth, SHOW_FIELD_COLUMN );
1377 m_viewControlsGrid->SetColSize( groupByColWidth, GROUP_BY_COLUMN );
1378
1379 if( m_viewControlsGrid->IsColShown( DISPLAY_NAME_COLUMN ) && m_viewControlsGrid->IsColShown( LABEL_COLUMN ) )
1380 {
1381 m_viewControlsGrid->SetColSize( DISPLAY_NAME_COLUMN, std::max( remainingWidth / 2, 60 ) );
1382 m_viewControlsGrid->SetColSize( LABEL_COLUMN, std::max( remainingWidth - ( remainingWidth / 2 ), 60 ) );
1383 }
1384 else if( m_viewControlsGrid->IsColShown( DISPLAY_NAME_COLUMN ) )
1385 {
1386 m_viewControlsGrid->SetColSize( DISPLAY_NAME_COLUMN, std::max( remainingWidth, 60 ) );
1387 }
1388 else if( m_viewControlsGrid->IsColShown( LABEL_COLUMN ) )
1389 {
1390 m_viewControlsGrid->SetColSize( LABEL_COLUMN, std::max( remainingWidth, 60 ) );
1391 }
1392
1393 event.Skip();
1394}
1395
1396
1398{
1400 {
1401 m_parent->SaveProject();
1402 ClearModify();
1403 }
1404}
1405
1406
1407void DIALOG_SYMBOL_FIELDS_TABLE::OnPageChanged( wxNotebookEvent& event )
1408{
1409 if( m_dataModel->GetColsCount() )
1411}
1412
1413
1415{
1418}
1419
1420
1422{
1423 bool saveIncludeExcudedFromBOM = m_dataModel->GetIncludeExcludedFromBOM();
1424
1425 m_dataModel->SetIncludeExcludedFromBOM( false );
1426 m_dataModel->RebuildRows();
1427
1428 m_textOutput->SetValue( m_dataModel->Export( GetCurrentBomFmtSettings() ) );
1429
1430 if( saveIncludeExcudedFromBOM )
1431 {
1432 m_dataModel->SetIncludeExcludedFromBOM( true );
1433 m_dataModel->RebuildRows();
1434 }
1435}
1436
1437
1439{
1440 BOM_FMT_PRESET current;
1441
1442 current.name = m_cbBomFmtPresets->GetStringSelection();
1443 current.fieldDelimiter = m_textFieldDelimiter->GetValue();
1444 current.stringDelimiter = m_textStringDelimiter->GetValue();
1445 current.refDelimiter = m_textRefDelimiter->GetValue();
1446 current.refRangeDelimiter = m_textRefRangeDelimiter->GetValue();
1447 current.keepTabs = m_checkKeepTabs->GetValue();
1448 current.keepLineBreaks = m_checkKeepLineBreaks->GetValue();
1449
1450 return current;
1451}
1452
1453
1455{
1456 m_nbPages->SetSelection( 0 );
1457}
1458
1459
1461{
1462 m_nbPages->SetSelection( 1 );
1463}
1464
1465
1467{
1468 // Build the absolute path of current output directory to preselect it in the file browser.
1469 wxString path = ExpandEnvVarSubstitutions( m_outputFileName->GetValue(), &Prj() );
1470 path = Prj().AbsolutePath( path );
1471
1472
1473 // Calculate the export filename
1474 wxFileName fn( Prj().AbsolutePath( m_parent->Schematic().GetFileName() ) );
1475 fn.SetExt( FILEEXT::CsvFileExtension );
1476
1477 wxFileDialog saveDlg( this, _( "Bill of Materials Output File" ), path, fn.GetFullName(),
1478 FILEEXT::CsvFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1479
1481
1482 if( saveDlg.ShowModal() == wxID_CANCEL )
1483 return;
1484
1485
1486 wxFileName file = wxFileName( saveDlg.GetPath() );
1487 wxString defaultPath = fn.GetPathWithSep();
1488
1489 if( IsOK( this, wxString::Format( _( "Do you want to use a path relative to\n'%s'?" ), defaultPath ) ) )
1490 {
1491 if( !file.MakeRelativeTo( defaultPath ) )
1492 {
1493 DisplayErrorMessage( this, _( "Cannot make path relative (target volume different from schematic "
1494 "file volume)!" ) );
1495 }
1496 }
1497
1498 m_outputFileName->SetValue( file.GetFullPath() );
1499}
1500
1501
1503{
1504 EESCHEMA_SETTINGS::PANEL_SYMBOL_FIELDS_TABLE& cfg = m_parent->eeconfig()->m_FieldEditorPanel;
1505
1506 if( cfg.sidebar_collapsed )
1507 {
1508 cfg.sidebar_collapsed = false;
1509 m_splitterMainWindow->SplitVertically( m_leftPanel, m_rightPanel, cfg.sash_pos );
1510 }
1511 else
1512 {
1513 cfg.sash_pos = m_splitterMainWindow->GetSashPosition();
1514
1515 cfg.sidebar_collapsed = true;
1517 }
1518
1520}
1521
1522
1523void DIALOG_SYMBOL_FIELDS_TABLE::OnExport( wxCommandEvent& aEvent )
1524{
1525 if( m_dataModel->IsEdited() )
1526 {
1527 if( OKOrCancelDialog( nullptr, _( "Unsaved data" ),
1528 _( "Changes have not yet been saved. Export unsaved data?" ), "",
1529 _( "OK" ), _( "Cancel" ) )
1530 == wxID_CANCEL )
1531 {
1532 return;
1533 }
1534 }
1535
1536 // Create output directory if it does not exist (also transform it in absolute form).
1537 // Bail if it fails.
1538
1539 std::function<bool( wxString* )> textResolver =
1540 [&]( wxString* token ) -> bool
1541 {
1542 SCHEMATIC& schematic = m_parent->Schematic();
1543
1544 // Handles m_board->GetTitleBlock() *and* m_board->GetProject()
1545 return schematic.ResolveTextVar( &schematic.CurrentSheet(), token, 0 );
1546 };
1547
1548 wxString path = m_outputFileName->GetValue();
1549
1550 if( path.IsEmpty() )
1551 {
1552 DisplayError( this, _( "No output file specified in Export tab." ) );
1553 return;
1554 }
1555
1556 path = ExpandTextVars( path, &textResolver );
1558
1559 wxFileName outputFile = wxFileName::FileName( path );
1560 wxString msg;
1561
1562 if( !EnsureFileDirectoryExists( &outputFile, Prj().AbsolutePath( m_parent->Schematic().GetFileName() ),
1564 {
1565 msg.Printf( _( "Could not open/create path '%s'." ), outputFile.GetPath() );
1566 DisplayError( this, msg );
1567 return;
1568 }
1569
1570 wxFFile out( outputFile.GetFullPath(), "wb" );
1571
1572 if( !out.IsOpened() )
1573 {
1574 msg.Printf( _( "Could not create BOM output '%s'." ), outputFile.GetFullPath() );
1575 DisplayError( this, msg );
1576 return;
1577 }
1578
1580
1581 if( !out.Write( m_textOutput->GetValue() ) )
1582 {
1583 msg.Printf( _( "Could not write BOM output '%s'." ), outputFile.GetFullPath() );
1584 DisplayError( this, msg );
1585 return;
1586 }
1587
1588 // close the file before we tell the user it's done with the info modal :workflow meme:
1589 out.Close();
1590 msg.Printf( _( "Wrote BOM output to '%s'" ), outputFile.GetFullPath() );
1591 DisplayInfoMessage( this, msg );
1592}
1593
1594
1595void DIALOG_SYMBOL_FIELDS_TABLE::OnCancel( wxCommandEvent& aEvent )
1596{
1597 if( m_job )
1598 EndModal( wxID_CANCEL );
1599 else
1600 Close();
1601}
1602
1603
1604void DIALOG_SYMBOL_FIELDS_TABLE::OnOk( wxCommandEvent& aEvent )
1605{
1607
1608 if( m_job )
1609 {
1610 m_job->SetConfiguredOutputPath( m_outputFileName->GetValue() );
1611
1613 m_job->m_bomFmtPresetName = m_currentBomFmtPreset->name;
1614 else
1615 m_job->m_bomFmtPresetName = wxEmptyString;
1616
1617 if( m_currentBomPreset )
1618 m_job->m_bomPresetName = m_currentBomPreset->name;
1619 else
1620 m_job->m_bomPresetName = wxEmptyString;
1621
1623 m_job->m_fieldDelimiter = fmtSettings.fieldDelimiter;
1624 m_job->m_stringDelimiter = fmtSettings.stringDelimiter;
1625 m_job->m_refDelimiter = fmtSettings.refDelimiter;
1626 m_job->m_refRangeDelimiter = fmtSettings.refRangeDelimiter;
1627 m_job->m_keepTabs = fmtSettings.keepTabs;
1628 m_job->m_keepLineBreaks = fmtSettings.keepLineBreaks;
1629
1630 BOM_PRESET presetFields = m_dataModel->GetBomSettings();
1631 m_job->m_sortAsc = presetFields.sortAsc;
1632 m_job->m_excludeDNP = presetFields.excludeDNP;
1633 m_job->m_filterString = presetFields.filterString;
1634 m_job->m_sortField = presetFields.sortField;
1635 m_job->m_groupSymbols = presetFields.groupSymbols;
1636
1637 m_job->m_fieldsOrdered.clear();
1638 m_job->m_fieldsLabels.clear();
1639 m_job->m_fieldsGroupBy.clear();
1640
1641 for( const BOM_FIELD& modelField : m_dataModel->GetFieldsOrdered() )
1642 {
1643 if( modelField.show )
1644 m_job->m_fieldsOrdered.emplace_back( modelField.name );
1645 else
1646 m_job->m_fieldsOrdered.emplace_back( wxT( "__" ) + modelField.name );
1647
1648 m_job->m_fieldsLabels.emplace_back( modelField.label );
1649
1650 if( modelField.groupBy )
1651 m_job->m_fieldsGroupBy.emplace_back( modelField.name );
1652 }
1653
1654 wxString selectedVariant = getSelectedVariant();
1655
1656 if( !selectedVariant.IsEmpty() )
1657 m_job->m_variantNames.push_back( selectedVariant );
1658
1659 EndModal( wxID_OK );
1660 }
1661 else
1662 {
1663 Close();
1664 }
1665}
1666
1667
1668void DIALOG_SYMBOL_FIELDS_TABLE::OnClose( wxCloseEvent& aEvent )
1669{
1670 if( m_job )
1671 {
1672 aEvent.Skip();
1673 return;
1674 }
1675
1676 m_grid->CommitPendingChanges( true );
1677
1678 if( m_dataModel->IsEdited() && aEvent.CanVeto() )
1679 {
1680 if( !HandleUnsavedChanges( this, _( "Save changes?" ),
1681 [&]() -> bool
1682 {
1683 return TransferDataFromWindow();
1684 } ) )
1685 {
1686 aEvent.Veto();
1687 return;
1688 }
1689 }
1690
1691 // Stop listening to schematic events
1692 m_parent->Schematic().RemoveListener( this );
1693 m_parent->ClearFocus();
1694
1695 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, wxID_ANY );
1696
1697 if( wxWindow* parent = GetParent() )
1698 wxQueueEvent( parent, evt );
1699}
1700
1701
1703{
1704 std::vector<BOM_PRESET> ret;
1705
1706 for( const std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1707 {
1708 if( !pair.second.readOnly )
1709 ret.emplace_back( pair.second );
1710 }
1711
1712 return ret;
1713}
1714
1715
1716void DIALOG_SYMBOL_FIELDS_TABLE::SetUserBomPresets( std::vector<BOM_PRESET>& aPresetList )
1717{
1718 // Reset to defaults
1720
1721 for( const BOM_PRESET& preset : aPresetList )
1722 {
1723 if( m_bomPresets.count( preset.name ) )
1724 continue;
1725
1726 m_bomPresets[preset.name] = preset;
1727
1728 m_bomPresetMRU.Add( preset.name );
1729 }
1730
1732}
1733
1734
1735void DIALOG_SYMBOL_FIELDS_TABLE::ApplyBomPreset( const wxString& aPresetName )
1736{
1737 updateBomPresetSelection( aPresetName );
1738
1739 wxCommandEvent dummy;
1741}
1742
1743
1745{
1746 if( m_bomPresets.count( aPreset.name ) )
1748 else
1749 m_currentBomPreset = nullptr;
1750
1751 if( m_currentBomPreset && !m_currentBomPreset->readOnly )
1753 else
1754 m_lastSelectedBomPreset = nullptr;
1755
1756 updateBomPresetSelection( aPreset.name );
1757 doApplyBomPreset( aPreset );
1758}
1759
1760
1762{
1763 m_bomPresets.clear();
1764 m_bomPresetMRU.clear();
1765
1766 // Load the read-only defaults
1767 for( const BOM_PRESET& preset : BOM_PRESET::BuiltInPresets() )
1768 {
1769 m_bomPresets[preset.name] = preset;
1770 m_bomPresets[preset.name].readOnly = true;
1771
1772 m_bomPresetMRU.Add( preset.name );
1773 }
1774}
1775
1776
1778{
1779 m_cbBomPresets->Clear();
1780
1781 int idx = 0;
1782 int default_idx = 0;
1783
1784 for( const auto& [presetName, preset] : m_bomPresets )
1785 {
1786 m_cbBomPresets->Append( wxGetTranslation( presetName ), (void*) &preset );
1787
1788 if( presetName == BOM_PRESET::DefaultEditing().name )
1789 default_idx = idx;
1790
1791 idx++;
1792 }
1793
1794 m_cbBomPresets->Append( wxT( "---" ) );
1795 m_cbBomPresets->Append( _( "Save preset..." ) );
1796 m_cbBomPresets->Append( _( "Delete preset..." ) );
1797
1798 // At least the built-in presets should always be present
1799 wxASSERT( !m_bomPresets.empty() );
1800
1801 m_cbBomPresets->SetSelection( default_idx );
1802 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( default_idx ) );
1803}
1804
1805
1807{
1808 BOM_PRESET current = m_dataModel->GetBomSettings();
1809
1810 auto it = std::find_if( m_bomPresets.begin(), m_bomPresets.end(),
1811 [&]( const std::pair<const wxString, BOM_PRESET>& aPair )
1812 {
1813 const BOM_PRESET& preset = aPair.second;
1814
1815 // Check the simple settings first
1816 if( !( preset.sortAsc == current.sortAsc
1817 && preset.filterString == current.filterString
1818 && preset.groupSymbols == current.groupSymbols
1819 && preset.excludeDNP == current.excludeDNP
1820 && preset.includeExcludedFromBOM == current.includeExcludedFromBOM ) )
1821 {
1822 return false;
1823 }
1824
1825 // We should compare preset.name and current.name. Unfortunately current.name is
1826 // empty because m_dataModel->GetBomSettings() does not store the .name member.
1827 // So use sortField member as a (not very efficient) auxiliary filter.
1828 // As a further complication, sortField can be translated in m_bomPresets list, so
1829 // current.sortField needs to be translated.
1830 // Probably this not efficient and error prone test should be removed (JPC).
1831 if( preset.sortField != wxGetTranslation( current.sortField ) )
1832 return false;
1833
1834 // Only compare shown or grouped fields
1835 std::vector<BOM_FIELD> A, B;
1836
1837 for( const BOM_FIELD& field : preset.fieldsOrdered )
1838 {
1839 if( field.show || field.groupBy )
1840 A.emplace_back( field );
1841 }
1842
1843 for( const BOM_FIELD& field : current.fieldsOrdered )
1844 {
1845 if( field.show || field.groupBy )
1846 B.emplace_back( field );
1847 }
1848
1849 return A == B;
1850 } );
1851
1852 if( it != m_bomPresets.end() )
1853 {
1854 // Select the right m_cbBomPresets item.
1855 // but these items are translated if they are predefined items.
1856 bool do_translate = it->second.readOnly;
1857 wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
1858 m_cbBomPresets->SetStringSelection( text );
1859 }
1860 else
1861 {
1862 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 ); // separator
1863 }
1864
1865 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( m_cbBomPresets->GetSelection() ) );
1866}
1867
1868
1870{
1871 // Look at m_userBomPresets to know if aName is a read only preset, or a user preset.
1872 // Read-only presets have translated names in UI, so we have to use a translated name
1873 // in UI selection. But for a user preset name we search for the untranslated aName.
1874 wxString ui_label = aName;
1875
1876 for( const auto& [presetName, preset] : m_bomPresets )
1877 {
1878 if( presetName == aName )
1879 {
1880 if( preset.readOnly == true )
1881 ui_label = wxGetTranslation( aName );
1882
1883 break;
1884 }
1885 }
1886
1887 int idx = m_cbBomPresets->FindString( ui_label );
1888
1889 if( idx >= 0 && m_cbBomPresets->GetSelection() != idx )
1890 {
1891 m_cbBomPresets->SetSelection( idx );
1892 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( idx ) );
1893 }
1894 else if( idx < 0 )
1895 {
1896 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 ); // separator
1897 }
1898}
1899
1900
1902{
1903 int count = m_cbBomPresets->GetCount();
1904 int index = m_cbBomPresets->GetSelection();
1905
1906 auto resetSelection =
1907 [&]()
1908 {
1909 if( m_currentBomPreset )
1910 m_cbBomPresets->SetStringSelection( m_currentBomPreset->name );
1911 else
1912 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 );
1913 };
1914
1915 if( index == count - 3 )
1916 {
1917 // Separator: reject the selection
1918 resetSelection();
1919 return;
1920 }
1921 else if( index == count - 2 )
1922 {
1923 // Save current state to new preset
1924 wxString name;
1925
1928
1929 wxTextEntryDialog dlg( this, _( "BOM preset name:" ), _( "Save BOM Preset" ), name );
1930
1931 if( dlg.ShowModal() != wxID_OK )
1932 {
1933 resetSelection();
1934 return;
1935 }
1936
1937 name = dlg.GetValue();
1938 bool exists = m_bomPresets.count( name );
1939
1940 if( !exists )
1941 {
1942 m_bomPresets[name] = m_dataModel->GetBomSettings();
1943 m_bomPresets[name].readOnly = false;
1944 m_bomPresets[name].name = name;
1945 }
1946
1947 BOM_PRESET* preset = &m_bomPresets[name];
1948
1949 if( !exists )
1950 {
1951 index = m_cbBomPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
1952 }
1953 else if( preset->readOnly )
1954 {
1955 wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
1956 _( "Error" ), wxOK | wxICON_ERROR, this );
1957 resetSelection();
1958 return;
1959 }
1960 else
1961 {
1962 // Ask the user if they want to overwrite the existing preset
1963 if( !IsOK( this, _( "Overwrite existing preset?" ) ) )
1964 {
1965 resetSelection();
1966 return;
1967 }
1968
1969 *preset = m_dataModel->GetBomSettings();
1970 preset->name = name;
1971
1972 index = m_cbBomPresets->FindString( name );
1973
1974 if( m_bomPresetMRU.Index( name ) != wxNOT_FOUND )
1975 m_bomPresetMRU.Remove( name );
1976 }
1977
1978 m_currentBomPreset = preset;
1979 m_cbBomPresets->SetSelection( index );
1980 m_bomPresetMRU.Insert( name, 0 );
1981
1982 return;
1983 }
1984 else if( index == count - 1 )
1985 {
1986 // Delete a preset
1987 wxArrayString headers;
1988 std::vector<wxArrayString> items;
1989
1990 headers.Add( _( "Presets" ) );
1991
1992 for( const auto& [name, preset] : m_bomPresets )
1993 {
1994 if( !preset.readOnly )
1995 {
1996 wxArrayString item;
1997 item.Add( name );
1998 items.emplace_back( item );
1999 }
2000 }
2001
2002 EDA_LIST_DIALOG dlg( this, _( "Delete Preset" ), headers, items );
2003 dlg.SetListLabel( _( "Select preset:" ) );
2004
2005 if( dlg.ShowModal() == wxID_OK )
2006 {
2007 wxString presetName = dlg.GetTextSelection();
2008 int idx = m_cbBomPresets->FindString( presetName );
2009
2010 if( idx != wxNOT_FOUND )
2011 {
2012 m_bomPresets.erase( presetName );
2013
2014 m_cbBomPresets->Delete( idx );
2015 m_currentBomPreset = nullptr;
2016 }
2017
2018 if( m_bomPresetMRU.Index( presetName ) != wxNOT_FOUND )
2019 m_bomPresetMRU.Remove( presetName );
2020 }
2021
2022 resetSelection();
2023 return;
2024 }
2025
2026 BOM_PRESET* preset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( index ) );
2027 m_currentBomPreset = preset;
2028
2029 m_lastSelectedBomPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
2030
2031 if( preset )
2032 {
2033 doApplyBomPreset( *preset );
2035 m_currentBomPreset = preset;
2036
2037 if( !m_currentBomPreset->name.IsEmpty() )
2038 {
2039 if( m_bomPresetMRU.Index( preset->name ) != wxNOT_FOUND )
2040 m_bomPresetMRU.Remove( preset->name );
2041
2042 m_bomPresetMRU.Insert( preset->name, 0 );
2043 }
2044 }
2045}
2046
2047
2049{
2050 // Disable rebuilds while we're applying the preset otherwise we'll be
2051 // rebuilding the model constantly while firing off wx events
2052 m_dataModel->DisableRebuilds();
2053
2054 // Basically, we apply the BOM preset to the data model and then
2055 // update our UI to reflect resulting the data model state, not the preset.
2056 m_dataModel->ApplyBomPreset( aPreset, m_parent->Schematic().GetCurrentVariant() );
2057
2058 // BOM Presets can add, but not remove, columns, so make sure the view controls
2059 // grid has all of them before starting
2060 for( int i = 0; i < m_dataModel->GetColsCount(); i++ )
2061 {
2062 const wxString& fieldName( m_dataModel->GetColFieldName( i ) );
2063 bool found = false;
2064
2065 for( int j = 0; j < m_viewControlsDataModel->GetNumberRows(); j++ )
2066 {
2067 if( m_viewControlsDataModel->GetCanonicalFieldName( j ) == fieldName )
2068 {
2069 found = true;
2070 break;
2071 }
2072 }
2073
2074 // Properties like label, etc. will be added in the next loop
2075 if( !found )
2076 AddField( fieldName, GetGeneratedFieldDisplayName( fieldName ), false, false );
2077 }
2078
2079 // Sync all fields
2080 for( int i = 0; i < m_viewControlsDataModel->GetNumberRows(); i++ )
2081 {
2082 const wxString& fieldName( m_viewControlsDataModel->GetCanonicalFieldName( i ) );
2083 int col = m_dataModel->GetFieldNameCol( fieldName );
2084
2085 if( col == -1 )
2086 {
2087 wxASSERT_MSG( true, "Fields control has a field not found in the data model." );
2088 continue;
2089 }
2090
2091 EESCHEMA_SETTINGS* cfg = m_parent->eeconfig();
2092 std::string fieldNameStr( fieldName.ToUTF8() );
2093
2094 // Set column labels
2095 const wxString& label = m_dataModel->GetColLabelValue( col );
2096 m_viewControlsDataModel->SetValue( i, LABEL_COLUMN, label );
2097 m_grid->SetColLabelValue( col, label );
2098
2099 if( cfg->m_FieldEditorPanel.field_widths.count( fieldNameStr ) )
2100 m_grid->SetColSize( col, cfg->m_FieldEditorPanel.field_widths.at( fieldNameStr ) );
2101
2102 // Set shown columns
2103 bool show = m_dataModel->GetShowColumn( col );
2104 m_viewControlsDataModel->SetValueAsBool( i, SHOW_FIELD_COLUMN, show );
2105
2106 if( show )
2107 m_grid->ShowCol( col );
2108 else
2109 m_grid->HideCol( col );
2110
2111 // Set grouped columns
2112 bool groupBy = m_dataModel->GetGroupColumn( col );
2113 m_viewControlsDataModel->SetValueAsBool( i, GROUP_BY_COLUMN, groupBy );
2114 }
2115
2116 m_grid->SetSortingColumn( m_dataModel->GetSortCol(), m_dataModel->GetSortAsc() );
2117 m_groupSymbolsBox->SetValue( m_dataModel->GetGroupingEnabled() );
2118 m_filter->ChangeValue( m_dataModel->GetFilter() );
2119
2121
2122 // This will rebuild all rows and columns in the model such that the order
2123 // and labels are right, then we refresh the shown grid data to match
2124 m_dataModel->EnableRebuilds();
2125 m_dataModel->RebuildRows();
2126
2127 if( m_nbPages->GetSelection() == 1 )
2129 else
2130 m_grid->ForceRefresh();
2131}
2132
2133
2134std::vector<BOM_FMT_PRESET> DIALOG_SYMBOL_FIELDS_TABLE::GetUserBomFmtPresets() const
2135{
2136 std::vector<BOM_FMT_PRESET> ret;
2137
2138 for( const auto& [name, preset] : m_bomFmtPresets )
2139 {
2140 if( !preset.readOnly )
2141 ret.emplace_back( preset );
2142 }
2143
2144 return ret;
2145}
2146
2147
2148void DIALOG_SYMBOL_FIELDS_TABLE::SetUserBomFmtPresets( std::vector<BOM_FMT_PRESET>& aPresetList )
2149{
2150 // Reset to defaults
2152
2153 for( const BOM_FMT_PRESET& preset : aPresetList )
2154 {
2155 if( m_bomFmtPresets.count( preset.name ) )
2156 continue;
2157
2158 m_bomFmtPresets[preset.name] = preset;
2159
2160 m_bomFmtPresetMRU.Add( preset.name );
2161 }
2162
2164}
2165
2166
2167void DIALOG_SYMBOL_FIELDS_TABLE::ApplyBomFmtPreset( const wxString& aPresetName )
2168{
2169 updateBomFmtPresetSelection( aPresetName );
2170
2171 wxCommandEvent dummy;
2173}
2174
2175
2177{
2178 m_currentBomFmtPreset = nullptr;
2180
2181 if( m_bomFmtPresets.count( aPreset.name ) )
2183
2186
2188 doApplyBomFmtPreset( aPreset );
2189}
2190
2191
2193{
2194 m_bomFmtPresets.clear();
2195 m_bomFmtPresetMRU.clear();
2196
2197 // Load the read-only defaults
2198 for( const BOM_FMT_PRESET& preset : BOM_FMT_PRESET::BuiltInPresets() )
2199 {
2200 m_bomFmtPresets[preset.name] = preset;
2201 m_bomFmtPresets[preset.name].readOnly = true;
2202
2203 m_bomFmtPresetMRU.Add( preset.name );
2204 }
2205}
2206
2207
2209{
2210 m_cbBomFmtPresets->Clear();
2211
2212 int idx = 0;
2213 int default_idx = 0;
2214
2215 for( const auto& [presetName, preset] : m_bomFmtPresets )
2216 {
2217 m_cbBomFmtPresets->Append( wxGetTranslation( presetName ), (void*) &preset );
2218
2219 if( presetName == BOM_FMT_PRESET::CSV().name )
2220 default_idx = idx;
2221
2222 idx++;
2223 }
2224
2225 m_cbBomFmtPresets->Append( wxT( "---" ) );
2226 m_cbBomFmtPresets->Append( _( "Save preset..." ) );
2227 m_cbBomFmtPresets->Append( _( "Delete preset..." ) );
2228
2229 // At least the built-in presets should always be present
2230 wxASSERT( !m_bomFmtPresets.empty() );
2231
2232 m_cbBomFmtPresets->SetSelection( default_idx );
2233 m_currentBomFmtPreset = static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( default_idx ) );
2234}
2235
2236
2238{
2240
2241 auto it = std::find_if( m_bomFmtPresets.begin(), m_bomFmtPresets.end(),
2242 [&]( const std::pair<const wxString, BOM_FMT_PRESET>& aPair )
2243 {
2244 return ( aPair.second.fieldDelimiter == current.fieldDelimiter
2245 && aPair.second.stringDelimiter == current.stringDelimiter
2246 && aPair.second.refDelimiter == current.refDelimiter
2247 && aPair.second.refRangeDelimiter == current.refRangeDelimiter
2248 && aPair.second.keepTabs == current.keepTabs
2249 && aPair.second.keepLineBreaks == current.keepLineBreaks );
2250 } );
2251
2252 if( it != m_bomFmtPresets.end() )
2253 {
2254 // Select the right m_cbBomFmtPresets item.
2255 // but these items are translated if they are predefined items.
2256 bool do_translate = it->second.readOnly;
2257 wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
2258
2259 m_cbBomFmtPresets->SetStringSelection( text );
2260 }
2261 else
2262 {
2263 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 ); // separator
2264 }
2265
2266 int idx = m_cbBomFmtPresets->GetSelection();
2267 m_currentBomFmtPreset = static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( idx ) );
2268}
2269
2270
2272{
2273 // look at m_userBomFmtPresets to know if aName is a read only preset, or a user preset.
2274 // Read only presets have translated names in UI, so we have to use a translated name in UI selection.
2275 // But for a user preset name we should search for aName (not translated)
2276 wxString ui_label = aName;
2277
2278 for( const auto& [presetName, preset] : m_bomFmtPresets )
2279 {
2280 if( presetName == aName )
2281 {
2282 if( preset.readOnly )
2283 ui_label = wxGetTranslation( aName );
2284
2285 break;
2286 }
2287 }
2288
2289 int idx = m_cbBomFmtPresets->FindString( ui_label );
2290
2291 if( idx >= 0 && m_cbBomFmtPresets->GetSelection() != idx )
2292 {
2293 m_cbBomFmtPresets->SetSelection( idx );
2294 m_currentBomFmtPreset = static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( idx ) );
2295 }
2296 else if( idx < 0 )
2297 {
2298 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 ); // separator
2299 }
2300}
2301
2302
2304{
2305 int count = m_cbBomFmtPresets->GetCount();
2306 int index = m_cbBomFmtPresets->GetSelection();
2307
2308 auto resetSelection =
2309 [&]()
2310 {
2312 m_cbBomFmtPresets->SetStringSelection( m_currentBomFmtPreset->name );
2313 else
2314 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 );
2315 };
2316
2317 if( index == count - 3 )
2318 {
2319 // Separator: reject the selection
2320 resetSelection();
2321 return;
2322 }
2323 else if( index == count - 2 )
2324 {
2325 // Save current state to new preset
2326 wxString name;
2327
2330
2331 wxTextEntryDialog dlg( this, _( "BOM preset name:" ), _( "Save BOM Preset" ), name );
2332
2333 if( dlg.ShowModal() != wxID_OK )
2334 {
2335 resetSelection();
2336 return;
2337 }
2338
2339 name = dlg.GetValue();
2340 bool exists = m_bomFmtPresets.count( name );
2341
2342 if( !exists )
2343 {
2345 m_bomFmtPresets[name].readOnly = false;
2346 m_bomFmtPresets[name].name = name;
2347 }
2348
2350
2351 if( !exists )
2352 {
2353 index = m_cbBomFmtPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
2354 }
2355 else if( preset->readOnly )
2356 {
2357 wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
2358 _( "Error" ), wxOK | wxICON_ERROR, this );
2359 resetSelection();
2360 return;
2361 }
2362 else
2363 {
2364 // Ask the user if they want to overwrite the existing preset
2365 if( !IsOK( this, _( "Overwrite existing preset?" ) ) )
2366 {
2367 resetSelection();
2368 return;
2369 }
2370
2371 *preset = GetCurrentBomFmtSettings();
2372 preset->name = name;
2373
2374 index = m_cbBomFmtPresets->FindString( name );
2375
2376 if( m_bomFmtPresetMRU.Index( name ) != wxNOT_FOUND )
2377 m_bomFmtPresetMRU.Remove( name );
2378 }
2379
2380 m_currentBomFmtPreset = preset;
2381 m_cbBomFmtPresets->SetSelection( index );
2382 m_bomFmtPresetMRU.Insert( name, 0 );
2383
2384 return;
2385 }
2386 else if( index == count - 1 )
2387 {
2388 // Delete a preset
2389 wxArrayString headers;
2390 std::vector<wxArrayString> items;
2391
2392 headers.Add( _( "Presets" ) );
2393
2394 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
2395 {
2396 if( !pair.second.readOnly )
2397 {
2398 wxArrayString item;
2399 item.Add( pair.first );
2400 items.emplace_back( item );
2401 }
2402 }
2403
2404 EDA_LIST_DIALOG dlg( this, _( "Delete Preset" ), headers, items );
2405 dlg.SetListLabel( _( "Select preset:" ) );
2406
2407 if( dlg.ShowModal() == wxID_OK )
2408 {
2409 wxString presetName = dlg.GetTextSelection();
2410 int idx = m_cbBomFmtPresets->FindString( presetName );
2411
2412 if( idx != wxNOT_FOUND )
2413 {
2414 m_bomFmtPresets.erase( presetName );
2415
2416 m_cbBomFmtPresets->Delete( idx );
2417 m_currentBomFmtPreset = nullptr;
2418 }
2419
2420 if( m_bomFmtPresetMRU.Index( presetName ) != wxNOT_FOUND )
2421 m_bomFmtPresetMRU.Remove( presetName );
2422 }
2423
2424 resetSelection();
2425 return;
2426 }
2427
2428 auto* preset = static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( index ) );
2429 m_currentBomFmtPreset = preset;
2430
2431 m_lastSelectedBomFmtPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
2432
2433 if( preset )
2434 {
2435 doApplyBomFmtPreset( *preset );
2437 m_currentBomFmtPreset = preset;
2438
2439 if( !m_currentBomFmtPreset->name.IsEmpty() )
2440 {
2441 if( m_bomFmtPresetMRU.Index( preset->name ) != wxNOT_FOUND )
2442 m_bomFmtPresetMRU.Remove( preset->name );
2443
2444 m_bomFmtPresetMRU.Insert( preset->name, 0 );
2445 }
2446 }
2447}
2448
2449
2451{
2452 m_textFieldDelimiter->ChangeValue( aPreset.fieldDelimiter );
2453 m_textStringDelimiter->ChangeValue( aPreset.stringDelimiter );
2454 m_textRefDelimiter->ChangeValue( aPreset.refDelimiter );
2455 m_textRefRangeDelimiter->ChangeValue( aPreset.refRangeDelimiter );
2456 m_checkKeepTabs->SetValue( aPreset.keepTabs );
2457 m_checkKeepLineBreaks->SetValue( aPreset.keepLineBreaks );
2458
2459 // Refresh the preview if that's the current page
2460 if( m_nbPages->GetSelection() == 1 )
2462}
2463
2464
2466{
2467 bool modified = false;
2468
2469 // Save our BOM presets
2470 std::vector<BOM_PRESET> presets;
2471
2472 for( const auto& [name, preset] : m_bomPresets )
2473 {
2474 if( !preset.readOnly )
2475 presets.emplace_back( preset );
2476 }
2477
2478 if( m_schSettings.m_BomPresets != presets )
2479 {
2480 modified = true;
2481 m_schSettings.m_BomPresets = presets;
2482 }
2483
2484 if( m_schSettings.m_BomSettings != m_dataModel->GetBomSettings() && !m_job )
2485 {
2486 modified = true;
2487 m_schSettings.m_BomSettings = m_dataModel->GetBomSettings();
2488 }
2489
2490 // Save our BOM Format presets
2491 std::vector<BOM_FMT_PRESET> fmts;
2492
2493 for( const auto& [name, preset] : m_bomFmtPresets )
2494 {
2495 if( !preset.readOnly )
2496 fmts.emplace_back( preset );
2497 }
2498
2499 if( m_schSettings.m_BomFmtPresets != fmts )
2500 {
2501 modified = true;
2502 m_schSettings.m_BomFmtPresets = fmts;
2503 }
2504
2505 if( m_schSettings.m_BomFmtSettings != GetCurrentBomFmtSettings() && !m_job )
2506 {
2507 modified = true;
2508 m_schSettings.m_BomFmtSettings = GetCurrentBomFmtSettings();
2509 }
2510
2511 if( modified )
2512 m_parent->OnModify();
2513}
2514
2515
2516void DIALOG_SYMBOL_FIELDS_TABLE::OnSchItemsAdded( SCHEMATIC& aSch, std::vector<SCH_ITEM*>& aSchItem )
2517{
2518 std::set<wxString> savedSelection = SaveGridSelection();
2519
2520 SCH_REFERENCE_LIST allRefs;
2521 m_parent->Schematic().Hierarchy().GetSymbols( allRefs, SYMBOL_FILTER_ALL );
2522
2523 for( SCH_ITEM* item : aSchItem )
2524 {
2525 if( item->Type() == SCH_SYMBOL_T )
2526 {
2527 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2528
2529 // Don't add power symbols
2530 if( !symbol->IsMissingLibSymbol() && symbol->IsPower() )
2531 continue;
2532
2533 // Add all fields again in case this symbol has a new one
2534 for( SCH_FIELD& field : symbol->GetFields() )
2535 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2536
2537 m_dataModel->AddReferences( getSymbolReferences( symbol, allRefs ) );
2538 }
2539 else if( item->Type() == SCH_SHEET_T )
2540 {
2541 std::set<SCH_SYMBOL*> symbols;
2542 SCH_REFERENCE_LIST refs = getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) );
2543
2544 for( SCH_REFERENCE& ref : refs )
2545 symbols.insert( ref.GetSymbol() );
2546
2547 for( SCH_SYMBOL* symbol : symbols )
2548 {
2549 // Add all fields again in case this symbol has a new one
2550 for( SCH_FIELD& field : symbol->GetFields() )
2551 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2552 }
2553
2554 m_dataModel->AddReferences( refs );
2555 }
2556 }
2557
2559 m_dataModel->RebuildRows();
2560 RestoreGridSelection( savedSelection );
2562}
2563
2564
2565void DIALOG_SYMBOL_FIELDS_TABLE::OnSchItemsRemoved( SCHEMATIC& aSch, std::vector<SCH_ITEM*>& aSchItem )
2566{
2567 std::set<wxString> savedSelection = SaveGridSelection();
2568
2569 for( SCH_ITEM* item : aSchItem )
2570 {
2571 if( item->Type() == SCH_SYMBOL_T )
2572 m_dataModel->RemoveSymbol( *static_cast<SCH_SYMBOL*>( item ) );
2573 else if( item->Type() == SCH_SHEET_T )
2574 m_dataModel->RemoveReferences( getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) ) );
2575 }
2576
2578 m_dataModel->RebuildRows();
2579 RestoreGridSelection( savedSelection );
2581}
2582
2583
2584void DIALOG_SYMBOL_FIELDS_TABLE::OnSchItemsChanged( SCHEMATIC& aSch, std::vector<SCH_ITEM*>& aSchItem )
2585{
2586 std::set<wxString> savedSelection = SaveGridSelection();
2587
2588 SCH_REFERENCE_LIST allRefs;
2589 m_parent->Schematic().Hierarchy().GetSymbols( allRefs, SYMBOL_FILTER_ALL );
2590
2591 for( SCH_ITEM* item : aSchItem )
2592 {
2593 if( item->Type() == SCH_SYMBOL_T )
2594 {
2595 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2596
2597 // Don't add power symbols
2598 if( !symbol->IsMissingLibSymbol() && symbol->IsPower() )
2599 continue;
2600
2601 // Add all fields again in case this symbol has a new one
2602 for( SCH_FIELD& field : symbol->GetFields() )
2603 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2604
2605 m_dataModel->UpdateReferences( getSymbolReferences( symbol, allRefs ),
2606 m_parent->Schematic().GetCurrentVariant() );
2607 }
2608 else if( item->Type() == SCH_SHEET_T )
2609 {
2610 std::set<SCH_SYMBOL*> symbols;
2611 SCH_REFERENCE_LIST refs = getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) );
2612
2613 for( SCH_REFERENCE& ref : refs )
2614 symbols.insert( ref.GetSymbol() );
2615
2616 for( SCH_SYMBOL* symbol : symbols )
2617 {
2618 // Add all fields again in case this symbol has a new one
2619 for( SCH_FIELD& field : symbol->GetFields() )
2620 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2621 }
2622
2623 m_dataModel->UpdateReferences( refs, m_parent->Schematic().GetCurrentVariant() );
2624 }
2625 }
2626
2628 m_dataModel->RebuildRows();
2629 RestoreGridSelection( savedSelection );
2631}
2632
2633
2635{
2636 m_dataModel->SetPath( aSch.CurrentSheet() );
2637
2638 if( m_dataModel->GetScope() != FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_ALL )
2639 {
2640 std::set<wxString> savedSelection = SaveGridSelection();
2641
2643 m_dataModel->RebuildRows();
2644 RestoreGridSelection( savedSelection );
2646 }
2647}
2648
2649
2651{
2652 m_grid->Connect( wxEVT_GRID_RANGE_SELECTED,
2653 wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected ),
2654 nullptr, this );
2655}
2656
2657
2659{
2660 m_grid->Disconnect( wxEVT_GRID_RANGE_SELECTED,
2661 wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected ),
2662 nullptr, this );
2663}
2664
2665
2667{
2668 std::set<wxString> selectedFullPaths;
2669
2670 wxGridCellCoordsArray topLeft = m_grid->GetSelectionBlockTopLeft();
2671 wxGridCellCoordsArray bottomRight = m_grid->GetSelectionBlockBottomRight();
2672
2673 for( size_t i = 0; i < topLeft.size(); ++i )
2674 {
2675 for( int row = topLeft[i].GetRow(); row <= bottomRight[i].GetRow(); ++row )
2676 {
2677 for( const SCH_REFERENCE& ref : m_dataModel->GetRowReferences( row ) )
2678 selectedFullPaths.insert( ref.GetFullPath() );
2679 }
2680 }
2681
2682 wxArrayInt selectedRows = m_grid->GetSelectedRows();
2683
2684 for( int row : selectedRows )
2685 {
2686 for( const SCH_REFERENCE& ref : m_dataModel->GetRowReferences( row ) )
2687 selectedFullPaths.insert( ref.GetFullPath() );
2688 }
2689
2690 int cursorRow = m_grid->GetGridCursorRow();
2691
2692 if( cursorRow >= 0 && selectedFullPaths.empty() )
2693 {
2694 for( const SCH_REFERENCE& ref : m_dataModel->GetRowReferences( cursorRow ) )
2695 selectedFullPaths.insert( ref.GetFullPath() );
2696 }
2697
2698 return selectedFullPaths;
2699}
2700
2701
2702void DIALOG_SYMBOL_FIELDS_TABLE::RestoreGridSelection( const std::set<wxString>& aFullPaths )
2703{
2704 if( aFullPaths.empty() )
2705 return;
2706
2707 m_grid->ClearSelection();
2708
2709 bool firstSelection = true;
2710
2711 for( int row = 0; row < m_dataModel->GetNumberRows(); ++row )
2712 {
2713 std::vector<SCH_REFERENCE> refs = m_dataModel->GetRowReferences( row );
2714
2715 for( const SCH_REFERENCE& ref : refs )
2716 {
2717 if( aFullPaths.count( ref.GetFullPath() ) )
2718 {
2719 m_grid->SelectRow( row, true );
2720
2721 if( firstSelection )
2722 {
2723 m_grid->SetGridCursor( row, m_grid->GetGridCursorCol() );
2724 firstSelection = false;
2725 }
2726
2727 break;
2728 }
2729 }
2730 }
2731}
2732
2733
2735 SCH_REFERENCE_LIST& aCachedRefs )
2736{
2737 SCH_REFERENCE_LIST symbolRefs;
2738
2739 for( size_t i = 0; i < aCachedRefs.GetCount(); i++ )
2740 {
2741 SCH_REFERENCE& ref = aCachedRefs[i];
2742
2743 if( ref.GetSymbol() == aSymbol )
2744 {
2745 ref.Split(); // Figures out if we are annotated or not
2746 symbolRefs.AddItem( ref );
2747 }
2748 }
2749
2750 return symbolRefs;
2751}
2752
2753
2755{
2756 SCH_SHEET_LIST allSheets = m_parent->Schematic().Hierarchy();
2757 SCH_REFERENCE_LIST sheetRefs;
2758
2759 // We need to operate on all instances of the sheet
2760 for( const SCH_SHEET_INSTANCE& instance : aSheet.GetInstances() )
2761 {
2762 // For every sheet instance we need to get the current schematic sheet
2763 // instance that matches that particular sheet path from the root
2764 for( SCH_SHEET_PATH& basePath : allSheets )
2765 {
2766 if( basePath.Path() == instance.m_Path )
2767 {
2768 SCH_SHEET_PATH sheetPath = basePath;
2769 sheetPath.push_back( &aSheet );
2770
2771 // Create a list of all sheets in this path, starting with the path
2772 // of the sheet that we just deleted, then all of its subsheets
2773 SCH_SHEET_LIST subSheets;
2774 subSheets.push_back( sheetPath );
2775 allSheets.GetSheetsWithinPath( subSheets, sheetPath );
2776
2777 subSheets.GetSymbolsWithinPath( sheetRefs, sheetPath, SYMBOL_FILTER_NON_POWER, false );
2778 break;
2779 }
2780 }
2781 }
2782
2783 for( SCH_REFERENCE& ref : sheetRefs )
2784 ref.Split();
2785
2786 return sheetRefs;
2787}
2788
2789
2790void DIALOG_SYMBOL_FIELDS_TABLE::onAddVariant( wxCommandEvent& aEvent )
2791{
2792 if( !m_parent->ShowAddVariantDialog() )
2793 return;
2794
2795 wxArrayString ctrlContents;
2796 ctrlContents.Add( GetDefaultVariantName() );
2797
2798 for( const wxString& variant : m_parent->Schematic().GetVariantNames() )
2799 ctrlContents.Add( variant );
2800
2801 ctrlContents.Sort( SortVariantNames );
2802 m_variantListBox->Set( ctrlContents );
2803
2804 wxString currentVariant = m_parent->Schematic().GetCurrentVariant();
2805 int newSelection = m_variantListBox->FindString(
2806 currentVariant.IsEmpty() ? GetDefaultVariantName() : currentVariant );
2807
2808 if( newSelection != wxNOT_FOUND )
2809 m_variantListBox->SetSelection( newSelection );
2810
2812}
2813
2814
2816{
2817 int selection = m_variantListBox->GetSelection();
2818
2819 // An empty or default selection cannot be deleted.
2820 if( ( selection == wxNOT_FOUND ) || ( selection == 0 ) )
2821 {
2822 m_parent->GetInfoBar()->ShowMessageFor( _( "Cannot delete the default variant." ),
2823 10000, wxICON_ERROR );
2824 return;
2825 }
2826
2827 wxString variantName = m_variantListBox->GetString( selection );
2828 m_variantListBox->Delete( selection );
2829 m_parent->Schematic().DeleteVariant( variantName );
2830 m_parent->OnModify();
2831
2832 int newSelection = std::max( 0, selection - 1 );
2833 m_variantListBox->SetSelection( newSelection );
2834
2835 wxString selectedVariant = getSelectedVariant();
2836 m_parent->SetCurrentVariant( selectedVariant );
2837
2838 if( m_grid->CommitPendingChanges( true ) )
2839 {
2840 m_dataModel->SetCurrentVariant( selectedVariant );
2841 m_dataModel->UpdateReferences( m_dataModel->GetReferenceList(), selectedVariant );
2842 m_dataModel->RebuildRows();
2843
2844 if( m_nbPages->GetSelection() == 1 )
2846 else
2847 m_grid->ForceRefresh();
2848 }
2849
2851 m_parent->UpdateVariantSelectionCtrl( m_parent->Schematic().GetVariantNamesForUI() );
2852}
2853
2854
2856{
2857 int selection = m_variantListBox->GetSelection();
2858
2859 // An empty or default selection cannot be renamed.
2860 if( ( selection == wxNOT_FOUND ) || ( selection == 0 ) )
2861 {
2862 m_parent->GetInfoBar()->ShowMessageFor( _( "Cannot rename the default variant." ),
2863 10000, wxICON_ERROR );
2864 return;
2865 }
2866
2867 wxString oldVariantName = m_variantListBox->GetString( selection );
2868
2869 wxTextEntryDialog dlg( this, _( "Enter new variant name:" ), _( "Rename Design Variant" ),
2870 oldVariantName, wxOK | wxCANCEL | wxCENTER );
2871
2872 if( dlg.ShowModal() == wxID_CANCEL )
2873 return;
2874
2875 wxString newVariantName = dlg.GetValue().Trim().Trim( false );
2876
2877 // Empty name is not allowed.
2878 if( newVariantName.IsEmpty() )
2879 {
2880 m_parent->GetInfoBar()->ShowMessageFor( _( "Variant name cannot be empty." ), 10000, wxICON_ERROR );
2881 return;
2882 }
2883
2884 // Reserved name is not allowed (case-insensitive).
2885 if( newVariantName.CmpNoCase( GetDefaultVariantName() ) == 0 )
2886 {
2887 m_parent->GetInfoBar()->ShowMessageFor( wxString::Format( _( "'%s' is a reserved variant name." ),
2889 10000, wxICON_ERROR );
2890 return;
2891 }
2892
2893 // Same name (exact match) - nothing to do
2894 if( newVariantName == oldVariantName )
2895 return;
2896
2897 // Duplicate name is not allowed (case-insensitive).
2898 for( const wxString& existingName : m_parent->Schematic().GetVariantNames() )
2899 {
2900 if( existingName.CmpNoCase( newVariantName ) == 0
2901 && existingName.CmpNoCase( oldVariantName ) != 0 )
2902 {
2903 m_parent->GetInfoBar()->ShowMessageFor( wxString::Format( _( "Variant '%s' already exists." ),
2904 existingName ),
2905 0000, wxICON_ERROR );
2906 return;
2907 }
2908 }
2909
2910 m_parent->Schematic().RenameVariant( oldVariantName, newVariantName );
2911 m_parent->OnModify();
2912
2913 wxArrayString ctrlContents = m_variantListBox->GetStrings();
2914 ctrlContents.Remove( oldVariantName );
2915 ctrlContents.Add( newVariantName );
2916 ctrlContents.Sort( SortVariantNames );
2917 m_variantListBox->Set( ctrlContents );
2918
2919 int newSelection = m_variantListBox->FindString( newVariantName );
2920
2921 if( newSelection != wxNOT_FOUND )
2922 m_variantListBox->SetSelection( newSelection );
2923
2925 m_parent->UpdateVariantSelectionCtrl( m_parent->Schematic().GetVariantNamesForUI() );
2926}
2927
2928
2929void DIALOG_SYMBOL_FIELDS_TABLE::onCopyVariant( wxCommandEvent& aEvent )
2930{
2931 int selection = m_variantListBox->GetSelection();
2932
2933 // An empty or default selection cannot be copied.
2934 if( ( selection == wxNOT_FOUND ) || ( selection == 0 ) )
2935 {
2936 m_parent->GetInfoBar()->ShowMessageFor( _( "Cannot copy the default variant." ),
2937 10000, wxICON_ERROR );
2938 return;
2939 }
2940
2941 wxString sourceVariantName = m_variantListBox->GetString( selection );
2942
2943 wxTextEntryDialog dlg( this, _( "Enter name for the copied variant:" ), _( "Copy Design Variant" ),
2944 sourceVariantName + wxS( "_copy" ), wxOK | wxCANCEL | wxCENTER );
2945
2946 if( dlg.ShowModal() == wxID_CANCEL )
2947 return;
2948
2949 wxString newVariantName = dlg.GetValue().Trim().Trim( false );
2950
2951 // Empty name is not allowed.
2952 if( newVariantName.IsEmpty() )
2953 {
2954 m_parent->GetInfoBar()->ShowMessageFor( _( "Variant name cannot be empty." ), 10000, wxICON_ERROR );
2955 return;
2956 }
2957
2958 // Duplicate name is not allowed.
2959 if( m_variantListBox->FindString( newVariantName ) != wxNOT_FOUND )
2960 {
2961 m_parent->GetInfoBar()->ShowMessageFor( wxString::Format( _( "Variant '%s' already exists." ),
2962 newVariantName ),
2963 10000, wxICON_ERROR );
2964 return;
2965 }
2966
2967 m_parent->Schematic().CopyVariant( sourceVariantName, newVariantName );
2968 m_parent->OnModify();
2969
2970 wxArrayString ctrlContents = m_variantListBox->GetStrings();
2971 ctrlContents.Add( newVariantName );
2972 ctrlContents.Sort( SortVariantNames );
2973 m_variantListBox->Set( ctrlContents );
2974
2975 int newSelection = m_variantListBox->FindString( newVariantName );
2976
2977 if( newSelection != wxNOT_FOUND )
2978 m_variantListBox->SetSelection( newSelection );
2979
2981 m_parent->UpdateVariantSelectionCtrl( m_parent->Schematic().GetVariantNamesForUI() );
2982}
2983
2984
2986{
2987 int selection = m_variantListBox->GetSelection();
2988
2989 if( ( selection == wxNOT_FOUND ) || ( selection == 0 ) )
2990 {
2991 m_parent->GetInfoBar()->ShowMessageFor( _( "Cannot edit the default variant description." ), 10000,
2992 wxICON_ERROR );
2993 return;
2994 }
2995
2996 wxString variantName = m_variantListBox->GetString( selection );
2997 wxString currentDesc = m_parent->Schematic().GetVariantDescription( variantName );
2998
2999 wxDialog dlg( this, wxID_ANY, wxString::Format( _( "Edit Description for '%s'" ), variantName ), wxDefaultPosition,
3000 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER );
3001
3002 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
3003
3004 wxStaticText* label = new wxStaticText( &dlg, wxID_ANY, _( "Description:" ) );
3005 mainSizer->Add( label, 0, wxLEFT | wxRIGHT | wxTOP | wxEXPAND, 10 );
3006
3007 mainSizer->AddSpacer( 3 );
3008
3009 wxTextCtrl* descCtrl =
3010 new wxTextCtrl( &dlg, wxID_ANY, currentDesc, wxDefaultPosition, wxSize( 300, 60 ), wxTE_MULTILINE );
3011 mainSizer->Add( descCtrl, 1, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 10 );
3012
3013 wxStdDialogButtonSizer* btnSizer = new wxStdDialogButtonSizer();
3014 btnSizer->AddButton( new wxButton( &dlg, wxID_OK ) );
3015 btnSizer->AddButton( new wxButton( &dlg, wxID_CANCEL ) );
3016 btnSizer->Realize();
3017 mainSizer->Add( btnSizer, 0, wxALL | wxALIGN_RIGHT, 5 );
3018
3019 dlg.SetSizer( mainSizer );
3020 dlg.Fit();
3021 dlg.Centre();
3022
3023 if( dlg.ShowModal() == wxID_CANCEL )
3024 return;
3025
3026 wxString newDesc = descCtrl->GetValue().Trim().Trim( false );
3027
3028 m_parent->Schematic().SetVariantDescription( variantName, newDesc );
3029 m_parent->OnModify();
3030}
3031
3032
3034{
3035 wxString currentVariant;
3036 wxString selectedVariant = getSelectedVariant();
3037
3039
3040 if( m_parent )
3041 {
3042 currentVariant = m_parent->Schematic().GetCurrentVariant();
3043
3044 if( currentVariant != selectedVariant )
3045 m_parent->SetCurrentVariant( selectedVariant );
3046 }
3047
3048 if( currentVariant != selectedVariant )
3049 {
3050 m_grid->CommitPendingChanges( true );
3051
3052 SCH_COMMIT commit( m_parent );
3053
3054 m_dataModel->ApplyData( commit, m_schSettings.m_TemplateFieldNames, currentVariant );
3055
3056 if( !commit.Empty() )
3057 {
3058 commit.Push( wxS( "Symbol Fields Table Edit" ) ); // Push clears the commit buffer.
3059 m_parent->OnModify();
3060 }
3061
3062 // Update the data model's current variant for field highlighting
3063 m_dataModel->SetCurrentVariant( selectedVariant );
3064 m_dataModel->UpdateReferences( m_dataModel->GetReferenceList(), selectedVariant );
3065 m_dataModel->RebuildRows();
3066
3067 if( m_nbPages->GetSelection() == 1 )
3069 else
3070 m_grid->ForceRefresh();
3071
3073 }
3074}
3075
3076
3078{
3079 int selection = m_variantListBox->GetSelection();
3080
3081 // Copy, rename, and delete are only enabled for non-default variant selections
3082 bool canModify = ( selection != wxNOT_FOUND ) && ( selection != 0 );
3083
3084 m_copyVariantButton->Enable( canModify );
3085 m_renameVariantButton->Enable( canModify );
3086 m_editVariantDescButton->Enable( canModify );
3087 m_deleteVariantButton->Enable( canModify );
3088}
3089
3090
3092{
3093 wxString retv;
3094
3095 int selection = m_variantListBox->GetSelection();
3096
3097 if( ( selection == wxNOT_FOUND ) || ( m_variantListBox->GetString( selection ) == GetDefaultVariantName() ) )
3098 return retv;
3099
3100 return m_variantListBox->GetString( selection );
3101}
int index
const char * name
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
bool Empty() const
Definition commit.h:134
int vertPixelsFromDU(int y) const
Convert an integer number of dialog units to pixels, vertically.
void ExcludeFromControlUndoRedo(wxWindow *aWindow)
Opt a control out of the dialog's generic Ctrl+Z/Ctrl+Y undo/redo.
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition dialog_shim.h:79
void SetupStandardButtons(std::map< int, wxString > aLabels={})
std::string m_hash_key
int horizPixelsFromDU(int x) const
Convert an integer number of dialog units to pixels, horizontally.
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
int ShowModal() override
DIALOG_SYMBOL_FIELDS_TABLE_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Symbol Fields Table"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER)
void OnTableColSize(wxGridSizeEvent &event) override
void OnSaveAndContinue(wxCommandEvent &aEvent) override
void OnSchItemsRemoved(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem) override
void onAddVariant(wxCommandEvent &aEvent) override
void OnPreviewRefresh(wxCommandEvent &event) override
void OnAddField(wxCommandEvent &event) override
SCH_REFERENCE_LIST getSheetSymbolReferences(SCH_SHEET &aSheet)
void SetUserBomPresets(std::vector< BOM_PRESET > &aPresetList)
void OnSidebarToggle(wxCommandEvent &event) override
void OnOk(wxCommandEvent &aEvent) override
void OnGroupSymbolsToggled(wxCommandEvent &event) override
void OnSchItemsAdded(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem) override
std::map< wxString, BOM_PRESET > m_bomPresets
void OnSchItemsChanged(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem) override
void ApplyBomFmtPreset(const wxString &aPresetName)
void ShowHideColumn(int aCol, bool aShow)
VIEW_CONTROLS_GRID_DATA_MODEL * m_viewControlsDataModel
void setSideBarButtonLook(bool aIsLeftPanelCollapsed)
FIELDS_EDITOR_GRID_DATA_MODEL * m_dataModel
void updateBomPresetSelection(const wxString &aName)
void updateBomFmtPresetSelection(const wxString &aName)
void OnFilterText(wxCommandEvent &aEvent) override
std::map< FIELD_T, int > m_mandatoryFieldListIndexes
void OnRemoveField(wxCommandEvent &event) override
void OnTableCellClick(wxGridEvent &event) override
void onVariantSelectionChange(wxCommandEvent &aEvent) override
void doApplyBomFmtPreset(const BOM_FMT_PRESET &aPreset)
void RestoreGridSelection(const std::set< wxString > &aFullPaths)
Restores the grid selection from a previously saved set of symbol full paths.
void OnScope(wxCommandEvent &event) override
void onBomPresetChanged(wxCommandEvent &aEvent)
void OnTableValueChanged(wxGridEvent &event) override
void OnExport(wxCommandEvent &aEvent) override
void OnClose(wxCloseEvent &aEvent) override
void OnSchSheetChanged(SCHEMATIC &aSch) override
void onCopyVariant(wxCommandEvent &aEvent) override
void onDeleteVariant(wxCommandEvent &aEvent) override
void OnTableRangeSelected(wxGridRangeSelectEvent &aEvent)
void OnMenu(wxCommandEvent &event) override
void setScope(FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE aScope)
std::vector< BOM_FMT_PRESET > GetUserBomFmtPresets() const
void OnCancel(wxCommandEvent &aEvent) override
void OnFilterMouseMoved(wxMouseEvent &event) override
std::map< wxString, BOM_FMT_PRESET > m_bomFmtPresets
DIALOG_SYMBOL_FIELDS_TABLE(SCH_EDIT_FRAME *parent, JOB_EXPORT_SCH_BOM *aJob=nullptr)
BOM_FMT_PRESET GetCurrentBomFmtSettings()
Returns a formatting configuration corresponding to the values in the UI controls of the dialog.
std::set< wxString > SaveGridSelection()
Saves the current grid selection as a set of symbol full paths for later restoration.
void AddField(const wxString &displayName, const wxString &aCanonicalName, bool show, bool groupBy, bool addedByUser=false)
SCH_REFERENCE_LIST getSymbolReferences(SCH_SYMBOL *aSymbol, SCH_REFERENCE_LIST &aCachedRefs)
void OnGridMouseMove(wxMouseEvent &aEvent)
void doApplyBomPreset(const BOM_PRESET &aPreset)
void OnPageChanged(wxNotebookEvent &event) override
void SetUserBomFmtPresets(std::vector< BOM_FMT_PRESET > &aPresetList)
void OnRegroupSymbols(wxCommandEvent &aEvent) override
void OnViewControlsCellChanged(wxGridEvent &aEvent) override
void onRenameVariant(wxCommandEvent &aEvent) override
std::vector< BOM_PRESET > GetUserBomPresets() const
void OnOutputFileBrowseClicked(wxCommandEvent &event) override
void LoadFieldNames()
Construct the rows of m_fieldsCtrl and the columns of m_dataModel from a union of all field names in ...
void onBomFmtPresetChanged(wxCommandEvent &aEvent)
void ApplyBomPreset(const wxString &aPresetName)
void OnSizeViewControlsGrid(wxSizeEvent &event) override
void onEditVariantDescription(wxCommandEvent &aEvent) override
void OnRenameField(wxCommandEvent &event) override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
EDA_ITEM * GetParent() const
Definition eda_item.h:110
A dialog which shows:
wxString GetTextSelection(int aColumn=0)
Return the selected text from aColumn in the wxListCtrl in the dialog.
void SetListLabel(const wxString &aLabel)
PANEL_SYMBOL_FIELDS_TABLE m_FieldEditorPanel
static const wxString ITEM_NUMBER_VARIABLE
static const wxString QUANTITY_VARIABLE
VIEW_CONTROLS_GRID_DATA_MODEL * m_viewControlsDataModel
DIALOG_SYMBOL_FIELDS_TABLE * m_dlg
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
void doPopupSelection(wxCommandEvent &event) override
FIELDS_EDITOR_GRID_TRICKS(DIALOG_SYMBOL_FIELDS_TABLE *aParent, WX_GRID *aGrid, VIEW_CONTROLS_GRID_DATA_MODEL *aViewFieldsData, FIELDS_EDITOR_GRID_DATA_MODEL *aDataModel, EMBEDDED_FILES *aFiles)
FIELDS_EDITOR_GRID_DATA_MODEL * m_dataModel
A general-purpose text renderer for WX_GRIDs backed by WX_GRID_TABLE_BASE tables that can handle draw...
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:57
GRID_TRICKS(WX_GRID *aGrid)
virtual void doPopupSelection(wxCommandEvent &event)
virtual void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent)
WX_GRID * m_grid
I don't own the grid, but he owns me.
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
static REPORTER & GetInstance()
Definition reporter.cpp:120
static SEARCH_STACK * SchSearchS(PROJECT *aProject)
Accessor for Eeschema search stack.
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition project.cpp:407
Holds all the data relating to one schematic.
Definition schematic.h:90
wxArrayString GetVariantNamesForUI() const
Return an array of variant names for using in wxWidgets UI controls.
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:189
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
Handle actions specific to the schematic editor.
Schematic editor (Eeschema) main window.
SCHEMATIC & Schematic() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
void AddItem(const SCH_REFERENCE &aItem)
A helper to define a symbol's reference designator in a schematic.
void Split()
Attempt to split the reference designator into a name (U) and number (1).
SCH_SYMBOL * GetSymbol() const
void SyncSelection(const std::optional< SCH_SHEET_PATH > &targetSheetPath, SCH_ITEM *focusItem, const std::vector< SCH_ITEM * > &items)
int ClearSelection(const TOOL_EVENT &aEvent)
Select all visible items in sheet.
SCH_SELECTION & GetSelection()
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void GetSymbolsWithinPath(SCH_REFERENCE_LIST &aReferences, const SCH_SHEET_PATH &aSheetPath, SYMBOL_FILTER aSymbolFilter, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets that are contained wi...
void GetSheetsWithinPath(std::vector< SCH_SHEET_PATH > &aSheets, const SCH_SHEET_PATH &aSheetPath) const
Add a SCH_SHEET_PATH object to aSheets for each sheet in the list that are contained within aSheetPat...
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
const std::vector< SCH_SHEET_INSTANCE > & GetInstances() const
Definition sch_sheet.h:505
Schematic symbol object.
Definition sch_symbol.h:69
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
bool IsMissingLibSymbol() const
Check to see if the library symbol is set to the dummy library symbol.
bool IsPower() const override
Master controller class:
void doPopupSelection(wxCommandEvent &event) override
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:704
wxString GetGeneratedFieldDisplayName(const wxString &aSource)
Returns any variables unexpanded, e.g.
Definition common.cpp:444
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:59
bool EnsureFileDirectoryExists(wxFileName *aTargetFullFileName, const wxString &aBaseFilename, REPORTER *aReporter)
Make aTargetFullFileName absolute and create the path of this file if it doesn't yet exist.
Definition common.cpp:725
bool IsGeneratedField(const wxString &aSource)
Returns true if the string is generated, e.g contains a single text var reference.
Definition common.cpp:456
The common library.
int OKOrCancelDialog(wxWindow *aParent, const wxString &aWarning, const wxString &aMessage, const wxString &aDetailedMessage, const wxString &aOKLabel, const wxString &aCancelLabel, bool *aApplyToAll)
Display a warning dialog with aMessage and returns the user response.
Definition confirm.cpp:165
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:274
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition confirm.cpp:245
bool HandleUnsavedChanges(wxWindow *aParent, const wxString &aMessage, const std::function< bool()> &aSaveFunction)
Display a dialog with Save, Cancel and Discard Changes buttons.
Definition confirm.cpp:146
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:192
This file is part of the common library.
#define COLUMN_MARGIN
@ MYID_SELECT_FOOTPRINT
wxDEFINE_EVENT(EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, wxCommandEvent)
FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE SCOPE
#define _(s)
bool GetAssociatedDocument(wxWindow *aParent, const wxString &aDocName, PROJECT *aProject, SEARCH_STACK *aPaths, std::vector< EMBEDDED_FILES * > aFilesStack)
Open a document (file) with the suitable browser.
Definition eda_doc.cpp:59
This file is part of the common library.
static FILENAME_RESOLVER * resolver
std::vector< FIELD_CASE_CONFLICT > DetectFieldCaseConflicts(const SCH_REFERENCE_LIST &aSymbols)
#define LABEL_COLUMN
#define DISPLAY_NAME_COLUMN
#define GROUP_BY_COLUMN
#define SHOW_FIELD_COLUMN
@ FRAME_FOOTPRINT_CHOOSER
Definition frame_type.h:40
@ GRIDTRICKS_FIRST_SHOWHIDE
Definition grid_tricks.h:47
@ GRIDTRICKS_FIRST_CLIENT_ID
Definition grid_tricks.h:44
static const std::string CsvFileExtension
static wxString CsvFileWildcard()
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:448
KICOMMON_API wxSize GetTextSize(const wxString &aSingleLine, wxWindow *aWindow)
Return the size of aSingleLine of text when it is rendered in aWindow using whatever font is currentl...
Definition ui_common.cpp:78
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
@ HIGHLIGHT_SYMBOL
@ SYMBOL_FILTER_NON_POWER
@ SYMBOL_FILTER_ALL
std::vector< FAB_LAYER_COLOR > dummy
wxString GetDefaultVariantName()
int SortVariantNames(const wxString &aLhs, const wxString &aRhs)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
wxString label
wxString name
wxString fieldDelimiter
static BOM_FMT_PRESET CSV()
static std::vector< BOM_FMT_PRESET > BuiltInPresets()
wxString stringDelimiter
wxString refRangeDelimiter
wxString refDelimiter
wxString name
static BOM_PRESET DefaultEditing()
wxString sortField
bool groupSymbols
std::vector< BOM_FIELD > fieldsOrdered
static std::vector< BOM_PRESET > BuiltInPresets()
bool excludeDNP
wxString filterString
A simple container for sheet instance information.
Hold a name of a symbol's field, field value, and default visibility.
wxString GetDefaultFieldName(FIELD_T aFieldId, bool aTranslateForHI)
Return a default symbol field name for a mandatory field type.
#define DO_TRANSLATE
#define MANDATORY_FIELDS
FIELD_T
The set of all field indices assuming an array like sequence that a SCH_COMPONENT or LIB_PART can hol...
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
wxString GetCanonicalFieldName(FIELD_T aFieldType)
std::string path
@ SCH_SYMBOL_T
Definition typeinfo.h:169
@ SCH_SHEET_T
Definition typeinfo.h:172
Definition of file extensions used in Kicad.