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 out.Close(); // close the file before we tell the user it's done with the info modal :workflow meme:
1301 msg.Printf( _( "Wrote BOM output to '%s'" ), outputFile.GetFullPath() );
1302 DisplayInfoMessage( this, msg );
1303}
1304
1305
1306void DIALOG_SYMBOL_FIELDS_TABLE::OnCancel( wxCommandEvent& aEvent )
1307{
1308 Close();
1309}
1310
1311
1312void DIALOG_SYMBOL_FIELDS_TABLE::OnOk( wxCommandEvent& aEvent )
1313{
1315 Close();
1316}
1317
1318
1319void DIALOG_SYMBOL_FIELDS_TABLE::OnClose( wxCloseEvent& aEvent )
1320{
1321 // This is a cancel, so commit quietly as we're going to throw the results away anyway.
1323
1324 if( m_dataModel->IsEdited() )
1325 {
1326 if( !HandleUnsavedChanges( this, _( "Save changes?" ),
1327 [&]() -> bool
1328 {
1329 return TransferDataFromWindow();
1330 } ) )
1331 {
1332 aEvent.Veto();
1333 return;
1334 }
1335 }
1336
1337 // Stop listening to schematic events
1339
1340 // Save all our settings since we're really closing
1343
1345
1346 cfg->m_FieldEditorPanel.width = GetSize().x;
1347 cfg->m_FieldEditorPanel.height = GetSize().y;
1348 cfg->m_FieldEditorPanel.page = m_nbPages->GetSelection();
1349
1350 if( m_radioHighlight->GetValue() )
1352 else if( m_radioSelect->GetValue() )
1354 else if( m_radioOff->GetValue() )
1356
1357 if( m_radioProject->GetValue() )
1358 cfg->m_FieldEditorPanel.scope = SCOPE::SCOPE_ALL;
1359 else if( m_radioCurrentSheet->GetValue() )
1360 cfg->m_FieldEditorPanel.scope = SCOPE::SCOPE_SHEET;
1361 else if( m_radioRecursive->GetValue() )
1362 cfg->m_FieldEditorPanel.scope = SCOPE::SCOPE_SHEET_RECURSIVE;
1363
1364 for( int i = 0; i < m_grid->GetNumberCols(); i++ )
1365 {
1366 if( m_grid->IsColShown( i ) )
1367 {
1368 std::string fieldName( m_dataModel->GetColFieldName( i ).ToUTF8() );
1369 cfg->m_FieldEditorPanel.field_widths[fieldName] = m_grid->GetColSize( i );
1370 }
1371 }
1372
1373 m_parent->FocusOnItem( nullptr );
1374
1375 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, wxID_ANY );
1376
1377 wxWindow* parent = GetParent();
1378
1379 if( parent )
1380 wxQueueEvent( parent, evt );
1381}
1382
1383
1385{
1386 std::vector<BOM_PRESET> ret;
1387
1388 for( const std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1389 {
1390 if( !pair.second.readOnly )
1391 ret.emplace_back( pair.second );
1392 }
1393
1394 return ret;
1395}
1396
1397
1398void DIALOG_SYMBOL_FIELDS_TABLE::SetUserBomPresets( std::vector<BOM_PRESET>& aPresetList )
1399{
1400 // Reset to defaults
1402
1403 for( const BOM_PRESET& preset : aPresetList )
1404 {
1405 if( m_bomPresets.count( preset.name ) )
1406 continue;
1407
1408 m_bomPresets[preset.name] = preset;
1409
1410 m_bomPresetMRU.Add( preset.name );
1411 }
1412
1414}
1415
1416
1417void DIALOG_SYMBOL_FIELDS_TABLE::ApplyBomPreset( const wxString& aPresetName )
1418{
1419 updateBomPresetSelection( aPresetName );
1420
1421 wxCommandEvent dummy;
1423}
1424
1425
1427{
1428 if( m_bomPresets.count( aPreset.name ) )
1430 else
1431 m_currentBomPreset = nullptr;
1432
1435 else
1436 m_lastSelectedBomPreset = nullptr;
1437
1438 updateBomPresetSelection( aPreset.name );
1439 doApplyBomPreset( aPreset );
1440}
1441
1442
1444{
1445 m_bomPresets.clear();
1446 m_bomPresetMRU.clear();
1447
1448 // Load the read-only defaults
1449 for( const BOM_PRESET& preset : BOM_PRESET::BuiltInPresets() )
1450 {
1451 m_bomPresets[preset.name] = preset;
1452 m_bomPresets[preset.name].readOnly = true;
1453
1454 m_bomPresetMRU.Add( preset.name );
1455 }
1456}
1457
1458
1460{
1461 m_cbBomPresets->Clear();
1462
1463 // Build the layers preset list.
1464 // By default, the presetAllLayers will be selected
1465 int idx = 0;
1466 int default_idx = 0;
1467
1468 for( std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1469 {
1470 m_cbBomPresets->Append( wxGetTranslation( pair.first ),
1471 static_cast<void*>( &pair.second ) );
1472
1473 if( pair.first == BOM_PRESET::DefaultEditing().name )
1474 default_idx = idx;
1475
1476 idx++;
1477 }
1478
1479 m_cbBomPresets->Append( wxT( "---" ) );
1480 m_cbBomPresets->Append( _( "Save preset..." ) );
1481 m_cbBomPresets->Append( _( "Delete preset..." ) );
1482
1483 // At least the built-in presets should always be present
1484 wxASSERT( !m_bomPresets.empty() );
1485
1486 // Default preset: all Boms
1487 m_cbBomPresets->SetSelection( default_idx );
1488 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( default_idx ) );
1489}
1490
1491
1493{
1495
1496 auto it = std::find_if( m_bomPresets.begin(), m_bomPresets.end(),
1497 [&]( const std::pair<const wxString, BOM_PRESET>& aPair )
1498 {
1499 const BOM_PRESET& preset = aPair.second;
1500
1501 // Check the simple settings first
1502 if( !( preset.sortField == current.sortField
1503 && preset.sortAsc == current.sortAsc
1504 && preset.filterString == current.filterString
1505 && preset.groupSymbols == current.groupSymbols
1506 && preset.excludeDNP == current.excludeDNP ) )
1507 {
1508 return false;
1509 }
1510
1511 // Only compare shown or grouped fields
1512 std::vector<BOM_FIELD> A, B;
1513
1514 for( const BOM_FIELD& field : preset.fieldsOrdered )
1515 {
1516 if( field.show || field.groupBy )
1517 A.emplace_back( field );
1518 }
1519
1520 for( const BOM_FIELD& field : current.fieldsOrdered )
1521 {
1522 if( field.show || field.groupBy )
1523 B.emplace_back( field );
1524 }
1525
1526 return A == B;
1527 } );
1528
1529 if( it != m_bomPresets.end() )
1530 {
1531 // Select the right m_cbBomPresets item.
1532 // but these items are translated if they are predefined items.
1533 bool do_translate = it->second.readOnly;
1534 wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
1535
1536 m_cbBomPresets->SetStringSelection( text );
1537 }
1538 else
1539 {
1540 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 ); // separator
1541 }
1542
1543 m_currentBomPreset = static_cast<BOM_PRESET*>(
1544 m_cbBomPresets->GetClientData( m_cbBomPresets->GetSelection() ) );
1545}
1546
1547
1549{
1550 // look at m_userBomPresets to know if aName is a read only preset, or a user preset.
1551 // Read only presets have translated names in UI, so we have to use
1552 // a translated name in UI selection.
1553 // But for a user preset name we should search for aName (not translated)
1554 wxString ui_label = aName;
1555
1556 for( std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1557 {
1558 if( pair.first != aName )
1559 continue;
1560
1561 if( pair.second.readOnly == true )
1562 ui_label = wxGetTranslation( aName );
1563
1564 break;
1565 }
1566
1567 int idx = m_cbBomPresets->FindString( ui_label );
1568
1569 if( idx >= 0 && m_cbBomPresets->GetSelection() != idx )
1570 {
1571 m_cbBomPresets->SetSelection( idx );
1572 m_currentBomPreset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( idx ) );
1573 }
1574 else if( idx < 0 )
1575 {
1576 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 ); // separator
1577 }
1578}
1579
1580
1582{
1583 int count = m_cbBomPresets->GetCount();
1584 int index = m_cbBomPresets->GetSelection();
1585
1586 auto resetSelection =
1587 [&]()
1588 {
1589 if( m_currentBomPreset )
1590 m_cbBomPresets->SetStringSelection( m_currentBomPreset->name );
1591 else
1592 m_cbBomPresets->SetSelection( m_cbBomPresets->GetCount() - 3 );
1593 };
1594
1595 if( index == count - 3 )
1596 {
1597 // Separator: reject the selection
1598 resetSelection();
1599 return;
1600 }
1601 else if( index == count - 2 )
1602 {
1603 // Save current state to new preset
1604 wxString name;
1605
1608
1609 wxTextEntryDialog dlg( this, _( "BOM preset name:" ), _( "Save BOM Preset" ), name );
1610
1611 if( dlg.ShowModal() != wxID_OK )
1612 {
1613 resetSelection();
1614 return;
1615 }
1616
1617 name = dlg.GetValue();
1618 bool exists = m_bomPresets.count( name );
1619
1620 if( !exists )
1621 {
1623 m_bomPresets[name].readOnly = false;
1624 m_bomPresets[name].name = name;
1625 }
1626
1627 BOM_PRESET* preset = &m_bomPresets[name];
1628
1629 if( !exists )
1630 {
1631 index = m_cbBomPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
1632 }
1633 else if( preset->readOnly )
1634 {
1635 wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
1636 _( "Error" ), wxOK | wxICON_ERROR, this );
1637 resetSelection();
1638 return;
1639 }
1640 else
1641 {
1642 // Ask the user if they want to overwrite the existing preset
1643 if( !IsOK( this, _( "Overwrite existing preset?" ) ) )
1644 {
1645 resetSelection();
1646 return;
1647 }
1648
1649 *preset = m_dataModel->GetBomSettings();
1650 preset->name = name;
1651
1652 index = m_cbBomPresets->FindString( name );
1653 m_bomPresetMRU.Remove( name );
1654 }
1655
1656 m_currentBomPreset = preset;
1657 m_cbBomPresets->SetSelection( index );
1658 m_bomPresetMRU.Insert( name, 0 );
1659
1660 return;
1661 }
1662 else if( index == count - 1 )
1663 {
1664 // Delete a preset
1665 wxArrayString headers;
1666 std::vector<wxArrayString> items;
1667
1668 headers.Add( _( "Presets" ) );
1669
1670 for( std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
1671 {
1672 if( !pair.second.readOnly )
1673 {
1674 wxArrayString item;
1675 item.Add( pair.first );
1676 items.emplace_back( item );
1677 }
1678 }
1679
1680 EDA_LIST_DIALOG dlg( this, _( "Delete Preset" ), headers, items );
1681 dlg.SetListLabel( _( "Select preset:" ) );
1682
1683 if( dlg.ShowModal() == wxID_OK )
1684 {
1685 wxString presetName = dlg.GetTextSelection();
1686 int idx = m_cbBomPresets->FindString( presetName );
1687
1688 if( idx != wxNOT_FOUND )
1689 {
1690 m_bomPresets.erase( presetName );
1691
1692 m_cbBomPresets->Delete( idx );
1693 m_currentBomPreset = nullptr;
1694
1695 m_bomPresetMRU.Remove( presetName );
1696 }
1697 }
1698
1699 resetSelection();
1700 return;
1701 }
1702
1703 BOM_PRESET* preset = static_cast<BOM_PRESET*>( m_cbBomPresets->GetClientData( index ) );
1704 m_currentBomPreset = preset;
1705
1706 m_lastSelectedBomPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
1707
1708 if( preset )
1709 {
1710 doApplyBomPreset( *preset );
1712 m_currentBomPreset = preset;
1713
1714 if( !m_currentBomPreset->name.IsEmpty() )
1715 {
1716 m_bomPresetMRU.Remove( preset->name );
1717 m_bomPresetMRU.Insert( preset->name, 0 );
1718 }
1719 }
1720}
1721
1722
1724{
1725 // Disable rebuilds while we're applying the preset otherwise we'll be
1726 // rebuilding the model constantly while firing off wx events
1728
1729 // Basically, we apply the BOM preset to the data model and then
1730 // update our UI to reflect resulting the data model state, not the preset.
1731 m_dataModel->ApplyBomPreset( aPreset );
1732
1733 // BOM Presets can add, but not remove, columns, so make sure the field control
1734 // grid has all of them before starting
1735 for( int i = 0; i < m_dataModel->GetColsCount(); i++ )
1736 {
1737 const wxString& fieldName( m_dataModel->GetColFieldName( i ) );
1738 bool found = false;
1739
1740 for( int j = 0; j < m_fieldsCtrl->GetItemCount(); j++ )
1741 {
1742 if( m_fieldsCtrl->GetTextValue( j, FIELD_NAME_COLUMN ) == fieldName )
1743 {
1744 found = true;
1745 break;
1746 }
1747 }
1748
1749 // Properties like label, etc. will be added in the next loop
1750 if( !found )
1751 AddField( fieldName, GetTextVars( fieldName ), false, false );
1752 }
1753
1754 // Sync all fields
1755 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); i++ )
1756 {
1757 const wxString& fieldName( m_fieldsCtrl->GetTextValue( i, FIELD_NAME_COLUMN ) );
1758 int col = m_dataModel->GetFieldNameCol( fieldName );
1759
1760 if( col == -1 )
1761 {
1762 wxASSERT_MSG( true, "Fields control has a field not found in the data model." );
1763 continue;
1764 }
1765
1767 std::string fieldNameStr( fieldName.ToUTF8() );
1768
1769 // Set column labels
1770 const wxString& label = m_dataModel->GetColLabelValue( col );
1771 m_fieldsCtrl->SetTextValue( label, i, LABEL_COLUMN );
1772 m_grid->SetColLabelValue( col, label );
1773
1774 if( cfg->m_FieldEditorPanel.field_widths.count( fieldNameStr ) )
1775 m_grid->SetColSize( col, cfg->m_FieldEditorPanel.field_widths.at( fieldNameStr ) );
1776
1777 // Set shown colums
1778 bool show = m_dataModel->GetShowColumn( col );
1779 m_fieldsCtrl->SetToggleValue( show, i, SHOW_FIELD_COLUMN );
1780
1781 if( show )
1782 m_grid->ShowCol( col );
1783 else
1784 m_grid->HideCol( col );
1785
1786 // Set grouped columns
1787 bool groupBy = m_dataModel->GetGroupColumn( col );
1788 m_fieldsCtrl->SetToggleValue( groupBy, i, GROUP_BY_COLUMN );
1789 }
1790
1791 m_grid->SetSortingColumn( m_dataModel->GetSortCol(), m_dataModel->GetSortAsc() );
1793 m_filter->ChangeValue( m_dataModel->GetFilter() );
1796
1798
1799 // This will rebuild all rows and columns in the model such that the order
1800 // and labels are right, then we refresh the shown grid data to match
1803 m_grid->ForceRefresh();
1804}
1805
1806
1807std::vector<BOM_FMT_PRESET> DIALOG_SYMBOL_FIELDS_TABLE::GetUserBomFmtPresets() const
1808{
1809 std::vector<BOM_FMT_PRESET> ret;
1810
1811 for( const std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
1812 {
1813 if( !pair.second.readOnly )
1814 ret.emplace_back( pair.second );
1815 }
1816
1817 return ret;
1818}
1819
1820
1821void DIALOG_SYMBOL_FIELDS_TABLE::SetUserBomFmtPresets( std::vector<BOM_FMT_PRESET>& aPresetList )
1822{
1823 // Reset to defaults
1825
1826 for( const BOM_FMT_PRESET& preset : aPresetList )
1827 {
1828 if( m_bomFmtPresets.count( preset.name ) )
1829 continue;
1830
1831 m_bomFmtPresets[preset.name] = preset;
1832
1833 m_bomFmtPresetMRU.Add( preset.name );
1834 }
1835
1837}
1838
1839
1840void DIALOG_SYMBOL_FIELDS_TABLE::ApplyBomFmtPreset( const wxString& aPresetName )
1841{
1842 updateBomFmtPresetSelection( aPresetName );
1843
1844 wxCommandEvent dummy;
1846}
1847
1848
1850{
1851 if( m_bomFmtPresets.count( aPreset.name ) )
1853 else
1854 m_currentBomFmtPreset = nullptr;
1855
1858 : nullptr;
1859
1861 doApplyBomFmtPreset( aPreset );
1862}
1863
1864
1866{
1867 m_bomFmtPresets.clear();
1868 m_bomFmtPresetMRU.clear();
1869
1870 // Load the read-only defaults
1871 for( const BOM_FMT_PRESET& preset : BOM_FMT_PRESET::BuiltInPresets() )
1872 {
1873 m_bomFmtPresets[preset.name] = preset;
1874 m_bomFmtPresets[preset.name].readOnly = true;
1875
1876 m_bomFmtPresetMRU.Add( preset.name );
1877 }
1878}
1879
1880
1882{
1883 m_cbBomFmtPresets->Clear();
1884
1885 // Build the layers preset list.
1886 // By default, the presetAllLayers will be selected
1887 int idx = 0;
1888 int default_idx = 0;
1889
1890 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
1891 {
1892 m_cbBomFmtPresets->Append( wxGetTranslation( pair.first ),
1893 static_cast<void*>( &pair.second ) );
1894
1895 if( pair.first == BOM_FMT_PRESET::CSV().name )
1896 default_idx = idx;
1897
1898 idx++;
1899 }
1900
1901 m_cbBomFmtPresets->Append( wxT( "---" ) );
1902 m_cbBomFmtPresets->Append( _( "Save preset..." ) );
1903 m_cbBomFmtPresets->Append( _( "Delete preset..." ) );
1904
1905 // At least the built-in presets should always be present
1906 wxASSERT( !m_bomFmtPresets.empty() );
1907
1908 // Default preset: all Boms
1909 m_cbBomFmtPresets->SetSelection( default_idx );
1911 static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( default_idx ) );
1912}
1913
1914
1916{
1918
1919 auto it = std::find_if( m_bomFmtPresets.begin(), m_bomFmtPresets.end(),
1920 [&]( const std::pair<const wxString, BOM_FMT_PRESET>& aPair )
1921 {
1922 return ( aPair.second.fieldDelimiter == current.fieldDelimiter
1923 && aPair.second.stringDelimiter == current.stringDelimiter
1924 && aPair.second.refDelimiter == current.refDelimiter
1925 && aPair.second.refRangeDelimiter == current.refRangeDelimiter
1926 && aPair.second.keepTabs == current.keepTabs
1927 && aPair.second.keepLineBreaks == current.keepLineBreaks );
1928 } );
1929
1930 if( it != m_bomFmtPresets.end() )
1931 {
1932 // Select the right m_cbBomFmtPresets item.
1933 // but these items are translated if they are predefined items.
1934 bool do_translate = it->second.readOnly;
1935 wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
1936
1937 m_cbBomFmtPresets->SetStringSelection( text );
1938 }
1939 else
1940 {
1941 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 ); // separator
1942 }
1943
1944 m_currentBomFmtPreset = static_cast<BOM_FMT_PRESET*>(
1945 m_cbBomFmtPresets->GetClientData( m_cbBomFmtPresets->GetSelection() ) );
1946}
1947
1948
1950{
1951 // look at m_userBomFmtPresets to know if aName is a read only preset, or a user preset.
1952 // Read only presets have translated names in UI, so we have to use
1953 // a translated name in UI selection.
1954 // But for a user preset name we should search for aName (not translated)
1955 wxString ui_label = aName;
1956
1957 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
1958 {
1959 if( pair.first != aName )
1960 continue;
1961
1962 if( pair.second.readOnly == true )
1963 ui_label = wxGetTranslation( aName );
1964
1965 break;
1966 }
1967
1968 int idx = m_cbBomFmtPresets->FindString( ui_label );
1969
1970 if( idx >= 0 && m_cbBomFmtPresets->GetSelection() != idx )
1971 {
1972 m_cbBomFmtPresets->SetSelection( idx );
1974 static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( idx ) );
1975 }
1976 else if( idx < 0 )
1977 {
1978 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 ); // separator
1979 }
1980}
1981
1982
1984{
1985 int count = m_cbBomFmtPresets->GetCount();
1986 int index = m_cbBomFmtPresets->GetSelection();
1987
1988 auto resetSelection =
1989 [&]()
1990 {
1992 m_cbBomFmtPresets->SetStringSelection( m_currentBomFmtPreset->name );
1993 else
1994 m_cbBomFmtPresets->SetSelection( m_cbBomFmtPresets->GetCount() - 3 );
1995 };
1996
1997 if( index == count - 3 )
1998 {
1999 // Separator: reject the selection
2000 resetSelection();
2001 return;
2002 }
2003 else if( index == count - 2 )
2004 {
2005 // Save current state to new preset
2006 wxString name;
2007
2010
2011 wxTextEntryDialog dlg( this, _( "BOM preset name:" ), _( "Save BOM Preset" ), name );
2012
2013 if( dlg.ShowModal() != wxID_OK )
2014 {
2015 resetSelection();
2016 return;
2017 }
2018
2019 name = dlg.GetValue();
2020 bool exists = m_bomFmtPresets.count( name );
2021
2022 if( !exists )
2023 {
2025 m_bomFmtPresets[name].readOnly = false;
2026 m_bomFmtPresets[name].name = name;
2027 }
2028
2030
2031 if( !exists )
2032 {
2033 index = m_cbBomFmtPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
2034 }
2035 else if( preset->readOnly )
2036 {
2037 wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
2038 _( "Error" ), wxOK | wxICON_ERROR, this );
2039 resetSelection();
2040 return;
2041 }
2042 else
2043 {
2044 // Ask the user if they want to overwrite the existing preset
2045 if( !IsOK( this, _( "Overwrite existing preset?" ) ) )
2046 {
2047 resetSelection();
2048 return;
2049 }
2050
2051 *preset = GetCurrentBomFmtSettings();
2052 preset->name = name;
2053
2054 index = m_cbBomFmtPresets->FindString( name );
2055 m_bomFmtPresetMRU.Remove( name );
2056 }
2057
2058 m_currentBomFmtPreset = preset;
2059 m_cbBomFmtPresets->SetSelection( index );
2060 m_bomFmtPresetMRU.Insert( name, 0 );
2061
2062 return;
2063 }
2064 else if( index == count - 1 )
2065 {
2066 // Delete a preset
2067 wxArrayString headers;
2068 std::vector<wxArrayString> items;
2069
2070 headers.Add( _( "Presets" ) );
2071
2072 for( std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
2073 {
2074 if( !pair.second.readOnly )
2075 {
2076 wxArrayString item;
2077 item.Add( pair.first );
2078 items.emplace_back( item );
2079 }
2080 }
2081
2082 EDA_LIST_DIALOG dlg( this, _( "Delete Preset" ), headers, items );
2083 dlg.SetListLabel( _( "Select preset:" ) );
2084
2085 if( dlg.ShowModal() == wxID_OK )
2086 {
2087 wxString presetName = dlg.GetTextSelection();
2088 int idx = m_cbBomFmtPresets->FindString( presetName );
2089
2090 if( idx != wxNOT_FOUND )
2091 {
2092 m_bomFmtPresets.erase( presetName );
2093
2094 m_cbBomFmtPresets->Delete( idx );
2095 m_currentBomFmtPreset = nullptr;
2096
2097 m_bomFmtPresetMRU.Remove( presetName );
2098 }
2099 }
2100
2101 resetSelection();
2102 return;
2103 }
2104
2105 BOM_FMT_PRESET* preset =
2106 static_cast<BOM_FMT_PRESET*>( m_cbBomFmtPresets->GetClientData( index ) );
2107 m_currentBomFmtPreset = preset;
2108
2109 m_lastSelectedBomFmtPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
2110
2111 if( preset )
2112 {
2113 doApplyBomFmtPreset( *preset );
2115 m_currentBomFmtPreset = preset;
2116
2117 if( !m_currentBomFmtPreset->name.IsEmpty() )
2118 {
2119 m_bomFmtPresetMRU.Remove( preset->name );
2120 m_bomFmtPresetMRU.Insert( preset->name, 0 );
2121 }
2122 }
2123}
2124
2125
2127{
2128 m_textFieldDelimiter->ChangeValue( aPreset.fieldDelimiter );
2129 m_textStringDelimiter->ChangeValue( aPreset.stringDelimiter );
2130 m_textRefDelimiter->ChangeValue( aPreset.refDelimiter );
2131 m_textRefRangeDelimiter->ChangeValue( aPreset.refRangeDelimiter );
2132 m_checkKeepTabs->SetValue( aPreset.keepTabs );
2133 m_checkKeepLineBreaks->SetValue( aPreset.keepLineBreaks );
2134
2135
2136 // Refresh the preview if that's the current page
2137 if( m_nbPages->GetSelection() == 1 )
2139}
2140
2141
2143{
2144 bool modified = false;
2145
2146 // Save our BOM presets
2147 std::vector<BOM_PRESET> presets;
2148
2149 for( const std::pair<const wxString, BOM_PRESET>& pair : m_bomPresets )
2150 {
2151 if( !pair.second.readOnly )
2152 presets.emplace_back( pair.second );
2153 }
2154
2155 if( m_schSettings.m_BomPresets != presets )
2156 {
2157 modified = true;
2158 m_schSettings.m_BomPresets = presets;
2159 }
2160
2162 {
2163 modified = true;
2165 }
2166
2167
2168 // Save our BOM Format presets
2169 std::vector<BOM_FMT_PRESET> fmts;
2170
2171 for( const std::pair<const wxString, BOM_FMT_PRESET>& pair : m_bomFmtPresets )
2172 {
2173 if( !pair.second.readOnly )
2174 fmts.emplace_back( pair.second );
2175 }
2176
2177 if( m_schSettings.m_BomFmtPresets != fmts )
2178 {
2179 modified = true;
2181 }
2182
2184 {
2185 modified = true;
2187 }
2188
2189 if( modified )
2190 m_parent->OnModify();
2191}
2192
2193
2195 std::vector<SCH_ITEM*>& aSchItem )
2196{
2197 SCH_SHEET_LIST allSheets = m_parent->Schematic().GetSheets();
2198 SCH_REFERENCE_LIST allRefs;
2199
2200 allSheets.GetSymbols( allRefs );
2201
2202 for( SCH_ITEM* item : aSchItem )
2203 {
2204 if( item->Type() == SCH_SYMBOL_T )
2205 {
2206 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2207
2208 // Add all fields again in case this symbol has a new one
2209 for( SCH_FIELD& field : symbol->GetFields() )
2210 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2211
2212 m_dataModel->AddReferences( getSymbolReferences( symbol, allRefs ) );
2213 }
2214 else if( item->Type() == SCH_SHEET_T )
2215 {
2216 std::set<SCH_SYMBOL*> symbols;
2217 SCH_REFERENCE_LIST refs = getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) );
2218
2219 for( SCH_REFERENCE& ref : refs )
2220 symbols.insert( ref.GetSymbol() );
2221
2222 for( SCH_SYMBOL* symbol : symbols )
2223 {
2224 // Add all fields again in case this symbol has a new one
2225 for( SCH_FIELD& field : symbol->GetFields() )
2226 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2227 }
2228
2229 m_dataModel->AddReferences( refs );
2230 }
2231 }
2232
2236}
2237
2238
2240 std::vector<SCH_ITEM*>& aSchItem )
2241{
2242 for( SCH_ITEM* item : aSchItem )
2243 {
2244 if( item->Type() == SCH_SYMBOL_T )
2245 m_dataModel->RemoveSymbol( *static_cast<SCH_SYMBOL*>( item ) );
2246 else if( item->Type() == SCH_SHEET_T )
2248 getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) ) );
2249 }
2250
2254}
2255
2256
2258 std::vector<SCH_ITEM*>& aSchItem )
2259{
2260 SCH_SHEET_LIST allSheets = m_parent->Schematic().GetSheets();
2261 SCH_REFERENCE_LIST allRefs;
2262
2263 allSheets.GetSymbols( allRefs );
2264
2265 for( SCH_ITEM* item : aSchItem )
2266 {
2267 if( item->Type() == SCH_SYMBOL_T )
2268 {
2269 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
2270
2271 // Add all fields again in case this symbol has a new one
2272 for( SCH_FIELD& field : symbol->GetFields() )
2273 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2274
2275 m_dataModel->UpdateReferences( getSymbolReferences( symbol, allRefs ) );
2276 }
2277 else if( item->Type() == SCH_SHEET_T )
2278 {
2279 std::set<SCH_SYMBOL*> symbols;
2280 SCH_REFERENCE_LIST refs = getSheetSymbolReferences( *static_cast<SCH_SHEET*>( item ) );
2281
2282 for( SCH_REFERENCE& ref : refs )
2283 symbols.insert( ref.GetSymbol() );
2284
2285 for( SCH_SYMBOL* symbol : symbols )
2286 {
2287 // Add all fields again in case this symbol has a new one
2288 for( SCH_FIELD& field : symbol->GetFields() )
2289 AddField( field.GetCanonicalName(), field.GetName(), true, false, true );
2290 }
2291
2293 }
2294 }
2295
2299}
2300
2301
2303{
2304 m_dataModel->SetPath( aSch.CurrentSheet() );
2305
2306 if( m_dataModel->GetScope() != FIELDS_EDITOR_GRID_DATA_MODEL::SCOPE::SCOPE_ALL )
2307 {
2311 }
2312}
2313
2314
2316{
2317 m_grid->Connect(
2318 wxEVT_GRID_RANGE_SELECTED,
2319 wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected ),
2320 nullptr, this );
2321}
2322
2323
2325{
2326 m_grid->Disconnect(
2327 wxEVT_GRID_RANGE_SELECTED,
2328 wxGridRangeSelectEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected ),
2329 nullptr, this );
2330}
2331
2332
2335 SCH_REFERENCE_LIST& aCachedRefs )
2336{
2337 SCH_SHEET_LIST allSheets = m_parent->Schematic().GetSheets();
2338 SCH_REFERENCE_LIST symbolRefs;
2339
2340 for( size_t i = 0; i < aCachedRefs.GetCount(); i++ )
2341 {
2342 SCH_REFERENCE& ref = aCachedRefs[i];
2343
2344 if( ref.GetSymbol() == aSymbol )
2345 {
2346 ref.Split(); // Figures out if we are annotated or not
2347 symbolRefs.AddItem( ref );
2348 }
2349 }
2350
2351 return symbolRefs;
2352}
2353
2354
2356{
2357 SCH_SHEET_LIST allSheets = m_parent->Schematic().GetSheets();
2358 SCH_REFERENCE_LIST sheetRefs;
2359
2360 // We need to operate on all instances of the sheet
2361 for( const SCH_SHEET_INSTANCE& instance : aSheet.GetInstances() )
2362 {
2363 // For every sheet instance we need to get the current schematic sheet
2364 // instance that matches that particular sheet path from the root
2365 for( SCH_SHEET_PATH& basePath : allSheets )
2366 {
2367 if( basePath.Path() == instance.m_Path )
2368 {
2369 SCH_SHEET_PATH sheetPath = basePath;
2370 sheetPath.push_back( &aSheet );
2371
2372 // Create a list of all sheets in this path, starting with the path
2373 // of the sheet that we just deleted, then all of its subsheets
2374 SCH_SHEET_LIST subSheets;
2375 subSheets.push_back( sheetPath );
2376 allSheets.GetSheetsWithinPath( subSheets, sheetPath );
2377
2378 subSheets.GetSymbolsWithinPath( sheetRefs, sheetPath, false, false );
2379 break;
2380 }
2381 }
2382 }
2383
2384 for( SCH_REFERENCE& ref : sheetRefs )
2385 ref.Split();
2386
2387 return sheetRefs;
2388}
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:405
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: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:958
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: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.