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}
572
573
575{
576 return aFieldName == GetDefaultFieldName( FIELD_T::VALUE, UNTRANSLATED )
577 || aFieldName == wxS( "${EXCLUDE_FROM_BOARD}" )
578 || aFieldName == wxS( "${EXCLUDE_FROM_BOM}" )
579 || aFieldName == wxS( "${EXCLUDE_FROM_POS_FILES}" )
580 || aFieldName == wxS( "${EXCLUDE_FROM_SIM}" );
581}
582
583
585{
586 wxString value;
587
588 if( getStoredFieldValue( aSymbol, SYMBOL_IS_POWER, value ) )
589 return value == wxS( "1" );
590
591 return GetFieldNameCol( SYMBOL_IS_POWER ) == -1 && aSymbol->IsPower();
592}
593
594
596{
598
599 static const std::vector<wxString> exclusionFields = {
600 wxS( "${EXCLUDE_FROM_BOARD}" ),
601 wxS( "${EXCLUDE_FROM_BOM}" ),
602 wxS( "${EXCLUDE_FROM_POS_FILES}" ),
603 wxS( "${EXCLUDE_FROM_SIM}" ),
604 };
605
606 for( const wxString& fieldName : exclusionFields )
607 {
608 if( GetFieldNameCol( fieldName ) != -1 )
609 setStoredFieldValue( aSymbol, fieldName, wxS( "1" ) );
610 }
611}
612
613
614wxString LIB_FIELDS_EDITOR_GRID_DATA_MODEL::getAttributeResolvedValue( const wxString& aFieldName, bool aValue ) const
615{
616 if( !aValue )
617 return wxEmptyString;
618
619 if( aFieldName == SYMBOL_IS_POWER )
620 return wxS( "Power Symbol" );
621 else if( aFieldName == SYMBOL_IS_LOCAL_POWER )
622 return wxS( "Local Power Symbol" );
623
625}
626
627
629{
630 if( !m_rebuildsEnabled )
631 return;
632
633 wxLogTrace( traceLibFieldTable, "RebuildRows: Starting rebuild with %zu symbols in list", m_symbolsList.size() );
634
635 if( GetView() )
636 {
637 // Commit any pending in-place edits before the row gets moved out from under
638 // the editor.
639 static_cast<WX_GRID*>( GetView() )->CommitPendingChanges( true );
640
641 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, (int) m_rows.size() );
642 GetView()->ProcessTableMessage( msg );
643 }
644
645 m_rows.clear();
646
647 wxLogTrace( traceLibFieldTable, "RebuildRows: About to process %zu symbols", m_symbolsList.size() );
648
649 EDA_COMBINED_MATCHER matcher( m_filter.Lower(), CTX_SEARCH );
650 int parentCol = GetFieldNameCol( SYMBOL_PARENT );
651 bool groupByParent = parentCol >= 0 && GetGroupColumn( parentCol );
652 int rootCol = GetFieldNameCol( SYMBOL_ROOT );
653 bool groupByRootOnly = m_groupingEnabled && rootCol >= 0 && GetGroupColumn( rootCol );
654
655 for( const DATA_MODEL_COL& col : m_cols )
656 {
657 if( col.m_group && col.m_fieldName != SYMBOL_ROOT )
658 groupByRootOnly = false;
659 }
660
661 for( LIB_SYMBOL* symbol : m_symbolsList )
662 {
663 wxLogTrace( traceLibFieldTable, "RebuildRows: Processing symbol '%s' (UUID: %s)",
664 symbol->GetName(), symbol->m_Uuid.AsString() );
665
666 if( m_scope == SCOPE::SCOPE_RELATED_SYMBOLS )
667 {
668 std::shared_ptr<LIB_SYMBOL> root = symbol->GetRootSymbol();
669
670 if( !root || root->GetName() != m_relatedSymbolRoot )
671 continue;
672 }
673
674 if( !MatchesFilter( symbol, symbol->GetName(), matcher ) )
675 continue;
676
677 if( m_excludeDNP && symbol->GetDNP() )
678 continue;
679
680 if( !m_includeExcluded && symbol->GetExcludedFromBOM() )
681 continue;
682
683 bool matchFound = false;
684
685 // Keep unrelated roots separate instead of grouping their empty parent values together.
686 // Also skip the N^2 grouping loop entirely when grouping is disabled.
687 if( !m_groupingEnabled || ( groupByParent && symbol->IsRoot() ) )
688 {
689 m_rows.emplace_back( LIB_FIELDS_TABLE_DATA_MODEL_ROW( symbol, ROW_STATE::NON_EXPANDABLE ) );
690 continue;
691 }
692
693 // See if we already have a row which this symbol fits into
695 {
696 // all group members must have identical refs so just use the first one
697 LIB_SYMBOL* rowSymbol = row.m_items[0];
698
699 if( groupMatch( symbol, rowSymbol ) )
700 {
701 matchFound = true;
702 row.m_items.push_back( symbol );
703 row.m_state = ROW_STATE::GROUP_COLLAPSED;
704 break;
705 }
706 }
707
708 if( !matchFound )
709 m_rows.emplace_back( LIB_FIELDS_TABLE_DATA_MODEL_ROW( symbol, ROW_STATE::NON_EXPANDABLE ) );
710 }
711
712 if( groupByRootOnly )
713 {
714 // We're going to break m_rows into potentially more rows, which we collect
715 // into this, then we'll swap it all out when we're done.
716 std::vector<LIB_FIELDS_TABLE_DATA_MODEL_ROW> families;
717
719 {
720 auto root = std::find_if( row.m_items.begin(), row.m_items.end(),
721 []( LIB_SYMBOL* aSymbol )
722 {
723 return aSymbol->IsRoot();
724 } );
725
726 if( root == row.m_items.end() )
727 {
728 // Do not reintroduce a root excluded by the filter or scope. Matching
729 // descendants remain individually editable instead of becoming an aggregate.
730 for( LIB_SYMBOL* symbol : row.m_items )
731 families.emplace_back( symbol, ROW_STATE::NON_EXPANDABLE );
732
733 continue;
734 }
735
736 // Root must go first so when we find it, put it at the beginning
737 std::iter_swap( row.m_items.begin(), root );
738
739 if( row.m_items.size() > 1 )
740 row.m_state = ROW_STATE::PARENT_COLLAPSED;
741
742 families.push_back( row );
743 }
744
745 m_rows.swap( families );
746 }
747
748 if( GetView() )
749 {
750 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, (int) m_rows.size() );
751 GetView()->ProcessTableMessage( msg );
752 }
753
754 wxLogTrace( traceLibFieldTable, "RebuildRows: Completed rebuild with %zu rows created", m_rows.size() );
755 Sort();
756}
757
758
760{
761 return lhItem == rhItem;
762}
763
764
766 const wxString& aFieldName )
767{
768 SCH_FIELD* field = aRef->GetField( aFieldName );
769
770 if( field )
771 {
772 if( field->IsPrivate() )
773 return wxEmptyString;
774 else
775 return field->GetShownText( nullptr, INTERNAL );
776 }
777
778 // Handle generated fields with variables as names (e.g. ${QUANTITY}) that are not present in
779 // the symbol by giving them the correct value by resolving against the symbol
780 if( IsGeneratedField( aFieldName ) )
781 {
782 int depth = 0;
783
784 std::function<bool( wxString* )> libSymbolResolver =
785 [&]( wxString* token ) -> bool
786 {
787 return aRef->ResolveTextVar( token, depth + 1 );
788 };
789
790 return ResolveTextVars( aFieldName, &libSymbolResolver, depth );
791 }
792
793 return wxEmptyString;
794}
795
796
797wxString LIB_FIELDS_EDITOR_GRID_DATA_MODEL::resolveTextVars( LIB_SYMBOL* const& aLibSymbol, const wxString& aText )
798{
799 int depth = 0;
800
801 std::function<bool( wxString* )> libSymbolResolver =
802 [&]( wxString* token ) -> bool
803 {
804 if( resolveStoredTextVar( aLibSymbol, token ) )
805 return true;
806
807 return aLibSymbol->ResolveTextVar( token, depth );
808 };
809
810 return ResolveTextVars( aText, &libSymbolResolver, depth );
811}
812
813
814void LIB_FIELDS_EDITOR_GRID_DATA_MODEL::ApplyData( std::function<void( LIB_SYMBOL* )> symbolChangeHandler,
815 std::function<void()> postApplyHandler )
816{
817 int powerCol = GetFieldNameCol( SYMBOL_IS_POWER );
818 int localPowerCol = GetFieldNameCol( SYMBOL_IS_LOCAL_POWER );
819 std::set<LIB_SYMBOL*> modifiedSymbols;
820
821 // Power state belongs to the root. Apply it before fields so derived symbols see
822 // the final inherited state regardless of their position in the table.
823 for( LIB_SYMBOL* symbol : m_symbolsList )
824 {
825 if( symbol->IsDerived() )
826 continue;
827
828 bool symbolIsPower = symbol->IsPower();
829 bool symbolIsLocalPower = symbol->IsLocalPower();
830 wxString value;
831
832 if( powerCol != -1 )
833 symbolIsPower = getStoredFieldValue( symbol, SYMBOL_IS_POWER, value ) && value == wxS( "1" );
834
835 if( localPowerCol != -1 )
836 {
837 symbolIsLocalPower = getStoredFieldValue( symbol, SYMBOL_IS_LOCAL_POWER, value )
838 && value == wxS( "1" );
839 }
840
841 // Local power is a subtype of power, so apply the two checkbox columns as one
842 // three-state property rather than allowing column order to determine the result.
843 if( powerCol != -1 && !symbolIsPower )
844 symbolIsLocalPower = false;
845 else if( symbolIsLocalPower )
846 symbolIsPower = true;
847
848 if( symbol->IsPower() != symbolIsPower || symbol->IsLocalPower() != symbolIsLocalPower )
849 {
850 if( symbolIsLocalPower )
851 symbol->SetLocalPower();
852 else if( symbolIsPower )
853 symbol->SetGlobalPower();
854 else
855 symbol->SetNormal();
856
857 modifiedSymbols.insert( symbol );
858 }
859 }
860
861 for( LIB_SYMBOL* symbol : m_symbolsList )
862 {
863 bool symbolModified = modifiedSymbols.contains( symbol );
864 const bool symbolIsPower = symbol->IsPower();
865
866 for( size_t i = 0; i < m_cols.size(); ++i )
867 {
868 const DATA_MODEL_COL& col = m_cols[i];
869 const wxString& srcName = col.m_fieldName;
870 wxString srcValue;
871 bool currentlyPresent = getStoredFieldValue( symbol, srcName, srcValue );
872
873 if( srcName == SYMBOL_NAME )
874 continue;
875
876 if( srcName == SYMBOL_IS_POWER || srcName == SYMBOL_IS_LOCAL_POWER )
877 continue;
878
879 // Attributes bypass the field logic, so handle them first
880 if( ColIsAttribute( static_cast<int>( i ) ) )
881 {
882 wxString newValue = currentlyPresent ? srcValue : wxString( wxS( "0" ) );
883
884 if( getAttributeValue( symbol, srcName ) != newValue )
885 {
886 setAttributeValue( symbol, srcName, newValue );
887 symbolModified = true;
888 }
889
890 continue;
891 }
892
893 if( srcName == SYMBOL_KEYWORDS )
894 {
895 if( symbol->GetRawKeyWords() != srcValue )
896 {
897 symbol->SetKeyWords( srcValue );
898 symbolModified = true;
899 }
900
901 continue;
902 }
903
904 // Skip special fields with variables as names (e.g. ${QUANTITY}),
905 // they can't be edited
906 if( IsGeneratedField( srcName ) )
907 continue;
908
909 SCH_FIELD* destField = symbol->GetField( srcName );
910
911 if( !currentlyPresent && destField )
912 {
913 if( destField->IsMandatory() )
914 {
915 // Value cannot be empty, but the other mandatory fields can be cleared.
916 if( destField->GetId() != FIELD_T::VALUE && !destField->GetText().IsEmpty() )
917 {
918 destField->SetText( wxEmptyString );
919 symbolModified = true;
920 }
921 }
922 else if( !destField->IsPrivate() )
923 {
924 symbol->RemoveField( destField );
925 symbolModified = true;
926 }
927
928 continue;
929 }
930
931 // Every present data-store entry represents a field that should exist,
932 // including entries with an explicitly empty value
933 bool createField = !destField && currentlyPresent;
934
935 if( createField )
936 {
937 const VECTOR2I symbolPos = symbol->GetPosition();
938 destField = new SCH_FIELD( symbol, FIELD_T::USER, srcName );
939 destField->SetPosition( symbolPos );
940 symbol->AddField( destField );
941 symbolModified = true;
942 }
943
944 if( !destField )
945 continue;
946
947 if( destField->GetId() == FIELD_T::VALUE )
948 {
949 // Value field cannot be empty
950 wxString newValue = symbolIsPower ? symbol->GetName() : srcValue;
951
952 if( !newValue.IsEmpty() && destField->GetText() != newValue )
953 {
954 symbol->GetField( FIELD_T::VALUE )->SetText( newValue );
955 symbolModified = true;
956 }
957 }
958 else if( destField->GetText() != srcValue )
959 {
960 destField->SetText( srcValue );
961 symbolModified = true;
962 }
963 }
964
965 std::vector<SCH_FIELD*> symbolFields;
966 symbol->GetFields( symbolFields );
967
968 // Remove any fields that are not mandatory
969 for( SCH_FIELD* field : symbolFields )
970 {
971 if( field->IsMandatory() || field->IsPrivate() )
972 continue;
973
974 const wxString& existingName = field->GetName();
975
976 // Entries for fields whose columns still exist were handled above. Anything left here
977 // has had its column removed from the table.
978 if( GetFieldNameCol( existingName ) == -1 )
979 {
980 symbol->RemoveField( field );
981 symbolModified = true;
982 }
983 }
984
985 if( symbolModified )
986 modifiedSymbols.insert( symbol );
987 }
988
989 // Apply all field edits before rebuilding metadata. Publish parents first so children
990 // also see edits made to a separate copy of their parent on the editing canvas.
991 std::vector<LIB_SYMBOL*> symbols = m_symbolsList;
992
993 std::stable_sort( symbols.begin(), symbols.end(),
994 []( const LIB_SYMBOL* aLeft, const LIB_SYMBOL* aRight )
995 {
996 return aLeft->GetInheritanceDepth() < aRight->GetInheritanceDepth();
997 } );
998
999 for( LIB_SYMBOL* symbol : symbols )
1000 {
1001 // Unedited children can inherit changed descriptions and keywords too.
1002 symbol->RefreshLibraryTreeCaches();
1003
1004 if( modifiedSymbols.contains( symbol ) )
1005 symbolChangeHandler( symbol );
1006
1007 acceptDataStoreItem( symbol );
1008 }
1009
1010 m_edited = false;
1011
1012 // Call post-apply handler if provided (for library operations and tree refresh)
1013 if( postApplyHandler )
1014 postApplyHandler();
1015}
1016
1017
1018bool LIB_FIELDS_EDITOR_GRID_DATA_MODEL::ValidateReferences( wxString& aSymbolName, wxString& aErrorMessage ) const
1019{
1020 const wxString referenceFieldName = GetDefaultFieldName( FIELD_T::REFERENCE, UNTRANSLATED );
1021
1022 for( LIB_SYMBOL* symbol : m_symbolsList )
1023 {
1024 wxString reference;
1025 getStoredFieldValue( symbol, referenceFieldName, reference );
1026
1027 // An empty reference on a derived symbol inherits the parent's reference.
1028 if( symbol->IsDerived() && reference.IsEmpty() )
1029 continue;
1030
1031 aErrorMessage = GetFieldValidationErrorMessage( FIELD_T::REFERENCE, reference );
1032
1033 if( !aErrorMessage.IsEmpty() )
1034 {
1035 aSymbolName = UnescapeString( symbol->GetName() );
1036 return false;
1037 }
1038 }
1039
1040 return true;
1041}
1042
1043
1045{
1046 if( ColIsAttribute( col ) )
1047 return wxGRID_VALUE_BOOL;
1048
1049 return wxGridTableBase::GetTypeName( row, col );
1050}
1051
1052
1054{
1055 KIID_PATH key;
1056 key.push_back( aItem->m_Uuid );
1057 return key;
1058}
1059
1060
1062{
1063 return aItem->GetName();
1064}
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:96
@ 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:47
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