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 (C) 2017-2023 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>
41#include <schematic.h>
43#include <kiplatform/ui.h>
47#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>
55#include <fields_data_model.h>
56#include <eda_list_dialog.h>
57#include <project_sch.h>
58
59wxDEFINE_EVENT( EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, wxCommandEvent );
60
61#ifdef __WXMAC__
62#define COLUMN_MARGIN 3
63#else
64#define COLUMN_MARGIN 15
65#endif
66
68
69
70enum
71{
74};
75
77{
78public:
80 wxDataViewListCtrl* aFieldsCtrl,
81 FIELDS_EDITOR_GRID_DATA_MODEL* aDataModel ) :
82 GRID_TRICKS( aGrid ),
83 m_dlg( aParent ),
84 m_fieldsCtrl( aFieldsCtrl ),
85 m_dataModel( aDataModel )
86 {}
87
88protected:
89 void showPopupMenu( wxMenu& menu, wxGridEvent& aEvent ) override
90 {
91 if( m_grid->GetGridCursorCol() == FOOTPRINT_FIELD )
92 {
93 menu.Append( MYID_SELECT_FOOTPRINT, _( "Select Footprint..." ),
94 _( "Browse for footprint" ) );
95 menu.AppendSeparator();
96 }
97 else if( m_grid->GetGridCursorCol() == DATASHEET_FIELD )
98 {
99 menu.Append( MYID_SHOW_DATASHEET, _( "Show Datasheet" ),
100 _( "Show datasheet in browser" ) );
101 menu.AppendSeparator();
102 }
103
104 GRID_TRICKS::showPopupMenu( menu, aEvent );
105 }
106
107 void doPopupSelection( wxCommandEvent& event ) override
108 {
109 if( event.GetId() == MYID_SELECT_FOOTPRINT )
110 {
111 // pick a footprint using the footprint picker.
112 wxString fpid = m_grid->GetCellValue( m_grid->GetGridCursorRow(),
115
116 if( frame->ShowModal( &fpid, m_dlg ) )
117 m_grid->SetCellValue( m_grid->GetGridCursorRow(), FOOTPRINT_FIELD, fpid );
118
119 frame->Destroy();
120 }
121 else if (event.GetId() == MYID_SHOW_DATASHEET )
122 {
123 wxString datasheet_uri = m_grid->GetCellValue( m_grid->GetGridCursorRow(),
125 GetAssociatedDocument( m_dlg, datasheet_uri, &m_dlg->Prj(),
127 }
128 else
129 {
130 // We have grid tricks events to show/hide the columns from the popup menu
131 // and we need to make sure the data model is updated to match the grid,
132 // so do it through our code instead
133 if( event.GetId() >= GRIDTRICKS_FIRST_SHOWHIDE )
134 {
135 // Pop-up column order is the order of the shown fields, not the
136 // fieldsCtrl order
137 int col = event.GetId() - GRIDTRICKS_FIRST_SHOWHIDE;
138
139 bool show = !m_dataModel->GetShowColumn( col );
140
141 // Convert data model column to by iterating over m_fieldsCtrl rows
142 // and finding the matching field name
143 wxString fieldName = m_dataModel->GetColFieldName( col );
144
145 for( int row = 0; row < m_fieldsCtrl->GetItemCount(); row++ )
146 {
147 if( m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN ) == fieldName )
148 {
149 m_fieldsCtrl->SetToggleValue( show, row, SHOW_FIELD_COLUMN );
150 break;
151 }
152 }
153 }
154 else
156 }
157 }
158
160 wxDataViewListCtrl* m_fieldsCtrl;
162};
163
164
166 DIALOG_SYMBOL_FIELDS_TABLE_BASE( parent ), m_currentBomPreset( nullptr ),
167 m_lastSelectedBomPreset( nullptr ), m_parent( parent ),
168 m_schSettings( parent->Schematic().Settings() )
169{
170 // Get all symbols from the list of schematic sheets
172
173 m_bRefresh->SetBitmap( KiBitmapBundle( BITMAPS::small_refresh ) );
174 m_bRefreshPreview->SetBitmap( KiBitmapBundle( BITMAPS::small_refresh ) );
175 m_browseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
176
177 m_addFieldButton->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
178 m_removeFieldButton->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
179 m_renameFieldButton->SetBitmap( KiBitmapBundle( BITMAPS::small_edit ) );
180
181 m_removeFieldButton->Enable( false );
182 m_renameFieldButton->Enable( false );
183
184 m_bomPresetsLabel->SetFont( KIUI::GetInfoFont( this ) );
185 m_labelBomExportPresets->SetFont( KIUI::GetInfoFont( this ) );
186
187 m_fieldsCtrl->AppendTextColumn( _( "Field" ), wxDATAVIEW_CELL_INERT, 0, wxALIGN_LEFT, 0 );
188 m_fieldsCtrl->AppendTextColumn( _( "Label" ), wxDATAVIEW_CELL_EDITABLE, 0, wxALIGN_LEFT, 0 );
189 m_fieldsCtrl->AppendToggleColumn( _( "Show" ), wxDATAVIEW_CELL_ACTIVATABLE, 0,
190 wxALIGN_CENTER, 0 );
191 m_fieldsCtrl->AppendToggleColumn( _( "Group By" ), wxDATAVIEW_CELL_ACTIVATABLE, 0,
192 wxALIGN_CENTER, 0 );
193
194 // GTK asserts if the number of columns doesn't match the data, but we still don't want
195 // to display the canonical names. So we'll insert a column for them, but keep it 0 width.
196 m_fieldsCtrl->AppendTextColumn( _( "Name" ), wxDATAVIEW_CELL_INERT, 0, wxALIGN_LEFT, 0 );
197
198 // SetWidth( wxCOL_WIDTH_AUTOSIZE ) fails here on GTK, so we calculate the title sizes and
199 // set the column widths ourselves.
200 wxDataViewColumn* column = m_fieldsCtrl->GetColumn( SHOW_FIELD_COLUMN );
201 m_showColWidth = KIUI::GetTextSize( column->GetTitle(), m_fieldsCtrl ).x + COLUMN_MARGIN;
202 column->SetMinWidth( m_showColWidth );
203
204 column = m_fieldsCtrl->GetColumn( GROUP_BY_COLUMN );
206 column->SetMinWidth( m_groupByColWidth );
207
208 // The fact that we're a list should keep the control from reserving space for the
209 // expander buttons... but it doesn't. Fix by forcing the indent to 0.
210 m_fieldsCtrl->SetIndent( 0 );
211
212 m_filter->SetDescriptiveText( _( "Filter" ) );
214
215 // We want to show excluded symbols because the Edit page needs to show all symbols,
216 // they will still be excluded from the export.
218
219 LoadFieldNames(); // loads rows into m_fieldsCtrl and columns into m_dataModel
220
221 // Now that the fields are loaded we can set the initial location of the splitter
222 // based on the list width. Again, SetWidth( wxCOL_WIDTH_AUTOSIZE ) fails us on GTK.
224 m_labelColWidth = 0;
225
226 int colWidth = 0;
227
228 for( int row = 0; row < m_fieldsCtrl->GetItemCount(); ++row )
229 {
230 const wxString& displayName = m_fieldsCtrl->GetTextValue( row, DISPLAY_NAME_COLUMN );
231 colWidth = std::max( colWidth, KIUI::GetTextSize( displayName, m_fieldsCtrl ).x );
232
233 const wxString& label = m_fieldsCtrl->GetTextValue( row, LABEL_COLUMN );
234 colWidth = std::max( colWidth, KIUI::GetTextSize( label, m_fieldsCtrl ).x );
235 }
236
237 m_fieldNameColWidth = colWidth + 20;
238 m_labelColWidth = colWidth + 20;
239
241
242 m_fieldsCtrl->GetColumn( DISPLAY_NAME_COLUMN )->SetWidth( m_fieldNameColWidth );
243 m_fieldsCtrl->GetColumn( LABEL_COLUMN )->SetWidth( m_labelColWidth );
244
245 // This is used for data only. Don't show it to the user.
246 m_fieldsCtrl->GetColumn( FIELD_NAME_COLUMN )->SetHidden( true );
247
248 m_splitterMainWindow->SetMinimumPaneSize( fieldsMinWidth );
249 m_splitterMainWindow->SetSashPosition( fieldsMinWidth + 40 );
250
251 m_grid->UseNativeColHeader( true );
252 m_grid->SetTable( m_dataModel, true );
253
254 // must be done after SetTable(), which appears to re-set it
255 m_grid->SetSelectionMode( wxGrid::wxGridSelectCells );
256
257 // add Cut, Copy, and Paste to wxGrid
258 m_grid->PushEventHandler( new FIELDS_EDITOR_GRID_TRICKS( this, m_grid, m_fieldsCtrl,
259 m_dataModel ) );
260
261 // give a bit more room for comboboxes
262 m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
263
264 // Load our BOM view presets
268
269 // Load BOM export format presets
273
275 m_grid->ClearSelection();
276
278
280
283
284 wxSize dlgSize( panelCfg.width > 0 ? panelCfg.width : horizPixelsFromDU( 600 ),
285 panelCfg.height > 0 ? panelCfg.height : vertPixelsFromDU( 300 ) );
286 SetSize( dlgSize );
287
288 m_nbPages->SetSelection( cfg->m_FieldEditorPanel.page );
289
291 {
292 case 0: m_radioHighlight->SetValue( true ); break;
293 case 1: m_radioSelect->SetValue( true ); break;
294 case 2: m_radioOff->SetValue( true ); break;
295 }
296
297 switch( cfg->m_FieldEditorPanel.scope )
298 {
299 case SCOPE::SCOPE_ALL: m_radioProject->SetValue( true ); break;
300 case SCOPE::SCOPE_SHEET: m_radioCurrentSheet->SetValue( true ); break;
301 case SCOPE::SCOPE_SHEET_RECURSIVE: m_radioRecursive->SetValue( true ); break;
302 }
303
305
306 Center();
307
308 // Connect Events
309 m_grid->Connect( wxEVT_GRID_COL_SORT,
310 wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColSort ), nullptr, this );
311 m_grid->Connect( wxEVT_GRID_COL_MOVE,
312 wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColMove ), nullptr, this );
315 m_fieldsCtrl->Bind( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED,
317
318 // Start listening for schematic changes
319 m_parent->Schematic().AddListener( this );
320}
321
322
324{
325 wxGridCellAttr* attr = new wxGridCellAttr;
326 attr->SetReadOnly( false );
327
328 // Set some column types to specific editors
329 if( m_dataModel->ColIsReference( aCol ) )
330 {
331 attr->SetReadOnly();
332 m_grid->SetColAttr( aCol, attr );
333 }
335 {
336 attr->SetEditor( new GRID_CELL_FPID_EDITOR( this, wxEmptyString ) );
337 m_grid->SetColAttr( aCol, attr );
338 }
340 {
341 // set datasheet column viewer button
342 attr->SetEditor(
344 m_grid->SetColAttr( aCol, attr );
345 }
346 else if( m_dataModel->ColIsQuantity( aCol ) || m_dataModel->ColIsItemNumber( aCol ) )
347 {
348 attr->SetReadOnly();
349 m_grid->SetColAttr( aCol, attr );
350 m_grid->SetColFormatNumber( aCol );
351 }
352 else if( m_dataModel->ColIsAttribute( aCol ) )
353 {
354 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
355 m_grid->SetColAttr( aCol, attr );
356 m_grid->SetColFormatBool( aCol );
357 }
358 else if( IsTextVar( m_dataModel->GetColFieldName( aCol ) ) )
359 {
360 attr->SetReadOnly();
361 m_grid->SetColAttr( aCol, attr );
362 }
363 else
364 {
365 attr->SetEditor( m_grid->GetDefaultEditor() );
366 m_grid->SetColAttr( aCol, attr );
367 m_grid->SetColFormatCustom( aCol, wxGRID_VALUE_STRING );
368 }
369}
370
371
373{
375 wxSize defaultDlgSize = ConvertDialogToPixels( wxSize( 600, 300 ) );
376
377 // Restore column sorting order and widths
378 m_grid->AutoSizeColumns( false );
379 int sortCol = 0;
380 bool sortAscending = true;
381
382 for( int col = 0; col < m_grid->GetNumberCols(); ++col )
383 {
385
386 if( col == m_dataModel->GetSortCol() )
387 {
388 sortCol = col;
389 sortAscending = m_dataModel->GetSortAsc();
390 }
391 }
392
393 // sync m_grid's column visibilities to Show checkboxes in m_fieldsCtrl
394 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); ++i )
395 {
396 int col = m_dataModel->GetFieldNameCol( m_fieldsCtrl->GetTextValue( i, FIELD_NAME_COLUMN ) );
397
398 if( col == -1 )
399 continue;
400
401 bool show = m_fieldsCtrl->GetToggleValue( i, SHOW_FIELD_COLUMN );
402 m_dataModel->SetShowColumn( col, show );
403
404 if( show )
405 {
406 m_grid->ShowCol( col );
407
408 std::string key( m_dataModel->GetColFieldName( col ).ToUTF8() );
409
410 if( cfg->m_FieldEditorPanel.field_widths.count( key )
411 && ( cfg->m_FieldEditorPanel.field_widths.at( key ) > 0 ) )
412 {
413 m_grid->SetColSize( col, cfg->m_FieldEditorPanel.field_widths.at( key ) );
414 }
415 else
416 {
417 int textWidth = m_dataModel->GetDataWidth( col ) + COLUMN_MARGIN;
418 int maxWidth = defaultDlgSize.x / 3;
419
420 m_grid->SetColSize( col, Clamp( 100, textWidth, maxWidth ) );
421 }
422 }
423 else
424 {
425 m_grid->HideCol( col );
426 }
427 }
428
429 m_dataModel->SetSorting( sortCol, sortAscending );
430 m_grid->SetSortingColumn( sortCol, sortAscending );
431}
432
433
435{
436 // Disconnect Events
437 m_grid->Disconnect( wxEVT_GRID_COL_SORT,
438 wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColSort ), nullptr,
439 this );
440 m_grid->Disconnect( wxEVT_GRID_COL_SORT,
441 wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColMove ), nullptr,
442 this );
443
444 // Delete the GRID_TRICKS.
445 m_grid->PopEventHandler( true );
446
447 // we gave ownership of m_dataModel to the wxGrid...
448}
449
450
452{
453 if( !wxDialog::TransferDataFromWindow() )
454 return false;
455
457 EE_SELECTION_TOOL* selectionTool = toolMgr->GetTool<EE_SELECTION_TOOL>();
458 EE_SELECTION& selection = selectionTool->GetSelection();
459 SCH_SYMBOL* symbol = nullptr;
460
461 UpdateScope();
462
463 if( selection.GetSize() == 1 )
464 {
465 EDA_ITEM* item = selection.Front();
466
467 if( item->Type() == SCH_SYMBOL_T )
468 symbol = (SCH_SYMBOL*) item;
469 else if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T )
470 symbol = (SCH_SYMBOL*) item->GetParent();
471 }
472
473 if( symbol )
474 {
475 for( int row = 0; row < m_dataModel->GetNumberRows(); ++row )
476 {
477 std::vector<SCH_REFERENCE> references = m_dataModel->GetRowReferences( row );
478 bool found = false;
479
480 for( const SCH_REFERENCE& ref : references )
481 {
482 if( ref.GetSymbol() == symbol )
483 {
484 found = true;
485 break;
486 }
487 }
488
489 if( found )
490 {
491 // Find the value column and the reference column if they're shown
492 int valueCol = -1;
493 int refCol = -1;
494 int anyCol = -1;
495
496 for( int col = 0; col < m_dataModel->GetNumberCols(); col++ )
497 {
498 if( m_dataModel->ColIsValue( col ) )
499 valueCol = col;
500 else if( m_dataModel->ColIsReference( col ) )
501 refCol = col;
502 else if( anyCol == -1 && m_dataModel->GetShowColumn( col ) )
503 anyCol = col;
504 }
505
506 if( valueCol != -1 && m_dataModel->GetShowColumn( valueCol ) )
507 m_grid->GoToCell( row, valueCol );
508 else if( refCol != -1 && m_dataModel->GetShowColumn( refCol ) )
509 m_grid->GoToCell( row, refCol );
510 else if( anyCol != -1 )
511 m_grid->GoToCell( row, anyCol );
512
513 break;
514 }
515 }
516 }
517
518 // We don't want table range selection events to happen until we've loaded the data or we
519 // we'll clear our selection as the grid is built before the code above can get the
520 // user's current selection.
522
523 return true;
524}
525
526
528{
530 return false;
531
532 if( !wxDialog::TransferDataFromWindow() )
533 return false;
534
535 SCH_COMMIT commit( m_parent );
536 SCH_SHEET_PATH currentSheet = m_parent->GetCurrentSheet();
537
538 std::function<void( SCH_SYMBOL&, SCH_SHEET_PATH & aPath )> changeHandler =
539 [&commit]( SCH_SYMBOL& aSymbol, SCH_SHEET_PATH& aPath ) -> void
540 {
541 commit.Modify( &aSymbol, aPath.LastScreen() );
542 };
543
544 m_dataModel->ApplyData( changeHandler );
545
546 commit.Push( wxS( "Symbol Fields Table Edit" ) );
547
548 // Reset the view to where we left the user
549 m_parent->SetCurrentSheet( currentSheet );
551 m_parent->Refresh();
552
554
555 return true;
556}
557
558
559void DIALOG_SYMBOL_FIELDS_TABLE::AddField( const wxString& aFieldName, const wxString& aLabelValue,
560 bool show, bool groupBy, bool addedByUser )
561{
562 // Users can add fields with variable names that match the special names in the grid,
563 // e.g. ${QUANTITY} so make sure we don't add them twice
564 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); i++ )
565 {
566 if( m_fieldsCtrl->GetTextValue( i, FIELD_NAME_COLUMN ) == aFieldName )
567 return;
568 }
569
570 m_dataModel->AddColumn( aFieldName, aLabelValue, addedByUser );
571
572 wxVector<wxVariant> fieldsCtrlRow;
573
574 std::string key( aFieldName.ToUTF8() );
575
576 // Don't change these to emplace_back: some versions of wxWidgets don't support it
577 fieldsCtrlRow.push_back( wxVariant( aFieldName ) );
578 fieldsCtrlRow.push_back( wxVariant( aLabelValue ) );
579 fieldsCtrlRow.push_back( wxVariant( show ) );
580 fieldsCtrlRow.push_back( wxVariant( groupBy ) );
581 fieldsCtrlRow.push_back( wxVariant( aFieldName ) );
582
583 m_fieldsCtrl->AppendItem( fieldsCtrlRow );
584
585 wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_APPENDED, 1 );
586 m_grid->ProcessTableMessage( msg );
587}
588
589
591{
592 // Add mandatory fields first
593 for( int i = 0; i < MANDATORY_FIELDS; ++i )
594 {
595 bool show = false;
596 bool groupBy = false;
597
598 switch( i )
599 {
600 case REFERENCE_FIELD:
601 case VALUE_FIELD:
602 case FOOTPRINT_FIELD:
603 show = true;
604 groupBy = true;
605 break;
606 case DATASHEET_FIELD:
607 show = true;
608 groupBy = false;
609 break;
610 }
611
613 TEMPLATE_FIELDNAME::GetDefaultFieldName( i, true ), show, groupBy );
614 }
615
616 // Generated fields present only in the fields table
619
620 // User fields next
621 std::set<wxString> userFieldNames;
622
623 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
624 {
625 SCH_SYMBOL* symbol = m_symbolsList[ i ].GetSymbol();
626
627 for( int j = MANDATORY_FIELDS; j < symbol->GetFieldCount(); ++j )
628 userFieldNames.insert( symbol->GetFields()[j].GetName() );
629 }
630
631 for( const wxString& fieldName : userFieldNames )
632 AddField( fieldName, GetTextVars( fieldName ), true, false );
633
634 // Add any templateFieldNames which aren't already present in the userFieldNames
635 for( const TEMPLATE_FIELDNAME& templateFieldname :
637 {
638 if( userFieldNames.count( templateFieldname.m_Name ) == 0 )
639 AddField( templateFieldname.m_Name, GetTextVars( templateFieldname.m_Name ), false,
640 false );
641 }
642}
643
644
645void DIALOG_SYMBOL_FIELDS_TABLE::OnAddField( wxCommandEvent& event )
646{
647 wxTextEntryDialog dlg( this, _( "New field name:" ), _( "Add Field" ) );
648
649 if( dlg.ShowModal() != wxID_OK )
650 return;
651
652 wxString fieldName = dlg.GetValue();
653
654 if( fieldName.IsEmpty() )
655 {
656 DisplayError( this, _( "Field must have a name." ) );
657 return;
658 }
659
660 for( int i = 0; i < m_dataModel->GetNumberCols(); ++i )
661 {
662 if( fieldName == m_dataModel->GetColFieldName( i ) )
663 {
664 DisplayError( this, wxString::Format( _( "Field name '%s' already in use." ),
665 fieldName ) );
666 return;
667 }
668 }
669
670 AddField( fieldName, GetTextVars( fieldName ), true, false, true );
671
672 SetupColumnProperties( m_dataModel->GetColsCount() - 1 );
673
675}
676
677
678void DIALOG_SYMBOL_FIELDS_TABLE::OnRemoveField( wxCommandEvent& event )
679{
680 int col = -1;
681 int row = m_fieldsCtrl->GetSelectedRow();
682
683 // Should never occur: "Remove Field..." button should be disabled if invalid selection
684 // via OnFieldsCtrlSelectionChanged()
685 wxCHECK_RET( row != -1, wxS( "Some user defined field must be selected first" ) );
686 wxCHECK_RET( row >= MANDATORY_FIELDS, wxS( "Mandatory fields cannot be removed" ) );
687
688 wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
689 wxString displayName = m_fieldsCtrl->GetTextValue( row, DISPLAY_NAME_COLUMN );
690
691 wxString confirm_msg =
692 wxString::Format( _( "Are you sure you want to remove the field '%s'?" ), displayName );
693
694 if( !IsOK( this, confirm_msg ) )
695 return;
696
697 for( int i = 0; i < m_dataModel->GetNumberCols(); ++i )
698 {
699 if( fieldName == m_dataModel->GetColFieldName( i ) )
700 col = i;
701 }
702
703 m_fieldsCtrl->DeleteItem( row );
705
706 // Make selection and update the state of "Remove field..." button via OnFieldsCtrlSelectionChanged()
707 // Safe to decrement row index because we always have mandatory fields
708 m_fieldsCtrl->SelectRow( --row );
709
710 if( row < MANDATORY_FIELDS )
711 {
712 m_removeFieldButton->Enable( false );
713 m_renameFieldButton->Enable( false );
714 }
715
716 wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_DELETED, col, 1 );
717
718 m_grid->ProcessTableMessage( msg );
719
721}
722
723
724void DIALOG_SYMBOL_FIELDS_TABLE::OnRenameField( wxCommandEvent& event )
725{
726 int row = m_fieldsCtrl->GetSelectedRow();
727 wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
728
729 // Should never occur: "Rename Field..." button should be disabled if invalid selection
730 // via OnFieldsCtrlSelectionChanged()
731 wxCHECK_RET( row != -1, wxS( "Some user defined field must be selected first" ) );
732 wxCHECK_RET( row >= MANDATORY_FIELDS, wxS( "Mandatory fields cannot be renamed" ) );
733 wxCHECK_RET( !fieldName.IsEmpty(), wxS( "Field must have a name" ) );
734
735 int col = m_dataModel->GetFieldNameCol( fieldName );
736 wxCHECK_RET( col != -1, wxS( "Existing field name missing from data model" ) );
737
738 wxTextEntryDialog dlg( this, _( "New field name:" ), _( "Rename Field" ) );
739
740 if( dlg.ShowModal() != wxID_OK )
741 return;
742
743 wxString newFieldName = dlg.GetValue();
744
745 // No change, no-op
746 if( newFieldName == fieldName )
747 return;
748
749 // New field name already exists
750 if( m_dataModel->GetFieldNameCol( newFieldName ) != -1 )
751 {
752 wxString confirm_msg = wxString::Format(
753 _( "Field name %s already exists. Cannot rename over existing field." ),
754 newFieldName );
755 DisplayError( this, confirm_msg );
756 return;
757 }
758
759 m_dataModel->RenameColumn( col, newFieldName );
760 m_fieldsCtrl->SetTextValue( newFieldName, row, DISPLAY_NAME_COLUMN );
761 m_fieldsCtrl->SetTextValue( newFieldName, row, FIELD_NAME_COLUMN );
762 m_fieldsCtrl->SetTextValue( newFieldName, row, LABEL_COLUMN );
763
765}
766
767
768void DIALOG_SYMBOL_FIELDS_TABLE::OnFilterText( wxCommandEvent& aEvent )
769{
770 m_dataModel->SetFilter( m_filter->GetValue() );
772 m_grid->ForceRefresh();
773
775}
776
777
779{
780 wxPoint pos = aEvent.GetPosition();
781 wxRect ctrlRect = m_filter->GetScreenRect();
782 int buttonWidth = ctrlRect.GetHeight(); // Presume buttons are square
783
784 // TODO: restore cursor when mouse leaves the filter field (or is it a MSW bug?)
785 if( m_filter->IsSearchButtonVisible() && pos.x < buttonWidth )
786 SetCursor( wxCURSOR_ARROW );
787 else if( m_filter->IsCancelButtonVisible() && pos.x > ctrlRect.GetWidth() - buttonWidth )
788 SetCursor( wxCURSOR_ARROW );
789 else
790 SetCursor( wxCURSOR_IBEAM );
791}
792
793
795{
796 int row = m_fieldsCtrl->GetSelectedRow();
797
798 if( row >= MANDATORY_FIELDS )
799 {
802 }
803 else
804 {
805 m_removeFieldButton->Enable( false );
806 m_renameFieldButton->Enable( false );
807 }
808}
809
811{
812 wxDataViewItem item = event.GetItem();
813 int row = m_fieldsCtrl->ItemToRow( item );
814 int col = event.GetColumn();
815
816 switch ( col )
817 {
819 {
820 bool value = m_fieldsCtrl->GetToggleValue( row, col );
821 int dataCol = m_dataModel->GetFieldNameCol(
822 m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN ) );
823
824 m_dataModel->SetShowColumn( dataCol, value );
825
826 if( dataCol != -1 )
827 {
828 if( value )
829 m_grid->ShowCol( dataCol );
830 else
831 m_grid->HideCol( dataCol );
832 }
833
834 break;
835 }
836
837 case GROUP_BY_COLUMN:
838 {
839 bool value = m_fieldsCtrl->GetToggleValue( row, col );
840 int dataCol = m_dataModel->GetFieldNameCol(
841 m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN ) );
842
843 if( m_dataModel->ColIsQuantity( dataCol ) && value )
844 {
845 DisplayError( this, _( "The Quantity column cannot be grouped by." ) );
846
847 value = false;
848 m_fieldsCtrl->SetToggleValue( value, row, col );
849 }
850
851 if( m_dataModel->ColIsItemNumber( dataCol ) && value )
852 {
853 DisplayError( this, _( "The Item Number column cannot be grouped by." ) );
854
855 value = false;
856 m_fieldsCtrl->SetToggleValue( value, row, col );
857 }
858
859 wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
860
861 m_dataModel->SetGroupColumn( m_dataModel->GetFieldNameCol( fieldName ), value );
863 m_grid->ForceRefresh();
864 break;
865 }
866
867 default:
868 break;
869 }
870
872}
873
874
876{
879 m_grid->ForceRefresh();
880
882}
883
884
886{
889 m_grid->ForceRefresh();
890
892}
893
894
895void DIALOG_SYMBOL_FIELDS_TABLE::OnColSort( wxGridEvent& aEvent )
896{
897 int sortCol = aEvent.GetCol();
898 std::string key( m_dataModel->GetColFieldName( sortCol ).ToUTF8() );
899 bool ascending;
900
901 // Don't sort by item number, it is generated by the sort
902 if( m_dataModel->ColIsItemNumber( sortCol ) )
903 {
904 aEvent.Veto();
905 return;
906 }
907
908 // This is bonkers, but wxWidgets doesn't tell us ascending/descending in the event, and
909 // if we ask it will give us pre-event info.
910 if( m_grid->IsSortingBy( sortCol ) )
911 {
912 // same column; invert ascending
913 ascending = !m_grid->IsSortOrderAscending();
914 }
915 else
916 {
917 // different column; start with ascending
918 ascending = true;
919 }
920
921 m_dataModel->SetSorting( sortCol, ascending );
923 m_grid->ForceRefresh();
924
926}
927
928
929void DIALOG_SYMBOL_FIELDS_TABLE::OnColMove( wxGridEvent& aEvent )
930{
931 int origPos = aEvent.GetCol();
932
933 // Save column widths since the setup function uses the saved config values
935
936 for( int i = 0; i < m_grid->GetNumberCols(); i++ )
937 {
938 if( m_grid->IsColShown( i ) )
939 {
940 std::string fieldName( m_dataModel->GetColFieldName( i ).ToUTF8() );
941 cfg->m_FieldEditorPanel.field_widths[fieldName] = m_grid->GetColSize( i );
942 }
943 }
944
945 CallAfter(
946 [origPos, this]()
947 {
948 int newPos = m_grid->GetColPos( origPos );
949
950 m_dataModel->MoveColumn( origPos, newPos );
951
952 // "Unmove" the column since we've moved the column internally
953 m_grid->ResetColPos();
954
955 // We need to reset all the column attr's to the correct column order
957
958 m_grid->ForceRefresh();
959 } );
960
962}
963
964
965void DIALOG_SYMBOL_FIELDS_TABLE::OnColLabelChange( wxDataViewEvent& aEvent )
966{
967 wxDataViewItem item = aEvent.GetItem();
968 int row = m_fieldsCtrl->ItemToRow( item );
969 wxString label = m_fieldsCtrl->GetTextValue( row, LABEL_COLUMN );
970 wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
971 int col = m_dataModel->GetFieldNameCol( fieldName );
972
973 if( col != -1 )
974 m_dataModel->SetColLabelValue( col, label );
975
977
978 aEvent.Skip();
979
980 m_grid->ForceRefresh();
981}
982
984{
985 m_grid->ForceRefresh();
986}
987
988
989void DIALOG_SYMBOL_FIELDS_TABLE::OnTableColSize( wxGridSizeEvent& aEvent )
990{
991 int col = aEvent.GetRowOrCol();
992 std::string key( m_dataModel->GetColFieldName( col ).ToUTF8() );
993
994 aEvent.Skip();
995
996 m_grid->ForceRefresh();
997}
998
999
1001{
1003 m_grid->ForceRefresh();
1004}
1005
1007{
1008 UpdateScope();
1009}
1010
1012{
1014
1015 if( m_radioProject->GetValue() )
1016 m_dataModel->SetScope( FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_ALL );
1017 else if( m_radioCurrentSheet->GetValue() )
1018 m_dataModel->SetScope( FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_SHEET );
1019 else if( m_radioRecursive->GetValue() )
1020 m_dataModel->SetScope( FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_SHEET_RECURSIVE );
1021
1023}
1024
1026{
1027 if( m_dataModel->ColIsReference( event.GetCol() ) )
1028 {
1029 m_grid->ClearSelection();
1030
1031 m_dataModel->ExpandCollapseRow( event.GetRow() );
1032 m_grid->SetGridCursor( event.GetRow(), event.GetCol() );
1033 }
1034 else
1035 {
1036 event.Skip();
1037 }
1038}
1039
1040void DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected( wxGridRangeSelectEvent& aEvent )
1041{
1042 // Multi-select can grab the rows that are expanded child refs, and also the row
1043 // containing the list of all child refs. Make sure we add refs/symbols uniquely
1044 std::set<SCH_REFERENCE> refs;
1045 std::set<SCH_ITEM*> symbols;
1046
1047 // This handler handles selecting and deselecting
1048 if( aEvent.Selecting() )
1049 {
1050 for( int i = aEvent.GetTopRow(); i <= aEvent.GetBottomRow(); i++ )
1051 {
1052 for( const SCH_REFERENCE& ref : m_dataModel->GetRowReferences( i ) )
1053 refs.insert( ref );
1054 }
1055
1056 for( const SCH_REFERENCE& ref : refs )
1057 symbols.insert( ref.GetSymbol() );
1058 }
1059
1060 if( m_radioHighlight->GetValue() )
1061 {
1063
1064 if( refs.size() > 0 )
1065 {
1066 // Use of full path based on UUID allows select of not yet annotated or duplicaded symbols
1067 wxString symbol_path = refs.begin()->GetFullPath();
1068
1069 // Focus only handles on item at this time
1070 editor->FindSymbolAndItem( &symbol_path, nullptr, true, HIGHLIGHT_SYMBOL,
1071 wxEmptyString );
1072 }
1073 else
1074 {
1075 m_parent->FocusOnItem( nullptr );
1076 }
1077 }
1078 else if( m_radioSelect->GetValue() )
1079 {
1081
1082 std::vector<SCH_ITEM*> items( symbols.begin(), symbols.end() );
1083
1084 if( refs.size() > 0 )
1085 selTool->SyncSelection( refs.begin()->GetSheetPath(), nullptr, items );
1086 else
1087 selTool->ClearSelection();
1088 }
1089}
1090
1091
1093{
1094 // TODO: Option to select footprint if FOOTPRINT column selected
1095
1096 event.Skip();
1097}
1098
1099
1101{
1105#ifdef __WXMAC__
1106 // TODO: something in wxWidgets 3.1.x pads checkbox columns with extra space. (It used to
1107 // also be that the width of the column would get set too wide (to 30), but that's patched in
1108 // our local wxWidgets fork.)
1109 width -= 50;
1110#endif
1111
1112 m_fieldNameColWidth = width / 2;
1114
1115 // GTK loses its head and messes these up when resizing the splitter bar:
1116 m_fieldsCtrl->GetColumn( SHOW_FIELD_COLUMN )->SetWidth( m_showColWidth );
1117 m_fieldsCtrl->GetColumn( GROUP_BY_COLUMN )->SetWidth( m_groupByColWidth );
1118
1119 m_fieldsCtrl->GetColumn( FIELD_NAME_COLUMN )->SetHidden( true );
1120 m_fieldsCtrl->GetColumn( DISPLAY_NAME_COLUMN )->SetWidth( m_fieldNameColWidth );
1121 m_fieldsCtrl->GetColumn( LABEL_COLUMN )->SetWidth( m_labelColWidth );
1122
1123 m_fieldsCtrl->Refresh(); // To refresh checkboxes on Windows.
1124
1125 event.Skip();
1126}
1127
1128
1130{
1133}
1134
1135
1136void DIALOG_SYMBOL_FIELDS_TABLE::OnPageChanged( wxNotebookEvent& event )
1137{
1139}
1140
1141
1143{
1146}
1147
1148
1150{
1153
1155
1158}
1159
1160
1162{
1163 BOM_FMT_PRESET current;
1164
1165 current.name = m_cbBomFmtPresets->GetStringSelection();
1166 current.fieldDelimiter = m_textFieldDelimiter->GetValue();
1167 current.stringDelimiter = m_textStringDelimiter->GetValue();
1168 current.refDelimiter = m_textRefDelimiter->GetValue();
1169 current.refRangeDelimiter = m_textRefRangeDelimiter->GetValue();
1170 current.keepTabs = m_checkKeepTabs->GetValue();
1171 current.keepLineBreaks = m_checkKeepLineBreaks->GetValue();
1172
1173 return current;
1174}
1175
1176
1178{
1179 m_nbPages->SetSelection( 0 );
1180}
1181
1182
1184{
1185 m_nbPages->SetSelection( 1 );
1186}
1187
1188
1190{
1191 // Build the absolute path of current output directory to preselect it in the file browser.
1192 wxString path = ExpandEnvVarSubstitutions( m_outputFileName->GetValue(), &Prj() );
1193 path = Prj().AbsolutePath( path );
1194
1195
1196 // Calculate the export filename
1197 wxFileName fn( Prj().AbsolutePath( m_parent->Schematic().GetFileName() ) );
1198 fn.SetExt( FILEEXT::CsvFileExtension );
1199
1200 wxFileDialog saveDlg( this, _( "Bill of Materials Output File" ), path, fn.GetFullName(),
1201 FILEEXT::CsvFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1202
1203 if( saveDlg.ShowModal() == wxID_CANCEL )
1204 return;
1205
1206
1207 wxFileName file = wxFileName( saveDlg.GetPath() );
1208 wxString defaultPath = fn.GetPathWithSep();
1209 wxString msg;
1210 msg.Printf( _( "Do you want to use a path relative to\n'%s'?" ), defaultPath );
1211
1212 wxMessageDialog dialog( this, msg, _( "BOM Output File" ),
1213 wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT );
1214
1215 if( dialog.ShowModal() == wxID_YES )
1216 {
1217 if( !file.MakeRelativeTo( defaultPath ) )
1218 {
1219 wxMessageBox( _( "Cannot make path relative (target volume different from schematic "
1220 "file volume)!" ),
1221 _( "BOM Output File" ), wxOK | wxICON_ERROR );
1222 }
1223 }
1224
1225 m_outputFileName->SetValue( file.GetFullPath() );
1226}
1227
1228
1229void DIALOG_SYMBOL_FIELDS_TABLE::OnExport( wxCommandEvent& aEvent )
1230{
1231 if( m_dataModel->IsEdited() )
1232 if( OKOrCancelDialog( nullptr, _( "Unsaved data" ),
1233 _( "Changes are unsaved. Export unsaved data?" ), "", _( "OK" ),
1234 _( "Cancel" ) )
1235 == wxID_CANCEL )
1236 return;
1237
1238 // Create output directory if it does not exist (also transform it in absolute form).
1239 // Bail if it fails.
1240
1241 std::function<bool( wxString* )> textResolver =
1242 [&]( wxString* token ) -> bool
1243 {
1244 SCHEMATIC& schematic = m_parent->Schematic();
1245
1246 // Handles m_board->GetTitleBlock() *and* m_board->GetProject()
1247 return schematic.ResolveTextVar( &schematic.CurrentSheet(), token, 0 );
1248 };
1249
1250 wxString path = m_outputFileName->GetValue();
1251
1252 if( path.IsEmpty() )
1253 {
1254 DisplayError( this, _( "No filename specified in exporter" ) );
1255 return;
1256 }
1257
1258 path = ExpandTextVars( path, &textResolver );
1259 path = ExpandEnvVarSubstitutions( path, nullptr );
1260
1261 wxFileName outputFile = wxFileName::FileName( path );
1262 wxString msg;
1263
1264 if( !EnsureFileDirectoryExists( &outputFile,
1265 Prj().AbsolutePath( m_parent->Schematic().GetFileName() ),
1267 {
1268 msg.Printf( _( "Could not open/create path '%s'." ), outputFile.GetPath() );
1269 DisplayError( this, msg );
1270 return;
1271 }
1272
1273 wxFFile out( outputFile.GetFullPath(), "wb" );
1274
1275 if( !out.IsOpened() )
1276 {
1277 msg.Printf( _( "Could not create BOM output '%s'." ), outputFile.GetFullPath() );
1278 DisplayError( this, msg );
1279 return;
1280 }
1281
1283
1284 if( !out.Write( m_textOutput->GetValue() ) )
1285 {
1286 msg.Printf( _( "Could not write BOM output '%s'." ), outputFile.GetFullPath() );
1287 DisplayError( this, msg );
1288 return;
1289 }
1290
1291 msg.Printf( _( "Wrote BOM output to '%s'" ), outputFile.GetFullPath() );
1292 DisplayInfoMessage( this, msg );
1293}
1294
1295
1296void DIALOG_SYMBOL_FIELDS_TABLE::OnCancel( wxCommandEvent& aEvent )
1297{
1298 Close();
1299}
1300
1301
1302void DIALOG_SYMBOL_FIELDS_TABLE::OnOk( wxCommandEvent& aEvent )
1303{
1305 Close();
1306}
1307
1308
1309void DIALOG_SYMBOL_FIELDS_TABLE::OnClose( wxCloseEvent& aEvent )
1310{
1311 // This is a cancel, so commit quietly as we're going to throw the results away anyway.
1313
1314 if( m_dataModel->IsEdited() )
1315 {
1316 if( !HandleUnsavedChanges( this, _( "Save changes?" ),
1317 [&]() -> bool
1318 {
1319 return TransferDataFromWindow();
1320 } ) )
1321 {
1322 aEvent.Veto();
1323 return;
1324 }
1325 }
1326
1327 // Stop listening to schematic events
1329
1330 // Save all our settings since we're really closing
1333
1334 cfg->m_FieldEditorPanel.width = GetSize().x;
1335 cfg->m_FieldEditorPanel.height = GetSize().y;
1336 cfg->m_FieldEditorPanel.page = m_nbPages->GetSelection();
1338
1339 if( m_radioHighlight->GetValue() )
1341 else if( m_radioSelect->GetValue() )
1343 else if( m_radioOff->GetValue() )
1345
1346 if( m_radioProject->GetValue() )
1347 cfg->m_FieldEditorPanel.scope = SCOPE::SCOPE_ALL;
1348 else if( m_radioCurrentSheet->GetValue() )
1349 cfg->m_FieldEditorPanel.scope = SCOPE::SCOPE_SHEET;
1350 else if( m_radioRecursive->GetValue() )
1351 cfg->m_FieldEditorPanel.scope = SCOPE::SCOPE_SHEET_RECURSIVE;
1352
1353 for( int i = 0; i < m_grid->GetNumberCols(); i++ )
1354 {
1355 if( m_grid->IsColShown( i ) )
1356 {
1357 std::string fieldName( m_dataModel->GetColFieldName( i ).ToUTF8() );
1358 cfg->m_FieldEditorPanel.field_widths[fieldName] = m_grid->GetColSize( i );
1359 }
1360 }
1361
1362 m_parent->FocusOnItem( nullptr );
1363
1364 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, wxID_ANY );
1365
1366 wxWindow* parent = GetParent();
1367
1368 if( parent )
1369 wxQueueEvent( parent, evt );
1370}
1371
1372
1374{
1375 std::vector<BOM_PRESET> ret;
1376
1377 for( const std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1378 {
1379 if( !pair.second.readOnly )
1380 ret.emplace_back( pair.second );
1381 }
1382
1383 return ret;
1384}
1385
1386
1387void DIALOG_SYMBOL_FIELDS_TABLE::SetUserBomPresets( std::vector<BOM_PRESET>& aPresetList )
1388{
1389 // Reset to defaults
1391
1392 for( const BOM_PRESET& preset : aPresetList )
1393 {
1394 if( m_bomPresets.count( preset.name ) )
1395 continue;
1396
1397 m_bomPresets[preset.name] = preset;
1398
1399 m_bomPresetMRU.Add( preset.name );
1400 }
1401
1403}
1404
1405
1406void DIALOG_SYMBOL_FIELDS_TABLE::ApplyBomPreset( const wxString& aPresetName )
1407{
1408 updateBomPresetSelection( aPresetName );
1409
1410 wxCommandEvent dummy;
1412}
1413
1414
1416{
1417 if( m_bomPresets.count( aPreset.name ) )
1419 else
1420 m_currentBomPreset = nullptr;
1421
1424 else
1425 m_lastSelectedBomPreset = nullptr;
1426
1427 updateBomPresetSelection( aPreset.name );
1428 doApplyBomPreset( aPreset );
1429}
1430
1431
1433{
1434 m_bomPresets.clear();
1435 m_bomPresetMRU.clear();
1436
1437 // Load the read-only defaults
1438 for( const BOM_PRESET& preset : BOM_PRESET::BuiltInPresets() )
1439 {
1440 m_bomPresets[preset.name] = preset;
1441 m_bomPresets[preset.name].readOnly = true;
1442
1443 m_bomPresetMRU.Add( preset.name );
1444 }
1445}
1446
1447
1449{
1450 m_cbBomPresets->Clear();
1451
1452 // Build the layers preset list.
1453 // By default, the presetAllLayers will be selected
1454 int idx = 0;
1455 int default_idx = 0;
1456
1457 for( std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1458 {
1459 m_cbBomPresets->Append( wxGetTranslation( pair.first ),
1460 static_cast<void*>( &pair.second ) );
1461
1462 if( pair.first == BOM_PRESET::GroupedByValue().name )
1463 default_idx = idx;
1464
1465 idx++;
1466 }
1467
1468 m_cbBomPresets->Append( wxT( "---" ) );
1469 m_cbBomPresets->Append( _( "Save preset..." ) );
1470 m_cbBomPresets->Append( _( "Delete preset..." ) );
1471
1472 // At least the built-in presets should always be present
1473 wxASSERT( !m_bomPresets.empty() );
1474
1475 // Default preset: all Boms
1476 m_cbBomPresets->SetSelection( default_idx );
1477 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( default_idx ) );
1478}
1479
1480
1482{
1484
1485 auto it = std::find_if( m_bomPresets.begin(), m_bomPresets.end(),
1486 [&]( const std::pair<const wxString, BOM_PRESET>& aPair )
1487 {
1488 const BOM_PRESET& preset = aPair.second;
1489
1490 // Check the simple settings first
1491 if( !( preset.sortField == current.sortField
1492 && preset.sortAsc == current.sortAsc
1493 && preset.filterString == current.filterString
1494 && preset.groupSymbols == current.groupSymbols
1495 && preset.excludeDNP == current.excludeDNP ) )
1496 {
1497 return false;
1498 }
1499
1500 // Only compare shown or grouped fields
1501 std::vector<BOM_FIELD> A, B;
1502
1503 for( const BOM_FIELD& field : preset.fieldsOrdered )
1504 {
1505 if( field.show || field.groupBy )
1506 A.emplace_back( field );
1507 }
1508
1509 for( const BOM_FIELD& field : current.fieldsOrdered )
1510 {
1511 if( field.show || field.groupBy )
1512 B.emplace_back( field );
1513 }
1514
1515 return A == B;
1516 } );
1517
1518 if( it != m_bomPresets.end() )
1519 {
1520 // Select the right m_cbBomPresets item.
1521 // but these items are translated if they are predefined items.
1522 bool do_translate = it->second.readOnly;
1523 wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
1524
1525 m_cbBomPresets->SetStringSelection( text );
1526 }
1527 else
1528 {
1529 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 ); // separator
1530 }
1531
1532 m_currentBomPreset = static_cast<BOM_PRESET*>(
1533 m_cbBomPresets->GetClientData( m_cbBomPresets->GetSelection() ) );
1534}
1535
1536
1538{
1539 // look at m_userBomPresets to know if aName is a read only preset, or a user preset.
1540 // Read only presets have translated names in UI, so we have to use
1541 // a translated name in UI selection.
1542 // But for a user preset name we should search for aName (not translated)
1543 wxString ui_label = aName;
1544
1545 for( std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1546 {
1547 if( pair.first != aName )
1548 continue;
1549
1550 if( pair.second.readOnly == true )
1551 ui_label = wxGetTranslation( aName );
1552
1553 break;
1554 }
1555
1556 int idx = m_cbBomPresets->FindString( ui_label );
1557
1558 if( idx >= 0 && m_cbBomPresets->GetSelection() != idx )
1559 {
1560 m_cbBomPresets->SetSelection( idx );
1561 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( idx ) );
1562 }
1563 else if( idx < 0 )
1564 {
1565 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 ); // separator
1566 }
1567}
1568
1569
1571{
1572 int count = m_cbBomPresets->GetCount();
1573 int index = m_cbBomPresets->GetSelection();
1574
1575 auto resetSelection =
1576 [&]()
1577 {
1578 if( m_currentBomPreset )
1579 m_cbBomPresets->SetStringSelection( m_currentBomPreset->name );
1580 else
1581 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 );
1582 };
1583
1584 if( index == count - 3 )
1585 {
1586 // Separator: reject the selection
1587 resetSelection();
1588 return;
1589 }
1590 else if( index == count - 2 )
1591 {
1592 // Save current state to new preset
1593 wxString name;
1594
1597
1598 wxTextEntryDialog dlg( this, _( "BOM preset name:" ), _( "Save BOM Preset" ), name );
1599
1600 if( dlg.ShowModal() != wxID_OK )
1601 {
1602 resetSelection();
1603 return;
1604 }
1605
1606 name = dlg.GetValue();
1607 bool exists = m_bomPresets.count( name );
1608
1609 if( !exists )
1610 {
1612 m_bomPresets[name].readOnly = false;
1613 m_bomPresets[name].name = name;
1614 }
1615
1616 BOM_PRESET* preset = &m_bomPresets[name];
1617
1618 if( !exists )
1619 {
1620 index = m_cbBomPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
1621 }
1622 else if( preset->readOnly )
1623 {
1624 wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
1625 _( "Error" ), wxOK | wxICON_ERROR, this );
1626 resetSelection();
1627 return;
1628 }
1629 else
1630 {
1631 // Ask the user if they want to overwrite the existing preset
1632 if( !IsOK( this, _( "Overwrite existing preset?" ) ) )
1633 {
1634 resetSelection();
1635 return;
1636 }
1637
1638 *preset = m_dataModel->GetBomSettings();
1639 preset->name = name;
1640
1641 index = m_cbBomPresets->FindString( name );
1642 m_bomPresetMRU.Remove( name );
1643 }
1644
1645 m_currentBomPreset = preset;
1646 m_cbBomPresets->SetSelection( index );
1647 m_bomPresetMRU.Insert( name, 0 );
1648
1649 return;
1650 }
1651 else if( index == count - 1 )
1652 {
1653 // Delete a preset
1654 wxArrayString headers;
1655 std::vector<wxArrayString> items;
1656
1657 headers.Add( _( "Presets" ) );
1658
1659 for( std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1660 {
1661 if( !pair.second.readOnly )
1662 {
1663 wxArrayString item;
1664 item.Add( pair.first );
1665 items.emplace_back( item );
1666 }
1667 }
1668
1669 EDA_LIST_DIALOG dlg( this, _( "Delete Preset" ), headers, items );
1670 dlg.SetListLabel( _( "Select preset:" ) );
1671
1672 if( dlg.ShowModal() == wxID_OK )
1673 {
1674 wxString presetName = dlg.GetTextSelection();
1675 int idx = m_cbBomPresets->FindString( presetName );
1676
1677 if( idx != wxNOT_FOUND )
1678 {
1679 m_bomPresets.erase( presetName );
1680
1681 m_cbBomPresets->Delete( idx );
1682 m_currentBomPreset = nullptr;
1683
1684 m_bomPresetMRU.Remove( presetName );
1685 }
1686 }
1687
1688 resetSelection();
1689 return;
1690 }
1691
1692 BOM_PRESET* preset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( index ) );
1693 m_currentBomPreset = preset;
1694
1695 m_lastSelectedBomPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
1696
1697 if( preset )
1698 {
1699 doApplyBomPreset( *preset );
1701 m_currentBomPreset = preset;
1702
1703 if( !m_currentBomPreset->name.IsEmpty() )
1704 {
1705 m_bomPresetMRU.Remove( preset->name );
1706 m_bomPresetMRU.Insert( preset->name, 0 );
1707 }
1708 }
1709}
1710
1711
1713{
1714 // Disable rebuilds while we're applying the preset otherwise we'll be
1715 // rebuilding the model constantly while firing off wx events
1717
1718 // Basically, we apply the BOM preset to the data model and then
1719 // update our UI to reflect resulting the data model state, not the preset.
1720 m_dataModel->ApplyBomPreset( aPreset );
1721
1722 // BOM Presets can add, but not remove, columns, so make sure the field control
1723 // grid has all of them before starting
1724 for( int i = 0; i < m_dataModel->GetColsCount(); i++ )
1725 {
1726 const wxString& fieldName( m_dataModel->GetColFieldName( i ) );
1727 bool found = false;
1728
1729 for( int j = 0; j < m_fieldsCtrl->GetItemCount(); j++ )
1730 {
1731 if( m_fieldsCtrl->GetTextValue( j, FIELD_NAME_COLUMN ) == fieldName )
1732 {
1733 found = true;
1734 break;
1735 }
1736 }
1737
1738 // Properties like label, etc. will be added in the next loop
1739 if( !found )
1740 AddField( fieldName, GetTextVars( fieldName ), false, false );
1741 }
1742
1743 // Sync all fields
1744 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); i++ )
1745 {
1746 const wxString& fieldName( m_fieldsCtrl->GetTextValue( i, FIELD_NAME_COLUMN ) );
1747 int col = m_dataModel->GetFieldNameCol( fieldName );
1748
1749 if( col == -1 )
1750 {
1751 wxASSERT_MSG( true, "Fields control has a field not found in the data model." );
1752 continue;
1753 }
1754
1756 std::string fieldNameStr( fieldName.ToUTF8() );
1757
1758 // Set column labels
1759 const wxString& label = m_dataModel->GetColLabelValue( col );
1760 m_fieldsCtrl->SetTextValue( label, i, LABEL_COLUMN );
1761 m_grid->SetColLabelValue( col, label );
1762
1763 if( cfg->m_FieldEditorPanel.field_widths.count( fieldNameStr ) )
1764 m_grid->SetColSize( col, cfg->m_FieldEditorPanel.field_widths.at( fieldNameStr ) );
1765
1766 // Set shown colums
1767 bool show = m_dataModel->GetShowColumn( col );
1768 m_fieldsCtrl->SetToggleValue( show, i, SHOW_FIELD_COLUMN );
1769
1770 if( show )
1771 m_grid->ShowCol( col );
1772 else
1773 m_grid->HideCol( col );
1774
1775 // Set grouped columns
1776 bool groupBy = m_dataModel->GetGroupColumn( col );
1777 m_fieldsCtrl->SetToggleValue( groupBy, i, GROUP_BY_COLUMN );
1778 }
1779
1780 m_grid->SetSortingColumn( m_dataModel->GetSortCol(), m_dataModel->GetSortAsc() );
1782 m_filter->ChangeValue( m_dataModel->GetFilter() );
1784
1786
1787 // This will rebuild all rows and columns in the model such that the order
1788 // and labels are right, then we refresh the shown grid data to match
1791 m_grid->ForceRefresh();
1792}
1793
1794
1795std::vector<BOM_FMT_PRESET> DIALOG_SYMBOL_FIELDS_TABLE::GetUserBomFmtPresets() const
1796{
1797 std::vector<BOM_FMT_PRESET> ret;
1798
1799 for( const std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
1800 {
1801 if( !pair.second.readOnly )
1802 ret.emplace_back( pair.second );
1803 }
1804
1805 return ret;
1806}
1807
1808
1809void DIALOG_SYMBOL_FIELDS_TABLE::SetUserBomFmtPresets( std::vector<BOM_FMT_PRESET>& aPresetList )
1810{
1811 // Reset to defaults
1813
1814 for( const BOM_FMT_PRESET& preset : aPresetList )
1815 {
1816 if( m_bomFmtPresets.count( preset.name ) )
1817 continue;
1818
1819 m_bomFmtPresets[preset.name] = preset;
1820
1821 m_bomFmtPresetMRU.Add( preset.name );
1822 }
1823
1825}
1826
1827
1828void DIALOG_SYMBOL_FIELDS_TABLE::ApplyBomFmtPreset( const wxString& aPresetName )
1829{
1830 updateBomFmtPresetSelection( aPresetName );
1831
1832 wxCommandEvent dummy;
1834}
1835
1836
1838{
1839 if( m_bomFmtPresets.count( aPreset.name ) )
1841 else
1842 m_currentBomFmtPreset = nullptr;
1843
1846 : nullptr;
1847
1849 doApplyBomFmtPreset( aPreset );
1850}
1851
1852
1854{
1855 m_bomFmtPresets.clear();
1856 m_bomFmtPresetMRU.clear();
1857
1858 // Load the read-only defaults
1859 for( const BOM_FMT_PRESET& preset : BOM_FMT_PRESET::BuiltInPresets() )
1860 {
1861 m_bomFmtPresets[preset.name] = preset;
1862 m_bomFmtPresets[preset.name].readOnly = true;
1863
1864 m_bomFmtPresetMRU.Add( preset.name );
1865 }
1866}
1867
1868
1870{
1871 m_cbBomFmtPresets->Clear();
1872
1873 // Build the layers preset list.
1874 // By default, the presetAllLayers will be selected
1875 int idx = 0;
1876 int default_idx = 0;
1877
1878 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
1879 {
1880 m_cbBomFmtPresets->Append( wxGetTranslation( pair.first ),
1881 static_cast<void*>( &pair.second ) );
1882
1883 if( pair.first == BOM_FMT_PRESET::CSV().name )
1884 default_idx = idx;
1885
1886 idx++;
1887 }
1888
1889 m_cbBomFmtPresets->Append( wxT( "---" ) );
1890 m_cbBomFmtPresets->Append( _( "Save preset..." ) );
1891 m_cbBomFmtPresets->Append( _( "Delete preset..." ) );
1892
1893 // At least the built-in presets should always be present
1894 wxASSERT( !m_bomFmtPresets.empty() );
1895
1896 // Default preset: all Boms
1897 m_cbBomFmtPresets->SetSelection( default_idx );
1899 static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( default_idx ) );
1900}
1901
1902
1904{
1906
1907 auto it = std::find_if( m_bomFmtPresets.begin(), m_bomFmtPresets.end(),
1908 [&]( const std::pair<const wxString, BOM_FMT_PRESET>& aPair )
1909 {
1910 return ( aPair.second.fieldDelimiter == current.fieldDelimiter
1911 && aPair.second.stringDelimiter == current.stringDelimiter
1912 && aPair.second.refDelimiter == current.refDelimiter
1913 && aPair.second.refRangeDelimiter == current.refRangeDelimiter
1914 && aPair.second.keepTabs == current.keepTabs
1915 && aPair.second.keepLineBreaks == current.keepLineBreaks );
1916 } );
1917
1918 if( it != m_bomFmtPresets.end() )
1919 {
1920 // Select the right m_cbBomFmtPresets item.
1921 // but these items are translated if they are predefined items.
1922 bool do_translate = it->second.readOnly;
1923 wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
1924
1925 m_cbBomFmtPresets->SetStringSelection( text );
1926 }
1927 else
1928 {
1929 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 ); // separator
1930 }
1931
1932 m_currentBomFmtPreset = static_cast<BOM_FMT_PRESET*>(
1933 m_cbBomFmtPresets->GetClientData( m_cbBomFmtPresets->GetSelection() ) );
1934}
1935
1936
1938{
1939 // look at m_userBomFmtPresets to know if aName is a read only preset, or a user preset.
1940 // Read only presets have translated names in UI, so we have to use
1941 // a translated name in UI selection.
1942 // But for a user preset name we should search for aName (not translated)
1943 wxString ui_label = aName;
1944
1945 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
1946 {
1947 if( pair.first != aName )
1948 continue;
1949
1950 if( pair.second.readOnly == true )
1951 ui_label = wxGetTranslation( aName );
1952
1953 break;
1954 }
1955
1956 int idx = m_cbBomFmtPresets->FindString( ui_label );
1957
1958 if( idx >= 0 && m_cbBomFmtPresets->GetSelection() != idx )
1959 {
1960 m_cbBomFmtPresets->SetSelection( idx );
1962 static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( idx ) );
1963 }
1964 else if( idx < 0 )
1965 {
1966 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 ); // separator
1967 }
1968}
1969
1970
1972{
1973 int count = m_cbBomFmtPresets->GetCount();
1974 int index = m_cbBomFmtPresets->GetSelection();
1975
1976 auto resetSelection =
1977 [&]()
1978 {
1980 m_cbBomFmtPresets->SetStringSelection( m_currentBomFmtPreset->name );
1981 else
1982 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 );
1983 };
1984
1985 if( index == count - 3 )
1986 {
1987 // Separator: reject the selection
1988 resetSelection();
1989 return;
1990 }
1991 else if( index == count - 2 )
1992 {
1993 // Save current state to new preset
1994 wxString name;
1995
1998
1999 wxTextEntryDialog dlg( this, _( "BOM preset name:" ), _( "Save BOM Preset" ), name );
2000
2001 if( dlg.ShowModal() != wxID_OK )
2002 {
2003 resetSelection();
2004 return;
2005 }
2006
2007 name = dlg.GetValue();
2008 bool exists = m_bomFmtPresets.count( name );
2009
2010 if( !exists )
2011 {
2013 m_bomFmtPresets[name].readOnly = false;
2014 m_bomFmtPresets[name].name = name;
2015 }
2016
2018
2019 if( !exists )
2020 {
2021 index = m_cbBomFmtPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
2022 }
2023 else if( preset->readOnly )
2024 {
2025 wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
2026 _( "Error" ), wxOK | wxICON_ERROR, this );
2027 resetSelection();
2028 return;
2029 }
2030 else
2031 {
2032 // Ask the user if they want to overwrite the existing preset
2033 if( !IsOK( this, _( "Overwrite existing preset?" ) ) )
2034 {
2035 resetSelection();
2036 return;
2037 }
2038
2039 *preset = GetCurrentBomFmtSettings();
2040 preset->name = name;
2041
2042 index = m_cbBomFmtPresets->FindString( name );
2043 m_bomFmtPresetMRU.Remove( name );
2044 }
2045
2046 m_currentBomFmtPreset = preset;
2047 m_cbBomFmtPresets->SetSelection( index );
2048 m_bomFmtPresetMRU.Insert( name, 0 );
2049
2050 return;
2051 }
2052 else if( index == count - 1 )
2053 {
2054 // Delete a preset
2055 wxArrayString headers;
2056 std::vector<wxArrayString> items;
2057
2058 headers.Add( _( "Presets" ) );
2059
2060 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
2061 {
2062 if( !pair.second.readOnly )
2063 {
2064 wxArrayString item;
2065 item.Add( pair.first );
2066 items.emplace_back( item );
2067 }
2068 }
2069
2070 EDA_LIST_DIALOG dlg( this, _( "Delete Preset" ), headers, items );
2071 dlg.SetListLabel( _( "Select preset:" ) );
2072
2073 if( dlg.ShowModal() == wxID_OK )
2074 {
2075 wxString presetName = dlg.GetTextSelection();
2076 int idx = m_cbBomFmtPresets->FindString( presetName );
2077
2078 if( idx != wxNOT_FOUND )
2079 {
2080 m_bomFmtPresets.erase( presetName );
2081
2082 m_cbBomFmtPresets->Delete( idx );
2083 m_currentBomFmtPreset = nullptr;
2084
2085 m_bomFmtPresetMRU.Remove( presetName );
2086 }
2087 }
2088
2089 resetSelection();
2090 return;
2091 }
2092
2093 BOM_FMT_PRESET* preset =
2094 static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( index ) );
2095 m_currentBomFmtPreset = preset;
2096
2097 m_lastSelectedBomFmtPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
2098
2099 if( preset )
2100 {
2101 doApplyBomFmtPreset( *preset );
2103 m_currentBomFmtPreset = preset;
2104
2105 if( !m_currentBomFmtPreset->name.IsEmpty() )
2106 {
2107 m_bomFmtPresetMRU.Remove( preset->name );
2108 m_bomFmtPresetMRU.Insert( preset->name, 0 );
2109 }
2110 }
2111}
2112
2113
2115{
2116 m_textFieldDelimiter->ChangeValue( aPreset.fieldDelimiter );
2117 m_textStringDelimiter->ChangeValue( aPreset.stringDelimiter );
2118 m_textRefDelimiter->ChangeValue( aPreset.refDelimiter );
2119 m_textRefRangeDelimiter->ChangeValue( aPreset.refRangeDelimiter );
2120 m_checkKeepTabs->SetValue( aPreset.keepTabs );
2121 m_checkKeepLineBreaks->SetValue( aPreset.keepLineBreaks );
2122
2123
2124 // Refresh the preview if that's the current page
2125 if( m_nbPages->GetSelection() == 1 )
2127}
2128
2129
2131{
2132 bool modified = false;
2133
2134 // Save our BOM presets
2135 std::vector<BOM_PRESET> presets;
2136
2137 for( const std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
2138 {
2139 if( !pair.second.readOnly )
2140 presets.emplace_back( pair.second );
2141 }
2142
2143 if( m_schSettings.m_BomPresets != presets )
2144 {
2145 modified = true;
2146 m_schSettings.m_BomPresets = presets;
2147 }
2148
2150 {
2151 modified = true;
2153 }
2154
2155
2156 // Save our BOM Format presets
2157 std::vector<BOM_FMT_PRESET> fmts;
2158
2159 for( const std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
2160 {
2161 if( !pair.second.readOnly )
2162 fmts.emplace_back( pair.second );
2163 }
2164
2165 if( m_schSettings.m_BomFmtPresets != fmts )
2166 {
2167 modified = true;
2169 }
2170
2172 {
2173 modified = true;
2175 }
2176
2177 if( modified )
2178 m_parent->OnModify();
2179}
2180
2181
2183 std::vector<SCH_ITEM*>& aSchItem )
2184{
2185 SCH_SHEET_LIST allSheets = m_parent->Schematic().GetSheets();
2186 SCH_REFERENCE_LIST allRefs;
2187
2188 allSheets.GetSymbols( allRefs );
2189
2190 for( SCH_ITEM* item : aSchItem )
2191 {
2192 if( item->Type() == SCH_SYMBOL_T )
2193 {
2194 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2195
2196 // Add all fields again in case this symbol has a new one
2197 for( SCH_FIELD& field : symbol->GetFields() )
2198 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2199
2200 m_dataModel->AddReferences( getSymbolReferences( symbol, allRefs ) );
2201 }
2202 else if( item->Type() == SCH_SHEET_T )
2203 {
2204 std::set<SCH_SYMBOL*> symbols;
2205 SCH_REFERENCE_LIST refs = getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) );
2206
2207 for( SCH_REFERENCE& ref : refs )
2208 symbols.insert( ref.GetSymbol() );
2209
2210 for( SCH_SYMBOL* symbol : symbols )
2211 {
2212 // Add all fields again in case this symbol has a new one
2213 for( SCH_FIELD& field : symbol->GetFields() )
2214 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2215 }
2216
2217 m_dataModel->AddReferences( refs );
2218 }
2219 }
2220
2224}
2225
2226
2228 std::vector<SCH_ITEM*>& aSchItem )
2229{
2230 for( SCH_ITEM* item : aSchItem )
2231 {
2232 if( item->Type() == SCH_SYMBOL_T )
2233 m_dataModel->RemoveSymbol( *static_cast<SCH_SYMBOL*>( item ) );
2234 else if( item->Type() == SCH_SHEET_T )
2236 getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) ) );
2237 }
2238
2242}
2243
2244
2246 std::vector<SCH_ITEM*>& aSchItem )
2247{
2248 SCH_SHEET_LIST allSheets = m_parent->Schematic().GetSheets();
2249 SCH_REFERENCE_LIST allRefs;
2250
2251 allSheets.GetSymbols( allRefs );
2252
2253 for( SCH_ITEM* item : aSchItem )
2254 {
2255 if( item->Type() == SCH_SYMBOL_T )
2256 {
2257 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2258
2259 // Add all fields again in case this symbol has a new one
2260 for( SCH_FIELD& field : symbol->GetFields() )
2261 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2262
2263 m_dataModel->UpdateReferences( getSymbolReferences( symbol, allRefs ) );
2264 }
2265 else if( item->Type() == SCH_SHEET_T )
2266 {
2267 std::set<SCH_SYMBOL*> symbols;
2268 SCH_REFERENCE_LIST refs = getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) );
2269
2270 for( SCH_REFERENCE& ref : refs )
2271 symbols.insert( ref.GetSymbol() );
2272
2273 for( SCH_SYMBOL* symbol : symbols )
2274 {
2275 // Add all fields again in case this symbol has a new one
2276 for( SCH_FIELD& field : symbol->GetFields() )
2277 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2278 }
2279
2281 }
2282 }
2283
2287}
2288
2289
2291{
2292 m_dataModel->SetPath( aSch.CurrentSheet() );
2293
2294 if( m_dataModel->GetScope() != FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_ALL )
2295 {
2299 }
2300}
2301
2302
2304{
2305 m_grid->Connect(
2306 wxEVT_GRID_RANGE_SELECTED,
2307 wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected ),
2308 nullptr, this );
2309}
2310
2311
2313{
2314 m_grid->Disconnect(
2315 wxEVT_GRID_RANGE_SELECTED,
2316 wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected ),
2317 nullptr, this );
2318}
2319
2320
2323 SCH_REFERENCE_LIST& aCachedRefs )
2324{
2325 SCH_SHEET_LIST allSheets = m_parent->Schematic().GetSheets();
2326 SCH_REFERENCE_LIST symbolRefs;
2327
2328 for( size_t i = 0; i < aCachedRefs.GetCount(); i++ )
2329 {
2330 SCH_REFERENCE& ref = aCachedRefs[i];
2331
2332 if( ref.GetSymbol() == aSymbol )
2333 {
2334 ref.Split(); // Figures out if we are annotated or not
2335 symbolRefs.AddItem( ref );
2336 }
2337 }
2338
2339 return symbolRefs;
2340}
2341
2342
2344{
2345 SCH_SHEET_LIST allSheets = m_parent->Schematic().GetSheets();
2346 SCH_REFERENCE_LIST sheetRefs;
2347
2348 // We need to operate on all instances of the sheet
2349 for( const SCH_SHEET_INSTANCE& instance : aSheet.GetInstances() )
2350 {
2351 // For every sheet instance we need to get the current schematic sheet
2352 // instance that matches that particular sheet path from the root
2353 for( SCH_SHEET_PATH& basePath : allSheets )
2354 {
2355 if( basePath.Path() == instance.m_Path )
2356 {
2357 SCH_SHEET_PATH sheetPath = basePath;
2358 sheetPath.push_back( &aSheet );
2359
2360 // Create a list of all sheets in this path, starting with the path
2361 // of the sheet that we just deleted, then all of its subsheets
2362 SCH_SHEET_LIST subSheets;
2363 subSheets.push_back( sheetPath );
2364 allSheets.GetSheetsWithinPath( subSheets, sheetPath );
2365
2366 subSheets.GetSymbolsWithinPath( sheetRefs, sheetPath, false, false );
2367 break;
2368 }
2369 }
2370 }
2371
2372 for( SCH_REFERENCE& ref : sheetRefs )
2373 ref.Split();
2374
2375 return sheetRefs;
2376}
const char * name
Definition: DXF_plotter.cpp:57
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:83
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:97
void SetupStandardButtons(std::map< int, wxString > aLabels={})
int horizPixelsFromDU(int x) const
Convert an integer number of dialog units to pixels, horizontally.
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
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
DIALOG_SYMBOL_FIELDS_TABLE(SCH_EDIT_FRAME *parent)
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
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:85
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
EDA_ITEM * GetParent() const
Definition: eda_item.h:99
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)
int GetFieldNameCol(wxString aFieldName)
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)
static const wxString ITEM_NUMBER_VARIABLE
void SetIncludeExcludedFromBOM(bool include)
void UpdateReferences(const SCH_REFERENCE_LIST &aRefs)
void ApplyData(std::function< void(SCH_SYMBOL &, SCH_SHEET_PATH &)> symbolChangeHandler)
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)
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
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:53
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Definition: kiway_player.h:67
virtual bool ShowModal(wxString *aResult=nullptr, wxWindow *aResultantFocusWindow=nullptr)
Show this wxFrame as if it were a modal dialog, with all other instantiated wxFrames disabled until t...
bool Destroy() override
Our version of Destroy() which is virtual from wxWidgets.
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:432
static REPORTER & GetInstance()
Definition: reporter.cpp:119
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:320
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:75
SCH_SHEET_PATH & CurrentSheet() const override
Definition: schematic.h:136
void RemoveListener(SCHEMATIC_LISTENER *aListener)
Remove the specified listener.
Definition: schematic.cpp:797
wxString GetFileName() const override
Helper to retrieve the filename from the root sheet screen.
Definition: schematic.cpp:281
SCH_SHEET_LIST GetSheets() const override
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:100
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:790
bool ResolveTextVar(const SCH_SHEET_PATH *aSheetPath, wxString *token, int aDepth) const
Definition: schematic.cpp:228
void SyncView()
Mark all items for refresh.
EESCHEMA_SETTINGS * eeconfig() const
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
Definition: sch_commit.cpp:393
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:52
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:165
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 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...
void GetSheetsWithinPath(SCH_SHEET_PATHS &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...
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
SCH_SCREEN * LastScreen()
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:57
const std::vector< SCH_SHEET_INSTANCE > & GetInstances() const
Definition: sch_sheet.h:392
Schematic symbol object.
Definition: sch_symbol.h:109
int GetFieldCount() const
Return the number of fields in this symbol.
Definition: sch_symbol.h:588
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
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:57
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:156
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:462
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:334
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:362
bool IsTextVar(const wxString &aSource)
Returns true if the string is a text var, e.g starts with ${.
Definition: common.cpp:127
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:58
wxString GetTextVars(const wxString &aSource)
Returns any variables unexpanded, e.g.
Definition: common.cpp:115
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:253
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:360
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:280
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition: confirm.cpp:332
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:240
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)
Open a document (file) with the suitable browser.
Definition: eda_doc.cpp:60
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: gtk/ui.cpp:195
wxFont GetInfoFont(wxWindow *aWindow)
Definition: ui_common.cpp:151
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:74
@ HIGHLIGHT_SYMBOL
std::vector< FAB_LAYER_COLOR > dummy
wxString fieldDelimiter
Definition: bom_settings.h:81
wxString name
Definition: bom_settings.h:79
static BOM_FMT_PRESET CSV()
static std::vector< BOM_FMT_PRESET > BuiltInPresets()
wxString stringDelimiter
Definition: bom_settings.h:82
wxString refRangeDelimiter
Definition: bom_settings.h:84
wxString refDelimiter
Definition: bom_settings.h:83
wxString name
Definition: bom_settings.h:51
std::vector< BOM_FIELD > fieldsOrdered
Definition: bom_settings.h:53
static BOM_PRESET GroupedByValue()
bool readOnly
Definition: bom_settings.h:52
static std::vector< BOM_PRESET > BuiltInPresets()
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.
static const wxString GetDefaultFieldName(int aFieldNdx, bool aTranslateForHI=false)
Return a default symbol field name for field aFieldNdx for all components.
Definition for symbol library class.
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_FIELDS
The first 5 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
@ SCH_SYMBOL_T
Definition: typeinfo.h:160
@ SCH_SHEET_T
Definition: typeinfo.h:162
constexpr T Clamp(const T &lower, const T &value, const T &upper)
Limit value within the range lower <= value <= upper.
Definition: util.h:64
Definition of file extensions used in Kicad.