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