Add expand/collapse to grouped rows in Fields Editor.
Fixes: lp:1767746 * https://bugs.launchpad.net/kicad/+bug/1767746
This commit is contained in:
parent
ba1e0efdad
commit
9311393016
|
@ -41,18 +41,39 @@
|
|||
#include "dialog_fields_editor_global.h"
|
||||
|
||||
|
||||
enum GROUP_TYPE
|
||||
{
|
||||
GROUP_SINGLETON,
|
||||
GROUP_COLLAPSED,
|
||||
GROUP_EXPANDED,
|
||||
CHILD_ITEM
|
||||
};
|
||||
|
||||
struct DATA_MODEL_ROW
|
||||
{
|
||||
DATA_MODEL_ROW( SCH_REFERENCE aFirstReference, GROUP_TYPE aType )
|
||||
{
|
||||
m_Refs.push_back( aFirstReference );
|
||||
m_Flag = aType;
|
||||
}
|
||||
|
||||
GROUP_TYPE m_Flag;
|
||||
std::vector<SCH_REFERENCE> m_Refs;
|
||||
};
|
||||
|
||||
|
||||
#define FIELD_NAME_COLUMN 0
|
||||
#define SHOW_FIELD_COLUMN 1
|
||||
#define GROUP_BY_COLUMN 2
|
||||
|
||||
#define QUANTITY_COLUMN ( GetNumberCols() - 1 )
|
||||
|
||||
#ifdef __WXMAC__
|
||||
#define CHECKBOX_COLUMN_MARGIN 5
|
||||
#else
|
||||
#define CHECKBOX_COLUMN_MARGIN 15
|
||||
#endif
|
||||
|
||||
#define QUANTITY_COLUMN ( GetNumberCols() - 1 )
|
||||
|
||||
|
||||
// Indicator that multiple values exist in child rows
|
||||
#define ROW_MULT_ITEMS wxString( "< ... >" )
|
||||
|
@ -74,7 +95,7 @@ protected:
|
|||
// when the groupings change), and we let the wxGrid handle (2) (ie: the number
|
||||
// of columns is constant but are hidden/shown by the wxGrid control).
|
||||
|
||||
std::vector< std::vector<SCH_REFERENCE> > m_rows;
|
||||
std::vector< DATA_MODEL_ROW > m_rows;
|
||||
|
||||
// Data store
|
||||
// A map of compID : fieldSet, where fieldSet is a map of fieldName : fieldValue
|
||||
|
@ -129,12 +150,12 @@ public:
|
|||
}
|
||||
|
||||
|
||||
wxString GetValue( std::vector<SCH_REFERENCE>& group, int aCol )
|
||||
wxString GetValue( DATA_MODEL_ROW& group, int aCol )
|
||||
{
|
||||
std::vector<wxString> rootReferences;
|
||||
wxString fieldValue;
|
||||
|
||||
for( const auto& ref : group )
|
||||
for( const auto& ref : group.m_Refs )
|
||||
{
|
||||
if( aCol == REFERENCE || aCol == QUANTITY_COLUMN )
|
||||
{
|
||||
|
@ -144,7 +165,7 @@ public:
|
|||
{
|
||||
timestamp_t compID = ref.GetComp()->GetTimeStamp();
|
||||
|
||||
if( &ref == &group.front() )
|
||||
if( &ref == &group.m_Refs.front() )
|
||||
fieldValue = m_dataStore[ compID ][ m_fieldNames[ aCol ] ];
|
||||
else if ( fieldValue != m_dataStore[ compID ][ m_fieldNames[ aCol ] ] )
|
||||
return ROW_MULT_ITEMS;
|
||||
|
@ -174,6 +195,16 @@ public:
|
|||
fieldValue += wxT( ", " );
|
||||
fieldValue += ref;
|
||||
}
|
||||
|
||||
// Poor-man's tree controls
|
||||
if( group.m_Flag == GROUP_COLLAPSED )
|
||||
fieldValue = wxT( "> " ) + fieldValue;
|
||||
else if (group.m_Flag == GROUP_EXPANDED )
|
||||
fieldValue = wxT( "v " ) + fieldValue;
|
||||
else if( group.m_Flag == CHILD_ITEM )
|
||||
fieldValue = wxT( " " ) + fieldValue;
|
||||
else
|
||||
fieldValue = wxT( " " ) + fieldValue;
|
||||
}
|
||||
else if( aCol == QUANTITY_COLUMN )
|
||||
{
|
||||
|
@ -189,34 +220,34 @@ public:
|
|||
if( aCol == REFERENCE || aCol == QUANTITY_COLUMN )
|
||||
return; // Can't modify references or quantity
|
||||
|
||||
std::vector<SCH_REFERENCE>& rowGroup = m_rows[ aRow ];
|
||||
DATA_MODEL_ROW& rowGroup = m_rows[ aRow ];
|
||||
wxString fieldName = m_fieldNames[ aCol ];
|
||||
|
||||
for( const auto& ref : rowGroup )
|
||||
for( const auto& ref : rowGroup.m_Refs )
|
||||
m_dataStore[ ref.GetComp()->GetTimeStamp() ][ fieldName ] = aValue;
|
||||
}
|
||||
|
||||
|
||||
static bool cmp( const std::vector<SCH_REFERENCE>& lhGroup, const std::vector<SCH_REFERENCE>& rhGroup,
|
||||
static bool cmp( const DATA_MODEL_ROW& lhGroup, const DATA_MODEL_ROW& rhGroup,
|
||||
FIELDS_EDITOR_GRID_DATA_MODEL* dataModel, int sortCol, bool ascending )
|
||||
{
|
||||
// Empty rows always go to the bottom, whether ascending or descending
|
||||
if( lhGroup.size() == 0 )
|
||||
if( lhGroup.m_Refs.size() == 0 )
|
||||
return true;
|
||||
else if( rhGroup.size() == 0 )
|
||||
else if( rhGroup.m_Refs.size() == 0 )
|
||||
return false;
|
||||
|
||||
bool retVal;
|
||||
|
||||
// Primary sort key is sortCol; secondary is always REFERENCE (column 0)
|
||||
|
||||
wxString lhs = dataModel->GetValue( (std::vector<SCH_REFERENCE>&)lhGroup, sortCol );
|
||||
wxString rhs = dataModel->GetValue( (std::vector<SCH_REFERENCE>&)rhGroup, sortCol );
|
||||
wxString lhs = dataModel->GetValue( (DATA_MODEL_ROW&) lhGroup, sortCol );
|
||||
wxString rhs = dataModel->GetValue( (DATA_MODEL_ROW&) rhGroup, sortCol );
|
||||
|
||||
if( lhs == rhs || sortCol == REFERENCE )
|
||||
{
|
||||
wxString lhRef = lhGroup[ 0 ].GetRef() + lhGroup[ 0 ].GetRefNumber();
|
||||
wxString rhRef = rhGroup[ 0 ].GetRef() + rhGroup[ 0 ].GetRefNumber();
|
||||
wxString lhRef = lhGroup.m_Refs[ 0 ].GetRef() + lhGroup.m_Refs[ 0 ].GetRefNumber();
|
||||
wxString rhRef = rhGroup.m_Refs[ 0 ].GetRef() + rhGroup.m_Refs[ 0 ].GetRefNumber();
|
||||
retVal = RefDesStringCompare( lhRef, rhRef ) < 0;
|
||||
}
|
||||
else
|
||||
|
@ -234,27 +265,30 @@ public:
|
|||
if( aColumn < 0 )
|
||||
aColumn = 0;
|
||||
|
||||
CollapseAll();
|
||||
|
||||
std::sort( m_rows.begin(), m_rows.end(),
|
||||
[ this, aColumn, ascending ]( const std::vector<SCH_REFERENCE>& lhs,
|
||||
const std::vector<SCH_REFERENCE>& rhs ) -> bool
|
||||
[ this, aColumn, ascending ]( const DATA_MODEL_ROW& lhs, const DATA_MODEL_ROW& rhs ) -> bool
|
||||
{
|
||||
return cmp( lhs, rhs, this, aColumn, ascending );
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
bool match( const SCH_REFERENCE& lhRef, const SCH_REFERENCE& rhRef,
|
||||
wxCheckBox* groupComponentsBox, wxDataViewListCtrl* fieldsCtrl )
|
||||
bool unitMatch( const SCH_REFERENCE& lhRef, const SCH_REFERENCE& rhRef )
|
||||
{
|
||||
// Units of same component always match
|
||||
if( lhRef.GetRef() == rhRef.GetRef() && lhRef.GetRefNumber() != wxT( "?" )
|
||||
&& lhRef.GetRefNumber() == rhRef.GetRefNumber() )
|
||||
return true;
|
||||
|
||||
// If we're not grouping, then nothing else matches
|
||||
if( !groupComponentsBox->GetValue() )
|
||||
// If items are unannotated then we can't tell if they're units of the same
|
||||
// component or not
|
||||
if( lhRef.GetRefNumber() != wxT( "?" ) )
|
||||
return false;
|
||||
|
||||
return ( lhRef.GetRef() == rhRef.GetRef() && lhRef.GetRefNumber() == rhRef.GetRefNumber() );
|
||||
}
|
||||
|
||||
|
||||
bool groupMatch( const SCH_REFERENCE& lhRef, const SCH_REFERENCE& rhRef,
|
||||
wxDataViewListCtrl* fieldsCtrl )
|
||||
{
|
||||
bool matchFound = false;
|
||||
|
||||
// First check the reference column. This can be done directly out of the
|
||||
|
@ -306,33 +340,37 @@ public:
|
|||
|
||||
for( unsigned i = 0; i < m_componentRefs.GetCount(); ++i )
|
||||
{
|
||||
SCH_REFERENCE compRef = m_componentRefs[ i ];
|
||||
SCH_REFERENCE ref = m_componentRefs[ i ];
|
||||
bool matchFound = false;
|
||||
|
||||
// See if we already have a group which this component fits into
|
||||
for( auto& rowGroup : m_rows )
|
||||
// See if we already have a row which this component fits into
|
||||
for( auto& row : m_rows )
|
||||
{
|
||||
// group members are by definition all matching, so just check
|
||||
// against the first member
|
||||
if( match( rowGroup[ 0 ], compRef, groupComponentsBox, fieldsCtrl ) )
|
||||
// all group members must have identical refs so just use the first one
|
||||
SCH_REFERENCE rowRef = row.m_Refs[ 0 ];
|
||||
|
||||
if( unitMatch( ref, rowRef ) )
|
||||
{
|
||||
matchFound = true;
|
||||
rowGroup.push_back( compRef );
|
||||
row.m_Refs.push_back( ref );
|
||||
break;
|
||||
}
|
||||
else if (groupComponentsBox->GetValue() && groupMatch( ref, rowRef, fieldsCtrl ) )
|
||||
{
|
||||
matchFound = true;
|
||||
row.m_Refs.push_back( ref );
|
||||
row.m_Flag = GROUP_COLLAPSED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !matchFound )
|
||||
{
|
||||
std::vector<SCH_REFERENCE> newGroup;
|
||||
newGroup.push_back( compRef );
|
||||
m_rows.push_back( newGroup );
|
||||
}
|
||||
m_rows.push_back( DATA_MODEL_ROW( ref, GROUP_SINGLETON ) );
|
||||
}
|
||||
|
||||
for( auto& rowGroup : m_rows )
|
||||
for( auto& row : m_rows )
|
||||
{
|
||||
std::sort( rowGroup.begin(), rowGroup.end(),
|
||||
std::sort( row.m_Refs.begin(), row.m_Refs.end(),
|
||||
[]( const SCH_REFERENCE& lhs, const SCH_REFERENCE& rhs ) -> bool
|
||||
{
|
||||
wxString lhRef( lhs.GetRef() << lhs.GetRefNumber() );
|
||||
|
@ -349,6 +387,83 @@ public:
|
|||
}
|
||||
|
||||
|
||||
void ExpandRow( int aRow )
|
||||
{
|
||||
std::vector<DATA_MODEL_ROW> children;
|
||||
|
||||
for( auto& ref : m_rows[ aRow ].m_Refs )
|
||||
{
|
||||
bool matchFound = false;
|
||||
|
||||
// See if we already have a child group which this component fits into
|
||||
for( auto& child : children )
|
||||
{
|
||||
// group members are by definition all matching, so just check
|
||||
// against the first member
|
||||
if( unitMatch( ref, child.m_Refs[ 0 ] ) )
|
||||
{
|
||||
matchFound = true;
|
||||
child.m_Refs.push_back( ref );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !matchFound )
|
||||
children.push_back( DATA_MODEL_ROW( ref, CHILD_ITEM ) );
|
||||
}
|
||||
|
||||
if( children.size() < 2 )
|
||||
return;
|
||||
|
||||
m_rows[ aRow ].m_Flag = GROUP_EXPANDED;
|
||||
m_rows.insert( m_rows.begin() + aRow + 1, children.begin(), children.end() );
|
||||
|
||||
wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, aRow, children.size() );
|
||||
GetView()->ProcessTableMessage( msg );
|
||||
}
|
||||
|
||||
|
||||
void CollapseRow( int aRow )
|
||||
{
|
||||
auto firstChild = m_rows.begin() + aRow + 1;
|
||||
auto afterLastChild = firstChild;
|
||||
int deleted = 0;
|
||||
|
||||
while( afterLastChild != m_rows.end() && afterLastChild->m_Flag == CHILD_ITEM )
|
||||
{
|
||||
deleted++;
|
||||
afterLastChild++;
|
||||
}
|
||||
|
||||
m_rows[ aRow ].m_Flag = GROUP_COLLAPSED;
|
||||
m_rows.erase( firstChild, afterLastChild );
|
||||
|
||||
wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, aRow + 1, deleted );
|
||||
GetView()->ProcessTableMessage( msg );
|
||||
}
|
||||
|
||||
|
||||
void ExpandCollapseRow( int aRow )
|
||||
{
|
||||
DATA_MODEL_ROW& group = m_rows[ aRow ];
|
||||
|
||||
if( group.m_Flag == GROUP_COLLAPSED )
|
||||
ExpandRow( aRow );
|
||||
else if( group.m_Flag == GROUP_EXPANDED )
|
||||
CollapseRow( aRow );
|
||||
}
|
||||
|
||||
|
||||
void CollapseAll()
|
||||
{
|
||||
for( int i = 0; i < m_rows.size(); ++i )
|
||||
{
|
||||
if( m_rows[ i ].m_Flag == GROUP_EXPANDED )
|
||||
CollapseRow( i );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ApplyData( SCH_EDIT_FRAME* aParent )
|
||||
{
|
||||
for( unsigned i = 0; i < m_componentRefs.GetCount(); ++i )
|
||||
|
@ -603,6 +718,15 @@ void DIALOG_FIELDS_EDITOR_GLOBAL::OnRegroupComponents( wxCommandEvent& event )
|
|||
}
|
||||
|
||||
|
||||
void DIALOG_FIELDS_EDITOR_GLOBAL::OnTableCellClick( wxGridEvent& event )
|
||||
{
|
||||
if( event.GetCol() == REFERENCE )
|
||||
m_dataModel->ExpandCollapseRow( event.GetRow());
|
||||
else
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
|
||||
void DIALOG_FIELDS_EDITOR_GLOBAL::OnTableItemContextMenu( wxGridEvent& event )
|
||||
{
|
||||
/* TODO
|
||||
|
|
|
@ -59,6 +59,7 @@ private:
|
|||
void OnGroupComponentsToggled( wxCommandEvent& event ) override;
|
||||
void OnRegroupComponents( wxCommandEvent& event ) override;
|
||||
void OnTableValueChanged( wxGridEvent& event ) override;
|
||||
void OnTableCellClick( wxGridEvent& event ) override;
|
||||
void OnTableItemContextMenu( wxGridEvent& event ) override;
|
||||
void OnSizeFieldList( wxSizeEvent& event ) override;
|
||||
void OnSaveAndContinue( wxCommandEvent& aEvent ) override;
|
||||
|
|
|
@ -125,6 +125,8 @@ DIALOG_FIELDS_EDITOR_GLOBAL_BASE::DIALOG_FIELDS_EDITOR_GLOBAL_BASE( wxWindow* pa
|
|||
m_fieldsCtrl->Connect( wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, wxDataViewEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnColumnItemToggled ), NULL, this );
|
||||
m_fieldsCtrl->Connect( wxEVT_SIZE, wxSizeEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnSizeFieldList ), NULL, this );
|
||||
m_grid->Connect( wxEVT_GRID_CELL_CHANGED, wxGridEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnTableValueChanged ), NULL, this );
|
||||
m_grid->Connect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnTableCellClick ), NULL, this );
|
||||
m_grid->Connect( wxEVT_GRID_CELL_LEFT_DCLICK, wxGridEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnTableCellClick ), NULL, this );
|
||||
m_grid->Connect( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnTableItemContextMenu ), NULL, this );
|
||||
m_button1->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnSaveAndContinue ), NULL, this );
|
||||
m_sdbSizer1Cancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnCancel ), NULL, this );
|
||||
|
@ -139,6 +141,8 @@ DIALOG_FIELDS_EDITOR_GLOBAL_BASE::~DIALOG_FIELDS_EDITOR_GLOBAL_BASE()
|
|||
m_fieldsCtrl->Disconnect( wxEVT_COMMAND_DATAVIEW_ITEM_VALUE_CHANGED, wxDataViewEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnColumnItemToggled ), NULL, this );
|
||||
m_fieldsCtrl->Disconnect( wxEVT_SIZE, wxSizeEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnSizeFieldList ), NULL, this );
|
||||
m_grid->Disconnect( wxEVT_GRID_CELL_CHANGED, wxGridEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnTableValueChanged ), NULL, this );
|
||||
m_grid->Disconnect( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnTableCellClick ), NULL, this );
|
||||
m_grid->Disconnect( wxEVT_GRID_CELL_LEFT_DCLICK, wxGridEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnTableCellClick ), NULL, this );
|
||||
m_grid->Disconnect( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnTableItemContextMenu ), NULL, this );
|
||||
m_button1->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnSaveAndContinue ), NULL, this );
|
||||
m_sdbSizer1Cancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_FIELDS_EDITOR_GLOBAL_BASE::OnCancel ), NULL, this );
|
||||
|
|
|
@ -704,8 +704,8 @@
|
|||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnGridCellChange">OnTableValueChanged</event>
|
||||
<event name="OnGridCellLeftClick"></event>
|
||||
<event name="OnGridCellLeftDClick"></event>
|
||||
<event name="OnGridCellLeftClick">OnTableCellClick</event>
|
||||
<event name="OnGridCellLeftDClick">OnTableCellClick</event>
|
||||
<event name="OnGridCellRightClick">OnTableItemContextMenu</event>
|
||||
<event name="OnGridCellRightDClick"></event>
|
||||
<event name="OnGridCmdCellChange"></event>
|
||||
|
|
|
@ -60,6 +60,7 @@ class DIALOG_FIELDS_EDITOR_GLOBAL_BASE : public DIALOG_SHIM
|
|||
virtual void OnColumnItemToggled( wxDataViewEvent& event ) { event.Skip(); }
|
||||
virtual void OnSizeFieldList( wxSizeEvent& event ) { event.Skip(); }
|
||||
virtual void OnTableValueChanged( wxGridEvent& event ) { event.Skip(); }
|
||||
virtual void OnTableCellClick( wxGridEvent& event ) { event.Skip(); }
|
||||
virtual void OnTableItemContextMenu( wxGridEvent& event ) { event.Skip(); }
|
||||
virtual void OnSaveAndContinue( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
|
||||
|
|
Loading…
Reference in New Issue