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