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
169 setCellValues( aRow, aCol, std::vector<wxString>( m_rows[aRow].GetCellItems().size(), aValue ) );
170 m_edited = true;
171}
172
173
175{
176 wxCHECK_RET( aRow >= 0 && aRow < static_cast<int>( m_rows.size() ), wxS( "Invalid row number" ) );
177 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), wxS( "Invalid column number" ) );
178
179 if( !CanClearCell( aRow, aCol ) )
180 return;
181
182 const wxString& fieldName = m_cols[aCol].m_fieldName;
183 std::set<const SCH_SYMBOL*> clearedSymbols;
184
185 for( const SCH_REFERENCE& ref : m_rows[aRow].m_items )
186 clearedSymbols.insert( ref.GetSymbol() );
187
188 // Clearing a field is a symbol-wide operation, even when the table is showing one variant
189 // or one instance of a shared sheet. Clear it from every data-store entry for the symbol so
190 // a later entry cannot recreate it while ApplyData() walks the other instances.
191 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
192 {
193 const SCH_REFERENCE& ref = m_symbolsList[ii];
194
195 if( clearedSymbols.contains( ref.GetSymbol() ) )
196 clearStoredField( ref, fieldName );
197 }
198
199 m_edited = true;
200}
201
202
204{
205 wxCHECK_RET( aRow >= 0 && aRow < static_cast<int>( m_rows.size() ), wxS( "Invalid row number" ) );
206
207 std::set<const SCH_SYMBOL*> rowSymbols;
208
209 for( const SCH_REFERENCE& ref : m_rows[aRow].m_items )
210 rowSymbols.insert( ref.GetSymbol() );
211
212 for( const DATA_MODEL_COL& col : m_cols )
213 {
214 bool revertAllPaths = storageIsSharedAcrossPaths( col.m_fieldName );
215
216 // Field presence belongs to the symbol rather than an individual path. If an edit added
217 // or removed the field, restore every path so a hidden path cannot recreate it or retain
218 // a pending creation when ApplyData() walks the symbol's other instances.
219 if( !revertAllPaths )
220 {
221 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
222 {
223 const SCH_REFERENCE& ref = m_symbolsList[ii];
224
225 if( !rowSymbols.contains( ref.GetSymbol() ) )
226 continue;
227
228 wxString liveValue;
229 wxString storedValue;
230 bool liveFieldPresent = getLiveFieldValue( ref, col.m_fieldName, liveValue );
231 bool storedFieldPresent = getStoredFieldValue( ref, col.m_fieldName, storedValue );
232
233 if( liveFieldPresent != storedFieldPresent )
234 {
235 revertAllPaths = true;
236 break;
237 }
238 }
239 }
240
241 if( !revertAllPaths )
242 continue;
243
244 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
245 {
246 const SCH_REFERENCE& ref = m_symbolsList[ii];
247
248 if( rowSymbols.contains( ref.GetSymbol() ) )
249 updateDataStoreItemFieldFromLive( ref, col.m_fieldName );
250 }
251 }
252
254}
255
256
257void SYMBOL_FIELDS_EDITOR_GRID_DATA_MODEL::setCellValues( int aRow, int aCol, const std::vector<wxString>& aValues )
258{
259 std::span<const SCH_REFERENCE> items = m_rows[aRow].GetCellItems();
260 const wxString& fieldName = m_cols[aCol].m_fieldName;
261
262 std::map<const SCH_SYMBOL*, wxString> editedSymbols;
263
264 for( size_t i = 0; i < items.size(); ++i )
265 editedSymbols[items[i].GetSymbol()] = aValues[i];
266
267 // Field presence is on the symbol object and applies to all instances.
268 // Before editing one path, ensure every instance for that symbol has this field
269 // marked present at least.
270 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
271 {
272 const SCH_REFERENCE& ref = m_symbolsList[ii];
273
274 if( !editedSymbols.contains( ref.GetSymbol() ) )
275 continue;
276
277 wxString unused;
278
279 if( !getStoredFieldValue( ref, fieldName, unused ) )
280 ensureStoredFieldPresent( ref, fieldName );
281 }
282
283 for( size_t i = 0; i < items.size(); ++i )
284 setStoredFieldValue( items[i], fieldName, aValues[i] );
285
286 // ApplyData walks every path a symbol is reachable through, so an edit to storage those
287 // paths have in common must also reach the ones the current scope and filter hide
288 if( storageIsSharedAcrossPaths( fieldName ) )
289 {
290 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
291 {
292 const SCH_REFERENCE& ref = m_symbolsList[ii];
293
294 auto it = editedSymbols.find( ref.GetSymbol() );
295
296 if( it != editedSymbols.end() )
297 setStoredFieldValue( ref, fieldName, it->second );
298 }
299 }
300}
301
302
304{
306 && aFieldName != wxS( "${EXCLUDE_FROM_BOARD}" );
307}
308
309
311{
312 // If items are unannotated then we can't tell if they're units of the same symbol or not
313 if( lhItem.GetRefNumber() == wxT( "?" ) )
314 return false;
315
316 return ( lhItem.GetRef() == rhItem.GetRef() && lhItem.GetRefNumber() == rhItem.GetRefNumber() );
317}
318
319
321 const wxString& aFieldName,
322 wxString& aValue )
323{
324 return getLiveFieldValueForVariant( aRef, aFieldName, m_currentVariant, aValue );
325}
326
327
329{
330 std::vector<SCH_REFERENCE> items;
331
332 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
333 {
334 if( m_symbolsList[i].GetSymbol() )
335 items.push_back( m_symbolsList[i] );
336 }
337
338 return items;
339}
340
341
343 const wxString& aFieldName )
344{
345 SCH_FIELD* field = aRef.GetSymbol()->GetField( aFieldName );
346
347 if( field )
348 {
349 if( field->IsPrivate() )
350 return wxEmptyString;
351 else
352 return field->GetShownText( &aRef.GetSheetPath(), INTERNAL, m_currentVariant );
353 }
354
355 // Handle generated fields with variables as names (e.g. ${QUANTITY}) that are not present in
356 // the symbol by giving them the correct value by resolving against the symbol
357 if( IsGeneratedField( aFieldName ) )
358 {
359 int depth = 0;
360 const SCH_SHEET_PATH& path = aRef.GetSheetPath();
361
362 std::function<bool( wxString* )> symbolResolver =
363 [&]( wxString* token ) -> bool
364 {
365 return aRef.GetSymbol()->ResolveTextVar( &path, token, m_currentVariant, depth + 1 );
366 };
367
368 return ResolveTextVars( aFieldName, &symbolResolver, depth );
369 }
370
371 return wxEmptyString;
372}
373
374
375wxString SYMBOL_FIELDS_EDITOR_GRID_DATA_MODEL::resolveTextVars( const SCH_REFERENCE& aRef, const wxString& aText )
376{
377 int depth = 0;
378
379 std::function<bool( wxString* )> symbolResolver =
380 [&]( wxString* token ) -> bool
381 {
382 if( resolveStoredTextVar( aRef, token ) )
383 return true;
384
385 return aRef.GetSymbol()->ResolveTextVar( &aRef.GetSheetPath(), token, m_currentVariant, depth );
386 };
387
388 return ResolveTextVars( aText, &symbolResolver, depth );
389}
390
391
393{
394 // Variant edits are kept on the symbol instance, but SCH_REFERENCE has no variant form of
395 // the board exclusion so that one always lands on the symbol
396 if( aFieldName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
397 return true;
398
399 return m_currentVariant.IsEmpty();
400}
401
402
404 const wxString& aAttributeName,
405 const wxString& aVariantName )
406{
407 if( aAttributeName == wxS( "${DNP}" ) )
408 return aRef.GetSymbolDNP( aVariantName ) ? wxS( "1" ) : wxS( "0" );
409
410 if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
411 return aRef.GetSymbolExcludedFromBoard() ? wxS( "1" ) : wxS( "0" );
412
413 if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
414 return aRef.GetSymbolExcludedFromBOM( aVariantName ) ? wxS( "1" ) : wxS( "0" );
415
416 if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
417 return aRef.GetSymbolExcludedFromSim( aVariantName ) ? wxS( "1" ) : wxS( "0" );
418
419 if( aAttributeName == wxS( "${EXCLUDE_FROM_POS_FILES}" ) )
420 return aRef.GetSymbolExcludedFromPosFiles( aVariantName ) ? wxS( "1" ) : wxS( "0" );
421
422 return wxS( "0" );
423}
424
425
427 const wxString& aAttributeName,
428 const wxString& aVariantName ) const
429{
430 const SCH_SHEET_PATH& path = aRef.GetSheetPath();
431
432 if( aAttributeName == wxS( "${DNP}" ) )
433 return path.GetDNP( aVariantName );
434 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
435 return path.GetExcludedFromBoard( aVariantName );
436 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
437 return path.GetExcludedFromBOM( aVariantName );
438 else if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
439 return path.GetExcludedFromSim( aVariantName );
440
441 return false;
442}
443
444
446 const wxString& aFieldName,
447 const wxString& aVariantName, wxString& aValue )
448{
449 aValue.clear();
450
451 const SCH_SYMBOL* symbol = aRef.GetSymbol();
452
453 if( !symbol )
454 return false;
455
456 if( fieldIsAttribute( aFieldName ) )
457 {
458 aValue = getAttributeValue( aRef, aFieldName, aVariantName );
459 return true;
460 }
461
462 if( const SCH_FIELD* field = symbol->GetField( aFieldName ) )
463 {
464 if( field->IsPrivate() )
465 return false;
466
467 aValue = symbol->Schematic()->ConvertKIIDsToRefs( field->GetText( &aRef.GetSheetPath(), aVariantName ) );
468 return true;
469 }
470
471 // For generated fields, return the field name itself
472 if( IsGeneratedField( aFieldName ) )
473 {
474 aValue = aFieldName;
475 return true;
476 }
477
478 return false;
479}
480
481
483 const wxString& aFieldName )
484{
485 wxString value;
486 getLiveFieldValueForVariant( aRef, aFieldName, wxEmptyString, value );
487 return value;
488}
489
490
492 const wxString& aAttributeName,
493 const wxString& aValue,
494 const wxString& aVariantName )
495{
496 bool attrChanged = false;
497 bool newValue = aValue == wxS( "1" );
498
499 if( aAttributeName == wxS( "${DNP}" ) )
500 {
501 attrChanged = aRef.GetSymbolDNP( aVariantName ) != newValue;
502
503 if( attrChanged )
504 aRef.SetSymbolDNP( newValue, aVariantName );
505 }
506 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
507 {
508 attrChanged = aRef.GetSymbolExcludedFromBoard() != newValue;
509
510 if( attrChanged )
511 aRef.SetSymbolExcludedFromBoard( newValue );
512 }
513 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
514 {
515 attrChanged = aRef.GetSymbolExcludedFromBOM( aVariantName ) != newValue;
516
517 if( attrChanged )
518 aRef.SetSymbolExcludedFromBOM( newValue, aVariantName );
519 }
520 else if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
521 {
522 attrChanged = aRef.GetSymbolExcludedFromSim( aVariantName ) != newValue;
523
524 if( attrChanged )
525 aRef.SetSymbolExcludedFromSim( newValue, aVariantName );
526 }
527 else if( aAttributeName == wxS( "${EXCLUDE_FROM_POS_FILES}" ) )
528 {
529 attrChanged = aRef.GetSymbolExcludedFromPosFiles( aVariantName ) != newValue;
530
531 if( attrChanged )
532 aRef.SetSymbolExcludedFromPosFiles( newValue, aVariantName );
533 }
534
535 return attrChanged;
536}
537
538
540{
541 if( !m_rebuildsEnabled )
542 return;
543
544 if( GetView() )
545 {
546 // Commit any pending in-place edits before the row gets moved out from under
547 // the editor.
548 static_cast<WX_GRID*>( GetView() )->CommitPendingChanges( true );
549
550 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, m_rows.size() );
551 GetView()->ProcessTableMessage( msg );
552 }
553
554 m_rows.clear();
555
556 EDA_COMBINED_MATCHER matcher( m_filter.Lower(), CTX_SEARCH );
557
558 for( unsigned i = 0; i < m_symbolsList.GetCount(); ++i )
559 {
561
562 if( m_scope == SCOPE::SCOPE_SELECTION
563 && !m_selectionItems.contains( getDataStoreKey( ref ) ) )
564 {
565 continue;
566 }
567
568 if( !MatchesFilter( ref, ref.GetFullRef(), matcher ) )
569 continue;
570
571 if( m_excludeDNP )
572 {
573 bool isDNP = false;
574
575 if( !m_variantNames.empty() )
576 {
577 for( const wxString& variantName : m_variantNames )
578 {
579 if( ref.GetSymbol()->ResolveDNP( &ref.GetSheetPath(), variantName )
580 || ref.GetSheetPath().GetDNP( variantName ) )
581 {
582 isDNP = true;
583 break;
584 }
585 }
586 }
587 else
588 {
589 isDNP = ref.GetSymbol()->ResolveDNP( &ref.GetSheetPath(), m_currentVariant )
591 }
592
593 if( isDNP )
594 continue;
595 }
596
597 if( !m_includeExcluded )
598 {
599 bool isExcluded = false;
600
601 if( !m_variantNames.empty() )
602 {
603 for( const wxString& variantName : m_variantNames )
604 {
605 if( ref.GetSymbol()->ResolveExcludedFromBOM( &ref.GetSheetPath(), variantName )
606 || ref.GetSheetPath().GetExcludedFromBOM( variantName ) )
607 {
608 isExcluded = true;
609 break;
610 }
611 }
612 }
613 else
614 {
617 }
618
619 if( isExcluded )
620 continue;
621 }
622
623 // Check if the symbol if on the current sheet or, in the sheet path somewhere
624 // depending on scope
625 if( ( m_scope == SCOPE::SCOPE_SHEET && ref.GetSheetPath() != m_path )
626 || ( m_scope == SCOPE::SCOPE_SHEET_RECURSIVE
627 && !ref.GetSheetPath().IsContainedWithin( m_path ) ) )
628 {
629 continue;
630 }
631
632 bool matchFound = false;
633
634 // Performance optimization for ungrouped case to skip the N^2 for loop
635 if( !m_groupingEnabled && !ref.IsMultiUnit() )
636 {
637 m_rows.emplace_back( SYMBOL_FIELDS_TABLE_DATA_MODEL_ROW( ref, ROW_STATE::NON_EXPANDABLE ) );
638 continue;
639 }
640
641 // See if we already have a row which this symbol fits into
643 {
644 // all group members must have identical refs so just use the first one
645 SCH_REFERENCE rowRef = row.m_items[0];
646
647 if( unitMatch( ref, rowRef ) )
648 {
649 matchFound = true;
650 row.m_items.push_back( ref );
651 break;
652 }
653 else if( m_groupingEnabled && groupMatch( ref, rowRef ) )
654 {
655 matchFound = true;
656 row.m_items.push_back( ref );
657 row.m_state = ROW_STATE::GROUP_COLLAPSED;
658 break;
659 }
660 }
661
662 if( !matchFound )
663 m_rows.emplace_back( SYMBOL_FIELDS_TABLE_DATA_MODEL_ROW( ref, ROW_STATE::NON_EXPANDABLE ) );
664 }
665
666 if( GetView() )
667 {
668 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_rows.size() );
669 GetView()->ProcessTableMessage( msg );
670 }
671
672 Sort();
673}
674
675
677{
678 for( const SCH_REFERENCE& ref : m_symbolsList )
680
681 bool symbolModified = false;
682 std::unique_ptr<SCH_SYMBOL> symbolCopy;
683
684 for( size_t i = 0; i < m_symbolsList.GetCount(); i++ )
685 {
686 SCH_SYMBOL* symbol = m_symbolsList[i].GetSymbol();
687 SCH_SYMBOL* nextSymbol = nullptr;
688
689 if( ( i + 1 ) < m_symbolsList.GetCount() )
690 nextSymbol = m_symbolsList[i + 1].GetSymbol();
691
692 if( i == 0 )
693 symbolCopy = std::make_unique<SCH_SYMBOL>( *symbol );
694
695 for( const wxString& variantName : storedVariants( m_symbolsList[i] ) )
696 {
697 const std::map<wxString, wxString> fieldStore = getStoredFields( m_symbolsList[i], variantName );
698
699 for( const auto& [srcName, srcValue] : fieldStore )
700 {
701 // Attributes bypass the field logic, so handle them first
702 if( fieldIsAttribute( srcName ) )
703 {
704 symbolModified |= setAttributeValue( m_symbolsList[i], srcName, srcValue, variantName );
705 continue;
706 }
707
708 // Skip generated fields with variables as names (e.g. ${QUANTITY});
709 // they can't be edited
710 if( IsGeneratedField( srcName ) )
711 continue;
712
713 SCH_FIELD* destField = symbol->GetField( srcName );
714
715 if( destField && destField->IsPrivate() )
716 {
717 if( srcValue.IsEmpty() )
718 continue;
719 else
720 destField->SetPrivate( false );
721 }
722
723 // Reaching this point means the data store field is at least marked present,
724 // so add the field to the symbol even when its stored value is empty.
725 bool createField = !destField;
726
727 if( createField )
728 {
729 destField = symbol->AddField( SCH_FIELD( symbol, FIELD_T::USER, srcName ) );
730 destField->SetTextAngle( symbol->GetField( FIELD_T::REFERENCE )->GetTextAngle() );
731
732 if( const TEMPLATE_FIELDNAME* srcTemplate = aTemplateFieldnames.GetFieldName( srcName ) )
733 destField->SetVisible( srcTemplate->m_Visible );
734 else
735 destField->SetVisible( false );
736
737 destField->SetTextPos( symbol->GetPosition() );
738 symbolModified = true;
739 }
740
741 if( !destField )
742 continue;
743
744 // Reference is not editable from this dialog
745 if( destField->GetId() == FIELD_T::REFERENCE )
746 continue;
747
748 wxString previousValue = destField->GetText( &m_symbolsList[i].GetSheetPath(), variantName );
749
750 destField->SetText( symbol->Schematic()->ConvertRefsToKIIDs( srcValue ),
751 &m_symbolsList[i].GetSheetPath(), variantName );
752
753 if( !createField && ( previousValue != srcValue ) )
754 symbolModified = true;
755 }
756 }
757
758 for( int ii = static_cast<int>( symbol->GetFields().size() ) - 1; ii >= 0; ii-- )
759 {
760 if( symbol->GetFields()[ii].IsMandatory() || symbol->GetFields()[ii].IsPrivate() )
761 continue;
762
763 const wxString& existingName = symbol->GetFields()[ii].GetName();
764
765 if( storedFieldIsRemoved( m_symbolsList[i], existingName ) )
766 {
767 symbol->RemoveField( existingName );
768 symbolModified = true;
769 }
770 }
771
772 if( symbolModified && ( symbol != nextSymbol ) )
773 aCommit.Modified( symbol, symbolCopy.release(), m_symbolsList[i].GetSheetPath().LastScreen() );
774
775 // Only reset the modified flag and next symbol copy if the next symbol is different from the current one.
776 if( symbol != nextSymbol )
777 {
778 if( nextSymbol )
779 symbolCopy = std::make_unique<SCH_SYMBOL>( *nextSymbol );
780 else
781 symbolCopy.reset( nullptr );
782
783 symbolModified = false;
784 }
785 }
786
787 for( const SCH_REFERENCE& ref : m_symbolsList )
788 acceptDataStoreItem( ref );
789
790 m_edited = false;
791}
792
793
795{
796 bool refListChanged = false;
797
798 for( const SCH_REFERENCE& ref : aRefs )
799 {
800 if( !m_symbolsList.Contains( ref ) )
801 {
802 const SCH_REFERENCE* existingRef = nullptr;
803
804 // A field belongs to the symbol object not reference, so an additional sheet path must have
805 // the same field presence as the paths which already reach that symbol. Field values may
806 // still differ by path when editing a variant.
807 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
808 {
809 if( m_symbolsList[ii].GetSymbol() == ref.GetSymbol() )
810 {
811 existingRef = &m_symbolsList[ii];
812 break;
813 }
814 }
815
816 if( existingRef )
817 {
818 const auto& existingFields = m_dataStore[getDataStoreKey( *existingRef )];
819
820 for( const auto& [name, source] : existingFields )
821 {
822 FIELD_STORE_VALUE& field = storedField( ref, name );
823 field.m_present = source.m_present;
824
825 for( const auto& [variant, sourceState] : source.m_variants )
826 {
827 FIELD_VALUE_STATE& state = field.m_variants[variant];
828 getLiveFieldValueForVariant( ref, name, variant, state.m_baseline );
829 state.m_value = variant.IsEmpty() ? sourceState.m_value : state.m_baseline;
830 }
831 }
832 }
833 else
834 {
836 }
837
838 m_symbolsList.AddItem( ref );
839
840 refListChanged = true;
841 }
842 }
843
844 if( refListChanged )
845 m_symbolsList.SortBySymbolPtr();
846}
847
848
850{
851 // The schematic event listener passes us the symbol after it has been removed,
852 // so we can't just work with a SCH_REFERENCE_LIST like the other handlers as the
853 // references are already gone. Instead we need to prune our list.
854
855 // Since we now use full KIID_PATH as keys, we need to find and remove all entries
856 // that correspond to this symbol (their keys end with the symbol's UUID)
857 KIID symbolUuid = aSymbol.m_Uuid;
858 std::vector<KIID_PATH> keysToRemove;
859
860 for( const auto& [key, value] : m_dataStore )
861 {
862 if( !key.empty() && ( key.back() == symbolUuid ) )
863 keysToRemove.push_back( key );
864 }
865
866 for( const KIID_PATH& key : keysToRemove )
867 m_dataStore.erase( key );
868
869 // Remove all refs that match this symbol using remove_if
870 m_symbolsList.erase( std::remove_if( m_symbolsList.begin(), m_symbolsList.end(),
871 [&aSymbol]( const SCH_REFERENCE& ref ) -> bool
872 {
873 return ref.GetSymbol()->m_Uuid == aSymbol.m_Uuid;
874 } ),
875 m_symbolsList.end() );
876}
877
878
880{
881 for( const SCH_REFERENCE& ref : aRefs )
882 {
883 int index = m_symbolsList.FindRefByFullPath( ref.GetFullPath() );
884
885 if( index != -1 )
886 {
887 m_dataStore.erase( getDataStoreKey( ref ) );
888 m_symbolsList.RemoveItem( index );
889 }
890 }
891}
892
893
895{
896 bool refListChanged = false;
897 std::set<SCH_SYMBOL*> updatedSymbols;
898
899 for( const SCH_REFERENCE& incomingRef : aRefs )
900 {
901 if( incomingRef.GetSymbol() )
902 updatedSymbols.insert( incomingRef.GetSymbol() );
903
904 SCH_REFERENCE* cachedRef = m_symbolsList.FindItem( incomingRef );
905
906 // This looks like it might be assigning to itself because it often is
907 if( cachedRef )
908 {
909 // When these things don't happen to be the same pointer, this will update our
910 // cached reference's stuff like reference/unit/etc, but not the pointer to the symbol itself
911 *cachedRef = incomingRef;
912 }
913 else
914 {
915 m_symbolsList.AddItem( incomingRef );
916 refListChanged = true;
917 }
918 }
919
920 if( refListChanged )
921 m_symbolsList.SortBySymbolPtr();
922
923 // Field presence is on the symbol object, so refresh every path/instance which reaches a
924 // changed symbol even if the event supplied only one path.
925 for( unsigned ii = 0; ii < m_symbolsList.GetCount(); ++ii )
926 {
927 const SCH_REFERENCE& ref = m_symbolsList[ii];
928
929 if( !updatedSymbols.contains( ref.GetSymbol() ) )
930 continue;
931
933 }
934
936}
937
938
939bool SYMBOL_FIELDS_EDITOR_GRID_DATA_MODEL::DeleteRows( size_t aPosition, size_t aNumRows )
940{
941 size_t curNumRows = m_rows.size();
942
943 if( aPosition >= curNumRows )
944 {
945 wxFAIL_MSG( wxString::Format( wxT( "Called SYMBOL_FIELDS_EDITOR_GRID_DATA_MODEL::DeleteRows(aPosition=%lu, "
946 "aNumRows=%lu)\nPosition value is invalid for present table with %lu rows" ),
947 (unsigned long) aPosition, (unsigned long) aNumRows,
948 (unsigned long) curNumRows ) );
949
950 return false;
951 }
952
953 if( aNumRows > curNumRows - aPosition )
954 {
955 aNumRows = curNumRows - aPosition;
956 }
957
958 if( aNumRows >= curNumRows )
959 {
960 m_rows.clear();
961 m_dataStore.clear();
962 m_symbolsList.Clear();
963
964 if( GetView() )
965 {
966 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aPosition, aNumRows );
967 GetView()->ProcessTableMessage( msg );
968 }
969 }
970 else
971 {
972 // Note: this code is currently dead, as all current usage calls the clear path above,
973 // which is left because it is faster. This code *should* be correct but is untested.
974 auto first = m_rows.begin() + aPosition;
975 auto last = first + aNumRows;
976
977 SCH_REFERENCE_LIST refsToDelete;
978 for( auto it = first; it != last && it != m_rows.end(); ++it )
979 {
980 for( const SCH_REFERENCE& ref : it->m_items )
981 refsToDelete.AddItem( ref );
982 }
983
984 RemoveReferences( refsToDelete );
985 // This will also notify the view
986 RebuildRows();
987 }
988
989 return true;
990}
991
992
993std::vector<FIELD_CASE_CONFLICT> DetectFieldCaseConflicts( const SCH_REFERENCE_LIST& aSymbols )
994{
995 std::vector<FIELD_CASE_CONFLICT> conflicts;
996
997 for( unsigned i = 0; i < aSymbols.GetCount(); ++i )
998 {
999 SCH_SYMBOL* symbol = aSymbols[i].GetSymbol();
1000
1001 if( !symbol )
1002 continue;
1003
1004 std::map<wxString, std::vector<std::pair<wxString, wxString>>> groups;
1005
1006 for( const SCH_FIELD& field : symbol->GetFields() )
1007 {
1008 if( field.IsMandatory() || field.IsPrivate() )
1009 continue;
1010
1011 groups[field.GetName().Lower()].emplace_back( field.GetName(), field.GetText() );
1012 }
1013
1014 for( const auto& [key, members] : groups )
1015 {
1016 if( members.size() < 2 )
1017 continue;
1018
1020 c.symbol = symbol;
1021 c.sheetPath = aSymbols[i].GetSheetPath();
1022 c.reference = symbol->GetRef( &c.sheetPath );
1023 c.caseFoldedKey = key;
1024 c.variants = members;
1025 conflicts.push_back( std::move( c ) );
1026 }
1027 }
1028
1029 return conflicts;
1030}
int index
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:224
const KIID m_Uuid
Definition eda_item.h:599
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:556
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
virtual EDA_ANGLE GetTextAngle() const
Definition eda_text.h:178
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:268
std::map< KIID_PATH, std::map< wxString, FIELD_STORE_VALUE > > m_dataStore
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
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 updateDataStoreItemFieldFromLive(const SCH_REFERENCE &aItem, const wxString &aFieldName, bool aAllVariants=false)
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)
void initializeDataStoreItem(const SCH_REFERENCE &aItem)
bool storedFieldIsRemoved(const SCH_REFERENCE &aItem, const wxString &aName) const
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
FIELD_STORE_VALUE & storedField(const SCH_REFERENCE &aItem, const wxString &aFieldName)
bool resolveStoredTextVar(const SCH_REFERENCE &aItem, wxString *aToken, bool aCaseSensitive=false) const
bool IsCellReadOnly(int aRow, int aCol) override
std::map< wxString, wxString > getStoredFields(const SCH_REFERENCE &aItem, const wxString &aVariant) const
void refreshDataStoreItem(const SCH_REFERENCE &aItem)
bool MatchesFilter(const SCH_REFERENCE &aItem, const wxString &aReference, EDA_COMBINED_MATCHER &aMatcher)
void acceptDataStoreItem(const SCH_REFERENCE &aItem)
std::set< wxString > storedVariants(const SCH_REFERENCE &aItem) const
Definition kiid.h:46
wxString ConvertKIIDsToRefs(const wxString &aSource) const
wxString ConvertRefsToKIIDs(const wxString &aSource) const
Commit for the schematic and symbol editors.
Definition sch_commit.h:54
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
FIELD_T GetId() const
Definition sch_field.h:143
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:258
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:302
bool IsPrivate() const
Definition sch_item.h:259
bool ResolveExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:392
bool ResolveDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:446
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)
wxString GetFullRef() const
Return reference name with unit altogether.
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
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:73
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:895
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)
void setCellValues(int aRow, int aCol, const std::vector< wxString > &aValues) override
Write one value per item represented by the cell. Models may propagate shared storage.
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)
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 fieldSupportsVariants(const wxString &aFieldName) const override
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)
void ApplyData(SCH_COMMIT &aCommit, TEMPLATES &aTemplateFieldnames)
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.
bool getLiveFieldValueForVariant(const SCH_REFERENCE &aRef, const wxString &aFieldName, const wxString &aVariantName, wxString &aValue) override
void SetValue(int aRow, int aCol, const wxString &aValue) override
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...
bool attributeForcedOnBySheet(const SCH_REFERENCE &aRef, const wxString &aAttributeName, const wxString &aVariantName) const override
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:133
wxGridCellAttr * GetAttr(int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind) override
Definition wx_grid.h:96
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:94
@ CTX_SEARCH
std::string source
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
std::map< wxString, FIELD_VALUE_STATE > m_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
wxString GetDefaultFieldName(FIELD_T aFieldId, TRANSLATION aTranslation)
Return a default symbol field name for a mandatory field type.
@ USER
The field ID hasn't been set yet; field is invalid.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ UNTRANSLATED
std::string path
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition ui_common.h:46