KiCad PCB EDA Suite
Loading...
Searching...
No Matches
fields_table_data_model.h
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#pragma once
21
22#include <set>
23#include <unordered_set>
24#include <vector>
25#include <algorithm>
26#include <map>
27#include <span>
28
29#include <widgets/wx_grid.h>
30#include <widgets/ui_common.h>
31
32#include <common.h>
33#include <eda_pattern_match.h>
34#include <kiid.h>
35#include <refdes_utils.h>
37#include <string_utils.h>
38#include <wx/debug.h>
39
40
42{
43inline const wxColour VARIANT_SYMBOL_OVERRIDE_DARK_BLUE( 40, 60, 80 );
44inline const wxColour VARIANT_SYMBOL_OVERRIDE_LIGHT_BLUE( 220, 235, 255 );
45inline const wxColour VARIANT_FIELD_OVERRIDE_DARK_YELLOW( 80, 80, 40 );
46inline const wxColour VARIANT_FIELD_OVERRIDE_LIGHT_YELLOW( 255, 255, 200 );
47inline const wxColour TEXT_VARIABLE_DARK_AMBER( 80, 70, 30 );
48inline const wxColour TEXT_VARIABLE_LIGHT_YELLOW( 255, 252, 200 );
49inline const wxColour STRIPED_CLEARED_EMPTY_FIELD_LIGHT_GREEN( 180, 220, 180 );
50inline const wxColour STRIPED_CLEARED_NONEMPTY_FIELD_LIGHT_RED( 220, 180, 180 );
51inline const wxColour STRIPED_EDITED_EMPTY_FIELD_MUTED_GREEN( 180, 200, 180 );
52inline const wxColour STRIPED_EDITED_NONEMPTY_FIELD_MUTED_RED( 200, 180, 180 );
53inline const wxColour EDITED_EMPTY_FIELD_BRIGHT_GREEN( 192, 255, 192 );
54inline const wxColour CLEARED_FIELD_STRIPE_ON_DARK_LIGHT_RED( 220, 180, 180 );
55inline const wxColour CLEARED_FIELD_STRIPE_ON_LIGHT_DARK_RED( 100, 10, 10 );
56} // namespace FIELDS_TABLE_COLOR
57
58
75
91
93{
94 wxString m_fieldName;
95 wxString m_label;
97 bool m_show;
98 bool m_group;
99};
100
101
103{
104 wxString m_value;
105 wxString m_baseline;
106};
107
108
110{
111 // Field existence is shared by every variant; an empty value is still a present field.
112 bool m_present = false;
113 bool m_baselinePresent = false;
114 std::map<wxString, FIELD_VALUE_STATE> m_variants;
115};
116
117
118template <typename ITEM_TYPE>
120{
121 DATA_MODEL_ROW( const ITEM_TYPE& aFirstItem, ROW_STATE aGroupingState )
122 {
123 m_itemNumber = 0;
124 m_items.push_back( aFirstItem );
125 m_state = aGroupingState;
126 }
127
129 std::span<const ITEM_TYPE> GetCellItems() const
130 {
131 std::span<const ITEM_TYPE> items( m_items );
132 return IsParentRow( m_state ) ? items.first( 1 ) : items;
133 }
134
137 // All group members; real-parent states require the parent to remain first.
138 std::vector<ITEM_TYPE> m_items;
139};
140
141
147{
148public:
151
152 static const wxString QUANTITY_VARIABLE;
153 static const wxString ITEM_NUMBER_VARIABLE;
154
155 bool IsEdited() { return m_edited; }
156
157 virtual void AddColumn( const wxString& aFieldName, const wxString& aLabel, bool aAddedByUser ) = 0;
158
159 void MoveColumn( int aCol, int aNewPos );
160 void RemoveColumn( int aCol );
161 void RenameColumn( int aCol, const wxString& newName );
162
163 int GetNumberCols() override { return static_cast<int>( m_cols.size() ); }
164
165 void SetColLabelValue( int aCol, const wxString& aLabel ) override;
166 wxString GetColLabelValue( int aCol ) override;
167 wxString GetColFieldName( int aCol );
168 int GetColDataWidth( int aCol );
169 int GetFieldNameCol( const wxString& aFieldName ) const;
170
171 std::vector<BOM_FIELD> GetFieldsOrdered();
172 void SetFieldsOrder( const std::vector<wxString>& aNewOrder );
173
174 bool IsEmptyCell( int aRow, int aCol ) override
175 {
176 return false; // don't allow adjacent cell overflow, even if we are actually empty
177 }
178
179 bool ColIsReference( int aCol ) const;
180 bool ColIsQuantity( int aCol ) const;
181 bool ColIsItemNumber( int aCol ) const;
182 bool ColIsValue( int aCol ) const;
183 bool ColIsFootprint( int aCol ) const;
184 // Whether a column is something like DNP, e.g. one of the checkboxes in the Attributes section
185 // of symbols/footprints
186 bool ColIsAttribute( int aCol ) const;
187 // Whether a column is an attribute, or a special property like ${SYMBOL_NAME}
188 bool ColIsItemProperty( int aCol ) const;
189 // Whether a column is magic variable that is calculated or resolved, e.g. ${QUANTITY}, ${ITEM_NUMBER},
190 // but not something like ${SYMBOL_NAME}.
191 // The important part is that the column’s value is derived at read time rather than staged as stored data
192 bool ColIsComputed( int aCol ) const;
193
198 virtual bool ColIsItemIdentifier( int aCol ) const { return ColIsReference( aCol ); }
199
200 virtual bool ColIsReadOnly( int aCol ) const;
201 bool HasRowLabelExpanders() const override { return true; }
202 virtual bool IsCellReadOnly( int aRow, int aCol );
203
204 void SetSorting( int aCol, bool aAscending );
205 int GetSortCol() { return m_sortColumn; }
206 bool GetSortAsc() { return m_sortAscending; }
207
208 // These are used to disable RebuildRows() while generating batches of UI events, e.g.
209 // applying a BOM preset, that would otherwise thrash the grid.
210 void EnableRebuilds();
211 void DisableRebuilds();
212 virtual void RebuildRows() = 0;
213
214 void SetFilter( const wxString& aFilter ) { m_filter = aFilter; }
215 const wxString& GetFilter() { return m_filter; }
216 void SetFilterScope( BOM_FILTER_SCOPE aScope ) { m_filterScope = aScope; }
218
219 void SetSelectionItems( const std::unordered_set<KIID_PATH>& aItems ) { m_selectionItems = aItems; }
221
222 void SetGroupingEnabled( bool aGroup ) { m_groupingEnabled = aGroup; }
224
225 /* These contradictorily named functions force including items that have the Exclude from
226 * BOM flag set. This is needed so they can be viewed in a fields table while still being
227 * excluded from BOM export.
228 */
229 void SetIncludeExcludedFromBOM( bool aInclude ) { m_includeExcluded = aInclude; }
231
232 void SetExcludeDNP( bool aExclude ) { m_excludeDNP = aExclude; }
233 bool GetExcludeDNP() { return m_excludeDNP; }
234
235 void SetGroupColumn( int aCol, bool aGroup );
236 bool GetGroupColumn( int aCol );
237
238 void SetShowColumn( int aCol, bool aShow );
239 bool GetShowColumn( int aCol );
240
241 void ApplyBomPreset( const BOM_PRESET& aPreset );
242 BOM_PRESET GetBomSettings();
243 wxString Export( const BOM_FMT_PRESET& aSettings );
244
245 virtual wxString GetResolvedValue( int aRow, int aCol ) = 0;
246 virtual wxString GetExportValue( int aRow, int aCol, const wxString& aRefDelimiter,
247 const wxString& aRefRangeDelimiter ) = 0;
248
249 virtual void ClearCell( int aRow, int aCol ) = 0;
250 bool CanClearCell( int aRow, int aCol );
251 bool CanCopyCellFromVariant( int aRow, int aCol );
253 virtual void CopyCellFromVariant( int aRow, int aCol, const wxString& aVariantName ) = 0;
254 virtual bool IsCellClear( int aRow, int aCol ) = 0;
255 virtual bool IsCellEdited( int aRow, int aCol ) = 0;
257 bool IsRowEdited( int aRow );
258 virtual void RevertRow( int aRow ) = 0;
259
261 virtual std::vector<KIID_PATH> GetRowItemKeys( int aRow ) const = 0;
262
271 virtual void SetCurrentVariant( const wxString& aVariantName ) = 0;
272 const wxString& GetCurrentVariant() const { return m_currentVariant; }
273
274 void RenameStoredVariant( const wxString& aOldName, const wxString& aNewName );
275 void DeleteStoredVariant( const wxString& aName );
276
277 void SetVariantNames( const std::vector<wxString>& aVariantNames ) { m_variantNames = aVariantNames; }
278 const std::vector<wxString>& GetVariantNames() const { return m_variantNames; }
279
280 // Identity-based undo serialization (keyed by symbol, not row position) for the dialog's
281 // Ctrl+Z, so it stays correct as rows are grouped/sorted/reordered.
282 bool HasUndoStateSerialization() const override { return true; }
283 wxString SerializeUndoState() const override;
284 void RestoreUndoState( const wxString& aState ) override;
285
286
287 // Stuff this class knows nothing about, but needs to guarantee that the templated class implements
288 virtual void ExpandCollapseRow( int aRow ) = 0;
289
290protected:
291 // One of the special field names like ${DNP} that refer to the Attributes in symbols/footprints
292 virtual bool fieldIsAttribute( const wxString& aFieldName ) const;
293 // One of the special field names like ${SYMBOL_KEYWORDS} that refer to sometimes-editable
294 // properties in the symbol/footprint itself. Especially used for lib symbols/footprints,
295 // that have properties that overlap with mandatory fields, like lib footprints having
296 // a library description property as well as a mandatory description field
297 virtual bool fieldIsItemProperty( const wxString& aFieldName ) const;
298
299 virtual bool fieldSupportsVariants( const wxString& aFieldName ) const { return false; }
300 wxString fieldVariant( const wxString& aFieldName, const wxString& aVariantName ) const;
301 void updateEditedState();
302
303 // Helper function to translate named attribute values like ${DNP}.
304 virtual wxString getAttributeResolvedValue( const wxString& aFieldName, bool aValue ) const;
305
306 bool cellUsesResolvedTextRenderer( int aRow, int aCol );
307 void applyResolvedTextRenderer( wxGridCellAttr* aAttr, bool aApplyTint );
308
309 bool cellUsesUrlEditor( int aRow, int aCol );
310 wxGridCellAttr* cloneUrlEditorAttr();
311
312 wxGridCellAttr* applyCellDecorations( wxGridCellAttr* aAttr, int aRow, int aCol );
313 wxGridCellAttr* applyFieldPresenceRenderer( wxGridCellAttr* aAttr, int aRow, int aCol );
314
315 // Column mutations invalidate the position-based coordinates held by an open wxGrid editor.
317
318protected:
319 wxGridCellRenderer* m_stripedRenderer;
320 wxGridCellRenderer* m_resolvedTextRenderer;
321
325 wxString m_filter;
331
333 std::unordered_set<KIID_PATH> m_selectionItems;
334
336 std::vector<wxString> m_variantNames;
337
338
339 std::vector<DATA_MODEL_COL> m_cols;
340
341 // Data store
342 //
343 // This is the storage of the dialog's currently edited field values for each item in the table.
344 // Having a store separate from the live values in the symbols/footprints objects
345 // allows us to keep our edits separate from the actual sch/pcb until the user explicitly
346 // applies them.
347 //
348 // Items (symbols/footprints) are both identified by KIID_PATH, which for a symbol
349 // is the sheet path + symbol UUID (symbols have multiple instances),
350 // and for a footprint is the footprint UUID.
351 //
352 // m_rows and m_cols are just a generated view based on the data store,
353 // and are rebuilt as the user changes grouping, sorting, filtering, etc.
354 //
355 // NOTE: be very careful about how you "read" this data store, you should
356 // use getStoredFieldValue() to read values from the data store.
357 //
358 // Each field tracks shared presence and staged/baseline values for the visited variants.
359 //
360 // Use the get/set/clear/update/initialize functions to access the data store,
361 // rather than accessing it directly, so that live baselines and field presence
362 // are initialized consistently.
363 std::map<KIID_PATH, std::map<wxString, FIELD_STORE_VALUE>> m_dataStore;
364};
365
366
372class GRID_CELL_RESOLVED_TEXT_RENDERER : public wxGridCellStringRenderer
373{
374public:
376
377 void Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC, const wxRect& aRect, int aRow, int aCol,
378 bool isSelected ) override;
379
380 wxSize GetBestSize( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC, int aRow, int aCol ) override;
381
382 wxGridCellRenderer* Clone() const override;
383};
384
385
391template <typename ITEM_TYPE>
393{
394public:
395 void SetCurrentVariant( const wxString& aVariantName ) override
396 {
398 m_currentVariant = aVariantName.CmpNoCase( GetDefaultVariantName() ) == 0 ? wxString() : aVariantName;
399
400 for( const ITEM_TYPE& item : getAllItems() )
401 refreshDataStoreItem( item );
402
404 }
405
406 void AddColumn( const wxString& aFieldName, const wxString& aLabel,
407 bool aAddedByUser ) override
408 {
409 // Don't add a field twice
410 if( GetFieldNameCol( aFieldName ) != -1 )
411 return;
412
414
415 m_cols.push_back( { aFieldName, aLabel, aAddedByUser, false, false } );
416 FinalizeTextVarExpansion( m_cols.back().m_label, FOR_GUI );
417
418 for( const ITEM_TYPE& item : getAllItems() )
419 initializeDataStoreItemField( item, m_cols.back() );
420
421 if( aAddedByUser )
422 m_edited = true;
423
424 if( wxGrid* grid = GetView() )
425 {
426 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_COLS_APPENDED, 1 );
427 grid->ProcessTableMessage( msg );
428 }
429 }
430
431
435 void ClearCell( int aRow, int aCol ) override
436 {
437 wxCHECK_RET( aRow >= 0 && aRow < static_cast<int>( m_rows.size() ), "Invalid Row Number" );
438 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
439
440 if( !CanClearCell( aRow, aCol ) )
441 return;
442
443 const wxString& fieldName = m_cols[aCol].m_fieldName;
444
445 for( const ITEM_TYPE& item : m_rows[aRow].GetCellItems() )
446 clearStoredField( item, fieldName );
447
448 m_edited = true;
449 }
450
451
452 void CopyCellFromVariant( int aRow, int aCol, const wxString& aVariantName ) override
453 {
454 if( !CanCopyCellFromVariant( aRow, aCol ) )
455 return;
456
457 const wxString& fieldName = m_cols[aCol].m_fieldName;
458 wxString variant = aVariantName.CmpNoCase( GetDefaultVariantName() ) == 0 ? wxString() : aVariantName;
459 std::vector<wxString> values;
460
461 // Read every source before writing: grouped items may have different source values.
462 for( const ITEM_TYPE& item : m_rows[aRow].GetCellItems() )
463 {
464 FIELD_STORE_VALUE& field = storedField( item, fieldName, variant );
465 wxString value = field.m_present ? field.m_variants[variant].m_value : wxString();
466
467 if( ColIsAttribute( aCol ) && attributeForcedOnBySheet( item, fieldName, variant ) )
468 value = wxS( "1" );
469
470 values.push_back( value );
471 }
472
473 setCellValues( aRow, aCol, values );
475 }
476
477
482 bool IsCellClear( int aRow, int aCol ) override
483 {
484 wxCHECK_MSG( aRow >= 0 && aRow < static_cast<int>( m_rows.size() ), false, "Invalid Row Number" );
485 wxCHECK_MSG( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false, "Invalid Column Number" );
486
487 for( const ITEM_TYPE& item : m_rows[aRow].GetCellItems() )
488 {
489 wxString unused;
490
491 if( getStoredFieldValue( item, m_cols[aCol].m_fieldName, unused ) )
492 return false;
493 }
494
495 return true;
496 }
497
498
503 bool IsCellEdited( int aRow, int aCol ) override
504 {
505 wxCHECK_MSG( aRow >= 0 && aRow < static_cast<int>( m_rows.size() ), false, "Invalid Row Number" );
506 wxCHECK_MSG( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false, "Invalid Column Number" );
507
508 for( const ITEM_TYPE& item : m_rows[aRow].GetCellItems() )
509 {
510 if( fieldIsModified( item, m_cols[aCol].m_fieldName ) )
511 return true;
512 }
513
514 return false;
515 }
516
517
518 int GetNumberRows() override { return (int) m_rows.size(); }
519 ROW_STATE GetRowState( int aRow ) const override { return m_rows[aRow].m_state; }
520
521
522 std::vector<KIID_PATH> GetRowItemKeys( int aRow ) const override
523 {
524 wxCHECK( aRow >= 0 && aRow < (int) m_rows.size(), std::vector<KIID_PATH>() );
525
526 std::vector<KIID_PATH> keys;
527
528 for( const ITEM_TYPE& item : m_rows[aRow].GetCellItems() )
529 keys.push_back( getDataStoreKey( item ) );
530
531 return keys;
532 }
533
534
535 std::vector<ITEM_TYPE> GetRowReferences( int aRow ) const
536 {
537 wxCHECK( aRow >= 0 && aRow < (int) m_rows.size(), std::vector<ITEM_TYPE>() );
538 std::span<const ITEM_TYPE> items = m_rows[aRow].GetCellItems();
539 return { items.begin(), items.end() };
540 }
541
542
548 void RevertRow( int aRow ) override
549 {
550 wxCHECK_RET( aRow >= 0 && aRow < static_cast<int>( m_rows.size() ), "Invalid Row Number" );
551
552 for( const ITEM_TYPE& item : m_rows[aRow].GetCellItems() )
553 {
554 for( const DATA_MODEL_COL& col : m_cols )
555 updateDataStoreItemFieldFromLive( item, col.m_fieldName );
556 }
557
559 }
560
561
562 void ExpandRow( int aRow )
563 {
564 std::vector<DATA_MODEL_ROW<ITEM_TYPE>> children;
565 bool isParent = IsParentRow( m_rows[aRow].m_state );
566 std::span<const ITEM_TYPE> items( m_rows[aRow].m_items );
567
568 for( const ITEM_TYPE& ref : items.subspan( isParent ? 1 : 0 ) )
569 {
570 bool matchFound = false;
571
572 // See if we already have a child group which this symbol fits into
573 for( DATA_MODEL_ROW<ITEM_TYPE>& child : children )
574 {
575 // group members are by definition all matching, so just check
576 // against the first member
577 if( unitMatch( ref, child.m_items[0] ) )
578 {
579 matchFound = true;
580 child.m_items.push_back( ref );
581 break;
582 }
583 }
584
585 if( !matchFound )
586 children.emplace_back( ref, ROW_STATE::EXPANDED_CHILD );
587 }
588
589 if( children.empty() || ( !isParent && children.size() < 2 ) )
590 return;
591
592 std::sort( children.begin(), children.end(),
593 [this]( const DATA_MODEL_ROW<ITEM_TYPE>& lhs,
594 const DATA_MODEL_ROW<ITEM_TYPE>& rhs ) -> bool
595 {
596 return cmpRows( lhs, rhs, m_sortColumn, m_sortAscending );
597 } );
598
599 m_rows[aRow].m_state = isParent ? ROW_STATE::PARENT_EXPANDED : ROW_STATE::GROUP_EXPANDED;
600 m_rows.insert( m_rows.begin() + aRow + 1, children.begin(), children.end() );
601
602 if( GetView() )
603 {
604 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, aRow + 1, children.size() );
605 GetView()->ProcessTableMessage( msg );
606 }
607 }
608
609
610 void CollapseRow( int aRow )
611 {
612 auto firstChild = m_rows.begin() + aRow + 1;
613 auto afterLastChild = firstChild;
614 int deleted = 0;
615
616 while( afterLastChild != m_rows.end() && afterLastChild->m_state == ROW_STATE::EXPANDED_CHILD )
617 {
618 deleted++;
619 afterLastChild++;
620 }
621
622 m_rows[aRow].m_state =
623 IsParentRow( m_rows[aRow].m_state ) ? ROW_STATE::PARENT_COLLAPSED : ROW_STATE::GROUP_COLLAPSED;
624 m_rows.erase( firstChild, afterLastChild );
625
626 if( GetView() )
627 {
628 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aRow + 1, deleted );
629 GetView()->ProcessTableMessage( msg );
630 }
631 }
632
633
634 void ExpandCollapseRow( int aRow ) override
635 {
636 if( IsRowCollapsed( m_rows[aRow].m_state ) )
637 ExpandRow( aRow );
638 else if( IsRowExpanded( m_rows[aRow].m_state ) )
639 CollapseRow( aRow );
640 }
641
642
644 {
645 for( size_t i = 0; i < m_rows.size(); ++i )
646 {
647 if( IsRowExpanded( m_rows[i].m_state ) )
648 {
649 CollapseRow( i );
650 m_rows[i].m_state = IsParentRow( m_rows[i].m_state ) ? ROW_STATE::PARENT_COLLAPSED_DURING_SORT
651 : ROW_STATE::GROUP_COLLAPSED_DURING_SORT;
652 }
653 }
654 }
655
656
658 {
659 for( size_t i = 0; i < m_rows.size(); ++i )
660 {
661 if( m_rows[i].m_state == ROW_STATE::GROUP_COLLAPSED_DURING_SORT
662 || m_rows[i].m_state == ROW_STATE::PARENT_COLLAPSED_DURING_SORT )
663 ExpandRow( i );
664 }
665 }
666
667 wxString GetGroupedValue( const DATA_MODEL_ROW<ITEM_TYPE>& aRow, int aCol,
668 const wxString& refDelimiter = wxT( ", " ),
669 const wxString& refRangeDelimiter = wxT( "-" ), bool resolveVars = false,
670 bool forExport = false )
671 {
672 std::vector<ITEM_TYPE> items;
673 std::set<wxString> mixedValues;
674 wxString fieldValue;
675
676 // Export always aggregates the full group, even when the grid displays its real parent.
677 std::span<const ITEM_TYPE> rowItems =
678 forExport ? std::span<const ITEM_TYPE>( aRow.m_items ) : aRow.GetCellItems();
679
680 for( const ITEM_TYPE& item : rowItems )
681 {
682 if( ColIsItemIdentifier( aCol ) || ColIsQuantity( aCol ) || ColIsItemNumber( aCol ) )
683 {
684 items.push_back( item );
685 }
686 else // Other columns are either a single value or ROW_MULTI_ITEMS
687 {
688 wxString itemFieldValue;
689 getStoredFieldValue( item, m_cols[aCol].m_fieldName, itemFieldValue );
690
691 if( resolveVars )
692 getEffectiveFieldValue( item, m_cols[aCol].m_fieldName, itemFieldValue );
693
694 // Show the effective state when a sheet forces it on, but do not change
695 // the stored value so the symbol is never stamped on apply.
696 if( ColIsAttribute( aCol ) && attributeForcedOnBySheet( item, m_cols[aCol].m_fieldName ) )
697 itemFieldValue = wxS( "1" );
698
699 if( resolveVars )
700 {
701 if( ColIsAttribute( aCol ) )
702 {
703 itemFieldValue =
704 getAttributeResolvedValue( m_cols[aCol].m_fieldName, itemFieldValue == wxS( "1" ) );
705 }
706 // Computed fields (e.g. ${FOOTPRINT_LIBRARY}) can't have un-applied values.
707 // Resolve them against the item.
708 else if( ColIsComputed( aCol ) )
709 {
710 itemFieldValue = getFieldResolvedLiveValue( item, m_cols[aCol].m_fieldName );
711 }
712 // We have a field that contains both non-variable text and a variable
713 else if( IsGeneratedValue( itemFieldValue ) )
714 {
715 // Resolve variables in the un-applied value using the parent symbol and instance
716 // data.
717 itemFieldValue = resolveTextVars( item, itemFieldValue );
718 }
719 }
720
721 if( forExport )
722 mixedValues.insert( itemFieldValue );
723 else if( &item == &rowItems.front() )
724 fieldValue = itemFieldValue;
725 else if( fieldValue != itemFieldValue )
726 return INDETERMINATE_STATE;
727 }
728 }
729
730 if( forExport )
731 {
732 fieldValue = wxEmptyString;
733
734 for( const wxString& value : mixedValues )
735 {
736 if( value.IsEmpty() )
737 continue;
738 else if( fieldValue.IsEmpty() )
739 fieldValue = value;
740 else
741 fieldValue += "," + value;
742 }
743 }
744
745 if( ColIsItemIdentifier( aCol ) || ColIsQuantity( aCol ) || ColIsItemNumber( aCol ) )
746 {
747 // Remove duplicates (other units of multi-unit parts)
748 std::sort( items.begin(), items.end(),
749 [this]( const ITEM_TYPE& lhs, const ITEM_TYPE& rhs ) -> bool
750 {
751 return cmpRowItems( lhs, rhs );
752 } );
753
754 auto logicalEnd = std::unique( items.begin(), items.end(),
755 [this]( const ITEM_TYPE& lhs, const ITEM_TYPE& rhs ) -> bool
756 {
757 return unitMatch( lhs, rhs );
758 } );
759
760 items.erase( logicalEnd, items.end() );
761 }
762
763 // References are the item-identifier column for schematic symbols and footprints and have
764 // special formatting for representing reference ranges. Library item identifiers are joined
765 // without reference-range formatting.
766 if( ColIsItemIdentifier( aCol ) )
767 {
768 std::vector<wxString> itemIdentifiers;
769
770 for( const ITEM_TYPE& item : items )
771 itemIdentifiers.push_back( getItemIdentifier( item ) );
772
773 // C1-15, C17 kind of formatting
774 if( ColIsReference( aCol ) )
775 {
776 fieldValue = UTIL::FormatRefDesRanges( itemIdentifiers, refDelimiter, refRangeDelimiter );
777 }
778 else
779 {
780 fieldValue.clear();
781
782 for( const wxString& itemIdentifier : itemIdentifiers )
783 {
784 if( !fieldValue.IsEmpty() )
785 fieldValue += refDelimiter;
786
787 fieldValue += itemIdentifier;
788 }
789 }
790 }
791 else if( ColIsQuantity( aCol ) )
792 fieldValue = wxString::Format( wxT( "%d" ), (int) items.size() );
793 else if( ColIsItemNumber( aCol ) && aRow.m_state != ROW_STATE::EXPANDED_CHILD )
794 fieldValue = wxString::Format( wxT( "%d" ), aRow.m_itemNumber );
795
796 return fieldValue;
797 }
798
799
800 wxString GetValue( int aRow, int aCol ) override
801 {
802 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), wxEmptyString );
803 wxCHECK( aCol >= 0 && aCol < GetNumberCols(), wxEmptyString );
804
805 return GetGroupedValue( m_rows[aRow], aCol );
806 }
807
808 wxString GetResolvedValue( int aRow, int aCol ) override
809 {
810 return GetGroupedValue( m_rows[aRow], aCol, wxT( ", " ), wxT( "-" ), true, false );
811 }
812
813 wxString GetExportValue( int aRow, int aCol, const wxString& refDelimiter,
814 const wxString& refRangeDelimiter ) override
815 {
816 return GetGroupedValue( m_rows[aRow], aCol, refDelimiter, refRangeDelimiter, true, true );
817 }
818
819 bool IsCellReadOnly( int aRow, int aCol ) override
820 {
823 }
824
825
826protected:
827 bool MatchesFilter( const ITEM_TYPE& aItem, const wxString& aReference,
828 EDA_COMBINED_MATCHER& aMatcher )
829 {
830 if( m_filter.IsEmpty() )
831 return true;
832
833 // There is a slight difference between how the reference field is matched between
834 // 'reference' mode and all other modes that only applies to multiunit symbols in the
835 // symbol fields table.
836 //
837 // For compatibility, a filter like U1A will match a multiunit U1 that has a unit A
838 // in 'reference' mode, but not in any other mode. It won't match a C1A to a C1 that
839 // isn't mulitunit.
840 //
841 // In non-reference mode, because multiunit symbols are all grouped together,
842 // e.g. U1A and U1B are in the same row as U1, the filter will only match if the
843 // reference is exactly U1.
844 //
845 // None of this matters for the footprint fields table / other future tables.
847 return aMatcher.Find( aReference.Lower() );
848
849 for( size_t i = 0; i < m_cols.size(); ++i )
850 {
851 const DATA_MODEL_COL& col = m_cols[i];
852
854 continue;
855
856 wxString value;
857 getStoredFieldValue( aItem, col.m_fieldName, value );
858
859 // We want to match on things like DNP and Excluded from BOM when the
860 // checkbox is checked
861 if( ColIsAttribute( static_cast<int>( i ) ) )
862 {
863 bool effectiveValue = value == wxS( "1" ) || attributeForcedOnBySheet( aItem, col.m_fieldName );
864 value = getAttributeResolvedValue( col.m_fieldName, effectiveValue );
865 }
866 // Match computed fields on their resolved value rather than the stored variable.
867 else if( ColIsComputed( static_cast<int>( i ) ) )
868 {
869 value = getFieldResolvedLiveValue( aItem, col.m_fieldName );
870 }
871 // Same as above, but for field values that contain a mix of text and variables, e.g. "Value: ${VALUE}"
872 // The point is to match on what the user can see
873 else if( IsGeneratedValue( value ) )
874 {
875 value = resolveTextVars( aItem, value );
876 }
877
878 if( aMatcher.Find( value.Lower() ) )
879 return true;
880 }
881
882 return false;
883 }
884
885
891 bool attributeForcedOnBySheet( const ITEM_TYPE& aItem, const wxString& aAttributeName ) const
892 {
893 return attributeForcedOnBySheet( aItem, aAttributeName, m_currentVariant );
894 }
895
896 virtual bool attributeForcedOnBySheet( const ITEM_TYPE& aItem, const wxString& aAttributeName,
897 const wxString& aVariantName ) const
898 {
899 return false;
900 }
901
902
904 {
905 if( !ColIsAttribute( aCol ) || aRow.m_items.empty() )
906 return false;
907
908 // Lock the cell only when every symbol in the row inherits it, so a mixed group
909 // stays editable and shows the indeterminate state.
910 for( const ITEM_TYPE& item : aRow.GetCellItems() )
911 {
912 if( !attributeForcedOnBySheet( item, m_cols[aCol].m_fieldName ) )
913 return false;
914 }
915
916 return true;
917 }
918
920 int aSortCol, bool aAscending )
921 {
922 // Empty rows always go to the bottom, whether ascending or descending
923 if( lhRow.m_items.empty() )
924 return false;
925 else if( rhRow.m_items.empty() )
926 return true;
927
928 // N.B. To meet the iterator sort conditions, we cannot simply invert the truth
929 // to get the opposite sort. i.e. ~(a<b) != (a>b)
930 auto local_cmp =
931 [aAscending]( const auto a, const auto b )
932 {
933 if( aAscending )
934 return a < b;
935 else
936 return a > b;
937 };
938
939 // Primary sort key is sortCol; secondary is always the item identifier.
940 if( aSortCol < 0 || aSortCol >= this->GetNumberCols() )
941 aSortCol = 0;
942
943 wxString lhs = this->GetGroupedValue( lhRow, aSortCol, wxT( ", " ), wxT( "-" ), true )
944 .Trim( true )
945 .Trim( false );
946 wxString rhs = this->GetGroupedValue( rhRow, aSortCol, wxT( ", " ), wxT( "-" ), true )
947 .Trim( true )
948 .Trim( false );
949
950 if( lhs == rhs || this->ColIsItemIdentifier( aSortCol ) )
951 {
952 if( aAscending )
953 return cmpRowItems( lhRow.m_items[0], rhRow.m_items[0] );
954 else
955 return cmpRowItems( rhRow.m_items[0], lhRow.m_items[0] );
956 }
957 else
958 {
959 return local_cmp( ValueStringCompare( lhs, rhs ), 0 );
960 }
961 }
962
963 // Used for sorting items that are grouped within a single row by their item identifiers.
964 virtual bool cmpRowItems( const ITEM_TYPE& lhItem, const ITEM_TYPE& rhItem )
965 {
966 return StrNumCmp( getItemIdentifier( lhItem ), getItemIdentifier( rhItem ), true ) < 0;
967 }
968
969 virtual bool unitMatch( const ITEM_TYPE& lhItem, const ITEM_TYPE& rhItem ) = 0;
970
971 bool groupMatch( const ITEM_TYPE& lhItem, const ITEM_TYPE& rhItem )
972 {
973 bool matchFound = false;
974
975 for( size_t i = 0; i < m_cols.size(); ++i )
976 {
977 if( !m_cols[i].m_group )
978 continue;
979
980 int col = static_cast<int>( i );
981
982 // Schematic and PCB references are item identifiers and group by reference prefix.
983 // Library item identifiers and library references use ordinary exact matching below.
984 if( ColIsItemIdentifier( col ) && ColIsReference( col ) )
985 {
988 {
989 return false;
990 }
991
992 matchFound = true;
993 continue;
994 }
995
996 const wxString& fieldName = m_cols[i].m_fieldName;
997
998 wxString lh;
999 wxString rh;
1000 getStoredFieldValue( lhItem, fieldName, lh );
1001 getStoredFieldValue( rhItem, fieldName, rh );
1002
1003 // Normalize attributes so absent and explicitly false values group together, while
1004 // still honoring edits in the data store and effective values inherited from sheets.
1005 if( ColIsAttribute( col ) )
1006 {
1007 // Sheets can force values like DNP on, but it can't force them off
1008 bool lhEffectiveValue = lh == wxS( "1" ) || attributeForcedOnBySheet( lhItem, fieldName );
1009 bool rhEffectiveValue = rh == wxS( "1" ) || attributeForcedOnBySheet( rhItem, fieldName );
1010
1011 if( lhEffectiveValue != rhEffectiveValue )
1012 return false;
1013
1014 matchFound = true;
1015 continue;
1016 }
1017 // Computed fields (e.g. ${QUANTITY}) are resolved through the item. Property-backed
1018 // fields use the store so regrouping includes changes that haven't been applied yet.
1019 else if( ColIsComputed( col ) )
1020 {
1021 lh = getFieldResolvedLiveValue( lhItem, fieldName );
1022 rh = getFieldResolvedLiveValue( rhItem, fieldName );
1023 }
1024 // If we're not generated, we might still have a variable reference in the value,
1025 // e.g. "10K ${TOLERANCE}", which still needs to be resolved
1026 else
1027 {
1028 if( IsGeneratedValue( lh ) )
1029 lh = resolveTextVars( lhItem, lh );
1030 if( IsGeneratedValue( rh ) )
1031 rh = resolveTextVars( rhItem, rh );
1032 }
1033
1034 if( lh != rh )
1035 return false;
1036
1037 matchFound = true;
1038 }
1039
1040 return matchFound;
1041 }
1042
1043 void Sort()
1044 {
1046
1047 // Aggregate rows use their lowest item identifier; real parents must remain first.
1048 for( DATA_MODEL_ROW<ITEM_TYPE>& row : m_rows )
1049 {
1050 std::sort( row.m_items.begin() + ( IsParentRow( row.m_state ) ? 1 : 0 ), row.m_items.end(),
1051 [this]( const ITEM_TYPE& lhs, const ITEM_TYPE& rhs ) -> bool
1052 {
1053 return cmpRowItems( lhs, rhs );
1054 } );
1055 }
1056
1057 std::sort( m_rows.begin(), m_rows.end(),
1058 [this]( const DATA_MODEL_ROW<ITEM_TYPE>& lhs, const DATA_MODEL_ROW<ITEM_TYPE>& rhs ) -> bool
1059 {
1060 return cmpRows( lhs, rhs, m_sortColumn, m_sortAscending );
1061 } );
1062
1063 // Time to renumber the item numbers
1064 int itemNumber = 1;
1065
1066 for( DATA_MODEL_ROW<ITEM_TYPE>& row : m_rows )
1067 {
1068 row.m_itemNumber = itemNumber++;
1069 }
1070
1072 }
1073
1074
1081 virtual bool getLiveFieldValue( const ITEM_TYPE& aItem, const wxString& aFieldName, wxString& aValue ) = 0;
1082
1083 virtual bool getLiveFieldValueForVariant( const ITEM_TYPE& aItem, const wxString& aFieldName,
1084 const wxString& aVariant, wxString& aValue )
1085 {
1086 return getLiveFieldValue( aItem, aFieldName, aValue );
1087 }
1088
1092 std::map<wxString, wxString> getStoredFields( const ITEM_TYPE& aItem, const wxString& aVariant ) const
1093 {
1094 std::map<wxString, wxString> fields;
1095 auto itemIt = m_dataStore.find( getDataStoreKey( aItem ) );
1096
1097 if( itemIt == m_dataStore.end() )
1098 return fields;
1099
1100 for( const auto& [name, field] : itemIt->second )
1101 {
1102 auto valueIt = field.m_variants.find( aVariant );
1103
1104 if( !field.m_present || valueIt == field.m_variants.end() )
1105 continue;
1106
1107 const FIELD_VALUE_STATE& value = valueIt->second;
1108
1109 if( !field.m_baselinePresent || value.m_value != value.m_baseline )
1110 fields[name] = value.m_value;
1111 }
1112
1113 return fields;
1114 }
1115
1122 bool getStoredFieldValue( const ITEM_TYPE& aItem, const wxString& aFieldName, wxString& aValue ) const
1123 {
1124 aValue.clear();
1125
1126 auto itemIt = m_dataStore.find( getDataStoreKey( aItem ) );
1127
1128 if( itemIt == m_dataStore.end() )
1129 return false;
1130
1131 auto fieldIt = itemIt->second.find( aFieldName );
1132
1133 if( fieldIt == itemIt->second.end() || !fieldIt->second.m_present )
1134 return false;
1135
1136 auto valueIt = fieldIt->second.m_variants.find( fieldVariant( aFieldName, m_currentVariant ) );
1137
1138 if( valueIt != fieldIt->second.m_variants.end() )
1139 aValue = valueIt->second.m_value;
1140
1141 return true;
1142 }
1143
1149 bool resolveStoredTextVar( const ITEM_TYPE& aItem, wxString* aToken, bool aCaseSensitive = false ) const
1150 {
1151 auto itemIt = m_dataStore.find( getDataStoreKey( aItem ) );
1152
1153 if( itemIt == m_dataStore.end() )
1154 return false;
1155
1156 const auto& fields = itemIt->second;
1157 auto fieldIt = fields.find( *aToken );
1158
1159 if( fieldIt == fields.end() && !aCaseSensitive )
1160 {
1161 fieldIt = std::find_if( fields.begin(), fields.end(),
1162 [&]( const auto& aEntry )
1163 {
1164 return aToken->IsSameAs( aEntry.first, false );
1165 } );
1166 }
1167
1168 if( fieldIt == fields.end() || IsGeneratedField( fieldIt->first ) )
1169 return false;
1170
1171 const wxString& name = fieldIt->first;
1172 int col = GetFieldNameCol( name );
1173
1174 // Identifiers can have instance-specific formatting, such as multi-unit references.
1175 if( col >= 0 && ColIsItemIdentifier( col ) )
1176 return false;
1177
1178 const FIELD_STORE_VALUE& field = fieldIt->second;
1179
1180 if( !field.m_present )
1181 {
1182 if( !field.m_baselinePresent )
1183 return false;
1184
1185 // A pending deletion must not fall back to the old live field value.
1186 aToken->clear();
1187 return true;
1188 }
1189
1190 wxString value;
1191 getStoredFieldValue( aItem, name, value );
1192 *aToken = UnescapeString( value );
1193 return true;
1194 }
1195
1199 void setStoredFieldValue( const ITEM_TYPE& aItem, const wxString& aFieldName,
1200 const wxString& aValue )
1201 {
1202 FIELD_STORE_VALUE& field = storedField( aItem, aFieldName );
1203 field.m_present = true;
1204 field.m_variants[fieldVariant( aFieldName, m_currentVariant )].m_value = aValue;
1205 }
1206
1208 virtual void setCellValues( int aRow, int aCol, const std::vector<wxString>& aValues )
1209 {
1210 std::span<const ITEM_TYPE> items = m_rows[aRow].GetCellItems();
1211
1212 for( size_t i = 0; i < items.size(); ++i )
1213 setStoredFieldValue( items[i], m_cols[aCol].m_fieldName, aValues[i] );
1214 }
1215
1220 void ensureStoredFieldPresent( const ITEM_TYPE& aItem, const wxString& aFieldName )
1221 {
1222 storedField( aItem, aFieldName ).m_present = true;
1223 }
1224
1228 void clearStoredField( const ITEM_TYPE& aItem, const wxString& aFieldName )
1229 {
1230 FIELD_STORE_VALUE& field = storedField( aItem, aFieldName );
1231 field.m_present = false;
1232
1233 for( auto& [variant, state] : field.m_variants )
1234 state.m_value = state.m_baseline;
1235 }
1236
1242 bool fieldIsModified( const ITEM_TYPE& aItem, const wxString& aFieldName )
1243 {
1244 wxString liveValue;
1245 wxString storedValue;
1246 bool livePresent = getLiveFieldValue( aItem, aFieldName, liveValue );
1247 bool storedPresent = getStoredFieldValue( aItem, aFieldName, storedValue );
1248
1249 return livePresent != storedPresent || liveValue != storedValue;
1250 }
1251
1252
1259 void updateDataStoreItemFieldFromLive( const ITEM_TYPE& aItem, const wxString& aFieldName,
1260 bool aAllVariants = false )
1261 {
1262 FIELD_STORE_VALUE& field = storedField( aItem, aFieldName );
1263 wxString currentVariant = fieldVariant( aFieldName, m_currentVariant );
1264 wxString liveValue;
1265 bool livePresent = getLiveFieldValueForVariant( aItem, aFieldName, currentVariant, liveValue );
1266
1267 // Reverting a presence change affects every variant. Otherwise revert only the
1268 // displayed variant; successful Apply explicitly accepts all cached variants.
1269 bool allVariants = aAllVariants || field.m_present != livePresent;
1270
1271 for( auto& [variant, state] : field.m_variants )
1272 {
1273 if( allVariants || variant == currentVariant )
1274 {
1275 getLiveFieldValueForVariant( aItem, aFieldName, variant, state.m_value );
1276 state.m_baseline = state.m_value;
1277 }
1278 }
1279
1280 field.m_present = field.m_baselinePresent = livePresent;
1281 }
1282
1288 void initializeDataStoreItemField( const ITEM_TYPE& aItem, const DATA_MODEL_COL& aCol )
1289 {
1290 FIELD_STORE_VALUE& field = storedField( aItem, aCol.m_fieldName );
1291
1292 if( aCol.m_userAdded )
1293 field.m_present = true;
1294 }
1295
1302 void initializeDataStoreItem( const ITEM_TYPE& aItem )
1303 {
1304 for( const DATA_MODEL_COL& col : m_cols )
1305 initializeDataStoreItemField( aItem, col );
1306 }
1307
1308 FIELD_STORE_VALUE& storedField( const ITEM_TYPE& aItem, const wxString& aFieldName )
1309 {
1310 return storedField( aItem, aFieldName, m_currentVariant );
1311 }
1312
1313 FIELD_STORE_VALUE& storedField( const ITEM_TYPE& aItem, const wxString& aFieldName, const wxString& aVariantName )
1314 {
1315 auto& fields = m_dataStore[getDataStoreKey( aItem )];
1316 auto [fieldIt, newField] = fields.try_emplace( aFieldName );
1317 FIELD_STORE_VALUE& field = fieldIt->second;
1318 wxString variant = fieldVariant( aFieldName, aVariantName );
1319 auto [valueIt, newVariant] = field.m_variants.try_emplace( variant );
1320
1321 if( newVariant )
1322 {
1323 FIELD_VALUE_STATE& state = valueIt->second;
1324 bool present = getLiveFieldValueForVariant( aItem, aFieldName, variant, state.m_baseline );
1325 state.m_value = state.m_baseline;
1326
1327 if( newField )
1328 field.m_present = field.m_baselinePresent = present;
1329 }
1330
1331 return field;
1332 }
1333
1334 void refreshDataStoreItem( const ITEM_TYPE& aItem )
1335 {
1337
1338 for( const DATA_MODEL_COL& col : m_cols )
1339 {
1340 auto& fields = m_dataStore[getDataStoreKey( aItem )];
1341
1342 if( !fields.contains( col.m_fieldName ) )
1343 initializeDataStoreItemField( aItem, col );
1344 else
1345 storedField( aItem, col.m_fieldName );
1346 }
1347
1348 for( auto& [name, field] : m_dataStore[getDataStoreKey( aItem )] )
1349 {
1350 bool livePresent = field.m_baselinePresent;
1351 bool liveChanged = false;
1352
1353 for( auto& [variant, state] : field.m_variants )
1354 {
1355 wxString live;
1356 livePresent = getLiveFieldValueForVariant( aItem, name, variant, live );
1357 bool changed = livePresent != field.m_baselinePresent || live != state.m_baseline;
1358
1359 // External changes to this field win; a move/rotation leaves staged edits alone.
1360 if( changed || state.m_value == state.m_baseline )
1361 state.m_value = live;
1362
1363 state.m_baseline = live;
1364 liveChanged |= changed;
1365 }
1366
1367 if( liveChanged || field.m_present == field.m_baselinePresent )
1368 field.m_present = livePresent;
1369
1370 field.m_baselinePresent = livePresent;
1371 }
1372 }
1373
1374 bool storedFieldIsRemoved( const ITEM_TYPE& aItem, const wxString& aName ) const
1375 {
1376 auto itemIt = m_dataStore.find( getDataStoreKey( aItem ) );
1377
1378 if( itemIt == m_dataStore.end() )
1379 return false;
1380
1381 auto fieldIt = itemIt->second.find( aName );
1382 return fieldIt != itemIt->second.end() && !fieldIt->second.m_present && fieldIt->second.m_baselinePresent;
1383 }
1384
1385 std::set<wxString> storedVariants( const ITEM_TYPE& aItem ) const
1386 {
1387 std::set<wxString> variants;
1388 auto itemIt = m_dataStore.find( getDataStoreKey( aItem ) );
1389
1390 if( itemIt != m_dataStore.end() )
1391 {
1392 for( const auto& [name, field] : itemIt->second )
1393 {
1394 for( const auto& [variant, state] : field.m_variants )
1395 variants.insert( variant );
1396 }
1397 }
1398
1399 return variants;
1400 }
1401
1402 void acceptDataStoreItem( const ITEM_TYPE& aItem )
1403 {
1404 for( auto& [name, field] : m_dataStore[getDataStoreKey( aItem )] )
1405 updateDataStoreItemFieldFromLive( aItem, name, true );
1406 }
1407
1408 // Display inherited values without turning them into locally stored overrides.
1409 virtual void getEffectiveFieldValue( const ITEM_TYPE& aItem, const wxString& aFieldName,
1410 wxString& aValue ) const
1411 {
1412 getStoredFieldValue( aItem, aFieldName, aValue );
1413 }
1414
1415 virtual std::vector<ITEM_TYPE> getAllItems() const = 0;
1416
1417public:
1418 virtual KIID_PATH getDataStoreKey( const ITEM_TYPE& aItem ) const = 0;
1419 virtual wxString getItemIdentifier( const ITEM_TYPE& aItem ) const = 0;
1420
1430 virtual wxString getFieldResolvedLiveValue( const ITEM_TYPE& aItem, const wxString& aFieldName ) = 0;
1431
1432 virtual wxString resolveTextVars( const ITEM_TYPE& aItem, const wxString& aText ) = 0;
1433
1434protected:
1435 std::vector<DATA_MODEL_ROW<ITEM_TYPE>> m_rows;
1436};
const char * name
BOM_FILTER_SCOPE
bool Find(const wxString &aTerm, int &aMatchersTriggered, int &aPosition)
Look in all existing matchers, return the earliest match of any of the existing.
virtual void CopyCellFromVariant(int aRow, int aCol, const wxString &aVariantName)=0
Copy each item's raw source value into the displayed variant, retaining staged edits.
void SetSelectionItems(const std::unordered_set< KIID_PATH > &aItems)
bool cellUsesResolvedTextRenderer(int aRow, int aCol)
const wxString & GetCurrentVariant() const
virtual bool fieldIsItemProperty(const wxString &aFieldName) const
void DeleteStoredVariant(const wxString &aName)
void SetVariantNames(const std::vector< wxString > &aVariantNames)
bool cellUsesUrlEditor(int aRow, int aCol)
virtual void SetCurrentVariant(const wxString &aVariantName)=0
Select the displayed variant, retaining un-applied edits in every variant.
BOM_FILTER_SCOPE GetFilterScope() const
virtual bool IsCellEdited(int aRow, int aCol)=0
std::map< KIID_PATH, std::map< wxString, FIELD_STORE_VALUE > > m_dataStore
void SetSorting(int aCol, bool aAscending)
void MoveColumn(int aCol, int aNewPos)
virtual void ClearCell(int aRow, int aCol)=0
bool IsEmptyCell(int aRow, int aCol) override
void SetShowColumn(int aCol, bool aShow)
void SetIncludeExcludedFromBOM(bool aInclude)
bool IsRowEdited(int aRow)
Return true if any cell in the row has been edited.
wxGridCellAttr * applyFieldPresenceRenderer(wxGridCellAttr *aAttr, int aRow, int aCol)
std::vector< BOM_FIELD > GetFieldsOrdered()
void applyResolvedTextRenderer(wxGridCellAttr *aAttr, bool aApplyTint)
static const wxString ITEM_NUMBER_VARIABLE
virtual bool ColIsReadOnly(int aCol) const
virtual void AddColumn(const wxString &aFieldName, const wxString &aLabel, bool aAddedByUser)=0
bool HasUndoStateSerialization() const override
Optional identity-based serialization for the host dialog's Ctrl+Z/Ctrl+Y.
wxString Export(const BOM_FMT_PRESET &aSettings)
wxGridCellRenderer * m_resolvedTextRenderer
virtual wxString GetExportValue(int aRow, int aCol, const wxString &aRefDelimiter, const wxString &aRefRangeDelimiter)=0
virtual bool ColIsItemIdentifier(int aCol) const
Reference for symbol/fields tables, lib_id for lib tables.
void SetColLabelValue(int aCol, const wxString &aLabel) override
void SetFilterScope(BOM_FILTER_SCOPE aScope)
virtual bool fieldIsAttribute(const wxString &aFieldName) const
wxGridCellAttr * applyCellDecorations(wxGridCellAttr *aAttr, int aRow, int aCol)
virtual wxString getAttributeResolvedValue(const wxString &aFieldName, bool aValue) const
void SetFieldsOrder(const std::vector< wxString > &aNewOrder)
virtual void RevertRow(int aRow)=0
virtual bool IsCellReadOnly(int aRow, int aCol)
int GetFieldNameCol(const wxString &aFieldName) const
virtual wxString GetResolvedValue(int aRow, int aCol)=0
wxString fieldVariant(const wxString &aFieldName, const wxString &aVariantName) const
wxString SerializeUndoState() const override
std::unordered_set< KIID_PATH > m_selectionItems
Items included by the user selection scope.
virtual bool fieldSupportsVariants(const wxString &aFieldName) const
std::vector< wxString > m_variantNames
Variant names for multi-variant DNP filtering.
virtual void ExpandCollapseRow(int aRow)=0
std::vector< DATA_MODEL_COL > m_cols
bool CanCopyCellFromVariant(int aRow, int aCol)
const std::vector< wxString > & GetVariantNames() const
void RenameStoredVariant(const wxString &aOldName, const wxString &aNewName)
void RestoreUndoState(const wxString &aState) override
void RenameColumn(int aCol, const wxString &newName)
void SetGroupColumn(int aCol, bool aGroup)
void SetFilter(const wxString &aFilter)
wxString GetColLabelValue(int aCol) override
virtual std::vector< KIID_PATH > GetRowItemKeys(int aRow) const =0
Return the stable data-store keys of every item represented by a row.
void ApplyBomPreset(const BOM_PRESET &aPreset)
virtual void RebuildRows()=0
virtual bool IsCellClear(int aRow, int aCol)=0
wxString m_currentVariant
Current variant name for highlighting.
bool HasRowLabelExpanders() const override
Show grouping controls in the row headers instead of row numbers.
The rest of the generic fields data model stuff, primarily around row functionality.
void CopyCellFromVariant(int aRow, int aCol, const wxString &aVariantName) override
Copy each item's raw source value into the displayed variant, retaining staged edits.
wxString GetGroupedValue(const DATA_MODEL_ROW< ITEM_TYPE > &aRow, int aCol, const wxString &refDelimiter=wxT(", "), const wxString &refRangeDelimiter=wxT("-"), bool resolveVars=false, bool forExport=false)
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 ITEM_TYPE &aItem, const wxString &aFieldName, bool aAllVariants=false)
Updates the data store with the current value of the field from the live item.
virtual wxString getItemIdentifier(const ITEM_TYPE &aItem) const =0
std::vector< ITEM_TYPE > GetRowReferences(int aRow) const
bool groupMatch(const ITEM_TYPE &lhItem, const ITEM_TYPE &rhItem)
void ensureStoredFieldPresent(const ITEM_TYPE &aItem, const wxString &aFieldName)
Makes sure a field is at least marked present-but-empty without changing its value if it has one alre...
void ExpandCollapseRow(int aRow) override
virtual std::vector< ITEM_TYPE > getAllItems() const =0
wxString GetExportValue(int aRow, int aCol, const wxString &refDelimiter, const wxString &refRangeDelimiter) override
void setStoredFieldValue(const ITEM_TYPE &aItem, const wxString &aFieldName, const wxString &aValue)
Creates or updates a field in the data store.
bool fieldIsModified(const ITEM_TYPE &aItem, const wxString &aFieldName)
Compares the live value of the field to the stored value in the data store.
void initializeDataStoreItemField(const ITEM_TYPE &aItem, const DATA_MODEL_COL &aCol)
Puts the live value of the field in the data store, correctly handling a missing field in the souce i...
void initializeDataStoreItem(const ITEM_TYPE &aItem)
Initializes every column in the data store for a newly added (or refreshed) item.
bool storedFieldIsRemoved(const ITEM_TYPE &aItem, const wxString &aName) const
void clearStoredField(const ITEM_TYPE &aItem, const wxString &aFieldName)
Clears a field, e.g.
virtual bool unitMatch(const ITEM_TYPE &lhItem, const ITEM_TYPE &rhItem)=0
bool attributeForcedOnBySheet(const ITEM_TYPE &aItem, const wxString &aAttributeName) const
Sheets can force all the symbols in them to have certain attributes on, like DNP.
FIELD_STORE_VALUE & storedField(const ITEM_TYPE &aItem, const wxString &aFieldName, const wxString &aVariantName)
bool cmpRows(const DATA_MODEL_ROW< ITEM_TYPE > &lhRow, const DATA_MODEL_ROW< ITEM_TYPE > &rhRow, int aSortCol, bool aAscending)
bool getStoredFieldValue(const ITEM_TYPE &aItem, const wxString &aFieldName, wxString &aValue) const
Gets the stored value of the field from the data store, not from the live item, e....
virtual bool getLiveFieldValue(const ITEM_TYPE &aItem, const wxString &aFieldName, wxString &aValue)=0
Gets the current value of the field from the live item, e.g.
virtual void getEffectiveFieldValue(const ITEM_TYPE &aItem, const wxString &aFieldName, wxString &aValue) const
bool IsCellEdited(int aRow, int aCol) override
Returns true if the cell has been modified from the live value in any item in the row (symbol/footpri...
std::vector< DATA_MODEL_ROW< ITEM_TYPE > > m_rows
FIELD_STORE_VALUE & storedField(const ITEM_TYPE &aItem, const wxString &aFieldName)
bool resolveStoredTextVar(const ITEM_TYPE &aItem, wxString *aToken, bool aCaseSensitive=false) const
Resolve an ordinary field token from the current variant's staged values.
bool IsCellReadOnly(int aRow, int aCol) override
std::map< wxString, wxString > getStoredFields(const ITEM_TYPE &aItem, const wxString &aVariant) const
Returns the edited, present fields belonging to a variant without creating entries.
void AddColumn(const wxString &aFieldName, const wxString &aLabel, bool aAddedByUser) override
void refreshDataStoreItem(const ITEM_TYPE &aItem)
wxString GetValue(int aRow, int aCol) override
std::vector< KIID_PATH > GetRowItemKeys(int aRow) const override
Return the stable data-store keys of every item represented by a row.
bool MatchesFilter(const ITEM_TYPE &aItem, const wxString &aReference, EDA_COMBINED_MATCHER &aMatcher)
bool IsCellClear(int aRow, int aCol) override
Returns true if the cell is not present in the data store for any item in the row,...
virtual bool attributeForcedOnBySheet(const ITEM_TYPE &aItem, const wxString &aAttributeName, const wxString &aVariantName) const
bool allRowItemsHaveAttributeForcedOnBySheet(const DATA_MODEL_ROW< ITEM_TYPE > &aRow, int aCol)
virtual KIID_PATH getDataStoreKey(const ITEM_TYPE &aItem) const =0
void ClearCell(int aRow, int aCol) override
Clears the field from the data store, rather than setting its value to an empty string.
ROW_STATE GetRowState(int aRow) const override
void acceptDataStoreItem(const ITEM_TYPE &aItem)
wxString GetResolvedValue(int aRow, int aCol) override
virtual void setCellValues(int aRow, int aCol, const std::vector< wxString > &aValues)
Write one value per item represented by the cell. Models may propagate shared storage.
void SetCurrentVariant(const wxString &aVariantName) override
Select the displayed variant, retaining un-applied edits in every variant.
virtual wxString getFieldResolvedLiveValue(const ITEM_TYPE &aItem, const wxString &aFieldName)=0
Explicitly bypasses the data store's field values and retries them from the item's current field valu...
virtual wxString resolveTextVars(const ITEM_TYPE &aItem, const wxString &aText)=0
virtual bool cmpRowItems(const ITEM_TYPE &lhItem, const ITEM_TYPE &rhItem)
std::set< wxString > storedVariants(const ITEM_TYPE &aItem) const
virtual bool getLiveFieldValueForVariant(const ITEM_TYPE &aItem, const wxString &aFieldName, const wxString &aVariant, wxString &aValue)
wxSize GetBestSize(wxGrid &aGrid, wxGridCellAttr &aAttr, wxDC &aDC, int aRow, int aCol) override
wxGridCellRenderer * Clone() const override
void Draw(wxGrid &aGrid, wxGridCellAttr &aAttr, wxDC &aDC, const wxRect &aRect, int aRow, int aCol, bool isSelected) override
void FinalizeTextVarExpansion(wxString &aText, RESOLUTION_CONTEXT aContext)
Definition common.cpp:88
bool IsGeneratedValue(const wxString &aValue)
Returns true if some of the string is generated, e.g contains a text var or expression.
Definition common.cpp:502
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
@ FOR_GUI
Definition common.h:91
Abstract pattern-matching tool and implementations.
const wxColour VARIANT_FIELD_OVERRIDE_DARK_YELLOW(80, 80, 40)
const wxColour VARIANT_FIELD_OVERRIDE_LIGHT_YELLOW(255, 255, 200)
const wxColour STRIPED_CLEARED_EMPTY_FIELD_LIGHT_GREEN(180, 220, 180)
const wxColour TEXT_VARIABLE_DARK_AMBER(80, 70, 30)
const wxColour CLEARED_FIELD_STRIPE_ON_DARK_LIGHT_RED(220, 180, 180)
const wxColour VARIANT_SYMBOL_OVERRIDE_LIGHT_BLUE(220, 235, 255)
const wxColour STRIPED_EDITED_EMPTY_FIELD_MUTED_GREEN(180, 200, 180)
const wxColour EDITED_EMPTY_FIELD_BRIGHT_GREEN(192, 255, 192)
const wxColour TEXT_VARIABLE_LIGHT_YELLOW(255, 252, 200)
const wxColour VARIANT_SYMBOL_OVERRIDE_DARK_BLUE(40, 60, 80)
const wxColour STRIPED_EDITED_NONEMPTY_FIELD_MUTED_RED(200, 180, 180)
const wxColour STRIPED_CLEARED_NONEMPTY_FIELD_LIGHT_RED(220, 180, 180)
const wxColour CLEARED_FIELD_STRIPE_ON_LIGHT_DARK_RED(100, 10, 10)
wxString GetRefDesPrefix(const wxString &aRefDes)
Get the (non-numeric) prefix from a refdes - e.g.
wxString FormatRefDesRanges(const std::vector< wxString > &aReferences, const wxString &aRefDelimiter, const wxString &aRefRangeDelimiter)
Format a sorted list of reference designators, shortening consecutive sequences of three or more refe...
Collection of utility functions for component reference designators (refdes)
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
wxString GetDefaultVariantName()
wxString UnescapeString(const wxString &aSource)
int ValueStringCompare(const wxString &strFWord, const wxString &strSWord)
Compare strings like the strcmp function but handle numbers and modifiers within the string text corr...
The point of the data model classes is fundamentally to represent three things:
std::vector< LIB_SYMBOL * > m_items
DATA_MODEL_ROW(const ITEM_TYPE &aFirstItem, ROW_STATE aGroupingState)
std::span< const ITEM_TYPE > GetCellItems() const
Items displayed and edited by this row, excluding descendants of a real parent.
std::map< wxString, FIELD_VALUE_STATE > m_variants
Functions to provide common constants and other functions to assist in making a consistent UI.
#define INDETERMINATE_STATE
Used for holding indeterminate values, such as with multiple selections holding different values or c...
Definition ui_common.h:46
bool IsRowCollapsed(ROW_STATE aState)
Definition wx_grid.h:69
enum KICOMMON_API ROW_STATE
Used primarily by the fields tables to collapse multiple grouped symbols, etc.
Definition wx_grid.h:44
bool IsParentRow(ROW_STATE aState)
Definition wx_grid.h:62
bool IsRowExpanded(ROW_STATE aState)
Definition wx_grid.h:75