2023-10-12 13:36:28 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2023 <author>
|
|
|
|
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
* option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2023-02-28 18:37:04 +00:00
|
|
|
#include <sch_reference_list.h>
|
|
|
|
#include <wx/grid.h>
|
|
|
|
|
|
|
|
// The field name in the data model (translated)
|
|
|
|
#define DISPLAY_NAME_COLUMN 0
|
2023-10-12 13:36:28 +00:00
|
|
|
|
2023-02-28 18:37:04 +00:00
|
|
|
// The field name's label for exporting (CSV, etc.)
|
|
|
|
#define LABEL_COLUMN 1
|
|
|
|
#define SHOW_FIELD_COLUMN 2
|
|
|
|
#define GROUP_BY_COLUMN 3
|
2023-10-12 13:36:28 +00:00
|
|
|
|
2023-02-28 18:37:04 +00:00
|
|
|
// The internal field name (untranslated)
|
|
|
|
#define FIELD_NAME_COLUMN 4
|
|
|
|
|
2023-03-27 20:48:37 +00:00
|
|
|
struct BOM_FIELD;
|
2023-03-15 00:03:57 +00:00
|
|
|
struct BOM_PRESET;
|
|
|
|
struct BOM_FMT_PRESET;
|
2023-02-28 18:37:04 +00:00
|
|
|
|
|
|
|
enum GROUP_TYPE
|
|
|
|
{
|
|
|
|
GROUP_SINGLETON,
|
|
|
|
GROUP_COLLAPSED,
|
|
|
|
GROUP_COLLAPSED_DURING_SORT,
|
|
|
|
GROUP_EXPANDED,
|
|
|
|
CHILD_ITEM
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct DATA_MODEL_ROW
|
|
|
|
{
|
|
|
|
DATA_MODEL_ROW( const SCH_REFERENCE& aFirstReference, GROUP_TYPE aType )
|
|
|
|
{
|
2023-08-17 08:06:02 +00:00
|
|
|
m_ItemNumber = 0;
|
2023-02-28 18:37:04 +00:00
|
|
|
m_Refs.push_back( aFirstReference );
|
|
|
|
m_Flag = aType;
|
|
|
|
}
|
|
|
|
|
2023-07-12 19:08:32 +00:00
|
|
|
int m_ItemNumber;
|
2023-02-28 18:37:04 +00:00
|
|
|
GROUP_TYPE m_Flag;
|
|
|
|
std::vector<SCH_REFERENCE> m_Refs;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct DATA_MODEL_COL
|
|
|
|
{
|
|
|
|
wxString m_fieldName;
|
|
|
|
wxString m_label;
|
|
|
|
bool m_userAdded;
|
|
|
|
bool m_show;
|
|
|
|
bool m_group;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class FIELDS_EDITOR_GRID_DATA_MODEL : public wxGridTableBase
|
|
|
|
{
|
|
|
|
public:
|
2023-08-07 19:04:46 +00:00
|
|
|
enum SCOPE : int
|
|
|
|
{
|
|
|
|
SCOPE_ALL = 0,
|
|
|
|
SCOPE_SHEET = 1,
|
|
|
|
SCOPE_SHEET_RECURSIVE = 2
|
|
|
|
};
|
|
|
|
|
2023-03-15 00:03:57 +00:00
|
|
|
FIELDS_EDITOR_GRID_DATA_MODEL( SCH_REFERENCE_LIST& aSymbolsList ) :
|
|
|
|
m_symbolsList( aSymbolsList ), m_edited( false ), m_sortColumn( 0 ),
|
2023-08-07 19:04:46 +00:00
|
|
|
m_sortAscending( false ), m_scope( SCOPE_ALL ), m_groupingEnabled( false ),
|
|
|
|
m_excludeDNP( false ), m_includeExcluded( false ), m_rebuildsEnabled( true )
|
2023-02-28 18:37:04 +00:00
|
|
|
{
|
|
|
|
m_symbolsList.SplitReferences();
|
|
|
|
}
|
|
|
|
|
2023-08-01 14:13:56 +00:00
|
|
|
static const wxString QUANTITY_VARIABLE;
|
|
|
|
static const wxString ITEM_NUMBER_VARIABLE;
|
|
|
|
|
2023-02-28 18:37:04 +00:00
|
|
|
void AddColumn( const wxString& aFieldName, const wxString& aLabel, bool aAddedByUser );
|
|
|
|
void RemoveColumn( int aCol );
|
|
|
|
void RenameColumn( int aCol, const wxString& newName );
|
2023-10-12 13:36:28 +00:00
|
|
|
|
2023-03-01 15:06:00 +00:00
|
|
|
void MoveColumn( int aCol, int aNewPos )
|
|
|
|
{
|
|
|
|
wxCHECK_RET( aCol >= 0 && aCol < (int) m_cols.size(), "Invalid Column Number" );
|
|
|
|
std::swap( m_cols[aCol], m_cols[aNewPos] );
|
|
|
|
}
|
2023-02-28 18:37:04 +00:00
|
|
|
|
|
|
|
int GetNumberRows() override { return (int) m_rows.size(); }
|
|
|
|
int GetNumberCols() override { return (int) m_cols.size(); }
|
|
|
|
|
|
|
|
void SetColLabelValue( int aCol, const wxString& aLabel ) override
|
|
|
|
{
|
2023-03-01 15:06:00 +00:00
|
|
|
wxCHECK_RET( aCol >= 0 && aCol < (int) m_cols.size(), "Invalid Column Number" );
|
2023-02-28 18:37:04 +00:00
|
|
|
m_cols[aCol].m_label = aLabel;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-03-01 15:06:00 +00:00
|
|
|
wxString GetColLabelValue( int aCol ) override
|
|
|
|
{
|
|
|
|
wxCHECK( aCol >= 0 && aCol < (int) m_cols.size(), wxString() );
|
|
|
|
return m_cols[aCol].m_label;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxString GetColFieldName( int aCol )
|
|
|
|
{
|
|
|
|
wxCHECK( aCol >= 0 && aCol < (int) m_cols.size(), wxString() );
|
|
|
|
return m_cols[aCol].m_fieldName;
|
|
|
|
}
|
2023-02-28 18:37:04 +00:00
|
|
|
|
|
|
|
int GetFieldNameCol( wxString aFieldName );
|
|
|
|
|
2023-03-27 20:48:37 +00:00
|
|
|
const std::vector<BOM_FIELD> GetFieldsOrdered();
|
2023-10-12 13:36:28 +00:00
|
|
|
void SetFieldsOrder( const std::vector<wxString>& aNewOrder );
|
2023-02-28 18:37:04 +00:00
|
|
|
|
|
|
|
bool IsEmptyCell( int aRow, int aCol ) override
|
|
|
|
{
|
|
|
|
return false; // don't allow adjacent cell overflow, even if we are actually empty
|
|
|
|
}
|
|
|
|
|
|
|
|
wxString GetValue( int aRow, int aCol ) override;
|
2023-03-15 00:03:57 +00:00
|
|
|
wxString GetValue( const DATA_MODEL_ROW& group, int aCol,
|
|
|
|
const wxString& refDelimiter = wxT( ", " ),
|
2023-03-30 14:17:09 +00:00
|
|
|
const wxString& refRangDelimiter = wxT( "-" ),
|
|
|
|
bool resolveVars = false );
|
2023-10-12 13:36:28 +00:00
|
|
|
|
2023-03-30 14:17:09 +00:00
|
|
|
wxString GetExportValue( int aRow, int aCol, const wxString& refDelimiter,
|
|
|
|
const wxString& refRangeDelimiter )
|
2023-03-03 13:43:40 +00:00
|
|
|
{
|
2023-03-30 14:17:09 +00:00
|
|
|
return GetValue( m_rows[aRow], aCol, refDelimiter, refRangeDelimiter, true );
|
2023-03-03 13:43:40 +00:00
|
|
|
}
|
2023-10-12 13:36:28 +00:00
|
|
|
|
2023-02-28 18:37:04 +00:00
|
|
|
void SetValue( int aRow, int aCol, const wxString& aValue ) override;
|
|
|
|
|
|
|
|
GROUP_TYPE GetRowFlags( int aRow ) { return m_rows[aRow].m_Flag; }
|
|
|
|
|
|
|
|
std::vector<SCH_REFERENCE> GetRowReferences( int aRow ) const
|
|
|
|
{
|
2023-03-01 15:06:00 +00:00
|
|
|
wxCHECK( aRow >= 0 && aRow < (int) m_rows.size(), std::vector<SCH_REFERENCE>() );
|
2023-02-28 18:37:04 +00:00
|
|
|
return m_rows[aRow].m_Refs;
|
|
|
|
}
|
|
|
|
|
2023-03-16 13:53:37 +00:00
|
|
|
bool ColIsReference( int aCol );
|
2023-09-20 13:40:48 +00:00
|
|
|
bool ColIsValue( int aCol );
|
2023-03-16 13:53:37 +00:00
|
|
|
bool ColIsQuantity( int aCol );
|
2023-07-12 19:08:32 +00:00
|
|
|
bool ColIsItemNumber( int aCol );
|
2023-08-01 18:29:25 +00:00
|
|
|
bool ColIsAttribute( int aCol );
|
2023-02-28 18:37:04 +00:00
|
|
|
|
2023-03-01 15:06:00 +00:00
|
|
|
void SetSorting( int aCol, bool ascending )
|
|
|
|
{
|
|
|
|
wxCHECK_RET( aCol >= 0 && aCol < (int) m_cols.size(), "Invalid Column Number" );
|
|
|
|
m_sortColumn = aCol;
|
|
|
|
m_sortAscending = ascending;
|
|
|
|
}
|
2023-10-12 13:36:28 +00:00
|
|
|
|
2023-03-15 00:03:57 +00:00
|
|
|
int GetSortCol() { return m_sortColumn; }
|
|
|
|
bool GetSortAsc() { return m_sortAscending; }
|
2023-02-28 18:37:04 +00:00
|
|
|
|
2023-06-17 16:56:00 +00:00
|
|
|
// These are used to disable the RebuildRows functionality while we're generating
|
|
|
|
// lots of events in the UI, e.g. applying a BOM preset, that would thrash the grid.
|
|
|
|
void EnableRebuilds();
|
|
|
|
void DisableRebuilds();
|
2023-03-01 15:06:00 +00:00
|
|
|
void RebuildRows();
|
2023-06-17 16:56:00 +00:00
|
|
|
|
2023-02-28 18:37:04 +00:00
|
|
|
void ExpandRow( int aRow );
|
|
|
|
void CollapseRow( int aRow );
|
|
|
|
void ExpandCollapseRow( int aRow );
|
|
|
|
void CollapseForSort();
|
|
|
|
void ExpandAfterSort();
|
|
|
|
|
2023-03-15 00:03:57 +00:00
|
|
|
void ApplyData( std::function<void( SCH_SYMBOL&, SCH_SHEET_PATH& )> symbolChangeHandler );
|
2023-02-28 18:37:04 +00:00
|
|
|
|
|
|
|
bool IsEdited() { return m_edited; }
|
|
|
|
|
|
|
|
int GetDataWidth( int aCol );
|
|
|
|
|
2023-03-01 15:06:00 +00:00
|
|
|
void SetFilter( const wxString& aFilter ) { m_filter = aFilter; }
|
2023-03-15 00:03:57 +00:00
|
|
|
const wxString& GetFilter() { return m_filter; }
|
|
|
|
|
2023-08-07 19:04:46 +00:00
|
|
|
void SetScope( SCOPE aScope ) { m_scope = aScope; }
|
|
|
|
SCOPE GetScope() { return m_scope; }
|
|
|
|
|
|
|
|
void SetPath( const SCH_SHEET_PATH& aPath ) { m_path = aPath; }
|
|
|
|
const SCH_SHEET_PATH& GetPath() { return m_path; }
|
|
|
|
|
2023-03-01 15:06:00 +00:00
|
|
|
void SetGroupingEnabled( bool group ) { m_groupingEnabled = group; }
|
2023-03-15 00:03:57 +00:00
|
|
|
bool GetGroupingEnabled() { return m_groupingEnabled; }
|
|
|
|
|
2023-03-30 14:17:09 +00:00
|
|
|
/* These contradictorily named functions force including symbols that
|
|
|
|
* have the Exclude from BOM check box ticked. This is needed so we can view
|
|
|
|
* these parts in the symbol fields table dialog, while also excluding from the
|
|
|
|
* BOM export */
|
|
|
|
void SetIncludeExcludedFromBOM( bool include ) { m_includeExcluded = include; }
|
|
|
|
bool GetIncludeExcludedFromBOM() { return m_includeExcluded; }
|
|
|
|
|
|
|
|
void SetExcludeDNP( bool exclude ) { m_excludeDNP = exclude; }
|
|
|
|
bool GetExcludeDNP() { return m_excludeDNP; }
|
|
|
|
|
2023-03-01 15:06:00 +00:00
|
|
|
void SetGroupColumn( int aCol, bool group )
|
|
|
|
{
|
|
|
|
wxCHECK_RET( aCol >= 0 && aCol < (int) m_cols.size(), "Invalid Column Number" );
|
|
|
|
m_cols[aCol].m_group = group;
|
|
|
|
}
|
2023-10-12 13:36:28 +00:00
|
|
|
|
2023-03-15 00:03:57 +00:00
|
|
|
bool GetGroupColumn( int aCol )
|
|
|
|
{
|
|
|
|
wxCHECK_MSG( aCol >= 0 && aCol < (int) m_cols.size(), false, "Invalid Column Number" );
|
|
|
|
return m_cols[aCol].m_group;
|
|
|
|
}
|
2023-03-01 15:06:00 +00:00
|
|
|
|
|
|
|
void SetShowColumn( int aCol, bool show )
|
|
|
|
{
|
|
|
|
wxCHECK_RET( aCol >= 0 && aCol < (int) m_cols.size(), "Invalid Column Number" );
|
|
|
|
m_cols[aCol].m_show = show;
|
|
|
|
}
|
2023-10-12 13:36:28 +00:00
|
|
|
|
2023-03-15 00:03:57 +00:00
|
|
|
bool GetShowColumn( int aCol )
|
|
|
|
{
|
|
|
|
wxCHECK_MSG( aCol >= 0 && aCol < (int) m_cols.size(), false, "Invalid Column Number" );
|
|
|
|
return m_cols[aCol].m_show;
|
|
|
|
}
|
2023-03-01 15:06:00 +00:00
|
|
|
|
2023-03-15 00:03:57 +00:00
|
|
|
void ApplyBomPreset( const BOM_PRESET& preset );
|
2023-04-01 12:57:59 +00:00
|
|
|
BOM_PRESET GetBomSettings();
|
2023-03-09 15:54:57 +00:00
|
|
|
wxString Export( const BOM_FMT_PRESET& settings );
|
2023-03-03 13:43:40 +00:00
|
|
|
|
2023-08-14 18:19:00 +00:00
|
|
|
void AddReferences( const SCH_REFERENCE_LIST& aRefs );
|
|
|
|
void RemoveReferences( const SCH_REFERENCE_LIST& aRefs );
|
2023-08-14 14:41:28 +00:00
|
|
|
void RemoveSymbol( const SCH_SYMBOL& aSymbol );
|
2023-08-14 18:19:00 +00:00
|
|
|
void UpdateReferences( const SCH_REFERENCE_LIST& aRefs );
|
2023-08-14 14:41:28 +00:00
|
|
|
|
|
|
|
|
2023-02-28 18:37:04 +00:00
|
|
|
private:
|
|
|
|
static bool cmp( const DATA_MODEL_ROW& lhGroup, const DATA_MODEL_ROW& rhGroup,
|
|
|
|
FIELDS_EDITOR_GRID_DATA_MODEL* dataModel, int sortCol, bool ascending );
|
|
|
|
bool unitMatch( const SCH_REFERENCE& lhRef, const SCH_REFERENCE& rhRef );
|
2023-03-01 15:06:00 +00:00
|
|
|
bool groupMatch( const SCH_REFERENCE& lhRef, const SCH_REFERENCE& rhRef );
|
|
|
|
|
2023-08-01 18:29:25 +00:00
|
|
|
// Helper functions to deal with translating wxGrid values to and from
|
|
|
|
// named field values like ${DNP}
|
|
|
|
bool isAttribute( const wxString& aFieldName );
|
2023-08-14 14:36:18 +00:00
|
|
|
wxString getAttributeValue( const SCH_SYMBOL&, const wxString& aAttributeName );
|
|
|
|
void setAttributeValue( SCH_SYMBOL& aSymbol, const wxString& aAttributeName,
|
2023-08-01 18:29:25 +00:00
|
|
|
const wxString& aValue );
|
|
|
|
|
2023-07-12 20:40:08 +00:00
|
|
|
/* Helper function to get the resolved field value.
|
|
|
|
* Handles symbols that are missing fields that would have a variable
|
|
|
|
* in their value because their name is the same as a variable.
|
|
|
|
* Example: BOM template provides ${DNP} as a field, but they symbol doesn't have the field. */
|
|
|
|
wxString getFieldShownText( const SCH_REFERENCE& aRef, const wxString& aFieldName );
|
|
|
|
|
2023-03-01 15:06:00 +00:00
|
|
|
void Sort();
|
|
|
|
|
2023-08-14 14:41:28 +00:00
|
|
|
SCH_REFERENCE_LIST getSymbolReferences( SCH_SYMBOL* aSymbol );
|
|
|
|
void storeReferenceFields( SCH_REFERENCE& aRef );
|
2023-08-14 14:36:18 +00:00
|
|
|
void updateDataStoreSymbolField( const SCH_SYMBOL& aSymbol, const wxString& aFieldName );
|
|
|
|
|
2023-02-28 18:37:04 +00:00
|
|
|
protected:
|
|
|
|
SCH_REFERENCE_LIST m_symbolsList;
|
|
|
|
bool m_edited;
|
|
|
|
int m_sortColumn;
|
|
|
|
bool m_sortAscending;
|
2023-03-01 15:06:00 +00:00
|
|
|
wxString m_filter;
|
2023-08-07 19:04:46 +00:00
|
|
|
enum SCOPE m_scope;
|
|
|
|
SCH_SHEET_PATH m_path;
|
2023-03-01 15:06:00 +00:00
|
|
|
bool m_groupingEnabled;
|
2023-03-30 14:17:09 +00:00
|
|
|
bool m_excludeDNP;
|
|
|
|
bool m_includeExcluded;
|
2023-06-17 16:56:00 +00:00
|
|
|
bool m_rebuildsEnabled;
|
2023-02-28 18:37:04 +00:00
|
|
|
|
|
|
|
std::vector<DATA_MODEL_COL> m_cols;
|
|
|
|
std::vector<DATA_MODEL_ROW> m_rows;
|
|
|
|
|
|
|
|
// Data store
|
|
|
|
// The data model is fundamentally m_componentRefs X m_fieldNames.
|
|
|
|
// A map of compID : fieldSet, where fieldSet is a map of fieldName : fieldValue
|
|
|
|
std::map<KIID, std::map<wxString, wxString>> m_dataStore;
|
|
|
|
};
|