From 2017389f2d917737c5d2bc8779cd7653a92c48f7 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Fri, 7 Feb 2020 17:06:24 +0000 Subject: [PATCH] Pinning for library trees in FPEditor and SymbolEditor. Fixes https://gitlab.com/kicad/code/kicad/issues/2288 --- common/gbr_metadata.cpp | 2 +- common/lib_tree_model.cpp | 163 +++++++++--------- common/lib_tree_model.h | 42 ++--- common/lib_tree_model_adapter.cpp | 95 ++++++---- common/lib_tree_model_adapter.h | 10 +- common/tool/actions.cpp | 12 +- common/widgets/lib_tree.cpp | 13 ++ common/widgets/lib_tree.h | 3 + eeschema/libedit/lib_edit_frame.cpp | 19 +- eeschema/libedit/lib_edit_frame.h | 9 + eeschema/sch_legacy_plugin.cpp | 2 +- .../symbol_tree_synchronizing_adapter.cpp | 57 +++--- eeschema/tools/lib_control.cpp | 51 ++++++ eeschema/tools/lib_control.h | 2 + eeschema/widgets/symbol_tree_pane.cpp | 7 - eeschema/widgets/symbol_tree_pane.h | 3 - include/env_paths.h | 4 +- include/tool/actions.h | 3 + pcbnew/autorouter/ar_autoplacer.cpp | 4 +- pcbnew/footprint_edit_frame.cpp | 19 +- pcbnew/footprint_edit_frame.h | 14 +- pcbnew/footprint_tree_pane.cpp | 7 - pcbnew/footprint_tree_pane.h | 3 - pcbnew/fp_tree_synchronizing_adapter.cpp | 36 ++-- pcbnew/tools/footprint_editor_tools.cpp | 42 +++++ pcbnew/tools/footprint_editor_tools.h | 4 +- 26 files changed, 412 insertions(+), 214 deletions(-) diff --git a/common/gbr_metadata.cpp b/common/gbr_metadata.cpp index d80d7a09d8..97c7f2e298 100644 --- a/common/gbr_metadata.cpp +++ b/common/gbr_metadata.cpp @@ -519,7 +519,7 @@ std::string FormatStringToGerber( const wxString& aString ) } // Netname and Pan num fields cannot be empty in Gerber files -// Normalized names must be used, if any +// m_Normalized names must be used, if any #define NO_NET_NAME wxT( "N/C" ) // net name of not connected pads (one pad net) (normalized) #define NO_PAD_NAME wxT( "" ) // pad name of pads without pad name/number (not normalized) diff --git a/common/lib_tree_model.cpp b/common/lib_tree_model.cpp index 6983a72084..070cc66651 100644 --- a/common/lib_tree_model.cpp +++ b/common/lib_tree_model.cpp @@ -51,10 +51,10 @@ static int matchPosScore(int aPosition, int aMaximum) void LIB_TREE_NODE::ResetScore() { - for( auto& child: Children ) + for( auto& child: m_Children ) child->ResetScore(); - Score = kLowestDefaultScore; + m_Score = kLowestDefaultScore; } @@ -64,62 +64,65 @@ void LIB_TREE_NODE::AssignIntrinsicRanks( bool presorted ) if( presorted ) { - int max = Children.size() - 1; + int max = m_Children.size() - 1; for( int i = 0; i <= max; ++i ) - Children[i]->IntrinsicRank = max - i; + m_Children[i]->m_IntrinsicRank = max - i; } else { - for( auto const& node: Children ) + for( auto const& node: m_Children ) sort_buf.push_back( &*node ); std::sort( sort_buf.begin(), sort_buf.end(), []( LIB_TREE_NODE* a, LIB_TREE_NODE* b ) -> bool - { return StrNumCmp( a->Name, b->Name, true ) > 0; } ); + { + return StrNumCmp( a->m_Name, b->m_Name, true ) > 0; + } ); for( int i = 0; i < (int) sort_buf.size(); ++i ) - sort_buf[i]->IntrinsicRank = i; + sort_buf[i]->m_IntrinsicRank = i; } } void LIB_TREE_NODE::SortNodes() { - std::sort( Children.begin(), Children.end(), + std::sort( m_Children.begin(), m_Children.end(), []( std::unique_ptr& a, std::unique_ptr& b ) { return Compare( *a, *b ) > 0; } ); - for( std::unique_ptr& node: Children ) + for( std::unique_ptr& node: m_Children ) node->SortNodes(); } int LIB_TREE_NODE::Compare( LIB_TREE_NODE const& aNode1, LIB_TREE_NODE const& aNode2 ) { - if( aNode1.Type != aNode2.Type ) + if( aNode1.m_Type != aNode2.m_Type ) return 0; - if( aNode1.Score != aNode2.Score ) - return aNode1.Score - aNode2.Score; + if( aNode1.m_Score != aNode2.m_Score ) + return aNode1.m_Score - aNode2.m_Score; - if( aNode1.Parent != aNode2.Parent ) + if( aNode1.m_Parent != aNode2.m_Parent ) return 0; - return aNode1.IntrinsicRank - aNode2.IntrinsicRank; + return aNode1.m_IntrinsicRank - aNode2.m_IntrinsicRank; } LIB_TREE_NODE::LIB_TREE_NODE() - : Parent( nullptr ), - Type( INVALID ), - IntrinsicRank( 0 ), - Score( kLowestDefaultScore ), - Normalized( false ), - Unit( 0 ), - IsRoot( false ) + : m_Parent( nullptr ), + m_Type( INVALID ), + m_IntrinsicRank( 0 ), + m_Score( kLowestDefaultScore ), + m_Pinned( false ), + m_Normalized( false ), + m_Unit( 0 ), + m_IsRoot( false ) {} @@ -136,36 +139,36 @@ LIB_TREE_NODE_UNIT::LIB_TREE_NODE_UNIT( LIB_TREE_NODE* aParent, LIB_TREE_ITEM* a locale = Pgm().GetLocale(); } - Parent = aParent; - Type = UNIT; + m_Parent = aParent; + m_Type = UNIT; - Unit = aUnit; - LibId = aParent->LibId; + m_Unit = aUnit; + m_LibId = aParent->m_LibId; - Name = namePrefix + " " + aItem->GetUnitReference( aUnit ); - Desc = wxEmptyString; - MatchName = wxEmptyString; + m_Name = namePrefix + " " + aItem->GetUnitReference( aUnit ); + m_Desc = wxEmptyString; + m_MatchName = wxEmptyString; - IntrinsicRank = -aUnit; + m_IntrinsicRank = -aUnit; } LIB_TREE_NODE_LIB_ID::LIB_TREE_NODE_LIB_ID( LIB_TREE_NODE* aParent, LIB_TREE_ITEM* aItem ) { - Type = LIBID; - Parent = aParent; + m_Type = LIBID; + m_Parent = aParent; - LibId.SetLibNickname( aItem->GetLibNickname() ); - LibId.SetLibItemName( aItem->GetName () ); + m_LibId.SetLibNickname( aItem->GetLibNickname() ); + m_LibId.SetLibItemName( aItem->GetName () ); - Name = aItem->GetName(); - Desc = aItem->GetDescription(); + m_Name = aItem->GetName(); + m_Desc = aItem->GetDescription(); - MatchName = aItem->GetName(); - SearchText = aItem->GetSearchText(); - Normalized = false; + m_MatchName = aItem->GetName(); + m_SearchText = aItem->GetSearchText(); + m_Normalized = false; - IsRoot = aItem->IsRoot(); + m_IsRoot = aItem->IsRoot(); if( aItem->GetUnitCount() > 1 ) { @@ -178,7 +181,7 @@ LIB_TREE_NODE_LIB_ID::LIB_TREE_NODE_LIB_ID( LIB_TREE_NODE* aParent, LIB_TREE_ITE LIB_TREE_NODE_UNIT& LIB_TREE_NODE_LIB_ID::AddUnit( LIB_TREE_ITEM* aItem, int aUnit ) { LIB_TREE_NODE_UNIT* unit = new LIB_TREE_NODE_UNIT( this, aItem, aUnit ); - Children.push_back( std::unique_ptr( unit ) ); + m_Children.push_back( std::unique_ptr( unit ) ); return *unit; } @@ -187,15 +190,15 @@ void LIB_TREE_NODE_LIB_ID::Update( LIB_TREE_ITEM* aItem ) { // Update is called when the names match, so just update the other fields. - LibId.SetLibNickname( aItem->GetLibId().GetLibNickname() ); + m_LibId.SetLibNickname( aItem->GetLibId().GetLibNickname() ); - Desc = aItem->GetDescription(); + m_Desc = aItem->GetDescription(); - SearchText = aItem->GetSearchText(); - Normalized = false; + m_SearchText = aItem->GetSearchText(); + m_Normalized = false; - IsRoot = aItem->IsRoot(); - Children.clear(); + m_IsRoot = aItem->IsRoot(); + m_Children.clear(); for( int u = 1; u <= aItem->GetUnitCount(); ++u ) AddUnit( aItem, u ); @@ -204,14 +207,14 @@ void LIB_TREE_NODE_LIB_ID::Update( LIB_TREE_ITEM* aItem ) void LIB_TREE_NODE_LIB_ID::UpdateScore( EDA_COMBINED_MATCHER& aMatcher ) { - if( Score <= 0 ) + if( m_Score <= 0 ) return; // Leaf nodes without scores are out of the game. - if( !Normalized ) + if( !m_Normalized ) { - MatchName = MatchName.Lower(); - SearchText = SearchText.Lower(); - Normalized = true; + m_MatchName = m_MatchName.Lower(); + m_SearchText = m_SearchText.Lower(); + m_Normalized = true; } // Keywords and description we only count if the match string is at @@ -220,20 +223,20 @@ void LIB_TREE_NODE_LIB_ID::UpdateScore( EDA_COMBINED_MATCHER& aMatcher ) int found_pos = EDA_PATTERN_NOT_FOUND; int matchers_fired = 0; - if( aMatcher.GetPattern() == MatchName ) + if( aMatcher.GetPattern() == m_MatchName ) { - Score += 1000; // exact match. High score :) + m_Score += 1000; // exact match. High score :) } - else if( aMatcher.Find( MatchName, matchers_fired, found_pos ) ) + else if( aMatcher.Find( m_MatchName, matchers_fired, found_pos ) ) { // Substring match. The earlier in the string the better. - Score += matchPosScore( found_pos, 20 ) + 20; + m_Score += matchPosScore( found_pos, 20 ) + 20; } - else if( aMatcher.Find( Parent->MatchName, matchers_fired, found_pos ) ) + else if( aMatcher.Find( m_Parent->m_MatchName, matchers_fired, found_pos ) ) { - Score += 19; // parent name matches. score += 19 + m_Score += 19; // parent name matches. score += 19 } - else if( aMatcher.Find( SearchText, matchers_fired, found_pos ) ) + else if( aMatcher.Find( m_SearchText, matchers_fired, found_pos ) ) { // If we have a very short search term (like one or two letters), // we don't want to accumulate scores if they just happen to be in @@ -243,52 +246,52 @@ void LIB_TREE_NODE_LIB_ID::UpdateScore( EDA_COMBINED_MATCHER& aMatcher ) { // For longer terms, we add scores 1..18 for positional match // (higher in the front, where the keywords are). - Score += matchPosScore( found_pos, 17 ) + 1; + m_Score += matchPosScore( found_pos, 17 ) + 1; } } else { // No match. That's it for this item. - Score = 0; + m_Score = 0; } // More matchers = better match - Score += 2 * matchers_fired; + m_Score += 2 * matchers_fired; } LIB_TREE_NODE_LIB::LIB_TREE_NODE_LIB( LIB_TREE_NODE* aParent, wxString const& aName, wxString const& aDesc ) { - Type = LIB; - Name = aName; - MatchName = aName.Lower(); - Desc = aDesc; - Parent = aParent; - LibId.SetLibNickname( aName ); + m_Type = LIB; + m_Name = aName; + m_MatchName = aName.Lower(); + m_Desc = aDesc; + m_Parent = aParent; + m_LibId.SetLibNickname( aName ); } LIB_TREE_NODE_LIB_ID& LIB_TREE_NODE_LIB::AddItem( LIB_TREE_ITEM* aItem ) { LIB_TREE_NODE_LIB_ID* item = new LIB_TREE_NODE_LIB_ID( this, aItem ); - Children.push_back( std::unique_ptr( item ) ); + m_Children.push_back( std::unique_ptr( item ) ); return *item; } void LIB_TREE_NODE_LIB::UpdateScore( EDA_COMBINED_MATCHER& aMatcher ) { - Score = 0; + m_Score = 0; // We need to score leaf nodes, which are usually (but not always) children. - if( Children.size() ) + if( m_Children.size() ) { - for( auto& child: Children ) + for( auto& child: m_Children ) { child->UpdateScore( aMatcher ); - Score = std::max( Score, child->Score ); + m_Score = std::max( m_Score, child->m_Score ); } } else @@ -297,39 +300,39 @@ void LIB_TREE_NODE_LIB::UpdateScore( EDA_COMBINED_MATCHER& aMatcher ) int found_pos = EDA_PATTERN_NOT_FOUND; int matchers_fired = 0; - if( aMatcher.GetPattern() == MatchName ) + if( aMatcher.GetPattern() == m_MatchName ) { - Score += 1000; // exact match. High score :) + m_Score += 1000; // exact match. High score :) } - else if( aMatcher.Find( MatchName, matchers_fired, found_pos ) ) + else if( aMatcher.Find( m_MatchName, matchers_fired, found_pos ) ) { // Substring match. The earlier in the string the better. - Score += matchPosScore( found_pos, 20 ) + 20; + m_Score += matchPosScore( found_pos, 20 ) + 20; } // More matchers = better match - Score += 2 * matchers_fired; + m_Score += 2 * matchers_fired; } } LIB_TREE_NODE_ROOT::LIB_TREE_NODE_ROOT() { - Type = ROOT; + m_Type = ROOT; } LIB_TREE_NODE_LIB& LIB_TREE_NODE_ROOT::AddLib( wxString const& aName, wxString const& aDesc ) { LIB_TREE_NODE_LIB* lib = new LIB_TREE_NODE_LIB( this, aName, aDesc ); - Children.push_back( std::unique_ptr( lib ) ); + m_Children.push_back( std::unique_ptr( lib ) ); return *lib; } void LIB_TREE_NODE_ROOT::UpdateScore( EDA_COMBINED_MATCHER& aMatcher ) { - for( auto& child: Children ) + for( auto& child: m_Children ) child->UpdateScore( aMatcher ); } diff --git a/common/lib_tree_model.h b/common/lib_tree_model.h index 98b21fdbd7..76d70a200e 100644 --- a/common/lib_tree_model.h +++ b/common/lib_tree_model.h @@ -62,17 +62,18 @@ class EDA_COMBINED_MATCHER; * - `Parent` - parent node, or nullptr if root * - `Children` - vector of unique_ptrs to children * - `Type` - ROOT, LIB, ALIAS, or UNIT - * - `IntrinsicRank` - cached initial sort order - * - `Score` - score taking into account search terms. Zero means irrelevant and + * - `m_IntrinsicRank` - cached initial sort order + * - `m_Score` - score taking into account search terms. Zero means irrelevant and * should be hidden. * - `Name` - name of the library/alias/unit, to be displayed * - `Desc` - description of the alias, to be displayed - * - `MatchName` - Name, normalized to lowercase for matching - * - `SearchText` - normalized composite of keywords and description + * - `m_MatchName` - Name, normalized to lowercase for matching + * - `m_SearchText` - normalized composite of keywords and description * - `LibId` - the #LIB_ID this alias or unit is from, or not valid * - `Unit` - the unit number, or zero for non-units */ -class LIB_TREE_NODE { +class LIB_TREE_NODE +{ public: enum TYPE { ROOT, LIB, LIBID, UNIT, INVALID @@ -80,31 +81,30 @@ public: typedef std::vector> PTR_VECTOR; - LIB_TREE_NODE* Parent; ///< Parent node or null - PTR_VECTOR Children; ///< List of child nodes - enum TYPE Type; ///< Node type + LIB_TREE_NODE* m_Parent; // Parent node or null + PTR_VECTOR m_Children; // List of child nodes + enum TYPE m_Type; // Node type /** * The rank of the item before any search terms are applied. This is * a fairly expensive sort (involving string compares) so it helps to * store the result of that sort. */ - int IntrinsicRank; + int m_IntrinsicRank; - /// The score of an item resulting from the search algorithm. - int Score; + int m_Score; // The score of an item resulting from the search algorithm. + bool m_Pinned; // Item should appear at top when there is no search string - wxString Name; ///< Actual name of the part - wxString Desc; ///< Description to be displayed - wxString MatchName; ///< Normalized name for matching - wxString SearchText; ///< Descriptive text to search - bool Normalized; ///< Support for lazy normalization. + wxString m_Name; // Actual name of the part + wxString m_Desc; // Description to be displayed + wxString m_MatchName; // Normalized name for matching + wxString m_SearchText; // Descriptive text to search + bool m_Normalized; // Support for lazy normalization. - LIB_ID LibId; ///< LIB_ID determined by the parent library nickname and alias name. - int Unit; ///< Actual unit, or zero - bool IsRoot; ///< Indicates if the symbol is a root symbol instead of an alias. - int VisLen; ///< Length of the string as shown on screen + LIB_ID m_LibId; // LIB_ID determined by the parent library nickname and alias name. + int m_Unit; // Actual unit, or zero + bool m_IsRoot; // Indicates if the symbol is a root symbol instead of an alias. /** * Update the score for this part. This is accumulative - it will be @@ -120,7 +120,7 @@ public: void ResetScore(); /** - * Store intrinsic ranks on all children of this node. See IntrinsicRank + * Store intrinsic ranks on all children of this node. See m_IntrinsicRank * member doc for more information. */ void AssignIntrinsicRanks( bool presorted = false ); diff --git a/common/lib_tree_model_adapter.cpp b/common/lib_tree_model_adapter.cpp index 90c464121b..313ba6df26 100644 --- a/common/lib_tree_model_adapter.cpp +++ b/common/lib_tree_model_adapter.cpp @@ -27,6 +27,7 @@ #define LIST_COLUMN_WIDTH_KEY wxT( "SelectorColumnWidth" ) +#define PINNED_ITEMS_KEY wxT( "PinnedItems" ) static const int kDataViewIndent = 20; @@ -43,9 +44,9 @@ wxDataViewItem LIB_TREE_MODEL_ADAPTER::ToItem( LIB_TREE_NODE const* aNode ) /** * Convert wxDataViewItem -> CMP_TREE_NODE */ -LIB_TREE_NODE const* LIB_TREE_MODEL_ADAPTER::ToNode( wxDataViewItem aItem ) +LIB_TREE_NODE* LIB_TREE_MODEL_ADAPTER::ToNode( wxDataViewItem aItem ) { - return static_cast( aItem.GetID() ); + return static_cast( aItem.GetID() ); } @@ -57,9 +58,9 @@ unsigned int LIB_TREE_MODEL_ADAPTER::IntoArray( LIB_TREE_NODE const& aNode, { unsigned int n = 0; - for( auto const& child: aNode.Children ) + for( auto const& child: aNode.m_Children ) { - if( child->Score > 0 ) + if( child->m_Score > 0 ) { aChildren.Add( ToItem( &*child ) ); ++n; @@ -91,6 +92,10 @@ LIB_TREE_MODEL_ADAPTER::LIB_TREE_MODEL_ADAPTER() if( m_config->Read( m_configPrefix + LIST_COLUMN_WIDTH_KEY, &colWidth ) ) m_colWidths[PART_COL] = colWidth; + + // JEY TODO NEW SETTINGS ARCH: read pinned items array.... + //for( UFT8 pinnedItem : pinnedItems ) + // m_pinnedLibIDs.insert( pinnedItem ); } @@ -111,6 +116,20 @@ void LIB_TREE_MODEL_ADAPTER::SaveColWidths() } } +void LIB_TREE_MODEL_ADAPTER::SavePinnedItems() +{ + // JEY TODO NEW SETTINGS ARCH: clear pinned items array in settings.... + + for( auto& child: m_tree.m_Children ) + { + if( child->m_Pinned ) + { + UTF8 pinnedItem = child->m_LibId.Format(); + // JEY TODO NEW SETTINGS ARCH: add pinned entry to settings array.... + } + } +} + void LIB_TREE_MODEL_ADAPTER::SetFilter( CMP_FILTER_TYPE aFilter ) { @@ -137,6 +156,8 @@ void LIB_TREE_MODEL_ADAPTER::DoAddLibrary( wxString const& aNodeName, wxString c { LIB_TREE_NODE_LIB& lib_node = m_tree.AddLib( aNodeName, aDesc ); + lib_node.m_Pinned = m_pinnedLibIDs.count( lib_node.m_LibId.Format() ) > 0; + for( LIB_TREE_ITEM* item: aItemList ) lib_node.AddItem( item ); @@ -148,6 +169,12 @@ void LIB_TREE_MODEL_ADAPTER::UpdateSearchString( wxString const& aSearch ) { m_tree.ResetScore(); + for( auto& child: m_tree.m_Children ) + { + if( child->m_Pinned ) + child->m_Score *= 2; + } + wxStringTokenizer tokenizer( aSearch ); while( tokenizer.HasMoreTokens() ) @@ -267,21 +294,27 @@ LIB_ID LIB_TREE_MODEL_ADAPTER::GetAliasFor( const wxDataViewItem& aSelection ) c if( !node ) return emptyId; - return node->LibId; + return node->m_LibId; } int LIB_TREE_MODEL_ADAPTER::GetUnitFor( const wxDataViewItem& aSelection ) const { const LIB_TREE_NODE* node = ToNode( aSelection ); - return node ? node->Unit : 0; + return node ? node->m_Unit : 0; } LIB_TREE_NODE::TYPE LIB_TREE_MODEL_ADAPTER::GetTypeFor( const wxDataViewItem& aSelection ) const { const LIB_TREE_NODE* node = ToNode( aSelection ); - return node ? node->Type : LIB_TREE_NODE::INVALID; + return node ? node->m_Type : LIB_TREE_NODE::INVALID; +} + + +LIB_TREE_NODE* LIB_TREE_MODEL_ADAPTER::GetTreeNodeFor( const wxDataViewItem& aSelection ) const +{ + return ToNode( aSelection ); } @@ -289,8 +322,8 @@ int LIB_TREE_MODEL_ADAPTER::GetItemCount() const { int n = 0; - for( const std::unique_ptr& lib: m_tree.Children ) - n += lib->Children.size(); + for( const std::unique_ptr& lib: m_tree.m_Children ) + n += lib->m_Children.size(); return n; } @@ -298,18 +331,18 @@ int LIB_TREE_MODEL_ADAPTER::GetItemCount() const wxDataViewItem LIB_TREE_MODEL_ADAPTER::FindItem( const LIB_ID& aLibId ) { - for( auto& lib: m_tree.Children ) + for( auto& lib: m_tree.m_Children ) { - if( lib->Name != aLibId.GetLibNickname() ) + if( lib->m_Name != aLibId.GetLibNickname() ) continue; // if part name is not specified, return the library node if( aLibId.GetLibItemName() == "" ) return ToItem( lib.get() ); - for( auto& alias: lib->Children ) + for( auto& alias: lib->m_Children ) { - if( alias->Name == aLibId.GetLibItemName() ) + if( alias->m_Name == aLibId.GetLibItemName() ) return ToItem( alias.get() ); } @@ -325,8 +358,8 @@ unsigned int LIB_TREE_MODEL_ADAPTER::GetChildren( wxDataViewItem const& aItem, { auto node = ( aItem.IsOk() ? ToNode( aItem ) : &m_tree ); - if( node->Type != LIB_TREE_NODE::TYPE::LIBID - || ( m_show_units && node->Type == LIB_TREE_NODE::TYPE::LIBID ) ) + if( node->m_Type != LIB_TREE_NODE::TYPE::LIBID + || ( m_show_units && node->m_Type == LIB_TREE_NODE::TYPE::LIBID ) ) return IntoArray( *node, aChildren ); else return 0; @@ -342,18 +375,18 @@ bool LIB_TREE_MODEL_ADAPTER::HasContainerColumns( wxDataViewItem const& aItem ) bool LIB_TREE_MODEL_ADAPTER::IsContainer( wxDataViewItem const& aItem ) const { auto node = ToNode( aItem ); - return node ? node->Children.size() : true; + return node ? node->m_Children.size() : true; } wxDataViewItem LIB_TREE_MODEL_ADAPTER::GetParent( wxDataViewItem const& aItem ) const { auto node = ToNode( aItem ); - auto parent = node ? node->Parent : nullptr; + auto parent = node ? node->m_Parent : nullptr; // wxDataViewModel has no root node, but rather top-level elements have // an invalid (null) parent. - if( !node || !parent || parent->Type == LIB_TREE_NODE::TYPE::ROOT ) + if( !node || !parent || parent->m_Type == LIB_TREE_NODE::TYPE::ROOT ) return ToItem( nullptr ); else return ToItem( parent ); @@ -377,10 +410,10 @@ void LIB_TREE_MODEL_ADAPTER::GetValue( wxVariant& aVariant, { default: // column == -1 is used for default Compare function case 0: - aVariant = node->Name; + aVariant = node->m_Name; break; case 1: - aVariant = node->Desc; + aVariant = node->m_Desc; break; } } @@ -396,13 +429,13 @@ bool LIB_TREE_MODEL_ADAPTER::GetAttr( wxDataViewItem const& aItem, auto node = ToNode( aItem ); wxASSERT( node ); - if( node->Type != LIB_TREE_NODE::LIBID ) + if( node->m_Type != LIB_TREE_NODE::LIBID ) { // Currently only aliases are formatted at all return false; } - if( !node->IsRoot && aCol == 0 ) + if( !node->m_IsRoot && aCol == 0 ) { // Names of non-root aliases are italicized aAttr.SetItalic( true ); @@ -419,14 +452,14 @@ void LIB_TREE_MODEL_ADAPTER::FindAndExpand( LIB_TREE_NODE& aNode, std::function aFunc, LIB_TREE_NODE** aHighScore ) { - for( auto& node: aNode.Children ) + for( auto& node: aNode.m_Children ) { if( aFunc( &*node ) ) { auto item = wxDataViewItem( &*node ); m_widget->ExpandAncestors( item ); - if( !(*aHighScore) || node->Score > (*aHighScore)->Score ) + if( !(*aHighScore) || node->m_Score > (*aHighScore)->m_Score ) (*aHighScore) = &*node; } @@ -443,7 +476,7 @@ LIB_TREE_NODE* LIB_TREE_MODEL_ADAPTER::ShowResults() []( LIB_TREE_NODE const* n ) { // return leaf nodes with some level of matching - return n->Type == LIB_TREE_NODE::TYPE::LIBID && n->Score > 1; + return n->m_Type == LIB_TREE_NODE::TYPE::LIBID && n->m_Score > 1; }, &highScore ); @@ -461,10 +494,10 @@ LIB_TREE_NODE* LIB_TREE_MODEL_ADAPTER::ShowPreselect() FindAndExpand( m_tree, [&]( LIB_TREE_NODE const* n ) { - if( n->Type == LIB_TREE_NODE::LIBID && ( n->Children.empty() || !m_preselect_unit ) ) - return m_preselect_lib_id == n->LibId; - else if( n->Type == LIB_TREE_NODE::UNIT && m_preselect_unit ) - return m_preselect_lib_id == n->Parent->LibId && m_preselect_unit == n->Unit; + if( n->m_Type == LIB_TREE_NODE::LIBID && ( n->m_Children.empty() || !m_preselect_unit ) ) + return m_preselect_lib_id == n->m_LibId; + else if( n->m_Type == LIB_TREE_NODE::UNIT && m_preselect_unit ) + return m_preselect_lib_id == n->m_Parent->m_LibId && m_preselect_unit == n->m_Unit; else return false; }, @@ -481,8 +514,8 @@ LIB_TREE_NODE* LIB_TREE_MODEL_ADAPTER::ShowSingleLibrary() FindAndExpand( m_tree, []( LIB_TREE_NODE const* n ) { - return n->Type == LIB_TREE_NODE::TYPE::LIBID && - n->Parent->Parent->Children.size() == 1; + return n->m_Type == LIB_TREE_NODE::TYPE::LIBID && + n->m_Parent->m_Parent->m_Children.size() == 1; }, &highScore ); diff --git a/common/lib_tree_model_adapter.h b/common/lib_tree_model_adapter.h index f9677f783d..1c633f9b6f 100644 --- a/common/lib_tree_model_adapter.h +++ b/common/lib_tree_model_adapter.h @@ -29,6 +29,7 @@ #include #include #include +#include /** * Adapter class in the component selector Model-View-Adapter (mediated MVC) @@ -129,6 +130,7 @@ public: * valid. */ void SaveColWidths(); + void SavePinnedItems(); /** * Set the component filter type. Must be set before adding libraries @@ -222,6 +224,8 @@ public: */ LIB_TREE_NODE::TYPE GetTypeFor( const wxDataViewItem& aSelection ) const; + LIB_TREE_NODE* GetTreeNodeFor( const wxDataViewItem& aSelection ) const; + virtual wxString GenerateInfo( LIB_ID const& aLibId, int aUnit ) { return wxEmptyString; }; /** @@ -234,7 +238,7 @@ public: */ virtual int GetLibrariesCount() const { - return m_tree.Children.size(); + return m_tree.m_Children.size(); } /** @@ -265,7 +269,7 @@ public: protected: static wxDataViewItem ToItem( LIB_TREE_NODE const* aNode ); - static LIB_TREE_NODE const* ToNode( wxDataViewItem aItem ); + static LIB_TREE_NODE* ToNode( wxDataViewItem aItem ); static unsigned int IntoArray( LIB_TREE_NODE const& aNode, wxDataViewItemArray& aChildren ); LIB_TREE_NODE_ROOT m_tree; @@ -343,6 +347,8 @@ private: wxConfigBase* m_config; wxString m_configPrefix; + std::set m_pinnedLibIDs; + /** * Find any results worth highlighting and expand them, according to given * criteria (f(CMP_TREE_NODE const*) -> bool) diff --git a/common/tool/actions.cpp b/common/tool/actions.cpp index 02f6b860e7..e7ec6723c7 100644 --- a/common/tool/actions.cpp +++ b/common/tool/actions.cpp @@ -356,18 +356,26 @@ TOOL_ACTION ACTIONS::cursorRightFast( "common.Control.cursorRightFast", TOOL_ACTION ACTIONS::cursorClick( "common.Control.cursorClick", AS_GLOBAL, WXK_RETURN, LEGACY_HK_NAME( "Mouse Left Click" ), - _( "Click" ), "Performs left mouse button click", + _( "Click" ), _( "Performs left mouse button click" ), nullptr, AF_NONE, (void*) CURSOR_CLICK ); TOOL_ACTION ACTIONS::cursorDblClick( "common.Control.cursorDblClick", AS_GLOBAL, WXK_END, LEGACY_HK_NAME( "Mouse Left Double Click" ), - _( "Double-click" ), "Performs left mouse button double-click", + _( "Double-click" ), _( "Performs left mouse button double-click" ), nullptr, AF_NONE, (void*) CURSOR_DBL_CLICK ); TOOL_ACTION ACTIONS::refreshPreview( "common.Control.refreshPreview", AS_GLOBAL ); +TOOL_ACTION ACTIONS::pinLibrary( "common.Control.pinLibrary", + AS_GLOBAL, 0, "", + _( "Pin Library" ), "Keep the library at the top of the list" ); + +TOOL_ACTION ACTIONS::unpinLibrary( "common.Control.unpinLibrary", + AS_GLOBAL, 0, "", + _( "Unpin Library" ), "No longer keep the library at the top of the list" ); + TOOL_ACTION ACTIONS::panUp( "common.Control.panUp", AS_GLOBAL, MD_SHIFT + WXK_UP, "", diff --git a/common/widgets/lib_tree.cpp b/common/widgets/lib_tree.cpp index 5d60b36945..a0d2e6bc76 100644 --- a/common/widgets/lib_tree.cpp +++ b/common/widgets/lib_tree.cpp @@ -139,6 +139,8 @@ LIB_TREE::~LIB_TREE() { // Save the column widths to the config file m_adapter->SaveColWidths(); + + m_adapter->SavePinnedItems(); } @@ -160,6 +162,17 @@ LIB_ID LIB_TREE::GetSelectedLibId( int* aUnit ) const } +LIB_TREE_NODE* LIB_TREE::GetCurrentTreeNode() const +{ + auto sel = m_tree_ctrl->GetSelection(); + + if( !sel ) + return nullptr; + + return m_adapter->GetTreeNodeFor( sel ); +} + + void LIB_TREE::SelectLibId( const LIB_ID& aLibId ) { selectIfValid( m_adapter->FindItem( aLibId ) ); diff --git a/common/widgets/lib_tree.h b/common/widgets/lib_tree.h index befb740ee2..1eabbf7a95 100644 --- a/common/widgets/lib_tree.h +++ b/common/widgets/lib_tree.h @@ -71,6 +71,8 @@ public: */ LIB_ID GetSelectedLibId( int* aUnit = nullptr ) const; + LIB_TREE_NODE* GetCurrentTreeNode() const; + /** * Select an item in the tree widget. */ @@ -136,6 +138,7 @@ protected: { ///> List of expanded nodes std::vector expanded; + std::vector pinned; ///> Current selection, might be not valid if nothing was selected LIB_ID selection; diff --git a/eeschema/libedit/lib_edit_frame.cpp b/eeschema/libedit/lib_edit_frame.cpp index 8058fc2ace..51069a7f4c 100644 --- a/eeschema/libedit/lib_edit_frame.cpp +++ b/eeschema/libedit/lib_edit_frame.cpp @@ -540,6 +540,12 @@ LIB_ID LIB_EDIT_FRAME::getTargetLibId() const } +LIB_TREE_NODE* LIB_EDIT_FRAME::GetCurrentTreeNode() const +{ + return m_treePane->GetLibTree()->GetCurrentTreeNode(); +} + + wxString LIB_EDIT_FRAME::getTargetLib() const { return getTargetLibId().GetLibNickname(); @@ -583,7 +589,7 @@ void LIB_EDIT_FRAME::SyncLibraries( bool aShowProgress ) m_treePane->GetLibTree()->Unselect(); } - m_treePane->Regenerate(); + m_treePane->GetLibTree()->Regenerate( true ); // Try to select the parent library, in case the part is not found if( !found && selected.IsValid() ) @@ -605,6 +611,17 @@ void LIB_EDIT_FRAME::SyncLibraries( bool aShowProgress ) } +void LIB_EDIT_FRAME::RegenerateLibraryTree() +{ + LIB_ID target = getTargetLibId(); + + m_treePane->GetLibTree()->Regenerate( true ); + + if( target.IsValid() ) + m_treePane->GetLibTree()->CenterLibId( target ); +} + + SYMBOL_LIB_TABLE* LIB_EDIT_FRAME::selectSymLibTable( bool aOptional ) { wxArrayString libTableNames; diff --git a/eeschema/libedit/lib_edit_frame.h b/eeschema/libedit/lib_edit_frame.h index e17233061a..5b82a40f38 100644 --- a/eeschema/libedit/lib_edit_frame.h +++ b/eeschema/libedit/lib_edit_frame.h @@ -40,6 +40,7 @@ class LIB_PART; class LIB_FIELD; class DIALOG_LIB_EDIT_TEXT; class SYMBOL_TREE_PANE; +class LIB_TREE_NODE; class LIB_ID; class LIB_MANAGER; @@ -146,6 +147,8 @@ public: /** Sets the current library nickname and returns the old library nickname. */ wxString SetCurLib( const wxString& aLibNickname ); + LIB_TREE_NODE* GetCurrentTreeNode() const; + /** * Return the LIB_ID of the library or symbol selected in the symbol tree. */ @@ -417,6 +420,12 @@ public: */ void SyncLibraries( bool aShowProgress ); + /** + * Filter, sort, and redisplay the library tree. Does NOT synchronize it with libraries + * in disk. + */ + void RegenerateLibraryTree(); + /** * Allows Libedit to install its preferences panel into the preferences dialog. */ diff --git a/eeschema/sch_legacy_plugin.cpp b/eeschema/sch_legacy_plugin.cpp index 83374a33ec..890633e0f8 100644 --- a/eeschema/sch_legacy_plugin.cpp +++ b/eeschema/sch_legacy_plugin.cpp @@ -617,7 +617,7 @@ SCH_SHEET* SCH_LEGACY_PLUGIN::Load( const wxString& aFileName, KIWAY* aKiway, if( m_path.IsEmpty() ) m_path = aKiway->Prj().GetProjectPath(); - wxLogTrace( traceSchLegacyPlugin, "Normalized append path \"%s\".", m_path ); + wxLogTrace( traceSchLegacyPlugin, "m_Normalized append path \"%s\".", m_path ); } else { diff --git a/eeschema/symbol_tree_synchronizing_adapter.cpp b/eeschema/symbol_tree_synchronizing_adapter.cpp index 4a099ad05e..22b531ef03 100644 --- a/eeschema/symbol_tree_synchronizing_adapter.cpp +++ b/eeschema/symbol_tree_synchronizing_adapter.cpp @@ -56,7 +56,7 @@ TOOL_INTERACTIVE* SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetContextMenuTool() bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::IsContainer( const wxDataViewItem& aItem ) const { const LIB_TREE_NODE* node = ToNode( aItem ); - return node ? node->Type == LIB_TREE_NODE::LIB : true; + return node ? node->m_Type == LIB_TREE_NODE::LIB : true; } @@ -76,9 +76,9 @@ void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::Sync( bool aForce, int i = 0, max = GetLibrariesCount(); // Process already stored libraries - for( auto it = m_tree.Children.begin(); it != m_tree.Children.end(); /* iteration inside */ ) + for( auto it = m_tree.m_Children.begin(); it != m_tree.m_Children.end(); /* iteration inside */ ) { - const wxString& name = it->get()->Name; + const wxString& name = it->get()->m_Name; if( wxGetUTCTimeMillis() > nextUpdate ) { @@ -142,25 +142,25 @@ int SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetLibrariesCount() const void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIB& aLibNode ) { - auto hashIt = m_libHashes.find( aLibNode.Name ); + auto hashIt = m_libHashes.find( aLibNode.m_Name ); if( hashIt == m_libHashes.end() ) { // add a new library - for( auto alias : m_libMgr->GetAliases( aLibNode.Name ) ) + for( auto alias : m_libMgr->GetAliases( aLibNode.m_Name ) ) aLibNode.AddItem( alias ); } - else if( hashIt->second != m_libMgr->GetLibraryHash( aLibNode.Name ) ) + else if( hashIt->second != m_libMgr->GetLibraryHash( aLibNode.m_Name ) ) { // update an existing library - std::list aliases = m_libMgr->GetAliases( aLibNode.Name ); + std::list aliases = m_libMgr->GetAliases( aLibNode.m_Name ); // remove the common part from the aliases list - for( auto nodeIt = aLibNode.Children.begin(); nodeIt != aLibNode.Children.end(); /**/ ) + for( auto nodeIt = aLibNode.m_Children.begin(); nodeIt != aLibNode.m_Children.end(); /**/ ) { auto aliasIt = std::find_if( aliases.begin(), aliases.end(), [&] ( const LIB_PART* a ) { - return a->GetName() == (*nodeIt)->Name; + return a->GetName() == (*nodeIt)->m_Name; } ); if( aliasIt != aliases.end() ) @@ -174,7 +174,7 @@ void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIB& aLibNo else { // node does not exist in the library manager, remove the corresponding node - nodeIt = aLibNode.Children.erase( nodeIt ); + nodeIt = aLibNode.m_Children.erase( nodeIt ); } } @@ -184,7 +184,7 @@ void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIB& aLibNo } aLibNode.AssignIntrinsicRanks(); - m_libHashes[aLibNode.Name] = m_libMgr->GetLibraryHash( aLibNode.Name ); + m_libHashes[aLibNode.m_Name] = m_libMgr->GetLibraryHash( aLibNode.m_Name ); } @@ -192,8 +192,8 @@ LIB_TREE_NODE::PTR_VECTOR::iterator SYMBOL_TREE_SYNCHRONIZING_ADAPTER::deleteLib LIB_TREE_NODE::PTR_VECTOR::iterator& aLibNodeIt ) { LIB_TREE_NODE* node = aLibNodeIt->get(); - m_libHashes.erase( node->Name ); - auto it = m_tree.Children.erase( aLibNodeIt ); + m_libHashes.erase( node->m_Name ); + auto it = m_tree.m_Children.erase( aLibNodeIt ); return it; } @@ -213,25 +213,28 @@ void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetValue( wxVariant& aVariant, wxDataVie switch( aCol ) { case 0: - aVariant = node->Name; + aVariant = node->m_Name; + + if( node->m_Pinned ) + aVariant = "☆ " + node->m_Name; // mark modified libs with an asterisk - if( node->Type == LIB_TREE_NODE::LIB && m_libMgr->IsLibraryModified( node->Name ) ) - aVariant = node->Name + " *"; + if( node->m_Type == LIB_TREE_NODE::LIB && m_libMgr->IsLibraryModified( node->m_Name ) ) + aVariant = aVariant.GetString() + " *"; // mark modified parts with an aster-ix - if( node->Type == LIB_TREE_NODE::LIBID - && m_libMgr->IsPartModified( node->Name, node->Parent->Name ) ) - aVariant = node->Name + " *"; + if( node->m_Type == LIB_TREE_NODE::LIBID + && m_libMgr->IsPartModified( node->m_Name, node->m_Parent->m_Name ) ) + aVariant = aVariant.GetString() + " *"; break; case 1: - aVariant = node->Desc; + aVariant = node->m_Desc; break; default: // column == -1 is used for default Compare function - aVariant = node->Name; + aVariant = node->m_Name; break; } } @@ -250,11 +253,11 @@ bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, un auto node = ToNode( aItem ); wxCHECK( node, false ); - switch( node->Type ) + switch( node->m_Type ) { case LIB_TREE_NODE::LIB: // mark modified libs with bold font - aAttr.SetBold( m_libMgr->IsLibraryModified( node->Name ) ); + aAttr.SetBold( m_libMgr->IsLibraryModified( node->m_Name ) ); #ifdef __WXGTK__ // The native wxGTK+ impl ignores background colour, so set the text colour instead. @@ -263,7 +266,7 @@ bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, un aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) ); #else // mark the current library with background color - if( node->Name == m_libMgr->GetCurrentLib() ) + if( node->m_Name == m_libMgr->GetCurrentLib() ) { aAttr.SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) ); aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT ) ); @@ -273,10 +276,10 @@ bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, un case LIB_TREE_NODE::LIBID: // mark modified part with bold font - aAttr.SetBold( m_libMgr->IsPartModified( node->Name, node->Parent->Name ) ); + aAttr.SetBold( m_libMgr->IsPartModified( node->m_Name, node->m_Parent->m_Name ) ); // mark aliases with italic font - aAttr.SetItalic( !node->IsRoot ); + aAttr.SetItalic( !node->m_IsRoot ); #ifdef __WXGTK__ // The native wxGTK+ impl ignores background colour, so set the text colour instead. @@ -285,7 +288,7 @@ bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, un aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) ); #else // mark the current part with background color - if( node->LibId == m_libMgr->GetCurrentLibId() ) + if( node->m_LibId == m_libMgr->GetCurrentLibId() ) { aAttr.SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) ); aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_LISTBOXHIGHLIGHTTEXT ) ); diff --git a/eeschema/tools/lib_control.cpp b/eeschema/tools/lib_control.cpp index 4c1c4ad602..f0a0eb18de 100644 --- a/eeschema/tools/lib_control.cpp +++ b/eeschema/tools/lib_control.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -51,11 +52,23 @@ bool LIB_CONTROL::Init() LIB_ID sel = editFrame->GetTreeLIBID(); return !sel.GetLibNickname().empty() && sel.GetLibItemName().empty(); }; + auto pinnedLibSelectedCondition = [ editFrame ] ( const SELECTION& aSel ) { + LIB_TREE_NODE* current = editFrame->GetCurrentTreeNode(); + return current && current->m_Type == LIB_TREE_NODE::LIB && current->m_Pinned; + }; + auto unpinnedLibSelectedCondition = [ editFrame ] (const SELECTION& aSel ) { + LIB_TREE_NODE* current = editFrame->GetCurrentTreeNode(); + return current && current->m_Type == LIB_TREE_NODE::LIB && !current->m_Pinned; + }; auto symbolSelectedCondition = [ editFrame ] ( const SELECTION& aSel ) { LIB_ID sel = editFrame->GetTreeLIBID(); return !sel.GetLibNickname().empty() && !sel.GetLibItemName().empty(); }; + ctxMenu.AddItem( ACTIONS::pinLibrary, unpinnedLibSelectedCondition ); + ctxMenu.AddItem( ACTIONS::unpinLibrary, pinnedLibSelectedCondition ); + ctxMenu.AddSeparator(); + ctxMenu.AddItem( ACTIONS::newLibrary, SELECTION_CONDITIONS::ShowAlways ); ctxMenu.AddItem( ACTIONS::addLibrary, SELECTION_CONDITIONS::ShowAlways ); ctxMenu.AddItem( ACTIONS::save, libSelectedCondition ); @@ -220,6 +233,42 @@ int LIB_CONTROL::OnDeMorgan( const TOOL_EVENT& aEvent ) } +int LIB_CONTROL::PinLibrary( const TOOL_EVENT& aEvent ) +{ + if( m_frame->IsType( FRAME_SCH_LIB_EDITOR ) ) + { + LIB_EDIT_FRAME* editFrame = static_cast( m_frame ); + LIB_TREE_NODE* currentNode = editFrame->GetCurrentTreeNode(); + + if( currentNode && !currentNode->m_Pinned ) + { + currentNode->m_Pinned = true; + editFrame->RegenerateLibraryTree(); + } + } + + return 0; +} + + +int LIB_CONTROL::UnpinLibrary( const TOOL_EVENT& aEvent ) +{ + if( m_frame->IsType( FRAME_SCH_LIB_EDITOR ) ) + { + LIB_EDIT_FRAME* editFrame = static_cast( m_frame ); + LIB_TREE_NODE* currentNode = editFrame->GetCurrentTreeNode(); + + if( currentNode && currentNode->m_Pinned ) + { + currentNode->m_Pinned = false; + editFrame->RegenerateLibraryTree(); + } + } + + return 0; +} + + int LIB_CONTROL::ShowComponentTree( const TOOL_EVENT& aEvent ) { if( m_frame->IsType( FRAME_SCH_LIB_EDITOR ) ) @@ -435,6 +484,8 @@ void LIB_CONTROL::setTransitions() Go( &LIB_CONTROL::OnDeMorgan, EE_ACTIONS::showDeMorganAlternate.MakeEvent() ); Go( &LIB_CONTROL::ShowElectricalTypes, EE_ACTIONS::showElectricalTypes.MakeEvent() ); + Go( &LIB_CONTROL::PinLibrary, ACTIONS::pinLibrary.MakeEvent() ); + Go( &LIB_CONTROL::UnpinLibrary, ACTIONS::unpinLibrary.MakeEvent() ); Go( &LIB_CONTROL::ShowComponentTree, EE_ACTIONS::showComponentTree.MakeEvent() ); Go( &LIB_CONTROL::ToggleSyncedPinsMode, EE_ACTIONS::toggleSyncedPinsMode.MakeEvent() ); } diff --git a/eeschema/tools/lib_control.h b/eeschema/tools/lib_control.h index 4a7afcc839..b85af000db 100644 --- a/eeschema/tools/lib_control.h +++ b/eeschema/tools/lib_control.h @@ -65,6 +65,8 @@ public: int OnDeMorgan( const TOOL_EVENT& aEvent ); int ShowElectricalTypes( const TOOL_EVENT& aEvent ); + int PinLibrary( const TOOL_EVENT& aEvent ); + int UnpinLibrary( const TOOL_EVENT& aEvent ); int ShowComponentTree( const TOOL_EVENT& aEvent ); int ToggleSyncedPinsMode( const TOOL_EVENT& aEvent ); diff --git a/eeschema/widgets/symbol_tree_pane.cpp b/eeschema/widgets/symbol_tree_pane.cpp index 0d0b5efe3b..2b4c574ee4 100644 --- a/eeschema/widgets/symbol_tree_pane.cpp +++ b/eeschema/widgets/symbol_tree_pane.cpp @@ -57,13 +57,6 @@ SYMBOL_TREE_PANE::~SYMBOL_TREE_PANE() } -void SYMBOL_TREE_PANE::Regenerate() -{ - if( m_tree ) - m_tree->Regenerate( true ); -} - - void SYMBOL_TREE_PANE::onComponentSelected( wxCommandEvent& aEvent ) { m_libEditFrame->GetToolManager()->RunAction( EE_ACTIONS::editSymbol, true ); diff --git a/eeschema/widgets/symbol_tree_pane.h b/eeschema/widgets/symbol_tree_pane.h index 5ffdcfea0a..806c408b2f 100644 --- a/eeschema/widgets/symbol_tree_pane.h +++ b/eeschema/widgets/symbol_tree_pane.h @@ -48,9 +48,6 @@ public: return m_tree; } - ///> Updates the component tree - void Regenerate(); - protected: void onComponentSelected( wxCommandEvent& aEvent ); diff --git a/include/env_paths.h b/include/env_paths.h index a27fbcc7ca..0e0e0e0912 100644 --- a/include/env_paths.h +++ b/include/env_paths.h @@ -33,7 +33,7 @@ * @param aFilePath is the full file path (path and file name) to be normalized. * @param aEnvVars is an optional map of environmental variables to try substition with. * @param aProject is an optional project, to normalize the file path to the project path. - * @return Normalized full file path (path and file name) if succeeded or empty string if the + * @return m_Normalized full file path (path and file name) if succeeded or empty string if the * path could not be normalized. */ wxString NormalizePath( const wxFileName& aFilePath, const ENV_VAR_MAP* aEnvVars, @@ -45,7 +45,7 @@ wxString NormalizePath( const wxFileName& aFilePath, const ENV_VAR_MAP* aEnvVars * @param aFilePath is the full file path (path and file name) to be normalized. * @param aEnvVars is an optional map of environmental variables to try substition with. * @param aProjectPath is an optional string to normalize the file path to the project path. - * @return Normalized full file path (path and file name) if succeeded or empty string if the + * @return m_Normalized full file path (path and file name) if succeeded or empty string if the * path could not be normalized. */ wxString NormalizePath( diff --git a/include/tool/actions.h b/include/tool/actions.h index 1d27131fc5..12410f1ce5 100644 --- a/include/tool/actions.h +++ b/include/tool/actions.h @@ -101,6 +101,9 @@ public: static TOOL_ACTION refreshPreview; // Similar to a synthetic mouseMoved event, but also // used after a rotate, mirror, etc. + static TOOL_ACTION pinLibrary; + static TOOL_ACTION unpinLibrary; + /// Cursor control with keyboard static TOOL_ACTION cursorUp; static TOOL_ACTION cursorDown; diff --git a/pcbnew/autorouter/ar_autoplacer.cpp b/pcbnew/autorouter/ar_autoplacer.cpp index 9680a313af..638e0d1544 100644 --- a/pcbnew/autorouter/ar_autoplacer.cpp +++ b/pcbnew/autorouter/ar_autoplacer.cpp @@ -649,7 +649,7 @@ int AR_AUTOPLACER::getOptimalModulePlacement(MODULE* aModule) fpBBox.SetOrigin( fpBBoxOrg + m_curPosition ); min_cost = -1.0; -// m_frame->SetStatusText( wxT( "Score ??, pos ??" ) ); +// m_frame->SetStatusText( wxT( "m_Score ??, pos ??" ) ); for( ; m_curPosition.x < xylimit.x; m_curPosition.x += m_matrix.m_GridRouting ) @@ -675,7 +675,7 @@ int AR_AUTOPLACER::getOptimalModulePlacement(MODULE* aModule) LastPosOK = m_curPosition; min_cost = Score; wxString msg; -/* msg.Printf( wxT( "Score %g, pos %s, %s" ), +/* msg.Printf( wxT( "m_Score %g, pos %s, %s" ), min_cost, GetChars( ::CoordinateToString( LastPosOK.x ) ), GetChars( ::CoordinateToString( LastPosOK.y ) ) ); diff --git a/pcbnew/footprint_edit_frame.cpp b/pcbnew/footprint_edit_frame.cpp index 6d3b48339e..e9b84e60f0 100644 --- a/pcbnew/footprint_edit_frame.cpp +++ b/pcbnew/footprint_edit_frame.cpp @@ -298,6 +298,12 @@ LIB_ID FOOTPRINT_EDIT_FRAME::GetTreeFPID() const } +LIB_TREE_NODE* FOOTPRINT_EDIT_FRAME::GetCurrentTreeNode() const +{ + return m_treePane->GetLibTree()->GetCurrentTreeNode(); +} + + LIB_ID FOOTPRINT_EDIT_FRAME::GetTargetFPID() const { LIB_ID id = GetTreeFPID(); @@ -715,7 +721,7 @@ void FOOTPRINT_EDIT_FRAME::SyncLibraryTree( bool aProgress ) adapter->Sync(); m_treePane->GetLibTree()->Unselect(); - m_treePane->Regenerate(); + m_treePane->GetLibTree()->Regenerate( true ); if( target.IsValid() ) { @@ -736,6 +742,17 @@ void FOOTPRINT_EDIT_FRAME::SyncLibraryTree( bool aProgress ) } +void FOOTPRINT_EDIT_FRAME::RegenerateLibraryTree() +{ + LIB_ID target = GetTargetFPID(); + + m_treePane->GetLibTree()->Regenerate( true ); + + if( target.IsValid() ) + m_treePane->GetLibTree()->CenterLibId( target ); +} + + void FOOTPRINT_EDIT_FRAME::FocusOnLibID( const LIB_ID& aLibID ) { m_treePane->GetLibTree()->SelectLibId( aLibID ); diff --git a/pcbnew/footprint_edit_frame.h b/pcbnew/footprint_edit_frame.h index 3720d4bc27..a6ce6369a8 100644 --- a/pcbnew/footprint_edit_frame.h +++ b/pcbnew/footprint_edit_frame.h @@ -208,6 +208,8 @@ public: /// Return the LIB_ID of the part or library selected in the footprint tree. LIB_ID GetTreeFPID() const; + LIB_TREE_NODE* GetCurrentTreeNode() const; + /// Return the LIB_ID of the part being edited. LIB_ID GetLoadedFPID() const; @@ -215,11 +217,6 @@ public: /// there is no selection in the tree. LIB_ID GetTargetFPID() const; - /** - * Perform a geometric transform on the current footprint. - */ - void Transform( MODULE* module, int transform ); - // importing / exporting Footprint /** * Create a file containing only one footprint. @@ -339,6 +336,13 @@ public: * @param aProgress */ void SyncLibraryTree( bool aProgress ); + + /** + * Filter, sort, and redisplay the library tree. Does NOT synchronize it with libraries + * in disk. + */ + void RegenerateLibraryTree(); + void FocusOnLibID( const LIB_ID& aLibID ); void KiwayMailIn( KIWAY_EXPRESS& mail ) override; diff --git a/pcbnew/footprint_tree_pane.cpp b/pcbnew/footprint_tree_pane.cpp index 51d76451d3..8d3ac4a61d 100644 --- a/pcbnew/footprint_tree_pane.cpp +++ b/pcbnew/footprint_tree_pane.cpp @@ -53,13 +53,6 @@ FOOTPRINT_TREE_PANE::~FOOTPRINT_TREE_PANE() } -void FOOTPRINT_TREE_PANE::Regenerate() -{ - if( m_tree ) - m_tree->Regenerate( true ); -} - - void FOOTPRINT_TREE_PANE::onComponentSelected( wxCommandEvent& aEvent ) { m_frame->LoadModuleFromLibrary( GetLibTree()->GetSelectedLibId() ); diff --git a/pcbnew/footprint_tree_pane.h b/pcbnew/footprint_tree_pane.h index 5b6647929b..c06b319cd5 100644 --- a/pcbnew/footprint_tree_pane.h +++ b/pcbnew/footprint_tree_pane.h @@ -47,9 +47,6 @@ public: return m_tree; } - ///> Updates the footprint tree - void Regenerate(); - protected: void onComponentSelected( wxCommandEvent& aEvent ); void onUpdateUI( wxUpdateUIEvent& aEvent ); diff --git a/pcbnew/fp_tree_synchronizing_adapter.cpp b/pcbnew/fp_tree_synchronizing_adapter.cpp index 048f560159..39c11ee9f0 100644 --- a/pcbnew/fp_tree_synchronizing_adapter.cpp +++ b/pcbnew/fp_tree_synchronizing_adapter.cpp @@ -57,7 +57,7 @@ TOOL_INTERACTIVE* FP_TREE_SYNCHRONIZING_ADAPTER::GetContextMenuTool() bool FP_TREE_SYNCHRONIZING_ADAPTER::IsContainer( const wxDataViewItem& aItem ) const { const LIB_TREE_NODE* node = ToNode( aItem ); - return node ? node->Type == LIB_TREE_NODE::LIB : true; + return node ? node->m_Type == LIB_TREE_NODE::LIB : true; } @@ -66,9 +66,9 @@ bool FP_TREE_SYNCHRONIZING_ADAPTER::IsContainer( const wxDataViewItem& aItem ) c void FP_TREE_SYNCHRONIZING_ADAPTER::Sync() { // Process already stored libraries - for( auto it = m_tree.Children.begin(); it != m_tree.Children.end(); ) + for( auto it = m_tree.m_Children.begin(); it != m_tree.m_Children.end(); ) { - const wxString& name = it->get()->Name; + const wxString& name = it->get()->m_Name; if( !m_libs->HasLibrary( name, true ) ) { @@ -107,14 +107,14 @@ int FP_TREE_SYNCHRONIZING_ADAPTER::GetLibrariesCount() const void FP_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIB& aLibNode ) { - std::vector footprints = getFootprints( aLibNode.Name ); + std::vector footprints = getFootprints( aLibNode.m_Name ); // remove the common part from the footprints list - for( auto nodeIt = aLibNode.Children.begin(); nodeIt != aLibNode.Children.end(); ) + for( auto nodeIt = aLibNode.m_Children.begin(); nodeIt != aLibNode.m_Children.end(); ) { // Since the list is sorted we can use a binary search to speed up searches within // libraries with lots of footprints. - FOOTPRINT_INFO_IMPL dummy( wxEmptyString, (*nodeIt)->Name ); + FOOTPRINT_INFO_IMPL dummy( wxEmptyString, (*nodeIt)->m_Name ); auto footprintIt = std::lower_bound( footprints.begin(), footprints.end(), &dummy, []( LIB_TREE_ITEM* a, LIB_TREE_ITEM* b ) { @@ -132,7 +132,7 @@ void FP_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIB& aLibNode ) else { // node does not exist in the library manager, remove the corresponding node - nodeIt = aLibNode.Children.erase( nodeIt ); + nodeIt = aLibNode.m_Children.erase( nodeIt ); } } @@ -141,7 +141,7 @@ void FP_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIB& aLibNode ) aLibNode.AddItem( footprint ); aLibNode.AssignIntrinsicRanks(); - m_libMap.insert( aLibNode.Name ); + m_libMap.insert( aLibNode.m_Name ); } @@ -149,8 +149,8 @@ LIB_TREE_NODE::PTR_VECTOR::iterator FP_TREE_SYNCHRONIZING_ADAPTER::deleteLibrary LIB_TREE_NODE::PTR_VECTOR::iterator& aLibNodeIt ) { LIB_TREE_NODE* node = aLibNodeIt->get(); - m_libMap.erase( node->Name ); - auto it = m_tree.Children.erase( aLibNodeIt ); + m_libMap.erase( node->m_Name ); + auto it = m_tree.m_Children.erase( aLibNodeIt ); return it; } @@ -169,7 +169,7 @@ void FP_TREE_SYNCHRONIZING_ADAPTER::GetValue( wxVariant& aVariant, wxDataViewIte switch( aCol ) { case 0: - if( node->LibId == m_frame->GetLoadedFPID() && !m_frame->IsCurrentFPFromBoard() ) + if( node->m_LibId == m_frame->GetLoadedFPID() && !m_frame->IsCurrentFPFromBoard() ) { auto mod = m_frame->GetBoard()->GetFirstModule(); @@ -183,16 +183,18 @@ void FP_TREE_SYNCHRONIZING_ADAPTER::GetValue( wxVariant& aVariant, wxDataViewIte else aVariant = currentFPName; } + else if( node->m_Pinned ) + aVariant = "☆ " + node->m_Name; else - aVariant = node->Name; + aVariant = node->m_Name; break; case 1: - aVariant = node->Desc; + aVariant = node->m_Desc; break; default: // column == -1 is used for default Compare function - aVariant = node->Name; + aVariant = node->m_Name; break; } } @@ -215,10 +217,10 @@ bool FP_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, unsign auto node = ToNode( aItem ); wxCHECK( node, false ); - switch( node->Type ) + switch( node->m_Type ) { case LIB_TREE_NODE::LIB: - if( node->Name == m_frame->GetLoadedFPID().GetLibNickname() ) + if( node->m_Name == m_frame->GetLoadedFPID().GetLibNickname() ) { #ifdef __WXGTK__ // The native wxGTK+ impl ignores background colour, so set the text colour @@ -236,7 +238,7 @@ bool FP_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, unsign break; case LIB_TREE_NODE::LIBID: - if( node->LibId == m_frame->GetLoadedFPID() ) + if( node->m_LibId == m_frame->GetLoadedFPID() ) { #ifdef __WXGTK__ // The native wxGTK+ impl ignores background colour, so set the text colour diff --git a/pcbnew/tools/footprint_editor_tools.cpp b/pcbnew/tools/footprint_editor_tools.cpp index 7e267c064c..243d7cd7b8 100644 --- a/pcbnew/tools/footprint_editor_tools.cpp +++ b/pcbnew/tools/footprint_editor_tools.cpp @@ -82,11 +82,23 @@ bool MODULE_EDITOR_TOOLS::Init() LIB_ID sel = m_frame->GetTreeFPID(); return !sel.GetLibNickname().empty() && sel.GetLibItemName().empty(); }; + auto pinnedLibSelectedCondition = [ this ] ( const SELECTION& aSel ) { + LIB_TREE_NODE* current = m_frame->GetCurrentTreeNode(); + return current && current->m_Type == LIB_TREE_NODE::LIB && current->m_Pinned; + }; + auto unpinnedLibSelectedCondition = [ this ] (const SELECTION& aSel ) { + LIB_TREE_NODE* current = m_frame->GetCurrentTreeNode(); + return current && current->m_Type == LIB_TREE_NODE::LIB && !current->m_Pinned; + }; auto fpSelectedCondition = [ this ] ( const SELECTION& aSel ) { LIB_ID sel = m_frame->GetTreeFPID(); return !sel.GetLibNickname().empty() && !sel.GetLibItemName().empty(); }; + ctxMenu.AddItem( ACTIONS::pinLibrary, unpinnedLibSelectedCondition ); + ctxMenu.AddItem( ACTIONS::unpinLibrary, pinnedLibSelectedCondition ); + ctxMenu.AddSeparator(); + ctxMenu.AddItem( ACTIONS::newLibrary, SELECTION_CONDITIONS::ShowAlways ); ctxMenu.AddItem( ACTIONS::addLibrary, SELECTION_CONDITIONS::ShowAlways ); ctxMenu.AddItem( ACTIONS::save, libSelectedCondition ); @@ -256,6 +268,34 @@ int MODULE_EDITOR_TOOLS::EditFootprint( const TOOL_EVENT& aEvent ) } +int MODULE_EDITOR_TOOLS::PinLibrary( const TOOL_EVENT& aEvent ) +{ + LIB_TREE_NODE* currentNode = m_frame->GetCurrentTreeNode(); + + if( currentNode && !currentNode->m_Pinned ) + { + currentNode->m_Pinned = true; + m_frame->RegenerateLibraryTree(); + } + + return 0; +} + + +int MODULE_EDITOR_TOOLS::UnpinLibrary( const TOOL_EVENT& aEvent ) +{ + LIB_TREE_NODE* currentNode = m_frame->GetCurrentTreeNode(); + + if( currentNode && currentNode->m_Pinned ) + { + currentNode->m_Pinned = false; + m_frame->RegenerateLibraryTree(); + } + + return 0; +} + + int MODULE_EDITOR_TOOLS::ToggleFootprintTree( const TOOL_EVENT& aEvent ) { m_frame->ToggleSearchTree(); @@ -558,6 +598,8 @@ void MODULE_EDITOR_TOOLS::setTransitions() Go( &MODULE_EDITOR_TOOLS::ImportFootprint, PCB_ACTIONS::importFootprint.MakeEvent() ); Go( &MODULE_EDITOR_TOOLS::ExportFootprint, PCB_ACTIONS::exportFootprint.MakeEvent() ); + Go( &MODULE_EDITOR_TOOLS::PinLibrary, ACTIONS::pinLibrary.MakeEvent() ); + Go( &MODULE_EDITOR_TOOLS::UnpinLibrary, ACTIONS::unpinLibrary.MakeEvent() ); Go( &MODULE_EDITOR_TOOLS::ToggleFootprintTree, PCB_ACTIONS::toggleFootprintTree.MakeEvent() ); Go( &MODULE_EDITOR_TOOLS::Properties, PCB_ACTIONS::footprintProperties.MakeEvent() ); Go( &MODULE_EDITOR_TOOLS::DefaultPadProperties, PCB_ACTIONS::defaultPadProperties.MakeEvent() ); diff --git a/pcbnew/tools/footprint_editor_tools.h b/pcbnew/tools/footprint_editor_tools.h index 4edef6262d..04f74533dd 100644 --- a/pcbnew/tools/footprint_editor_tools.h +++ b/pcbnew/tools/footprint_editor_tools.h @@ -61,7 +61,9 @@ public: int DeleteFootprint( const TOOL_EVENT& aEvent ); int ImportFootprint( const TOOL_EVENT& aEvent ); int ExportFootprint( const TOOL_EVENT& aEvent ); - + + int PinLibrary( const TOOL_EVENT& aEvent ); + int UnpinLibrary( const TOOL_EVENT& aEvent ); int ToggleFootprintTree( const TOOL_EVENT& aEvent ); int Properties( const TOOL_EVENT& aEvent );