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