KiCad PCB EDA Suite
Loading...
Searching...
No Matches
fields_table_data_model.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <algorithm>
21#include <set>
22#include <utility>
23
26#include <template_fieldnames.h>
27
28#include <nlohmann/json.hpp>
30#include <widgets/ui_common.h>
31#include <wx/dc.h>
32#include <wx/settings.h>
33
34
39
40
41void GRID_CELL_RESOLVED_TEXT_RENDERER::Draw( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC, const wxRect& aRect,
42 int aRow, int aCol, bool isSelected )
43{
44 wxString value = aGrid.GetCellValue( aRow, aCol );
45
46 if( auto* model = dynamic_cast<FIELDS_TABLE_DATA_MODEL_BASE*>( aGrid.GetTable() ) )
47 value = model->GetResolvedValue( aRow, aCol );
48
49 wxRect rect = aRect;
50 rect.Inflate( -1 );
51
52 wxGridCellRenderer::Draw( aGrid, aAttr, aDC, aRect, aRow, aCol, isSelected );
53 SetTextColoursAndFont( aGrid, aAttr, aDC, isSelected );
54 aGrid.DrawTextRectangle( aDC, value, rect, wxALIGN_LEFT, wxALIGN_CENTRE );
55}
56
57
58wxSize GRID_CELL_RESOLVED_TEXT_RENDERER::GetBestSize( wxGrid& aGrid, wxGridCellAttr& aAttr, wxDC& aDC, int aRow,
59 int aCol )
60{
61 wxString value = aGrid.GetCellValue( aRow, aCol );
62
63 if( auto* model = dynamic_cast<FIELDS_TABLE_DATA_MODEL_BASE*>( aGrid.GetTable() ) )
64 value = model->GetResolvedValue( aRow, aCol );
65
66 return wxGridCellStringRenderer::DoGetBestSize( aAttr, aDC, value );
67}
68
69
70wxGridCellRenderer* GRID_CELL_RESOLVED_TEXT_RENDERER::Clone() const
71{
73}
74
75
76const wxString FIELDS_TABLE_DATA_MODEL_BASE::QUANTITY_VARIABLE = wxS( "${QUANTITY}" );
77const wxString FIELDS_TABLE_DATA_MODEL_BASE::ITEM_NUMBER_VARIABLE = wxS( "${ITEM_NUMBER}" );
78
79
93
94
100
101
103{
104 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), false );
105 wxCHECK( aCol >= 0 && aCol < GetNumberCols(), false );
106
107 return !ColIsItemIdentifier( aCol ) && !ColIsQuantity( aCol ) && !ColIsItemNumber( aCol )
108 && ( ColIsComputed( aCol ) || IsGeneratedValue( GetValue( aRow, aCol ) ) );
109}
110
111
112void FIELDS_TABLE_DATA_MODEL_BASE::applyResolvedTextRenderer( wxGridCellAttr* aAttr, bool aApplyTint )
113{
114 wxCHECK_RET( aAttr, wxS( "Cannot apply a renderer to a null cell attribute" ) );
115
118
119 m_resolvedTextRenderer->IncRef();
120 aAttr->SetRenderer( m_resolvedTextRenderer );
121
122 if( aApplyTint )
123 {
124 wxColour bg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
125 bool isDark = ( bg.Red() + bg.Green() + bg.Blue() ) < 384;
126
127 aAttr->SetBackgroundColour( isDark ? FIELDS_TABLE_COLOR::TEXT_VARIABLE_DARK_AMBER
129 }
130}
131
132
134{
135 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), false );
136 wxCHECK( aCol >= 0 && aCol < GetNumberCols(), false );
137
139
140 if( datasheetCol < 0 )
141 return false;
142
143 auto attrIt = m_colAttrs.find( datasheetCol );
144
145 if( attrIt == m_colAttrs.end() || !attrIt->second )
146 return false;
147
148 return aCol == datasheetCol || IsURL( GetValue( aRow, aCol ) );
149}
150
151
153{
155 wxCHECK( datasheetCol >= 0, nullptr );
156
157 auto attrIt = m_colAttrs.find( datasheetCol );
158 wxCHECK( attrIt != m_colAttrs.end() && attrIt->second, nullptr );
160 return attrIt->second->Clone();
162
163
164wxGridCellAttr* FIELDS_TABLE_DATA_MODEL_BASE::applyFieldPresenceRenderer( wxGridCellAttr* aAttr, int aRow, int aCol )
166 if( !IsCellClear( aRow, aCol ) || ColIsAttribute( aCol ) || ColIsComputed( aCol ) )
167 return aAttr;
169 wxGridCellAttr* stripedAttr = aAttr ? aAttr->Clone() : new wxGridCellAttr;
170 wxSafeDecRef( aAttr );
173 {
176 }
177
178 m_stripedRenderer->IncRef();
179 stripedAttr->SetRenderer( m_stripedRenderer );
181 if( !stripedAttr->HasBackgroundColour() )
182 stripedAttr->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
184 return stripedAttr;
185}
187
188wxGridCellAttr* FIELDS_TABLE_DATA_MODEL_BASE::applyCellDecorations( wxGridCellAttr* aAttr, int aRow, int aCol )
189{
190 constexpr double ROW_HINT_OPACITY = 0.1;
191 constexpr double COLUMN_HINT_OPACITY = 0.05;
193 aAttr = applyFieldPresenceRenderer( aAttr, aRow, aCol );
194
195 WX_GRID* grid = dynamic_cast<WX_GRID*>( GetView() );
196
197 if( !grid || !grid->IsCursorRowColumnHighlightEnabled()
198 || ( aRow != grid->GetGridCursorRow() && aCol != grid->GetGridCursorCol() ) )
199 {
200 return aAttr;
201 }
202
203 wxColour background = aAttr && aAttr->HasBackgroundColour() ? aAttr->GetBackgroundColour()
204 : grid->GetDefaultCellBackgroundColour();
205 wxColour highlight = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
206 double hintOpacity = aRow == grid->GetGridCursorRow() ? ROW_HINT_OPACITY : COLUMN_HINT_OPACITY;
207
208 wxColour hintedBackground( wxColour::AlphaBlend( highlight.Red(), background.Red(), hintOpacity ),
209 wxColour::AlphaBlend( highlight.Green(), background.Green(), hintOpacity ),
210 wxColour::AlphaBlend( highlight.Blue(), background.Blue(), hintOpacity ) );
212 wxGridCellAttr* hintedAttr = aAttr ? aAttr->Clone() : new wxGridCellAttr;
213 wxSafeDecRef( aAttr );
214 hintedAttr->SetBackgroundColour( hintedBackground );
215
216 return hintedAttr;
217}
218
219
221{
222 if( wxGrid* grid = GetView() )
223 static_cast<WX_GRID*>( grid )->CommitPendingChanges( true );
224}
225
226
227void FIELDS_TABLE_DATA_MODEL_BASE::MoveColumn( int aCol, int aNewPos )
228{
229 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
230 wxCHECK_RET( aNewPos >= 0 && aNewPos < static_cast<int>( m_cols.size() ), "Invalid New Column Position" );
231
232 if( aCol == aNewPos )
233 {
234 return;
239 if( aCol < aNewPos )
240 {
241 if( m_sortColumn == aCol )
242 m_sortColumn = aNewPos;
243 else if( m_sortColumn > aCol && m_sortColumn <= aNewPos )
244 m_sortColumn--;
245
246 std::rotate( std::begin( m_cols ) + aCol, std::begin( m_cols ) + aCol + 1, std::begin( m_cols ) + aNewPos + 1 );
247 }
248 else
249 {
250 if( m_sortColumn == aCol )
251 m_sortColumn = aNewPos;
252 else if( m_sortColumn >= aNewPos && m_sortColumn < aCol )
253 m_sortColumn++;
254
255 std::rotate( std::begin( m_cols ) + aNewPos, std::begin( m_cols ) + aCol, std::begin( m_cols ) + aCol + 1 );
256 }
258
259
261{
262 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
263
265
266 for( auto& [unused, fieldsStore] : m_dataStore )
267 {
268 FIELD_STORE_VALUE& field = fieldsStore[m_cols[aCol].m_fieldName];
269 field.m_present = false;
270
271 for( auto& [variant, state] : field.m_variants )
272 state.m_value = state.m_baseline;
273 }
275 m_cols.erase( m_cols.begin() + aCol );
276
277 if( m_sortColumn == aCol )
278 m_sortColumn = 0;
279 else if( m_sortColumn > aCol )
280 m_sortColumn--;
281
282 if( auto attrIt = m_colAttrs.find( aCol ); attrIt != m_colAttrs.end() )
284 wxSafeDecRef( attrIt->second );
285 m_colAttrs.erase( attrIt );
286 }
287
288 std::map<int, wxGridCellAttr*> shiftedColAttrs;
289
290 for( const auto& [col, attr] : m_colAttrs )
291 shiftedColAttrs[col > aCol ? col - 1 : col] = attr;
293 m_colAttrs.swap( shiftedColAttrs );
294
295 if( wxGrid* grid = GetView() )
296 {
297 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_COLS_DELETED, aCol, 1 );
298 grid->ProcessTableMessage( msg );
299 }
301 m_edited = true;
302}
303
305void FIELDS_TABLE_DATA_MODEL_BASE::RenameColumn( int aCol, const wxString& newName )
307 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
308
311 const wxString oldName = m_cols[aCol].m_fieldName;
312 bool wasComputed = ColIsComputed( aCol );
313 bool willBeComputed = IsGeneratedField( newName ) && !fieldIsItemProperty( newName );
314
315 for( auto& [unused, fieldsStore] : m_dataStore )
317 auto fieldIt = fieldsStore.find( oldName );
318
319 if( fieldIt == fieldsStore.end() )
320 continue;
321
322 FIELD_STORE_VALUE& oldField = fieldIt->second;
323 FIELD_STORE_VALUE& newField = fieldsStore[newName];
324 newField.m_present = oldField.m_present || wasComputed || willBeComputed;
325
326 // Computed columns are virtual, their stored values are only placeholders for the
327 // generated field name. Don't copy that placeholder into an ordinary field, or
328 // keep ordinary per-item values when the destination is computed.
329 for( const auto& [variant, state] : oldField.m_variants )
330 {
331 newField.m_variants[variant].m_value = willBeComputed ? newName : wasComputed ? wxString() : state.m_value;
332 }
333
334 oldField.m_present = false;
335 }
336
337 m_cols[aCol].m_fieldName = newName;
338 m_edited = true;
339}
340
341
342void FIELDS_TABLE_DATA_MODEL_BASE::SetColLabelValue( int aCol, const wxString& aLabel )
343{
344 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
345
346 m_cols[aCol].m_label = aLabel;
347 FinalizeTextVarExpansion( m_cols[aCol].m_label, FOR_GUI );
348}
349
350
352{
353 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), wxString() );
354 return m_cols[aCol].m_label;
355}
356
357
359{
360 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), wxString() );
361 return m_cols[aCol].m_fieldName;
362}
363
364
366{
367 int width = KIUI::GetTextSize( GetColLabelValue( aCol ), GetView() ).x;
368
369 for( int row = 0; row < GetNumberRows(); ++row )
370 width = std::max( width, KIUI::GetTextSize( GetResolvedValue( row, aCol ), GetView() ).x );
371
372 return width;
373}
374
375
376int FIELDS_TABLE_DATA_MODEL_BASE::GetFieldNameCol( const wxString& aFieldName ) const
377{
378 for( size_t i = 0; i < m_cols.size(); i++ )
379 {
380 if( FieldNamesAreDuplicates( m_cols[i].m_fieldName, aFieldName ) )
381 return static_cast<int>( i );
382 }
383
384 return -1;
385}
386
387
389{
390 std::vector<BOM_FIELD> fields;
391
392 for( const DATA_MODEL_COL& col : m_cols )
393 fields.push_back( { col.m_fieldName, col.m_label, col.m_show, col.m_group } );
394
395 return fields;
396}
397
398
399void FIELDS_TABLE_DATA_MODEL_BASE::SetFieldsOrder( const std::vector<wxString>& aNewOrder )
400{
402
403 size_t foundCount = 0;
404
405 for( const wxString& newField : aNewOrder )
406 {
407 if( foundCount >= m_cols.size() )
408 break;
409
410 for( DATA_MODEL_COL& col : m_cols )
411 {
412 if( col.m_fieldName == newField )
413 {
414 std::swap( m_cols[foundCount], col );
415 foundCount++;
416 break;
417 }
418 }
419 }
420}
421
422
424{
425 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
426 return m_cols[aCol].m_fieldName == GetDefaultFieldName( FIELD_T::REFERENCE, UNTRANSLATED );
427}
428
429
431{
432 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
433 return m_cols[aCol].m_fieldName == QUANTITY_VARIABLE;
434}
435
436
438{
439 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
440 return m_cols[aCol].m_fieldName == ITEM_NUMBER_VARIABLE;
441}
442
443
445{
446 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
447 return m_cols[aCol].m_fieldName == GetDefaultFieldName( FIELD_T::VALUE, UNTRANSLATED );
448}
449
450
452{
453 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
454 return m_cols[aCol].m_fieldName == GetDefaultFieldName( FIELD_T::FOOTPRINT, UNTRANSLATED );
455}
456
457
459{
460 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
461
462 return fieldIsAttribute( m_cols[aCol].m_fieldName );
463}
464
465
467{
468 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
469
470 return fieldIsItemProperty( m_cols[aCol].m_fieldName );
471}
472
473
475{
476 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
477
478 return IsGeneratedField( m_cols[aCol].m_fieldName ) && !ColIsItemProperty( aCol );
479}
480
481
483{
484 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), true );
485
486 return ColIsItemIdentifier( aCol ) || ColIsComputed( aCol );
487}
488
489
491{
492 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), true );
493 wxCHECK( aCol >= 0 && aCol < GetNumberCols(), true );
494
495 return ColIsReadOnly( aCol );
496}
497
498
500{
501 return !IsCellReadOnly( aRow, aCol ) && fieldSupportsVariants( m_cols[aCol].m_fieldName );
502}
503
504
506{
507 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), false );
508 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
509
510 if( IsCellReadOnly( aRow, aCol ) || IsCellClear( aRow, aCol )
511 || IsGeneratedField( m_cols[aCol].m_fieldName ) )
512 {
513 return false;
514 }
515
516 for( FIELD_T fieldId : MANDATORY_FIELDS )
517 {
518 if( m_cols[aCol].m_fieldName == GetDefaultFieldName( fieldId, UNTRANSLATED ) )
519 return false;
520 }
521
522 // Template fields are added by default to symbols, but it's unclear whether or not
523 // that means they should be mandatory. For now, allow them to be cleared.
524
525 return true;
526}
527
528
530{
531 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), false );
532
533 for( int col = 0; col < GetNumberCols(); ++col )
534 {
535 if( IsCellEdited( aRow, col ) )
536 return true;
537 }
538
539 return false;
540}
541
542
543void FIELDS_TABLE_DATA_MODEL_BASE::SetSorting( int aCol, bool aAscending )
544{
545 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
546 m_sortColumn = aCol;
547 m_sortAscending = aAscending;
548}
549
550
555
556
561
562
564{
565 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
566
567 if( ColIsQuantity( aCol ) || ColIsItemNumber( aCol ) )
568 aGroup = false;
569
570 m_cols[aCol].m_group = aGroup;
571}
572
573
575{
576 wxCHECK_MSG( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false, "Invalid Column Number" );
577 return m_cols[aCol].m_group;
578}
579
580
582{
583 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
584 m_cols[aCol].m_show = aShow;
585}
586
587
589{
590 wxCHECK_MSG( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false, "Invalid Column Number" );
591 return m_cols[aCol].m_show;
592}
593
594
595void FIELDS_TABLE_DATA_MODEL_BASE::ApplyBomPreset( const BOM_PRESET& aPreset )
596{
597 // Hide and un-group everything by default
598 for( size_t i = 0; i < m_cols.size(); i++ )
599 {
600 SetShowColumn( i, false );
601 SetGroupColumn( i, false );
602 }
603
604 std::set<wxString> seen;
605 std::vector<wxString> order;
606
607 // Set columns that are present and shown
608 for( const BOM_FIELD& field : aPreset.fieldsOrdered )
609 {
610 // Ignore empty fields
611 if( !field.name || seen.count( field.name ) )
612 continue;
613
614 seen.insert( field.name );
615 order.emplace_back( field.name );
616
617 int col = GetFieldNameCol( field.name );
618
619 // Add any missing fields; if the user doesn't add any data they won't be saved to the
620 // design anyway.
621 if( col == -1 )
622 {
623 AddColumn( field.name, field.label, false );
624 col = GetFieldNameCol( field.name );
625 }
626 else
627 {
628 SetColLabelValue( col, field.label );
629 }
630
631 SetGroupColumn( col, field.groupBy );
632 SetShowColumn( col, field.show );
633 }
634
635 SetGroupingEnabled( aPreset.groupSymbols );
636 SetFieldsOrder( order );
637
638 int sortCol = GetFieldNameCol( aPreset.sortField );
639
640 if( sortCol == -1 )
641 {
642 for( int col = 0; col < GetNumberCols(); ++col )
643 {
644 if( ColIsItemIdentifier( col ) )
645 {
646 sortCol = col;
647 break;
648 }
649 }
650 }
651
652 SetSorting( sortCol, aPreset.sortAsc );
653
654 SetFilter( aPreset.filterString );
655 SetFilterScope( aPreset.filterScope );
656 SetExcludeDNP( aPreset.excludeDNP );
657 SetIncludeExcludedFromBOM( aPreset.includeExcludedFromBOM );
658
659 RebuildRows();
660}
661
662
664{
665 BOM_PRESET current;
666 current.readOnly = false;
667 current.fieldsOrdered = GetFieldsOrdered();
668
669 if( GetSortCol() >= 0 && GetSortCol() < GetNumberCols() )
670 current.sortField = GetColFieldName( GetSortCol() );
671
672 current.sortAsc = GetSortAsc();
673 current.filterString = GetFilter();
674 current.filterScope = GetFilterScope();
675 current.groupSymbols = GetGroupingEnabled();
676 current.excludeDNP = GetExcludeDNP();
677 current.includeExcludedFromBOM = GetIncludeExcludedFromBOM();
678
679 return current;
680}
681
682
684{
685 wxString out;
686
687 if( m_cols.empty() )
688 return out;
689
690 int lastCol = -1;
691
692 // Find the location for the line terminator
693 for( size_t col = 0; col < m_cols.size(); col++ )
694 {
695 if( m_cols[col].m_show )
696 lastCol = static_cast<int>( col );
697 }
698
699 // No shown columns
700 if( lastCol == -1 )
701 return out;
702
703 if( aSettings.includeByteOrderMark )
704 out.Append( wxString::FromUTF8( "\xEF\xBB\xBF" ) );
705
706 auto formatField = [&]( wxString aField, bool aLast ) -> wxString
707 {
708 if( !aSettings.keepLineBreaks )
709 {
710 aField.Replace( wxS( "\r" ), wxS( "" ) );
711 aField.Replace( wxS( "\n" ), wxS( "" ) );
712 }
713
714 if( !aSettings.keepTabs )
715 aField.Replace( wxS( "\t" ), wxS( "" ) );
716
717 if( !aSettings.stringDelimiter.IsEmpty() )
718 {
719 aField.Replace( aSettings.stringDelimiter, aSettings.stringDelimiter + aSettings.stringDelimiter );
720 }
721
722 return aSettings.stringDelimiter + aField + aSettings.stringDelimiter
723 + ( aLast ? wxString( wxS( "\n" ) ) : aSettings.fieldDelimiter );
724 };
725
726 // Column names
727 for( size_t col = 0; col < m_cols.size(); col++ )
728 {
729 if( !m_cols[col].m_show )
730 continue;
731
732 out.Append( formatField( m_cols[col].m_label, col == static_cast<size_t>( lastCol ) ) );
733 }
734
735 // Data rows
736 for( int row = 0; row < GetNumberRows(); row++ )
737 {
738 // Don't output child rows
739 if( GetRowState( row ) == ROW_STATE::EXPANDED_CHILD )
740 continue;
741
742 for( size_t col = 0; col < m_cols.size(); col++ )
743 {
744 if( !m_cols[col].m_show )
745 continue;
746
747 out.Append( formatField(
748 GetExportValue( row, static_cast<int>( col ), aSettings.refDelimiter, aSettings.refRangeDelimiter ),
749 col == static_cast<size_t>( lastCol ) ) );
750 }
751 }
752
753 return out;
754}
755
756
757bool FIELDS_TABLE_DATA_MODEL_BASE::fieldIsAttribute( const wxString& aFieldName ) const
758{
759 return aFieldName == wxS( "${DNP}" ) || aFieldName == wxS( "${EXCLUDE_FROM_BOARD}" )
760 || aFieldName == wxS( "${EXCLUDE_FROM_BOM}" ) || aFieldName == wxS( "${EXCLUDE_FROM_POS_FILES}" )
761 || aFieldName == wxS( "${EXCLUDE_FROM_SIM}" );
762}
763
764
765bool FIELDS_TABLE_DATA_MODEL_BASE::fieldIsItemProperty( const wxString& aFieldName ) const
766{
767 return fieldIsAttribute( aFieldName );
768}
769
770
771wxString FIELDS_TABLE_DATA_MODEL_BASE::getAttributeResolvedValue( const wxString& aFieldName, bool aValue ) const
772{
773 if( !aValue )
774 return wxEmptyString;
775
776 if( aFieldName == wxS( "${DNP}" ) )
777 return wxS( "DNP" );
778 else if( aFieldName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
779 return wxS( "Excluded from board" );
780 else if( aFieldName == wxS( "${EXCLUDE_FROM_BOM}" ) )
781 return wxS( "Excluded from BOM" );
782 else if( aFieldName == wxS( "${EXCLUDE_FROM_POS_FILES}" ) )
783 return wxS( "Excluded from position files" );
784 else if( aFieldName == wxS( "${EXCLUDE_FROM_SIM}" ) )
785 return wxS( "Excluded from simulation" );
786
787 return wxEmptyString;
788}
789
790
791wxString FIELDS_TABLE_DATA_MODEL_BASE::fieldVariant( const wxString& aFieldName, const wxString& aVariantName ) const
792{
793 return fieldSupportsVariants( aFieldName ) ? aVariantName : wxString();
794}
795
796
798{
799 m_edited = false;
800
801 for( const auto& [key, fields] : m_dataStore )
802 {
803 for( const auto& [name, field] : fields )
804 {
805 if( field.m_present != field.m_baselinePresent )
806 {
807 m_edited = true;
808 return;
809 }
810
811 if( field.m_present )
812 {
813 for( const auto& [variant, state] : field.m_variants )
814 {
815 if( state.m_value != state.m_baseline )
816 {
817 m_edited = true;
818 return;
819 }
820 }
821 }
822 }
823 }
824}
825
826
827void FIELDS_TABLE_DATA_MODEL_BASE::RenameStoredVariant( const wxString& aOldName, const wxString& aNewName )
828{
830
831 for( auto& [key, fields] : m_dataStore )
832 {
833 for( auto& [name, field] : fields )
834 {
835 if( auto it = field.m_variants.find( aOldName ); it != field.m_variants.end() )
836 {
837 field.m_variants[aNewName] = it->second;
838 field.m_variants.erase( it );
839 }
840 }
841 }
842
843 if( m_currentVariant == aOldName )
844 m_currentVariant = aNewName;
845}
846
847
849{
851
852 for( auto& [key, fields] : m_dataStore )
853 {
854 for( auto& [name, field] : fields )
855 field.m_variants.erase( aName );
856 }
857
859}
860
861
863{
864 // Serialize the un-applied edit store keyed by symbol identity (sheet path + UUID), so that
865 // restoring it is independent of the current row grouping/order.
866 nlohmann::json j = nlohmann::json::object();
867
868 for( const auto& [key, fields] : m_dataStore )
869 {
870 nlohmann::json jfields = nlohmann::json::object();
871
872 for( const auto& [name, field] : fields )
873 {
874 auto& stored = jfields[std::string( name.ToUTF8() )];
875 stored["present"] = field.m_present;
876 stored["baseline_present"] = field.m_baselinePresent;
877 stored["variants"] = nlohmann::json::object();
878
879 for( const auto& [variant, state] : field.m_variants )
880 {
881 stored["variants"][std::string( variant.ToUTF8() )] = { std::string( state.m_value.ToUTF8() ),
882 std::string( state.m_baseline.ToUTF8() ) };
883 }
884 }
885
886 j[std::string( key.AsString().ToUTF8() )] = jfields;
887 }
888
889 return wxString( j.dump() );
890}
891
892
894{
895 nlohmann::json j = nlohmann::json::parse( aState.ToStdString(), nullptr, false );
896
897 if( !j.is_object() )
898 return;
899
900 // We want to wipe out key/value presence so we can properly test for
901 // empty vs. not-present
902 m_dataStore.clear();
903
904 for( auto it = j.begin(); it != j.end(); ++it )
905 {
906 KIID_PATH key( wxString::FromUTF8( it.key().c_str() ) );
907 auto& fields = m_dataStore[key];
908
909 for( auto fit = it.value().begin(); fit != it.value().end(); ++fit )
910 {
911 FIELD_STORE_VALUE& field = fields[wxString::FromUTF8( fit.key().c_str() )];
912 field.m_present = fit.value().at( "present" ).get<bool>();
913 field.m_baselinePresent = fit.value().at( "baseline_present" ).get<bool>();
914
915 for( const auto& [variant, state] : fit.value().at( "variants" ).items() )
916 {
917 field.m_variants[wxString::FromUTF8( variant )] = {
918 wxString::FromUTF8( state.at( 0 ).get<std::string>() ),
919 wxString::FromUTF8( state.at( 1 ).get<std::string>() )
920 };
921 }
922 }
923 }
924
926 RebuildRows();
927
928 if( GetView() )
929 GetView()->ForceRefresh();
930}
const char * name
BOM_FILTER_SCOPE
Contains everything completly generic to fields tables data models, as well as column functionality t...
bool cellUsesResolvedTextRenderer(int aRow, int aCol)
virtual bool fieldIsItemProperty(const wxString &aFieldName) const
void DeleteStoredVariant(const wxString &aName)
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)
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
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 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
virtual bool fieldSupportsVariants(const wxString &aFieldName) const
std::vector< DATA_MODEL_COL > m_cols
bool CanCopyCellFromVariant(int aRow, int aCol)
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
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.
Cell renderer that shows the expanded result of text variables (e.g.
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
virtual ROW_STATE GetRowState(int aRow) const
Definition wx_grid.h:109
std::map< int, wxGridCellAttr * > m_colAttrs
Definition wx_grid.h:133
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
STRIPED_CELL_RENDERER< wxGridCellStringRenderer > STRIPED_STRING_RENDERER
const wxColour TEXT_VARIABLE_DARK_AMBER(80, 70, 30)
const wxColour CLEARED_FIELD_STRIPE_ON_DARK_LIGHT_RED(220, 180, 180)
const wxColour TEXT_VARIABLE_LIGHT_YELLOW(255, 252, 200)
const wxColour CLEARED_FIELD_STRIPE_ON_LIGHT_DARK_RED(100, 10, 10)
KICOMMON_API wxSize GetTextSize(const wxString &aSingleLine, wxWindow *aWindow)
Return the size of aSingleLine of text when it is rendered in aWindow using whatever font is currentl...
Definition ui_common.cpp:78
bool IsURL(wxString aStr)
Performs a URL sniff-test on a string.
wxString label
wxString name
wxString fieldDelimiter
bool includeByteOrderMark
wxString stringDelimiter
wxString refRangeDelimiter
wxString refDelimiter
The point of the data model classes is fundamentally to represent three things:
std::map< wxString, FIELD_VALUE_STATE > m_variants
bool FieldNamesAreDuplicates(const wxString &aLhs, const wxString &aRhs, std::initializer_list< FIELD_T > aMandatoryFields)
Test whether two field names should be treated as duplicates for the purposes of field name uniquenes...
wxString GetDefaultFieldName(FIELD_T aFieldId, TRANSLATION aTranslation)
Return a default symbol field name for a mandatory field type.
#define MANDATORY_FIELDS
FIELD_T
The set of all field indices assuming an array like sequence that a SCH_COMPONENT or LIB_PART can hol...
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
@ UNTRANSLATED
KIBIS_MODEL * model
Functions to provide common constants and other functions to assist in making a consistent UI.