/*************************************************************************** * Copyright (c) Jürgen Riegel (juergen.riegel@web.de) 2002 * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #ifndef APP_PROPERTYLINKS_H #define APP_PROPERTYLINKS_H // Std. configurations #include #include #include #include #include #include #include "Property.h" namespace Base { class Writer; } namespace App { class DocumentObject; class Document; class DocInfo; typedef std::shared_ptr DocInfoPtr; class PropertyXLink; /** * @brief Defines different scopes for which a link can be valid * The scopes defined in this enum describe the different possibilities of where a link can point to. * Local: links are valid only within the same GeoFeatureGroup as the linkowner is in or in none. * Child: links are valid within the same or any sub GeoFeatureGroup * Global: all possible links are valid * Hidden: links are not included in dependency calculation */ enum class LinkScope { Local, Child, Global, Hidden, }; /** * @brief Enables scope handling for links * This class is a base for all link properties and enables them to handle scopes of the linked objects. * The possible scopes are defined by LinkScope enum class. The default value is Local. * The scope of a property is not saved in the document. It is a value that needs to be fixed when * the object holding the property is loaded. That is possible with two methods: * 1. Set the scope value in the constructor of the link property * 2. Use setScope to change the scope in the constructor of the link property * * The second option is only available in c++, not in python, as setscope is not exposed. It would * not make sense to expose it there, as restoring python objects does not call the constructor again. * Hence in python the only way to create a LinkProperty with different scope than local is to use a * specialized property for that. In c++ existing properties can simply be changed via setScope in the * objects constructor. */ class AppExport ScopedLink { public: /** * @brief Set the links scope * Allows to define what kind of links are allowed. Only in the Local GeoFeatureGroup, in this and * all Childs or to all objects within the Glocal scope. */ void setScope(LinkScope scope) {_pcScope = scope;}; /** * @brief Get the links scope * Retrieve what kind of links are allowed. Only in the Local GeoFeatureGroup, in this and * all Childs or to all objects within the Glocal scope. */ LinkScope getScope() {return _pcScope;}; protected: LinkScope _pcScope = LinkScope::Local; }; /// Parent class of all link type properties class AppExport PropertyLinkBase : public Property, public ScopedLink { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: typedef std::pair ShadowSub; PropertyLinkBase(); virtual ~PropertyLinkBase(); friend class DocInfo; /** Link type property interface APIs * These APIs are moved here so that any type of property can have the * property link behavior, e.g. the PropertyExpressionEngine */ //@{ /** Called to update the element reference of this link property * * @sa _updateElementReference() */ virtual void updateElementReference(App::DocumentObject *feature, bool reverse=false, bool notify=false) { (void)feature; (void)reverse; (void)notify; } /// Clear internal element reference registration void unregisterElementReference(); /** Register label reference for future object relabel update * * @param labels: labels to be registered * @param reset: if true, then calls unregisterLabelReference() before * registering */ void registerLabelReferences(std::vector &&labels, bool reset=true); /** Check subnames for label registration * * @param subs: subname references * @param reset: if true, then calls unregisterLabelReference() before * registering * * Check the give subname references and extract any label reference * inside (by calling getLabelReferences()), and register them. */ void checkLabelReferences(const std::vector &subs, bool reset=true); /// Clear internal label references registration void unregisterLabelReferences(); /// Test if the element reference has changed after restore virtual bool referenceChanged() const { return false; } /** Test if the link is restored unchanged * * @param msg: optional error message * * @return For external linked object, return 2 in case the link is * missing, and 1 if the time stamp has changed. */ virtual int checkRestore(std::string *msg=0) const { (void)msg; return 0; } /** Obtain the linked objects * * @param objs: hold the returned linked objects on output * @param all: if true, then return all the linked object regardless of * this LinkScope. If false, then return only if the LinkScope * is not hidden. * @param sub: if given, then return subname references. * @param newStyle: whether to return new or old style subname reference */ virtual void getLinks(std::vector &objs, bool all=false, std::vector *subs=0, bool newStyle=true) const = 0; /** Called to reset this link property * * @param obj: reset link property if it is linked to this object * @param clear: if true, then also reset property if the owner of this property is \a obj * * @sa breakLinks() */ virtual void breakLink(App::DocumentObject *obj, bool clear) = 0; /** Called to adjust the link to avoid potential cyclic dependency * * @param inList: recursive in-list of the would-be parent * * @return Return whether the link has been adjusted * * This function tries to correct the link to avoid any (sub)object inside * in-list. If the adjustment is impossible, exception will be raised */ virtual bool adjustLink(const std::set &inList) = 0; /** Return a copy of the property if the link replacement affects this property * * @param owner: the parent object whose link property is to be replace. * Note that The parent may not be the container of this * property. Link sub property can use this opportunity to * adjust its relative links. * @param oldObj: object to be replaced * @param newObj: object to replace with * * @return Return a copy of the property that is adjusted for the link * replacement operation. */ virtual Property *CopyOnLinkReplace(const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const = 0; /** Return a copy of the property if any changes caused by importing external linked object * * @param nameMap: a map from the original external object name to the * imported new object name * * @return Returns a copy of the property with the updated link reference if * affected. The copy will later be assgiend to this property by calling its * Paste(). */ virtual Property *CopyOnImportExternal(const std::map &nameMap) const { (void)nameMap; return 0; } /** Update object label reference in this property * * @param obj: the object owner of the changing label * @param ref: subname reference to old label * @param newLabel: the future new label * * @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(). */ virtual Property *CopyOnLabelChange(App::DocumentObject *obj, const std::string &ref, const char *newLabel) const { (void)obj; (void)ref; (void)newLabel; return 0; } /// Helper function to return all linked objects of this property std::vector linkedObjects(bool all=false) const { std::vector ret; getLinks(ret,all); return ret; } /// Helper function to return linked objects using an std::inserter template void getLinkedObjects(T &inserter, bool all=false) const { std::vector ret; getLinks(ret,all); std::copy(ret.begin(),ret.end(),inserter); } /// Helper function to return a map of linked object and its subname references void getLinkedElements(std::map > &elements, bool newStyle=true, bool all=true) const { std::vector ret; std::vector subs; getLinks(ret,all,&subs,newStyle); assert(ret.size()==subs.size()); int i=0; for(auto obj : ret) elements[obj].push_back(subs[i++]); } /// Helper function to return a map of linked object and its subname references std::map > linkedElements(bool newStyle=true, bool all=true) const { std::map > ret; getLinkedElements(ret,newStyle,all); return ret; } //@} /** Enable/disable temporary holding external object without throwing exception * * Warning, non-PropertyXLink related property does not have internal * tracking of external objects, therefore the link will not by auto broken * when external document is closed. Only use this for temporary case, or * if you handle signalDeleteDocument yourself, or use one of the * PropertyXLink related property. */ void setAllowExternal(bool allow); /// Helper functions //@{ /** Helper function to check and replace a link * * @param owner: the owner of the current property * @param obj: the current linked object * @param parent: the parent of the changing link property, may or may not * be equal to \c owner * @param oldObj: the object to be replaced * @param newObj: the object to replace with * @param sub: optional the current subname reference * * @return Returns a pair(obj,subname). If no replacement is found, * pair.first will be NULL * * Say a group has one of its child object replaced with another. Any * existing link sub reference that refer to the original child object * through the group will be broken. This helper function is used to check * and correct any link sub reference. */ static std::pair tryReplaceLink( const App::PropertyContainer *owner, App::DocumentObject *obj, const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj, const char *sub=0); /** Helper function to check and replace a link with multiple subname references * * @param owner: the owner of the current property * @param obj: the current linked object * @param parent: the parent of the changing link property, may or may not * be equal to \c owner * @param oldObj: the object to be replaced * @param newObj: the object to replace with * @param subs: the current subname references * * @return Returns the a pair(obj,subs). If no replacement is found, * pair.first will be NULL * @sa tryReplaceLink() */ static std::pair > tryReplaceLinkSubs( const App::PropertyContainer *owner, App::DocumentObject *obj, const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj, const std::vector &subs); /// Update all element references in all link properties of \a feature static void updateElementReferences(DocumentObject *feature, bool reverse=false); /** Helper function for update individual element reference * * @param feature: if given, than only update element reference belonging * to this feature. If not, then update geometry element * references. * @param sub: the subname reference to be updated. * @param shadow: a pair of new and old style element references to be updated. * @param reverse: if true, then use the old style, i.e. non-mapped element * reference to query for the new style, i.e. mapped * element reference when update. If false, then the other * way around. * @param notify: if true, call aboutToSetValue() before change * * This helper function is to be called by each link property in the event of * geometry element reference change due to geometry model changes. */ bool _updateElementReference(App::DocumentObject *feature, App::DocumentObject *obj, std::string &sub, ShadowSub &shadow, bool reverse, bool notify=false); /** Helper function to register geometry element reference * * @param obj: the linked object * @param sub: the subname reference * @param shadow: a pair of new and old style element references to be updated. * * Search for any geometry element reference inside the subname, and * register for future update in case of geometry model update. */ void _registerElementReference(App::DocumentObject *obj, std::string &sub, ShadowSub &shadow); /** Helper function for breaking link properties * * @param link: reset link property if it is linked to this object * @param objs: the objects to check for the link properties * @param clear: if true, then also reset property if the owner of the link property is \a link * * App::Document::breakDependency() calls this function to break the link property */ static void breakLinks(App::DocumentObject *link, const std::vector &objs, bool clear); /** Helper function for link import operation * * @param obj: the linked object * @param sub: subname reference * @param doc: importing document * @param nameMap: a name map from source object to its imported counter part * * @return Return a changed subname reference, or empty string if no change. * * Link import operation will go through all link property and imports all * externally linked object. After import, the link property must be * changed to point to the newly imported objects, which should happen inside * the API CopyOnImportExternal(). This function helps to rewrite subname * reference to point to the correct sub objects that are imported. */ static std::string tryImportSubName(const App::DocumentObject *obj, const char *sub, const App::Document *doc, const std::map &nameMap); /** Helper function for link import operation * * @param doc: owner document of the imported objects * @param obj: the linked object * @param nameMap: a name map from source object to its imported counter part * * @return Return the imported object if found, or the input \c obj if no change. * @sa tryImportSubNames * * This function searches for the name map and tries to find the imported * object from the given source object. */ static App::DocumentObject *tryImport(const App::Document *doc, const App::DocumentObject *obj, const std::map &nameMap); /** Helper function to export a subname reference * * @param output: output subname if the subname is modified * @param obj: linked object * @param sub: input subname reference * @param first_obj: if true, then the first object referenced in subname * is obtained by searching the owner document of obj, * otherwise the subname search among obj's sub-objects. * * @return Return output.c_str() if the subname is modified for exporting * otherwise, return the input subname * * @sa importSubName(), restoreLabelReference() * * The function go through the input subname reference and changes any sub * object references inside for exporting. If the sub object is referenced * by its internal object name, then the reference is changed from * 'objName' to 'objName@docName'. If referenced by label, then it will be * changed to 'objName@docName@' instead. importSubName() and * restoreLabelReference() can be used together to restore the reference * during import. */ static const char *exportSubName(std::string &output, const App::DocumentObject *obj, const char *subname, bool first_obj=false); /** Helper function to import a subname reference * * @param reader: the import reader * @param sub: input subname reference * @param restoreLabel: output indicate whether post process is required * after restore. * * @sa exportSubName(), restoreLabelReference() * * @return return either an updated subname reference or the input * reference if no change. If restoreLabel is set to true on output, it * means there are some label reference changes that must be corrected * after restore, by calling restoreLabelReference() in property's * afterRestore(). */ static std::string importSubName(Base::XMLReader &reader, const char *sub, bool &restoreLabel); /** Helper function to restore label references during import * * @param obj: linked object * @param sub: subname reference * @param shadow: optional shadow subname reference * * @sa exportSubName(), importSubName() * * When exporting and importing (i.e. copy and paste) objects into the same * document, the new object must be renamed, both the internal name and the * label. Therefore, the link reference of the new objects must be * corrected accordingly. The basic idea is that when exporting object, all * object name references are changed to 'objName@docName', and label * references are changed to 'objName@docName@'. During import, * MergeDocument will maintain a map from objName@docName to object's new * name. Object name reference can be restored on spot by consulting the * map, while label reference will be restored later in property's * afterRestore() function, which calls this function to do the string * parsing. */ static void restoreLabelReference(const App::DocumentObject *obj, std::string &sub, ShadowSub *shadow=0); /** Helper function to extract labels from a subname reference * * @param labels: output vector of extracted labels * @param subname: subname reference * * @sa registerLabelReferences() * * This function is used to extract label from subname reference for * registering of label changes. */ static void getLabelReferences(std::vector &labels, const char *subname); /** Helper function to collect changed property when an object re-label * * @param obj: the object that owns the label * @param newLabel: the new label * * @return return a map from the affected property to a copy of it with * updated subname references */ static std::vector > > updateLabelReferences( App::DocumentObject *obj, const char *newLabel); /** Helper function to update subname reference on label change * * @param linked: linked object * @param subname: subname reference * @param obj: the object that owns the label * @param ref: label reference in the format of '$.', which is * the format used in subname reference for label reference. * This parameter is provided for easy search of label * reference. * @param newLabel: new label * * @return Returns an updated subname reference, or empty string if no change. * * This function helps to update subname reference on label change. It is * usually called inside CopyOnLabelChange(), the API for handling label * change, which is called just before label change. In other word, when * called, the sub object can still be reached using the original label * references, but not the new labels. */ static std::string updateLabelReference(const App::DocumentObject *linked, const char *subname, App::DocumentObject *obj, const std::string &ref, const char *newLabel); //@} enum LinkFlags { LinkAllowExternal, LinkDetached, LinkRestoring, LinkAllowPartial, LinkRestoreLabel, }; inline bool testFlag(int flag) const { return _Flags.test((std::size_t)flag); } virtual void setAllowPartial(bool enable) { (void)enable; } protected: virtual void hasSetValue() override; protected: std::bitset<32> _Flags; inline void setFlag(int flag, bool value=true) { _Flags.set((std::size_t)flag,value); } private: std::set _LabelRefs; std::set _ElementRefs; }; /** The general Link Property * Main Purpose of this property is to Link Objects and Features in a document. Like all links this * property is scope aware, meaning it does define which objects are allowed to be linked depending * of the GeoFeatureGroup where it is in. Default is Local. * * @note Links that are invalid in respect to the scope of this property is set to are not rejected. * They are only detected to be invalid and prevent the feature from recomputing. */ class AppExport PropertyLink : public PropertyLinkBase { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: /** * A constructor. * A more elaborate description of the constructor. */ PropertyLink(); /** * A destructor. * A more elaborate description of the destructor. */ virtual ~PropertyLink(); void resetLink(); /** Sets the property */ virtual void setValue(App::DocumentObject *); /** This method returns the linked DocumentObject */ App::DocumentObject * getValue(void) const; /** Returns the link type checked */ App::DocumentObject * getValue(Base::Type t) const; /** Returns the link type checked */ template inline _type getValue(void) const { return _pcLink ? dynamic_cast<_type>(_pcLink) : 0; } virtual PyObject *getPyObject(void) override; virtual void setPyObject(PyObject *) override; virtual void Save (Base::Writer &writer) const override; virtual void Restore(Base::XMLReader &reader) override; virtual Property *Copy(void) const override; virtual void Paste(const Property &from) override; virtual unsigned int getMemSize (void) const override { return sizeof(App::DocumentObject *); } virtual const char* getEditorName(void) const override { return "Gui::PropertyEditor::PropertyLinkItem"; } virtual void getLinks(std::vector &objs, bool all=false, std::vector *subs=0, bool newStyle=true) const override; virtual void breakLink(App::DocumentObject *obj, bool clear) override; virtual bool adjustLink(const std::set &inList) override; virtual Property *CopyOnLinkReplace(const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const override; protected: App::DocumentObject *_pcLink; }; /** The general Link Property with Child scope */ class AppExport PropertyLinkChild : public PropertyLink { TYPESYSTEM_HEADER(); public: PropertyLinkChild() {_pcScope = LinkScope::Child;}; }; /** The general Link Property with Global scope */ class AppExport PropertyLinkGlobal : public PropertyLink { TYPESYSTEM_HEADER(); public: PropertyLinkGlobal() {_pcScope = LinkScope::Global;}; }; /** The general Link Property that are hidden from dependency checking */ class AppExport PropertyLinkHidden : public PropertyLink { TYPESYSTEM_HEADER(); public: PropertyLinkHidden() {_pcScope = LinkScope::Hidden;}; }; class AppExport PropertyLinkListBase: public PropertyLinkBase, public PropertyListsBase { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: virtual void setPyObject(PyObject *obj) override { _setPyObject(obj); } }; class AppExport PropertyLinkList : public PropertyListsT, PropertyLinkListBase> { TYPESYSTEM_HEADER_WITH_OVERRIDE(); typedef PropertyListsT,PropertyLinkListBase> inherited; public: /** * A constructor. * A more elaborate description of the constructor. */ PropertyLinkList(); /** * A destructor. * A more elaborate description of the destructor. */ virtual ~PropertyLinkList(); virtual void setSize(int newSize) override; virtual void setSize(int newSize, const_reference def) override; /** Sets the property */ void setValues(const std::vector&) override; void set1Value(int idx, DocumentObject * const &value) override; virtual PyObject *getPyObject(void) override; virtual void Save(Base::Writer &writer) const override; virtual void Restore(Base::XMLReader &reader) override; virtual Property *Copy(void) const override; virtual void Paste(const Property &from) override; virtual unsigned int getMemSize(void) const override; virtual const char* getEditorName(void) const override { return "Gui::PropertyEditor::PropertyLinkListItem"; } virtual void getLinks(std::vector &objs, bool all=false, std::vector *subs=0, bool newStyle=true) const override; virtual void breakLink(App::DocumentObject *obj, bool clear) override; virtual bool adjustLink(const std::set &inList) override; virtual Property *CopyOnLinkReplace(const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const override; DocumentObject *find(const std::string &, int *pindex=0) const; DocumentObject *find(const char *sub, int *pindex=0) const { if(!sub) return 0; return find(std::string(sub),pindex); } protected: DocumentObject *getPyValue(PyObject *item) const override; protected: mutable std::map _nameMap; }; /** The general Link Property with Child scope */ class AppExport PropertyLinkListChild : public PropertyLinkList { TYPESYSTEM_HEADER(); public: PropertyLinkListChild() {_pcScope = LinkScope::Child;}; }; /** The general Link Property with Global scope */ class AppExport PropertyLinkListGlobal : public PropertyLinkList { TYPESYSTEM_HEADER(); public: PropertyLinkListGlobal() {_pcScope = LinkScope::Global;}; }; /** The general Link Property that are hidden from dependency checking */ class AppExport PropertyLinkListHidden : public PropertyLinkList { TYPESYSTEM_HEADER(); public: PropertyLinkListHidden() {_pcScope = LinkScope::Hidden;}; }; class PropertyXLinkSub; /** the Link Property with sub elements * This property links an object and a defined sequence of * sub elements. These subelements (like Edges of a Shape) * are stored as names, which can be resolved by the * ComplexGeoDataType interface to concrete sub objects. */ class AppExport PropertyLinkSub : public PropertyLinkBase { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: /** * A constructor. * A more elaborate description of the constructor. */ PropertyLinkSub(); /** * A destructor. * A more elaborate description of the destructor. */ virtual ~PropertyLinkSub(); virtual void afterRestore() override; virtual void onContainerRestored() override; /** Sets the property */ void setValue(App::DocumentObject *,const std::vector &SubList, std::vector &&ShadowSubList={}); void setValue(App::DocumentObject *,std::vector &&SubList={}, std::vector &&ShadowSubList={}); /** This method returns the linked DocumentObject */ App::DocumentObject * getValue(void) const; /// return the list of sub elements const std::vector& getSubValues(void) const; /// return the list of sub elements with mapped names const std::vector &getShadowSubs() const { return _ShadowSubList; } std::vector getSubValues(bool newStyle) const; /// return the list of sub elements starts with a special string std::vector getSubValuesStartsWith(const char*, bool newStyle=false) const; /** Returns the link type checked */ App::DocumentObject * getValue(Base::Type t) const; /** Returns the link type checked */ template inline _type getValue(void) const { return _pcLinkSub ? dynamic_cast<_type>(_pcLinkSub) : 0; } virtual PyObject *getPyObject(void) override; virtual void setPyObject(PyObject *) override; virtual void Save (Base::Writer &writer) const override; virtual void Restore(Base::XMLReader &reader) override; virtual Property *Copy(void) const override; virtual void Paste(const Property &from) override; /// Return a copy of the property if any changes caused by importing external object virtual Property *CopyOnImportExternal(const std::map &nameMap) const override; virtual Property *CopyOnLabelChange(App::DocumentObject *obj, const std::string &ref, const char *newLabel) const override; virtual Property *CopyOnLinkReplace(const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const override; virtual unsigned int getMemSize (void) const override { return sizeof(App::DocumentObject *); } virtual void updateElementReference( DocumentObject *feature,bool reverse=false, bool notify=false) override; virtual bool referenceChanged() const override; virtual void getLinks(std::vector &objs, bool all=false, std::vector *subs=0, bool newStyle=true) const override; virtual void breakLink(App::DocumentObject *obj, bool clear) override; virtual bool adjustLink(const std::set &inList) override; protected: App::DocumentObject* _pcLinkSub; std::vector _cSubList; std::vector _ShadowSubList; std::vector _mapped; bool _restoreLabel; }; /** The general Link Property with Child scope */ class AppExport PropertyLinkSubChild : public PropertyLinkSub { TYPESYSTEM_HEADER(); public: PropertyLinkSubChild() {_pcScope = LinkScope::Child;}; }; /** The general Link Property with Global scope */ class AppExport PropertyLinkSubGlobal : public PropertyLinkSub { TYPESYSTEM_HEADER(); public: PropertyLinkSubGlobal() {_pcScope = LinkScope::Global;}; }; /** The general Link Property that are hidden from dependency checking */ class AppExport PropertyLinkSubHidden : public PropertyLinkSub { TYPESYSTEM_HEADER(); public: PropertyLinkSubHidden() {_pcScope = LinkScope::Hidden;}; }; class AppExport PropertyLinkSubList : public PropertyLinkBase { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: typedef std::pair > SubSet; /** * A constructor. * A more elaborate description of the constructor. */ PropertyLinkSubList(); /** * A destructor. * A more elaborate description of the destructor. */ virtual ~PropertyLinkSubList(); virtual void afterRestore() override; virtual void onContainerRestored() override; int getSize(void) const; void setSize(int newSize); /** Sets the property. * setValue(0, whatever) clears the property */ void setValue(DocumentObject*,const char*); void setValues(const std::vector&,const std::vector&); void setValues(const std::vector&,const std::vector&, std::vector &&ShadowSubList={}); void setValues(std::vector&&, std::vector &&subs, std::vector &&ShadowSubList={}); /** * @brief setValue: PropertyLinkSub-compatible overload * @param SubList */ void setValue(App::DocumentObject *lValue, const std::vector &SubList=std::vector()); const std::vector &getValues(void) const { return _lValueList; } const std::string getPyReprString() const; /** * @brief getValue emulates the action of a single-object link. * @return reference to object, if the link is to only one object. NULL if * the link is empty, or links to subelements of more than one document * object. */ DocumentObject* getValue() const; const std::vector &getSubValues(void) const { return _lSubList; } std::vector getSubValues(bool newStyle) const; const std::vector &getShadowSubs() const { return _ShadowSubList; } /** * @brief Removes all occurrences of \a lValue in the property * together with its sub-elements and returns the number of entries removed. */ int removeValue(App::DocumentObject *lValue); void setSubListValues(const std::vector&); std::vector getSubListValues(bool newStyle=false) const; virtual PyObject *getPyObject(void) override; virtual void setPyObject(PyObject *) override; virtual void Save (Base::Writer &writer) const override; virtual void Restore(Base::XMLReader &reader) override; virtual Property *Copy(void) const override; virtual void Paste(const Property &from) override; /// Return a copy of the property if any changes caused by importing external object virtual Property *CopyOnImportExternal(const std::map &nameMap) const override; virtual Property *CopyOnLabelChange(App::DocumentObject *obj, const std::string &ref, const char *newLabel) const override; virtual Property *CopyOnLinkReplace(const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const override; virtual unsigned int getMemSize (void) const override; virtual void updateElementReference( DocumentObject *feature,bool reverse=false, bool notify=false) override; virtual bool referenceChanged() const override; virtual void getLinks(std::vector &objs, bool all=false, std::vector *subs=0, bool newStyle=true) const override; virtual void breakLink(App::DocumentObject *obj, bool clear) override; virtual bool adjustLink(const std::set &inList) override; private: //FIXME: Do not make two independent lists because this will lead to some inconsistencies! std::vector _lValueList; std::vector _lSubList; std::vector _ShadowSubList; std::vector _mapped; }; /** The general Link Property with Child scope */ class AppExport PropertyLinkSubListChild : public PropertyLinkSubList { TYPESYSTEM_HEADER(); public: PropertyLinkSubListChild() {_pcScope = LinkScope::Child;}; }; /** The general Link Property with Global scope */ class AppExport PropertyLinkSubListGlobal : public PropertyLinkSubList { TYPESYSTEM_HEADER(); public: PropertyLinkSubListGlobal() {_pcScope = LinkScope::Global;}; }; /** The general Link Property that are hidden from dependency checking */ class AppExport PropertyLinkSubListHidden : public PropertyLinkSubList { TYPESYSTEM_HEADER(); public: PropertyLinkSubListHidden() {_pcScope = LinkScope::Hidden;}; }; class PropertyXLinkSubList; /** Link to an (sub)object in the same or different document */ class AppExport PropertyXLink : public PropertyLinkGlobal { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: PropertyXLink(bool allowPartial=false, PropertyLinkBase *parent=0); virtual ~PropertyXLink(); PropertyLinkBase *parent() const { return parentProp; } virtual void afterRestore() override; virtual void onContainerRestored() override; void setValue(App::DocumentObject *) override; void setValue(App::DocumentObject *, const char *subname); void setValue(std::string &&filePath, std::string &&objectName, std::vector &&SubList, std::vector &&ShadowSubList = {}); void setValue(App::DocumentObject *,std::vector &&SubList, std::vector &&ShadowSubList = {}); void setValue(App::DocumentObject *,const std::vector &SubList, std::vector &&ShadowSubList={}); void setSubValues(std::vector &&SubList, std::vector &&ShadowSubList = {}); const char *getSubName(bool newStyle=true) const; void setSubName(const char *subname); bool hasSubName() const {return !_SubList.empty();} App::Document *getDocument() const; const char *getDocumentPath() const; const char *getObjectName() const; virtual int checkRestore(std::string *msg=0) const override; virtual void Save (Base::Writer &writer) const override; virtual void Restore(Base::XMLReader &reader) override; virtual Property *Copy(void) const override; virtual void Paste(const Property &from) override; /// Return a copy of the property if any changes caused by importing external object virtual Property *CopyOnImportExternal(const std::map &nameMap) const override; virtual Property *CopyOnLabelChange(App::DocumentObject *obj, const std::string &ref, const char *newLabel) const override; virtual Property *CopyOnLinkReplace(const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const override; virtual PyObject *getPyObject(void) override; virtual void setPyObject(PyObject *) override; friend class DocInfo; static bool supportXLink(const App::Property *prop); static bool hasXLink(const App::Document *doc); static bool hasXLink(const std::vector &objs, std::vector *unsaved=0); static std::map > getDocumentOutList(App::Document *doc=0); static std::map > getDocumentInList(App::Document *doc=0); virtual void updateElementReference( DocumentObject *feature,bool reverse=false, bool notify=false) override; virtual bool referenceChanged() const override; virtual void getLinks(std::vector &objs, bool all=false, std::vector *subs=0, bool newStyle=true) const override; virtual bool adjustLink(const std::set &inList) override; const std::vector& getSubValues(void) const { return _SubList; } const std::vector &getShadowSubs() const { return _ShadowSubList; } std::vector getSubValues(bool newStyle) const; std::vector getSubValuesStartsWith(const char*, bool newStyle=false) const; virtual void setAllowPartial(bool enable) override; const char *getFilePath() const { return filePath.c_str(); } protected: void unlink(); void detach(); void restoreLink(App::DocumentObject *); virtual PropertyXLink *createInstance() const; virtual bool upgrade(Base::XMLReader &reader, const char *typeName); void copyTo(PropertyXLink &other, App::DocumentObject *linked=0, std::vector *subs=0) const; virtual void aboutToSetValue() override; virtual void hasSetValue() override; friend class PropertyXLinkSubList; protected: DocInfoPtr docInfo; std::string filePath; std::string docName; std::string objectName; std::string stamp; std::vector _SubList; std::vector _ShadowSubList; std::vector _mapped; PropertyLinkBase *parentProp; }; /** Link to one or more (sub)object from the same or different document */ class AppExport PropertyXLinkSub: public PropertyXLink { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: PropertyXLinkSub(bool allowPartial=false, PropertyLinkBase *parent=0); virtual ~PropertyXLinkSub(); virtual bool upgrade(Base::XMLReader &reader, const char *typeName) override; virtual PyObject *getPyObject(void) override; protected: virtual PropertyXLink *createInstance() const override; }; /** Link to one or more (sub)object(s) of one or more object(s) from the same or different document */ class AppExport PropertyXLinkSubList: public PropertyLinkBase { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: PropertyXLinkSubList(); virtual ~PropertyXLinkSubList(); virtual void afterRestore() override; virtual void onContainerRestored() override; int getSize(void) const; /** Sets the property. * setValue(0, whatever) clears the property */ void setValue(DocumentObject*,const char*); void setValues(const std::vector&,const std::vector&); void setValues(const std::vector&,const std::vector&); void setValues(std::map > &&); void setValues(const std::map > &); void addValue(App::DocumentObject *obj, const std::vector &SubList={}, bool reset=false); void addValue(App::DocumentObject *obj, std::vector &&SubList={}, bool reset=false); /** * @brief setValue: PropertyLinkSub-compatible overload * @param SubList */ void setValue(App::DocumentObject *lValue, const std::vector &SubList=std::vector()); std::vector getValues(void); const std::string getPyReprString() const; DocumentObject* getValue() const; const std::vector &getSubValues(App::DocumentObject *obj) const; std::vector getSubValues(App::DocumentObject *obj, bool newStyle) const; const std::vector &getShadowSubs(App::DocumentObject *obj) const; /** * @brief Removes all occurrences of \a lValue in the property * together with its sub-elements and returns the number of entries removed. */ int removeValue(App::DocumentObject *lValue); void setSubListValues(const std::vector&); const std::list &getSubListValues() const { return _Links; } virtual PyObject *getPyObject(void) override; virtual void setPyObject(PyObject *) override; virtual void Save (Base::Writer &writer) const override; virtual void Restore(Base::XMLReader &reader) override; virtual Property *Copy(void) const override; virtual void Paste(const Property &from) override; virtual Property *CopyOnImportExternal(const std::map &nameMap) const override; virtual Property *CopyOnLabelChange(App::DocumentObject *obj, const std::string &ref, const char *newLabel) const override; virtual Property *CopyOnLinkReplace(const App::DocumentObject *parent, App::DocumentObject *oldObj, App::DocumentObject *newObj) const override; virtual unsigned int getMemSize (void) const override; virtual void updateElementReference( DocumentObject *feature,bool reverse=false, bool notify=false) override; virtual bool referenceChanged() const override; virtual void getLinks(std::vector &objs, bool all=false, std::vector *subs=0, bool newStyle=true) const override; virtual void breakLink(App::DocumentObject *obj, bool clear) override; virtual bool adjustLink(const std::set &inList) override; bool upgrade(Base::XMLReader &reader, const char *typeName); virtual int checkRestore(std::string *msg=0) const override; virtual void setAllowPartial(bool enable) override; virtual void hasSetChildValue(Property &) override; virtual void aboutToSetChildValue(Property &) override; protected: std::list _Links; }; /** Abstract property that can link to multiple external objects * * @sa See PropertyExpressionEngine for example usage */ class AppExport PropertyXLinkContainer : public PropertyLinkBase { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: PropertyXLinkContainer(); ~PropertyXLinkContainer(); virtual void afterRestore() override; virtual int checkRestore(std::string *msg=0) const override; virtual void Save (Base::Writer &writer) const override; virtual void Restore(Base::XMLReader &reader) override; virtual void breakLink(App::DocumentObject *obj, bool clear) override; virtual void getLinks(std::vector &objs, bool all=false, std::vector *subs=0, bool newStyle=true) const override; bool isLinkedToDocument(const App::Document &doc) const; protected: virtual void aboutToSetChildValue(App::Property &prop) override; virtual PropertyXLink *createXLink(); virtual void onBreakLink(App::DocumentObject *obj); virtual void onAddDep(App::DocumentObject *) {} virtual void onRemoveDep(App::DocumentObject *) {} void updateDeps(std::set &&newDeps); void clearDeps(); protected: std::set _Deps; std::map > _XLinks; std::map _DocMap; bool _LinkRestored; private: struct RestoreInfo { std::unique_ptr xlink; std::string docName; std::string docLabel; }; std::unique_ptr > _XLinkRestores; }; } // namespace App #endif // APP_PROPERTYLINKS_H