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