KiCad PCB EDA Suite
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-2022 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 <base_units.h>
27#include <bitmaps.h>
28#include <symbol_library.h>
29#include <confirm.h>
30#include <eda_doc.h>
32#include <eeschema_settings.h>
33#include <general.h>
34#include <grid_tricks.h>
35#include <string_utils.h>
36#include <kiface_base.h>
37#include <sch_edit_frame.h>
38#include <sch_reference_list.h>
39#include <schematic.h>
41#include <kiplatform/ui.h>
44#include <widgets/wx_grid.h>
45#include <wx/ffile.h>
46#include <wx/grid.h>
47#include <wx/textdlg.h>
48#include <wx/filedlg.h>
50
51#define DISPLAY_NAME_COLUMN 0
52#define SHOW_FIELD_COLUMN 1
53#define GROUP_BY_COLUMN 2
54#define CANONICAL_NAME_COLUMN 3
55
56#define QUANTITY_COLUMN ( GetNumberCols() - 1 )
57
58#ifdef __WXMAC__
59#define COLUMN_MARGIN 5
60#else
61#define COLUMN_MARGIN 15
62#endif
63
64enum
65{
68};
69
70
72{
73public:
75 wxDataViewListCtrl* aFieldsCtrl ) :
76 GRID_TRICKS( aGrid ),
77 m_dlg( aParent ),
78 m_fieldsCtrl( aFieldsCtrl )
79 {}
80
81protected:
82 void showPopupMenu( wxMenu& menu ) override
83 {
84 if( m_grid->GetGridCursorCol() == FOOTPRINT_FIELD )
85 {
86 menu.Append( MYID_SELECT_FOOTPRINT, _( "Select Footprint..." ),
87 _( "Browse for footprint" ) );
88 menu.AppendSeparator();
89 }
90 else if( m_grid->GetGridCursorCol() == DATASHEET_FIELD )
91 {
92 menu.Append( MYID_SHOW_DATASHEET, _( "Show Datasheet" ),
93 _( "Show datasheet in browser" ) );
94 menu.AppendSeparator();
95 }
96
98 }
99
100 void doPopupSelection( wxCommandEvent& event ) override
101 {
102 if( event.GetId() == MYID_SELECT_FOOTPRINT )
103 {
104 // pick a footprint using the footprint picker.
105 wxString fpid = m_grid->GetCellValue( m_grid->GetGridCursorRow(),
108 m_dlg );
109
110 if( frame->ShowModal( &fpid, m_dlg ) )
111 m_grid->SetCellValue( m_grid->GetGridCursorRow(), FOOTPRINT_FIELD, fpid );
112
113 frame->Destroy();
114 }
115 else if (event.GetId() == MYID_SHOW_DATASHEET )
116 {
117 wxString datasheet_uri = m_grid->GetCellValue( m_grid->GetGridCursorRow(),
119 GetAssociatedDocument( m_dlg, datasheet_uri, &m_dlg->Prj(), m_dlg->Prj().SchSearchS() );
120 }
121 else
122 {
124 }
125
126 if( event.GetId() >= GRIDTRICKS_FIRST_SHOWHIDE && event.GetId() < GRIDTRICKS_LAST_ID )
127 {
128 if( !m_grid->IsColShown( REFERENCE_FIELD ) )
129 {
130 DisplayError( m_dlg, _( "The Reference column cannot be hidden." ) );
131
132 m_grid->ShowCol( REFERENCE_FIELD );
133 }
134
135 // Refresh Show checkboxes from grid columns
136 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); ++i )
137 m_fieldsCtrl->SetToggleValue( m_grid->IsColShown( i ), i, 1 );
138 }
139 }
140
142 wxDataViewListCtrl* m_fieldsCtrl;
143};
144
145
147{
154
155
157{
158 DATA_MODEL_ROW( const SCH_REFERENCE& aFirstReference, GROUP_TYPE aType )
159 {
160 m_Refs.push_back( aFirstReference );
161 m_Flag = aType;
162 }
163
165 std::vector<SCH_REFERENCE> m_Refs;
166};
167
168
169class FIELDS_EDITOR_GRID_DATA_MODEL : public wxGridTableBase
170{
171protected:
172 // The data model is fundamentally m_componentRefs X m_fieldNames.
173
177 std::vector<wxString> m_fieldNames;
180 std::vector<wxString> m_userAddedFields;
181
182 // However, the grid view can vary in two ways:
183 // 1) the componentRefs can be grouped into fewer rows
184 // 2) some columns can be hidden
185 //
186 // We handle (1) here (ie: a table row maps to a group, and the table is rebuilt
187 // when the groupings change), and we let the wxGrid handle (2) (ie: the number
188 // of columns is constant but are hidden/shown by the wxGrid control).
189
190 std::vector< DATA_MODEL_ROW > m_rows;
191
192 // Data store
193 // A map of compID : fieldSet, where fieldSet is a map of fieldName : fieldValue
194 std::map< KIID, std::map<wxString, wxString> > m_dataStore;
195
196public:
198 m_frame( aFrame ),
199 m_symbolsList( aSymbolsList ),
200 m_edited( false ),
201 m_sortColumn( 0 ),
202 m_sortAscending( false )
203 {
205 }
206
207 void AddColumn( const wxString& aFieldName, bool aAddedByUser )
208 {
209 m_fieldNames.push_back( aFieldName );
210
211 if( aAddedByUser )
212 m_userAddedFields.push_back( aFieldName );
213
214 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
215 {
216 SCH_SYMBOL* symbol = m_symbolsList[ i ].GetSymbol();
217
218 wxCHECK( symbol && ( symbol->GetInstanceReferences().size() != 0 ), /* void */ );
219
220 wxString val = symbol->GetFieldText( aFieldName );
221
222 if( aFieldName == wxT( "Value" ) )
223 val = symbol->GetValueFieldText( true );
224 else if( aFieldName == wxT( "Footprint" ) )
225 val = symbol->GetFootprintFieldText( true );
226
227 m_dataStore[ symbol->m_Uuid ][ aFieldName ] = val;
228 }
229 }
230
231 void RemoveColumn( int aCol )
232 {
233 wxString fieldName = m_fieldNames[ aCol ];
234
235 m_fieldNames.erase( m_fieldNames.begin() + aCol );
236
237 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
238 {
239 SCH_SYMBOL* symbol = m_symbolsList[ i ].GetSymbol();
240 m_dataStore[ symbol->m_Uuid ].erase( fieldName );
241 }
242 }
243
244 int GetNumberRows() override { return m_rows.size(); }
245
246 // Columns are fieldNames + quantity column
247 int GetNumberCols() override { return (int) m_fieldNames.size() + 1; }
248
249 wxString GetColLabelValue( int aCol ) override
250 {
251 if( aCol == QUANTITY_COLUMN )
252 return _( "Qty" );
253 else if( aCol < MANDATORY_FIELDS )
254 // FIX ME: the column label should be displayed translated.
255 // but when translated, and the DATASHEET column is shown, a new field
256 // with the translated DATASHEET field name is added when saving fields
257 // return TEMPLATE_FIELDNAME::GetDefaultFieldName( aCol, DO_TRANSLATE );
259 else
260 return m_fieldNames[ aCol ];
261 }
262
263 wxString GetCanonicalColLabel( int aCol )
264 {
265 if( aCol == QUANTITY_COLUMN )
266 return _( "Qty" );
267 else
268 return m_fieldNames[ aCol ];
269 }
270
271 bool IsEmptyCell( int aRow, int aCol ) override
272 {
273 return false; // don't allow adjacent cell overflow, even if we are actually empty
274 }
275
276 wxString GetValue( int aRow, int aCol ) override
277 {
278 if( aCol == REFERENCE_FIELD )
279 {
280 // Poor-man's tree controls
281 if( m_rows[ aRow ].m_Flag == GROUP_COLLAPSED )
282 return wxT( "> " ) + GetValue( m_rows[ aRow ], aCol );
283 else if (m_rows[ aRow ].m_Flag == GROUP_EXPANDED )
284 return wxT( "v " ) + GetValue( m_rows[ aRow ], aCol );
285 else if( m_rows[ aRow ].m_Flag == CHILD_ITEM )
286 return wxT( " " ) + GetValue( m_rows[ aRow ], aCol );
287 else
288 return wxT( " " ) + GetValue( m_rows[ aRow ], aCol );
289 }
290 else
291 {
292 return GetValue( m_rows[ aRow ], aCol );
293 }
294 }
295
296 wxString GetRawValue( int aRow, int aCol )
297 {
298 return GetValue( m_rows[ aRow ], aCol );
299 }
300
302 {
303 return m_rows[ aRow ].m_Flag;
304 }
305
306 std::vector<SCH_REFERENCE> GetRowReferences( int aRow ) const
307 {
308 wxCHECK( aRow < (int)m_rows.size(), std::vector<SCH_REFERENCE>() );
309 return m_rows[ aRow ].m_Refs;
310 }
311
312 wxString GetValue( const DATA_MODEL_ROW& group, int aCol )
313 {
314 std::vector<SCH_REFERENCE> references;
315 wxString fieldValue;
316
317 for( const SCH_REFERENCE& ref : group.m_Refs )
318 {
319 if( aCol == REFERENCE_FIELD || aCol == QUANTITY_COLUMN )
320 {
321 references.push_back( ref );
322 }
323 else // Other columns are either a single value or ROW_MULTI_ITEMS
324 {
325 const KIID& symbolID = ref.GetSymbol()->m_Uuid;
326
327 if( !m_dataStore.count( symbolID )
328 || !m_dataStore[ symbolID ].count( m_fieldNames[ aCol ] ) )
329 {
330 return INDETERMINATE_STATE;
331 }
332
333 if( &ref == &group.m_Refs.front() )
334 fieldValue = m_dataStore[ symbolID ][ m_fieldNames[ aCol ] ];
335 else if ( fieldValue != m_dataStore[ symbolID ][ m_fieldNames[ aCol ] ] )
336 return INDETERMINATE_STATE;
337 }
338 }
339
340 if( aCol == REFERENCE_FIELD || aCol == QUANTITY_COLUMN )
341 {
342 // Remove duplicates (other units of multi-unit parts)
343 std::sort( references.begin(), references.end(),
344 []( const SCH_REFERENCE& l, const SCH_REFERENCE& r ) -> bool
345 {
346 wxString l_ref( l.GetRef() << l.GetRefNumber() );
347 wxString r_ref( r.GetRef() << r.GetRefNumber() );
348 return StrNumCmp( l_ref, r_ref, true ) < 0;
349 } );
350
351 auto logicalEnd = std::unique( references.begin(), references.end(),
352 []( const SCH_REFERENCE& l, const SCH_REFERENCE& r ) -> bool
353 {
354 // If unannotated then we can't tell what units belong together
355 // so we have to leave them all
356 if( l.GetRefNumber() == wxT( "?" ) )
357 return false;
358
359 wxString l_ref( l.GetRef() << l.GetRefNumber() );
360 wxString r_ref( r.GetRef() << r.GetRefNumber() );
361 return l_ref == r_ref;
362 } );
363
364 references.erase( logicalEnd, references.end() );
365 }
366
367 if( aCol == REFERENCE_FIELD )
368 fieldValue = SCH_REFERENCE_LIST::Shorthand( references );
369 else if( aCol == QUANTITY_COLUMN )
370 fieldValue = wxString::Format( wxT( "%d" ), ( int )references.size() );
371
372 return fieldValue;
373 }
374
375 void SetValue( int aRow, int aCol, const wxString &aValue ) override
376 {
377 if( aCol == REFERENCE_FIELD || aCol == QUANTITY_COLUMN )
378 return; // Can't modify references or quantity
379
380 DATA_MODEL_ROW& rowGroup = m_rows[ aRow ];
381 wxString fieldName = m_fieldNames[ aCol ];
382
383 for( const SCH_REFERENCE& ref : rowGroup.m_Refs )
384 m_dataStore[ ref.GetSymbol()->m_Uuid ][ fieldName ] = aValue;
385
386 m_edited = true;
387 }
388
389 static bool cmp( const DATA_MODEL_ROW& lhGroup, const DATA_MODEL_ROW& rhGroup,
390 FIELDS_EDITOR_GRID_DATA_MODEL* dataModel, int sortCol, bool ascending )
391 {
392 // Empty rows always go to the bottom, whether ascending or descending
393 if( lhGroup.m_Refs.size() == 0 )
394 return true;
395 else if( rhGroup.m_Refs.size() == 0 )
396 return false;
397
398 // N.B. To meet the iterator sort conditions, we cannot simply invert the truth
399 // to get the opposite sort. i.e. ~(a<b) != (a>b)
400 auto local_cmp =
401 [ ascending ]( const auto a, const auto b )
402 {
403 if( ascending )
404 return a < b;
405 else
406 return a > b;
407 };
408
409 // Primary sort key is sortCol; secondary is always REFERENCE (column 0)
410
411 wxString lhs = dataModel->GetValue( (DATA_MODEL_ROW&) lhGroup, sortCol );
412 wxString rhs = dataModel->GetValue( (DATA_MODEL_ROW&) rhGroup, sortCol );
413
414 if( lhs == rhs || sortCol == REFERENCE_FIELD )
415 {
416 wxString lhRef = lhGroup.m_Refs[ 0 ].GetRef() + lhGroup.m_Refs[ 0 ].GetRefNumber();
417 wxString rhRef = rhGroup.m_Refs[ 0 ].GetRef() + rhGroup.m_Refs[ 0 ].GetRefNumber();
418 return local_cmp( StrNumCmp( lhRef, rhRef, true ), 0 );
419 }
420 else
421 {
422 return local_cmp( ValueStringCompare( lhs, rhs ), 0 );
423 }
424 }
425
426 void Sort( int aColumn, bool ascending )
427 {
428 if( aColumn < 0 )
429 aColumn = 0;
430
431 m_sortColumn = aColumn;
432 m_sortAscending = ascending;
433
435
436 // We're going to sort the rows based on their first reference, so the first reference
437 // had better be the lowest one.
438 for( DATA_MODEL_ROW& row : m_rows )
439 {
440 std::sort( row.m_Refs.begin(), row.m_Refs.end(),
441 []( const SCH_REFERENCE& lhs, const SCH_REFERENCE& rhs )
442 {
443 wxString lhs_ref( lhs.GetRef() << lhs.GetRefNumber() );
444 wxString rhs_ref( rhs.GetRef() << rhs.GetRefNumber() );
445 return StrNumCmp( lhs_ref, rhs_ref, true ) < 0;
446 } );
447 }
448
449 std::sort( m_rows.begin(), m_rows.end(),
450 [this]( const DATA_MODEL_ROW& lhs, const DATA_MODEL_ROW& rhs ) -> bool
451 {
452 return cmp( lhs, rhs, this, m_sortColumn, m_sortAscending );
453 } );
454
456 }
457
458 bool unitMatch( const SCH_REFERENCE& lhRef, const SCH_REFERENCE& rhRef )
459 {
460 // If items are unannotated then we can't tell if they're units of the same symbol or not
461 if( lhRef.GetRefNumber() == wxT( "?" ) )
462 return false;
463
464 return ( lhRef.GetRef() == rhRef.GetRef() && lhRef.GetRefNumber() == rhRef.GetRefNumber() );
465 }
466
467 bool groupMatch( const SCH_REFERENCE& lhRef, const SCH_REFERENCE& rhRef,
468 wxDataViewListCtrl* fieldsCtrl )
469 {
470 bool matchFound = false;
471
472 // First check the reference column. This can be done directly out of the
473 // SCH_REFERENCEs as the references can't be edited in the grid.
474 if( fieldsCtrl->GetToggleValue( REFERENCE_FIELD, GROUP_BY_COLUMN ) )
475 {
476 // if we're grouping by reference, then only the prefix must match
477 if( lhRef.GetRef() != rhRef.GetRef() )
478 return false;
479
480 matchFound = true;
481 }
482
483 const KIID& lhRefID = lhRef.GetSymbol()->m_Uuid;
484 const KIID& rhRefID = rhRef.GetSymbol()->m_Uuid;
485
486 // Now check all the other columns. This must be done out of the dataStore
487 // for the refresh button to work after editing.
488 for( int i = REFERENCE_FIELD + 1; i < fieldsCtrl->GetItemCount(); ++i )
489 {
490 if( !fieldsCtrl->GetToggleValue( i, GROUP_BY_COLUMN ) )
491 continue;
492
493 wxString fieldName = fieldsCtrl->GetTextValue( i, CANONICAL_NAME_COLUMN );
494
495 if( m_dataStore[ lhRefID ][ fieldName ] != m_dataStore[ rhRefID ][ fieldName ] )
496 return false;
497
498 matchFound = true;
499 }
500
501 return matchFound;
502 }
503
504 void RebuildRows( wxSearchCtrl* aFilter, wxCheckBox* aGroupSymbolsBox,
505 wxDataViewListCtrl* aFieldsCtrl )
506 {
507 if( GetView() )
508 {
509 // Commit any pending in-place edits before the row gets moved out from under
510 // the editor.
511 static_cast<WX_GRID*>( GetView() )->CommitPendingChanges( true );
512
513 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, m_rows.size() );
514 GetView()->ProcessTableMessage( msg );
515 }
516
517 m_rows.clear();
518
519 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
520 {
521 SCH_REFERENCE ref = m_symbolsList[ i ];
522
523 if( !aFilter->GetValue().IsEmpty()
524 && !WildCompareString( aFilter->GetValue(), ref.GetFullRef(), false ) )
525 {
526 continue;
527 }
528
529 bool matchFound = false;
530
531 // See if we already have a row which this symbol fits into
532 for( DATA_MODEL_ROW& row : m_rows )
533 {
534 // all group members must have identical refs so just use the first one
535 SCH_REFERENCE rowRef = row.m_Refs[ 0 ];
536
537 if( unitMatch( ref, rowRef ) )
538 {
539 matchFound = true;
540 row.m_Refs.push_back( ref );
541 break;
542 }
543 else if ( aGroupSymbolsBox->GetValue() && groupMatch( ref, rowRef, aFieldsCtrl ) )
544 {
545 matchFound = true;
546 row.m_Refs.push_back( ref );
547 row.m_Flag = GROUP_COLLAPSED;
548 break;
549 }
550 }
551
552 if( !matchFound )
553 m_rows.emplace_back( DATA_MODEL_ROW( ref, GROUP_SINGLETON ) );
554 }
555
556 if ( GetView() )
557 {
558 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_rows.size() );
559 GetView()->ProcessTableMessage( msg );
560 }
561 }
562
563 void ExpandRow( int aRow )
564 {
565 std::vector<DATA_MODEL_ROW> children;
566
567 for( SCH_REFERENCE& ref : m_rows[ aRow ].m_Refs )
568 {
569 bool matchFound = false;
570
571 // See if we already have a child group which this symbol fits into
572 for( DATA_MODEL_ROW& child : children )
573 {
574 // group members are by definition all matching, so just check
575 // against the first member
576 if( unitMatch( ref, child.m_Refs[ 0 ] ) )
577 {
578 matchFound = true;
579 child.m_Refs.push_back( ref );
580 break;
581 }
582 }
583
584 if( !matchFound )
585 children.emplace_back( DATA_MODEL_ROW( ref, CHILD_ITEM ) );
586 }
587
588 if( children.size() < 2 )
589 return;
590
591 std::sort( children.begin(), children.end(),
592 [ this ] ( const DATA_MODEL_ROW& lhs, const DATA_MODEL_ROW& rhs ) -> bool
593 {
594 return cmp( lhs, rhs, this, m_sortColumn, m_sortAscending );
595 } );
596
597 m_rows[ aRow ].m_Flag = GROUP_EXPANDED;
598 m_rows.insert( m_rows.begin() + aRow + 1, children.begin(), children.end() );
599
600 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, aRow, children.size() );
601 GetView()->ProcessTableMessage( msg );
602 }
603
604 void CollapseRow( int aRow )
605 {
606 auto firstChild = m_rows.begin() + aRow + 1;
607 auto afterLastChild = firstChild;
608 int deleted = 0;
609
610 while( afterLastChild != m_rows.end() && afterLastChild->m_Flag == CHILD_ITEM )
611 {
612 deleted++;
613 afterLastChild++;
614 }
615
616 m_rows[ aRow ].m_Flag = GROUP_COLLAPSED;
617 m_rows.erase( firstChild, afterLastChild );
618
619 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aRow + 1, deleted );
620 GetView()->ProcessTableMessage( msg );
621 }
622
623 void ExpandCollapseRow( int aRow )
624 {
625 DATA_MODEL_ROW& group = m_rows[ aRow ];
626
627 if( group.m_Flag == GROUP_COLLAPSED )
628 ExpandRow( aRow );
629 else if( group.m_Flag == GROUP_EXPANDED )
630 CollapseRow( aRow );
631 }
632
634 {
635 for( size_t i = 0; i < m_rows.size(); ++i )
636 {
637 if( m_rows[ i ].m_Flag == GROUP_EXPANDED )
638 {
639 CollapseRow( i );
641 }
642 }
643 }
644
646 {
647 for( size_t i = 0; i < m_rows.size(); ++i )
648 {
649 if( m_rows[ i ].m_Flag == GROUP_COLLAPSED_DURING_SORT )
650 ExpandRow( i );
651 }
652 }
653
655 {
656 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
657 {
658 SCH_SYMBOL& symbol = *m_symbolsList[ i ].GetSymbol();
659 SCH_SCREEN* screen = m_symbolsList[i].GetSheetPath().LastScreen();
660
661 m_frame->SaveCopyInUndoList( screen, &symbol, UNDO_REDO::CHANGED, true );
662
663 const std::map<wxString, wxString>& fieldStore = m_dataStore[symbol.m_Uuid];
664
665 for( const std::pair<wxString, wxString> srcData : fieldStore )
666 {
667 if( srcData.first == _( "Qty" ) )
668 continue;
669
670 const wxString& srcName = srcData.first;
671 const wxString& srcValue = srcData.second;
672 SCH_FIELD* destField = symbol.FindField( srcName );
673
674 // Add a not existing field if it has a value for this symbol
675 bool createField = !destField && !srcValue.IsEmpty();
676
677 if( alg::contains( m_userAddedFields, srcName ) )
678 createField = true;
679
680 if( createField )
681 {
682 const VECTOR2I symbolPos = symbol.GetPosition();
683 destField = symbol.AddField( SCH_FIELD( symbolPos, -1, &symbol, srcName ) );
684 }
685
686 if( !destField )
687 continue;
688
689 if( destField->GetId() == REFERENCE_FIELD )
690 {
691 // Reference is not editable from this dialog
692 }
693 else if( destField->GetId() == VALUE_FIELD )
694 {
695 // Value field cannot be empty
696 if( !srcValue.IsEmpty() )
697 symbol.SetValueFieldText( srcValue );
698 }
699 else if( destField->GetId() == FOOTPRINT_FIELD )
700 {
701 symbol.SetFootprintFieldText( srcValue );
702 }
703 else
704 {
705 destField->SetText( srcValue );
706 }
707 }
708
709 for( int ii = symbol.GetFields().size() - 1; ii >= MANDATORY_FIELDS; ii-- )
710 {
711 if( fieldStore.count( symbol.GetFields()[ii].GetName() ) == 0 )
712 symbol.GetFields().erase( symbol.GetFields().begin() + ii );
713 }
714 }
715
716 m_edited = false;
717 }
718
719 int GetDataWidth( int aCol )
720 {
721 int width = 0;
722
723 if( aCol == REFERENCE_FIELD )
724 {
725 for( int row = 0; row < GetNumberRows(); ++row )
726 width = std::max( width, KIUI::GetTextSize( GetValue( row, aCol ), GetView() ).x );
727 }
728 else
729 {
730 wxString column_label = GetColLabelValue( aCol ); // symbol fieldName or Qty string
731
732 for( unsigned symbolRef = 0; symbolRef < m_symbolsList.GetCount(); ++ symbolRef )
733 {
734 const KIID& symbolID = m_symbolsList[ symbolRef ].GetSymbol()->m_Uuid;
735 wxString text = m_dataStore[ symbolID ][ column_label ];
736
737 width = std::max( width, KIUI::GetTextSize( text, GetView() ).x );
738 }
739 }
740
741 return width;
742 }
743
744
745 bool IsEdited()
746 {
747 return m_edited;
748 }
749};
750
751
754 m_parent( parent )
755{
756 wxSize defaultDlgSize = ConvertDialogToPixels( wxSize( 600, 300 ) );
757 int nameColWidthMargin = 44;
758
759 // Get all symbols from the list of schematic sheets
761
765
766 m_fieldsCtrl->AppendTextColumn( _( "Field" ), wxDATAVIEW_CELL_INERT, 0, wxALIGN_LEFT, 0 );
767 m_fieldsCtrl->AppendToggleColumn( _( "Show" ), wxDATAVIEW_CELL_ACTIVATABLE, 0,
768 wxALIGN_CENTER, 0 );
769 m_fieldsCtrl->AppendToggleColumn( _( "Group By" ), wxDATAVIEW_CELL_ACTIVATABLE, 0,
770 wxALIGN_CENTER, 0 );
771
772 // GTK asserts if the number of columns doesn't match the data, but we still don't want
773 // to display the canonical names. So we'll insert a column for them, but keep it 0 width.
774 m_fieldsCtrl->AppendTextColumn( _( "Name" ), wxDATAVIEW_CELL_INERT, 0, wxALIGN_LEFT, 0 );
775
776 // SetWidth( wxCOL_WIDTH_AUTOSIZE ) fails here on GTK, so we calculate the title sizes and
777 // set the column widths ourselves.
778 wxDataViewColumn* column = m_fieldsCtrl->GetColumn( SHOW_FIELD_COLUMN );
779 m_showColWidth = KIUI::GetTextSize( column->GetTitle(), m_fieldsCtrl ).x + COLUMN_MARGIN;
780 column->SetMinWidth( m_showColWidth );
781
782 column = m_fieldsCtrl->GetColumn( GROUP_BY_COLUMN );
784 column->SetMinWidth( m_groupByColWidth );
785
786 // The fact that we're a list should keep the control from reserving space for the
787 // expander buttons... but it doesn't. Fix by forcing the indent to 0.
788 m_fieldsCtrl->SetIndent( 0 );
789
790 m_filter->SetDescriptiveText( _( "Filter" ) );
791
793
794 LoadFieldNames(); // loads rows into m_fieldsCtrl and columns into m_dataModel
795
796 // Now that the fields are loaded we can set the initial location of the splitter
797 // based on the list width. Again, SetWidth( wxCOL_WIDTH_AUTOSIZE ) fails us on GTK.
798 int nameColWidth = 0;
799
800 for( int row = 0; row < m_fieldsCtrl->GetItemCount(); ++row )
801 {
802 const wxString& fieldName = m_fieldsCtrl->GetTextValue( row, DISPLAY_NAME_COLUMN );
803 nameColWidth = std::max( nameColWidth, KIUI::GetTextSize( fieldName, m_fieldsCtrl ).x );
804 }
805
806 nameColWidth += nameColWidthMargin;
807
808 int fieldsMinWidth = nameColWidth + m_groupByColWidth + m_showColWidth;
809
810 m_fieldsCtrl->GetColumn( DISPLAY_NAME_COLUMN )->SetWidth( nameColWidth );
811
812 // This is used for data only. Don't show it to the user.
813 m_fieldsCtrl->GetColumn( CANONICAL_NAME_COLUMN )->SetHidden( true );
814
815 m_splitterMainWindow->SetMinimumPaneSize( fieldsMinWidth );
816 m_splitterMainWindow->SetSashPosition( fieldsMinWidth + 40 );
817
819 m_dataModel->Sort( 0, true );
820
821 m_grid->UseNativeColHeader( true );
822 m_grid->SetTable( m_dataModel, true );
823
824 // must be done after SetTable(), which appears to re-set it
825 m_grid->SetSelectionMode( wxGrid::wxGridSelectCells );
826
827 // sync m_grid's column visibilities to Show checkboxes in m_fieldsCtrl
828 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); ++i )
829 {
830 if( m_fieldsCtrl->GetToggleValue( i, 1 ) )
831 m_grid->ShowCol( i );
832 else
833 m_grid->HideCol( i );
834 }
835
836 // add Cut, Copy, and Paste to wxGrid
837 m_grid->PushEventHandler( new FIELDS_EDITOR_GRID_TRICKS( this, m_grid, m_fieldsCtrl ) );
838
839 // give a bit more room for comboboxes
840 m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
841
842 // set reference column attributes
843 wxGridCellAttr* attr = new wxGridCellAttr;
844 attr->SetReadOnly();
845 m_grid->SetColAttr( REFERENCE_FIELD, attr );
846
847 // set footprint column browse button
848 attr = new wxGridCellAttr;
849 attr->SetEditor( new GRID_CELL_FPID_EDITOR( this, wxEmptyString ) );
850 m_grid->SetColAttr( FOOTPRINT_FIELD, attr );
851
852 // set datasheet column viewer button
853 attr = new wxGridCellAttr;
854 attr->SetEditor( new GRID_CELL_URL_EDITOR( this, Prj().SchSearchS() ) );
855 m_grid->SetColAttr( DATASHEET_FIELD, attr );
856
857 // set quantities column attributes
858 attr = new wxGridCellAttr;
859 attr->SetReadOnly();
860 m_grid->SetColAttr( m_dataModel->GetColsCount() - 1, attr );
861 m_grid->SetColFormatNumber( m_dataModel->GetColsCount() - 1 );
862 m_grid->AutoSizeColumns( false );
863
864 for( int col = 0; col < m_grid->GetNumberCols(); ++col )
865 {
866 // Columns are hidden by setting their width to 0 so if we resize them they will
867 // become unhidden.
868 if( m_grid->IsColShown( col ) )
869 {
870 EESCHEMA_SETTINGS* cfg = static_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
871 std::string key( m_dataModel->GetCanonicalColLabel( col ).ToUTF8() );
872
873 if( cfg->m_FieldEditorPanel.column_widths.count( key ) )
874 {
875 int width = cfg->m_FieldEditorPanel.column_widths.at( key );
876 m_grid->SetColSize( col, width );
877 }
878 else
879 {
880 int textWidth = m_dataModel->GetDataWidth( col ) + COLUMN_MARGIN;
881 int maxWidth = defaultDlgSize.x / 3;
882
883 if( col == m_grid->GetNumberCols() - 1 )
884 m_grid->SetColSize( col, std::min( std::max( 50, textWidth ), maxWidth ) );
885 else
886 m_grid->SetColSize( col, std::min( std::max( 100, textWidth ), maxWidth ) );
887 }
888 }
889 }
890
891 m_grid->SelectRow( 0 );
892 m_grid->SetGridCursor( 0, 1 );
894
896
898 SetSize( defaultDlgSize );
899 Center();
900
901 // Connect Events
902 m_grid->Connect( wxEVT_GRID_COL_SORT,
903 wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColSort ), nullptr, this );
904}
905
906
908{
909 // Disconnect Events
910 m_grid->Disconnect( wxEVT_GRID_COL_SORT,
911 wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColSort ), nullptr,
912 this );
913
914 // Delete the GRID_TRICKS.
915 m_grid->PopEventHandler( true );
916
917 // we gave ownership of m_dataModel to the wxGrid...
918}
919
920
922{
923 if( !wxDialog::TransferDataFromWindow() )
924 return false;
925
927 EE_SELECTION_TOOL* selectionTool = toolMgr->GetTool<EE_SELECTION_TOOL>();
928 EE_SELECTION& selection = selectionTool->GetSelection();
929 SCH_SYMBOL* symbol = nullptr;
930
931 if( selection.GetSize() == 1 )
932 {
933 EDA_ITEM* item = selection.Front();
934
935 if( item->Type() == SCH_SYMBOL_T )
936 symbol = (SCH_SYMBOL*) item;
937 else if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T )
938 symbol = (SCH_SYMBOL*) item->GetParent();
939 }
940
941 if( symbol )
942 {
943 for( int row = 0; row < m_dataModel->GetNumberRows(); ++row )
944 {
945 std::vector<SCH_REFERENCE> references = m_dataModel->GetRowReferences( row );
946 bool found = false;
947
948 for( const SCH_REFERENCE& ref : references )
949 {
950 if( ref.GetSymbol() == symbol )
951 {
952 found = true;
953 break;
954 }
955 }
956
957 if( found )
958 {
959 m_grid->GoToCell( row, 1 );
960 break;
961 }
962 }
963 }
964
965 return true;
966}
967
968
970{
972 return false;
973
974 if( !wxDialog::TransferDataFromWindow() )
975 return false;
976
977 SCH_SHEET_PATH currentSheet = m_parent->GetCurrentSheet();
978
980
981 // Reset the view to where we left the user
982 m_parent->SetCurrentSheet( currentSheet );
984 m_parent->Refresh();
985
987
988 return true;
989}
990
991
992void DIALOG_SYMBOL_FIELDS_TABLE::AddField( const wxString& aDisplayName,
993 const wxString& aCanonicalName,
994 bool defaultShow, bool defaultSortBy, bool addedByUser )
995{
996 m_dataModel->AddColumn( aCanonicalName, addedByUser );
997
998 wxVector<wxVariant> fieldsCtrlRow;
999
1000 EESCHEMA_SETTINGS* cfg = static_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
1001 bool show = defaultShow;
1002 bool sort_by = defaultSortBy;
1003
1004 std::string key( aCanonicalName.ToUTF8() );
1005
1006 if( cfg->m_FieldEditorPanel.fields_show.count( key ) )
1007 show = cfg->m_FieldEditorPanel.fields_show.at( key );
1008
1009 if( cfg->m_FieldEditorPanel.fields_group_by.count( key ) )
1010 sort_by = cfg->m_FieldEditorPanel.fields_group_by.at( key );
1011
1012 // Don't change these to emplace_back: some versions of wxWidgets don't support it
1013 fieldsCtrlRow.push_back( wxVariant( aDisplayName ) );
1014 fieldsCtrlRow.push_back( wxVariant( show ) );
1015 fieldsCtrlRow.push_back( wxVariant( sort_by ) );
1016 fieldsCtrlRow.push_back( wxVariant( aCanonicalName ) );
1017
1018 m_fieldsCtrl->AppendItem( fieldsCtrlRow );
1019}
1020
1021
1023{
1024 std::set<wxString> userFieldNames;
1025
1026 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
1027 {
1028 SCH_SYMBOL* symbol = m_symbolsList[ i ].GetSymbol();
1029
1030 for( int j = MANDATORY_FIELDS; j < symbol->GetFieldCount(); ++j )
1031 userFieldNames.insert( symbol->GetFields()[j].GetName() );
1032 }
1033
1034 // Force References to always be shown
1035 EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
1036 wxCHECK( cfg, /*void*/ );
1037
1038 cfg->m_FieldEditorPanel.fields_show["Reference"] = true;
1039
1040 AddField( _( "Reference" ), wxT( "Reference" ), true, true );
1041 AddField( _( "Value" ), wxT( "Value" ), true, true );
1042 AddField( _( "Footprint" ), wxT( "Footprint" ), true, true );
1043 AddField( _( "Datasheet" ), wxT( "Datasheet" ), true, false );
1044
1045 for( const wxString& fieldName : userFieldNames )
1046 AddField( fieldName, fieldName, true, false );
1047
1048 // Add any templateFieldNames which aren't already present in the userFieldNames
1049 for( const TEMPLATE_FIELDNAME& templateFieldname :
1051 {
1052 if( userFieldNames.count( templateFieldname.m_Name ) == 0 )
1053 AddField( templateFieldname.m_Name, templateFieldname.m_Name, false, false );
1054 }
1055}
1056
1057
1058void DIALOG_SYMBOL_FIELDS_TABLE::OnAddField( wxCommandEvent& event )
1059{
1060 // quantities column will become new field column, so it needs to be reset
1061 wxGridCellAttr* attr = new wxGridCellAttr;
1062 m_grid->SetColAttr( m_dataModel->GetColsCount() - 1, attr );
1063 m_grid->SetColFormatCustom( m_dataModel->GetColsCount() - 1, wxGRID_VALUE_STRING );
1064
1065 wxTextEntryDialog dlg( this, _( "New field name:" ), _( "Add Field" ) );
1066
1067 if( dlg.ShowModal() != wxID_OK )
1068 return;
1069
1070 wxString fieldName = dlg.GetValue();
1071
1072 if( fieldName.IsEmpty() )
1073 {
1074 DisplayError( this, _( "Field must have a name." ) );
1075 return;
1076 }
1077
1078 for( int i = 0; i < m_dataModel->GetNumberCols(); ++i )
1079 {
1080 if( fieldName == m_dataModel->GetColLabelValue( i ) )
1081 {
1082 DisplayError( this, wxString::Format( _( "Field name '%s' already in use." ),
1083 fieldName ) );
1084 return;
1085 }
1086 }
1087
1088 std::string key( fieldName.ToUTF8() );
1089
1090 EESCHEMA_SETTINGS* cfg = static_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
1091 cfg->m_FieldEditorPanel.fields_show[key] = true;
1092
1093 AddField( fieldName, fieldName, true, false, true );
1094
1095 wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_INSERTED,
1096 m_fieldsCtrl->GetItemCount(), 1 );
1097 m_grid->ProcessTableMessage( msg );
1098
1099 // set up attributes on the new quantities column
1100 attr = new wxGridCellAttr;
1101 attr->SetReadOnly();
1102 m_grid->SetColAttr( m_dataModel->GetColsCount() - 1, attr );
1103 m_grid->SetColFormatNumber( m_dataModel->GetColsCount() - 1 );
1104 m_grid->SetColSize( m_dataModel->GetColsCount() - 1, 50 );
1105}
1106
1107
1109{
1110 int col = -1;
1111 int row = m_fieldsCtrl->GetSelectedRow();
1112
1113 // Should never occur: "Remove Field..." button should be disabled if invalid selection
1114 // via OnFieldsCtrlSelectionChanged()
1115 wxCHECK_RET( row != -1, "Some user defined field must be selected first" );
1116 wxCHECK_RET( row >= MANDATORY_FIELDS, "Mandatory fields cannot be removed" );
1117
1118 wxString fieldName = m_fieldsCtrl->GetTextValue( row, 0 );
1119
1120 wxString confirm_msg = wxString::Format( _( "Are you sure you want to remove the field '%s'?" ),
1121 fieldName );
1122
1123 if( !IsOK( this, confirm_msg ) )
1124 return;
1125
1126 for( int i = 0; i < m_dataModel->GetNumberCols(); ++i )
1127 {
1128 if( fieldName == m_dataModel->GetColLabelValue( i ) )
1129 col = i;
1130 }
1131
1132 m_fieldsCtrl->DeleteItem( row );
1133 m_dataModel->RemoveColumn( col );
1134
1135 // Make selection and update the state of "Remove field..." button via OnFieldsCtrlSelectionChanged()
1136 // Safe to decrement row index because we always have mandatory fields
1137 m_fieldsCtrl->SelectRow( --row );
1138
1139 if( row < MANDATORY_FIELDS )
1140 m_removeFieldButton->Enable( false );
1141
1142 wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_DELETED,
1143 m_fieldsCtrl->GetItemCount(), 1 );
1144
1145 m_grid->ProcessTableMessage( msg );
1146
1147 // set up attributes on the new quantities column
1148 wxGridCellAttr* attr = new wxGridCellAttr;
1149 attr->SetReadOnly();
1150
1151 m_grid->SetColAttr( m_dataModel->GetColsCount() - 1, attr );
1152 m_grid->SetColFormatNumber( m_dataModel->GetColsCount() - 1 );
1153 m_grid->SetColSize( m_dataModel->GetColsCount() - 1, 50 );
1154}
1155
1156
1157void DIALOG_SYMBOL_FIELDS_TABLE::OnFilterText( wxCommandEvent& aEvent )
1158{
1160 m_dataModel->Sort( m_grid->GetSortingColumn(), m_grid->IsSortOrderAscending() );
1161 m_grid->ForceRefresh();
1162}
1163
1164
1166{
1167 wxPoint pos = aEvent.GetPosition();
1168 wxRect ctrlRect = m_filter->GetScreenRect();
1169 int buttonWidth = ctrlRect.GetHeight(); // Presume buttons are square
1170
1171 if( m_filter->IsSearchButtonVisible() && pos.x < buttonWidth )
1172 SetCursor( wxCURSOR_ARROW );
1173 else if( m_filter->IsCancelButtonVisible() && pos.x > ctrlRect.GetWidth() - buttonWidth )
1174 SetCursor( wxCURSOR_ARROW );
1175 else
1176 SetCursor( wxCURSOR_IBEAM );
1177}
1178
1180{
1181 int row = m_fieldsCtrl->GetSelectedRow();
1182
1183 if( row >= MANDATORY_FIELDS )
1184 m_removeFieldButton->Enable( true );
1185 else
1186 m_removeFieldButton->Enable( false );
1187}
1188
1190{
1191 EESCHEMA_SETTINGS* cfg = static_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
1192 wxDataViewItem item = event.GetItem();
1193
1194 int row = m_fieldsCtrl->ItemToRow( item );
1195 int col = event.GetColumn();
1196
1197 switch ( col )
1198 {
1199 case SHOW_FIELD_COLUMN:
1200 {
1201 bool value = m_fieldsCtrl->GetToggleValue( row, col );
1202
1203 if( row == REFERENCE_FIELD && !value )
1204 {
1205 DisplayError( this, _( "The Reference column cannot be hidden." ) );
1206
1207 value = true;
1208 m_fieldsCtrl->SetToggleValue( value, row, col );
1209 }
1210
1211 std::string fieldName( m_fieldsCtrl->GetTextValue( row, CANONICAL_NAME_COLUMN ).ToUTF8() );
1212 cfg->m_FieldEditorPanel.fields_show[fieldName] = value;
1213
1214 if( value )
1215 m_grid->ShowCol( row );
1216 else
1217 m_grid->HideCol( row ); // grid's columns map to fieldsCtrl's rows
1218
1219 break;
1220 }
1221
1222 case GROUP_BY_COLUMN:
1223 {
1224 bool value = m_fieldsCtrl->GetToggleValue( row, col );
1225 std::string fieldName( m_fieldsCtrl->GetTextValue( row, CANONICAL_NAME_COLUMN ).ToUTF8() );
1226 cfg->m_FieldEditorPanel.fields_group_by[fieldName] = value;
1227
1229 m_dataModel->Sort( m_grid->GetSortingColumn(), m_grid->IsSortOrderAscending() );
1230 m_grid->ForceRefresh();
1231 break;
1232 }
1233
1234 default:
1235 break;
1236 }
1237}
1238
1239
1241{
1243 m_dataModel->Sort( m_grid->GetSortingColumn(), m_grid->IsSortOrderAscending() );
1244 m_grid->ForceRefresh();
1245}
1246
1247
1248void DIALOG_SYMBOL_FIELDS_TABLE::OnColSort( wxGridEvent& aEvent )
1249{
1250 int sortCol = aEvent.GetCol();
1251 bool ascending;
1252
1253 // This is bonkers, but wxWidgets doesn't tell us ascending/descending in the event, and
1254 // if we ask it will give us pre-event info.
1255 if( m_grid->IsSortingBy( sortCol ) )
1256 {
1257 // same column; invert ascending
1258 ascending = !m_grid->IsSortOrderAscending();
1259 }
1260 else
1261 {
1262 // different column; start with ascending
1263 ascending = true;
1264 }
1265
1266 m_dataModel->Sort( sortCol, ascending );
1267 m_grid->ForceRefresh();
1268}
1269
1270
1272{
1273 m_grid->ForceRefresh();
1274}
1275
1276
1277void DIALOG_SYMBOL_FIELDS_TABLE::OnTableColSize( wxGridSizeEvent& aEvent )
1278{
1279 EESCHEMA_SETTINGS* cfg = static_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
1280 int col = aEvent.GetRowOrCol();
1281 std::string key( m_dataModel->GetCanonicalColLabel( col ).ToUTF8() );
1282
1283 if( m_grid->GetColSize( col ) )
1284 cfg->m_FieldEditorPanel.column_widths[ key ] = m_grid->GetColSize( col );
1285
1286 aEvent.Skip();
1287}
1288
1289
1291{
1293 m_dataModel->Sort( m_grid->GetSortingColumn(), m_grid->IsSortOrderAscending() );
1294 m_grid->ForceRefresh();
1295}
1296
1297
1299{
1300 if( event.GetCol() == REFERENCE_FIELD )
1301 {
1302 m_grid->ClearSelection();
1303 m_grid->SetGridCursor( event.GetRow(), event.GetCol() );
1304
1305 int flag = m_dataModel->GetRowFlags( event.GetRow() );
1306 m_dataModel->ExpandCollapseRow( event.GetRow() );
1307 std::vector<SCH_REFERENCE> refs = m_dataModel->GetRowReferences( event.GetRow() );
1308
1309 // Focus Eeschema view on the symbol selected in the dialog
1310 // TODO: Highlight or select more than one unit
1311 if( ( flag == GROUP_SINGLETON || flag == CHILD_ITEM ) && refs.size() >= 1 )
1312 {
1314
1315 std::sort( refs.begin(), refs.end(),
1316 []( const SCH_REFERENCE& a, const SCH_REFERENCE& b )
1317 {
1318 return a.GetUnit() < b.GetUnit();
1319 } );
1320
1321 // search and highlight the symbol found by its full path.
1322 // It allows select of not yet annotated or duplicaded symbols
1323 wxString symbol_path = refs[0].GetFullPath();
1324 // wxString reference = refs[0].GetRef() + refs[0].GetRefNumber(); // Not used
1325 editor->FindSymbolAndItem( &symbol_path, nullptr, true, HIGHLIGHT_SYMBOL, wxEmptyString );
1326 }
1327 }
1328 else
1329 {
1330 event.Skip();
1331 }
1332}
1333
1334
1336{
1337 // TODO: Option to select footprint if FOOTPRINT column selected
1338
1339 event.Skip();
1340}
1341
1342
1344{
1345 int nameColWidth = KIPLATFORM::UI::GetUnobscuredSize( m_fieldsCtrl ).x;
1346 nameColWidth -= m_showColWidth + m_groupByColWidth;
1347#ifdef __WXMAC__
1348 // TODO: something in wxWidgets 3.1.x pads checkbox columns with extra space. (It used to
1349 // also be that the width of the column would get set too wide (to 30), but that's patched in
1350 // our local wxWidgets fork.)
1351 nameColWidth -= 30;
1352#endif
1353
1354 // GTK loses its head and messes these up when resizing the splitter bar:
1355 m_fieldsCtrl->GetColumn( SHOW_FIELD_COLUMN )->SetWidth( m_showColWidth );
1356 m_fieldsCtrl->GetColumn( GROUP_BY_COLUMN )->SetWidth( m_groupByColWidth );
1357
1358 m_fieldsCtrl->GetColumn( CANONICAL_NAME_COLUMN )->SetHidden( true );
1359 m_fieldsCtrl->GetColumn( DISPLAY_NAME_COLUMN )->SetWidth( nameColWidth );
1360
1361 m_fieldsCtrl->Refresh(); // To refresh checkboxes on Windows.
1362
1363 event.Skip();
1364}
1365
1366
1368{
1371}
1372
1373
1374void DIALOG_SYMBOL_FIELDS_TABLE::OnExport( wxCommandEvent& aEvent )
1375{
1376 int last_col = m_grid->GetNumberCols() - 1;
1377
1378 if( m_dataModel->IsEdited() )
1379 if( OKOrCancelDialog( nullptr, _( "Unsaved data" ),
1380 _( "Changes are unsaved. Export unsaved data?" ), "", _( "OK" ),
1381 _( "Cancel" ) )
1382 == wxID_CANCEL )
1383 return;
1384
1385
1386 // Calculate the netlist filename
1387 wxFileName fn = m_parent->Schematic().GetFileName();
1388 fn.SetExt( CsvFileExtension );
1389
1390 wxFileDialog saveDlg( this, _( "Save as CSV" ), wxPathOnly( Prj().GetProjectFullName() ),
1391 fn.GetFullName(), CsvFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1392
1393 if( saveDlg.ShowModal() == wxID_CANCEL )
1394 return;
1395
1396 wxFFile out( saveDlg.GetPath(), "wb" );
1397
1398 if( !out.IsOpened() )
1399 return;
1400
1401 // Find the location for the line terminator
1402 for( int col = m_grid->GetNumberCols() - 1; col >=0 ; --col )
1403 {
1404 if( m_grid->IsColShown( col ) )
1405 {
1406 last_col = col;
1407 break;
1408 }
1409 }
1410
1411 // Column names
1412 for( int col = 0; col < m_grid->GetNumberCols(); col++ )
1413 {
1414 if( !m_grid->IsColShown( col ) )
1415 continue;
1416
1417 wxString escapedValue = m_grid->GetColLabelValue( col );
1418 escapedValue.Replace( "\"", "\"\"" );
1419
1420 wxString format = col == last_col ? "\"%s\"\r\n" : "\"%s\",";
1421
1422 out.Write( wxString::Format( format, escapedValue ) );
1423 }
1424
1425 // Data rows
1426 for( int row = 0; row < m_grid->GetNumberRows(); row++ )
1427 {
1428 // Don't output child rows
1429 if( m_dataModel->GetRowFlags( row ) == CHILD_ITEM )
1430 continue;
1431
1432 for( int col = 0; col < m_grid->GetNumberCols(); col++ )
1433 {
1434 if( !m_grid->IsColShown( col ) )
1435 continue;
1436
1437 // Get the unanottated version of the field, e.g. no "> " or "v " by
1438 wxString escapedValue = m_dataModel->GetRawValue( row, col );
1439 escapedValue.Replace( "\"", "\"\"" );
1440
1441 wxString format = col == last_col ? "\"%s\"\r\n" : "\"%s\",";
1442
1443 out.Write( wxString::Format( format, escapedValue ) );
1444 }
1445 }
1446}
1447
1448
1449void DIALOG_SYMBOL_FIELDS_TABLE::OnCancel( wxCommandEvent& event )
1450{
1451 Close();
1452}
1453
1454
1455void DIALOG_SYMBOL_FIELDS_TABLE::OnClose( wxCloseEvent& event )
1456{
1457 // This is a cancel, so commit quietly as we're going to throw the results away anyway.
1459
1460 if( m_dataModel->IsEdited() )
1461 {
1462 if( !HandleUnsavedChanges( this, _( "Save changes?" ),
1463 [&]() -> bool
1464 {
1465 return TransferDataFromWindow();
1466 } ) )
1467 {
1468 event.Veto();
1469 return;
1470 }
1471 }
1472
1473 event.Skip();
1474}
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:105
@ small_refresh
void SetIsSeparator()
Render button as a toolbar separator.
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition: dialog_shim.h:83
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition: dialog_shim.h:97
void SetupStandardButtons(std::map< int, wxString > aLabels={})
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 OnAddField(wxCommandEvent &event) override
void OnGroupSymbolsToggled(wxCommandEvent &event) override
void OnColumnItemToggled(wxDataViewEvent &event) override
FIELDS_EDITOR_GRID_DATA_MODEL * m_dataModel
void OnTableItemContextMenu(wxGridEvent &event) override
void OnFilterText(wxCommandEvent &aEvent) override
void AddField(const wxString &displayName, const wxString &aCanonicalName, bool defaultShow, bool defaultSortBy, bool addedByUser=false)
void OnRemoveField(wxCommandEvent &event) override
void OnTableCellClick(wxGridEvent &event) override
DIALOG_SYMBOL_FIELDS_TABLE(SCH_EDIT_FRAME *parent)
void OnTableValueChanged(wxGridEvent &event) override
void OnExport(wxCommandEvent &aEvent) override
void OnClose(wxCloseEvent &aEvent) override
void OnFieldsCtrlSelectionChanged(wxDataViewEvent &event) override
void OnCancel(wxCommandEvent &aEvent) override
void OnFilterMouseMoved(wxMouseEvent &event) override
void OnSizeFieldList(wxSizeEvent &event) override
void OnRegroupSymbols(wxCommandEvent &aEvent) override
void LoadFieldNames()
Construct the rows of m_fieldsCtrl and the columns of m_dataModel from a union of all field names in ...
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
const KIID m_Uuid
Definition: eda_item.h:494
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
EDA_ITEM * GetParent() const
Definition: eda_item.h:99
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:163
PANEL_FIELD_EDITOR m_FieldEditorPanel
EE_SELECTION & GetSelection()
void AddColumn(const wxString &aFieldName, bool aAddedByUser)
std::vector< SCH_REFERENCE > GetRowReferences(int aRow) const
wxString GetColLabelValue(int aCol) override
bool unitMatch(const SCH_REFERENCE &lhRef, const SCH_REFERENCE &rhRef)
bool IsEmptyCell(int aRow, int aCol) override
void Sort(int aColumn, bool ascending)
bool groupMatch(const SCH_REFERENCE &lhRef, const SCH_REFERENCE &rhRef, wxDataViewListCtrl *fieldsCtrl)
std::map< KIID, std::map< wxString, wxString > > m_dataStore
void RebuildRows(wxSearchCtrl *aFilter, wxCheckBox *aGroupSymbolsBox, wxDataViewListCtrl *aFieldsCtrl)
wxString GetValue(int aRow, int aCol) override
wxString GetRawValue(int aRow, int aCol)
FIELDS_EDITOR_GRID_DATA_MODEL(SCH_EDIT_FRAME *aFrame, SCH_REFERENCE_LIST &aSymbolsList)
static bool cmp(const DATA_MODEL_ROW &lhGroup, const DATA_MODEL_ROW &rhGroup, FIELDS_EDITOR_GRID_DATA_MODEL *dataModel, int sortCol, bool ascending)
void SetValue(int aRow, int aCol, const wxString &aValue) override
std::vector< DATA_MODEL_ROW > m_rows
wxString GetValue(const DATA_MODEL_ROW &group, int aCol)
FIELDS_EDITOR_GRID_TRICKS(DIALOG_SHIM *aParent, WX_GRID *aGrid, wxDataViewListCtrl *aFieldsCtrl)
void doPopupSelection(wxCommandEvent &event) override
void showPopupMenu(wxMenu &menu) override
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)
WX_GRID * m_grid
I don't own the grid, but he owns me.
Definition: grid_tricks.h:121
virtual void showPopupMenu(wxMenu &menu)
APP_SETTINGS_BASE * KifaceSettings() const
Definition: kiface_base.h:93
Definition: kiid.h:48
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Definition: kiway_holder.h:53
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Definition: kiway_player.h:66
virtual bool ShowModal(wxString *aResult=nullptr, wxWindow *aResultantFocusWindow=nullptr)
Show this wxFrame as if it were a modal dialog, with all other instantiated wxFrames disabled until t...
bool Destroy() override
Our version of Destroy() which is virtual from wxWidgets.
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:394
TEMPLATES m_TemplateFieldNames
wxString GetFileName() const override
Helper to retrieve the filename from the root sheet screen.
Definition: schematic.cpp:166
SCHEMATIC_SETTINGS & Settings() const
Definition: schematic.cpp:172
SCH_SHEET_LIST GetSheets() const override
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:86
void SyncView()
Mark all items for refresh.
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...
void SaveCopyInUndoList(SCH_SCREEN *aScreen, SCH_ITEM *aItemToCopy, UNDO_REDO aTypeCommand, bool aAppend, bool aDirtyConnectivity=true)
Create a copy of the current schematic item, and put it in the undo list.
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.
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:50
int GetId() const
Definition: sch_field.h:116
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
static wxString Shorthand(std::vector< SCH_REFERENCE > aList)
Return a shorthand string representing all the references in the list.
size_t GetCount() const
void SplitReferences()
Attempt to split all reference designators into a name (U) and number (1).
A helper to define a symbol's reference designator in a schematic.
wxString GetFullRef() const
SCH_SYMBOL * GetSymbol() const
wxString GetRef() const
wxString GetRefNumber() const
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.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Schematic symbol object.
Definition: sch_symbol.h:80
const std::vector< SYMBOL_INSTANCE_REFERENCE > & GetInstanceReferences()
Definition: sch_symbol.h:139
int GetFieldCount() const
Return the number of fields in this symbol.
Definition: sch_symbol.h:478
SCH_FIELD * FindField(const wxString &aFieldName, bool aIncludeDefaultFields=true)
Search for a SCH_FIELD with aFieldName.
Definition: sch_symbol.cpp:861
void SetValueFieldText(const wxString &aValue)
Definition: sch_symbol.cpp:767
const wxString GetFootprintFieldText(bool aResolve) const
Definition: sch_symbol.cpp:773
void SetFootprintFieldText(const wxString &aFootprint)
Definition: sch_symbol.cpp:782
VECTOR2I GetPosition() const override
Definition: sch_symbol.h:697
const wxString GetValueFieldText(bool aResolve) const
Definition: sch_symbol.cpp:758
SCH_FIELD * AddField(const SCH_FIELD &aField)
Add a field to the symbol.
Definition: sch_symbol.cpp:839
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:824
wxString GetFieldText(const wxString &aFieldName) const
Search for a field named aFieldName and returns text associated with this field.
Definition: sch_symbol.cpp:812
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:54
Master controller class:
Definition: tool_manager.h:55
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:95
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:280
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:342
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:280
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.
@ GROUP_COLLAPSED_DURING_SORT
#define DISPLAY_NAME_COLUMN
#define QUANTITY_COLUMN
#define COLUMN_MARGIN
#define GROUP_BY_COLUMN
#define CANONICAL_NAME_COLUMN
#define SHOW_FIELD_COLUMN
#define _(s)
bool GetAssociatedDocument(wxWindow *aParent, const wxString &aDocName, PROJECT *aProject, SEARCH_STACK *aPaths)
Open a document (file) with the suitable browser.
Definition: eda_doc.cpp:74
This file is part of the common library.
E_SERIE r
Definition: eserie.cpp:41
@ FRAME_FOOTPRINT_VIEWER_MODAL
Definition: frame_type.h:43
@ GRIDTRICKS_FIRST_SHOWHIDE
Definition: grid_tricks.h:51
@ GRIDTRICKS_LAST_ID
Definition: grid_tricks.h:53
@ GRIDTRICKS_FIRST_CLIENT_ID
Definition: grid_tricks.h:48
const std::string CsvFileExtension
wxString CsvFileWildcard()
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition: gtk/ui.cpp:126
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:70
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:99
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
@ HIGHLIGHT_SYMBOL
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
bool WildCompareString(const wxString &pattern, const wxString &string_to_tst, bool case_sensitive)
Compare a string against wild card (* and ?) pattern using the usual rules.
int ValueStringCompare(const wxString &strFWord, const wxString &strSWord)
Compare strings like the strcmp function but handle numbers and modifiers within the string text corr...
std::vector< SCH_REFERENCE > m_Refs
DATA_MODEL_ROW(const SCH_REFERENCE &aFirstReference, GROUP_TYPE aType)
std::map< std::string, bool > fields_show
std::map< std::string, int > column_widths
std::map< std::string, bool > fields_group_by
Hold a name of a symbol's field, field value, and default visibility.
static const wxString GetDefaultFieldName(int aFieldNdx, bool aTranslateForHI=false)
Return a default symbol field name for field aFieldNdx for all components.
Definition for symbol library class.
@ DATASHEET_FIELD
name of datasheet
@ FOOTPRINT_FIELD
Field Name Module PCB, i.e. "16DIP300".
@ VALUE_FIELD
Field Value of part, i.e. "3.3K".
@ MANDATORY_FIELDS
The first 4 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:156
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition: ui_common.h:42
Definition of file extensions used in Kicad.