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