KiCad PCB EDA Suite
Loading...
Searching...
No Matches
fields_data_model.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) 2023 <author>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20#include <wx/string.h>
21#include <wx/debug.h>
22#include <wx/grid.h>
23#include <common.h>
24#include <widgets/wx_grid.h>
25#include <sch_reference_list.h>
26#include <sch_commit.h>
27#include <sch_screen.h>
28#include "string_utils.h"
29
30#include "fields_data_model.h"
31
32
33static wxString multipleValues = wxS( "<...>" );
34
35
44static KIID_PATH makeDataStoreKey( const SCH_SHEET_PATH& aSheetPath, const SCH_SYMBOL& aSymbol )
45{
46 KIID_PATH path = aSheetPath.Path();
47 path.push_back( aSymbol.m_Uuid );
48 return path;
49}
50
51
53{
54 switch( aCol )
55 {
56 case DISPLAY_NAME_COLUMN: return _( "Field" );
57 case LABEL_COLUMN: return m_forBOM ? _( "BOM Name" ) : _( "Label" );
58 case SHOW_FIELD_COLUMN: return _( "Include" );
59 case GROUP_BY_COLUMN: return _( "Group By" );
60 default: return wxT( "unknown column" );
61 };
62}
63
64
65wxString VIEW_CONTROLS_GRID_DATA_MODEL::GetValue( int aRow, int aCol )
66{
67 wxCHECK( aRow < GetNumberRows(), wxT( "bad row!" ) );
68
69 BOM_FIELD& rowData = m_fields[aRow];
70
71 switch( aCol )
72 {
74 for( FIELD_T fieldId : MANDATORY_FIELDS )
75 {
76 if( GetDefaultFieldName( fieldId, !DO_TRANSLATE ) == rowData.name )
77 return GetDefaultFieldName( fieldId, DO_TRANSLATE );
78 }
79
80 return rowData.name;
81
82 case LABEL_COLUMN:
83 return rowData.label;
84
85 default:
86 // we can't assert here because wxWidgets sometimes calls this without checking
87 // the column type when trying to see if there's an overflow
88 return wxT( "bad wxWidgets!" );
89 }
90}
91
92
94{
95 wxCHECK( aRow < GetNumberRows(), false );
96
97 BOM_FIELD& rowData = m_fields[aRow];
98
99 switch( aCol )
100 {
101 case SHOW_FIELD_COLUMN: return rowData.show;
102 case GROUP_BY_COLUMN: return rowData.groupBy;
103
104 default:
105 wxFAIL_MSG( wxString::Format( wxT( "column %d doesn't hold a bool value" ), aCol ) );
106 return false;
107 }
108}
109
110
111void VIEW_CONTROLS_GRID_DATA_MODEL::SetValue( int aRow, int aCol, const wxString &aValue )
112{
113 wxCHECK( aRow < GetNumberRows(), /*void*/ );
114
115 BOM_FIELD& rowData = m_fields[aRow];
116
117 switch( aCol )
118 {
120 // Not editable
121 break;
122
123 case LABEL_COLUMN:
124 rowData.label = aValue;
125 break;
126
127 default:
128 wxFAIL_MSG( wxString::Format( wxT( "column %d doesn't hold a string value" ), aCol ) );
129 }
130
131 GetView()->Refresh();
132}
133
134
135void VIEW_CONTROLS_GRID_DATA_MODEL::SetValueAsBool( int aRow, int aCol, bool aValue )
136{
137 wxCHECK( aRow < GetNumberRows(), /*void*/ );
138
139 BOM_FIELD& rowData = m_fields[aRow];
140
141 switch( aCol )
142 {
143 case SHOW_FIELD_COLUMN: rowData.show = aValue; break;
144 case GROUP_BY_COLUMN: rowData.groupBy = aValue; break;
145
146 default:
147 wxFAIL_MSG( wxString::Format( wxT( "column %d doesn't hold a bool value" ), aCol ) );
148 }
149}
150
151
152void VIEW_CONTROLS_GRID_DATA_MODEL::AppendRow( const wxString& aFieldName, const wxString& aBOMName,
153 bool aShow, bool aGroupBy )
154{
155 m_fields.emplace_back( BOM_FIELD{ aFieldName, aBOMName, aShow, aGroupBy } );
156
157 if( wxGrid* grid = GetView() )
158 {
159 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, 1 );
160 grid->ProcessTableMessage( msg );
161 }
162}
163
164
166{
167 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), /* void */ );
168
169 m_fields.erase( m_fields.begin() + aRow );
170
171 if( wxGrid* grid = GetView() )
172 {
173 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aRow, 1 );
174 grid->ProcessTableMessage( msg );
175 }
176}
177
178
180{
181 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), wxEmptyString );
182
183 BOM_FIELD& rowData = m_fields[aRow];
184
185 return rowData.name;
186}
187
188
189void VIEW_CONTROLS_GRID_DATA_MODEL::SetCanonicalFieldName( int aRow, const wxString& aName )
190{
191 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), /* void */ );
192
193 BOM_FIELD& rowData = m_fields[aRow];
194
195 rowData.name = aName;
196}
197
198
199const wxString FIELDS_EDITOR_GRID_DATA_MODEL::QUANTITY_VARIABLE = wxS( "${QUANTITY}" );
200const wxString FIELDS_EDITOR_GRID_DATA_MODEL::ITEM_NUMBER_VARIABLE = wxS( "${ITEM_NUMBER}" );
201
202
203void FIELDS_EDITOR_GRID_DATA_MODEL::AddColumn( const wxString& aFieldName, const wxString& aLabel,
204 bool aAddedByUser, const std::set<wxString>& aVariantNames )
205{
206 // Don't add a field twice
207 if( GetFieldNameCol( aFieldName ) != -1 )
208 return;
209
210 m_cols.push_back( { aFieldName, aLabel, aAddedByUser, false, false } );
211
212 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
213 updateDataStoreSymbolField( m_symbolsList[i], aFieldName, aVariantNames );
214}
215
216
218 const wxString& aFieldName,
219 const std::set<wxString>& aVariantNames )
220{
221 const SCH_SYMBOL* symbol = aSymbolRef.GetSymbol();
222
223 if( !symbol )
224 return;
225
226 KIID_PATH key = makeDataStoreKey( aSymbolRef.GetSheetPath(), *symbol );
227
228 if( isAttribute( aFieldName ) )
229 {
230 m_dataStore[key][aFieldName] = getAttributeValue( *symbol, aFieldName, aVariantNames );
231 }
232 else if( const SCH_FIELD* field = symbol->GetField( aFieldName ) )
233 {
234 if( field->IsPrivate() )
235 {
236 m_dataStore[key][aFieldName] = wxEmptyString;
237 return;
238 }
239
240 wxString value = symbol->Schematic()->ConvertKIIDsToRefs( field->GetText() );
241
242 for( const wxString& variantName : aVariantNames )
243 {
244 std::optional<SCH_SYMBOL_VARIANT> variant = symbol->GetVariant( aSymbolRef.GetSheetPath(), variantName );
245
246 if( !variant || !variant->m_Fields.contains( aFieldName ) )
247 continue;
248
249 if( value.IsEmpty() )
250 {
251 value = variant->m_Fields[aFieldName];
252 continue;
253 }
254
255 if( value != variant->m_Fields[aFieldName] )
256 value = multipleValues;
257 }
258
259 m_dataStore[key][aFieldName] = value;
260 }
261 else if( IsGeneratedField( aFieldName ) )
262 {
263 // Handle generated fields with variables as names (e.g. ${QUANTITY}) that are not present in
264 // the symbol by giving them the correct value
265 m_dataStore[key][aFieldName] = aFieldName;
266 }
267 else
268 {
269 m_dataStore[key][aFieldName] = wxEmptyString;
270 }
271}
272
273
275{
276 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
277 {
278 if( SCH_SYMBOL* symbol = m_symbolsList[i].GetSymbol() )
279 {
280 KIID_PATH key = makeDataStoreKey( m_symbolsList[i].GetSheetPath(), *symbol );
281 m_dataStore[key].erase( m_cols[aCol].m_fieldName );
282 }
283 }
284
285 m_cols.erase( m_cols.begin() + aCol );
286}
287
288
289void FIELDS_EDITOR_GRID_DATA_MODEL::RenameColumn( int aCol, const wxString& newName )
290{
291 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
292 {
293 SCH_SYMBOL* symbol = m_symbolsList[i].GetSymbol();
294 KIID_PATH key = makeDataStoreKey( m_symbolsList[i].GetSheetPath(), *symbol );
295
296 // Careful; field may have already been renamed from another sheet instance
297 if( auto node = m_dataStore[key].extract( m_cols[aCol].m_fieldName ) )
298 {
299 node.key() = newName;
300 m_dataStore[key].insert( std::move( node ) );
301 }
302 }
303
304 m_cols[aCol].m_fieldName = newName;
305 m_cols[aCol].m_label = newName;
306}
307
308
309int FIELDS_EDITOR_GRID_DATA_MODEL::GetFieldNameCol( const wxString& aFieldName ) const
310{
311 for( size_t i = 0; i < m_cols.size(); i++ )
312 {
313 if( m_cols[i].m_fieldName == aFieldName )
314 return static_cast<int>( i );
315 }
316
317 return -1;
318}
319
320
322{
323 std::vector<BOM_FIELD> fields;
324
325 for( const DATA_MODEL_COL& col : m_cols )
326 fields.push_back( { col.m_fieldName, col.m_label, col.m_show, col.m_group } );
327
328 return fields;
329}
330
331
332void FIELDS_EDITOR_GRID_DATA_MODEL::SetFieldsOrder( const std::vector<wxString>& aNewOrder )
333{
334 size_t foundCount = 0;
335
336 for( const wxString& newField : aNewOrder )
337 {
338 if( foundCount >= m_cols.size() )
339 break;
340
341 for( DATA_MODEL_COL& col : m_cols )
342 {
343 if( col.m_fieldName == newField )
344 {
345 std::swap( m_cols[foundCount], col );
346 foundCount++;
347 break;
348 }
349 }
350 }
351}
352
353
355{
356 // Check if aCol is the first visible column
357 for( int col = 0; col < aCol; ++col )
358 {
359 if( m_cols[col].m_show )
360 return false;
361 }
362
363 return true;
364}
365
366
367wxString FIELDS_EDITOR_GRID_DATA_MODEL::GetValue( int aRow, int aCol )
368{
369 GetView()->SetReadOnly( aRow, aCol, IsExpanderColumn( aCol ) );
370 return GetValue( m_rows[aRow], aCol );
371}
372
373
374wxGridCellAttr* FIELDS_EDITOR_GRID_DATA_MODEL::GetAttr( int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind )
375{
377 || IsURL( GetValue( m_rows[aRow], aCol ) ) )
378 {
379 if( m_urlEditor )
380 {
381 m_urlEditor->IncRef();
382 return enhanceAttr( m_urlEditor, aRow, aCol, aKind );
383 }
384 }
385
386 return WX_GRID_TABLE_BASE::GetAttr( aRow, aCol, aKind );
387}
388
389
391 const wxString& refDelimiter,
392 const wxString& refRangeDelimiter,
393 bool resolveVars,
394 bool listMixedValues )
395{
396 std::vector<SCH_REFERENCE> references;
397 std::set<wxString> mixedValues;
398 wxString fieldValue;
399
400 for( const SCH_REFERENCE& ref : group.m_Refs )
401 {
402 if( ColIsReference( aCol ) || ColIsQuantity( aCol ) || ColIsItemNumber( aCol ) )
403 {
404 references.push_back( ref );
405 }
406 else // Other columns are either a single value or ROW_MULTI_ITEMS
407 {
408 KIID_PATH symbolKey = makeDataStoreKey( ref.GetSheetPath(), *ref.GetSymbol() );
409
410 if( !m_dataStore.contains( symbolKey ) || !m_dataStore[symbolKey].contains( m_cols[aCol].m_fieldName ) )
411 return INDETERMINATE_STATE;
412
413 wxString refFieldValue = m_dataStore[symbolKey][m_cols[aCol].m_fieldName];
414
415 if( resolveVars )
416 {
417 if( IsGeneratedField( m_cols[aCol].m_fieldName ) )
418 {
419 // Generated fields (e.g. ${QUANTITY}) can't have un-applied values as they're
420 // read-only. Resolve them against the field.
421 refFieldValue = getFieldShownText( ref, m_cols[aCol].m_fieldName );
422 }
423 else if( refFieldValue.Contains( wxT( "${" ) ) )
424 {
425 // Resolve variables in the un-applied value using the parent symbol and instance
426 // data.
427 std::function<bool( wxString* )> symbolResolver =
428 [&]( wxString* token ) -> bool
429 {
430 return ref.GetSymbol()->ResolveTextVar( &ref.GetSheetPath(), token );
431 };
432
433 refFieldValue = ExpandTextVars( refFieldValue, & symbolResolver );
434 }
435 }
436
437 if( listMixedValues )
438 mixedValues.insert( refFieldValue );
439 else if( &ref == &group.m_Refs.front() )
440 fieldValue = refFieldValue;
441 else if( fieldValue != refFieldValue )
442 return INDETERMINATE_STATE;
443 }
444 }
445
446 if( listMixedValues )
447 {
448 fieldValue = wxEmptyString;
449
450 for( const wxString& value : mixedValues )
451 {
452 if( value.IsEmpty() )
453 continue;
454 else if( fieldValue.IsEmpty() )
455 fieldValue = value;
456 else
457 fieldValue += "," + value;
458 }
459 }
460
461 if( ColIsReference( aCol ) || ColIsQuantity( aCol ) || ColIsItemNumber( aCol ) )
462 {
463 // Remove duplicates (other units of multi-unit parts)
464 std::sort( references.begin(), references.end(),
465 []( const SCH_REFERENCE& l, const SCH_REFERENCE& r ) -> bool
466 {
467 wxString l_ref( l.GetRef() << l.GetRefNumber() );
468 wxString r_ref( r.GetRef() << r.GetRefNumber() );
469 return StrNumCmp( l_ref, r_ref, true ) < 0;
470 } );
471
472 auto logicalEnd = std::unique( references.begin(), references.end(),
473 []( const SCH_REFERENCE& l, const SCH_REFERENCE& r ) -> bool
474 {
475 // If unannotated then we can't tell what units belong together
476 // so we have to leave them all
477 if( l.GetRefNumber() == wxT( "?" ) )
478 return false;
479
480 wxString l_ref( l.GetRef() << l.GetRefNumber() );
481 wxString r_ref( r.GetRef() << r.GetRefNumber() );
482 return l_ref == r_ref;
483 } );
484
485 references.erase( logicalEnd, references.end() );
486 }
487
488 if( ColIsReference( aCol ) )
489 fieldValue = SCH_REFERENCE_LIST::Shorthand( references, refDelimiter, refRangeDelimiter );
490 else if( ColIsQuantity( aCol ) )
491 fieldValue = wxString::Format( wxT( "%d" ), (int) references.size() );
492 else if( ColIsItemNumber( aCol ) && group.m_Flag != CHILD_ITEM )
493 fieldValue = wxString::Format( wxT( "%d" ), group.m_ItemNumber );
494
495 return fieldValue;
496}
497
498
499void FIELDS_EDITOR_GRID_DATA_MODEL::SetValue( int aRow, int aCol, const wxString& aValue )
500{
501 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), wxS( "Invalid column number" ) );
502
503 // Can't modify references or generated fields (e.g. ${QUANTITY})
504 if( ColIsReference( aCol )
505 || ( IsGeneratedField( m_cols[aCol].m_fieldName ) && !ColIsAttribute( aCol ) ) )
506 {
507 return;
508 }
509
510 DATA_MODEL_ROW& rowGroup = m_rows[aRow];
511
512 const SCH_SYMBOL* sharedSymbol = nullptr;
513 bool isSharedInstance = false;
514
515 for( const SCH_REFERENCE& ref : rowGroup.m_Refs )
516 {
517 const SCH_SCREEN* screen = nullptr;
518
519 // Check to see if the symbol associated with this row has more than one instance.
520 if( const SCH_SYMBOL* symbol = ref.GetSymbol() )
521 {
522 screen = static_cast<const SCH_SCREEN*>( symbol->GetParent() );
523
524 isSharedInstance = ( screen && ( screen->GetRefCount() > 1 ) );
525 sharedSymbol = symbol;
526 }
527
528 KIID_PATH key = makeDataStoreKey( ref.GetSheetPath(), *ref.GetSymbol() );
529 m_dataStore[key][m_cols[aCol].m_fieldName] = aValue;
530 }
531
532 // Update all of the other instances for the shared symbol as required.
533 if( isSharedInstance
534 && ( ( rowGroup.m_Flag == GROUP_SINGLETON ) || ( rowGroup.m_Flag == CHILD_ITEM ) ) )
535 {
536 for( DATA_MODEL_ROW& row : m_rows )
537 {
538 if( row.m_ItemNumber == aRow + 1 )
539 continue;
540
541 for( const SCH_REFERENCE& ref : row.m_Refs )
542 {
543 if( ref.GetSymbol() != sharedSymbol )
544 continue;
545
546 KIID_PATH key = makeDataStoreKey( ref.GetSheetPath(), *ref.GetSymbol() );
547 m_dataStore[key][m_cols[aCol].m_fieldName] = aValue;
548 }
549 }
550 }
551
552 m_edited = true;
553}
554
555
557{
558 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
559 return m_cols[aCol].m_fieldName == GetCanonicalFieldName( FIELD_T::REFERENCE );
560}
561
562
564{
565 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
566 return m_cols[aCol].m_fieldName == GetCanonicalFieldName( FIELD_T::VALUE );
567}
568
569
571{
572 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
573 return m_cols[aCol].m_fieldName == QUANTITY_VARIABLE;
574}
575
576
578{
579 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
580 return m_cols[aCol].m_fieldName == ITEM_NUMBER_VARIABLE;
581}
582
583
585{
586 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
587 return isAttribute( m_cols[aCol].m_fieldName );
588}
589
590
592 const DATA_MODEL_ROW& rhGroup,
593 FIELDS_EDITOR_GRID_DATA_MODEL* dataModel, int sortCol,
594 bool ascending )
595{
596 // Empty rows always go to the bottom, whether ascending or descending
597 if( lhGroup.m_Refs.size() == 0 )
598 return true;
599 else if( rhGroup.m_Refs.size() == 0 )
600 return false;
601
602 // N.B. To meet the iterator sort conditions, we cannot simply invert the truth
603 // to get the opposite sort. i.e. ~(a<b) != (a>b)
604 auto local_cmp =
605 [ ascending ]( const auto a, const auto b )
606 {
607 if( ascending )
608 return a < b;
609 else
610 return a > b;
611 };
612
613 // Primary sort key is sortCol; secondary is always REFERENCE (column 0)
614 if( sortCol < 0 || sortCol >= dataModel->GetNumberCols() )
615 sortCol = 0;
616
617 wxString lhs = dataModel->GetValue( lhGroup, sortCol ).Trim( true ).Trim( false );
618 wxString rhs = dataModel->GetValue( rhGroup, sortCol ).Trim( true ).Trim( false );
619
620 if( lhs == rhs || dataModel->ColIsReference( sortCol ) )
621 {
622 wxString lhRef = lhGroup.m_Refs[0].GetRef() + lhGroup.m_Refs[0].GetRefNumber();
623 wxString rhRef = rhGroup.m_Refs[0].GetRef() + rhGroup.m_Refs[0].GetRefNumber();
624 return local_cmp( StrNumCmp( lhRef, rhRef, true ), 0 );
625 }
626 else
627 {
628 return local_cmp( ValueStringCompare( lhs, rhs ), 0 );
629 }
630}
631
632
634{
636
637 // We're going to sort the rows based on their first reference, so the first reference
638 // had better be the lowest one.
639 for( DATA_MODEL_ROW& row : m_rows )
640 {
641 std::sort( row.m_Refs.begin(), row.m_Refs.end(),
642 []( const SCH_REFERENCE& lhs, const SCH_REFERENCE& rhs )
643 {
644 wxString lhs_ref( lhs.GetRef() << lhs.GetRefNumber() );
645 wxString rhs_ref( rhs.GetRef() << rhs.GetRefNumber() );
646 return StrNumCmp( lhs_ref, rhs_ref, true ) < 0;
647 } );
648 }
649
650 std::sort( m_rows.begin(), m_rows.end(),
651 [this]( const DATA_MODEL_ROW& lhs, const DATA_MODEL_ROW& rhs ) -> bool
652 {
653 return cmp( lhs, rhs, this, m_sortColumn, m_sortAscending );
654 } );
655
656 // Time to renumber the item numbers
657 int itemNumber = 1;
658
659 for( DATA_MODEL_ROW& row : m_rows )
660 {
661 row.m_ItemNumber = itemNumber++;
662 }
663
665}
666
667
669{
670 // If items are unannotated then we can't tell if they're units of the same symbol or not
671 if( lhRef.GetRefNumber() == wxT( "?" ) )
672 return false;
673
674 return ( lhRef.GetRef() == rhRef.GetRef() && lhRef.GetRefNumber() == rhRef.GetRefNumber() );
675}
676
677
679{
681 bool matchFound = false;
682
683 if( refCol == -1 )
684 return false;
685
686 // First check the reference column. This can be done directly out of the
687 // SCH_REFERENCEs as the references can't be edited in the grid.
688 if( m_cols[refCol].m_group )
689 {
690 // if we're grouping by reference, then only the prefix must match
691 if( lhRef.GetRef() != rhRef.GetRef() )
692 return false;
693
694 matchFound = true;
695 }
696
697 KIID_PATH lhRefKey = makeDataStoreKey( lhRef.GetSheetPath(), *lhRef.GetSymbol() );
698 KIID_PATH rhRefKey = makeDataStoreKey( rhRef.GetSheetPath(), *rhRef.GetSymbol() );
699
700 // Now check all the other columns.
701 for( size_t i = 0; i < m_cols.size(); ++i )
702 {
703 //Handled already
704 if( static_cast<int>( i ) == refCol )
705 continue;
706
707 if( !m_cols[i].m_group )
708 continue;
709
710 // If the field is generated (e.g. ${QUANTITY}), we need to resolve it through the symbol
711 // to get the actual current value; otherwise we need to pull it out of the store so the
712 // refresh can regroup based on values that haven't been applied to the schematic yet.
713 wxString lh, rh;
714
715 if( IsGeneratedField( m_cols[i].m_fieldName )
716 || IsGeneratedField( m_dataStore[lhRefKey][m_cols[i].m_fieldName] ) )
717 {
718 lh = getFieldShownText( lhRef, m_cols[i].m_fieldName );
719 }
720 else
721 {
722 lh = m_dataStore[lhRefKey][m_cols[i].m_fieldName];
723 }
724
725 if( IsGeneratedField( m_cols[i].m_fieldName )
726 || IsGeneratedField( m_dataStore[rhRefKey][m_cols[i].m_fieldName] ) )
727 {
728 rh = getFieldShownText( rhRef, m_cols[i].m_fieldName );
729 }
730 else
731 {
732 rh = m_dataStore[rhRefKey][m_cols[i].m_fieldName];
733 }
734
735 wxString fieldName = m_cols[i].m_fieldName;
736
737 if( lh != rh )
738 return false;
739
740 matchFound = true;
741 }
742
743 return matchFound;
744}
745
746
748 const wxString& aFieldName )
749{
750 SCH_FIELD* field = aRef.GetSymbol()->GetField( aFieldName );
751
752 if( field )
753 {
754 if( field->IsPrivate() )
755 return wxEmptyString;
756 else
757 return field->GetShownText( &aRef.GetSheetPath(), false );
758 }
759
760 // Handle generated fields with variables as names (e.g. ${QUANTITY}) that are not present in
761 // the symbol by giving them the correct value by resolving against the symbol
762 if( IsGeneratedField( aFieldName ) )
763 {
764 int depth = 0;
765 const SCH_SHEET_PATH& path = aRef.GetSheetPath();
766
767 std::function<bool( wxString* )> symbolResolver =
768 [&]( wxString* token ) -> bool
769 {
770 return aRef.GetSymbol()->ResolveTextVar( &path, token, depth + 1 );
771 };
772
773 return ExpandTextVars( aFieldName, &symbolResolver );
774 }
775
776 return wxEmptyString;
777}
778
779
780bool FIELDS_EDITOR_GRID_DATA_MODEL::isAttribute( const wxString& aFieldName )
781{
782 return aFieldName == wxS( "${DNP}" )
783 || aFieldName == wxS( "${EXCLUDE_FROM_BOARD}" )
784 || aFieldName == wxS( "${EXCLUDE_FROM_BOM}" )
785 || aFieldName == wxS( "${EXCLUDE_FROM_SIM}" );
786}
787
788
789wxString FIELDS_EDITOR_GRID_DATA_MODEL::getAttributeValue( const SCH_SYMBOL& aSymbol, const wxString& aAttributeName,
790 const std::set<wxString>& aVariantNames )
791{
792 wxString retv;
793
794 auto getAttrString = [&]( const wxString aVariantName = wxEmptyString )->wxString
795 {
796 if( aAttributeName == wxS( "${DNP}" ) )
797 return aSymbol.GetDNP( nullptr, aVariantName ) ? wxS( "1" ) : wxS( "0" );
798
799 if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
800 return aSymbol.GetExcludedFromBoard() ? wxS( "1" ) : wxS( "0" );
801
802 if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
803 return aSymbol.GetExcludedFromBOM( nullptr, aVariantName ) ? wxS( "1" ) : wxS( "0" );
804
805 if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
806 return aSymbol.GetExcludedFromSim( nullptr, aVariantName ) ? wxS( "1" ) : wxS( "0" );
807
808 return wxS( "0" );
809 };
810
811 if( aVariantNames.empty() )
812 {
813 retv = getAttrString();
814 }
815 else
816 {
817 for( const wxString& variantName : aVariantNames )
818 {
819 if( retv.IsEmpty() )
820 {
821 retv = getAttrString( variantName );
822 continue;
823 }
824
825 if( retv != getAttrString( variantName ) )
826 retv = multipleValues;
827 }
828 }
829
830 return retv;
831}
832
833
835 const wxString& aAttributeName,
836 const wxString& aValue,
837 const wxString& aVariantName )
838{
839 if( aValue == multipleValues )
840 return false;
841
842 bool attrChanged = false;
843 bool newValue = aValue == wxS( "1" );
844
845 if( aAttributeName == wxS( "${DNP}" ) )
846 {
847 attrChanged = aSymbol.GetDNP( nullptr, aVariantName ) != newValue;
848 aSymbol.SetDNP( newValue, nullptr, aVariantName );
849 }
850 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
851 {
852 attrChanged = aSymbol.GetExcludedFromBoard() != newValue;
853 aSymbol.SetExcludedFromBoard( newValue );
854 }
855 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
856 {
857 attrChanged = aSymbol.GetExcludedFromBOM( nullptr, aVariantName ) != newValue;
858 aSymbol.SetExcludedFromBOM( newValue, nullptr, aVariantName );
859 }
860 else if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
861 {
862 attrChanged = aSymbol.GetExcludedFromSim( nullptr, aVariantName ) != newValue;
863 aSymbol.SetExcludedFromSim( newValue, nullptr, aVariantName );
864 }
865
866 return attrChanged;
867}
868
869
874
875
880
881
883{
884 if( !m_rebuildsEnabled )
885 return;
886
887 if( GetView() )
888 {
889 // Commit any pending in-place edits before the row gets moved out from under
890 // the editor.
891 static_cast<WX_GRID*>( GetView() )->CommitPendingChanges( true );
892
893 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, m_rows.size() );
894 GetView()->ProcessTableMessage( msg );
895 }
896
897 m_rows.clear();
898
899 EDA_COMBINED_MATCHER matcher( m_filter.Lower(), CTX_SEARCH );
900
901 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
902 {
904
905 if( !m_filter.IsEmpty() && !matcher.Find( ref.GetFullRef().Lower() ) )
906 continue;
907
908 if( m_excludeDNP && ( ref.GetSymbol()->GetDNP( &ref.GetSheetPath() )
909 || ref.GetSheetPath().GetDNP() ) )
910 {
911 continue;
912 }
913
915 || ref.GetSheetPath().GetExcludedFromBOM() ) )
916 {
917 continue;
918 }
919
920 // Check if the symbol if on the current sheet or, in the sheet path somewhere
921 // depending on scope
922 if( ( m_scope == SCOPE::SCOPE_SHEET && ref.GetSheetPath() != m_path )
923 || ( m_scope == SCOPE::SCOPE_SHEET_RECURSIVE
924 && !ref.GetSheetPath().IsContainedWithin( m_path ) ) )
925 {
926 continue;
927 }
928
929 bool matchFound = false;
930
931 // Performance optimization for ungrouped case to skip the N^2 for loop
932 if( !m_groupingEnabled && !ref.IsMultiUnit() )
933 {
934 m_rows.emplace_back( DATA_MODEL_ROW( ref, GROUP_SINGLETON ) );
935 continue;
936 }
937
938 // See if we already have a row which this symbol fits into
939 for( DATA_MODEL_ROW& row : m_rows )
940 {
941 // all group members must have identical refs so just use the first one
942 SCH_REFERENCE rowRef = row.m_Refs[0];
943
944 if( unitMatch( ref, rowRef ) )
945 {
946 matchFound = true;
947 row.m_Refs.push_back( ref );
948 break;
949 }
950 else if( m_groupingEnabled && groupMatch( ref, rowRef ) )
951 {
952 matchFound = true;
953 row.m_Refs.push_back( ref );
954 row.m_Flag = GROUP_COLLAPSED;
955 break;
956 }
957 }
958
959 if( !matchFound )
960 m_rows.emplace_back( DATA_MODEL_ROW( ref, GROUP_SINGLETON ) );
961 }
962
963 if( GetView() )
964 {
965 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_rows.size() );
966 GetView()->ProcessTableMessage( msg );
967 }
968
969 Sort();
970}
971
972
974{
975 std::vector<DATA_MODEL_ROW> children;
976
977 for( SCH_REFERENCE& ref : m_rows[aRow].m_Refs )
978 {
979 bool matchFound = false;
980
981 // See if we already have a child group which this symbol fits into
982 for( DATA_MODEL_ROW& child : children )
983 {
984 // group members are by definition all matching, so just check
985 // against the first member
986 if( unitMatch( ref, child.m_Refs[0] ) )
987 {
988 matchFound = true;
989 child.m_Refs.push_back( ref );
990 break;
991 }
992 }
993
994 if( !matchFound )
995 children.emplace_back( DATA_MODEL_ROW( ref, CHILD_ITEM ) );
996 }
997
998 if( children.size() < 2 )
999 return;
1000
1001 std::sort( children.begin(), children.end(),
1002 [this]( const DATA_MODEL_ROW& lhs, const DATA_MODEL_ROW& rhs ) -> bool
1003 {
1004 return cmp( lhs, rhs, this, m_sortColumn, m_sortAscending );
1005 } );
1006
1007 m_rows[aRow].m_Flag = GROUP_EXPANDED;
1008 m_rows.insert( m_rows.begin() + aRow + 1, children.begin(), children.end() );
1009
1010 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, aRow, children.size() );
1011 GetView()->ProcessTableMessage( msg );
1012}
1013
1014
1016{
1017 auto firstChild = m_rows.begin() + aRow + 1;
1018 auto afterLastChild = firstChild;
1019 int deleted = 0;
1020
1021 while( afterLastChild != m_rows.end() && afterLastChild->m_Flag == CHILD_ITEM )
1022 {
1023 deleted++;
1024 afterLastChild++;
1025 }
1026
1027 m_rows[aRow].m_Flag = GROUP_COLLAPSED;
1028 m_rows.erase( firstChild, afterLastChild );
1029
1030 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aRow + 1, deleted );
1031 GetView()->ProcessTableMessage( msg );
1032}
1033
1034
1036{
1037 DATA_MODEL_ROW& group = m_rows[aRow];
1038
1039 if( group.m_Flag == GROUP_COLLAPSED )
1040 ExpandRow( aRow );
1041 else if( group.m_Flag == GROUP_EXPANDED )
1042 CollapseRow( aRow );
1043}
1044
1045
1047{
1048 for( size_t i = 0; i < m_rows.size(); ++i )
1049 {
1050 if( m_rows[i].m_Flag == GROUP_EXPANDED )
1051 {
1052 CollapseRow( i );
1054 }
1055 }
1056}
1057
1058
1060{
1061 for( size_t i = 0; i < m_rows.size(); ++i )
1062 {
1063 if( m_rows[i].m_Flag == GROUP_COLLAPSED_DURING_SORT )
1064 ExpandRow( i );
1065 }
1066}
1067
1068
1070 std::set<wxString>& aVariantNames )
1071{
1072 bool symbolModified = false;
1073 std::unique_ptr<SCH_SYMBOL> symbolCopy;
1074
1075 for( size_t i = 0; i < m_symbolsList.GetCount(); i++ )
1076 {
1077 SCH_SYMBOL* symbol = m_symbolsList[i].GetSymbol();
1078 SCH_SYMBOL* nextSymbol = nullptr;
1079
1080 if( ( i + 1 ) < m_symbolsList.GetCount() )
1081 nextSymbol = m_symbolsList[i + 1].GetSymbol();
1082
1083 if( i == 0 )
1084 symbolCopy = std::make_unique<SCH_SYMBOL>( *symbol );
1085
1086 KIID_PATH key = makeDataStoreKey( m_symbolsList[i].GetSheetPath(), *symbol );
1087 const std::map<wxString, wxString>& fieldStore = m_dataStore[key];
1088
1089 for( const auto& [srcName, srcValue] : fieldStore )
1090 {
1091 // Attributes bypass the field logic, so handle them first
1092 if( isAttribute( srcName ) )
1093 {
1094 if( aVariantNames.empty() )
1095 {
1096 symbolModified |= setAttributeValue( *symbol, srcName, srcValue );
1097 }
1098 else
1099 {
1100 for( const wxString& name : aVariantNames )
1101 symbolModified |= setAttributeValue( *symbol, srcName, srcValue, name );
1102 }
1103
1104 continue;
1105 }
1106
1107 // Skip generated fields with variables as names (e.g. ${QUANTITY});
1108 // they can't be edited
1109 if( IsGeneratedField( srcName ) )
1110 continue;
1111
1112 // Don't change values in the case of multiple variants selected.
1113 if( srcValue == multipleValues )
1114 continue;
1115
1116 SCH_FIELD* destField = symbol->GetField( srcName );
1117
1118 if( destField && destField->IsPrivate() )
1119 {
1120 if( srcValue.IsEmpty() )
1121 continue;
1122 else
1123 destField->SetPrivate( false );
1124 }
1125
1126 int col = GetFieldNameCol( srcName );
1127 bool userAdded = ( col != -1 && m_cols[col].m_userAdded );
1128
1129 // Add a not existing field if it has a value for this symbol
1130 bool createField = !destField && ( !srcValue.IsEmpty() || userAdded );
1131
1132 if( createField )
1133 {
1134 destField = symbol->AddField( SCH_FIELD( symbol, FIELD_T::USER, srcName ) );
1135 destField->SetTextAngle( symbol->GetField( FIELD_T::REFERENCE )->GetTextAngle() );
1136
1137 if( const TEMPLATE_FIELDNAME* srcTemplate = aTemplateFieldnames.GetFieldName( srcName ) )
1138 destField->SetVisible( srcTemplate->m_Visible );
1139 else
1140 destField->SetVisible( false );
1141
1142 destField->SetTextPos( symbol->GetPosition() );
1143 symbolModified = true;
1144 }
1145
1146 if( !destField )
1147 continue;
1148
1149 // Reference is not editable from this dialog
1150 if( destField->GetId() == FIELD_T::REFERENCE )
1151 continue;
1152
1153 wxString previousValue = destField->GetText();
1154
1155 if( aVariantNames.empty() )
1156 {
1157 destField->SetText( symbol->Schematic()->ConvertRefsToKIIDs( srcValue ) );
1158 }
1159 else
1160 {
1161 for( const wxString& variantName : aVariantNames )
1162 {
1163 std::optional<SCH_SYMBOL_VARIANT> variant = symbol->GetVariant( m_symbolsList[i].GetSheetPath(),
1164 variantName );
1165
1166 if( !variant )
1167 {
1168 SCH_SYMBOL_VARIANT newVariant( variantName );
1169
1170 newVariant.m_Fields[srcName] = srcValue;
1171 symbol->AddVariant( m_symbolsList[i].GetSheetPath(), newVariant );
1172 symbolModified |= true;
1173 }
1174 else if( !variant->m_Fields.contains( srcName )
1175 || ( variant->m_Fields[srcName] != srcValue ) )
1176 {
1177 variant->m_Fields[srcName] = srcValue;
1178 symbol->AddVariant( m_symbolsList[i].GetSheetPath(), *variant );
1179 symbolModified |= true;
1180 }
1181 }
1182 }
1183
1184 if( !createField && ( previousValue != srcValue ) )
1185 symbolModified = true;
1186 }
1187
1188 for( int ii = static_cast<int>( symbol->GetFields().size() ) - 1; ii >= 0; ii-- )
1189 {
1190 if( symbol->GetFields()[ii].IsMandatory() || symbol->GetFields()[ii].IsPrivate() )
1191 continue;
1192
1193 if( fieldStore.count( symbol->GetFields()[ii].GetName() ) == 0 )
1194 {
1195 symbol->GetFields().erase( symbol->GetFields().begin() + ii );
1196 symbolModified = true;
1197 }
1198 }
1199
1200 if( symbolModified && ( symbol != nextSymbol ) )
1201 aCommit.Modified( symbol, symbolCopy.release(), m_symbolsList[i].GetSheetPath().LastScreen() );
1202
1203 // Only reset the modified flag and next symbol copy if the next symbol is different from the current one.
1204 if( symbol != nextSymbol )
1205 {
1206 if( nextSymbol )
1207 symbolCopy = std::make_unique<SCH_SYMBOL>( *nextSymbol );
1208 else
1209 symbolCopy.reset( nullptr );
1210
1211 symbolModified = false;
1212 }
1213 }
1214
1215 m_edited = false;
1216}
1217
1218
1220{
1221 int width = 0;
1222
1223 if( ColIsReference( aCol ) )
1224 {
1225 for( int row = 0; row < GetNumberRows(); ++row )
1226 width = std::max( width, KIUI::GetTextSize( GetValue( row, aCol ), GetView() ).x );
1227 }
1228 else
1229 {
1230 wxString fieldName = GetColFieldName( aCol ); // symbol fieldName or Qty string
1231
1232 for( unsigned symbolRef = 0; symbolRef < m_symbolsList.GetCount(); ++symbolRef )
1233 {
1234 KIID_PATH key = makeDataStoreKey( m_symbolsList[symbolRef].GetSheetPath(),
1235 *m_symbolsList[symbolRef].GetSymbol() );
1236 wxString text = m_dataStore[key][fieldName];
1237
1238 width = std::max( width, KIUI::GetTextSize( text, GetView() ).x );
1239 }
1240 }
1241
1242 return width;
1243}
1244
1245
1247 const std::set<wxString>& aVariantNames )
1248{
1249 // Hide and un-group everything by default
1250 for( size_t i = 0; i < m_cols.size(); i++ )
1251 {
1252 SetShowColumn( i, false );
1253 SetGroupColumn( i, false );
1254 }
1255
1256 std::set<wxString> seen;
1257 std::vector<wxString> order;
1258
1259 // Set columns that are present and shown
1260 for( const BOM_FIELD& field : aPreset.fieldsOrdered )
1261 {
1262 // Ignore empty fields
1263 if( !field.name || seen.count( field.name ) )
1264 continue;
1265
1266 seen.insert( field.name );
1267 order.emplace_back( field.name );
1268
1269 int col = GetFieldNameCol( field.name );
1270
1271 // Add any missing fields, if the user doesn't add any data
1272 // they won't be saved to the symbols anyway
1273 if( col == -1 )
1274 {
1275 AddColumn( field.name, field.label, true, aVariantNames );
1276 col = GetFieldNameCol( field.name );
1277 }
1278 else
1279 {
1280 SetColLabelValue( col, field.label );
1281 }
1282
1283 SetGroupColumn( col, field.groupBy );
1284 SetShowColumn( col, field.show );
1285 }
1286
1287 // Set grouping columns
1289
1290 SetFieldsOrder( order );
1291
1292 // Set our sorting
1293 int sortCol = GetFieldNameCol( aPreset.sortField );
1294
1295 if( sortCol == -1 )
1297
1298 SetSorting( sortCol, aPreset.sortAsc );
1299
1300 SetFilter( aPreset.filterString );
1301 SetExcludeDNP( aPreset.excludeDNP );
1303
1304 RebuildRows();
1305}
1306
1307
1309{
1310 BOM_PRESET current;
1311 current.readOnly = false;
1312 current.fieldsOrdered = GetFieldsOrdered();
1313
1314 if( GetSortCol() >= 0 && GetSortCol() < GetNumberCols() )
1315 current.sortField = GetColFieldName( GetSortCol() );
1316
1317 current.sortAsc = GetSortAsc();
1318 current.filterString = GetFilter();
1319 current.groupSymbols = GetGroupingEnabled();
1320 current.excludeDNP = GetExcludeDNP();
1322
1323 return current;
1324}
1325
1326
1328{
1329 wxString out;
1330
1331 if( m_cols.empty() )
1332 return out;
1333
1334 int last_col = -1;
1335
1336 // Find the location for the line terminator
1337 for( size_t col = 0; col < m_cols.size(); col++ )
1338 {
1339 if( m_cols[col].m_show )
1340 last_col = static_cast<int>( col );
1341 }
1342
1343 // No shown columns
1344 if( last_col == -1 )
1345 return out;
1346
1347 auto formatField =
1348 [&]( wxString field, bool last ) -> wxString
1349 {
1350 if( !settings.keepLineBreaks )
1351 {
1352 field.Replace( wxS( "\r" ), wxS( "" ) );
1353 field.Replace( wxS( "\n" ), wxS( "" ) );
1354 }
1355
1356 if( !settings.keepTabs )
1357 {
1358 field.Replace( wxS( "\t" ), wxS( "" ) );
1359 }
1360
1361 if( !settings.stringDelimiter.IsEmpty() )
1362 {
1363 field.Replace( settings.stringDelimiter,
1364 settings.stringDelimiter + settings.stringDelimiter );
1365 }
1366
1367 return settings.stringDelimiter + field + settings.stringDelimiter
1368 + ( last ? wxString( wxS( "\n" ) ) : settings.fieldDelimiter );
1369 };
1370
1371 // Column names
1372 for( size_t col = 0; col < m_cols.size(); col++ )
1373 {
1374 if( !m_cols[col].m_show )
1375 continue;
1376
1377 out.Append( formatField( m_cols[col].m_label, col == static_cast<size_t>( last_col ) ) );
1378 }
1379
1380 // Data rows
1381 for( size_t row = 0; row < m_rows.size(); row++ )
1382 {
1383 // Don't output child rows
1384 if( GetRowFlags( static_cast<int>( row ) ) == CHILD_ITEM )
1385 continue;
1386
1387 for( size_t col = 0; col < m_cols.size(); col++ )
1388 {
1389 if( !m_cols[col].m_show )
1390 continue;
1391
1392 // Get the unannotated version of the field, e.g. no "> " or "v " by
1393 out.Append( formatField( GetExportValue( static_cast<int>( row ), static_cast<int>( col ),
1394 settings.refDelimiter, settings.refRangeDelimiter ),
1395 col == static_cast<size_t>( last_col ) ) );
1396 }
1397 }
1398
1399 return out;
1400}
1401
1402
1404{
1405 bool refListChanged = false;
1406
1407 for( const SCH_REFERENCE& ref : aRefs )
1408 {
1409 if( !m_symbolsList.Contains( ref ) )
1410 {
1411 SCH_SYMBOL* symbol = ref.GetSymbol();
1412
1413 m_symbolsList.AddItem( ref );
1414
1415 KIID_PATH key = makeDataStoreKey( ref.GetSheetPath(), *symbol );
1416
1417 // Update the fields of every reference
1418 for( const SCH_FIELD& field : symbol->GetFields() )
1419 {
1420 if( !field.IsPrivate() )
1421 {
1422 wxString name = field.GetCanonicalName();
1423 wxString value = symbol->Schematic()->ConvertKIIDsToRefs( field.GetText() );
1424
1425 m_dataStore[key][name] = value;
1426 }
1427 }
1428
1429 refListChanged = true;
1430 }
1431 }
1432
1433 if( refListChanged )
1434 m_symbolsList.SortBySymbolPtr();
1435}
1436
1437
1439{
1440 // The schematic event listener passes us the symbol after it has been removed,
1441 // so we can't just work with a SCH_REFERENCE_LIST like the other handlers as the
1442 // references are already gone. Instead we need to prune our list.
1443
1444 // Since we now use full KIID_PATH as keys, we need to find and remove all entries
1445 // that correspond to this symbol (their keys end with the symbol's UUID)
1446 KIID symbolUuid = aSymbol.m_Uuid;
1447 std::vector<KIID_PATH> keysToRemove;
1448
1449 for( const auto& [key, value] : m_dataStore )
1450 {
1451 if( !key.empty() && ( key.back() == symbolUuid ) )
1452 keysToRemove.push_back( key );
1453 }
1454
1455 for( const KIID_PATH& key : keysToRemove )
1456 m_dataStore.erase( key );
1457
1458 // Remove all refs that match this symbol using remove_if
1459 m_symbolsList.erase( std::remove_if( m_symbolsList.begin(), m_symbolsList.end(),
1460 [&aSymbol]( const SCH_REFERENCE& ref ) -> bool
1461 {
1462 return ref.GetSymbol()->m_Uuid == aSymbol.m_Uuid;
1463 } ),
1464 m_symbolsList.end() );
1465}
1466
1467
1469{
1470 for( const SCH_REFERENCE& ref : aRefs )
1471 {
1472 int index = m_symbolsList.FindRefByFullPath( ref.GetFullPath() );
1473
1474 if( index != -1 )
1475 {
1476 KIID_PATH key = makeDataStoreKey( ref.GetSheetPath(), *ref.GetSymbol() );
1477 m_dataStore.erase( key );
1478 m_symbolsList.RemoveItem( index );
1479 }
1480 }
1481}
1482
1483
1485 const std::set<wxString>& aVariantNames )
1486{
1487 bool refListChanged = false;
1488
1489 for( const SCH_REFERENCE& ref : aRefs )
1490 {
1491 // Update the fields of every reference. Do this by iterating through the data model
1492 // columns; we must have all fields in the symbol added to the data model at this point,
1493 // and some of the data model columns may be variables that are not present in the symbol
1494 for( const DATA_MODEL_COL& col : m_cols )
1495 updateDataStoreSymbolField( ref, col.m_fieldName, aVariantNames );
1496
1497 if( !m_symbolsList.Contains( ref ) )
1498 {
1499 m_symbolsList.AddItem( ref );
1500 refListChanged = true;
1501 }
1502 }
1503
1504 if( refListChanged )
1505 m_symbolsList.SortBySymbolPtr();
1506}
const char * name
COMMIT & Modified(EDA_ITEM *aItem, EDA_ITEM *aCopy, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition commit.cpp:156
bool Find(const wxString &aTerm, int &aMatchersTriggered, int &aPosition)
Look in all existing matchers, return the earliest match of any of the existing.
const KIID m_Uuid
Definition eda_item.h:516
const EDA_ANGLE & GetTextAngle() const
Definition eda_text.h:147
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:98
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:589
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:397
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:310
int GetFieldNameCol(const wxString &aFieldName) const
void ApplyBomPreset(const BOM_PRESET &preset, const std::set< wxString > &aVariantNames)
std::vector< DATA_MODEL_ROW > m_rows
wxString getAttributeValue(const SCH_SYMBOL &, const wxString &aAttributeName, const std::set< wxString > &aVariantNames)
void SetFieldsOrder(const std::vector< wxString > &aNewOrder)
void UpdateReferences(const SCH_REFERENCE_LIST &aRefs, const std::set< wxString > &aVariantNames)
SCH_REFERENCE_LIST m_symbolsList
The flattened by hierarchy list of symbols.
bool groupMatch(const SCH_REFERENCE &lhRef, const SCH_REFERENCE &rhRef)
void ApplyData(SCH_COMMIT &aCommit, TEMPLATES &aTemplateFieldnames, std::set< wxString > &aVariantNames)
bool unitMatch(const SCH_REFERENCE &lhRef, const SCH_REFERENCE &rhRef)
wxString GetExportValue(int aRow, int aCol, const wxString &refDelimiter, const wxString &refRangeDelimiter)
void AddColumn(const wxString &aFieldName, const wxString &aLabel, bool aAddedByUser, const std::set< wxString > &aVariantNames)
wxString getFieldShownText(const SCH_REFERENCE &aRef, const wxString &aFieldName)
void RenameColumn(int aCol, const wxString &newName)
FIELDS_EDITOR_GRID_DATA_MODEL(const SCH_REFERENCE_LIST &aSymbolsList, wxGridCellAttr *aURLEditor)
bool IsExpanderColumn(int aCol) const override
wxString Export(const BOM_FMT_PRESET &settings)
std::vector< DATA_MODEL_COL > m_cols
void SetSorting(int aCol, bool ascending)
wxGridCellAttr * GetAttr(int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind) override
void SetFilter(const wxString &aFilter)
bool setAttributeValue(SCH_SYMBOL &aSymbol, const wxString &aAttributeName, const wxString &aValue, const wxString &aVariantName=wxEmptyString)
Set the attribute value.
static bool cmp(const DATA_MODEL_ROW &lhGroup, const DATA_MODEL_ROW &rhGroup, FIELDS_EDITOR_GRID_DATA_MODEL *dataModel, int sortCol, bool ascending)
static const wxString ITEM_NUMBER_VARIABLE
void SetIncludeExcludedFromBOM(bool include)
bool isAttribute(const wxString &aFieldName)
wxString GetValue(int aRow, int aCol) override
void updateDataStoreSymbolField(const SCH_REFERENCE &aSymbolRef, const wxString &aFieldName, const std::set< wxString > &aVariantNames)
static const wxString QUANTITY_VARIABLE
void SetGroupColumn(int aCol, bool group)
void RemoveSymbol(const SCH_SYMBOL &aSymbol)
std::vector< BOM_FIELD > GetFieldsOrdered()
void SetValue(int aRow, int aCol, const wxString &aValue) override
std::map< KIID_PATH, std::map< wxString, wxString > > m_dataStore
void SetColLabelValue(int aCol, const wxString &aLabel) override
void RemoveReferences(const SCH_REFERENCE_LIST &aRefs)
void SetShowColumn(int aCol, bool show)
void AddReferences(const SCH_REFERENCE_LIST &aRefs)
Definition kiid.h:49
wxString ConvertKIIDsToRefs(const wxString &aSource) const
wxString ConvertRefsToKIIDs(const wxString &aSource) const
FIELD_T GetId() const
Definition sch_field.h:116
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
void SetText(const wxString &aText) override
void SetPrivate(bool aPrivate)
Definition sch_item.h:252
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:244
bool IsPrivate() const
Definition sch_item.h:253
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, const wxString &refDelimiter, const wxString &refRangeDelimiter)
Return a shorthand string representing all the references in the list.
A helper to define a symbol's reference designator in a schematic.
const SCH_SHEET_PATH & GetSheetPath() const
SCH_SYMBOL * GetSymbol() const
wxString GetRef() const
wxString GetFullRef(bool aIncludeUnit=true) const
Return reference name with unit altogether.
bool IsMultiUnit() const
wxString GetRefNumber() const
int GetRefCount() const
Definition sch_screen.h:169
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
bool GetExcludedFromBOM() const
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
bool IsContainedWithin(const SCH_SHEET_PATH &aSheetPathToTest) const
Check if this path is contained inside aSheetPathToTest.
bool GetDNP() const
Variant information for a schematic symbol.
Schematic symbol object.
Definition sch_symbol.h:76
bool GetExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
virtual void SetDNP(bool aEnable, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
void SetExcludedFromSim(bool aEnable, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear the exclude from simulation flag.
std::optional< SCH_SYMBOL_VARIANT > GetVariant(const SCH_SHEET_PATH &aInstance, const wxString &aVariantName) const
bool GetExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
VECTOR2I GetPosition() const override
Definition sch_symbol.h:811
bool ResolveTextVar(const SCH_SHEET_PATH *aPath, wxString *token, int aDepth=0) const
Resolve any references to system tokens supported by the symbol.
void AddVariant(const SCH_SHEET_PATH &aInstance, const SCH_SYMBOL_VARIANT &aVariant)
SCH_FIELD * AddField(const SCH_FIELD &aField)
Add a field to the symbol.
void SetExcludedFromBOM(bool aEnable, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear the exclude from schematic bill of materials flag.
virtual bool GetDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Set or clear the 'Do Not Populate' flag.
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
void SetExcludedFromBoard(bool aExcludeFromBoard) override
Set or clear exclude from board netlist flag.
Definition symbol.h:207
bool GetExcludedFromBoard() const override
Definition symbol.h:208
const TEMPLATE_FIELDNAME * GetFieldName(const wxString &aName)
Search for aName in the template field name list.
std::map< wxString, wxString > m_Fields
void SetValueAsBool(int aRow, int aCol, bool aValue) override
wxString GetColLabelValue(int aCol) override
std::vector< BOM_FIELD > m_fields
void SetValue(int aRow, int aCol, const wxString &aValue) override
void AppendRow(const wxString &aFieldName, const wxString &aBOMName, bool aShow, bool aGroupBy)
bool GetValueAsBool(int aRow, int aCol) override
void SetCanonicalFieldName(int aRow, const wxString &aName)
wxString GetValue(int aRow, int aCol) override
wxGridCellAttr * enhanceAttr(wxGridCellAttr *aInputAttr, int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind)
Definition wx_grid.cpp:46
wxGridCellAttr * GetAttr(int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind) override
Definition wx_grid.h:67
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:60
bool IsGeneratedField(const wxString &aSource)
Returns true if the string is generated, e.g contains a single text var reference.
Definition common.cpp:137
The common library.
#define _(s)
@ CTX_SEARCH
static wxString multipleValues
static KIID_PATH makeDataStoreKey(const SCH_SHEET_PATH &aSheetPath, const SCH_SYMBOL &aSymbol)
Create a unique key for the data store by combining the KIID_PATH from the SCH_SHEET_PATH with the sy...
#define LABEL_COLUMN
#define DISPLAY_NAME_COLUMN
#define GROUP_BY_COLUMN
#define SHOW_FIELD_COLUMN
KICOMMON_API wxSize GetTextSize(const wxString &aSingleLine, wxWindow *aWindow)
Return the size of aSingleLine of text when it is rendered in aWindow using whatever font is currentl...
Definition ui_common.cpp:78
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
int ValueStringCompare(const wxString &strFWord, const wxString &strSWord)
Compare strings like the strcmp function but handle numbers and modifiers within the string text corr...
bool IsURL(wxString aStr)
Performs a URL sniff-test on a string.
wxString label
wxString name
wxString fieldDelimiter
wxString stringDelimiter
wxString refRangeDelimiter
wxString refDelimiter
wxString sortField
bool groupSymbols
std::vector< BOM_FIELD > fieldsOrdered
bool includeExcludedFromBOM
bool excludeDNP
wxString filterString
std::vector< SCH_REFERENCE > m_Refs
Hold a name of a symbol's field, field value, and default visibility.
wxString GetDefaultFieldName(FIELD_T aFieldId, bool aTranslateForHI)
Return a default symbol field name for a mandatory field type.
#define DO_TRANSLATE
#define MANDATORY_FIELDS
FIELD_T
The set of all field indices assuming an array like sequence that a SCH_COMPONENT or LIB_PART can hol...
@ USER
The field ID hasn't been set yet; field is invalid.
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
wxString GetCanonicalFieldName(FIELD_T aFieldType)
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition ui_common.h:46
@ GROUP_COLLAPSED_DURING_SORT
Definition wx_grid.h:46
@ GROUP_EXPANDED
Definition wx_grid.h:47
@ GROUP_COLLAPSED
Definition wx_grid.h:45
@ GROUP_SINGLETON
Definition wx_grid.h:44
@ CHILD_ITEM
Definition wx_grid.h:48