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 && 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
135 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), false );
136 wxCHECK( aCol >= 0 && aCol < GetNumberCols(), false );
137
139
140 if( datasheetCol < 0 )
141 return false;
143 auto attrIt = m_colAttrs.find( datasheetCol );
145 if( attrIt == m_colAttrs.end() || !attrIt->second )
146 return false;
148 return aCol == datasheetCol || IsURL( GetValue( aRow, aCol ) );
149}
150
151
153{
155 wxCHECK( datasheetCol >= 0, nullptr );
157 auto attrIt = m_colAttrs.find( datasheetCol );
158 wxCHECK( attrIt != m_colAttrs.end() && attrIt->second, nullptr );
159
160 return attrIt->second->Clone();
162
164wxGridCellAttr* FIELDS_TABLE_DATA_MODEL_BASE::applyFieldPresenceRenderer( wxGridCellAttr* aAttr, int aRow, int aCol )
165{
166 if( !IsCellClear( aRow, aCol ) || ColIsAttribute( aCol ) )
167 return aAttr;
168
169 wxGridCellAttr* stripedAttr = aAttr ? aAttr->Clone() : new wxGridCellAttr;
170 wxSafeDecRef( aAttr );
171
172 if( !m_stripedRenderer )
173 {
177
178 m_stripedRenderer->IncRef();
179 stripedAttr->SetRenderer( m_stripedRenderer );
180
181 if( !stripedAttr->HasBackgroundColour() )
182 stripedAttr->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
183
184 return stripedAttr;
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;
192
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;
219
221{
222 if( wxGrid* grid = GetView() )
223 static_cast<WX_GRID*>( grid )->CommitPendingChanges( true );
224}
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;
235 }
236
238
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 )
254
255 std::rotate( std::begin( m_cols ) + aNewPos, std::begin( m_cols ) + aCol, std::begin( m_cols ) + aCol + 1 );
256 }
257}
258
259
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 fieldsStore.erase( m_cols[aCol].m_fieldName );
270
271 m_cols.erase( m_cols.begin() + aCol );
273 if( m_sortColumn == aCol )
275 else if( m_sortColumn > aCol )
276 m_sortColumn--;
278 if( auto attrIt = m_colAttrs.find( aCol ); attrIt != m_colAttrs.end() )
279 {
280 wxSafeDecRef( attrIt->second );
281 m_colAttrs.erase( attrIt );
282 }
283
284 std::map<int, wxGridCellAttr*> shiftedColAttrs;
285
286 for( const auto& [col, attr] : m_colAttrs )
287 shiftedColAttrs[col > aCol ? col - 1 : col] = attr;
288
289 m_colAttrs.swap( shiftedColAttrs );
290
291 if( wxGrid* grid = GetView() )
292 {
293 wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_COLS_DELETED, aCol, 1 );
294 grid->ProcessTableMessage( msg );
295 }
296
297 m_edited = true;
298}
299
300
301void FIELDS_TABLE_DATA_MODEL_BASE::RenameColumn( int aCol, const wxString& newName )
302{
303 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
304
306
307 for( auto& [unused, fieldsStore] : m_dataStore )
308 {
309 auto node = fieldsStore.extract( m_cols[aCol].m_fieldName );
310
311 if( !node.empty() )
312 {
313 node.key() = newName;
314 fieldsStore.insert( std::move( node ) );
315 }
316 }
317
318 m_cols[aCol].m_fieldName = newName;
319 m_cols[aCol].m_label = newName;
320 m_edited = true;
321}
322
323
324void FIELDS_TABLE_DATA_MODEL_BASE::SetColLabelValue( int aCol, const wxString& aLabel )
325{
326 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
327 m_cols[aCol].m_label = aLabel;
328}
329
330
332{
333 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), wxString() );
334 return m_cols[aCol].m_label;
335}
336
337
339{
340 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), wxString() );
341 return m_cols[aCol].m_fieldName;
342}
343
344
346{
347 int width = KIUI::GetTextSize( GetColLabelValue( aCol ), GetView() ).x;
348
349 for( int row = 0; row < GetNumberRows(); ++row )
350 width = std::max( width, KIUI::GetTextSize( GetResolvedValue( row, aCol ), GetView() ).x );
351
352 return width;
353}
354
355
356int FIELDS_TABLE_DATA_MODEL_BASE::GetFieldNameCol( const wxString& aFieldName ) const
357{
358 for( size_t i = 0; i < m_cols.size(); i++ )
359 {
360 if( FieldNamesAreDuplicates( m_cols[i].m_fieldName, aFieldName ) )
361 return static_cast<int>( i );
362 }
363
364 return -1;
365}
366
367
369{
370 std::vector<BOM_FIELD> fields;
371
372 for( const DATA_MODEL_COL& col : m_cols )
373 fields.push_back( { col.m_fieldName, col.m_label, col.m_show, col.m_group } );
374
375 return fields;
376}
377
378
379void FIELDS_TABLE_DATA_MODEL_BASE::SetFieldsOrder( const std::vector<wxString>& aNewOrder )
380{
382
383 size_t foundCount = 0;
384
385 for( const wxString& newField : aNewOrder )
386 {
387 if( foundCount >= m_cols.size() )
388 break;
389
390 for( DATA_MODEL_COL& col : m_cols )
391 {
392 if( col.m_fieldName == newField )
393 {
394 std::swap( m_cols[foundCount], col );
395 foundCount++;
396 break;
397 }
398 }
399 }
400}
401
402
404{
405 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
406 return m_cols[aCol].m_fieldName == GetDefaultFieldName( FIELD_T::REFERENCE, UNTRANSLATED );
407}
408
409
411{
412 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
413 return m_cols[aCol].m_fieldName == QUANTITY_VARIABLE;
414}
415
416
418{
419 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
420 return m_cols[aCol].m_fieldName == ITEM_NUMBER_VARIABLE;
421}
422
423
425{
426 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
427 return m_cols[aCol].m_fieldName == GetDefaultFieldName( FIELD_T::VALUE, UNTRANSLATED );
428}
429
430
432{
433 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
434 return m_cols[aCol].m_fieldName == GetDefaultFieldName( FIELD_T::FOOTPRINT, UNTRANSLATED );
435}
436
437
439{
440 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
441
442 return fieldIsAttribute( m_cols[aCol].m_fieldName );
443}
444
445
447{
448 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
449
450 return fieldIsItemProperty( m_cols[aCol].m_fieldName );
451}
452
453
455{
456 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
457
458 return IsGeneratedField( m_cols[aCol].m_fieldName ) && !ColIsItemProperty( aCol );
459}
460
461
463{
464 // Check if aCol is the first visible column
465 for( int col = 0; col < aCol; ++col )
466 {
467 if( m_cols[col].m_show )
468 return false;
469 }
470
471 return true;
472}
473
474
476{
477 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), true );
478
479 return ColIsItemIdentifier( aCol ) || IsExpanderColumn( aCol ) || ColIsComputed( aCol );
480}
481
482
484{
485 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), true );
486 wxCHECK( aCol >= 0 && aCol < GetNumberCols(), true );
487
488 return ColIsReadOnly( aCol );
489}
490
491
493{
494 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), false );
495 wxCHECK( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false );
496
497 if( IsCellReadOnly( aRow, aCol ) || IsCellClear( aRow, aCol )
498 || IsGeneratedField( m_cols[aCol].m_fieldName ) )
499 {
500 return false;
501 }
502
503 for( FIELD_T fieldId : MANDATORY_FIELDS )
504 {
505 if( m_cols[aCol].m_fieldName == GetDefaultFieldName( fieldId, UNTRANSLATED ) )
506 return false;
507 }
508
509 // Template fields are added by default to symbols, but it's unclear whether or not
510 // that means they should be mandatory. For now, allow them to be cleared.
511
512 return true;
513}
514
515
517{
518 wxCHECK( aRow >= 0 && aRow < GetNumberRows(), false );
519
520 for( int col = 0; col < GetNumberCols(); ++col )
521 {
522 if( IsCellEdited( aRow, col ) )
523 return true;
524 }
525
526 return false;
527}
528
529
530void FIELDS_TABLE_DATA_MODEL_BASE::SetSorting( int aCol, bool aAscending )
531{
532 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
533 m_sortColumn = aCol;
534 m_sortAscending = aAscending;
535}
536
537
542
543
548
549
551{
552 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
553
554 if( ColIsQuantity( aCol ) || ColIsItemNumber( aCol ) )
555 aGroup = false;
556
557 m_cols[aCol].m_group = aGroup;
558}
559
560
562{
563 wxCHECK_MSG( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false, "Invalid Column Number" );
564 return m_cols[aCol].m_group;
565}
566
567
569{
570 wxCHECK_RET( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), "Invalid Column Number" );
571 m_cols[aCol].m_show = aShow;
572}
573
574
576{
577 wxCHECK_MSG( aCol >= 0 && aCol < static_cast<int>( m_cols.size() ), false, "Invalid Column Number" );
578 return m_cols[aCol].m_show;
579}
580
581
582void FIELDS_TABLE_DATA_MODEL_BASE::ApplyBomPreset( const BOM_PRESET& aPreset )
583{
584 // Hide and un-group everything by default
585 for( size_t i = 0; i < m_cols.size(); i++ )
586 {
587 SetShowColumn( i, false );
588 SetGroupColumn( i, false );
589 }
590
591 std::set<wxString> seen;
592 std::vector<wxString> order;
593
594 // Set columns that are present and shown
595 for( const BOM_FIELD& field : aPreset.fieldsOrdered )
596 {
597 // Ignore empty fields
598 if( !field.name || seen.count( field.name ) )
599 continue;
600
601 seen.insert( field.name );
602 order.emplace_back( field.name );
603
604 int col = GetFieldNameCol( field.name );
605
606 // Add any missing fields; if the user doesn't add any data they won't be saved to the
607 // design anyway.
608 if( col == -1 )
609 {
610 AddColumn( field.name, field.label, false );
611 col = GetFieldNameCol( field.name );
612 }
613 else
614 {
615 SetColLabelValue( col, field.label );
616 }
617
618 SetGroupColumn( col, field.groupBy );
619 SetShowColumn( col, field.show );
620 }
621
622 SetGroupingEnabled( aPreset.groupSymbols );
623 SetFieldsOrder( order );
624
625 int sortCol = GetFieldNameCol( aPreset.sortField );
626
627 if( sortCol == -1 )
628 {
629 for( int col = 0; col < GetNumberCols(); ++col )
630 {
631 if( ColIsItemIdentifier( col ) )
632 {
633 sortCol = col;
634 break;
635 }
636 }
637 }
638
639 SetSorting( sortCol, aPreset.sortAsc );
640
641 SetFilter( aPreset.filterString );
642 SetFilterScope( aPreset.filterScope );
643 SetExcludeDNP( aPreset.excludeDNP );
644 SetIncludeExcludedFromBOM( aPreset.includeExcludedFromBOM );
645
646 RebuildRows();
647}
648
649
651{
652 BOM_PRESET current;
653 current.readOnly = false;
654 current.fieldsOrdered = GetFieldsOrdered();
655
656 if( GetSortCol() >= 0 && GetSortCol() < GetNumberCols() )
657 current.sortField = GetColFieldName( GetSortCol() );
658
659 current.sortAsc = GetSortAsc();
660 current.filterString = GetFilter();
661 current.filterScope = GetFilterScope();
662 current.groupSymbols = GetGroupingEnabled();
663 current.excludeDNP = GetExcludeDNP();
664 current.includeExcludedFromBOM = GetIncludeExcludedFromBOM();
665
666 return current;
667}
668
669
671{
672 wxString out;
673
674 if( m_cols.empty() )
675 return out;
676
677 int lastCol = -1;
678
679 // Find the location for the line terminator
680 for( size_t col = 0; col < m_cols.size(); col++ )
681 {
682 if( m_cols[col].m_show )
683 lastCol = static_cast<int>( col );
684 }
685
686 // No shown columns
687 if( lastCol == -1 )
688 return out;
689
690 if( aSettings.includeByteOrderMark )
691 out.Append( wxString::FromUTF8( "\xEF\xBB\xBF" ) );
692
693 auto formatField = [&]( wxString aField, bool aLast ) -> wxString
694 {
695 if( !aSettings.keepLineBreaks )
696 {
697 aField.Replace( wxS( "\r" ), wxS( "" ) );
698 aField.Replace( wxS( "\n" ), wxS( "" ) );
699 }
700
701 if( !aSettings.keepTabs )
702 aField.Replace( wxS( "\t" ), wxS( "" ) );
703
704 if( !aSettings.stringDelimiter.IsEmpty() )
705 {
706 aField.Replace( aSettings.stringDelimiter, aSettings.stringDelimiter + aSettings.stringDelimiter );
707 }
708
709 return aSettings.stringDelimiter + aField + aSettings.stringDelimiter
710 + ( aLast ? wxString( wxS( "\n" ) ) : aSettings.fieldDelimiter );
711 };
712
713 // Column names
714 for( size_t col = 0; col < m_cols.size(); col++ )
715 {
716 if( !m_cols[col].m_show )
717 continue;
718
719 out.Append( formatField( m_cols[col].m_label, col == static_cast<size_t>( lastCol ) ) );
720 }
721
722 // Data rows
723 for( int row = 0; row < GetNumberRows(); row++ )
724 {
725 // Don't output child rows
726 if( GetRowState( row ) == ROW_STATE::EXPANDED_CHILD )
727 continue;
728
729 for( size_t col = 0; col < m_cols.size(); col++ )
730 {
731 if( !m_cols[col].m_show )
732 continue;
733
734 out.Append( formatField(
735 GetExportValue( row, static_cast<int>( col ), aSettings.refDelimiter, aSettings.refRangeDelimiter ),
736 col == static_cast<size_t>( lastCol ) ) );
737 }
738 }
739
740 return out;
741}
742
743
744bool FIELDS_TABLE_DATA_MODEL_BASE::fieldIsAttribute( const wxString& aFieldName ) const
745{
746 return aFieldName == wxS( "${DNP}" ) || aFieldName == wxS( "${EXCLUDE_FROM_BOARD}" )
747 || aFieldName == wxS( "${EXCLUDE_FROM_BOM}" ) || aFieldName == wxS( "${EXCLUDE_FROM_POS_FILES}" )
748 || aFieldName == wxS( "${EXCLUDE_FROM_SIM}" );
749}
750
751
752bool FIELDS_TABLE_DATA_MODEL_BASE::fieldIsItemProperty( const wxString& aFieldName ) const
753{
754 return fieldIsAttribute( aFieldName );
755}
756
757
758wxString FIELDS_TABLE_DATA_MODEL_BASE::getAttributeResolvedValue( const wxString& aFieldName, bool aValue ) const
759{
760 if( !aValue )
761 return wxEmptyString;
762
763 if( aFieldName == wxS( "${DNP}" ) )
764 return wxS( "DNP" );
765 else if( aFieldName == wxS( "${EXCLUDE_FROM_BOARD}" ) )
766 return wxS( "Excluded from board" );
767 else if( aFieldName == wxS( "${EXCLUDE_FROM_BOM}" ) )
768 return wxS( "Excluded from BOM" );
769 else if( aFieldName == wxS( "${EXCLUDE_FROM_POS_FILES}" ) )
770 return wxS( "Excluded from position files" );
771 else if( aFieldName == wxS( "${EXCLUDE_FROM_SIM}" ) )
772 return wxS( "Excluded from simulation" );
773
774 return wxEmptyString;
775}
776
777
779{
780 // Serialize the un-applied edit store keyed by symbol identity (sheet path + UUID), so that
781 // restoring it is independent of the current row grouping/order.
782 nlohmann::json j = nlohmann::json::object();
783
784 for( const auto& [key, fields] : m_dataStore )
785 {
786 nlohmann::json jfields = nlohmann::json::object();
787
788 for( const auto& [name, value] : fields )
789 jfields[std::string( name.ToUTF8() )] = std::string( value.ToUTF8() );
790
791 j[std::string( key.AsString().ToUTF8() )] = jfields;
792 }
793
794 return wxString( j.dump() );
795}
796
797
799{
800 nlohmann::json j = nlohmann::json::parse( aState.ToStdString(), nullptr, false );
801
802 if( !j.is_object() )
803 return;
804
805 // We want to wipe out key/value presence so we can properly test for
806 // empty vs. not-present
807 m_dataStore.clear();
808
809 for( auto it = j.begin(); it != j.end(); ++it )
810 {
811 KIID_PATH key( wxString::FromUTF8( it.key().c_str() ) );
812 std::map<wxString, wxString>& fields = m_dataStore[key];
813
814 for( auto fit = it.value().begin(); fit != it.value().end(); ++fit )
815 fields[wxString::FromUTF8( fit.key().c_str() )] =
816 wxString::FromUTF8( fit.value().get<std::string>().c_str() );
817 }
818
819 m_edited = true;
820 RebuildRows();
821
822 if( GetView() )
823 GetView()->ForceRefresh();
824}
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
bool cellUsesUrlEditor(int aRow, int aCol)
BOM_FILTER_SCOPE GetFilterScope() const
virtual bool IsCellEdited(int aRow, int aCol)=0
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 SerializeUndoState() const override
bool IsExpanderColumn(int aCol) const override
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
bool m_rebuildsEnabled
Items included by the user selection scope.
std::map< KIID_PATH, std::map< wxString, wxString > > m_dataStore
wxString GetValue(int aRow, int aCol) override
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:84
std::map< int, wxGridCellAttr * > m_colAttrs
Definition wx_grid.h:108
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
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:
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.