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 <eda_pattern_match.h>
25 #include <wx/tokenzr.h>
26 #include <symbol_lib_table.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( LIB_ID const& aLibId, int aUnit )
109 {
110  m_preselect_lib_id = aLibId;
111  m_preselect_unit = aUnit;
112 }
113 
114 
115 void CMP_TREE_MODEL_ADAPTER::AddLibrary( wxString const& aLibNickname )
116 {
117  bool onlyPowerSymbols = ( m_filter == CMP_FILTER_POWER );
118 
119  wxArrayString aliases;
120 
121  try
122  {
123  m_libs->EnumerateSymbolLib( aLibNickname, aliases, onlyPowerSymbols );
124  }
125  catch( const IO_ERROR& ioe )
126  {
127  wxLogError( wxString::Format( _( "Error occurred loading symbol library %s."
128  "\n\n%s" ), aLibNickname, ioe.What() ) );
129  return;
130  }
131 
132  AddAliasList( aLibNickname, aliases );
133 
135 }
136 
137 
139  wxString const& aNodeName,
140  wxArrayString const& aAliasNameList )
141 {
142  std::vector<LIB_ALIAS*> alias_list;
143 
144  for( const wxString& name: aAliasNameList )
145  {
146  LIB_ALIAS* a = nullptr;
147 
148  try
149  {
150  a = m_libs->LoadSymbol( aNodeName, name );
151  }
152  catch( const IO_ERROR& ioe )
153  {
154  wxLogError( wxString::Format( _( "Error occurred loading symbol %s from library %s."
155  "\n\n%s" ), name, aNodeName, ioe.What() ) );
156  continue;
157  }
158 
159  if( a )
160  alias_list.push_back( a );
161  }
162 
163  AddAliasList( aNodeName, alias_list );
164 }
165 
166 
168  wxString const& aNodeName,
169  std::vector<LIB_ALIAS*> const& aAliasList )
170 {
171  auto& lib_node = m_tree.AddLib( aNodeName );
172 
173  for( auto a: aAliasList )
174  {
175  lib_node.AddAlias( a );
176  }
177 
178  lib_node.AssignIntrinsicRanks();
180 }
181 
182 
183 void CMP_TREE_MODEL_ADAPTER::UpdateSearchString( wxString const& aSearch )
184 {
185  m_tree.ResetScore();
186 
187  wxStringTokenizer tokenizer( aSearch );
188 
189  while( tokenizer.HasMoreTokens() )
190  {
191  const wxString term = tokenizer.GetNextToken().Lower();
192  EDA_COMBINED_MATCHER matcher( term );
193 
194  m_tree.UpdateScore( matcher );
195  }
196 
197  m_tree.SortNodes();
198  Cleared();
199  AttachTo( m_widget );
200 
202 }
203 
204 
205 void CMP_TREE_MODEL_ADAPTER::AttachTo( wxDataViewCtrl* aDataViewCtrl )
206 {
207  m_widget = aDataViewCtrl;
208  aDataViewCtrl->Freeze();
209  aDataViewCtrl->SetIndent( kDataViewIndent );
210  aDataViewCtrl->AssociateModel( this );
211  aDataViewCtrl->ClearColumns();
212 
213  wxString part_head = _( "Part" );
214  wxString desc_head = _( "Desc" );
215 
216  m_col_part = aDataViewCtrl->AppendTextColumn( part_head, 0, wxDATAVIEW_CELL_INERT,
217  ColWidth( m_tree, 0, part_head ) );
218  m_col_desc = aDataViewCtrl->AppendTextColumn( desc_head, 1, wxDATAVIEW_CELL_INERT,
219  ColWidth( m_tree, 1, desc_head ) );
220  aDataViewCtrl->Thaw();
221 }
222 
223 
224 LIB_ID CMP_TREE_MODEL_ADAPTER::GetAliasFor( wxDataViewItem aSelection ) const
225 {
226  auto node = ToNode( aSelection );
227 
228  LIB_ID emptyId;
229 
230  if( !node )
231  return emptyId;
232 
233  return node->LibId;
234 }
235 
236 
237 int CMP_TREE_MODEL_ADAPTER::GetUnitFor( wxDataViewItem aSelection ) const
238 {
239  auto node = ToNode( aSelection );
240  return node ? node->Unit : 0;
241 }
242 
243 
245 {
246  int n = 0;
247 
248  for( auto& lib: m_tree.Children )
249  {
250  for( auto& alias: lib->Children )
251  {
252  (void) alias;
253  ++n;
254  }
255  }
256 
257  return n;
258 }
259 
260 
261 bool CMP_TREE_MODEL_ADAPTER::HasContainerColumns( wxDataViewItem const& aItem ) const
262 {
263  return IsContainer( aItem );
264 }
265 
266 
267 bool CMP_TREE_MODEL_ADAPTER::IsContainer( wxDataViewItem const& aItem ) const
268 {
269  auto node = ToNode( aItem );
270  return node ? node->Children.size() : true;
271 }
272 
273 
274 wxDataViewItem CMP_TREE_MODEL_ADAPTER::GetParent( wxDataViewItem const& aItem ) const
275 {
276  auto node = ToNode( aItem );
277  auto parent = node ? node->Parent : nullptr;
278 
279  // wxDataViewModel has no root node, but rather top-level elements have
280  // an invalid (null) parent.
281  if( !node || !parent || parent->Type == CMP_TREE_NODE::TYPE::ROOT )
282  {
283  return ToItem( nullptr );
284  }
285  else
286  {
287  return ToItem( parent );
288  }
289 }
290 
291 
293  wxDataViewItem const& aItem,
294  wxDataViewItemArray& aChildren ) const
295 {
296  auto node = ( aItem.IsOk() ? ToNode( aItem ) : &m_tree );
297 
298  if( node->Type != CMP_TREE_NODE::TYPE::LIBID || m_show_units )
299  return IntoArray( *node, aChildren );
300  else
301  return 0;
302 }
303 
304 
306  wxVariant& aVariant,
307  wxDataViewItem const& aItem,
308  unsigned int aCol ) const
309 {
310  auto node = ToNode( aItem );
311  wxASSERT( node );
312 
313  switch( aCol )
314  {
315  case 0:
316  aVariant = node->Name;
317  break;
318  case 1:
319  aVariant = node->Desc;
320  break;
321  default:
322  wxFAIL_MSG( "Invalid column ID!" );
323  }
324 }
325 
326 
328  wxDataViewItem const& aItem,
329  unsigned int aCol,
330  wxDataViewItemAttr& aAttr ) const
331 {
332  auto node = ToNode( aItem );
333  wxASSERT( node );
334 
335  if( node->Type != CMP_TREE_NODE::LIBID )
336  {
337  // Currently only aliases are formatted at all
338  return false;
339  }
340 
341  if( !node->IsRoot && aCol == 0 )
342  {
343  // Names of non-root aliases are italicized
344  aAttr.SetItalic( true );
345  return true;
346  }
347  else
348  {
349  return false;
350  }
351 }
352 
353 
354 int CMP_TREE_MODEL_ADAPTER::ColWidth( CMP_TREE_NODE& aTree, int aCol, wxString const& aHeading )
355 {
356  const int indent = aCol ? 0 : kDataViewIndent;
357 
358  int min_width = WidthFor( aHeading, aCol );
359  int width = std::max( aTree.Score > 0 ? WidthFor( aTree, aCol ) : 0, min_width );
360 
361  if( aTree.Score > 0 )
362  {
363  for( auto& node: aTree.Children )
364  {
365  width = std::max( width, ColWidth( *node, aCol, aHeading ) + indent );
366  }
367  }
368 
369  return width;
370 }
371 
372 
374 {
375  auto result = m_width_cache.find( aNode.Name );
376 
377  if( result != m_width_cache.end() )
378  {
379  return result->second[aCol];
380  }
381  else
382  {
383  int wname = m_widget->GetTextExtent( aNode.Name ).x + kDataViewIndent;
384  int wdesc = m_widget->GetTextExtent( aNode.Desc ).x;
385 
386  auto& val = m_width_cache[aNode.Name];
387  val.push_back( wname );
388  val.push_back( wdesc );
389  return val[aCol];
390  }
391 }
392 
393 
394 int CMP_TREE_MODEL_ADAPTER::WidthFor( wxString const& aHeading, int aCol )
395 {
396  static std::vector<int> widths;
397 
398  for( int i = (int) widths.size(); i <= aCol; ++i )
399  {
400  widths.push_back( 0 );
401  }
402 
403  if( widths[aCol] == 0 )
404  {
405  widths[aCol] = m_widget->GetTextExtent( aHeading ).x;
406  }
407 
408  return widths[aCol];
409 }
410 
411 
413  CMP_TREE_NODE& aNode,
414  std::function<bool( CMP_TREE_NODE const* )> aFunc )
415 {
416  for( auto& node: aNode.Children )
417  {
418  if( aFunc( &*node ) )
419  {
420  auto item = wxDataViewItem(
421  const_cast<void*>( static_cast<void const*>( &*node ) ) );
422  m_widget->ExpandAncestors( item );
423  m_widget->EnsureVisible( item );
424  m_widget->Select( item );
425  return true;
426  }
427  else if( FindAndExpand( *node, aFunc ) )
428  {
429  return true;
430  }
431  }
432 
433  return false;
434 }
435 
436 
438 {
439  return FindAndExpand( m_tree,
440  []( CMP_TREE_NODE const* n )
441  {
442  return n->Type == CMP_TREE_NODE::TYPE::LIBID && n->Score > 1;
443  } );
444 }
445 
446 
448 {
449  if( !m_preselect_lib_id.IsValid() )
450  return false;
451 
452  return FindAndExpand( m_tree,
453  [&]( CMP_TREE_NODE const* n )
454  {
455  if( n->Type == CMP_TREE_NODE::LIBID && ( n->Children.empty() || !m_preselect_unit ) )
456  return m_preselect_lib_id == n->LibId;
457  else if( n->Type == CMP_TREE_NODE::UNIT && m_preselect_unit )
458  return m_preselect_lib_id == n->Parent->LibId && m_preselect_unit == n->Unit;
459  else
460  return false;
461  } );
462 }
463 
464 
466 {
467  return FindAndExpand( m_tree,
468  []( CMP_TREE_NODE const* n )
469  {
470  return n->Type == CMP_TREE_NODE::TYPE::LIBID &&
471  n->Parent->Parent->Children.size() == 1;
472  } );
473 }
LIB_ALIAS * LoadSymbol(const wxString &aNickname, const wxString &aAliasName)
Load a LIB_ALIAS having aAliasName from the library given by aNickname.
bool IsValid() const
Function IsValid.
Definition: lib_id.h:177
wxString Name
Actual name of the part.
wxString Desc
Description to be displayed.
static PTR Create(SYMBOL_LIB_TABLE *aLibs)
Factory function: create a model adapter in a reference-counting container.
LIB_ID GetAliasFor(wxDataViewItem aSelection) const
Return the alias for the given item.
void EnumerateSymbolLib(const wxString &aNickname, wxArrayString &aAliasNames, bool aPowerSymbolsOnly=false)
Return a list of symbol alias names contained within the library given by aNickname.
Part library alias object definition.
virtual void GetValue(wxVariant &aVariant, wxDataViewItem const &aItem, unsigned int aCol) const override
Get the value of an item.
void AddLibrary(wxString const &aLibNickname)
Add all the components and their aliases 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.
CMP_TREE_NODE_LIB_ID & AddAlias(LIB_ALIAS *aAlias)
Construct a new alias node, add it to this library, and return it.
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).
static WIDTH_CACHE m_width_cache
void AttachTo(wxDataViewCtrl *aDataViewCtrl)
Attach to a wxDataViewCtrl and initialize it.
LIB_ID LibId
LIB_ID determined by the parent library nickname and alias name.
Abstract pattern-matching tool and implementations.
virtual bool GetAttr(wxDataViewItem const &aItem, unsigned int aCol, wxDataViewItemAttr &aAttr) const override
Get any formatting for an item.
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.
CMP_FILTER_TYPE
This enum allows a selective filtering of components to list.
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.
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
#define max(a, b)
Definition: auxiliary.h:86
void SetFilter(CMP_FILTER_TYPE aFilter)
Set the component filter type.
CMP_TREE_MODEL_ADAPTER(SYMBOL_LIB_TABLE *aLibs)
Constructor; takes a set of libraries to be included in the search.
const char * name
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.
void SetPreselectNode(LIB_ID const &aLibId, int aUnit)
Set the component name to be selected if there are no search results.
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.
bool ShowPreselect()
Find and expand preselected node.
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:47
static const int kDataViewIndent
void AddAliasList(wxString const &aNodeName, wxArrayString const &aAliasNameList)
Add the given list of components, by name.
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.