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