KiCad PCB EDA Suite
cmp_tree_model_adapter.cpp
Go to the documentation of this file.
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) 2014-2017 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software: you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation, either version 3 of the License, or (at your
11  * option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <cmp_tree_model_adapter.h>
23 
24 #include <class_library.h>
25 #include <eda_pattern_match.h>
26 #include <wx/tokenzr.h>
27 
28 
29 CMP_TREE_MODEL_ADAPTER::WIDTH_CACHE CMP_TREE_MODEL_ADAPTER::m_width_cache;
30 
31 
32 static const int kDataViewIndent = 20;
33 
34 
38 static wxDataViewItem ToItem( CMP_TREE_NODE const* aNode )
39 {
40  return wxDataViewItem( const_cast<void*>( static_cast<void const*>( aNode ) ) );
41 }
42 
43 
47 static CMP_TREE_NODE const* ToNode( wxDataViewItem aItem )
48 {
49  return static_cast<CMP_TREE_NODE const*>( aItem.GetID() );
50 }
51 
52 
56 static unsigned int IntoArray( CMP_TREE_NODE const& aNode, wxDataViewItemArray& aChildren )
57 {
58  unsigned int n = 0;
59 
60  for( auto const& child: aNode.Children )
61  {
62  if( child->Score > 0 )
63  {
64  aChildren.Add( ToItem( &*child ) );
65  ++n;
66  }
67  }
68 
69  return n;
70 }
71 
72 
74 {
75  auto adapter = new CMP_TREE_MODEL_ADAPTER( aLibs );
76  auto container = CMP_TREE_MODEL_ADAPTER::PTR( adapter );
77  return container;
78 }
79 
80 
82  :m_filter( CMP_FILTER_NONE ),
83  m_show_units( true ),
84  m_libs( aLibs ),
85  m_preselect_unit( 0 ),
86  m_col_part( nullptr ),
87  m_col_desc( nullptr ),
88  m_widget( nullptr )
89 {}
90 
91 
93 {}
94 
95 
97 {
98  m_filter = aFilter;
99 }
100 
101 
103 {
104  m_show_units = aShow;
105 }
106 
107 
108 void CMP_TREE_MODEL_ADAPTER::SetPreselectNode( wxString const& aName, int aUnit )
109 {
110  m_preselect_name = aName;
111  m_preselect_unit = aUnit;
112 }
113 
114 
116 {
117  if( m_filter == CMP_FILTER_POWER )
118  {
119  wxArrayString all_aliases;
120  aLib.GetEntryTypePowerNames( all_aliases );
121  AddAliasList( aLib.GetName(), all_aliases, &aLib );
122  }
123  else
124  {
125  std::vector<LIB_ALIAS*> all_aliases;
126  aLib.GetAliases( all_aliases );
127  AddAliasList( aLib.GetName(), all_aliases, &aLib );
128  }
129 
131 }
132 
133 
135  wxString const& aNodeName,
136  wxArrayString const& aAliasNameList,
137  PART_LIB* aOptionalLib )
138 {
139  std::vector<LIB_ALIAS*> alias_list;
140 
141  for( const wxString& name: aAliasNameList )
142  {
143  LIB_ALIAS* a;
144 
145  if( aOptionalLib )
146  a = aOptionalLib->FindAlias( name );
147  else
148  a = m_libs->FindLibraryAlias( LIB_ID( wxEmptyString, name ), wxEmptyString );
149 
150  if( a )
151  alias_list.push_back( a );
152  }
153 
154  AddAliasList( aNodeName, alias_list, aOptionalLib );
155 }
156 
157 
159  wxString const& aNodeName,
160  std::vector<LIB_ALIAS*> const& aAliasList,
161  PART_LIB* aOptionalLib )
162 {
163  auto& lib_node = m_tree.AddLib( aNodeName );
164 
165  for( auto a: aAliasList )
166  {
167  lib_node.AddAlias( a );
168  }
169 
170  lib_node.AssignIntrinsicRanks();
172 }
173 
174 
175 void CMP_TREE_MODEL_ADAPTER::UpdateSearchString( wxString const& aSearch )
176 {
177  m_tree.ResetScore();
178 
179  wxStringTokenizer tokenizer( aSearch );
180 
181  while( tokenizer.HasMoreTokens() )
182  {
183  const wxString term = tokenizer.GetNextToken().Lower();
184  EDA_COMBINED_MATCHER matcher( term );
185 
186  m_tree.UpdateScore( matcher );
187  }
188 
189  m_tree.SortNodes();
190  Cleared();
191  AttachTo( m_widget );
192 
194 }
195 
196 
197 void CMP_TREE_MODEL_ADAPTER::AttachTo( wxDataViewCtrl* aDataViewCtrl )
198 {
199  m_widget = aDataViewCtrl;
200  aDataViewCtrl->Freeze();
201  aDataViewCtrl->SetIndent( kDataViewIndent );
202  aDataViewCtrl->AssociateModel( this );
203  aDataViewCtrl->ClearColumns();
204 
205  wxString part_head = _( "Part" );
206  wxString desc_head = _( "Desc" );
207 
208  m_col_part = aDataViewCtrl->AppendTextColumn( part_head, 0, wxDATAVIEW_CELL_INERT,
209  ColWidth( m_tree, 0, part_head ) );
210  m_col_desc = aDataViewCtrl->AppendTextColumn( desc_head, 1, wxDATAVIEW_CELL_INERT,
211  ColWidth( m_tree, 1, desc_head ) );
212  aDataViewCtrl->Thaw();
213 }
214 
215 
216 LIB_ALIAS* CMP_TREE_MODEL_ADAPTER::GetAliasFor( wxDataViewItem aSelection ) const
217 {
218  auto node = ToNode( aSelection );
219  return node ? node->Alias : nullptr;
220 }
221 
222 
223 int CMP_TREE_MODEL_ADAPTER::GetUnitFor( wxDataViewItem aSelection ) const
224 {
225  auto node = ToNode( aSelection );
226  return node ? node->Unit : 0;
227 }
228 
229 
231 {
232  int n = 0;
233 
234  for( auto& lib: m_tree.Children )
235  {
236  for( auto& alias: lib->Children )
237  {
238  (void) alias;
239  ++n;
240  }
241  }
242 
243  return n;
244 }
245 
246 
247 bool CMP_TREE_MODEL_ADAPTER::HasContainerColumns( wxDataViewItem const& aItem ) const
248 {
249  return IsContainer( aItem );
250 }
251 
252 
253 bool CMP_TREE_MODEL_ADAPTER::IsContainer( wxDataViewItem const& aItem ) const
254 {
255  auto node = ToNode( aItem );
256  return node ? node->Children.size() : true;
257 }
258 
259 
260 wxDataViewItem CMP_TREE_MODEL_ADAPTER::GetParent( wxDataViewItem const& aItem ) const
261 {
262  auto node = ToNode( aItem );
263  auto parent = node ? node->Parent : nullptr;
264 
265  // wxDataViewModel has no root node, but rather top-level elements have
266  // an invalid (null) parent.
267  if( !node || !parent || parent->Type == CMP_TREE_NODE::TYPE::ROOT )
268  {
269  return ToItem( nullptr );
270  }
271  else
272  {
273  return ToItem( parent );
274  }
275 }
276 
277 
279  wxDataViewItem const& aItem,
280  wxDataViewItemArray& aChildren ) const
281 {
282  auto node = ( aItem.IsOk() ? ToNode( aItem ) : &m_tree );
283 
284  if( node->Type != CMP_TREE_NODE::TYPE::ALIAS || m_show_units )
285  return IntoArray( *node, aChildren );
286  else
287  return 0;
288 }
289 
290 
292  wxVariant& aVariant,
293  wxDataViewItem const& aItem,
294  unsigned int aCol ) const
295 {
296  auto node = ToNode( aItem );
297  wxASSERT( node );
298 
299  switch( aCol )
300  {
301  case 0:
302  aVariant = node->Name;
303  break;
304  case 1:
305  aVariant = node->Desc;
306  break;
307  default:
308  wxFAIL_MSG( "Invalid column ID!" );
309  }
310 }
311 
312 
313 int CMP_TREE_MODEL_ADAPTER::ColWidth( CMP_TREE_NODE& aTree, int aCol, wxString const& aHeading )
314 {
315  const int indent = aCol ? 0 : kDataViewIndent;
316 
317  int min_width = WidthFor( aHeading, aCol );
318  int width = std::max( aTree.Score > 0 ? WidthFor( aTree, aCol ) : 0, min_width );
319 
320  if( aTree.Score > 0 )
321  {
322  for( auto& node: aTree.Children )
323  {
324  width = std::max( width, ColWidth( *node, aCol, aHeading ) + indent );
325  }
326  }
327 
328  return width;
329 }
330 
331 
333 {
334  auto result = m_width_cache.find( aNode.Name );
335 
336  if( result != m_width_cache.end() )
337  {
338  return result->second[aCol];
339  }
340  else
341  {
342  int wname = m_widget->GetTextExtent( aNode.Name ).x + kDataViewIndent;
343  int wdesc = m_widget->GetTextExtent( aNode.Desc ).x;
344 
345  auto& val = m_width_cache[aNode.Name];
346  val.push_back( wname );
347  val.push_back( wdesc );
348  return val[aCol];
349  }
350 }
351 
352 
353 int CMP_TREE_MODEL_ADAPTER::WidthFor( wxString const& aHeading, int aCol )
354 {
355  static std::vector<int> widths;
356 
357  for( int i = (int) widths.size(); i <= aCol; ++i )
358  {
359  widths.push_back( 0 );
360  }
361 
362  if( widths[aCol] == 0 )
363  {
364  widths[aCol] = m_widget->GetTextExtent( aHeading ).x;
365  }
366 
367  return widths[aCol];
368 }
369 
370 
372  CMP_TREE_NODE& aNode,
373  std::function<bool( CMP_TREE_NODE const* )> aFunc )
374 {
375  for( auto& node: aNode.Children )
376  {
377  if( aFunc( &*node ) )
378  {
379  auto item = wxDataViewItem(
380  const_cast<void*>( static_cast<void const*>( &*node ) ) );
381  m_widget->ExpandAncestors( item );
382  m_widget->EnsureVisible( item );
383  m_widget->Select( item );
384  return true;
385  }
386  else if( FindAndExpand( *node, aFunc ) )
387  {
388  return true;
389  }
390  }
391 
392  return false;
393 }
394 
395 
397 {
398  return FindAndExpand( m_tree,
399  []( CMP_TREE_NODE const* n )
400  {
401  return n->Type == CMP_TREE_NODE::TYPE::ALIAS && n->Score > 1;
402  } );
403 }
404 
405 
407 {
408  if( m_preselect_name == wxEmptyString )
409  return false;
410 
411  return FindAndExpand( m_tree,
412  [&]( CMP_TREE_NODE const* n )
413  {
414  if( n->Type == CMP_TREE_NODE::ALIAS && ( n->Children.empty() || !m_preselect_unit ) )
415  return m_preselect_name == n->Name;
416  else if( n->Type == CMP_TREE_NODE::UNIT && m_preselect_unit )
417  return m_preselect_name == n->Parent->Name && m_preselect_unit == n->Unit;
418  else
419  return false;
420  } );
421 }
422 
423 
425 {
426  return FindAndExpand( m_tree,
427  []( CMP_TREE_NODE const* n )
428  {
429  return n->Type == CMP_TREE_NODE::TYPE::ALIAS &&
430  n->Parent->Parent->Children.size() == 1;
431  } );
432 }
wxString Name
Actual name of the part.
wxString Desc
Description to be displayed.
void SetPreselectNode(wxString const &aName, int aUnit)
Set the component name to be selected if there are no search results.
Part library alias object definition.
void AddLibrary(PART_LIB &aLib)
Add all the components and their aliases in this library.
virtual void GetValue(wxVariant &aVariant, wxDataViewItem const &aItem, unsigned int aCol) const override
Get the value of an item.
void GetEntryTypePowerNames(wxArrayString &aNames)
Load a string array with the names of entries of type POWER in this library.
static unsigned int IntoArray(CMP_TREE_NODE const &aNode, wxDataViewItemArray &aChildren)
Convert CMP_TREE_NODE's children to wxDataViewItemArray.
virtual void UpdateScore(EDA_COMBINED_MATCHER &aMatcher) override
Update the score for this part.
enum TYPE Type
Node type.
void ResetScore()
Initialize score to kLowestDefaultScore, recursively.
wxObjectDataPtr< CMP_TREE_MODEL_ADAPTER > PTR
Reference-counting container for a pointer to CMP_TREE_MODEL_ADAPTER.
int WidthFor(CMP_TREE_NODE &aNode, int aCol)
Return the width required to display a single row's aCol text.
virtual bool HasContainerColumns(wxDataViewItem const &aItem) const override
Check whether a container has columns too.
Class LIB_ID.
Definition: lib_id.h:56
bool FindAndExpand(CMP_TREE_NODE &aNode, std::function< bool(CMP_TREE_NODE const *)> aFunc)
Find any results worth highlighting and expand them, according to given criteria (f(CMP_TREE_NODE con...
virtual wxDataViewItem GetParent(wxDataViewItem const &aItem) const override
Get the parent of an item.
void SortNodes()
Sort child nodes quickly and recursively (IntrinsicRanks must have been set).
LIB_ALIAS * FindLibraryAlias(const LIB_ID &aLibId, const wxString &aLibraryName=wxEmptyString)
Function FindLibraryEntry searches all libraries in the list for an entry.
static WIDTH_CACHE m_width_cache
void AttachTo(wxDataViewCtrl *aDataViewCtrl)
Attach to a wxDataViewCtrl and initialize it.
Abstract pattern-matching tool and implementations.
static PTR Create(PART_LIBS *aLibs)
Factory function: create a model adapter in a reference-counting container.
void GetAliases(std::vector< LIB_ALIAS * > &aAliases)
Load a vector with all the entries in this library.
void UpdateSearchString(wxString const &aSearch)
Set the search string provided by the user.
void AssignIntrinsicRanks()
Store intrinsic ranks on all children of this node.
int GetComponentsCount() const
Return the number of components loaded in the tree.
void AddAliasList(wxString const &aNodeName, wxArrayString const &aAliasNameList, PART_LIB *aOptionalLib=nullptr)
Add the given list of components, by name.
CMP_FILTER_TYPE
This enum allows a selective filtering of components to list.
const wxString GetName() const
Return the file name without path or extension.
CMP_TREE_MODEL_ADAPTER(PART_LIBS *aLibs)
Constructor; takes a set of libraries to be included in the search.
int ColWidth(CMP_TREE_NODE &aTree, int aCol, wxString const &aHeading)
Compute the width required for the given column of a node and its children.
virtual unsigned int GetChildren(wxDataViewItem const &aItem, wxDataViewItemArray &aChildren) const override
Populate a list of all the children of an item.
Model class in the component selector Model-View-Adapter (mediated MVC) architecture.
CMP_TREE_NODE * Parent
Parent node or null.
static wxDataViewItem ToItem(CMP_TREE_NODE const *aNode)
Convert CMP_TREE_NODE -> wxDataViewItem.
Class PART_LIBS is a collection of PART_LIBs.
#define max(a, b)
Definition: auxiliary.h:86
void SetFilter(CMP_FILTER_TYPE aFilter)
Set the component filter type.
CMP_TREE_NODE_ALIAS & AddAlias(LIB_ALIAS *aAlias)
Construct a new alias node, add it to this library, and return it.
LIB_ALIAS * GetAliasFor(wxDataViewItem aSelection) const
Return the alias for the given item.
bool ShowSingleLibrary()
Find and expand a library if there is only one.
void ShowUnits(bool aShow)
Whether or not to show units.
virtual bool IsContainer(wxDataViewItem const &aItem) const override
Check whether an item can have children.
int Unit
Actual unit, or zero.
Definition for part library class.
int GetUnitFor(wxDataViewItem aSelection) const
Return the unit for the given item.
bool ShowResults()
Find and expand successful search results.
std::vector< std::unique_ptr< CMP_TREE_NODE > > Children
List of child nodes.
Class PART_LIB is used to load, save, search, and otherwise manipulate part library files...
bool ShowPreselect()
Find and expand preselected node.
static const int kDataViewIndent
LIB_ALIAS * FindAlias(const wxString &aName)
Find LIB_ALIAS by aName.
CMP_TREE_NODE_LIB & AddLib(wxString const &aName)
Construct an empty library node, add it to the root, and return it.
int Score
The score of an item resulting from the search algorithm.
static CMP_TREE_NODE const * ToNode(wxDataViewItem aItem)
Convert wxDataViewItem -> CMP_TREE_NODE.