KiCad PCB EDA Suite
Loading...
Searching...
No Matches
lib_fields_data_model.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2023 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#include <wx/string.h>
20#include <wx/debug.h>
21#include <wx/grid.h>
22#include <wx/settings.h>
23#include <wx/brush.h>
24#include <common.h>
25#include <widgets/wx_grid.h>
26#include <sch_reference_list.h>
27#include <schematic_settings.h>
28#include <template_fieldnames.h>
29#include "string_utils.h"
30#include <trace_helpers.h>
31#include <validators.h>
32
34
35
36const wxString LIB_FIELDS_EDITOR_GRID_DATA_MODEL::SYMBOL_NAME = wxS( "${SYMBOL_NAME}" );
37const wxString LIB_FIELDS_EDITOR_GRID_DATA_MODEL::SYMBOL_PARENT = wxS( "${SYMBOL_PARENT}" );
38const wxString LIB_FIELDS_EDITOR_GRID_DATA_MODEL::SYMBOL_ROOT = wxS( "${SYMBOL_ROOT}" );
39const wxString LIB_FIELDS_EDITOR_GRID_DATA_MODEL::SYMBOL_KEYWORDS = wxS( "${SYMBOL_KEYWORDS}" );
40const wxString LIB_FIELDS_EDITOR_GRID_DATA_MODEL::SYMBOL_IS_POWER = wxS( "${SYMBOL_IS_POWER}" );
41const wxString LIB_FIELDS_EDITOR_GRID_DATA_MODEL::SYMBOL_IS_LOCAL_POWER = wxS( "${SYMBOL_IS_LOCAL_POWER}" );
42
43
45 const wxString& aFieldName,
46 wxString& aValue )
47{
48 int col = GetFieldNameCol( aFieldName );
49 aValue.clear();
50
51 if( col != -1 && ColIsAttribute( col ) )
52 {
53 aValue = getAttributeValue( aSymbol, aFieldName );
54 return true;
55 }
56 else if( const SCH_FIELD* field = aSymbol->GetField( aFieldName ) )
57 {
58 aValue = field->GetText();
59 return true;
60 }
61 else if( aFieldName == SYMBOL_KEYWORDS )
62 {
63 aValue = aSymbol->GetRawKeyWords();
64 return true;
65 }
66 else if( aFieldName == SYMBOL_NAME )
67 {
68 aValue = aSymbol->GetName();
69 return true;
70 }
71
72 return false;
73}
74
75
77 const wxString& aFieldName,
78 wxString& aValue ) const
79{
80 getStoredFieldValue( aSymbol, aFieldName, aValue );
81
82 const SCH_FIELD* field = aSymbol->GetField( aFieldName );
83 const bool keywords = aFieldName == SYMBOL_KEYWORDS;
84
85 // Flatten() inherits empty mandatory fields and keywords. Keep that distinction in the data store:
86 // displaying a parent's value must not create an override in the derived symbol.
87 if( !aValue.IsEmpty() || ( !keywords && ( !field || !field->IsMandatory() ) ) )
88 return;
89
90 std::set<const LIB_SYMBOL*> visited{ aSymbol };
91 std::shared_ptr<LIB_SYMBOL> parent = aSymbol->GetParent().lock();
92
93 while( parent && visited.insert( parent.get() ).second )
94 {
95 if( m_dataStore.contains( getDataStoreKey( parent.get() ) ) )
96 getStoredFieldValue( parent.get(), aFieldName, aValue );
97 else if( keywords )
98 aValue = parent->GetRawKeyWords();
99 else if( const SCH_FIELD* parentField = parent->GetField( aFieldName ) )
100 aValue = parentField->GetText();
101
102 if( !aValue.IsEmpty() )
103 return;
104
105 parent = parent->GetParent().lock();
106 }
107}
108
109
111{
112 return m_symbolsList;
113}
114
115
116void LIB_FIELDS_EDITOR_GRID_DATA_MODEL::SetValue( int aRow, int aCol, const wxString& aValue )
117{
118 wxCHECK_RET( aRow >= 0 && aRow < (int) m_rows.size(), wxS( "Invalid row number" ) );
119 wxCHECK_RET( aCol >= 0 && aCol < (int) m_cols.size(), wxS( "Invalid column number" ) );
120
121 if( IsCellReadOnly( aRow, aCol ) )
122 return;
123
124 if( aValue == INDETERMINATE_STATE )
125 return;
126
127 LIB_FIELDS_TABLE_DATA_MODEL_ROW& rowGroup = m_rows[aRow];
128 const wxString& fieldName = m_cols[aCol].m_fieldName;
129
130 for( LIB_SYMBOL* symbol : rowGroup.GetCellItems() )
131 {
132 if( fieldName == SYMBOL_IS_POWER )
133 {
134 setStoredFieldValue( symbol, fieldName, aValue );
135
136 if( aValue == wxS( "1" ) )
137 setPowerSymbolDefaults( symbol );
138 else
139 setStoredFieldValue( symbol, SYMBOL_IS_LOCAL_POWER, wxS( "0" ) );
140 }
141 else if( fieldName == SYMBOL_IS_LOCAL_POWER )
142 {
143 // Local Power is disabled in the symbol properties dialog until Power is enabled.
144 if( getStoredPowerSymbolValue( symbol ) )
145 {
146 setStoredFieldValue( symbol, fieldName, aValue );
147 setPowerSymbolDefaults( symbol );
148 }
149 }
150 else if( getStoredPowerSymbolValue( symbol ) && isPowerSymbolControlledField( fieldName ) )
151 {
152 if( fieldName == GetDefaultFieldName( FIELD_T::VALUE, UNTRANSLATED ) )
153 setStoredFieldValue( symbol, fieldName, symbol->GetName() );
154 else
155 setStoredFieldValue( symbol, fieldName, wxS( "1" ) );
156 }
157 else
158 {
159 setStoredFieldValue( symbol, fieldName, aValue );
160 }
161
162 if( fieldName == SYMBOL_IS_POWER || fieldName == SYMBOL_IS_LOCAL_POWER )
163 {
164 const bool isPower = getStoredPowerSymbolValue( symbol );
165 wxString localPower;
166 getStoredFieldValue( symbol, SYMBOL_IS_LOCAL_POWER, localPower );
167
168 // Show pending changes in descendants too, including those hidden by the filter.
169 for( LIB_SYMBOL* child : m_symbolsList )
170 {
171 if( !child->IsDerived() )
172 continue;
173
174 std::shared_ptr<LIB_SYMBOL> root = child->GetRootSymbol();
175
176 // The root on the editing canvas can be a different object from the library copy.
177 if( !root || root->m_Uuid != symbol->m_Uuid )
178 continue;
179
180 setStoredFieldValue( child, SYMBOL_IS_POWER, isPower ? wxS( "1" ) : wxS( "0" ) );
182 isPower && localPower == wxS( "1" ) ? wxS( "1" ) : wxS( "0" ) );
183
184 if( isPower )
185 setPowerSymbolDefaults( child );
186 }
187 }
188 }
189
190 m_edited = true;
191
192 if( GetView() )
193 GetView()->ForceRefresh();
194}
195
196
198{
199 wxCHECK( aRow >= 0 && aRow < static_cast<int>( m_rows.size() ), false );
200 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
201
202 // Value must remain non-empty when applied to a library symbol.
203 if( IsCellReadOnly( aRow, aCol ) || ColIsValue( aCol ) )
204 return false;
205
206 const wxString& fieldName = m_cols[aCol].m_fieldName;
207 bool hasOverride = false;
208
209 for( LIB_SYMBOL* symbol : m_rows[aRow].GetCellItems() )
210 {
211 // A grouped cell must not clear any root symbols along with the derived ones.
212 if( !symbol->IsDerived() )
213 return false;
214
215 const SCH_FIELD* field = symbol->GetField( fieldName );
216
217 if( fieldName != SYMBOL_KEYWORDS && ( !field || !field->IsMandatory() ) )
218 return false;
219
220 wxString value;
221 getStoredFieldValue( symbol, fieldName, value );
222 hasOverride |= !value.IsEmpty();
223 }
224
225 return hasOverride;
226}
227
228
230{
231 if( CanUseParentValue( aRow, aCol ) )
232 SetValue( aRow, aCol, wxEmptyString );
233}
234
235
237{
238 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
239 return m_cols[aCol].m_fieldName == LIB_FIELDS_EDITOR_GRID_DATA_MODEL::SYMBOL_NAME;
240}
241
242
243bool LIB_FIELDS_EDITOR_GRID_DATA_MODEL::fieldIsAttribute( const wxString& aFieldName ) const
244{
245 return FIELDS_TABLE_DATA_MODEL_BASE::fieldIsAttribute( aFieldName ) || aFieldName == SYMBOL_IS_POWER
246 || aFieldName == SYMBOL_IS_LOCAL_POWER;
247}
248
249
250bool LIB_FIELDS_EDITOR_GRID_DATA_MODEL::fieldIsItemProperty( const wxString& aFieldName ) const
251{
252 return aFieldName == SYMBOL_NAME || aFieldName == SYMBOL_KEYWORDS
254}
255
256
258{
260 return true;
261
262 if( m_cols[aCol].m_fieldName == SYMBOL_IS_POWER || m_cols[aCol].m_fieldName == SYMBOL_IS_LOCAL_POWER )
263 {
264 for( LIB_SYMBOL* symbol : m_rows[aRow].GetCellItems() )
265 {
266 if( symbol->IsDerived() )
267 return true;
268 }
269 }
270
271 if( m_cols[aCol].m_fieldName == SYMBOL_IS_LOCAL_POWER )
272 {
273 for( LIB_SYMBOL* symbol : m_rows[aRow].GetCellItems() )
274 {
275 if( getStoredPowerSymbolValue( symbol ) )
276 return false;
277 }
278
279 return true;
280 }
281
282 if( !isPowerSymbolControlledField( m_cols[aCol].m_fieldName ) )
283 return false;
284
285 for( LIB_SYMBOL* symbol : m_rows[aRow].GetCellItems() )
286 {
287 if( !getStoredPowerSymbolValue( symbol ) )
288 return false;
289 }
290
291 return true;
292}
293
294
295wxGridCellAttr* LIB_FIELDS_EDITOR_GRID_DATA_MODEL::GetAttr( int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind )
296{
297 wxGridCellAttr* attr = wxGridTableBase::GetAttr( aRow, aCol, aKind );
298 bool needsUrlEditor = cellUsesUrlEditor( aRow, aCol );
299 bool needsResolvedTextRenderer = cellUsesResolvedTextRenderer( aRow, aCol );
300
301 for( LIB_SYMBOL* symbol : m_rows[aRow].GetCellItems() )
302 {
303 wxString storedValue;
304 wxString effectiveValue;
305 getStoredFieldValue( symbol, m_cols[aCol].m_fieldName, storedValue );
306 getEffectiveFieldValue( symbol, m_cols[aCol].m_fieldName, effectiveValue );
307
308 if( storedValue != effectiveValue )
309 needsResolvedTextRenderer = true;
310 }
311
312 wxGridCellAttr* modelAttr = nullptr;
313
314 if( needsUrlEditor )
315 modelAttr = cloneUrlEditorAttr();
316 else if( m_colAttrs.find( aCol ) != m_colAttrs.end() && m_colAttrs[aCol] )
317 modelAttr = m_colAttrs[aCol]->Clone();
318
319 if( modelAttr )
320 {
321 if( attr )
322 {
323 // Copy any existing attributes that aren't overridden
324 if( attr->HasBackgroundColour() && !modelAttr->HasBackgroundColour() )
325 modelAttr->SetBackgroundColour( attr->GetBackgroundColour() );
326 if( attr->HasTextColour() && !modelAttr->HasTextColour() )
327 modelAttr->SetTextColour( attr->GetTextColour() );
328 if( attr->HasFont() && !modelAttr->HasFont() )
329 modelAttr->SetFont( attr->GetFont() );
330
331 attr->DecRef();
332 }
333
334 attr = modelAttr;
335 }
336 else if( !attr )
337 {
338 attr = new wxGridCellAttr;
339 }
340
341 if( IsCellReadOnly( aRow, aCol ) )
342 attr->SetReadOnly();
343
344 bool rowModified = false;
345 bool cellModified = false;
346 bool cellEmpty = true;
347 bool blankModified = false;
348
349 const wxString& fieldName = m_cols[aCol].m_fieldName;
350
351 for( LIB_SYMBOL* symbol : m_rows[aRow].GetCellItems() )
352 {
353 wxString liveValue;
354 wxString storedValue;
355 bool liveFieldPresent = getLiveFieldValue( symbol, fieldName, liveValue );
356 bool storedFieldPresent = getStoredFieldValue( symbol, fieldName, storedValue );
357 bool modified = liveFieldPresent != storedFieldPresent || liveValue != storedValue;
358
359 if( modified )
360 cellModified = true;
361
362 bool elementEmpty = !storedFieldPresent || ( !liveFieldPresent && !modified );
363
364 if( !elementEmpty )
365 cellEmpty = false;
366
367 if( storedValue.IsEmpty() && modified )
368 blankModified = true;
369
370 if( !rowModified )
371 {
372 for( const DATA_MODEL_COL& col : m_cols )
373 {
374 if( fieldIsModified( symbol, col.m_fieldName ) )
375 {
376 rowModified = true;
377 break;
378 }
379 }
380 }
381
382 if( cellModified && rowModified && !cellEmpty )
383 break;
384 }
385
386 if( cellEmpty && !ColIsAttribute( aCol ) )
387 {
388 attr->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
389
390 for( LIB_SYMBOL* symbol : m_rows[aRow].GetCellItems() )
391 {
392 wxString liveValue;
393 wxString storedValue;
394 bool liveFieldPresent = getLiveFieldValue( symbol, fieldName, liveValue );
395 bool storedFieldPresent = getStoredFieldValue( symbol, fieldName, storedValue );
396 bool modified = liveFieldPresent != storedFieldPresent || liveValue != storedValue;
397
398 if( modified )
399 {
400 if( !storedFieldPresent )
401 {
402 if( !liveFieldPresent )
403 {
404 attr->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
405 }
406 else if( liveValue.empty() )
407 {
409 }
410 else
411 {
413 }
414 }
415 else if( storedValue.IsEmpty() )
416 {
418 }
419 else
420 {
422 }
423 }
424 }
425 }
426 else
427 {
428 if( rowModified )
429 attr->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWFRAME ) );
430
431 if( blankModified )
432 attr->SetBackgroundColour( FIELDS_TABLE_COLOR::EDITED_EMPTY_FIELD_BRIGHT_GREEN );
433 }
434
435 if( needsResolvedTextRenderer )
436 applyResolvedTextRenderer( attr, !rowModified );
437
438 if( cellModified )
439 {
440 wxFont font;
441
442 if( attr->HasFont() )
443 font = attr->GetFont();
444 else if( GetView() )
445 font = GetView()->GetDefaultCellFont();
446 else
447 font = wxFont();
448
449 if( font.IsOk() )
450 {
451 font.MakeBold();
452 attr->SetFont( font );
453 }
454 }
455
456 return applyCellDecorations( attr, aRow, aCol );
457}
458
459
461 wxString& aNewSymbolName )
462{
463 wxCHECK_RET( aRow >= 0 && aRow < (int) m_rows.size(), "Invalid Row Number" );
464 wxCHECK_RET( aCol >= 0 && aCol < (int) m_cols.size(), "Invalid Column Number" );
465
466 const LIB_SYMBOL* parentSymbol = m_rows[aRow].m_items[0];
467
468 wxLogTrace( traceLibFieldTable, "CreateDerivedSymbolImmediate: Creating '%s' from parent '%s' immediately",
469 aNewSymbolName, parentSymbol->GetName() );
470
471 // Generate a fresh UUID for the new derived symbol
472 KIID newDerivedSymbolUuid;
473
474 // Create the symbol immediately
475 createActualDerivedSymbol( parentSymbol, aNewSymbolName, newDerivedSymbolUuid );
476
477 // Rebuild the grid to show the new symbol
478 RebuildRows();
479
480 m_edited = true;
481}
482
483void LIB_FIELDS_EDITOR_GRID_DATA_MODEL::createActualDerivedSymbol( const LIB_SYMBOL* aParentSymbol, const wxString& aNewSymbolName, const KIID& aNewSymbolUuid )
484{
485 wxLogTrace( traceLibFieldTable, "createActualDerivedSymbol: Creating '%s' from parent '%s', symbol list size before: %zu",
486 aNewSymbolName, aParentSymbol->GetName(), m_symbolsList.size() );
487
488 LIB_SYMBOL* newSymbol = nullptr;
489
490 for( LIB_SYMBOL* sym : m_symbolsList )
491 {
492 if( sym->m_Uuid == aNewSymbolUuid )
493 {
494 newSymbol = sym;
495 break;
496 }
497 }
498
499 if( !newSymbol )
500 {
501 newSymbol = new LIB_SYMBOL( *aParentSymbol );
502 newSymbol->SetName( aNewSymbolName );
503
504 // Also update the VALUE field to reflect the new name for derived symbols
505 newSymbol->GetValueField().SetText( aNewSymbolName );
506
507 newSymbol->SetParent( const_cast<LIB_SYMBOL*>( aParentSymbol ) );
508 // Note: SetLib() not called here - library association handled by dialog's library manager
509 const_cast<KIID&>( newSymbol->m_Uuid ) = aNewSymbolUuid;
510 m_symbolsList.push_back( newSymbol );
511
512 wxLogTrace( traceLibFieldTable, "createActualDerivedSymbol: Added new symbol to list, size now: %zu",
513 m_symbolsList.size() );
514
515 // Initialize field data for the new symbol in the data store
516 initializeDataStoreItem( newSymbol );
517
518 wxLogTrace( traceLibFieldTable, "createActualDerivedSymbol: Initialized field data for new symbol" );
519 }
520
521 // Note: Not adding to symbolLibrary directly - this will be handled by the dialog's library manager integration
522 wxString libraryName = aParentSymbol->GetLibId().GetLibNickname();
523 m_createdDerivedSymbols.emplace_back( newSymbol, libraryName );
524
525 wxLogTrace( traceLibFieldTable, "Created derived symbol '%s' for library '%s', total tracked: %zu",
526 aNewSymbolName, libraryName, m_createdDerivedSymbols.size() );
527}
528
530 const wxString& aAttributeName )
531{
532 if( aAttributeName == wxS( "${DNP}" ) )
533 return aSymbol->GetDNP() ? wxS( "1" ) : wxS( "0" );
534
535 if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
536 return aSymbol->GetExcludedFromBoard() ? wxS( "1" ) : wxS( "0" );
537
538 if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
539 return aSymbol->GetExcludedFromBOM() ? wxS( "1" ) : wxS( "0" );
540
541 if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
542 return aSymbol->GetExcludedFromSim() ? wxS( "1" ) : wxS( "0" );
543
544 if( aAttributeName == wxS( "${EXCLUDE_FROM_POS_FILES}" ) )
545 return aSymbol->GetExcludedFromPosFiles() ? wxS( "1" ) : wxS( "0" );
546
547 if( aAttributeName == SYMBOL_IS_POWER )
548 return aSymbol->IsPower() ? wxS( "1" ) : wxS( "0" );
549
550 if( aAttributeName == SYMBOL_IS_LOCAL_POWER )
551 return aSymbol->IsLocalPower() ? wxS( "1" ) : wxS( "0" );
552
553
554 return wxS( "0" );
555}
556
558 const wxString& aAttributeName,
559 const wxString& aValue )
560{
561 if( aAttributeName == wxS( "${DNP}" ) )
562 aSymbol->SetDNP( aValue == wxS( "1" ) );
563 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
564 aSymbol->SetExcludedFromBoard( aValue == wxS( "1" ) );
565 else if( aAttributeName == wxS( "${EXCLUDE_FROM_BOM}" ) )
566 aSymbol->SetExcludedFromBOM( aValue == wxS( "1" ) );
567 else if( aAttributeName == wxS( "${EXCLUDE_FROM_SIM}" ) )
568 aSymbol->SetExcludedFromSim( aValue == wxS( "1" ) );
569 else if( aAttributeName == wxS( "${EXCLUDE_FROM_POS_FILES}" ) )
570 aSymbol->SetExcludedFromPosFiles( aValue == wxS( "1" ) );
571 else
572 wxLogDebug( "Unknown attribute name: %s", aAttributeName );
573}
574
575
577{
578 return aFieldName == GetDefaultFieldName( FIELD_T::VALUE, UNTRANSLATED )
579 || aFieldName == wxS( "${EXCLUDE_FROM_BOARD}" )
580 || aFieldName == wxS( "${EXCLUDE_FROM_BOM}" )
581 || aFieldName == wxS( "${EXCLUDE_FROM_POS_FILES}" )
582 || aFieldName == wxS( "${EXCLUDE_FROM_SIM}" );
583}
584
585
587{
588 wxString value;
589
590 if( getStoredFieldValue( aSymbol, SYMBOL_IS_POWER, value ) )
591 return value == wxS( "1" );
592
593 return GetFieldNameCol( SYMBOL_IS_POWER ) == -1 && aSymbol->IsPower();
594}
595
596
598{
600
601 static const std::vector<wxString> exclusionFields = {
602 wxS( "${EXCLUDE_FROM_BOARD}" ),
603 wxS( "${EXCLUDE_FROM_BOM}" ),
604 wxS( "${EXCLUDE_FROM_POS_FILES}" ),
605 wxS( "${EXCLUDE_FROM_SIM}" ),
606 };
607
608 for( const wxString& fieldName : exclusionFields )
609 {
610 if( GetFieldNameCol( fieldName ) != -1 )
611 setStoredFieldValue( aSymbol, fieldName, wxS( "1" ) );
612 }
613}
614
615
616wxString LIB_FIELDS_EDITOR_GRID_DATA_MODEL::getAttributeResolvedValue( const wxString& aFieldName, bool aValue ) const
617{
618 if( !aValue )
619 return wxEmptyString;
620
621 if( aFieldName == SYMBOL_IS_POWER )
622 return wxS( "Power Symbol" );
623 else if( aFieldName == SYMBOL_IS_LOCAL_POWER )
624 return wxS( "Local Power Symbol" );
625
627}
628
629
631{
632 if( !m_rebuildsEnabled )
633 return;
634
635 wxLogTrace( traceLibFieldTable, "RebuildRows: Starting rebuild with %zu symbols in list", m_symbolsList.size() );
636
637 if( GetView() )
638 {
639 // Commit any pending in-place edits before the row gets moved out from under
640 // the editor.
641 static_cast<WX_GRID*>( GetView() )->CommitPendingChanges( true );
642
643 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, (int) m_rows.size() );
644 GetView()->ProcessTableMessage( msg );
645 }
646
647 m_rows.clear();
648
649 wxLogTrace( traceLibFieldTable, "RebuildRows: About to process %zu symbols", m_symbolsList.size() );
650
651 EDA_COMBINED_MATCHER matcher( m_filter.Lower(), CTX_SEARCH );
652 int parentCol = GetFieldNameCol( SYMBOL_PARENT );
653 bool groupByParent = parentCol >= 0 && GetGroupColumn( parentCol );
654 int rootCol = GetFieldNameCol( SYMBOL_ROOT );
655 bool groupByRootOnly = m_groupingEnabled && rootCol >= 0 && GetGroupColumn( rootCol );
656
657 for( const DATA_MODEL_COL& col : m_cols )
658 {
659 if( col.m_group && col.m_fieldName != SYMBOL_ROOT )
660 groupByRootOnly = false;
661 }
662
663 for( LIB_SYMBOL* symbol : m_symbolsList )
664 {
665 wxLogTrace( traceLibFieldTable, "RebuildRows: Processing symbol '%s' (UUID: %s)",
666 symbol->GetName(), symbol->m_Uuid.AsString() );
667
668 if( m_scope == SCOPE::SCOPE_RELATED_SYMBOLS )
669 {
670 std::shared_ptr<LIB_SYMBOL> root = symbol->GetRootSymbol();
671
672 if( !root || root->GetName() != m_relatedSymbolRoot )
673 continue;
674 }
675
676 if( !MatchesFilter( symbol, symbol->GetName(), matcher ) )
677 continue;
678
679 if( m_excludeDNP && symbol->GetDNP() )
680 continue;
681
682 if( !m_includeExcluded && symbol->GetExcludedFromBOM() )
683 continue;
684
685 bool matchFound = false;
686
687 // Keep unrelated roots separate instead of grouping their empty parent values together.
688 // Also skip the N^2 grouping loop entirely when grouping is disabled.
689 if( !m_groupingEnabled || ( groupByParent && symbol->IsRoot() ) )
690 {
691 m_rows.emplace_back( LIB_FIELDS_TABLE_DATA_MODEL_ROW( symbol, ROW_STATE::NON_EXPANDABLE ) );
692 continue;
693 }
694
695 // See if we already have a row which this symbol fits into
697 {
698 // all group members must have identical refs so just use the first one
699 LIB_SYMBOL* rowSymbol = row.m_items[0];
700
701 if( groupMatch( symbol, rowSymbol ) )
702 {
703 matchFound = true;
704 row.m_items.push_back( symbol );
705 row.m_state = ROW_STATE::GROUP_COLLAPSED;
706 break;
707 }
708 }
709
710 if( !matchFound )
711 m_rows.emplace_back( LIB_FIELDS_TABLE_DATA_MODEL_ROW( symbol, ROW_STATE::NON_EXPANDABLE ) );
712 }
713
714 if( groupByRootOnly )
715 {
716 // We're going to break m_rows into potentially more rows, which we collect
717 // into this, then we'll swap it all out when we're done.
718 std::vector<LIB_FIELDS_TABLE_DATA_MODEL_ROW> families;
719
721 {
722 auto root = std::find_if( row.m_items.begin(), row.m_items.end(),
723 []( LIB_SYMBOL* aSymbol )
724 {
725 return aSymbol->IsRoot();
726 } );
727
728 if( root == row.m_items.end() )
729 {
730 // Do not reintroduce a root excluded by the filter or scope. Matching
731 // descendants remain individually editable instead of becoming an aggregate.
732 for( LIB_SYMBOL* symbol : row.m_items )
733 families.emplace_back( symbol, ROW_STATE::NON_EXPANDABLE );
734
735 continue;
736 }
737
738 // Root must go first so when we find it, put it at the beginning
739 std::iter_swap( row.m_items.begin(), root );
740
741 if( row.m_items.size() > 1 )
742 row.m_state = ROW_STATE::PARENT_COLLAPSED;
743
744 families.push_back( row );
745 }
746
747 m_rows.swap( families );
748 }
749
750 if( GetView() )
751 {
752 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, (int) m_rows.size() );
753 GetView()->ProcessTableMessage( msg );
754 }
755
756 wxLogTrace( traceLibFieldTable, "RebuildRows: Completed rebuild with %zu rows created", m_rows.size() );
757 Sort();
758}
759
760
762{
763 return lhItem == rhItem;
764}
765
766
768 const wxString& aFieldName )
769{
770 SCH_FIELD* field = aRef->GetField( aFieldName );
771
772 if( field )
773 {
774 if( field->IsPrivate() )
775 return wxEmptyString;
776 else
777 return field->GetShownText( nullptr, INTERNAL );
778 }
779
780 // Handle generated fields with variables as names (e.g. ${QUANTITY}) that are not present in
781 // the symbol by giving them the correct value by resolving against the symbol
782 if( IsGeneratedField( aFieldName ) )
783 {
784 int depth = 0;
785
786 std::function<bool( wxString* )> libSymbolResolver =
787 [&]( wxString* token ) -> bool
788 {
789 return aRef->ResolveTextVar( token, depth + 1 );
790 };
791
792 return ResolveTextVars( aFieldName, &libSymbolResolver, depth );
793 }
794
795 return wxEmptyString;
796}
797
798
799wxString LIB_FIELDS_EDITOR_GRID_DATA_MODEL::resolveTextVars( LIB_SYMBOL* const& aLibSymbol, const wxString& aText )
800{
801 int depth = 0;
802
803 std::function<bool( wxString* )> libSymbolResolver =
804 [&]( wxString* token ) -> bool
805 {
806 if( resolveStoredTextVar( aLibSymbol, token ) )
807 return true;
808
809 return aLibSymbol->ResolveTextVar( token, depth );
810 };
811
812 return ResolveTextVars( aText, &libSymbolResolver, depth );
813}
814
815
816void LIB_FIELDS_EDITOR_GRID_DATA_MODEL::ApplyData( std::function<void( LIB_SYMBOL* )> symbolChangeHandler,
817 std::function<void()> postApplyHandler )
818{
819 int powerCol = GetFieldNameCol( SYMBOL_IS_POWER );
820 int localPowerCol = GetFieldNameCol( SYMBOL_IS_LOCAL_POWER );
821 std::set<LIB_SYMBOL*> modifiedSymbols;
822
823 // Power state belongs to the root. Apply it before fields so derived symbols see
824 // the final inherited state regardless of their position in the table.
825 for( LIB_SYMBOL* symbol : m_symbolsList )
826 {
827 if( symbol->IsDerived() )
828 continue;
829
830 bool symbolIsPower = symbol->IsPower();
831 bool symbolIsLocalPower = symbol->IsLocalPower();
832 wxString value;
833
834 if( powerCol != -1 )
835 symbolIsPower = getStoredFieldValue( symbol, SYMBOL_IS_POWER, value ) && value == wxS( "1" );
836
837 if( localPowerCol != -1 )
838 {
839 symbolIsLocalPower = getStoredFieldValue( symbol, SYMBOL_IS_LOCAL_POWER, value )
840 && value == wxS( "1" );
841 }
842
843 // Local power is a subtype of power, so apply the two checkbox columns as one
844 // three-state property rather than allowing column order to determine the result.
845 if( powerCol != -1 && !symbolIsPower )
846 symbolIsLocalPower = false;
847 else if( symbolIsLocalPower )
848 symbolIsPower = true;
849
850 if( symbol->IsPower() != symbolIsPower || symbol->IsLocalPower() != symbolIsLocalPower )
851 {
852 if( symbolIsLocalPower )
853 symbol->SetLocalPower();
854 else if( symbolIsPower )
855 symbol->SetGlobalPower();
856 else
857 symbol->SetNormal();
858
859 modifiedSymbols.insert( symbol );
860 }
861 }
862
863 for( LIB_SYMBOL* symbol : m_symbolsList )
864 {
865 bool symbolModified = modifiedSymbols.contains( symbol );
866 const bool symbolIsPower = symbol->IsPower();
867
868 for( size_t i = 0; i < m_cols.size(); ++i )
869 {
870 const DATA_MODEL_COL& col = m_cols[i];
871 const wxString& srcName = col.m_fieldName;
872 wxString srcValue;
873 bool currentlyPresent = getStoredFieldValue( symbol, srcName, srcValue );
874
875 if( srcName == SYMBOL_NAME )
876 continue;
877
878 if( srcName == SYMBOL_IS_POWER || srcName == SYMBOL_IS_LOCAL_POWER )
879 continue;
880
881 // Attributes bypass the field logic, so handle them first
882 if( ColIsAttribute( static_cast<int>( i ) ) )
883 {
884 wxString newValue = currentlyPresent ? srcValue : wxString( wxS( "0" ) );
885
886 if( getAttributeValue( symbol, srcName ) != newValue )
887 {
888 setAttributeValue( symbol, srcName, newValue );
889 symbolModified = true;
890 }
891
892 continue;
893 }
894
895 if( srcName == SYMBOL_KEYWORDS )
896 {
897 if( symbol->GetRawKeyWords() != srcValue )
898 {
899 symbol->SetKeyWords( srcValue );
900 symbolModified = true;
901 }
902
903 continue;
904 }
905
906 // Skip special fields with variables as names (e.g. ${QUANTITY}),
907 // they can't be edited
908 if( IsGeneratedField( srcName ) )
909 continue;
910
911 SCH_FIELD* destField = symbol->GetField( srcName );
912
913 if( !currentlyPresent && destField )
914 {
915 if( destField->IsMandatory() )
916 {
917 // Value cannot be empty, but the other mandatory fields can be cleared.
918 if( destField->GetId() != FIELD_T::VALUE && !destField->GetText().IsEmpty() )
919 {
920 destField->SetText( wxEmptyString );
921 symbolModified = true;
922 }
923 }
924 else if( !destField->IsPrivate() )
925 {
926 symbol->RemoveField( destField );
927 symbolModified = true;
928 }
929
930 continue;
931 }
932
933 // Every present data-store entry represents a field that should exist,
934 // including entries with an explicitly empty value
935 bool createField = !destField && currentlyPresent;
936
937 if( createField )
938 {
939 const VECTOR2I symbolPos = symbol->GetPosition();
940 destField = new SCH_FIELD( symbol, FIELD_T::USER, srcName );
941 destField->SetPosition( symbolPos );
942 symbol->AddField( destField );
943 symbolModified = true;
944 }
945
946 if( !destField )
947 continue;
948
949 if( destField->GetId() == FIELD_T::VALUE )
950 {
951 // Value field cannot be empty
952 wxString newValue = symbolIsPower ? symbol->GetName() : srcValue;
953
954 if( !newValue.IsEmpty() && destField->GetText() != newValue )
955 {
956 symbol->GetField( FIELD_T::VALUE )->SetText( newValue );
957 symbolModified = true;
958 }
959 }
960 else if( destField->GetText() != srcValue )
961 {
962 destField->SetText( srcValue );
963 symbolModified = true;
964 }
965 }
966
967 std::vector<SCH_FIELD*> symbolFields;
968 symbol->GetFields( symbolFields );
969
970 // Remove any fields that are not mandatory
971 for( SCH_FIELD* field : symbolFields )
972 {
973 if( field->IsMandatory() || field->IsPrivate() )
974 continue;
975
976 const wxString& existingName = field->GetName();
977
978 // Entries for fields whose columns still exist were handled above. Anything left here
979 // has had its column removed from the table.
980 if( GetFieldNameCol( existingName ) == -1 )
981 {
982 symbol->RemoveField( field );
983 symbolModified = true;
984 }
985 }
986
987 if( symbolModified )
988 modifiedSymbols.insert( symbol );
989 }
990
991 // Apply all field edits before rebuilding metadata. Publish parents first so children
992 // also see edits made to a separate copy of their parent on the editing canvas.
993 std::vector<LIB_SYMBOL*> symbols = m_symbolsList;
994
995 std::stable_sort( symbols.begin(), symbols.end(),
996 []( const LIB_SYMBOL* aLeft, const LIB_SYMBOL* aRight )
997 {
998 return aLeft->GetInheritanceDepth() < aRight->GetInheritanceDepth();
999 } );
1000
1001 for( LIB_SYMBOL* symbol : symbols )
1002 {
1003 // Unedited children can inherit changed descriptions and keywords too.
1004 symbol->RefreshLibraryTreeCaches();
1005
1006 if( modifiedSymbols.contains( symbol ) )
1007 symbolChangeHandler( symbol );
1008
1009 acceptDataStoreItem( symbol );
1010 }
1011
1012 m_edited = false;
1013
1014 // Call post-apply handler if provided (for library operations and tree refresh)
1015 if( postApplyHandler )
1016 postApplyHandler();
1017}
1018
1019
1020bool LIB_FIELDS_EDITOR_GRID_DATA_MODEL::ValidateReferences( wxString& aSymbolName, wxString& aErrorMessage ) const
1021{
1022 const wxString referenceFieldName = GetDefaultFieldName( FIELD_T::REFERENCE, UNTRANSLATED );
1023
1024 for( LIB_SYMBOL* symbol : m_symbolsList )
1025 {
1026 wxString reference;
1027 getStoredFieldValue( symbol, referenceFieldName, reference );
1028
1029 // An empty reference on a derived symbol inherits the parent's reference.
1030 if( symbol->IsDerived() && reference.IsEmpty() )
1031 continue;
1032
1033 aErrorMessage = GetFieldValidationErrorMessage( FIELD_T::REFERENCE, reference );
1034
1035 if( !aErrorMessage.IsEmpty() )
1036 {
1037 aSymbolName = UnescapeString( symbol->GetName() );
1038 return false;
1039 }
1040 }
1041
1042 return true;
1043}
1044
1045
1047{
1048 if( ColIsAttribute( col ) )
1049 return wxGRID_VALUE_BOOL;
1050
1051 return wxGridTableBase::GetTypeName( row, col );
1052}
1053
1054
1056{
1057 KIID_PATH key;
1058 key.push_back( aItem->m_Uuid );
1059 return key;
1060}
1061
1062
1064{
1065 return aItem->GetName();
1066}
const KIID m_Uuid
Definition eda_item.h:599
virtual bool fieldIsItemProperty(const wxString &aFieldName) const
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)
virtual wxString getAttributeResolvedValue(const wxString &aFieldName, bool aValue) const
int GetFieldNameCol(const wxString &aFieldName) const
bool groupMatch(const LIB_SYMBOL *&lhItem, const LIB_SYMBOL *&rhItem)
void setStoredFieldValue(const LIB_SYMBOL *&aItem, const wxString &aFieldName, const wxString &aValue)
bool fieldIsModified(const LIB_SYMBOL *&aItem, const wxString &aFieldName)
void initializeDataStoreItem(const LIB_SYMBOL *&aItem)
bool getStoredFieldValue(const LIB_SYMBOL *&aItem, const wxString &aFieldName, wxString &aValue) const
std::vector< DATA_MODEL_ROW< LIB_SYMBOL * > > m_rows
bool resolveStoredTextVar(const LIB_SYMBOL *&aItem, wxString *aToken, bool aCaseSensitive=false) const
bool IsCellReadOnly(int aRow, int aCol) override
bool MatchesFilter(const LIB_SYMBOL *&aItem, const wxString &aReference, EDA_COMBINED_MATCHER &aMatcher)
void acceptDataStoreItem(const LIB_SYMBOL *&aItem)
Definition kiid.h:46
bool fieldIsItemProperty(const wxString &aFieldName) const override
void SetValue(int aRow, int aCol, const wxString &aValue) override
wxString getAttributeResolvedValue(const wxString &aFieldName, bool aValue) const override
wxString getAttributeValue(const LIB_SYMBOL *, const wxString &aAttributeName)
std::vector< std::pair< LIB_SYMBOL *, wxString > > m_createdDerivedSymbols
std::vector< LIB_SYMBOL * > getAllItems() const override
bool IsCellReadOnly(int aRow, int aCol) override
KIID_PATH getDataStoreKey(LIB_SYMBOL *const &aItem) const override
wxGridCellAttr * GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind) override
bool ColIsItemIdentifier(int aCol) const override
Reference for symbol/fields tables, lib_id for lib tables.
void CreateDerivedSymbolImmediate(int aRow, int aCol, wxString &aNewSymbolName)
bool getStoredPowerSymbolValue(LIB_SYMBOL *aSymbol) const
wxString getFieldResolvedLiveValue(LIB_SYMBOL *const &aSymbol, const wxString &aFieldName) override
Explicitly bypasses the data store's field values and retries them from the item's current field valu...
void createActualDerivedSymbol(const LIB_SYMBOL *aParentSymbol, const wxString &aNewSymbolName, const KIID &aNewSymbolUuid)
bool fieldIsAttribute(const wxString &aFieldName) const override
void ApplyData(std::function< void(LIB_SYMBOL *)> symbolChangeHandler, std::function< void()> postApplyHandler=nullptr)
std::vector< LIB_SYMBOL * > m_symbolsList
void setAttributeValue(LIB_SYMBOL *aSymbol, const wxString &aAttributeName, const wxString &aValue)
bool unitMatch(LIB_SYMBOL *const &lhItem, LIB_SYMBOL *const &rhItem) override
bool getLiveFieldValue(LIB_SYMBOL *const &aSymbol, const wxString &aFieldName, wxString &aValue) override
Gets the current value of the field from the live item, e.g.
wxString getItemIdentifier(LIB_SYMBOL *const &aItem) const override
wxString GetTypeName(int row, int col) override
bool isPowerSymbolControlledField(const wxString &aFieldName) const
wxString resolveTextVars(LIB_SYMBOL *const &aSymbol, const wxString &aText) override
void setPowerSymbolDefaults(LIB_SYMBOL *aSymbol)
void getEffectiveFieldValue(LIB_SYMBOL *const &aSymbol, const wxString &aFieldName, wxString &aValue) const override
bool ValidateReferences(wxString &aSymbolName, wxString &aErrorMessage) const
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
Define a library symbol object.
Definition lib_symbol.h:114
const wxString & GetRawKeyWords() const
Return only this symbol's keywords, without inheriting from its parent.
Definition lib_symbol.h:218
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:190
std::weak_ptr< LIB_SYMBOL > & GetParent()
Definition lib_symbol.h:152
bool IsPower() const override
SCH_FIELD * GetField(const wxString &aFieldName)
Find a field within this symbol matching aFieldName; return nullptr if not found.
void SetParent(LIB_SYMBOL *aParent=nullptr)
wxString GetName() const override
Definition lib_symbol.h:183
SCH_FIELD & GetValueField()
Definition lib_symbol.h:447
bool IsLocalPower() const override
bool ResolveTextVar(wxString *aToken, int aDepth=0) const
Resolve any references to system tokens supported by the symbol.
virtual void SetName(const wxString &aName)
bool IsMandatory() const
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 SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
bool IsPrivate() const
Definition sch_item.h:259
bool GetExcludedFromPosFiles(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition symbol.h:231
void SetExcludedFromBoard(bool aExclude, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear exclude from board netlist flag.
Definition symbol.h:210
virtual bool GetDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Set or clear the 'Do Not Populate' flag.
Definition symbol.h:240
virtual void SetExcludedFromSim(bool aExcludeFromSim, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear the exclude from simulation flag.
Definition symbol.h:180
virtual bool GetExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition symbol.h:201
virtual void SetDNP(bool aDNP, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Definition symbol.h:242
bool GetExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition symbol.h:216
void SetExcludedFromPosFiles(bool aExclude, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear exclude from position files flag.
Definition symbol.h:225
virtual bool GetExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Definition symbol.h:186
virtual void SetExcludedFromBOM(bool aExcludeFromBOM, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear the exclude from schematic bill of materials flag.
Definition symbol.h:195
std::map< int, wxGridCellAttr * > m_colAttrs
Definition wx_grid.h:133
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
const wxChar *const traceLibFieldTable
DATA_MODEL_ROW< LIB_SYMBOL * > LIB_FIELDS_TABLE_DATA_MODEL_ROW
static const std::map< wxString, FAMILY > & families()
Keyed by the names Family() returns. Every family there must appear here.
const wxColour STRIPED_CLEARED_EMPTY_FIELD_LIGHT_GREEN(180, 220, 180)
const wxColour STRIPED_EDITED_EMPTY_FIELD_MUTED_GREEN(180, 200, 180)
const wxColour EDITED_EMPTY_FIELD_BRIGHT_GREEN(192, 255, 192)
const wxColour STRIPED_EDITED_NONEMPTY_FIELD_MUTED_RED(200, 180, 180)
const wxColour STRIPED_CLEARED_NONEMPTY_FIELD_LIGHT_RED(220, 180, 180)
wxString UnescapeString(const wxString &aSource)
The point of the data model classes is fundamentally to represent three things:
std::span< const ITEM_TYPE > GetCellItems() const
Items displayed and edited by this row, excluding descendants of a real parent.
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".
@ VALUE
Field Value of part, i.e. "3.3K".
@ UNTRANSLATED
wxLogTrace helper definitions.
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition ui_common.h:46
wxString GetFieldValidationErrorMessage(FIELD_T aFieldId, const wxString &aValue)
Return the error message if aValue is invalid for aFieldId.
Custom text control validator definitions.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708