KiCad PCB EDA Suite
lib_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 <lib_tree_model_adapter.h>
23 
24 #include <eda_pattern_match.h>
25 
26 #include <wx/progdlg.h>
27 #include <wx/tokenzr.h>
28 #include <wx/wupdlock.h>
29 
30 
31 LIB_TREE_MODEL_ADAPTER::WIDTH_CACHE LIB_TREE_MODEL_ADAPTER::m_width_cache;
32 
33 static const int kDataViewIndent = 20;
34 
35 
39 wxDataViewItem LIB_TREE_MODEL_ADAPTER::ToItem( LIB_TREE_NODE const* aNode )
40 {
41  return wxDataViewItem( const_cast<void*>( static_cast<void const*>( aNode ) ) );
42 }
43 
44 
48 LIB_TREE_NODE const* LIB_TREE_MODEL_ADAPTER::ToNode( wxDataViewItem aItem )
49 {
50  return static_cast<LIB_TREE_NODE const*>( aItem.GetID() );
51 }
52 
53 
58  wxDataViewItemArray& aChildren )
59 {
60  unsigned int n = 0;
61 
62  for( auto const& child: aNode.Children )
63  {
64  if( child->Score > 0 )
65  {
66  aChildren.Add( ToItem( &*child ) );
67  ++n;
68  }
69  }
70 
71  return n;
72 }
73 
74 
77  m_show_units( true ),
78  m_preselect_unit( 0 ),
79  m_freeze( 0 ),
80  m_col_part( nullptr ),
81  m_col_desc( nullptr ),
82  m_widget( nullptr )
83 {}
84 
85 
87 {}
88 
89 
91 {
92  m_filter = aFilter;
93 }
94 
95 
97 {
98  m_show_units = aShow;
99 }
100 
101 
102 void LIB_TREE_MODEL_ADAPTER::SetPreselectNode( LIB_ID const& aLibId, int aUnit )
103 {
104  m_preselect_lib_id = aLibId;
105  m_preselect_unit = aUnit;
106 }
107 
108 
109 void LIB_TREE_MODEL_ADAPTER::DoAddLibrary( wxString const& aNodeName, wxString const& aDesc,
110  std::vector<LIB_TREE_ITEM*> const& aItemList,
111  bool presorted )
112 {
113  auto& lib_node = m_tree.AddLib( aNodeName, aDesc );
114 
115  for( auto item: aItemList )
116  lib_node.AddItem( item );
117 
118  lib_node.AssignIntrinsicRanks( presorted );
119 }
120 
121 
122 void LIB_TREE_MODEL_ADAPTER::UpdateSearchString( wxString const& aSearch )
123 {
124  m_tree.ResetScore();
125 
126  wxStringTokenizer tokenizer( aSearch );
127 
128  while( tokenizer.HasMoreTokens() )
129  {
130  const wxString term = tokenizer.GetNextToken().Lower();
131  EDA_COMBINED_MATCHER matcher( term );
132 
133  m_tree.UpdateScore( matcher );
134  }
135 
136  m_tree.SortNodes();
137 
138  {
139  wxWindowUpdateLocker updateLock( m_widget );
140 
141  // Even with the updateLock, wxWidgets sometimes ties its knickers in
142  // a knot when trying to run a wxdataview_selection_changed_callback()
143  // on a row that has been deleted.
144  // https://bugs.launchpad.net/kicad/+bug/1756255
145  m_widget->UnselectAll();
146 
147  Cleared();
148 #ifndef __WINDOWS__
149  // The fastest method to update wxDataViewCtrl is to rebuild from
150  // scratch by calling Cleared(). Linux requires to reassociate model to
151  // display data, but Windows will create multiple associations.
152  AttachTo( m_widget );
153 #endif
154  }
155 
156  LIB_TREE_NODE* bestMatch = ShowResults();
157 
158  if( !bestMatch )
159  bestMatch = ShowPreselect();
160 
161  if( !bestMatch )
162  bestMatch = ShowSingleLibrary();
163 
164  if( bestMatch )
165  {
166  auto item = wxDataViewItem( bestMatch );
167  m_widget->Select( item );
168  m_widget->EnsureVisible( item );
169  }
170 }
171 
172 
173 void LIB_TREE_MODEL_ADAPTER::AttachTo( wxDataViewCtrl* aDataViewCtrl )
174 {
175  m_widget = aDataViewCtrl;
176  aDataViewCtrl->SetIndent( kDataViewIndent );
177  aDataViewCtrl->AssociateModel( this );
178  aDataViewCtrl->ClearColumns();
179 
180  wxString part_head = _( "Item" );
181  wxString desc_head = _( "Description" );
182 
183  m_col_part = aDataViewCtrl->AppendTextColumn( part_head, 0, wxDATAVIEW_CELL_INERT,
184  ColWidth( m_tree, 0, part_head ) );
185  m_col_desc = aDataViewCtrl->AppendTextColumn( desc_head, 1, wxDATAVIEW_CELL_INERT,
186  ColWidth( m_tree, 1, desc_head ) );
187 }
188 
189 
190 LIB_ID LIB_TREE_MODEL_ADAPTER::GetAliasFor( const wxDataViewItem& aSelection ) const
191 {
192  auto node = ToNode( aSelection );
193 
194  LIB_ID emptyId;
195 
196  if( !node )
197  return emptyId;
198 
199  return node->LibId;
200 }
201 
202 
203 int LIB_TREE_MODEL_ADAPTER::GetUnitFor( const wxDataViewItem& aSelection ) const
204 {
205  auto node = ToNode( aSelection );
206  return node ? node->Unit : 0;
207 }
208 
209 
210 LIB_TREE_NODE::TYPE LIB_TREE_MODEL_ADAPTER::GetTypeFor( const wxDataViewItem& aSelection ) const
211 {
212  auto node = ToNode( aSelection );
213  return node ? node->Type : LIB_TREE_NODE::INVALID;
214 }
215 
216 
218 {
219  int n = 0;
220 
221  for( auto& lib: m_tree.Children )
222  n += lib->Children.size();
223 
224  return n;
225 }
226 
227 
228 wxDataViewItem LIB_TREE_MODEL_ADAPTER::FindItem( const LIB_ID& aLibId )
229 {
230  for( auto& lib: m_tree.Children )
231  {
232  if( lib->Name != aLibId.GetLibNickname() )
233  continue;
234 
235  // if part name is not specified, return the library node
236  if( aLibId.GetLibItemName() == "" )
237  return ToItem( lib.get() );
238 
239  for( auto& alias: lib->Children )
240  {
241  if( alias->Name == aLibId.GetLibItemName() )
242  return ToItem( alias.get() );
243  }
244 
245  break; // could not find the part in the requested library
246  }
247 
248  return wxDataViewItem();
249 }
250 
251 
252 unsigned int LIB_TREE_MODEL_ADAPTER::GetChildren( wxDataViewItem const& aItem,
253  wxDataViewItemArray& aChildren ) const
254 {
255  auto node = ( aItem.IsOk() ? ToNode( aItem ) : &m_tree );
256 
257  if( node->Type != LIB_TREE_NODE::TYPE::LIBID
258  || ( m_show_units && node->Type == LIB_TREE_NODE::TYPE::LIBID ) )
259  return IntoArray( *node, aChildren );
260  else
261  return 0;
262 }
263 
264 
265 bool LIB_TREE_MODEL_ADAPTER::HasContainerColumns( wxDataViewItem const& aItem ) const
266 {
267  return IsContainer( aItem );
268 }
269 
270 
271 bool LIB_TREE_MODEL_ADAPTER::IsContainer( wxDataViewItem const& aItem ) const
272 {
273  auto node = ToNode( aItem );
274  return node ? node->Children.size() : true;
275 }
276 
277 
278 wxDataViewItem LIB_TREE_MODEL_ADAPTER::GetParent( wxDataViewItem const& aItem ) const
279 {
280  auto node = ToNode( aItem );
281  auto parent = node ? node->Parent : nullptr;
282 
283  // wxDataViewModel has no root node, but rather top-level elements have
284  // an invalid (null) parent.
285  if( !node || !parent || parent->Type == LIB_TREE_NODE::TYPE::ROOT )
286  return ToItem( nullptr );
287  else
288  return ToItem( parent );
289 }
290 
291 
292 void LIB_TREE_MODEL_ADAPTER::GetValue( wxVariant& aVariant,
293  wxDataViewItem const& aItem,
294  unsigned int aCol ) const
295 {
296  if( IsFrozen() )
297  {
298  aVariant = wxEmptyString;
299  return;
300  }
301 
302  auto node = ToNode( aItem );
303  wxASSERT( node );
304 
305  switch( aCol )
306  {
307  default: // column == -1 is used for default Compare function
308  case 0:
309  aVariant = node->Name;
310  break;
311  case 1:
312  aVariant = node->Desc;
313  break;
314  }
315 }
316 
317 
318 bool LIB_TREE_MODEL_ADAPTER::GetAttr( wxDataViewItem const& aItem,
319  unsigned int aCol,
320  wxDataViewItemAttr& aAttr ) const
321 {
322  if( IsFrozen() )
323  return false;
324 
325  auto node = ToNode( aItem );
326  wxASSERT( node );
327 
328  if( node->Type != LIB_TREE_NODE::LIBID )
329  {
330  // Currently only aliases are formatted at all
331  return false;
332  }
333 
334  if( !node->IsRoot && aCol == 0 )
335  {
336  // Names of non-root aliases are italicized
337  aAttr.SetItalic( true );
338  return true;
339  }
340  else
341  {
342  return false;
343  }
344 }
345 
346 
347 int LIB_TREE_MODEL_ADAPTER::ColWidth( LIB_TREE_NODE& aTree, int aCol, wxString const& aHeading )
348 {
349  // It's too expensive to calculate widths on really big trees, and the user probably
350  // wants it left where they dragged it anyway.
351  if( aCol == 0 )
352  return 360;
353  else
354  return 2000;
355 }
356 
357 
359  std::function<bool( LIB_TREE_NODE const* )> aFunc,
360  LIB_TREE_NODE** aHighScore )
361 {
362  for( auto& node: aNode.Children )
363  {
364  if( aFunc( &*node ) )
365  {
366  auto item = wxDataViewItem( &*node );
367  m_widget->ExpandAncestors( item );
368 
369  if( !(*aHighScore) || node->Score > (*aHighScore)->Score )
370  (*aHighScore) = &*node;
371  }
372 
373  FindAndExpand( *node, aFunc, aHighScore );
374  }
375 }
376 
377 
379 {
380  LIB_TREE_NODE* highScore = nullptr;
381 
383  []( LIB_TREE_NODE const* n )
384  {
385  // return leaf nodes with some level of matching
386  return n->Children.size() == 0 && n->Score > 1;
387  },
388  &highScore );
389 
390  return highScore;
391 }
392 
393 
395 {
396  LIB_TREE_NODE* highScore = nullptr;
397 
398  if( !m_preselect_lib_id.IsValid() )
399  return highScore;
400 
402  [&]( LIB_TREE_NODE const* n )
403  {
404  if( n->Type == LIB_TREE_NODE::LIBID && ( n->Children.empty() || !m_preselect_unit ) )
405  return m_preselect_lib_id == n->LibId;
406  else if( n->Type == LIB_TREE_NODE::UNIT && m_preselect_unit )
407  return m_preselect_lib_id == n->Parent->LibId && m_preselect_unit == n->Unit;
408  else
409  return false;
410  },
411  &highScore );
412 
413  return highScore;
414 }
415 
416 
418 {
419  LIB_TREE_NODE* highScore = nullptr;
420 
422  []( LIB_TREE_NODE const* n )
423  {
424  return n->Type == LIB_TREE_NODE::TYPE::LIBID &&
425  n->Parent->Parent->Children.size() == 1;
426  },
427  &highScore );
428 
429  return highScore;
430 }
void DoAddLibrary(wxString const &aNodeName, wxString const &aDesc, std::vector< LIB_TREE_ITEM * > const &aItemList, bool presorted)
Add the given list of components by alias.
bool IsValid() const
Definition: lib_id.h:171
void SortNodes()
Sort child nodes quickly and recursively (IntrinsicRanks must have been set).
virtual bool IsContainer(wxDataViewItem const &aItem) const override
Check whether an item can have children.
CMP_FILTER_TYPE
This enum allows a selective filtering of components to list.
PTR_VECTOR Children
List of child nodes.
enum TYPE Type
Node type.
int GetUnitFor(const wxDataViewItem &aSelection) const
Return the unit for the given item.
void SetPreselectNode(LIB_ID const &aLibId, int aUnit)
Set the component name to be selected if there are no search results.
void UpdateSearchString(wxString const &aSearch)
Set the search string provided by the user.
LIB_TREE_NODE * ShowResults()
Find and expand successful search results.
LIB_TREE_NODE * ShowPreselect()
Find and expand preselected node.
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
LIB_TREE_NODE * Parent
Parent node or null.
int Score
The score of an item resulting from the search algorithm.
int ColWidth(LIB_TREE_NODE &aTree, int aCol, wxString const &aHeading)
Compute the width required for the given column of a node and its children.
static wxDataViewItem ToItem(LIB_TREE_NODE const *aNode)
Convert CMP_TREE_NODE -> wxDataViewItem.
Abstract pattern-matching tool and implementations.
static unsigned int IntoArray(LIB_TREE_NODE const &aNode, wxDataViewItemArray &aChildren)
Convert CMP_TREE_NODE&#39;s children to wxDataViewItemArray.
virtual bool GetAttr(wxDataViewItem const &aItem, unsigned int aCol, wxDataViewItemAttr &aAttr) const override
Get any formatting for an item.
static const int kDataViewIndent
virtual bool HasContainerColumns(wxDataViewItem const &aItem) const override
Check whether a container has columns too.
LIB_TREE_NODE_LIB_ID & AddItem(LIB_TREE_ITEM *aItem)
Construct a new alias node, add it to this library, and return it.
void FindAndExpand(LIB_TREE_NODE &aNode, std::function< bool(LIB_TREE_NODE const *)> aFunc, LIB_TREE_NODE **aHighScore)
Find any results worth highlighting and expand them, according to given criteria (f(CMP_TREE_NODE con...
const UTF8 & GetLibItemName() const
Definition: lib_id.h:114
void SetFilter(CMP_FILTER_TYPE aFilter)
Set the component filter type.
static LIB_TREE_NODE const * ToNode(wxDataViewItem aItem)
Convert wxDataViewItem -> CMP_TREE_NODE.
virtual wxDataViewItem GetParent(wxDataViewItem const &aItem) const override
Get the parent of an item.
LIB_ID LibId
LIB_ID determined by the parent library nickname and alias name.
void ResetScore()
Initialize score to kLowestDefaultScore, recursively.
Model class in the component selector Model-View-Adapter (mediated MVC) architecture.
LIB_ID GetAliasFor(const wxDataViewItem &aSelection) const
Return the alias for the given item.
LIB_TREE_NODE_LIB & AddLib(wxString const &aName, wxString const &aDesc)
Construct an empty library node, add it to the root, and return it.
void AssignIntrinsicRanks(bool presorted=false)
Store intrinsic ranks on all children of this node.
virtual void GetValue(wxVariant &aVariant, wxDataViewItem const &aItem, unsigned int aCol) const override
Get the value of an item.
LIB_TREE_NODE::TYPE GetTypeFor(const wxDataViewItem &aSelection) const
Return node type for the given item.
int Unit
Actual unit, or zero.
static WIDTH_CACHE m_width_cache
wxDataViewItem FindItem(const LIB_ID &aLibId)
Returns tree item corresponding to part.
virtual void UpdateScore(EDA_COMBINED_MATCHER &aMatcher) override
Update the score for this part.
LIB_TREE_NODE * ShowSingleLibrary()
Find and expand a library if there is only one.
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition: lib_id.h:97
int GetItemCount() const
Return the number of components loaded in the tree.
unsigned int GetChildren(wxDataViewItem const &aItem, wxDataViewItemArray &aChildren) const override
Populate a list of all the children of an item.
void AttachTo(wxDataViewCtrl *aDataViewCtrl)
Attach to a wxDataViewCtrl and initialize it.
void ShowUnits(bool aShow)
Whether or not to show units.