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
344 m_viewControlsGrid->ShowHideColumns( "0 1 2 3" );
345
346 CallAfter( [this, cfg]()
347 {
348 if( cfg.sidebar_collapsed )
350 else
351 m_splitterMainWindow->SetSashPosition( cfg.sash_pos );
352
354 } );
355
356 if( m_job )
357 m_outputFileName->SetValue( m_job->GetConfiguredOutputPath() );
358 else
359 m_outputFileName->SetValue( m_schSettings.m_BomExportFileName );
360
361 Center();
362
363 // Connect Events
364 m_grid->Bind( wxEVT_GRID_COL_SORT, &DIALOG_SYMBOL_FIELDS_TABLE::OnColSort, this );
365 m_grid->Bind( wxEVT_GRID_COL_MOVE, &DIALOG_SYMBOL_FIELDS_TABLE::OnColMove, this );
368 m_viewControlsGrid->Bind( wxEVT_GRID_CELL_CHANGED, &DIALOG_SYMBOL_FIELDS_TABLE::OnViewControlsCellChanged, this );
369
370 if( !m_job )
371 {
372 // Start listening for schematic changes
373 m_parent->Schematic().AddListener( this );
374 }
375 else
376 {
377 // Don't allow editing
378 m_grid->EnableEditing( false );
379 m_buttonApply->Hide();
380 m_buttonExport->Hide();
381 }
382}
383
384
386{
388 m_schSettings.m_BomExportFileName = m_outputFileName->GetValue();
389
390 EESCHEMA_SETTINGS::PANEL_SYMBOL_FIELDS_TABLE& cfg = m_parent->eeconfig()->m_FieldEditorPanel;
391
392 if( !cfg.sidebar_collapsed )
393 cfg.sash_pos = m_splitterMainWindow->GetSashPosition();
394
395 for( int i = 0; i < m_grid->GetNumberCols(); i++ )
396 {
397 if( m_grid->IsColShown( i ) )
398 {
399 std::string fieldName( m_dataModel->GetColFieldName( i ).ToUTF8() );
400 cfg.field_widths[fieldName] = m_grid->GetColSize( i );
401 }
402 }
403
404 // Disconnect Events
405 m_grid->Unbind( wxEVT_GRID_COL_SORT, &DIALOG_SYMBOL_FIELDS_TABLE::OnColSort, this );
406 m_grid->Unbind( wxEVT_GRID_COL_SORT, &DIALOG_SYMBOL_FIELDS_TABLE::OnColMove, this );
409 m_viewControlsGrid->Unbind( wxEVT_GRID_CELL_CHANGED, &DIALOG_SYMBOL_FIELDS_TABLE::OnViewControlsCellChanged, this );
410
411 // Delete the GRID_TRICKS.
412 m_viewControlsGrid->PopEventHandler( true );
413 m_grid->PopEventHandler( true );
414
415 // we gave ownership of m_viewControlsDataModel & m_dataModel to the wxGrids...
416}
417
418
419void DIALOG_SYMBOL_FIELDS_TABLE::setSideBarButtonLook( bool aIsLeftPanelCollapsed )
420{
421 // Set bitmap and tooltip according to left panel visibility
422
423 if( aIsLeftPanelCollapsed )
424 {
426 m_sidebarButton->SetToolTip( _( "Expand left panel" ) );
427 }
428 else
429 {
431 m_sidebarButton->SetToolTip( _( "Collapse left panel" ) );
432 }
433}
434
435
437{
438 wxGridCellAttr* attr = new wxGridCellAttr;
439 attr->SetReadOnly( false );
440
441 // Set some column types to specific editors
442 if( m_dataModel->ColIsReference( aCol ) )
443 {
444 attr->SetReadOnly();
445 attr->SetRenderer( new GRID_CELL_TEXT_RENDERER() );
446 m_dataModel->SetColAttr( attr, aCol );
447 }
448 else if( m_dataModel->GetColFieldName( aCol ) == GetCanonicalFieldName( FIELD_T::FOOTPRINT ) )
449 {
450 attr->SetEditor( new GRID_CELL_FPID_EDITOR( this, wxEmptyString ) );
451 m_dataModel->SetColAttr( attr, aCol );
452 }
453 else if( m_dataModel->GetColFieldName( aCol ) == GetCanonicalFieldName( FIELD_T::DATASHEET ) )
454 {
455 // set datasheet column viewer button
456 attr->SetEditor( new GRID_CELL_URL_EDITOR( this, PROJECT_SCH::SchSearchS( &Prj() ),
457 { &m_parent->Schematic() } ) );
458 m_dataModel->SetColAttr( attr, aCol );
459 }
460 else if( m_dataModel->ColIsQuantity( aCol ) || m_dataModel->ColIsItemNumber( aCol ) )
461 {
462 attr->SetReadOnly();
463 attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_CENTER );
464 attr->SetRenderer( new wxGridCellNumberRenderer() );
465 m_dataModel->SetColAttr( attr, aCol );
466 }
467 else if( m_dataModel->ColIsAttribute( aCol ) )
468 {
469 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
470 attr->SetRenderer( new GRID_CELL_CHECKBOX_RENDERER() );
471 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
472 m_dataModel->SetColAttr( attr, aCol );
473 }
474 else if( IsGeneratedField( m_dataModel->GetColFieldName( aCol ) ) )
475 {
476 attr->SetReadOnly();
477 m_dataModel->SetColAttr( attr, aCol );
478 }
479 else
480 {
481 attr->SetRenderer( new GRID_CELL_TEXT_RENDERER() );
482 attr->SetEditor( m_grid->GetDefaultEditor() );
483 m_dataModel->SetColAttr( attr, aCol );
484 }
485}
486
487
489{
490 EESCHEMA_SETTINGS* cfg = m_parent->eeconfig();
491 wxSize defaultDlgSize = ConvertDialogToPixels( wxSize( 600, 300 ) );
492
493 // Restore column sorting order and widths
494 m_grid->AutoSizeColumns( false );
495 int sortCol = 0;
496 bool sortAscending = true;
497
498 for( int col = 0; col < m_grid->GetNumberCols(); ++col )
499 {
501
502 if( col == m_dataModel->GetSortCol() )
503 {
504 sortCol = col;
505 sortAscending = m_dataModel->GetSortAsc();
506 }
507 }
508
509 // sync m_grid's column visibilities to Show checkboxes in m_viewControlsGrid
510 for( int i = 0; i < m_viewControlsDataModel->GetNumberRows(); ++i )
511 {
512 int col = m_dataModel->GetFieldNameCol( m_viewControlsDataModel->GetCanonicalFieldName( i ) );
513
514 if( col == -1 )
515 continue;
516
517 bool show = m_viewControlsDataModel->GetValueAsBool( i, SHOW_FIELD_COLUMN );
518 m_dataModel->SetShowColumn( col, show );
519
520 if( show )
521 {
522 m_grid->ShowCol( col );
523
524 std::string key( m_dataModel->GetColFieldName( col ).ToUTF8() );
525
526 if( cfg->m_FieldEditorPanel.field_widths.count( key )
527 && ( cfg->m_FieldEditorPanel.field_widths.at( key ) > 0 ) )
528 {
529 m_grid->SetColSize( col, cfg->m_FieldEditorPanel.field_widths.at( key ) );
530 }
531 else
532 {
533 int textWidth = m_dataModel->GetDataWidth( col ) + COLUMN_MARGIN;
534 int maxWidth = defaultDlgSize.x / 3;
535
536 m_grid->SetColSize( col, std::clamp( textWidth, 100, maxWidth ) );
537 }
538 }
539 else
540 {
541 m_grid->HideCol( col );
542 }
543 }
544
545 m_dataModel->SetSorting( sortCol, sortAscending );
546 m_grid->SetSortingColumn( sortCol, sortAscending );
547}
548
549
551{
552 if( !wxDialog::TransferDataFromWindow() )
553 return false;
554
555 TOOL_MANAGER* toolMgr = m_parent->GetToolManager();
556 SCH_SELECTION_TOOL* selectionTool = toolMgr->GetTool<SCH_SELECTION_TOOL>();
557 SCH_SELECTION& selection = selectionTool->GetSelection();
558 SCH_SYMBOL* symbol = nullptr;
559
560 m_dataModel->SetGroupingEnabled( m_groupSymbolsBox->GetValue() );
561
562 wxCommandEvent dummy;
563 OnScope( dummy );
564
565 if( selection.GetSize() == 1 )
566 {
567 EDA_ITEM* item = selection.Front();
568
569 if( item->Type() == SCH_SYMBOL_T )
570 symbol = (SCH_SYMBOL*) item;
571 else if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T )
572 symbol = (SCH_SYMBOL*) item->GetParent();
573 }
574
575 if( symbol )
576 {
577 for( int row = 0; row < m_dataModel->GetNumberRows(); ++row )
578 {
579 std::vector<SCH_REFERENCE> references = m_dataModel->GetRowReferences( row );
580 bool found = false;
581
582 for( const SCH_REFERENCE& ref : references )
583 {
584 if( ref.GetSymbol() == symbol )
585 {
586 found = true;
587 break;
588 }
589 }
590
591 if( found )
592 {
593 // Find the value column and the reference column if they're shown
594 int valueCol = -1;
595 int refCol = -1;
596 int anyCol = -1;
597
598 for( int col = 0; col < m_dataModel->GetNumberCols(); col++ )
599 {
600 if( m_dataModel->ColIsValue( col ) )
601 valueCol = col;
602 else if( m_dataModel->ColIsReference( col ) )
603 refCol = col;
604 else if( anyCol == -1 && m_dataModel->GetShowColumn( col ) )
605 anyCol = col;
606 }
607
608 if( valueCol != -1 && m_dataModel->GetShowColumn( valueCol ) )
609 m_grid->GoToCell( row, valueCol );
610 else if( refCol != -1 && m_dataModel->GetShowColumn( refCol ) )
611 m_grid->GoToCell( row, refCol );
612 else if( anyCol != -1 )
613 m_grid->GoToCell( row, anyCol );
614
615 break;
616 }
617 }
618 }
619
620 // We don't want table range selection events to happen until we've loaded the data or we
621 // we'll clear our selection as the grid is built before the code above can get the
622 // user's current selection.
624
625 return true;
626}
627
628
630{
631 if( !m_grid->CommitPendingChanges() )
632 return false;
633
634 if( !wxDialog::TransferDataFromWindow() )
635 return false;
636
637 if( m_job )
638 {
639 // and exit, don't even dream of saving changes from the data model
640 return true;
641 }
642
643 std::set<wxString> selectedVariantNames;
644 SCH_COMMIT commit( m_parent );
645 SCH_SHEET_PATH currentSheet = m_parent->GetCurrentSheet();
646
647 m_dataModel->ApplyData( commit, m_schSettings.m_TemplateFieldNames, selectedVariantNames );
648
649 if( !commit.Empty() )
650 {
651 commit.Push( wxS( "Symbol Fields Table Edit" ) ); // Push clears the commit buffer.
652 m_parent->OnModify();
653 }
654
655 // Reset the view to where we left the user
656 m_parent->SetCurrentSheet( currentSheet );
657 m_parent->SyncView();
658 m_parent->Refresh();
659
660 return true;
661}
662
663
664void DIALOG_SYMBOL_FIELDS_TABLE::AddField( const wxString& aFieldName, const wxString& aLabelValue,
665 bool show, bool groupBy, bool addedByUser )
666{
667 // Users can add fields with variable names that match the special names in the grid,
668 // e.g. ${QUANTITY} so make sure we don't add them twice
669 for( int row = 0; row < m_viewControlsDataModel->GetNumberRows(); row++ )
670 {
671 if( m_viewControlsDataModel->GetCanonicalFieldName( row ) == aFieldName )
672 return;
673 }
674
675 m_dataModel->AddColumn( aFieldName, aLabelValue, addedByUser, getSelectedVariants() );
676
677 wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_APPENDED, 1 );
678 m_grid->ProcessTableMessage( msg );
679
680 m_viewControlsGrid->OnAddRow(
681 [&]() -> std::pair<int, int>
682 {
683 m_viewControlsDataModel->AppendRow( aFieldName, aLabelValue, show, groupBy );
684
685 return { m_viewControlsDataModel->GetNumberRows() - 1, -1 };
686 } );
687}
688
689
691{
692 auto addMandatoryField =
693 [&]( FIELD_T fieldId, bool show, bool groupBy )
694 {
695 m_mandatoryFieldListIndexes[fieldId] = m_viewControlsDataModel->GetNumberRows();
696
698 show, groupBy );
699 };
700
701 // Add mandatory fields first show groupBy
702 addMandatoryField( FIELD_T::REFERENCE, true, true );
703 addMandatoryField( FIELD_T::VALUE, true, true );
704 addMandatoryField( FIELD_T::FOOTPRINT, true, true );
705 addMandatoryField( FIELD_T::DATASHEET, true, false );
706 addMandatoryField( FIELD_T::DESCRIPTION, false, false );
707
708 // Generated fields present only in the fields table
711
712 // User fields next
713 std::set<wxString> userFieldNames;
714
715 for( int ii = 0; ii < (int) m_symbolsList.GetCount(); ++ii )
716 {
717 SCH_SYMBOL* symbol = m_symbolsList[ii].GetSymbol();
718
719 for( const SCH_FIELD& field : symbol->GetFields() )
720 {
721 if( !field.IsMandatory() && !field.IsPrivate() )
722 userFieldNames.insert( field.GetName() );
723 }
724 }
725
726 for( const wxString& fieldName : userFieldNames )
727 AddField( fieldName, GetGeneratedFieldDisplayName( fieldName ), true, false );
728
729 // Add any templateFieldNames which aren't already present in the userFieldNames
730 for( const TEMPLATE_FIELDNAME& tfn : m_schSettings.m_TemplateFieldNames.GetTemplateFieldNames() )
731 {
732 if( userFieldNames.count( tfn.m_Name ) == 0 )
733 AddField( tfn.m_Name, GetGeneratedFieldDisplayName( tfn.m_Name ), false, false );
734 }
735}
736
737
738void DIALOG_SYMBOL_FIELDS_TABLE::OnAddField( wxCommandEvent& event )
739{
740 wxTextEntryDialog dlg( this, _( "New field name:" ), _( "Add Field" ) );
741
742 if( dlg.ShowModal() != wxID_OK )
743 return;
744
745 wxString fieldName = dlg.GetValue();
746
747 if( fieldName.IsEmpty() )
748 {
749 DisplayError( this, _( "Field must have a name." ) );
750 return;
751 }
752
753 for( int i = 0; i < m_dataModel->GetNumberCols(); ++i )
754 {
755 if( fieldName == m_dataModel->GetColFieldName( i ) )
756 {
757 DisplayError( this, wxString::Format( _( "Field name '%s' already in use." ), fieldName ) );
758 return;
759 }
760 }
761
762 AddField( fieldName, GetGeneratedFieldDisplayName( fieldName ), true, false, true );
763
764 SetupColumnProperties( m_dataModel->GetColsCount() - 1 );
765
767 OnModify();
768}
769
770
771void DIALOG_SYMBOL_FIELDS_TABLE::OnRemoveField( wxCommandEvent& event )
772{
773 m_viewControlsGrid->OnDeleteRows(
774 [&]( int row )
775 {
776 for( FIELD_T id : MANDATORY_FIELDS )
777 {
778 if( m_mandatoryFieldListIndexes[id] == row )
779 {
780 DisplayError( this, wxString::Format( _( "The first %d fields are mandatory." ),
781 (int) m_mandatoryFieldListIndexes.size() ) );
782 return false;
783 }
784 }
785
786 return IsOK( this, wxString::Format( _( "Are you sure you want to remove the field '%s'?" ),
787 m_viewControlsDataModel->GetValue( row, DISPLAY_NAME_COLUMN ) ) );
788 },
789 [&]( int row )
790 {
791 wxString fieldName = m_viewControlsDataModel->GetCanonicalFieldName( row );
792 int col = m_dataModel->GetFieldNameCol( fieldName );
793
794 if( col != -1 )
795 m_dataModel->RemoveColumn( col );
796
797 m_viewControlsDataModel->DeleteRow( row );
798
800 OnModify();
801 } );
802}
803
804
805void DIALOG_SYMBOL_FIELDS_TABLE::OnRenameField( wxCommandEvent& event )
806{
807 wxArrayInt selectedRows = m_viewControlsGrid->GetSelectedRows();
808
809 if( selectedRows.empty() && m_viewControlsGrid->GetGridCursorRow() >= 0 )
810 selectedRows.push_back( m_viewControlsGrid->GetGridCursorRow() );
811
812 if( selectedRows.empty() )
813 return;
814
815 int row = selectedRows[0];
816
817 for( FIELD_T id : MANDATORY_FIELDS )
818 {
819 if( m_mandatoryFieldListIndexes[id] == row )
820 {
821 DisplayError( this, wxString::Format( _( "The first %d fields are mandatory and names cannot be changed." ),
822 (int) m_mandatoryFieldListIndexes.size() ) );
823 return;
824 }
825 }
826
827 wxString fieldName = m_viewControlsDataModel->GetCanonicalFieldName( row );
828
829 int col = m_dataModel->GetFieldNameCol( fieldName );
830 wxCHECK_RET( col != -1, wxS( "Existing field name missing from data model" ) );
831
832 wxTextEntryDialog dlg( this, _( "New field name:" ), _( "Rename Field" ), fieldName );
833
834 if( dlg.ShowModal() != wxID_OK )
835 return;
836
837 wxString newFieldName = dlg.GetValue();
838
839 // No change, no-op
840 if( newFieldName == fieldName )
841 return;
842
843 // New field name already exists
844 if( m_dataModel->GetFieldNameCol( newFieldName ) != -1 )
845 {
846 wxString confirm_msg = wxString::Format( _( "Field name %s already exists." ), newFieldName );
847 DisplayError( this, confirm_msg );
848 return;
849 }
850
851 m_dataModel->RenameColumn( col, newFieldName );
852 m_viewControlsDataModel->SetCanonicalFieldName( row, newFieldName );
853 m_viewControlsDataModel->SetValue( row, DISPLAY_NAME_COLUMN, newFieldName );
854 m_viewControlsDataModel->SetValue( row, LABEL_COLUMN, GetGeneratedFieldDisplayName( newFieldName ) );
855
857 OnModify();
858}
859
860
861void DIALOG_SYMBOL_FIELDS_TABLE::OnFilterText( wxCommandEvent& aEvent )
862{
863 m_dataModel->SetFilter( m_filter->GetValue() );
864 m_dataModel->RebuildRows();
865 m_grid->ForceRefresh();
866
868}
869
870
872{
873#if defined( __WXOSX__ ) || wxCHECK_VERSION( 3, 3, 0 ) // Doesn't work properly on other ports
874 wxPoint pos = aEvent.GetPosition();
875 wxRect ctrlRect = m_filter->GetScreenRect();
876 int buttonWidth = ctrlRect.GetHeight(); // Presume buttons are square
877
878 // TODO: restore cursor when mouse leaves the filter field (or is it a MSW bug?)
879 if( m_filter->IsSearchButtonVisible() && pos.x < buttonWidth )
880 SetCursor( wxCURSOR_ARROW );
881 else if( m_filter->IsCancelButtonVisible() && pos.x > ctrlRect.GetWidth() - buttonWidth )
882 SetCursor( wxCURSOR_ARROW );
883 else
884 SetCursor( wxCURSOR_IBEAM );
885#endif
886}
887
888
890{
891 m_dataModel->SetPath( m_parent->GetCurrentSheet() );
892 m_dataModel->SetScope( aScope );
893 m_dataModel->RebuildRows();
894}
895
896
897void DIALOG_SYMBOL_FIELDS_TABLE::OnScope( wxCommandEvent& aEvent )
898{
899 switch( aEvent.GetSelection() )
900 {
901 case 0: setScope( SCOPE::SCOPE_ALL ); break;
902 case 1: setScope( SCOPE::SCOPE_SHEET ); break;
903 case 2: setScope( SCOPE::SCOPE_SHEET_RECURSIVE ); break;
904 }
905}
906
907
909{
910 m_dataModel->SetGroupingEnabled( m_groupSymbolsBox->GetValue() );
911 m_dataModel->RebuildRows();
912 m_grid->ForceRefresh();
913
915}
916
917
918void DIALOG_SYMBOL_FIELDS_TABLE::OnMenu( wxCommandEvent& event )
919{
920 EESCHEMA_SETTINGS::PANEL_SYMBOL_FIELDS_TABLE& cfg = m_parent->eeconfig()->m_FieldEditorPanel;
921
922 // Build a pop menu:
923 wxMenu menu;
924
925 menu.Append( 4204, _( "Include 'DNP' Symbols" ),
926 _( "Show symbols marked 'DNP' in the table. This setting also controls whether or not 'DNP' "
927 "symbols are included on export." ),
928 wxITEM_CHECK );
929 menu.Check( 4204, !m_dataModel->GetExcludeDNP() );
930
931 menu.Append( 4205, _( "Include 'Exclude from BOM' Symbols" ),
932 _( "Show symbols marked 'Exclude from BOM' in the table. Symbols marked 'Exclude from BOM' "
933 "are never included on export." ),
934 wxITEM_CHECK );
935 menu.Check( 4205, m_dataModel->GetIncludeExcludedFromBOM() );
936
937 menu.AppendSeparator();
938
939 menu.Append( 4206, _( "Highlight on Cross-probe" ),
940 _( "Highlight corresponding item on canvas when it is selected in the table" ),
941 wxITEM_CHECK );
942 menu.Check( 4206, cfg.selection_mode == 0 );
943
944 menu.Append( 4207, _( "Select on Cross-probe" ),
945 _( "Select corresponding item on canvas when it is selected in the table" ),
946 wxITEM_CHECK );
947 menu.Check( 4207, cfg.selection_mode == 1 );
948
949 // menu_id is the selected submenu id from the popup menu or wxID_NONE
950 int menu_id = m_bMenu->GetPopupMenuSelectionFromUser( menu );
951
952 if( menu_id == 0 || menu_id == 4204 )
953 {
954 m_dataModel->SetExcludeDNP( !m_dataModel->GetExcludeDNP() );
955 m_dataModel->RebuildRows();
956 m_grid->ForceRefresh();
957
959 }
960 else if( menu_id == 1 || menu_id == 4205 )
961 {
962 m_dataModel->SetIncludeExcludedFromBOM( !m_dataModel->GetIncludeExcludedFromBOM() );
963 m_dataModel->RebuildRows();
964 m_grid->ForceRefresh();
965
967 }
968 else if( menu_id == 3 || menu_id == 4206 )
969 {
970 if( cfg.selection_mode != 0 )
971 cfg.selection_mode = 0;
972 else
973 cfg.selection_mode = 2;
974 }
975 else if( menu_id == 4 || menu_id == 4207 )
976 {
977 if( cfg.selection_mode != 1 )
978 cfg.selection_mode = 1;
979 else
980 cfg.selection_mode = 2;
981 }
982}
983
984
985void DIALOG_SYMBOL_FIELDS_TABLE::OnColSort( wxGridEvent& aEvent )
986{
987 int sortCol = aEvent.GetCol();
988 std::string key( m_dataModel->GetColFieldName( sortCol ).ToUTF8() );
989 bool ascending;
990
991 // Don't sort by item number, it is generated by the sort
992 if( m_dataModel->ColIsItemNumber( sortCol ) )
993 {
994 aEvent.Veto();
995 return;
996 }
997
998 // This is bonkers, but wxWidgets doesn't tell us ascending/descending in the event, and
999 // if we ask it will give us pre-event info.
1000 if( m_grid->IsSortingBy( sortCol ) )
1001 {
1002 // same column; invert ascending
1003 ascending = !m_grid->IsSortOrderAscending();
1004 }
1005 else
1006 {
1007 // different column; start with ascending
1008 ascending = true;
1009 }
1010
1011 m_dataModel->SetSorting( sortCol, ascending );
1012 m_dataModel->RebuildRows();
1013 m_grid->ForceRefresh();
1014
1016}
1017
1018
1019void DIALOG_SYMBOL_FIELDS_TABLE::OnColMove( wxGridEvent& aEvent )
1020{
1021 int origPos = aEvent.GetCol();
1022
1023 // Save column widths since the setup function uses the saved config values
1024 EESCHEMA_SETTINGS* cfg = m_parent->eeconfig();
1025
1026 for( int i = 0; i < m_grid->GetNumberCols(); i++ )
1027 {
1028 if( m_grid->IsColShown( i ) )
1029 {
1030 std::string fieldName( m_dataModel->GetColFieldName( i ).ToUTF8() );
1031 cfg->m_FieldEditorPanel.field_widths[fieldName] = m_grid->GetColSize( i );
1032 }
1033 }
1034
1035 CallAfter(
1036 [origPos, this]()
1037 {
1038 int newPos = m_grid->GetColPos( origPos );
1039
1040#ifdef __WXMAC__
1041 if( newPos < origPos )
1042 newPos += 1;
1043#endif
1044
1045 m_dataModel->MoveColumn( origPos, newPos );
1046
1047 // "Unmove" the column since we've moved the column internally
1048 m_grid->ResetColPos();
1049
1050 // We need to reset all the column attr's to the correct column order
1052
1053 m_grid->ForceRefresh();
1054 } );
1055
1057}
1058
1059
1061{
1062 if( aShow )
1063 m_grid->ShowCol( aCol );
1064 else
1065 m_grid->HideCol( aCol );
1066
1067 m_dataModel->SetShowColumn( aCol, aShow );
1068
1070
1071 if( m_nbPages->GetSelection() == 1 )
1073 else
1074 m_grid->ForceRefresh();
1075
1076 OnModify();
1077}
1078
1079
1081{
1082 int row = aEvent.GetRow();
1083
1084 wxCHECK( row < m_viewControlsGrid->GetNumberRows(), /* void */ );
1085
1086 switch( aEvent.GetCol() )
1087 {
1089 {
1090 wxString label = m_viewControlsDataModel->GetValue( row, DISPLAY_NAME_COLUMN );
1091 wxString fieldName = m_viewControlsDataModel->GetCanonicalFieldName( row );
1092 int dataCol = m_dataModel->GetFieldNameCol( fieldName );
1093
1094 if( dataCol != -1 )
1095 {
1096 m_dataModel->SetColLabelValue( dataCol, label );
1097 m_grid->SetColLabelValue( dataCol, label );
1098
1099 if( m_nbPages->GetSelection() == 1 )
1101 else
1102 m_grid->ForceRefresh();
1103
1105 OnModify();
1106 }
1107
1108 break;
1109 }
1110
1111 case SHOW_FIELD_COLUMN:
1112 {
1113 wxString fieldName = m_viewControlsDataModel->GetCanonicalFieldName( row );
1114 bool value = m_viewControlsDataModel->GetValueAsBool( row, SHOW_FIELD_COLUMN );
1115 int dataCol = m_dataModel->GetFieldNameCol( fieldName );
1116
1117 if( dataCol != -1 )
1118 ShowHideColumn( dataCol, value );
1119
1120 break;
1121 }
1122
1123 case GROUP_BY_COLUMN:
1124 {
1125 wxString fieldName = m_viewControlsDataModel->GetCanonicalFieldName( row );
1126 bool value = m_viewControlsDataModel->GetValueAsBool( row, GROUP_BY_COLUMN );
1127 int dataCol = m_dataModel->GetFieldNameCol( fieldName );
1128
1129 if( m_dataModel->ColIsQuantity( dataCol ) && value )
1130 {
1131 DisplayError( this, _( "The Quantity column cannot be grouped by." ) );
1132
1133 value = false;
1134 m_viewControlsDataModel->SetValueAsBool( row, GROUP_BY_COLUMN, value );
1135 break;
1136 }
1137
1138 if( m_dataModel->ColIsItemNumber( dataCol ) && value )
1139 {
1140 DisplayError( this, _( "The Item Number column cannot be grouped by." ) );
1141
1142 value = false;
1143 m_viewControlsDataModel->SetValueAsBool( row, GROUP_BY_COLUMN, value );
1144 break;
1145 }
1146
1147 m_dataModel->SetGroupColumn( dataCol, value );
1148 m_dataModel->RebuildRows();
1149
1150 if( m_nbPages->GetSelection() == 1 )
1152 else
1153 m_grid->ForceRefresh();
1154
1156 OnModify();
1157 break;
1158 }
1159
1160 default:
1161 break;
1162 }
1163}
1164
1165
1167{
1168 m_grid->ForceRefresh();
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->IsExpanderColumn( 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 );
1399 }
1400 else
1401 {
1402 cfg.sash_pos = m_splitterMainWindow->GetSashPosition();
1403
1404 cfg.sidebar_collapsed = true;
1406 }
1407
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, Prj().AbsolutePath( m_parent->Schematic().GetFileName() ),
1453 {
1454 msg.Printf( _( "Could not open/create path '%s'." ), outputFile.GetPath() );
1455 DisplayError( this, msg );
1456 return;
1457 }
1458
1459 wxFFile out( outputFile.GetFullPath(), "wb" );
1460
1461 if( !out.IsOpened() )
1462 {
1463 msg.Printf( _( "Could not create BOM output '%s'." ), outputFile.GetFullPath() );
1464 DisplayError( this, msg );
1465 return;
1466 }
1467
1469
1470 if( !out.Write( m_textOutput->GetValue() ) )
1471 {
1472 msg.Printf( _( "Could not write BOM output '%s'." ), outputFile.GetFullPath() );
1473 DisplayError( this, msg );
1474 return;
1475 }
1476
1477 // close the file before we tell the user it's done with the info modal :workflow meme:
1478 out.Close();
1479 msg.Printf( _( "Wrote BOM output to '%s'" ), outputFile.GetFullPath() );
1480 DisplayInfoMessage( this, msg );
1481}
1482
1483
1484void DIALOG_SYMBOL_FIELDS_TABLE::OnCancel( wxCommandEvent& aEvent )
1485{
1486 if( m_job )
1487 EndModal( wxID_CANCEL );
1488 else
1489 Close();
1490}
1491
1492
1493void DIALOG_SYMBOL_FIELDS_TABLE::OnOk( wxCommandEvent& aEvent )
1494{
1496
1497 if( m_job )
1498 {
1499 m_job->SetConfiguredOutputPath( m_outputFileName->GetValue() );
1500
1502 m_job->m_bomFmtPresetName = m_currentBomFmtPreset->name;
1503 else
1504 m_job->m_bomFmtPresetName = wxEmptyString;
1505
1506 if( m_currentBomPreset )
1507 m_job->m_bomPresetName = m_currentBomPreset->name;
1508 else
1509 m_job->m_bomPresetName = wxEmptyString;
1510
1512 m_job->m_fieldDelimiter = fmtSettings.fieldDelimiter;
1513 m_job->m_stringDelimiter = fmtSettings.stringDelimiter;
1514 m_job->m_refDelimiter = fmtSettings.refDelimiter;
1515 m_job->m_refRangeDelimiter = fmtSettings.refRangeDelimiter;
1516 m_job->m_keepTabs = fmtSettings.keepTabs;
1517 m_job->m_keepLineBreaks = fmtSettings.keepLineBreaks;
1518
1519 BOM_PRESET presetFields = m_dataModel->GetBomSettings();
1520 m_job->m_sortAsc = presetFields.sortAsc;
1521 m_job->m_excludeDNP = presetFields.excludeDNP;
1522 m_job->m_filterString = presetFields.filterString;
1523 m_job->m_sortField = presetFields.sortField;
1524
1525 m_job->m_fieldsOrdered.clear();
1526 m_job->m_fieldsLabels.clear();
1527 m_job->m_fieldsGroupBy.clear();
1528
1529 for( const BOM_FIELD& modelField : m_dataModel->GetFieldsOrdered() )
1530 {
1531 if( modelField.show )
1532 m_job->m_fieldsOrdered.emplace_back( modelField.name );
1533 else
1534 m_job->m_fieldsOrdered.emplace_back( wxT( "__" ) + modelField.name );
1535
1536 m_job->m_fieldsLabels.emplace_back( modelField.label );
1537
1538 if( modelField.groupBy )
1539 m_job->m_fieldsGroupBy.emplace_back( modelField.name );
1540 }
1541
1542 EndModal( wxID_OK );
1543 }
1544 else
1545 {
1546 Close();
1547 }
1548}
1549
1550
1551void DIALOG_SYMBOL_FIELDS_TABLE::OnClose( wxCloseEvent& aEvent )
1552{
1553 if( m_job )
1554 {
1555 aEvent.Skip();
1556 return;
1557 }
1558
1559 m_grid->CommitPendingChanges( true );
1560
1561 if( m_dataModel->IsEdited() && aEvent.CanVeto() )
1562 {
1563 if( !HandleUnsavedChanges( this, _( "Save changes?" ),
1564 [&]() -> bool
1565 {
1566 return TransferDataFromWindow();
1567 } ) )
1568 {
1569 aEvent.Veto();
1570 return;
1571 }
1572 }
1573
1574 // Stop listening to schematic events
1575 m_parent->Schematic().RemoveListener( this );
1576 m_parent->ClearFocus();
1577
1578 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, wxID_ANY );
1579
1580 if( wxWindow* parent = GetParent() )
1581 wxQueueEvent( parent, evt );
1582}
1583
1584
1586{
1587 std::vector<BOM_PRESET> ret;
1588
1589 for( const std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1590 {
1591 if( !pair.second.readOnly )
1592 ret.emplace_back( pair.second );
1593 }
1594
1595 return ret;
1596}
1597
1598
1599void DIALOG_SYMBOL_FIELDS_TABLE::SetUserBomPresets( std::vector<BOM_PRESET>& aPresetList )
1600{
1601 // Reset to defaults
1603
1604 for( const BOM_PRESET& preset : aPresetList )
1605 {
1606 if( m_bomPresets.count( preset.name ) )
1607 continue;
1608
1609 m_bomPresets[preset.name] = preset;
1610
1611 m_bomPresetMRU.Add( preset.name );
1612 }
1613
1615}
1616
1617
1618void DIALOG_SYMBOL_FIELDS_TABLE::ApplyBomPreset( const wxString& aPresetName )
1619{
1620 updateBomPresetSelection( aPresetName );
1621
1622 wxCommandEvent dummy;
1624}
1625
1626
1628{
1629 if( m_bomPresets.count( aPreset.name ) )
1631 else
1632 m_currentBomPreset = nullptr;
1633
1634 if( m_currentBomPreset && !m_currentBomPreset->readOnly )
1636 else
1637 m_lastSelectedBomPreset = nullptr;
1638
1639 updateBomPresetSelection( aPreset.name );
1640 doApplyBomPreset( aPreset );
1641}
1642
1643
1645{
1646 m_bomPresets.clear();
1647 m_bomPresetMRU.clear();
1648
1649 // Load the read-only defaults
1650 for( const BOM_PRESET& preset : BOM_PRESET::BuiltInPresets() )
1651 {
1652 m_bomPresets[preset.name] = preset;
1653 m_bomPresets[preset.name].readOnly = true;
1654
1655 m_bomPresetMRU.Add( preset.name );
1656 }
1657}
1658
1659
1661{
1662 m_cbBomPresets->Clear();
1663
1664 int idx = 0;
1665 int default_idx = 0;
1666
1667 for( const auto& [presetName, preset] : m_bomPresets )
1668 {
1669 m_cbBomPresets->Append( wxGetTranslation( presetName ), (void*) &preset );
1670
1671 if( presetName == BOM_PRESET::DefaultEditing().name )
1672 default_idx = idx;
1673
1674 idx++;
1675 }
1676
1677 m_cbBomPresets->Append( wxT( "---" ) );
1678 m_cbBomPresets->Append( _( "Save preset..." ) );
1679 m_cbBomPresets->Append( _( "Delete preset..." ) );
1680
1681 // At least the built-in presets should always be present
1682 wxASSERT( !m_bomPresets.empty() );
1683
1684 m_cbBomPresets->SetSelection( default_idx );
1685 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( default_idx ) );
1686}
1687
1688
1690{
1691 BOM_PRESET current = m_dataModel->GetBomSettings();
1692
1693 auto it = std::find_if( m_bomPresets.begin(), m_bomPresets.end(),
1694 [&]( const std::pair<const wxString, BOM_PRESET>& aPair )
1695 {
1696 const BOM_PRESET& preset = aPair.second;
1697
1698 // Check the simple settings first
1699 if( !( preset.sortAsc == current.sortAsc
1700 && preset.filterString == current.filterString
1701 && preset.groupSymbols == current.groupSymbols
1702 && preset.excludeDNP == current.excludeDNP
1703 && preset.includeExcludedFromBOM == current.includeExcludedFromBOM ) )
1704 {
1705 return false;
1706 }
1707
1708 // We should compare preset.name and current.name. Unfortunately current.name is
1709 // empty because m_dataModel->GetBomSettings() does not store the .name member.
1710 // So use sortField member as a (not very efficient) auxiliary filter.
1711 // As a further complication, sortField can be translated in m_bomPresets list, so
1712 // current.sortField needs to be translated.
1713 // Probably this not efficient and error prone test should be removed (JPC).
1714 if( preset.sortField != wxGetTranslation( current.sortField ) )
1715 return false;
1716
1717 // Only compare shown or grouped fields
1718 std::vector<BOM_FIELD> A, B;
1719
1720 for( const BOM_FIELD& field : preset.fieldsOrdered )
1721 {
1722 if( field.show || field.groupBy )
1723 A.emplace_back( field );
1724 }
1725
1726 for( const BOM_FIELD& field : current.fieldsOrdered )
1727 {
1728 if( field.show || field.groupBy )
1729 B.emplace_back( field );
1730 }
1731
1732 return A == B;
1733 } );
1734
1735 if( it != m_bomPresets.end() )
1736 {
1737 // Select the right m_cbBomPresets item.
1738 // but these items are translated if they are predefined items.
1739 bool do_translate = it->second.readOnly;
1740 wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
1741 m_cbBomPresets->SetStringSelection( text );
1742 }
1743 else
1744 {
1745 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 ); // separator
1746 }
1747
1748 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( m_cbBomPresets->GetSelection() ) );
1749}
1750
1751
1753{
1754 // Look at m_userBomPresets to know if aName is a read only preset, or a user preset.
1755 // Read-only presets have translated names in UI, so we have to use a translated name
1756 // in UI selection. But for a user preset name we search for the untranslated aName.
1757 wxString ui_label = aName;
1758
1759 for( const auto& [presetName, preset] : m_bomPresets )
1760 {
1761 if( presetName == aName )
1762 {
1763 if( preset.readOnly == true )
1764 ui_label = wxGetTranslation( aName );
1765
1766 break;
1767 }
1768 }
1769
1770 int idx = m_cbBomPresets->FindString( ui_label );
1771
1772 if( idx >= 0 && m_cbBomPresets->GetSelection() != idx )
1773 {
1774 m_cbBomPresets->SetSelection( idx );
1775 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( idx ) );
1776 }
1777 else if( idx < 0 )
1778 {
1779 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 ); // separator
1780 }
1781}
1782
1783
1785{
1786 int count = m_cbBomPresets->GetCount();
1787 int index = m_cbBomPresets->GetSelection();
1788
1789 auto resetSelection =
1790 [&]()
1791 {
1792 if( m_currentBomPreset )
1793 m_cbBomPresets->SetStringSelection( m_currentBomPreset->name );
1794 else
1795 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 );
1796 };
1797
1798 if( index == count - 3 )
1799 {
1800 // Separator: reject the selection
1801 resetSelection();
1802 return;
1803 }
1804 else if( index == count - 2 )
1805 {
1806 // Save current state to new preset
1807 wxString name;
1808
1811
1812 wxTextEntryDialog dlg( this, _( "BOM preset name:" ), _( "Save BOM Preset" ), name );
1813
1814 if( dlg.ShowModal() != wxID_OK )
1815 {
1816 resetSelection();
1817 return;
1818 }
1819
1820 name = dlg.GetValue();
1821 bool exists = m_bomPresets.count( name );
1822
1823 if( !exists )
1824 {
1825 m_bomPresets[name] = m_dataModel->GetBomSettings();
1826 m_bomPresets[name].readOnly = false;
1827 m_bomPresets[name].name = name;
1828 }
1829
1830 BOM_PRESET* preset = &m_bomPresets[name];
1831
1832 if( !exists )
1833 {
1834 index = m_cbBomPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
1835 }
1836 else if( preset->readOnly )
1837 {
1838 wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
1839 _( "Error" ), wxOK | wxICON_ERROR, this );
1840 resetSelection();
1841 return;
1842 }
1843 else
1844 {
1845 // Ask the user if they want to overwrite the existing preset
1846 if( !IsOK( this, _( "Overwrite existing preset?" ) ) )
1847 {
1848 resetSelection();
1849 return;
1850 }
1851
1852 *preset = m_dataModel->GetBomSettings();
1853 preset->name = name;
1854
1855 index = m_cbBomPresets->FindString( name );
1856
1857 if( m_bomPresetMRU.Index( name ) != wxNOT_FOUND )
1858 m_bomPresetMRU.Remove( name );
1859 }
1860
1861 m_currentBomPreset = preset;
1862 m_cbBomPresets->SetSelection( index );
1863 m_bomPresetMRU.Insert( name, 0 );
1864
1865 return;
1866 }
1867 else if( index == count - 1 )
1868 {
1869 // Delete a preset
1870 wxArrayString headers;
1871 std::vector<wxArrayString> items;
1872
1873 headers.Add( _( "Presets" ) );
1874
1875 for( const auto& [name, preset] : m_bomPresets )
1876 {
1877 if( !preset.readOnly )
1878 {
1879 wxArrayString item;
1880 item.Add( name );
1881 items.emplace_back( item );
1882 }
1883 }
1884
1885 EDA_LIST_DIALOG dlg( this, _( "Delete Preset" ), headers, items );
1886 dlg.SetListLabel( _( "Select preset:" ) );
1887
1888 if( dlg.ShowModal() == wxID_OK )
1889 {
1890 wxString presetName = dlg.GetTextSelection();
1891 int idx = m_cbBomPresets->FindString( presetName );
1892
1893 if( idx != wxNOT_FOUND )
1894 {
1895 m_bomPresets.erase( presetName );
1896
1897 m_cbBomPresets->Delete( idx );
1898 m_currentBomPreset = nullptr;
1899 }
1900
1901 if( m_bomPresetMRU.Index( presetName ) != wxNOT_FOUND )
1902 m_bomPresetMRU.Remove( presetName );
1903 }
1904
1905 resetSelection();
1906 return;
1907 }
1908
1909 BOM_PRESET* preset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( index ) );
1910 m_currentBomPreset = preset;
1911
1912 m_lastSelectedBomPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
1913
1914 if( preset )
1915 {
1916 doApplyBomPreset( *preset );
1918 m_currentBomPreset = preset;
1919
1920 if( !m_currentBomPreset->name.IsEmpty() )
1921 {
1922 if( m_bomPresetMRU.Index( preset->name ) != wxNOT_FOUND )
1923 m_bomPresetMRU.Remove( preset->name );
1924
1925 m_bomPresetMRU.Insert( preset->name, 0 );
1926 }
1927 }
1928}
1929
1930
1932{
1933 // Disable rebuilds while we're applying the preset otherwise we'll be
1934 // rebuilding the model constantly while firing off wx events
1935 m_dataModel->DisableRebuilds();
1936
1937 // Basically, we apply the BOM preset to the data model and then
1938 // update our UI to reflect resulting the data model state, not the preset.
1939 m_dataModel->ApplyBomPreset( aPreset, getSelectedVariants() );
1940
1941 // BOM Presets can add, but not remove, columns, so make sure the view controls
1942 // grid has all of them before starting
1943 for( int i = 0; i < m_dataModel->GetColsCount(); i++ )
1944 {
1945 const wxString& fieldName( m_dataModel->GetColFieldName( i ) );
1946 bool found = false;
1947
1948 for( int j = 0; j < m_viewControlsDataModel->GetNumberRows(); j++ )
1949 {
1950 if( m_viewControlsDataModel->GetCanonicalFieldName( j ) == fieldName )
1951 {
1952 found = true;
1953 break;
1954 }
1955 }
1956
1957 // Properties like label, etc. will be added in the next loop
1958 if( !found )
1959 AddField( fieldName, GetGeneratedFieldDisplayName( fieldName ), false, false );
1960 }
1961
1962 // Sync all fields
1963 for( int i = 0; i < m_viewControlsDataModel->GetNumberRows(); i++ )
1964 {
1965 const wxString& fieldName( m_viewControlsDataModel->GetCanonicalFieldName( i ) );
1966 int col = m_dataModel->GetFieldNameCol( fieldName );
1967
1968 if( col == -1 )
1969 {
1970 wxASSERT_MSG( true, "Fields control has a field not found in the data model." );
1971 continue;
1972 }
1973
1974 EESCHEMA_SETTINGS* cfg = m_parent->eeconfig();
1975 std::string fieldNameStr( fieldName.ToUTF8() );
1976
1977 // Set column labels
1978 const wxString& label = m_dataModel->GetColLabelValue( col );
1979 m_viewControlsDataModel->SetValue( i, LABEL_COLUMN, label );
1980 m_grid->SetColLabelValue( col, label );
1981
1982 if( cfg->m_FieldEditorPanel.field_widths.count( fieldNameStr ) )
1983 m_grid->SetColSize( col, cfg->m_FieldEditorPanel.field_widths.at( fieldNameStr ) );
1984
1985 // Set shown columns
1986 bool show = m_dataModel->GetShowColumn( col );
1987 m_viewControlsDataModel->SetValueAsBool( i, SHOW_FIELD_COLUMN, show );
1988
1989 if( show )
1990 m_grid->ShowCol( col );
1991 else
1992 m_grid->HideCol( col );
1993
1994 // Set grouped columns
1995 bool groupBy = m_dataModel->GetGroupColumn( col );
1996 m_viewControlsDataModel->SetValueAsBool( i, GROUP_BY_COLUMN, groupBy );
1997 }
1998
1999 m_grid->SetSortingColumn( m_dataModel->GetSortCol(), m_dataModel->GetSortAsc() );
2000 m_groupSymbolsBox->SetValue( m_dataModel->GetGroupingEnabled() );
2001 m_filter->ChangeValue( m_dataModel->GetFilter() );
2002
2004
2005 // This will rebuild all rows and columns in the model such that the order
2006 // and labels are right, then we refresh the shown grid data to match
2007 m_dataModel->EnableRebuilds();
2008 m_dataModel->RebuildRows();
2009
2010 if( m_nbPages->GetSelection() == 1 )
2012 else
2013 m_grid->ForceRefresh();
2014}
2015
2016
2017std::vector<BOM_FMT_PRESET> DIALOG_SYMBOL_FIELDS_TABLE::GetUserBomFmtPresets() const
2018{
2019 std::vector<BOM_FMT_PRESET> ret;
2020
2021 for( const auto& [name, preset] : m_bomFmtPresets )
2022 {
2023 if( !preset.readOnly )
2024 ret.emplace_back( preset );
2025 }
2026
2027 return ret;
2028}
2029
2030
2031void DIALOG_SYMBOL_FIELDS_TABLE::SetUserBomFmtPresets( std::vector<BOM_FMT_PRESET>& aPresetList )
2032{
2033 // Reset to defaults
2035
2036 for( const BOM_FMT_PRESET& preset : aPresetList )
2037 {
2038 if( m_bomFmtPresets.count( preset.name ) )
2039 continue;
2040
2041 m_bomFmtPresets[preset.name] = preset;
2042
2043 m_bomFmtPresetMRU.Add( preset.name );
2044 }
2045
2047}
2048
2049
2050void DIALOG_SYMBOL_FIELDS_TABLE::ApplyBomFmtPreset( const wxString& aPresetName )
2051{
2052 updateBomFmtPresetSelection( aPresetName );
2053
2054 wxCommandEvent dummy;
2056}
2057
2058
2060{
2061 m_currentBomFmtPreset = nullptr;
2063
2064 if( m_bomFmtPresets.count( aPreset.name ) )
2066
2069
2071 doApplyBomFmtPreset( aPreset );
2072}
2073
2074
2076{
2077 m_bomFmtPresets.clear();
2078 m_bomFmtPresetMRU.clear();
2079
2080 // Load the read-only defaults
2081 for( const BOM_FMT_PRESET& preset : BOM_FMT_PRESET::BuiltInPresets() )
2082 {
2083 m_bomFmtPresets[preset.name] = preset;
2084 m_bomFmtPresets[preset.name].readOnly = true;
2085
2086 m_bomFmtPresetMRU.Add( preset.name );
2087 }
2088}
2089
2090
2092{
2093 m_cbBomFmtPresets->Clear();
2094
2095 int idx = 0;
2096 int default_idx = 0;
2097
2098 for( const auto& [presetName, preset] : m_bomFmtPresets )
2099 {
2100 m_cbBomFmtPresets->Append( wxGetTranslation( presetName ), (void*) &preset );
2101
2102 if( presetName == BOM_FMT_PRESET::CSV().name )
2103 default_idx = idx;
2104
2105 idx++;
2106 }
2107
2108 m_cbBomFmtPresets->Append( wxT( "---" ) );
2109 m_cbBomFmtPresets->Append( _( "Save preset..." ) );
2110 m_cbBomFmtPresets->Append( _( "Delete preset..." ) );
2111
2112 // At least the built-in presets should always be present
2113 wxASSERT( !m_bomFmtPresets.empty() );
2114
2115 m_cbBomFmtPresets->SetSelection( default_idx );
2116 m_currentBomFmtPreset = static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( default_idx ) );
2117}
2118
2119
2121{
2123
2124 auto it = std::find_if( m_bomFmtPresets.begin(), m_bomFmtPresets.end(),
2125 [&]( const std::pair<const wxString, BOM_FMT_PRESET>& aPair )
2126 {
2127 return ( aPair.second.fieldDelimiter == current.fieldDelimiter
2128 && aPair.second.stringDelimiter == current.stringDelimiter
2129 && aPair.second.refDelimiter == current.refDelimiter
2130 && aPair.second.refRangeDelimiter == current.refRangeDelimiter
2131 && aPair.second.keepTabs == current.keepTabs
2132 && aPair.second.keepLineBreaks == current.keepLineBreaks );
2133 } );
2134
2135 if( it != m_bomFmtPresets.end() )
2136 {
2137 // Select the right m_cbBomFmtPresets item.
2138 // but these items are translated if they are predefined items.
2139 bool do_translate = it->second.readOnly;
2140 wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
2141
2142 m_cbBomFmtPresets->SetStringSelection( text );
2143 }
2144 else
2145 {
2146 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 ); // separator
2147 }
2148
2149 int idx = m_cbBomFmtPresets->GetSelection();
2150 m_currentBomFmtPreset = static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( idx ) );
2151}
2152
2153
2155{
2156 // look at m_userBomFmtPresets to know if aName is a read only preset, or a user preset.
2157 // Read only presets have translated names in UI, so we have to use a translated name in UI selection.
2158 // But for a user preset name we should search for aName (not translated)
2159 wxString ui_label = aName;
2160
2161 for( const auto& [presetName, preset] : m_bomFmtPresets )
2162 {
2163 if( presetName == aName )
2164 {
2165 if( preset.readOnly )
2166 ui_label = wxGetTranslation( aName );
2167
2168 break;
2169 }
2170 }
2171
2172 int idx = m_cbBomFmtPresets->FindString( ui_label );
2173
2174 if( idx >= 0 && m_cbBomFmtPresets->GetSelection() != idx )
2175 {
2176 m_cbBomFmtPresets->SetSelection( idx );
2177 m_currentBomFmtPreset = static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( idx ) );
2178 }
2179 else if( idx < 0 )
2180 {
2181 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 ); // separator
2182 }
2183}
2184
2185
2187{
2188 int count = m_cbBomFmtPresets->GetCount();
2189 int index = m_cbBomFmtPresets->GetSelection();
2190
2191 auto resetSelection =
2192 [&]()
2193 {
2195 m_cbBomFmtPresets->SetStringSelection( m_currentBomFmtPreset->name );
2196 else
2197 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 );
2198 };
2199
2200 if( index == count - 3 )
2201 {
2202 // Separator: reject the selection
2203 resetSelection();
2204 return;
2205 }
2206 else if( index == count - 2 )
2207 {
2208 // Save current state to new preset
2209 wxString name;
2210
2213
2214 wxTextEntryDialog dlg( this, _( "BOM preset name:" ), _( "Save BOM Preset" ), name );
2215
2216 if( dlg.ShowModal() != wxID_OK )
2217 {
2218 resetSelection();
2219 return;
2220 }
2221
2222 name = dlg.GetValue();
2223 bool exists = m_bomFmtPresets.count( name );
2224
2225 if( !exists )
2226 {
2228 m_bomFmtPresets[name].readOnly = false;
2229 m_bomFmtPresets[name].name = name;
2230 }
2231
2233
2234 if( !exists )
2235 {
2236 index = m_cbBomFmtPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
2237 }
2238 else if( preset->readOnly )
2239 {
2240 wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
2241 _( "Error" ), wxOK | wxICON_ERROR, this );
2242 resetSelection();
2243 return;
2244 }
2245 else
2246 {
2247 // Ask the user if they want to overwrite the existing preset
2248 if( !IsOK( this, _( "Overwrite existing preset?" ) ) )
2249 {
2250 resetSelection();
2251 return;
2252 }
2253
2254 *preset = GetCurrentBomFmtSettings();
2255 preset->name = name;
2256
2257 index = m_cbBomFmtPresets->FindString( name );
2258
2259 if( m_bomFmtPresetMRU.Index( name ) != wxNOT_FOUND )
2260 m_bomFmtPresetMRU.Remove( name );
2261 }
2262
2263 m_currentBomFmtPreset = preset;
2264 m_cbBomFmtPresets->SetSelection( index );
2265 m_bomFmtPresetMRU.Insert( name, 0 );
2266
2267 return;
2268 }
2269 else if( index == count - 1 )
2270 {
2271 // Delete a preset
2272 wxArrayString headers;
2273 std::vector<wxArrayString> items;
2274
2275 headers.Add( _( "Presets" ) );
2276
2277 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
2278 {
2279 if( !pair.second.readOnly )
2280 {
2281 wxArrayString item;
2282 item.Add( pair.first );
2283 items.emplace_back( item );
2284 }
2285 }
2286
2287 EDA_LIST_DIALOG dlg( this, _( "Delete Preset" ), headers, items );
2288 dlg.SetListLabel( _( "Select preset:" ) );
2289
2290 if( dlg.ShowModal() == wxID_OK )
2291 {
2292 wxString presetName = dlg.GetTextSelection();
2293 int idx = m_cbBomFmtPresets->FindString( presetName );
2294
2295 if( idx != wxNOT_FOUND )
2296 {
2297 m_bomFmtPresets.erase( presetName );
2298
2299 m_cbBomFmtPresets->Delete( idx );
2300 m_currentBomFmtPreset = nullptr;
2301 }
2302
2303 if( m_bomFmtPresetMRU.Index( presetName ) != wxNOT_FOUND )
2304 m_bomFmtPresetMRU.Remove( presetName );
2305 }
2306
2307 resetSelection();
2308 return;
2309 }
2310
2311 auto* preset = static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( index ) );
2312 m_currentBomFmtPreset = preset;
2313
2314 m_lastSelectedBomFmtPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
2315
2316 if( preset )
2317 {
2318 doApplyBomFmtPreset( *preset );
2320 m_currentBomFmtPreset = preset;
2321
2322 if( !m_currentBomFmtPreset->name.IsEmpty() )
2323 {
2324 if( m_bomFmtPresetMRU.Index( preset->name ) != wxNOT_FOUND )
2325 m_bomFmtPresetMRU.Remove( preset->name );
2326
2327 m_bomFmtPresetMRU.Insert( preset->name, 0 );
2328 }
2329 }
2330}
2331
2332
2334{
2335 m_textFieldDelimiter->ChangeValue( aPreset.fieldDelimiter );
2336 m_textStringDelimiter->ChangeValue( aPreset.stringDelimiter );
2337 m_textRefDelimiter->ChangeValue( aPreset.refDelimiter );
2338 m_textRefRangeDelimiter->ChangeValue( aPreset.refRangeDelimiter );
2339 m_checkKeepTabs->SetValue( aPreset.keepTabs );
2340 m_checkKeepLineBreaks->SetValue( aPreset.keepLineBreaks );
2341
2342 // Refresh the preview if that's the current page
2343 if( m_nbPages->GetSelection() == 1 )
2345}
2346
2347
2349{
2350 bool modified = false;
2351
2352 // Save our BOM presets
2353 std::vector<BOM_PRESET> presets;
2354
2355 for( const auto& [name, preset] : m_bomPresets )
2356 {
2357 if( !preset.readOnly )
2358 presets.emplace_back( preset );
2359 }
2360
2361 if( m_schSettings.m_BomPresets != presets )
2362 {
2363 modified = true;
2364 m_schSettings.m_BomPresets = presets;
2365 }
2366
2367 if( m_schSettings.m_BomSettings != m_dataModel->GetBomSettings() )
2368 {
2369 modified = true;
2370 m_schSettings.m_BomSettings = m_dataModel->GetBomSettings();
2371 }
2372
2373 // Save our BOM Format presets
2374 std::vector<BOM_FMT_PRESET> fmts;
2375
2376 for( const auto& [name, preset] : m_bomFmtPresets )
2377 {
2378 if( !preset.readOnly )
2379 fmts.emplace_back( preset );
2380 }
2381
2382 if( m_schSettings.m_BomFmtPresets != fmts )
2383 {
2384 modified = true;
2385 m_schSettings.m_BomFmtPresets = fmts;
2386 }
2387
2388 if( m_schSettings.m_BomFmtSettings != GetCurrentBomFmtSettings() )
2389 {
2390 modified = true;
2391 m_schSettings.m_BomFmtSettings = GetCurrentBomFmtSettings();
2392 }
2393
2394 if( modified )
2395 m_parent->OnModify();
2396}
2397
2398
2399void DIALOG_SYMBOL_FIELDS_TABLE::OnSchItemsAdded( SCHEMATIC& aSch, std::vector<SCH_ITEM*>& aSchItem )
2400{
2401 SCH_REFERENCE_LIST allRefs;
2402 m_parent->Schematic().Hierarchy().GetSymbols( allRefs );
2403
2404 for( SCH_ITEM* item : aSchItem )
2405 {
2406 if( item->Type() == SCH_SYMBOL_T )
2407 {
2408 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2409
2410 // Don't add power symbols
2411 if( !symbol->IsMissingLibSymbol() && symbol->IsPower() )
2412 continue;
2413
2414 // Add all fields again in case this symbol has a new one
2415 for( SCH_FIELD& field : symbol->GetFields() )
2416 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2417
2418 m_dataModel->AddReferences( getSymbolReferences( symbol, allRefs ) );
2419 }
2420 else if( item->Type() == SCH_SHEET_T )
2421 {
2422 std::set<SCH_SYMBOL*> symbols;
2423 SCH_REFERENCE_LIST refs = getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) );
2424
2425 for( SCH_REFERENCE& ref : refs )
2426 symbols.insert( ref.GetSymbol() );
2427
2428 for( SCH_SYMBOL* symbol : symbols )
2429 {
2430 // Add all fields again in case this symbol has a new one
2431 for( SCH_FIELD& field : symbol->GetFields() )
2432 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2433 }
2434
2435 m_dataModel->AddReferences( refs );
2436 }
2437 }
2438
2440 m_dataModel->RebuildRows();
2442}
2443
2444
2445void DIALOG_SYMBOL_FIELDS_TABLE::OnSchItemsRemoved( SCHEMATIC& aSch, std::vector<SCH_ITEM*>& aSchItem )
2446{
2447 for( SCH_ITEM* item : aSchItem )
2448 {
2449 if( item->Type() == SCH_SYMBOL_T )
2450 m_dataModel->RemoveSymbol( *static_cast<SCH_SYMBOL*>( item ) );
2451 else if( item->Type() == SCH_SHEET_T )
2452 m_dataModel->RemoveReferences( getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) ) );
2453 }
2454
2456 m_dataModel->RebuildRows();
2458}
2459
2460
2461void DIALOG_SYMBOL_FIELDS_TABLE::OnSchItemsChanged( SCHEMATIC& aSch, std::vector<SCH_ITEM*>& aSchItem )
2462{
2463 SCH_REFERENCE_LIST allRefs;
2464 m_parent->Schematic().Hierarchy().GetSymbols( allRefs );
2465
2466 for( SCH_ITEM* item : aSchItem )
2467 {
2468 if( item->Type() == SCH_SYMBOL_T )
2469 {
2470 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2471
2472 // Don't add power symbols
2473 if( !symbol->IsMissingLibSymbol() && symbol->IsPower() )
2474 continue;
2475
2476 // Add all fields again in case this symbol has a new one
2477 for( SCH_FIELD& field : symbol->GetFields() )
2478 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2479
2480 m_dataModel->UpdateReferences( getSymbolReferences( symbol, allRefs ), getSelectedVariants() );
2481 }
2482 else if( item->Type() == SCH_SHEET_T )
2483 {
2484 std::set<SCH_SYMBOL*> symbols;
2485 SCH_REFERENCE_LIST refs = getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) );
2486
2487 for( SCH_REFERENCE& ref : refs )
2488 symbols.insert( ref.GetSymbol() );
2489
2490 for( SCH_SYMBOL* symbol : symbols )
2491 {
2492 // Add all fields again in case this symbol has a new one
2493 for( SCH_FIELD& field : symbol->GetFields() )
2494 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2495 }
2496
2497 m_dataModel->UpdateReferences( refs, getSelectedVariants() );
2498 }
2499 }
2500
2502 m_dataModel->RebuildRows();
2504}
2505
2506
2508{
2509 m_dataModel->SetPath( aSch.CurrentSheet() );
2510
2511 if( m_dataModel->GetScope() != FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_ALL )
2512 {
2514 m_dataModel->RebuildRows();
2516 }
2517}
2518
2519
2521{
2522 m_grid->Connect( wxEVT_GRID_RANGE_SELECTED,
2523 wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected ),
2524 nullptr, this );
2525}
2526
2527
2529{
2530 m_grid->Disconnect( wxEVT_GRID_RANGE_SELECTED,
2531 wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected ),
2532 nullptr, this );
2533}
2534
2535
2537 SCH_REFERENCE_LIST& aCachedRefs )
2538{
2539 SCH_REFERENCE_LIST symbolRefs;
2540
2541 for( size_t i = 0; i < aCachedRefs.GetCount(); i++ )
2542 {
2543 SCH_REFERENCE& ref = aCachedRefs[i];
2544
2545 if( ref.GetSymbol() == aSymbol )
2546 {
2547 ref.Split(); // Figures out if we are annotated or not
2548 symbolRefs.AddItem( ref );
2549 }
2550 }
2551
2552 return symbolRefs;
2553}
2554
2555
2557{
2558 SCH_SHEET_LIST allSheets = m_parent->Schematic().Hierarchy();
2559 SCH_REFERENCE_LIST sheetRefs;
2560
2561 // We need to operate on all instances of the sheet
2562 for( const SCH_SHEET_INSTANCE& instance : aSheet.GetInstances() )
2563 {
2564 // For every sheet instance we need to get the current schematic sheet
2565 // instance that matches that particular sheet path from the root
2566 for( SCH_SHEET_PATH& basePath : allSheets )
2567 {
2568 if( basePath.Path() == instance.m_Path )
2569 {
2570 SCH_SHEET_PATH sheetPath = basePath;
2571 sheetPath.push_back( &aSheet );
2572
2573 // Create a list of all sheets in this path, starting with the path
2574 // of the sheet that we just deleted, then all of its subsheets
2575 SCH_SHEET_LIST subSheets;
2576 subSheets.push_back( sheetPath );
2577 allSheets.GetSheetsWithinPath( subSheets, sheetPath );
2578
2579 subSheets.GetSymbolsWithinPath( sheetRefs, sheetPath, false, false );
2580 break;
2581 }
2582 }
2583 }
2584
2585 for( SCH_REFERENCE& ref : sheetRefs )
2586 ref.Split();
2587
2588 return sheetRefs;
2589}
2590
2591
2592void DIALOG_SYMBOL_FIELDS_TABLE::onAddVariant( wxCommandEvent& aEvent )
2593{
2594 wxTextEntryDialog dlg( this, _( "Add new variant name:" ), _( "New Variant" ), wxEmptyString,
2595 wxOK | wxCANCEL | wxCENTER );
2596
2597 if( dlg.ShowModal() == wxID_CANCEL )
2598 return;
2599
2600 // Empty strings and duplicate variant names are not allowed.
2601 if( dlg.GetValue().IsEmpty() || ( m_variantListBox->FindString( dlg.GetValue() ) != wxNOT_FOUND ) )
2602 {
2603 wxBell();
2604 return;
2605 }
2606
2607 wxArrayString ctrlContents = m_variantListBox->GetStrings();
2608
2609 ctrlContents.Add( dlg.GetValue() );
2610 ctrlContents.Sort( SortVariantNames );
2611 m_variantListBox->Set( ctrlContents );
2612}
2613
2614
2616{
2617 wxArrayInt selections;
2618
2619 // An empty or default selection cannot be deleted.
2620 if( ( m_variantListBox->GetSelections( selections ) == 0 ) || ( selections[0] == 0 ) )
2621 {
2622 wxBell();
2623 return;
2624 }
2625
2626 wxArrayString ctrlContents = m_variantListBox->GetStrings();
2627
2628 for( int selection : selections )
2629 {
2630 wxString variantName = m_variantListBox->GetString( selection );
2631 ctrlContents.Remove( variantName );
2632 m_parent->Schematic().DeleteVariant( variantName );
2633 }
2634
2635 m_variantListBox->Set( ctrlContents );
2636}
2637
2638
2640{
2641 wxArrayInt selections;
2642
2643 // Only allow renaming a single selection that is not the default.
2644 if( ( m_variantListBox->GetSelections( selections ) != 1 ) || ( selections[0] == 0 ) )
2645 {
2646 wxBell();
2647 return;
2648 }
2649
2650 wxArrayString ctrlContents = m_variantListBox->GetStrings();
2651 wxString oldVariantName = ctrlContents[selections[0]];
2652
2653 wxTextEntryDialog dlg( this, _( "Add new variant name:" ), _( "New Variant" ), oldVariantName,
2654 wxOK | wxCANCEL | wxCENTER );
2655
2656 if( dlg.ShowModal() == wxID_CANCEL )
2657 return;
2658
2659 wxString newVariantName = dlg.GetValue();
2660
2661 if( newVariantName.IsEmpty() || ( newVariantName == oldVariantName ) || ( newVariantName == ctrlContents[0] ) )
2662 {
2663 wxBell();
2664 return;
2665 }
2666
2667 ctrlContents.Remove( m_variantListBox->GetString( selections[0] ) );
2668 ctrlContents.Add( newVariantName );
2669 ctrlContents.Sort( SortVariantNames );
2670 m_variantListBox->Set( ctrlContents );
2671}
2672
2673
2675{
2676 std::set<wxString> selectedVariants = getSelectedVariants();
2677
2678 m_dataModel->UpdateReferences( m_symbolsList, selectedVariants );
2679
2680 if( m_parent )
2681 {
2682 wxString selectedVariant = GetDefaultVariantName();
2683
2684 // Selecting more than one variant in the schematic editor doesn't make any sense. Select the first
2685 // variant in the table editor dialog as the variant to show in the schematic editor.
2686 if( selectedVariants.size() >= 1 )
2687 selectedVariant = *selectedVariants.cbegin();
2688
2689 m_parent->SetCurrentVariant( selectedVariant );
2690 }
2691}
2692
2693
2695{
2696 std::set<wxString> retv;
2697
2698 wxArrayInt selections;
2699
2700 if( m_variantListBox->GetSelections( selections ) )
2701 {
2702 for( int selection : selections )
2703 {
2704 if( selection == 0 )
2705 continue;
2706
2707 retv.emplace( m_variantListBox->GetString( selection ) );
2708 }
2709 }
2710
2711 return retv;
2712}
int index
const char * name
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
bool Empty() const
Definition commit.h:137
int vertPixelsFromDU(int y) const
Convert an integer number of dialog units to pixels, vertically.
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition dialog_shim.h:82
void SetupStandardButtons(std::map< int, wxString > aLabels={})
std::string m_hash_key
int horizPixelsFromDU(int x) const
Convert an integer number of dialog units to pixels, horizontally.
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
int ShowModal() override
DIALOG_SYMBOL_FIELDS_TABLE_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Symbol Fields Table"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER)
void OnTableColSize(wxGridSizeEvent &event) override
void OnSaveAndContinue(wxCommandEvent &aEvent) override
void OnSchItemsRemoved(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem) override
void onAddVariant(wxCommandEvent &aEvent) override
void OnPreviewRefresh(wxCommandEvent &event) override
void OnAddField(wxCommandEvent &event) override
SCH_REFERENCE_LIST getSheetSymbolReferences(SCH_SHEET &aSheet)
void SetUserBomPresets(std::vector< BOM_PRESET > &aPresetList)
void OnSidebarToggle(wxCommandEvent &event) override
void OnOk(wxCommandEvent &aEvent) override
void OnGroupSymbolsToggled(wxCommandEvent &event) override
void OnSchItemsAdded(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem) override
std::map< wxString, BOM_PRESET > m_bomPresets
void OnSchItemsChanged(SCHEMATIC &aSch, std::vector< SCH_ITEM * > &aSchItem) override
void ApplyBomFmtPreset(const wxString &aPresetName)
void ShowHideColumn(int aCol, bool aShow)
VIEW_CONTROLS_GRID_DATA_MODEL * m_viewControlsDataModel
std::set< wxString > getSelectedVariants() const
void setSideBarButtonLook(bool aIsLeftPanelCollapsed)
FIELDS_EDITOR_GRID_DATA_MODEL * m_dataModel
void updateBomPresetSelection(const wxString &aName)
void updateBomFmtPresetSelection(const wxString &aName)
void OnFilterText(wxCommandEvent &aEvent) override
std::map< FIELD_T, int > m_mandatoryFieldListIndexes
void OnRemoveField(wxCommandEvent &event) override
void OnTableCellClick(wxGridEvent &event) override
void onVariantSelectionChange(wxCommandEvent &aEvent) override
void doApplyBomFmtPreset(const BOM_FMT_PRESET &aPreset)
void OnScope(wxCommandEvent &event) override
void onBomPresetChanged(wxCommandEvent &aEvent)
void OnTableValueChanged(wxGridEvent &event) override
void OnExport(wxCommandEvent &aEvent) override
void OnClose(wxCloseEvent &aEvent) override
void OnSchSheetChanged(SCHEMATIC &aSch) override
void onDeleteVariant(wxCommandEvent &aEvent) override
void OnTableRangeSelected(wxGridRangeSelectEvent &aEvent)
void OnMenu(wxCommandEvent &event) override
void setScope(FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE aScope)
std::vector< BOM_FMT_PRESET > GetUserBomFmtPresets() const
void OnCancel(wxCommandEvent &aEvent) override
void OnFilterMouseMoved(wxMouseEvent &event) override
std::map< wxString, BOM_FMT_PRESET > m_bomFmtPresets
DIALOG_SYMBOL_FIELDS_TABLE(SCH_EDIT_FRAME *parent, JOB_EXPORT_SCH_BOM *aJob=nullptr)
BOM_FMT_PRESET GetCurrentBomFmtSettings()
Returns a formatting configuration corresponding to the values in the UI controls of the dialog.
void AddField(const wxString &displayName, const wxString &aCanonicalName, bool show, bool groupBy, bool addedByUser=false)
SCH_REFERENCE_LIST getSymbolReferences(SCH_SYMBOL *aSymbol, SCH_REFERENCE_LIST &aCachedRefs)
void doApplyBomPreset(const BOM_PRESET &aPreset)
void OnPageChanged(wxNotebookEvent &event) override
void SetUserBomFmtPresets(std::vector< BOM_FMT_PRESET > &aPresetList)
void OnRegroupSymbols(wxCommandEvent &aEvent) override
void OnViewControlsCellChanged(wxGridEvent &aEvent) override
void onRenameVariant(wxCommandEvent &aEvent) override
std::vector< BOM_PRESET > GetUserBomPresets() const
void OnOutputFileBrowseClicked(wxCommandEvent &event) override
void LoadFieldNames()
Construct the rows of m_fieldsCtrl and the columns of m_dataModel from a union of all field names in ...
void onBomFmtPresetChanged(wxCommandEvent &aEvent)
void ApplyBomPreset(const wxString &aPresetName)
void OnSizeViewControlsGrid(wxSizeEvent &event) override
void OnRenameField(wxCommandEvent &event) override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
EDA_ITEM * GetParent() const
Definition eda_item.h:112
A dialog which shows:
wxString GetTextSelection(int aColumn=0)
Return the selected text from aColumn in the wxListCtrl in the dialog.
void SetListLabel(const wxString &aLabel)
PANEL_SYMBOL_FIELDS_TABLE m_FieldEditorPanel
static const wxString ITEM_NUMBER_VARIABLE
static const wxString QUANTITY_VARIABLE
VIEW_CONTROLS_GRID_DATA_MODEL * m_viewControlsDataModel
DIALOG_SYMBOL_FIELDS_TABLE * m_dlg
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
void doPopupSelection(wxCommandEvent &event) override
FIELDS_EDITOR_GRID_TRICKS(DIALOG_SYMBOL_FIELDS_TABLE *aParent, WX_GRID *aGrid, VIEW_CONTROLS_GRID_DATA_MODEL *aViewFieldsData, FIELDS_EDITOR_GRID_DATA_MODEL *aDataModel, EMBEDDED_FILES *aFiles)
FIELDS_EDITOR_GRID_DATA_MODEL * m_dataModel
A general-purpose text renderer for WX_GRIDs backed by WX_GRID_TABLE_BASE tables that can handle draw...
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:61
GRID_TRICKS(WX_GRID *aGrid)
virtual void doPopupSelection(wxCommandEvent &event)
virtual void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent)
WX_GRID * m_grid
I don't own the grid, but he owns me.
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
static REPORTER & GetInstance()
Definition reporter.cpp:96
static SEARCH_STACK * SchSearchS(PROJECT *aProject)
Accessor for Eeschema search stack.
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition project.cpp:391
Holds all the data relating to one schematic.
Definition schematic.h:88
bool ResolveTextVar(const SCH_SHEET_PATH *aSheetPath, wxString *token, int aDepth) const
wxArrayString GetVariantNamesForUI() const
Return an array of variant names for using in wxWidgets UI controls.
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:186
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
Handle actions specific to the schematic editor.
Schematic editor (Eeschema) main window.
SCHEMATIC & Schematic() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
void AddItem(const SCH_REFERENCE &aItem)
A helper to define a symbol's reference designator in a schematic.
void Split()
Attempt to split the reference designator into a name (U) and number (1).
SCH_SYMBOL * GetSymbol() const
void SyncSelection(const std::optional< SCH_SHEET_PATH > &targetSheetPath, SCH_ITEM *focusItem, const std::vector< SCH_ITEM * > &items)
int ClearSelection(const TOOL_EVENT &aEvent)
Select all visible items in sheet.
SCH_SELECTION & GetSelection()
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void GetSheetsWithinPath(std::vector< SCH_SHEET_PATH > &aSheets, const SCH_SHEET_PATH &aSheetPath) const
Add a SCH_SHEET_PATH object to aSheets for each sheet in the list that are contained within aSheetPat...
void GetSymbolsWithinPath(SCH_REFERENCE_LIST &aReferences, const SCH_SHEET_PATH &aSheetPath, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets that are contained wi...
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:47
const std::vector< SCH_SHEET_INSTANCE > & GetInstances() const
Definition sch_sheet.h:478
Schematic symbol object.
Definition sch_symbol.h:76
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
bool IsMissingLibSymbol() const
Check to see if the library symbol is set to the dummy library symbol.
bool IsPower() const override
Master controller class:
void doPopupSelection(wxCommandEvent &event) override
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:509
wxString GetGeneratedFieldDisplayName(const wxString &aSource)
Returns any variables unexpanded, e.g.
Definition common.cpp:274
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:61
bool EnsureFileDirectoryExists(wxFileName *aTargetFullFileName, const wxString &aBaseFilename, REPORTER *aReporter)
Make aTargetFullFileName absolute and create the path of this file if it doesn't yet exist.
Definition common.cpp:530
bool IsGeneratedField(const wxString &aSource)
Returns true if the string is generated, e.g contains a single text var reference.
Definition common.cpp:286
The common library.
int OKOrCancelDialog(wxWindow *aParent, const wxString &aWarning, const wxString &aMessage, const wxString &aDetailedMessage, const wxString &aOKLabel, const wxString &aCancelLabel, bool *aApplyToAll)
Display a warning dialog with aMessage and returns the user response.
Definition confirm.cpp:150
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:259
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition confirm.cpp:230
bool HandleUnsavedChanges(wxWindow *aParent, const wxString &aMessage, const std::function< bool()> &aSaveFunction)
Display a dialog with Save, Cancel and Discard Changes buttons.
Definition confirm.cpp:131
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:202
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:177
This file is part of the common library.
#define COLUMN_MARGIN
@ MYID_SELECT_FOOTPRINT
wxDEFINE_EVENT(EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, wxCommandEvent)
FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE SCOPE
#define _(s)
bool GetAssociatedDocument(wxWindow *aParent, const wxString &aDocName, PROJECT *aProject, SEARCH_STACK *aPaths, std::vector< EMBEDDED_FILES * > aFilesStack)
Open a document (file) with the suitable browser.
Definition eda_doc.cpp:63
This file is part of the common library.
#define LABEL_COLUMN
#define DISPLAY_NAME_COLUMN
#define GROUP_BY_COLUMN
#define SHOW_FIELD_COLUMN
@ FRAME_FOOTPRINT_CHOOSER
Definition frame_type.h:44
@ GRIDTRICKS_FIRST_SHOWHIDE
Definition grid_tricks.h:51
@ GRIDTRICKS_FIRST_CLIENT_ID
Definition grid_tricks.h:48
static const std::string CsvFileExtension
static wxString CsvFileWildcard()
KICOMMON_API wxSize GetTextSize(const wxString &aSingleLine, wxWindow *aWindow)
Return the size of aSingleLine of text when it is rendered in aWindow using whatever font is currentl...
Definition ui_common.cpp:78
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:100
@ HIGHLIGHT_SYMBOL
std::vector< FAB_LAYER_COLOR > dummy
wxString GetDefaultVariantName()
int SortVariantNames(const wxString &aLhs, const wxString &aRhs)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
wxString label
wxString name
wxString fieldDelimiter
static BOM_FMT_PRESET CSV()
static std::vector< BOM_FMT_PRESET > BuiltInPresets()
wxString stringDelimiter
wxString refRangeDelimiter
wxString refDelimiter
wxString name
static BOM_PRESET DefaultEditing()
wxString sortField
bool groupSymbols
std::vector< BOM_FIELD > fieldsOrdered
static std::vector< BOM_PRESET > BuiltInPresets()
bool excludeDNP
wxString filterString
A simple container for sheet instance information.
Hold a name of a symbol's field, field value, and default visibility.
wxString GetDefaultFieldName(FIELD_T aFieldId, bool aTranslateForHI)
Return a default symbol field name for a mandatory field type.
#define DO_TRANSLATE
#define MANDATORY_FIELDS
FIELD_T
The set of all field indices assuming an array like sequence that a SCH_COMPONENT or LIB_PART can hol...
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
wxString GetCanonicalFieldName(FIELD_T aFieldType)
std::string path
@ SCH_SYMBOL_T
Definition typeinfo.h:176
@ SCH_SHEET_T
Definition typeinfo.h:179
Definition of file extensions used in Kicad.