1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
6  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
24  */
25 
26 /**
27  * @file sch_sheet_path.h
28  * Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
29  */
30 
31 #ifndef CLASS_DRAWSHEET_PATH_H
32 #define CLASS_DRAWSHEET_PATH_H
33 
34 #include <map>
35 
36 #include <kiid.h>
37 
38 /**
39  * A simple container for schematic symbol instance information.
40  */
41 struct SYMBOL_INSTANCE_REFERENCE
42 {
43     KIID_PATH m_Path;
44 
45     // Things that can be annotated:
46     wxString  m_Reference;
47     int       m_Unit;
48 
49     // Things that can be back-annotated:
50     wxString  m_Value;
51     wxString  m_Footprint;
52 };
53 
54 
55 /**
56  * A simple container for sheet instance information.
57  */
58 struct SCH_SHEET_INSTANCE
59 {
60     KIID_PATH m_Path;
61 
62     wxString  m_PageNumber;
63 };
64 
65 
66 /**
67  * Complex hierarchies
68  *
69  * A hierarchical schematic uses sheets (hierarchical sheets) included in a given sheet.
70  * Each sheet corresponds to a schematic drawing handled by a SCH_SCREEN structure.  A
71  * SCH_SCREEN structure contains drawings, and have a filename to write its data.  Also a
72  * SCH_SCREEN displays a sheet number and the name of the sheet.
73  *
74  * In simple (and flat) hierarchies a sheet is linked to a SCH_SCREEN, and a SCH_SCREEN is
75  * used by the single hierarchical sheet.
76  *
77  * In complex hierarchies the same SCH_SCREEN (and its data) is shared by more than one sheet.
78  * Therefore subsheets (like subsheets in a SCH_SCREEN shared by many sheets) can also be
79  * shared. So the same SCH_SCREEN must handle different symbol references and unit selections
80  * depending on which sheet is currently selected, and how a given subsheet is selected. Two
81  * sheets share the same SCH_SCREEN (the same drawings) if they have the same filename.
82  *
83  * In KiCad each symbol and sheet receives (when created) a uuid.  So each sheet has 2 id: its
84  * uuid (which cannot change) and its name (that can be edited and therefore is not reliable
85  * for strong identification).
86  * A given sheet in a hierarchy is fully labeled by its path (or sheet path) that is the list
87  * of uuids found to access it through the hierarchy.  The root sheet is /.  All  other sheets
88  * have a path like /1234ABCD or /4567FEDC/AA2233DD/.  This path can be displayed as human
89  * readable sheet name like: / or /sheet1/include_sheet/ or /sheet2/include_sheet/
90  *
91  * So to know for a given SCH_SCREEN (a given schematic drawings) we must:
92  *   1) Handle all references possibilities.
93  *   2) When acceded by a given selected sheet, display (update) the
94  *      corresponding references and sheet path
95  *
96  * The class SCH_SHEET_PATH handles paths used to access a sheet.  The class SCH_SHEET_LIST
97  * allows one to handle the full (or partial) list of sheets and their paths in a complex
98  * hierarchy.  The class EDA_ScreenList allows one to handle the list of SCH_SCREEN. It is
99  * useful  to clear or save data, but is not suitable to handle the full complex hierarchy
100  * possibilities (usable in flat and simple hierarchies).
101  */
102 
103 
104 class wxFindReplaceData;
105 class EDA_ITEM;
106 class SCH_SHEET;
107 class SCH_SCREEN;
108 class SCH_MARKER;
109 class SCH_ITEM;
110 class SCH_SYMBOL;
111 class SCH_REFERENCE_LIST;
112 
113 
114 /**
115  * Container to map reference designators for multi-unit parts.
116  */
117 typedef std::map<wxString, SCH_REFERENCE_LIST> SCH_MULTI_UNIT_REFERENCE_MAP;
118 
119 /**
120  * Handle access to a stack of flattened #SCH_SHEET objects by way of a path for
121  * creating a flattened schematic hierarchy.
122  *
123  * The #SCH_SHEET objects are stored in a list from first (usually the root sheet) to a
124  * given sheet in last position.  The _last_ sheet is usually the sheet we want to select
125  * or reach (which is what the function Last() returns).   Others sheets constitute the
126  * "path" from the first to the last sheet.
127  */
128 class SCH_SHEET_PATH
129 {
130 public:
131     SCH_SHEET_PATH();
132 
133     SCH_SHEET_PATH( const SCH_SHEET_PATH& aOther );
134 
135     SCH_SHEET_PATH& operator=( const SCH_SHEET_PATH& aOther );
136 
137     ~SCH_SHEET_PATH() = default;
138 
139     /// Forwarded method from std::vector
at(size_t aIndex)140     SCH_SHEET* at( size_t aIndex ) const { return m_sheets.at( aIndex ); }
141 
142     /// Forwarded method from std::vector
clear()143     void clear()
144     {
145         m_sheets.clear();
146         Rehash();
147     }
148 
149     /// Forwarded method from std::vector
empty()150     bool empty() const { return m_sheets.empty(); }
151 
152     /// Forwarded method from std::vector
pop_back()153     void pop_back()
154     {
155         m_sheets.pop_back();
156         Rehash();
157     }
158 
159     /// Forwarded method from std::vector
push_back(SCH_SHEET * aSheet)160     void push_back( SCH_SHEET* aSheet )
161     {
162         m_sheets.push_back( aSheet );
163         Rehash();
164     }
165 
166     /// Forwarded method from std::vector
size()167     size_t size() const { return m_sheets.size(); }
168 
169     void Rehash();
170 
GetCurrentHash()171     size_t GetCurrentHash() const { return m_current_hash; }
172 
173     /**
174      * Set the sheet instance virtual page number.
175      *
176      * Virtual page numbers are incremental integers set automatically when the sheet path
177      * hierarchy is created (@see #SCH_SHEET_LIST::BuildSheetList).  The virtual page
178      * numbering is ordered by the X and Y position of the sheet in a schematic which
179      * mimics the page numbering code prior to the addition of actual user definable page
180      * numbers.  Virtual page numbers should only be use when annotating schematics.
181      */
SetVirtualPageNumber(int aPageNumber)182     void SetVirtualPageNumber( int aPageNumber ) { m_virtualPageNumber = aPageNumber; }
183 
GetVirtualPageNumber()184     int GetVirtualPageNumber() const { return m_virtualPageNumber; }
185 
186     /**
187      * Set the sheet instance user definable page number.
188      *
189      * @note User definable page numbers can be any string devoid of white space characters.
190      */
191     void SetPageNumber( const wxString& aPageNumber );
192 
193     wxString GetPageNumber() const;
194 
GetSheet(unsigned aIndex)195     const SCH_SHEET* GetSheet( unsigned aIndex ) const
196     {
197         SCH_SHEET* retv = nullptr;
198 
199         if( aIndex < size() )
200             retv = at( aIndex );
201 
202         return retv;
203     }
204 
205     bool IsFullPath() const;
206 
207     /**
208      * Compare if this is the same sheet path as \a aSheetPathToTest.
209      *
210      * @param aSheetPathToTest is the sheet path to compare.
211      * @return 1 if this sheet path has more sheets than aSheetPathToTest,
212      *   -1 if this sheet path has fewer sheets than aSheetPathToTest,
213      *   or 0 if same
214      */
215     int Cmp( const SCH_SHEET_PATH& aSheetPathToTest ) const;
216 
217     /**
218      * Compare sheets by their page number and then by their name. Finally
219      * compare using #Cmp()
220      *
221      * @return -1 if aSheetPathToTest is greater than this (should appear later in the sort order)
222      *          0 if aSheetPathToTest is equal to this
223      *          1 if aSheetPathToTest is less than this (should appear earlier in the sort order)
224      */
225     int ComparePageNumAndName( const SCH_SHEET_PATH& aSheetPathToTest ) const;
226 
227     /**
228      * Check if this path is contained inside aSheetPathToTest.
229      *
230      * @param aSheetPathToTest is the sheet path to compare against.
231      * @return true if this path is contained inside or equal to aSheetPathToTest.
232      */
233     bool IsContainedWithin( const SCH_SHEET_PATH& aSheetPathToTest ) const;
234 
235     /**
236      * Return a pointer to the last #SCH_SHEET of the list.
237      *
238      * One can see the others sheet as the "path" to reach this last sheet.
239      */
240     SCH_SHEET* Last() const;
241 
242     /**
243      * @return the #SCH_SCREEN relative to the last sheet in list.
244      */
245     SCH_SCREEN* LastScreen();
246 
247 
248     ///< @copydoc SCH_SHEET_PATH::LastScreen()
249     SCH_SCREEN* LastScreen() const;
250 
251     /**
252      * Return the path of time stamps which do not changes even when editing sheet parameters.
253      *
254      * A path is something like / (root) or /34005677 or /34005677/00AE4523.
255      */
256     wxString PathAsString() const;
257 
258     /**
259      * Get the sheet path as an #KIID_PATH.
260      *
261      * @note This #KIID_PATH includes the root sheet UUID prefixed to the path.
262      */
263     KIID_PATH Path() const;
264 
265     /**
266      * Get the sheet path as an #KIID_PATH without the root sheet UUID prefix.
267      *
268      * @note This #KIID_PATH does not include the root sheet UUID prefixed to the path.
269      */
270     KIID_PATH PathWithoutRootUuid() const;
271 
272     /**
273      * Return the sheet path in a human readable form made from the sheet names.
274      *
275      * The "normal" path instead uses the #KIID objects in the path that do not change
276      * even when editing sheet parameters.
277      */
278     wxString PathHumanReadable( bool aUseShortRootName = true ) const;
279 
280     /**
281      * Update all the symbol references for this sheet path.
282      *
283      * Mandatory in complex hierarchies because sheets may use the same screen (basic schematic)
284      * more than once but with different references and units according to the displayed sheet.
285      */
286     void UpdateAllScreenReferences();
287 
288     /**
289      * Append a #SCH_REFERENCE object to \a aReferences based on \a aSymbol
290      *
291      * @param aReferences List of references to populate.
292      * @param aSymbol A symbol to add to aReferences
293      * @param aIncludePowerSymbols set to false to only get normal symbols.
294      * @param aForceIncludeOrphanSymbols set to true to include symbols having no symbol found
295      *                                   in lib.   The normal option is false, and set to true
296      *                                   only to build the full list of symbols.
297      */
298     void AppendSymbol( SCH_REFERENCE_LIST& aReferences, SCH_SYMBOL* aSymbol,
299                        bool aIncludePowerSymbols = true,
300                        bool aForceIncludeOrphanSymbols = false ) const;
301 
302     /**
303      * Adds #SCH_REFERENCE object to \a aReferences for each symbol in the sheet.
304      *
305      * @param aReferences List of references to populate.
306      * @param aIncludePowerSymbols set to false to only get normal symbols.
307      * @param aForceIncludeOrphanSymbols set to true to include symbols having no symbol found
308      *                                   in lib.   The normal option is false, and set to true
309      *                                   only to build the full list of symbols.
310      */
311     void GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols = true,
312                      bool aForceIncludeOrphanSymbols = false ) const;
313 
314     /**
315      * Append a #SCH_REFERENCE_LIST object to \a aRefList based on \a aSymbol,
316      * storing same-reference set of multi-unit parts together.
317      *
318      * The map key for each element will be the reference designator.
319      *
320      * @param aRefList Map of reference designators to reference lists
321      * @param aSymbol A symbol to add to aRefList
322      * @param aIncludePowerSymbols Set to false to only get normal symbols.
323      */
324     void AppendMultiUnitSymbol( SCH_MULTI_UNIT_REFERENCE_MAP& aRefList, SCH_SYMBOL* aSymbol,
325                                 bool aIncludePowerSymbols = true ) const;
326 
327     /**
328      * Add a #SCH_REFERENCE_LIST object to \a aRefList for each same-reference set of
329      * multi-unit parts in the sheet.
330      *
331      * The map key for each element will be the reference designator.
332      *
333      * @param aRefList Map of reference designators to reference lists
334      * @param aIncludePowerSymbols Set to false to only get normal symbols.
335      */
336     void GetMultiUnitSymbols( SCH_MULTI_UNIT_REFERENCE_MAP &aRefList,
337                               bool aIncludePowerSymbols = true ) const;
338 
339     /**
340      * Test the SCH_SHEET_PATH file names to check adding the sheet stored in the file
341      * \a aSrcFileName to the sheet stored in file \a aDestFileName  will cause a sheet
342      * path recursion.
343      *
344      * @param aSrcFileName is the source file name of the sheet add to \a aDestFileName.
345      * @param aDestFileName is the file name of the destination sheet for \a aSrcFileName.
346      * @return true if \a aFileName will cause recursion in the sheet path.  Otherwise false.
347      */
348     bool TestForRecursion( const wxString& aSrcFileName, const wxString& aDestFileName );
349 
350     /**
351      * Make the sheet file name relative to its parent sheet.
352      *
353      * This should only be called when changing the parent sheet path such performing a save
354      * as or a new schematic without a project in stand alone mode.  The sheet file name is
355      * only made relative if the current file name is relative.  Absolute sheet file name paths
356      * are a user choice so do not change them.
357      *
358      * Sheet file name paths are set according to the following criteria:
359      *  - If the sheet file name path is in the same as the parent sheet file name path, set
360      *    the sheet file name to just the file name and extension with no path.
361      *  - If the sheet file name path can be made relative to the parent sheet file name path,
362      *    set the sheet file name using the relative path.
363      *  - If the sheet file name path cannot be converted to a relative path, then fall back to
364      *    the absolute file name path.
365      */
366     void MakeFilePathRelativeToParentSheet();
367 
368     bool operator==( const SCH_SHEET_PATH& d1 ) const;
369 
370     bool operator!=( const SCH_SHEET_PATH& d1 ) const { return !( *this == d1 ) ; }
371 
372     bool operator<( const SCH_SHEET_PATH& d1 ) const { return m_sheets < d1.m_sheets; }
373 
374 private:
375     void initFromOther( const SCH_SHEET_PATH& aOther );
376 
377 protected:
378     std::vector< SCH_SHEET* > m_sheets;
379 
380     size_t m_current_hash;
381 
382     int m_virtualPageNumber;           /// Page numbers are maintained by the sheet load order.
383 
384     std::map<std::pair<wxString, wxString>, bool> m_recursion_test_cache;
385 };
386 
387 
388 namespace std
389 {
390     template<> struct hash<SCH_SHEET_PATH>
391     {
392         size_t operator()( const SCH_SHEET_PATH& path ) const;
393     };
394 }
395 
396 
397 typedef std::vector< SCH_SHEET_PATH >            SCH_SHEET_PATHS;
398 typedef SCH_SHEET_PATHS::iterator                SCH_SHEET_PATHS_ITER;
399 
400 
401 /**
402  * A container for handling #SCH_SHEET_PATH objects in a flattened hierarchy.
403  *
404  * #SCH_SHEET objects are not unique, there can be many sheets with the same filename and
405  * that share the same #SCH_SCREEN reference.   Each The schematic file (#SCH_SCREEN) may
406  * be shared between these sheets and symbol references are specific to a sheet path.
407  * When a sheet is entered, symbol references and sheet page number are updated.
408  */
409 class SCH_SHEET_LIST : public SCH_SHEET_PATHS
410 {
411 public:
412     /**
413      * Construct a flattened list of SCH_SHEET_PATH objects from \a aSheet.
414      *
415      * If aSheet == NULL, then this is an empty hierarchy which the user can populate.
416      */
417     SCH_SHEET_LIST( SCH_SHEET* aSheet = nullptr, bool aCheckIntegrity = false );
418 
419     ~SCH_SHEET_LIST() {}
420 
421     /**
422      * Check the entire hierarchy for any modifications.
423      *
424      * @return True if the hierarchy is modified otherwise false.
425      */
426     bool IsModified() const;
427 
428     void ClearModifyStatus();
429 
430     /**
431      * Fetch a SCH_ITEM by ID.  Also returns the sheet the item was found on in \a aPathOut.
432      */
433     SCH_ITEM* GetItem( const KIID& aID, SCH_SHEET_PATH* aPathOut = nullptr ) const;
434 
435     /**
436      * Fill an item cache for temporary use when many items need to be fetched.
437      */
438     void FillItemMap( std::map<KIID, EDA_ITEM*>& aMap );
439 
440     /**
441      * Silently annotate the not yet annotated power symbols of the entire hierarchy of the
442      * sheet path list.
443      *
444      * It is called before creating a netlist, to annotate power symbols, without prompting
445      * the user about not annotated or duplicate for these symbols, if only these symbols
446      * need annotation ( a very frequent case ).
447      */
448     void AnnotatePowerSymbols();
449 
450     /**
451      * Add a #SCH_REFERENCE object to \a aReferences for each symbol in the list of sheets.
452      *
453      * @param aReferences List of references to populate.
454      * @param aIncludePowerSymbols Set to false to only get normal symbols.
455      * @param aForceIncludeOrphanSymbols Set to true to include symbols having no symbol found
456      *                                   in lib.   The normal option is false, and set to true
457      *                                   only to build the full list of symbols.
458      */
459     void GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols = true,
460                      bool aForceIncludeOrphanSymbols = false ) const;
461 
462     /**
463      * Add a #SCH_REFERENCE object to \a aReferences for each symbol in the list of sheets that are
464      * contained within \a aSheetPath as well as recursively downwards inside aSheetPath.
465      *
466      * @param aReferences List of references to populate.
467      * @param aSheetPath Path to return symbols from
468      * @param aIncludePowerSymbols Set to false to only get normal symbols.
469      * @param aForceIncludeOrphanSymbols Set to true to include symbols having no symbol found
470      *                                   in lib.   The normal option is false, and set to true
471      *                                   only to build the full list of symbols.
472      */
473     void GetSymbolsWithinPath( SCH_REFERENCE_LIST& aReferences, const SCH_SHEET_PATH& aSheetPath,
474                                bool aIncludePowerSymbols = true,
475                                bool aForceIncludeOrphanSymbols = false ) const;
476 
477     /**
478      * Add a #SCH_SHEET_PATH object to \a aSheets for each sheet in the list that are
479      * contained within \a aSheetPath as well as recursively downwards inside aSheetPath.
480      *
481      * @param aReferences List of sheets to populate.
482      * @param aSheetPath Path to return sheets from
483      */
484     void GetSheetsWithinPath( SCH_SHEET_PATHS& aSheets, const SCH_SHEET_PATH& aSheetPath ) const;
485 
486     /**
487      * Add a #SCH_REFERENCE_LIST object to \a aRefList for each same-reference set of
488      * multi-unit parts in the list of sheets. The map key for each element will be the
489      * reference designator.
490      *
491      * @param aRefList Map of reference designators to reference lists
492      * @param aIncludePowerSymbols Set to false to only get normal symbols.
493      */
494     void GetMultiUnitSymbols( SCH_MULTI_UNIT_REFERENCE_MAP &aRefList,
495                               bool aIncludePowerSymbols = true ) const;
496 
497     /**
498      * Test every #SCH_SHEET_PATH in this #SCH_SHEET_LIST to verify if adding the sheets stored
499      * in \a aSrcSheetHierarchy to the sheet stored in \a aDestFileName  will cause recursion.
500      *
501      * @param aSrcSheetHierarchy is the SCH_SHEET_LIST of the source sheet add to \a aDestFileName.
502      * @param aDestFileName is the file name of the destination sheet for \a aSrcFileName.
503      * @return true if \a aFileName will cause recursion in the sheet path.  Otherwise false.
504      */
505     bool TestForRecursion( const SCH_SHEET_LIST& aSrcSheetHierarchy,
506                            const wxString& aDestFileName );
507 
508     /**
509      * Return a pointer to the first #SCH_SHEET_PATH object (not necessarily the only one) using
510      * a particular screen.
511      */
512     SCH_SHEET_PATH* FindSheetForScreen( const SCH_SCREEN* aScreen );
513 
514     /**
515      * Return a #SCH_SHEET_LIST with a copy of all the #SCH_SHEET_PATH using a particular screen.
516      */
517     SCH_SHEET_LIST FindAllSheetsForScreen( const SCH_SCREEN* aScreen ) const;
518 
519     /**
520      * Build the list of sheets and their sheet path from \a aSheet.
521      *
522      * If \a aSheet is the root sheet, the full sheet path and sheet list are built.
523      *
524      * The list will be ordered as per #SCH_SCREEN::GetSheets which results in sheets being ordered
525      * in the legacy way of using the X and Y positions of the sheets.
526      *
527      * @see #SortByPageNumbers to sort by page numbers
528      *
529      * @param aSheet is the starting sheet from which the list is built, or NULL
530      *               indicating that g_RootSheet should be used.
531      * @throw std::bad_alloc if the memory for the sheet path list could not be allocated.
532      */
533     void BuildSheetList( SCH_SHEET* aSheet, bool aCheckIntegrity );
534 
535     /**
536      * Sort the list of sheets by page number. This should be called after #BuildSheetList
537      *
538      * If page numbers happen to be equal, then it compares the sheet names to ensure deterministic
539      * ordering.
540      *
541      * @param aUpdateVirtualPageNums If true, updates the virtual page numbers to match the new
542      * ordering
543      */
544     void SortByPageNumbers( bool aUpdateVirtualPageNums = true );
545 
546     bool NameExists( const wxString& aSheetName ) const;
547 
548     bool PageNumberExists( const wxString& aPageNumber ) const;
549 
550     /**
551      * Update all of the symbol instance information using \a aSymbolInstances.
552      * WARNING: Do not call this on anything other than the full hierarchy.
553      * @param aSymbolInstances is the symbol path information loaded from the root schematic.
554      */
555     void UpdateSymbolInstances( const std::vector<SYMBOL_INSTANCE_REFERENCE>& aSymbolInstances );
556 
557     /**
558      * Update all of the sheet instance information using \a aSheetInstances.
559      *
560      * @warning Do not call this on anything other than the full hierarchy.
561      *
562      * @param aSymbolInstances is the symbol path information loaded from the root schematic.
563      */
564     void UpdateSheetInstances( const std::vector<SCH_SHEET_INSTANCE>& aSheetInstances );
565 
566     std::vector<KIID_PATH> GetPaths() const;
567 
568     /**
569      * Fetch the instance information for all of the sheets in the hiearchy.
570      *
571      * @return all of the sheet instance data for the hierarchy.
572      */
573     std::vector<SCH_SHEET_INSTANCE> GetSheetInstances() const;
574 
575     /**
576      * Check all of the sheet instance for empty page numbers.
577      *
578      * @note This should only return true when loading a legacy schematic or an s-expression
579      *       schematic before version 20201005.
580      *
581      * @return true if all sheet instance page numbers are not defined.  Otherwise false.
582      */
583     bool AllSheetPageNumbersEmpty() const;
584 
585     /**
586      * Set initial sheet page numbers.
587      *
588      * The number scheme is base on the old pseudo sheet numbering algorithm prior to
589      * the implementation of user definable sheet page numbers.
590      */
591     void SetInitialPageNumbers();
592 
593 private:
594     SCH_SHEET_PATH  m_currentSheetPath;
595 };
596 
597 #endif // CLASS_DRAWSHEET_PATH_H
598