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