KiCad PCB EDA Suite
Loading...
Searching...
No Matches
symbol_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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <set>
21
22#include <wx/string.h>
23#include <wx/debug.h>
24#include <wx/grid.h>
25#include <wx/settings.h>
26#include <common.h>
27#include <widgets/wx_grid.h>
28#include <sch_reference_list.h>
29#include <sch_commit.h>
30#include <sch_screen.h>
31#include <template_fieldnames.h>
32#include <sch_sheet_path.h>
33#include "string_utils.h"
34
36
37
45{
46 KIID_PATH path = aItem.GetSheetPath().Path();
47 path.push_back( aItem.GetSymbol()->m_Uuid );
48 return path;
49}
50
51
53{
54 return aItem.GetRef() + aItem.GetRefNumber();
55}
56
57
58wxGridCellAttr* SYMBOL_FIELDS_EDITOR_GRID_DATA_MODEL::GetAttr( int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind )
59{
60 wxGridCellAttr* attr = nullptr;
61 bool needsReadOnly = IsCellReadOnly( aRow, aCol );
62 bool needsUrlEditor = cellUsesUrlEditor( aRow, aCol );
63 bool needsVariantHighlight = false;
64 bool needsResolvedTextRenderer = cellUsesResolvedTextRenderer( aRow, aCol );
65 wxColour highlightColor;
66
67 // Check if we need variant highlighting
68 if( !m_currentVariant.IsEmpty() && aRow >= 0 && aRow < (int) m_rows.size() && aCol >= 0
69 && aCol < (int) m_cols.size() )
70 {
71 const wxString& fieldName = m_cols[aCol].m_fieldName;
72
73 // Skip Reference and generated fields (like ${QUANTITY}) for highlighting
74 if( !ColIsReference( aCol ) && !ColIsQuantity( aCol ) && !ColIsItemNumber( aCol ) )
75 {
77
78 // Check if any symbol in this row has a variant-specific value
79 for( const SCH_REFERENCE& ref : row.m_items )
80 {
81 wxString defaultValue = getDefaultFieldValue( ref, fieldName );
82
83 // Get the current value from the data store
84 wxString currentValue;
85
86 if( ref.GetSymbol() )
87 getStoredFieldValue( ref, fieldName, currentValue );
88
89 if( currentValue != defaultValue )
90 {
91 needsVariantHighlight = true;
92
93 bool isPriority2 = false;
94
95 if( const SCH_SYMBOL* sym = ref.GetSymbol() )
96 {
97 auto variantData = sym->GetVariant( ref.GetSheetPath(),
99
100 if( variantData
101 && variantData->m_SymbolOverride
102 && !variantData->m_Fields.count( fieldName ) )
103 {
104 isPriority2 = true;
105 }
106 }
107
108 wxColour bg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
109 bool isDark = ( bg.Red() + bg.Green() + bg.Blue() ) < 384;
110
111 if( isPriority2 )
112 {
115 }
116 else
117 {
120 }
121
122 break;
123 }
124 }
125 }
126 }
127
128 // If we don't need any custom attributes, use the base class behavior
129 if( !needsReadOnly && !needsUrlEditor && !needsVariantHighlight && !needsResolvedTextRenderer )
130 return applyCellDecorations( WX_GRID_TABLE_BASE::GetAttr( aRow, aCol, aKind ), aRow, aCol );
131
132 // URL cells use the Datasheet column's editor. Other cells use their own column attributes.
133 if( needsUrlEditor )
134 {
135 attr = cloneUrlEditorAttr();
136 }
137 else if( m_colAttrs.find( aCol ) != m_colAttrs.end() && m_colAttrs[aCol] )
138 {
139 attr = m_colAttrs[aCol]->Clone();
140 }
141 else
142 {
143 attr = new wxGridCellAttr();
144 }
145
146 if( needsReadOnly )
147 attr->SetReadOnly();
148
149 if( needsVariantHighlight )
150 attr->SetBackgroundColour( highlightColor );
151
152 if( needsResolvedTextRenderer )
153 applyResolvedTextRenderer( attr, !needsVariantHighlight );
154
155 return applyCellDecorations( enhanceAttr( attr, aRow, aCol, aKind ), aRow, aCol );
156}
157
158
159void SYMBOL_FIELDS_EDITOR_GRID_DATA_MODEL::SetValue( int aRow, int aCol, const wxString& aValue )
160{
161 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), wxS( "Invalid column number" ) );
162
163 if( IsCellReadOnly( aRow, aCol ) )
164 return;
165
166 if( aValue == INDETERMINATE_STATE )
167 return;
168
170 const wxString& fieldName = m_cols[aCol].m_fieldName;
171
172 std::set<const SCH_SYMBOL*> editedSymbols;
173
174 for( const SCH_REFERENCE& ref : row.m_items )
175 editedSymbols.insert( ref.GetSymbol() );
176
177 // Field presence is on the symbol object and applies to all instances.
178 // Before editing one path, ensure every instance for that symbol has this field
179 // marked present at least.
180 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
181 {
182 const SCH_REFERENCE& ref = m_symbolsList[ii];
183
184 if( !editedSymbols.contains( ref.GetSymbol() ) )
185 continue;
186
187 wxString unused;
188
189 if( !getStoredFieldValue( ref, fieldName, unused ) )
190 {
191 updateDataStoreItemFieldFromLive( ref, fieldName );
192
193 if( !getStoredFieldValue( ref, fieldName, unused ) )
194 ensureStoredFieldPresent( ref, fieldName );
195 }
196 }
197
198 for( const SCH_REFERENCE& ref : row.m_items )
199 setStoredFieldValue( ref, fieldName, aValue );
200
201 // ApplyData walks every path a symbol is reachable through, so an edit to storage those
202 // paths have in common must also reach the ones the current scope and filter hide
203 if( storageIsSharedAcrossPaths( fieldName ) )
204 {
205 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
206 {
207 const SCH_REFERENCE& ref = m_symbolsList[ii];
208
209 if( !editedSymbols.contains( ref.GetSymbol() ) )
210 continue;
211
212 setStoredFieldValue( ref, fieldName, aValue );
213 }
214 }
215
216 m_edited = true;
217}
218
219
221{
222 wxCHECK_RET( aRow >= 0 && aRow < static_cast<int>( m_rows.size() ), wxS( "Invalid row number" ) );
223 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), wxS( "Invalid column number" ) );
224
225 if( !CanClearCell( aRow, aCol ) )
226 return;
227
228 const wxString& fieldName = m_cols[aCol].m_fieldName;
229 std::set<const SCH_SYMBOL*> clearedSymbols;
230
231 for( const SCH_REFERENCE& ref : m_rows[aRow].m_items )
232 clearedSymbols.insert( ref.GetSymbol() );
233
234 // Clearing a field is a symbol-wide operation, even when the table is showing one variant
235 // or one instance of a shared sheet. Clear it from every data-store entry for the symbol so
236 // a later entry cannot recreate it while ApplyData() walks the other instances.
237 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
238 {
239 const SCH_REFERENCE& ref = m_symbolsList[ii];
240
241 if( clearedSymbols.contains( ref.GetSymbol() ) )
242 clearStoredField( ref, fieldName );
243 }
244
245 m_edited = true;
246}
247
248
250{
251 wxCHECK_RET( aRow >= 0 && aRow < static_cast<int>( m_rows.size() ), wxS( "Invalid row number" ) );
252
253 std::set<const SCH_SYMBOL*> rowSymbols;
254
255 for( const SCH_REFERENCE& ref : m_rows[aRow].m_items )
256 rowSymbols.insert( ref.GetSymbol() );
257
258 for( const DATA_MODEL_COL& col : m_cols )
259 {
260 bool revertAllPaths = storageIsSharedAcrossPaths( col.m_fieldName );
261
262 // Field presence belongs to the symbol rather than an individual path. If an edit added
263 // or removed the field, restore every path so a hidden path cannot recreate it or retain
264 // a pending creation when ApplyData() walks the symbol's other instances.
265 if( !revertAllPaths )
266 {
267 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
268 {
269 const SCH_REFERENCE& ref = m_symbolsList[ii];
270
271 if( !rowSymbols.contains( ref.GetSymbol() ) )
272 continue;
273
274 wxString liveValue;
275 wxString storedValue;
276 bool liveFieldPresent = getLiveFieldValue( ref, col.m_fieldName, liveValue );
277 bool storedFieldPresent = getStoredFieldValue( ref, col.m_fieldName, storedValue );
278
279 if( liveFieldPresent != storedFieldPresent )
280 {
281 revertAllPaths = true;
282 break;
283 }
284 }
285 }
286
287 if( !revertAllPaths )
288 continue;
289
290 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
291 {
292 const SCH_REFERENCE& ref = m_symbolsList[ii];
293
294 if( rowSymbols.contains( ref.GetSymbol() ) )
295 updateDataStoreItemFieldFromLive( ref, col.m_fieldName );
296 }
297 }
298
300}
301
302
304{
305 // If items are unannotated then we can't tell if they're units of the same symbol or not
306 if( lhItem.GetRefNumber() == wxT( "?" ) )
307 return false;
308
309 return ( lhItem.GetRef() == rhItem.GetRef() && lhItem.GetRefNumber() == rhItem.GetRefNumber() );
310}
311
312
314 const wxString& aFieldName,
315 wxString& aValue )
316{
317 return getLiveFieldValueForVariant( aRef, aFieldName, m_currentVariant, aValue );
318}
319
320
322{
323 std::vector<SCH_REFERENCE> items;
324
325 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
326 {
327 if( m_symbolsList[i].GetSymbol() )
328 items.push_back( m_symbolsList[i] );
329 }
330
331 return items;
332}
333
334
336 const wxString& aFieldName )
337{
338 SCH_FIELD* field = aRef.GetSymbol()->GetField( aFieldName );
339
340 if( field )
341 {
342 if( field->IsPrivate() )
343 return wxEmptyString;
344 else
345 return field->GetShownText( &aRef.GetSheetPath(), INTERNAL, m_currentVariant );
346 }
347
348 // Handle generated fields with variables as names (e.g. ${QUANTITY}) that are not present in
349 // the symbol by giving them the correct value by resolving against the symbol
350 if( IsGeneratedField( aFieldName ) )
351 {
352 int depth = 0;
353 const SCH_SHEET_PATH& path = aRef.GetSheetPath();
354
355 std::function<bool( wxString* )> symbolResolver =
356 [&]( wxString* token ) -> bool
357 {
358 return aRef.GetSymbol()->ResolveTextVar( &path, token, m_currentVariant, depth + 1 );
359 };
360
361 return ResolveTextVars( aFieldName, &symbolResolver, depth );
362 }
363
364 return wxEmptyString;
365}
366
367
368wxString SYMBOL_FIELDS_EDITOR_GRID_DATA_MODEL::resolveTextVars( const SCH_REFERENCE& aRef, const wxString& aText )
369{
370 // TODO: this isn't technically correct, this should resolve against the
371 // data store's copy of variables whenever whenever possible,
372 // but currently it is resolving against the symbol's current values.
373 // For instance, if you have "My value is ${VALUE}" in the description field,
374 // ${VALUE} will be resolved against the symbol's live value, not the Value field
375 // stored in the data store.
376 std::function<bool( wxString* )> symbolResolver =
377 [&]( wxString* token ) -> bool
378 {
379 return aRef.GetSymbol()->ResolveTextVar( &aRef.GetSheetPath(), token, m_currentVariant );
380 };
381
382 int depth = 0;
383 return ResolveTextVars( aText, &symbolResolver, depth );
384}
385
386
388{
389 // Variant edits are kept on the symbol instance, but SCH_REFERENCE has no variant form of
390 // the board exclusion so that one always lands on the symbol
391 if( aFieldName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
392 return true;
393
394 return m_currentVariant.IsEmpty();
395}
396
397
399 const wxString& aAttributeName,
400 const wxString& aVariantName )
401{
402 if( aAttributeName == wxS( "${DNP}" ) )
403 return aRef.GetSymbolDNP( aVariantName ) ? wxS( "1" ) : wxS( "0" );
404
405 if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
406 return aRef.GetSymbolExcludedFromBoard() ? wxS( "1" ) : wxS( "0" );
407
408 if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
409 return aRef.GetSymbolExcludedFromBOM( aVariantName ) ? wxS( "1" ) : wxS( "0" );
410
411 if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
412 return aRef.GetSymbolExcludedFromSim( aVariantName ) ? wxS( "1" ) : wxS( "0" );
413
414 if( aAttributeName == wxS( "${EXCLUDE_FROM_POS_FILES}" ) )
415 return aRef.GetSymbolExcludedFromPosFiles( aVariantName ) ? wxS( "1" ) : wxS( "0" );
416
417 return wxS( "0" );
418}
419
420
422 const wxString& aAttributeName ) const
423{
424 const SCH_SHEET_PATH& path = aRef.GetSheetPath();
425
426 if( aAttributeName == wxS( "${DNP}" ) )
427 return path.GetDNP( m_currentVariant );
428 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
429 return path.GetExcludedFromBoard( m_currentVariant );
430 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
431 return path.GetExcludedFromBOM( m_currentVariant );
432 else if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
433 return path.GetExcludedFromSim( m_currentVariant );
434
435 return false;
436}
437
438
440 const wxString& aFieldName,
441 const wxString& aVariantName, wxString& aValue )
442{
443 aValue.clear();
444
445 const SCH_SYMBOL* symbol = aRef.GetSymbol();
446
447 if( !symbol )
448 return false;
449
450 if( fieldIsAttribute( aFieldName ) )
451 {
452 aValue = getAttributeValue( aRef, aFieldName, aVariantName );
453 return true;
454 }
455
456 if( const SCH_FIELD* field = symbol->GetField( aFieldName ) )
457 {
458 if( field->IsPrivate() )
459 return false;
460
461 aValue = symbol->Schematic()->ConvertKIIDsToRefs( field->GetText( &aRef.GetSheetPath(), aVariantName ) );
462 return true;
463 }
464
465 // For generated fields, return the field name itself
466 if( IsGeneratedField( aFieldName ) )
467 {
468 aValue = aFieldName;
469 return true;
470 }
471
472 return false;
473}
474
475
477 const wxString& aFieldName )
478{
479 wxString value;
480 getLiveFieldValueForVariant( aRef, aFieldName, wxEmptyString, value );
481 return value;
482}
483
484
486 const wxString& aAttributeName,
487 const wxString& aValue,
488 const wxString& aVariantName )
489{
490 bool attrChanged = false;
491 bool newValue = aValue == wxS( "1" );
492
493 if( aAttributeName == wxS( "${DNP}" ) )
494 {
495 attrChanged = aRef.GetSymbolDNP( aVariantName ) != newValue;
496
497 if( attrChanged )
498 aRef.SetSymbolDNP( newValue, aVariantName );
499 }
500 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
501 {
502 attrChanged = aRef.GetSymbolExcludedFromBoard() != newValue;
503
504 if( attrChanged )
505 aRef.SetSymbolExcludedFromBoard( newValue );
506 }
507 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
508 {
509 attrChanged = aRef.GetSymbolExcludedFromBOM( aVariantName ) != newValue;
510
511 if( attrChanged )
512 aRef.SetSymbolExcludedFromBOM( newValue, aVariantName );
513 }
514 else if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
515 {
516 attrChanged = aRef.GetSymbolExcludedFromSim( aVariantName ) != newValue;
517
518 if( attrChanged )
519 aRef.SetSymbolExcludedFromSim( newValue, aVariantName );
520 }
521 else if( aAttributeName == wxS( "${EXCLUDE_FROM_POS_FILES}" ) )
522 {
523 attrChanged = aRef.GetSymbolExcludedFromPosFiles( aVariantName ) != newValue;
524
525 if( attrChanged )
526 aRef.SetSymbolExcludedFromPosFiles( newValue, aVariantName );
527 }
528
529 return attrChanged;
530}
531
532
534{
535 if( !m_rebuildsEnabled )
536 return;
537
538 if( GetView() )
539 {
540 // Commit any pending in-place edits before the row gets moved out from under
541 // the editor.
542 static_cast<WX_GRID*>( GetView() )->CommitPendingChanges( true );
543
544 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, m_rows.size() );
545 GetView()->ProcessTableMessage( msg );
546 }
547
548 m_rows.clear();
549
550 EDA_COMBINED_MATCHER matcher( m_filter.Lower(), CTX_SEARCH );
551
552 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
553 {
555
556 if( m_scope == SCOPE::SCOPE_SELECTION
557 && !m_selectionItems.contains( getDataStoreKey( ref ) ) )
558 {
559 continue;
560 }
561
562 if( !MatchesFilter( ref, ref.GetFullRef(), matcher ) )
563 continue;
564
565 if( m_excludeDNP )
566 {
567 bool isDNP = false;
568
569 if( !m_variantNames.empty() )
570 {
571 for( const wxString& variantName : m_variantNames )
572 {
573 if( ref.GetSymbol()->ResolveDNP( &ref.GetSheetPath(), variantName )
574 || ref.GetSheetPath().GetDNP( variantName ) )
575 {
576 isDNP = true;
577 break;
578 }
579 }
580 }
581 else
582 {
583 isDNP = ref.GetSymbol()->ResolveDNP( &ref.GetSheetPath(), m_currentVariant )
585 }
586
587 if( isDNP )
588 continue;
589 }
590
591 if( !m_includeExcluded )
592 {
593 bool isExcluded = false;
594
595 if( !m_variantNames.empty() )
596 {
597 for( const wxString& variantName : m_variantNames )
598 {
599 if( ref.GetSymbol()->ResolveExcludedFromBOM( &ref.GetSheetPath(), variantName )
600 || ref.GetSheetPath().GetExcludedFromBOM( variantName ) )
601 {
602 isExcluded = true;
603 break;
604 }
605 }
606 }
607 else
608 {
611 }
612
613 if( isExcluded )
614 continue;
615 }
616
617 // Check if the symbol if on the current sheet or, in the sheet path somewhere
618 // depending on scope
619 if( ( m_scope == SCOPE::SCOPE_SHEET && ref.GetSheetPath() != m_path )
620 || ( m_scope == SCOPE::SCOPE_SHEET_RECURSIVE
621 && !ref.GetSheetPath().IsContainedWithin( m_path ) ) )
622 {
623 continue;
624 }
625
626 bool matchFound = false;
627
628 // Performance optimization for ungrouped case to skip the N^2 for loop
629 if( !m_groupingEnabled && !ref.IsMultiUnit() )
630 {
631 m_rows.emplace_back( SYMBOL_FIELDS_TABLE_DATA_MODEL_ROW( ref, ROW_STATE::NON_EXPANDABLE ) );
632 continue;
633 }
634
635 // See if we already have a row which this symbol fits into
637 {
638 // all group members must have identical refs so just use the first one
639 SCH_REFERENCE rowRef = row.m_items[0];
640
641 if( unitMatch( ref, rowRef ) )
642 {
643 matchFound = true;
644 row.m_items.push_back( ref );
645 break;
646 }
647 else if( m_groupingEnabled && groupMatch( ref, rowRef ) )
648 {
649 matchFound = true;
650 row.m_items.push_back( ref );
651 row.m_state = ROW_STATE::COLLAPSED;
652 break;
653 }
654 }
655
656 if( !matchFound )
657 m_rows.emplace_back( SYMBOL_FIELDS_TABLE_DATA_MODEL_ROW( ref, ROW_STATE::NON_EXPANDABLE ) );
658 }
659
660 if( GetView() )
661 {
662 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_rows.size() );
663 GetView()->ProcessTableMessage( msg );
664 }
665
666 Sort();
667}
668
669
671 const wxString& aVariantName )
672{
673 bool symbolModified = false;
674 std::unique_ptr<SCH_SYMBOL> symbolCopy;
675
676 for( size_t i = 0; i < m_symbolsList.GetCount(); i++ )
677 {
678 SCH_SYMBOL* symbol = m_symbolsList[i].GetSymbol();
679 SCH_SYMBOL* nextSymbol = nullptr;
680
681 if( ( i + 1 ) < m_symbolsList.GetCount() )
682 nextSymbol = m_symbolsList[i + 1].GetSymbol();
683
684 if( i == 0 )
685 symbolCopy = std::make_unique<SCH_SYMBOL>( *symbol );
686
687 const std::map<wxString, wxString>& fieldStore = getStoredFields( m_symbolsList[i] );
688
689 for( const auto& [srcName, srcValue] : fieldStore )
690 {
691 // Attributes bypass the field logic, so handle them first
692 if( fieldIsAttribute( srcName ) )
693 {
694 symbolModified |= setAttributeValue( m_symbolsList[i], srcName, srcValue, aVariantName );
695 continue;
696 }
697
698 // Skip generated fields with variables as names (e.g. ${QUANTITY});
699 // they can't be edited
700 if( IsGeneratedField( srcName ) )
701 continue;
702
703 SCH_FIELD* destField = symbol->GetField( srcName );
704
705 if( destField && destField->IsPrivate() )
706 {
707 if( srcValue.IsEmpty() )
708 continue;
709 else
710 destField->SetPrivate( false );
711 }
712
713 // Reaching this point means the data store field is at least marked present,
714 // so add the field to the symbol even when its stored value is empty.
715 bool createField = !destField;
716
717 if( createField )
718 {
719 destField = symbol->AddField( SCH_FIELD( symbol, FIELD_T::USER, srcName ) );
720 destField->SetTextAngle( symbol->GetField( FIELD_T::REFERENCE )->GetTextAngle() );
721
722 if( const TEMPLATE_FIELDNAME* srcTemplate = aTemplateFieldnames.GetFieldName( srcName ) )
723 destField->SetVisible( srcTemplate->m_Visible );
724 else
725 destField->SetVisible( false );
726
727 destField->SetTextPos( symbol->GetPosition() );
728 symbolModified = true;
729 }
730
731 if( !destField )
732 continue;
733
734 // Reference is not editable from this dialog
735 if( destField->GetId() == FIELD_T::REFERENCE )
736 continue;
737
738 wxString previousValue = destField->GetText( &m_symbolsList[i].GetSheetPath(), aVariantName );
739
740 destField->SetText( symbol->Schematic()->ConvertRefsToKIIDs( srcValue ), &m_symbolsList[i].GetSheetPath(),
741 aVariantName );
742
743 if( !createField && ( previousValue != srcValue ) )
744 symbolModified = true;
745 }
746
747 for( int ii = static_cast<int>( symbol->GetFields().size() ) - 1; ii >= 0; ii-- )
748 {
749 if( symbol->GetFields()[ii].IsMandatory() || symbol->GetFields()[ii].IsPrivate() )
750 continue;
751
752 const wxString& existingName = symbol->GetFields()[ii].GetName();
753
754 bool stillTracked = std::any_of( fieldStore.begin(), fieldStore.end(),
755 [&]( const auto& kv )
756 {
757 return kv.first == existingName;
758 } );
759
760 if( !stillTracked )
761 {
762 symbol->RemoveField( existingName );
763 symbolModified = true;
764 }
765 }
766
767 if( symbolModified && ( symbol != nextSymbol ) )
768 aCommit.Modified( symbol, symbolCopy.release(), m_symbolsList[i].GetSheetPath().LastScreen() );
769
770 // Only reset the modified flag and next symbol copy if the next symbol is different from the current one.
771 if( symbol != nextSymbol )
772 {
773 if( nextSymbol )
774 symbolCopy = std::make_unique<SCH_SYMBOL>( *nextSymbol );
775 else
776 symbolCopy.reset( nullptr );
777
778 symbolModified = false;
779 }
780 }
781
782 m_edited = false;
783}
784
785
787{
788 bool refListChanged = false;
789
790 for( const SCH_REFERENCE& ref : aRefs )
791 {
792 if( !m_symbolsList.Contains( ref ) )
793 {
794 const SCH_REFERENCE* existingRef = nullptr;
795
796 // A field belongs to the symbol object not reference, so an additional sheet path must have
797 // the same field presence as the paths which already reach that symbol. Field values may
798 // still differ by path when editing a variant.
799 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
800 {
801 if( m_symbolsList[ii].GetSymbol() == ref.GetSymbol() )
802 {
803 existingRef = &m_symbolsList[ii];
804 break;
805 }
806 }
807
808 if( existingRef )
809 {
810 for( const DATA_MODEL_COL& col : m_cols )
811 {
812 wxString existingValue;
813
814 if( !getStoredFieldValue( *existingRef, col.m_fieldName, existingValue ) )
815 continue;
816
817 wxString value;
818
819 if( storageIsSharedAcrossPaths( col.m_fieldName ) )
820 {
821 value = existingValue;
822 }
823 else if( !getLiveFieldValue( ref, col.m_fieldName, value ) )
824 {
825 value.clear();
826 }
827
828 setStoredFieldValue( ref, col.m_fieldName, value );
829 }
830 }
831 else
832 {
834 }
835
836 m_symbolsList.AddItem( ref );
837
838 refListChanged = true;
839 }
840 }
841
842 if( refListChanged )
843 m_symbolsList.SortBySymbolPtr();
844}
845
846
848{
849 // The schematic event listener passes us the symbol after it has been removed,
850 // so we can't just work with a SCH_REFERENCE_LIST like the other handlers as the
851 // references are already gone. Instead we need to prune our list.
852
853 // Since we now use full KIID_PATH as keys, we need to find and remove all entries
854 // that correspond to this symbol (their keys end with the symbol's UUID)
855 KIID symbolUuid = aSymbol.m_Uuid;
856 std::vector<KIID_PATH> keysToRemove;
857
858 for( const auto& [key, value] : m_dataStore )
859 {
860 if( !key.empty() && ( key.back() == symbolUuid ) )
861 keysToRemove.push_back( key );
862 }
863
864 for( const KIID_PATH& key : keysToRemove )
865 m_dataStore.erase( key );
866
867 // Remove all refs that match this symbol using remove_if
868 m_symbolsList.erase( std::remove_if( m_symbolsList.begin(), m_symbolsList.end(),
869 [&aSymbol]( const SCH_REFERENCE& ref ) -> bool
870 {
871 return ref.GetSymbol()->m_Uuid == aSymbol.m_Uuid;
872 } ),
873 m_symbolsList.end() );
874}
875
876
878{
879 for( const SCH_REFERENCE& ref : aRefs )
880 {
881 int index = m_symbolsList.FindRefByFullPath( ref.GetFullPath() );
882
883 if( index != -1 )
884 {
885 m_dataStore.erase( getDataStoreKey( ref ) );
886 m_symbolsList.RemoveItem( index );
887 }
888 }
889}
890
891
893{
894 bool refListChanged = false;
895 std::set<SCH_SYMBOL*> updatedSymbols;
896
897 for( const SCH_REFERENCE& incomingRef : aRefs )
898 {
899 if( incomingRef.GetSymbol() )
900 updatedSymbols.insert( incomingRef.GetSymbol() );
901
902 SCH_REFERENCE* cachedRef = m_symbolsList.FindItem( incomingRef );
903
904 // This looks like it might be assigning to itself because it often is
905 if( cachedRef )
906 {
907 // When these things don't happen to be the same pointer, this will update our
908 // cached reference's stuff like reference/unit/etc, but not the pointer to the symbol itself
909 *cachedRef = incomingRef;
910 }
911 else
912 {
913 m_symbolsList.AddItem( incomingRef );
914 refListChanged = true;
915 }
916 }
917
918 if( refListChanged )
919 m_symbolsList.SortBySymbolPtr();
920
921 // Field presence is on the symbol object, so refresh every path/instance which reaches a
922 // changed symbol even if the event supplied only one path.
923 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
924 {
925 const SCH_REFERENCE& ref = m_symbolsList[ii];
926
927 if( !updatedSymbols.contains( ref.GetSymbol() ) )
928 continue;
929
931 }
932}
933
934
935bool SYMBOL_FIELDS_EDITOR_GRID_DATA_MODEL::DeleteRows( size_t aPosition, size_t aNumRows )
936{
937 size_t curNumRows = m_rows.size();
938
939 if( aPosition >= curNumRows )
940 {
941 wxFAIL_MSG( wxString::Format( wxT( "Called SYMBOL_FIELDS_EDITOR_GRID_DATA_MODEL::DeleteRows(aPosition=%lu, "
942 "aNumRows=%lu)\nPosition value is invalid for present table with %lu rows" ),
943 (unsigned long) aPosition, (unsigned long) aNumRows,
944 (unsigned long) curNumRows ) );
945
946 return false;
947 }
948
949 if( aNumRows > curNumRows - aPosition )
950 {
951 aNumRows = curNumRows - aPosition;
952 }
953
954 if( aNumRows >= curNumRows )
955 {
956 m_rows.clear();
957 m_dataStore.clear();
958 m_symbolsList.Clear();
959
960 if( GetView() )
961 {
962 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aPosition, aNumRows );
963 GetView()->ProcessTableMessage( msg );
964 }
965 }
966 else
967 {
968 // Note: this code is currently dead, as all current usage calls the clear path above,
969 // which is left because it is faster. This code *should* be correct but is untested.
970 auto first = m_rows.begin() + aPosition;
971 auto last = first + aNumRows;
972
973 SCH_REFERENCE_LIST refsToDelete;
974 for( auto it = first; it != last && it != m_rows.end(); ++it )
975 {
976 for( const SCH_REFERENCE& ref : it->m_items )
977 refsToDelete.AddItem( ref );
978 }
979
980 RemoveReferences( refsToDelete );
981 // This will also notify the view
982 RebuildRows();
983 }
984
985 return true;
986}
987
988
989std::vector<FIELD_CASE_CONFLICT> DetectFieldCaseConflicts( const SCH_REFERENCE_LIST& aSymbols )
990{
991 std::vector<FIELD_CASE_CONFLICT> conflicts;
992
993 for( unsigned i = 0; i < aSymbols.GetCount(); ++i )
994 {
995 SCH_SYMBOL* symbol = aSymbols[i].GetSymbol();
996
997 if( !symbol )
998 continue;
999
1000 std::map<wxString, std::vector<std::pair<wxString, wxString>>> groups;
1001
1002 for( const SCH_FIELD& field : symbol->GetFields() )
1003 {
1004 if( field.IsMandatory() || field.IsPrivate() )
1005 continue;
1006
1007 groups[field.GetName().Lower()].emplace_back( field.GetName(), field.GetText() );
1008 }
1009
1010 for( const auto& [key, members] : groups )
1011 {
1012 if( members.size() < 2 )
1013 continue;
1014
1016 c.symbol = symbol;
1017 c.sheetPath = aSymbols[i].GetSheetPath();
1018 c.reference = symbol->GetRef( &c.sheetPath );
1019 c.caseFoldedKey = key;
1020 c.variants = members;
1021 conflicts.push_back( std::move( c ) );
1022 }
1023 }
1024
1025 return conflicts;
1026}
int index
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:221
const KIID m_Uuid
Definition eda_item.h:597
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:539
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:342
virtual EDA_ANGLE GetTextAngle() const
Definition eda_text.h:178
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:263
void applyResolvedTextRenderer(wxGridCellAttr *aAttr, bool aApplyTint)
virtual bool fieldIsAttribute(const wxString &aFieldName) const
wxGridCellAttr * applyCellDecorations(wxGridCellAttr *aAttr, int aRow, int aCol)
std::unordered_set< KIID_PATH > m_selectionItems
std::map< KIID_PATH, std::map< wxString, wxString > > m_dataStore
void RevertRow(int aRow) override
Go through and revert all the fields in the row to their live values from the item (symbol/footprint/...
bool groupMatch(const SCH_REFERENCE &lhItem, const SCH_REFERENCE &rhItem)
void ensureStoredFieldPresent(const SCH_REFERENCE &aItem, const wxString &aFieldName)
void setStoredFieldValue(const SCH_REFERENCE &aItem, const wxString &aFieldName, const wxString &aValue)
const std::map< wxString, wxString > & getStoredFields(const SCH_REFERENCE &aItem) const
void initializeDataStoreItem(const SCH_REFERENCE &aItem)
void clearStoredField(const SCH_REFERENCE &aItem, const wxString &aFieldName)
bool getStoredFieldValue(const SCH_REFERENCE &aItem, const wxString &aFieldName, wxString &aValue) const
std::vector< DATA_MODEL_ROW< SCH_REFERENCE > > m_rows
bool IsCellReadOnly(int aRow, int aCol) override
bool MatchesFilter(const SCH_REFERENCE &aItem, const wxString &aReference, EDA_COMBINED_MATCHER &aMatcher)
void updateDataStoreItemFieldFromLive(const SCH_REFERENCE &aItem, const wxString &aFieldName)
Definition kiid.h:46
wxString ConvertKIIDsToRefs(const wxString &aSource) const
wxString ConvertRefsToKIIDs(const wxString &aSource) const
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:138
FIELD_T GetId() const
Definition sch_field.h:142
wxString GetShownText(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, const wxString &aVariantName=wxEmptyString, 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:281
bool IsPrivate() const
Definition sch_item.h:253
bool ResolveExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:327
bool ResolveDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:375
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
void AddItem(const SCH_REFERENCE &aItem)
A helper to define a symbol's reference designator in a schematic.
void SetSymbolExcludedFromBoard(bool aEnable)
const SCH_SHEET_PATH & GetSheetPath() const
void SetSymbolExcludedFromBOM(bool aEnable, const wxString &aVariant=wxEmptyString)
bool GetSymbolExcludedFromBOM(const wxString &aVariant=wxEmptyString) const
bool GetSymbolDNP(const wxString &aVariant=wxEmptyString) const
SCH_SYMBOL * GetSymbol() const
bool GetSymbolExcludedFromPosFiles(const wxString &aVariant=wxEmptyString) const
void SetSymbolExcludedFromSim(bool aEnable, const wxString &aVariant=wxEmptyString)
wxString GetRef() const
bool GetSymbolExcludedFromBoard() const
wxString GetFullRef(bool aIncludeUnit=true) const
Return reference name with unit altogether.
bool GetSymbolExcludedFromSim(const wxString &aVariant=wxEmptyString) const
void SetSymbolExcludedFromPosFiles(bool aEnable, const wxString &aVariant=wxEmptyString)
bool IsMultiUnit() const
wxString GetRefNumber() const
void SetSymbolDNP(bool aEnable, const wxString &aVariant=wxEmptyString)
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
Schematic symbol object.
Definition sch_symbol.h:75
void RemoveField(const wxString &aFieldName)
Remove a user field from the symbol.
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:934
bool ResolveTextVar(const SCH_SHEET_PATH *aPath, wxString *token, int aDepth=0) const
Resolve any references to system tokens supported by the symbol.
SCH_FIELD * AddField(const SCH_FIELD &aField)
Add a field to the symbol.
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
void RemoveReferences(const SCH_REFERENCE_LIST &aRefs)
wxString getAttributeValue(const SCH_REFERENCE &aRef, const wxString &aAttributeName, const wxString &aVariantNames)
bool attributeForcedOnBySheet(const SCH_REFERENCE &aRef, const wxString &aAttributeName) const override
Sheets can force all the symbols in them to have certain attributes on, like DNP.
wxString getFieldResolvedLiveValue(const SCH_REFERENCE &aRef, const wxString &aFieldName) override
Explicitly bypasses the data store's field values and retries them from the item's current field valu...
void UpdateReferences(const SCH_REFERENCE_LIST &aRefs)
bool getLiveFieldValueForVariant(const SCH_REFERENCE &aRef, const wxString &aFieldName, const wxString &aVariantName, wxString &aValue)
wxString getItemIdentifier(const SCH_REFERENCE &aItem) const override
bool setAttributeValue(SCH_REFERENCE &aRef, const wxString &aAttributeName, const wxString &aValue, const wxString &aVariantName=wxEmptyString)
Set the attribute value.
SCH_REFERENCE_LIST m_symbolsList
The flattened by hierarchy list of symbols.
void RemoveSymbol(const SCH_SYMBOL &aSymbol)
bool getLiveFieldValue(const SCH_REFERENCE &aRef, const wxString &aFieldName, wxString &aValue) override
Gets the current value of the field from the live item, e.g.
void RevertRow(int aRow) override
Go through and revert all the fields in the row to their live values from the item (symbol/footprint/...
void AddReferences(const SCH_REFERENCE_LIST &aRefs)
wxString resolveTextVars(const SCH_REFERENCE &aRef, const wxString &aText) override
bool unitMatch(const SCH_REFERENCE &lhItem, const SCH_REFERENCE &rhItem) override
void ClearCell(int aRow, int aCol) override
Clears the field from the data store, rather than setting its value to an empty string.
void SetValue(int aRow, int aCol, const wxString &aValue) override
void ApplyData(SCH_COMMIT &aCommit, TEMPLATES &aTemplateFieldnames, const wxString &aVariantName)
wxGridCellAttr * GetAttr(int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind) override
KIID_PATH getDataStoreKey(const SCH_REFERENCE &aItem) const override
Create a unique key for the data store by combining the KIID_PATH from the SCH_SHEET_PATH with the sy...
wxString getDefaultFieldValue(const SCH_REFERENCE &aRef, const wxString &aFieldName)
Get the default (non-variant) value for a field.
std::vector< SCH_REFERENCE > getAllItems() const override
bool storageIsSharedAcrossPaths(const wxString &aFieldName) const
Test whether a field's storage is common to every sheet path that reaches a symbol.
bool DeleteRows(size_t aPosition=0, size_t aNumRows=1) override
const TEMPLATE_FIELDNAME * GetFieldName(const wxString &aName)
Search for aName in the template field name list.
wxGridCellAttr * enhanceAttr(wxGridCellAttr *aInputAttr, int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind)
Definition wx_grid.cpp:43
std::map< int, wxGridCellAttr * > m_colAttrs
Definition wx_grid.h:108
wxGridCellAttr * GetAttr(int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind) override
Definition wx_grid.h:72
bool IsGeneratedField(const wxString &aFieldName)
Returns true if the entire string is generated, e.g is a single text var reference.
Definition common.cpp:494
wxString ResolveTextVars(const wxString &aSource, const std::function< bool(wxString *)> *aResolver, int &aDepth)
Multi-pass text variable expansion and math expression evaluation.
Definition common.cpp:333
@ INTERNAL
Definition common.h:92
@ CTX_SEARCH
const wxColour VARIANT_FIELD_OVERRIDE_DARK_YELLOW(80, 80, 40)
const wxColour VARIANT_FIELD_OVERRIDE_LIGHT_YELLOW(255, 255, 200)
const wxColour VARIANT_SYMBOL_OVERRIDE_LIGHT_BLUE(220, 235, 255)
const wxColour VARIANT_SYMBOL_OVERRIDE_DARK_BLUE(40, 60, 80)
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
The point of the data model classes is fundamentally to represent three things:
std::vector< ITEM_TYPE > m_items
std::vector< std::pair< wxString, wxString > > variants
Hold a name of a symbol's field, field value, and default visibility.
std::vector< FIELD_CASE_CONFLICT > DetectFieldCaseConflicts(const SCH_REFERENCE_LIST &aSymbols)
DATA_MODEL_ROW< SCH_REFERENCE > SYMBOL_FIELDS_TABLE_DATA_MODEL_ROW
@ USER
The field ID hasn't been set yet; field is invalid.
@ REFERENCE
Field Reference of part, i.e. "IC21".
std::string path
#define kv
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition ui_common.h:46