From d0b1191b2db1bf0e00ca8fd61ccd14f82854876f Mon Sep 17 00:00:00 2001 From: Pieter Hijma Date: Tue, 20 May 2025 16:55:21 +0200 Subject: [PATCH 1/5] Doc: Move App::DocumentObject documentation App::DocumentObject had docstrings both in the cpp file and in the header file. All documentation has been moved to the header file. In cases where documentation existed in both the header and cpp file, the documentation has been reconciled. --- src/App/DocumentObject.cpp | 54 --------------------------- src/App/DocumentObject.h | 75 +++++++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 60 deletions(-) diff --git a/src/App/DocumentObject.cpp b/src/App/DocumentObject.cpp index 938ceb8738..403e367373 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)) { @@ -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..30f05c2486 100644 --- a/src/App/DocumentObject.h +++ b/src/App/DocumentObject.h @@ -182,13 +182,40 @@ public: /** Set the property touched -> changed, cause recomputation in Update() */ //@{ - /// set this document object touched (cause recomputation on dependent features) + + /** + * @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 to it (i.e. its InList) will be + * recomputed. If it should be forced to recompute a document object then + * use \ref enforceRecompute() instead. + */ 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 void purgeTouched() @@ -226,9 +253,25 @@ public: { 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 bool isFreezed() const @@ -555,13 +598,33 @@ public: /* Expression support */ + /** + * @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 + */ virtual void setExpression(const ObjectIdentifier& path, std::shared_ptr expr); + /** + * @brief Clear the expression of the object identifier \a path in this document object. + * @param path Target object identifier + */ void clearExpression(const ObjectIdentifier& path); + /** + * @brief Get expression information associated with \a path. + * @param path Object identifier + * @return Expression info, containing expression and optional comment. + */ virtual const PropertyExpressionEngine::ExpressionInfo 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 + */ virtual void renameObjectIdentifiers(const std::map& paths); From 788fe423e8b3e257ca59d5b2f75cc7e97ef859f8 Mon Sep 17 00:00:00 2001 From: Pieter Hijma Date: Sun, 8 Jun 2025 13:53:55 +0200 Subject: [PATCH 2/5] Doc: Improve App::DocumentObject documentation It also improves documentation from other files for relevant dependent functions. --- src/App/Application.h | 10 +- src/App/DocumentObject.cpp | 2 +- src/App/DocumentObject.h | 1105 ++++++++++++++++++++-------- src/App/DocumentObjectExtension.h | 27 +- src/App/Link.h | 38 + src/App/PropertyExpressionEngine.h | 1 - src/App/PropertyLinks.h | 32 +- 7 files changed, 883 insertions(+), 332 deletions(-) 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/DocumentObject.cpp b/src/App/DocumentObject.cpp index 403e367373..fcecdf15f0 100644 --- a/src/App/DocumentObject.cpp +++ b/src/App/DocumentObject.cpp @@ -616,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); } diff --git a/src/App/DocumentObject.h b/src/App/DocumentObject.h index 30f05c2486..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,76 +147,159 @@ 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. * - * Touching a document object does not mean to recompute it, it only means - * that other document objects that link to it (i.e. its InList) will be - * recomputed. If it should be forced to recompute a document object then - * use \ref enforceRecompute() instead. + * 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. */ void touch(bool noRecompute = false); @@ -198,8 +308,8 @@ public: * * @return true if document object is touched, false if not. */ - bool isTouched() const; + /** * @brief Enforce this document object to be recomputed. * @@ -217,38 +327,46 @@ public: * @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); @@ -273,207 +391,403 @@ public: * @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, @@ -481,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) { @@ -592,6 +974,13 @@ 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; @@ -599,40 +988,59 @@ public: /* Expression support */ /** - * @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 + * @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 the object identifier \a path in this document object. - * @param path Target object identifier + * @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 \a path. - * @param path Object identifier - * @return Expression info, containing expression and optional comment. + * @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 Invoke ExpressionEngine's renameObjectIdentifier, to possibly rewrite expressions using - * the \a paths map with current and new identifiers. + * @brief Rename object identifiers in expressions. * - * @param paths + * 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(); @@ -650,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, @@ -671,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 @@ -698,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/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); From c093fd484c5b530f1bccc8bf0832fb975e0b98c6 Mon Sep 17 00:00:00 2001 From: Pieter Hijma Date: Sun, 8 Jun 2025 17:52:41 +0200 Subject: [PATCH 3/5] Doc: Add a topic for App::DocumentObject --- src/App/core-app.dox | 110 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/src/App/core-app.dox b/src/App/core-app.dox index adb1bf8770..4b67de039e 100644 --- a/src/App/core-app.dox +++ b/src/App/core-app.dox @@ -48,7 +48,115 @@ /** * @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". */ /** From bd3a7c1368776574e536152d0bddc3cef868b556 Mon Sep 17 00:00:00 2001 From: Pieter Hijma Date: Tue, 14 Oct 2025 12:26:48 +0200 Subject: [PATCH 4/5] Doc: Improve App::Document documentation --- src/App/Document.cpp | 39 +- src/App/Document.h | 1334 ++++++++++++++++++++++++++++++++---------- src/App/Graphviz.cpp | 7 - 3 files changed, 1041 insertions(+), 339 deletions(-) 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/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, From df85b59de9a62c3914e49c0e7949790725d3e090 Mon Sep 17 00:00:00 2001 From: Pieter Hijma Date: Tue, 14 Oct 2025 14:36:19 +0200 Subject: [PATCH 5/5] Doc: Improve the topic for App::Document --- src/App/core-app.dox | 117 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 99 insertions(+), 18 deletions(-) diff --git a/src/App/core-app.dox b/src/App/core-app.dox index 4b67de039e..7dd9afc185 100644 --- a/src/App/core-app.dox +++ b/src/App/core-app.dox @@ -19,30 +19,111 @@ /** * @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". */ /**