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-2021 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 #ifndef LIB_TREE_MODEL_H
23 #define LIB_TREE_MODEL_H
24 
25 #include <vector>
26 #include <memory>
27 #include <wx/string.h>
28 #include <lib_tree_item.h>
29 
30 
31 class EDA_COMBINED_MATCHER;
32 
33 
34 /**
35  * Model class in the component selector Model-View-Adapter (mediated MVC)
36  * architecture. The other pieces are in:
37  *
38  * - Adapter: LIB_TREE_MODEL_ADAPTER in common/lib_tree_model_adapter.h
39  * - View:
40  *   - DIALOG_CHOOSE_COMPONENT in eeschema/dialogs/dialog_choose_component.h
41  *   - wxDataViewCtrl
42  *
43  * This model is populated from LIB_ALIASes; helper methods in the adapter
44  * can accept entire libraries.
45  *
46  * Quick summary of methods used to populate this class:
47  * - `CMP_TREE_NODE_ROOT::AddLib()` - add a new, empty library to the root
48  * - `CMP_TREE_NODE_LIB::AddAlias()` - add an alias and its units from a
49  *      given LIB_ALIAS*
50  *
51  * Quick summary of methods used to drive this class:
52  *
53  * - `UpdateScore()` - accumulate scores recursively given a new search token
54  * - `ResetScore()` - reset scores recursively for a new search string
55  * - `AssignIntrinsicRanks()` - calculate and cache the initial sort order
56  * - `SortNodes()` - recursively sort the tree by score
57  * - `Compare()` - compare two nodes; used by `SortNodes()`
58  *
59  * The data in the model is meant to be accessed directly. Quick summary of
60  * data members:
61  *
62  * - `Parent` - parent node, or nullptr if root
63  * - `Children` - vector of unique_ptrs to children
64  * - `Type` - ROOT, LIB, ALIAS, or UNIT
65  * - `m_IntrinsicRank` - cached initial sort order
66  * - `m_Score` - score taking into account search terms. Zero means irrelevant and
67  *      should be hidden.
68  * - `Name` - name of the library/alias/unit, to be displayed
69  * - `Desc` - description of the alias, to be displayed
70  * - `m_MatchName` - Name, normalized to lowercase for matching
71  * - `m_SearchText` - normalized composite of keywords and description
72  * - `LibId` - the #LIB_ID this alias or unit is from, or not valid
73  * - `Unit` - the unit number, or zero for non-units
74  */
75 class LIB_TREE_NODE
76 {
77 public:
78     /**
79      * Update the score for this part. This is accumulative - it will be
80      * called once per search term.
81      *
82      * @param aMatcher  an EDA_COMBINED_MATCHER initialized with the search term
83      */
84     virtual void UpdateScore( EDA_COMBINED_MATCHER& aMatcher, const wxString& aLib ) = 0;
85 
86     /**
87      * Initialize score to kLowestDefaultScore, recursively.
88      */
89     void ResetScore();
90 
91     /**
92      * Store intrinsic ranks on all children of this node. See m_IntrinsicRank
93      * member doc for more information.
94      */
95     void AssignIntrinsicRanks( bool presorted = false );
96 
97     /**
98      * Sort child nodes quickly and recursively (IntrinsicRanks must have been set).
99      */
100     void SortNodes();
101 
102     /**
103      * Compare two nodes. Returns negative if aNode1 < aNode2, zero if aNode1 ==
104      * aNode2, or positive if aNode1 > aNode2.
105      */
106     static int Compare( LIB_TREE_NODE const& aNode1, LIB_TREE_NODE const& aNode2 );
107 
108     LIB_TREE_NODE();
~LIB_TREE_NODE()109     virtual ~LIB_TREE_NODE() {}
110 
111     enum TYPE {
112         ROOT, LIB, LIBID, UNIT, INVALID
113     };
114 
115     typedef std::vector<std::unique_ptr<LIB_TREE_NODE>> PTR_VECTOR;
116 
117     LIB_TREE_NODE*  m_Parent;     // Parent node or null
118     PTR_VECTOR      m_Children;   // List of child nodes
119     enum TYPE       m_Type;       // Node type
120 
121     /**
122      * The rank of the item before any search terms are applied. This is
123      * a fairly expensive sort (involving string compares) so it helps to
124      * store the result of that sort.
125      */
126     int         m_IntrinsicRank;
127 
128     int         m_Score;       // The score of an item resulting from the search algorithm.
129     bool        m_Pinned;      // Item should appear at top when there is no search string
130 
131     wxString    m_Name;        // Actual name of the part
132     wxString    m_Desc;        // Description to be displayed
133     wxString    m_MatchName;   // Normalized name for matching
134     wxString    m_SearchText;  // Descriptive text to search
135     bool        m_Normalized;  // Support for lazy normalization.
136 
137 
138     LIB_ID      m_LibId;       // LIB_ID determined by the parent library nickname and alias name.
139     int         m_Unit;        // Actual unit, or zero
140     bool        m_IsRoot;      // Indicates if the symbol is a root symbol instead of an alias.
141 };
142 
143 
144 /**
145  * Node type: unit of component.
146  */
147 class LIB_TREE_NODE_UNIT: public LIB_TREE_NODE
148 {
149 public:
150     /**
151      * The addresses of CMP_TREE_NODEs are used as unique IDs for the
152      * wxDataViewModel, so don't let them be copied around.
153      */
154     LIB_TREE_NODE_UNIT( LIB_TREE_NODE_UNIT const& _ ) = delete;
155     void operator=( LIB_TREE_NODE_UNIT const& _ ) = delete;
156 
157     /**
158      * Construct a unit node.
159      *
160      * The name of the unit will be "Unit %s", where %s is aUnit formatted
161      * by LIB_PART::SubReference.
162      *
163      * @param aParent   parent node, should be a CMP_TREE_NODE_ALIAS
164      * @param aItem     parent item
165      * @param aUnit     unit number
166      */
167     LIB_TREE_NODE_UNIT( LIB_TREE_NODE* aParent, LIB_TREE_ITEM* aItem, int aUnit );
168 
169 
170     /**
171      * Do nothing, units just take the parent's score
172      */
UpdateScore(EDA_COMBINED_MATCHER & aMatcher,const wxString & aLib)173     virtual void UpdateScore( EDA_COMBINED_MATCHER& aMatcher, const wxString& aLib ) override {}
174 };
175 
176 
177 /**
178  * Node type: #LIB_ID.
179  */
180 class LIB_TREE_NODE_LIB_ID: public LIB_TREE_NODE
181 {
182 public:
183     /**
184      * The addresses of CMP_TREE_NODEs are used as unique IDs for the
185      * wxDataViewModel, so don't let them be copied around.
186      */
187     LIB_TREE_NODE_LIB_ID( LIB_TREE_NODE_LIB_ID const& _ ) = delete;
188     void operator=( LIB_TREE_NODE_LIB_ID const& _ ) = delete;
189 
190     /**
191      * Construct a #LIB_ID node.
192      *
193      * All fields will be populated from the LIB_ALIAS, including children
194      * (unit nodes will be generated automatically).  This does not keep
195      * the pointer to the #LIB_ALIAS object because at any time, a #LIB_ALIAS
196      * can be remove from a library which will result in an invalid pointer.
197      * The alias must be resolved at the time of use.  Anything else is a bug.
198      *
199      * @param aParent   parent node, should be a CMP_TREE_NODE_LIB
200      * @param aItem     LIB_COMPONENT to populate the node.
201      */
202     LIB_TREE_NODE_LIB_ID( LIB_TREE_NODE* aParent, LIB_TREE_ITEM* aItem );
203 
204     /**
205      * Update the node using data from a LIB_ALIAS object.
206      */
207     void Update( LIB_TREE_ITEM* aItem );
208 
209     /**
210      * Perform the actual search.
211      */
212     virtual void UpdateScore( EDA_COMBINED_MATCHER& aMatcher, const wxString& aLib ) override;
213 
214 protected:
215     /**
216      * Add a new unit to the component and return it.
217      *
218      * This should not be used directly, as the constructor adds all units.
219      */
220     LIB_TREE_NODE_UNIT& AddUnit( LIB_TREE_ITEM* aItem, int aUnit );
221 };
222 
223 
224 /**
225  * Node type: library
226  */
227 class LIB_TREE_NODE_LIB: public LIB_TREE_NODE
228 {
229 public:
230     /**
231      * The addresses of CMP_TREE_NODEs are used as unique IDs for the
232      * wxDataViewModel, so don't let them be copied around.
233      */
234     LIB_TREE_NODE_LIB( LIB_TREE_NODE_LIB const& _ ) = delete;
235     void operator=( LIB_TREE_NODE_LIB const& _ ) = delete;
236 
237     /**
238      * Construct an empty library node.
239      *
240      * @param aParent   parent node, should be a CMP_TREE_NODE_ROOT
241      * @param aName     display name of the library
242      * @param aDesc     a description of the library
243      */
244     LIB_TREE_NODE_LIB( LIB_TREE_NODE* aParent, const wxString& aName, const wxString& aDesc );
245 
246     /**
247      * Construct a new alias node, add it to this library, and return it.
248      *
249      * @param aItem    LIB_COMPONENT to provide data
250      */
251     LIB_TREE_NODE_LIB_ID& AddItem( LIB_TREE_ITEM* aItem );
252 
253     virtual void UpdateScore( EDA_COMBINED_MATCHER& aMatcher, const wxString& aLib ) override;
254 };
255 
256 
257 /**
258  * Node type: root
259  */
260 class LIB_TREE_NODE_ROOT: public LIB_TREE_NODE
261 {
262 public:
263     /**
264      * The addresses of CMP_TREE_NODEs are used as unique IDs for the
265      * wxDataViewModel, so don't let them be copied around.
266      */
267     LIB_TREE_NODE_ROOT( LIB_TREE_NODE_ROOT const& _ ) = delete;
268     void operator=( LIB_TREE_NODE_ROOT const& _ ) = delete;
269 
270     /**
271      * Construct the root node. Root nodes have no properties.
272      */
273     LIB_TREE_NODE_ROOT();
274 
275     /**
276      * Construct an empty library node, add it to the root, and return it.
277      */
278     LIB_TREE_NODE_LIB& AddLib( wxString const& aName, wxString const& aDesc );
279 
280     virtual void UpdateScore( EDA_COMBINED_MATCHER& aMatcher, const wxString& aLib ) override;
281 };
282 
283 
284 #endif // LIB_TREE_MODEL_H
285