You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

358 lines
10 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 Chris Pavlina <pavlina.chris@gmail.com>
  5. * Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
  6. * Copyright (C) 2023 CERN
  7. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  8. *
  9. * This program is free software: you can redistribute it and/or modify it
  10. * under the terms of the GNU General Public License as published by the
  11. * Free Software Foundation, either version 3 of the License, or (at your
  12. * option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License along
  20. * with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. #include <lib_tree_model.h>
  23. #include <algorithm>
  24. #include <eda_pattern_match.h>
  25. #include <lib_tree_item.h>
  26. #include <pgm_base.h>
  27. #include <string_utils.h>
  28. void LIB_TREE_NODE::ResetScore()
  29. {
  30. for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
  31. child->ResetScore();
  32. m_Score = 0;
  33. }
  34. void LIB_TREE_NODE::AssignIntrinsicRanks( bool presorted )
  35. {
  36. std::vector<LIB_TREE_NODE*> sort_buf;
  37. if( presorted )
  38. {
  39. int max = m_Children.size() - 1;
  40. for( int i = 0; i <= max; ++i )
  41. m_Children[i]->m_IntrinsicRank = max - i;
  42. }
  43. else
  44. {
  45. for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
  46. sort_buf.push_back( child.get() );
  47. std::sort( sort_buf.begin(), sort_buf.end(),
  48. []( LIB_TREE_NODE* a, LIB_TREE_NODE* b ) -> bool
  49. {
  50. return StrNumCmp( a->m_Name, b->m_Name, true ) > 0;
  51. } );
  52. for( int i = 0; i < (int) sort_buf.size(); ++i )
  53. sort_buf[i]->m_IntrinsicRank = i;
  54. }
  55. }
  56. void LIB_TREE_NODE::SortNodes( bool aUseScores )
  57. {
  58. std::sort( m_Children.begin(), m_Children.end(),
  59. [&]( std::unique_ptr<LIB_TREE_NODE>& a, std::unique_ptr<LIB_TREE_NODE>& b )
  60. {
  61. return Compare( *a, *b, aUseScores );
  62. } );
  63. for( std::unique_ptr<LIB_TREE_NODE>& node: m_Children )
  64. node->SortNodes( aUseScores );
  65. }
  66. bool LIB_TREE_NODE::Compare( LIB_TREE_NODE const& aNode1, LIB_TREE_NODE const& aNode2,
  67. bool aUseScores )
  68. {
  69. if( aNode1.m_Type != aNode2.m_Type )
  70. return aNode1.m_Type < aNode2.m_Type;
  71. // Recently used sorts at top
  72. if( aNode1.m_IsRecentlyUsedGroup )
  73. {
  74. if( aNode2.m_IsRecentlyUsedGroup )
  75. {
  76. // Make sure "-- Recently Used" is always at the top
  77. // Start by checking the name of aNode2, because we want to satisfy the irreflexive
  78. // property of the strict weak ordering.
  79. if( aNode2.m_IsRecentlyUsedGroup )
  80. return false;
  81. else if( aNode1.m_IsRecentlyUsedGroup )
  82. return true;
  83. return aNode1.m_IntrinsicRank > aNode2.m_IntrinsicRank;
  84. }
  85. else
  86. {
  87. return true;
  88. }
  89. }
  90. else if( aNode2.m_Name.StartsWith( wxT( "-- " ) ) )
  91. {
  92. return false;
  93. }
  94. // Pinned nodes go next
  95. if( aNode1.m_Pinned && !aNode2.m_Pinned )
  96. return true;
  97. else if( aNode2.m_Pinned && !aNode1.m_Pinned )
  98. return false;
  99. if( aUseScores && aNode1.m_Score != aNode2.m_Score )
  100. return aNode1.m_Score > aNode2.m_Score;
  101. if( aNode1.m_IntrinsicRank != aNode2.m_IntrinsicRank )
  102. return aNode1.m_IntrinsicRank > aNode2.m_IntrinsicRank;
  103. return reinterpret_cast<const void*>( &aNode1 ) < reinterpret_cast<const void*>( &aNode2 );
  104. }
  105. LIB_TREE_NODE::LIB_TREE_NODE()
  106. : m_Parent( nullptr ),
  107. m_Type( TYPE::INVALID ),
  108. m_IntrinsicRank( 0 ),
  109. m_Score( 0 ),
  110. m_Pinned( false ),
  111. m_PinCount( 0 ),
  112. m_Unit( 0 ),
  113. m_IsRoot( false ),
  114. m_IsRecentlyUsedGroup( false ),
  115. m_IsAlreadyPlacedGroup( false )
  116. {}
  117. LIB_TREE_NODE_UNIT::LIB_TREE_NODE_UNIT( LIB_TREE_NODE* aParent, LIB_TREE_ITEM* aItem, int aUnit )
  118. {
  119. static void* locale = nullptr;
  120. static wxString namePrefix;
  121. // Fetching translations can take a surprising amount of time when loading libraries,
  122. // so only do it when necessary.
  123. if( Pgm().GetLocale() != locale )
  124. {
  125. namePrefix = _( "Unit" );
  126. locale = Pgm().GetLocale();
  127. }
  128. m_Parent = aParent;
  129. m_Type = TYPE::UNIT;
  130. m_Unit = aUnit;
  131. m_LibId = aParent->m_LibId;
  132. m_Name = namePrefix + " " + aItem->GetUnitReference( aUnit );
  133. if( aItem->HasUnitDisplayName( aUnit ) )
  134. m_Desc = aItem->GetUnitDisplayName( aUnit );
  135. else
  136. m_Desc = wxEmptyString;
  137. m_IntrinsicRank = -aUnit;
  138. }
  139. void LIB_TREE_NODE_UNIT::UpdateScore( const std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>>& aMatchers,
  140. std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
  141. {
  142. // aMatchers test results are inherited from parent
  143. if( !aMatchers.empty() )
  144. m_Score = m_Parent->m_Score;
  145. // aFilter test is subtractive
  146. if( aFilter && !(*aFilter)(*this) )
  147. m_Score = 0;
  148. // show all nodes if no search/filter/etc. criteria are given
  149. if( aMatchers.empty() && ( !aFilter || (*aFilter)(*this) ) )
  150. m_Score = 1;
  151. }
  152. LIB_TREE_NODE_ITEM::LIB_TREE_NODE_ITEM( LIB_TREE_NODE* aParent, LIB_TREE_ITEM* aItem )
  153. {
  154. m_Type = TYPE::ITEM;
  155. m_Parent = aParent;
  156. m_LibId.SetLibNickname( aItem->GetLibNickname() );
  157. m_LibId.SetLibItemName( aItem->GetName() );
  158. m_Name = aItem->GetName();
  159. m_Desc = aItem->GetDesc();
  160. m_Footprint = aItem->GetFootprint();
  161. m_PinCount = aItem->GetPinCount();
  162. aItem->GetChooserFields( m_Fields );
  163. m_SearchTerms = aItem->GetSearchTerms();
  164. m_IsRoot = aItem->IsRoot();
  165. if( aItem->GetSubUnitCount() > 1 )
  166. {
  167. for( int u = 1; u <= aItem->GetSubUnitCount(); ++u )
  168. AddUnit( aItem, u );
  169. }
  170. }
  171. LIB_TREE_NODE_UNIT& LIB_TREE_NODE_ITEM::AddUnit( LIB_TREE_ITEM* aItem, int aUnit )
  172. {
  173. LIB_TREE_NODE_UNIT* unit = new LIB_TREE_NODE_UNIT( this, aItem, aUnit );
  174. m_Children.push_back( std::unique_ptr<LIB_TREE_NODE>( unit ) );
  175. return *unit;
  176. }
  177. void LIB_TREE_NODE_ITEM::Update( LIB_TREE_ITEM* aItem )
  178. {
  179. m_LibId.SetLibNickname( aItem->GetLIB_ID().GetLibNickname() );
  180. m_LibId.SetLibItemName( aItem->GetName() );
  181. m_Name = aItem->GetName();
  182. m_Desc = aItem->GetDesc();
  183. aItem->GetChooserFields( m_Fields );
  184. m_SearchTerms = aItem->GetSearchTerms();
  185. m_IsRoot = aItem->IsRoot();
  186. m_Children.clear();
  187. for( int u = 1; u <= aItem->GetSubUnitCount(); ++u )
  188. AddUnit( aItem, u );
  189. }
  190. void LIB_TREE_NODE_ITEM::UpdateScore( const std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>>& aMatchers,
  191. std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
  192. {
  193. m_Score = 0;
  194. for( const std::unique_ptr<EDA_COMBINED_MATCHER>& matcher : aMatchers )
  195. m_Score += matcher->ScoreTerms( m_SearchTerms );
  196. // aFilter test is subtractive
  197. if( aFilter && !(*aFilter)(*this) )
  198. m_Score = 0;
  199. // show all nodes if no search/filter/etc. criteria are given
  200. if( aMatchers.empty() && ( !aFilter || (*aFilter)(*this) ) )
  201. m_Score = 1;
  202. for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
  203. child->UpdateScore( aMatchers, aFilter );
  204. }
  205. LIB_TREE_NODE_LIBRARY::LIB_TREE_NODE_LIBRARY( LIB_TREE_NODE* aParent, wxString const& aName,
  206. wxString const& aDesc )
  207. {
  208. m_Type = TYPE::LIBRARY;
  209. m_Name = aName;
  210. m_Desc = aDesc;
  211. m_Parent = aParent;
  212. m_LibId.SetLibNickname( aName );
  213. m_SearchTerms.emplace_back( SEARCH_TERM( aName, 8 ) );
  214. }
  215. LIB_TREE_NODE_ITEM& LIB_TREE_NODE_LIBRARY::AddItem( LIB_TREE_ITEM* aItem )
  216. {
  217. LIB_TREE_NODE_ITEM* item = new LIB_TREE_NODE_ITEM( this, aItem );
  218. m_Children.push_back( std::unique_ptr<LIB_TREE_NODE>( item ) );
  219. return *item;
  220. }
  221. void LIB_TREE_NODE_LIBRARY::UpdateScore( const std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>>& aMatchers,
  222. std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
  223. {
  224. if( m_Children.empty() )
  225. {
  226. for( const std::unique_ptr<EDA_COMBINED_MATCHER>& matcher : aMatchers )
  227. m_Score += matcher->ScoreTerms( m_SearchTerms );
  228. }
  229. else
  230. {
  231. int maxChildScore = 0;
  232. for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
  233. {
  234. child->UpdateScore( aMatchers, aFilter );
  235. maxChildScore = std::max( maxChildScore, child->m_Score );
  236. }
  237. m_Score = std::max( m_Score, maxChildScore );
  238. }
  239. // show all nodes if no search/filter/etc. criteria are given
  240. if( m_Children.empty() && aMatchers.empty() && ( !aFilter || (*aFilter)(*this) ) )
  241. m_Score = 1;
  242. }
  243. LIB_TREE_NODE_ROOT::LIB_TREE_NODE_ROOT()
  244. {
  245. m_Type = TYPE::ROOT;
  246. }
  247. LIB_TREE_NODE_LIBRARY& LIB_TREE_NODE_ROOT::AddLib( wxString const& aName, wxString const& aDesc )
  248. {
  249. LIB_TREE_NODE_LIBRARY* lib = new LIB_TREE_NODE_LIBRARY( this, aName, aDesc );
  250. m_Children.push_back( std::unique_ptr<LIB_TREE_NODE>( lib ) );
  251. return *lib;
  252. }
  253. void LIB_TREE_NODE_ROOT::RemoveGroup( bool aRecentlyUsedGroup, bool aAlreadyPlacedGroup )
  254. {
  255. m_Children.erase( std::remove_if( m_Children.begin(), m_Children.end(),
  256. [&]( std::unique_ptr<LIB_TREE_NODE>& aNode )
  257. {
  258. if( aRecentlyUsedGroup && aNode->m_IsRecentlyUsedGroup )
  259. return true;
  260. if( aAlreadyPlacedGroup && aNode->m_IsAlreadyPlacedGroup )
  261. return true;
  262. return false;
  263. } ),
  264. m_Children.end() );
  265. }
  266. void LIB_TREE_NODE_ROOT::Clear()
  267. {
  268. m_Children.clear();
  269. }
  270. void LIB_TREE_NODE_ROOT::UpdateScore( const std::vector<std::unique_ptr<EDA_COMBINED_MATCHER>>& aMatchers,
  271. std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
  272. {
  273. for( std::unique_ptr<LIB_TREE_NODE>& child: m_Children )
  274. child->UpdateScore( aMatchers, aFilter );
  275. }