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:
81 FIELDS_EDITOR_GRID_TRICKS( DIALOG_SHIM* aParent, WX_GRID* aGrid, wxDataViewListCtrl* aFieldsCtrl,
82 FIELDS_EDITOR_GRID_DATA_MODEL* aDataModel, EMBEDDED_FILES* aFiles ) :
83 GRID_TRICKS( aGrid ),
84 m_dlg( aParent ),
85 m_fieldsCtrl( aFieldsCtrl ),
86 m_dataModel( aDataModel ),
87 m_files( aFiles )
88 {}
89
90protected:
91 void showPopupMenu( wxMenu& menu, wxGridEvent& aEvent ) override
92 {
93 int col = m_grid->GetGridCursorCol();
94
95 if( m_dataModel->GetColFieldName( col ) == GetCanonicalFieldName( FIELD_T::FOOTPRINT ) )
96 {
97 menu.Append( MYID_SELECT_FOOTPRINT, _( "Select Footprint..." ), _( "Browse for footprint" ) );
98 menu.AppendSeparator();
99 }
100 else if( m_dataModel->GetColFieldName( col ) == GetCanonicalFieldName( FIELD_T::DATASHEET ) )
101 {
102 menu.Append( MYID_SHOW_DATASHEET, _( "Show Datasheet" ), _( "Show datasheet in browser" ) );
103 menu.AppendSeparator();
104 }
105
106 GRID_TRICKS::showPopupMenu( menu, aEvent );
107 }
108
109 void doPopupSelection( wxCommandEvent& event ) override
110 {
111 int row = m_grid->GetGridCursorRow();
112 int col = m_grid->GetGridCursorCol();
113
114 if( event.GetId() == MYID_SELECT_FOOTPRINT )
115 {
116 // pick a footprint using the footprint picker.
117 wxString fpid = m_grid->GetCellValue( row, col );
118
119 if( KIWAY_PLAYER* frame = m_dlg->Kiway().Player( FRAME_FOOTPRINT_CHOOSER, true, m_dlg ) )
120 {
121 if( frame->ShowModal( &fpid, m_dlg ) )
122 m_grid->SetCellValue( row, col, fpid );
123
124 frame->Destroy();
125 }
126 }
127 else if (event.GetId() == MYID_SHOW_DATASHEET )
128 {
129 wxString datasheet_uri = m_grid->GetCellValue( row, col );
130 GetAssociatedDocument( m_dlg, datasheet_uri, &m_dlg->Prj(),
131 PROJECT_SCH::SchSearchS( &m_dlg->Prj() ), { m_files } );
132 }
133 else
134 {
135 // We have grid tricks events to show/hide the columns from the popup menu
136 // and we need to make sure the data model is updated to match the grid,
137 // so do it through our code instead
138 if( event.GetId() >= GRIDTRICKS_FIRST_SHOWHIDE )
139 {
140 // Pop-up column order is the order of the shown fields, not the
141 // fieldsCtrl order
142 col = event.GetId() - GRIDTRICKS_FIRST_SHOWHIDE;
143
144 bool show = !m_dataModel->GetShowColumn( col );
145
146 // Convert data model column to by iterating over m_fieldsCtrl rows
147 // and finding the matching field name
148 wxString fieldName = m_dataModel->GetColFieldName( col );
149
150 for( row = 0; row < m_fieldsCtrl->GetItemCount(); row++ )
151 {
152 if( m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN ) == fieldName )
153 {
154 if( m_grid->CommitPendingChanges( false ) )
155 m_fieldsCtrl->SetToggleValue( show, row, SHOW_FIELD_COLUMN );
156
157 break;
158 }
159 }
160 }
161 else
162 {
164 }
165 }
166 }
167
168private:
170 wxDataViewListCtrl* m_fieldsCtrl;
173};
174
175
177 JOB_EXPORT_SCH_BOM* aJob ) :
179 m_currentBomPreset( nullptr ),
180 m_lastSelectedBomPreset( nullptr ),
181 m_parent( parent ),
182 m_schSettings( parent->Schematic().Settings() ),
183 m_job( aJob )
184{
185 // Get all symbols from the list of schematic sheets
187
188 m_bRefresh->SetBitmap( KiBitmapBundle( BITMAPS::small_refresh ) );
189 m_bRefreshPreview->SetBitmap( KiBitmapBundle( BITMAPS::small_refresh ) );
190 m_browseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
191
192 m_addFieldButton->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
193 m_removeFieldButton->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
194 m_renameFieldButton->SetBitmap( KiBitmapBundle( BITMAPS::small_edit ) );
195
196 m_fieldsCtrl->AppendTextColumn( _( "Field" ), wxDATAVIEW_CELL_INERT, 0, wxALIGN_LEFT, 0 );
197 m_fieldsCtrl->AppendTextColumn( _( "Column Label" ), wxDATAVIEW_CELL_EDITABLE, 0, wxALIGN_LEFT, 0 );
198
199 // The two next columns look better when on 2 lines
200 // Unfortunately this is not handled on WXMSW
201#ifdef __WXMSW__
202 m_fieldsCtrl->AppendToggleColumn( _( "Show Column" ), wxDATAVIEW_CELL_ACTIVATABLE, 0, wxALIGN_CENTER, 0 );
203 m_fieldsCtrl->AppendToggleColumn( _( "Group By" ), wxDATAVIEW_CELL_ACTIVATABLE, 0, wxALIGN_CENTER, 0 );
204#else
205 m_fieldsCtrl->AppendToggleColumn( _( "Show\nColumn" ), wxDATAVIEW_CELL_ACTIVATABLE, 0, wxALIGN_CENTER, 0 );
206 m_fieldsCtrl->AppendToggleColumn( _( "Group\nBy" ), wxDATAVIEW_CELL_ACTIVATABLE, 0, wxALIGN_CENTER, 0 );
207#endif
208
209 // GTK asserts if the number of columns doesn't match the data, but we still don't want
210 // to display the canonical names. So we'll insert a column for them, but keep it 0 width.
211 m_fieldsCtrl->AppendTextColumn( _( "Name" ), wxDATAVIEW_CELL_INERT, 0, wxALIGN_LEFT, 0 );
212
213 // SetWidth( wxCOL_WIDTH_AUTOSIZE ) fails here on GTK, so we calculate the title sizes and
214 // set the column widths ourselves.
215 wxDataViewColumn* column = m_fieldsCtrl->GetColumn( SHOW_FIELD_COLUMN );
216#ifdef __WXMSW__
217 m_showColWidth = KIUI::GetTextSize( column->GetTitle(), m_fieldsCtrl ).x,
218#else
219 m_showColWidth = std::max( KIUI::GetTextSize( column->GetTitle().Before( '\n' ), m_fieldsCtrl ).x,
220 KIUI::GetTextSize( column->GetTitle().After( '\n' ), m_fieldsCtrl ).x );
221#endif
223 column->SetMinWidth( m_showColWidth );
224
225 column = m_fieldsCtrl->GetColumn( GROUP_BY_COLUMN );
226#ifdef __WXMSW__
227 m_groupByColWidth = KIUI::GetTextSize( column->GetTitle(), m_fieldsCtrl ).x,
228#else
229 m_groupByColWidth = std::max( KIUI::GetTextSize( column->GetTitle().Before( '\n' ), m_fieldsCtrl ).x,
230 KIUI::GetTextSize( column->GetTitle().After( '\n' ), m_fieldsCtrl ).x );
231#endif
233 column->SetMinWidth( m_groupByColWidth );
234
235 // The fact that we're a list should keep the control from reserving space for the
236 // expander buttons... but it doesn't. Fix by forcing the indent to 0.
237 m_fieldsCtrl->SetIndent( 0 );
238
239 m_filter->SetDescriptiveText( _( "Filter" ) );
240
241 wxGridCellAttr* attr = new wxGridCellAttr;
242 attr->SetEditor( new GRID_CELL_URL_EDITOR( this, PROJECT_SCH::SchSearchS( &Prj() ),
243 { &m_parent->Schematic() } ) );
245
246 LoadFieldNames(); // loads rows into m_fieldsCtrl and columns into m_dataModel
247
248 // Now that the fields are loaded we can set the initial location of the splitter
249 // based on the list width. Again, SetWidth( wxCOL_WIDTH_AUTOSIZE ) fails us on GTK.
251 m_labelColWidth = 0;
252
253 for( int row = 0; row < m_fieldsCtrl->GetItemCount(); ++row )
254 {
255 const wxString& displayName = m_fieldsCtrl->GetTextValue( row, DISPLAY_NAME_COLUMN );
257
258 const wxString& label = m_fieldsCtrl->GetTextValue( row, LABEL_COLUMN );
260 }
261
262 int colWidth = std::max( m_fieldNameColWidth, m_labelColWidth ) + 30;
263
264 int fieldsMinWidth = colWidth + colWidth + m_groupByColWidth + m_showColWidth;
265
266 m_fieldsCtrl->GetColumn( DISPLAY_NAME_COLUMN )->SetWidth( m_fieldNameColWidth );
267 m_fieldsCtrl->GetColumn( LABEL_COLUMN )->SetWidth( m_labelColWidth );
268
269 // This is used for data only. Don't show it to the user.
270 m_fieldsCtrl->GetColumn( FIELD_NAME_COLUMN )->SetHidden( true );
271
272 m_splitterMainWindow->SetMinimumPaneSize( fieldsMinWidth );
273 m_splitterMainWindow->SetSashPosition( fieldsMinWidth + 40 );
274
275 m_grid->UseNativeColHeader( true );
276 m_grid->SetTable( m_dataModel, true );
277
278 // must be done after SetTable(), which appears to re-set it
279 m_grid->SetSelectionMode( wxGrid::wxGridSelectCells );
280
281 // add Cut, Copy, and Paste to wxGrid
282 m_grid->PushEventHandler( new FIELDS_EDITOR_GRID_TRICKS( this, m_grid, m_fieldsCtrl,
284
285 // Load our BOM view presets
287
289
290 if( m_job )
291 {
292 SetTitle( m_job->GetSettingsDialogTitle() );
293
294 preset.name = m_job->m_bomPresetName;
295 preset.excludeDNP = m_job->m_excludeDNP;
298 preset.sortAsc = m_job->m_sortAsc;
299 preset.sortField = m_job->m_sortField;
300 preset.groupSymbols = ( m_job->m_fieldsGroupBy.size() > 0 );
301
302 preset.fieldsOrdered.clear();
303
304 size_t i = 0;
305
306 for( const wxString& fieldName : m_job->m_fieldsOrdered )
307 {
308 BOM_FIELD field;
309 field.name = fieldName;
310 field.show = !fieldName.StartsWith( wxT( "__" ), &field.name );
312
313 if( ( m_job->m_fieldsLabels.size() > i ) && !m_job->m_fieldsLabels[i].IsEmpty() )
314 field.label = m_job->m_fieldsLabels[i];
315 else if( IsGeneratedField( field.name ) )
316 field.label = GetGeneratedFieldDisplayName( field.name );
317 else
318 field.label = field.name;
319
320 preset.fieldsOrdered.emplace_back( field );
321 i++;
322 }
323 }
324
325 // DIALOG_SHIM needs a unique hash_key because classname will be the same for both job and
326 // non-job versions (which have different sizes).
327 m_hash_key = TO_UTF8( GetTitle() );
328
329 ApplyBomPreset( preset );
331
332 // Load BOM export format presets
335
336 if( m_job )
337 {
338 fmtPreset.name = m_job->m_bomFmtPresetName;
341 fmtPreset.keepTabs = m_job->m_keepTabs;
342 fmtPreset.refDelimiter = m_job->m_refDelimiter;
345 }
346
347 ApplyBomFmtPreset( fmtPreset );
349
351 m_grid->ClearSelection();
352
354
356
359
360 wxSize dlgSize( panelCfg.width > 0 ? panelCfg.width : horizPixelsFromDU( 600 ),
361 panelCfg.height > 0 ? panelCfg.height : vertPixelsFromDU( 300 ) );
362 SetSize( dlgSize );
363
364 m_nbPages->SetSelection( cfg->m_FieldEditorPanel.page );
365
367 {
368 case 0: m_radioHighlight->SetValue( true ); break;
369 case 1: m_radioSelect->SetValue( true ); break;
370 case 2: m_radioOff->SetValue( true ); break;
371 }
372
373 switch( cfg->m_FieldEditorPanel.scope )
374 {
375 case SCOPE::SCOPE_ALL: m_radioProject->SetValue( true ); break;
376 case SCOPE::SCOPE_SHEET: m_radioCurrentSheet->SetValue( true ); break;
377 case SCOPE::SCOPE_SHEET_RECURSIVE: m_radioRecursive->SetValue( true ); break;
378 }
379
380 if( m_job )
382 else
384
385 Center();
386
387 // Connect Events
388 m_grid->Connect( wxEVT_GRID_COL_SORT, wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColSort ),
389 nullptr, this );
390 m_grid->Connect( wxEVT_GRID_COL_MOVE, wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColMove ),
391 nullptr, this );
394 m_fieldsCtrl->Bind( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &DIALOG_SYMBOL_FIELDS_TABLE::OnColLabelChange,
395 this );
396
397 if( !m_job )
398 {
399 // Start listening for schematic changes
400 m_parent->Schematic().AddListener( this );
401 }
402 else
403 {
404 // Don't allow editing
405 m_grid->EnableEditing( false );
406 m_buttonApply->Hide();
407 m_buttonExport->Hide();
408
410 }
411}
412
413
415{
416 wxGridCellAttr* attr = new wxGridCellAttr;
417 attr->SetReadOnly( false );
418
419 // Set some column types to specific editors
420 if( m_dataModel->ColIsReference( aCol ) )
421 {
422 attr->SetReadOnly();
423 m_dataModel->SetColAttr( attr, aCol );
424 }
425 else if( m_dataModel->GetColFieldName( aCol ) == GetCanonicalFieldName( FIELD_T::FOOTPRINT ) )
426 {
427 attr->SetEditor( new GRID_CELL_FPID_EDITOR( this, wxEmptyString ) );
428 m_dataModel->SetColAttr( attr, aCol );
429 }
430 else if( m_dataModel->GetColFieldName( aCol ) == GetCanonicalFieldName( FIELD_T::DATASHEET ) )
431 {
432 // set datasheet column viewer button
433 attr->SetEditor( new GRID_CELL_URL_EDITOR( this, PROJECT_SCH::SchSearchS( &Prj() ),
434 { &m_parent->Schematic() } ) );
435 m_dataModel->SetColAttr( attr, aCol );
436 }
437 else if( m_dataModel->ColIsQuantity( aCol ) || m_dataModel->ColIsItemNumber( aCol ) )
438 {
439 attr->SetReadOnly();
440 attr->SetAlignment( wxALIGN_RIGHT, wxALIGN_CENTER );
441 attr->SetRenderer( new wxGridCellNumberRenderer() );
442 m_dataModel->SetColAttr( attr, aCol );
443 }
444 else if( m_dataModel->ColIsAttribute( aCol ) )
445 {
446 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
447 attr->SetRenderer( new GRID_CELL_CHECKBOX_RENDERER() );
448 attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
449 m_dataModel->SetColAttr( attr, aCol );
450 }
451 else if( IsGeneratedField( m_dataModel->GetColFieldName( aCol ) ) )
452 {
453 attr->SetReadOnly();
454 m_dataModel->SetColAttr( attr, aCol );
455 }
456 else
457 {
458 attr->SetEditor( m_grid->GetDefaultEditor() );
459 m_dataModel->SetColAttr( attr, aCol );
460 }
461}
462
463
465{
467 wxSize defaultDlgSize = ConvertDialogToPixels( wxSize( 600, 300 ) );
468
469 // Restore column sorting order and widths
470 m_grid->AutoSizeColumns( false );
471 int sortCol = 0;
472 bool sortAscending = true;
473
474 for( int col = 0; col < m_grid->GetNumberCols(); ++col )
475 {
477
478 if( col == m_dataModel->GetSortCol() )
479 {
480 sortCol = col;
481 sortAscending = m_dataModel->GetSortAsc();
482 }
483 }
484
485 // sync m_grid's column visibilities to Show checkboxes in m_fieldsCtrl
486 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); ++i )
487 {
488 int col = m_dataModel->GetFieldNameCol( m_fieldsCtrl->GetTextValue( i, FIELD_NAME_COLUMN ) );
489
490 if( col == -1 )
491 continue;
492
493 bool show = m_fieldsCtrl->GetToggleValue( i, SHOW_FIELD_COLUMN );
494 m_dataModel->SetShowColumn( col, show );
495
496 if( show )
497 {
498 m_grid->ShowCol( col );
499
500 std::string key( m_dataModel->GetColFieldName( col ).ToUTF8() );
501
502 if( cfg->m_FieldEditorPanel.field_widths.count( key )
503 && ( cfg->m_FieldEditorPanel.field_widths.at( key ) > 0 ) )
504 {
505 m_grid->SetColSize( col, cfg->m_FieldEditorPanel.field_widths.at( key ) );
506 }
507 else
508 {
509 int textWidth = m_dataModel->GetDataWidth( col ) + COLUMN_MARGIN;
510 int maxWidth = defaultDlgSize.x / 3;
511
512 m_grid->SetColSize( col, std::clamp( textWidth, 100, maxWidth ) );
513 }
514 }
515 else
516 {
517 m_grid->HideCol( col );
518 }
519 }
520
521 m_dataModel->SetSorting( sortCol, sortAscending );
522 m_grid->SetSortingColumn( sortCol, sortAscending );
523}
524
525
527{
528 // Disconnect Events
529 m_grid->Disconnect( wxEVT_GRID_COL_SORT, wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColSort ),
530 nullptr, this );
531 m_grid->Disconnect( wxEVT_GRID_COL_SORT, wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColMove ),
532 nullptr, this );
533
534 // Delete the GRID_TRICKS.
535 m_grid->PopEventHandler( true );
536
537 // we gave ownership of m_dataModel to the wxGrid...
538}
539
540
542{
543 if( !wxDialog::TransferDataFromWindow() )
544 return false;
545
547 SCH_SELECTION_TOOL* selectionTool = toolMgr->GetTool<SCH_SELECTION_TOOL>();
548 SCH_SELECTION& selection = selectionTool->GetSelection();
549 SCH_SYMBOL* symbol = nullptr;
550
551 UpdateScope();
552
553 if( selection.GetSize() == 1 )
554 {
555 EDA_ITEM* item = selection.Front();
556
557 if( item->Type() == SCH_SYMBOL_T )
558 symbol = (SCH_SYMBOL*) item;
559 else if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T )
560 symbol = (SCH_SYMBOL*) item->GetParent();
561 }
562
563 if( symbol )
564 {
565 for( int row = 0; row < m_dataModel->GetNumberRows(); ++row )
566 {
567 std::vector<SCH_REFERENCE> references = m_dataModel->GetRowReferences( row );
568 bool found = false;
569
570 for( const SCH_REFERENCE& ref : references )
571 {
572 if( ref.GetSymbol() == symbol )
573 {
574 found = true;
575 break;
576 }
577 }
578
579 if( found )
580 {
581 // Find the value column and the reference column if they're shown
582 int valueCol = -1;
583 int refCol = -1;
584 int anyCol = -1;
585
586 for( int col = 0; col < m_dataModel->GetNumberCols(); col++ )
587 {
588 if( m_dataModel->ColIsValue( col ) )
589 valueCol = col;
590 else if( m_dataModel->ColIsReference( col ) )
591 refCol = col;
592 else if( anyCol == -1 && m_dataModel->GetShowColumn( col ) )
593 anyCol = col;
594 }
595
596 if( valueCol != -1 && m_dataModel->GetShowColumn( valueCol ) )
597 m_grid->GoToCell( row, valueCol );
598 else if( refCol != -1 && m_dataModel->GetShowColumn( refCol ) )
599 m_grid->GoToCell( row, refCol );
600 else if( anyCol != -1 )
601 m_grid->GoToCell( row, anyCol );
602
603 break;
604 }
605 }
606 }
607
608 // We don't want table range selection events to happen until we've loaded the data or we
609 // we'll clear our selection as the grid is built before the code above can get the
610 // user's current selection.
612
613 return true;
614}
615
616
618{
620 return false;
621
622 if( !wxDialog::TransferDataFromWindow() )
623 return false;
624
625 if( m_job )
626 {
627 // and exit, don't even dream of saving changes from the data model
628 return true;
629 }
630
631 SCH_COMMIT commit( m_parent );
632 SCH_SHEET_PATH currentSheet = m_parent->GetCurrentSheet();
633
635
636 commit.Push( wxS( "Symbol Fields Table Edit" ) );
637
638 // Reset the view to where we left the user
639 m_parent->SetCurrentSheet( currentSheet );
641 m_parent->Refresh();
642
644
645 return true;
646}
647
648
649void DIALOG_SYMBOL_FIELDS_TABLE::AddField( const wxString& aFieldName, const wxString& aLabelValue,
650 bool show, bool groupBy, bool addedByUser )
651{
652 // Users can add fields with variable names that match the special names in the grid,
653 // e.g. ${QUANTITY} so make sure we don't add them twice
654 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); i++ )
655 {
656 if( m_fieldsCtrl->GetTextValue( i, FIELD_NAME_COLUMN ) == aFieldName )
657 return;
658 }
659
660 m_dataModel->AddColumn( aFieldName, aLabelValue, addedByUser );
661
662 wxVector<wxVariant> fieldsCtrlRow;
663 std::string key( aFieldName.ToUTF8() );
664
665 // Don't change these to emplace_back: some versions of wxWidgets don't support it
666 fieldsCtrlRow.push_back( wxVariant( aFieldName ) );
667 fieldsCtrlRow.push_back( wxVariant( aLabelValue ) );
668 fieldsCtrlRow.push_back( wxVariant( show ) );
669 fieldsCtrlRow.push_back( wxVariant( groupBy ) );
670 fieldsCtrlRow.push_back( wxVariant( aFieldName ) );
671
672 m_fieldsCtrl->AppendItem( fieldsCtrlRow );
673
674 wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_APPENDED, 1 );
675 m_grid->ProcessTableMessage( msg );
676}
677
678
680{
681 auto addMandatoryField =
682 [&]( FIELD_T fieldId, bool show, bool groupBy )
683 {
684 m_mandatoryFieldListIndexes[fieldId] = m_fieldsCtrl->GetItemCount();
685
687 GetDefaultFieldName( fieldId, DO_TRANSLATE ), show, groupBy );
688 };
689
690 // Add mandatory fields first show groupBy
691 addMandatoryField( FIELD_T::REFERENCE, true, true );
692 addMandatoryField( FIELD_T::VALUE, true, true );
693 addMandatoryField( FIELD_T::FOOTPRINT, true, true );
694 addMandatoryField( FIELD_T::DATASHEET, true, false );
695 addMandatoryField( FIELD_T::DESCRIPTION, false, false );
696
697 // Generated fields present only in the fields table
700
701 // User fields next
702 std::set<wxString> userFieldNames;
703
704 for( int ii = 0; ii < (int) m_symbolsList.GetCount(); ++ii )
705 {
706 SCH_SYMBOL* symbol = m_symbolsList[ii].GetSymbol();
707
708 for( const SCH_FIELD& field : symbol->GetFields() )
709 {
710 if( !field.IsMandatory() && !field.IsPrivate() )
711 userFieldNames.insert( field.GetName() );
712 }
713 }
714
715 for( const wxString& fieldName : userFieldNames )
716 AddField( fieldName, GetGeneratedFieldDisplayName( fieldName ), true, false );
717
718 // Add any templateFieldNames which aren't already present in the userFieldNames
720 {
721 if( userFieldNames.count( tfn.m_Name ) == 0 )
722 AddField( tfn.m_Name, GetGeneratedFieldDisplayName( tfn.m_Name ), false, false );
723 }
724}
725
726
727void DIALOG_SYMBOL_FIELDS_TABLE::OnAddField( wxCommandEvent& event )
728{
729 wxTextEntryDialog dlg( this, _( "New field name:" ), _( "Add Field" ) );
730
731 if( dlg.ShowModal() != wxID_OK )
732 return;
733
734 wxString fieldName = dlg.GetValue();
735
736 if( fieldName.IsEmpty() )
737 {
738 DisplayError( this, _( "Field must have a name." ) );
739 return;
740 }
741
742 for( int i = 0; i < m_dataModel->GetNumberCols(); ++i )
743 {
744 if( fieldName == m_dataModel->GetColFieldName( i ) )
745 {
746 DisplayError( this, wxString::Format( _( "Field name '%s' already in use." ), fieldName ) );
747 return;
748 }
749 }
750
751 AddField( fieldName, GetGeneratedFieldDisplayName( fieldName ), true, false, true );
752
753 SetupColumnProperties( m_dataModel->GetColsCount() - 1 );
754
756 OnModify();
757}
758
759
760void DIALOG_SYMBOL_FIELDS_TABLE::OnRemoveField( wxCommandEvent& event )
761{
762 int col = -1;
763 int row = m_fieldsCtrl->GetSelectedRow();
764
765 if( row == -1 )
766 {
767 wxBell();
768 return;
769 }
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;
778 }
779 }
780
781 wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
782 wxString displayName = m_fieldsCtrl->GetTextValue( row, DISPLAY_NAME_COLUMN );
783
784 wxString confirm_msg = wxString::Format( _( "Are you sure you want to remove the field '%s'?" ),
785 displayName );
786
787 if( !IsOK( this, confirm_msg ) )
788 return;
789
790 for( int i = 0; i < m_dataModel->GetNumberCols(); ++i )
791 {
792 if( fieldName == m_dataModel->GetColFieldName( i ) )
793 col = i;
794 }
795
796 m_fieldsCtrl->DeleteItem( row );
798
799 // Make selection and update the state of "Remove field..." button via
800 // OnFieldsCtrlSelectionChanged().
801 // Safe to decrement row index because we always have mandatory fields.
802 m_fieldsCtrl->SelectRow( --row );
803
804 wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_DELETED, col, 1 );
805
806 m_grid->ProcessTableMessage( msg );
807
809 OnModify();
810}
811
812
813void DIALOG_SYMBOL_FIELDS_TABLE::OnRenameField( wxCommandEvent& event )
814{
815 int row = m_fieldsCtrl->GetSelectedRow();
816
817 if( row == -1 )
818 {
819 wxBell();
820 return;
821 }
822
823 for( FIELD_T id : MANDATORY_FIELDS )
824 {
825 if( m_mandatoryFieldListIndexes[id] == row )
826 {
827 DisplayError( this, wxString::Format( _( "The first %d fields are mandatory and names cannot be changed." ),
828 (int) m_mandatoryFieldListIndexes.size() ) );
829 return;
830 }
831 }
832
833 wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
834
835 int col = m_dataModel->GetFieldNameCol( fieldName );
836 wxCHECK_RET( col != -1, wxS( "Existing field name missing from data model" ) );
837
838 wxTextEntryDialog dlg( this, _( "New field name:" ), _( "Rename Field" ), fieldName );
839
840 if( dlg.ShowModal() != wxID_OK )
841 return;
842
843 wxString newFieldName = dlg.GetValue();
844
845 // No change, no-op
846 if( newFieldName == fieldName )
847 return;
848
849 // New field name already exists
850 if( m_dataModel->GetFieldNameCol( newFieldName ) != -1 )
851 {
852 wxString confirm_msg = wxString::Format( _( "Field name %s already exists." ), newFieldName );
853 DisplayError( this, confirm_msg );
854 return;
855 }
856
857 m_dataModel->RenameColumn( col, newFieldName );
858 m_fieldsCtrl->SetTextValue( newFieldName, row, DISPLAY_NAME_COLUMN );
859 m_fieldsCtrl->SetTextValue( newFieldName, row, FIELD_NAME_COLUMN );
860 m_fieldsCtrl->SetTextValue( newFieldName, row, LABEL_COLUMN );
861
863 OnModify();
864}
865
866
867void DIALOG_SYMBOL_FIELDS_TABLE::OnFilterText( wxCommandEvent& aEvent )
868{
869 m_dataModel->SetFilter( m_filter->GetValue() );
871 m_grid->ForceRefresh();
872
874}
875
876
878{
879 wxPoint pos = aEvent.GetPosition();
880 wxRect ctrlRect = m_filter->GetScreenRect();
881 int buttonWidth = ctrlRect.GetHeight(); // Presume buttons are square
882
883 // TODO: restore cursor when mouse leaves the filter field (or is it a MSW bug?)
884 if( m_filter->IsSearchButtonVisible() && pos.x < buttonWidth )
885 SetCursor( wxCURSOR_ARROW );
886 else if( m_filter->IsCancelButtonVisible() && pos.x > ctrlRect.GetWidth() - buttonWidth )
887 SetCursor( wxCURSOR_ARROW );
888 else
889 SetCursor( wxCURSOR_IBEAM );
890}
891
892
894{
895 wxDataViewItem item = event.GetItem();
896 int row = m_fieldsCtrl->ItemToRow( item );
897 int col = event.GetColumn();
898
899 switch ( col )
900 {
902 {
903 wxString name = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
904 bool value = m_fieldsCtrl->GetToggleValue( row, col );
905 int dataCol = m_dataModel->GetFieldNameCol( name );
906
907 m_dataModel->SetShowColumn( dataCol, value );
908
909 if( dataCol != -1 )
910 {
911 if( value )
912 m_grid->ShowCol( dataCol );
913 else
914 m_grid->HideCol( dataCol );
915 }
916
917 break;
918 }
919
920 case GROUP_BY_COLUMN:
921 {
922 wxString name = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
923 bool value = m_fieldsCtrl->GetToggleValue( row, col );
924 int dataCol = m_dataModel->GetFieldNameCol( name );
925
926 if( m_dataModel->ColIsQuantity( dataCol ) && value )
927 {
928 DisplayError( this, _( "The Quantity column cannot be grouped by." ) );
929
930 value = false;
931 m_fieldsCtrl->SetToggleValue( value, row, col );
932 }
933
934 if( m_dataModel->ColIsItemNumber( dataCol ) && value )
935 {
936 DisplayError( this, _( "The Item Number column cannot be grouped by." ) );
937
938 value = false;
939 m_fieldsCtrl->SetToggleValue( value, row, col );
940 }
941
942 wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
943
944 m_dataModel->SetGroupColumn( m_dataModel->GetFieldNameCol( fieldName ), value );
946 m_grid->ForceRefresh();
947 break;
948 }
949
950 default:
951 break;
952 }
953
955}
956
957
959{
962 m_grid->ForceRefresh();
963
965}
966
967
969{
972 m_grid->ForceRefresh();
973
975}
976
977
979{
982 m_grid->ForceRefresh();
983
985}
986
987
988void DIALOG_SYMBOL_FIELDS_TABLE::OnColSort( wxGridEvent& aEvent )
989{
990 int sortCol = aEvent.GetCol();
991 std::string key( m_dataModel->GetColFieldName( sortCol ).ToUTF8() );
992 bool ascending;
993
994 // Don't sort by item number, it is generated by the sort
995 if( m_dataModel->ColIsItemNumber( sortCol ) )
996 {
997 aEvent.Veto();
998 return;
999 }
1000
1001 // This is bonkers, but wxWidgets doesn't tell us ascending/descending in the event, and
1002 // if we ask it will give us pre-event info.
1003 if( m_grid->IsSortingBy( sortCol ) )
1004 {
1005 // same column; invert ascending
1006 ascending = !m_grid->IsSortOrderAscending();
1007 }
1008 else
1009 {
1010 // different column; start with ascending
1011 ascending = true;
1012 }
1013
1014 m_dataModel->SetSorting( sortCol, ascending );
1016 m_grid->ForceRefresh();
1017
1019}
1020
1021
1022void DIALOG_SYMBOL_FIELDS_TABLE::OnColMove( wxGridEvent& aEvent )
1023{
1024 int origPos = aEvent.GetCol();
1025
1026 // Save column widths since the setup function uses the saved config values
1028
1029 for( int i = 0; i < m_grid->GetNumberCols(); i++ )
1030 {
1031 if( m_grid->IsColShown( i ) )
1032 {
1033 std::string fieldName( m_dataModel->GetColFieldName( i ).ToUTF8() );
1034 cfg->m_FieldEditorPanel.field_widths[fieldName] = m_grid->GetColSize( i );
1035 }
1036 }
1037
1038 CallAfter(
1039 [origPos, this]()
1040 {
1041 int newPos = m_grid->GetColPos( origPos );
1042
1043#ifdef __WXMAC__
1044 if( newPos < origPos )
1045 newPos += 1;
1046#endif
1047
1048 m_dataModel->MoveColumn( origPos, newPos );
1049
1050 // "Unmove" the column since we've moved the column internally
1051 m_grid->ResetColPos();
1052
1053 // We need to reset all the column attr's to the correct column order
1055
1056 m_grid->ForceRefresh();
1057 } );
1058
1060}
1061
1062
1064{
1065 wxDataViewItem item = aEvent.GetItem();
1066 int row = m_fieldsCtrl->ItemToRow( item );
1067 wxString label = m_fieldsCtrl->GetTextValue( row, LABEL_COLUMN );
1068 wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
1069 int col = m_dataModel->GetFieldNameCol( fieldName );
1070
1071 if( col != -1 )
1072 {
1073 m_dataModel->SetColLabelValue( col, label );
1074 m_grid->SetColLabelValue( col, label );
1075 }
1076
1078
1079 aEvent.Skip();
1080
1081 m_grid->ForceRefresh();
1082}
1083
1084
1086{
1087 m_grid->ForceRefresh();
1088 OnModify();
1089}
1090
1091
1092void DIALOG_SYMBOL_FIELDS_TABLE::OnTableColSize( wxGridSizeEvent& aEvent )
1093{
1094 int col = aEvent.GetRowOrCol();
1095 std::string key( m_dataModel->GetColFieldName( col ).ToUTF8() );
1096
1097 aEvent.Skip();
1098
1099 m_grid->ForceRefresh();
1100}
1101
1102
1104{
1106 m_grid->ForceRefresh();
1107}
1108
1109
1111{
1112 UpdateScope();
1113}
1114
1115
1117{
1119
1120 if( m_radioProject->GetValue() )
1121 m_dataModel->SetScope( FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_ALL );
1122 else if( m_radioCurrentSheet->GetValue() )
1123 m_dataModel->SetScope( FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_SHEET );
1124 else if( m_radioRecursive->GetValue() )
1125 m_dataModel->SetScope( FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_SHEET_RECURSIVE );
1126
1128}
1129
1130
1132{
1133 if( m_dataModel->ColIsReference( event.GetCol() ) )
1134 {
1135 m_grid->ClearSelection();
1136
1137 m_dataModel->ExpandCollapseRow( event.GetRow() );
1138 m_grid->SetGridCursor( event.GetRow(), event.GetCol() );
1139 }
1140 else
1141 {
1142 event.Skip();
1143 }
1144}
1145
1146
1147void DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected( wxGridRangeSelectEvent& aEvent )
1148{
1149 // Cross-probing should only work in Edit page
1150 if( m_nbPages->GetSelection() != 0 )
1151 return;
1152
1153 // Multi-select can grab the rows that are expanded child refs, and also the row
1154 // containing the list of all child refs. Make sure we add refs/symbols uniquely
1155 std::set<SCH_REFERENCE> refs;
1156 std::set<SCH_ITEM*> symbols;
1157
1158 // This handler handles selecting and deselecting
1159 if( aEvent.Selecting() )
1160 {
1161 for( int i = aEvent.GetTopRow(); i <= aEvent.GetBottomRow(); i++ )
1162 {
1163 for( const SCH_REFERENCE& ref : m_dataModel->GetRowReferences( i ) )
1164 refs.insert( ref );
1165 }
1166
1167 for( const SCH_REFERENCE& ref : refs )
1168 symbols.insert( ref.GetSymbol() );
1169 }
1170
1171 if( m_radioHighlight->GetValue() )
1172 {
1174
1175 if( refs.size() > 0 )
1176 {
1177 // Use of full path based on UUID allows select of not yet annotated or duplicated
1178 // symbols
1179 wxString symbol_path = refs.begin()->GetFullPath();
1180
1181 // Focus only handles one item at this time
1182 editor->FindSymbolAndItem( &symbol_path, nullptr, true, HIGHLIGHT_SYMBOL,
1183 wxEmptyString );
1184 }
1185 else
1186 {
1188 }
1189 }
1190 else if( m_radioSelect->GetValue() )
1191 {
1193 std::vector<SCH_ITEM*> items( symbols.begin(), symbols.end() );
1194
1195 if( refs.size() > 0 )
1196 selTool->SyncSelection( refs.begin()->GetSheetPath(), nullptr, items );
1197 else
1198 selTool->ClearSelection();
1199 }
1200}
1201
1202
1204{
1205 // TODO: Option to select footprint if FOOTPRINT column selected
1206
1207 event.Skip();
1208}
1209
1210
1212{
1214
1215#ifdef __WXMAC__
1216 // TODO: something in wxWidgets 3.1.x pads checkbox columns with extra space. (It used to
1217 // also be that the width of the column would get set too wide (to 30), but that's patched in
1218 // our local wxWidgets fork.)
1219 width -= 50;
1220#endif
1221
1222 m_fieldNameColWidth = width / 2;
1224
1225 // GTK loses its head and messes these up when resizing the splitter bar:
1226 m_fieldsCtrl->GetColumn( SHOW_FIELD_COLUMN )->SetWidth( m_showColWidth );
1227 m_fieldsCtrl->GetColumn( GROUP_BY_COLUMN )->SetWidth( m_groupByColWidth );
1228
1229 m_fieldsCtrl->GetColumn( FIELD_NAME_COLUMN )->SetHidden( true );
1230 m_fieldsCtrl->GetColumn( DISPLAY_NAME_COLUMN )->SetWidth( m_fieldNameColWidth );
1231 m_fieldsCtrl->GetColumn( LABEL_COLUMN )->SetWidth( m_labelColWidth );
1232
1233 m_fieldsCtrl->Refresh(); // To refresh checkboxes on Windows.
1234
1235 event.Skip();
1236}
1237
1238
1240{
1242 {
1244 ClearModify();
1245 }
1246}
1247
1248
1249void DIALOG_SYMBOL_FIELDS_TABLE::OnPageChanged( wxNotebookEvent& event )
1250{
1252}
1253
1254
1256{
1259}
1260
1261
1263{
1266}
1267
1268
1270{
1271 BOM_FMT_PRESET current;
1272
1273 current.name = m_cbBomFmtPresets->GetStringSelection();
1274 current.fieldDelimiter = m_textFieldDelimiter->GetValue();
1275 current.stringDelimiter = m_textStringDelimiter->GetValue();
1276 current.refDelimiter = m_textRefDelimiter->GetValue();
1277 current.refRangeDelimiter = m_textRefRangeDelimiter->GetValue();
1278 current.keepTabs = m_checkKeepTabs->GetValue();
1279 current.keepLineBreaks = m_checkKeepLineBreaks->GetValue();
1280
1281 return current;
1282}
1283
1284
1286{
1287 m_nbPages->SetSelection( 0 );
1288}
1289
1290
1292{
1293 m_nbPages->SetSelection( 1 );
1294}
1295
1296
1298{
1299 // Build the absolute path of current output directory to preselect it in the file browser.
1300 wxString path = ExpandEnvVarSubstitutions( m_outputFileName->GetValue(), &Prj() );
1301 path = Prj().AbsolutePath( path );
1302
1303
1304 // Calculate the export filename
1305 wxFileName fn( Prj().AbsolutePath( m_parent->Schematic().GetFileName() ) );
1306 fn.SetExt( FILEEXT::CsvFileExtension );
1307
1308 wxFileDialog saveDlg( this, _( "Bill of Materials Output File" ), path, fn.GetFullName(),
1309 FILEEXT::CsvFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1310
1311 if( saveDlg.ShowModal() == wxID_CANCEL )
1312 return;
1313
1314
1315 wxFileName file = wxFileName( saveDlg.GetPath() );
1316 wxString defaultPath = fn.GetPathWithSep();
1317
1318 if( IsOK( this, wxString::Format( _( "Do you want to use a path relative to\n'%s'?" ), defaultPath ) ) )
1319 {
1320 if( !file.MakeRelativeTo( defaultPath ) )
1321 {
1322 DisplayErrorMessage( this, _( "Cannot make path relative (target volume different from schematic "
1323 "file volume)!" ) );
1324 }
1325 }
1326
1327 m_outputFileName->SetValue( file.GetFullPath() );
1328}
1329
1330
1331void DIALOG_SYMBOL_FIELDS_TABLE::OnExport( wxCommandEvent& aEvent )
1332{
1333 if( m_dataModel->IsEdited() )
1334 {
1335 if( OKOrCancelDialog( nullptr, _( "Unsaved data" ),
1336 _( "Changes have not yet been saved. Export unsaved data?" ), "",
1337 _( "OK" ), _( "Cancel" ) )
1338 == wxID_CANCEL )
1339 {
1340 return;
1341 }
1342 }
1343
1344 // Create output directory if it does not exist (also transform it in absolute form).
1345 // Bail if it fails.
1346
1347 std::function<bool( wxString* )> textResolver =
1348 [&]( wxString* token ) -> bool
1349 {
1350 SCHEMATIC& schematic = m_parent->Schematic();
1351
1352 // Handles m_board->GetTitleBlock() *and* m_board->GetProject()
1353 return schematic.ResolveTextVar( &schematic.CurrentSheet(), token, 0 );
1354 };
1355
1356 wxString path = m_outputFileName->GetValue();
1357
1358 if( path.IsEmpty() )
1359 {
1360 DisplayError( this, _( "No output file specified in Export tab." ) );
1361 return;
1362 }
1363
1364 path = ExpandTextVars( path, &textResolver );
1365 path = ExpandEnvVarSubstitutions( path, nullptr );
1366
1367 wxFileName outputFile = wxFileName::FileName( path );
1368 wxString msg;
1369
1370 if( !EnsureFileDirectoryExists( &outputFile,
1371 Prj().AbsolutePath( m_parent->Schematic().GetFileName() ),
1373 {
1374 msg.Printf( _( "Could not open/create path '%s'." ), outputFile.GetPath() );
1375 DisplayError( this, msg );
1376 return;
1377 }
1378
1379 wxFFile out( outputFile.GetFullPath(), "wb" );
1380
1381 if( !out.IsOpened() )
1382 {
1383 msg.Printf( _( "Could not create BOM output '%s'." ), outputFile.GetFullPath() );
1384 DisplayError( this, msg );
1385 return;
1386 }
1387
1389
1390 if( !out.Write( m_textOutput->GetValue() ) )
1391 {
1392 msg.Printf( _( "Could not write BOM output '%s'." ), outputFile.GetFullPath() );
1393 DisplayError( this, msg );
1394 return;
1395 }
1396
1397 // close the file before we tell the user it's done with the info modal :workflow meme:
1398 out.Close();
1399 msg.Printf( _( "Wrote BOM output to '%s'" ), outputFile.GetFullPath() );
1400 DisplayInfoMessage( this, msg );
1401}
1402
1403
1404void DIALOG_SYMBOL_FIELDS_TABLE::OnCancel( wxCommandEvent& aEvent )
1405{
1406 if( m_job )
1407 EndModal( wxID_CANCEL );
1408 else
1409 Close();
1410}
1411
1412
1413void DIALOG_SYMBOL_FIELDS_TABLE::OnOk( wxCommandEvent& aEvent )
1414{
1416
1417 if( m_job )
1418 {
1420
1423 else
1424 m_job->m_bomFmtPresetName = wxEmptyString;
1425
1426 if( m_currentBomPreset )
1428 else
1429 m_job->m_bomPresetName = wxEmptyString;
1430
1432 m_job->m_fieldDelimiter = fmtSettings.fieldDelimiter;
1434 m_job->m_refDelimiter = fmtSettings.refDelimiter;
1436 m_job->m_keepTabs = fmtSettings.keepTabs;
1437 m_job->m_keepLineBreaks = fmtSettings.keepLineBreaks;
1438
1439 BOM_PRESET presetFields = m_dataModel->GetBomSettings();
1440 m_job->m_sortAsc = presetFields.sortAsc;
1441 m_job->m_excludeDNP = presetFields.excludeDNP;
1443 m_job->m_filterString = presetFields.filterString;
1444 m_job->m_sortField = presetFields.sortField;
1445
1446 m_job->m_fieldsOrdered.clear();
1447 m_job->m_fieldsLabels.clear();
1448 m_job->m_fieldsGroupBy.clear();
1449
1450 for( const BOM_FIELD& modelField : m_dataModel->GetFieldsOrdered() )
1451 {
1452 if( modelField.show )
1453 m_job->m_fieldsOrdered.emplace_back( modelField.name );
1454 else
1455 m_job->m_fieldsOrdered.emplace_back( wxT( "__" ) + modelField.name );
1456
1457 m_job->m_fieldsLabels.emplace_back( modelField.label );
1458
1459 if( modelField.groupBy )
1460 m_job->m_fieldsGroupBy.emplace_back( modelField.name );
1461 }
1462
1463 EndModal( wxID_OK );
1464 }
1465 else
1466 {
1467 Close();
1468 }
1469}
1470
1471
1472void DIALOG_SYMBOL_FIELDS_TABLE::OnClose( wxCloseEvent& aEvent )
1473{
1474 if( m_job )
1475 {
1476 aEvent.Skip();
1477 return;
1478 }
1479
1480 // This is a cancel, so commit quietly as we're going to throw the results away anyway.
1482
1483 if( m_dataModel->IsEdited() && aEvent.CanVeto() )
1484 {
1485 if( !HandleUnsavedChanges( this, _( "Save changes?" ),
1486 [&]() -> bool
1487 {
1488 return TransferDataFromWindow();
1489 } ) )
1490 {
1491 aEvent.Veto();
1492 return;
1493 }
1494 }
1495
1496 // Stop listening to schematic events
1498
1499 // Save all our settings since we're really closing
1502
1504
1505 cfg->m_FieldEditorPanel.width = GetSize().x;
1506 cfg->m_FieldEditorPanel.height = GetSize().y;
1507 cfg->m_FieldEditorPanel.page = m_nbPages->GetSelection();
1508
1509 if( m_radioHighlight->GetValue() )
1511 else if( m_radioSelect->GetValue() )
1513 else if( m_radioOff->GetValue() )
1515
1516 if( m_radioProject->GetValue() )
1517 cfg->m_FieldEditorPanel.scope = SCOPE::SCOPE_ALL;
1518 else if( m_radioCurrentSheet->GetValue() )
1519 cfg->m_FieldEditorPanel.scope = SCOPE::SCOPE_SHEET;
1520 else if( m_radioRecursive->GetValue() )
1521 cfg->m_FieldEditorPanel.scope = SCOPE::SCOPE_SHEET_RECURSIVE;
1522
1523 for( int i = 0; i < m_grid->GetNumberCols(); i++ )
1524 {
1525 if( m_grid->IsColShown( i ) )
1526 {
1527 std::string fieldName( m_dataModel->GetColFieldName( i ).ToUTF8() );
1528 cfg->m_FieldEditorPanel.field_widths[fieldName] = m_grid->GetColSize( i );
1529 }
1530 }
1531
1533
1534 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, wxID_ANY );
1535
1536 if( wxWindow* parent = GetParent() )
1537 wxQueueEvent( parent, evt );
1538}
1539
1540
1542{
1543 std::vector<BOM_PRESET> ret;
1544
1545 for( const std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1546 {
1547 if( !pair.second.readOnly )
1548 ret.emplace_back( pair.second );
1549 }
1550
1551 return ret;
1552}
1553
1554
1555void DIALOG_SYMBOL_FIELDS_TABLE::SetUserBomPresets( std::vector<BOM_PRESET>& aPresetList )
1556{
1557 // Reset to defaults
1559
1560 for( const BOM_PRESET& preset : aPresetList )
1561 {
1562 if( m_bomPresets.count( preset.name ) )
1563 continue;
1564
1565 m_bomPresets[preset.name] = preset;
1566
1567 m_bomPresetMRU.Add( preset.name );
1568 }
1569
1571}
1572
1573
1574void DIALOG_SYMBOL_FIELDS_TABLE::ApplyBomPreset( const wxString& aPresetName )
1575{
1576 updateBomPresetSelection( aPresetName );
1577
1578 wxCommandEvent dummy;
1580}
1581
1582
1584{
1585 if( m_bomPresets.count( aPreset.name ) )
1587 else
1588 m_currentBomPreset = nullptr;
1589
1592 else
1593 m_lastSelectedBomPreset = nullptr;
1594
1595 updateBomPresetSelection( aPreset.name );
1596 doApplyBomPreset( aPreset );
1597}
1598
1599
1601{
1602 m_bomPresets.clear();
1603 m_bomPresetMRU.clear();
1604
1605 // Load the read-only defaults
1606 for( const BOM_PRESET& preset : BOM_PRESET::BuiltInPresets() )
1607 {
1608 m_bomPresets[preset.name] = preset;
1609 m_bomPresets[preset.name].readOnly = true;
1610
1611 m_bomPresetMRU.Add( preset.name );
1612 }
1613}
1614
1615
1617{
1618 m_cbBomPresets->Clear();
1619
1620 // Build the layers preset list.
1621 // By default, the presetAllLayers will be selected
1622 int idx = 0;
1623 int default_idx = 0;
1624
1625 for( std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1626 {
1627 m_cbBomPresets->Append( wxGetTranslation( pair.first ),
1628 static_cast<void*>( &pair.second ) );
1629
1630 if( pair.first == BOM_PRESET::DefaultEditing().name )
1631 default_idx = idx;
1632
1633 idx++;
1634 }
1635
1636 m_cbBomPresets->Append( wxT( "---" ) );
1637 m_cbBomPresets->Append( _( "Save preset..." ) );
1638 m_cbBomPresets->Append( _( "Delete preset..." ) );
1639
1640 // At least the built-in presets should always be present
1641 wxASSERT( !m_bomPresets.empty() );
1642
1643 // Default preset: all Boms
1644 m_cbBomPresets->SetSelection( default_idx );
1645 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( default_idx ) );
1646}
1647
1648
1650{
1652
1653 auto it = std::find_if( m_bomPresets.begin(), m_bomPresets.end(),
1654 [&]( const std::pair<const wxString, BOM_PRESET>& aPair )
1655 {
1656 const BOM_PRESET& preset = aPair.second;
1657
1658 // Check the simple settings first
1659 if( !( preset.sortAsc == current.sortAsc
1660 && preset.filterString == current.filterString
1661 && preset.groupSymbols == current.groupSymbols
1662 && preset.excludeDNP == current.excludeDNP
1663 && preset.includeExcludedFromBOM == current.includeExcludedFromBOM ) )
1664 {
1665 return false;
1666 }
1667
1668 // We should compare preset.name and current.name. Unfortunately current.name is
1669 // empty because m_dataModel->GetBomSettings() does not store the .name member.
1670 // So use sortField member as a (not very efficient) auxiliary filter.
1671 // As a further complication, sortField can be translated in m_bomPresets list, so
1672 // current.sortField needs to be translated.
1673 // Probably this not efficient and error prone test should be removed (JPC).
1674 if( preset.sortField != wxGetTranslation( current.sortField ) )
1675 return false;
1676
1677 // Only compare shown or grouped fields
1678 std::vector<BOM_FIELD> A, B;
1679
1680 for( const BOM_FIELD& field : preset.fieldsOrdered )
1681 {
1682 if( field.show || field.groupBy )
1683 A.emplace_back( field );
1684 }
1685
1686 for( const BOM_FIELD& field : current.fieldsOrdered )
1687 {
1688 if( field.show || field.groupBy )
1689 B.emplace_back( field );
1690 }
1691
1692 return A == B;
1693 } );
1694
1695 if( it != m_bomPresets.end() )
1696 {
1697 // Select the right m_cbBomPresets item.
1698 // but these items are translated if they are predefined items.
1699 bool do_translate = it->second.readOnly;
1700 wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
1701 m_cbBomPresets->SetStringSelection( text );
1702 }
1703 else
1704 {
1705 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 ); // separator
1706 }
1707
1708 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( m_cbBomPresets->GetSelection() ) );
1709}
1710
1711
1713{
1714 // Look at m_userBomPresets to know if aName is a read only preset, or a user preset.
1715 // Read-only presets have translated names in UI, so we have to use a translated name
1716 // in UI selection. But for a user preset name we search for the untranslated aName.
1717 wxString ui_label = aName;
1718
1719 for( std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1720 {
1721 if( pair.first != aName )
1722 continue;
1723
1724 if( pair.second.readOnly == true )
1725 ui_label = wxGetTranslation( aName );
1726
1727 break;
1728 }
1729
1730 int idx = m_cbBomPresets->FindString( ui_label );
1731
1732 if( idx >= 0 && m_cbBomPresets->GetSelection() != idx )
1733 {
1734 m_cbBomPresets->SetSelection( idx );
1735 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( idx ) );
1736 }
1737 else if( idx < 0 )
1738 {
1739 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 ); // separator
1740 }
1741}
1742
1743
1745{
1746 int count = m_cbBomPresets->GetCount();
1747 int index = m_cbBomPresets->GetSelection();
1748
1749 auto resetSelection =
1750 [&]()
1751 {
1752 if( m_currentBomPreset )
1753 m_cbBomPresets->SetStringSelection( m_currentBomPreset->name );
1754 else
1755 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 );
1756 };
1757
1758 if( index == count - 3 )
1759 {
1760 // Separator: reject the selection
1761 resetSelection();
1762 return;
1763 }
1764 else if( index == count - 2 )
1765 {
1766 // Save current state to new preset
1767 wxString name;
1768
1771
1772 wxTextEntryDialog dlg( this, _( "BOM preset name:" ), _( "Save BOM Preset" ), name );
1773
1774 if( dlg.ShowModal() != wxID_OK )
1775 {
1776 resetSelection();
1777 return;
1778 }
1779
1780 name = dlg.GetValue();
1781 bool exists = m_bomPresets.count( name );
1782
1783 if( !exists )
1784 {
1786 m_bomPresets[name].readOnly = false;
1787 m_bomPresets[name].name = name;
1788 }
1789
1790 BOM_PRESET* preset = &m_bomPresets[name];
1791
1792 if( !exists )
1793 {
1794 index = m_cbBomPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
1795 }
1796 else if( preset->readOnly )
1797 {
1798 wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
1799 _( "Error" ), wxOK | wxICON_ERROR, this );
1800 resetSelection();
1801 return;
1802 }
1803 else
1804 {
1805 // Ask the user if they want to overwrite the existing preset
1806 if( !IsOK( this, _( "Overwrite existing preset?" ) ) )
1807 {
1808 resetSelection();
1809 return;
1810 }
1811
1812 *preset = m_dataModel->GetBomSettings();
1813 preset->name = name;
1814
1815 index = m_cbBomPresets->FindString( name );
1816
1817 if( m_bomPresetMRU.Index( name ) != wxNOT_FOUND )
1818 m_bomPresetMRU.Remove( name );
1819 }
1820
1821 m_currentBomPreset = preset;
1822 m_cbBomPresets->SetSelection( index );
1823 m_bomPresetMRU.Insert( name, 0 );
1824
1825 return;
1826 }
1827 else if( index == count - 1 )
1828 {
1829 // Delete a preset
1830 wxArrayString headers;
1831 std::vector<wxArrayString> items;
1832
1833 headers.Add( _( "Presets" ) );
1834
1835 for( const auto& [name, preset] : m_bomPresets )
1836 {
1837 if( !preset.readOnly )
1838 {
1839 wxArrayString item;
1840 item.Add( name );
1841 items.emplace_back( item );
1842 }
1843 }
1844
1845 EDA_LIST_DIALOG dlg( this, _( "Delete Preset" ), headers, items );
1846 dlg.SetListLabel( _( "Select preset:" ) );
1847
1848 if( dlg.ShowModal() == wxID_OK )
1849 {
1850 wxString presetName = dlg.GetTextSelection();
1851 int idx = m_cbBomPresets->FindString( presetName );
1852
1853 if( idx != wxNOT_FOUND )
1854 {
1855 m_bomPresets.erase( presetName );
1856
1857 m_cbBomPresets->Delete( idx );
1858 m_currentBomPreset = nullptr;
1859 }
1860
1861 if( m_bomPresetMRU.Index( presetName ) != wxNOT_FOUND )
1862 m_bomPresetMRU.Remove( presetName );
1863 }
1864
1865 resetSelection();
1866 return;
1867 }
1868
1869 BOM_PRESET* preset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( index ) );
1870 m_currentBomPreset = preset;
1871
1872 m_lastSelectedBomPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
1873
1874 if( preset )
1875 {
1876 doApplyBomPreset( *preset );
1878 m_currentBomPreset = preset;
1879
1880 if( !m_currentBomPreset->name.IsEmpty() )
1881 {
1882 if( m_bomPresetMRU.Index( preset->name ) != wxNOT_FOUND )
1883 m_bomPresetMRU.Remove( preset->name );
1884
1885 m_bomPresetMRU.Insert( preset->name, 0 );
1886 }
1887 }
1888}
1889
1890
1892{
1893 // Disable rebuilds while we're applying the preset otherwise we'll be
1894 // rebuilding the model constantly while firing off wx events
1896
1897 // Basically, we apply the BOM preset to the data model and then
1898 // update our UI to reflect resulting the data model state, not the preset.
1899 m_dataModel->ApplyBomPreset( aPreset );
1900
1901 // BOM Presets can add, but not remove, columns, so make sure the field control
1902 // grid has all of them before starting
1903 for( int i = 0; i < m_dataModel->GetColsCount(); i++ )
1904 {
1905 const wxString& fieldName( m_dataModel->GetColFieldName( i ) );
1906 bool found = false;
1907
1908 for( int j = 0; j < m_fieldsCtrl->GetItemCount(); j++ )
1909 {
1910 if( m_fieldsCtrl->GetTextValue( j, FIELD_NAME_COLUMN ) == fieldName )
1911 {
1912 found = true;
1913 break;
1914 }
1915 }
1916
1917 // Properties like label, etc. will be added in the next loop
1918 if( !found )
1919 AddField( fieldName, GetGeneratedFieldDisplayName( fieldName ), false, false );
1920 }
1921
1922 // Sync all fields
1923 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); i++ )
1924 {
1925 const wxString& fieldName( m_fieldsCtrl->GetTextValue( i, FIELD_NAME_COLUMN ) );
1926 int col = m_dataModel->GetFieldNameCol( fieldName );
1927
1928 if( col == -1 )
1929 {
1930 wxASSERT_MSG( true, "Fields control has a field not found in the data model." );
1931 continue;
1932 }
1933
1935 std::string fieldNameStr( fieldName.ToUTF8() );
1936
1937 // Set column labels
1938 const wxString& label = m_dataModel->GetColLabelValue( col );
1939 m_fieldsCtrl->SetTextValue( label, i, LABEL_COLUMN );
1940 m_grid->SetColLabelValue( col, label );
1941
1942 if( cfg->m_FieldEditorPanel.field_widths.count( fieldNameStr ) )
1943 m_grid->SetColSize( col, cfg->m_FieldEditorPanel.field_widths.at( fieldNameStr ) );
1944
1945 // Set shown columns
1946 bool show = m_dataModel->GetShowColumn( col );
1947 m_fieldsCtrl->SetToggleValue( show, i, SHOW_FIELD_COLUMN );
1948
1949 if( show )
1950 m_grid->ShowCol( col );
1951 else
1952 m_grid->HideCol( col );
1953
1954 // Set grouped columns
1955 bool groupBy = m_dataModel->GetGroupColumn( col );
1956 m_fieldsCtrl->SetToggleValue( groupBy, i, GROUP_BY_COLUMN );
1957 }
1958
1959 m_grid->SetSortingColumn( m_dataModel->GetSortCol(), m_dataModel->GetSortAsc() );
1961 m_filter->ChangeValue( m_dataModel->GetFilter() );
1964
1966
1967 // This will rebuild all rows and columns in the model such that the order
1968 // and labels are right, then we refresh the shown grid data to match
1971 m_grid->ForceRefresh();
1972}
1973
1974
1975std::vector<BOM_FMT_PRESET> DIALOG_SYMBOL_FIELDS_TABLE::GetUserBomFmtPresets() const
1976{
1977 std::vector<BOM_FMT_PRESET> ret;
1978
1979 for( const auto& [name, preset] : m_bomFmtPresets )
1980 {
1981 if( !preset.readOnly )
1982 ret.emplace_back( preset );
1983 }
1984
1985 return ret;
1986}
1987
1988
1989void DIALOG_SYMBOL_FIELDS_TABLE::SetUserBomFmtPresets( std::vector<BOM_FMT_PRESET>& aPresetList )
1990{
1991 // Reset to defaults
1993
1994 for( const BOM_FMT_PRESET& preset : aPresetList )
1995 {
1996 if( m_bomFmtPresets.count( preset.name ) )
1997 continue;
1998
1999 m_bomFmtPresets[preset.name] = preset;
2000
2001 m_bomFmtPresetMRU.Add( preset.name );
2002 }
2003
2005}
2006
2007
2008void DIALOG_SYMBOL_FIELDS_TABLE::ApplyBomFmtPreset( const wxString& aPresetName )
2009{
2010 updateBomFmtPresetSelection( aPresetName );
2011
2012 wxCommandEvent dummy;
2014}
2015
2016
2018{
2019 m_currentBomFmtPreset = nullptr;
2021
2022 if( m_bomFmtPresets.count( aPreset.name ) )
2024
2027
2029 doApplyBomFmtPreset( aPreset );
2030}
2031
2032
2034{
2035 m_bomFmtPresets.clear();
2036 m_bomFmtPresetMRU.clear();
2037
2038 // Load the read-only defaults
2039 for( const BOM_FMT_PRESET& preset : BOM_FMT_PRESET::BuiltInPresets() )
2040 {
2041 m_bomFmtPresets[preset.name] = preset;
2042 m_bomFmtPresets[preset.name].readOnly = true;
2043
2044 m_bomFmtPresetMRU.Add( preset.name );
2045 }
2046}
2047
2048
2050{
2051 m_cbBomFmtPresets->Clear();
2052
2053 // Build the layers preset list.
2054 // By default, the presetAllLayers will be selected
2055 int idx = 0;
2056 int default_idx = 0;
2057
2058 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
2059 {
2060 m_cbBomFmtPresets->Append( wxGetTranslation( pair.first ),
2061 static_cast<void*>( &pair.second ) );
2062
2063 if( pair.first == BOM_FMT_PRESET::CSV().name )
2064 default_idx = idx;
2065
2066 idx++;
2067 }
2068
2069 m_cbBomFmtPresets->Append( wxT( "---" ) );
2070 m_cbBomFmtPresets->Append( _( "Save preset..." ) );
2071 m_cbBomFmtPresets->Append( _( "Delete preset..." ) );
2072
2073 // At least the built-in presets should always be present
2074 wxASSERT( !m_bomFmtPresets.empty() );
2075
2076 // Default preset: all Boms
2077 m_cbBomFmtPresets->SetSelection( default_idx );
2078 m_currentBomFmtPreset = static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( default_idx ) );
2079}
2080
2081
2083{
2085
2086 auto it = std::find_if( m_bomFmtPresets.begin(), m_bomFmtPresets.end(),
2087 [&]( const std::pair<const wxString, BOM_FMT_PRESET>& aPair )
2088 {
2089 return ( aPair.second.fieldDelimiter == current.fieldDelimiter
2090 && aPair.second.stringDelimiter == current.stringDelimiter
2091 && aPair.second.refDelimiter == current.refDelimiter
2092 && aPair.second.refRangeDelimiter == current.refRangeDelimiter
2093 && aPair.second.keepTabs == current.keepTabs
2094 && aPair.second.keepLineBreaks == current.keepLineBreaks );
2095 } );
2096
2097 if( it != m_bomFmtPresets.end() )
2098 {
2099 // Select the right m_cbBomFmtPresets item.
2100 // but these items are translated if they are predefined items.
2101 bool do_translate = it->second.readOnly;
2102 wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
2103
2104 m_cbBomFmtPresets->SetStringSelection( text );
2105 }
2106 else
2107 {
2108 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 ); // separator
2109 }
2110
2111 int idx = m_cbBomFmtPresets->GetSelection();
2112 m_currentBomFmtPreset = static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( idx ) );
2113}
2114
2115
2117{
2118 // look at m_userBomFmtPresets to know if aName is a read only preset, or a user preset.
2119 // Read only presets have translated names in UI, so we have to use
2120 // a translated name in UI selection.
2121 // But for a user preset name we should search for aName (not translated)
2122 wxString ui_label = aName;
2123
2124 for( const auto& [name, preset] : m_bomFmtPresets )
2125 {
2126 if( name != aName )
2127 continue;
2128
2129 if( preset.readOnly )
2130 ui_label = wxGetTranslation( aName );
2131
2132 break;
2133 }
2134
2135 int idx = m_cbBomFmtPresets->FindString( ui_label );
2136
2137 if( idx >= 0 && m_cbBomFmtPresets->GetSelection() != idx )
2138 {
2139 m_cbBomFmtPresets->SetSelection( idx );
2140 m_currentBomFmtPreset = static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( idx ) );
2141 }
2142 else if( idx < 0 )
2143 {
2144 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 ); // separator
2145 }
2146}
2147
2148
2150{
2151 int count = m_cbBomFmtPresets->GetCount();
2152 int index = m_cbBomFmtPresets->GetSelection();
2153
2154 auto resetSelection =
2155 [&]()
2156 {
2158 m_cbBomFmtPresets->SetStringSelection( m_currentBomFmtPreset->name );
2159 else
2160 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 );
2161 };
2162
2163 if( index == count - 3 )
2164 {
2165 // Separator: reject the selection
2166 resetSelection();
2167 return;
2168 }
2169 else if( index == count - 2 )
2170 {
2171 // Save current state to new preset
2172 wxString name;
2173
2176
2177 wxTextEntryDialog dlg( this, _( "BOM preset name:" ), _( "Save BOM Preset" ), name );
2178
2179 if( dlg.ShowModal() != wxID_OK )
2180 {
2181 resetSelection();
2182 return;
2183 }
2184
2185 name = dlg.GetValue();
2186 bool exists = m_bomFmtPresets.count( name );
2187
2188 if( !exists )
2189 {
2191 m_bomFmtPresets[name].readOnly = false;
2192 m_bomFmtPresets[name].name = name;
2193 }
2194
2196
2197 if( !exists )
2198 {
2199 index = m_cbBomFmtPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
2200 }
2201 else if( preset->readOnly )
2202 {
2203 wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
2204 _( "Error" ), wxOK | wxICON_ERROR, this );
2205 resetSelection();
2206 return;
2207 }
2208 else
2209 {
2210 // Ask the user if they want to overwrite the existing preset
2211 if( !IsOK( this, _( "Overwrite existing preset?" ) ) )
2212 {
2213 resetSelection();
2214 return;
2215 }
2216
2217 *preset = GetCurrentBomFmtSettings();
2218 preset->name = name;
2219
2220 index = m_cbBomFmtPresets->FindString( name );
2221
2222 if( m_bomFmtPresetMRU.Index( name ) != wxNOT_FOUND )
2223 m_bomFmtPresetMRU.Remove( name );
2224 }
2225
2226 m_currentBomFmtPreset = preset;
2227 m_cbBomFmtPresets->SetSelection( index );
2228 m_bomFmtPresetMRU.Insert( name, 0 );
2229
2230 return;
2231 }
2232 else if( index == count - 1 )
2233 {
2234 // Delete a preset
2235 wxArrayString headers;
2236 std::vector<wxArrayString> items;
2237
2238 headers.Add( _( "Presets" ) );
2239
2240 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
2241 {
2242 if( !pair.second.readOnly )
2243 {
2244 wxArrayString item;
2245 item.Add( pair.first );
2246 items.emplace_back( item );
2247 }
2248 }
2249
2250 EDA_LIST_DIALOG dlg( this, _( "Delete Preset" ), headers, items );
2251 dlg.SetListLabel( _( "Select preset:" ) );
2252
2253 if( dlg.ShowModal() == wxID_OK )
2254 {
2255 wxString presetName = dlg.GetTextSelection();
2256 int idx = m_cbBomFmtPresets->FindString( presetName );
2257
2258 if( idx != wxNOT_FOUND )
2259 {
2260 m_bomFmtPresets.erase( presetName );
2261
2262 m_cbBomFmtPresets->Delete( idx );
2263 m_currentBomFmtPreset = nullptr;
2264 }
2265
2266 if( m_bomFmtPresetMRU.Index( presetName ) != wxNOT_FOUND )
2267 m_bomFmtPresetMRU.Remove( presetName );
2268 }
2269
2270 resetSelection();
2271 return;
2272 }
2273
2274 auto* preset = static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( index ) );
2275 m_currentBomFmtPreset = preset;
2276
2277 m_lastSelectedBomFmtPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
2278
2279 if( preset )
2280 {
2281 doApplyBomFmtPreset( *preset );
2283 m_currentBomFmtPreset = preset;
2284
2285 if( !m_currentBomFmtPreset->name.IsEmpty() )
2286 {
2287 if( m_bomFmtPresetMRU.Index( preset->name ) != wxNOT_FOUND )
2288 m_bomFmtPresetMRU.Remove( preset->name );
2289
2290 m_bomFmtPresetMRU.Insert( preset->name, 0 );
2291 }
2292 }
2293}
2294
2295
2297{
2298 m_textFieldDelimiter->ChangeValue( aPreset.fieldDelimiter );
2299 m_textStringDelimiter->ChangeValue( aPreset.stringDelimiter );
2300 m_textRefDelimiter->ChangeValue( aPreset.refDelimiter );
2301 m_textRefRangeDelimiter->ChangeValue( aPreset.refRangeDelimiter );
2302 m_checkKeepTabs->SetValue( aPreset.keepTabs );
2303 m_checkKeepLineBreaks->SetValue( aPreset.keepLineBreaks );
2304
2305
2306 // Refresh the preview if that's the current page
2307 if( m_nbPages->GetSelection() == 1 )
2309}
2310
2311
2313{
2314 bool modified = false;
2315
2316 // Save our BOM presets
2317 std::vector<BOM_PRESET> presets;
2318
2319 for( const auto& [name, preset] : m_bomPresets )
2320 {
2321 if( !preset.readOnly )
2322 presets.emplace_back( preset );
2323 }
2324
2325 if( m_schSettings.m_BomPresets != presets )
2326 {
2327 modified = true;
2328 m_schSettings.m_BomPresets = presets;
2329 }
2330
2332 {
2333 modified = true;
2335 }
2336
2337 // Save our BOM Format presets
2338 std::vector<BOM_FMT_PRESET> fmts;
2339
2340 for( const auto& [name, preset] : m_bomFmtPresets )
2341 {
2342 if( !preset.readOnly )
2343 fmts.emplace_back( preset );
2344 }
2345
2346 if( m_schSettings.m_BomFmtPresets != fmts )
2347 {
2348 modified = true;
2350 }
2351
2353 {
2354 modified = true;
2356 }
2357
2358 if( modified )
2359 m_parent->OnModify();
2360}
2361
2362
2364 std::vector<SCH_ITEM*>& aSchItem )
2365{
2366 SCH_REFERENCE_LIST allRefs;
2367 m_parent->Schematic().Hierarchy().GetSymbols( allRefs );
2368
2369 for( SCH_ITEM* item : aSchItem )
2370 {
2371 if( item->Type() == SCH_SYMBOL_T )
2372 {
2373 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2374
2375 // Don't add power symbols
2376 if( !symbol->IsMissingLibSymbol() && symbol->IsPower() )
2377 continue;
2378
2379 // Add all fields again in case this symbol has a new one
2380 for( SCH_FIELD& field : symbol->GetFields() )
2381 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2382
2383 m_dataModel->AddReferences( getSymbolReferences( symbol, allRefs ) );
2384 }
2385 else if( item->Type() == SCH_SHEET_T )
2386 {
2387 std::set<SCH_SYMBOL*> symbols;
2388 SCH_REFERENCE_LIST refs = getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) );
2389
2390 for( SCH_REFERENCE& ref : refs )
2391 symbols.insert( ref.GetSymbol() );
2392
2393 for( SCH_SYMBOL* symbol : symbols )
2394 {
2395 // Add all fields again in case this symbol has a new one
2396 for( SCH_FIELD& field : symbol->GetFields() )
2397 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2398 }
2399
2400 m_dataModel->AddReferences( refs );
2401 }
2402 }
2403
2407}
2408
2409
2411 std::vector<SCH_ITEM*>& aSchItem )
2412{
2413 for( SCH_ITEM* item : aSchItem )
2414 {
2415 if( item->Type() == SCH_SYMBOL_T )
2416 m_dataModel->RemoveSymbol( *static_cast<SCH_SYMBOL*>( item ) );
2417 else if( item->Type() == SCH_SHEET_T )
2418 m_dataModel->RemoveReferences( getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) ) );
2419 }
2420
2424}
2425
2426
2428 std::vector<SCH_ITEM*>& aSchItem )
2429{
2430 SCH_REFERENCE_LIST allRefs;
2431 m_parent->Schematic().Hierarchy().GetSymbols( allRefs );
2432
2433 for( SCH_ITEM* item : aSchItem )
2434 {
2435 if( item->Type() == SCH_SYMBOL_T )
2436 {
2437 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2438
2439 // Don't add power symbols
2440 if( !symbol->IsMissingLibSymbol() && symbol->IsPower() )
2441 continue;
2442
2443 // Add all fields again in case this symbol has a new one
2444 for( SCH_FIELD& field : symbol->GetFields() )
2445 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2446
2447 m_dataModel->UpdateReferences( getSymbolReferences( symbol, allRefs ) );
2448 }
2449 else if( item->Type() == SCH_SHEET_T )
2450 {
2451 std::set<SCH_SYMBOL*> symbols;
2452 SCH_REFERENCE_LIST refs = getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) );
2453
2454 for( SCH_REFERENCE& ref : refs )
2455 symbols.insert( ref.GetSymbol() );
2456
2457 for( SCH_SYMBOL* symbol : symbols )
2458 {
2459 // Add all fields again in case this symbol has a new one
2460 for( SCH_FIELD& field : symbol->GetFields() )
2461 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2462 }
2463
2465 }
2466 }
2467
2471}
2472
2473
2475{
2476 m_dataModel->SetPath( aSch.CurrentSheet() );
2477
2478 if( m_dataModel->GetScope() != FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_ALL )
2479 {
2483 }
2484}
2485
2486
2488{
2489 m_grid->Connect( wxEVT_GRID_RANGE_SELECTED,
2490 wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected ),
2491 nullptr, this );
2492}
2493
2494
2496{
2497 m_grid->Disconnect( wxEVT_GRID_RANGE_SELECTED,
2498 wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected ),
2499 nullptr, this );
2500}
2501
2502
2505 SCH_REFERENCE_LIST& aCachedRefs )
2506{
2507 SCH_REFERENCE_LIST symbolRefs;
2508
2509 for( size_t i = 0; i < aCachedRefs.GetCount(); i++ )
2510 {
2511 SCH_REFERENCE& ref = aCachedRefs[i];
2512
2513 if( ref.GetSymbol() == aSymbol )
2514 {
2515 ref.Split(); // Figures out if we are annotated or not
2516 symbolRefs.AddItem( ref );
2517 }
2518 }
2519
2520 return symbolRefs;
2521}
2522
2523
2525{
2526 SCH_SHEET_LIST allSheets = m_parent->Schematic().Hierarchy();
2527 SCH_REFERENCE_LIST sheetRefs;
2528
2529 // We need to operate on all instances of the sheet
2530 for( const SCH_SHEET_INSTANCE& instance : aSheet.GetInstances() )
2531 {
2532 // For every sheet instance we need to get the current schematic sheet
2533 // instance that matches that particular sheet path from the root
2534 for( SCH_SHEET_PATH& basePath : allSheets )
2535 {
2536 if( basePath.Path() == instance.m_Path )
2537 {
2538 SCH_SHEET_PATH sheetPath = basePath;
2539 sheetPath.push_back( &aSheet );
2540
2541 // Create a list of all sheets in this path, starting with the path
2542 // of the sheet that we just deleted, then all of its subsheets
2543 SCH_SHEET_LIST subSheets;
2544 subSheets.push_back( sheetPath );
2545 allSheets.GetSheetsWithinPath( subSheets, sheetPath );
2546
2547 subSheets.GetSymbolsWithinPath( sheetRefs, sheetPath, false, false );
2548 break;
2549 }
2550 }
2551 }
2552
2553 for( SCH_REFERENCE& ref : sheetRefs )
2554 ref.Split();
2555
2556 return sheetRefs;
2557}
const char * name
Definition: DXF_plotter.cpp:62
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition: bitmap.cpp:110
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:61
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:75
void SetupStandardButtons(std::map< int, wxString > aLabels={})
std::string m_hash_key
Definition: dialog_shim.h:236
int horizPixelsFromDU(int x) const
Convert an integer number of dialog units to pixels, horizontally.
void ClearModify()
void OnModify()
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
int ShowModal() override
Class DIALOG_SYMBOL_FIELDS_TABLE_BASE.
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 OnOk(wxCommandEvent &aEvent) override
void OnGroupSymbolsToggled(wxCommandEvent &event) override
void OnColumnItemToggled(wxDataViewEvent &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)
FIELDS_EDITOR_GRID_DATA_MODEL * m_dataModel
void updateBomPresetSelection(const wxString &aName)
void OnTableItemContextMenu(wxGridEvent &event) override
void updateBomFmtPresetSelection(const wxString &aName)
void OnFilterText(wxCommandEvent &aEvent) override
void OnScopeChanged(wxCommandEvent &aEvent) override
std::map< FIELD_T, int > m_mandatoryFieldListIndexes
void OnRemoveField(wxCommandEvent &event) override
void OnTableCellClick(wxGridEvent &event) override
void OnShowExcludedToggled(wxCommandEvent &event) override
void OnColLabelChange(wxDataViewEvent &aEvent)
void doApplyBomFmtPreset(const BOM_FMT_PRESET &aPreset)
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)
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 OnSizeFieldList(wxSizeEvent &event) override
void doApplyBomPreset(const BOM_PRESET &aPreset)
void OnPageChanged(wxNotebookEvent &event) override
void SetUserBomFmtPresets(std::vector< BOM_FMT_PRESET > &aPresetList)
void OnRegroupSymbols(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 OnExcludeDNPToggled(wxCommandEvent &event) override
void OnRenameField(wxCommandEvent &event) override
virtual void ClearFocus()
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_FIELD_EDITOR m_FieldEditorPanel
int GetFieldNameCol(const wxString &aFieldName) const
wxString GetColFieldName(int aCol)
void ApplyBomPreset(const BOM_PRESET &preset)
std::vector< SCH_REFERENCE > GetRowReferences(int aRow) const
wxString GetColLabelValue(int aCol) override
void SetPath(const SCH_SHEET_PATH &aPath)
void RenameColumn(int aCol, const wxString &newName)
wxString Export(const BOM_FMT_PRESET &settings)
void AddColumn(const wxString &aFieldName, const wxString &aLabel, bool aAddedByUser)
void SetSorting(int aCol, bool ascending)
void SetFilter(const wxString &aFilter)
void SetColAttr(wxGridCellAttr *aAttr, int aCol) override
static const wxString ITEM_NUMBER_VARIABLE
void ApplyData(SCH_COMMIT &aCommit, TEMPLATES &aTemplateFieldnames)
void SetIncludeExcludedFromBOM(bool include)
void UpdateReferences(const SCH_REFERENCE_LIST &aRefs)
static const wxString QUANTITY_VARIABLE
void SetGroupColumn(int aCol, bool group)
void RemoveSymbol(const SCH_SYMBOL &aSymbol)
std::vector< BOM_FIELD > GetFieldsOrdered()
void SetColLabelValue(int aCol, const wxString &aLabel) override
void MoveColumn(int aCol, int aNewPos)
void RemoveReferences(const SCH_REFERENCE_LIST &aRefs)
void SetShowColumn(int aCol, bool show)
void AddReferences(const SCH_REFERENCE_LIST &aRefs)
void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
void doPopupSelection(wxCommandEvent &event) override
FIELDS_EDITOR_GRID_DATA_MODEL * m_dataModel
FIELDS_EDITOR_GRID_TRICKS(DIALOG_SHIM *aParent, WX_GRID *aGrid, wxDataViewListCtrl *aFieldsCtrl, FIELDS_EDITOR_GRID_DATA_MODEL *aDataModel, EMBEDDED_FILES *aFiles)
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition: grid_tricks.h:61
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.
Definition: grid_tricks.h:128
std::vector< wxString > m_fieldsLabels
std::vector< wxString > m_fieldsOrdered
wxString GetSettingsDialogTitle() const override
std::vector< wxString > m_fieldsGroupBy
void SetConfiguredOutputPath(const wxString &aPath)
Sets the configured output path for the job, this path is always saved to file.
Definition: job.cpp:153
wxString GetConfiguredOutputPath() const
Returns the configured output path for the job.
Definition: job.h:232
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Definition: kiway_holder.h:55
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Definition: kiway_player.h:65
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:395
static REPORTER & GetInstance()
Definition: reporter.cpp:108
static SEARCH_STACK * SchSearchS(PROJECT *aProject)
Accessor for Eeschema search stack.
Definition: project_sch.cpp:41
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:373
BOM_FMT_PRESET m_BomFmtSettings
List of stored BOM format presets.
TEMPLATES m_TemplateFieldNames
std::vector< BOM_PRESET > m_BomPresets
std::vector< BOM_FMT_PRESET > m_BomFmtPresets
BOM_PRESET m_BomSettings
List of stored BOM presets.
Holds all the data relating to one schematic.
Definition: schematic.h:88
void RemoveListener(SCHEMATIC_LISTENER *aListener)
Remove the specified listener.
Definition: schematic.cpp:866
wxString GetFileName() const
Helper to retrieve the filename from the root sheet screen.
Definition: schematic.cpp:350
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
Definition: schematic.cpp:258
void AddListener(SCHEMATIC_LISTENER *aListener)
Add a listener to the schematic to receive calls whenever something on the schematic has been modifie...
Definition: schematic.cpp:859
bool ResolveTextVar(const SCH_SHEET_PATH *aSheetPath, wxString *token, int aDepth) const
Definition: schematic.cpp:297
SCH_SHEET_PATH & CurrentSheet() const
Definition: schematic.h:171
void SyncView()
Mark all items for refresh.
EESCHEMA_SETTINGS * eeconfig() const
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
Definition: sch_commit.cpp:489
Handle actions specific to the schematic editor.
Schematic editor (Eeschema) main window.
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag and update other data struc...
SCH_SHEET_PATH & GetCurrentSheet() const
SCHEMATIC & Schematic() const
void SetCurrentSheet(const SCH_SHEET_PATH &aSheet)
bool SaveProject(bool aSaveAs=false)
Save the currently-open schematic (including its hierarchy) and associated project.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:168
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
size_t GetCount() const
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 GetSymbols(SCH_REFERENCE_LIST &aReferences, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets.
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:429
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.
Definition: sch_symbol.cpp:788
bool IsMissingLibSymbol() const
Check to see if the library symbol is set to the dummy library symbol.
Definition: sch_symbol.cpp:210
bool IsPower() const override
void SetBitmap(const wxBitmapBundle &aBmp)
const std::vector< TEMPLATE_FIELDNAME > & GetTemplateFieldNames()
Return a template field name list for read only access.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:55
Master controller class:
Definition: tool_manager.h:62
void SetTable(wxGridTableBase *table, bool aTakeOwnership=false)
Hide wxGrid's SetTable() method with one which doesn't mess up the grid column widths when setting th...
Definition: wx_grid.cpp:273
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:632
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.
wxDEFINE_EVENT(EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, wxCommandEvent)
#define COLUMN_MARGIN
#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 FIELD_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()
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition: wxgtk/ui.cpp:258
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.
Definition: string_utils.h:429
wxString label
Definition: bom_settings.h:33
bool groupBy
Definition: bom_settings.h:35
wxString name
Definition: bom_settings.h:32
wxString fieldDelimiter
Definition: bom_settings.h:83
wxString name
Definition: bom_settings.h:81
static BOM_FMT_PRESET CSV()
static std::vector< BOM_FMT_PRESET > BuiltInPresets()
wxString stringDelimiter
Definition: bom_settings.h:84
wxString refRangeDelimiter
Definition: bom_settings.h:86
wxString refDelimiter
Definition: bom_settings.h:85
wxString name
Definition: bom_settings.h:51
static BOM_PRESET DefaultEditing()
wxString sortField
Definition: bom_settings.h:54
bool groupSymbols
Definition: bom_settings.h:57
std::vector< BOM_FIELD > fieldsOrdered
Definition: bom_settings.h:53
bool includeExcludedFromBOM
Definition: bom_settings.h:59
bool readOnly
Definition: bom_settings.h:52
static std::vector< BOM_PRESET > BuiltInPresets()
bool excludeDNP
Definition: bom_settings.h:58
bool sortAsc
Definition: bom_settings.h:55
wxString filterString
Definition: bom_settings.h:56
std::map< std::string, int > field_widths
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...
wxString GetCanonicalFieldName(FIELD_T aFieldType)
@ SCH_SYMBOL_T
Definition: typeinfo.h:173
@ SCH_SHEET_T
Definition: typeinfo.h:176
Definition of file extensions used in Kicad.