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, wxGridEvent& aEvent ) 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
97 GRID_TRICKS::showPopupMenu( menu, aEvent );
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 =
676 !destField
677 && ( !srcValue.IsEmpty() || alg::contains( m_userAddedFields, srcName ) );
678
679 if( createField )
680 {
681 const VECTOR2I symbolPos = symbol.GetPosition();
682 destField = symbol.AddField( SCH_FIELD( symbolPos, -1, &symbol, srcName ) );
683 }
684
685 if( !destField )
686 continue;
687
688 if( destField->GetId() == REFERENCE_FIELD )
689 {
690 // Reference is not editable from this dialog
691 }
692 else if( destField->GetId() == VALUE_FIELD )
693 {
694 // Value field cannot be empty
695 if( !srcValue.IsEmpty() )
696 symbol.SetValueFieldText( srcValue );
697 }
698 else if( destField->GetId() == FOOTPRINT_FIELD )
699 {
700 symbol.SetFootprintFieldText( srcValue );
701 }
702 else
703 {
704 destField->SetText( srcValue );
705 }
706 }
707
708 for( int ii = symbol.GetFields().size() - 1; ii >= MANDATORY_FIELDS; ii-- )
709 {
710 if( fieldStore.count( symbol.GetFields()[ii].GetName() ) == 0 )
711 symbol.GetFields().erase( symbol.GetFields().begin() + ii );
712 }
713 }
714
715 m_edited = false;
716 }
717
718 int GetDataWidth( int aCol )
719 {
720 int width = 0;
721
722 if( aCol == REFERENCE_FIELD )
723 {
724 for( int row = 0; row < GetNumberRows(); ++row )
725 width = std::max( width, KIUI::GetTextSize( GetValue( row, aCol ), GetView() ).x );
726 }
727 else
728 {
729 wxString column_label = GetColLabelValue( aCol ); // symbol fieldName or Qty string
730
731 for( unsigned symbolRef = 0; symbolRef < m_symbolsList.GetCount(); ++ symbolRef )
732 {
733 const KIID& symbolID = m_symbolsList[ symbolRef ].GetSymbol()->m_Uuid;
734 wxString text = m_dataStore[ symbolID ][ column_label ];
735
736 width = std::max( width, KIUI::GetTextSize( text, GetView() ).x );
737 }
738 }
739
740 return width;
741 }
742
743
744 bool IsEdited()
745 {
746 return m_edited;
747 }
748};
749
750
753 m_parent( parent )
754{
755 wxSize defaultDlgSize = ConvertDialogToPixels( wxSize( 600, 300 ) );
756 int nameColWidthMargin = 44;
757
758 // Get all symbols from the list of schematic sheets
760
764
765 m_fieldsCtrl->AppendTextColumn( _( "Field" ), wxDATAVIEW_CELL_INERT, 0, wxALIGN_LEFT, 0 );
766 m_fieldsCtrl->AppendToggleColumn( _( "Show" ), wxDATAVIEW_CELL_ACTIVATABLE, 0,
767 wxALIGN_CENTER, 0 );
768 m_fieldsCtrl->AppendToggleColumn( _( "Group By" ), wxDATAVIEW_CELL_ACTIVATABLE, 0,
769 wxALIGN_CENTER, 0 );
770
771 // GTK asserts if the number of columns doesn't match the data, but we still don't want
772 // to display the canonical names. So we'll insert a column for them, but keep it 0 width.
773 m_fieldsCtrl->AppendTextColumn( _( "Name" ), wxDATAVIEW_CELL_INERT, 0, wxALIGN_LEFT, 0 );
774
775 // SetWidth( wxCOL_WIDTH_AUTOSIZE ) fails here on GTK, so we calculate the title sizes and
776 // set the column widths ourselves.
777 wxDataViewColumn* column = m_fieldsCtrl->GetColumn( SHOW_FIELD_COLUMN );
778 m_showColWidth = KIUI::GetTextSize( column->GetTitle(), m_fieldsCtrl ).x + COLUMN_MARGIN;
779 column->SetMinWidth( m_showColWidth );
780
781 column = m_fieldsCtrl->GetColumn( GROUP_BY_COLUMN );
783 column->SetMinWidth( m_groupByColWidth );
784
785 // The fact that we're a list should keep the control from reserving space for the
786 // expander buttons... but it doesn't. Fix by forcing the indent to 0.
787 m_fieldsCtrl->SetIndent( 0 );
788
789 m_filter->SetDescriptiveText( _( "Filter" ) );
790
792
793 LoadFieldNames(); // loads rows into m_fieldsCtrl and columns into m_dataModel
794
795 // Now that the fields are loaded we can set the initial location of the splitter
796 // based on the list width. Again, SetWidth( wxCOL_WIDTH_AUTOSIZE ) fails us on GTK.
797 int nameColWidth = 0;
798
799 for( int row = 0; row < m_fieldsCtrl->GetItemCount(); ++row )
800 {
801 const wxString& fieldName = m_fieldsCtrl->GetTextValue( row, DISPLAY_NAME_COLUMN );
802 nameColWidth = std::max( nameColWidth, KIUI::GetTextSize( fieldName, m_fieldsCtrl ).x );
803 }
804
805 nameColWidth += nameColWidthMargin;
806
807 int fieldsMinWidth = nameColWidth + m_groupByColWidth + m_showColWidth;
808
809 m_fieldsCtrl->GetColumn( DISPLAY_NAME_COLUMN )->SetWidth( nameColWidth );
810
811 // This is used for data only. Don't show it to the user.
812 m_fieldsCtrl->GetColumn( CANONICAL_NAME_COLUMN )->SetHidden( true );
813
814 m_splitterMainWindow->SetMinimumPaneSize( fieldsMinWidth );
815 m_splitterMainWindow->SetSashPosition( fieldsMinWidth + 40 );
816
818 m_dataModel->Sort( 0, true );
819
820 m_grid->UseNativeColHeader( true );
821 m_grid->SetTable( m_dataModel, true );
822
823 // must be done after SetTable(), which appears to re-set it
824 m_grid->SetSelectionMode( wxGrid::wxGridSelectCells );
825
826 // sync m_grid's column visibilities to Show checkboxes in m_fieldsCtrl
827 for( int i = 0; i < m_fieldsCtrl->GetItemCount(); ++i )
828 {
829 if( m_fieldsCtrl->GetToggleValue( i, 1 ) )
830 m_grid->ShowCol( i );
831 else
832 m_grid->HideCol( i );
833 }
834
835 // add Cut, Copy, and Paste to wxGrid
836 m_grid->PushEventHandler( new FIELDS_EDITOR_GRID_TRICKS( this, m_grid, m_fieldsCtrl ) );
837
838 // give a bit more room for comboboxes
839 m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
840
841 // set reference column attributes
842 wxGridCellAttr* attr = new wxGridCellAttr;
843 attr->SetReadOnly();
844 m_grid->SetColAttr( REFERENCE_FIELD, attr );
845
846 // set footprint column browse button
847 attr = new wxGridCellAttr;
848 attr->SetEditor( new GRID_CELL_FPID_EDITOR( this, wxEmptyString ) );
849 m_grid->SetColAttr( FOOTPRINT_FIELD, attr );
850
851 // set datasheet column viewer button
852 attr = new wxGridCellAttr;
853 attr->SetEditor( new GRID_CELL_URL_EDITOR( this, Prj().SchSearchS() ) );
854 m_grid->SetColAttr( DATASHEET_FIELD, attr );
855
856 // set quantities column attributes
857 attr = new wxGridCellAttr;
858 attr->SetReadOnly();
859 m_grid->SetColAttr( m_dataModel->GetColsCount() - 1, attr );
860 m_grid->SetColFormatNumber( m_dataModel->GetColsCount() - 1 );
861 m_grid->AutoSizeColumns( false );
862
863 for( int col = 0; col < m_grid->GetNumberCols(); ++col )
864 {
865 // Columns are hidden by setting their width to 0 so if we resize them they will
866 // become unhidden.
867 if( m_grid->IsColShown( col ) )
868 {
869 EESCHEMA_SETTINGS* cfg = static_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
870 std::string key( m_dataModel->GetCanonicalColLabel( col ).ToUTF8() );
871
872 if( cfg->m_FieldEditorPanel.column_widths.count( key ) )
873 {
874 int width = cfg->m_FieldEditorPanel.column_widths.at( key );
875 m_grid->SetColSize( col, width );
876 }
877 else
878 {
879 int textWidth = m_dataModel->GetDataWidth( col ) + COLUMN_MARGIN;
880 int maxWidth = defaultDlgSize.x / 3;
881
882 if( col == m_grid->GetNumberCols() - 1 )
883 m_grid->SetColSize( col, Clamp( 50, textWidth, maxWidth ) );
884 else
885 m_grid->SetColSize( col, Clamp( 100, textWidth, maxWidth ) );
886 }
887 }
888 }
889
890 m_grid->SelectRow( 0 );
891 m_grid->SetGridCursor( 0, 1 );
893
895
897 SetSize( defaultDlgSize );
898 Center();
899
900 // Connect Events
901 m_grid->Connect( wxEVT_GRID_COL_SORT,
902 wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColSort ), nullptr, this );
903}
904
905
907{
908 // Disconnect Events
909 m_grid->Disconnect( wxEVT_GRID_COL_SORT,
910 wxGridEventHandler( DIALOG_SYMBOL_FIELDS_TABLE::OnColSort ), nullptr,
911 this );
912
913 // Delete the GRID_TRICKS.
914 m_grid->PopEventHandler( true );
915
916 // we gave ownership of m_dataModel to the wxGrid...
917}
918
919
921{
922 if( !wxDialog::TransferDataFromWindow() )
923 return false;
924
926 EE_SELECTION_TOOL* selectionTool = toolMgr->GetTool<EE_SELECTION_TOOL>();
927 EE_SELECTION& selection = selectionTool->GetSelection();
928 SCH_SYMBOL* symbol = nullptr;
929
930 if( selection.GetSize() == 1 )
931 {
932 EDA_ITEM* item = selection.Front();
933
934 if( item->Type() == SCH_SYMBOL_T )
935 symbol = (SCH_SYMBOL*) item;
936 else if( item->GetParent() && item->GetParent()->Type() == SCH_SYMBOL_T )
937 symbol = (SCH_SYMBOL*) item->GetParent();
938 }
939
940 if( symbol )
941 {
942 for( int row = 0; row < m_dataModel->GetNumberRows(); ++row )
943 {
944 std::vector<SCH_REFERENCE> references = m_dataModel->GetRowReferences( row );
945 bool found = false;
946
947 for( const SCH_REFERENCE& ref : references )
948 {
949 if( ref.GetSymbol() == symbol )
950 {
951 found = true;
952 break;
953 }
954 }
955
956 if( found )
957 {
958 m_grid->GoToCell( row, 1 );
959 break;
960 }
961 }
962 }
963
964 return true;
965}
966
967
969{
971 return false;
972
973 if( !wxDialog::TransferDataFromWindow() )
974 return false;
975
976 SCH_SHEET_PATH currentSheet = m_parent->GetCurrentSheet();
977
979
980 // Reset the view to where we left the user
981 m_parent->SetCurrentSheet( currentSheet );
983 m_parent->Refresh();
984
986
987 return true;
988}
989
990
991void DIALOG_SYMBOL_FIELDS_TABLE::AddField( const wxString& aDisplayName,
992 const wxString& aCanonicalName,
993 bool defaultShow, bool defaultSortBy, bool addedByUser )
994{
995 m_dataModel->AddColumn( aCanonicalName, addedByUser );
996
997 wxVector<wxVariant> fieldsCtrlRow;
998
999 EESCHEMA_SETTINGS* cfg = static_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
1000 bool show = defaultShow;
1001 bool sort_by = defaultSortBy;
1002
1003 std::string key( aCanonicalName.ToUTF8() );
1004
1005 if( cfg->m_FieldEditorPanel.fields_show.count( key ) )
1006 show = cfg->m_FieldEditorPanel.fields_show.at( key );
1007
1008 if( cfg->m_FieldEditorPanel.fields_group_by.count( key ) )
1009 sort_by = cfg->m_FieldEditorPanel.fields_group_by.at( key );
1010
1011 // Don't change these to emplace_back: some versions of wxWidgets don't support it
1012 fieldsCtrlRow.push_back( wxVariant( aDisplayName ) );
1013 fieldsCtrlRow.push_back( wxVariant( show ) );
1014 fieldsCtrlRow.push_back( wxVariant( sort_by ) );
1015 fieldsCtrlRow.push_back( wxVariant( aCanonicalName ) );
1016
1017 m_fieldsCtrl->AppendItem( fieldsCtrlRow );
1018}
1019
1020
1022{
1023 std::set<wxString> userFieldNames;
1024
1025 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
1026 {
1027 SCH_SYMBOL* symbol = m_symbolsList[ i ].GetSymbol();
1028
1029 for( int j = MANDATORY_FIELDS; j < symbol->GetFieldCount(); ++j )
1030 userFieldNames.insert( symbol->GetFields()[j].GetName() );
1031 }
1032
1033 // Force References to always be shown
1034 EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
1035 wxCHECK( cfg, /*void*/ );
1036
1037 cfg->m_FieldEditorPanel.fields_show["Reference"] = true;
1038
1039 AddField( _( "Reference" ), wxT( "Reference" ), true, true );
1040 AddField( _( "Value" ), wxT( "Value" ), true, true );
1041 AddField( _( "Footprint" ), wxT( "Footprint" ), true, true );
1042 AddField( _( "Datasheet" ), wxT( "Datasheet" ), true, false );
1043
1044 for( const wxString& fieldName : userFieldNames )
1045 AddField( fieldName, fieldName, true, false );
1046
1047 // Add any templateFieldNames which aren't already present in the userFieldNames
1048 for( const TEMPLATE_FIELDNAME& templateFieldname :
1050 {
1051 if( userFieldNames.count( templateFieldname.m_Name ) == 0 )
1052 AddField( templateFieldname.m_Name, templateFieldname.m_Name, false, false );
1053 }
1054}
1055
1056
1057void DIALOG_SYMBOL_FIELDS_TABLE::OnAddField( wxCommandEvent& event )
1058{
1059 wxTextEntryDialog dlg( this, _( "New field name:" ), _( "Add Field" ) );
1060
1061 if( dlg.ShowModal() != wxID_OK )
1062 return;
1063
1064 wxString fieldName = dlg.GetValue();
1065
1066 if( fieldName.IsEmpty() )
1067 {
1068 DisplayError( this, _( "Field must have a name." ) );
1069 return;
1070 }
1071
1072 for( int i = 0; i < m_dataModel->GetNumberCols(); ++i )
1073 {
1074 if( fieldName == m_dataModel->GetColLabelValue( i ) )
1075 {
1076 DisplayError( this, wxString::Format( _( "Field name '%s' already in use." ),
1077 fieldName ) );
1078 return;
1079 }
1080 }
1081
1082 // quantities column will become new field column, so it needs to be reset
1083 wxGridCellAttr* attr = new wxGridCellAttr;
1084 m_grid->SetColAttr( m_dataModel->GetColsCount() - 1, attr );
1085 m_grid->SetColFormatCustom( m_dataModel->GetColsCount() - 1, wxGRID_VALUE_STRING );
1086
1087 std::string key( fieldName.ToUTF8() );
1088
1089 EESCHEMA_SETTINGS* cfg = static_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
1090 cfg->m_FieldEditorPanel.fields_show[key] = true;
1091
1092 AddField( fieldName, fieldName, true, false, true );
1093
1094 wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_INSERTED,
1095 m_fieldsCtrl->GetItemCount(), 1 );
1096 m_grid->ProcessTableMessage( msg );
1097
1098 // set up attributes on the new quantities column
1099 attr = new wxGridCellAttr;
1100 attr->SetReadOnly();
1101 m_grid->SetColAttr( m_dataModel->GetColsCount() - 1, attr );
1102 m_grid->SetColFormatNumber( m_dataModel->GetColsCount() - 1 );
1103 m_grid->SetColSize( m_dataModel->GetColsCount() - 1, 50 );
1104}
1105
1106
1108{
1109 int col = -1;
1110 int row = m_fieldsCtrl->GetSelectedRow();
1111
1112 // Should never occur: "Remove Field..." button should be disabled if invalid selection
1113 // via OnFieldsCtrlSelectionChanged()
1114 wxCHECK_RET( row != -1, wxS( "Some user defined field must be selected first" ) );
1115 wxCHECK_RET( row >= MANDATORY_FIELDS, wxS( "Mandatory fields cannot be removed" ) );
1116
1117 wxString fieldName = m_fieldsCtrl->GetTextValue( row, 0 );
1118
1119 wxString confirm_msg = wxString::Format( _( "Are you sure you want to remove the field '%s'?" ),
1120 fieldName );
1121
1122 if( !IsOK( this, confirm_msg ) )
1123 return;
1124
1125 for( int i = 0; i < m_dataModel->GetNumberCols(); ++i )
1126 {
1127 if( fieldName == m_dataModel->GetColLabelValue( i ) )
1128 col = i;
1129 }
1130
1131 m_fieldsCtrl->DeleteItem( row );
1132 m_dataModel->RemoveColumn( col );
1133
1134 // Make selection and update the state of "Remove field..." button via OnFieldsCtrlSelectionChanged()
1135 // Safe to decrement row index because we always have mandatory fields
1136 m_fieldsCtrl->SelectRow( --row );
1137
1138 if( row < MANDATORY_FIELDS )
1139 m_removeFieldButton->Enable( false );
1140
1141 wxGridTableMessage msg( m_dataModel, wxGRIDTABLE_NOTIFY_COLS_DELETED,
1142 m_fieldsCtrl->GetItemCount(), 1 );
1143
1144 m_grid->ProcessTableMessage( msg );
1145
1146 // set up attributes on the new quantities column
1147 wxGridCellAttr* attr = new wxGridCellAttr;
1148 attr->SetReadOnly();
1149
1150 m_grid->SetColAttr( m_dataModel->GetColsCount() - 1, attr );
1151 m_grid->SetColFormatNumber( m_dataModel->GetColsCount() - 1 );
1152 m_grid->SetColSize( m_dataModel->GetColsCount() - 1, 50 );
1153}
1154
1155
1156void DIALOG_SYMBOL_FIELDS_TABLE::OnFilterText( wxCommandEvent& aEvent )
1157{
1159 m_dataModel->Sort( m_grid->GetSortingColumn(), m_grid->IsSortOrderAscending() );
1160 m_grid->ForceRefresh();
1161}
1162
1163
1165{
1166 wxPoint pos = aEvent.GetPosition();
1167 wxRect ctrlRect = m_filter->GetScreenRect();
1168 int buttonWidth = ctrlRect.GetHeight(); // Presume buttons are square
1169
1170 if( m_filter->IsSearchButtonVisible() && pos.x < buttonWidth )
1171 SetCursor( wxCURSOR_ARROW );
1172 else if( m_filter->IsCancelButtonVisible() && pos.x > ctrlRect.GetWidth() - buttonWidth )
1173 SetCursor( wxCURSOR_ARROW );
1174 else
1175 SetCursor( wxCURSOR_IBEAM );
1176}
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 m_dataModel->ExpandCollapseRow( event.GetRow() );
1306 }
1307 else
1308 {
1309 event.Skip();
1310 }
1311}
1312
1313void DIALOG_SYMBOL_FIELDS_TABLE::OnTableRangeSelected( wxGridRangeSelectEvent& event )
1314{
1315 wxGridCellCoordsArray selectedCells = m_grid->GetSelectedCells();
1316
1317 if( selectedCells.GetCount() == 1 )
1318 {
1319 int row = selectedCells[0].GetRow();
1320 int flag = m_dataModel->GetRowFlags( row );
1321 std::vector<SCH_REFERENCE> refs = m_dataModel->GetRowReferences( row );
1322
1323 // Focus Eeschema view on the symbol selected in the dialog
1324 // TODO: Highlight or select more than one unit
1325 if( ( flag == GROUP_SINGLETON || flag == CHILD_ITEM ) && refs.size() >= 1 )
1326 {
1328
1329 std::sort( refs.begin(), refs.end(),
1330 []( const SCH_REFERENCE& a, const SCH_REFERENCE& b )
1331 {
1332 return a.GetUnit() < b.GetUnit();
1333 } );
1334
1335 // search and highlight the symbol found by its full path.
1336 // It allows select of not yet annotated or duplicaded symbols
1337 wxString symbol_path = refs[0].GetFullPath();
1338 // wxString reference = refs[0].GetRef() + refs[0].GetRefNumber(); // Not used
1339 editor->FindSymbolAndItem( &symbol_path, nullptr, true, HIGHLIGHT_SYMBOL, wxEmptyString );
1340 }
1341
1342 return;
1343 }
1344
1345 event.Skip();
1346}
1347
1349{
1350 // TODO: Option to select footprint if FOOTPRINT column selected
1351
1352 event.Skip();
1353}
1354
1355
1357{
1358 int nameColWidth = KIPLATFORM::UI::GetUnobscuredSize( m_fieldsCtrl ).x;
1359 nameColWidth -= m_showColWidth + m_groupByColWidth;
1360#ifdef __WXMAC__
1361 // TODO: something in wxWidgets 3.1.x pads checkbox columns with extra space. (It used to
1362 // also be that the width of the column would get set too wide (to 30), but that's patched in
1363 // our local wxWidgets fork.)
1364 nameColWidth -= 30;
1365#endif
1366
1367 // GTK loses its head and messes these up when resizing the splitter bar:
1368 m_fieldsCtrl->GetColumn( SHOW_FIELD_COLUMN )->SetWidth( m_showColWidth );
1369 m_fieldsCtrl->GetColumn( GROUP_BY_COLUMN )->SetWidth( m_groupByColWidth );
1370
1371 m_fieldsCtrl->GetColumn( CANONICAL_NAME_COLUMN )->SetHidden( true );
1372 m_fieldsCtrl->GetColumn( DISPLAY_NAME_COLUMN )->SetWidth( nameColWidth );
1373
1374 m_fieldsCtrl->Refresh(); // To refresh checkboxes on Windows.
1375
1376 event.Skip();
1377}
1378
1379
1381{
1384}
1385
1386
1387void DIALOG_SYMBOL_FIELDS_TABLE::OnExport( wxCommandEvent& aEvent )
1388{
1389 int last_col = m_grid->GetNumberCols() - 1;
1390
1391 if( m_dataModel->IsEdited() )
1392 if( OKOrCancelDialog( nullptr, _( "Unsaved data" ),
1393 _( "Changes are unsaved. Export unsaved data?" ), "", _( "OK" ),
1394 _( "Cancel" ) )
1395 == wxID_CANCEL )
1396 return;
1397
1398
1399 // Calculate the netlist filename
1400 wxFileName fn = m_parent->Schematic().GetFileName();
1401 fn.SetExt( CsvFileExtension );
1402
1403 wxFileDialog saveDlg( this, _( "Save as CSV" ), wxPathOnly( Prj().GetProjectFullName() ),
1404 fn.GetFullName(), CsvFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1405
1406 if( saveDlg.ShowModal() == wxID_CANCEL )
1407 return;
1408
1409 wxFFile out( saveDlg.GetPath(), "wb" );
1410
1411 if( !out.IsOpened() )
1412 return;
1413
1414 // Find the location for the line terminator
1415 for( int col = m_grid->GetNumberCols() - 1; col >=0 ; --col )
1416 {
1417 if( m_grid->IsColShown( col ) )
1418 {
1419 last_col = col;
1420 break;
1421 }
1422 }
1423
1424 // Column names
1425 for( int col = 0; col < m_grid->GetNumberCols(); col++ )
1426 {
1427 if( !m_grid->IsColShown( col ) )
1428 continue;
1429
1430 wxString escapedValue = m_grid->GetColLabelValue( col );
1431 escapedValue.Replace( wxS( "\"" ), wxS( "\"\"" ) );
1432
1433 wxString format = col == last_col ? wxS( "\"%s\"\r\n" ) : wxS( "\"%s\"," );
1434
1435 out.Write( wxString::Format( format, escapedValue ) );
1436 }
1437
1438 // Data rows
1439 for( int row = 0; row < m_grid->GetNumberRows(); row++ )
1440 {
1441 // Don't output child rows
1442 if( m_dataModel->GetRowFlags( row ) == CHILD_ITEM )
1443 continue;
1444
1445 for( int col = 0; col < m_grid->GetNumberCols(); col++ )
1446 {
1447 if( !m_grid->IsColShown( col ) )
1448 continue;
1449
1450 // Get the unanottated version of the field, e.g. no "> " or "v " by
1451 wxString escapedValue = m_dataModel->GetRawValue( row, col );
1452 escapedValue.Replace( wxS( "\"" ), wxS( "\"\"" ) );
1453
1454 wxString format = col == last_col ? wxS( "\"%s\"\r\n" ) : wxS( "\"%s\"," );
1455
1456 out.Write( wxString::Format( format, escapedValue ) );
1457 }
1458 }
1459}
1460
1461
1462void DIALOG_SYMBOL_FIELDS_TABLE::OnCancel( wxCommandEvent& event )
1463{
1464 Close();
1465}
1466
1467
1468void DIALOG_SYMBOL_FIELDS_TABLE::OnClose( wxCloseEvent& event )
1469{
1470 // This is a cancel, so commit quietly as we're going to throw the results away anyway.
1472
1473 if( m_dataModel->IsEdited() )
1474 {
1475 if( !HandleUnsavedChanges( this, _( "Save changes?" ),
1476 [&]() -> bool
1477 {
1478 return TransferDataFromWindow();
1479 } ) )
1480 {
1481 event.Veto();
1482 return;
1483 }
1484 }
1485
1486 event.Skip();
1487}
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:106
@ 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 OnTableRangeSelected(wxGridRangeSelectEvent &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:492
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:165
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 showPopupMenu(wxMenu &menu, wxGridEvent &aEvent) override
void doPopupSelection(wxCommandEvent &event) 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)
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
APP_SETTINGS_BASE * KifaceSettings() const
Definition: kiface_base.h:95
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:199
SCHEMATIC_SETTINGS & Settings() const
Definition: schematic.cpp:205
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:51
int GetId() const
Definition: sch_field.h:125
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:81
int GetFieldCount() const
Return the number of fields in this symbol.
Definition: sch_symbol.h:486
const std::vector< SCH_SYMBOL_INSTANCE > & GetInstanceReferences()
Definition: sch_symbol.h:140
SCH_FIELD * FindField(const wxString &aFieldName, bool aIncludeDefaultFields=true)
Search for a SCH_FIELD with aFieldName.
Definition: sch_symbol.cpp:938
void SetValueFieldText(const wxString &aValue)
Definition: sch_symbol.cpp:844
const wxString GetFootprintFieldText(bool aResolve) const
Definition: sch_symbol.cpp:850
void SetFootprintFieldText(const wxString &aFootprint)
Definition: sch_symbol.cpp:859
VECTOR2I GetPosition() const override
Definition: sch_symbol.h:712
const wxString GetValueFieldText(bool aResolve) const
Definition: sch_symbol.cpp:835
SCH_FIELD * AddField(const SCH_FIELD &aField)
Add a field to the symbol.
Definition: sch_symbol.cpp:916
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:901
wxString GetFieldText(const wxString &aFieldName) const
Search for a field named aFieldName and returns text associated with this field.
Definition: sch_symbol.cpp:889
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:164
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:423
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:273
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:380
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:300
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:260
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.
@ 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:133
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
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.