diff --git a/src/App/Application.h b/src/App/Application.h index 511badca96..fb593fe69e 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -461,16 +461,18 @@ public: /** @name Link handling */ //@{ - /** Check for link recursion depth + /** + * @brief Check for link recursion depth. + * + * The function uses an internal count of all objects in all documents as + * the limit of recursion depth. If the depth exceeds this limit, it means + * that there is likely a cyclic reference in the links. * * @param depth: current depth * @param option: whether to throw exception, print an error message or quieten any output. * In the latter case the caller must check the returned value. * * @return Return the maximum remaining depth. - * - * The function uses an internal count of all objects in all documents as - * the limit of recursion depth. */ int checkLinkDepth(int depth, MessageOption option = MessageOption::Error); diff --git a/src/App/Document.cpp b/src/App/Document.cpp index 0e63148238..14d2c51c58 100644 --- a/src/App/Document.cpp +++ b/src/App/Document.cpp @@ -1251,12 +1251,12 @@ constexpr auto fcAttrDepObjName {"Name"}; constexpr auto fcAttrDepAllowPartial {"AllowPartial"}; constexpr auto fcElementObjectDep {"Dep"}; -void Document::writeObjects(const std::vector& obj, +void Document::writeObjects(const std::vector& objs, Base::Writer& writer) const { // writing the features types writer.incInd(); // indentation for 'Objects count' - writer.Stream() << writer.ind() << "& obj, writer.incInd(); // indentation for 'Object type' if (isExporting(nullptr) == 0U) { - for (const auto o : obj) { + for (const auto o : objs) { const auto& outList = o->getOutList(DocumentObject::OutListNoHidden | DocumentObject::OutListNoXLinked); writer.Stream() << writer.ind() @@ -1294,7 +1294,7 @@ void Document::writeObjects(const std::vector& obj, } std::vector::const_iterator it; - for (it = obj.begin(); it != obj.end(); ++it) { + for (it = objs.begin(); it != objs.end(); ++it) { writer.Stream() << writer.ind() << "getTypeId().getName() << "\" " << "name=\"" << (*it)->getExportName() << "\" " @@ -1324,10 +1324,10 @@ void Document::writeObjects(const std::vector& obj, writer.Stream() << writer.ind() << "" << '\n'; // writing the features itself - writer.Stream() << writer.ind() << "" << '\n'; + writer.Stream() << writer.ind() << "" << '\n'; writer.incInd(); // indentation for 'Object name' - for (it = obj.begin(); it != obj.end(); ++it) { + for (it = objs.begin(); it != objs.end(); ++it) { writer.Stream() << writer.ind() << "getExportName() << "\""; if ((*it)->hasExtensions()) { writer.Stream() << " Extensions=\"True\""; @@ -2036,11 +2036,11 @@ bool Document::afterRestore(const std::vector& objArray, bool c return false; } - // some link type property cannot restore link information until other - // objects has been restored. For example, PropertyExpressionEngine and - // PropertySheet with expression containing label reference. So we add the - // Property::afterRestore() interface to let them sort it out. Note, this - // API is not called in object dedpenency order, because the order + // Some link type properties cannot restore link information until other + // objects have been restored. For example, PropertyExpressionEngine and + // PropertySheet with expressions containing a label reference. So we add + // the Property::afterRestore() interface to let them sort it out. Note, + // this API is not called in object dependency order, because the order // information is not ready yet. std::map> propMap; for (auto obj : objArray) { @@ -2228,7 +2228,7 @@ vector Document::getTouched() const return result; } -void Document::setClosable(const bool c) // NOLINT +void Document::setClosable(bool c) // NOLINT { setStatus(Document::Closable, c); } @@ -2508,7 +2508,7 @@ std::vector Document::getDependentDocuments(const bool sort) } std::vector Document::getDependentDocuments(std::vector docs, - const bool sort) + const bool sort) { DependencyList depList; std::map docMap; @@ -2572,11 +2572,6 @@ std::vector Document::getDependentDocuments(std::vector do return ret; } -void Document::_rebuildDependencyList(const std::vector& objs) -{ - (void)objs; -} - /** * @brief Signal that object identifiers, typically a property or document object has been renamed. * @@ -3144,15 +3139,15 @@ Document::addObjects(const char* sType, const std::vector& objectNa return objects; } -void Document::addObject(DocumentObject* pcObject, const char* pObjectName) +void Document::addObject(DocumentObject* obj, const char* name) { - if (pcObject->getDocument()) { + if (obj->getDocument()) { throw Base::RuntimeError("Document object is already added to a document"); } - pcObject->setDocument(this); + obj->setDocument(this); - _addObject(pcObject, pObjectName, AddObjectOption::SetNewStatus | AddObjectOption::ActivateObject); + _addObject(obj, name, AddObjectOption::SetNewStatus | AddObjectOption::ActivateObject); } void Document::_addObject(DocumentObject* pcObject, const char* pObjectName, AddObjectOptions options, const char* viewType) diff --git a/src/App/Document.h b/src/App/Document.h index 536f3d8a46..7a647d2b50 100644 --- a/src/App/Document.h +++ b/src/App/Document.h @@ -48,7 +48,7 @@ namespace Base class Writer; } -namespace App +namespace App { enum class AddObjectOption { @@ -64,9 +64,9 @@ using AddObjectOptions = Base::Flags; enum class RemoveObjectOption { None = 0, - MayRemoveWhileRecomputing = 1, + MayRemoveWhileRecomputing = 1, MayDestroyOutOfTransaction = 2, - DestroyOnRollback = 4, + DestroyOnRollback = 4, PreserveChildrenVisibility = 8 }; using RemoveObjectOptions = Base::Flags; @@ -88,7 +88,12 @@ class StringHasher; using StringHasherRef = Base::Reference; /** - * @brief The document class + * @brief A class that represents a FreeCAD document. + * + * A document is a container for all objects that are part of a FreeCAD + * project. Besides managing the objects, it also maintains properties itself. + * As such, it is also a PropertyContainer. + * * @ingroup DocumentGroup * @details For a more high-level discussion see the topic @ref DocumentGroup "Document". */ @@ -98,165 +103,300 @@ class AppExport Document: public PropertyContainer public: // clang-format off + /// Defines the position of the status bits for documents. enum Status { + /// Avoid recomputing the document. SkipRecompute = 0, + /// Keep trailing digits in the object names when reading objects. KeepTrailingDigits = 1, + /// Whether the document can be closed. Closable = 2, + /// Whether the document is currently being restored. Restoring = 3, + /// Whether the document is currently recomputing. Recomputing = 4, + /** Whether the document is being partially restored, + * typically meaning that there was an error. + */ PartialRestore = 5, + /// Whether the document is currently importing objects. Importing = 6, + /** Whether the document is a partial document, + * meaning that it is not fully loaded. + */ PartialDoc = 7, - AllowPartialRecompute = 8, // allow recomputing editing object if SkipRecompute is set - TempDoc = 9, // Mark as temporary document without prompt for save + /// Whether only the editing or active object is recomputed if SkipRecompute is set. + AllowPartialRecompute = 8, + /// Whether the document is a temporary document, meaning that it is not saved to a file. + TempDoc = 9, + /// Whether the document has an error during restore. RestoreError = 10, - LinkStampChanged = 11, // Indicates during restore time if any linked document's time stamp has changed - IgnoreErrorOnRecompute = 12, // Don't report errors if the recompute failed - RecomputeOnRestore = 13, // Mark pending recompute on restore for migration purposes - MigrateLCS = 14 // Migrate local coordinate system of older versions + /// Whether any linked document's time stamp has changed during restore time. + LinkStampChanged = 11, + /// Whether errors are reported if the recompute failed. + IgnoreErrorOnRecompute = 12, + /// Whether a recompute is necessary on restore for migration purposes. + RecomputeOnRestore = 13, + /// Whether the local coordinate system of older versions should be migrated. + MigrateLCS = 14 }; // clang-format on // NOLINTBEGIN - /** @name Properties */ - //@{ - /// holds the long name of the document (utf-8 coded) + /** @name Properties + * @{ + */ + + /// The long name of the document (utf-8 coded). PropertyString Label; - /// full qualified (with path) file name (utf-8 coded) + + /// The fully qualified (with path) file name (utf-8 coded). PropertyString FileName; - /// creators name (utf-8) + + /// The creator's name (utf-8). PropertyString CreatedBy; + /// The date when the document was created. PropertyString CreationDate; - /// user last modified the document + /// The user that last modified the document. PropertyString LastModifiedBy; + /// The date when the document was last modified. PropertyString LastModifiedDate; - /// company name UTF8(optional) + /// The company name (utf-8, optional). PropertyString Company; - /// Unit System + /// The Unit System for this document. PropertyEnumeration UnitSystem; - /// long comment or description (UTF8 with line breaks) + /// A long comment or description (utf-8 with line breaks). PropertyString Comment; - /// Id e.g. Part number + /// The Id, e.g. a Part number. PropertyString Id; - /// unique identifier of the document + /// A unique identifier of the document. PropertyUUID Uid; - /// Full name of the licence e.g. "Creative Commons Attribution". See https://spdx.org/licenses/ + /// The full name of the licence e.g. "Creative Commons Attribution". See https://spdx.org/licenses/. PropertyString License; - /// License description/contract URL + /// The URL to the license description or contract. PropertyString LicenseURL; - /// Meta descriptions + /// Meta descriptions. PropertyMap Meta; /// Material descriptions, used and defined in the Material module. PropertyMap Material; - /// read-only name of the temp dir created when the document is opened + /// Read-only name of the temp dir created when the document is opened. PropertyString TransientDir; - /// Tip object of the document (if any) + /// Tip object of the document (if any). PropertyLink Tip; - /// Tip object of the document (if any) + /// Tip object name of the document (if any). PropertyString TipName; - /// Whether to show hidden items in TreeView + /// Whether to show hidden items in TreeView. PropertyBool ShowHidden; - /// Whether to use hasher on topological naming + /// Whether to use hasher on topological naming. PropertyBool UseHasher; - //@} + /// @} - /** @name Signals of the document */ - //@{ + /** @name Signals of the document + * @{ + */ // clang-format off - /// signal before changing an doc property + + /// Signal before changing a document property. fastsignals::signal signalBeforeChange; - /// signal on changed doc property + /// Signal on a changed document property. fastsignals::signal signalChanged; - /// signal on new Object + /// Signal on new object. fastsignals::signal signalNewObject; - /// signal on deleted Object + /// Signal on a deleted object. fastsignals::signal signalDeletedObject; - /// signal before changing an Object + /// Signal before changing an object. fastsignals::signal signalBeforeChangeObject; - /// signal on changed Object + /// Signal on a changed object. fastsignals::signal signalChangedObject; - /// signal on manually called DocumentObject::touch() + /// Signal on a manually called DocumentObject::touch(). fastsignals::signal signalTouchedObject; - /// signal on relabeled Object + /// Signal on relabeled object. fastsignals::signal signalRelabelObject; - /// signal on activated Object + /// Signal on an activated object. fastsignals::signal signalActivatedObject; - /// signal on created object + /// Signal on a created object. fastsignals::signal signalTransactionAppend; - /// signal on removed object + /// Signal on a removed object. fastsignals::signal signalTransactionRemove; - /// signal on undo + /// Signal on undo. fastsignals::signal signalUndo; - /// signal on redo + /// Signal on redo. fastsignals::signal signalRedo; - /** signal on load/save document - * this signal is given when the document gets streamed. - * you can use this hook to write additional information in - * the file (like the Gui::Document does). + + /** + * @brief Signal on load/save document. + * + * This signal is given when the document gets streamed. You can use this + * hook to write additional information in the file (like Gui::Document + * does). */ fastsignals::signal signalSaveDocument; + /// Signal on restoring a document. fastsignals::signal signalRestoreDocument; + /// Signal on exporting objects. fastsignals::signal&, Base::Writer&)> signalExportObjects; + /// Signal on exporting view objects. fastsignals::signal&, Base::Writer&)> signalExportViewObjects; + /// Signal on importing objects. fastsignals::signal&, Base::XMLReader&)> signalImportObjects; + /// Signal on importing view objects. fastsignals::signal&, Base::Reader&, const std::map&)> signalImportViewObjects; + /// Signal after finishing importing objects. fastsignals::signal&)> signalFinishImportObjects; - // signal starting a save action to a file + /// Signal starting a save action to a file. fastsignals::signal signalStartSave; - // signal finishing a save action to a file + /// Signal finishing a save action to a file. fastsignals::signal signalFinishSave; + /// Signal before recomputing the document. fastsignals::signal signalBeforeRecompute; + /// Signal after recomputing the document. fastsignals::signal&)> signalRecomputed; + /// Signal after recomputing an object. fastsignals::signal signalRecomputedObject; - // signal a new opened transaction + /// Signal on a new opened transaction. fastsignals::signal signalOpenTransaction; - // signal a committed transaction + /// Signal on a committed transaction. fastsignals::signal signalCommitTransaction; - // signal an aborted transaction + /// Signal on an aborted transaction. fastsignals::signal signalAbortTransaction; + /// Signal on a skipping a recompute. fastsignals::signal&)> signalSkipRecompute; + /// Signal on finishing restoring an object. fastsignals::signal signalFinishRestoreObject; + /// Signal on a changed property in the property editor. fastsignals::signal signalChangePropertyEditor; + /// Signal on setting a value in an external link. fastsignals::signal signalLinkXsetValue; + // clang-format on - //@} + /// @} // NOLINTEND using PreRecomputeHook = std::function; + + /** @brief Set a hook for before a recompute. + * + * @param[in] hook: The function to be called before a recompute. + */ void setPreRecomputeHook(const PreRecomputeHook& hook); + /// Clear the document of all its administration. void clearDocument(); - /** @name File handling of the document */ - //@{ - /// Save the Document under a new Name - // void saveAs (const char* Name); + /** @name File handling of the document + * @{ + */ + /// Save the document to the file in Property Path bool save(); + + /** + * @brief Save the document to a specified file. + * + * @param[in] file: The file name to save the document to. + */ bool saveAs(const char* file); + + /** + * @brief Save a copy of the document to a specified file. + * + * @param[in] file: The file name to save the copy to. + */ bool saveCopy(const char* file) const; - /// Restore the document from the file in Property Path + + /** + * @brief Restore the document from the file in Property Path. + * + * @param[in] filename: The file name to restore the document from. If nullptr, then + * use the file name in property 'FileName'. + * @param[in] delaySignal: if true, the signals for restored objects are delayed until + * all objects are restored. + * @param[in] objNames: if not empty, only restore the objects with these + * names. This is useful to partially restore a document. If empty, + * restore all objects in the document. + */ void restore(const char* filename = nullptr, bool delaySignal = false, const std::vector& objNames = {}); + + /** + * @brief Called after a document is restored. + * + * This function can be delayed in restore() if `delaySignal` is true. + * + * @param[in] checkPartial: Check if the document is partially loaded. + * + * @returns True if the document is restored. Returning false means that + * the document must have a full reload. + */ bool afterRestore(bool checkPartial = false); - bool afterRestore(const std::vector&, bool checkPartial = false); + + /** + * @brief Called after a document is restored to check a list of objects. + * + * @param[in] checkPartial: Check if the document is partially loaded. + * @param[in] objs: The list of objects to check. + * + * @returns True if the document is restored. Returning false means that + * the document must have a full reload. + */ + bool afterRestore(const std::vector& objs, bool checkPartial = false); + + /// The status of export. enum ExportStatus { NotExporting, Exporting, }; + + /** + * @brief Check if an object is being exported. + * @param[in] obj: The object to check. + * @return The export status. + */ ExportStatus isExporting(const DocumentObject* obj) const; + + /// Get the export info. ExportInfo exportInfo() const; - void setExportInfo(const ExportInfo& info); - void exportObjects(const std::vector&, std::ostream&); - void exportGraphviz(std::ostream&) const; - std::vector importObjects(Base::XMLReader& reader); - /** Import any externally linked objects + + /** + * @brief Set the export info in this document. * - * @param objs: input list of objects. Only objects belonging to this document will + * @param[in] info: The export info to set. + */ + void setExportInfo(const ExportInfo& info); + + /** + *@brief Export a list of objects to a stream. + * + * @param[in] objs: The list of objects to export. + * @param[in, out] out: The output stream to write to. + */ + void exportObjects(const std::vector& objs, std::ostream& out); + + /** + * @brief Write the dependency graph of this document. + * + * The output is in the DOT format of Graphviz. + * + * @param[in, out] out: The output stream to write to. + */ + void exportGraphviz(std::ostream& out) const; + + /** + * @brief Import objects from a stream. + * + * @param[in, out] reader: The XML reader to read from. + * @return The list of imported objects. + */ + std::vector importObjects(Base::XMLReader& reader); + + /** + * @brief Import any externally linked objects + * + * @param[in] objs: input list of objects. Only objects belonging to this document will * be checked for external links. And all found external linked object will be imported * to this document. Link type properties of those input objects will be automatically * reassigned to the imported objects. Note that the link properties of other objects @@ -266,96 +406,144 @@ public: * * @return the list of imported objects */ - std::vector - importLinks(const std::vector& objs = {}); - /// Opens the document from its file name + std::vector importLinks(const std::vector& objs = {}); + + // Opens the document from its file name // void open (void); - /// Is the document already saved to a file? + + /// Check if the document has been saved. bool isSaved() const; - /// Get the document name + + /// Get the document name. const char* getName() const; - /// Get program version the project file was created with + + /// Get program version the project file was created with. const char* getProgramVersion() const; - /** Returned filename + + /** + * @brief Get the filename of the document. * - * For saved document, this will be the content stored in property - * 'Filename'. For unsaved temporary file, this will be the content of - * property 'TransientDir'. + * @return For a saved document, this will be the content stored in + * property 'Filename'. For an unsaved temporary file, this will be the + * content of property 'TransientDir'. */ const char* getFileName() const; - //@} + /// @} void Save(Base::Writer& writer) const override; void Restore(Base::XMLReader& reader) override; - /// returns the complete document memory consumption, including all managed DocObjects and Undo - /// Redo. unsigned int getMemSize() const override; - /** @name Object handling */ - //@{ - /** Add a feature of sType with sName (ASCII) to this document and set it active. - * Unicode names are set through the Label property. - * @param sType the type of created object - * @param pObjectName if nonNULL use that name otherwise generate a new unique name based on the - * \a sType - * @param isNew if false don't call the \c DocumentObject::setupObject() callback (default - * is true) - * @param viewType override object's view provider name - * @param isPartial indicate if this object is meant to be partially loaded + /** @name Object handling + * @{ + */ + + /** + * @brief Add an object of given type to the document. + * + * Add an object of given type with @p pObjectName that should be ASCII to + * this document and set it active. Unicode names are set through the + * Label property. + * + * @param[in] sType the type of created object + * @param[in] pObjectName if `nullptr` generate a new unique name based on @p + * sType, otherwise use this name. + * @param[in] isNew if false don't call the DocumentObject::setupObject() + * callback (default is true) + * @param[in] viewType override the object's view provider name + * @param[in] isPartial indicate if this object is meant to be partially loaded + * + * @return The newly added object. */ DocumentObject* addObject(const char* sType, const char* pObjectName = nullptr, bool isNew = true, const char* viewType = nullptr, bool isPartial = false); - //@{ - /** Add a feature of T type with sName (ASCII) to this document and set it active. - * Unicode names are set through the Label property. - * @param pObjectName if nonNULL use that name otherwise generate a new unique name based on the - * \a sType - * @param isNew if false don't call the \c DocumentObject::setupObject() callback (default - * is true) - * @param viewType override object's view provider name - * @param isPartial indicate if this object is meant to be partially loaded + + /** + * @brief Add an object of a given type to the document. + * + * Add an object of of a given type with @p pObjectName that should be + * ASCII to this document and set it active. Unicode names are set through + * the Label property. + * + * @tparam T The type of created object. + * @param[in] pObjectName if `nullptr` generate a new unique name based on @p + * T, otherwise use this name. + * @param[in] isNew if `false don't call the DocumentObject::setupObject() + * callback (default * is true) + * @param[in] viewType override object's view provider name + * @param[in] isPartial indicate if this object is meant to be partially loaded + * + * @return The newly added object. */ template T* addObject(const char* pObjectName = nullptr, bool isNew = true, const char* viewType = nullptr, bool isPartial = false); - /** Add an array of features of the given types and names. - * Unicode names are set through the Label property. - * @param sType The type of created object - * @param objectNames A list of object names - * @param isNew If false don't call the \c DocumentObject::setupObject() callback (default - * is true) + + /** + * @brief Add multiple objects of a given type to the document. + * + * Add multiple objects of a given type with @p objectNames that should be + * ASCII to this document. Unicode names are set through the Label + * property. + * + * @param[in] sType The type of created object + * @param[in] objectNames A list of object names + * @param[in] isNew If false don't call the DocumentObject::setupObject() + * callback (default is true) */ std::vector addObjects(const char* sType, const std::vector& objectNames, bool isNew = true); - /// Remove a feature out of the document - void removeObject(const char* sName); - /** Add an existing feature with sName (ASCII) to this document and set it active. - * Unicode names are set through the Label property. - * This is an overloaded function of the function above and can be used to create - * a feature outside and add it to the document afterwards. - * \note The passed feature must not yet be added to a document, otherwise an exception - * is raised. + + /** + * @brief Remove an object from the document. + * + * @param[in] sName The name of the object to remove. */ - void addObject(DocumentObject*, const char* pObjectName = nullptr); + void removeObject(const char* sName); - /// returns whether this is actually contains the DocumentObject. - /// Testing the DocumentObject's pDoc pointer is not sufficient because the object - /// removeObject and _removeObject leave _pDoc unchanged - bool containsObject(const DocumentObject*) const; + /** + * @brief Add an existing object to the document. + * + * Add an existing feature with @p pObjectName (ASCII) to this document and set it active. + * Unicode names are set through the Label property. + * + * This is an overloaded function of the function above and can be used to + * create a feature outside and add it to the document afterwards. + * + * @param[in] obj The object to add. + * @param[in] name if `nullptr` generate a new unique name based on @p + * this name. + * + * @throws Base::RuntimeError If the object is already in the document. + */ + void addObject(DocumentObject* obj, const char* name = nullptr); - /** Copy objects from another document to this document + /** + * @brief Check whether the document contains a document object. * - * @param objs - * @param recursive: if true, then all objects this object depends on are - * copied as well. By default \a recursive is false. + * @note Testing the @c DocumentObject's @c pDoc pointer is not sufficient + * because removeObject() leaves @c _pDoc unchanged. * - * @param returnAll: if true, return all copied objects including those + * @param[in] pcObject The object to check. + * + * @return Returns true if the object is in this document, false otherwise. + */ + bool containsObject(const DocumentObject* pcObject) const; + + /** + * @brief Copy objects from another document to this document. + * + * @param[in] objs The objects to copy. + * @param[in] recursive if true, then all objects this object depends on are + * copied as well. By default @a recursive is false. + * + * @param[in] returnAll: if true, return all copied objects including those * auto included by recursive searching. If false, then only return the * copied object corresponding to the input objects. * @@ -364,250 +552,639 @@ public: std::vector copyObject(const std::vector& objs, bool recursive = false, bool returnAll = false); - /** Move an object from another document to this document - * If \a recursive is true then all objects this object depends on - * are moved as well. By default \a recursive is false. - * Returns the moved object itself or 0 if the object is already part of this - * document.. + + /** + * @brief Move an object from another document to this document. + * + * @param[in] obj The object to move. + * @param[in] recursive If true then all objects this object depends on + * are moved as well. By default @a recursive is false. + * + * @returns The moved object itself or `nullptr` if the object is already part of this + * document. */ DocumentObject* moveObject(DocumentObject* obj, bool recursive = false); - /// Returns the active Object of this document + + /// Get the active Object of this document. DocumentObject* getActiveObject() const; - /// Returns a Object of this document + + /** + * @brief Get the object with the given name. + * + * @param[in] Name The name of the object to get. + * + * @return The document object with the given name or `nullptr` if no such + * object exists. + */ DocumentObject* getObject(const char* Name) const; - /// Returns a Object of this document by its id + + /** + * @brief Get the object with the given id. + * + * @param[in] id The id of the object to get. + * @return The document object with the given id or `nullptr` if no such + * object exists. + */ DocumentObject* getObjectByID(long id) const; - /// Returns true if the DocumentObject is contained in this document + + /** + * @brief Check whether the object is in this document. + * + * @param[in] pFeat The object to check. + * @return Returns true if the object is in this document, false otherwise. + */ bool isIn(const DocumentObject* pFeat) const; - /// Returns a Name of an Object or 0 - const char *getObjectName(const DocumentObject* pFeat) const; - /// Returns a Name for a new Object or empty if proposedName is null or empty. + + /** + * @brief Get the name of an object. + * + * @param[in] pFeat The object to get the name of. + * + * @return The name of the object or `nullptr` if the object is not in this document. + */ + const char* getObjectName(const DocumentObject* pFeat) const; + + /** + * @brief Get a unique name for an object given a proposed name. + * + * @param[in] proposedName The proposed name for the object. + * + * @return A unique name for the object or an empty string if the proposed + * name is empty. + */ std::string getUniqueObjectName(const char* proposedName) const; - /// Returns a name different from any of the Labels of any objects in this document, based on the given modelName. - std::string getStandardObjectLabel(const char* modelName, int d) const; - /// Determine if a given DocumentObject Name and a proposed Label are based on the same base name + + /** + * @brief Get a unique label for an object. + * + * The name is different from any of the labels of any object in this + * document, based on the given modelname. + * + * @param[in] modelName The base name to use for generating the label. + * @param[in] digitCount The number of digits to use for the numeric suffix. + * + * @return A unique label for the object. + */ + std::string getStandardObjectLabel(const char* modelName, int digitCount) const; + + /** + * @brief Check if an object name and a label have the same base. + * + * Determine if a given DocumentObject Name and a proposed Label are based + * on the same base name. + * + * @param[in] name The object name. + * @param[in] label The proposed label. + * + * @return Returns true if the base names are the same, false otherwise. + */ bool haveSameBaseName(const std::string& name, const std::string& label); - /// Returns a list of document's objects including the dependencies + + /// Get a list of the document's objects including the dependencies. std::vector getDependingObjects() const; - /// Returns a list of all Objects + + /// Get a list of all document objects. const std::vector& getObjects() const; + + /** + * @brief Get all objects of a given type. + * + * @param[in] typeId The type to search for. + * @return A vector of objects of the given type. + */ std::vector getObjectsOfType(const Base::Type& typeId) const; + + /** + * @brief Get all objects of any of the given types. + * + * @param[in] types The types to search for. + * @return A vector of objects of any of the given types. + */ std::vector getObjectsOfType(const std::vector& types) const; - /// Returns all object with given extensions. If derived=true also all objects with extensions - /// derived from the given one + + /** + * @brief Get all objects with a given extension. + * + * @param[in] typeId The extension type to search for. + * @param[in] derived If true, also include objects with extensions + * derived from the given one. + * + * @return A vector of objects with the given extension. + */ std::vector getObjectsWithExtension(const Base::Type& typeId, bool derived = true) const; + + /** + * @brief Find objects by type, name, and label. + * + * Find the objects that match all criteria. It is possible to ignore the + * name or label. + * + * @param[in] typeId The type to search for. + * @param[in] objname The name of the object to search for, or `nullptr` to + * ignore searching for the name. + * @param[in] label The label of the object to search for, or `nullptr` to + * ignore searching for the label. + * + *@return A vector of objects matching the given criteria. + */ std::vector findObjects(const Base::Type& typeId, const char* objname, const char* label) const; - /// Returns an array with the correct types already. + + /** + * @brief Get all objects of a given type. + * + * @tparam T The type to search for. + * @return A vector of objects of the given type. + */ template inline std::vector getObjectsOfType() const; + + /** + * @brief Count the number of objects of a given type. + * + * @tparam T The type to search for. + * @return The number of objects of the given type. + */ template inline int countObjectsOfType() const; - int countObjectsOfType(const char* typeName) const; - /// get the number of objects in the document - int countObjects() const; - //@} - /** @name methods for modification and state handling - */ - //@{ - /// Remove all modifications. After this call The document becomes Valid again. - void purgeTouched(); - /// check if there is any touched object in this document - bool isTouched() const; - /// check if there is any object must execute in this document - bool mustExecute() const; - /// returns all touched objects - std::vector getTouched() const; - /// set the document to be closable, this is on by default. - void setClosable(bool); - /// check whether the document can be closed - bool isClosable() const; - /// set the document to autoCreated, this is off by default. - void setAutoCreated(bool); - /// check whether the document is autoCreated. - bool isAutoCreated() const; - /** Recompute touched features and return the number of recalculated features + /** + * @brief Count the number of objects of a given type. * - * @param objs: specify a sub set of objects to recompute. If empty, then - * all object in this document is checked for recompute - * @param force - * @param hasError - * @param options + * @param[in] typeName The name of the type to search for. + * @return The number of objects of the given type. + */ + int countObjectsOfType(const char* typeName) const; + + /// Get the number of objects in the document + int countObjects() const; + /// @} + + /** @name Methods for modification and state handling. + * @{ + */ + + /** + * @brief Remove all modifications. + * + * After this call The document becomes Valid again. + */ + void purgeTouched(); + + /// Check if there is any touched object in this document. + bool isTouched() const; + + /// Check if there is any object must execute in this document. + bool mustExecute() const; + + /// Get all touched objects. + std::vector getTouched() const; + + /** + * @brief Set the document to be closable. + * + * This is on by default. + */ + void setClosable(bool c); + + /// Check whether the document can be closed. + bool isClosable() const; + + /// Set the document to autoCreated, this is off by default. + void setAutoCreated(bool); + + /// Check whether the document is autoCreated. + bool isAutoCreated() const; + + /** + * @brief Recompute touched features. + * + * @param[in] objs The subset of objects to recompute. If empty, then all + * object in this document are checked to be recomputed. + * + * @param[in] force If true, force a recompute even if the document is + * marked to skip recomputes. + * @param[out] hasError If not `nullptr`, set to true if there was any error. + * @param[in] options A bitmask of DependencyOption. + * + * @returns The number of objects recomputed. */ int recompute(const std::vector& objs = {}, bool force = false, bool* hasError = nullptr, int options = 0); - /// Recompute only one feature - bool recomputeFeature(DocumentObject* Feat, bool recursive = false); - /// get the text of the error of a specified object - const char* getErrorDescription(const DocumentObject*) const; - /// return the status bits - bool testStatus(Status pos) const; - /// set the status bits - void setStatus(Status pos, bool on); - //@} - - /** @name methods for the UNDO REDO and Transaction handling + /** + * @brief Recompute a single object. * - * Introduce a new concept of transaction ID. Each transaction must be - * unique inside the document. Multiple transactions from different - * documents can be grouped together with the same transaction ID. + * @param[in] Feat The object to recompute. + * @param[in] recursive If true, then all objects depending on this object + * are recomputed as well. * - * When undo, Gui component can query getAvailableUndo(id) to see if it is - * possible to undo with a given ID. If there more than one undo - * transactions, meaning that there are other transactions before the given - * ID. The Gui component shall ask user if they want to undo multiple steps. - * And if the user agrees, call undo(id) to unroll all transaction before - * and including the one with the given ID. Same applies for redo. - * - * The new transaction ID describe here is fully backward compatible. - * Calling the APIs with a default id=0 gives the original behavior. + * @return True if the object was recomputed, false if there was an error. + */ + bool recomputeFeature(DocumentObject* Feat, bool recursive = false); + + /** + * @brief Get the text of the error for a specified object. + * @param[in] Obj The object to get the error text for. + * + * @return The error text, or `nullptr` if there is no error. + */ + const char* getErrorDescription(const DocumentObject* Obj) const; + + /** + * @brief Get the status of this document for a given status bit. + * + * @param[in] pos The status bit to check. + * @return The status of the given bit. + */ + bool testStatus(Status pos) const; + + /** + * @brief Set or unset a status bit of this document. + * + * @param[in] pos The status bit to set or unset. + * @param[in] on If true, set the bit, if false, unset the bit. + */ + void setStatus(Status pos, bool on); + /// @} + + + /** @name Methods for the Undo, Redo, and Transaction handling. + * + * The concept of a transaction ID ensures that each transaction is unique + * inside the document. Multiple transactions from different documents can + * be grouped together with the same transaction ID. + * + * When undoing, a Gui component can query getAvailableUndo(id) to see if + * it is possible to undo with a given ID. If there are more than one undo + * transactions, this means that there are other transactions before the + * given ID. The Gui component shall ask the user if they want to undo + * multiple steps. And if the user agrees, calling undo(id) unrolls all + * transactions before and including the one with the given ID. The same + * applies for redo. + * + * The transaction ID described here is fully backward compatible. Calling + * the APIs with a default id=0 gives the original behavior. + * + * @{ + */ + + /** + * @brief Set the level of Undo/Redo. + * + * A mode of 0 disables Undo/Redo completely, while a nonzero value turns + * it on. + * + * @param[in] iMode The Undo/Redo mode. */ - //@{ - /// switch the level of Undo/Redo void setUndoMode(int iMode); - /// switch the level of Undo/Redo + + /// Get the Undo/Redo mode. int getUndoMode() const; - /// switch the transaction mode + + /// Set the transaction mode. void setTransactionMode(int iMode); - /** Open a new command Undo/Redo, an UTF-8 name can be specified + + /** + * @brief Open a new command Undo/Redo. * - * @param name: transaction name + * A UTF-8 name can be specified. * - * This function calls Application::setActiveTransaction(name) instead - * to setup a potential transaction which will only be created if there is + * @param[in] name The transaction name. + * + * This function calls Application::setActiveTransaction(name) instead of + * setup a potential transaction that will only be created if there are * actual changes. */ void openTransaction(const char* name = nullptr); - /// Rename the current transaction if the id matches - void renameTransaction(const char* name, int id) const; - /// Commit the Command transaction. Do nothing If there is no Command transaction open. - void commitTransaction(); - /// Abort the actually running transaction. - void abortTransaction() const; - /// Check if a transaction is open - bool hasPendingTransaction() const; - /// Return the undo/redo transaction ID starting from the back - int getTransactionID(bool undo, unsigned pos = 0) const; - /// Check if a transaction is open and its list is empty. - /// If no transaction is open true is returned. - bool isTransactionEmpty() const; - /// Set the Undo limit in Byte! - void setUndoLimit(unsigned int UndoMemSize = 0); - /// Returns the actual memory consumption of the Undo redo stuff. - unsigned int getUndoMemSize() const; - /// Set the Undo limit as stack size - void setMaxUndoStackSize(unsigned int UndoMaxStackSize = 20); // NOLINT - /// Set the Undo limit as stack size - unsigned int getMaxUndoStackSize() const; - /// Remove all stored Undos and Redos - void clearUndos(); - /// Returns the number of stored Undos. If greater than 0 Undo will be effective. - int getAvailableUndos(int id = 0) const; - /// Returns a list of the Undo names - std::vector getAvailableUndoNames() const; - /// Will UNDO one step, returns False if no undo was done (Undos == 0). - bool undo(int id = 0); - /// Returns the number of stored Redos. If greater than 0 Redo will be effective. - int getAvailableRedos(int id = 0) const; - /// Returns a list of the Redo names. - std::vector getAvailableRedoNames() const; - /// Will REDO one step, returns False if no redo was done (Redos == 0). - bool redo(int id = 0); - /// returns true if the document is in an Transaction phase, e.g. currently performing a - /// redo/undo or rollback - bool isPerformingTransaction() const; - /// \internal add or remove property from a transactional object - void addOrRemovePropertyOfObject(TransactionalObject*, const Property* prop, bool add); - void renamePropertyOfObject(TransactionalObject*, const Property* prop, const char* newName); - //@} - /** @name dependency stuff */ - //@{ - /// write GraphViz file + /** + * @brief Rename the current transaction. + * + * Rename the current transaction if it matches the given ID. + * + * @param[in] name The new name of the transaction. + * @param[in] id The transaction ID to match. + */ + void renameTransaction(const char* name, int id) const; + + /** + * @brief Commit the Command transaction. + * + * Do nothing If there is no Command transaction open. + */ + void commitTransaction(); + + /// Abort the currently running transaction. + void abortTransaction() const; + + /// Check whether a transaction is open. + bool hasPendingTransaction() const; + + /** + * @brief Get the undo or redo transaction ID. + * + * Get the transaction ID starting from the back. + * + * @param[in] undo If true, get the undo transaction ID, if false get the + * redo transaction ID. + * @param[in] pos The position from the back, 0 is the last transaction, + * 1 is the one before last, and so on. + * + * @return The transaction ID, or 0 if @p pos is out of range. + */ + int getTransactionID(bool undo, unsigned pos = 0) const; + + /** + * @brief Check if a transaction is open and its list is empty. + * + * @return True if there is a transaction open and its list is empty, true + * if there is no open transaction, false if a transaction is open but its + * list is not empty. + */ + bool isTransactionEmpty() const; + + /** + * @brief Set the undo limit. + * @param[in] UndoMemSize The maximum memory in bytes. + */ + void setUndoLimit(unsigned int UndoMemSize = 0); + + /** + * @brief Get the undo memory size. + * @return The memory used by the undo stack in bytes. + */ + unsigned int getUndoMemSize() const; + + /** + * @brief Set the Undo limit as stack size. + * + * @param[in] UndoMaxStackSize The maximum number of undos to store. + */ + void setMaxUndoStackSize(unsigned int UndoMaxStackSize = 20); // NOLINT + // + /// Get the maximum number of undos that can be stored. + unsigned int getMaxUndoStackSize() const; + + /// Remove all stored undos and redos. + void clearUndos(); + + /** + * @brief Get the number of undos stored. + * + * Get the number of stored for a given transaction ID. If @p id is 0, then + * return the total number of undos stored. If the number returned is greater + * than 0, then this means that an undo will be effective. + * + * @param[in] id The transaction ID to match or 0 to get the total number of undos. + * + * @return The number of undos stored. + */ + int getAvailableUndos(int id = 0) const; + + /// Get a list of the undo names. + std::vector getAvailableUndoNames() const; + + /** + * @brief Undo one or multiple steps. + * + * If @p id is 0, then undo one step. If @p id is not 0, then undo + * multiple steps until the transaction with the given ID is undone. + * + * @param[in] id The transaction ID to match or 0 to undo one step. + * + * @return Returns true if an undo was done, false if no undo was done. + */ + bool undo(int id = 0); + + /** + * @brief Get the number of redos stored. + * + * Get the number of stored redos for a given transaction ID. If @p id is 0, + * then return the total number of redos stored. If the number returned is + * greater than 0, then this means that a redo will be effective. + * + * @param[in] id The transaction ID to match or 0 to get the total number of redos. + * + * @return The number of redos stored. + */ + int getAvailableRedos(int id = 0) const; + + /// Get a list of the redo names. + std::vector getAvailableRedoNames() const; + + /** + * @brief Redo one or multiple steps. + * + * If @p id is 0, then redo one step. If @p id is not 0, then redo + * multiple steps until the transaction with the given ID is redone. + * + * @param[in] id The transaction ID to match or 0 to redo one step. + * + * @return Returns true if a redo was done, false if no redo was done. + */ + bool redo(int id = 0); + + /** + * @brief Check if the document is performing a transaction. + * + * This function returns true if the document is in a transaction phase, + * i.e. currently performing a redo/undo or rollback. + * + * @return True if the document is performing a transaction, false otherwise. + */ + bool isPerformingTransaction() const; + + /** + * @brief Register that a property of an object has changed in a transaction. + * + * @param[in] obj The object whose property has changed. + * @param[in] prop The property that has changed. + * @param[in] add If true, the property was added, if false it was removed. + * + * @warning This function is only for internal use. + */ + + void addOrRemovePropertyOfObject(TransactionalObject* obj, const Property* prop, bool add); + /** + * @brief Register that a property of an object has been renamed in a transaction. + * + * @param[in] obj The object whose property has changed. + * @param[in] prop The property that has changed. + * @param[in] newName The new name of the property. + * + * @warning This function is only for internal use. + */ + void renamePropertyOfObject(TransactionalObject* obj, const Property* prop, const char* newName); + /// @} + + /** @name Dependency items. + * @{ + */ + + /** + * @brief Write the dependency graph of this document. + * + * The dependency graph is in Graphviz DOT format. + * + * @param[in, out] out The output stream to write to. + */ void writeDependencyGraphViz(std::ostream& out); - /// checks if the graph is directed and has no cycles + + /// Checks if the dependency graph is directed and has no cycles. static bool checkOnCycle(); - /// get a list of all objects linking to the given object + + /** + * @brief Get the Inlist of an object. + * + * The Inlist represents the list of objects that have an edge pointing to + * @p me. This means that these objects depend on @p me and that on a + * recompute, @p me should be computed first. + * + * @param[in] me The object to get the Inlist for. + * + * @return The Inlist of the object. + */ std::vector getInList(const DocumentObject* me) const; - /// Option bit flags used by getDepenencyList() + /// The position of option flags used by getDependencyList() enum DependencyOption { - /// Return topological sorted list - DepSort = 1, - /// Do no include object linked by PropertyXLink, as it can handle external link - DepNoXLinked = 2, - /// Raise exception on cycles - DepNoCycle = 4, + DepSort = 1, ///< For a topologically sorted list + DepNoXLinked = 2, ///< Ignore external links + DepNoCycle = 4, ///< Ignore cyclic links }; - /** Get a complete list of all objects the given objects depend on. + + /** + * @brief Get a list of all objects that the given objects depend on. * * This function is defined as static because it accepts objects from * different documents, and the returned list will contain dependent - * objects from all relevant documents + * objects from all relevant documents. * - * @param objs: input objects to query for dependency. - * @param options: See DependencyOption + * @param[in] objs Objects for which we want to find the dependencies. + * @param[in] options A bitmask of DependencyOption. + * + * @return A list of all objects that the given objects depend on. */ static std::vector getDependencyList(const std::vector& objs, int options = 0); + /** + * @brief Get a list of documents that depend on this document. + * + * @param[in] sort If true, the returned list is topologically sorted. + * + * @return A list of documents that depend on this document. + */ std::vector getDependentDocuments(bool sort = true); + + /** + * @brief Get a list of documents that depend on the given documents. + * + * @param[in] docs The documents for which we want to find the dependencies. + * @param[in] sort If true, the returned list is topologically sorted. + * + * @return A list of documents that depend on the given documents. + */ static std::vector getDependentDocuments(std::vector docs, - bool sort); + bool sort); // set Changed // void setChanged(DocumentObject* change); - /// get a list of topological sorted objects (https://en.wikipedia.org/wiki/Topological_sorting) + + /** + * @brief Get a list of topologically sorted objects. + * + * For more information on topological sorting see + * https://en.wikipedia.org/wiki/Topological_sorting. + * + * @return A list of the topologically sorted objects of this document. + */ std::vector topologicalSort() const; - /// get all root objects (objects no other one reference too) + + /** + * @brief Get all root objects in the document. + * + * Root objects are objects that no other objects references. + * + * @return A list of all root objects. + */ std::vector getRootObjects() const; - /// get all tree root objects (objects that are at the root of the object tree) + + /** + * @brief Get all tree root objects in the document. + * + * These are objects that are at the root of the object tree. + * + * @return A list of all tree root objects. + */ std::vector getRootObjectsIgnoreLinks() const; - /// get all possible paths from one object to another following the OutList + + /** + * @brief Get all possible paths from one object to another. + * + * This functions follows the outlist to find all paths from @p from to @p to. + * + * @param[in] from The object to start from. + * @param[in] to The object to end at. + * + * @return A vector of object lists, each list is one path from @p from to + * @p to. + */ std::vector> getPathsByOutList(const DocumentObject* from, const DocumentObject* to) const; - //@} + /// @} - /** Called by a property during save to store its StringHasher + /** + * @brief Add a string hasher to the document. + * + * This function is called by a property during save to store its + * StringHasher. The StringHasher object is designed to be shared among + * multiple objects. We must not save duplicate copies of the same hasher, + * and must be able to restore with the same sharing relationship. This + * function returns whether the hasher has been added before by other + * objects, and the index of the hasher. If the hasher has not been added + * before, the object must save the hasher by calling StringHasher::Save + * + * @param[in] hasher The input hasher. * - * @param hasher: the input hasher * @return Returns a pair. The boolean indicates if the * StringHasher has been added before. The integer is the hasher index. - * - * The StringHasher object is designed to be shared among multiple objects. - * We must not save duplicate copies of the same hasher, and must be - * able to restore with the same sharing relationship. This function returns - * whether the hasher has been added before by other objects, and the index - * of the hasher. If the hasher has not been added before, the object must - * save the hasher by calling StringHasher::Save */ std::pair addStringHasher(const StringHasherRef& hasher) const; - /** Called by property to restore its StringHasher + /** + * @brief Get a string hasher from the document given an index. * - * @param index: the index previously returned by calling addStringHasher() - * during save. Or if is negative, then return document's own string hasher. + * This function is called by a property to restore its StringHasher. The + * caller is responsible for restoring the hasher if the caller is the + * first owner of the hasher, i.e. if addStringHasher() returns true during + * save. * - * @return Return the resulting string hasher. + * @param[in] index The index previously returned by calling + * addStringHasher() during save. Or if the index is negative, return the + * document's own string hasher. * - * The caller is responsible for restoring the hasher if the caller is the first - * owner of the hasher, i.e. if addStringHasher() returns true during save. + * @return Return the resulting string hasher or a new one if the index is + * not found. */ StringHasherRef getStringHasher(int index = -1) const; - /** Return the links to a given object + /** + * @brief Get the links to a given object. * - * @param links: holds the links found - * @param obj: the linked object. If NULL, then all links are returned. - * @param options: @sa GetLinkOption - * @param maxCount: limit the number of links returned, 0 means no limit - * @param objs: optional objects to search for, if empty, then all objects + * Get the links to an object that is contained in this document. If the + * object is `nullptr`, then all links in this document are returned. + * + * @param[in, out] links Holds the links found + * @param[in] obj The linked object. If `nullptr`, then all links are returned. + * @param[in] options: A bitmask of type GetLinkOption + * @param[in] maxCount: limit the number of links returned, 0 means no limit + * @param[in] objs: optional objects to search for, if empty, then all objects * of this document are searched. */ void getLinksTo(std::set& links, @@ -616,18 +1193,38 @@ public: int maxCount = 0, const std::vector& objs = {}) const; - /// Check if there is any link to the given object + /** + * @brief Check if there is any link to the given object. + * + * @param[in] obj The linked object. + * + * @return True if there is at least one link to the given object, false otherwise. + */ bool hasLinksTo(const DocumentObject* obj) const; - /// Called by objects during restore to ask for recompute + /** + * @brief Mark an object for recompute during restore. + * + * Called by objects during restore to ask for recompute. + * + * @param[in] obj The object to mark for recompute. + */ void addRecomputeObject(DocumentObject* obj); + /// Get the old label of an object before it was changed. const std::string& getOldLabel() const { return oldLabel; } - /// Function called to signal that an object identifier has been renamed + /** + * @brief Rename object identifiers. + * + * @param[in] paths A map of old to new object identifiers. + * @param[in] selector A function that returns true for objects that should + * be renamed, and false for objects that should be skipped. By default + * all objects are renamed. + */ void renameObjectIdentifiers( const std::map& paths, const std::function& selector = @@ -639,57 +1236,160 @@ public: std::string getFullName() const override; - /// Indicate if there is any document restoring/importing + /// Check if there is any document restoring/importing. static bool isAnyRestoring(); + /// Register a new label. void registerLabel(const std ::string& newLabel); + /// Unregister a label. void unregisterLabel(const std::string& oldLabel); + /// Check if a label exists. bool containsLabel(const std::string& label); + /// Create a unique label based on the given modelLabel. std::string makeUniqueLabel(const std::string& modelLabel); friend class Application; - /// because of transaction handling + // because of transaction handling friend class TransactionalObject; friend class DocumentObject; friend class Transaction; friend class TransactionDocumentObject; - /// Destruction ~Document() override; protected: - /// Construction + /** + * @brief Construct a new Document object. + * + * @param[in] documentName The name of the document. The default value is + * the empty string. + */ explicit Document(const char* documentName = ""); - void _removeObject(DocumentObject* pcObject, RemoveObjectOptions options = RemoveObjectOption::DestroyOnRollback | RemoveObjectOption::PreserveChildrenVisibility); + /** + *@brief Remove an object from the document. + * + * @param[in] pcObject The object to remove. + * @param[in] options A bitmask of RemoveObjectOptions. + */ + void _removeObject(DocumentObject* pcObject, + RemoveObjectOptions options = RemoveObjectOption::DestroyOnRollback + | RemoveObjectOption::PreserveChildrenVisibility); + + /** + * @brief Add an object to the document. + * + * @param[in] pcObject The object to add. + * @param[in] pObjectName if `nullptr` generate a new unique name based on @p + * pcObject type, otherwise use this name. + * @param[in] options A bitmask of AddObjectOptions. + * @param[in] viewType Override object's view provider name. + */ void _addObject(DocumentObject* pcObject, const char* pObjectName, AddObjectOptions options = AddObjectOption::ActivateObject, const char* viewType = nullptr); - /// checks if a valid transaction is open + + /** + * @brief Check if a valid transaction is open. + * + * Check if a valid transaction is open, and if not, open a new + * transaction. With the arguments we can check what kind of transaction + * we expect to be open. + * + * @param[in] pcDelObj The object being deleted, or `nullptr` if no + * object is being deleted. + * @param[in] What The property being changed, or `nullptr` if no property + * is being changed. + * @param[in] line The line number where this function is called. + */ void _checkTransaction(DocumentObject* pcDelObj, const Property* What, int line); + + /** + * @brief Break dependencies of an object. + * + * Break all dependencies of an object, i.e. remove all links to and from + * the object. + * + * @param[in] pcObject The object to break dependencies for. + * @param[in] clear If true, then also clear all link properties of @p pcObject. + */ void breakDependency(DocumentObject* pcObject, bool clear); + + /** + * @brief Read objects from an XML reader. + * + * @param[in, out] reader The XML reader to read from. + * @return A vector of objects read. + */ std::vector readObjects(Base::XMLReader& reader); - void writeObjects(const std::vector&, Base::Writer& writer) const; + + /** + * @brief Write objects to an XML writer. + * + * @param[in] objs The objects to write. + * @param[in, out] writer The XML writer to write to. + */ + void writeObjects(const std::vector& objs, Base::Writer& writer) const; + + /** + * @brief Save the document to a file. + * + * @param[in] filename The name of the file to save to. + * @return True if the document was saved successfully. + * @throw Base::FileException if the file could not be written. + */ bool saveToFile(const char* filename) const; + + /** + * @brief Count the object of a given type. + * + * @param[in] typeId The type to count. + * @return The number of objects of the given type. + */ int countObjectsOfType(const Base::Type& typeId) const; void onBeforeChange(const Property* prop) override; void onChanged(const Property* prop) override; - /// callback from the Document objects before property will be changed + + /** + * @brief Notify the document that a property is about to be changed. + * + * @param[in] Who The object whose property is about to be changed. + * @param[in] What The property that is about to be changed. + */ void onBeforeChangeProperty(const TransactionalObject* Who, const Property* What); - /// callback from the Document objects after property was changed + + /** + * @brief Notify the document that a property has changed. + * + * @param[in] Who The object whose property has changed. + * @param[in] What The property that has changed. + */ void onChangedProperty(const DocumentObject* Who, const Property* What); - /// helper which Recompute only this feature - /// @return 0 if succeeded, 1 if failed, -1 if aborted by user. + + /** + * @brief Recompute a single object. + * @param[in] Feat The object to recompute. + * @return 0 if succeeded, 1 if failed, -1 if aborted by user. + */ int _recomputeFeature(DocumentObject* Feat); + + /// Clear the redos. void _clearRedos(); - /// refresh the internal dependency graph - void _rebuildDependencyList( - const std::vector& objs = std::vector()); - + /** + * @brief Get the name of the transient directory for a given UUID and filename. + * + * @param[in] uuid The UUID of the document. + * @param[in] filename The name of the file. + * + * @return The name of the transient directory. + */ std::string getTransientDirectoryName(const std::string& uuid, const std::string& filename) const; - /** Open a new command Undo/Redo, an UTF-8 name can be specified + /** + * @brief Open a new command Undo/Redo. + * + * A UTF-8 name can be specified * * @param name: transaction name * @param id: transaction ID, if 0 then the ID is auto generated. @@ -700,9 +1400,23 @@ protected: * AutoTransaction setting. */ int _openTransaction(const char* name = nullptr, int id = 0); - /// Internally called by Application to commit the Command transaction. + + /** + * @brief Commit the Command transaction. + * + * This method is internally called by Application to commit the Command + * transaction. + * + * @param notify If true, notify the application to close the transaction. + */ void _commitTransaction(bool notify = false); - /// Internally called by Application to abort the running transaction. + + /** + * @brief Abort the running transaction. + * + * This method is internally called by Application to abort the running + * transaction. + */ void _abortTransaction(); private: diff --git a/src/App/DocumentObject.cpp b/src/App/DocumentObject.cpp index 938ceb8738..fcecdf15f0 100644 --- a/src/App/DocumentObject.cpp +++ b/src/App/DocumentObject.cpp @@ -206,13 +206,6 @@ bool DocumentObject::recomputeFeature(bool recursive) return isValid(); } -/** - * @brief Set this document object touched. - * Touching a document object does not mean to recompute it, it only means that - * other document objects that link it (i.e. its InList) will be recomputed. - * If it should be forced to recompute a document object then use - * \ref enforceRecompute() instead. - */ void DocumentObject::touch(bool noRecompute) { if (!noRecompute) { @@ -224,10 +217,6 @@ void DocumentObject::touch(bool noRecompute) } } -/** - * @brief Set this document object freezed. - * A freezed document object does not recompute ever. - */ void DocumentObject::freeze() { StatusBits.set(ObjectStatus::Freeze); @@ -250,10 +239,6 @@ void DocumentObject::freeze() } } -/** - * @brief Set this document object unfreezed. - * A freezed document object does not recompute ever. - */ void DocumentObject::unfreeze(bool noRecompute) { StatusBits.reset(ObjectStatus::Freeze); @@ -271,31 +256,16 @@ void DocumentObject::unfreeze(bool noRecompute) touch(noRecompute); } -/** - * @brief Check whether the document object is touched or not. - * @return true if document object is touched, false if not. - */ bool DocumentObject::isTouched() const { return ExpressionEngine.isTouched() || StatusBits.test(ObjectStatus::Touch); } -/** - * @brief Enforces this document object to be recomputed. - * This can be useful to recompute the feature without - * having to change one of its input properties. - */ void DocumentObject::enforceRecompute() { touch(false); } -/** - * @brief Check whether the document object must be recomputed or not. - * This means that the 'Enforce' flag is set or that \ref mustExecute() - * returns a value > 0. - * @return true if document object must be recomputed, false if not. - */ bool DocumentObject::mustRecompute() const { if (StatusBits.test(ObjectStatus::Freeze)) { @@ -646,7 +616,7 @@ bool DocumentObject::isInOutListRecursive(DocumentObject* linkTo) const } std::vector> -DocumentObject::getPathsByOutList(App::DocumentObject* to) const +DocumentObject::getPathsByOutList(const App::DocumentObject* to) const { return _pDoc->getPathsByOutList(this, to); } @@ -1210,33 +1180,16 @@ void DocumentObject::Save(Base::Writer& writer) const App::ExtensionContainer::Save(writer); } -/** - * @brief Associate the expression \expr with the object identifier \a path in this document object. - * @param path Target object identifier for the result of the expression - * @param expr Expression tree - */ - void DocumentObject::setExpression(const ObjectIdentifier& path, std::shared_ptr expr) { ExpressionEngine.setValue(path, std::move(expr)); } -/** - * @brief Clear the expression of the object identifier \a path in this document object. - * @param path Target object identifier - */ - void DocumentObject::clearExpression(const ObjectIdentifier& path) { setExpression(path, std::shared_ptr()); } -/** - * @brief Get expression information associated with \a path. - * @param path Object identifier - * @return Expression info, containing expression and optional comment. - */ - const PropertyExpressionEngine::ExpressionInfo DocumentObject::getExpression(const ObjectIdentifier& path) const { @@ -1250,13 +1203,6 @@ DocumentObject::getExpression(const ObjectIdentifier& path) const } } -/** - * @brief Invoke ExpressionEngine's renameObjectIdentifier, to possibly rewrite expressions using - * the \a paths map with current and new identifiers. - * - * @param paths - */ - void DocumentObject::renameObjectIdentifiers( const std::map& paths) { diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index 6414b05842..6b0ac2bc9c 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -56,42 +56,65 @@ class DocumentObjectPy; class Expression; // clang-format off +/// Defines the position of the status bits for document objects. enum ObjectStatus { - Touch = 0, - Error = 1, - New = 2, - Recompute = 3, // set when the object is currently being recomputed - Restore = 4, - Remove = 5, - PythonCall = 6, - Destroy = 7, - Enforce = 8, - Recompute2 = 9, // set when the object is being recomputed in the second pass - PartialObject = 10, - PendingRecompute = 11, // set by Document, indicating the object is in recomputation queue - ObjImporting = 13, // Mark the object as importing - NoTouch = 14, // no touch on any property change - GeoExcluded = 15, // mark as a member but not claimed by GeoFeatureGroup - Expand = 16, // indicate the object's tree item expansion status - NoAutoExpand = 17, // disable tree item auto expand on selection for this object - PendingTransactionUpdate = 18, // mark that the object expects a call to onUndoRedoFinished() after transaction is finished. - RecomputeExtension = 19, // mark the object to recompute its extensions - TouchOnColorChange = 20, // inform view provider touch object on color change - Freeze = 21, // do not recompute ever + Touch = 0, ///< Whether the object is marked as 'touched'. + Error = 1, ///< Whether the object is in an error state. + New = 2, ///< Whether the object is newly created. + Recompute = 3, ///< Whether the object is currently recomputing. + Restore = 4, ///< Whether the object is currently restoring from a file. + Remove = 5, ///< Whether the object is about to be removed from the document. + PythonCall = 6, ///< Unused flag. + Destroy = 7, ///< Whether the object is about to be destroyed. + Enforce = 8, ///< Whether the object is forced to be recomputed. + Recompute2 = 9, ///< Whether the object is going to be recomputed in the second pass. + PartialObject = 10, ///< Whether this is a partially loaded object. + PendingRecompute = 11, ///< Whether the object is in the recomputation queue, set by Document. + ObjImporting = 13, ///< Whether the object is being imported. + NoTouch = 14, ///< Whether the object should be touched on a property change. + GeoExcluded = 15, ///< Whether the object is a member but not claimed by a GeoFeatureGroup. + Expand = 16, ///< Whether the object is expanded in the tree view. + NoAutoExpand = 17, ///< Whether the object should have auto expansion disabled on selection. + /// Whether the object expects a call to onUndoRedoFinished() after transaction is finished. + PendingTransactionUpdate = 18, + RecomputeExtension = 19, ///< Whether the extensions of this object should be recomputed. + TouchOnColorChange = 20, ///< Whether the object should be touched on color change. + Freeze = 21, ///< Whether the object is frozen and is excluded from recomputation. }; // clang-format on -/** Return object for feature execution +/** + * @brief A return object for feature execution. + * + * This class is used to return the result of a feature execution and is + * returned by the recompute(), execute() and executeExtension() methods. + * + * Note that these functions return a pointer to an instance and that + * DocumentObject::StdReturn is a static instance of this class that has as + * value `nullptr`. Returning @ref DocumentObject::StdReturn "StdReturn" + * indicates that the execution was successful and that no further action is + * needed. */ class AppExport DocumentObjectExecReturn { public: + /** + * @brief Construct a DocumentObjectExecReturn object. + * + * Constructing an object of this type means that the execution was + * unsuccessful and the reason is given in the `Why` member. + * + * @param[in] sWhy The reason for the failed execution. + * @param[in] WhichObject The object that caused the failed execution. + */ explicit DocumentObjectExecReturn(const std::string& sWhy, DocumentObject* WhichObject = nullptr) : Why(sWhy) , Which(WhichObject) {} + + /// @copydoc DocumentObjectExecReturn::DocumentObjectExecReturn(const std::string&, DocumentObject*) explicit DocumentObjectExecReturn(const char* sWhy, DocumentObject* WhichObject = nullptr) : Which(WhichObject) { @@ -100,15 +123,19 @@ public: } } + /// The reason for the failed execution. std::string Why; + + /// The object that caused the failed execution. DocumentObject* Which; }; /** * @brief %Base class of all objects handled in the @ref App::Document "Document". - * @ingroup DocObject - * @details For a more high-level overview see topic @ref DocObject "Document Object". + * @ingroup DocumentObjectGroup + * + * For a more high-level overview see topic @ref DocumentObjectGroup "Document Object". */ class AppExport DocumentObject: public App::TransactionalObject { @@ -120,317 +147,647 @@ private: std::vector readOnlyProperties; public: + /** + * @name Static properties + * @{ + */ + + /// A property for the label of the document object. PropertyString Label; + + /// A property for the description of the document object. PropertyString Label2; + + /// A property that manages the expressions in the document object. PropertyExpressionEngine ExpressionEngine; - /// Allow control visibility status in App name space + /// A property that controls the visibility status in App name space. PropertyBool Visibility; + /// @} + /** + * @name Signals for property change + * @{ + */ // clang-format off - /// signal before changing a property of this object + /** + * @brief Signal that the property of this object is about to change. + * + * Subscribers will get the current object and the property about to be modified. + * + * @param[in] obj The document object whose property is changing. + * @param[in] prop The property that is about to change. + */ fastsignals::signal signalBeforeChange; - /// signal on changed property of this object + + /** + * @brief Signal that the property of this object has changed. + * + * Subscribers will get the current object and the property that has changed. + * + * @param[in] obj The document object whose property just changed. + * @param[in] prop The property that was changed. + */ fastsignals::signal signalChanged; - /// signal on changed property of this object before document scoped signalChangedObject + + /** + * @brief Emitted immediately after any property of this object has changed. + * + * This is fired before the outer "document-scoped" change notification. + * + * @param[in] obj The document object whose property just changed. + * @param[in] prop The property that was changed. + */ fastsignals::signal signalEarlyChanged; + /// @} // clang-format on - /// returns the type name of the ViewProvider + /** + * @brief Get the name of the type of the ViewProvider. + * + * By overriding this function, document objects can indicate what the type + * of the belonging view provider is. Note that this function is + * essentially the boundary between the App and Gui namespaces. + * + * @return The name of the type of the ViewProvider. + */ virtual const char* getViewProviderName() const { return ""; } + /** - * This function is introduced to allow Python feature override its view provider. - * The default implementation just returns \ref getViewProviderName(). + * @brief Get the name of the type of the ViewProvider for Python. + * + * This function is introduced to allow Python feature override its view + * provider. The default implementation just returns \ref + * getViewProviderName(). * * The core will only accept the overridden view provider if it returns * true when calling Gui::ViewProviderDocumentObject::allowOverride(obj). * If not, the view provider will be reverted to the one returned from \ref * getViewProviderName(). + * + * @return The name of the type of the ViewProvider for Python. */ virtual const char* getViewProviderNameOverride() const { return getViewProviderName(); } - /// Constructor DocumentObject(); + ~DocumentObject() override; - /// returns a value that uniquely identifies this DocumentObject. + /// Get a value that uniquely identifies this document object. const char* getDagKey() const; - /// returns the name which is set in the document for this object (not the name property!) + + /// Get the name of the object in the document. const char* getNameInDocument() const; - /// Return the object ID that is unique within its owner document + + /// Get the object ID that is unique within its owner document. long getID() const { return _Id; } - /// returns the name that is safe to be exported to other document + + /** + * @brief Get the export name of the object. + * + * This name is safe to be exported to another document. + * + * @param[in] forced If true, the name is forced to be unique. + * + * @return The export name of the object. + */ std::string getExportName(bool forced = false) const; - /// Return the object full name of the form DocName#ObjName + + /** + * @brief Get the full name of the object. + * + * The name is of the form `DocName#ObjName`. + * + * @return The full name of the object. + */ std::string getFullName() const override; - /// Return the object full label in the form DocName#ObjName + + /** + * @brief Get the full label of the object. + * + * The label is of the form `DocName#Label`. + * + * @return The full label of the object. + */ std::string getFullLabel() const; + bool isAttachedToDocument() const override; const char* detachFromDocument() override; - /// gets the document in which this Object is handled + + /// Get the document of which this object is part. App::Document* getDocument() const; - /** Set the property touched -> changed, cause recomputation in Update() + /** + * @name Status handling + * @{ + */ + + /** + * @brief Set this document object touched. + * + * With @p noRecompute it is possible to make sure that only other document + * objects that link to it (i.e. its InList) will be recomputed. By + * default the document object itself will be recomputed as well. + * + * @param[in] noRecompute If true, the object will not be recomputed when + * touched. */ - //@{ - /// set this document object touched (cause recomputation on dependent features) void touch(bool noRecompute = false); - /// test if this document object is touched + + /** + * @brief Check whether the document object is touched or not. + * + * @return true if document object is touched, false if not. + */ bool isTouched() const; - /// Enforce this document object to be recomputed + + /** + * @brief Enforce this document object to be recomputed. + * + * This can be useful to recompute the feature without + * having to change one of its input properties. + */ void enforceRecompute(); - /// Test if this document object must be recomputed + + /** + * @brief Check whether the document object must be recomputed. + * + * This means that the 'Enforce' flag is set or that \ref mustExecute() + * returns a value > 0. + * + * @return true if document object must be recomputed, false if not. + */ bool mustRecompute() const; - /// reset this document object touched + + /// Reset the touch flags of the document object. void purgeTouched() { StatusBits.reset(ObjectStatus::Touch); StatusBits.reset(ObjectStatus::Enforce); setPropertyStatus(0, false); } - /// set this feature to error + + /// Check whether this document object is in an error state. bool isError() const { return StatusBits.test(ObjectStatus::Error); } + + /// Check whether this document object is in a valid state. bool isValid() const { return !StatusBits.test(ObjectStatus::Error); } - /// remove the error from the object + + /// Remove the error from the object. void purgeError() { StatusBits.reset(ObjectStatus::Error); } - /// returns true if this objects is currently recomputing + + /// Check whether this document object is being recomputed. bool isRecomputing() const { return StatusBits.test(ObjectStatus::Recompute); } - /// returns true if this objects is currently restoring from file + + /// Check whether the document is restoring from file. bool isRestoring() const { return StatusBits.test(ObjectStatus::Restore); } - /// returns true if this objects is currently removed from the document + + /// Check whether this document object is being removed. bool isRemoving() const { return StatusBits.test(ObjectStatus::Remove); } - /// set this document object freezed (prevent recomputation) + + /** + * @brief Set this document object freezed. + * + * A freezed document object is excluded from recomputation. + */ void freeze(); - /// set this document object unfreezed (and touch it) + + /** + * @brief Set this document object unfreezed. + * + * A freezed document object is excluded from recomputation. This function + * enables recomputation and touches the object. + * + * @param[in] noRecompute: if true, the object will not be recomputed when + * touched. + * + * @see touch() + */ void unfreeze(bool noRecompute = false); - /// returns true if this objects is currently freezed + + /// Check whether this document object is being freezed. bool isFreezed() const { return StatusBits.test(ObjectStatus::Freeze); } - /// return the status bits + + /// Get the status bits. unsigned long getStatus() const { return StatusBits.to_ulong(); } + + /** + * @brief Test the status of the document object. + * + * @param[in] pos The status bit to test. + * + * @return true if the status bit is set, false otherwise. + */ bool testStatus(ObjectStatus pos) const { return StatusBits.test(size_t(pos)); } + + /** + * @brief Set the status of the document object for a particular bit. + * + * @param[in] pos The status bit to set. + * @param[in] on The value to set the status bit to. + */ void setStatus(ObjectStatus pos, bool on) { StatusBits.set(size_t(pos), on); } - //@} + /// Check whether the document object is exporting. int isExporting() const; + /// @} - /** Child element handling + /** + * @name Child element handling + * @{ */ - //@{ - /** Set sub-element visibility + + /** @brief Set the visibility of a sub-element. * - * For performance reason, \c element must not contain any further - * sub-elements, i.e. there should be no '.' inside \c element. + * For performance reason, @p element must not contain any further + * sub-elements, i.e. there should be no '.' inside @p element. * * @return -1 if element visibility is not supported, 0 if element is not * found, 1 if success */ virtual int setElementVisible(const char* element, bool visible); - /** Get sub-element visibility + /** @brief Get the visibility of a sub-element. * - * @return -1 if element visibility is not supported or element not found, 0 - * if element is invisible, or else 1 + * @return -1 if element visibility is not supported or if the element is not found, 0 + * if element is invisible, or else 1. */ virtual int isElementVisible(const char* element) const; - /// return true to activate tree view group object handling and element visibility + /// Check whether the object has child elements. virtual bool hasChildElement() const; - //@} + ///@} - /** DAG handling - This part of the interface deals with viewing the document as - a DAG (directed acyclic graph). - */ - //@{ - /// OutList options + /** + * @name DAG handling + * + * This part of the interface deals with viewing the document as a DAG + * (directed acyclic graph). + * + * @{ + */ + + /// Options for computing the OutList enum OutListOption { - /// Do not include link from expression engine - OutListNoExpression = 1, - /// Do not hide any link (i.e. include links with LinkScopeHidden) - OutListNoHidden = 2, - /// Do not include link from PropertyXLink - OutListNoXLinked = 4, + OutListNoExpression = 1, ///< Do not include links from the expression engine. + OutListNoHidden = 2, ///< Do not hide any link (i.e. include links with LinkScopeHidden). + OutListNoXLinked = 4, ///< Do not include links from PropertyXLink properties. }; - /// returns a list of objects this object is pointing to by Links + + /// Get a list of objects this object links to. const std::vector& getOutList() const; + + /** + * @brief Get a list of objects this object links to. + * + * @param[in] option Options for computing the OutList. + * + * @return A vector of objects this object links to. + * + * @see OutListOption for available options. + */ std::vector getOutList(int option) const; + + /** + * @brief Get a list of objects this object links to. + * + * @param[in] option Options for computing the OutList. + * + * @param[in,out] res The vector to fill with the objects this object depends on. + * + * @see OutListOption for available options. + */ void getOutList(int option, std::vector& res) const; - /// returns a list of objects linked by the property - std::vector getOutListOfProperty(App::Property*) const; - /// returns a list of objects this object is pointing to by Links and all further descended - std::vector getOutListRecursive() const; - /// clear internal out list cache - void clearOutListCache() const; - /// get all possible paths from this to another object following the OutList - std::vector> getPathsByOutList(App::DocumentObject* to) const; - /// get all objects link to this object - const std::vector& getInList() const; - /// get all objects link directly or indirectly to this object - std::vector getInListRecursive() const; - /** Get a set of all objects linking to this object, including possible external parent objects + /** + * @brief Get a list of objects that a property links to. * - * @param inSet [out]: a set containing all objects linking to this object. - * @param recursive [in]: whether to obtain recursive in list - * @param inList [in, out]: optional pointer to a vector holding the output + * @param[in] prop The property to query for linked objects. + * @return A vector of objects that the property links to. + */ + std::vector getOutListOfProperty(App::Property* prop) const; + + /** + * @brief Get a list of objects this object links to, recursively. + * + * This function returns all objects that this object links to and all + * further descendants. + * + * @return A vector of objects this object links to, recursively. + */ + std::vector getOutListRecursive() const; + + /// Clear the internal OutList cache. + void clearOutListCache() const; + + /** + * @brief Get all possible paths from this object to another object. + * + * This function returns all paths from this object to the specified object + * by following the OutList. The paths are returned as a vector of lists, + * where each list represents a path from this object to the target object. + * + * @param[in] to The target object to which paths are sought. + * + * @return A vector of lists, where each list contains the objects in a + * path from this object to the target object. + */ + std::vector> getPathsByOutList(const App::DocumentObject* to) const; + + /** @brief Get a list of objects that link to this object. + * + * This function returns a list of objects that directly link to this + * object, i.e. the InList. + * + * @return A reference to the list of objects that depend on this object. + */ + const std::vector& getInList() const; + + /** + * @brief Get a list of objects that link to this object, recursively. + * + * This function returns all objects that link to this object directly and + * indirectly. + * + * @return A vector of objects that link to this object, recursively. + */ + std::vector getInListRecursive() const; + + /** @brief Get a set of all objects linking to this object. + * + * This function returns a set of all objects that link to this object, + * including possible external parent objects. + * + * @param[in,out] inSet A set containing all objects linking to this object. + * @param[in] recursive Whether to obtain the InList recursively. + * @param[in,out] inList An optional pointer to a vector holding the output. * objects, with the furthest linking object ordered last. */ void getInListEx(std::set& inSet, bool recursive, std::vector* inList = nullptr) const; - /** Return a set of all objects linking to this object, including possible external parent - * objects - * @param recursive [in]: whether to obtain recursive in list + + /** + * @brief Get a set of all objects linking to this object. + * + * This function returns a set of all objects that link to this object, + * including possible external parent objects. + * + * @param[in] recursive Whether to obtain the InList recursively. + * + * @return A set containing all objects linking to this object. */ std::set getInListEx(bool recursive) const; - /// get group if object is part of a group, otherwise 0 is returned + /** + * @brief Get the group this object belongs to. + * + * This function returns the group that this object is part of, if any. + * + * @return A pointer to the group this object belongs to, or nullptr if it + * is not part of any group. + */ DocumentObjectGroup* getGroup() const; - /// test if this object is in the InList and recursive further down + /** + * @brief Check if an object is (in)directly in the InList of this object. + * + * This function checks if the specified object is in the InList of this + * object, either directly or recursively. + * + * @param[in] objToTest The object to test for presence in the InList. + * + * @return true if the object is in the InList, false otherwise. + */ bool isInInListRecursive(DocumentObject* objToTest) const; - /// test if this object is directly (non recursive) in the InList - bool isInInList(DocumentObject* objToTest) const; - /// test if the given object is in the OutList and recursive further down - bool isInOutListRecursive(DocumentObject* objToTest) const; - /// test if this object is directly (non recursive) in the OutList - bool isInOutList(DocumentObject* objToTest) const; - /// internal, used by PropertyLink to maintain DAG back links - void _removeBackLink(DocumentObject*); - /// internal, used by PropertyLink to maintain DAG back links - void _addBackLink(DocumentObject*); - //@} /** - * @brief testIfLinkIsDAG tests a link that is about to be created for - * circular references. - * @param objToLinkIn (input). The object this object is to depend on after - * the link is going to be created. - * @return true if link can be created (no cycles will be made). False if - * the link will cause a circular dependency and break recomputes. Throws an - * error if the document already has a circular dependency. - * That is, if the return is true, the link is allowed. + * @brief Check if an object is directly in the InList of this object. + * + * This function checks if the specified object is directly in the InList + * of this object, without checking recursively. + * + * @param[in] objToTest The object to test for presence in the InList. + * + * @return true if the object is directly in the InList, false otherwise. + */ + bool isInInList(DocumentObject* objToTest) const; + + /** + * @brief Check if an object is (in)directly in the OutList of this object. + * + * This function checks if the specified object is in the OutList of this + * object, either directly or recursively. + * + * @param[in] objToTest The object to test for presence in the OutList. + * + * @return true if the object is in the OutList, false otherwise. + */ + bool isInOutListRecursive(DocumentObject* objToTest) const; + + /** + * @brief Check if an object is directly in the OutList of this object. + * + * This function checks if the specified object is directly in the OutList + * of this object, without checking recursively. + * + * @param[in] objToTest The object to test for presence in the OutList. + * + * @return true if the object is directly in the OutList, false otherwise. + */ + bool isInOutList(DocumentObject* objToTest) const; + + /** + * @brief Remove a back link from the InList of this object. + * + * This is an internal method, used by @ref App::PropertyLink + * "PropertyLink" (and variants) to maintain DAG back links when a link + * property's value is set. + * + * @param[in] obj The object to remove from the InList. + */ + void _removeBackLink(DocumentObject* obj); + + /** + * @brief Add a back link to the InList of this object. + * + * This is an internal method, used by @ref App::PropertyLink + * "PropertyLink" (and variants) to maintain DAG back links when a link + * property's value is set. + * + * @param[in] obj The object to remove from the InList. + */ + void _addBackLink(DocumentObject*); + + /** + * @brief Test an about to created link for circular references. + * + * This function checks if the link to the specified parameter is DAG + * compatible, which means that no circular references will be created. + * + * @param[in] linkTo The object that this object is about to link to. + * + * @return true if the link can be created without a circular dependency, + * false if it will cause a circular dependency. */ bool testIfLinkDAGCompatible(DocumentObject* linkTo) const; - bool testIfLinkDAGCompatible(const std::vector& linksTo) const; - bool testIfLinkDAGCompatible(App::PropertyLinkSubList& linksTo) const; - bool testIfLinkDAGCompatible(App::PropertyLinkSub& linkTo) const; - /** Return the element map version of the geometry data stored in the given property + /** + * @copybrief testIfLinkDAGCompatible(DocumentObject*) const * - * @param prop: the geometry property to query for element map version - * @param restored: whether to query for the restored element map version. - * In case of version upgrade, the restored version may - * be different from the current version. + * @param[in] linksTo A list of objects that this object is about to link to. + * + * @return true if the links can be created without a circular dependency, + * false if they will cause a circular dependency. + */ + bool testIfLinkDAGCompatible(const std::vector& linksTo) const; + + /// @copydoc testIfLinkDAGCompatible(const std::vector&) const + bool testIfLinkDAGCompatible(App::PropertyLinkSubList& linksTo) const; + + /// @copydoc testIfLinkDAGCompatible(DocumentObject*) const + bool testIfLinkDAGCompatible(App::PropertyLinkSub& linkTo) const; + ///@} + + /** + * @brief Get the element map version of the geometry data stored in the given property. + * + * @param[in] prop: the geometry property to query for element map version + * @param[in] restored: whether to query for the restored element map version. + * In case of version upgrade, the restored version may + * be different from the current version. * * @return Return the element map version string. */ virtual std::string getElementMapVersion(const App::Property* prop, bool restored = false) const; - /// Return true to signal re-generation of geometry element names + /** @brief Check the element map versionn of the property. + * + * This function checks whether the element map version of the given + * property matches the given version string. If the function returns + * true, the geometry element names need to be recomputed. + * + * @param[in] prop: the geometry property to query for element map version + * @param[in] ver: the version string to compare with + * + * @return true if the element map version differs and the geometry element + * namess need to be recomputed. + */ virtual bool checkElementMapVersion(const App::Property* prop, const char* ver) const; public: - /** mustExecute - * We call this method to check if the object was modified to - * be invoked. If the object label or an argument is modified. - * If we must recompute the object - to call the method execute(). - * 0: no recomputation is needed - * 1: recomputation needed + + /** + * @brief Whether the object must be executed. * - * @remark If an object is marked as 'touched' then this does not + * This method checks if the object was modified to be invoked. + * + * @return 0 if no execute is needed, 1 if the object must be executed. + * + * @remark If an object is marked as 'touched', then this does not * necessarily mean that it will be recomputed. It only means that all - * objects that link it (i.e. its InList) will be recomputed. + * objects that link to it (i.e. its InList) will be recomputed. */ virtual short mustExecute() const; - /** Recompute only this feature + /** + * @brief Recompute only this feature. * - * @param recursive: set to true to recompute any dependent objects as well + * @param recursive: set to true to recompute any dependent objects as well. + * + * @return true if the feature was recomputed, false if it was not. */ bool recomputeFeature(bool recursive = false); - /// get the status Message + /// Get the status message. const char* getStatusString() const; - /** Called in case of losing a link - * Get called by the document when a object got deleted a link property of this - * object ist pointing to. The standard behaviour of the DocumentObject implementation - * is to reset the links to nothing. You may override this method to implement - * additional or different behavior. + /** + * @brief Called when a link to a DocumentObject is lost. + * + * This method is called by the document when a link property of this + * object is pointing to a DocumentObject that has been deleted. The + * standard behavior of the DocumentObject implementation is to reset the + * links to nothing. You may override this method to implement additional + * or different behavior. + * + * @param[in] obj The DocumentObject that has been lost. */ - virtual void onLostLinkToObject(DocumentObject*); + virtual void onLostLinkToObject(DocumentObject* obj); + PyObject* getPyObject() override; - /** Get the sub element/object by name + /** + * @brief Get the sub element/object by name. * - * @param subname: a string which is dot separated name to refer to a sub - * element or object. An empty string can be used to refer to the object - * itself + * @param[in] subname A string that is a dot separated name to refer to a + * sub element or object. An empty string can be used to refer to the + * object itself. * - * @param pyObj: if non zero, returns the python object corresponding to + * @param[out] pyObj If not null, returns the python object corresponding to * this sub object. The actual type of this python object is implementation - * dependent. For example, The current implementation of Part::Feature will - * return the TopoShapePy, event if there is no sub-element reference, in - * which case it returns the whole shape. + * dependent. For example, The current implementation of @c Part::Feature + * will return the TopoShapePy, even if there is no sub-element reference, + * in which case it returns the whole shape. * - * @param mat: If non zero, it is used as the current transformation matrix - * on input. And output as the accumulated transformation up until and - * include the transformation applied by the final object reference in \c - * subname. For Part::Feature, the transformation is applied to the - * TopoShape inside \c pyObj before returning. + * @param[in,out] mat If not null, it is used as the current + * transformation matrix on input. On output it is used as the accumulated + * transformation up until and include the transformation applied by the + * final object reference in @c subname. For @c Part::Feature, the + * transformation is applied to the @c TopoShape inside @c pyObj before + * returning. * - * @param transform: if false, then it will not apply the object's own - * transformation to \c mat, which lets you override the object's placement + * @param[in] transform: If false, then it will not apply the object's own + * transformation to @p mat, which lets you override the object's placement * (and possibly scale). * - * @param depth: depth limitation as hint for cyclic link detection + * @param[in] depth: Depth limitation as hint for cyclic link detection. * - * @return The last document object referred in subname. If subname is empty, - * then it shall return itself. If subname is invalid, then it shall return - * zero. + * @return The last document object referred in subname. If @p subname is + * empty, then it returns itself. If @p subname is invalid, then it returns @c + * nullptr. */ virtual DocumentObject* getSubObject(const char* subname, PyObject** pyObj = nullptr, @@ -438,107 +795,175 @@ public: bool transform = true, int depth = 0) const; - /** Return a list of objects referenced by a given subname including this object - * @param subname: the sub name path - * @param subsizes: optional sub name sizes for each returned object, that is, - * ret[i] = getSubObject(std::string(subname, subsizes[i]).c_str()); - * @param flatten: whether to flatten the object hierarchies that belong to - * the same geo feature group, e.g. (Part.Fusion.Box -> Part.Box) + /** + * @brief Get a list of objects referenced by a given subname. * - * @return Return a list of objects along the path. + * This method returns a list of objects along the path that is represented + * by the subname. It includes this object. With the optional argument @p + * subsizes, it is possible to infer which part of the subname relates to + * which document object, with the following relation: + * @code + * ret[i] = getSubObject(std::string(subname, subsizes[i]).c_str()); + * @endcode + * + * @param[in] subname: The subname path. + * @param[in,out] subsizes: The optional subname sizes for each returned object. + * @param[in] flatten: Whether to flatten the object hierarchies that belong to + * the same geo feature group, e.g. (Part.Fusion.Box -> Part.Box) + * + * @return Return a list of objects along the path of the subname. */ std::vector getSubObjectList(const char* subname, std::vector* subsizes = nullptr, bool flatten = false) const; - /// reason of calling getSubObjects() + /// Reason of calling getSubObjects() enum GSReason { - /// default, mostly for exporting shape objects + /// Default, mostly for exporting shape objects GS_DEFAULT, - /// for element selection + /// For element selection GS_SELECT, }; - /** Return name reference of all sub-objects + /** + * @brief Get name references of all sub-objects. * - * @param reason: indicate reason of obtaining the sub objects + * This function returns a list of sub-object names for this object. The + * default implementation returns all object references in PropertyLink, + * and PropertyLinkList, if any. * - * The default implementation returns all object references in - * PropertyLink, and PropertyLinkList, if any - * - * @return Return a vector of subname references for all sub-objects. In - * most cases, the name returned will be the object name plus an ending - * '.', which can be passed directly to getSubObject() to retrieve the + * In most cases, the names returned will be the object name plus an ending + * '.' that can be passed directly to getSubObject() to retrieve the * name. The reason to return the name reference instead of the sub object - * itself is because there may be no real sub object, or the sub object - * need special transformation. For example, sub objects of an array type - * of object. + * itself is because there may be no real sub object or the sub object is a + * special case, for example sub objects of an array type of object. + * + * @param[in] reason The reason for obtaining the sub objects. + * + * @return A vector of subname references for all sub-objects. */ virtual std::vector getSubObjects(int reason = 0) const; - /// Obtain top parents and subnames of this object using its InList + /** + * @brief Get the parent objects and the subnames of this object. + * + * This function returns a vector of pairs, where each pair contains a + * pointer to a parent DocumentObject and its subname using thee object's + * InList. The @p depth parameter ensures that there is no endless loop in + * case there is a cyclic dependency. + * + * @param[in] depth Optional depth that specifies on which level in parents + * the search is. + * + * @return A vector of pairs containing parent DocumentObjects and their subnames. + * @throws Base::RuntimeError If the search triggers a cycle in the dependencies in the InList. + */ std::vector> getParents(int depth = 0) const; - /// Obtain the first parent group of this object - App::DocumentObject* getFirstParent() const; + /** + * @brief Get the first parent object of this object that is a group. + * + * This function returns the first parent object that is a group, i.e. that + * has the extension GroupExtension. + * + * @return A pointer to the first parent group object, or @c nullptr if no such + * parent exists. + */ + App::DocumentObject* getFirstParent() const; - /** Return the linked object with optional transformation + /** + * @brief Get the linked object with an optional transformation. + * + * This method returns the linked object of this document object. The @p + * depth parameter indicates the current depth of recursion, which is used + * to prevent infinite recursion in case of cyclic dependencies. * * @param recurse: If false, return the immediate linked object, or else * recursively call this function to return the final linked object. * - * @param mat: If non zero, it is used as the current transformation matrix - * on input. And output as the accumulated transformation till the final + * @param mat: If non-null, it is used as the current transformation matrix + * on input. And output as the accumulated transformation until the final * linked object. * * @param transform: if false, then it will not accumulate the object's own * placement into \c mat, which lets you override the object's placement. * - * @return Return the linked object. This function must return itself if the - * it is not a link or the link is invalid. + * @param depth: Indicates the current depth of recursion. + * + * @return Return the linked object. This function returns itself if it is + * not a link or the link is invalid. */ virtual DocumentObject* getLinkedObject(bool recurse = true, Base::Matrix4D* mat = nullptr, bool transform = false, int depth = 0) const; - /* Return true to cause PropertyView to show linked object's property */ + /** + * @brief Check whether this object can adopt properties from linked objects. + * + * This function is used to determine if the object can adopt properties + * from linked objects. It is typically used in the context of the + * property view to decide whether to show properties from linked objects. + * + * @return true if the object can adopt properties from links, false otherwise. + */ virtual bool canLinkProperties() const { return true; } - /* Return whether this object is a link */ + /// Check whether this object is a link. virtual bool isLink() const { return false; } - /* Return whether this object is a link group */ + /// Check whether this object is a link group. virtual bool isLinkGroup() const { return false; } - /* Return true to bypass duplicate label checking */ + /** + * @brief Check whether this object allows duplicate labels. + * + * This function is used to determine if the object allows duplicate + * labels. Overriding this function and returning true allows to bypass + * duplicate labell checking. + * + * @return true if duplicate labels are allowed, false otherwise. + */ virtual bool allowDuplicateLabel() const { return false; } - /// Handle Label changes, including forcing unique label values, - /// signalling OnBeforeLabelChange, and arranging to update linked references, - /// on the assumption that after returning the label will indeed be changed to - /// the (altered) value of newLabel. - /// Returns a vector of referenging (linking) properties as produced by - /// PropertyLinkBase::updateLabelReferences which is needed for undo/redo purposes. + + /** + * @brief Called when a new label for the document object is proposed. + * + * This method is called when a new label is proposed for the document + * object and it handles label changes,including ensuring unique label values, + * signaling onBeforeChangeLabel(), and updating linked references. + * + * It assumes that after returning, the label will indeed be changed to the + * (possibly altered) value of @p newLabel. + * + * @param[in,out] newLabel The proposed new label for the document object. + * + * @return A vector of referencing (linking) properties as produced by + * PropertyLinkBase::updateLabelReferences(), which is needed for undo/redo + * purposes. + */ std::vector>> onProposedLabelChange(std::string& newLabel); - /*** Called to let object itself control relabeling + + /** + * @brief Called to ensure that the object can control its relabeling. * - * @param newLabel: input as the new label, which can be modified by object itself + * @param[in,out] newLabel The new label proposed for the object. * - * This function is called before onBeforeChange() + * @note This function is called before onBeforeChange(). */ virtual void onBeforeChangeLabel(std::string& newLabel) { @@ -549,27 +974,73 @@ public: friend class Transaction; friend class ObjectExecution; + /** + * @brief The standard return object for document object execution. + * + * Its value is set to `nullptr` which means that the execution + * was successful. If the execution fails, it is set to a pointer to an + * instance of this class that contains the error message. + */ static DocumentObjectExecReturn* StdReturn; void Save(Base::Writer& writer) const override; /* Expression support */ + /** + * @brief Set an expression for a given object identifier. + * + * Associate the expression @p expr with object identifier @p path in this document object. + * + * @param[in] path The target object identifier for the result of the expression. + * @param[in,out] expr The Expression tree. + */ virtual void setExpression(const ObjectIdentifier& path, std::shared_ptr expr); + /** + * @brief Clear the expression of an object identifier in this document object. + * + * @param[in] path The target object identifier. + */ void clearExpression(const ObjectIdentifier& path); + /** + * @brief Get expression information associated with an object identifier. + * + * @param[in] path The object identifier. + * + * @return Expression info that contains the expression and whether the + * expression is busy. + */ virtual const PropertyExpressionEngine::ExpressionInfo getExpression(const ObjectIdentifier& path) const; + /** + * @brief Rename object identifiers in expressions. + * + * Invoke ExpressionEngine's renameObjectIdentifier, to possibly rewrite + * expressions using the @p paths map with current and new identifiers. + * + * @param[in] paths A map of old to new object identifiers. + */ virtual void renameObjectIdentifiers(const std::map& paths); + /// Get the old label of the object. const std::string& getOldLabel() const { return oldLabel; } + /** + * @brief Get the name of the view provider that was stored for this object. + * + * This function returns the name of the view provider that was stored for + * this object. This is the case when the object has a custom view + * provider. + * + * @return The name of the view provider that was stored for this object. + */ const char* getViewProviderNameStored() const { return _pcViewProviderName.c_str(); @@ -587,17 +1058,30 @@ public: bool ro = false, bool hidden = false) override; - /** Resolve the last document object referenced in the subname + /** + * @brief Resolve the last document object referenced in the subname. * - * @param subname: dot separated subname - * @param parent: return the direct parent of the object - * @param childName: return child name to be passed to is/setElementVisible() - * @param subElement: return non-object sub-element name if found. The - * pointer is guaranteed to be within the buffer pointed to by 'subname' + * If @p childName is not null, the method will return the name of the + * child that can be passed to isElementVisible() or setElementVisible(). + * + * @param[in] subname A dot separated subname. + * @param[in,out] parent If not null, return the direct parent of the object. + * @param[in,out] childName If not null, return the child name. + * @param[in,out] subElement If not null, return the non-object sub-element + * name if found. The pointer is guaranteed to be within the buffer pointed + * to by @p subname. + * @param[in,out] pyObj If not null, return the python object corresponding + * to this sub object. + * @param[in,out] mat If not null, it is used for (accumulated) + * transformation of the sub object. + * @param[in] transform Whether to apply the object's own transformation. + * @param[in] depth Maintains the current depth of recursion for cyclic + * link detection. * * @sa getSubObject() - * @return Returns the last referenced document object in the subname. If no - * such object in subname, return pObject. + * + * @return The last referenced document object in the subname or if no such + * object in subname, itself. */ App::DocumentObject* resolve(const char* subname, App::DocumentObject** parent = nullptr, @@ -608,26 +1092,16 @@ public: bool transform = true, int depth = 0) const; - /** Resolve a link reference that is relative to this object reference + /** + * @brief Resolve a link reference that is relative to this object reference. * - * @param subname: on input, this is the subname reference to the object - * that is to be assigned a link. On output, the reference may be offset - * to be rid of any common parent. - * @param link: on input, this is the top parent of the link reference. On - * output, it may be altered to one of its child to be rid off any common - * parent. - * @param linkSub: on input, this the subname of the link reference. On - * output, it may be offset to be rid off any common parent. - * - * @return The corrected top parent of the object that is to be assigned the - * link. If the output 'subname' is empty, then return the object itself. - * - * To avoid any cyclic reference, an object must not be assign a link to any - * of the object in its parent. This function can be used to resolve any + * To avoid cyclic references, an object must not be assigned a link to any + * of the objects in its parent. This function can be used to resolve any * common parents of an object and its link target. * - * For example, with the following object hierarchy + * For example, with the following object hierarchy: * + * ``` * Group * |--Group001 * | |--Box @@ -635,169 +1109,239 @@ public: * |--Group002 * |--Box001 * |--Cylinder001 + * ``` * - * If you want add a link of Group.Group002.Box001 to Group.Group001, you - * can call with the following parameter (which are usually obtained from - * Selection.getSelectionEx(), check usage in TreeWidget::onDropEvent()): + * If you want to add a link of `Group.Group002.Box001` to + * `Group.Group001`, you can call the method with the following parameters: + * + * ```cpp * std::string subname("Group002."); * auto link = Group; * std::string linkSub("Group001.Box001."); * parent = Group.resolveRelativeLink(subname,link,linkSub); + * ``` * * The resolving result is as follow: + * + * ``` * return -> Group001 * subname -> "" * link -> Group002 * linkSub -> "Box001." + * ``` * - * The common parent 'Group' is removed. + * The common parent `Group` is removed. + * + * @param[in,out] subname The subname reference to the object that is to be + * assigned a link on input. On output, the reference may be offset to get + * rid of any common parent. + * + * @param[in,out] link The top parent of the link reference on input. On + * output, it may be altered to one of its children to get rid of any + * common parent. + * + * @param[in,out] linkSub The subname of the link reference on input. On + * output, it may be offset to get rid of any common parent. + * + * @return The corrected top parent of the object that is to be assigned + * the link. If the output @p subname is empty, then the object + * itself is returned. */ App::DocumentObject* resolveRelativeLink(std::string& subname, App::DocumentObject*& link, std::string& linkSub) const; - /** Called to adjust link properties to avoid cyclic links - * - * @param inList: the recursive in-list of the future parent object, - * including the parent itself. - * @param visited: optional set holding the visited objects. If null then - * only this object is adjusted, or else all object inside the out-list of - * this object will be checked. - * - * @return Return whether the object has been modified + /** + * @brief Adjust relative link properties to avoid cyclic links. * * This function tries to adjust any relative link properties (i.e. link - * properties that can hold subnames) to avoid cyclic when added to the - * future parent. + * properties that can hold subnames) to avoid cycles between links when + * added to the future parent. + * + * @param[in] inList The recursive in-list of the future parent object, + * including the parent itself. + * + * @param[in,out] visited An optional set holding the visited objects. If + * null then only this object is adjusted, or else all objects inside the + * out-list of this object will be checked. + * + * @return True if the object has been modified, false otherwise. */ virtual bool adjustRelativeLinks(const std::set& inList, std::set* visited = nullptr); - /** allow partial loading of dependent objects + /** + * @brief Check whether the object allows partial loading of dependent objects. * - * @return Returns 0 means do not support partial loading. 1 means allow - * dependent objects to be partially loaded, i.e. only create, but not - * restored. 2 means this object itself can be partially loaded. + * The possible return values are: + * - 0: Do not support partial loading. + * - 1: Allow dependent objects to be partially loaded, i.e. only create them, but do not + * restore them. + * - 2: This object itself can be partially loaded. + * + * @return Whether the object supports partial loading. */ virtual int canLoadPartial() const { return 0; } - virtual void onUpdateElementReference(const Property*) + /** + * @brief Called when an element reference is updated. + * + * @param[in] prop The property that was updated. + */ + virtual void onUpdateElementReference([[maybe_unused]] const Property* prop) {} - /** Allow object to redirect a subname path - * - * @param ss: input as the current subname path from \a topParent leading - * just before this object, i.e. ends at the parent of this object. This - * function should append its own name to this path, or redirect the - * subname to other place. - * @param topParent: top parent of this subname path - * @param child: the immediate child object in the path + /** + * @brief Allow an object to redirect a subname path. * * This function is called by tree view to generate a subname path when an - * item is selected in the tree. Document object can use this function to - * redirect the selection to some other objects. + * item is selected in the tree. A document object can use this function + * to redirect the selection to some other object. + * + * @param[in,out] ss The current subname path from @p topParent leading to + * just before this object as input, i.e. ends at the parent of this + * object. As output, this method should append its own name to this path, or + * redirect the subname to another place. + * + * @param[in] topParent The top parent of this subname path. + * @param[in] child The immediate child object in the path. + * + * @return true if the subname was redirected, false otherwise. */ virtual bool redirectSubName(std::ostringstream& ss, DocumentObject* topParent, DocumentObject* child) const; - /** Special marker to mark the object as hidden + /** + * @brief A special marker to mark the object as hidden. * * It is used by Gui::ViewProvider::getElementColors(), but exposed here - * for convenience + * for convenience. + * + * @return A string that is used to mark the object as hidden. */ static const std::string& hiddenMarker(); - /// Check if the subname reference ends with hidden marker. + + /** + * @brief Check if the subname reference ends with hidden marker. + * + * @param[in] subname The subname to check. + * + * @return A pointer to the hidden marker if it is found, or nullptr if not. + */ static const char* hasHiddenMarker(const char* subname); - /* Find the placement of a target object as seen from this. - If no targetObj given, the last object found in the subname is used as target. - */ + /** + * @brief Find the placement of a target object as seen from this. + * + * If no targetObj given, the last object found in the subname is used as + * target. + * + * @param[in] sub The subname that is targeted. + * @param[in] targetObj The object that is targeted. + * + * @return The placement of the target object from the perspective of this + * object. + */ virtual Base::Placement getPlacementOf(const std::string& sub, DocumentObject* targetObj = nullptr); - /* Returns the Placement property value if any.*/ + /// Returns the Placement property value if any. virtual Base::Placement getPlacement() const; - /* Returns the Placement property to use if any*/ + /// Returns the Placement property to use if any. virtual App::PropertyPlacement* getPlacementProperty() const; protected: - /// recompute only this object + /// Recompute only this object. virtual App::DocumentObjectExecReturn* recompute(); - /** get called by the document to recompute this feature - * Normally this method get called in the processing of - * Document::recompute(). - * In execute() the output properties get recomputed - * with the data from linked objects and objects own - * properties. + + /** + * @brief Execute the document object. + * + * In some contexts this is called "invoking" the object. It is called by + * the document to recompute this feature, normally in the processing of + * Document::recompute(). In this method, the output properties get + * recomputed with the data from linked objects and the properties from the + * object itself. + * + * @return On success, A pointer to StdReturn is returned which is set to + * `nullptr`. On failure, it returns a pointer to a newly created + * App::DocumentObjectExecReturn that containss the error message. */ virtual App::DocumentObjectExecReturn* execute(); /** - * Executes the extensions of a document object. + * @brief Executes the extensions of a document object. + * + * @sa recompute() */ App::DocumentObjectExecReturn* executeExtensions(); - /** Status bits of the document object - * The first 8 bits are used for the base system the rest can be used in - * descendent classes to mark special statuses on the objects. - * The bits and their meaning are listed below: - * 0 - object is marked as 'touched' - * 1 - object is marked as 'erroneous' - * 2 - object is marked as 'new' - * 3 - object is marked as 'recompute', i.e. the object gets recomputed now - * 4 - object is marked as 'restoring', i.e. the object gets loaded at the moment - * 5 - object is marked as 'deleting', i.e. the object gets deleted at the moment - * 6 - reserved - * 7 - reserved - * 16 - object is marked as 'expanded' in the tree view + /** + * @brief Status bits of the document object. + * + * For the status bits, see ObjectStatus. */ std::bitset<32> StatusBits; + /// Set this object in the error state. void setError() { StatusBits.set(ObjectStatus::Error); } + + /// Reset the error state of this object. void resetError() { StatusBits.reset(ObjectStatus::Error); } + + /// Set the document this object belongs to. void setDocument(App::Document* doc); - /// get called before the value is changed void onBeforeChange(const Property* prop) override; - /// get called by the container when a property was changed void onChanged(const Property* prop) override; - /// get called by the container when a property was changed void onEarlyChange(const Property* prop) override; - /// get called after a document has been fully restored + + /// Called after a document has been fully restored. virtual void onDocumentRestored(); - /// get called after an object finishes restoreContent. + void restoreFinished() override; - /// get called after an undo/redo transaction is finished + + /// Called after an undo/redo transaction has finished. virtual void onUndoRedoFinished(); - /// get called after setting the document + + /// Called after setting the document. virtual void onSettingDocument(); - /// get called after a brand new object was created + + /// Called after a brand new object was created. virtual void setupObject(); - /// get called when object is going to be removed from the document + + /// Called when object is going to be removed from the document. virtual void unsetupObject(); - /// get called when a property status has changed + /** + * @brief Called when a property status has changed. + * + * @param[in] prop The property of which the status has changed. + * @param[in] oldStatus The old status of the property. + */ void onPropertyStatusChanged(const Property& prop, unsigned long oldStatus) override; private: void printInvalidLinks() const; - /// python object of this class and all descendent protected: // attributes + + /// Python object of this class and all descendent classes. Py::SmartPtr PythonObject; - /// pointer to the document this object belongs to + + /// A pointer to the document this object belongs to. App::Document* _pDoc {nullptr}; - /// Old label; used for renaming expressions + /// The old label that is used for renaming expressions. std::string oldLabel; private: diff --git a/src/App/DocumentObjectExtension.h b/src/App/DocumentObjectExtension.h index 32f1500816..f8030db4d8 100644 --- a/src/App/DocumentObjectExtension.h +++ b/src/App/DocumentObjectExtension.h @@ -97,10 +97,31 @@ public: */ virtual bool extensionGetSubObjects(std::vector& ret, int reason) const; - /** Get the linked object - * @sa DocumentObject::getLinkedObject() + /** + * @brief Get the linked object of this extension. * - * @return Return turn if handled, the linked object is returned in \c ret + * This method returns the linked object of this document object. If there + * is no linked object, it will return the container object of the + * extension. + * + * @param[out] ret The linked object is returned in this parameter. + * + * @param[in] recurse If true, it will recursively resolve the link until it + * reaches the final linked object. + * + * @param mat[in,out] If non-null, it is used as the current transformation + * matrix on input. On output it is used as the accumulated transformation + * up until the final linked object. + * + * @param[in] transform If false, then it will not accumulate the object's own + * placement into @p mat, which lets you override the object's placement. + * + * @param[in] depth This parameter indicates the level on which we are + * resolving the link. + * + * @return Returns true if the linked object is successfully retrieved and + * returned in @p ret. If the linked object is not found or is invalid, it + * returns false. */ virtual bool extensionGetLinkedObject(DocumentObject*& ret, bool recursive, diff --git a/src/App/Graphviz.cpp b/src/App/Graphviz.cpp index af1a0c73fb..6bb66128e0 100644 --- a/src/App/Graphviz.cpp +++ b/src/App/Graphviz.cpp @@ -97,7 +97,6 @@ void Document::exportGraphviz(std::ostream& out) const * This class creates the dependency graph for a document. * */ - class GraphCreator { public: @@ -132,7 +131,6 @@ void Document::exportGraphviz(std::ostream& out) const * @param docObj Document object to get an ID from * @return A string */ - std::string getId(const DocumentObject* docObj) { std::string id; @@ -150,7 +148,6 @@ void Document::exportGraphviz(std::ostream& out) const * @param path * @return A string */ - std::string getId(const ObjectIdentifier& path) { DocumentObject* docObj = path.getDocumentObject(); @@ -183,7 +180,6 @@ void Document::exportGraphviz(std::ostream& out) const * @brief setGraphAttributes Set graph attributes on a subgraph for a DocumentObject node. * @param obj DocumentObject */ - void setGraphAttributes(const DocumentObject* obj) { assert(GraphList.find(obj) != GraphList.end()); @@ -201,7 +197,6 @@ void Document::exportGraphviz(std::ostream& out) const * @param vertex Property node * @param name Name of node */ - void setPropertyVertexAttributes(Graph& g, Vertex vertex, const std::string& name) { get(vertex_attribute, g)[vertex]["label"] = name; @@ -217,7 +212,6 @@ void Document::exportGraphviz(std::ostream& out) const * @param obj DocumentObject to assess. * @param CSSubgraphs Boolean if the GeoFeatureGroups are created as subgraphs */ - void addExpressionSubgraphIfNeeded(DocumentObject* obj, bool CSsubgraphs) { @@ -283,7 +277,6 @@ void Document::exportGraphviz(std::ostream& out) const * @param docObj The document object to add. * @param name Name of node. */ - void add(DocumentObject* docObj, const std::string& name, const std::string& label, diff --git a/src/App/Link.h b/src/App/Link.h index 11c02633e3..b1cae01ddb 100644 --- a/src/App/Link.h +++ b/src/App/Link.h @@ -341,6 +341,17 @@ public: const char* flattenSubname(const char* subname) const; void expandSubname(std::string& subname) const; + /** + * @brief Get the object the link points to. + * + * This method returns the linked object of this link. The @p depth + * parameter is used as an indication of how deep the recursion is as a + * safeguard against infinite recursion caused by cyclic dependencies. + * + * @param[in] depth The level on which we are resolving the link. + * + * @return Returns the linked object or @c nullptr if there is no linked value. + */ DocumentObject* getLink(int depth = 0) const; Base::Matrix4D getTransform(bool transform) const; @@ -403,6 +414,33 @@ public: const char* subname = nullptr, const std::vector& subs = std::vector()); + /** + * @brief Get the true linked object. + * + * This method returns the true linked object, which is the object that the + * link points to. The @p depth parameter is used as an indication of the + * current level of recursion is as a safeguard against infinite recursion + * caused by cyclic dependencies. + * + * @param recurse If true, it will recursively resolve the link until it reaches + * the final linked object, or until it reaches the maximum recursion depth. + * + * @param mat If non-null, it is used as the current transformation matrix on + * input. On output it is used as the accumulated transformation up until + * the final linked object. + * + * @param depth This parameter indicates the level on which we are + * resolving the link. + * + * @param noElement If true, it will not return the linked object if it is + * a link to an element. + * + * @return Returns the true linked object. If the linked object is not found + * or is invalid, it returns @c nullptr. + * + * @sa DocumentObject::getLinkedObject() which may return itself if it is not a link. + * + */ DocumentObject* getTrueLinkedObject(bool recurse, Base::Matrix4D* mat = nullptr, int depth = 0, diff --git a/src/App/PropertyExpressionEngine.h b/src/App/PropertyExpressionEngine.h index 3edb835c12..b1d83e0a56 100644 --- a/src/App/PropertyExpressionEngine.h +++ b/src/App/PropertyExpressionEngine.h @@ -100,7 +100,6 @@ public: /** * @brief The ExpressionInfo struct encapsulates an expression. */ - struct ExpressionInfo { std::shared_ptr expression; /**< The actual expression tree */ diff --git a/src/App/PropertyLinks.h b/src/App/PropertyLinks.h index 973cfdd0e3..a0af06530d 100644 --- a/src/App/PropertyLinks.h +++ b/src/App/PropertyLinks.h @@ -262,14 +262,20 @@ public: return nullptr; } - /** Update object label reference in this property + /** + * @brief Update the object label reference of this property. * - * @param obj: the object owner of the changing label - * @param ref: subname reference to old label - * @param newLabel: the future new label + * This method does not update the property itself, but returns a copy of + * the property with the updated label reference if the property is + * affected. The copy will later be assigned to this property by calling its + * Paste() method. * - * @return Returns a copy of the property if its link reference is affected. - * The copy will later be assgiend to this property by calling its Paste(). + * @param[in] obj The object owner of the changing label. + * @param[in] ref The subname reference to old label. + * @param[in] newLabel The future new label. + * + * @return A copy of the property if its link reference is affected, + * otherwise `nullptr`. */ virtual Property* CopyOnLabelChange(App::DocumentObject* obj, const std::string& ref, const char* newLabel) const @@ -556,13 +562,17 @@ public: */ static void getLabelReferences(std::vector& labels, const char* subname); - /** Helper function to collect changed property when an object re-label + /** + * @brief Update label references on label change of a document object. * - * @param obj: the object that owns the label - * @param newLabel: the new label + * This helper function collects changed properties when an object re-label + * takes place. It returns a map from affected properties to copies of + * those affectedd propertiess with updated subname references. * - * @return return a map from the affected property to a copy of it with - * updated subname references + * @param[in] obj The object that owns the label that is changed. + * @param[in] newLabel The new label of the object. + * + * @return The map from affected property to a copy of it. */ static std::vector>> updateLabelReferences(App::DocumentObject* obj, const char* newLabel); diff --git a/src/App/core-app.dox b/src/App/core-app.dox index adb1bf8770..7dd9afc185 100644 --- a/src/App/core-app.dox +++ b/src/App/core-app.dox @@ -19,36 +19,225 @@ /** * @defgroup DocumentGroup Document * @ingroup APP - * @brief The class that represents a FreeCAD document + * @brief The class that represents a FreeCAD document. * - * This (besides the App::Application class) is the most important class in FreeCAD. - * It contains all the data of the opened, saved, or newly created FreeCAD Document. - * The App::Document manages the Undo and Redo mechanism and the linking of documents. + * This (besides the App::Application class) is the most important class in + * FreeCAD. It contains all the data of the opened, saved, or newly created + * FreeCAD %Document. The App::Document class manages the undo and redo + * mechanism and linking of documents. * - * Note: the documents are not free objects. They are completely handled by the - * App::Application. Only the Application can Open or destroy a document. + * Note: Documents are not freestanding objects. They are completely handled by the + * App::Application class. Only the application can open or destroy a document. * - * \section Exception Exception handling - * As the document is the main data structure of FreeCAD we have to take a close - * look at how Exceptions affect the integrity of the App::Document. + * @section SecDocumentProperties Documents as property containers * - * \section UndoRedo Undo Redo an Transactions - * Undo Redo handling is one of the major mechanism of a document in terms of - * user friendliness and speed (no one will wait for Undo too long). + * The main role of a document is being a container of @ref DocumentObjectGroup + * "DocumentObjects". Both a @ref DocumentObjectGroup "DocumentObject" and a + * @ref App::Document "Document" are @ref PropertyFramework + * "PropertyContainers" and can hold properties. * - * \section Dependency Graph and dependency handling - * The FreeCAD document handles the dependencies of its DocumentObjects with - * an adjacency list. This gives the opportunity to calculate the shortest - * recompute path. Also, it enables more complicated dependencies beyond trees. + * However, there is a crucial difference between the properties in a document + * and in a document object. The properties in a document cannot be the target + * of expressions and they do not take part in the dependency check mechanism. + * This means that if a document object references a property of a document and + * the document property is changed, the document object that references the + * document property will not be marked to be recomputed. As such, the + * properties in a document should be regarded as simple properties for + * file-related information. * - * @see App::Application - * @see App::DocumentObject + * @section SecObjectManagement Object management + * + * As mentioned above, a document is a container of document objects. Document + * objects can only exist inside a document and the document provides + * functionality to create, remove, and access document objects. For example, + * it is possible to get objects of a specific type or with a specific + * extension. + * + * The document also manages the names and labels document objects, ensuring + * that names are unique and that labels are unique if configured to have + * unique labels. + * + * @section DependencyGraph Dependencies between document objects + * + * Objects can link to other objects in the same document or in other documents + * and as such dependencies between document objects come into existence. The + * document is responsible for recomputing document objects if some property of + * a document object changes. The dependencies between document objects + * determine in which order the objects must be recomputed. + * + * To compute the objects in the right order, the document computes a + * dependency graph based on the information contained in the OutLists and + * InLists of the document objects. For more information on OutLists and + * InLists, see @ref SecDocumentObjectRecompute "Document Objects". Given the + * dependency graph, the document computes the list of objects in topological + * order (not to be confused with topological naming) which means that the + * objects without dependencies come first and then the objects that depend on + * these objects, etc. + * + * The document will check for each object in the list whether the object is + * "touched", which means that it is marked to be recomputed. If so, the + * object is recomputed and the objects in the InList of that object are + * touched as well. Because of the topological order, these objects will come + * in a later iteration. + * + * This mechanism allows FreeCAD to recompute only the objects that need to be + * recomputed and in only one pass over the list of objects. However, it is + * important that the dependency graph does not contain cycles and is a + * "Directed Acyclic Graph" or DAG. As such, cyclic dependencies are not + * allowed in FreeCAD. + * + * @section SecUndoRedo Undo Redo and Transactions + * + * Another important responsbility of documents is to manage undo and redo + * actions. The information to support undo and redo is captured in + * transactions. It is important to understand that transactions are a feature + * of document objects and not of documents. This means that a change of a + * property inside a document (as opposed to inside a document object) is not + * captured by the transaction system (similar to the fact that document + * properties do not support expressions). + * + * The transaction system is quite complex and acts on various levels. + * Although the transactions themselves only capture changes regarding document + * objects, the transactions are managed in the document. Although the + * transactions are managed inside a document, it is possible to support + * transactions that affect multiple documents. This is managed by the GUI and + * if a change affects multiple documents, then all documents will store + * transactions that change its document and they will all have the same + * transaction ID. + * + * The mechanisms for undo and redo are part of the @ref APP "App" level, but + * the GUI defines the transactions (where to open a transaction, where to + * commit or abort) and it initiates the undo and redo actions. + * + * The transaction system makes use of classes with very similar names which + * can be confusing. Here is a short overview: + * + * - A @ref App::TransactionalObject "TransactionalObject" is a base class that + * provides the functionality to support transactions. It is inherited by + * @ref App::DocumentObject "DocumentObject". + * - A @ref App::TransactionObject "TransactionObject" is a class that + * represents a single action and can express adding a new object, removing + * an object or changing an object. Subclasses of the transaction object are + * @ref App::TransactionDocumentObject "TransactionDocumentObject" and + * @ref Gui::TransactionViewProvider "Gui::TransactionViewProvider". + * - A @ref App::Transaction "Transaction" captures a single undo or redo + * action and consists of multiple @ref App::TransactionObject + * "TransactionObjects" associated with their @ref App::TransactionalObject + * "TransactionalObject", often a @ref App::DocumentObject "DocumentObject". */ /** * @defgroup DocumentObjectGroup Document Object * @ingroup APP - * @brief %Base class of all objects handled in the Document. + * @brief %Base class of all objects handled in the @ref App::Document "Document". + * + * A DocumentObject is the base class of all objects that can be contained in a + * FreeCAD @ref App::Document "Document". Document objects can represent any + * object that can be created in a FreeCAD Document, such as features: + * Part::Feature, Mesh::Feature, Sketcher::SketchObject, but also other objects + * such as App::Link, App::DocumentObjectGroup, App::VarSet, or + * SpreadSheet::Sheet that do not necessary represent geometry. + * + * Document objects provide the functionality to handle properties (it extends + * @ref App::PropertyContainer "PropertyContainer"), transactions (it extends + * @ref App::TransactionalObject "TransactionalObject") and extensions (it + * extends @ref App::ExtensionContainer "ExtensionContainer"), and expressions. + * Expressions are managed by the property @ref + * App::DocumentObject::ExpressionEngine "ExpressionEngine", which is a special + * type of a link property that contains expressions that may link to other + * document objects. + * + * @section SecDocumentObjectRecompute Recomputing Document Objects + * + * Objects can be recomputed, which means that the properties of the object are + * recomputed. Since properties may depend on other document objects, for + * example because of the @ref App::DocumentObject::ExpressionEngine + * "ExpressionEngine" property, these document objects have to be computed + * first. Managing the order of recomputation is managed by the @ref + * App::Document "Document" which calls the @ref + * App::DocumentObject::recompute() "recompute()" on the document object. To + * signal that a document object needs to be recomputed, the document object + * can be touched (see @ref App::DocumentObject::touch() "touch()" and @ref + * App::DocumentObject::isTouched() "isTouched()"). + * + * As part of recomputation, the document object can also be executed or + * "invoked", (see @ref App::DocumentObject::execute() "execute()") which + * results in recomputing the output properties of the object. + * + * Although recomputation is mostly orchestrated by the @ref App::Document + * "Document", document objects play an important role in capturing the + * dependencies between document objects in terms of OutLists and InLists. + * + * Dependencies between document objects come into existence because of links + * and if a document object `A` links to another document object `B`, then `B` + * is in the OutList of `A` and `A` is in the InList of `B`. The InList for an + * object `Obj` represents the document objects that are dependent on it. The + * outlist of an object `Obj` represent the dependencies of it. So, to + * recompute `Obj`, first the objects in the outlist need to be recomputed. + * Vice versa, if a property of an object is changed, the InList indicates + * which other objects depend on it and need to be recomputed. + * + * So, as mentioned above, links define dependencies between document objects + * These dependencies are recorded by means of setting the value of a @ref + * App::PropertyLink "PropertyLink" (and similar properties) in a document + * object `Obj` to a document object `Value`. Within @ref + * App::PropertyLink::setValue() "PropertyLink::setValue()" (and similar + * methods for other link properties), the (internal) methods _addBackLink() or + * _removeBackLink() will be called on `Value` with as argument the container + * of the property link `Obj`, indicating that `Obj` depends on `Value`. With + * this methodology, each document object will build up its own InList that can + * be used during recomputation to query which other document objects need to + * be computed. + * + * The OutList and InList can be accessed by the methods @ref + * App::DocumentObject::getOutList() "getOutList()" and @ref + * App::DocumentObject::getInList() "getInList()". + * + * @section SecDocumentObjectProperties Properties + * + * The document object introduces three properties: + * + * - @ref App::DocumentObject::Label "Label" which is a string that identifies + * the object in the document and is used to display the object in the GUI. + * - @ref App::DocumentObject::Label2 "Label2" which is a string that contains + * additional information about the object and is used for the description of + * the object. + * - @ref App::DocumentObject::ExpressionEngine "ExpressionEngine" which + * contains a mapping from @ref App::ObjectIdentifier "ObjectIdentifier" to + * @ref App::Expression "Expression". The object identifier defines the + * properties that store the result of the expression. + * - @ref App::DocumentObject::Visibility "Visibility" which is a boolean + * that indicates whether the object is visible in App namespace. + * + * @section SecDocumentObjectSignals Signals + * + * A document object has three signals that can be connected to: + * + * - @ref App::DocumentObject::signalBeforeChange "signalBeforeChange" which is + * emitted before a property of the document object is changed. This signal + * can be used to prepare for changes to the document object. + * - @ref App::DocumentObject::signalChanged "signalChanged" which is emitted + * after a property of the document object has changed. + * - @ref App::DocumentObject::signalEarlyChanged "signalEarlyChanged" which + * is also emitted after a property has changed but emitted right before + * "signalChanged". + * + * Note that at the @ref App::Document "Document" level, there are also similar + * signals. + * + * @section SecDocumentObjectSeparation Separation App/Gui + * + * In FreeCAD there is a strict separation between the App and Gui parts. A + * @ref App::DocumentObject "DocumentObject" typically has a Gui::ViewProvider + * class associated with it. The method @ref + * App::DocumentObject::getViewProviderName() "getViewProviderName()" defines + * what the type of the class of the view provider is, allowing the Gui to + * retrieve the type and construct a view provider. + * + * Another intresting aspect of the separation is that the property @ref + * App::DocumentObject::Visibility "Visibility" is defined both in + * App::DocumentObject and in @ref Gui::ViewProviderDocumentObject::Visibility + * "Gui::ViewProviderDocumentObject". */ /**