KiCad PCB EDA Suite
Loading...
Searching...
No Matches
fields_data_model.cpp
Go to the documentation of this file.
1#include <wx/string.h>
2#include <wx/debug.h>
3#include <wx/grid.h>
4#include <common.h>
5#include <widgets/wx_grid.h>
8#include "string_utils.h"
9
10#include "fields_data_model.h"
11
12const wxString FIELDS_EDITOR_GRID_DATA_MODEL::QUANTITY_VARIABLE = wxS( "${QUANTITY}" );
13const wxString FIELDS_EDITOR_GRID_DATA_MODEL::ITEM_NUMBER_VARIABLE = wxS( "${ITEM_NUMBER}" );
14
15void FIELDS_EDITOR_GRID_DATA_MODEL::AddColumn( const wxString& aFieldName, const wxString& aLabel,
16 bool aAddedByUser )
17{
18 // Don't add a field twice
19 if( GetFieldNameCol( aFieldName ) != -1 )
20 return;
21
22 m_cols.push_back( { aFieldName, aLabel, aAddedByUser, false, false } );
23
24 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
25 if( SCH_SYMBOL* symbol = m_symbolsList[i].GetSymbol() )
26 updateDataStoreSymbolField( *symbol, aFieldName );
27}
28
29
31 const wxString& aFieldName )
32{
33 if( const SCH_FIELD* field = aSymbol.GetFieldByName( aFieldName ) )
34 m_dataStore[aSymbol.m_Uuid][aFieldName] = field->GetText();
35 else if( isAttribute( aFieldName ) )
36 m_dataStore[aSymbol.m_Uuid][aFieldName] = getAttributeValue( aSymbol, aFieldName );
37 // Handle fields with variables as names that are not present in the symbol
38 // by giving them the correct value
39 else if( IsTextVar( aFieldName ) )
40 m_dataStore[aSymbol.m_Uuid][aFieldName] = aFieldName;
41 else
42 m_dataStore[aSymbol.m_Uuid][aFieldName] = wxEmptyString;
43}
44
45
47{
48 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
49 {
50 if( SCH_SYMBOL* symbol = m_symbolsList[i].GetSymbol() )
51 m_dataStore[symbol->m_Uuid].erase( m_cols[aCol].m_fieldName );
52 }
53
54 m_cols.erase( m_cols.begin() + aCol );
55}
56
57
58void FIELDS_EDITOR_GRID_DATA_MODEL::RenameColumn( int aCol, const wxString& newName )
59{
60 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
61 {
62 SCH_SYMBOL* symbol = m_symbolsList[i].GetSymbol();
63
64 auto node = m_dataStore[symbol->m_Uuid].extract( m_cols[aCol].m_fieldName );
65 node.key() = newName;
66 m_dataStore[symbol->m_Uuid].insert( std::move( node ) );
67 }
68
69 m_cols[aCol].m_fieldName = newName;
70}
71
72
74{
75 for( size_t i = 0; i < m_cols.size(); i++ )
76 {
77 if( m_cols[i].m_fieldName == aFieldName )
78 return (int) i;
79 }
80
81 return -1;
82}
83
84
86{
87 std::vector<BOM_FIELD> fields;
88
89 for( const DATA_MODEL_COL& col : m_cols )
90 fields.push_back( { col.m_fieldName, col.m_label, col.m_show, col.m_group } );
91
92 return fields;
93}
94
95
96void FIELDS_EDITOR_GRID_DATA_MODEL::SetFieldsOrder( const std::vector<wxString>& aNewOrder )
97{
98 size_t foundCount = 0;
99
100 for( const wxString& newField : aNewOrder )
101 {
102 for( size_t i = 0; i < m_cols.size(); i++ )
103 {
104 if( m_cols[i].m_fieldName == newField )
105 {
106 std::swap( m_cols[foundCount], m_cols[i] );
107 foundCount++;
108 }
109 }
110 }
111}
112
113
114wxString FIELDS_EDITOR_GRID_DATA_MODEL::GetValue( int aRow, int aCol )
115{
116 if( ColIsReference( aCol ) )
117 {
118 // Poor-man's tree controls
119 if( m_rows[aRow].m_Flag == GROUP_COLLAPSED )
120 return wxT( "> " ) + GetValue( m_rows[aRow], aCol );
121 else if( m_rows[aRow].m_Flag == GROUP_EXPANDED )
122 return wxT( "v " ) + GetValue( m_rows[aRow], aCol );
123 else if( m_rows[aRow].m_Flag == CHILD_ITEM )
124 return wxT( " " ) + GetValue( m_rows[aRow], aCol );
125 else
126 return wxT( " " ) + GetValue( m_rows[aRow], aCol );
127 }
128 else
129 {
130 return GetValue( m_rows[aRow], aCol );
131 }
132}
133
134
136 const wxString& refDelimiter,
137 const wxString& refRangeDelimiter,
138 bool resolveVars )
139{
140 std::vector<SCH_REFERENCE> references;
141 wxString fieldValue;
142
143 for( const SCH_REFERENCE& ref : group.m_Refs )
144 {
145 if( ColIsReference( aCol ) || ColIsQuantity( aCol ) || ColIsItemNumber( aCol ) )
146 {
147 references.push_back( ref );
148 }
149 else // Other columns are either a single value or ROW_MULTI_ITEMS
150 {
151 const KIID& symbolID = ref.GetSymbol()->m_Uuid;
152
153 if( !m_dataStore.count( symbolID )
154 || !m_dataStore[symbolID].count( m_cols[aCol].m_fieldName ) )
155 {
156 return INDETERMINATE_STATE;
157 }
158
159 wxString refFieldValue;
160
161 if( resolveVars )
162 refFieldValue = getFieldShownText( ref, m_cols[aCol].m_fieldName );
163 else
164 refFieldValue = m_dataStore[symbolID][m_cols[aCol].m_fieldName];
165
166 if( &ref == &group.m_Refs.front() )
167 fieldValue = refFieldValue;
168 else if( fieldValue != refFieldValue )
169 return INDETERMINATE_STATE;
170 }
171 }
172
173 if( ColIsReference( aCol ) || ColIsQuantity( aCol ) || ColIsItemNumber( aCol ) )
174 {
175 // Remove duplicates (other units of multi-unit parts)
176 std::sort( references.begin(), references.end(),
177 []( const SCH_REFERENCE& l, const SCH_REFERENCE& r ) -> bool
178 {
179 wxString l_ref( l.GetRef() << l.GetRefNumber() );
180 wxString r_ref( r.GetRef() << r.GetRefNumber() );
181 return StrNumCmp( l_ref, r_ref, true ) < 0;
182 } );
183
184 auto logicalEnd = std::unique( references.begin(), references.end(),
185 []( const SCH_REFERENCE& l, const SCH_REFERENCE& r ) -> bool
186 {
187 // If unannotated then we can't tell what units belong together
188 // so we have to leave them all
189 if( l.GetRefNumber() == wxT( "?" ) )
190 return false;
191
192 wxString l_ref( l.GetRef() << l.GetRefNumber() );
193 wxString r_ref( r.GetRef() << r.GetRefNumber() );
194 return l_ref == r_ref;
195 } );
196
197 references.erase( logicalEnd, references.end() );
198 }
199
200 if( ColIsReference( aCol ) )
201 fieldValue = SCH_REFERENCE_LIST::Shorthand( references, refDelimiter, refRangeDelimiter );
202 else if( ColIsQuantity( aCol ) )
203 fieldValue = wxString::Format( wxT( "%d" ), (int) references.size() );
204 else if( ColIsItemNumber( aCol ) && group.m_Flag != CHILD_ITEM )
205 fieldValue = wxString::Format( wxT( "%d" ), group.m_ItemNumber );
206
207 return fieldValue;
208}
209
210
211void FIELDS_EDITOR_GRID_DATA_MODEL::SetValue( int aRow, int aCol, const wxString& aValue )
212{
213 wxCHECK_RET( aCol >= 0 && aCol < (int) m_cols.size(), wxS( "Invalid column number" ) );
214
215 // Can't modify references or text variables column, e.g. ${QUANTITY}
216 if( ColIsReference( aCol )
217 || ( IsTextVar( m_cols[aCol].m_fieldName ) && !ColIsAttribute( aCol ) ) )
218 {
219 return;
220 }
221
222 DATA_MODEL_ROW& rowGroup = m_rows[aRow];
223
224 for( const SCH_REFERENCE& ref : rowGroup.m_Refs )
225 m_dataStore[ref.GetSymbol()->m_Uuid][m_cols[aCol].m_fieldName] = aValue;
226
227 m_edited = true;
228}
229
230
232{
233 wxCHECK( aCol >= 0 && aCol < (int) m_cols.size(), false );
235}
236
238{
239 wxCHECK( aCol >= 0 && aCol < (int) m_cols.size(), false );
240 return m_cols[aCol].m_fieldName == TEMPLATE_FIELDNAME::GetDefaultFieldName( VALUE_FIELD );
241}
242
244{
245 wxCHECK( aCol >= 0 && aCol < (int) m_cols.size(), false );
246 return m_cols[aCol].m_fieldName == QUANTITY_VARIABLE;
247}
248
250{
251 wxCHECK( aCol >= 0 && aCol < (int) m_cols.size(), false );
252 return m_cols[aCol].m_fieldName == ITEM_NUMBER_VARIABLE;
253}
254
256{
257 wxCHECK( aCol >= 0 && aCol < (int) m_cols.size(), false );
258 return isAttribute( m_cols[aCol].m_fieldName );
259}
260
261
263 const DATA_MODEL_ROW& rhGroup,
264 FIELDS_EDITOR_GRID_DATA_MODEL* dataModel, int sortCol,
265 bool ascending )
266{
267 // Empty rows always go to the bottom, whether ascending or descending
268 if( lhGroup.m_Refs.size() == 0 )
269 return true;
270 else if( rhGroup.m_Refs.size() == 0 )
271 return false;
272
273 // N.B. To meet the iterator sort conditions, we cannot simply invert the truth
274 // to get the opposite sort. i.e. ~(a<b) != (a>b)
275 auto local_cmp =
276 [ ascending ]( const auto a, const auto b )
277 {
278 if( ascending )
279 return a < b;
280 else
281 return a > b;
282 };
283
284 // Primary sort key is sortCol; secondary is always REFERENCE (column 0)
285
286 wxString lhs = dataModel->GetValue( (DATA_MODEL_ROW&) lhGroup, sortCol );
287 wxString rhs = dataModel->GetValue( (DATA_MODEL_ROW&) rhGroup, sortCol );
288
289 if( lhs == rhs || sortCol == REFERENCE_FIELD )
290 {
291 wxString lhRef = lhGroup.m_Refs[0].GetRef() + lhGroup.m_Refs[0].GetRefNumber();
292 wxString rhRef = rhGroup.m_Refs[0].GetRef() + rhGroup.m_Refs[0].GetRefNumber();
293 return local_cmp( StrNumCmp( lhRef, rhRef, true ), 0 );
294 }
295 else
296 {
297 return local_cmp( ValueStringCompare( lhs, rhs ), 0 );
298 }
299}
300
301
303{
305
306 // We're going to sort the rows based on their first reference, so the first reference
307 // had better be the lowest one.
308 for( DATA_MODEL_ROW& row : m_rows )
309 {
310 std::sort( row.m_Refs.begin(), row.m_Refs.end(),
311 []( const SCH_REFERENCE& lhs, const SCH_REFERENCE& rhs )
312 {
313 wxString lhs_ref( lhs.GetRef() << lhs.GetRefNumber() );
314 wxString rhs_ref( rhs.GetRef() << rhs.GetRefNumber() );
315 return StrNumCmp( lhs_ref, rhs_ref, true ) < 0;
316 } );
317 }
318
319 std::sort( m_rows.begin(), m_rows.end(),
320 [this]( const DATA_MODEL_ROW& lhs, const DATA_MODEL_ROW& rhs ) -> bool
321 {
322 return cmp( lhs, rhs, this, m_sortColumn, m_sortAscending );
323 } );
324
325 // Time to renumber the item numbers
326 int itemNumber = 1;
327 for( DATA_MODEL_ROW& row : m_rows )
328 {
329 row.m_ItemNumber = itemNumber++;
330 }
331
333}
334
335
337 const SCH_REFERENCE& rhRef )
338{
339 // If items are unannotated then we can't tell if they're units of the same symbol or not
340 if( lhRef.GetRefNumber() == wxT( "?" ) )
341 return false;
342
343 return ( lhRef.GetRef() == rhRef.GetRef() && lhRef.GetRefNumber() == rhRef.GetRefNumber() );
344}
345
347 const SCH_REFERENCE& rhRef )
348
349{
351 bool matchFound = false;
352
353 if( refCol == -1 )
354 return false;
355
356 // First check the reference column. This can be done directly out of the
357 // SCH_REFERENCEs as the references can't be edited in the grid.
358 if( m_cols[refCol].m_group )
359 {
360 // if we're grouping by reference, then only the prefix must match
361 if( lhRef.GetRef() != rhRef.GetRef() )
362 return false;
363
364 matchFound = true;
365 }
366
367 // Now check all the other columns. This must be done out of the dataStore
368 // for the refresh button to work after editing.
369 for( size_t i = 0; i < m_cols.size(); ++i )
370 {
371 //Handled already
372 if( (int) i == refCol )
373 continue;
374
375 if( !m_cols[i].m_group )
376 continue;
377
378 if( getFieldShownText( lhRef, m_cols[i].m_fieldName )
379 != getFieldShownText( rhRef, m_cols[i].m_fieldName ) )
380 {
381 return false;
382 }
383
384 matchFound = true;
385 }
386
387 return matchFound;
388}
389
390
392 const wxString& aFieldName )
393{
394 SCH_FIELD* field = aRef.GetSymbol()->GetFieldByName( aFieldName );
395
396 if( field )
397 return field->GetShownText( &aRef.GetSheetPath(), false );
398
399 // Handle fields with variables as names that are not present in the symbol
400 // by giving them the correct value by resolving against the symbol
401 if( IsTextVar( aFieldName ) )
402 {
403 int depth = 0;
404 const SCH_SHEET_PATH& path = aRef.GetSheetPath();
405
406 std::function<bool( wxString* )> symbolResolver =
407 [&]( wxString* token ) -> bool
408 {
409 return aRef.GetSymbol()->ResolveTextVar( &path, token, depth + 1 );
410 };
411
412 return ExpandTextVars( aFieldName, &symbolResolver );
413 }
414
415 return wxEmptyString;
416}
417
418
419bool FIELDS_EDITOR_GRID_DATA_MODEL::isAttribute( const wxString& aFieldName )
420{
421 return aFieldName == wxS( "${DNP}" )
422 || aFieldName == wxS( "${EXCLUDE_FROM_BOARD}" )
423 || aFieldName == wxS( "${EXCLUDE_FROM_BOM}" )
424 || aFieldName == wxS( "${EXCLUDE_FROM_SIM}" );
425}
426
427
429 const wxString& aAttributeName )
430{
431 if( aAttributeName == wxS( "${DNP}" ) )
432 return aSymbol.GetDNP() ? wxS( "1" ) : wxS( "0" );
433
434 if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
435 return aSymbol.GetExcludedFromBoard() ? wxS( "1" ) : wxS( "0" );
436
437 if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
438 return aSymbol.GetExcludedFromBOM() ? wxS( "1" ) : wxS( "0" );
439
440 if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
441 return aSymbol.GetExcludedFromSim() ? wxS( "1" ) : wxS( "0" );
442
443 return wxS( "0" );
444}
445
447 const wxString& aAttributeName,
448 const wxString& aValue )
449{
450 if( aAttributeName == wxS( "${DNP}" ) )
451 aSymbol.SetDNP( aValue == wxS( "1" ) );
452 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
453 aSymbol.SetExcludedFromBoard( aValue == wxS( "1" ) );
454 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
455 aSymbol.SetExcludedFromBOM( aValue == wxS( "1" ) );
456 else if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
457 aSymbol.SetExcludedFromSim( aValue == wxS( "1" ) );
458}
459
460
462{
463 m_rebuildsEnabled = true;
464}
465
466
468{
469 m_rebuildsEnabled = false;
470}
471
472
474{
475 if( !m_rebuildsEnabled )
476 return;
477
478 if( GetView() )
479 {
480 // Commit any pending in-place edits before the row gets moved out from under
481 // the editor.
482 static_cast<WX_GRID*>( GetView() )->CommitPendingChanges( true );
483
484 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, m_rows.size() );
485 GetView()->ProcessTableMessage( msg );
486 }
487
488 m_rows.clear();
489
490 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
491 {
493
494 if( !m_filter.IsEmpty() && !WildCompareString( m_filter, ref.GetFullRef(), false ) )
495 continue;
496
497 if( m_excludeDNP && ref.GetSymbol()->GetDNP() )
498 continue;
499
501 continue;
502
503 // Check if the symbol if on the current sheet or, in the sheet path somewhere
504 // depending on scope
505 if( ( m_scope == SCOPE::SCOPE_SHEET && ref.GetSheetPath() != m_path )
506 || ( m_scope == SCOPE::SCOPE_SHEET_RECURSIVE
507 && !ref.GetSheetPath().IsContainedWithin( m_path ) ) )
508 {
509 continue;
510 }
511
512 bool matchFound = false;
513
514 // Performance optimization for ungrouped case to skip the N^2 for loop
515 if( !m_groupingEnabled && !ref.IsMultiUnit() )
516 {
517 m_rows.emplace_back( DATA_MODEL_ROW( ref, GROUP_SINGLETON ) );
518 continue;
519 }
520
521 // See if we already have a row which this symbol fits into
522 for( DATA_MODEL_ROW& row : m_rows )
523 {
524 // all group members must have identical refs so just use the first one
525 SCH_REFERENCE rowRef = row.m_Refs[0];
526
527 if( unitMatch( ref, rowRef ) )
528 {
529 matchFound = true;
530 row.m_Refs.push_back( ref );
531 break;
532 }
533 else if( m_groupingEnabled && groupMatch( ref, rowRef ) )
534 {
535 matchFound = true;
536 row.m_Refs.push_back( ref );
537 row.m_Flag = GROUP_COLLAPSED;
538 break;
539 }
540 }
541
542 if( !matchFound )
543 m_rows.emplace_back( DATA_MODEL_ROW( ref, GROUP_SINGLETON ) );
544 }
545
546 if( GetView() )
547 {
548 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_rows.size() );
549 GetView()->ProcessTableMessage( msg );
550 }
551
552 Sort();
553}
554
556{
557 std::vector<DATA_MODEL_ROW> children;
558
559 for( SCH_REFERENCE& ref : m_rows[aRow].m_Refs )
560 {
561 bool matchFound = false;
562
563 // See if we already have a child group which this symbol fits into
564 for( DATA_MODEL_ROW& child : children )
565 {
566 // group members are by definition all matching, so just check
567 // against the first member
568 if( unitMatch( ref, child.m_Refs[0] ) )
569 {
570 matchFound = true;
571 child.m_Refs.push_back( ref );
572 break;
573 }
574 }
575
576 if( !matchFound )
577 children.emplace_back( DATA_MODEL_ROW( ref, CHILD_ITEM ) );
578 }
579
580 if( children.size() < 2 )
581 return;
582
583 std::sort( children.begin(), children.end(),
584 [this]( const DATA_MODEL_ROW& lhs, const DATA_MODEL_ROW& rhs ) -> bool
585 {
586 return cmp( lhs, rhs, this, m_sortColumn, m_sortAscending );
587 } );
588
589 m_rows[aRow].m_Flag = GROUP_EXPANDED;
590 m_rows.insert( m_rows.begin() + aRow + 1, children.begin(), children.end() );
591
592 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, aRow, children.size() );
593 GetView()->ProcessTableMessage( msg );
594}
595
597{
598 auto firstChild = m_rows.begin() + aRow + 1;
599 auto afterLastChild = firstChild;
600 int deleted = 0;
601
602 while( afterLastChild != m_rows.end() && afterLastChild->m_Flag == CHILD_ITEM )
603 {
604 deleted++;
605 afterLastChild++;
606 }
607
608 m_rows[aRow].m_Flag = GROUP_COLLAPSED;
609 m_rows.erase( firstChild, afterLastChild );
610
611 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aRow + 1, deleted );
612 GetView()->ProcessTableMessage( msg );
613}
614
615
617{
618 DATA_MODEL_ROW& group = m_rows[aRow];
619
620 if( group.m_Flag == GROUP_COLLAPSED )
621 ExpandRow( aRow );
622 else if( group.m_Flag == GROUP_EXPANDED )
623 CollapseRow( aRow );
624}
625
626
628{
629 for( size_t i = 0; i < m_rows.size(); ++i )
630 {
631 if( m_rows[i].m_Flag == GROUP_EXPANDED )
632 {
633 CollapseRow( i );
635 }
636 }
637}
638
639
641{
642 for( size_t i = 0; i < m_rows.size(); ++i )
643 {
644 if( m_rows[i].m_Flag == GROUP_COLLAPSED_DURING_SORT )
645 ExpandRow( i );
646 }
647}
648
649
651 std::function<void( SCH_SYMBOL&, SCH_SHEET_PATH& )> symbolChangeHandler )
652{
653 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
654 {
655 SCH_SYMBOL& symbol = *m_symbolsList[i].GetSymbol();
656
657 symbolChangeHandler( symbol, m_symbolsList[i].GetSheetPath() );
658
659 const std::map<wxString, wxString>& fieldStore = m_dataStore[symbol.m_Uuid];
660
661 for( const std::pair<wxString, wxString> srcData : fieldStore )
662 {
663 const wxString& srcName = srcData.first;
664 const wxString& srcValue = srcData.second;
665
666 // Attributes bypass the field logic, so handle them first
667 if( isAttribute( srcName ) )
668 {
669 setAttributeValue( *m_symbolsList[i].GetSymbol(), srcName, srcValue );
670 continue;
671 }
672
673 // Skip special fields with variables as names (e.g. ${QUANTITY}),
674 // they can't be edited
675 if( IsTextVar( srcName ) )
676 continue;
677
678 SCH_FIELD* destField = symbol.FindField( srcName );
679 int col = GetFieldNameCol( srcName );
680 bool userAdded = ( col != -1 && m_cols[col].m_userAdded );
681
682 // Add a not existing field if it has a value for this symbol
683 bool createField = !destField && ( !srcValue.IsEmpty() || userAdded );
684
685 if( createField )
686 {
687 const VECTOR2I symbolPos = symbol.GetPosition();
688 destField = symbol.AddField( SCH_FIELD( symbolPos, -1, &symbol, srcName ) );
689 }
690
691 if( !destField )
692 continue;
693
694 if( destField->GetId() == REFERENCE_FIELD )
695 {
696 // Reference is not editable from this dialog
697 }
698 else if( destField->GetId() == VALUE_FIELD )
699 {
700 // Value field cannot be empty
701 if( !srcValue.IsEmpty() )
702 symbol.SetValueFieldText( srcValue );
703 }
704 else if( destField->GetId() == FOOTPRINT_FIELD )
705 {
706 symbol.SetFootprintFieldText( srcValue );
707 }
708 else
709 {
710 destField->SetText( srcValue );
711 }
712 }
713
714 for( int ii = symbol.GetFields().size() - 1; ii >= MANDATORY_FIELDS; ii-- )
715 {
716 if( fieldStore.count( symbol.GetFields()[ii].GetName() ) == 0 )
717 symbol.GetFields().erase( symbol.GetFields().begin() + ii );
718 }
719 }
720
721 m_edited = false;
722}
723
725{
726 int width = 0;
727
728 if( ColIsReference( aCol ) )
729 {
730 for( int row = 0; row < GetNumberRows(); ++row )
731 width = std::max( width, KIUI::GetTextSize( GetValue( row, aCol ), GetView() ).x );
732 }
733 else
734 {
735 wxString fieldName = GetColFieldName( aCol ); // symbol fieldName or Qty string
736
737 for( unsigned symbolRef = 0; symbolRef < m_symbolsList.GetCount(); ++symbolRef )
738 {
739 const KIID& symbolID = m_symbolsList[symbolRef].GetSymbol()->m_Uuid;
740 wxString text = m_dataStore[symbolID][fieldName];
741
742 width = std::max( width, KIUI::GetTextSize( text, GetView() ).x );
743 }
744 }
745
746 return width;
747}
748
749
751{
752 // Hide and un-group everything by default
753 for( size_t i = 0; i < m_cols.size(); i++ )
754 {
755 SetShowColumn( i, false );
756 SetGroupColumn( i, false );
757 }
758
759 std::vector<wxString> order;
760
761 // Set columns that are present and shown
762 for( BOM_FIELD field : aPreset.fieldsOrdered )
763 {
764 order.emplace_back( field.name );
765
766 int col = GetFieldNameCol( field.name );
767
768 // Add any missing fields, if the user doesn't add any data
769 // they won't be saved to the symbols anyway
770 if( col == -1 )
771 {
772 AddColumn( field.name, field.label, true );
773 col = GetFieldNameCol( field.name );
774 }
775 else
776 SetColLabelValue( col, field.label );
777
778 SetGroupColumn( col, field.groupBy );
779 SetShowColumn( col, field.show );
780 }
781
782 // Set grouping columns
784
785 SetFieldsOrder( order );
786
787 // Set our sorting
788 int sortCol = GetFieldNameCol( aPreset.sortField );
789
790 if( sortCol != -1 )
791 SetSorting( sortCol, aPreset.sortAsc );
792 else
794 aPreset.sortAsc );
795
796 SetFilter( aPreset.filterString );
797 SetExcludeDNP( aPreset.excludeDNP );
798
799 RebuildRows();
800}
801
802
804{
805 BOM_PRESET current;
806 current.readOnly = false;
808 current.sortField = GetColFieldName( GetSortCol() );
809 current.sortAsc = GetSortAsc();
810 current.filterString = GetFilter();
812 current.excludeDNP = GetExcludeDNP();
813
814 return current;
815}
816
817
819{
820 wxString out;
821
822 if( m_cols.empty() )
823 return out;
824
825 int last_col = -1;
826
827 // Find the location for the line terminator
828 for( size_t col = 0; col < m_cols.size(); col++ )
829 {
830 if( m_cols[col].m_show )
831 last_col = (int) col;
832 }
833
834 // No shown columns
835 if( last_col == -1 )
836 return out;
837
838 auto formatField = [&]( wxString field, bool last ) -> wxString
839 {
840 if( !settings.keepLineBreaks )
841 {
842 field.Replace( wxS( "\r" ), wxS( "" ) );
843 field.Replace( wxS( "\n" ), wxS( "" ) );
844 }
845
846 if( !settings.keepTabs )
847 {
848 field.Replace( wxS( "\t" ), wxS( "" ) );
849 }
850
851 if( !settings.stringDelimiter.IsEmpty() )
852 field.Replace( settings.stringDelimiter,
853 settings.stringDelimiter + settings.stringDelimiter );
854
855 return settings.stringDelimiter + field + settings.stringDelimiter
856 + ( last ? wxString( wxS( "\n" ) ) : settings.fieldDelimiter );
857 };
858
859 // Column names
860 for( size_t col = 0; col < m_cols.size(); col++ )
861 {
862 if( !m_cols[col].m_show )
863 continue;
864
865 out.Append( formatField( m_cols[col].m_label, col == (size_t) last_col ) );
866 }
867
868 // Data rows
869 for( size_t row = 0; row < m_rows.size(); row++ )
870 {
871 // Don't output child rows
872 if( GetRowFlags( (int) row ) == CHILD_ITEM )
873 continue;
874
875 for( size_t col = 0; col < m_cols.size(); col++ )
876 {
877 if( !m_cols[col].m_show )
878 continue;
879
880 // Get the unanottated version of the field, e.g. no "> " or "v " by
881 out.Append( formatField( GetExportValue( (int) row, (int) col, settings.refDelimiter,
882 settings.refRangeDelimiter ),
883 col == (size_t) last_col ) );
884 }
885 }
886
887 return out;
888}
889
890
892{
893 for( const SCH_REFERENCE& ref : aRefs )
894 {
895 if( !m_symbolsList.Contains( ref ) )
896 {
897 m_symbolsList.AddItem( ref );
898
899 // Update the fields of every reference
900 for( const SCH_FIELD& field : ref.GetSymbol()->GetFields() )
901 m_dataStore[ref.GetSymbol()->m_Uuid][field.GetCanonicalName()] = field.GetText();
902 }
903 }
904}
905
906
908{
909 // The schematic event listener passes us the symbol after it has been removed,
910 // so we can't just work with a SCH_REFERENCE_LIST like the other handlers as the
911 // references are already gone. Instead we need to prune our list.
912 m_dataStore[aSymbol.m_Uuid].clear();
913
914 // Remove all refs that match this symbol using remove_if
916 [aSymbol]( const SCH_REFERENCE& ref ) -> bool
917 {
918 return ref.GetSymbol()->m_Uuid == aSymbol.m_Uuid;
919 } ),
920 m_symbolsList.end() );
921}
922
923
925{
926 for( const SCH_REFERENCE& ref : aRefs )
927 {
928 int index = m_symbolsList.FindRefByFullPath( ref.GetFullPath() );
929
930 if( index != -1 )
931 {
932 m_symbolsList.RemoveItem( index );
933
934 // If we're out of instances then remove the symbol, too
935 if( ref.GetSymbol()->GetInstanceReferences().empty() )
936 m_dataStore.erase( ref.GetSymbol()->m_Uuid );
937 }
938 }
939}
940
941
943{
944 for( const SCH_REFERENCE& ref : aRefs )
945 {
946 // Update the fields of every reference. Do this by iterating through the data model
947 // colums; we must have all fields in the symbol added to the data model at this point,
948 // and some of the data model columns may be variables that are not present in the symbol
949 for( const DATA_MODEL_COL& col : m_cols )
950 updateDataStoreSymbolField( *ref.GetSymbol(), col.m_fieldName );
951
952 if( !m_symbolsList.Contains( ref ) )
953 m_symbolsList.AddItem( ref );
954 }
955}
const KIID m_Uuid
Definition: eda_item.h:482
wxString GetColFieldName(int aCol)
std::vector< DATA_MODEL_ROW > m_rows
int GetFieldNameCol(wxString aFieldName)
void ApplyBomPreset(const BOM_PRESET &preset)
void SetFieldsOrder(const std::vector< wxString > &aNewOrder)
wxString getAttributeValue(const SCH_SYMBOL &, const wxString &aAttributeName)
void updateDataStoreSymbolField(const SCH_SYMBOL &aSymbol, const wxString &aFieldName)
bool groupMatch(const SCH_REFERENCE &lhRef, const SCH_REFERENCE &rhRef)
bool unitMatch(const SCH_REFERENCE &lhRef, const SCH_REFERENCE &rhRef)
wxString GetExportValue(int aRow, int aCol, const wxString &refDelimiter, const wxString &refRangeDelimiter)
wxString getFieldShownText(const SCH_REFERENCE &aRef, const wxString &aFieldName)
void RenameColumn(int aCol, const wxString &newName)
wxString Export(const BOM_FMT_PRESET &settings)
void AddColumn(const wxString &aFieldName, const wxString &aLabel, bool aAddedByUser)
std::vector< DATA_MODEL_COL > m_cols
void SetSorting(int aCol, bool ascending)
void SetFilter(const wxString &aFilter)
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
bool isAttribute(const wxString &aFieldName)
wxString GetValue(int aRow, int aCol) override
void UpdateReferences(const SCH_REFERENCE_LIST &aRefs)
void ApplyData(std::function< void(SCH_SYMBOL &, SCH_SHEET_PATH &)> symbolChangeHandler)
GROUP_TYPE GetRowFlags(int aRow)
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)
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 setAttributeValue(SCH_SYMBOL &aSymbol, const wxString &aAttributeName, const wxString &aValue)
void AddReferences(const SCH_REFERENCE_LIST &aRefs)
const std::vector< BOM_FIELD > GetFieldsOrdered()
Definition: kiid.h:49
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:52
int GetId() const
Definition: sch_field.h:128
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
Definition: sch_field.cpp:188
void SetText(const wxString &aText) override
Definition: sch_field.cpp:985
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
bool Contains(const SCH_REFERENCE &aItem) const
Return true if aItem exists in this list.
iterator erase(iterator position)
size_t GetCount() const
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.
int FindRefByFullPath(const wxString &aFullPath) const
Search the list for a symbol with the given KIID path (as string).
void AddItem(const SCH_REFERENCE &aItem)
void RemoveItem(unsigned int aIndex)
Remove an item from the list of references.
A helper to define a symbol's reference designator in a schematic.
const SCH_SHEET_PATH & GetSheetPath() const
wxString GetFullRef() const
SCH_SYMBOL * GetSymbol() const
wxString GetRef() const
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 IsContainedWithin(const SCH_SHEET_PATH &aSheetPathToTest) const
Check if this path is contained inside aSheetPathToTest.
Schematic symbol object.
Definition: sch_symbol.h:81
SCH_FIELD * GetFieldByName(const wxString &aFieldName)
Return a field in this symbol.
Definition: sch_symbol.cpp:937
void SetDNP(bool aDNP)
Definition: sch_symbol.h:748
void SetValueFieldText(const wxString &aValue)
Definition: sch_symbol.cpp:891
SCH_FIELD * FindField(const wxString &aFieldName, bool aIncludeDefaultFields=true, bool aCaseInsensitive=false)
Search for a SCH_FIELD with aFieldName.
void SetExcludedFromSim(bool aExclude) override
Definition: sch_symbol.h:739
void SetFootprintFieldText(const wxString &aFootprint)
Definition: sch_symbol.cpp:907
bool GetExcludedFromBOM() const
Definition: sch_symbol.h:741
VECTOR2I GetPosition() const override
Definition: sch_symbol.h:703
bool ResolveTextVar(const SCH_SHEET_PATH *aPath, wxString *token, int aDepth=0) const
Resolve any references to system tokens supported by the symbol.
bool GetExcludedFromSim() const override
Definition: sch_symbol.h:738
void SetExcludedFromBOM(bool aIncludeInBOM)
Definition: sch_symbol.h:742
SCH_FIELD * AddField(const SCH_FIELD &aField)
Add a field to the symbol.
Definition: sch_symbol.cpp:988
bool GetExcludedFromBoard() const
Definition: sch_symbol.h:744
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:973
void SetExcludedFromBoard(bool aIncludeOnBoard)
Definition: sch_symbol.h:745
bool GetDNP() const
Definition: sch_symbol.h:747
bool IsTextVar(const wxString &aSource)
Returns true if the string is a text var, e.g starts with ${.
Definition: common.cpp:127
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:58
The common library.
@ GROUP_COLLAPSED_DURING_SORT
@ GROUP_EXPANDED
@ GROUP_COLLAPSED
@ GROUP_SINGLETON
@ CHILD_ITEM
wxSize GetTextSize(const wxString &aSingleLine, wxWindow *aWindow)
Return the size of aSingleLine of text when it is rendered in aWindow using whatever font is currentl...
Definition: ui_common.cpp:74
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
bool WildCompareString(const wxString &pattern, const wxString &string_to_tst, bool case_sensitive)
Compare a string against wild card (* and ?) pattern using the usual rules.
int ValueStringCompare(const wxString &strFWord, const wxString &strSWord)
Compare strings like the strcmp function but handle numbers and modifiers within the string text corr...
wxString label
Definition: bom_settings.h:33
bool groupBy
Definition: bom_settings.h:35
wxString name
Definition: bom_settings.h:32
wxString fieldDelimiter
Definition: bom_settings.h:81
wxString stringDelimiter
Definition: bom_settings.h:82
wxString refRangeDelimiter
Definition: bom_settings.h:84
wxString refDelimiter
Definition: bom_settings.h:83
wxString sortField
Definition: bom_settings.h:54
bool groupSymbols
Definition: bom_settings.h:57
std::vector< BOM_FIELD > fieldsOrdered
Definition: bom_settings.h:53
bool readOnly
Definition: bom_settings.h:52
bool excludeDNP
Definition: bom_settings.h:58
bool sortAsc
Definition: bom_settings.h:55
wxString filterString
Definition: bom_settings.h:56
std::vector< SCH_REFERENCE > m_Refs
static const wxString GetDefaultFieldName(int aFieldNdx, bool aTranslateForHI=false)
Return a default symbol field name for field aFieldNdx for all components.
@ FOOTPRINT_FIELD
Field Name Module PCB, i.e. "16DIP300".
@ VALUE_FIELD
Field Value of part, i.e. "3.3K".
@ MANDATORY_FIELDS
The first 5 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition: ui_common.h:42