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