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
175 m_bRefresh->SetBitmap( KiBitmap( BITMAPS::small_refresh ) );
176 m_bRefreshPreview->SetBitmap( KiBitmap( BITMAPS::small_refresh ) );
177 m_browseButton->SetBitmap( KiBitmap( BITMAPS::small_folder ) );
178
179 m_addFieldButton->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
180 m_removeFieldButton->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
181 m_renameFieldButton->SetBitmap( KiBitmap( BITMAPS::small_edit ) );
182
183 m_removeFieldButton->Enable( false );
184 m_renameFieldButton->Enable( false );
185
186 m_bomPresetsLabel->SetFont( KIUI::GetInfoFont( this ) );
187 m_labelBomExportPresets->SetFont( KIUI::GetInfoFont( this ) );
189
190 m_fieldsCtrl->AppendTextColumn( _( "Field" ), wxDATAVIEW_CELL_INERT, 0, wxALIGN_LEFT, 0 );
191 m_fieldsCtrl->AppendTextColumn( _( "Label" ), wxDATAVIEW_CELL_EDITABLE, 0, wxALIGN_LEFT, 0 );
192 m_fieldsCtrl->AppendToggleColumn( _( "Show" ), wxDATAVIEW_CELL_ACTIVATABLE, 0,
193 wxALIGN_CENTER, 0 );
194 m_fieldsCtrl->AppendToggleColumn( _( "Group By" ), wxDATAVIEW_CELL_ACTIVATABLE, 0,
195 wxALIGN_CENTER, 0 );
196
197 // GTK asserts if the number of columns doesn't match the data, but we still don't want
198 // to display the canonical names. So we'll insert a column for them, but keep it 0 width.
199 m_fieldsCtrl->AppendTextColumn( _( "Name" ), wxDATAVIEW_CELL_INERT, 0, wxALIGN_LEFT, 0 );
200
201 // SetWidth( wxCOL_WIDTH_AUTOSIZE ) fails here on GTK, so we calculate the title sizes and
202 // set the column widths ourselves.
203 wxDataViewColumn* column = m_fieldsCtrl->GetColumn( SHOW_FIELD_COLUMN );
204 m_showColWidth = KIUI::GetTextSize( column->GetTitle(), m_fieldsCtrl ).x + COLUMN_MARGIN;
205 column->SetMinWidth( m_showColWidth );
206
207 column = m_fieldsCtrl->GetColumn( GROUP_BY_COLUMN );
209 column->SetMinWidth( m_groupByColWidth );
210
211 // The fact that we're a list should keep the control from reserving space for the
212 // expander buttons... but it doesn't. Fix by forcing the indent to 0.
213 m_fieldsCtrl->SetIndent( 0 );
214
215 m_filter->SetDescriptiveText( _( "Filter" ) );
217
218 // We want to show excluded symbols because the Edit page needs to show all symbols,
219 // they will still be excluded from the export.
221
222 LoadFieldNames(); // loads rows into m_fieldsCtrl and columns into m_dataModel
223
224 // Now that the fields are loaded we can set the initial location of the splitter
225 // based on the list width. Again, SetWidth( wxCOL_WIDTH_AUTOSIZE ) fails us on GTK.
227 m_labelColWidth = 0;
228
229 int colWidth = 0;
230
231 for( int row = 0; row < m_fieldsCtrl->GetItemCount(); ++row )
232 {
233 const wxString& displayName = m_fieldsCtrl->GetTextValue( row, DISPLAY_NAME_COLUMN );
234 colWidth = std::max( colWidth, KIUI::GetTextSize( displayName, m_fieldsCtrl ).x );
235
236 const wxString& label = m_fieldsCtrl->GetTextValue( row, LABEL_COLUMN );
237 colWidth = std::max( colWidth, KIUI::GetTextSize( label, m_fieldsCtrl ).x );
238 }
239
240 m_fieldNameColWidth = colWidth + 20;
241 m_labelColWidth = colWidth + 20;
242
244
245 m_fieldsCtrl->GetColumn( DISPLAY_NAME_COLUMN )->SetWidth( m_fieldNameColWidth );
246 m_fieldsCtrl->GetColumn( LABEL_COLUMN )->SetWidth( m_labelColWidth );
247
248 // This is used for data only. Don't show it to the user.
249 m_fieldsCtrl->GetColumn( FIELD_NAME_COLUMN )->SetHidden( true );
250
251 m_splitterMainWindow->SetMinimumPaneSize( fieldsMinWidth );
252 m_splitterMainWindow->SetSashPosition( fieldsMinWidth + 40 );
253
254 m_grid->UseNativeColHeader( true );
255 m_grid->SetTable( m_dataModel, true );
256
257 // must be done after SetTable(), which appears to re-set it
258 m_grid->SetSelectionMode( wxGrid::wxGridSelectCells );
259
260 // add Cut, Copy, and Paste to wxGrid
261 m_grid->PushEventHandler( new FIELDS_EDITOR_GRID_TRICKS( this, m_grid, m_fieldsCtrl,
262 m_dataModel ) );
263
264 // give a bit more room for comboboxes
265 m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
266
267 // Load our BOM view presets
271
272 // Load BOM export format presets
276
278 m_grid->ClearSelection();
279
281
283
286
287 wxSize dlgSize( panelCfg.width > 0 ? panelCfg.width : horizPixelsFromDU( 600 ),
288 panelCfg.height > 0 ? panelCfg.height : vertPixelsFromDU( 300 ) );
289 SetSize( dlgSize );
290
291 m_nbPages->SetSelection( cfg->m_FieldEditorPanel.page );
292
294 {
295 case 0: m_radioHighlight->SetValue( true ); break;
296 case 1: m_radioSelect->SetValue( true ); break;
297 case 2: m_radioOff->SetValue( true ); break;
298 }
299
300 switch( cfg->m_FieldEditorPanel.scope )
301 {
302 case SCOPE::SCOPE_ALL: m_radioProject->SetValue( true ); break;
303 case SCOPE::SCOPE_SHEET: m_radioCurrentSheet->SetValue( true ); break;
304 case SCOPE::SCOPE_SHEET_RECURSIVE: m_radioRecursive->SetValue( true ); break;
305 }
306
308
309 Center();
310
311 // Connect Events
312 m_grid->Connect( wxEVT_GRID_COL_SORT,
313 wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColSort ), nullptr, this );
314 m_grid->Connect( wxEVT_GRID_COL_MOVE,
315 wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColMove ), nullptr, this );
318 m_fieldsCtrl->Bind( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED,
320
321 // Start listening for schematic changes
322 m_parent->Schematic().AddListener( this );
323}
324
325
327{
328 wxGridCellAttr* attr = new wxGridCellAttr;
329 attr->SetReadOnly( false );
330
331 // Set some column types to specific editors
332 if( m_dataModel->ColIsReference( aCol ) )
333 {
334 attr->SetReadOnly();
335 m_grid->SetColAttr( aCol, attr );
336 }
337 else if( m_dataModel->GetColFieldName( aCol )
339 {
340 attr->SetEditor( new GRID_CELL_FPID_EDITOR( this, wxEmptyString ) );
341 m_grid->SetColAttr( aCol, attr );
342 }
343 else if( m_dataModel->GetColFieldName( aCol )
345 {
346 // set datasheet column viewer button
347 attr->SetEditor(
349 m_grid->SetColAttr( aCol, attr );
350 }
351 else if( m_dataModel->ColIsQuantity( aCol ) || m_dataModel->ColIsItemNumber( aCol ) )
352 {
353 attr->SetReadOnly();
354 m_grid->SetColAttr( aCol, attr );
355 m_grid->SetColFormatNumber( aCol );
356 }
357 else if( m_dataModel->ColIsAttribute( aCol ) )
358 {
359 attr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
360 m_grid->SetColAttr( aCol, attr );
361 m_grid->SetColFormatBool( aCol );
362 }
363 else if( IsTextVar( m_dataModel->GetColFieldName( aCol ) ) )
364 {
365 attr->SetReadOnly();
366 m_grid->SetColAttr( aCol, attr );
367 }
368 else
369 {
370 attr->SetEditor( m_grid->GetDefaultEditor() );
371 m_grid->SetColAttr( aCol, attr );
372 m_grid->SetColFormatCustom( aCol, wxGRID_VALUE_STRING );
373 }
374}
375
376
378{
380 wxSize defaultDlgSize = ConvertDialogToPixels( wxSize( 600, 300 ) );
381
382 // Restore column sorting order and widths
383 m_grid->AutoSizeColumns( false );
384 int sortCol = 0;
385 bool sortAscending = true;
386
387 for( int col = 0; col < m_grid->GetNumberCols(); ++col )
388 {
390
391 if( col == m_dataModel->GetSortCol() )
392 {
393 sortCol = col;
394 sortAscending = m_dataModel->GetSortAsc();
395 }
396 }
397
398 // sync m_grid's column visibilities to Show checkboxes in m_fieldsCtrl
399 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); ++i )
400 {
401 int col = m_dataModel->GetFieldNameCol( m_fieldsCtrl->GetTextValue( i, FIELD_NAME_COLUMN ) );
402
403 if( col == -1 )
404 continue;
405
406 bool show = m_fieldsCtrl->GetToggleValue( i, SHOW_FIELD_COLUMN );
407 m_dataModel->SetShowColumn( col, show );
408
409 if( show )
410 {
411 m_grid->ShowCol( col );
412
413 std::string key( m_dataModel->GetColFieldName( col ).ToUTF8() );
414
415 if( cfg->m_FieldEditorPanel.field_widths.count( key )
416 && ( cfg->m_FieldEditorPanel.field_widths.at( key ) > 0 ) )
417 {
418 m_grid->SetColSize( col, cfg->m_FieldEditorPanel.field_widths.at( key ) );
419 }
420 else
421 {
422 int textWidth = m_dataModel->GetDataWidth( col ) + COLUMN_MARGIN;
423 int maxWidth = defaultDlgSize.x / 3;
424
425 m_grid->SetColSize( col, Clamp( 100, textWidth, maxWidth ) );
426 }
427 }
428 else
429 {
430 m_grid->HideCol( col );
431 }
432 }
433
434 m_dataModel->SetSorting( sortCol, sortAscending );
435 m_grid->SetSortingColumn( sortCol, sortAscending );
436}
437
438
440{
441 // Disconnect Events
442 m_grid->Disconnect( wxEVT_GRID_COL_SORT,
443 wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColSort ), nullptr,
444 this );
445 m_grid->Disconnect( wxEVT_GRID_COL_SORT,
446 wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColMove ), nullptr,
447 this );
448
449 // Delete the GRID_TRICKS.
450 m_grid->PopEventHandler( true );
451
452 // we gave ownership of m_dataModel to the wxGrid...
453}
454
455
457{
458 if( !wxDialog::TransferDataFromWindow() )
459 return false;
460
462 EE_SELECTION_TOOL* selectionTool = toolMgr->GetTool<EE_SELECTION_TOOL>();
463 EE_SELECTION& selection = selectionTool->GetSelection();
464 SCH_SYMBOL* symbol = nullptr;
465
466 UpdateScope();
467
468 if( selection.GetSize() == 1 )
469 {
470 EDA_ITEM* item = selection.Front();
471
472 if( item->Type() == SCH_SYMBOL_T )
473 symbol = (SCH_SYMBOL*) item;
474 else if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T )
475 symbol = (SCH_SYMBOL*) item->GetParent();
476 }
477
478 if( symbol )
479 {
480 for( int row = 0; row < m_dataModel->GetNumberRows(); ++row )
481 {
482 std::vector<SCH_REFERENCE> references = m_dataModel->GetRowReferences( row );
483 bool found = false;
484
485 for( const SCH_REFERENCE& ref : references )
486 {
487 if( ref.GetSymbol() == symbol )
488 {
489 found = true;
490 break;
491 }
492 }
493
494 if( found )
495 {
496 // Find the value column and the reference column if they're shown
497 int valueCol = -1;
498 int refCol = -1;
499 int anyCol = -1;
500
501 for( int col = 0; col < m_dataModel->GetNumberCols(); col++ )
502 {
503 if( m_dataModel->ColIsValue( col ) )
504 valueCol = col;
505 else if( m_dataModel->ColIsReference( col ) )
506 refCol = col;
507 else if( anyCol == -1 && m_dataModel->GetShowColumn( col ) )
508 anyCol = col;
509 }
510
511 if( valueCol != -1 && m_dataModel->GetShowColumn( valueCol ) )
512 m_grid->GoToCell( row, valueCol );
513 else if( refCol != -1 && m_dataModel->GetShowColumn( refCol ) )
514 m_grid->GoToCell( row, refCol );
515 else if( anyCol != -1 )
516 m_grid->GoToCell( row, anyCol );
517
518 break;
519 }
520 }
521 }
522
523 // We don't want table range selection events to happen until we've loaded the data or we
524 // we'll clear our selection as the grid is built before the code above can get the
525 // user's current selection.
527
528 return true;
529}
530
531
533{
535 return false;
536
537 if( !wxDialog::TransferDataFromWindow() )
538 return false;
539
540 SCH_COMMIT commit( m_parent );
541 SCH_SHEET_PATH currentSheet = m_parent->GetCurrentSheet();
542
543 std::function<void( SCH_SYMBOL&, SCH_SHEET_PATH & aPath )> changeHandler =
544 [&commit]( SCH_SYMBOL& aSymbol, SCH_SHEET_PATH& aPath ) -> void
545 {
546 commit.Modify( &aSymbol, aPath.LastScreen() );
547 };
548
549 m_dataModel->ApplyData( changeHandler );
550
551 commit.Push( wxS( "Symbol Fields Table Edit" ) );
552
553 // Reset the view to where we left the user
554 m_parent->SetCurrentSheet( currentSheet );
556 m_parent->Refresh();
557
559
560 return true;
561}
562
563
564void DIALOG_SYMBOL_FIELDS_TABLE::AddField( const wxString& aFieldName, const wxString& aLabelValue,
565 bool show, bool groupBy, bool addedByUser )
566{
567 // Users can add fields with variable names that match the special names in the grid,
568 // e.g. ${QUANTITY} so make sure we don't add them twice
569 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); i++ )
570 if( m_fieldsCtrl->GetTextValue( i, FIELD_NAME_COLUMN ) == aFieldName )
571 return;
572
573 m_dataModel->AddColumn( aFieldName, aLabelValue, addedByUser );
574
575 wxVector<wxVariant> fieldsCtrlRow;
576
577 std::string key( aFieldName.ToUTF8() );
578
579 // Don't change these to emplace_back: some versions of wxWidgets don't support it
580 fieldsCtrlRow.push_back( wxVariant( aFieldName ) );
581 fieldsCtrlRow.push_back( wxVariant( aLabelValue ) );
582 fieldsCtrlRow.push_back( wxVariant( show ) );
583 fieldsCtrlRow.push_back( wxVariant( groupBy ) );
584 fieldsCtrlRow.push_back( wxVariant( aFieldName ) );
585
586 m_fieldsCtrl->AppendItem( fieldsCtrlRow );
587
588 wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_APPENDED, 1 );
589 m_grid->ProcessTableMessage( msg );
590}
591
592
594{
595 // Add mandatory fields first
596 for( int i = 0; i < MANDATORY_FIELDS; ++i )
597 {
598 bool show = false;
599 bool groupBy = false;
600
601 switch( i )
602 {
603 case REFERENCE_FIELD:
604 case VALUE_FIELD:
605 case FOOTPRINT_FIELD:
606 show = true;
607 groupBy = true;
608 break;
609 case DATASHEET_FIELD:
610 show = true;
611 groupBy = false;
612 break;
613 }
614
616 TEMPLATE_FIELDNAME::GetDefaultFieldName( i, true ), show, groupBy );
617 }
618
619 // Generated fields present only in the fields table
622
623 // User fields next
624 std::set<wxString> userFieldNames;
625
626 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
627 {
628 SCH_SYMBOL* symbol = m_symbolsList[ i ].GetSymbol();
629
630 for( int j = MANDATORY_FIELDS; j < symbol->GetFieldCount(); ++j )
631 userFieldNames.insert( symbol->GetFields()[j].GetName() );
632 }
633
634 for( const wxString& fieldName : userFieldNames )
635 AddField( fieldName, GetTextVars( fieldName ), true, false );
636
637 // Add any templateFieldNames which aren't already present in the userFieldNames
638 for( const TEMPLATE_FIELDNAME& templateFieldname :
640 {
641 if( userFieldNames.count( templateFieldname.m_Name ) == 0 )
642 AddField( templateFieldname.m_Name, GetTextVars( templateFieldname.m_Name ), false,
643 false );
644 }
645}
646
647
648void DIALOG_SYMBOL_FIELDS_TABLE::OnAddField( wxCommandEvent& event )
649{
650 wxTextEntryDialog dlg( this, _( "New field name:" ), _( "Add Field" ) );
651
652 if( dlg.ShowModal() != wxID_OK )
653 return;
654
655 wxString fieldName = dlg.GetValue();
656
657 if( fieldName.IsEmpty() )
658 {
659 DisplayError( this, _( "Field must have a name." ) );
660 return;
661 }
662
663 for( int i = 0; i < m_dataModel->GetNumberCols(); ++i )
664 {
665 if( fieldName == m_dataModel->GetColFieldName( i ) )
666 {
667 DisplayError( this, wxString::Format( _( "Field name '%s' already in use." ),
668 fieldName ) );
669 return;
670 }
671 }
672
673 AddField( fieldName, GetTextVars( fieldName ), true, false, true );
674
675 SetupColumnProperties( m_dataModel->GetColsCount() - 1 );
676
678}
679
680
681void DIALOG_SYMBOL_FIELDS_TABLE::OnRemoveField( wxCommandEvent& event )
682{
683 int col = -1;
684 int row = m_fieldsCtrl->GetSelectedRow();
685
686 // Should never occur: "Remove Field..." button should be disabled if invalid selection
687 // via OnFieldsCtrlSelectionChanged()
688 wxCHECK_RET( row != -1, wxS( "Some user defined field must be selected first" ) );
689 wxCHECK_RET( row >= MANDATORY_FIELDS, wxS( "Mandatory fields cannot be removed" ) );
690
691 wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
692 wxString displayName = m_fieldsCtrl->GetTextValue( row, DISPLAY_NAME_COLUMN );
693
694 wxString confirm_msg =
695 wxString::Format( _( "Are you sure you want to remove the field '%s'?" ), displayName );
696
697 if( !IsOK( this, confirm_msg ) )
698 return;
699
700 for( int i = 0; i < m_dataModel->GetNumberCols(); ++i )
701 {
702 if( fieldName == m_dataModel->GetColFieldName( i ) )
703 col = i;
704 }
705
706 m_fieldsCtrl->DeleteItem( row );
708
709 // Make selection and update the state of "Remove field..." button via OnFieldsCtrlSelectionChanged()
710 // Safe to decrement row index because we always have mandatory fields
711 m_fieldsCtrl->SelectRow( --row );
712
713 if( row < MANDATORY_FIELDS )
714 {
715 m_removeFieldButton->Enable( false );
716 m_renameFieldButton->Enable( false );
717 }
718
719 wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_DELETED, col, 1 );
720
721 m_grid->ProcessTableMessage( msg );
722
724}
725
726
727void DIALOG_SYMBOL_FIELDS_TABLE::OnRenameField( wxCommandEvent& event )
728{
729 int row = m_fieldsCtrl->GetSelectedRow();
730 wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
731
732 // Should never occur: "Rename Field..." button should be disabled if invalid selection
733 // via OnFieldsCtrlSelectionChanged()
734 wxCHECK_RET( row != -1, wxS( "Some user defined field must be selected first" ) );
735 wxCHECK_RET( row >= MANDATORY_FIELDS, wxS( "Mandatory fields cannot be renamed" ) );
736 wxCHECK_RET( !fieldName.IsEmpty(), wxS( "Field must have a name" ) );
737
738 int col = m_dataModel->GetFieldNameCol( fieldName );
739 wxCHECK_RET( col != -1, wxS( "Existing field name missing from data model" ) );
740
741 wxTextEntryDialog dlg( this, _( "New field name:" ), _( "Rename Field" ) );
742
743 if( dlg.ShowModal() != wxID_OK )
744 return;
745
746 wxString newFieldName = dlg.GetValue();
747
748 // No change, no-op
749 if( newFieldName == fieldName )
750 return;
751
752 // New field name already exists
753 if( m_dataModel->GetFieldNameCol( newFieldName ) != -1 )
754 {
755 wxString confirm_msg = wxString::Format(
756 _( "Field name %s already exists. Cannot rename over existing field." ),
757 newFieldName );
758 DisplayError( this, confirm_msg );
759 return;
760 }
761
762 m_dataModel->RenameColumn( col, newFieldName );
763 m_fieldsCtrl->SetTextValue( newFieldName, col, DISPLAY_NAME_COLUMN );
764 m_fieldsCtrl->SetTextValue( newFieldName, col, FIELD_NAME_COLUMN );
765
767}
768
769
770void DIALOG_SYMBOL_FIELDS_TABLE::OnFilterText( wxCommandEvent& aEvent )
771{
772 m_dataModel->SetFilter( m_filter->GetValue() );
774 m_grid->ForceRefresh();
775
777}
778
779
781{
782 wxPoint pos = aEvent.GetPosition();
783 wxRect ctrlRect = m_filter->GetScreenRect();
784 int buttonWidth = ctrlRect.GetHeight(); // Presume buttons are square
785
786 if( m_filter->IsSearchButtonVisible() && pos.x < buttonWidth )
787 SetCursor( wxCURSOR_ARROW );
788 else if( m_filter->IsCancelButtonVisible() && pos.x > ctrlRect.GetWidth() - buttonWidth )
789 SetCursor( wxCURSOR_ARROW );
790 else
791 SetCursor( wxCURSOR_IBEAM );
792}
793
794
796{
797 int row = m_fieldsCtrl->GetSelectedRow();
798
799 if( row >= MANDATORY_FIELDS )
800 {
803 }
804 else
805 {
806 m_removeFieldButton->Enable( false );
807 m_renameFieldButton->Enable( false );
808 }
809}
810
812{
813 wxDataViewItem item = event.GetItem();
814 int row = m_fieldsCtrl->ItemToRow( item );
815 int col = event.GetColumn();
816
817 switch ( col )
818 {
820 {
821 bool value = m_fieldsCtrl->GetToggleValue( row, col );
822 int dataCol = m_dataModel->GetFieldNameCol(
823 m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN ) );
824
825 m_dataModel->SetShowColumn( dataCol, value );
826
827 if( dataCol != -1 )
828 {
829 if( value )
830 m_grid->ShowCol( dataCol );
831 else
832 m_grid->HideCol( dataCol );
833 }
834
835 break;
836 }
837
838 case GROUP_BY_COLUMN:
839 {
840 bool value = m_fieldsCtrl->GetToggleValue( row, col );
841 int dataCol = m_dataModel->GetFieldNameCol(
842 m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN ) );
843
844 if( m_dataModel->ColIsQuantity( dataCol ) && value )
845 {
846 DisplayError( this, _( "The Quantity column cannot be grouped by." ) );
847
848 value = false;
849 m_fieldsCtrl->SetToggleValue( value, row, col );
850 }
851
852 if( m_dataModel->ColIsItemNumber( dataCol ) && value )
853 {
854 DisplayError( this, _( "The Item Number column cannot be grouped by." ) );
855
856 value = false;
857 m_fieldsCtrl->SetToggleValue( value, row, col );
858 }
859
860 wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
861
862 m_dataModel->SetGroupColumn( m_dataModel->GetFieldNameCol( fieldName ), value );
864 m_grid->ForceRefresh();
865 break;
866 }
867
868 default:
869 break;
870 }
871
873}
874
875
877{
880 m_grid->ForceRefresh();
881
883}
884
885
887{
890 m_grid->ForceRefresh();
891
893}
894
895
896void DIALOG_SYMBOL_FIELDS_TABLE::OnColSort( wxGridEvent& aEvent )
897{
898 int sortCol = aEvent.GetCol();
899 std::string key( m_dataModel->GetColFieldName( sortCol ).ToUTF8() );
900 bool ascending;
901
902 // Don't sort by item number, it is generated by the sort
903 if( m_dataModel->ColIsItemNumber( sortCol ) )
904 {
905 aEvent.Veto();
906 return;
907 }
908
909 // This is bonkers, but wxWidgets doesn't tell us ascending/descending in the event, and
910 // if we ask it will give us pre-event info.
911 if( m_grid->IsSortingBy( sortCol ) )
912 {
913 // same column; invert ascending
914 ascending = !m_grid->IsSortOrderAscending();
915 }
916 else
917 {
918 // different column; start with ascending
919 ascending = true;
920 }
921
922 m_dataModel->SetSorting( sortCol, ascending );
924 m_grid->ForceRefresh();
925
927}
928
929
930void DIALOG_SYMBOL_FIELDS_TABLE::OnColMove( wxGridEvent& aEvent )
931{
932 int origPos = aEvent.GetCol();
933
934 CallAfter(
935 [origPos, this]()
936 {
937 int newPos = m_grid->GetColPos( origPos );
938
939 m_dataModel->MoveColumn( origPos, newPos );
940
941 // "Unmove" the column since we've moved the column internally
942 m_grid->ResetColPos();
943
944 // We need to reset all the column attr's to the correct column order
946
947 m_grid->ForceRefresh();
948 } );
949
951}
952
953
954void DIALOG_SYMBOL_FIELDS_TABLE::OnColLabelChange( wxDataViewEvent& aEvent )
955{
956 wxDataViewItem item = aEvent.GetItem();
957 int row = m_fieldsCtrl->ItemToRow( item );
958 wxString label = m_fieldsCtrl->GetTextValue( row, LABEL_COLUMN );
959 wxString fieldName = m_fieldsCtrl->GetTextValue( row, FIELD_NAME_COLUMN );
960 int col = m_dataModel->GetFieldNameCol( fieldName );
961
962 if( col != -1 )
963 m_dataModel->SetColLabelValue( col, label );
964
966
967 aEvent.Skip();
968
969 m_grid->ForceRefresh();
970}
971
973{
974 m_grid->ForceRefresh();
975}
976
977
978void DIALOG_SYMBOL_FIELDS_TABLE::OnTableColSize( wxGridSizeEvent& aEvent )
979{
980 int col = aEvent.GetRowOrCol();
981 std::string key( m_dataModel->GetColFieldName( col ).ToUTF8() );
982
983 aEvent.Skip();
984
985 m_grid->ForceRefresh();
986}
987
988
990{
992 m_grid->ForceRefresh();
993}
994
995void DIALOG_SYMBOL_FIELDS_TABLE::OnScopeChanged( wxCommandEvent& aEvent )
996{
997 UpdateScope();
998}
999
1001{
1003
1004 if( m_radioProject->GetValue() )
1005 m_dataModel->SetScope( FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_ALL );
1006 else if( m_radioCurrentSheet->GetValue() )
1007 m_dataModel->SetScope( FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_SHEET );
1008 else if( m_radioRecursive->GetValue() )
1009 m_dataModel->SetScope( FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_SHEET_RECURSIVE );
1010
1012}
1013
1015{
1016 if( m_dataModel->ColIsReference( event.GetCol() ) )
1017 {
1018 m_grid->ClearSelection();
1019
1020 m_dataModel->ExpandCollapseRow( event.GetRow() );
1021 m_grid->SetGridCursor( event.GetRow(), event.GetCol() );
1022 }
1023 else
1024 {
1025 event.Skip();
1026 }
1027}
1028
1029void DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected( wxGridRangeSelectEvent& aEvent )
1030{
1031 // Multi-select can grab the rows that are expanded child refs, and also the row
1032 // containing the list of all child refs. Make sure we add refs/symbols uniquely
1033 std::set<SCH_REFERENCE> refs;
1034 std::set<SCH_ITEM*> symbols;
1035
1036 // This handler handles selecting and deselecting
1037 if( aEvent.Selecting() )
1038 {
1039 for( int i = aEvent.GetTopRow(); i <= aEvent.GetBottomRow(); i++ )
1040 {
1041 for( const SCH_REFERENCE& ref : m_dataModel->GetRowReferences( i ) )
1042 refs.insert( ref );
1043 }
1044
1045 for( const SCH_REFERENCE& ref : refs )
1046 symbols.insert( ref.GetSymbol() );
1047 }
1048
1049 if( m_radioHighlight->GetValue() )
1050 {
1052
1053 if( refs.size() > 0 )
1054 {
1055 // Use of full path based on UUID allows select of not yet annotated or duplicaded symbols
1056 wxString symbol_path = refs.begin()->GetFullPath();
1057
1058 // Focus only handles on item at this time
1059 editor->FindSymbolAndItem( &symbol_path, nullptr, true, HIGHLIGHT_SYMBOL,
1060 wxEmptyString );
1061 }
1062 else
1063 {
1064 m_parent->FocusOnItem( nullptr );
1065 }
1066 }
1067 else if( m_radioSelect->GetValue() )
1068 {
1070
1071 std::vector<SCH_ITEM*> items( symbols.begin(), symbols.end() );
1072
1073 if( refs.size() > 0 )
1074 selTool->SyncSelection( refs.begin()->GetSheetPath(), nullptr, items );
1075 else
1076 selTool->ClearSelection();
1077 }
1078}
1079
1080
1082{
1083 // TODO: Option to select footprint if FOOTPRINT column selected
1084
1085 event.Skip();
1086}
1087
1088
1090{
1094#ifdef __WXMAC__
1095 // TODO: something in wxWidgets 3.1.x pads checkbox columns with extra space. (It used to
1096 // also be that the width of the column would get set too wide (to 30), but that's patched in
1097 // our local wxWidgets fork.)
1098 width -= 50;
1099#endif
1100
1101 m_fieldNameColWidth = width / 2;
1103
1104 // GTK loses its head and messes these up when resizing the splitter bar:
1105 m_fieldsCtrl->GetColumn( SHOW_FIELD_COLUMN )->SetWidth( m_showColWidth );
1106 m_fieldsCtrl->GetColumn( GROUP_BY_COLUMN )->SetWidth( m_groupByColWidth );
1107
1108 m_fieldsCtrl->GetColumn( FIELD_NAME_COLUMN )->SetHidden( true );
1109 m_fieldsCtrl->GetColumn( DISPLAY_NAME_COLUMN )->SetWidth( m_fieldNameColWidth );
1110 m_fieldsCtrl->GetColumn( LABEL_COLUMN )->SetWidth( m_labelColWidth );
1111
1112 m_fieldsCtrl->Refresh(); // To refresh checkboxes on Windows.
1113
1114 event.Skip();
1115}
1116
1117
1119{
1122}
1123
1124
1125void DIALOG_SYMBOL_FIELDS_TABLE::OnPageChanged( wxNotebookEvent& event )
1126{
1128}
1129
1130
1132{
1135}
1136
1137
1139{
1142
1144
1147}
1148
1149
1151{
1152 BOM_FMT_PRESET current;
1153
1154 current.name = m_cbBomFmtPresets->GetStringSelection();
1155 current.fieldDelimiter = m_textFieldDelimiter->GetValue();
1156 current.stringDelimiter = m_textStringDelimiter->GetValue();
1157 current.refDelimiter = m_textRefDelimiter->GetValue();
1158 current.refRangeDelimiter = m_textRefRangeDelimiter->GetValue();
1159 current.keepTabs = m_checkKeepTabs->GetValue();
1160 current.keepLineBreaks = m_checkKeepLineBreaks->GetValue();
1161
1162 return current;
1163}
1164
1165
1167{
1168 m_nbPages->SetSelection( 0 );
1169}
1170
1171
1173{
1174 m_nbPages->SetSelection( 1 );
1175}
1176
1177
1179{
1180 // Build the absolute path of current output directory to preselect it in the file browser.
1181 wxString path = ExpandEnvVarSubstitutions( m_outputFileName->GetValue(), &Prj() );
1182 path = Prj().AbsolutePath( path );
1183
1184
1185 // Calculate the export filename
1186 wxFileName fn( Prj().AbsolutePath( m_parent->Schematic().GetFileName() ) );
1187 fn.SetExt( CsvFileExtension );
1188
1189 wxFileDialog saveDlg( this, _( "Selected Output Filename" ), path, fn.GetFullName(),
1190 CsvFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1191
1192 if( saveDlg.ShowModal() == wxID_CANCEL )
1193 return;
1194
1195
1196 wxFileName file = wxFileName( saveDlg.GetPath() );
1197 wxString defaultPath = fn.GetPathWithSep();
1198 wxString msg;
1199 msg.Printf( _( "Do you want to use a path relative to\n'%s'?" ), defaultPath );
1200
1201 wxMessageDialog dialog( this, msg, _( "BOM Output File" ),
1202 wxYES_NO | wxICON_QUESTION | wxYES_DEFAULT );
1203
1204 if( dialog.ShowModal() == wxID_YES )
1205 {
1206 if( !file.MakeRelativeTo( defaultPath ) )
1207 {
1208 wxMessageBox( _( "Cannot make path relative (target volume different from schematic "
1209 "file volume)!" ),
1210 _( "BOM Output File" ), wxOK | wxICON_ERROR );
1211 }
1212 }
1213
1214 m_outputFileName->SetValue( file.GetFullPath() );
1215}
1216
1217
1218void DIALOG_SYMBOL_FIELDS_TABLE::OnExport( wxCommandEvent& aEvent )
1219{
1220 if( m_dataModel->IsEdited() )
1221 if( OKOrCancelDialog( nullptr, _( "Unsaved data" ),
1222 _( "Changes are unsaved. Export unsaved data?" ), "", _( "OK" ),
1223 _( "Cancel" ) )
1224 == wxID_CANCEL )
1225 return;
1226
1227 // Create output directory if it does not exist (also transform it in absolute form).
1228 // Bail if it fails.
1229
1230 std::function<bool( wxString* )> textResolver = [&]( wxString* token ) -> bool
1231 {
1232 // Handles m_board->GetTitleBlock() *and* m_board->GetProject()
1233 return m_parent->Schematic().ResolveTextVar( token, 0 );
1234 };
1235
1236 wxString path = m_outputFileName->GetValue();
1237 path = ExpandTextVars( path, &textResolver );
1238 path = ExpandEnvVarSubstitutions( path, nullptr );
1239
1240 wxFileName outputFile = wxFileName::FileName( path );
1241
1242 auto displayErr = [&]()
1243 {
1244 wxString msg;
1245 msg.Printf( _( "Could not write BOM output to '%s'." ), outputFile.GetPath() );
1246 DisplayError( this, msg );
1247 };
1248
1249 if( !EnsureFileDirectoryExists( &outputFile,
1250 Prj().AbsolutePath( m_parent->Schematic().GetFileName() ),
1252 {
1253 displayErr();
1254 return;
1255 }
1256
1257 wxFFile out( outputFile.GetFullPath(), "wb" );
1258
1259 if( !out.IsOpened() )
1260 {
1261 displayErr();
1262 return;
1263 }
1264
1266
1267 if( !out.Write( m_textOutput->GetValue() ) )
1268 {
1269 displayErr();
1270 return;
1271 }
1272
1273 wxString msg;
1274 msg.Printf( _( "Wrote BOM output to '%s'" ), outputFile.GetFullPath() );
1275 DisplayInfoMessage( this, msg );
1276}
1277
1278
1279void DIALOG_SYMBOL_FIELDS_TABLE::OnCancel( wxCommandEvent& aEvent )
1280{
1281 Close();
1282}
1283
1284
1285void DIALOG_SYMBOL_FIELDS_TABLE::OnOk( wxCommandEvent& aEvent )
1286{
1288 Close();
1289}
1290
1291
1292void DIALOG_SYMBOL_FIELDS_TABLE::OnClose( wxCloseEvent& aEvent )
1293{
1294 // This is a cancel, so commit quietly as we're going to throw the results away anyway.
1296
1297 if( m_dataModel->IsEdited() )
1298 {
1299 if( !HandleUnsavedChanges( this, _( "Save changes?" ),
1300 [&]() -> bool
1301 {
1302 return TransferDataFromWindow();
1303 } ) )
1304 {
1305 aEvent.Veto();
1306 return;
1307 }
1308 }
1309
1310 // Stop listening to schematic events
1312
1313 // Save all our settings since we're really closing
1316
1317 cfg->m_FieldEditorPanel.width = GetSize().x;
1318 cfg->m_FieldEditorPanel.height = GetSize().y;
1319 cfg->m_FieldEditorPanel.page = m_nbPages->GetSelection();
1321
1322 if( m_radioHighlight->GetValue() )
1324 else if( m_radioHighlight->GetValue() )
1326 else if( m_radioOff->GetValue() )
1328
1329 if( m_radioProject->GetValue() )
1330 cfg->m_FieldEditorPanel.scope = SCOPE::SCOPE_ALL;
1331 else if( m_radioCurrentSheet->GetValue() )
1332 cfg->m_FieldEditorPanel.scope = SCOPE::SCOPE_SHEET;
1333 else if( m_radioRecursive->GetValue() )
1334 cfg->m_FieldEditorPanel.scope = SCOPE::SCOPE_SHEET_RECURSIVE;
1335
1336 for( int i = 0; i < m_grid->GetNumberCols(); i++ )
1337 {
1338 if( m_grid->IsColShown( i ) )
1339 {
1340 std::string fieldName( m_dataModel->GetColFieldName( i ).ToUTF8() );
1341 cfg->m_FieldEditorPanel.field_widths[fieldName] = m_grid->GetColSize( i );
1342 }
1343 }
1344
1345 m_parent->FocusOnItem( nullptr );
1346
1347 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, wxID_ANY );
1348
1349 wxWindow* parent = GetParent();
1350
1351 if( parent )
1352 wxQueueEvent( parent, evt );
1353}
1354
1355
1357{
1358 std::vector<BOM_PRESET> ret;
1359
1360 for( const std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1361 {
1362 if( !pair.second.readOnly )
1363 ret.emplace_back( pair.second );
1364 }
1365
1366 return ret;
1367}
1368
1369
1370void DIALOG_SYMBOL_FIELDS_TABLE::SetUserBomPresets( std::vector<BOM_PRESET>& aPresetList )
1371{
1372 // Reset to defaults
1374
1375 for( const BOM_PRESET& preset : aPresetList )
1376 {
1377 if( m_bomPresets.count( preset.name ) )
1378 continue;
1379
1380 m_bomPresets[preset.name] = preset;
1381
1382 m_bomPresetMRU.Add( preset.name );
1383 }
1384
1386}
1387
1388
1389void DIALOG_SYMBOL_FIELDS_TABLE::ApplyBomPreset( const wxString& aPresetName )
1390{
1391 updateBomPresetSelection( aPresetName );
1392
1393 wxCommandEvent dummy;
1395}
1396
1397
1399{
1400 if( m_bomPresets.count( aPreset.name ) )
1402 else
1403 m_currentBomPreset = nullptr;
1404
1407
1408 updateBomPresetSelection( aPreset.name );
1409 doApplyBomPreset( aPreset );
1410}
1411
1412
1414{
1415 m_bomPresets.clear();
1416 m_bomPresetMRU.clear();
1417
1418 // Load the read-only defaults
1419 for( const BOM_PRESET& preset : BOM_PRESET::BuiltInPresets() )
1420 {
1421 m_bomPresets[preset.name] = preset;
1422 m_bomPresets[preset.name].readOnly = true;
1423
1424 m_bomPresetMRU.Add( preset.name );
1425 }
1426}
1427
1428
1430{
1431 m_cbBomPresets->Clear();
1432
1433 // Build the layers preset list.
1434 // By default, the presetAllLayers will be selected
1435 int idx = 0;
1436 int default_idx = 0;
1437
1438 for( std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1439 {
1440 m_cbBomPresets->Append( wxGetTranslation( pair.first ),
1441 static_cast<void*>( &pair.second ) );
1442
1443 if( pair.first == BOM_PRESET::GroupedByValue().name )
1444 default_idx = idx;
1445
1446 idx++;
1447 }
1448
1449 m_cbBomPresets->Append( wxT( "---" ) );
1450 m_cbBomPresets->Append( _( "Save preset..." ) );
1451 m_cbBomPresets->Append( _( "Delete preset..." ) );
1452
1453 // At least the built-in presets should always be present
1454 wxASSERT( !m_bomPresets.empty() );
1455
1456 // Default preset: all Boms
1457 m_cbBomPresets->SetSelection( default_idx );
1458 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( default_idx ) );
1459}
1460
1461
1463{
1465
1466 auto it = std::find_if( m_bomPresets.begin(), m_bomPresets.end(),
1467 [&]( const std::pair<const wxString, BOM_PRESET>& aPair )
1468 {
1469 const BOM_PRESET& preset = aPair.second;
1470
1471 // Check the simple settings first
1472 if( !( preset.sortField == current.sortField
1473 && preset.sortAsc == current.sortAsc
1474 && preset.filterString == current.filterString
1475 && preset.groupSymbols == current.groupSymbols
1476 && preset.excludeDNP == current.excludeDNP ) )
1477 return false;
1478
1479 // Only compare shown or grouped fields
1480 std::vector<BOM_FIELD> A, B;
1481
1482 for( const BOM_FIELD& field : preset.fieldsOrdered )
1483 if( field.show || field.groupBy )
1484 A.emplace_back( field );
1485
1486 for( const BOM_FIELD& field : current.fieldsOrdered )
1487 if( field.show || field.groupBy )
1488 B.emplace_back( field );
1489
1490 return A == B;
1491 } );
1492
1493 if( it != m_bomPresets.end() )
1494 {
1495 // Select the right m_cbBomPresets item.
1496 // but these items are translated if they are predefined items.
1497 bool do_translate = it->second.readOnly;
1498 wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
1499
1500 m_cbBomPresets->SetStringSelection( text );
1501 }
1502 else
1503 {
1504 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 ); // separator
1505 }
1506
1507 m_currentBomPreset = static_cast<BOM_PRESET*>(
1508 m_cbBomPresets->GetClientData( m_cbBomPresets->GetSelection() ) );
1509}
1510
1511
1513{
1514 // look at m_userBomPresets to know if aName is a read only preset, or a user preset.
1515 // Read only presets have translated names in UI, so we have to use
1516 // a translated name in UI selection.
1517 // But for a user preset name we should search for aName (not translated)
1518 wxString ui_label = aName;
1519
1520 for( std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1521 {
1522 if( pair.first != aName )
1523 continue;
1524
1525 if( pair.second.readOnly == true )
1526 ui_label = wxGetTranslation( aName );
1527
1528 break;
1529 }
1530
1531 int idx = m_cbBomPresets->FindString( ui_label );
1532
1533 if( idx >= 0 && m_cbBomPresets->GetSelection() != idx )
1534 {
1535 m_cbBomPresets->SetSelection( idx );
1536 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( idx ) );
1537 }
1538 else if( idx < 0 )
1539 {
1540 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 ); // separator
1541 }
1542}
1543
1544
1546{
1547 int count = m_cbBomPresets->GetCount();
1548 int index = m_cbBomPresets->GetSelection();
1549
1550 auto resetSelection =
1551 [&]()
1552 {
1553 if( m_currentBomPreset )
1554 m_cbBomPresets->SetStringSelection( m_currentBomPreset->name );
1555 else
1556 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 );
1557 };
1558
1559 if( index == count - 3 )
1560 {
1561 // Separator: reject the selection
1562 resetSelection();
1563 return;
1564 }
1565 else if( index == count - 2 )
1566 {
1567 // Save current state to new preset
1568 wxString name;
1569
1572
1573 wxTextEntryDialog dlg( this, _( "BOM preset name:" ), _( "Save BOM Preset" ), name );
1574
1575 if( dlg.ShowModal() != wxID_OK )
1576 {
1577 resetSelection();
1578 return;
1579 }
1580
1581 name = dlg.GetValue();
1582 bool exists = m_bomPresets.count( name );
1583
1584 if( !exists )
1585 {
1587 m_bomPresets[name].readOnly = false;
1588 m_bomPresets[name].name = name;
1589 }
1590
1591 BOM_PRESET* preset = &m_bomPresets[name];
1592
1593 if( !exists )
1594 {
1595 index = m_cbBomPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
1596 }
1597 else if( preset->readOnly )
1598 {
1599 wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
1600 _( "Error" ), wxOK | wxICON_ERROR, this );
1601 resetSelection();
1602 return;
1603 }
1604 else
1605 {
1606 // Ask the user if they want to overwrite the existing preset
1607 if( !IsOK( this, _( "Overwrite existing preset?" ) ) )
1608 {
1609 resetSelection();
1610 return;
1611 }
1612
1613 *preset = m_dataModel->GetBomSettings();
1614 preset->name = name;
1615
1616 index = m_cbBomPresets->FindString( name );
1617 m_bomPresetMRU.Remove( name );
1618 }
1619
1620 m_currentBomPreset = preset;
1621 m_cbBomPresets->SetSelection( index );
1622 m_bomPresetMRU.Insert( name, 0 );
1623
1624 return;
1625 }
1626 else if( index == count - 1 )
1627 {
1628 // Delete a preset
1629 wxArrayString headers;
1630 std::vector<wxArrayString> items;
1631
1632 headers.Add( _( "Presets" ) );
1633
1634 for( std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1635 {
1636 if( !pair.second.readOnly )
1637 {
1638 wxArrayString item;
1639 item.Add( pair.first );
1640 items.emplace_back( item );
1641 }
1642 }
1643
1644 EDA_LIST_DIALOG dlg( this, _( "Delete Preset" ), headers, items );
1645 dlg.SetListLabel( _( "Select preset:" ) );
1646
1647 if( dlg.ShowModal() == wxID_OK )
1648 {
1649 wxString presetName = dlg.GetTextSelection();
1650 int idx = m_cbBomPresets->FindString( presetName );
1651
1652 if( idx != wxNOT_FOUND )
1653 {
1654 m_bomPresets.erase( presetName );
1655
1656 m_cbBomPresets->Delete( idx );
1657 m_currentBomPreset = nullptr;
1658
1659 m_bomPresetMRU.Remove( presetName );
1660 }
1661 }
1662
1663 resetSelection();
1664 return;
1665 }
1666
1667 BOM_PRESET* preset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( index ) );
1668 m_currentBomPreset = preset;
1669
1670 m_lastSelectedBomPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
1671
1672 if( preset )
1673 {
1674 doApplyBomPreset( *preset );
1676 m_currentBomPreset = preset;
1677
1678 if( !m_currentBomPreset->name.IsEmpty() )
1679 {
1680 m_bomPresetMRU.Remove( preset->name );
1681 m_bomPresetMRU.Insert( preset->name, 0 );
1682 }
1683 }
1684}
1685
1686
1688{
1689 // Disable rebuilds while we're applying the preset otherwise we'll be
1690 // rebuilding the model constantly while firing off wx events
1692
1693 // Basically, we apply the BOM preset to the data model and then
1694 // update our UI to reflect resulting the data model state, not the preset.
1695 m_dataModel->ApplyBomPreset( aPreset );
1696
1697 // BOM Presets can add, but not remove, columns, so make sure the field control
1698 // grid has all of them before starting
1699 for( int i = 0; i < m_dataModel->GetColsCount(); i++ )
1700 {
1701 const wxString& fieldName( m_dataModel->GetColFieldName( i ) );
1702 bool found = false;
1703
1704 for( int j = 0; j < m_fieldsCtrl->GetItemCount(); j++ )
1705 {
1706 if( m_fieldsCtrl->GetTextValue( j, FIELD_NAME_COLUMN ) == fieldName )
1707 {
1708 found = true;
1709 break;
1710 }
1711 }
1712
1713 // Properties like label, etc. will be added in the next loop
1714 if( !found )
1715 AddField( fieldName, GetTextVars( fieldName ), false, false );
1716 }
1717
1718 // Sync all fields
1719 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); i++ )
1720 {
1721 const wxString& fieldName( m_fieldsCtrl->GetTextValue( i, FIELD_NAME_COLUMN ) );
1722 int col = m_dataModel->GetFieldNameCol( fieldName );
1723
1724 if( col == -1 )
1725 {
1726 wxASSERT_MSG( true, "Fields control has a field not found in the data model." );
1727 continue;
1728 }
1729
1731 std::string fieldNameStr( fieldName.ToUTF8() );
1732
1733 // Set column labels
1734 const wxString& label = m_dataModel->GetColLabelValue( col );
1735 m_fieldsCtrl->SetTextValue( label, i, LABEL_COLUMN );
1736 m_grid->SetColLabelValue( col, label );
1737
1738 if( cfg->m_FieldEditorPanel.field_widths.count( fieldNameStr ) )
1739 m_grid->SetColSize( col, cfg->m_FieldEditorPanel.field_widths.at( fieldNameStr ) );
1740
1741 // Set shown colums
1742 bool show = m_dataModel->GetShowColumn( col );
1743 m_fieldsCtrl->SetToggleValue( show, i, SHOW_FIELD_COLUMN );
1744
1745 if( show )
1746 m_grid->ShowCol( col );
1747 else
1748 m_grid->HideCol( col );
1749
1750 // Set grouped columns
1751 bool groupBy = m_dataModel->GetGroupColumn( col );
1752 m_fieldsCtrl->SetToggleValue( groupBy, i, GROUP_BY_COLUMN );
1753 }
1754
1755 m_grid->SetSortingColumn( m_dataModel->GetSortCol(), m_dataModel->GetSortAsc() );
1757 m_filter->ChangeValue( m_dataModel->GetFilter() );
1759
1761
1762 // This will rebuild all rows and columns in the model such that the order
1763 // and labels are right, then we refresh the shown grid data to match
1766 m_grid->ForceRefresh();
1767}
1768
1769
1770std::vector<BOM_FMT_PRESET> DIALOG_SYMBOL_FIELDS_TABLE::GetUserBomFmtPresets() const
1771{
1772 std::vector<BOM_FMT_PRESET> ret;
1773
1774 for( const std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
1775 {
1776 if( !pair.second.readOnly )
1777 ret.emplace_back( pair.second );
1778 }
1779
1780 return ret;
1781}
1782
1783
1784void DIALOG_SYMBOL_FIELDS_TABLE::SetUserBomFmtPresets( std::vector<BOM_FMT_PRESET>& aPresetList )
1785{
1786 // Reset to defaults
1788
1789 for( const BOM_FMT_PRESET& preset : aPresetList )
1790 {
1791 if( m_bomFmtPresets.count( preset.name ) )
1792 continue;
1793
1794 m_bomFmtPresets[preset.name] = preset;
1795
1796 m_bomFmtPresetMRU.Add( preset.name );
1797 }
1798
1800}
1801
1802
1803void DIALOG_SYMBOL_FIELDS_TABLE::ApplyBomFmtPreset( const wxString& aPresetName )
1804{
1805 updateBomFmtPresetSelection( aPresetName );
1806
1807 wxCommandEvent dummy;
1809}
1810
1811
1813{
1814 if( m_bomFmtPresets.count( aPreset.name ) )
1816 else
1817 m_currentBomFmtPreset = nullptr;
1818
1821 : nullptr;
1822
1824 doApplyBomFmtPreset( aPreset );
1825}
1826
1827
1829{
1830 m_bomFmtPresets.clear();
1831 m_bomFmtPresetMRU.clear();
1832
1833 // Load the read-only defaults
1834 for( const BOM_FMT_PRESET& preset : BOM_FMT_PRESET::BuiltInPresets() )
1835 {
1836 m_bomFmtPresets[preset.name] = preset;
1837 m_bomFmtPresets[preset.name].readOnly = true;
1838
1839 m_bomFmtPresetMRU.Add( preset.name );
1840 }
1841}
1842
1843
1845{
1846 m_cbBomFmtPresets->Clear();
1847
1848 // Build the layers preset list.
1849 // By default, the presetAllLayers will be selected
1850 int idx = 0;
1851 int default_idx = 0;
1852
1853 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
1854 {
1855 m_cbBomFmtPresets->Append( wxGetTranslation( pair.first ),
1856 static_cast<void*>( &pair.second ) );
1857
1858 if( pair.first == BOM_FMT_PRESET::CSV().name )
1859 default_idx = idx;
1860
1861 idx++;
1862 }
1863
1864 m_cbBomFmtPresets->Append( wxT( "---" ) );
1865 m_cbBomFmtPresets->Append( _( "Save preset..." ) );
1866 m_cbBomFmtPresets->Append( _( "Delete preset..." ) );
1867
1868 // At least the built-in presets should always be present
1869 wxASSERT( !m_bomFmtPresets.empty() );
1870
1871 // Default preset: all Boms
1872 m_cbBomFmtPresets->SetSelection( default_idx );
1874 static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( default_idx ) );
1875}
1876
1877
1879{
1881
1882 auto it = std::find_if( m_bomFmtPresets.begin(), m_bomFmtPresets.end(),
1883 [&]( const std::pair<const wxString, BOM_FMT_PRESET>& aPair )
1884 {
1885 return ( aPair.second.fieldDelimiter == current.fieldDelimiter
1886 && aPair.second.stringDelimiter == current.stringDelimiter
1887 && aPair.second.refDelimiter == current.refDelimiter
1888 && aPair.second.refRangeDelimiter == current.refRangeDelimiter
1889 && aPair.second.keepTabs == current.keepTabs
1890 && aPair.second.keepLineBreaks == current.keepLineBreaks );
1891 } );
1892
1893 if( it != m_bomFmtPresets.end() )
1894 {
1895 // Select the right m_cbBomFmtPresets item.
1896 // but these items are translated if they are predefined items.
1897 bool do_translate = it->second.readOnly;
1898 wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
1899
1900 m_cbBomFmtPresets->SetStringSelection( text );
1901 }
1902 else
1903 {
1904 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 ); // separator
1905 }
1906
1907 m_currentBomFmtPreset = static_cast<BOM_FMT_PRESET*>(
1908 m_cbBomFmtPresets->GetClientData( m_cbBomFmtPresets->GetSelection() ) );
1909}
1910
1911
1913{
1914 // look at m_userBomFmtPresets to know if aName is a read only preset, or a user preset.
1915 // Read only presets have translated names in UI, so we have to use
1916 // a translated name in UI selection.
1917 // But for a user preset name we should search for aName (not translated)
1918 wxString ui_label = aName;
1919
1920 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
1921 {
1922 if( pair.first != aName )
1923 continue;
1924
1925 if( pair.second.readOnly == true )
1926 ui_label = wxGetTranslation( aName );
1927
1928 break;
1929 }
1930
1931 int idx = m_cbBomFmtPresets->FindString( ui_label );
1932
1933 if( idx >= 0 && m_cbBomFmtPresets->GetSelection() != idx )
1934 {
1935 m_cbBomFmtPresets->SetSelection( idx );
1937 static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( idx ) );
1938 }
1939 else if( idx < 0 )
1940 {
1941 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 ); // separator
1942 }
1943}
1944
1945
1947{
1948 int count = m_cbBomFmtPresets->GetCount();
1949 int index = m_cbBomFmtPresets->GetSelection();
1950
1951 auto resetSelection =
1952 [&]()
1953 {
1955 m_cbBomFmtPresets->SetStringSelection( m_currentBomFmtPreset->name );
1956 else
1957 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 );
1958 };
1959
1960 if( index == count - 3 )
1961 {
1962 // Separator: reject the selection
1963 resetSelection();
1964 return;
1965 }
1966 else if( index == count - 2 )
1967 {
1968 // Save current state to new preset
1969 wxString name;
1970
1973
1974 wxTextEntryDialog dlg( this, _( "BOM preset name:" ), _( "Save BOM Preset" ), name );
1975
1976 if( dlg.ShowModal() != wxID_OK )
1977 {
1978 resetSelection();
1979 return;
1980 }
1981
1982 name = dlg.GetValue();
1983 bool exists = m_bomFmtPresets.count( name );
1984
1985 if( !exists )
1986 {
1988 m_bomFmtPresets[name].readOnly = false;
1989 m_bomFmtPresets[name].name = name;
1990 }
1991
1993
1994 if( !exists )
1995 {
1996 index = m_cbBomFmtPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
1997 }
1998 else if( preset->readOnly )
1999 {
2000 wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
2001 _( "Error" ), wxOK | wxICON_ERROR, this );
2002 resetSelection();
2003 return;
2004 }
2005 else
2006 {
2007 // Ask the user if they want to overwrite the existing preset
2008 if( !IsOK( this, _( "Overwrite existing preset?" ) ) )
2009 {
2010 resetSelection();
2011 return;
2012 }
2013
2014 *preset = GetCurrentBomFmtSettings();
2015 preset->name = name;
2016
2017 index = m_cbBomFmtPresets->FindString( name );
2018 m_bomFmtPresetMRU.Remove( name );
2019 }
2020
2021 m_currentBomFmtPreset = preset;
2022 m_cbBomFmtPresets->SetSelection( index );
2023 m_bomFmtPresetMRU.Insert( name, 0 );
2024
2025 return;
2026 }
2027 else if( index == count - 1 )
2028 {
2029 // Delete a preset
2030 wxArrayString headers;
2031 std::vector<wxArrayString> items;
2032
2033 headers.Add( _( "Presets" ) );
2034
2035 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
2036 {
2037 if( !pair.second.readOnly )
2038 {
2039 wxArrayString item;
2040 item.Add( pair.first );
2041 items.emplace_back( item );
2042 }
2043 }
2044
2045 EDA_LIST_DIALOG dlg( this, _( "Delete Preset" ), headers, items );
2046 dlg.SetListLabel( _( "Select preset:" ) );
2047
2048 if( dlg.ShowModal() == wxID_OK )
2049 {
2050 wxString presetName = dlg.GetTextSelection();
2051 int idx = m_cbBomFmtPresets->FindString( presetName );
2052
2053 if( idx != wxNOT_FOUND )
2054 {
2055 m_bomFmtPresets.erase( presetName );
2056
2057 m_cbBomFmtPresets->Delete( idx );
2058 m_currentBomFmtPreset = nullptr;
2059
2060 m_bomFmtPresetMRU.Remove( presetName );
2061 }
2062 }
2063
2064 resetSelection();
2065 return;
2066 }
2067
2068 BOM_FMT_PRESET* preset =
2069 static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( index ) );
2070 m_currentBomFmtPreset = preset;
2071
2072 m_lastSelectedBomFmtPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
2073
2074 if( preset )
2075 {
2076 doApplyBomFmtPreset( *preset );
2078 m_currentBomFmtPreset = preset;
2079
2080 if( !m_currentBomFmtPreset->name.IsEmpty() )
2081 {
2082 m_bomFmtPresetMRU.Remove( preset->name );
2083 m_bomFmtPresetMRU.Insert( preset->name, 0 );
2084 }
2085 }
2086}
2087
2088
2090{
2091 m_textFieldDelimiter->ChangeValue( aPreset.fieldDelimiter );
2092 m_textStringDelimiter->ChangeValue( aPreset.stringDelimiter );
2093 m_textRefDelimiter->ChangeValue( aPreset.refDelimiter );
2094 m_textRefRangeDelimiter->ChangeValue( aPreset.refRangeDelimiter );
2095 m_checkKeepTabs->SetValue( aPreset.keepTabs );
2096 m_checkKeepLineBreaks->SetValue( aPreset.keepLineBreaks );
2097
2098
2099 // Refresh the preview if that's the current page
2100 if( m_nbPages->GetSelection() == 1 )
2102}
2103
2104
2106{
2107 // Save our BOM presets
2108 std::vector<BOM_PRESET> presets;
2109
2110 for( const std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
2111 {
2112 if( !pair.second.readOnly )
2113 presets.emplace_back( pair.second );
2114 }
2115
2116 m_schSettings.m_BomPresets = presets;
2118
2119 // Save our BOM Format presets
2120 std::vector<BOM_FMT_PRESET> fmts;
2121
2122 for( const std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
2123 {
2124 if( !pair.second.readOnly )
2125 fmts.emplace_back( pair.second );
2126 }
2127
2130}
2131
2132
2134 std::vector<SCH_ITEM*>& aSchItem )
2135{
2136 for( SCH_ITEM* item : aSchItem )
2137 {
2138 if( item->Type() == SCH_SYMBOL_T )
2139 {
2140 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2141
2142 // Add all fields again in case this symbol has a new one
2143 for( SCH_FIELD& field : symbol->GetFields() )
2144 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2145
2147 }
2148 else if( item->Type() == SCH_SHEET_T )
2149 {
2150 std::set<SCH_SYMBOL*> symbols;
2151 SCH_REFERENCE_LIST refs = getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) );
2152
2153 for( SCH_REFERENCE& ref : refs )
2154 symbols.insert( ref.GetSymbol() );
2155
2156 for( SCH_SYMBOL* symbol : symbols )
2157 {
2158 // Add all fields again in case this symbol has a new one
2159 for( SCH_FIELD& field : symbol->GetFields() )
2160 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2161 }
2162
2163 m_dataModel->AddReferences( refs );
2164 }
2165 }
2166
2170}
2171
2172
2174 std::vector<SCH_ITEM*>& aSchItem )
2175{
2176 for( SCH_ITEM* item : aSchItem )
2177 {
2178 if( item->Type() == SCH_SYMBOL_T )
2179 m_dataModel->RemoveSymbol( *static_cast<SCH_SYMBOL*>( item ) );
2180 else if( item->Type() == SCH_SHEET_T )
2182 getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) ) );
2183 }
2184
2188}
2189
2190
2192 std::vector<SCH_ITEM*>& aSchItem )
2193{
2194 for( SCH_ITEM* item : aSchItem )
2195 {
2196 if( item->Type() == SCH_SYMBOL_T )
2197 {
2198 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2199
2200 // Add all fields again in case this symbol has a new one
2201 for( SCH_FIELD& field : symbol->GetFields() )
2202 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2203
2205 }
2206 else if( item->Type() == SCH_SHEET_T )
2207 {
2208 std::set<SCH_SYMBOL*> symbols;
2209 SCH_REFERENCE_LIST refs = getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) );
2210
2211 for( SCH_REFERENCE& ref : refs )
2212 symbols.insert( ref.GetSymbol() );
2213
2214 for( SCH_SYMBOL* symbol : symbols )
2215 {
2216 // Add all fields again in case this symbol has a new one
2217 for( SCH_FIELD& field : symbol->GetFields() )
2218 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2219 }
2220
2222 }
2223 }
2224
2228}
2229
2230
2232{
2233 m_dataModel->SetPath( aSch.CurrentSheet() );
2234
2235 if( m_dataModel->GetScope() != FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_ALL )
2236 {
2240 }
2241}
2242
2243
2245{
2246 m_grid->Connect(
2247 wxEVT_GRID_RANGE_SELECTED,
2248 wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected ),
2249 nullptr, this );
2250}
2251
2252
2254{
2255 m_grid->Disconnect(
2256 wxEVT_GRID_RANGE_SELECTED,
2257 wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected ),
2258 nullptr, this );
2259}
2260
2261
2263{
2264 SCH_SHEET_LIST allSheets = m_parent->Schematic().GetSheets();
2265 SCH_REFERENCE_LIST allRefs;
2266 SCH_REFERENCE_LIST symbolRefs;
2267
2268 allSheets.GetSymbols( allRefs );
2269
2270 for( size_t i = 0; i < allRefs.GetCount(); i++ )
2271 {
2272 SCH_REFERENCE& ref = allRefs[i];
2273
2274 if( ref.GetSymbol() == aSymbol )
2275 {
2276 ref.Split(); // Figures out if we are annotated or not
2277 symbolRefs.AddItem( ref );
2278 }
2279 }
2280
2281 return symbolRefs;
2282}
2283
2284
2286{
2287 SCH_SHEET_LIST allSheets = m_parent->Schematic().GetSheets();
2288 SCH_REFERENCE_LIST sheetRefs;
2289
2290 // We need to operate on all instances of the sheet
2291 for( const SCH_SHEET_INSTANCE& instance : aSheet.GetInstances() )
2292 {
2293 // For every sheet instance we need to get the current schematic sheet
2294 // instance that matches that particular sheet path from the root
2295 for( SCH_SHEET_PATH& basePath : allSheets )
2296 {
2297 if( basePath.Path() == instance.m_Path )
2298 {
2299 SCH_SHEET_PATH sheetPath = basePath;
2300 sheetPath.push_back( &aSheet );
2301
2302 // Create a list of all sheets in this path, starting with the path
2303 // of the sheet that we just deleted, then all of its subsheets
2304 SCH_SHEET_LIST subSheets;
2305 subSheets.push_back( sheetPath );
2306 allSheets.GetSheetsWithinPath( subSheets, sheetPath );
2307
2308 subSheets.GetSymbolsWithinPath( sheetRefs, sheetPath, false, false );
2309 break;
2310 }
2311 }
2312 }
2313
2314 for( SCH_REFERENCE& ref : sheetRefs )
2315 ref.Split();
2316
2317 return sheetRefs;
2318}
const char * name
Definition: DXF_plotter.cpp:57
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:106
void SetIsSeparator()
Render button as a toolbar separator.
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
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
SCH_REFERENCE_LIST getSymbolReferences(SCH_SYMBOL *aSymbol)
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)
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:66
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:328
BOM_FMT_PRESET m_BomFmtSettings
List of stored BOM format presets.
TEMPLATES m_TemplateFieldNames
std::vector< BOM_PRESET > m_BomPresets
std::vector< BOM_FMT_PRESET > m_BomFmtPresets
BOM_PRESET m_BomSettings
List of stored BOM presets.
Holds all the data relating to one schematic.
Definition: schematic.h:75
SCH_SHEET_PATH & CurrentSheet() const override
Definition: schematic.h:136
void RemoveListener(SCHEMATIC_LISTENER *aListener)
Remove the specified listener.
Definition: schematic.cpp:713
bool ResolveTextVar(wxString *token, int aDepth) const
Definition: schematic.cpp:156
wxString GetFileName() const override
Helper to retrieve the filename from the root sheet screen.
Definition: schematic.cpp:203
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:706
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:353
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:150
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:391
Schematic symbol object.
Definition: sch_symbol.h:81
int GetFieldCount() const
Return the number of fields in this symbol.
Definition: sch_symbol.h:479
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:973
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:157
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:449
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:317
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:345
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:74
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:42
@ GRIDTRICKS_FIRST_SHOWHIDE
Definition: grid_tricks.h:51
@ GRIDTRICKS_FIRST_CLIENT_ID
Definition: grid_tricks.h:48
const std::string CsvFileExtension
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:160
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
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.
@ 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:155
@ SCH_SHEET_T
Definition: typeinfo.h:157
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.