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