/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2011 Wayne Stambaugh * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ /** * @file sch_sheet_path.h * Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema. */ #ifndef CLASS_DRAWSHEET_PATH_H #define CLASS_DRAWSHEET_PATH_H #include #include #include #include /** * A simple container for schematic symbol instance information. */ struct SCH_SYMBOL_INSTANCE { KIID_PATH m_Path; // Things that can be annotated: wxString m_Reference; int m_Unit = 1; // Do not use. This is left over from the dubious decision to instantiate symbol value // and footprint fields. wxString m_Value; wxString m_Footprint; // The project name associated with this instance. wxString m_ProjectName; }; /** * A simple container for sheet instance information. */ struct SCH_SHEET_INSTANCE { KIID_PATH m_Path; wxString m_PageNumber; // The project name associated with this instance. wxString m_ProjectName; }; /** * Complex hierarchies * * A hierarchical schematic uses sheets (hierarchical sheets) included in a given sheet. * Each sheet corresponds to a schematic drawing handled by a SCH_SCREEN structure. A * SCH_SCREEN structure contains drawings, and have a filename to write its data. Also a * SCH_SCREEN displays a sheet number and the name of the sheet. * * In simple (and flat) hierarchies a sheet is linked to a SCH_SCREEN, and a SCH_SCREEN is * used by the single hierarchical sheet. * * In complex hierarchies the same SCH_SCREEN (and its data) is shared by more than one sheet. * Therefore subsheets (like subsheets in a SCH_SCREEN shared by many sheets) can also be * shared. So the same SCH_SCREEN must handle different symbol references and unit selections * depending on which sheet is currently selected, and how a given subsheet is selected. Two * sheets share the same SCH_SCREEN (the same drawings) if they have the same filename. * * In KiCad each symbol and sheet receives (when created) a uuid. So each sheet has 2 id: its * uuid (which cannot change) and its name (that can be edited and therefore is not reliable * for strong identification). * A given sheet in a hierarchy is fully labeled by its path (or sheet path) that is the list * of uuids found to access it through the hierarchy. The root sheet is /. All other sheets * have a path like /1234ABCD or /4567FEDC/AA2233DD/. This path can be displayed as human * readable sheet name like: / or /sheet1/include_sheet/ or /sheet2/include_sheet/ * * So to know for a given SCH_SCREEN (a given schematic drawings) we must: * 1) Handle all references possibilities. * 2) When acceded by a given selected sheet, display (update) the * corresponding references and sheet path * * The class SCH_SHEET_PATH handles paths used to access a sheet. The class SCH_SHEET_LIST * allows one to handle the full (or partial) list of sheets and their paths in a complex * hierarchy. The class EDA_ScreenList allows one to handle the list of SCH_SCREEN. It is * useful to clear or save data, but is not suitable to handle the full complex hierarchy * possibilities (usable in flat and simple hierarchies). */ class EDA_ITEM; class SCH_SHEET; class SCH_SCREEN; class SCH_MARKER; class SCH_ITEM; class SCH_SYMBOL; class SCH_REFERENCE_LIST; /** * Container to map reference designators for multi-unit parts. */ typedef std::map SCH_MULTI_UNIT_REFERENCE_MAP; /** * Handle access to a stack of flattened #SCH_SHEET objects by way of a path for * creating a flattened schematic hierarchy. * * The #SCH_SHEET objects are stored in a list from first (usually the root sheet) to a * given sheet in last position. The _last_ sheet is usually the sheet we want to select * or reach (which is what the function Last() returns). Others sheets constitute the * "path" from the first to the last sheet. */ class SCH_SHEET_PATH { public: SCH_SHEET_PATH(); SCH_SHEET_PATH( const SCH_SHEET_PATH& aOther ); SCH_SHEET_PATH& operator=( const SCH_SHEET_PATH& aOther ); SCH_SHEET_PATH operator+( const SCH_SHEET_PATH& aOther ); ~SCH_SHEET_PATH() = default; /// Forwarded method from std::vector SCH_SHEET* at( size_t aIndex ) const { return m_sheets.at( aIndex ); } /// Forwarded method from std::vector void clear() { m_sheets.clear(); Rehash(); } /// Forwarded method from std::vector bool empty() const { return m_sheets.empty(); } /// Forwarded method from std::vector void pop_back() { m_sheets.pop_back(); Rehash(); } /// Forwarded method from std::vector void push_back( SCH_SHEET* aSheet ) { m_sheets.push_back( aSheet ); Rehash(); } /// Forwarded method from std::vector size_t size() const { return m_sheets.size(); } std::vector::iterator erase( std::vector::const_iterator aPosition ) { return m_sheets.erase( aPosition ); } void Rehash(); size_t GetCurrentHash() const { return m_current_hash; } /** * Set the sheet instance virtual page number. * * Virtual page numbers are incremental integers set automatically when the sheet path * hierarchy is created (@see #SCH_SHEET_LIST::BuildSheetList). The virtual page * numbering is ordered by the X and Y position of the sheet in a schematic which * mimics the page numbering code prior to the addition of actual user definable page * numbers. Virtual page numbers should only be use when annotating schematics. */ void SetVirtualPageNumber( int aPageNumber ) { m_virtualPageNumber = aPageNumber; } int GetVirtualPageNumber() const { return m_virtualPageNumber; } /** * Set the sheet instance user definable page number. * * @note User definable page numbers can be any string devoid of white space characters. */ void SetPageNumber( const wxString& aPageNumber ); wxString GetPageNumber() const; const SCH_SHEET* GetSheet( unsigned aIndex ) const { SCH_SHEET* retv = nullptr; if( aIndex < size() ) retv = at( aIndex ); return retv; } bool IsFullPath() const; /** * Compare if this is the same sheet path as \a aSheetPathToTest. * * @param aSheetPathToTest is the sheet path to compare. * @return 1 if this sheet path has more sheets than aSheetPathToTest, * -1 if this sheet path has fewer sheets than aSheetPathToTest, * or 0 if same */ int Cmp( const SCH_SHEET_PATH& aSheetPathToTest ) const; void CachePageNumber() const { m_cached_page_number = GetPageNumber(); } wxString GetCachedPageNumber() const { return m_cached_page_number; } /** * Compare sheets by their page number. If the actual page number is equal, use virtual page * numbers to compare. * * @return -1 if aSheetPathToTest is greater than this (should appear later in the sort order) * 0 if aSheetPathToTest is equal to this * 1 if aSheetPathToTest is less than this (should appear earlier in the sort order) */ int ComparePageNum( const SCH_SHEET_PATH& aSheetPathToTest ) const; /** * Check if this path is contained inside aSheetPathToTest. * * @param aSheetPathToTest is the sheet path to compare against. * @return true if this path is contained inside or equal to aSheetPathToTest. */ bool IsContainedWithin( const SCH_SHEET_PATH& aSheetPathToTest ) const; /** * Return a pointer to the last #SCH_SHEET of the list. * * One can see the others sheet as the "path" to reach this last sheet. */ SCH_SHEET* Last() const; /** * @return the #SCH_SCREEN relative to the last sheet in list. */ SCH_SCREEN* LastScreen(); ///< @copydoc SCH_SHEET_PATH::LastScreen() SCH_SCREEN* LastScreen() const; bool GetExcludedFromSim() const; bool GetExcludedFromBOM() const; bool GetExcludedFromBoard() const; bool GetDNP() const; /** * Fetch a SCH_ITEM by ID. */ SCH_ITEM* GetItem( const KIID& aID ) const; /** * Return the path of time stamps which do not changes even when editing sheet parameters. * * A path is something like / (root) or /34005677 or /34005677/00AE4523. */ wxString PathAsString() const; /** * Get the sheet path as an #KIID_PATH. * * @note This #KIID_PATH includes the root sheet UUID prefixed to the path. */ KIID_PATH Path() const; /** * Return the sheet path in a human readable form made from the sheet names. * * The "normal" path instead uses the #KIID objects in the path that do not change * even when editing sheet parameters. */ wxString PathHumanReadable( bool aUseShortRootName = true, bool aStripTrailingSeparator = false ) const; /** * Update all the symbol references for this sheet path. * * Mandatory in complex hierarchies because sheets may use the same screen (basic schematic) * more than once but with different references and units according to the displayed sheet. */ void UpdateAllScreenReferences() const; /** * Append a #SCH_REFERENCE object to \a aReferences based on \a aSymbol * * @param aReferences List of references to populate. * @param aSymbol A symbol to add to aReferences * @param aIncludePowerSymbols set to false to only get normal symbols. * @param aForceIncludeOrphanSymbols set to true to include symbols having no symbol found * in lib. The normal option is false, and set to true * only to build the full list of symbols. */ void AppendSymbol( SCH_REFERENCE_LIST& aReferences, SCH_SYMBOL* aSymbol, bool aIncludePowerSymbols = true, bool aForceIncludeOrphanSymbols = false ) const; /** * Adds #SCH_REFERENCE object to \a aReferences for each symbol in the sheet. * * @param aReferences List of references to populate. * @param aIncludePowerSymbols set to false to only get normal symbols. * @param aForceIncludeOrphanSymbols set to true to include symbols having no symbol found * in lib. The normal option is false, and set to true * only to build the full list of symbols. */ void GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols = true, bool aForceIncludeOrphanSymbols = false ) const; /** * Append a #SCH_REFERENCE_LIST object to \a aRefList based on \a aSymbol, * storing same-reference set of multi-unit parts together. * * The map key for each element will be the reference designator. * * @param aRefList Map of reference designators to reference lists * @param aSymbol A symbol to add to aRefList * @param aIncludePowerSymbols Set to false to only get normal symbols. */ void AppendMultiUnitSymbol( SCH_MULTI_UNIT_REFERENCE_MAP& aRefList, SCH_SYMBOL* aSymbol, bool aIncludePowerSymbols = true ) const; /** * Add a #SCH_REFERENCE_LIST object to \a aRefList for each same-reference set of * multi-unit parts in the sheet. * * The map key for each element will be the reference designator. * * @param aRefList Map of reference designators to reference lists * @param aIncludePowerSymbols Set to false to only get normal symbols. */ void GetMultiUnitSymbols( SCH_MULTI_UNIT_REFERENCE_MAP &aRefList, bool aIncludePowerSymbols = true ) const; /** * Test the SCH_SHEET_PATH file names to check adding the sheet stored in the file * \a aSrcFileName to the sheet stored in file \a aDestFileName will cause a sheet * path recursion. * * @param aSrcFileName is the source file name of the sheet add to \a aDestFileName. * @param aDestFileName is the file name of the destination sheet for \a aSrcFileName. * @return true if \a aFileName will cause recursion in the sheet path. Otherwise false. */ bool TestForRecursion( const wxString& aSrcFileName, const wxString& aDestFileName ); /** * Make the sheet file name relative to its parent sheet. * * This should only be called when changing the parent sheet path such performing a save * as or a new schematic without a project in stand alone mode. The sheet file name is * only made relative if the current file name is relative. Absolute sheet file name paths * are a user choice so do not change them. * * Sheet file name paths are set according to the following criteria: * - If the sheet file name path is in the same as the parent sheet file name path, set * the sheet file name to just the file name and extension with no path. * - If the sheet file name path can be made relative to the parent sheet file name path, * set the sheet file name using the relative path. * - If the sheet file name path cannot be converted to a relative path, then fall back to * the absolute file name path. */ void MakeFilePathRelativeToParentSheet(); /** * Attempt to add new symbol instances for all symbols in this sheet path prefixed * with \a aPrefixSheetPath. * * The new symbol instance data will be assigned by the following criteria: * - If the instance data can be found for this sheet path, use the instance data. * - If the instance data cannot be found for this sheet path and the instance data cache * for the symbol is not empty, use the first instance data in the cache. * - If the cache is empty and the library symbol link is valid, set the instance data * from the library symbol. * - If all else fails, set the reference to "U?", the unit to 1, and everything else to * an empty string. * * @param aPrefixSheetPath is the sheet path to prefix to this sheet path for the new symbol * instance. * @param aProjectName is the name of the project for the new symbol instance data. */ void AddNewSymbolInstances( const SCH_SHEET_PATH& aPrefixSheetPath, const wxString& aProjectName ); void RemoveSymbolInstances( const SCH_SHEET_PATH& aPrefixSheetPath ); bool operator==( const SCH_SHEET_PATH& d1 ) const; bool operator!=( const SCH_SHEET_PATH& d1 ) const { return !( *this == d1 ) ; } bool operator<( const SCH_SHEET_PATH& d1 ) const { return m_sheets < d1.m_sheets; } private: void initFromOther( const SCH_SHEET_PATH& aOther ); protected: std::vector m_sheets; size_t m_current_hash; mutable wxString m_cached_page_number; int m_virtualPageNumber; /// Page numbers are maintained by the sheet load order. std::map, bool> m_recursion_test_cache; }; namespace std { template<> struct hash { size_t operator()( const SCH_SHEET_PATH& path ) const; }; } struct SHEET_PATH_HASH { size_t operator()( const SCH_SHEET_PATH& path ) const { return path.GetCurrentHash(); } }; struct SHEET_PATH_CMP { bool operator()( const SCH_SHEET_PATH& lhs, const SCH_SHEET_PATH& rhs ) const { return lhs.GetCurrentHash() < rhs.GetCurrentHash(); } }; /** * A container for handling #SCH_SHEET_PATH objects in a flattened hierarchy. * * #SCH_SHEET objects are not unique, there can be many sheets with the same filename and * that share the same #SCH_SCREEN reference. Each The schematic file (#SCH_SCREEN) may * be shared between these sheets and symbol references are specific to a sheet path. * When a sheet is entered, symbol references and sheet page number are updated. */ class SCH_SHEET_LIST : public std::vector { public: /** * Construct a flattened list of SCH_SHEET_PATH objects from \a aSheet. * * If aSheet == NULL, then this is an empty hierarchy which the user can populate. */ SCH_SHEET_LIST( SCH_SHEET* aSheet = nullptr ); ~SCH_SHEET_LIST() {} /** * Check the entire hierarchy for any modifications. * * @return True if the hierarchy is modified otherwise false. */ bool IsModified() const; void ClearModifyStatus(); /** * Fetch a SCH_ITEM by ID. Also returns the sheet the item was found on in \a aPathOut. */ SCH_ITEM* GetItem( const KIID& aID, SCH_SHEET_PATH* aPathOut = nullptr ) const; /** * Fill an item cache for temporary use when many items need to be fetched. */ void FillItemMap( std::map& aMap ); /** * Silently annotate the not yet annotated power symbols of the entire hierarchy of the * sheet path list. * * It is called before creating a netlist, to annotate power symbols, without prompting * the user about not annotated or duplicate for these symbols, if only these symbols * need annotation ( a very frequent case ). */ void AnnotatePowerSymbols(); /** * Add a #SCH_REFERENCE object to \a aReferences for each symbol in the list of sheets. * * @param aReferences List of references to populate. * @param aIncludePowerSymbols Set to false to only get normal symbols. * @param aForceIncludeOrphanSymbols Set to true to include symbols having no symbol found * in lib. The normal option is false, and set to true * only to build the full list of symbols. */ void GetSymbols( SCH_REFERENCE_LIST& aReferences, bool aIncludePowerSymbols = true, bool aForceIncludeOrphanSymbols = false ) const; /** * Add a #SCH_REFERENCE object to \a aReferences for each symbol in the list of sheets that are * contained within \a aSheetPath as well as recursively downwards inside aSheetPath. * * @param aReferences List of references to populate. * @param aSheetPath Path to return symbols from * @param aIncludePowerSymbols Set to false to only get normal symbols. * @param aForceIncludeOrphanSymbols Set to true to include symbols having no symbol found * in lib. The normal option is false, and set to true * only to build the full list of symbols. */ void GetSymbolsWithinPath( SCH_REFERENCE_LIST& aReferences, const SCH_SHEET_PATH& aSheetPath, bool aIncludePowerSymbols = true, bool aForceIncludeOrphanSymbols = false ) const; /** * Add a #SCH_SHEET_PATH object to \a aSheets for each sheet in the list that are * contained within \a aSheetPath as well as recursively downwards inside aSheetPath. * * @param aReferences List of sheets to populate. * @param aSheetPath Path to return sheets from */ void GetSheetsWithinPath( std::vector& aSheets, const SCH_SHEET_PATH& aSheetPath ) const; /** * Finds a SCH_SHEET_PATH that matches the provided KIID_PATH. * * @param aPath The KIID_PATH to search for. */ std::optional GetSheetPathByKIIDPath( const KIID_PATH& aPath, bool aIncludeLastSheet = true ) const; /** * Add a #SCH_REFERENCE_LIST object to \a aRefList for each same-reference set of * multi-unit parts in the list of sheets. The map key for each element will be the * reference designator. * * @param aRefList Map of reference designators to reference lists * @param aIncludePowerSymbols Set to false to only get normal symbols. */ void GetMultiUnitSymbols( SCH_MULTI_UNIT_REFERENCE_MAP &aRefList, bool aIncludePowerSymbols = true ) const; /** * Test every #SCH_SHEET_PATH in this #SCH_SHEET_LIST to verify if adding the sheets stored * in \a aSrcSheetHierarchy to the sheet stored in \a aDestFileName will cause recursion. * * @param aSrcSheetHierarchy is the SCH_SHEET_LIST of the source sheet add to \a aDestFileName. * @param aDestFileName is the file name of the destination sheet for \a aSrcFileName. * @return true if \a aFileName will cause recursion in the sheet path. Otherwise false. */ bool TestForRecursion( const SCH_SHEET_LIST& aSrcSheetHierarchy, const wxString& aDestFileName ); /** * Return a pointer to the first #SCH_SHEET_PATH object (not necessarily the only one) * matching the provided path. Returns nullptr if not found. */ SCH_SHEET_PATH* FindSheetForPath( const SCH_SHEET_PATH* aPath ); /** * Return the first #SCH_SHEET_PATH object (not necessarily the only one) using a particular * screen. */ SCH_SHEET_PATH FindSheetForScreen( const SCH_SCREEN* aScreen ); /** * Return a #SCH_SHEET_LIST with a copy of all the #SCH_SHEET_PATH using a particular screen. */ SCH_SHEET_LIST FindAllSheetsForScreen( const SCH_SCREEN* aScreen ) const; /** * Build the list of sheets and their sheet path from \a aSheet. * * If \a aSheet is the root sheet, the full sheet path and sheet list are built. * * The list will be ordered as per #SCH_SCREEN::BuildSheetList which results in sheets being ordered * in the legacy way of using the X and Y positions of the sheets. * * @see #SortByPageNumbers to sort by page numbers * * @param aSheet is the starting sheet from which the list is built, or NULL * indicating that g_RootSheet should be used. * @throw std::bad_alloc if the memory for the sheet path list could not be allocated. */ void BuildSheetList( SCH_SHEET* aSheet, bool aCheckIntegrity ); /** * Sort the list of sheets by page number. This should be called after #BuildSheetList * * If page numbers happen to be equal, then it compares the sheet names to ensure deterministic * ordering. * * @param aUpdateVirtualPageNums If true, updates the virtual page numbers to match the new * ordering */ void SortByPageNumbers( bool aUpdateVirtualPageNums = true ); bool NameExists( const wxString& aSheetName ) const; bool PageNumberExists( const wxString& aPageNumber ) const; /** * Truncates the list by removing sheet's with page numbers not in the given list * * @param aPageInclusions List of Page Numbers (non-virtual) to keep */ void TrimToPageNumbers( const std::vector& aPageInclusions ); /** * Update all of the symbol instance information using \a aSymbolInstances. * WARNING: Do not call this on anything other than the full hierarchy. * @param aSymbolInstances is the symbol path information loaded from the root schematic. */ void UpdateSymbolInstanceData( const std::vector& aSymbolInstances ); /** * Update all of the sheet instance information using \a aSheetInstances. * * @warning Do not call this on anything other than the full hierarchy. * * @param aSymbolInstances is the symbol path information loaded from the root schematic. */ void UpdateSheetInstanceData( const std::vector& aSheetInstances ); std::vector GetPaths() const; /** * Fetch the instance information for all of the sheets in the hiearchy. * * @return all of the sheet instance data for the hierarchy. */ std::vector GetSheetInstances() const; /** * Check all of the sheet instance for empty page numbers. * * @note This should only return true when loading a legacy schematic or an s-expression * schematic before version 20201005. * * @return true if all sheet instance page numbers are not defined. Otherwise false. */ bool AllSheetPageNumbersEmpty() const; /** * Set initial sheet page numbers. * * The number scheme is base on the old pseudo sheet numbering algorithm prior to * the implementation of user definable sheet page numbers. */ void SetInitialPageNumbers(); /** * Attempt to add new symbol instances for all symbols in this list of sheet paths prefixed * with \a aPrefixSheetPath. * * @param aPrefixSheetPath is the sheet path to append the new symbol instances to. * @param aProjectName is the name of the project for the new symbol instance data. */ void AddNewSymbolInstances( const SCH_SHEET_PATH& aPrefixSheetPath, const wxString& aProjectName ); void AddNewSheetInstances( const SCH_SHEET_PATH& aPrefixSheetPath, int aLastVirtualPageNumber ); int GetLastVirtualPageNumber() const; void RemoveSymbolInstances( const SCH_SHEET_PATH& aPrefixSheetPath ); bool HasPath( const KIID_PATH& aPath ) const; bool ContainsSheet( const SCH_SHEET* aSheet ) const; private: SCH_SHEET_PATH m_currentSheetPath; }; #endif // CLASS_DRAWSHEET_PATH_H