From 16592cae480a4be2c2ca068e40b006a055904290 Mon Sep 17 00:00:00 2001 From: Pieter Hijma Date: Fri, 17 Oct 2025 15:15:26 +0200 Subject: [PATCH] Doc: Improve the App::Link documentation --- src/App/Link.h | 437 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 387 insertions(+), 50 deletions(-) diff --git a/src/App/Link.h b/src/App/Link.h index b1cae01ddb..22c604ee77 100644 --- a/src/App/Link.h +++ b/src/App/Link.h @@ -51,6 +51,12 @@ namespace App { +/** + * @brief The base class of the link extension. + * + * The functionality in this class is reused in LinkExtension, LinkElement, and + * LinkGroup. + */ class AppExport LinkBaseExtension: public App::DocumentObjectExtension { EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::LinkExtension); @@ -60,25 +66,31 @@ public: LinkBaseExtension(); ~LinkBaseExtension() override = default; + /// Whether the link has been touched (i.e. its linked object changed) PropertyBool _LinkTouched; + /// Contains the object ID of the object that owns the link. PropertyInteger _LinkOwner; + /// Cache of children of the link group. PropertyLinkList _ChildCache; // cache for plain group expansion + /// Options for the link mode. enum { - LinkModeNone, - LinkModeAutoDelete, - LinkModeAutoLink, - LinkModeAutoUnlink, + LinkModeNone, ///< No mode for the link. + LinkModeAutoDelete, ///< Delete the linked object when the link is deleted. + LinkModeAutoLink, ///< Create link elements of sub-element automatically (unused). + LinkModeAutoUnlink, ///< Unused option. }; - /** \name Parameter definition + /** + * @name Parameter definition. + * @brief Parameter definition (Name, Type, Property Type, Default, Document). * - * Parameter definition (Name, Type, Property Type, Default, Document). * The variadic is here so that the parameter can be extended by adding - * extra fields. See LINK_PARAM_EXT() for an example + * extra fields. See LINK_PARAM_EXT() for an example. + * + * @{ */ - //@{ #define LINK_PARAM_LINK_PLACEMENT(...) \ (LinkPlacement, \ @@ -229,7 +241,7 @@ public: #define LINK_PDOC(_param) BOOST_PP_TUPLE_ELEM(4, _param) #define LINK_PINDEX(_param) BOOST_PP_CAT(Prop, LINK_PNAME(_param)) - //@} + /// @} #define LINK_PARAMS \ LINK_PARAM(PLACEMENT) \ @@ -253,6 +265,7 @@ public: LINK_PARAM(COPY_ON_CHANGE_GROUP) \ LINK_PARAM(COPY_ON_CHANGE_TOUCHED) + /// The property indices. enum PropIndex { #define LINK_PINDEX_DEFINE(_1, _2, _param) LINK_PINDEX(_param), @@ -261,17 +274,49 @@ public: BOOST_PP_SEQ_FOR_EACH(LINK_PINDEX_DEFINE, _, LINK_PARAMS) PropMax }; + /** + * @brief Set a property to a given index.. + * + * @param[in] idx The property index obtained from the PropIndex enum. + * @param[in] prop The property to set. + */ virtual void setProperty(int idx, Property* prop); + + /** + * @brief Get a property by its index. + * + * @param[in] idx The property index obtained from the PropIndex enum. + * + * @return The property at the given index, or nullptr if the index is out + * of range or the property is not set. + */ Property* getProperty(int idx); + + /** + * + * @brief Get a property by its name. + * + * @param[in] name The name of the property to get. + * @return The property with the given name, or nullptr if not found. + */ Property* getProperty(const char*); + /// Information about a link property. struct PropInfo { - int index; - const char* name; - Base::Type type; - const char* doc; + int index; ///< The property index obtained from the PropIndex enum. + const char* name; ///< The name of the property. + Base::Type type; ///< The type of the property. + const char* doc; ///< The documentation string of the property. + /** + * @brief Construct a property info. + * + * @param[in] index The property index obtained from the PropIndex enum. + * @param[in] name The name of the property. + * @param[in] type The type of the property. + * @param[in] doc The documentation string of the property. + */ PropInfo(int index, const char* name, Base::Type type, const char* doc) : index(index) , name(name) @@ -292,17 +337,22 @@ public: LINK_PPTYPE(_param)::getClassTypeId(), \ LINK_PDOC(_param))); + /// Get the property info of this link. virtual const std::vector& getPropertyInfo() const; + /// A mapping from property name to its info. using PropInfoMap = std::map; + + /// Get the property info map of this link. virtual const PropInfoMap& getPropertyInfoMap() const; + /// The types for copy on change links. enum LinkCopyOnChangeType { - CopyOnChangeDisabled = 0, - CopyOnChangeEnabled = 1, - CopyOnChangeOwned = 2, - CopyOnChangeTracking = 3 + CopyOnChangeDisabled = 0, ///< No copy on change behavior. + CopyOnChangeEnabled = 1, ///< Copy on change is enabled but not necessarily in effect. + CopyOnChangeOwned = 2, ///< Copy on change is enabled and in effect. + CopyOnChangeTracking = 3 ///< Tracking copy-on-change behavior. }; #define LINK_PROP_GET(_1, _2, _param) \ @@ -327,18 +377,52 @@ public: // defines get##Name##Property() and get##Name##Value() accessor BOOST_PP_SEQ_FOR_EACH(LINK_PROP_GET, _, LINK_PARAMS) + /// Get the element list of this link. PropertyLinkList* _getElementListProperty() const; + + /// Get the element value list of this link. const std::vector& _getElementListValue() const; + /// Get the show element property. PropertyBool* _getShowElementProperty() const; + + /// Get the show element value. bool _getShowElementValue() const; + /// Get the element count property. PropertyInteger* _getElementCountProperty() const; + + /// Get the element count value. int _getElementCountValue() const; + /** + * @brief Get the linked children of this link. + * + * @param[in] filter If true, it will filter out objects that are a group. + * + * @return A vector of linked children. + */ std::vector getLinkedChildren(bool filter = true) const; + /** + * @brief Get a flattened subname. + * + * Get a flattened subname in case it references an object inside a linked + * plain group. + * + * @param[in] subname The subname to flatten. + * @return Returns the flattened subname. + */ const char* flattenSubname(const char* subname) const; + + /** + * @brief Expand the subname. + * + * Expand the subname in case it references an object inside a linked plain + * group. + * + * @param[in,out] subname The subname to expand. It will be modified in place. + */ void expandSubname(std::string& subname) const; /** @@ -354,19 +438,34 @@ public: */ DocumentObject* getLink(int depth = 0) const; + /** + * @brief Get the transformation matrix of the link. + * + * @param[in] transform If true, it will take into account the placement of + * the link or the original object, if false, it will only provide the + * scaling. + * + * @return The transformation matrix of the link. + */ Base::Matrix4D getTransform(bool transform) const; + + /// Get the scale vector of the link. Base::Vector3d getScaleVector() const; + /// Get the linked plain group if the linked object is a plain group. App::GroupExtension* linkedPlainGroup() const; + /// Whether to transform the link together with the linked object. bool linkTransform() const; + /// Get the subname of the link. const char* getSubName() const { parseSubName(); return !mySubName.empty() ? mySubName.c_str() : nullptr; } + /// Get the sub-elements of the link. const std::vector& getSubElements() const { parseSubName(); @@ -402,13 +501,56 @@ public: Property* extensionGetPropertyByName(const char* name) const override; + /** + * @brief Get the array index from a subname. + * + * @param[in] subname The subname to get the array index from. + * @param[in,out] psubname If not null, it will point to the position + * in the subname after the array index. + * + * @return The array index, or -1 if there is no array index. + */ static int getArrayIndex(const char* subname, const char** psubname = nullptr); + + /** + * @brief Get the element index from a subname. + * + * This method will acquire the element index from a subname using various + * strategies. + * + * @param[in] subname The subname to get the element index from. + * @param[in,out] psubname If not null, it will point to the position + * in the subname after the element index. + * + * @return The element index, or -1 if there is no element index. + */ int getElementIndex(const char* subname, const char** psubname = nullptr) const; + + /** + * + * @brief Get the element name from an index. + * + * This method will return the element name corresponding to the given + * index. + * + * @param[in] idx The index of the element. + * @param[out] ss The output stream to write the element name to. + */ void elementNameFromIndex(int idx, std::ostream& ss) const; + /// Get the container object of this link. DocumentObject* getContainer(); + /// Get the container object of this link (const version). const DocumentObject* getContainer() const; + /** + * @brief Set the linked object. + * + * @param[in] index The index of the link property to set. + * @param[in] obj The object to link to. + * @param[in] subname The subname to link to. + * @param[in] subs The sub-elements to link to. + */ void setLink(int index, DocumentObject* obj, const char* subname = nullptr, @@ -424,17 +566,13 @@ public: * * @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. * @@ -446,52 +584,109 @@ public: int depth = 0, bool noElement = false) const; - using LinkPropMap = std::map>; - + /// Check whether the link has a placement property. bool hasPlacement() const { return getLinkPlacementProperty() || getPlacementProperty(); } + /** + * @brief Enable the cache for child labels. + * + * @param[in] enable If -1, it will enable the cache and only clear it. If + * 0, it will clear the cache and disableit. If 1, it will enable the cache and fill it + * with the current child elements. + */ void cacheChildLabel(int enable = -1) const; + /** + * @brief Setup copy on change behavior. + * + * This static method sets up the copy on change behavior for a given object. + * + * @param[in] obj The object to set up copy on change for. + * @param[in] linked The linked object to copy from. + * @param[in,out] copyOnChangeConns The vector to store the connections for copy on change. + * @param[in] checkExisting If true, it will check the links properties + * against the properties of the linked object. + * + * @return True if the setup was successful, false otherwise. + */ static bool setupCopyOnChange(App::DocumentObject* obj, App::DocumentObject* linked, std::vector* copyOnChangeConns, bool checkExisting); + /** + * @brief Check if a property is marked as copy on change. + * + * @param[in] obj The object to check the property on. + * @param[in] prop The property to check. + * @return True if the property is marked as copy on change, false otherwise. + */ static bool isCopyOnChangeProperty(App::DocumentObject* obj, const Property& prop); + /** + * @brief Synchronize the copy on change object with the source object. + * + * This means updating the mutated copy in the copy-on-change group. + */ void syncCopyOnChange(); - /** Options used in setOnChangeCopyObject() - * Multiple options can be combined by bitwise or operator + /** + * @brief Options used in setOnChangeCopyObject() + * + * Multiple options can be combined by bitwise or operator. */ enum class OnChangeCopyOptions { - /// No options set - None = 0, - /// If set, then exclude the input from object list to copy on change, or else, include the - /// input object. - Exclude = 1, - /// If set , then apply the setting to all links to the input object, or else, apply only to - /// this link. - ApplyAll = 2, + None = 0, ///< No options set + Exclude = 1, ///< Exclude an object to be copied when the configuration changes. + ApplyAll = 2, ///< Apply the configuration to all links. }; - /** Include or exclude object from list of objects to copy on change - * @param obj: input object - * @param options: control options. @sa OnChangeCopyOptions. + /** + * @brief Include or exclude an object from the list of objects to copy on change. + * + * @param[in] obj: The object to include or exclude. + * @param[in] options: control options of type OnChangeCopyOptions. */ void setOnChangeCopyObject(App::DocumentObject* obj, OnChangeCopyOptions options); + /** + * @brief Get the list of objects that are set to be copied on change. + * + * @param[out] excludes: If not null, it will contain the objects that are + * excluded from copy-on-change. + * @param[in] src: The source of the copy on change link. If `nullptr`, it + * will use the `LinkCopyOnChangeSource` property to determine the source + * or if not a copy-on-change link, the linked object. + * + * @return Objects that depend on the source of the copy-on-change link. + */ std::vector getOnChangeCopyObjects(std::vector* excludes = nullptr, App::DocumentObject* src = nullptr); + /** + * @brief Check whether this link is configurable one. + * + * This essentially means that the linked object has copy-on-change properties. + * + * @return True if the link is configurable, false otherwise. + */ bool isLinkedToConfigurableObject() const; + /** + * @brief Monitor changes on the list of copy-on-change objects. + * + * This function has as input the list of dependencies of original + * dependencies of the copy-on-change link. It sets up connections to + * monitor these original objects, to update the copy-on-change links. + * + * @param[in] objs The list of objects to monitor. + */ void monitorOnChangeCopyObjects(const std::vector& objs); /// Check if the linked object is a copy on change @@ -500,43 +695,140 @@ public: protected: void _handleChangedPropertyName(Base::XMLReader& reader, const char* TypeName, const char* PropName); + + /// Parse the subname into mySubName and mySubElements void parseSubName() const; + + /** + * @brief Update the link when a property changes. + * + * It fullfills a role similar to DocumentObject::onChanged(). + * + * @param[in] parent The parent document object. + * @param[in] prop The property that changed. + */ void update(App::DocumentObject* parent, const Property* prop); + + /** + * @brief Check a property in case of copy-on-change. + * + * If a copy is a copy-on-change property in the parent, copy the property + * from the source to the link (in the copy-on-change group). + * + * @param[in] parent The parent document object. + * @param[in] prop The property to check. + */ void checkCopyOnChange(App::DocumentObject* parent, const App::Property& prop); + + /** + * @brief Setup copy-on-change behavior for this link. + * + * Transform a regular link into a copy-on-change link. This means that + * the linked object is copied in the copy-on-change group and that the + * linked object will point to this copy, while the copy-on-change source + * will point to the original. + * + * @param[in] parent The parent document object. + * @param[in] checkSource If true, it will check the and set the + * copy-on-change source property. + */ void setupCopyOnChange(App::DocumentObject* parent, bool checkSource = false); + + /** + * @brief Make a copy-of-change link from this link. + * + * This function retrieves the dependencies from the linked object and + * copies them. It will put these copies into the copy-on-change group and + * the linked object will be the root of these list of dependencies, making + * it equivalent to the original linked object. + * + * @return The new copy-on-change link object. + */ App::DocumentObject* makeCopyOnChange(); + + /// Sync the link elements in this link. void syncElementList(); + + /** + * @brief Detach a linked element. + * + * Depending on earlier set options, the object may be deleted. + * + * @param[in] obj The object to detach. + */ void detachElement(App::DocumentObject* obj); + + /// Detach all linked elements. void detachElements(); + + /** + * @brief Check the geo element map for a linked object. + * + * This method checks if subnames are in accordance with the geo element + * map. + * + * @param[in] obj The document object containing the link. + * @param[in] linked The linked document object. + * @param[in] pyObj The Python object corresponding to the linked object. + * @param[in] postfix The postfix that should be taken into account regarding subelements. + */ void checkGeoElementMap(const App::DocumentObject* obj, const App::DocumentObject* linked, PyObject** pyObj, const char* postfix) const; + + /// Update the connections for a group of link elements. void updateGroup(); - void slotChangedPlainGroup(const App::DocumentObject&, const App::Property&); + + /** + * @brief Slot called when a plain group changes. + * + * @param[in] obj The document object that changed. + * @param[in] prop The property that changed. + */ + void slotChangedPlainGroup(const App::DocumentObject& obj, const App::Property& prop); protected: + /// The properties for the link. std::vector props; + + /// A set of elements to hide. std::unordered_set myHiddenElements; + + /// Cached subelements. mutable std::vector mySubElements; + + /// Cached subname. mutable std::string mySubName; + /// Connections to monitor plain group changes. std::unordered_map plainGroupConns; - long prevLinkedObjectID = 0; - - mutable std::unordered_map myLabelCache; // for label based subname lookup + /// Cache for label based subname lookup. + mutable std::unordered_map myLabelCache; + /// Whether the label cache is enabled. mutable bool enableLabelCache {false}; + + /// Whether the link has old style subelement. bool hasOldSubElement {false}; + /// Connections for copy on change behavior. std::vector copyOnChangeConns; + + /// Connections for the source objects for copy on change. std::vector copyOnChangeSrcConns; + + /// Whether the link has copy on change behavior. bool hasCopyOnChange {true}; + /// Whether we are checking properties to avoid recursion. mutable bool checkingProperty = false; + + /// Whether to pause copy on change updates. bool pauseCopyOnChange = false; + /// Connection for monitoring changes on the copy on change source. fastsignals::scoped_connection connCopyOnChangeSource; }; @@ -546,6 +838,13 @@ using LinkBaseExtensionPython = ExtensionPythonT; /////////////////////////////////////////////////////////////////////////// + +/** + * @brief The link extension class. + * + * This class implements the link extension functionality and is the extension + * that makes @ref App::Link "Link" a link. + */ class AppExport LinkExtension: public LinkBaseExtension { EXTENSION_PROPERTY_HEADER_WITH_OVERRIDE(App::LinkExtension); @@ -555,20 +854,23 @@ public: LinkExtension(); ~LinkExtension() override = default; - /** \name Helpers for defining extended parameter + /** + * @name Helpers for defining extended properties. + * @brief Macros that help define properties that extend the properties of the linked object. * - * extended parameter definition - * (Name, Type, Property_Type, Default, Document, Property_Name, - * Derived_Property_Type, App_Property_Type, Group) + * Extended property definition: + * `(Name, Type, Property_Type, Default, %Document, Property_Name, + * Derived_Property_Type, App_Property_Type, Group)` * - * This helper simply reuses Name as Property_Name, Property_Type as - * Derived_Property_type, Prop_None as App_Propert_Type + * This helper simply reuses `Name` as `Property_Name`, `Property_Type` as + * `Derived_Property_type`, `Prop_None` as `App_Propert_Type`. * - * Note: Because PropertyView will merge linked object's properties into + * @note + * Because PropertyView will merge linked object's properties into * ours, we set the default group name as ' Link' with a leading space to * try to make our group before others + * {@ */ - //@{ #define LINK_ENAME(_param) BOOST_PP_TUPLE_ELEM(5, _param) #define LINK_ETYPE(_param) BOOST_PP_TUPLE_ELEM(6, _param) @@ -659,6 +961,13 @@ using LinkExtensionPython = ExtensionPythonT; /////////////////////////////////////////////////////////////////////////// + +/** + * @brief The Link class. + * + * Instances of this class represent links to other objects in the document or + * even to objects in other documents. + */ class AppExport Link: public App::DocumentObject, public App::LinkExtension { PROPERTY_HEADER_WITH_EXTENSIONS(App::Link); @@ -715,6 +1024,16 @@ using LinkPython = App::FeaturePythonT; /////////////////////////////////////////////////////////////////////////// + +/** + * @brief A class that represents an element of a link. + * + * A link with an element count greater than 0 will contain multiple links to + * the linked object, all with their own placement, scale, and visibility. + * These links are instances of this class. The link itself becomes a special + * link pointing to the same linked object, but its element list will contain + * references to the element links. + */ class AppExport LinkElement: public App::DocumentObject, public App::LinkBaseExtension { PROPERTY_HEADER_WITH_EXTENSIONS(App::LinkElement); @@ -733,10 +1052,12 @@ public: LINK_PARAM_EXT(COPY_ON_CHANGE_GROUP) \ LINK_PARAM_EXT(COPY_ON_CHANGE_TOUCHED) - // defines the actual properties + + /// Define the various properties for a link element. LINK_PROPS_DEFINE(LINK_PARAMS_ELEMENT) LinkElement(); + const char* getViewProviderName() const override { return "Gui::ViewProviderLink"; @@ -748,6 +1069,7 @@ public: inherited::onDocumentRestored(); } + /// Check whether this link element can be deleted. bool canDelete() const; void handleChangedPropertyName(Base::XMLReader& reader, @@ -759,6 +1081,7 @@ public: bool isLink() const override; + /// Get the parent link of this link element. App::Link* getLinkGroup() const; Base::Placement getPlacementOf(const std::string& sub, DocumentObject* targetObj = nullptr) override; @@ -768,6 +1091,20 @@ using LinkElementPython = App::FeaturePythonT; /////////////////////////////////////////////////////////////////////////// + +/** + * @brief A class that represents a group of links. + * + * Other than "upgrading" a normal Link to having multiple @ref + * App::LinkElement "LinkElements", a link group is a grouping for document + * objects where the group itself has a separate placement or visibility of the + * elements. + * + * A link group can contain the objects directly as children (called a simple + * group), or it can contain links to the objects. In the latter case, it is + * possible to create a group with transform links which means that the + * placement of the original objects affect the placement of the links as well. + */ class AppExport LinkGroup: public App::DocumentObject, public App::LinkBaseExtension { PROPERTY_HEADER_WITH_EXTENSIONS(App::LinkGroup); @@ -781,7 +1118,7 @@ public: LINK_PARAM_EXT(MODE) \ LINK_PARAM_EXT_ATYPE(COLORED_ELEMENTS, App::Prop_Hidden) - // defines the actual properties + /// Define the various properties of the link group. LINK_PROPS_DEFINE(LINK_PARAMS_GROUP) LinkGroup();