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