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