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