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