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