From a238209f3fc28c729f049681a93574c0f6e22afd Mon Sep 17 00:00:00 2001 From: Oleg Endo <5551493-oleg.endo@users.noreply.gitlab.com> Date: Wed, 4 Nov 2020 02:49:19 +0000 Subject: [PATCH] Add group-by-suffix display option in net inspector Fixes https://gitlab.com/kicad/code/kicad/-/issues/2469 --- 3d-viewer/3d_viewer/eda_3d_viewer.cpp | 4 +- common/eda_pattern_match.cpp | 38 +- common/footprint_filter.cpp | 2 +- include/eda_pattern_match.h | 31 +- .../class_board_stackup.h | 2 +- .../dialogs/dialog_select_net_from_list.cpp | 1710 +++++++++++++---- pcbnew/dialogs/dialog_select_net_from_list.h | 89 +- .../dialog_select_net_from_list_base.cpp | 75 +- .../dialog_select_net_from_list_base.fbp | 552 +++++- .../dialog_select_net_from_list_base.h | 16 +- 10 files changed, 2050 insertions(+), 469 deletions(-) diff --git a/3d-viewer/3d_viewer/eda_3d_viewer.cpp b/3d-viewer/3d_viewer/eda_3d_viewer.cpp index b0f3a96394..cd3ef524b6 100644 --- a/3d-viewer/3d_viewer/eda_3d_viewer.cpp +++ b/3d-viewer/3d_viewer/eda_3d_viewer.cpp @@ -709,9 +709,9 @@ void EDA_3D_VIEWER::SynchroniseColoursWithBoard() wxColour color; if( brd ) { - BOARD_STACKUP stckp = brd->GetDesignSettings().GetStackupDescriptor(); + const BOARD_STACKUP& stckp = brd->GetDesignSettings().GetStackupDescriptor(); - for( BOARD_STACKUP_ITEM* stckpItem : stckp.GetList() ) + for( const BOARD_STACKUP_ITEM* stckpItem : stckp.GetList() ) { wxString colorName = stckpItem->GetColor(); diff --git a/common/eda_pattern_match.cpp b/common/eda_pattern_match.cpp index 80f95c919c..a0a6561539 100644 --- a/common/eda_pattern_match.cpp +++ b/common/eda_pattern_match.cpp @@ -40,11 +40,14 @@ wxString const& EDA_PATTERN_MATCH_SUBSTR::GetPattern() const } -int EDA_PATTERN_MATCH_SUBSTR::Find( const wxString& aCandidate ) const +EDA_PATTERN_MATCH::FIND_RESULT EDA_PATTERN_MATCH_SUBSTR::Find( const wxString& aCandidate ) const { int loc = aCandidate.Find( m_pattern ); - return ( loc == wxNOT_FOUND ) ? EDA_PATTERN_NOT_FOUND : loc; + if( loc == wxNOT_FOUND ) + return {}; + else + return { loc, static_cast( m_pattern.size() ) }; } @@ -87,7 +90,7 @@ wxString const& EDA_PATTERN_MATCH_REGEX::GetPattern() const } -int EDA_PATTERN_MATCH_REGEX::Find( const wxString& aCandidate ) const +EDA_PATTERN_MATCH::FIND_RESULT EDA_PATTERN_MATCH_REGEX::Find( const wxString& aCandidate ) const { if( m_regex.IsValid() ) { @@ -95,17 +98,23 @@ int EDA_PATTERN_MATCH_REGEX::Find( const wxString& aCandidate ) const { size_t start, len; m_regex.GetMatch( &start, &len, 0 ); - return ( start > INT_MAX ) ? INT_MAX : start; + + return { static_cast( std::min( start, static_cast( INT_MAX ) ) ), + static_cast( std::min( len, static_cast( INT_MAX ) ) ) }; } else { - return EDA_PATTERN_NOT_FOUND; + return {}; } } else { int loc = aCandidate.Find( m_pattern ); - return ( loc == wxNOT_FOUND ) ? EDA_PATTERN_NOT_FOUND : loc; + + if( loc == wxNOT_FOUND ) + return {}; + else + return { loc, static_cast( m_pattern.size() ) }; } } @@ -152,7 +161,7 @@ wxString const& EDA_PATTERN_MATCH_WILDCARD::GetPattern() const } -int EDA_PATTERN_MATCH_WILDCARD::Find( const wxString& aCandidate ) const +EDA_PATTERN_MATCH::FIND_RESULT EDA_PATTERN_MATCH_WILDCARD::Find( const wxString& aCandidate ) const { return EDA_PATTERN_MATCH_REGEX::Find( aCandidate ); } @@ -253,7 +262,8 @@ wxString const& EDA_PATTERN_MATCH_RELATIONAL::GetPattern() const } -int EDA_PATTERN_MATCH_RELATIONAL::Find( const wxString& aCandidate ) const +EDA_PATTERN_MATCH::FIND_RESULT EDA_PATTERN_MATCH_RELATIONAL::Find( + const wxString& aCandidate ) const { wxStringTokenizer tokenizer( aCandidate ); size_t lastpos = 0; @@ -266,13 +276,13 @@ int EDA_PATTERN_MATCH_RELATIONAL::Find( const wxString& aCandidate ) const if( found_delta != EDA_PATTERN_NOT_FOUND ) { size_t found = (size_t) found_delta + lastpos; - return ( found > INT_MAX ) ? INT_MAX : (int) found; + return { static_cast( std::min( found, static_cast( INT_MAX ) ) ), 0 }; } lastpos = tokenizer.GetPosition(); } - return EDA_PATTERN_NOT_FOUND; + return {}; } @@ -357,15 +367,15 @@ bool EDA_COMBINED_MATCHER::Find( const wxString& aTerm, int& aMatchersTriggered, for( auto const& matcher : m_matchers ) { - int local_find = matcher->Find( aTerm ); + EDA_PATTERN_MATCH::FIND_RESULT local_find = matcher->Find( aTerm ); - if ( local_find != EDA_PATTERN_NOT_FOUND ) + if( local_find ) { aMatchersTriggered += 1; - if ( local_find < aPosition || aPosition == EDA_PATTERN_NOT_FOUND ) + if( local_find.start < aPosition || aPosition == EDA_PATTERN_NOT_FOUND ) { - aPosition = local_find; + aPosition = local_find.start; } } } diff --git a/common/footprint_filter.cpp b/common/footprint_filter.cpp index 9467cb68cb..fa985c37e0 100644 --- a/common/footprint_filter.cpp +++ b/common/footprint_filter.cpp @@ -144,7 +144,7 @@ bool FOOTPRINT_FILTER_IT::FootprintFilterMatch( FOOTPRINT_INFO& aItem ) name += aItem.GetFootprintName().Lower(); - if( each_filter->Find( name ) != EDA_PATTERN_NOT_FOUND ) + if( each_filter->Find( name ) ) { return true; } diff --git a/include/eda_pattern_match.h b/include/eda_pattern_match.h index 29f1795c17..bd08b362cf 100644 --- a/include/eda_pattern_match.h +++ b/include/eda_pattern_match.h @@ -44,6 +44,22 @@ static const int EDA_PATTERN_NOT_FOUND = wxNOT_FOUND; class EDA_PATTERN_MATCH { public: + struct FIND_RESULT + { + int start = EDA_PATTERN_NOT_FOUND; + int length = 0; + + bool valid() const + { + return start != EDA_PATTERN_NOT_FOUND; + } + + explicit operator bool() const + { + return valid(); + } + }; + virtual ~EDA_PATTERN_MATCH() {} /** @@ -58,10 +74,11 @@ public: virtual wxString const& GetPattern() const = 0; /** - * Return the location of a match iff a given candidate string matches the set pattern. - * Otherwise, return EDA_PATTERN_NOT_FOUND. + * Return the location and possibly length of a match iff a given candidate + * string matches the set pattern. + * Otherwise, return an invalid FIND_RESULT. */ - virtual int Find( const wxString& aCandidate ) const = 0; + virtual FIND_RESULT Find( const wxString& aCandidate ) const = 0; }; @@ -73,7 +90,7 @@ class EDA_PATTERN_MATCH_SUBSTR : public EDA_PATTERN_MATCH public: virtual bool SetPattern( const wxString& aPattern ) override; virtual wxString const& GetPattern() const override; - virtual int Find( const wxString& aCandidate ) const override; + virtual FIND_RESULT Find( const wxString& aCandidate ) const override; protected: wxString m_pattern; @@ -88,7 +105,7 @@ class EDA_PATTERN_MATCH_REGEX : public EDA_PATTERN_MATCH public: virtual bool SetPattern( const wxString& aPattern ) override; virtual wxString const& GetPattern() const override; - virtual int Find( const wxString& aCandidate ) const override; + virtual FIND_RESULT Find( const wxString& aCandidate ) const override; protected: wxString m_pattern; @@ -101,7 +118,7 @@ class EDA_PATTERN_MATCH_WILDCARD : public EDA_PATTERN_MATCH_REGEX public: virtual bool SetPattern( const wxString& aPattern ) override; virtual wxString const& GetPattern() const override; - virtual int Find( const wxString& aCandidate ) const override; + virtual FIND_RESULT Find( const wxString& aCandidate ) const override; protected: wxString m_wildcard_pattern; @@ -133,7 +150,7 @@ class EDA_PATTERN_MATCH_RELATIONAL : public EDA_PATTERN_MATCH public: virtual bool SetPattern( const wxString& aPattern ) override; virtual wxString const& GetPattern() const override; - virtual int Find( const wxString& aCandidate ) const override; + virtual FIND_RESULT Find( const wxString& aCandidate ) const override; int FindOne( const wxString& aCandidate ) const; protected: diff --git a/pcbnew/board_stackup_manager/class_board_stackup.h b/pcbnew/board_stackup_manager/class_board_stackup.h index f3cc81df60..6a7590f4d7 100644 --- a/pcbnew/board_stackup_manager/class_board_stackup.h +++ b/pcbnew/board_stackup_manager/class_board_stackup.h @@ -248,7 +248,7 @@ public: ~BOARD_STACKUP() { RemoveAll(); } - std::vector& GetList() { return m_list; } + const std::vector& GetList() const { return m_list; } /// @return a reference to the layer aIndex, or nullptr if not exists BOARD_STACKUP_ITEM* GetStackupLayer( int aIndex ); diff --git a/pcbnew/dialogs/dialog_select_net_from_list.cpp b/pcbnew/dialogs/dialog_select_net_from_list.cpp index d11567d5d9..0188e9641a 100644 --- a/pcbnew/dialogs/dialog_select_net_from_list.cpp +++ b/pcbnew/dialogs/dialog_select_net_from_list.cpp @@ -23,6 +23,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include +#include #include #include #include @@ -30,93 +32,776 @@ #include #include #include +#include #include #include +#include #include #include #include #include +#include +#include -struct DIALOG_SELECT_NET_FROM_LIST::COLUMN_ID +struct DIALOG_SELECT_NET_FROM_LIST::COLUMN_DESC { - int col_num; - wxString display_name; - - operator int() const + enum { - return col_num; + CSV_NONE = 0, + CSV_QUOTE = 1 << 0 + }; + + unsigned int num; + wxString display_name; + wxString csv_name; + unsigned int csv_flags; + + operator unsigned int() const + { + return num; } }; -#define def_col( c, num, name ) \ - const DIALOG_SELECT_NET_FROM_LIST::COLUMN_ID DIALOG_SELECT_NET_FROM_LIST::c = { num, name } -def_col( COLUMN_NET, 0, _( "Net" ) ); -def_col( COLUMN_NAME, 1, _( "Name" ) ); -def_col( COLUMN_PAD_COUNT, 2, _( "Pad Count" ) ); -def_col( COLUMN_VIA_COUNT, 3, _( "Via Count" ) ); -def_col( COLUMN_BOARD_LENGTH, 4, _( "Board Length" ) ); -def_col( COLUMN_CHIP_LENGTH, 5, _( "Die Length" ) ); -def_col( COLUMN_TOTAL_LENGTH, 6, _( "Length" ) ); +#define def_col( c, num, name, csv_name, csv_flags ) \ + const DIALOG_SELECT_NET_FROM_LIST::COLUMN_DESC DIALOG_SELECT_NET_FROM_LIST::c = { num, name, \ + csv_name, COLUMN_DESC::csv_flags } + +def_col( COLUMN_NET, 0, _( "Net" ), _( "Net Id" ), CSV_NONE ); +def_col( COLUMN_NAME, 1, _( "Name" ), _( "Net name" ), CSV_QUOTE ); +def_col( COLUMN_PAD_COUNT, 2, _( "Pad Count" ), _( "Pad count" ), CSV_NONE ); +def_col( COLUMN_VIA_COUNT, 3, _( "Via Count" ), _( "Via count" ), CSV_NONE ); +def_col( COLUMN_VIA_LENGTH, 4, _( "Via Length" ), _( "Via length" ), CSV_NONE ); +def_col( COLUMN_BOARD_LENGTH, 5, _( "Board Length" ), _( "Board length" ), CSV_NONE ); +def_col( COLUMN_CHIP_LENGTH, 6, _( "Die Length" ), _( "Die length" ), CSV_NONE ); +def_col( COLUMN_TOTAL_LENGTH, 7, _( "Total Length" ), _( "Net length" ), CSV_NONE ); #undef def_col -struct DIALOG_SELECT_NET_FROM_LIST::LIST_ITEM +class DIALOG_SELECT_NET_FROM_LIST::LIST_ITEM { +private: + // an item can be the child of only one parent at a time. + // FIXME: could use a more lightweight container like intrusive forward list. + LIST_ITEM* m_parent = nullptr; + std::vector m_children; + + bool m_is_group = false; + 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_board_wire_length = 0; + uint64_t m_chip_wire_length = 0; + + // dirty bits to record when some attribute has changed. this is to + // avoid unnecessary resort operations. + std::bitset<5> m_column_changed; + + // cached formatted net name for faster display sorting. + wxString m_net_name; + +public: + LIST_ITEM( unsigned int aGroupNumber, const wxString& aGroupName ) + : m_is_group( true ), m_group_number( aGroupNumber ), m_net_name( aGroupName ) + { + } + LIST_ITEM( NETINFO_ITEM* aNet ) : m_net( aNet ) { + m_net_name = UnescapeString( aNet->GetNetname() ); } - NETINFO_ITEM* m_net; - int m_pad_count = 0; - int m_via_count = 0; - int m_board_wire_length = 0; - int m_chip_wire_length = 0; - int m_total_length = 0; -}; + LIST_ITEM& operator=( const LIST_ITEM& ) = delete; - -struct DIALOG_SELECT_NET_FROM_LIST::LIST_ITEM_NET_CMP_LESS -{ - const LIST_ITEM* m_base_ptr; - - LIST_ITEM_NET_CMP_LESS( const std::vector& container ) - : m_base_ptr( container.data() ) + bool isGroup() const { + return m_is_group; } - bool operator()( unsigned int a, unsigned int b ) const + unsigned int groupNumber() const { - return m_base_ptr[a].m_net < m_base_ptr[b].m_net; + return m_group_number; } - bool operator()( unsigned int a, NETINFO_ITEM* b ) const + auto childrenBegin() const { - return m_base_ptr[a].m_net < b; + return m_children.begin(); } - bool operator()( NETINFO_ITEM* a, unsigned int b ) const + auto childrenEnd() const { - return a < m_base_ptr[b].m_net; + return m_children.end(); + } + + unsigned int childrenCount() const + { + return m_children.size(); + } + + NETINFO_ITEM* net() const + { + return m_net; + } + + int netCode() const + { + return isGroup() ? ( 0 - int( m_group_number ) - 1 ) : m_net->GetNet(); + } + + const wxString& netName() const + { + return m_net_name; + } + + const wxString& groupName() const + { + return m_net_name; + } + + void resetColumnChangedBits() + { + m_column_changed.reset(); + } + +#define gen( mvar, chg_bit, get, set, add, sub, changed ) \ + decltype( mvar ) get() const \ + { \ + return mvar; \ + } \ + \ + bool changed() const \ + { \ + return m_column_changed[chg_bit]; \ + } \ + \ + void set( const decltype( mvar )& aValue ) \ + { \ + if( m_parent ) \ + m_parent->set( m_parent->get() - mvar + aValue ); \ + \ + static_assert( chg_bit < decltype( m_column_changed )().size() ); \ + m_column_changed[chg_bit] = m_column_changed[chg_bit] | ( mvar != aValue ); \ + mvar = aValue; \ + } \ + \ + void add( const decltype( mvar )& aValue ) \ + { \ + if( m_parent ) \ + m_parent->add( aValue ); \ + \ + static_assert( chg_bit < decltype( m_column_changed )().size() ); \ + m_column_changed[chg_bit] = m_column_changed[chg_bit] | ( aValue != 0 ); \ + mvar += aValue; \ + } \ + \ + void sub( const decltype( mvar )& aValue ) \ + { \ + if( m_parent ) \ + m_parent->sub( aValue ); \ + \ + static_assert( chg_bit < decltype( m_column_changed )().size() ); \ + m_column_changed[chg_bit] = m_column_changed[chg_bit] | ( aValue != 0 ); \ + mvar -= aValue; \ + } + + gen( m_pad_count, 0, padCount, setPadCount, addPadCount, subPadCount, padCountChanged ); + gen( m_via_count, 1, viaCount, setViaCount, addViaCount, subViaCount, viaCountChanged ); + gen( m_via_length, 2, viaLength, setViaLength, addViaLength, subViaLength, viaLengthChanged ); + gen( m_board_wire_length, 3, boardWireLength, setBoardWireLength, addBoardWireLength, + subBoardWireLength, boardWireLengthChanged ); + gen( m_chip_wire_length, 4, chipWireLength, setChipWireLength, addChipWireLength, + subChipWireLength, chipWireLengthChanged ); + +#undef gen + + // the total length column is always computed, never stored. + auto totalLength() const + { + return boardWireLength() + viaLength() + chipWireLength(); + } + + bool totalLengthChanged() const + { + return boardWireLengthChanged() | viaLengthChanged() | chipWireLengthChanged(); + } + + LIST_ITEM* parent() const + { + return m_parent; + } + + void setParent( LIST_ITEM* aParent ) + { + if( m_parent == aParent ) + return; + + if( m_parent != nullptr ) + { + m_parent->subPadCount( padCount() ); + m_parent->subViaCount( viaCount() ); + m_parent->subViaLength( viaLength() ); + m_parent->subBoardWireLength( boardWireLength() ); + m_parent->subChipWireLength( chipWireLength() ); + + 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( padCount() ); + m_parent->addViaCount( viaCount() ); + m_parent->addViaLength( viaLength() ); + m_parent->addBoardWireLength( boardWireLength() ); + m_parent->addChipWireLength( chipWireLength() ); + + m_parent->m_children.push_back( this ); + } } }; -struct DIALOG_SELECT_NET_FROM_LIST::ROW_DESC +struct DIALOG_SELECT_NET_FROM_LIST::LIST_ITEM_NETCODE_CMP_LESS { - int row_num = -1; - decltype( DIALOG_SELECT_NET_FROM_LIST::m_list_items )::iterator by_row; - decltype( DIALOG_SELECT_NET_FROM_LIST::m_list_items_by_net )::iterator by_net; - - bool valid() const + template + bool operator()( const T& a, const T& b ) const { - return row_num != -1; + return a->netCode() < b->netCode(); } - explicit operator bool() const + template + bool operator()( const T& a, int b ) const { - return valid(); + return a->netCode() < b; + } + + template + bool operator()( int a, const T& b ) const + { + return a < b->netCode(); + } +}; + + +class DIALOG_SELECT_NET_FROM_LIST::DATA_MODEL : public wxDataViewModel +{ +private: + DIALOG_SELECT_NET_FROM_LIST& 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 (group mode 0, 1) or + // in order of occurance (group mode 2, 3). + std::vector> m_items; + + bool m_sort_groups_first = false; + +public: + static const auto& columnDesc() + { + static const std::array r = { { COLUMN_NET, COLUMN_NAME, COLUMN_PAD_COUNT, + COLUMN_VIA_COUNT, COLUMN_VIA_LENGTH, COLUMN_BOARD_LENGTH, COLUMN_CHIP_LENGTH, + COLUMN_TOTAL_LENGTH } }; + + return r; + } + + DATA_MODEL( DIALOG_SELECT_NET_FROM_LIST& parent ) : m_parent( parent ) + { + } + + unsigned int columnCount() const + { + return columnDesc().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( &*( m_items[aRow] ) ) ), aCol ); + return r; + } + + const LIST_ITEM& itemAt( unsigned int aRow ) const + { + return *m_items.at( aRow ); + } + + OPT 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 )->netCode() != aNetCode ) + return {}; + + return { i }; + } + + + OPT findItem( NETINFO_ITEM* aNet ) + { + if( aNet != nullptr ) + return findItem( aNet->GetNet() ); + else + return {}; + } + + + OPT addItem( std::unique_ptr aItem ) + { + if( aItem == nullptr ) + return {}; + + // make sure that the vector is always sorted. 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_i = std::lower_bound( + m_items.begin(), m_items.end(), aItem->netCode(), LIST_ITEM_NETCODE_CMP_LESS() ); + + new_i = m_items.insert( new_i, std::move( aItem ) ); + + if( m_parent.m_groupBy->IsChecked() + && ( m_parent.m_groupByKind->GetSelection() == 0 + || m_parent.m_groupByKind->GetSelection() == 1 ) ) + { + for( unsigned int j = 0; j < m_parent.m_groupFilter.size(); ++j ) + { + if( m_parent.m_groupFilter[j]->Find( ( *new_i )->netName() ) ) + { + ( *new_i )->setParent( &*m_items[j] ); + break; + } + } + } + else if( m_parent.m_groupBy->IsChecked() + && ( m_parent.m_groupByKind->GetSelection() == 2 + || m_parent.m_groupByKind->GetSelection() == 3 ) ) + { + auto groups_begin = m_items.begin(); + auto groups_end = std::find_if_not( + m_items.begin(), m_items.end(), []( const auto& x ) { return x->isGroup(); } ); + + for( auto&& f : m_parent.m_groupFilter ) + { + auto match = f->Find( ( *new_i )->netName() ); + + if( match ) + { + wxString match_str = ( *new_i )->netName().substr( match.start, match.length ); + + auto g = std::find_if( groups_begin, groups_end, + [&]( const auto& x ) { return x->netName() == match_str; } ); + + if( g == groups_end ) + { + g = m_items.insert( groups_end, + std::make_unique( + std::distance( groups_end, groups_begin ), match_str ) ); + + groups_end = g + 1; + + ItemAdded( wxDataViewItem( ( *g )->parent() ), wxDataViewItem( &**g ) ); + } + + ( *new_i )->setParent( &**g ); + break; + } + } + } + + ItemAdded( wxDataViewItem( ( *new_i )->parent() ), wxDataViewItem( &**new_i ) ); + + return { new_i }; + } + + + void addItems( std::vector>&& aItems ) + { + if( m_items.empty() ) + { + m_items = std::move( aItems ); + + if( m_parent.m_groupBy->IsChecked() + && ( m_parent.m_groupByKind->GetSelection() == 0 + || m_parent.m_groupByKind->GetSelection() == 1 ) ) + { + // assume that there are fewer group filters than nets. + // walk over the list items and assign them to groups. note that the + // first items are group items themselves, so start after those. + for( unsigned int i = m_parent.m_groupFilter.size(); i < m_items.size(); ++i ) + { + for( unsigned int j = 0; j < m_parent.m_groupFilter.size(); ++j ) + { + if( m_parent.m_groupFilter[j]->Find( m_items[i]->netName() ) ) + { + m_items[i]->setParent( &*m_items[j] ); + break; + } + } + } + } + else if( m_parent.m_groupBy->IsChecked() + && ( m_parent.m_groupByKind->GetSelection() == 2 + || m_parent.m_groupByKind->GetSelection() == 3 ) ) + { + // assume that there will be fewer resulting groups than nets. + // dynamically generate groups for the actual strings of the match result. + // try out each filter on each item and group by the resulting substrings. + std::vector> groups; + + for( auto&& i : m_items ) + { + for( auto&& f : m_parent.m_groupFilter ) + { + auto match = f->Find( i->netName() ); + + if( match ) + { + wxString match_str = i->netName().substr( match.start, match.length ); + + auto g = std::find_if( groups.begin(), groups.end(), + [&]( const auto& x ) { return x->netName() == match_str; } ); + + if( g == groups.end() ) + { + groups.emplace_back( + std::make_unique( groups.size(), match_str ) ); + g = groups.end() - 1; + } + + i->setParent( &**g ); + break; + } + } + } + + // insert the group items at the front of the items list. + for( auto&& g : groups ) + m_items.emplace_back( std::move( g ) ); + + std::rotate( m_items.begin(), m_items.end() - groups.size(), m_items.end() ); + } + + for( auto&& i : m_items ) + ItemAdded( wxDataViewItem( i->parent() ), wxDataViewItem( &*i ) ); + } + else + { + m_items.reserve( m_items.size() + aItems.size() ); + + for( auto&& i : aItems ) + addItem( std::move( i ) ); + } + } + + std::unique_ptr deleteItem( const OPT& aRow ) + { + if( !aRow ) + return {}; + + std::unique_ptr i = std::move( **aRow ); + + // if the row has a parent, detach it first + auto parent = i->parent(); + i->setParent( nullptr ); + + m_items.erase( *aRow ); + + ItemDeleted( wxDataViewItem( parent ), wxDataViewItem( &*i ) ); + + ItemChanged( wxDataViewItem( parent ) ); + + // for grouping type 2,3 a group item might disappear if it becomes empty. + if( ( m_parent.m_groupByKind->GetSelection() == 2 + || m_parent.m_groupByKind->GetSelection() == 3 ) + && parent != nullptr && parent->childrenCount() == 0 ) + { + auto p = std::find_if( + m_items.begin(), m_items.end(), [&]( auto& x ) { return x.get() == parent; } ); + assert( p != m_items.end() ); + m_items.erase( p ); + + ItemDeleted( wxDataViewItem( parent->parent() ), wxDataViewItem( parent ) ); + } + else + resortIfChanged( parent ); + + return i; + } + + + void deleteAllItems() + { + m_items.clear(); + Cleared(); + } + + + void updateItem( const OPT& aRow ) + { + if( !aRow ) + return; + + if( ( **aRow )->parent() ) + ItemChanged( wxDataViewItem( ( **aRow )->parent() ) ); + + ItemChanged( wxDataViewItem( &***aRow ) ); + resortIfChanged( &***aRow ); + } + + + void updateAllItems() + { + for( auto&& i : m_items ) + ItemChanged( wxDataViewItem( &*i ) ); + } + + + void resortIfChanged( LIST_ITEM* aItem ) + { + if( auto* c = m_parent.m_netsList->GetSortingColumn() ) + { + bool changed = false; + + for( const LIST_ITEM* i = aItem; i != nullptr; i = i->parent() ) + changed |= itemColumnChanged( i, c->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 >= columnDesc().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_CHIP_LENGTH ) + return aItem->chipWireLengthChanged(); + + else if( aCol == COLUMN_TOTAL_LENGTH ) + return aItem->totalLengthChanged(); + + return false; + } + + + void setSortGroupsFirst( bool aValue ) + { + if( aValue == m_sort_groups_first ) + return; + + m_sort_groups_first = aValue; + + if( m_parent.m_netsList->GetSortingColumn() ) + Resort(); + } + + + bool isSortGroupsFirst() const + { + return m_sort_groups_first; + } + + // 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: + virtual unsigned int GetColumnCount() const override + { + return columnCount(); + } + + virtual void GetValue( + wxVariant& aOutValue, const wxDataViewItem& aItem, unsigned int aCol ) const override + { + if( auto&& i = static_cast( aItem.GetID() ) ) + { + if( aCol == COLUMN_NET && !i->isGroup() ) + aOutValue = m_parent.formatNetCode( i->net() ); + + else if( aCol == COLUMN_NET && i->isGroup() ) + aOutValue = ""; + + else if( aCol == COLUMN_NAME ) + aOutValue = i->netName(); + + else if( aCol == COLUMN_PAD_COUNT ) + aOutValue = m_parent.formatCount( i->padCount() ); + + else if( aCol == COLUMN_VIA_COUNT ) + aOutValue = m_parent.formatCount( i->viaCount() ); + + else if( aCol == COLUMN_VIA_LENGTH ) + aOutValue = m_parent.formatLength( i->viaLength() ); + + else if( aCol == COLUMN_BOARD_LENGTH ) + aOutValue = m_parent.formatLength( i->boardWireLength() ); + + else if( aCol == COLUMN_CHIP_LENGTH ) + aOutValue = m_parent.formatLength( i->chipWireLength() ); + + else if( aCol == COLUMN_TOTAL_LENGTH ) + aOutValue = m_parent.formatLength( i->totalLength() ); + } + } + + static int compareUInt( uint64_t aValue1, uint64_t aValue2, bool aAsc ) + { + if( aAsc ) + return aValue1 < aValue2 ? -1 : 1; + else + return aValue2 < aValue1 ? -1 : 1; + } + + virtual int Compare( const wxDataViewItem& aItem1, const wxDataViewItem& aItem2, + unsigned int aCol, bool aAsc ) const override + { + const LIST_ITEM& i1 = *static_cast( aItem1.GetID() ); + const LIST_ITEM& i2 = *static_cast( aItem2.GetID() ); + + if( isSortGroupsFirst() ) + { + if( i1.isGroup() && !i2.isGroup() ) + return -1; + if( i2.isGroup() && !i1.isGroup() ) + return 1; + } + + if( aCol == COLUMN_NET && i1.netCode() != i2.netCode() ) + return aAsc ? ( i2.netCode() - i1.netCode() ) : ( i1.netCode() - i2.netCode() ); + + else if( aCol == COLUMN_NAME ) + { + const wxString& s1 = i1.netName(); + const wxString& s2 = i2.netName(); + + int res = aAsc ? s1.Cmp( s2 ) : s2.Cmp( s1 ); + if( res != 0 ) + return res; + } + + else if( aCol == COLUMN_PAD_COUNT && i1.padCount() != i2.padCount() ) + return compareUInt( i1.padCount(), i2.padCount(), aAsc ); + + else if( aCol == COLUMN_VIA_COUNT && i1.viaCount() != i2.viaCount() ) + return compareUInt( i1.viaCount(), i2.viaCount(), aAsc ); + + else if( aCol == COLUMN_VIA_LENGTH && i1.viaLength() != i2.viaLength() ) + return compareUInt( i1.viaLength(), i2.viaLength(), aAsc ); + + else if( aCol == COLUMN_BOARD_LENGTH && i1.boardWireLength() != i2.boardWireLength() ) + return compareUInt( i1.boardWireLength(), i2.boardWireLength(), aAsc ); + + else if( aCol == COLUMN_CHIP_LENGTH && i1.chipWireLength() != i2.chipWireLength() ) + return compareUInt( i1.chipWireLength(), i2.chipWireLength(), aAsc ); + + else if( aCol == COLUMN_TOTAL_LENGTH && i1.totalLength() != i2.totalLength() ) + return compareUInt( i1.totalLength(), i2.totalLength(), 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; + } + + virtual bool SetValue( + const wxVariant& aInValue, const wxDataViewItem& aItem, unsigned int aCol ) override + { + return false; + } + + virtual wxDataViewItem GetParent( const wxDataViewItem& aItem ) const override + { + if( !aItem.IsOk() ) + return wxDataViewItem(); + + return wxDataViewItem( static_cast( aItem.GetID() )->parent() ); + } + + virtual bool IsContainer( const wxDataViewItem& aItem ) const override + { + if( !aItem.IsOk() ) + return true; + + return static_cast( aItem.GetID() )->isGroup(); + } + + virtual bool HasContainerColumns( const wxDataViewItem& aItem ) const override + { + return IsContainer( aItem ); + } + + virtual unsigned int GetChildren( + const wxDataViewItem& aParent, wxDataViewItemArray& aChildren ) const override + { + auto&& p = static_cast( aParent.GetID() ); + + if( !aParent.IsOk() ) + { + aChildren.Alloc( m_items.size() ); + + for( auto&& i : m_items ) + { + if( i->parent() == nullptr ) + aChildren.Add( wxDataViewItem( &*i ) ); + } + + return aChildren.GetCount(); + } + else if( p->isGroup() ) + { + const auto 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; + } + + virtual wxString GetColumnType( unsigned int /* aCol */ ) const override + { + return wxS( "string" ); } }; @@ -127,26 +812,82 @@ DIALOG_SELECT_NET_FROM_LIST::DIALOG_SELECT_NET_FROM_LIST( PCB_EDIT_FRAME* aParen m_frame( aParent ) { m_brd = aParent->GetBoard(); - m_wasSelected = false; -#define ADD_COL( name, flag, align ) m_netsList->AppendTextColumn( name, flag, 0, align, \ - wxDATAVIEW_COL_RESIZABLE ); + m_data_model = new DATA_MODEL( *this ); + m_netsList->AssociateModel( &*m_data_model ); - ADD_COL( COLUMN_NET.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_LEFT ); - ADD_COL( COLUMN_NAME.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_LEFT ); - ADD_COL( COLUMN_PAD_COUNT.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_CENTER ); - ADD_COL( COLUMN_VIA_COUNT.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_CENTER ); - ADD_COL( COLUMN_BOARD_LENGTH.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_CENTER ); - ADD_COL( COLUMN_CHIP_LENGTH.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_CENTER ); - ADD_COL( COLUMN_TOTAL_LENGTH.display_name, wxDATAVIEW_CELL_INERT, wxALIGN_CENTER ); + std::array, 8> add_col = { + [&]( void ) { + m_netsList->AppendTextColumn( COLUMN_NET.display_name, COLUMN_NET, + wxDATAVIEW_CELL_INERT, -1, wxALIGN_LEFT, wxDATAVIEW_COL_SORTABLE ); + }, + [&]( void ) { + m_netsList->AppendTextColumn( COLUMN_NAME.display_name, COLUMN_NAME, + wxDATAVIEW_CELL_INERT, -1, wxALIGN_LEFT, + wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE + | wxDATAVIEW_COL_SORTABLE ); + }, + [&]( void ) { + m_netsList->AppendTextColumn( COLUMN_PAD_COUNT.display_name, COLUMN_PAD_COUNT, + wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, + wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE ); + }, + [&]( void ) { + m_netsList->AppendTextColumn( COLUMN_VIA_COUNT.display_name, COLUMN_VIA_COUNT, + wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, + wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE ); + }, + [&]( void ) { + m_netsList->AppendTextColumn( COLUMN_VIA_LENGTH.display_name, COLUMN_VIA_LENGTH, + wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, + wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE ); + }, + [&]( void ) { + m_netsList->AppendTextColumn( COLUMN_BOARD_LENGTH.display_name, COLUMN_BOARD_LENGTH, + wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, + wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE ); + }, + [&]( void ) { + m_netsList->AppendTextColumn( COLUMN_CHIP_LENGTH.display_name, COLUMN_CHIP_LENGTH, + wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, + wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE ); + }, + [&]( void ) { + m_netsList->AppendTextColumn( COLUMN_TOTAL_LENGTH.display_name, COLUMN_TOTAL_LENGTH, + wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, + wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE ); + } + }; - // The fact that we're a list should keep the control from reserving space for the - // expander buttons... but it doesn't. Fix by forcing the indent to 0. - m_netsList->SetIndent( 0 ); + std::vector col_order = aSettings.column_order; + + if( col_order.size() != add_col.size() ) + { + col_order.resize( add_col.size() ); + + for( unsigned int i = 0; i < add_col.size(); ++i ) + col_order[i] = i; + } + + for( unsigned int i : col_order ) + add_col.at( i )(); + + m_netsList->SetExpanderColumn( m_netsList->GetColumn( 0 ) ); + + // avoid onFilterChange for each of the settings as it will re-build the + // list over and over again. + m_filter_change_no_rebuild = true; m_textCtrlFilter->SetValue( aSettings.filter_string ); m_cbShowZeroPad->SetValue( aSettings.show_zero_pad_nets ); + m_groupBy->SetValue( aSettings.group_by ); + m_groupByKind->SetSelection( aSettings.group_by_kind ); + m_groupByText->SetValue( aSettings.group_by_text ); + m_viaLengthType->SetSelection( aSettings.via_length_type ); + m_constViaLengthValue = aSettings.const_via_length; + m_constViaLength->SetValue( formatLength( m_constViaLengthValue ) ); + m_filter_change_no_rebuild = false; buildNetsList(); adjustListColumns(); @@ -156,6 +897,16 @@ DIALOG_SELECT_NET_FROM_LIST::DIALOG_SELECT_NET_FROM_LIST( PCB_EDIT_FRAME* aParen m_deleteNet->SetBitmap( KiBitmap( trash_xpm ) ); m_sdbSizerOK->SetDefault(); + m_renameNet->Disable(); + m_deleteNet->Disable(); + + m_data_model->setSortGroupsFirst( m_groupsFirst->IsChecked() ); + + if( aSettings.sorting_column != -1 ) + { + if( auto* c = m_netsList->GetColumn( aSettings.sorting_column ) ) + c->SetSortOrder( aSettings.sort_order_asc ); + } FinishDialogSettings(); @@ -168,6 +919,9 @@ DIALOG_SELECT_NET_FROM_LIST::DIALOG_SELECT_NET_FROM_LIST( PCB_EDIT_FRAME* aParen #undef connect_event + // if the dialog is opened while something is highlighted on the board ... + OnBoardHighlightNetChanged( *m_brd ); + if( m_brd != nullptr ) m_brd->AddListener( this ); } @@ -175,6 +929,11 @@ DIALOG_SELECT_NET_FROM_LIST::DIALOG_SELECT_NET_FROM_LIST( PCB_EDIT_FRAME* aParen DIALOG_SELECT_NET_FROM_LIST::~DIALOG_SELECT_NET_FROM_LIST() { + // the displayed list elements are going to be deleted before the list view itself. + // in some cases it might still do queries on the data model, which would crash + // from now on. so just disassociate it. + m_netsList->AssociateModel( nullptr ); + #define disconnect_event( e, f ) \ m_frame->Disconnect( e, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST::f ), nullptr, this ) @@ -193,7 +952,26 @@ DIALOG_SELECT_NET_FROM_LIST::~DIALOG_SELECT_NET_FROM_LIST() DIALOG_SELECT_NET_FROM_LIST::SETTINGS DIALOG_SELECT_NET_FROM_LIST::Settings() const { - return { m_textCtrlFilter->GetValue(), m_cbShowZeroPad->IsChecked() }; + std::vector column_order( m_data_model->columnCount() ); + + for( unsigned int i = 0; i < column_order.size(); ++i ) + column_order[i] = m_netsList->GetColumn( i )->GetModelColumn(); + + auto* sorting_column = m_netsList->GetSortingColumn(); + + SETTINGS r; + r.filter_string = m_textCtrlFilter->GetValue(); + r.show_zero_pad_nets = m_cbShowZeroPad->IsChecked(); + r.group_by = m_groupBy->IsChecked(); + r.group_by_kind = m_groupByKind->GetSelection(); + r.group_by_text = m_groupByText->GetValue(); + r.sorting_column = sorting_column ? static_cast( sorting_column->GetModelColumn() ) : -1; + r.sort_order_asc = sorting_column ? sorting_column->IsSortOrderAscending() : true; + r.column_order = column_order; + r.const_via_length = m_constViaLengthValue; + r.via_length_type = m_viaLengthType->GetSelection(); + + return r; } @@ -208,8 +986,9 @@ void DIALOG_SELECT_NET_FROM_LIST::onUnitsChanged( wxCommandEvent& event ) { this->m_units = m_frame->GetUserUnits(); - buildNetsList(); - m_netsList->Refresh(); + m_constViaLength->SetValue( formatLength( m_constViaLengthValue ) ); + + m_data_model->updateAllItems(); event.Skip(); } @@ -225,8 +1004,6 @@ void DIALOG_SELECT_NET_FROM_LIST::onBoardChanged( wxCommandEvent& event ) if( m_brd != nullptr ) m_brd->AddListener( this ); - m_wasSelected = false; - buildNetsList(); m_netsList->Refresh(); @@ -238,11 +1015,18 @@ bool DIALOG_SELECT_NET_FROM_LIST::netFilterMatches( NETINFO_ITEM* aNet ) const { // Note: the filtering is case insensitive. - if( m_netFilter.GetPattern().IsEmpty() ) + if( m_netFilter.empty() ) return true; - return m_netFilter.Find( UnescapeString( aNet->GetNetname() ).Upper() ) - != EDA_PATTERN_NOT_FOUND; + auto net_str = UnescapeString( aNet->GetNetname() ).Upper(); + + for( auto&& f : m_netFilter ) + { + if( f->Find( net_str ) ) + return true; + } + + return false; } @@ -291,45 +1075,21 @@ std::vector DIALOG_SELECT_NET_FROM_LIST::relevantConnectivityItems() c } -DIALOG_SELECT_NET_FROM_LIST::ROW_DESC DIALOG_SELECT_NET_FROM_LIST::findRow( int aNetCode ) -{ - return findRow( m_brd->FindNet( aNetCode ) ); -} - - -DIALOG_SELECT_NET_FROM_LIST::ROW_DESC DIALOG_SELECT_NET_FROM_LIST::findRow( NETINFO_ITEM* aNet ) -{ - auto i = std::lower_bound( m_list_items_by_net.begin(), m_list_items_by_net.end(), aNet, - LIST_ITEM_NET_CMP_LESS( m_list_items ) ); - - if( i != m_list_items_by_net.end() && m_list_items[*i].m_net == aNet ) - return { static_cast( *i ), m_list_items.begin() + *i, i }; - else - return {}; -} - - -void DIALOG_SELECT_NET_FROM_LIST::deleteRow( const ROW_DESC& aRow ) +void DIALOG_SELECT_NET_FROM_LIST::updateDisplayedRowValues( const OPT& aRow ) { if( !aRow ) return; - m_netsList->DeleteItem( aRow.row_num ); - m_list_items.erase( aRow.by_row ); + wxDataViewItemArray sel; + m_netsList->GetSelections( sel ); - std::iter_swap( aRow.by_net, m_list_items_by_net.end() - 1 ); - m_list_items_by_net.pop_back(); + m_data_model->updateItem( aRow ); - std::sort( m_list_items_by_net.begin(), m_list_items_by_net.end(), - LIST_ITEM_NET_CMP_LESS( m_list_items ) ); -} - - -void DIALOG_SELECT_NET_FROM_LIST::setValue( const ROW_DESC& aRow, const COLUMN_ID& aCol, - wxString aVal ) -{ - if( aRow ) - m_netsList->SetValue( aVal, aRow.row_num, aCol.col_num ); + if( !sel.IsEmpty() ) + { + m_netsList->SetSelections( sel ); + m_netsList->EnsureVisible( sel.Item( 0 ) ); + } } @@ -351,9 +1111,9 @@ wxString DIALOG_SELECT_NET_FROM_LIST::formatCount( unsigned int aValue ) const } -wxString DIALOG_SELECT_NET_FROM_LIST::formatLength( int aValue ) const +wxString DIALOG_SELECT_NET_FROM_LIST::formatLength( int64_t aValue ) const { - return MessageTextFromValue( GetUserUnits(), aValue ); + return MessageTextFromValue( GetUserUnits(), static_cast( aValue ) ); } @@ -363,53 +1123,36 @@ void DIALOG_SELECT_NET_FROM_LIST::OnBoardItemAdded( BOARD& aBoard, BOARD_ITEM* a { // a new net has been added to the board. add it to our list if it // passes the netname filter test. + if( netFilterMatches( net ) ) { - m_list_items.emplace_back( net ); - m_list_items_by_net.push_back( m_list_items.size() - 1 ); + auto&& new_item = std::make_unique( net ); - std::sort( m_list_items_by_net.begin(), m_list_items_by_net.end(), - LIST_ITEM_NET_CMP_LESS( m_list_items ) ); + // the new net could have some pads already assigned, count them. + new_item->setPadCount( m_brd->GetNodesCount( net->GetNet() ) ); - LIST_ITEM& new_i = m_list_items.back(); - new_i.m_pad_count = m_brd->GetNodesCount( net->GetNet() ); - - wxVector new_row( 7 ); - new_row[COLUMN_NET] = formatNetCode( net ); - new_row[COLUMN_NAME] = formatNetName( net ); - new_row[COLUMN_PAD_COUNT] = formatCount( new_i.m_pad_count ); - new_row[COLUMN_VIA_COUNT] = formatCount( new_i.m_via_count ); - new_row[COLUMN_BOARD_LENGTH] = formatLength( new_i.m_board_wire_length ); - new_row[COLUMN_CHIP_LENGTH] = formatLength( new_i.m_chip_wire_length ); - new_row[COLUMN_TOTAL_LENGTH] = formatLength( new_i.m_total_length ); - - m_netsList->AppendItem( new_row ); + m_data_model->addItem( std::move( new_item ) ); } - - return; } - else if( BOARD_CONNECTED_ITEM* i = dynamic_cast( aBoardItem ) ) + else if( auto* i = dynamic_cast( aBoardItem ) ) { - const ROW_DESC& r = findRow( i->GetNet() ); + auto r = m_data_model->findItem( i->GetNet() ); if( r ) { // try to handle frequent operations quickly. - if( TRACK* track = dynamic_cast( i ) ) + if( auto* track = dynamic_cast( i ) ) { int len = track->GetLength(); - r.by_row->m_board_wire_length += len; - r.by_row->m_total_length += len; - - setValue( r, COLUMN_BOARD_LENGTH, formatLength( r.by_row->m_board_wire_length ) ); - setValue( r, COLUMN_TOTAL_LENGTH, formatLength( r.by_row->m_total_length ) ); + ( **r )->addBoardWireLength( len ); if( track->Type() == PCB_VIA_T ) { - r.by_row->m_via_count += 1; - setValue( r, COLUMN_VIA_COUNT, formatCount( r.by_row->m_via_count ) ); + ( **r )->addViaCount( 1 ); + ( **r )->addViaLength( calculateViaLength( track ) ); } + updateDisplayedRowValues( r ); return; } } @@ -417,55 +1160,83 @@ void DIALOG_SELECT_NET_FROM_LIST::OnBoardItemAdded( BOARD& aBoard, BOARD_ITEM* a // resort to generic slower net update otherwise. updateNet( i->GetNet() ); } + else if( auto* mod = dynamic_cast( aBoardItem ) ) + { + for( const D_PAD* pad : mod->Pads() ) + { + auto r = m_data_model->findItem( pad->GetNet() ); + + if( !r ) + { + // if show-zero-pads is off, we might not have this net + // in our list yet, so add it first. + // notice that at this point we are very certain that this net + // will have at least one pad. + + if( netFilterMatches( pad->GetNet() ) ) + r = m_data_model->addItem( std::make_unique( pad->GetNet() ) ); + } + + if( r ) + { + int len = pad->GetPadToDieLength(); + ( **r )->addPadCount( 1 ); + ( **r )->addChipWireLength( len ); + + if( ( **r )->padCount() == 0 && !m_cbShowZeroPad->IsChecked() ) + m_data_model->deleteItem( r ); + else + updateDisplayedRowValues( r ); + } + } + } } void DIALOG_SELECT_NET_FROM_LIST::OnBoardItemRemoved( BOARD& aBoard, BOARD_ITEM* aBoardItem ) { - if( NETINFO_ITEM* net = dynamic_cast( aBoardItem ) ) + if( auto* net = dynamic_cast( aBoardItem ) ) { - deleteRow( findRow( net ) ); - return; + m_data_model->deleteItem( m_data_model->findItem( net ) ); } - else if( MODULE* mod = dynamic_cast( aBoardItem ) ) + else if( auto* mod = dynamic_cast( aBoardItem ) ) { for( const D_PAD* pad : mod->Pads() ) { - const ROW_DESC& r = findRow( pad->GetNet() ); + auto r = m_data_model->findItem( pad->GetNet() ); if( r ) { - r.by_row->m_pad_count -= 1; + int len = pad->GetPadToDieLength(); + ( **r )->subPadCount( 1 ); + ( **r )->subChipWireLength( len ); - if( r.by_row->m_pad_count == 0 && !m_cbShowZeroPad->IsChecked() ) - deleteRow( r ); + if( ( **r )->padCount() == 0 && !m_cbShowZeroPad->IsChecked() ) + m_data_model->deleteItem( r ); else - setValue( r, COLUMN_PAD_COUNT, formatCount( r.by_row->m_pad_count ) ); + updateDisplayedRowValues( r ); } } } - else if( BOARD_CONNECTED_ITEM* i = dynamic_cast( aBoardItem ) ) + else if( auto* i = dynamic_cast( aBoardItem ) ) { - const ROW_DESC& r = findRow( i->GetNet() ); + auto r = m_data_model->findItem( i->GetNet() ); if( r ) { // try to handle frequent operations quickly. - if( TRACK* track = dynamic_cast( i ) ) + if( auto* track = dynamic_cast( i ) ) { int len = track->GetLength(); - r.by_row->m_board_wire_length -= len; - r.by_row->m_total_length -= len; - - setValue( r, COLUMN_BOARD_LENGTH, formatLength( r.by_row->m_board_wire_length ) ); - setValue( r, COLUMN_TOTAL_LENGTH, formatLength( r.by_row->m_total_length ) ); + ( **r )->subBoardWireLength( len ); if( track->Type() == PCB_VIA_T ) { - r.by_row->m_via_count -= 1; - setValue( r, COLUMN_VIA_COUNT, formatCount( r.by_row->m_via_count ) ); + ( **r )->subViaCount( 1 ); + ( **r )->subViaLength( calculateViaLength( track ) ); } + updateDisplayedRowValues( r ); return; } @@ -491,8 +1262,24 @@ void DIALOG_SELECT_NET_FROM_LIST::OnBoardHighlightNetChanged( BOARD& aBoard ) { if( !m_brd->IsHighLightNetON() ) m_netsList->UnselectAll(); - else if( !m_brd->GetHighLightNetCodes().empty() ) - HighlightNet( m_brd->FindNet( *m_brd->GetHighLightNetCodes().begin() ) ); + else + { + const auto& selected_codes = m_brd->GetHighLightNetCodes(); + + wxDataViewItemArray new_selection; + new_selection.Alloc( selected_codes.size() ); + + for( int code : selected_codes ) + { + if( auto r = m_data_model->findItem( code ) ) + new_selection.Add( wxDataViewItem( &***r ) ); + } + + m_netsList->SetSelections( new_selection ); + + if( !new_selection.IsEmpty() ) + m_netsList->EnsureVisible( new_selection.Item( 0 ) ); + } } @@ -506,93 +1293,156 @@ void DIALOG_SELECT_NET_FROM_LIST::OnBoardNetSettingsChanged( BOARD& aBoard ) void DIALOG_SELECT_NET_FROM_LIST::updateNet( NETINFO_ITEM* aNet ) { // something for the specified net has changed, update that row. + // ignore nets that are not in our list because the filter doesn't match. + if( !netFilterMatches( aNet ) ) + { + m_data_model->deleteItem( m_data_model->findItem( aNet ) ); return; + } // if the net had no pads before, it might not be in the displayed list yet. // if it had pads and now doesn't anymore, we might need to remove it from the list. - auto cur_net_row = findRow( aNet ); + auto cur_net_row = m_data_model->findItem( aNet ); const unsigned int node_count = m_brd->GetNodesCount( aNet->GetNet() ); if( node_count == 0 && !m_cbShowZeroPad->IsChecked() ) { - deleteRow( cur_net_row ); + m_data_model->deleteItem( cur_net_row ); return; } - std::vector all_cn_items = relevantConnectivityItems(); - - LIST_ITEM list_item( aNet ); - list_item.m_pad_count = node_count; - - const auto cn_items = std::equal_range( all_cn_items.begin(), all_cn_items.end(), - aNet->GetNet(), NETCODE_CMP_LESS() ); - - for( auto i = cn_items.first; i != cn_items.second; ++i ) - { - BOARD_CONNECTED_ITEM* item = ( *i )->Parent(); - - if( item->Type() == PCB_PAD_T ) - list_item.m_chip_wire_length += static_cast( item )->GetPadToDieLength(); - - else if( TRACK* track = dynamic_cast( item ) ) - { - list_item.m_board_wire_length += track->GetLength(); - - if( item->Type() == PCB_VIA_T ) - list_item.m_via_count += 1; - } - } - - list_item.m_total_length = list_item.m_board_wire_length + list_item.m_chip_wire_length; + auto list_item = buildNewItem( aNet, node_count, relevantConnectivityItems() ); if( !cur_net_row ) + m_data_model->addItem( std::move( list_item ) ); + + else if( ( **cur_net_row )->netName() != list_item->netName() ) { - m_list_items.push_back( list_item ); - m_list_items_by_net.push_back( m_list_items.size() - 1 ); - std::sort( m_list_items_by_net.begin(), m_list_items_by_net.end(), - LIST_ITEM_NET_CMP_LESS( m_list_items ) ); - - wxVector new_row( 7 ); - new_row[COLUMN_NET] = formatNetCode( aNet ); - new_row[COLUMN_NAME] = formatNetName( aNet ); - new_row[COLUMN_PAD_COUNT] = formatCount( list_item.m_pad_count ); - new_row[COLUMN_VIA_COUNT] = formatCount( list_item.m_via_count ); - new_row[COLUMN_BOARD_LENGTH] = formatLength( list_item.m_board_wire_length ); - new_row[COLUMN_CHIP_LENGTH] = formatLength( list_item.m_chip_wire_length ); - new_row[COLUMN_TOTAL_LENGTH] = formatLength( list_item.m_total_length ); - - m_netsList->AppendItem( new_row ); + // if the name has changed, it might require re-grouping. + // it's easier to remove and re-insert it + m_data_model->deleteItem( cur_net_row ); + m_data_model->addItem( std::move( list_item ) ); } else { - *cur_net_row.by_row = list_item; + // update fields only + ( **cur_net_row )->setPadCount( list_item->padCount() ); + ( **cur_net_row )->setViaCount( list_item->viaCount() ); + ( **cur_net_row )->setBoardWireLength( list_item->boardWireLength() ); + ( **cur_net_row )->setChipWireLength( list_item->chipWireLength() ); - setValue( cur_net_row, COLUMN_PAD_COUNT, formatCount( list_item.m_pad_count ) ); - setValue( cur_net_row, COLUMN_VIA_COUNT, formatCount( list_item.m_via_count ) ); - setValue( cur_net_row, COLUMN_BOARD_LENGTH, formatLength( list_item.m_board_wire_length ) ); - setValue( cur_net_row, COLUMN_CHIP_LENGTH, formatLength( list_item.m_chip_wire_length ) ); - setValue( cur_net_row, COLUMN_TOTAL_LENGTH, formatLength( list_item.m_total_length ) ); + updateDisplayedRowValues( cur_net_row ); } } +unsigned int DIALOG_SELECT_NET_FROM_LIST::calculateViaLength( const TRACK* aTrack ) const +{ + const VIA& via = dynamic_cast( *aTrack ); + + if( m_viaLengthType->GetSelection() == 0 ) + { + return m_constViaLengthValue; + } + else if( m_viaLengthType->GetSelection() == 1 ) + { + // calculate the via length individually from the board stackup and + // via's start and end layer. + const BOARD_STACKUP& stackup = m_brd->GetDesignSettings().GetStackupDescriptor(); + + std::pair layer_dist[2] = { std::make_pair( via.TopLayer(), 0 ), + std::make_pair( via.BottomLayer(), 0 ) }; + + for( const auto& i : stackup.GetList() ) + { + for( auto& j : layer_dist ) + { + if( j.first != UNDEFINED_LAYER ) + j.second += i->GetThickness(); + + if( j.first == i->GetBrdLayerId() ) + j.first = UNDEFINED_LAYER; + } + } + + return std::abs( layer_dist[0].second - layer_dist[1].second ); + } + else + return 0; +} + + +std::unique_ptr DIALOG_SELECT_NET_FROM_LIST::buildNewItem( + NETINFO_ITEM* aNet, unsigned int aPadCount, const std::vector& aCNItems ) +{ + auto new_item = std::make_unique( aNet ); + + new_item->setPadCount( aPadCount ); + + const auto cn_items = std::equal_range( + aCNItems.begin(), aCNItems.end(), aNet->GetNet(), NETCODE_CMP_LESS() ); + + for( auto i = cn_items.first; i != cn_items.second; ++i ) + { + auto item = ( *i )->Parent(); + + if( item->Type() == PCB_PAD_T ) + new_item->addChipWireLength( static_cast( item )->GetPadToDieLength() ); + + else if( auto* track = dynamic_cast( item ) ) + { + new_item->addBoardWireLength( track->GetLength() ); + + if( item->Type() == PCB_VIA_T ) + { + new_item->addViaCount( 1 ); + new_item->addViaLength( calculateViaLength( track ) ); + } + } + } + + return new_item; +} + + void DIALOG_SELECT_NET_FROM_LIST::buildNetsList() { - // when rebuilding the netlist, try to keep the row selection - const int prev_selected_row = m_netsList->GetSelectedRow(); - const int prev_selected_netcode = - prev_selected_row >= 0 ? m_list_items[prev_selected_row].m_net->GetNet() : -1; + m_in_build_nets_list = true; - m_netsList->DeleteAllItems(); - m_list_items.clear(); + // when rebuilding the netlist, try to keep the row selection + // FIXME: handle group selections, preserve expanded/collapsed group states + wxDataViewItemArray sel; + m_netsList->GetSelections( sel ); + + std::vector prev_selected_netcodes; + prev_selected_netcodes.reserve( sel.GetCount() ); + + for( unsigned int i = 0; i < sel.GetCount(); ++i ) + prev_selected_netcodes.push_back( + static_cast( sel.Item( i ).GetID() )->netCode() ); + + m_data_model->deleteAllItems(); + + std::vector> new_items; + + // for group mode 0,1 each group filter string represents one displayed group, + // so just add them first. for group mode 2,3 the groups are generated dynamically. + if( m_groupBy->IsChecked() + && ( m_groupByKind->GetSelection() == 0 || m_groupByKind->GetSelection() == 1 ) ) + { + for( unsigned int i = 0; i < m_groupFilter.size(); ++i ) + new_items.emplace_back( + std::make_unique( i, m_groupFilter[i]->GetPattern() ) ); + } std::vector prefiltered_cn_items = relevantConnectivityItems(); - // collect all nets which pass the filter string. + // collect all nets which pass the filter string and also remember the + // suffix after the filter match, if any. struct NET_INFO { int netcode; @@ -619,159 +1469,217 @@ void DIALOG_SELECT_NET_FROM_LIST::buildNetsList() std::vector nets; nets.reserve( m_brd->GetNetInfo().NetsByNetcode().size() ); - for( const std::pair& ni : m_brd->GetNetInfo().NetsByNetcode() ) + for( auto&& ni : m_brd->GetNetInfo().NetsByNetcode() ) { + if( ni.first == 0 ) + m_zero_netitem = ni.second; + if( netFilterMatches( ni.second ) ) nets.emplace_back( NET_INFO{ ni.first, ni.second, 0 } ); } // count the pads for each net. since the nets are sorted by netcode - // this way around is faster than using counting pads for each net. + // iterating over the modules' pads is faster. - for( MODULE* mod : m_brd->Modules() ) + for( auto&& mod : m_brd->Modules() ) { - for( D_PAD* pad : mod->Pads() ) + for( auto&& pad : mod->Pads() ) { - auto i = std::lower_bound( nets.begin(), nets.end(), pad->GetNetCode(), - NET_INFO_CMP_LESS() ); + auto i = std::lower_bound( + nets.begin(), nets.end(), pad->GetNetCode(), NET_INFO_CMP_LESS() ); if( i != nets.end() && i->netcode == pad->GetNetCode() ) i->pad_count += 1; } } - for( NET_INFO& ni : nets ) + for( auto&& ni : nets ) { - if( !m_cbShowZeroPad->IsChecked() && ni.pad_count == 0 ) - continue; - - m_list_items.emplace_back( ni.net ); - LIST_ITEM& list_item = m_list_items.back(); - - const auto cn_items = std::equal_range( prefiltered_cn_items.begin(), - prefiltered_cn_items.end(), ni.netcode, NETCODE_CMP_LESS() ); - - for( auto i = cn_items.first; i != cn_items.second; ++i ) - { - BOARD_CONNECTED_ITEM* item = ( *i )->Parent(); - - if( item->Type() == PCB_PAD_T ) - list_item.m_chip_wire_length += static_cast( item )->GetPadToDieLength(); - - else if( TRACK* track = dynamic_cast( item ) ) - { - list_item.m_board_wire_length += track->GetLength(); - - if( track->Type() == PCB_VIA_T ) - list_item.m_via_count += 1; - } - } - - list_item.m_pad_count = ni.pad_count; - list_item.m_total_length = list_item.m_board_wire_length + list_item.m_chip_wire_length; + if( m_cbShowZeroPad->IsChecked() || ni.pad_count > 0 ) + new_items.emplace_back( buildNewItem( ni.net, ni.pad_count, prefiltered_cn_items ) ); } - wxVector dataLine; - dataLine.resize( 7 ); - for( LIST_ITEM& i : m_list_items ) + m_data_model->addItems( std::move( new_items ) ); + + // try to restore the selected rows. set the ones that we can't find anymore to -1. + sel.Clear(); + + for( int& nc : prev_selected_netcodes ) { - dataLine[COLUMN_NET] = formatNetCode( i.m_net ); - dataLine[COLUMN_NAME] = formatNetName( i.m_net ); - dataLine[COLUMN_PAD_COUNT] = formatCount( i.m_pad_count ); - dataLine[COLUMN_VIA_COUNT] = formatCount( i.m_via_count ); - dataLine[COLUMN_BOARD_LENGTH] = formatLength( i.m_board_wire_length ); - dataLine[COLUMN_CHIP_LENGTH] = formatLength( i.m_chip_wire_length ); - dataLine[COLUMN_TOTAL_LENGTH] = formatLength( i.m_total_length ); - - m_netsList->AppendItem( dataLine ); - } - - m_list_items_by_net.clear(); - m_list_items_by_net.reserve( m_list_items.size() ); - - for( unsigned int i = 0; i < m_list_items.size(); ++i ) - m_list_items_by_net.push_back( i ); - - std::sort( m_list_items_by_net.begin(), m_list_items_by_net.end(), - LIST_ITEM_NET_CMP_LESS( m_list_items ) ); - - if( prev_selected_netcode == -1 ) - m_wasSelected = false; - else - { - const ROW_DESC& r = findRow( prev_selected_netcode ); + auto r = m_data_model->findItem( nc ); if( r ) - { - m_selection = r.by_row->m_net->GetNetname(); - m_wasSelected = true; - - wxDataViewItem i = m_netsList->RowToItem( r.row_num ); - m_netsList->Select( i ); - m_netsList->EnsureVisible( i ); - } + sel.Add( wxDataViewItem( &***r ) ); + else + nc = -1; } -} - -void DIALOG_SELECT_NET_FROM_LIST::HighlightNet( NETINFO_ITEM* aNet ) -{ - const ROW_DESC& r = findRow( aNet ); - - if( r ) + if( !sel.IsEmpty() ) { - wxDataViewItem i = m_netsList->RowToItem( r.row_num ); - m_netsList->Select( i ); - m_netsList->EnsureVisible( i ); + m_netsList->SetSelections( sel ); + m_netsList->EnsureVisible( sel.Item( 0 ) ); } else m_netsList->UnselectAll(); -} + prev_selected_netcodes.erase( + std::remove( prev_selected_netcodes.begin(), prev_selected_netcodes.end(), -1 ), + prev_selected_netcodes.end() ); -void DIALOG_SELECT_NET_FROM_LIST::highlightNetOnBoard( NETINFO_ITEM* aNet ) const -{ - int netCode = aNet != nullptr ? aNet->GetNet() : -1; + m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings()->SetHighlight( false ); - KIGFX::RENDER_SETTINGS *render = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings(); - render->SetHighlight( netCode >= 0, netCode ); + for( auto&& i : prev_selected_netcodes ) + m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings()->SetHighlight( true, i, true ); m_frame->GetCanvas()->GetView()->UpdateAllLayersColor(); m_frame->GetCanvas()->Refresh(); + + m_in_build_nets_list = false; } -void DIALOG_SELECT_NET_FROM_LIST::onFilterChange( wxCommandEvent& event ) +void DIALOG_SELECT_NET_FROM_LIST::onFilterChange( wxCommandEvent& aEvent ) { - m_netFilter.SetPattern( m_textCtrlFilter->GetValue().Upper() ); - buildNetsList(); + wxStringTokenizer filters( m_textCtrlFilter->GetValue().Upper(), "," ); + m_netFilter.clear(); + + while( filters.HasMoreTokens() ) + { + auto t = filters.GetNextToken(); + t.Trim( false ); + t.Trim( true ); + + if( !t.IsEmpty() ) + { + m_netFilter.emplace_back( std::make_unique() ); + m_netFilter.back()->SetPattern( t ); + } + } + + wxStringTokenizer group_filters( m_groupByText->GetValue(), "," ); + m_groupFilter.clear(); + + while( group_filters.HasMoreTokens() ) + { + auto t = group_filters.GetNextToken(); + t.Trim( false ); + t.Trim( true ); + + if( !t.IsEmpty() ) + { + if( m_groupByKind->GetSelection() == 0 || m_groupByKind->GetSelection() == 2 ) + { + // type 2: wildcard match, use the matching substring as a group key. + // the number of groups is determined dynamically by the number of + // resulting matches in the whole set. + m_groupFilter.emplace_back( std::make_unique() ); + m_groupFilter.back()->SetPattern( t ); + } + else if( m_groupByKind->GetSelection() == 1 || m_groupByKind->GetSelection() == 3 ) + { + // type 3: regex match, use the matching substring as a group key. + // the number of groups is determined dynamically by the number of + // resulting matches in the whole set. + m_groupFilter.emplace_back( std::make_unique() ); + m_groupFilter.back()->SetPattern( t ); + } + } + } + + if( !m_filter_change_no_rebuild ) + buildNetsList(); +} + + +void DIALOG_SELECT_NET_FROM_LIST::onViaLengthChange( wxCommandEvent& event ) +{ + m_constViaLengthValue = ValueFromString( GetUserUnits(), m_constViaLength->GetValue() ); + + // allow editing of the constant via length value only when in constant via length mode. + m_constViaLength->Enable( m_viaLengthType->GetSelection() == 0 ); + + if( !m_filter_change_no_rebuild ) + buildNetsList(); +} + + +void DIALOG_SELECT_NET_FROM_LIST::onGroupsFirstChanged( wxCommandEvent& event ) +{ + m_data_model->setSortGroupsFirst( m_groupsFirst->IsChecked() ); } void DIALOG_SELECT_NET_FROM_LIST::onSelChanged( wxDataViewEvent& ) { - int selected_row = m_netsList->GetSelectedRow(); + onSelChanged(); +} - if( selected_row >= 0 && selected_row < static_cast( m_list_items.size() ) ) - { - NETINFO_ITEM* net = m_list_items[selected_row].m_net; - m_selection = net->GetNetname(); - m_wasSelected = true; - highlightNetOnBoard( net ); +void DIALOG_SELECT_NET_FROM_LIST::onSelChanged() +{ + // ignore selection changes while the whole list is being rebuilt. + if( m_in_build_nets_list ) return; - } - highlightNetOnBoard( nullptr ); - m_wasSelected = false; + bool enable_rename_button = false; + bool enable_delete_button = false; + + if( m_netsList->HasSelection() ) + { + wxDataViewItemArray sel; + m_netsList->GetSelections( sel ); + + auto&& ps = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings(); + + ps->SetHighlight( false ); + + enable_rename_button = sel.GetCount() == 1; + enable_delete_button = true; + + for( unsigned int i = 0; i < sel.GetCount(); ++i ) + { + auto&& ii = static_cast( sel.Item( i ).GetID() ); + + if( ii->isGroup() ) + { + enable_rename_button = false; + + for( auto c = ii->childrenBegin(), end = ii->childrenEnd(); c != end; ++c ) + ps->SetHighlight( true, ( *c )->netCode(), true ); + } + else + ps->SetHighlight( true, ii->netCode(), true ); + } + } + else + m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings()->SetHighlight( false ); + + m_frame->GetCanvas()->GetView()->UpdateAllLayersColor(); + m_frame->GetCanvas()->Refresh(); + + m_renameNet->Enable( enable_rename_button ); + m_deleteNet->Enable( enable_delete_button ); +} + + +void DIALOG_SELECT_NET_FROM_LIST::onSortingChanged( wxDataViewEvent& aEvent ) +{ + // FIXME: Whenever the sort criteria changes (sorting column) + // the visible row-numers of the selection get preserved, not the actual + // elements. Don't know at the moment how to preserve the selection, + // so just clear it for now. + + m_netsList->UnselectAll(); + + m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings()->SetHighlight( false ); + m_frame->GetCanvas()->GetView()->UpdateAllLayersColor(); + m_frame->GetCanvas()->Refresh(); } void DIALOG_SELECT_NET_FROM_LIST::adjustListColumns() { - int w0, w1, w2, w3, w4, w5, w6; - /** * Calculating optimal width of the first (Net) and the last (Pad Count) columns. * That width must be enough to fit column header label and be not less than width of @@ -779,42 +1687,62 @@ void DIALOG_SELECT_NET_FROM_LIST::adjustListColumns() */ wxClientDC dc( GetParent() ); - int h, minw, minw_col0; + + int h, minw, minw_col0, minw_col1; + int w0, w1, w2, w3, w4, w5, w6, w7; dc.GetTextExtent( COLUMN_NET.display_name, &w0, &h ); + dc.GetTextExtent( COLUMN_NAME.display_name, &minw_col1, &h ); dc.GetTextExtent( COLUMN_PAD_COUNT.display_name, &w2, &h ); dc.GetTextExtent( COLUMN_VIA_COUNT.display_name, &w3, &h ); - dc.GetTextExtent( COLUMN_BOARD_LENGTH.display_name, &w4, &h ); - dc.GetTextExtent( COLUMN_CHIP_LENGTH.display_name, &w5, &h ); - dc.GetTextExtent( COLUMN_TOTAL_LENGTH.display_name, &w6, &h ); + dc.GetTextExtent( COLUMN_VIA_LENGTH.display_name, &w4, &h ); + dc.GetTextExtent( COLUMN_BOARD_LENGTH.display_name, &w5, &h ); + dc.GetTextExtent( COLUMN_CHIP_LENGTH.display_name, &w6, &h ); + dc.GetTextExtent( COLUMN_TOTAL_LENGTH.display_name, &w7, &h ); dc.GetTextExtent( "00000,000 mm", &minw, &h ); dc.GetTextExtent( "00000", &minw_col0, &h ); // Considering left and right margins. // For wxRenderGeneric it is 5px. - w0 = std::max( w0 + 10, minw_col0 ); - w2 = w2 + 10; - w3 = w3 + 10; - w4 = std::max( w4 + 10, minw ); - w5 = std::max( w5 + 10, minw ); - w6 = std::max( w6 + 10, minw ); + // Also account for the sorting arrow in the column header. + // Column 0 also needs space for any potential expander icons. + const int extra_width = 30; - m_netsList->GetColumn( 0 )->SetWidth( w0 ); - m_netsList->GetColumn( 2 )->SetWidth( w2 ); - m_netsList->GetColumn( 3 )->SetWidth( w3 ); - m_netsList->GetColumn( 4 )->SetWidth( w4 ); - m_netsList->GetColumn( 5 )->SetWidth( w5 ); - m_netsList->GetColumn( 6 )->SetWidth( w6 ); + w0 = std::max( w0, minw_col0 ) + extra_width; + minw_col1 = minw_col1 + extra_width; + w2 = w2 + extra_width; + w3 = w3 + extra_width; + w4 = std::max( w4 + extra_width, minw ); + w5 = std::max( w5 + extra_width, minw ); + w6 = std::max( w6 + extra_width, minw ); + w7 = std::max( w7 + extra_width, minw ); + + // the columns might have been reordered. we work on the column model numbers though. + std::vector column_order( m_data_model->columnCount() ); + + for( unsigned int i = 0; i < column_order.size(); ++i ) + column_order[m_netsList->GetColumn( i )->GetModelColumn()] = i; + + assert( column_order.size() == 8 ); + + m_netsList->GetColumn( column_order[0] )->SetWidth( w0 ); + m_netsList->GetColumn( column_order[1] )->SetMinWidth( minw_col1 ); + m_netsList->GetColumn( column_order[2] )->SetWidth( w2 ); + m_netsList->GetColumn( column_order[3] )->SetWidth( w3 ); + m_netsList->GetColumn( column_order[4] )->SetWidth( w4 ); + m_netsList->GetColumn( column_order[5] )->SetWidth( w5 ); + m_netsList->GetColumn( column_order[6] )->SetWidth( w6 ); + m_netsList->GetColumn( column_order[7] )->SetWidth( w7 ); // At resizing of the list the width of middle column (Net Names) changes only. int width = m_netsList->GetClientSize().x - 24; - w1 = width - w0 - w2 - w3 - w4 - w5 - w6; + w1 = width - w0 - w2 - w3 - w4 - w5 - w6 - w7; // Column 1 (net names) need a minimal width to display net names dc.GetTextExtent( "MMMMMMMMMMMMMMMM", &minw, &h ); w1 = std::max( w1, minw ); - m_netsList->GetColumn( 1 )->SetWidth( w1 ); + m_netsList->GetColumn( column_order[1] )->SetWidth( w1 ); m_netsList->Refresh(); } @@ -827,13 +1755,6 @@ void DIALOG_SELECT_NET_FROM_LIST::onListSize( wxSizeEvent& aEvent ) } -bool DIALOG_SELECT_NET_FROM_LIST::GetNetName( wxString& aName ) const -{ - aName = m_selection; - return m_wasSelected; -} - - void DIALOG_SELECT_NET_FROM_LIST::onAddNet( wxCommandEvent& aEvent ) { wxString newNetName; @@ -866,17 +1787,20 @@ void DIALOG_SELECT_NET_FROM_LIST::onAddNet( wxCommandEvent& aEvent ) m_brd->Add( newnet ); m_frame->OnModify(); - // We'll get an OnBoardItemChanged callback from this to update our listbox + // We'll get an OnBoardItemAdded callback from this to update our listbox } void DIALOG_SELECT_NET_FROM_LIST::onRenameNet( wxCommandEvent& aEvent ) { - int selected_row = m_netsList->GetSelectedRow(); - - if( selected_row >= 0 && selected_row < static_cast( m_list_items.size() ) ) + if( m_netsList->GetSelectedItemsCount() == 1 ) { - NETINFO_ITEM* net = m_list_items[selected_row].m_net; + auto&& sel = static_cast( m_netsList->GetSelection().GetID() ); + + if( sel->isGroup() ) + return; + + NETINFO_ITEM* net = sel->net(); wxString fullNetName = net->GetNetname(); wxString netPath; wxString shortNetName; @@ -926,11 +1850,31 @@ void DIALOG_SELECT_NET_FROM_LIST::onRenameNet( wxCommandEvent& aEvent ) } } + // the changed name might require re-grouping. remove and re-insert + // is easier. + auto removed_item = m_data_model->deleteItem( m_data_model->findItem( net ) ); + net->SetNetname( fullNetName ); m_frame->OnModify(); - buildNetsList(); - m_netsList->Refresh(); + if( netFilterMatches( net ) ) + { + auto new_item = std::make_unique( net ); + new_item->setPadCount( removed_item->padCount() ); + new_item->setViaCount( removed_item->viaCount() ); + new_item->setBoardWireLength( removed_item->boardWireLength() ); + new_item->setChipWireLength( removed_item->chipWireLength() ); + + auto added_row = m_data_model->addItem( std::move( new_item ) ); + + wxDataViewItemArray new_sel; + new_sel.Add( wxDataViewItem( &***added_row ) ); + m_netsList->SetSelections( new_sel ); + onSelChanged(); + } + else + DisplayInfoMessage( this, + _( "New net name does not match current filter and will disappear from the list." ) ); // Currently only tracks and pads have netname annotations and need to be redrawn, // but zones are likely to follow. Since we don't have a way to ask what is current, @@ -943,22 +1887,46 @@ void DIALOG_SELECT_NET_FROM_LIST::onRenameNet( wxCommandEvent& aEvent ) void DIALOG_SELECT_NET_FROM_LIST::onDeleteNet( wxCommandEvent& aEvent ) { - int selected_row = m_netsList->GetSelectedRow(); + if( !m_netsList->HasSelection() ) + return; - if( selected_row >= 0 && selected_row < static_cast( m_list_items.size() ) ) - { - NETINFO_ITEM* net = m_list_items[selected_row].m_net; + wxDataViewItemArray sel; + m_netsList->GetSelections( sel ); - if( m_list_items[selected_row].m_pad_count > 0 ) + auto delete_one = [this]( const LIST_ITEM* i ) { + if( i->padCount() == 0 + || IsOK( this, wxString::Format( _( "Net '%s' is in use. Delete anyway?" ), + i->netName() ) ) ) { - if( !IsOK( this, _( "Net is in use. Delete anyway?" ) ) ) - return; + m_brd->Remove( i->net() ); + m_frame->OnModify(); + + // We'll get an OnBoardItemRemoved callback from this to update our listbox } + }; - m_brd->Remove( net ); - m_frame->OnModify(); + for( unsigned int i = 0; i < sel.GetCount(); ++i ) + { + auto&& ii = static_cast( sel.Item( i ).GetID() ); - // We'll get an OnBoardItemChanged callback from this to update our listbox + if( ii->isGroup() ) + { + if( ii->childrenCount() != 0 + && IsOK( this, wxString::Format( _( "Delete all nets in group '%s'?" ), + ii->groupName() ) ) ) + { + // we can't be iterating the children container and deleting items from + // it at the same time. thus take a copy of it first. + std::vector children; + children.reserve( ii->childrenCount() ); + std::copy( ii->childrenBegin(), ii->childrenEnd(), std::back_inserter( children ) ); + + for( auto&& c : children ) + delete_one( c ); + } + } + else + delete_one( ii ); } } @@ -976,31 +1944,37 @@ void DIALOG_SELECT_NET_FROM_LIST::onReport( wxCommandEvent& aEvent ) f.Create(); - int rows = m_netsList->GetItemCount(); wxString txt; // Print Header: - txt.Printf( "\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";\"%s\";", - _( "Net Id" ), _( "Net name" ), - _( "Pad count" ), _( "Via count" ), - _( "Board length" ), _( "Die length" ), _( "Net length" ) ); + for( auto&& col : m_data_model->columnDesc() ) + txt += '"' + col.csv_name + "\";"; + f.AddLine( txt ); // Print list of nets: - for( int row = 1; row < rows; row++ ) - { - txt.Printf( "%s;\"%s\";%s;%s;%s;%s;%s;", - m_netsList->GetTextValue( row, COLUMN_NET ), - m_netsList->GetTextValue( row, COLUMN_NAME ), - m_netsList->GetTextValue( row, COLUMN_PAD_COUNT ), - m_netsList->GetTextValue( row, COLUMN_VIA_COUNT ), - m_netsList->GetTextValue( row, COLUMN_BOARD_LENGTH ), - m_netsList->GetTextValue( row, COLUMN_CHIP_LENGTH ), - m_netsList->GetTextValue( row, COLUMN_TOTAL_LENGTH ) ); + const unsigned int num_rows = m_data_model->itemCount(); - f.AddLine( txt ); - } + for( unsigned int row = 0; row < num_rows; row++ ) + { + auto& i = m_data_model->itemAt( row ); - f.Write(); - f.Close(); + if( i.isGroup() || i.netCode() == 0 ) + continue; + + txt = ""; + + for( auto&& col : m_data_model->columnDesc() ) + { + if( col.csv_flags & COLUMN_DESC::CSV_QUOTE ) + txt += '"' + m_data_model->valueAt( col.num, row ).GetString() + "\";"; + else + txt += m_data_model->valueAt( col.num, row ).GetString() + ';'; + } + + f.AddLine( txt ); + } + + f.Write(); + f.Close(); } diff --git a/pcbnew/dialogs/dialog_select_net_from_list.h b/pcbnew/dialogs/dialog_select_net_from_list.h index e936dca070..196a4f966e 100644 --- a/pcbnew/dialogs/dialog_select_net_from_list.h +++ b/pcbnew/dialogs/dialog_select_net_from_list.h @@ -25,13 +25,14 @@ #pragma once +#include #include -#include class PCB_EDIT_FRAME; class NETINFO_ITEM; class BOARD; class CN_ITEM; +class EDA_PATTERN_MATCH; class DIALOG_SELECT_NET_FROM_LIST : public DIALOG_SELECT_NET_FROM_LIST_BASE, public BOARD_LISTENER { @@ -40,6 +41,16 @@ public: { wxString filter_string; bool show_zero_pad_nets = true; + bool group_by = false; + int group_by_kind = 0; + wxString group_by_text; + int sorting_column = -1; + bool sort_order_asc = true; + + std::vector column_order; + + unsigned int const_via_length = 0; + int via_length_type = 0; }; DIALOG_SELECT_NET_FROM_LIST( PCB_EDIT_FRAME* aParent, const SETTINGS& aSettings ); @@ -47,16 +58,6 @@ public: SETTINGS Settings() const; - // returns true if a net was selected, and its name in aName - bool GetNetName( wxString& aName ) const; - - /** - * Visually highlights a net in the list view. - * @param aNet is the net item to be highlighted. Nullptr will unhighlight - * any currently highlighted net. - */ - void HighlightNet( NETINFO_ITEM* aNet ); - virtual void OnBoardItemAdded( BOARD& aBoard, BOARD_ITEM* aBoardItem ) override; virtual void OnBoardItemRemoved( BOARD& aBoard, BOARD_ITEM* aBoardItem ) override; virtual void OnBoardNetSettingsChanged( BOARD& aBoard ) override; @@ -64,41 +65,47 @@ public: virtual void OnBoardHighlightNetChanged( BOARD& aBoard ) override; private: - struct COLUMN_ID; - static const COLUMN_ID COLUMN_NET; - static const COLUMN_ID COLUMN_NAME; - static const COLUMN_ID COLUMN_PAD_COUNT; - static const COLUMN_ID COLUMN_VIA_COUNT; - static const COLUMN_ID COLUMN_BOARD_LENGTH; - static const COLUMN_ID COLUMN_CHIP_LENGTH; - static const COLUMN_ID COLUMN_TOTAL_LENGTH; + struct COLUMN_DESC; + class LIST_ITEM; + struct LIST_ITEM_NETCODE_CMP_LESS; - struct ROW_DESC; + using LIST_ITEM_ITER = std::vector>::iterator; + using LIST_ITEM_CONST_ITER = std::vector>::const_iterator; - ROW_DESC findRow( NETINFO_ITEM* aNet ); - ROW_DESC findRow( int aNetCode ); - - void deleteRow( const ROW_DESC& aRow ); - void setValue( const ROW_DESC& aRow, const COLUMN_ID& aCol, wxString aVal ); + static const COLUMN_DESC COLUMN_NET; + static const COLUMN_DESC COLUMN_NAME; + static const COLUMN_DESC COLUMN_PAD_COUNT; + static const COLUMN_DESC COLUMN_VIA_COUNT; + static const COLUMN_DESC COLUMN_VIA_LENGTH; + static const COLUMN_DESC COLUMN_BOARD_LENGTH; + static const COLUMN_DESC COLUMN_CHIP_LENGTH; + static const COLUMN_DESC COLUMN_TOTAL_LENGTH; wxString formatNetCode( const NETINFO_ITEM* aNet ) const; wxString formatNetName( const NETINFO_ITEM* aNet ) const; wxString formatCount( unsigned int aValue ) const; - wxString formatLength( int aValue ) const; + wxString formatLength( int64_t aValue ) const; std::vector relevantConnectivityItems() const; bool netFilterMatches( NETINFO_ITEM* aNet ) const; void updateNet( NETINFO_ITEM* aNet ); - void highlightNetOnBoard( NETINFO_ITEM* aNet ) const; + unsigned int calculateViaLength( const TRACK* ) const; + void onGroupsFirstChanged( wxCommandEvent& event ) override; void onSelChanged( wxDataViewEvent& event ) override; + void onSelChanged(); + void onSortingChanged( wxDataViewEvent& event ) override; void onFilterChange( wxCommandEvent& event ) override; + void onViaLengthChange( wxCommandEvent& event ) override; void onListSize( wxSizeEvent& event ) override; void onAddNet( wxCommandEvent& event ) override; void onRenameNet( wxCommandEvent& event ) override; void onDeleteNet( wxCommandEvent& event ) override; void onReport( wxCommandEvent& event ) override; + std::unique_ptr buildNewItem( + NETINFO_ITEM* aNet, unsigned int aPadCount, const std::vector& aCNItems ); + void buildNetsList(); void adjustListColumns(); @@ -106,24 +113,22 @@ private: void onUnitsChanged( wxCommandEvent& event ); void onBoardChanged( wxCommandEvent& event ); - // in addition to the displayed list data, we also keep some auxiliary - // data for each list item in order to speed up update of the displayed list. - struct LIST_ITEM; - struct LIST_ITEM_NET_CMP_LESS; + void updateDisplayedRowValues( const OPT& aRow ); - // primary vector, sorted by rows - std::vector m_list_items; + // special zero-netcode item. unconnected pads etc might use different + // (dummy) NETINFO_ITEM. redirect all of them to this item, which we get + // from the board object in buildNetsList. + NETINFO_ITEM* m_zero_netitem; - // we can't keep pointers to the elements in the primary vector because - // the underlyng storage might change when elements are added or removed. - // keep indices instead and look the them up in m_list_items. - std::vector m_list_items_by_net; + std::vector> m_netFilter; + std::vector> m_groupFilter; - - EDA_PATTERN_MATCH_WILDCARD m_netFilter; - - wxString m_selection; - bool m_wasSelected; BOARD* m_brd; PCB_EDIT_FRAME* m_frame; + bool m_in_build_nets_list = false; + bool m_filter_change_no_rebuild = false; + unsigned int m_constViaLengthValue = 0; + + class DATA_MODEL; + wxObjectDataPtr m_data_model; }; diff --git a/pcbnew/dialogs/dialog_select_net_from_list_base.cpp b/pcbnew/dialogs/dialog_select_net_from_list_base.cpp index b5d416d204..a61a13c570 100644 --- a/pcbnew/dialogs/dialog_select_net_from_list_base.cpp +++ b/pcbnew/dialogs/dialog_select_net_from_list_base.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Oct 26 2018) +// C++ code generated with wxFormBuilder (version 3.9.0 Oct 9 2020) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -34,12 +34,63 @@ DIALOG_SELECT_NET_FROM_LIST_BASE::DIALOG_SELECT_NET_FROM_LIST_BASE( wxWindow* pa bTopSizer->Add( m_cbShowZeroPad, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - bSizerMain->Add( bTopSizer, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); + bSizerMain->Add( bTopSizer, 0, wxEXPAND|wxLEFT|wxRIGHT, 5 ); - m_netsList = new wxDataViewListCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_HORIZ_RULES|wxDV_VERT_RULES ); + wxBoxSizer* bMidSizer; + bMidSizer = new wxBoxSizer( wxHORIZONTAL ); + + m_groupBy = new wxCheckBox( this, wxID_ANY, _("Group By"), wxDefaultPosition, wxDefaultSize, 0 ); + bMidSizer->Add( m_groupBy, 0, wxALIGN_CENTER|wxALL, 5 ); + + m_groupByKind = new wxComboBox( this, wxID_ANY, _("Wildcard"), wxDefaultPosition, wxSize( -1,-1 ), 0, NULL, wxCB_DROPDOWN|wxCB_READONLY|wxCB_SIMPLE ); + m_groupByKind->Append( _("Wildcard") ); + m_groupByKind->Append( _("RegEx") ); + m_groupByKind->Append( _("Wildcard Substr") ); + m_groupByKind->Append( _("RegEx Substr") ); + m_groupByKind->SetSelection( 0 ); + bMidSizer->Add( m_groupByKind, 0, wxALIGN_CENTER|wxALL|wxFIXED_MINSIZE, 5 ); + + m_groupByText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + bMidSizer->Add( m_groupByText, 1, wxBOTTOM|wxEXPAND|wxRIGHT|wxTOP, 5 ); + + + bMidSizer->Add( 0, 0, 0, wxEXPAND|wxRIGHT|wxLEFT, 10 ); + + m_groupsFirst = new wxCheckBox( this, wxID_ANY, _("Sort groups first"), wxDefaultPosition, wxDefaultSize, 0 ); + m_groupsFirst->SetValue(true); + bMidSizer->Add( m_groupsFirst, 0, wxALIGN_CENTER|wxALL, 5 ); + + + bSizerMain->Add( bMidSizer, 0, wxEXPAND|wxLEFT|wxRIGHT, 5 ); + + wxBoxSizer* bMidSizer1; + bMidSizer1 = new wxBoxSizer( wxHORIZONTAL ); + + m_staticTextFilter1 = new wxStaticText( this, wxID_ANY, _("Via Length:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextFilter1->Wrap( -1 ); + bMidSizer1->Add( m_staticTextFilter1, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + m_viaLengthType = new wxComboBox( this, wxID_ANY, _("Constant"), wxDefaultPosition, wxSize( -1,-1 ), 0, NULL, wxCB_DROPDOWN|wxCB_READONLY|wxCB_SIMPLE ); + m_viaLengthType->Append( _("Constant") ); + m_viaLengthType->Append( _("From Stackup") ); + m_viaLengthType->SetSelection( 0 ); + bMidSizer1->Add( m_viaLengthType, 0, wxALIGN_CENTER|wxALL|wxFIXED_MINSIZE, 5 ); + + m_constViaLength = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 96,-1 ), 0 ); + m_constViaLength->SetMaxSize( wxSize( 96,-1 ) ); + + bMidSizer1->Add( m_constViaLength, 1, wxBOTTOM|wxEXPAND|wxRIGHT|wxTOP, 5 ); + + + bMidSizer1->Add( 0, 0, 0, wxEXPAND|wxRIGHT|wxLEFT, 10 ); + + + bSizerMain->Add( bMidSizer1, 0, wxEXPAND|wxLEFT|wxRIGHT, 5 ); + + m_netsList = new wxDataViewCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_HORIZ_RULES|wxDV_MULTIPLE|wxDV_VERT_RULES ); m_netsList->SetMinSize( wxSize( 500,300 ) ); - bSizerMain->Add( m_netsList, 1, wxEXPAND|wxRIGHT|wxLEFT, 10 ); + bSizerMain->Add( m_netsList, 1, wxEXPAND|wxLEFT|wxRIGHT, 10 ); wxBoxSizer* bSizerListButtons; bSizerListButtons = new wxBoxSizer( wxHORIZONTAL ); @@ -74,8 +125,6 @@ DIALOG_SELECT_NET_FROM_LIST_BASE::DIALOG_SELECT_NET_FROM_LIST_BASE( wxWindow* pa m_sdbSizer = new wxStdDialogButtonSizer(); m_sdbSizerOK = new wxButton( this, wxID_OK ); m_sdbSizer->AddButton( m_sdbSizerOK ); - m_sdbSizerCancel = new wxButton( this, wxID_CANCEL ); - m_sdbSizer->AddButton( m_sdbSizerCancel ); m_sdbSizer->Realize(); bSizerBottom->Add( m_sdbSizer, 1, wxALL|wxEXPAND, 5 ); @@ -93,6 +142,13 @@ DIALOG_SELECT_NET_FROM_LIST_BASE::DIALOG_SELECT_NET_FROM_LIST_BASE( wxWindow* pa // Connect Events m_textCtrlFilter->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onFilterChange ), NULL, this ); m_cbShowZeroPad->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onFilterChange ), NULL, this ); + m_groupBy->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onFilterChange ), NULL, this ); + m_groupByKind->Connect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onFilterChange ), NULL, this ); + m_groupByText->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onFilterChange ), NULL, this ); + m_groupsFirst->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onGroupsFirstChanged ), NULL, this ); + m_viaLengthType->Connect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onViaLengthChange ), NULL, this ); + m_constViaLength->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onViaLengthChange ), NULL, this ); + m_netsList->Connect( wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED, wxDataViewEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onSortingChanged ), NULL, this ); m_netsList->Connect( wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onSelChanged ), NULL, this ); m_netsList->Connect( wxEVT_SIZE, wxSizeEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onListSize ), NULL, this ); m_addNet->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onAddNet ), NULL, this ); @@ -106,6 +162,13 @@ DIALOG_SELECT_NET_FROM_LIST_BASE::~DIALOG_SELECT_NET_FROM_LIST_BASE() // Disconnect Events m_textCtrlFilter->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onFilterChange ), NULL, this ); m_cbShowZeroPad->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onFilterChange ), NULL, this ); + m_groupBy->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onFilterChange ), NULL, this ); + m_groupByKind->Disconnect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onFilterChange ), NULL, this ); + m_groupByText->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onFilterChange ), NULL, this ); + m_groupsFirst->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onGroupsFirstChanged ), NULL, this ); + m_viaLengthType->Disconnect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onViaLengthChange ), NULL, this ); + m_constViaLength->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onViaLengthChange ), NULL, this ); + m_netsList->Disconnect( wxEVT_COMMAND_DATAVIEW_COLUMN_SORTED, wxDataViewEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onSortingChanged ), NULL, this ); m_netsList->Disconnect( wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onSelChanged ), NULL, this ); m_netsList->Disconnect( wxEVT_SIZE, wxSizeEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onListSize ), NULL, this ); m_addNet->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_SELECT_NET_FROM_LIST_BASE::onAddNet ), NULL, this ); diff --git a/pcbnew/dialogs/dialog_select_net_from_list_base.fbp b/pcbnew/dialogs/dialog_select_net_from_list_base.fbp index 8be98ae369..eb910075b9 100644 --- a/pcbnew/dialogs/dialog_select_net_from_list_base.fbp +++ b/pcbnew/dialogs/dialog_select_net_from_list_base.fbp @@ -14,6 +14,7 @@ dialog_select_net_from_list_base 1000 none + 1 dialog_select_net_from_list_base @@ -25,6 +26,7 @@ 1 1 UI + 0 0 0 @@ -60,9 +62,9 @@ none 5 - wxEXPAND|wxTOP|wxRIGHT|wxLEFT + wxEXPAND|wxLEFT|wxRIGHT 0 - + bTopSizer wxHORIZONTAL @@ -193,11 +195,11 @@ onFilterChange - + 10 wxEXPAND|wxRIGHT|wxLEFT 0 - + 0 protected 0 @@ -271,13 +273,508 @@ + 5 + wxEXPAND|wxLEFT|wxRIGHT + 0 + + + bMidSizer + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Group By + + 0 + + + 0 + + 1 + m_groupBy + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + onFilterChange + + + + 5 + wxALIGN_CENTER|wxALL|wxFIXED_MINSIZE + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "Wildcard" "RegEx" "Wildcard Substr" "RegEx Substr" + 1 + + 0 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_groupByKind + 1 + + + protected + 1 + + Resizable + 0 + 1 + -1,-1 + wxCB_DROPDOWN|wxCB_READONLY|wxCB_SIMPLE + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + Wildcard + + + + onFilterChange + + + + 5 + wxBOTTOM|wxEXPAND|wxRIGHT|wxTOP + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + m_groupByText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + onFilterChange + + + + 10 + wxEXPAND|wxRIGHT|wxLEFT + 0 + + 0 + protected + 0 + + + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Sort groups first + + 0 + + + 0 + + 1 + m_groupsFirst + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + onGroupsFirstChanged + + + + + + 5 + wxEXPAND|wxLEFT|wxRIGHT + 0 + + + bMidSizer1 + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Via Length: + 0 + + 0 + + + 0 + + 1 + m_staticTextFilter1 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxALIGN_CENTER|wxALL|wxFIXED_MINSIZE + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "Constant" "From Stackup" + 1 + + 0 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_viaLengthType + 1 + + + protected + 1 + + Resizable + 0 + 1 + -1,-1 + wxCB_DROPDOWN|wxCB_READONLY|wxCB_SIMPLE + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + Constant + + + + onViaLengthChange + + + + 5 + wxBOTTOM|wxEXPAND|wxRIGHT|wxTOP + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + 96,-1 + 0 + + 0 + + 1 + m_constViaLength + 1 + + + protected + 1 + + Resizable + 1 + 96,-1 + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + onViaLengthChange + + + + 10 + wxEXPAND|wxRIGHT|wxLEFT + 0 + + 0 + protected + 0 + + + + + 10 - wxEXPAND|wxRIGHT|wxLEFT + wxEXPAND|wxLEFT|wxRIGHT 1 - + - 1 + 0 1 @@ -289,13 +786,14 @@ protected - wxDV_HORIZ_RULES|wxDV_VERT_RULES - ; forward_declare + wxDV_HORIZ_RULES|wxDV_MULTIPLE|wxDV_VERT_RULES + - onSelChanged + onSortingChanged + onSelChanged onListSize @@ -303,16 +801,16 @@ 5 wxEXPAND|wxALL 0 - + bSizerListButtons wxHORIZONTAL none - + 5 wxTOP|wxBOTTOM|wxLEFT 0 - + 1 1 1 @@ -321,6 +819,7 @@ + 0 @@ -381,11 +880,11 @@ onAddNet - + 5 wxTOP|wxBOTTOM|wxLEFT 0 - + 1 1 1 @@ -394,6 +893,7 @@ + 0 @@ -454,21 +954,21 @@ onRenameNet - + 5 wxEXPAND 0 - + 0 protected 20 - + 5 wxALL 0 - + 1 1 1 @@ -477,6 +977,7 @@ + 0 @@ -537,21 +1038,21 @@ onDeleteNet - + 5 wxEXPAND 1 - + 0 protected 0 - + 5 wxALL|wxALIGN_CENTER_VERTICAL 0 - + 1 1 1 @@ -560,6 +1061,7 @@ + 0 @@ -684,7 +1186,7 @@ 5 wxEXPAND|wxLEFT 0 - + bSizerBottom wxHORIZONTAL @@ -695,7 +1197,7 @@ 1 0 - 1 + 0 0 0 0 diff --git a/pcbnew/dialogs/dialog_select_net_from_list_base.h b/pcbnew/dialogs/dialog_select_net_from_list_base.h index 44809cd479..a355e58bfd 100644 --- a/pcbnew/dialogs/dialog_select_net_from_list_base.h +++ b/pcbnew/dialogs/dialog_select_net_from_list_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Oct 26 2018) +// C++ code generated with wxFormBuilder (version 3.9.0 Oct 9 2020) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -43,7 +44,14 @@ class DIALOG_SELECT_NET_FROM_LIST_BASE : public DIALOG_SHIM wxStaticText* m_staticTextFilter; wxTextCtrl* m_textCtrlFilter; wxCheckBox* m_cbShowZeroPad; - wxDataViewListCtrl* m_netsList; + wxCheckBox* m_groupBy; + wxComboBox* m_groupByKind; + wxTextCtrl* m_groupByText; + wxCheckBox* m_groupsFirst; + wxStaticText* m_staticTextFilter1; + wxComboBox* m_viaLengthType; + wxTextCtrl* m_constViaLength; + wxDataViewCtrl* m_netsList; wxBitmapButton* m_addNet; wxBitmapButton* m_renameNet; wxBitmapButton* m_deleteNet; @@ -51,10 +59,12 @@ class DIALOG_SELECT_NET_FROM_LIST_BASE : public DIALOG_SHIM wxStaticLine* m_staticline1; wxStdDialogButtonSizer* m_sdbSizer; wxButton* m_sdbSizerOK; - wxButton* m_sdbSizerCancel; // Virtual event handlers, overide them in your derived class virtual void onFilterChange( wxCommandEvent& event ) { event.Skip(); } + virtual void onGroupsFirstChanged( wxCommandEvent& event ) { event.Skip(); } + virtual void onViaLengthChange( wxCommandEvent& event ) { event.Skip(); } + virtual void onSortingChanged( wxDataViewEvent& event ) { event.Skip(); } virtual void onSelChanged( wxDataViewEvent& event ) { event.Skip(); } virtual void onListSize( wxSizeEvent& event ) { event.Skip(); } virtual void onAddNet( wxCommandEvent& event ) { event.Skip(); }