882 lines
28 KiB
C++
882 lines
28 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2024 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/>.
|
|
*/
|
|
|
|
#ifndef PCB_NET_INSPECTOR_PANEL_DATA_MODEL
|
|
#define PCB_NET_INSPECTOR_PANEL_DATA_MODEL
|
|
|
|
#include <eda_pattern_match.h>
|
|
|
|
|
|
/**
|
|
* Primary data item for entries in the Net Inspector list.
|
|
*
|
|
* This class tracks all data for a given net entry in the net inspector list.
|
|
*/
|
|
class PCB_NET_INSPECTOR_PANEL::LIST_ITEM
|
|
{
|
|
public:
|
|
enum class GROUP_TYPE
|
|
{
|
|
NONE,
|
|
USER_DEFINED,
|
|
NETCLASS
|
|
};
|
|
|
|
LIST_ITEM( unsigned int aGroupNumber, const wxString& aGroupName, GROUP_TYPE aGroupType ) :
|
|
m_group_type( aGroupType ),
|
|
m_group_number( aGroupNumber ),
|
|
m_net_name( aGroupName )
|
|
{
|
|
m_group_name = aGroupName;
|
|
m_column_changed.resize( COLUMN_LAST_STATIC_COL + 1 + MAX_CU_LAYERS, 0 );
|
|
}
|
|
|
|
LIST_ITEM( NETINFO_ITEM* aNet ) :
|
|
m_group_type( GROUP_TYPE::NONE ),
|
|
m_net( aNet )
|
|
{
|
|
wxASSERT( aNet );
|
|
m_net_name = UnescapeString( aNet->GetNetname() );
|
|
m_net_class = UnescapeString( aNet->GetNetClass()->GetName() );
|
|
m_column_changed.resize( COLUMN_LAST_STATIC_COL + 1 + MAX_CU_LAYERS, 0 );
|
|
}
|
|
|
|
LIST_ITEM() { m_column_changed.resize( COLUMN_LAST_STATIC_COL + 1 + MAX_CU_LAYERS, 0 ); }
|
|
|
|
LIST_ITEM& operator=( const LIST_ITEM& ) = delete;
|
|
|
|
bool GetIsGroup() const { return m_group_type != GROUP_TYPE::NONE; }
|
|
const wxString& GetGroupName() const { return m_group_name; }
|
|
GROUP_TYPE GetGroupType() const { return m_group_type; }
|
|
int GetGroupNumber() const { return m_group_number; }
|
|
|
|
auto ChildrenBegin() const { return m_children.begin(); }
|
|
auto ChildrenEnd() const { return m_children.end(); }
|
|
unsigned int ChildrenCount() const { return m_children.size(); }
|
|
|
|
NETINFO_ITEM* GetNet() const { return m_net; }
|
|
|
|
int GetNetCode() const
|
|
{
|
|
return GetIsGroup() ? ( 0 - int( m_group_number ) - 1 ) : m_net->GetNetCode();
|
|
}
|
|
|
|
const wxString& GetNetName() const { return m_net_name; }
|
|
const wxString& GetNetclassName() const { return m_net_class; }
|
|
|
|
void ResetColumnChangedBits()
|
|
{
|
|
std::fill( m_column_changed.begin(), m_column_changed.end(), 0 );
|
|
}
|
|
|
|
unsigned int GetPadCount() const { return m_pad_count; }
|
|
|
|
bool PadCountChanged() const { return m_column_changed[COLUMN_PAD_COUNT]; }
|
|
|
|
void SetPadCount( unsigned int aValue )
|
|
{
|
|
if( m_parent )
|
|
m_parent->SetPadCount( m_parent->GetPadCount() - m_pad_count + aValue );
|
|
|
|
m_column_changed[COLUMN_PAD_COUNT] |= ( m_pad_count != aValue );
|
|
m_pad_count = aValue;
|
|
}
|
|
|
|
void AddPadCount( unsigned int aValue )
|
|
{
|
|
if( m_parent )
|
|
m_parent->AddPadCount( aValue );
|
|
|
|
m_column_changed[COLUMN_PAD_COUNT] |= ( aValue != 0 );
|
|
m_pad_count += aValue;
|
|
}
|
|
|
|
void SubPadCount( unsigned int aValue )
|
|
{
|
|
if( m_parent )
|
|
m_parent->SubPadCount( aValue );
|
|
|
|
m_column_changed[COLUMN_PAD_COUNT] |= ( aValue != 0 );
|
|
m_pad_count -= aValue;
|
|
}
|
|
|
|
unsigned GetViaCount() const { return m_via_count; }
|
|
|
|
bool ViaCountChanged() const { return m_column_changed[COLUMN_VIA_COUNT]; }
|
|
|
|
void SetViaCount( unsigned int aValue )
|
|
{
|
|
if( m_parent )
|
|
m_parent->SetViaCount( m_parent->GetViaCount() - m_via_count + aValue );
|
|
|
|
m_column_changed[COLUMN_VIA_COUNT] |= ( m_via_count != aValue );
|
|
m_via_count = aValue;
|
|
}
|
|
|
|
void AddViaCount( unsigned int aValue )
|
|
{
|
|
if( m_parent )
|
|
m_parent->AddViaCount( aValue );
|
|
|
|
m_column_changed[COLUMN_VIA_COUNT] |= ( aValue != 0 );
|
|
m_via_count += aValue;
|
|
}
|
|
|
|
void SubViaCount( unsigned int aValue )
|
|
{
|
|
if( m_parent )
|
|
m_parent->SubViaCount( aValue );
|
|
|
|
m_column_changed[COLUMN_VIA_COUNT] |= ( aValue != 0 );
|
|
m_via_count -= aValue;
|
|
}
|
|
|
|
uint64_t GetViaLength() const { return m_via_length; }
|
|
|
|
bool ViaLengthChanged() const { return m_column_changed[COLUMN_VIA_LENGTH]; }
|
|
|
|
void SetViaLength( unsigned int aValue )
|
|
{
|
|
if( m_parent )
|
|
m_parent->SetViaLength( m_parent->GetViaLength() - m_via_length + aValue );
|
|
|
|
m_column_changed[COLUMN_VIA_LENGTH] |= ( m_via_length != aValue );
|
|
m_via_length = aValue;
|
|
}
|
|
|
|
void AddViaLength( unsigned int aValue )
|
|
{
|
|
if( m_parent )
|
|
m_parent->AddViaLength( aValue );
|
|
|
|
m_column_changed[COLUMN_VIA_LENGTH] |= ( aValue != 0 );
|
|
m_via_length += aValue;
|
|
}
|
|
|
|
void SubViaLength( uint64_t aValue )
|
|
{
|
|
if( m_parent )
|
|
m_parent->SubViaLength( aValue );
|
|
|
|
m_column_changed[COLUMN_VIA_LENGTH] |= ( aValue != 0 );
|
|
m_via_length -= aValue;
|
|
}
|
|
|
|
uint64_t GetBoardWireLength() const
|
|
{
|
|
uint64_t retval = 0;
|
|
|
|
for( uint64_t val : m_layer_wire_length )
|
|
retval += val;
|
|
|
|
return retval;
|
|
}
|
|
|
|
uint64_t GetLayerWireLength( size_t aLayer ) const
|
|
{
|
|
wxCHECK_MSG( aLayer < m_layer_wire_length.size(), 0, wxT( "Invalid layer specified" ) );
|
|
|
|
return m_layer_wire_length[aLayer];
|
|
}
|
|
|
|
bool BoardWireLengthChanged() const { return m_column_changed[COLUMN_BOARD_LENGTH]; }
|
|
|
|
void SetLayerWireLength( const uint64_t aValue, size_t aLayer )
|
|
{
|
|
wxCHECK_RET( aLayer < m_layer_wire_length.size(), wxT( "Invalid layer specified" ) );
|
|
|
|
if( m_parent )
|
|
{
|
|
m_parent->SetLayerWireLength( m_parent->GetBoardWireLength()
|
|
- m_layer_wire_length[aLayer] + aValue,
|
|
aLayer );
|
|
}
|
|
|
|
m_column_changed[COLUMN_BOARD_LENGTH] |= ( m_layer_wire_length[aLayer] != aValue );
|
|
m_layer_wire_length[aLayer] = aValue;
|
|
}
|
|
|
|
void AddLayerWireLength( const uint64_t aValue, size_t aLayer )
|
|
{
|
|
if( m_parent )
|
|
m_parent->AddLayerWireLength( aValue, aLayer );
|
|
|
|
m_column_changed[COLUMN_BOARD_LENGTH] |= ( m_layer_wire_length[aLayer] != 0 );
|
|
m_layer_wire_length[aLayer] += aValue;
|
|
}
|
|
|
|
void SubLayerWireLength( const uint64_t aValue, size_t aLayer )
|
|
{
|
|
if( m_parent )
|
|
m_parent->SubLayerWireLength( aValue, aLayer );
|
|
|
|
m_column_changed[COLUMN_BOARD_LENGTH] |= ( m_layer_wire_length[aLayer] != 0 );
|
|
m_layer_wire_length[aLayer] -= aValue;
|
|
}
|
|
|
|
uint64_t GetPadDieLength() const { return m_pad_die_length; }
|
|
|
|
bool PadDieLengthChanged() const { return m_column_changed[COLUMN_PAD_DIE_LENGTH]; }
|
|
|
|
void SetPadDieLength( uint64_t aValue )
|
|
{
|
|
if( m_parent )
|
|
m_parent->SetPadDieLength( m_parent->GetPadDieLength() - m_pad_die_length + aValue );
|
|
|
|
m_column_changed[COLUMN_PAD_DIE_LENGTH] |= ( m_pad_die_length != aValue );
|
|
m_pad_die_length = aValue;
|
|
}
|
|
|
|
void AddPadDieLength( uint64_t aValue )
|
|
{
|
|
if( m_parent )
|
|
m_parent->AddPadDieLength( aValue );
|
|
|
|
m_column_changed[COLUMN_PAD_DIE_LENGTH] |= ( aValue != 0 );
|
|
m_pad_die_length += aValue;
|
|
}
|
|
|
|
void SubPadDieLength( uint64_t aValue )
|
|
{
|
|
if( m_parent )
|
|
m_parent->SubPadDieLength( aValue );
|
|
|
|
m_column_changed[COLUMN_PAD_DIE_LENGTH] |= ( aValue != 0 );
|
|
m_pad_die_length -= aValue;
|
|
}
|
|
|
|
// the total length column is always computed, never stored.
|
|
unsigned long long int GetTotalLength() const
|
|
{
|
|
return GetBoardWireLength() + GetViaLength() + GetPadDieLength();
|
|
}
|
|
|
|
bool TotalLengthChanged() const
|
|
{
|
|
return BoardWireLengthChanged() || ViaLengthChanged() || PadDieLengthChanged();
|
|
}
|
|
|
|
LIST_ITEM* Parent() const { return m_parent; }
|
|
|
|
void SetParent( LIST_ITEM* aParent )
|
|
{
|
|
if( m_parent == aParent )
|
|
return;
|
|
|
|
if( m_parent != nullptr )
|
|
{
|
|
m_parent->SubPadCount( GetPadCount() );
|
|
m_parent->SubViaCount( GetViaCount() );
|
|
m_parent->SubViaLength( GetViaLength() );
|
|
|
|
for( size_t ii = 0; ii < m_layer_wire_length.size(); ++ii )
|
|
m_parent->SubLayerWireLength( m_layer_wire_length[ii], ii );
|
|
|
|
m_parent->SubPadDieLength( GetPadDieLength() );
|
|
|
|
m_parent->m_children.erase( std::find( m_parent->m_children.begin(),
|
|
m_parent->m_children.end(), this ) );
|
|
}
|
|
|
|
m_parent = aParent;
|
|
|
|
if( m_parent != nullptr )
|
|
{
|
|
m_parent->AddPadCount( GetPadCount() );
|
|
m_parent->AddViaCount( GetViaCount() );
|
|
m_parent->AddViaLength( GetViaLength() );
|
|
|
|
for( size_t ii = 0; ii < m_layer_wire_length.size(); ++ii )
|
|
m_parent->AddLayerWireLength( m_layer_wire_length[ii], ii );
|
|
|
|
m_parent->AddPadDieLength( GetPadDieLength() );
|
|
|
|
m_parent->m_children.push_back( this );
|
|
}
|
|
}
|
|
|
|
private:
|
|
LIST_ITEM* m_parent = nullptr;
|
|
std::vector<LIST_ITEM*> m_children;
|
|
|
|
GROUP_TYPE m_group_type = GROUP_TYPE::NONE;
|
|
unsigned int m_group_number = 0;
|
|
NETINFO_ITEM* m_net = nullptr;
|
|
unsigned int m_pad_count = 0;
|
|
unsigned int m_via_count = 0;
|
|
uint64_t m_via_length = 0;
|
|
uint64_t m_pad_die_length = 0;
|
|
|
|
std::array<uint64_t, MAX_CU_LAYERS> m_layer_wire_length{};
|
|
|
|
// Dirty bits to record when some attribute has changed, in order to avoid unnecessary sort
|
|
// operations.
|
|
// The values are semantically bools, but STL auto-promotes a std::vector<bool> to a bitset,
|
|
// and then operator|= doesn't work.
|
|
std::vector<int> m_column_changed;
|
|
|
|
// cached formatted names for faster display sorting
|
|
wxString m_net_name;
|
|
wxString m_net_class;
|
|
wxString m_group_name;
|
|
};
|
|
|
|
|
|
struct PCB_NET_INSPECTOR_PANEL::LIST_ITEM_NETCODE_CMP_LESS
|
|
{
|
|
template <typename T>
|
|
bool operator()( const T& a, const T& b ) const
|
|
{
|
|
return a->GetNetCode() < b->GetNetCode();
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator()( const T& a, int b ) const
|
|
{
|
|
return a->GetNetCode() < b;
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator()( int a, const T& b ) const
|
|
{
|
|
return a < b->GetNetCode();
|
|
}
|
|
};
|
|
|
|
|
|
struct PCB_NET_INSPECTOR_PANEL::LIST_ITEM_GROUP_NUMBER_CMP_LESS
|
|
{
|
|
template <typename T>
|
|
bool operator()( const T& a, const T& b ) const
|
|
{
|
|
return a->GetGroupNumber() < b->GetGroupNumber();
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator()( const T& a, int b ) const
|
|
{
|
|
return a->GetGroupNumber() < b;
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator()( int a, const T& b ) const
|
|
{
|
|
return a < b->GetGroupNumber();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Data model for display in the Net Inspector panel.
|
|
*/
|
|
class PCB_NET_INSPECTOR_PANEL::DATA_MODEL : public wxDataViewModel
|
|
{
|
|
public:
|
|
DATA_MODEL( PCB_NET_INSPECTOR_PANEL& parent ) : m_parent( parent ) {}
|
|
|
|
unsigned int columnCount() const { return m_parent.m_columns.size(); }
|
|
|
|
unsigned int itemCount() const { return m_items.size(); }
|
|
|
|
wxVariant valueAt( unsigned int aCol, unsigned int aRow ) const
|
|
{
|
|
wxVariant r;
|
|
GetValue( r, wxDataViewItem( const_cast<LIST_ITEM*>( &*( m_items[aRow] ) ) ), aCol );
|
|
return r;
|
|
}
|
|
|
|
const LIST_ITEM& itemAt( unsigned int aRow ) const { return *m_items.at( aRow ); }
|
|
|
|
std::vector<std::pair<wxString, wxDataViewItem>> getGroupDataViewItems()
|
|
{
|
|
std::vector<std::pair<wxString, wxDataViewItem>> ret;
|
|
|
|
for( std::unique_ptr<LIST_ITEM>& item : m_items )
|
|
{
|
|
if( item->GetIsGroup() )
|
|
{
|
|
ret.push_back( std::make_pair( item->GetGroupName(),
|
|
wxDataViewItem( item.get() ) ) );
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
std::optional<LIST_ITEM_ITER> findItem( int aNetCode )
|
|
{
|
|
auto i = std::lower_bound( m_items.begin(), m_items.end(), aNetCode,
|
|
LIST_ITEM_NETCODE_CMP_LESS() );
|
|
|
|
if( i == m_items.end() || ( *i )->GetNetCode() != aNetCode )
|
|
return std::nullopt;
|
|
|
|
return { i };
|
|
}
|
|
|
|
|
|
std::optional<LIST_ITEM_ITER> findItem( NETINFO_ITEM* aNet )
|
|
{
|
|
if( aNet != nullptr )
|
|
return findItem( aNet->GetNetCode() );
|
|
else
|
|
return std::nullopt;
|
|
}
|
|
|
|
|
|
std::optional<LIST_ITEM_ITER> findGroupItem( int aGroupNumber )
|
|
{
|
|
auto i = std::lower_bound( m_items.begin(), m_items.end(), aGroupNumber,
|
|
LIST_ITEM_GROUP_NUMBER_CMP_LESS() );
|
|
|
|
if( i == m_items.end() || ( *i )->GetGroupNumber() != aGroupNumber )
|
|
return std::nullopt;
|
|
|
|
return { i };
|
|
}
|
|
|
|
|
|
LIST_ITEM_ITER addGroup( LIST_ITEM_ITER groupsBegin, LIST_ITEM_ITER groupsEnd,
|
|
wxString groupName, LIST_ITEM::GROUP_TYPE groupType )
|
|
{
|
|
LIST_ITEM_ITER group = std::find_if( groupsBegin, groupsEnd,
|
|
[&]( const std::unique_ptr<LIST_ITEM>& x )
|
|
{
|
|
return x->GetGroupName() == groupName
|
|
&& x->GetGroupType() == groupType;
|
|
} );
|
|
|
|
if( group == groupsEnd )
|
|
{
|
|
int dist = std::distance( groupsBegin, groupsEnd );
|
|
std::unique_ptr<LIST_ITEM> groupItem = std::make_unique<LIST_ITEM>( dist, groupName,
|
|
groupType );
|
|
group = m_items.insert( groupsEnd, std::move( groupItem ) );
|
|
ItemAdded( wxDataViewItem( ( *group )->Parent() ), wxDataViewItem( &**group ) );
|
|
}
|
|
|
|
return group;
|
|
}
|
|
|
|
|
|
std::optional<LIST_ITEM_ITER> addItem( std::unique_ptr<LIST_ITEM> aItem )
|
|
{
|
|
if( aItem == nullptr )
|
|
return {};
|
|
|
|
bool groupMatched = false;
|
|
|
|
// First see if item matches a group-by rule
|
|
if( m_parent.m_custom_group_rules.size() > 0 )
|
|
{
|
|
wxString searchName = aItem->GetNetName().Upper();
|
|
|
|
for( const std::unique_ptr<EDA_COMBINED_MATCHER>& rule : m_parent.m_custom_group_rules )
|
|
{
|
|
if( rule->Find( searchName.Upper() ) )
|
|
{
|
|
aItem->SetParent( m_custom_group_map[ rule->GetPattern() ] );
|
|
groupMatched = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Then add any netclass groups required by this item
|
|
if( m_parent.m_group_by_netclass && !groupMatched )
|
|
{
|
|
LIST_ITEM_ITER groups_begin = m_items.begin();
|
|
LIST_ITEM_ITER groups_end = std::find_if_not( m_items.begin(), m_items.end(),
|
|
[]( const std::unique_ptr<LIST_ITEM>& x )
|
|
{
|
|
return x->GetIsGroup();
|
|
} );
|
|
|
|
wxString match_str = aItem->GetNetclassName();
|
|
LIST_ITEM_ITER group = addGroup( groups_begin, groups_end, match_str,
|
|
LIST_ITEM::GROUP_TYPE::NETCLASS );
|
|
aItem->SetParent( &**group );
|
|
}
|
|
|
|
// Now add the item itself. Usually when new nets are added,
|
|
// they always get a higher netcode number than the already existing ones.
|
|
// however, if we've got filtering enabled, we might not have all the nets in
|
|
// our list, so do a sorted insertion.
|
|
auto new_iter = std::lower_bound( m_items.begin(), m_items.end(), aItem->GetNetCode(),
|
|
LIST_ITEM_NETCODE_CMP_LESS() );
|
|
|
|
new_iter = m_items.insert( new_iter, std::move( aItem ) );
|
|
const std::unique_ptr<LIST_ITEM>& new_item = *new_iter;
|
|
|
|
ItemAdded( wxDataViewItem( new_item->Parent() ), wxDataViewItem( new_item.get() ) );
|
|
|
|
return { new_iter };
|
|
}
|
|
|
|
void addItems( std::vector<std::unique_ptr<LIST_ITEM>> aItems )
|
|
{
|
|
m_items.reserve( m_items.size() + aItems.size() );
|
|
|
|
for( std::unique_ptr<LIST_ITEM>& i : aItems )
|
|
addItem( std::move( i ) );
|
|
}
|
|
|
|
std::unique_ptr<LIST_ITEM> deleteItem( const std::optional<LIST_ITEM_ITER>& aRow )
|
|
{
|
|
if( !aRow )
|
|
return {};
|
|
|
|
std::unique_ptr<LIST_ITEM> i = std::move( **aRow );
|
|
|
|
LIST_ITEM* parent = i->Parent();
|
|
i->SetParent( nullptr );
|
|
|
|
m_items.erase( *aRow );
|
|
ItemDeleted( wxDataViewItem( parent ), wxDataViewItem( &*i ) );
|
|
|
|
if( parent )
|
|
{
|
|
ItemChanged( wxDataViewItem( parent ) );
|
|
|
|
if( m_parent.m_group_by_netclass && parent != nullptr && parent->ChildrenCount() == 0 )
|
|
{
|
|
auto p = std::find_if( m_items.begin(), m_items.end(),
|
|
[&]( std::unique_ptr<LIST_ITEM>& x )
|
|
{
|
|
return x.get() == parent;
|
|
} );
|
|
|
|
wxASSERT( p != m_items.end() );
|
|
m_items.erase( p );
|
|
|
|
ItemDeleted( wxDataViewItem( parent->Parent() ), wxDataViewItem( parent ) );
|
|
}
|
|
}
|
|
|
|
Resort();
|
|
return i;
|
|
}
|
|
|
|
|
|
/**
|
|
* Adds all custom group-by entries to the items table
|
|
*
|
|
* Note this assumes that m_items is empty prior to adding these groups
|
|
*/
|
|
void addCustomGroups()
|
|
{
|
|
m_custom_group_map.clear();
|
|
int groupId = 0;
|
|
|
|
for( const std::unique_ptr<EDA_COMBINED_MATCHER>& rule : m_parent.m_custom_group_rules )
|
|
{
|
|
std::unique_ptr<LIST_ITEM>& group = m_items.emplace_back( std::make_unique<LIST_ITEM>(
|
|
groupId, rule->GetPattern(), LIST_ITEM::GROUP_TYPE::USER_DEFINED ) );
|
|
m_custom_group_map[ rule->GetPattern() ] = group.get();
|
|
ItemAdded( wxDataViewItem( group->Parent() ), wxDataViewItem( group.get() ) );
|
|
++groupId;
|
|
}
|
|
}
|
|
|
|
|
|
void deleteAllItems()
|
|
{
|
|
BeforeReset();
|
|
m_items.clear();
|
|
AfterReset();
|
|
}
|
|
|
|
void updateItem( const std::optional<LIST_ITEM_ITER>& aRow )
|
|
{
|
|
if( aRow )
|
|
{
|
|
const std::unique_ptr<LIST_ITEM>& listItem = *aRow.value();
|
|
|
|
if( listItem->Parent() )
|
|
ItemChanged( wxDataViewItem( listItem->Parent() ) );
|
|
|
|
ItemChanged( wxDataViewItem( listItem.get() ) );
|
|
resortIfChanged( listItem.get() );
|
|
}
|
|
}
|
|
|
|
void updateAllItems()
|
|
{
|
|
for( std::unique_ptr<LIST_ITEM>& i : m_items )
|
|
ItemChanged( wxDataViewItem( i.get() ) );
|
|
}
|
|
|
|
void resortIfChanged( LIST_ITEM* aItem )
|
|
{
|
|
if( wxDataViewColumn* column = m_parent.m_netsList->GetSortingColumn() )
|
|
{
|
|
bool changed = false;
|
|
|
|
for( const LIST_ITEM* i = aItem; i != nullptr; i = i->Parent() )
|
|
changed |= itemColumnChanged( i, column->GetModelColumn() );
|
|
|
|
for( LIST_ITEM* i = aItem; i != nullptr; i = i->Parent() )
|
|
i->ResetColumnChangedBits();
|
|
|
|
if( changed )
|
|
Resort();
|
|
}
|
|
}
|
|
|
|
bool itemColumnChanged( const LIST_ITEM* aItem, unsigned int aCol ) const
|
|
{
|
|
if( aItem == nullptr || aCol >= m_parent.m_columns.size() )
|
|
return false;
|
|
|
|
if( aCol == COLUMN_PAD_COUNT )
|
|
return aItem->PadCountChanged();
|
|
|
|
else if( aCol == COLUMN_VIA_COUNT )
|
|
return aItem->ViaCountChanged();
|
|
|
|
else if( aCol == COLUMN_VIA_LENGTH )
|
|
return aItem->ViaLengthChanged();
|
|
|
|
else if( aCol == COLUMN_BOARD_LENGTH )
|
|
return aItem->BoardWireLengthChanged();
|
|
|
|
else if( aCol == COLUMN_PAD_DIE_LENGTH )
|
|
return aItem->PadDieLengthChanged();
|
|
|
|
else if( aCol == COLUMN_TOTAL_LENGTH )
|
|
return aItem->TotalLengthChanged();
|
|
|
|
else if( aCol > COLUMN_LAST_STATIC_COL )
|
|
return aItem->BoardWireLengthChanged();
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
// implementation of wxDataViewModel interface
|
|
// these are used to query the data model by the GUI view implementation.
|
|
// these are not supposed to be used to modify the data model. for that
|
|
// use the public functions above.
|
|
|
|
protected:
|
|
unsigned int GetColumnCount() const override { return columnCount(); }
|
|
|
|
void GetValue( wxVariant& aOutValue, const wxDataViewItem& aItem,
|
|
unsigned int aCol ) const override
|
|
{
|
|
if( LIST_ITEM* i = static_cast<LIST_ITEM*>( aItem.GetID() ) )
|
|
{
|
|
if( i->GetIsGroup() )
|
|
{
|
|
if( aCol == COLUMN_NAME )
|
|
{
|
|
switch( i->GetGroupType() )
|
|
{
|
|
case LIST_ITEM::GROUP_TYPE::NETCLASS:
|
|
aOutValue = _( "Netclass" ) + ": " + i->GetGroupName();
|
|
break;
|
|
case LIST_ITEM::GROUP_TYPE::USER_DEFINED:
|
|
aOutValue = _( "Custom" ) + ": " + i->GetGroupName();
|
|
break;
|
|
default:
|
|
aOutValue = i->GetGroupName();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
aOutValue = "";
|
|
}
|
|
}
|
|
|
|
else if( aCol == COLUMN_NAME )
|
|
aOutValue = i->GetNetName();
|
|
|
|
else if( aCol == COLUMN_NETCLASS )
|
|
aOutValue = i->GetNetclassName();
|
|
|
|
else if( aCol == COLUMN_PAD_COUNT )
|
|
aOutValue = m_parent.formatCount( i->GetPadCount() );
|
|
|
|
else if( aCol == COLUMN_VIA_COUNT )
|
|
aOutValue = m_parent.formatCount( i->GetViaCount() );
|
|
|
|
else if( aCol == COLUMN_VIA_LENGTH )
|
|
aOutValue = m_parent.formatLength( i->GetViaLength() );
|
|
|
|
else if( aCol == COLUMN_BOARD_LENGTH )
|
|
aOutValue = m_parent.formatLength( i->GetBoardWireLength() );
|
|
|
|
else if( aCol == COLUMN_PAD_DIE_LENGTH )
|
|
aOutValue = m_parent.formatLength( i->GetPadDieLength() );
|
|
|
|
else if( aCol == COLUMN_TOTAL_LENGTH )
|
|
aOutValue = m_parent.formatLength( i->GetTotalLength() );
|
|
|
|
else if( aCol > COLUMN_LAST_STATIC_COL && aCol <= m_parent.m_columns.size() )
|
|
aOutValue = m_parent.formatLength( i->GetLayerWireLength( m_parent.m_columns[aCol].layer ) );
|
|
|
|
else
|
|
aOutValue = "";
|
|
}
|
|
}
|
|
|
|
static int compareUInt( uint64_t aValue1, uint64_t aValue2, bool aAsc )
|
|
{
|
|
if( aAsc )
|
|
return aValue1 < aValue2 ? -1 : 1;
|
|
else
|
|
return aValue2 < aValue1 ? -1 : 1;
|
|
}
|
|
|
|
int Compare( const wxDataViewItem& aItem1, const wxDataViewItem& aItem2, unsigned int aCol,
|
|
bool aAsc ) const override
|
|
{
|
|
const LIST_ITEM& i1 = *static_cast<const LIST_ITEM*>( aItem1.GetID() );
|
|
const LIST_ITEM& i2 = *static_cast<const LIST_ITEM*>( aItem2.GetID() );
|
|
|
|
if( i1.GetIsGroup() && !i2.GetIsGroup() )
|
|
return -1;
|
|
|
|
if( i2.GetIsGroup() && !i1.GetIsGroup() )
|
|
return 1;
|
|
|
|
if( aCol == COLUMN_NAME )
|
|
{
|
|
const wxString& s1 = i1.GetNetName();
|
|
const wxString& s2 = i2.GetNetName();
|
|
|
|
int res = aAsc ? ValueStringCompare( s1, s2 ) : ValueStringCompare( s2, s1 );
|
|
|
|
if( res != 0 )
|
|
return res;
|
|
}
|
|
|
|
else if( aCol == COLUMN_PAD_COUNT && i1.GetPadCount() != i2.GetPadCount() )
|
|
return compareUInt( i1.GetPadCount(), i2.GetPadCount(), aAsc );
|
|
|
|
else if( aCol == COLUMN_VIA_COUNT && i1.GetViaCount() != i2.GetViaCount() )
|
|
return compareUInt( i1.GetViaCount(), i2.GetViaCount(), aAsc );
|
|
|
|
else if( aCol == COLUMN_VIA_LENGTH && i1.GetViaLength() != i2.GetViaLength() )
|
|
return compareUInt( i1.GetViaLength(), i2.GetViaLength(), aAsc );
|
|
|
|
else if( aCol == COLUMN_BOARD_LENGTH && i1.GetBoardWireLength() != i2.GetBoardWireLength() )
|
|
return compareUInt( i1.GetBoardWireLength(), i2.GetBoardWireLength(), aAsc );
|
|
|
|
else if( aCol == COLUMN_PAD_DIE_LENGTH && i1.GetPadDieLength() != i2.GetPadDieLength() )
|
|
return compareUInt( i1.GetPadDieLength(), i2.GetPadDieLength(), aAsc );
|
|
|
|
else if( aCol == COLUMN_TOTAL_LENGTH && i1.GetTotalLength() != i2.GetTotalLength() )
|
|
return compareUInt( i1.GetTotalLength(), i2.GetTotalLength(), aAsc );
|
|
|
|
else if( aCol > COLUMN_LAST_STATIC_COL && aCol < m_parent.m_columns.size()
|
|
&& i1.GetLayerWireLength( m_parent.m_columns[aCol].layer )
|
|
!= i2.GetLayerWireLength( m_parent.m_columns[aCol].layer ) )
|
|
{
|
|
return compareUInt( i1.GetLayerWireLength( m_parent.m_columns[aCol].layer ),
|
|
i2.GetLayerWireLength( m_parent.m_columns[aCol].layer ), aAsc );
|
|
}
|
|
|
|
// when the item values compare equal resort to pointer comparison.
|
|
wxUIntPtr id1 = wxPtrToUInt( aItem1.GetID() );
|
|
wxUIntPtr id2 = wxPtrToUInt( aItem2.GetID() );
|
|
|
|
return aAsc ? id1 - id2 : id2 - id1;
|
|
}
|
|
|
|
bool SetValue( const wxVariant& aInValue, const wxDataViewItem& aItem,
|
|
unsigned int aCol ) override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
wxDataViewItem GetParent( const wxDataViewItem& aItem ) const override
|
|
{
|
|
if( !aItem.IsOk() )
|
|
return wxDataViewItem();
|
|
|
|
return wxDataViewItem( static_cast<const LIST_ITEM*>( aItem.GetID() )->Parent() );
|
|
}
|
|
|
|
bool IsContainer( const wxDataViewItem& aItem ) const override
|
|
{
|
|
if( !aItem.IsOk() )
|
|
return true;
|
|
|
|
return static_cast<const LIST_ITEM*>( aItem.GetID() )->GetIsGroup();
|
|
}
|
|
|
|
bool HasContainerColumns( const wxDataViewItem& aItem ) const override
|
|
{
|
|
return IsContainer( aItem );
|
|
}
|
|
|
|
unsigned int GetChildren( const wxDataViewItem& aParent,
|
|
wxDataViewItemArray& aChildren ) const override
|
|
{
|
|
const LIST_ITEM* p = static_cast<const LIST_ITEM*>( aParent.GetID() );
|
|
|
|
if( !aParent.IsOk() )
|
|
{
|
|
aChildren.Alloc( m_items.size() );
|
|
|
|
for( const std::unique_ptr<LIST_ITEM>& i : m_items )
|
|
{
|
|
if( i->Parent() == nullptr )
|
|
aChildren.Add( wxDataViewItem( &*i ) );
|
|
}
|
|
|
|
return aChildren.GetCount();
|
|
}
|
|
else if( p->GetIsGroup() )
|
|
{
|
|
const int count = p->ChildrenCount();
|
|
|
|
if( count == 0 )
|
|
return 0;
|
|
|
|
aChildren.Alloc( count );
|
|
|
|
for( auto i = p->ChildrenBegin(), end = p->ChildrenEnd(); i != end; ++i )
|
|
aChildren.Add( wxDataViewItem( *i ) );
|
|
|
|
return aChildren.GetCount();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
wxString GetColumnType( unsigned int /* aCol */ ) const override { return wxS( "string" ); }
|
|
|
|
private:
|
|
PCB_NET_INSPECTOR_PANEL& m_parent;
|
|
|
|
// primary container, sorted by netcode number.
|
|
// groups have netcode < 0, so they always come first, in the order
|
|
// of the filter strings as input by the user
|
|
std::vector<std::unique_ptr<LIST_ITEM>> m_items;
|
|
|
|
/// Map of custom group names to their representative list item
|
|
std::map<wxString, LIST_ITEM*> m_custom_group_map;
|
|
};
|
|
|
|
#endif
|