Implementation of Link

This patch includes the actual implementation of Link, which is
implemented as an extension named LinkBaseExtension in App namespace,
and a full view provider ViewProviderLink in Gui. The reason of not
using ViewProviderExtension is because it need full control when
display, not just extending existing functionalities.

Please see [here](https://git.io/fjPue) for more details of the
implementation.

This patch also includes a set of link manipulation commands, and a
task panel for overriding geometry element colors.
This commit is contained in:
Zheng, Lei
2019-07-11 12:15:29 +08:00
committed by wmayer
parent dddd3a95b5
commit ebf321fc47
22 changed files with 7944 additions and 6 deletions

View File

@@ -112,6 +112,7 @@
#include "Transactions.h"
#include <App/MaterialPy.h>
#include <Base/GeometryPyCXX.h>
#include "Link.h"
// If you stumble here, run the target "BuildExtractRevision" on Windows systems
// or the Python script "SubWCRev.py" on Linux based systems which builds
@@ -1835,6 +1836,10 @@ void Application::initTypes(void)
App ::GeoFeatureGroupExtensionPython::init();
App ::OriginGroupExtension ::init();
App ::OriginGroupExtensionPython ::init();
App ::LinkBaseExtension ::init();
App ::LinkBaseExtensionPython ::init();
App ::LinkExtension ::init();
App ::LinkExtensionPython ::init();
// Document classes
App ::TransactionalObject ::init();
@@ -1862,6 +1867,12 @@ void Application::initTypes(void)
App ::Line ::init();
App ::Part ::init();
App ::Origin ::init();
App ::Link ::init();
App ::LinkPython ::init();
App ::LinkElement ::init();
App ::LinkElementPython ::init();
App ::LinkGroup ::init();
App ::LinkGroupPython ::init();
// Expression classes
App ::Expression ::init();

View File

@@ -90,6 +90,7 @@ generate_from_xml(ExtensionPy)
generate_from_xml(ExtensionContainerPy)
generate_from_xml(DocumentObjectExtensionPy)
generate_from_xml(GroupExtensionPy)
generate_from_xml(LinkBaseExtensionPy)
generate_from_xml(DocumentObjectGroupPy)
generate_from_xml(GeoFeaturePy)
generate_from_xml(GeoFeatureGroupExtensionPy)
@@ -108,6 +109,7 @@ SET(FreeCADApp_XML_SRCS
ExtensionContainerPy.xml
DocumentObjectExtensionPy.xml
GroupExtensionPy.xml
LinkBaseExtensionPy.xml
DocumentObjectGroupPy.xml
DocumentObjectPy.xml
GeoFeaturePy.xml
@@ -165,6 +167,8 @@ SET(Document_CPP_SRCS
MaterialObject.cpp
MergeDocuments.cpp
TextDocument.cpp
Link.cpp
LinkBaseExtensionPyImp.cpp
)
SET(Document_HPP_SRCS
@@ -203,6 +207,7 @@ SET(Document_HPP_SRCS
MaterialObject.h
MergeDocuments.h
TextDocument.h
Link.h
)
SET(Document_SRCS
${Document_CPP_SRCS}

1390
src/App/Link.cpp Normal file

File diff suppressed because it is too large Load Diff

529
src/App/Link.h Normal file
View File

@@ -0,0 +1,529 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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_LINK_H
#define APP_LINK_H
#include <boost/signals2.hpp>
#include <boost/preprocessor/facilities/expand.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/seq/cat.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/tuple/enum.hpp>
#include "DocumentObject.h"
#include "FeaturePython.h"
#include "PropertyLinks.h"
#include "DocumentObjectExtension.h"
#include "FeaturePython.h"
#include "GroupExtension.h"
#define LINK_THROW(_type,_msg) do{\
if(FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_LOG))\
FC_ERR(_msg);\
throw _type(_msg);\
}while(0)
namespace App
{
class AppExport LinkBaseExtension : public App::DocumentObjectExtension
{
EXTENSION_PROPERTY_HEADER(App::LinkExtension);
typedef App::DocumentObjectExtension inherited;
public:
LinkBaseExtension();
virtual ~LinkBaseExtension();
PropertyBool _LinkRecomputed;
PropertyLinkList _ChildCache; // cache for plain group expansion
enum {
LinkModeNone,
LinkModeAutoDelete,
LinkModeAutoLink,
LinkModeAutoUnlink,
};
/** \name Parameter definition
*
* 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
*/
//@{
#define LINK_PARAM_LINK_PLACEMENT(...) \
(LinkPlacement, Base::Placement, App::PropertyPlacement, Base::Placement(), "Link placement", ##__VA_ARGS__)
#define LINK_PARAM_PLACEMENT(...) \
(Placement, Base::Placement, App::PropertyPlacement, Base::Placement(), \
"Alias to LinkPlacement to make the link object compatibale with other objects", ##__VA_ARGS__)
#define LINK_PARAM_OBJECT(...) \
(LinkedObject, App::DocumentObject*, App::PropertyLink, 0, "Linked object", ##__VA_ARGS__)
#define LINK_PARAM_SUB_ELEMENT(...) \
(SubElements, std::vector<std::string>, App::PropertyStringList, std::vector<std::string>(), \
"Non-object Sub-element list of the linked object, e.g. Face1", ##__VA_ARGS__)
#define LINK_PARAM_TRANSFORM(...) \
(LinkTransform, bool, App::PropertyBool, false, \
"Set to false to override linked object's placement", ##__VA_ARGS__)
#define LINK_PARAM_SCALE(...) \
(Scale, double, App::PropertyFloat, 1.0, "Scale factor", ##__VA_ARGS__)
#define LINK_PARAM_SCALE_VECTOR(...) \
(ScaleVector, Base::Vector3d, App::PropertyVector, Base::Vector3d(1,1,1), "Scale factors", ##__VA_ARGS__)
#define LINK_PARAM_PLACEMENTS(...) \
(PlacementList, std::vector<Base::Placement>, App::PropertyPlacementList, std::vector<Base::Placement>(),\
"The placement for each link element", ##__VA_ARGS__)
#define LINK_PARAM_SCALES(...) \
(ScaleList, std::vector<Base::Vector3d>, App::PropertyVectorList, std::vector<Base::Vector3d>(),\
"The scale factors for each link element", ##__VA_ARGS__)
#define LINK_PARAM_VISIBILITIES(...) \
(VisibilityList, boost::dynamic_bitset<>, App::PropertyBoolList, boost::dynamic_bitset<>(),\
"The visibility state of each link element", ##__VA_ARGS__)
#define LINK_PARAM_COUNT(...) \
(ElementCount, int, App::PropertyInteger, 0, "Link element count", ##__VA_ARGS__)
#define LINK_PARAM_ELEMENTS(...) \
(ElementList, std::vector<App::DocumentObject*>, App::PropertyLinkList, std::vector<App::DocumentObject*>(),\
"The link element object list", ##__VA_ARGS__)
#define LINK_PARAM_SHOW_ELEMENT(...) \
(ShowElement, bool, App::PropertyBool, true, "Enable link element list", ##__VA_ARGS__)
#define LINK_PARAM_MODE(...) \
(LinkMode, long, App::PropertyEnumeration, ((long)0), "Link group mode", ##__VA_ARGS__)
#define LINK_PARAM_COLORED_ELEMENTS(...) \
(ColoredElements, App::DocumentObject*, App::PropertyLinkSubHidden, \
0, "Link colored elements", ##__VA_ARGS__)
#define LINK_PARAM(_param) (LINK_PARAM_##_param())
#define LINK_PNAME(_param) BOOST_PP_TUPLE_ELEM(0,_param)
#define LINK_PTYPE(_param) BOOST_PP_TUPLE_ELEM(1,_param)
#define LINK_PPTYPE(_param) BOOST_PP_TUPLE_ELEM(2,_param)
#define LINK_PDEF(_param) BOOST_PP_TUPLE_ELEM(3,_param)
#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)\
LINK_PARAM(LINK_PLACEMENT)\
LINK_PARAM(OBJECT)\
LINK_PARAM(SUB_ELEMENT)\
LINK_PARAM(TRANSFORM)\
LINK_PARAM(SCALE)\
LINK_PARAM(SCALE_VECTOR)\
LINK_PARAM(PLACEMENTS)\
LINK_PARAM(SCALES)\
LINK_PARAM(VISIBILITIES)\
LINK_PARAM(COUNT)\
LINK_PARAM(ELEMENTS)\
LINK_PARAM(SHOW_ELEMENT)\
LINK_PARAM(MODE)\
LINK_PARAM(COLORED_ELEMENTS)
enum PropIndex {
#define LINK_PINDEX_DEFINE(_1,_2,_param) LINK_PINDEX(_param),
// defines Prop##Name enumeration value
BOOST_PP_SEQ_FOR_EACH(LINK_PINDEX_DEFINE,_,LINK_PARAMS)
PropMax
};
virtual void setProperty(int idx, Property *prop);
Property *getProperty(int idx);
Property *getProperty(const char *);
struct PropInfo {
int index;
const char *name;
Base::Type type;
const char *doc;
PropInfo(int index, const char *name,Base::Type type,const char *doc)
:index(index),name(name),type(type),doc(doc)
{}
PropInfo() {}
};
#define LINK_PROP_INFO(_1,_var,_param) \
_var.push_back(PropInfo(BOOST_PP_CAT(Prop,LINK_PNAME(_param)),\
BOOST_PP_STRINGIZE(LINK_PNAME(_param)),\
LINK_PPTYPE(_param)::getClassTypeId(), \
LINK_PDOC(_param)));
virtual const std::vector<PropInfo> &getPropertyInfo() const;
typedef std::map<std::string, PropInfo> PropInfoMap;
virtual const PropInfoMap &getPropertyInfoMap() const;
#define LINK_PROP_GET(_1,_2,_param) \
LINK_PTYPE(_param) BOOST_PP_SEQ_CAT((get)(LINK_PNAME(_param))(Value)) () const {\
auto prop = props[LINK_PINDEX(_param)];\
if(!prop) return LINK_PDEF(_param);\
return static_cast<const LINK_PPTYPE(_param) *>(prop)->getValue();\
}\
const LINK_PPTYPE(_param) *BOOST_PP_SEQ_CAT((get)(LINK_PNAME(_param))(Property)) () const {\
auto prop = props[LINK_PINDEX(_param)];\
return static_cast<const LINK_PPTYPE(_param) *>(prop);\
}\
LINK_PPTYPE(_param) *BOOST_PP_SEQ_CAT((get)(LINK_PNAME(_param))(Property)) () {\
auto prop = props[LINK_PINDEX(_param)];\
return static_cast<LINK_PPTYPE(_param) *>(prop);\
}\
// defines get##Name() and get##Name##Property() accessor
BOOST_PP_SEQ_FOR_EACH(LINK_PROP_GET,_,LINK_PARAMS)
PropertyLinkList *_getElementListProperty() const;
const std::vector<App::DocumentObject*> &_getElementListValue() const;
PropertyBool *_getShowElementProperty() const;
bool _getShowElementValue() const;
PropertyInteger *_getElementCountProperty() const;
int _getElementCountValue() const;
std::vector<DocumentObject*> getLinkedChildren(bool filter=true) const;
const char *flattenSubname(const char *subname) const;
void expandSubname(std::string &subname) const;
DocumentObject *getLink(int depth=0) const;
Base::Matrix4D getTransform(bool transform) const;
Base::Vector3d getScaleVector() const;
App::GroupExtension *linkedPlainGroup() const;
bool linkTransform() const;
const char *getSubName() const {
parseSubName();
return mySubName.size()?mySubName.c_str():0;
}
const char *getSubElement() const {
parseSubName();
return mySubElement.size()?mySubElement.c_str():0;
}
bool extensionGetSubObject(DocumentObject *&ret, const char *subname,
PyObject **pyObj=0, Base::Matrix4D *mat=0, bool transform=false, int depth=0) const override;
bool extensionGetSubObjects(std::vector<std::string>&ret, int reason) const override;
bool extensionGetLinkedObject(DocumentObject *&ret,
bool recurse, Base::Matrix4D *mat, bool transform, int depth) const override;
virtual App::DocumentObjectExecReturn *extensionExecute(void) override;
virtual short extensionMustExecute(void) override;
virtual void extensionOnChanged(const Property* p) override;
virtual void onExtendedUnsetupObject () override;
virtual void onExtendedDocumentRestored() override;
virtual int extensionSetElementVisible(const char *, bool) override;
virtual int extensionIsElementVisible(const char *) override;
virtual bool extensionHasChildElement() const override;
virtual PyObject* getExtensionPyObject(void) override;
virtual Property *extensionGetPropertyByName(const char* name) const override;
static int getArrayIndex(const char *subname, const char **psubname=0);
int getElementIndex(const char *subname, const char **psubname=0) const;
void elementNameFromIndex(int idx, std::ostream &ss) const;
DocumentObject *getContainer();
const DocumentObject *getContainer() const;
void setLink(int index, DocumentObject *obj, const char *subname=0,
const std::vector<std::string> &subs = std::vector<std::string>());
DocumentObject *getTrueLinkedObject(bool recurse,
Base::Matrix4D *mat=0,int depth=0, bool noElement=false) const;
typedef std::map<const Property*,std::pair<LinkBaseExtension*,int> > LinkPropMap;
bool hasPlacement() const {
return getLinkPlacementProperty() || getPlacementProperty();
}
void cacheChildLabel(int enable=-1) const;
protected:
void parseSubName() const;
void update(App::DocumentObject *parent, const Property *prop);
void syncElementList();
void detachElement(App::DocumentObject *obj);
void checkGeoElementMap(const App::DocumentObject *obj,
const App::DocumentObject *linked, PyObject **pyObj, const char *postfix) const;
void updateGroup();
void slotChangedPlainGroup(const App::DocumentObject &, const App::Property &);
protected:
std::vector<Property *> props;
std::unordered_set<const App::DocumentObject*> myHiddenElements;
mutable std::string mySubElement;
mutable std::string mySubName;
std::unordered_map<const App::DocumentObject*,
boost::signals2::scoped_connection> plainGroupConns;
mutable std::unordered_map<std::string,int> myLabelCache; // for label based subname lookup
mutable bool enableLabelCache;
long myOwner;
};
///////////////////////////////////////////////////////////////////////////
typedef ExtensionPythonT<LinkBaseExtension> LinkBaseExtensionPython;
///////////////////////////////////////////////////////////////////////////
class AppExport LinkExtension : public LinkBaseExtension
{
EXTENSION_PROPERTY_HEADER(App::LinkExtension);
typedef LinkBaseExtension inherited;
public:
LinkExtension();
virtual ~LinkExtension();
/** \name Helpers for defining extended parameter
*
* extended parameter 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
*
* 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)
#define LINK_EPTYPE(_param) BOOST_PP_TUPLE_ELEM(7,_param)
#define LINK_EGROUP(_param) BOOST_PP_TUPLE_ELEM(8,_param)
#define _LINK_PROP_ADD(_add_property, _param) \
_add_property(BOOST_PP_STRINGIZE(LINK_ENAME(_param)),LINK_ENAME(_param),\
(LINK_PDEF(_param)),LINK_EGROUP(_param),LINK_EPTYPE(_param),LINK_PDOC(_param));\
setProperty(LINK_PINDEX(_param),&LINK_ENAME(_param));
#define LINK_PROP_ADD(_1,_2,_param) \
_LINK_PROP_ADD(_ADD_PROPERTY_TYPE,_param);
#define LINK_PROP_ADD_EXTENSION(_1,_2,_param) \
_LINK_PROP_ADD(_EXTENSION_ADD_PROPERTY_TYPE,_param);
#define LINK_PROPS_ADD(_seq) \
BOOST_PP_SEQ_FOR_EACH(LINK_PROP_ADD,_,_seq)
#define LINK_PROPS_ADD_EXTENSION(_seq) \
BOOST_PP_SEQ_FOR_EACH(LINK_PROP_ADD_EXTENSION,_,_seq)
#define _LINK_PROP_SET(_1,_2,_param) \
setProperty(LINK_PINDEX(_param),&LINK_ENAME(_param));
#define LINK_PROPS_SET(_seq) BOOST_PP_SEQ_FOR_EACH(_LINK_PROP_SET,_,_seq)
/// Helper for defining default extended parameter
#define _LINK_PARAM_EXT(_name,_type,_ptype,_def,_doc,...) \
((_name,_type,_ptype,_def,_doc,_name,_ptype,App::Prop_None," Link"))
/** Define default extended parameter
* It simply reuses Name as Property_Name, Property_Type as
* Derived_Property_Type, and App::Prop_None as App::PropertyType
*/
#define LINK_PARAM_EXT(_param) BOOST_PP_EXPAND(_LINK_PARAM_EXT LINK_PARAM_##_param())
/// Helper for extended parameter with app property type
#define _LINK_PARAM_EXT_ATYPE(_name,_type,_ptype,_def,_doc,_atype) \
((_name,_type,_ptype,_def,_doc,_name,_ptype,_atype," Link"))
/// Define extended parameter with app property type
#define LINK_PARAM_EXT_ATYPE(_param,_atype) \
BOOST_PP_EXPAND(_LINK_PARAM_EXT_ATYPE LINK_PARAM_##_param(_atype))
/// Helper for extended parameter with derived property type
#define _LINK_PARAM_EXT_TYPE(_name,_type,_ptype,_def,_doc,_dtype) \
((_name,_type,_ptype,_def,_doc,_name,_dtype,App::Prop_None," Link"))
/// Define extended parameter with derived property type
#define LINK_PARAM_EXT_TYPE(_param,_dtype) \
BOOST_PP_EXPAND(_LINK_PARAM_EXT_TYPE LINK_PARAM_##_param(_dtype))
/// Helper for extended parameter with a different property name
#define _LINK_PARAM_EXT_NAME(_name,_type,_ptype,_def,_doc,_pname) \
((_name,_type,_ptype,_def,_doc,_pname,_ptype,App::Prop_None," Link"))
/// Define extended parameter with a different property name
#define LINK_PARAM_EXT_NAME(_param,_pname) BOOST_PP_EXPAND(_LINK_PARAM_EXT_NAME LINK_PARAM_##_param(_pname))
//@}
#define LINK_PARAMS_EXT \
LINK_PARAM_EXT(SCALE)\
LINK_PARAM_EXT(SCALES)\
LINK_PARAM_EXT(VISIBILITIES)\
LINK_PARAM_EXT(PLACEMENTS)\
LINK_PARAM_EXT(ELEMENTS)
#define LINK_PROP_DEFINE(_1,_2,_param) LINK_ETYPE(_param) LINK_ENAME(_param);
#define LINK_PROPS_DEFINE(_seq) BOOST_PP_SEQ_FOR_EACH(LINK_PROP_DEFINE,_,_seq)
// defines the actual properties
LINK_PROPS_DEFINE(LINK_PARAMS_EXT)
void onExtendedDocumentRestored() override {
LINK_PROPS_SET(LINK_PARAMS_EXT);
inherited::onExtendedDocumentRestored();
}
};
///////////////////////////////////////////////////////////////////////////
typedef ExtensionPythonT<LinkExtension> LinkExtensionPython;
///////////////////////////////////////////////////////////////////////////
class AppExport Link : public App::DocumentObject, public App::LinkExtension
{
PROPERTY_HEADER_WITH_EXTENSIONS(App::Link);
typedef App::DocumentObject inherited;
public:
#define LINK_PARAMS_LINK \
LINK_PARAM_EXT_TYPE(OBJECT, App::PropertyXLink)\
LINK_PARAM_EXT(TRANSFORM)\
LINK_PARAM_EXT(LINK_PLACEMENT)\
LINK_PARAM_EXT(PLACEMENT)\
LINK_PARAM_EXT(SUB_ELEMENT)\
LINK_PARAM_EXT(SHOW_ELEMENT)\
LINK_PARAM_EXT_TYPE(COUNT,App::PropertyIntegerConstraint)\
LINK_PARAM_EXT_ATYPE(COLORED_ELEMENTS,App::Prop_Hidden)
LINK_PROPS_DEFINE(LINK_PARAMS_LINK)
Link(void);
const char* getViewProviderName(void) const override{
return "Gui::ViewProviderLink";
}
void onDocumentRestored() override {
LINK_PROPS_SET(LINK_PARAMS_LINK);
inherited::onDocumentRestored();
}
bool canLinkProperties() const override;
};
typedef App::FeaturePythonT<Link> LinkPython;
///////////////////////////////////////////////////////////////////////////
class AppExport LinkElement : public App::DocumentObject, public App::LinkBaseExtension {
PROPERTY_HEADER_WITH_EXTENSIONS(App::LinkElement);
typedef App::DocumentObject inherited;
public:
#define LINK_PARAMS_ELEMENT \
LINK_PARAM_EXT(SCALE)\
LINK_PARAM_EXT_TYPE(OBJECT, App::PropertyXLink)\
LINK_PARAM_EXT(TRANSFORM) \
LINK_PARAM_EXT(LINK_PLACEMENT)\
LINK_PARAM_EXT(PLACEMENT)\
LINK_PARAM_EXT(SUB_ELEMENT)
// defines the actual properties
LINK_PROPS_DEFINE(LINK_PARAMS_ELEMENT)
LinkElement();
const char* getViewProviderName(void) const override{
return "Gui::ViewProviderLink";
}
void onDocumentRestored() override {
LINK_PROPS_SET(LINK_PARAMS_ELEMENT);
inherited::onDocumentRestored();
}
bool canDelete() const;
};
typedef App::FeaturePythonT<LinkElement> LinkElementPython;
///////////////////////////////////////////////////////////////////////////
class AppExport LinkGroup : public App::DocumentObject, public App::LinkBaseExtension {
PROPERTY_HEADER_WITH_EXTENSIONS(App::LinkGroup);
typedef App::DocumentObject inherited;
public:
#define LINK_PARAMS_GROUP \
LINK_PARAM_EXT(ELEMENTS)\
LINK_PARAM_EXT(PLACEMENT)\
LINK_PARAM_EXT(VISIBILITIES)\
LINK_PARAM_EXT(MODE)\
LINK_PARAM_EXT_ATYPE(COLORED_ELEMENTS,App::Prop_Hidden)
// defines the actual properties
LINK_PROPS_DEFINE(LINK_PARAMS_GROUP)
LinkGroup();
const char* getViewProviderName(void) const override{
return "Gui::ViewProviderLink";
}
void onDocumentRestored() override {
LINK_PROPS_SET(LINK_PARAMS_GROUP);
inherited::onDocumentRestored();
}
};
typedef App::FeaturePythonT<LinkGroup> LinkGroupPython;
} //namespace App
#endif // APP_LINK_H

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="DocumentObjectExtensionPy"
Name="LinkBaseExtensionPy"
TwinPointer="LinkBaseExtension"
Twin="LinkBaseExtension"
Include="App/Link.h"
Namespace="App"
FatherInclude="App/DocumentObjectExtensionPy.h"
FatherNamespace="App">
<Documentation>
<Author Licence="LGPL" Name="Zheng, Lei" EMail="realthunder.dev@gmail.com" />
<UserDocu>Link extension base class</UserDocu>
</Documentation>
<Methode Name="configLinkProperty" Keyword="true">
<Documentation>
<UserDocu>
configLinkProperty(key=val,...): property configuration
configLinkProperty(key,...): property configuration with default name
This methode is here to impelement what I called Property Design
Pattern. The extension operates on a predefined set of properties,
but it relies on the extended object to supply the actual property by
calling this methode. You can choose a sub set of functionality of
this extension by supplying only some of the supported properties.
The 'key' are names used to refer to properties supported by this
extension, and 'val' is the actual name of the property of your
object. You can obtain the key names and expected types using
getLinkPropertyInfo(). You can use property of derived type when
calling configLinkProperty(). Other types will cause exception to
ben thrown. The actual properties supported may be different
depending on the actual extension object underlying this python
object.
If 'val' is omitted, i.e. calling configLinkProperty(key,...), then
it is assumed the the actualy property name is the same as 'key'
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getLinkExtProperty">
<Documentation>
<UserDocu>getLinkExtProperty(name): return the property value by its predefined name </UserDocu>
</Documentation>
</Methode>
<Methode Name="getLinkExtPropertyName">
<Documentation>
<UserDocu>getLinkExtPropertyName(name): lookup the property name by its predefined name </UserDocu>
</Documentation>
</Methode>
<Methode Name="getLinkPropertyInfo">
<Documentation>
<UserDocu>
getLinkPropertyInfo(): return a tuple of (name,type,doc) for all supported properties.
getLinkPropertyInfo(index): return (name,type,doc) of a specific property
getLinkPropertyInfo(name): return (type,doc) of a specific property
</UserDocu>
</Documentation>
</Methode>
<Methode Name="setLink">
<Documentation>
<UserDocu>
setLink(obj,subName=None,subElements=None): Set link object.
setLink([obj,...]),
setLink([(obj,subName,subElements),...]),
setLink({index:obj,...}),
setLink({index:(obj,subName,subElements),...}): set link element of a link group.
obj (DocumentObject): the object to link to. If this is None, then the link is cleared
subName (String): Dot separated object path.
subElements (String|tuple(String)): non-object sub-elements, e.g. Face1, Edge2.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="cacheChildLabel">
<Documentation>
<UserDocu>
cacheChildLabel(enable=True): enable/disable child label cache
The cache is not updated on child label change for performance reason. You must
call this function on any child label change
</UserDocu>
</Documentation>
</Methode>
<Methode Name="flattenSubname">
<Documentation>
<UserDocu>
flattenSubname(subname) -> string
Return a flattened subname in case it references an object inside a linked plain group
</UserDocu>
</Documentation>
</Methode>
<Methode Name="expandSubname">
<Documentation>
<UserDocu>
expandSubname(subname) -> string
Return an expanded subname in case it references an object inside a linked plain group
</UserDocu>
</Documentation>
</Methode>
<CustomAttributes />
<Attribute Name="LinkedChildren" ReadOnly="true">
<Documentation>
<UserDocu>Return a flattend (in case grouped by plain group) list of linked children</UserDocu>
</Documentation>
<Parameter Name="LinkedChildren" Type="List"/>
</Attribute>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,316 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <sstream>
#endif
#include "DocumentObjectPy.h"
#include "LinkBaseExtensionPy.h"
#include "LinkBaseExtensionPy.cpp"
using namespace App;
// returns a string which represent the object e.g. when printed in python
std::string LinkBaseExtensionPy::representation(void) const
{
std::ostringstream str;
str << "<" << getLinkBaseExtensionPtr()->getExtensionClassTypeId().getName() << ">";
return str.str();
}
typedef std::map<std::string, std::pair<int,Property*> > PropTmpMap;
typedef std::map<std::string, Property*> PropMap;
static bool getProperty(PropTmpMap &props, const LinkBaseExtension::PropInfoMap &infoMap,
const PropMap &propMap, PyObject *key, PyObject *value)
{
std::ostringstream str;
#if PY_MAJOR_VERSION < 3
if(!PyString_Check(key)) {
PyErr_SetString(PyExc_TypeError, "key must be string");
return false;
}
const char *keyStr = PyString_AsString(key);
#else
if(!PyUnicode_Check(key)) {
PyErr_SetString(PyExc_TypeError, "key must be a unicode string");
return false;
}
const char *keyStr = PyUnicode_AsUTF8(key);
#endif
auto it = infoMap.find(keyStr);
if(it == infoMap.end()){
str << "unknown key '" << keyStr << "'";
PyErr_SetString(PyExc_KeyError, str.str().c_str());
return false;
}
const char *valStr = 0;
if(key == value)
valStr = keyStr;
else if (value!=Py_None) {
#if PY_MAJOR_VERSION < 3
if(!PyString_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be string");
return false;
}
valStr = PyString_AsString(value);
#else
if(!PyUnicode_Check(value)) {
PyErr_SetString(PyExc_TypeError, "value must be unicode string");
return false;
}
valStr = PyUnicode_AsUTF8(value);
#endif
}
App::Property *prop = 0;
auto &info = it->second;
if(valStr) {
auto pIt = propMap.find(valStr);
if(pIt == propMap.end()) {
str << "cannot find property '" << valStr << "'";
PyErr_SetString(PyExc_ValueError, str.str().c_str());
return false;
}
prop = pIt->second;
if(!prop->isDerivedFrom(info.type)) {
str << "expect property '" << keyStr << "(" << valStr
<< ") to be derived from '" << info.type.getName()
<< "', instead of '" << prop->getTypeId().getName() << "'";
PyErr_SetString(PyExc_TypeError, str.str().c_str());
}
}
props[keyStr] = std::make_pair(info.index,prop);
return true;
}
PyObject* LinkBaseExtensionPy::configLinkProperty(PyObject *args, PyObject *keywds) {
auto ext = getLinkBaseExtensionPtr();
const auto &info = ext->getPropertyInfoMap();
PropMap propMap;
ext->getExtendedContainer()->getPropertyMap(propMap);
PropTmpMap props;
if(args && PyTuple_Check(args)) {
for(Py_ssize_t pos=0;pos<PyTuple_GET_SIZE(args);++pos) {
auto key = PyTuple_GET_ITEM(args,pos);
if(!getProperty(props,info,propMap,key,key))
return 0;
}
}
if(keywds && PyDict_Check(keywds)) {
PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(keywds, &pos, &key, &value)) {
if(!getProperty(props,info,propMap,key,value))
return 0;
}
}
for(auto &v : props)
ext->setProperty(v.second.first,v.second.second);
Py_Return;
}
PyObject* LinkBaseExtensionPy::getLinkExtProperty(PyObject *args)
{
const char *name;
if(!PyArg_ParseTuple(args,"s",&name))
return 0;
auto prop = getLinkBaseExtensionPtr()->getProperty(name);
if(!prop) {
PyErr_SetString(PyExc_AttributeError, "unknown property name");
return 0;
}
return prop->getPyObject();
}
PyObject* LinkBaseExtensionPy::getLinkExtPropertyName(PyObject *args) {
const char *name;
if(!PyArg_ParseTuple(args,"s",&name))
return 0;
auto prop = getLinkBaseExtensionPtr()->getProperty(name);
if(!prop) {
PyErr_SetString(PyExc_AttributeError, "unknown property name");
return 0;
}
auto container = getLinkBaseExtensionPtr()->getExtendedContainer();
if(!container) {
PyErr_SetString(PyExc_RuntimeError, "no extended container");
return 0;
}
name = container->getPropertyName(prop);
if(!name) {
PyErr_SetString(PyExc_RuntimeError, "cannot find property name");
return 0;
}
return Py::new_reference_to(Py::String(name));
}
PyObject* LinkBaseExtensionPy::getLinkPropertyInfo(PyObject *args)
{
auto ext = getLinkBaseExtensionPtr();
const auto &infos = ext->getPropertyInfo();
if(PyArg_ParseTuple(args,"")) {
Py::Tuple ret(infos.size());
int i=0;
for(const auto &info : infos) {
ret.setItem(i++,Py::TupleN(Py::String(info.name),
Py::String(info.type.getName()),Py::String(info.doc)));
}
return Py::new_reference_to(ret);
}
short index = 0;
if(PyArg_ParseTuple(args,"h",&index)) {
if(index<0 || index>=(int)infos.size()) {
PyErr_SetString(PyExc_ValueError, "index out of range");
return 0;
}
Py::TupleN ret(Py::String(infos[index].name),
Py::String(infos[index].type.getName()),Py::String(infos[index].doc));
return Py::new_reference_to(ret);
}
char *name;
if(PyArg_ParseTuple(args,"s",&name)) {
for(int i=0;i<(int)infos.size();++i) {
if(strcmp(infos[i].name,name)==0) {
Py::TupleN ret(Py::String(infos[i].type.getName()),
Py::String(infos[i].doc));
return Py::new_reference_to(ret);
}
}
PyErr_SetString(PyExc_ValueError, "unknown property name");
return 0;
}
PyErr_SetString(PyExc_ValueError, "invalid arguments");
return 0;
}
void parseLink(LinkBaseExtension *ext, int index, PyObject *value) {
App::DocumentObject *obj = 0;
PropertyStringList subs;
PropertyString sub;
if(value!=Py_None) {
if(PyObject_TypeCheck(value,&DocumentObjectPy::Type)) {
obj = static_cast<DocumentObjectPy*>(value)->getDocumentObjectPtr();
}else if(!PySequence_Check(value))
throw Base::TypeError("Expects type of DocumentObject or sequence");
else{
Py::Sequence seq(value);
if(seq[0].ptr() != Py_None) {
if(!PyObject_TypeCheck(seq[0].ptr(),&DocumentObjectPy::Type))
throw Base::TypeError("Expects the first argument to be DocumentObject in sequence");
obj = static_cast<DocumentObjectPy*>(seq[0].ptr())->getDocumentObjectPtr();
if(seq.size()>1) {
sub.setPyObject(seq[1].ptr());
if(seq.size()>2)
subs.setPyObject(seq[2].ptr());
}
}
}
}
ext->setLink(index,obj,sub.getValue(),subs.getValue());
}
PyObject* LinkBaseExtensionPy::setLink(PyObject *_args)
{
Py::Sequence args(_args);
PY_TRY {
auto ext = getLinkBaseExtensionPtr();
PyObject *pcObj = args.size()?args[0].ptr():Py_None;
if(pcObj == Py_None) {
ext->setLink(-1,0);
}else if(PyDict_Check(pcObj)) {
PyObject *key, *value;
Py_ssize_t pos = 0;
while(PyDict_Next(pcObj, &pos, &key, &value))
parseLink(ext,Py::Int(key),value);
}else if(PySequence_Check(pcObj)) {
ext->setLink(-1,0);
Py::Sequence seq(pcObj);
for(size_t i=0;i<seq.size();++i)
parseLink(ext,i,seq[i].ptr());
}else
parseLink(ext,-1,_args);
Py_Return;
}PY_CATCH
}
PyObject* LinkBaseExtensionPy::cacheChildLabel(PyObject *args) {
PyObject *enable = Py_True;
if(!PyArg_ParseTuple(args,"|O",&enable))
return 0;
PY_TRY {
getLinkBaseExtensionPtr()->cacheChildLabel(PyObject_IsTrue(enable)?-1:0);
Py_Return;
}PY_CATCH;
}
PyObject* LinkBaseExtensionPy::flattenSubname(PyObject *args) {
const char *subname;
if(!PyArg_ParseTuple(args,"s",&subname))
return 0;
PY_TRY {
return Py::new_reference_to(Py::String(
getLinkBaseExtensionPtr()->flattenSubname(subname)));
}PY_CATCH;
}
PyObject* LinkBaseExtensionPy::expandSubname(PyObject *args) {
const char *subname;
if(!PyArg_ParseTuple(args,"s",&subname))
return 0;
PY_TRY {
std::string sub(subname);
getLinkBaseExtensionPtr()->expandSubname(sub);
return Py::new_reference_to(Py::String(sub));
}PY_CATCH;
}
Py::List LinkBaseExtensionPy::getLinkedChildren() const {
Py::List ret;
for(auto o : getLinkBaseExtensionPtr()->getLinkedChildren(true))
ret.append(Py::asObject(o->getPyObject()));
return ret;
}
PyObject *LinkBaseExtensionPy::getCustomAttributes(const char* /*attr*/) const
{
return 0;
}
int LinkBaseExtensionPy::setCustomAttributes(const char* /*attr*/, PyObject * /*obj*/)
{
return 0;
}

View File

@@ -282,6 +282,18 @@ public:
float transparency;
//@}
bool operator==(const Material& m) const
{
return _matType!=m._matType || shininess!=m.shininess ||
transparency!=m.transparency || ambientColor!=m.ambientColor ||
diffuseColor!=m.diffuseColor || specularColor!=m.specularColor ||
emissiveColor!=m.emissiveColor;
}
bool operator!=(const Material& m) const
{
return !operator==(m);
}
private:
MaterialType _matType;
};

View File

@@ -119,6 +119,8 @@
#include "ViewProviderMaterialObject.h"
#include "ViewProviderTextDocument.h"
#include "ViewProviderGroupExtension.h"
#include "ViewProviderLink.h"
#include "LinkViewPy.h"
#include "Language/Translator.h"
#include "TaskView/TaskView.h"
@@ -401,6 +403,7 @@ Application::Application(bool GUIenabled)
Gui::TaskView::ControlPy::init_type();
Py::Module(module).setAttr(std::string("Control"),
Py::Object(Gui::TaskView::ControlPy::getInstance(), true));
Base::Interpreter().addType(&LinkViewPy::Type,module,"LinkView");
}
Base::PyGILStateLocker lock;
@@ -668,6 +671,7 @@ void Application::createStandardOperations()
Gui::CreateWindowStdCommands();
Gui::CreateStructureCommands();
Gui::CreateTestCommands();
Gui::CreateLinkCommands();
}
void Application::slotNewDocument(const App::Document& Doc, bool isMainDoc)
@@ -1681,6 +1685,10 @@ void Application::initTypes(void)
Gui::ViewProviderMaterialObject ::init();
Gui::ViewProviderMaterialObjectPython ::init();
Gui::ViewProviderTextDocument ::init();
Gui::ViewProviderLinkObserver ::init();
Gui::LinkView ::init();
Gui::ViewProviderLink ::init();
Gui::ViewProviderLinkPython ::init();
// Workbench
Gui::Workbench ::init();

View File

@@ -210,6 +210,8 @@ generate_from_xml(ViewProviderPy)
generate_from_xml(ViewProviderDocumentObjectPy)
generate_from_xml(WorkbenchPy)
generate_from_xml(SelectionObjectPy)
generate_from_xml(LinkViewPy)
generate_from_xml(ViewProviderLinkPy)
generate_from_py(FreeCADGuiInit GuiInitScript.h)
@@ -221,6 +223,8 @@ SET(FreeCADGui_XML_SRCS
WorkbenchPy.xml
SelectionObjectPy.xml
DocumentPy.xml
LinkViewPy.xml
ViewProviderLinkPy.xml
)
SOURCE_GROUP("XML" FILES ${FreeCADApp_XML_SRCS})
@@ -361,6 +365,7 @@ set(Gui_MOC_HDRS
TaskView/TaskView.h
DAGView/DAGView.h
DAGView/DAGModel.h
TaskElementColors.h
DlgObjectSelection.h
${FreeCADGui_SDK_MOC_HDRS}
)
@@ -434,6 +439,7 @@ SET(Gui_UIC_SRCS
TextureMapping.ui
TaskView/TaskAppearance.ui
TaskView/TaskSelectLinkProperty.ui
TaskElementColors.ui
DlgObjectSelection.ui
)
@@ -466,6 +472,7 @@ SET(Command_CPP_SRCS
CommandTest.cpp
CommandView.cpp
CommandStructure.cpp
CommandLink.cpp
)
SET(Command_SRCS
${Command_CPP_SRCS}
@@ -507,6 +514,7 @@ SET(Dialog_CPP_SRCS
DownloadItem.cpp
DownloadManager.cpp
DocumentRecovery.cpp
TaskElementColors.cpp
DlgObjectSelection.cpp
)
@@ -541,6 +549,7 @@ SET(Dialog_HPP_SRCS
DownloadItem.h
DownloadManager.h
DocumentRecovery.h
TaskElementColors.h
DlgObjectSelection.h
)
@@ -577,6 +586,7 @@ SET(Dialog_SRCS
Placement.ui
SceneInspector.ui
TextureMapping.ui
TaskElementColors.ui
DlgObjectSelection.ui
)
SOURCE_GROUP("Dialog" FILES ${Dialog_SRCS})
@@ -983,6 +993,9 @@ SET(Viewprovider_CPP_SRCS
ViewProviderOrigin.cpp
ViewProviderMaterialObject.cpp
ViewProviderTextDocument.cpp
ViewProviderLink.cpp
LinkViewPyImp.cpp
ViewProviderLinkPyImp.cpp
)
SET(Viewprovider_SRCS
${Viewprovider_CPP_SRCS}
@@ -1013,6 +1026,7 @@ SET(Viewprovider_SRCS
ViewProviderOrigin.h
ViewProviderMaterialObject.h
ViewProviderTextDocument.h
ViewProviderLink.h
)
SOURCE_GROUP("View3D\\Viewprovider" FILES ${Viewprovider_SRCS})

View File

@@ -31,6 +31,7 @@
#include "Selection.h"
#include "ViewProvider.h"
#include "ViewProviderDocumentObject.h"
#include "ViewProviderLink.h"
using namespace Gui;
@@ -89,11 +90,17 @@ void StdCmdRandomColor::activated(int iMsg)
float fBlu = (float)rand()/fMax;
ViewProvider* view = Application::Instance->getDocument(it->pDoc)->getViewProvider(it->pObject);
App::Property* color = view->getPropertyByName("ShapeColor");
if (color && color->getTypeId() == App::PropertyColor::getClassTypeId()) {
auto vpLink = dynamic_cast<ViewProviderLink*>(view);
if(vpLink) {
if(!vpLink->OverrideMaterial.getValue())
FCMD_VOBJ_CMD2("OverrideMaterial = True",it->pObject);
FCMD_VOBJ_CMD2("ShapeMaterial.DiffuseColor=(%.2f,%.2f,%.2f)", it->pObject, fRed, fGrn, fBlu);
continue;
}
auto color = dynamic_cast<App::PropertyColor*>(view->getPropertyByName("ShapeColor"));
if (color) {
// get the view provider of the selected object and set the shape color
doCommand(Gui, "Gui.getDocument(\"%s\").getObject(\"%s\").ShapeColor=(%.2f,%.2f,%.2f)"
, it->DocName, it->FeatName, fRed, fGrn, fBlu);
FCMD_VOBJ_CMD2("ShapeColor=(%.2f,%.2f,%.2f)" , it->pObject, fRed, fGrn, fBlu);
}
}
}

810
src/Gui/CommandLink.cpp Normal file
View File

@@ -0,0 +1,810 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <sstream>
# include <QMessageBox>
#endif
#include <boost/algorithm/string/predicate.hpp>
#include "Command.h"
#include "Action.h"
#include "Application.h"
#include "MainWindow.h"
#include "Tree.h"
#include "Document.h"
#include "Selection.h"
#include "WaitCursor.h"
#include "ViewProviderDocumentObject.h"
#include <Base/Console.h>
#include <Base/Exception.h>
#include <Base/Parameter.h>
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/DocumentObserver.h>
#include <App/Link.h>
FC_LOG_LEVEL_INIT("CommandLink",true,true);
using namespace Gui;
static void setLinkLabel(App::DocumentObject *obj, const char *doc, const char *name) {
const char *label = obj->Label.getValue();
Command::doCommand(Command::Doc,"App.getDocument('%s').getObject('%s').Label='%s'",doc,name,label);
}
////////////////////////////////////////////////////////////////////////////////////////////
class StdCmdLinkMakeGroup : public Gui::Command
{
public:
StdCmdLinkMakeGroup();
const char* className() const
{ return "StdCmdLinkMakeGroup"; }
protected:
virtual void activated(int iMsg);
virtual bool isActive(void);
virtual Action * createAction(void);
virtual void languageChange();
};
StdCmdLinkMakeGroup::StdCmdLinkMakeGroup()
: Command("Std_LinkMakeGroup")
{
sGroup = QT_TR_NOOP("Link");
sMenuText = QT_TR_NOOP("Make link group");
sToolTipText = QT_TR_NOOP("Create a group of links");
sWhatsThis = "Std_LinkMakeGroup";
sStatusTip = sToolTipText;
eType = AlterDoc;
sPixmap = "LinkGroup";
}
bool StdCmdLinkMakeGroup::isActive() {
return !!App::GetApplication().getActiveDocument();
}
Action * StdCmdLinkMakeGroup::createAction(void)
{
ActionGroup* pcAction = new ActionGroup(this, getMainWindow());
pcAction->setDropDownMenu(true);
applyCommandData(this->className(), pcAction);
// add the action items
pcAction->addAction(QObject::tr("Simple group"));
pcAction->addAction(QObject::tr("Group with links"));
pcAction->addAction(QObject::tr("Group with transform links"));
return pcAction;
}
void StdCmdLinkMakeGroup::languageChange()
{
Command::languageChange();
if (!_pcAction)
return;
ActionGroup* pcAction = qobject_cast<ActionGroup*>(_pcAction);
QList<QAction*> acts = pcAction->actions();
acts[0]->setText(QObject::tr("Simple group"));
acts[1]->setText(QObject::tr("Group with links"));
acts[2]->setText(QObject::tr("Group with transform links"));
}
void StdCmdLinkMakeGroup::activated(int option) {
std::vector<App::DocumentObject*> objs;
std::set<App::DocumentObject*> objset;
auto doc = App::GetApplication().getActiveDocument();
if(!doc) {
FC_ERR("no active document");
return;
}
for(auto &sel : Selection().getCompleteSelection()) {
if(sel.pObject && sel.pObject->getNameInDocument() &&
objset.insert(sel.pObject).second)
objs.push_back(sel.pObject);
}
Selection().selStackPush();
Selection().clearCompleteSelection();
Command::openCommand("Make link group");
try {
std::string groupName = doc->getUniqueObjectName("LinkGroup");
Command::doCommand(Command::Doc,
"App.getDocument('%s').addObject('App::LinkGroup','%s')",doc->getName(),groupName.c_str());
if(objs.empty()) {
Selection().addSelection(doc->getName(),groupName.c_str());
Selection().selStackPush();
}else{
Command::doCommand(Command::Doc,"__objs__ = []");
for(auto obj : objs) {
std::string name;
if(option!=0 || doc!=obj->getDocument()) {
name = doc->getUniqueObjectName("Link");
Command::doCommand(Command::Doc,
"App.getDocument('%s').addObject('App::Link','%s').setLink("
"App.getDocument('%s').getObject('%s'))",
doc->getName(),name.c_str(),obj->getDocument()->getName(),obj->getNameInDocument());
setLinkLabel(obj,doc->getName(),name.c_str());
if(option==2)
Command::doCommand(Command::Doc,
"App.getDocument('%s').getObject('%s').LinkTransform = True",
doc->getName(),name.c_str());
else if(obj->getPropertyByName("Placement"))
Command::doCommand(Command::Doc,
"App.getDocument('%s').getObject('%s').Placement = "
"App.getDocument('%s').getObject('%s').Placement",
doc->getName(),name.c_str(),obj->getDocument()->getName(),obj->getNameInDocument());
}else
name = obj->getNameInDocument();
Command::doCommand(Command::Doc,"__objs__.append(App.getDocument('%s').getObject('%s'))",
doc->getName(),name.c_str());
Command::doCommand(Command::Doc,
"App.getDocument('%s').getObject('%s').ViewObject.Visibility=False",
doc->getName(),name.c_str());
}
Command::doCommand(Command::Doc,"App.getDocument('%s').getObject('%s').setLink(__objs__)",
doc->getName(),groupName.c_str());
Command::doCommand(Command::Doc,"del __objs__");
for(size_t i=0;i<objs.size();++i) {
auto name = std::to_string(i)+".";
Selection().addSelection(doc->getName(),groupName.c_str(),name.c_str());
}
Selection().selStackPush();
}
if(option!=0) {
Command::doCommand(Command::Doc,
"App.getDocument('%s').getObject('%s').LinkMode = 'Auto Delete'",
doc->getName(),groupName.c_str());
}
Command::commitCommand();
} catch (const Base::Exception& e) {
QMessageBox::critical(getMainWindow(), QObject::tr("Create link group failed"),
QString::fromLatin1(e.what()));
Command::abortCommand();
e.ReportException();
}
}
////////////////////////////////////////////////////////////////////////////////////////////
DEF_STD_CMD(StdCmdLinkMake)
StdCmdLinkMake::StdCmdLinkMake()
: Command("Std_LinkMake")
{
sGroup = QT_TR_NOOP("Link");
sMenuText = QT_TR_NOOP("Make link");
sToolTipText = QT_TR_NOOP("Create a link to the selected object(s)");
sWhatsThis = "Std_LinkMake";
sStatusTip = sToolTipText;
eType = AlterDoc;
sPixmap = "Link";
}
void StdCmdLinkMake::activated(int) {
auto doc = App::GetApplication().getActiveDocument();
if(!doc) {
FC_ERR("no active document");
return;
}
std::set<App::DocumentObject*> objs;
for(auto &sel : Selection().getCompleteSelection()) {
if(sel.pObject && sel.pObject->getNameInDocument())
objs.insert(sel.pObject);
}
Selection().selStackPush();
Selection().clearCompleteSelection();
Command::openCommand("Make link");
try {
if(objs.empty()) {
std::string name = doc->getUniqueObjectName("Link");
Command::doCommand(Command::Doc, "App.getDocument('%s').addObject('App::Link','%s')",
doc->getName(),name.c_str());
Selection().addSelection(doc->getName(),name.c_str());
}else{
for(auto obj : objs) {
std::string name = doc->getUniqueObjectName("Link");
Command::doCommand(Command::Doc,
"App.getDocument('%s').addObject('App::Link','%s').setLink(App.getDocument('%s').%s)",
doc->getName(),name.c_str(),obj->getDocument()->getName(),obj->getNameInDocument());
setLinkLabel(obj,doc->getName(),name.c_str());
Selection().addSelection(doc->getName(),name.c_str());
}
}
Selection().selStackPush();
Command::commitCommand();
} catch (const Base::Exception& e) {
Command::abortCommand();
QMessageBox::critical(getMainWindow(), QObject::tr("Create link failed"),
QString::fromLatin1(e.what()));
e.ReportException();
}
}
////////////////////////////////////////////////////////////////////////////////////////////
DEF_STD_CMD_A(StdCmdLinkMakeRelative)
StdCmdLinkMakeRelative::StdCmdLinkMakeRelative()
: Command("Std_LinkMakeRelative")
{
sGroup = QT_TR_NOOP("Link");
sMenuText = QT_TR_NOOP("Make relative link");
sToolTipText = QT_TR_NOOP("Create a relative link of two selected objects");
sWhatsThis = "Std_LinkMakeRelative";
sStatusTip = sToolTipText;
eType = AlterDoc;
sPixmap = "LinkSub";
}
bool StdCmdLinkMakeRelative::isActive() {
return Selection().hasSubSelection();
}
void StdCmdLinkMakeRelative::activated(int) {
auto doc = App::GetApplication().getActiveDocument();
if(!doc) {
FC_ERR("no active document");
return;
}
Command::openCommand("Make relative link");
try {
std::vector<std::string> newNames;
for(auto &sel : Selection().getCompleteSelection(0)) {
std::string name = doc->getUniqueObjectName("Link");
FCMD_DOC_CMD(doc,"addObject('App::Link','" << name << "').setLink("
<< getObjectCmd(sel.pObject) << ",'" << sel.SubName << "')");
auto link = doc->getObject(name.c_str());
FCMD_OBJ_CMD(link,"LinkTransform = True");
setLinkLabel(sel.pResolvedObject,doc->getName(),name.c_str());
newNames.push_back(std::move(name));
}
Selection().selStackPush();
Selection().clearCompleteSelection();
for(auto &name : newNames)
Selection().addSelection(doc->getName(),name.c_str());
Selection().selStackPush();
Command::commitCommand();
} catch (const Base::Exception& e) {
Command::abortCommand();
QMessageBox::critical(getMainWindow(), QObject::tr("Failed to create relative link"),
QString::fromLatin1(e.what()));
e.ReportException();
}
return;
}
/////////////////////////////////////////////////////////////////////////////////////
struct Info {
bool inited = false;
App::DocumentObjectT topParent;
std::string subname;
App::DocumentObjectT parent;
App::DocumentObjectT obj;
};
static void linkConvert(bool unlink) {
// We are trying to replace an object with a link (App::Link), or replace a
// link back to its linked object (i.e. unlink). This is a very complex
// operation. It works by reassign the link property of the parent of the
// selected object(s) to a newly created link to the original object.
// Everything should remain the same. This complexity is now largely handled
// by ViewProviderDocumentObject::replaceObject(), which in turn relies on
// PropertyLinkBase::CopyOnLinkReplace().
std::map<std::pair<App::DocumentObject*,App::DocumentObject*>, Info> infos;
for(auto sel : TreeWidget::getSelection()) {
auto obj = sel.vp->getObject();
auto parent = sel.parentVp;
if(!parent) {
FC_WARN("skip '" << obj->getFullName() << "' with no parent");
continue;
}
auto parentObj = parent->getObject();
auto &info = infos[std::make_pair(parentObj,obj)];
if(info.inited)
continue;
info.inited = true;
if(unlink) {
auto linked = obj->getLinkedObject(false);
if(!linked || !linked->getNameInDocument() || linked == obj) {
FC_WARN("skip non link");
continue;
}
}
info.topParent = sel.topParent;
info.parent = parentObj;
info.obj = obj;
}
if(infos.empty())
return;
Selection().selStackPush();
Selection().clearCompleteSelection();
// now, do actual operation
const char *transactionName = unlink?"Unlink":"Replace with link";
Command::openCommand(transactionName);
try {
std::unordered_map<App::DocumentObject*,App::DocumentObjectT> recomputeSet;
for(auto &v : infos) {
auto &info = v.second;
auto parent = info.parent.getObject();
auto parentVp = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(parent));
auto obj = info.obj.getObject();
if(!parent || !obj || !parentVp)
continue;
if(!recomputeSet.count(parent))
recomputeSet.emplace(parent,parent);
auto doc = parent->getDocument();
App::DocumentObject *replaceObj;
if(unlink) {
replaceObj = obj->getLinkedObject(false);
if(!replaceObj || !replaceObj->getNameInDocument() || replaceObj == obj)
continue;
}else{
auto name = doc->getUniqueObjectName("Link");
auto link = static_cast<App::Link*>(doc->addObject("App::Link",name.c_str()));
if(!link)
FC_THROWM(Base::RuntimeError,"Failed to create link");
link->setLink(-1,obj);
link->Label.setValue(obj->Label.getValue());
auto pla = Base::freecad_dynamic_cast<App::PropertyPlacement>(
obj->getPropertyByName("Placement"));
if(pla)
link->Placement.setValue(pla->getValue());
else
link->LinkTransform.setValue(true);
replaceObj = link;
}
// adjust subname for the the new object
auto pos = info.subname.rfind('.');
if(pos==std::string::npos && pos)
info.subname.clear();
else {
pos = info.subname.rfind('.',pos-1);
if(pos==std::string::npos)
info.subname.clear();
else {
info.subname.resize(pos+1);
info.subname += replaceObj->getNameInDocument();
info.subname += ".";
}
}
// do the replacement operation
if(parentVp->replaceObject(obj,replaceObj)<=0)
FC_THROWM(Base::RuntimeError,
"Failed to change link for " << parent->getFullName());
}
std::vector<App::DocumentObject *> recomputes;
for(auto &v : recomputeSet) {
auto obj = v.second.getObject();
if(obj)
recomputes.push_back(obj);
}
if(recomputes.size())
recomputes.front()->getDocument()->recompute(recomputes);
Command::commitCommand();
} catch (const Base::Exception& e) {
Command::abortCommand();
auto title = unlink?QObject::tr("Unlink failed"):QObject::tr("Replace link failed");
QMessageBox::critical(getMainWindow(), title, QString::fromLatin1(e.what()));
e.ReportException();
return;
}
}
static bool linkConvertible(bool unlink) {
int count = 0;
for(auto &sel : TreeWidget::getSelection()) {
auto parent = sel.parentVp;
if(!parent) return false;
auto obj = sel.vp->getObject();
if(unlink) {
auto linked = obj->getLinkedObject(false);
if(!linked || linked == obj)
return false;
}
++count;
}
return count!=0;
}
////////////////////////////////////////////////////////////////////////////////////////////
DEF_STD_CMD_A(StdCmdLinkReplace)
StdCmdLinkReplace::StdCmdLinkReplace()
: Command("Std_LinkReplace")
{
sGroup = QT_TR_NOOP("Link");
sMenuText = QT_TR_NOOP("Replace with link");
sToolTipText = QT_TR_NOOP("Replace the selected object(s) with link");
sWhatsThis = "Std_LinkReplace";
sStatusTip = sToolTipText;
eType = AlterDoc;
sPixmap = "LinkReplace";
}
bool StdCmdLinkReplace::isActive() {
return linkConvertible(false);
}
void StdCmdLinkReplace::activated(int) {
linkConvert(false);
}
////////////////////////////////////////////////////////////////////////////////////////////
DEF_STD_CMD_A(StdCmdLinkUnlink)
StdCmdLinkUnlink::StdCmdLinkUnlink()
: Command("Std_LinkUnlink")
{
sGroup = QT_TR_NOOP("Link");
sMenuText = QT_TR_NOOP("Unlink");
sToolTipText = QT_TR_NOOP("Strip on level of link");
sWhatsThis = "Std_LinkUnlink";
sStatusTip = sToolTipText;
eType = AlterDoc;
sPixmap = "Unlink";
}
bool StdCmdLinkUnlink::isActive() {
return linkConvertible(true);
}
void StdCmdLinkUnlink::activated(int) {
linkConvert(true);
}
////////////////////////////////////////////////////////////////////////////////////////////
DEF_STD_CMD_A(StdCmdLinkImport)
StdCmdLinkImport::StdCmdLinkImport()
: Command("Std_LinkImport")
{
sGroup = QT_TR_NOOP("Link");
sMenuText = QT_TR_NOOP("Import links");
sToolTipText = QT_TR_NOOP("Import selected external link(s)");
sWhatsThis = "Std_LinkImport";
sStatusTip = sToolTipText;
eType = AlterDoc;
sPixmap = "LinkImport";
}
static std::map<App::Document*, std::vector<App::DocumentObject*> > getLinkImportSelections()
{
std::map<App::Document*, std::vector<App::DocumentObject*> > objMap;
for(auto &sel : Selection().getCompleteSelection(false)) {
auto obj = sel.pObject->resolve(sel.SubName);
if(!obj || !obj->getNameInDocument())
continue;
for(auto o : obj->getOutList()) {
if(o && o->getNameInDocument() && o->getDocument()!=obj->getDocument()) {
objMap[obj->getDocument()].push_back(obj);
break;
}
}
}
return objMap;
}
bool StdCmdLinkImport::isActive() {
auto links = getLinkImportSelections();
if(links.empty())
return false;
for(auto &v : links) {
if(v.first->testStatus(App::Document::PartialDoc))
return false;
}
return true;
}
void StdCmdLinkImport::activated(int) {
Command::openCommand("Import links");
try {
WaitCursor wc;
wc.setIgnoreEvents(WaitCursor::NoEvents);
for(auto &v : getLinkImportSelections()) {
auto doc = v.first;
// TODO: Is it possible to do this using interpreter?
for(auto obj : doc->importLinks(v.second))
obj->Visibility.setValue(false);
}
Command::commitCommand();
}catch (const Base::Exception& e) {
Command::abortCommand();
QMessageBox::critical(getMainWindow(), QObject::tr("Failed to import links"),
QString::fromLatin1(e.what()));
e.ReportException();
}
}
////////////////////////////////////////////////////////////////////////////////////////////
DEF_STD_CMD_A(StdCmdLinkImportAll)
StdCmdLinkImportAll::StdCmdLinkImportAll()
: Command("Std_LinkImportAll")
{
sGroup = QT_TR_NOOP("Link");
sMenuText = QT_TR_NOOP("Import all links");
sToolTipText = QT_TR_NOOP("Import all links of the active document");
sWhatsThis = "Std_LinkImportAll";
sStatusTip = sToolTipText;
eType = AlterDoc;
sPixmap = "LinkImportAll";
}
bool StdCmdLinkImportAll::isActive() {
auto doc = App::GetApplication().getActiveDocument();
return doc && !doc->testStatus(App::Document::PartialDoc) && App::PropertyXLink::hasXLink(doc);
}
void StdCmdLinkImportAll::activated(int) {
Command::openCommand("Import all links");
try {
WaitCursor wc;
wc.setIgnoreEvents(WaitCursor::NoEvents);
auto doc = App::GetApplication().getActiveDocument();
if(doc) {
for(auto obj : doc->importLinks())
obj->Visibility.setValue(false);
}
Command::commitCommand();
} catch (const Base::Exception& e) {
QMessageBox::critical(getMainWindow(), QObject::tr("Failed to import all links"),
QString::fromLatin1(e.what()));
Command::abortCommand();
e.ReportException();
}
}
////////////////////////////////////////////////////////////////////////////////////////////
DEF_STD_CMD_A(StdCmdLinkSelectLinked)
StdCmdLinkSelectLinked::StdCmdLinkSelectLinked()
: Command("Std_LinkSelectLinked")
{
sGroup = QT_TR_NOOP("Link");
sMenuText = QT_TR_NOOP("Select linked object");
sToolTipText = QT_TR_NOOP("Select the linked object");
sWhatsThis = "Std_LinkSelectLinked";
sStatusTip = sToolTipText;
eType = AlterSelection;
sPixmap = "LinkSelect";
sAccel = "S, G";
}
static App::DocumentObject *getSelectedLink(bool finalLink, std::string *subname=0) {
const auto &sels = Selection().getSelection("*",0,true);
if(sels.empty())
return 0;
auto sobj = sels[0].pObject->getSubObject(sels[0].SubName);
if(!sobj)
return 0;
auto vp = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(sobj));
if(!vp)
return 0;
auto linkedVp = vp->getLinkedViewProvider(subname,finalLink);
if(!linkedVp || linkedVp==vp)
return 0;
if(finalLink && linkedVp == vp->getLinkedViewProvider())
return 0;
auto linked = linkedVp->getObject();
if(!linked || !linked->getNameInDocument())
return 0;
if(subname && sels[0].pObject!=sobj && sels[0].SubName) {
bool found = false;
int pre_len=0;
std::size_t post_len=0;
std::string prefix;
std::string prefix2;
// An object can be claimed by multiple objects. Let's try select one
// that causes minimum jump in tree view, and prefer upper over lower
// hierarchy (because of less depth/complexity of tree expansion)
for(auto &v : linked->getParents()) {
if(v.first != sels[0].pObject)
continue;
const char *sub = v.second.c_str();
const char *dot = sub;
for(const char *s=sels[0].SubName; *s && *sub==*s; ++s,++sub) {
if(*sub == '.')
dot = sub;
}
found = true;
if(dot-v.second.c_str() > pre_len
|| (dot-v.second.c_str()==pre_len
&& v.second.size()<post_len))
{
pre_len = dot-v.second.c_str();
prefix = std::string(sels[0].SubName,pre_len) + (v.second.c_str()+pre_len);
post_len = v.second.size();
}else if(!pre_len) {
if(prefix2.empty() || prefix2.size() > v.second.size())
prefix2 = v.second;
}
}
if(found) {
linked = sels[0].pObject;
*subname = prefix.size()?prefix:prefix2 + *subname;
}
}
return linked;
}
bool StdCmdLinkSelectLinked::isActive() {
return getSelectedLink(false)!=0;
}
void StdCmdLinkSelectLinked::activated(int)
{
std::string subname;
auto linked = getSelectedLink(false,&subname);
if(!linked){
FC_WARN("invalid selection");
return;
}
Selection().selStackPush();
Selection().clearCompleteSelection();
if(subname.size()) {
Selection().addSelection(linked->getDocument()->getName(),linked->getNameInDocument(),subname.c_str());
auto doc = Application::Instance->getDocument(linked->getDocument());
if(doc) {
auto vp = dynamic_cast<ViewProviderDocumentObject*>(Application::Instance->getViewProvider(linked));
doc->setActiveView(vp);
}
} else {
for(auto tree : getMainWindow()->findChildren<TreeWidget*>())
tree->selectLinkedObject(linked);
}
Selection().selStackPush();
}
////////////////////////////////////////////////////////////////////////////////////////////
DEF_STD_CMD_A(StdCmdLinkSelectLinkedFinal)
StdCmdLinkSelectLinkedFinal::StdCmdLinkSelectLinkedFinal()
: Command("Std_LinkSelectLinkedFinal")
{
sGroup = QT_TR_NOOP("Link");
sMenuText = QT_TR_NOOP("Select the deepest linked object");
sToolTipText = QT_TR_NOOP("Select the deepest linked object");
sWhatsThis = "Std_LinkSelectLinkedFinal";
sStatusTip = sToolTipText;
eType = AlterSelection;
sPixmap = "LinkSelectFinal";
sAccel = "S, D";
}
bool StdCmdLinkSelectLinkedFinal::isActive() {
return getSelectedLink(true)!=0;
}
void StdCmdLinkSelectLinkedFinal::activated(int) {
auto linked = getSelectedLink(true);
if(!linked){
FC_WARN("invalid selection");
return;
}
Selection().selStackPush();
Selection().clearCompleteSelection();
for(auto tree : getMainWindow()->findChildren<TreeWidget*>())
tree->selectLinkedObject(linked);
Selection().selStackPush();
}
////////////////////////////////////////////////////////////////////////////////////////////
DEF_STD_CMD_A(StdCmdLinkSelectAllLinks)
StdCmdLinkSelectAllLinks::StdCmdLinkSelectAllLinks()
: Command("Std_LinkSelectAllLinks")
{
sGroup = QT_TR_NOOP("Link");
sMenuText = QT_TR_NOOP("Select all links");
sToolTipText = QT_TR_NOOP("Select all links to the current selected object");
sWhatsThis = "Std_LinkSelectAllLinks";
sStatusTip = sToolTipText;
eType = AlterSelection;
sPixmap = "LinkSelectAll";
}
bool StdCmdLinkSelectAllLinks::isActive() {
const auto &sels = Selection().getSelection("*",true,true);
if(sels.empty())
return false;
return App::GetApplication().hasLinksTo(sels[0].pObject);
}
void StdCmdLinkSelectAllLinks::activated(int)
{
auto sels = Selection().getSelection("*",true,true);
if(sels.empty())
return;
Selection().selStackPush();
Selection().clearCompleteSelection();
for(auto tree : getMainWindow()->findChildren<TreeWidget*>())
tree->selectAllLinks(sels[0].pObject);
Selection().selStackPush();
}
//===========================================================================
// Instantiation
//===========================================================================
namespace Gui {
void CreateLinkCommands(void)
{
CommandManager &rcCmdMgr = Application::Instance->commandManager();
rcCmdMgr.addCommand(new StdCmdLinkSelectLinked());
rcCmdMgr.addCommand(new StdCmdLinkSelectLinkedFinal());
rcCmdMgr.addCommand(new StdCmdLinkSelectAllLinks());
rcCmdMgr.addCommand(new StdCmdLinkMake());
rcCmdMgr.addCommand(new StdCmdLinkMakeRelative());
rcCmdMgr.addCommand(new StdCmdLinkMakeGroup());
rcCmdMgr.addCommand(new StdCmdLinkReplace());
rcCmdMgr.addCommand(new StdCmdLinkUnlink());
rcCmdMgr.addCommand(new StdCmdLinkImport());
rcCmdMgr.addCommand(new StdCmdLinkImportAll());
}
} // namespace Gui

165
src/Gui/LinkViewPy.xml Normal file
View File

@@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="BaseClassPy"
Name="LinkViewPy"
Twin="LinkView"
TwinPointer="LinkView"
Include="Gui/ViewProviderLink.h"
Namespace="Gui"
FatherInclude="Base/BaseClass.h"
FatherNamespace="Base"
Constructor="true"
Delete="true">
<Documentation>
<Author Licence="LGPL" Name="Zheng, Lei" EMail="realthunder.dev@gmail.com" />
<UserDocu>Helper class to link to a view object</UserDocu>
</Documentation>
<Methode Name="reset">
<Documentation>
<UserDocu>Reset the link view and clear the links</UserDocu>
</Documentation>
</Methode>
<Methode Name="setMaterial">
<Documentation>
<UserDocu>
setMaterial(Material): set the override material of the entire linked object
setMaterial([Material,...]): set the materials for the elements of the link
array/group.
setMaterial({Int:Material,...}): set the material for the elements of the
link array/group by index.
If material is None, then the material is unset. If the material of an element
is unset, it defaults to the override material of the linked object, if there
is one
</UserDocu>
</Documentation>
</Methode>
<Methode Name="setType">
<Documentation>
<UserDocu>
setType(type, sublink=True): set the link type.
type=0: override transformation and visibility
type=1: override visibility
type=2: no override
type=-1: sub-object link with override visibility
type=-2: sub-object link with override transformation and visibility
sublink: auto delegate to the sub-object references in the link, if there is
one and only one.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="setTransform">
<Documentation>
<UserDocu>
setTransform(matrix): set transformation of the linked object
setTransform([matrix,...]): set transformation for the elements of the link
array/group
setTransform({index:matrix,...}): set transformation for elements of the link
array/group by index
</UserDocu>
</Documentation>
</Methode>
<Methode Name="setChildren">
<Documentation>
<UserDocu>
setChildren([obj...],vis=[],type=0)
Group a list of children objects. Note, this mode of operation is incompatible
with link array. Calling this function will deactivate link array. And calling
setSize() will reset all linked children.
vis: initial visibility status of the children
type: children linking type,
0: override transformation and visibility,
1: override visibility,
2: override none.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="setLink">
<Documentation>
<UserDocu>
setLink(object): Set the link
setLink(object, subname): Set the link with a sub-object reference
setLink(object, [subname,...]): Set the link with a list of sub object references
object: The linked document object or its view object
subname: a string or tuple/list of strings sub-name references to sub object
or sub elements (e.g. Face1, Edge2) belonging to the linked object.
The sub-name must end with a '.' if it is referencing an sub-object,
or else it is considered a sub-element reference.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getDetailPath">
<Documentation>
<UserDocu>
getDetailPath(element): get the 3d path an detail of an element.
Return a tuple(path,detail) for the coin3D SoPath and SoDetail of the element
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getElementPicked">
<Documentation>
<UserDocu>getElementPicked(pickPoint): get the element under a 3d pick point. </UserDocu>
</Documentation>
</Methode>
<Methode Name="getBoundBox">
<Documentation>
<UserDocu>getBoundBox(vobj=None): get the bounding box. </UserDocu>
</Documentation>
</Methode>
<Attribute Name="LinkedView" ReadOnly="true">
<Documentation>
<UserDocu>The linked view object</UserDocu>
</Documentation>
<Parameter Name="LinkedView" Type="Object" />
</Attribute>
<Attribute Name="SubNames" ReadOnly="true">
<Documentation>
<UserDocu>The sub-object reference of the link</UserDocu>
</Documentation>
<Parameter Name="SubNames" Type="Object" />
</Attribute>
<Attribute Name="RootNode" ReadOnly="true">
<Documentation>
<UserDocu>A pivy node holding the cloned representation of the linked view object</UserDocu>
</Documentation>
<Parameter Name="RootNode" Type="Object" />
</Attribute>
<Attribute Name="Owner">
<Documentation>
<UserDocu>The owner view object of this link handle</UserDocu>
</Documentation>
<Parameter Name="Owner" Type="Object" />
</Attribute>
<Attribute Name="Visibilities">
<Documentation>
<UserDocu>Get/set the child element visibility</UserDocu>
</Documentation>
<Parameter Name="Visibilities" Type="Object" />
</Attribute>
<Attribute Name="Count">
<Documentation>
<UserDocu>Set the element size to create an array of linked object</UserDocu>
</Documentation>
<Parameter Name="Count" Type="Int" />
</Attribute>
<Methode Name="getChildren" Const="true">
<Documentation>
<UserDocu>Get children view objects</UserDocu>
</Documentation>
</Methode>
</PythonExport>
</GenerateModel>

403
src/Gui/LinkViewPyImp.cpp Normal file
View File

@@ -0,0 +1,403 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <Inventor/nodes/SoSeparator.h>
#endif
#include <Base/MatrixPy.h>
#include <Base/VectorPy.h>
#include <Base/BoundBoxPy.h>
#include <App/MaterialPy.h>
#include <App/DocumentObjectPy.h>
#include "ViewProviderDocumentObjectPy.h"
#include "ViewProviderLink.h"
#include "WidgetFactory.h"
#include "LinkViewPy.h"
#include "LinkViewPy.cpp"
using namespace Gui;
PyObject *LinkViewPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper
{
return new LinkViewPy(new LinkView);
}
int LinkViewPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/)
{
return 0;
}
// returns a string which represent the object e.g. when printed in python
std::string LinkViewPy::representation(void) const
{
return "<Link view>";
}
PyObject* LinkViewPy::reset(PyObject *args) {
if (!PyArg_ParseTuple(args, ""))
return 0;
PY_TRY {
auto lv = getLinkViewPtr();
lv->setSize(0);
lv->setLink(0);
Py_Return;
} PY_CATCH;
}
PyObject* LinkViewPy::setMaterial(PyObject *args) {
PyObject *pyObj;
if (!PyArg_ParseTuple(args, "O", &pyObj))
return 0;
PY_TRY {
auto lv = getLinkViewPtr();
if(pyObj == Py_None) {
lv->setMaterial(-1,0);
Py_Return;
}
if(PyObject_TypeCheck(&pyObj,&App::MaterialPy::Type)) {
lv->setMaterial(-1,static_cast<App::MaterialPy*>(pyObj)->getMaterialPtr());
Py_Return;
}
if(PyDict_Check(pyObj)) {
PyObject *key, *value;
Py_ssize_t pos = 0;
std::map<int,App::Material*> materials;
while(PyDict_Next(pyObj, &pos, &key, &value)) {
Py::Int idx(key);
if(value == Py_None)
materials[(int)idx] = 0;
else if(!PyObject_TypeCheck(&value,&App::MaterialPy::Type)) {
PyErr_SetString(PyExc_TypeError, "exepcting a type of material");
return 0;
}else
materials[(int)idx] = static_cast<App::MaterialPy*>(value)->getMaterialPtr();
}
for(auto &v : materials)
lv->setMaterial(v.first,v.second);
Py_Return;
}
if(PySequence_Check(pyObj)) {
Py::Sequence seq(pyObj);
std::vector<App::Material*> materials;
materials.resize(seq.size(),0);
for(size_t i=0;i<seq.size();++i) {
PyObject* item = seq[i].ptr();
if(item == Py_None) continue;
if(!PyObject_TypeCheck(&item,&App::MaterialPy::Type)) {
PyErr_SetString(PyExc_TypeError, "exepcting a type of material");
return 0;
}
materials[i] = static_cast<App::MaterialPy*>(item)->getMaterialPtr();
}
for(size_t i=0;i<materials.size();++i)
lv->setMaterial(i,materials[i]);
Py_Return;
}
PyErr_SetString(PyExc_TypeError, "exepcting a type of Material, [Material,...] or {Int:Material,}");
return 0;
} PY_CATCH;
}
PyObject* LinkViewPy::setTransform(PyObject *args) {
PyObject *pyObj;
if (!PyArg_ParseTuple(args, "O", &pyObj))
return 0;
PY_TRY {
auto lv = getLinkViewPtr();
if(PyObject_TypeCheck(pyObj,&Base::MatrixPy::Type)) {
lv->setTransform(-1,*static_cast<Base::MatrixPy*>(pyObj)->getMatrixPtr());
Py_Return;
}
if(PyDict_Check(pyObj)) {
PyObject *key, *value;
Py_ssize_t pos = 0;
std::map<int,Base::Matrix4D*> mat;
while(PyDict_Next(pyObj, &pos, &key, &value)) {
Py::Int idx(key);
if(!PyObject_TypeCheck(&value,&Base::MatrixPy::Type)) {
PyErr_SetString(PyExc_TypeError, "exepcting a type of Matrix");
return 0;
}else
mat[(int)idx] = static_cast<Base::MatrixPy*>(value)->getMatrixPtr();
}
for(auto &v : mat)
lv->setTransform(v.first,*v.second);
Py_Return;
}
if(PySequence_Check(pyObj)) {
Py::Sequence seq(pyObj);
std::vector<Base::Matrix4D*> mat;
mat.resize(seq.size(),0);
for(size_t i=0;i<seq.size();++i) {
PyObject* item = seq[i].ptr();
if(!PyObject_TypeCheck(&item,&Base::MatrixPy::Type)) {
PyErr_SetString(PyExc_TypeError, "exepcting a type of Matrix");
return 0;
}
mat[i] = static_cast<Base::MatrixPy*>(item)->getMatrixPtr();
}
for(size_t i=0;i<mat.size();++i)
lv->setTransform(i,*mat[i]);
Py_Return;
}
PyErr_SetString(PyExc_TypeError, "exepcting a type of Matrix, [Matrix,...] or {Int:Matrix,...}");
return 0;
} PY_CATCH;
}
PyObject* LinkViewPy::setType(PyObject *args) {
short type;
PyObject *sublink = Py_True;
if (!PyArg_ParseTuple(args, "h|O", &type,&sublink))
return 0;
PY_TRY{
getLinkViewPtr()->setNodeType((LinkView::SnapshotType)type,PyObject_IsTrue(sublink));
Py_Return;
} PY_CATCH;
}
PyObject* LinkViewPy::setChildren(PyObject *args) {
PyObject *pyObj;
PyObject *pyVis = Py_None;
short type=0;
if (!PyArg_ParseTuple(args, "O|Os",&pyObj,&pyVis,&type))
return 0;
PY_TRY{
App::PropertyBoolList vis;
App::PropertyLinkList links;
if(pyObj!=Py_None)
links.setPyObject(pyObj);
if(pyVis!=Py_None)
vis.setPyObject(pyVis);
getLinkViewPtr()->setChildren(links.getValue(),vis.getValue(),(LinkView::SnapshotType)type);
Py_Return;
} PY_CATCH;
}
PyObject* LinkViewPy::setLink(PyObject *args)
{
PyObject *pyObj;
PyObject *pySubName = Py_None;
if (!PyArg_ParseTuple(args, "O|O",&pyObj,&pySubName))
return 0;
PY_TRY {
ViewProviderDocumentObject *vpd = 0;
App::DocumentObject *obj = 0;
if(pyObj!=Py_None) {
if(PyObject_TypeCheck(pyObj,&App::DocumentObjectPy::Type))
obj = static_cast<App::DocumentObjectPy*>(pyObj)->getDocumentObjectPtr();
else if(PyObject_TypeCheck(pyObj,&ViewProviderDocumentObjectPy::Type))
vpd = static_cast<ViewProviderDocumentObjectPy*>(pyObj)->getViewProviderDocumentObjectPtr();
else {
PyErr_SetString(PyExc_TypeError,
"exepcting a type of DocumentObject or ViewProviderDocumentObject");
return 0;
}
}
// Too lazy to parse the argument...
App::PropertyStringList prop;
if(pySubName!=Py_None)
prop.setPyObject(pySubName);
if(obj)
getLinkViewPtr()->setLink(obj,prop.getValue());
else
getLinkViewPtr()->setLinkViewObject(vpd,prop.getValue());
Py_Return;
} PY_CATCH;
}
Py::Object LinkViewPy::getOwner() const {
auto owner = getLinkViewPtr()->getOwner();
if(!owner) return Py::Object();
return Py::Object(owner->getPyObject(),true);
}
void LinkViewPy::setOwner(Py::Object owner) {
ViewProviderDocumentObject *vp = 0;
if(!owner.isNone()) {
if(!PyObject_TypeCheck(owner.ptr(),&ViewProviderDocumentObjectPy::Type))
throw Py::TypeError("exepcting the owner to be of ViewProviderDocumentObject");
vp = static_cast<ViewProviderDocumentObjectPy*>(
owner.ptr())->getViewProviderDocumentObjectPtr();
}
getLinkViewPtr()->setOwner(vp);
}
Py::Object LinkViewPy::getLinkedView() const {
auto linked = getLinkViewPtr()->getLinkedView();
if(!linked)
return Py::Object();
return Py::Object(linked->getPyObject(),true);
}
Py::Object LinkViewPy::getSubNames() const {
const auto &subs = getLinkViewPtr()->getSubNames();
if(subs.empty())
return Py::Object();
Py::Tuple ret(subs.size());
int i=0;
for(auto &s : subs)
ret.setItem(i++,Py::String(s.c_str()));
return ret;
}
PyObject* LinkViewPy::getElementPicked(PyObject* args)
{
PyObject *obj;
if (!PyArg_ParseTuple(args, "O",&obj))
return NULL;
void *ptr = 0;
Base::Interpreter().convertSWIGPointerObj("pivy.coin", "SoPickedPoint *", obj, &ptr, 0);
SoPickedPoint *pp = reinterpret_cast<SoPickedPoint*>(ptr);
if(!pp)
throw Py::TypeError("type must be of coin.SoPickedPoint");
PY_TRY{
std::string name;
if(!getLinkViewPtr()->linkGetElementPicked(pp,name))
Py_Return;
return Py::new_reference_to(Py::String(name));
}PY_CATCH
}
PyObject* LinkViewPy::getDetailPath(PyObject* args)
{
const char *sub;
PyObject *path;
if (!PyArg_ParseTuple(args, "sO",&sub,&path))
return NULL;
void *ptr = 0;
Base::Interpreter().convertSWIGPointerObj("pivy.coin", "SoPath *", path, &ptr, 0);
SoPath *pPath = reinterpret_cast<SoPath*>(ptr);
if(!pPath)
throw Py::TypeError("type must be of coin.SoPath");
PY_TRY{
SoDetail *det = 0;
getLinkViewPtr()->linkGetDetailPath(sub,static_cast<SoFullPath*>(pPath),det);
if(!det)
Py_Return;
return Base::Interpreter().createSWIGPointerObj("pivy.coin", "SoDetail *", (void*)det, 0);
}PY_CATCH
}
PyObject* LinkViewPy::getBoundBox(PyObject* args) {
PyObject *vobj = Py_None;
if (!PyArg_ParseTuple(args, "O",&vobj))
return 0;
ViewProviderDocumentObject *vpd = 0;
if(vobj!=Py_None) {
if(!PyObject_TypeCheck(vobj,&ViewProviderDocumentObjectPy::Type)) {
PyErr_SetString(PyExc_TypeError, "exepcting a type of ViewProviderDocumentObject");
return 0;
}
vpd = static_cast<ViewProviderDocumentObjectPy*>(vobj)->getViewProviderDocumentObjectPtr();
}
PY_TRY {
auto bbox = getLinkViewPtr()->getBoundBox(vpd);
Py::Object ret(new Base::BoundBoxPy(new Base::BoundBox3d(bbox)));
return Py::new_reference_to(ret);
}PY_CATCH
}
PyObject *LinkViewPy::getCustomAttributes(const char*) const
{
return 0;
}
int LinkViewPy::setCustomAttributes(const char*, PyObject*)
{
return 0;
}
Py::Object LinkViewPy::getRootNode(void) const
{
try {
SoNode* node = getLinkViewPtr()->getLinkRoot();
PyObject* Ptr = Base::Interpreter().createSWIGPointerObj("pivy.coin","SoSeparator *", node, 1);
node->ref();
return Py::Object(Ptr, true);
}
catch (const Base::Exception& e) {
throw Py::RuntimeError(e.what());
}
}
Py::Object LinkViewPy::getVisibilities() const {
auto linked = getLinkViewPtr();
if(!linked->getSize())
return Py::Object();
Py::Tuple ret(linked->getSize());
for(int i=0;i<linked->getSize();++i)
ret.setItem(i,Py::Boolean(linked->isElementVisible(i)));
return ret;
}
void LinkViewPy::setVisibilities(Py::Object value) {
App::PropertyBoolList v;
if(!value.isNone())
v.setPyObject(value.ptr());
auto linked = getLinkViewPtr();
const auto &vis = v.getValue();
for(int i=0;i<linked->getSize();++i)
linked->setElementVisible(i,i>=(int)vis.size()||vis[i]);
}
PyObject* LinkViewPy::getChildren(PyObject *args) {
if (!PyArg_ParseTuple(args, ""))
return 0;
auto children = getLinkViewPtr()->getChildren();
if(children.empty())
Py_Return;
Py::Tuple ret(children.size());
int i=0;
for(auto vp : children)
ret.setItem(i++,Py::Object(vp->getPyObject(),true));
return Py::new_reference_to(ret);
}
Py::Int LinkViewPy::getCount() const {
return Py::Int(getLinkViewPtr()->getSize());
}
void LinkViewPy::setCount(Py::Int count) {
try {
getLinkViewPtr()->setSize((int)count);
} catch (const Base::Exception& e) {
throw Py::RuntimeError(e.what());
}
}

View File

@@ -57,7 +57,7 @@ static double degreesToRadains(const double &degreesIn)
static double lastTranslationIncrement = 1.0;
static double lastRotationIncrement = degreesToRadains(15.0);
TaskCSysDragger::TaskCSysDragger(Gui::ViewProviderDragger* vpObjectIn, Gui::SoFCCSysDragger* draggerIn) :
TaskCSysDragger::TaskCSysDragger(Gui::ViewProviderDocumentObject* vpObjectIn, Gui::SoFCCSysDragger* draggerIn) :
dragger(draggerIn)
{
assert(vpObjectIn);

View File

@@ -37,7 +37,7 @@ namespace Gui
{
Q_OBJECT
public:
TaskCSysDragger(ViewProviderDragger *vpObjectIn, SoFCCSysDragger *draggerIn);
TaskCSysDragger(ViewProviderDocumentObject *vpObjectIn, SoFCCSysDragger *draggerIn);
virtual ~TaskCSysDragger() override;
virtual QDialogButtonBox::StandardButtons getStandardButtons() const override
{ return QDialogButtonBox::Ok;}

View File

@@ -0,0 +1,533 @@
/****************************************************************************
* Copyright (c) 2018 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <sstream>
#endif
#include <QColorDialog>
#include <boost/bind.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include "ui_TaskElementColors.h"
#include "TaskElementColors.h"
#include "ViewProviderLink.h"
#include <Base/Console.h>
#include <App/ComplexGeoData.h>
#include "Application.h"
#include "Control.h"
#include "Document.h"
#include "MainWindow.h"
#include "Selection.h"
#include "BitmapFactory.h"
#include "Command.h"
#include <App/Document.h>
#include <App/DocumentObject.h>
FC_LOG_LEVEL_INIT("Gui",true,true)
using namespace Gui;
class ElementColors::Private: public Gui::SelectionGate
{
public:
typedef boost::signals2::connection Connection;
std::unique_ptr<Ui_TaskElementColors> ui;
ViewProviderDocumentObject *vp;
ViewProviderDocumentObject *vpParent;
Document *vpDoc;
std::map<std::string,QListWidgetItem*> elements;
std::vector<QListWidgetItem*> items;
std::string hiddenSub;
Connection connectDelDoc;
Connection connectDelObj;
QPixmap px;
bool busy;
long onTopMode;
bool touched;
std::string editDoc;
std::string editObj;
std::string editSub;
std::string editElement;
Private(ViewProviderDocumentObject* vp, const char *element="")
: ui(new Ui_TaskElementColors()), vp(vp),editElement(element)
{
vpDoc = vp->getDocument();
vpParent = vp;
auto doc = Application::Instance->editDocument();
if(doc) {
auto editVp = doc->getInEdit(&vpParent,&editSub);
if(editVp == vp) {
auto obj = vpParent->getObject();
editDoc = obj->getDocument()->getName();
editObj = obj->getNameInDocument();
editSub = Data::ComplexGeoData::noElementName(editSub.c_str());
}
}
if(editDoc.empty()) {
vpParent = vp;
editDoc = vp->getObject()->getDocument()->getName();
editObj = vp->getObject()->getNameInDocument();
editSub.clear();
}
onTopMode = vpParent->OnTopWhenSelected.getValue();
busy = false;
touched = false;
int w = QApplication::style()->standardPixmap(QStyle::SP_DirClosedIcon).width();
px = QPixmap(w,w);
}
~Private() {
vpParent->OnTopWhenSelected.setValue(onTopMode);
}
bool allow(App::Document *doc, App::DocumentObject *obj, const char *subname) {
if(editDoc!=doc->getName() ||
editObj!=obj->getNameInDocument() ||
!boost::starts_with(subname,editSub))
return false;
if(editElement.empty())
return true;
const char *dot = strrchr(subname,'.');
if(!dot)
dot = subname;
else
++dot;
return *dot==0 || boost::starts_with(dot,editElement);
}
void populate() {
int i=0;
for(auto &v : vp->getElementColors())
addItem(i++,v.first.c_str());
apply();
}
void addItem(int i,const char *sub, bool push=false) {
auto itE = elements.find(sub);
if(i<0 && itE!=elements.end()) {
if(push && !ViewProvider::hasHiddenMarker(sub))
items.push_back(itE->second);
return;
}
const char *marker = ViewProvider::hasHiddenMarker(sub);
if(marker) {
auto icon = BitmapFactory().pixmap("Invisible");
QListWidgetItem* item = new QListWidgetItem(icon,
QString::fromLatin1(std::string(sub,marker-sub).c_str()), ui->elementList);
item->setData(Qt::UserRole,QColor());
item->setData(Qt::UserRole+1,QString::fromLatin1(sub));
elements.emplace(sub,item);
return;
}
for(auto &v : vp->getElementColors(sub)) {
auto it = elements.find(v.first.c_str());
if(it!=elements.end()) {
if(push)
items.push_back(it->second);
continue;
}
auto color = v.second;
QColor c;
c.setRgbF(color.r,color.g,color.b,1.0-color.a);
px.fill(c);
QListWidgetItem* item = new QListWidgetItem(QIcon(px),
QString::fromLatin1(Data::ComplexGeoData::oldElementName(v.first.c_str()).c_str()),
ui->elementList);
item->setData(Qt::UserRole,c);
item->setData(Qt::UserRole+1,QString::fromLatin1(v.first.c_str()));
if(push)
items.push_back(item);
elements.emplace(v.first,item);
}
}
void apply() {
std::map<std::string,App::Color> info;
int count = ui->elementList->count();
for(int i=0;i<count;++i) {
auto item = ui->elementList->item(i);
auto color = item->data(Qt::UserRole).value<QColor>();
std::string sub = qPrintable(item->data(Qt::UserRole+1).value<QString>());
info.emplace(qPrintable(item->data(Qt::UserRole+1).value<QString>()),
App::Color(color.redF(),color.greenF(),color.blueF(),1.0-color.alphaF()));
}
if(!App::GetApplication().getActiveTransaction())
App::GetApplication().setActiveTransaction("Set colors");
vp->setElementColors(info);
touched = true;
Selection().clearSelection();
}
void reset() {
touched = false;
App::GetApplication().closeActiveTransaction(true);
Selection().clearSelection();
}
void accept() {
if(touched && ui->recompute->isChecked()) {
auto obj = vp->getObject();
obj->touch();
obj->getDocument()->recompute(obj->getInListRecursive());
touched = false;
}
App::GetApplication().closeActiveTransaction();
}
void removeAll() {
if(elements.size()) {
hiddenSub.clear();
ui->elementList->clear();
elements.clear();
apply();
}
}
void removeItems() {
for(auto item : ui->elementList->selectedItems()) {
std::string sub = qPrintable(item->data(Qt::UserRole+1).value<QString>());
if(sub == hiddenSub)
hiddenSub.clear();
elements.erase(sub);
delete item;
}
apply();
}
void editItem(QWidget *parent, QListWidgetItem *item) {
std::string sub = qPrintable(item->data(Qt::UserRole+1).value<QString>());
if(ViewProvider::hasHiddenMarker(sub.c_str()))
return;
auto color = item->data(Qt::UserRole).value<QColor>();
QColorDialog cd(color, parent);
cd.setOption(QColorDialog::ShowAlphaChannel);
if (cd.exec()!=QDialog::Accepted || color==cd.selectedColor())
return;
color = cd.selectedColor();
item->setData(Qt::UserRole,color);
px.fill(color);
item->setIcon(QIcon(px));
apply();
}
void onSelectionChanged(const SelectionChanges &msg) {
// no object selected in the combobox or no sub-element was selected
if (busy)
return;
busy = true;
switch(msg.Type) {
case SelectionChanges::ClrSelection:
ui->elementList->clearSelection();
break;
case SelectionChanges::AddSelection:
case SelectionChanges::RmvSelection:
if(msg.pDocName && msg.pObjectName && msg.pSubName && msg.pSubName[0]) {
if(editDoc == msg.pDocName &&
editObj == msg.pObjectName &&
boost::starts_with(msg.pSubName,editSub))
{
for(auto item : ui->elementList->findItems(
QString::fromLatin1(msg.pSubName-editSub.size()),0))
item->setSelected(msg.Type==SelectionChanges::AddSelection);
}
}
default:
break;
}
busy = false;
}
void onSelectionChanged() {
if(busy) return;
busy = true;
std::map<std::string,int> sels;
for(auto &sel : Selection().getSelectionEx(
editDoc.c_str(),App::DocumentObject::getClassTypeId(),0))
{
if(sel.getFeatName()!=editObj) continue;
for(auto &sub : sel.getSubNames()) {
if(boost::starts_with(sub,editSub))
sels[sub.c_str()+editSub.size()] = 1;
}
break;
}
for(auto item : ui->elementList->selectedItems()) {
std::string name(qPrintable(item->data(Qt::UserRole+1).value<QString>()));
if(ViewProvider::hasHiddenMarker(name.c_str()))
continue;
auto &v = sels[name];
if(!v)
Selection().addSelection(editDoc.c_str(),
editObj.c_str(), (editSub+name).c_str());
v = 2;
}
for(auto &v : sels) {
if(v.second!=2) {
Selection().rmvSelection(editDoc.c_str(),
editObj.c_str(), (editSub+v.first).c_str());
}
}
busy = false;
}
};
/* TRANSLATOR Gui::TaskElementColors */
ElementColors::ElementColors(ViewProviderDocumentObject* vp, bool noHide)
:d(new Private(vp))
{
d->ui->setupUi(this);
d->ui->objectLabel->setText(QString::fromUtf8(vp->getObject()->Label.getValue()));
d->ui->elementList->setMouseTracking(true); // needed for itemEntered() to work
if(noHide)
d->ui->hideSelection->setVisible(false);
ParameterGrp::handle hPart = App::GetApplication().GetParameterGroupByPath
("User parameter:BaseApp/Preferences/View");
d->ui->recompute->setChecked(hPart->GetBool("ColorRecompute",true));
d->ui->onTop->setChecked(hPart->GetBool("ColorOnTop",true));
if(d->ui->onTop->isChecked())
d->vpParent->OnTopWhenSelected.setValue(3);
Selection().addSelectionGate(d,0);
d->connectDelDoc = Application::Instance->signalDeleteDocument.connect(boost::bind
(&ElementColors::slotDeleteDocument, this, _1));
d->connectDelObj = Application::Instance->signalDeletedObject.connect(boost::bind
(&ElementColors::slotDeleteObject, this, _1));
d->populate();
}
ElementColors::~ElementColors()
{
d->connectDelDoc.disconnect();
d->connectDelObj.disconnect();
Selection().rmvSelectionGate();
}
void ElementColors::on_recompute_clicked(bool checked) {
ParameterGrp::handle hPart = App::GetApplication().GetParameterGroupByPath
("User parameter:BaseApp/Preferences/View");
hPart->SetBool("ColorRecompute",checked);
}
void ElementColors::on_onTop_clicked(bool checked) {
ParameterGrp::handle hPart = App::GetApplication().GetParameterGroupByPath
("User parameter:BaseApp/Preferences/View");
hPart->SetBool("ColorOnTop",checked);
d->vpParent->OnTopWhenSelected.setValue(checked?3:d->onTopMode);
}
void ElementColors::slotDeleteDocument(const Document& Doc)
{
if (d->vpDoc==&Doc || d->editDoc==Doc.getDocument()->getName())
Control().closeDialog();
}
void ElementColors::slotDeleteObject(const ViewProvider& obj)
{
if (d->vp==&obj)
Control().closeDialog();
}
void ElementColors::on_removeSelection_clicked()
{
d->removeItems();
}
void ElementColors::on_boxSelect_clicked()
{
auto cmd = Application::Instance->commandManager().getCommandByName("Std_BoxElementSelection");
if(cmd)
cmd->invoke(0);
}
void ElementColors::on_hideSelection_clicked() {
auto sels = Selection().getSelectionEx(d->editDoc.c_str(),App::DocumentObject::getClassTypeId(),0);
for(auto &sel : sels) {
if(d->editObj!=sel.getFeatName())
continue;
const auto &subs = sel.getSubNames();
if(subs.size()) {
for(auto &sub : subs) {
if(boost::starts_with(sub,d->editSub)) {
auto name = Data::ComplexGeoData::noElementName(sub.c_str()+d->editSub.size());
name += ViewProvider::hiddenMarker();
d->addItem(-1,name.c_str());
}
}
d->apply();
}
return;
}
}
void ElementColors::on_addSelection_clicked()
{
auto sels = Selection().getSelectionEx(d->editDoc.c_str(),App::DocumentObject::getClassTypeId(),0);
d->items.clear();
if(sels.empty())
d->addItem(-1,"Face",true);
else {
for(auto &sel : sels) {
if(d->editObj!=sel.getFeatName())
continue;
const auto &subs = sel.getSubNames();
if(subs.empty()) {
d->addItem(-1,"Face",true);
break;
}
for(auto &sub : subs) {
if(boost::starts_with(sub,d->editSub))
d->addItem(-1,sub.c_str()+d->editSub.size(),true);
}
break;
}
}
if(d->items.size()) {
auto color = d->items.front()->data(Qt::UserRole).value<QColor>();
QColorDialog cd(color, this);
cd.setOption(QColorDialog::ShowAlphaChannel);
if (cd.exec()!=QDialog::Accepted)
return;
color = cd.selectedColor();
for(auto item : d->items) {
item->setData(Qt::UserRole,color);
d->px.fill(color);
item->setIcon(QIcon(d->px));
}
d->apply();
}
}
void ElementColors::on_removeAll_clicked()
{
d->removeAll();
}
bool ElementColors::accept()
{
d->accept();
Application::Instance->setEditDocument(0);
return true;
}
bool ElementColors::reject()
{
d->reset();
Application::Instance->setEditDocument(0);
return true;
}
void ElementColors::changeEvent(QEvent *e)
{
QWidget::changeEvent(e);
if (e->type() == QEvent::LanguageChange) {
d->ui->retranslateUi(this);
}
}
void ElementColors::leaveEvent(QEvent *e) {
QWidget::leaveEvent(e);
Selection().rmvPreselect();
if(d->hiddenSub.size()) {
d->vp->partialRender({d->hiddenSub},false);
d->hiddenSub.clear();
}
}
void ElementColors::on_elementList_itemEntered(QListWidgetItem *item) {
std::string name(qPrintable(item->data(Qt::UserRole+1).value<QString>()));
if(d->hiddenSub.size()) {
d->vp->partialRender({d->hiddenSub},false);
d->hiddenSub.clear();
}
if(ViewProvider::hasHiddenMarker(name.c_str())) {
d->hiddenSub = name;
d->vp->partialRender({name},true);
name.resize(name.size()-ViewProvider::hiddenMarker().size());
}
Selection().setPreselect(d->editDoc.c_str(),
d->editObj.c_str(), (d->editSub+name).c_str(),0,0,0,
d->ui->onTop->isChecked()?2:1);
}
void ElementColors::on_elementList_itemSelectionChanged() {
d->onSelectionChanged();
}
void ElementColors::onSelectionChanged(const SelectionChanges& msg)
{
d->onSelectionChanged(msg);
}
void ElementColors::on_elementList_itemDoubleClicked(QListWidgetItem *item) {
d->editItem(this,item);
}
/* TRANSLATOR Gui::TaskElementColors */
TaskElementColors::TaskElementColors(ViewProviderDocumentObject* vp, bool noHide)
{
widget = new ElementColors(vp,noHide);
taskbox = new TaskView::TaskBox(
QPixmap(), widget->windowTitle(), true, 0);
taskbox->groupLayout()->addWidget(widget);
Content.push_back(taskbox);
}
TaskElementColors::~TaskElementColors()
{
}
void TaskElementColors::open()
{
}
void TaskElementColors::clicked(int)
{
}
bool TaskElementColors::accept()
{
return widget->accept();
}
bool TaskElementColors::reject()
{
return widget->reject();
}
#include "moc_TaskElementColors.cpp"

View File

@@ -0,0 +1,93 @@
/****************************************************************************
* Copyright (c) 2018 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 GUI_TASKELEMENTCOLORS_H
#define GUI_TASKELEMENTCOLORS_H
#include <QListWidgetItem>
#include "TaskView/TaskView.h"
#include "TaskView/TaskDialog.h"
namespace Gui {
class Document;
class ViewProvider;
class ViewProviderDocumentObject;
class GuiExport ElementColors : public QWidget, public SelectionObserver
{
Q_OBJECT
public:
ElementColors(ViewProviderDocumentObject* vp, bool noHide=false);
~ElementColors();
bool accept();
bool reject();
private Q_SLOTS:
void on_removeSelection_clicked();
void on_addSelection_clicked();
void on_removeAll_clicked();
void on_elementList_itemDoubleClicked(QListWidgetItem *item);
void on_elementList_itemSelectionChanged();
void on_elementList_itemEntered(QListWidgetItem *item);
void on_recompute_clicked(bool checked);
void on_onTop_clicked(bool checked);
void on_hideSelection_clicked();
void on_boxSelect_clicked();
protected:
void onSelectionChanged(const SelectionChanges& msg);
void changeEvent(QEvent *e);
void leaveEvent(QEvent *);
void slotDeleteDocument(const Document&);
void slotDeleteObject(const ViewProvider&);
private:
class Private;
Private *d;
};
class GuiExport TaskElementColors : public TaskView::TaskDialog
{
Q_OBJECT
public:
TaskElementColors(ViewProviderDocumentObject* vp, bool noHide=false);
~TaskElementColors();
public:
void open();
bool accept();
bool reject();
void clicked(int);
QDialogButtonBox::StandardButtons getStandardButtons() const
{ return QDialogButtonBox::Ok|QDialogButtonBox::Cancel; }
private:
ElementColors* widget;
TaskView::TaskBox* taskbox;
};
} //namespace Gui
#endif // GUI_TASKELEMENTCOLORS_H

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Gui::TaskElementColors</class>
<widget class="QWidget" name="Gui::TaskElementColors">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>483</width>
<height>406</height>
</rect>
</property>
<property name="windowTitle">
<string>Set element color</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="objectLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="elementList">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="recompute">
<property name="text">
<string>Recompute after commit</string>
</property>
</widget>
</item>
<item row="3" column="0">
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="2">
<widget class="QPushButton" name="removeSelection">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="addSelection">
<property name="text">
<string>Edit</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="removeAll">
<property name="text">
<string>Remove all</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="hideSelection">
<property name="text">
<string>Hide</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="boxSelect">
<property name="text">
<string>Box select</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="onTop">
<property name="text">
<string>On-top when selected</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

2976
src/Gui/ViewProviderLink.cpp Normal file

File diff suppressed because it is too large Load Diff

339
src/Gui/ViewProviderLink.h Normal file
View File

@@ -0,0 +1,339 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 GUI_VIEWPROVIDER_LINK_H
#define GUI_VIEWPROVIDER_LINK_H
#include <boost/preprocessor/seq/for_each.hpp>
#include <App/PropertyGeo.h>
#include <App/Link.h>
#include "SoFCUnifiedSelection.h"
#include "ViewProviderPythonFeature.h"
#include "ViewProviderDocumentObject.h"
#include "ViewProviderExtension.h"
class SoBase;
class SoDragger;
class SoMaterialBinding;
namespace Gui {
class LinkInfo;
typedef boost::intrusive_ptr<LinkInfo> LinkInfoPtr;
class GuiExport ViewProviderLinkObserver: public ViewProviderExtension {
EXTENSION_TYPESYSTEM_HEADER_WITH_OVERRIDE();
public:
ViewProviderLinkObserver();
virtual ~ViewProviderLinkObserver();
void extensionReattach(App::DocumentObject *) override;
void extensionBeforeDelete() override;
void extensionOnChanged(const App::Property *) override;
void extensionUpdateData(const App::Property*) override;
void extensionFinishRestoring() override;
bool extensionCanDragObject(App::DocumentObject*) const override { return false; }
bool extensionCanDropObject(App::DocumentObject*) const override { return false; }
void extensionModeSwitchChange(void) override;
bool isLinkVisible() const;
void setLinkVisible(bool);
LinkInfoPtr linkInfo;
};
class GuiExport LinkOwner {
public:
virtual void unlink(LinkInfoPtr) {}
virtual void onLinkedIconChange(LinkInfoPtr) {}
virtual void onLinkedUpdateData(LinkInfoPtr,const App::Property *) {}
protected:
virtual ~LinkOwner() {}
};
class GuiExport LinkView : public Base::BaseClass, public LinkOwner {
TYPESYSTEM_HEADER();
public:
LinkView();
~LinkView();
LinkView &operator=(const LinkView&) = delete;
LinkView(const LinkView&) = delete;
virtual PyObject *getPyObject(void);
virtual void unlink(LinkInfoPtr) override;
virtual void onLinkedIconChange(LinkInfoPtr) override;
virtual void onLinkedUpdateData(LinkInfoPtr, const App::Property *) override;
bool isLinked() const;
SoFCSelectionRoot *getLinkRoot() const {return pcLinkRoot;}
QIcon getLinkedIcon(QPixmap overlay) const;
void updateLink();
void setLink(App::DocumentObject *obj,
const std::vector<std::string> &subs = std::vector<std::string>());
void setLinkViewObject(ViewProviderDocumentObject *vpd,
const std::vector<std::string> &subs = std::vector<std::string>());
std::vector<ViewProviderDocumentObject*> getChildren() const;
void setMaterial(int index, const App::Material *material);
void setDrawStyle(int linePattern, double lineWidth=0, double pointSize=0);
void setTransform(int index, const Base::Matrix4D &mat);
void renderDoubleSide(bool);
void setSize(int size);
int getSize() const { return nodeArray.size(); }
static void setTransform(SoTransform *pcTransform, const Base::Matrix4D &mat);
enum SnapshotType {
//three type of snapshot to override linked root node:
//override transform and visibility
SnapshotTransform = 0,
//override visibility
SnapshotVisible = 1,
//override none (for child objects of a container)
SnapshotChild = 2,
SnapshotMax,
//special type for sub object linking
SnapshotContainer = -1,
// sub object linking with transform override
SnapshotContainerTransform = -2,
};
void setNodeType(SnapshotType type, bool sublink=true);
void setChildren(const std::vector<App::DocumentObject*> &children,
const boost::dynamic_bitset<> &vis, SnapshotType type=SnapshotVisible);
bool linkGetDetailPath(const char *, SoFullPath *, SoDetail *&) const;
bool linkGetElementPicked(const SoPickedPoint *, std::string &) const;
void setElementVisible(int index, bool visible);
bool isElementVisible(int index) const;
ViewProviderDocumentObject *getOwner() const;
void setOwner(ViewProviderDocumentObject *vpd);
bool hasSubs() const;
std::vector<std::string> getSubNames() const;
ViewProviderDocumentObject *getLinkedView() const;
Base::BoundBox3d getBoundBox(ViewProviderDocumentObject *vpd=0) const;
void setInvalid();
protected:
void replaceLinkedRoot(SoSeparator *);
void resetRoot();
bool getGroupHierarchy(int index, SoFullPath *path) const;
protected:
LinkInfoPtr linkOwner;
LinkInfoPtr linkInfo;
CoinPtr<SoFCSelectionRoot> pcLinkRoot;
CoinPtr<SoTransform> pcTransform;
CoinPtr<SoSeparator> pcLinkedRoot;
CoinPtr<SoDrawStyle> pcDrawStyle; // for override line width and point size
CoinPtr<SoShapeHints> pcShapeHints; // for override double side rendering for mirror
SnapshotType nodeType;
SnapshotType childType;
bool autoSubLink; //auto delegate to linked sub object if there is only one sub object
class SubInfo;
friend class SubInfo;
std::map<std::string, std::unique_ptr<SubInfo> > subInfo;
class Element;
std::vector<std::unique_ptr<Element> > nodeArray;
std::unordered_map<SoNode*,int> nodeMap;
Py::Object PythonObject;
};
class GuiExport ViewProviderLink : public ViewProviderDocumentObject
{
PROPERTY_HEADER(Gui::ViewProviderLink);
typedef ViewProviderDocumentObject inherited;
public:
App::PropertyBool OverrideMaterial;
App::PropertyMaterial ShapeMaterial;
App::PropertyEnumeration DrawStyle;
App::PropertyFloatConstraint LineWidth;
App::PropertyFloatConstraint PointSize;
App::PropertyMaterialList MaterialList;
App::PropertyBoolList OverrideMaterialList;
App::PropertyBool Selectable;
App::PropertyColorList OverrideColorList;
App::PropertyPersistentObject ChildViewProvider;
ViewProviderLink();
virtual ~ViewProviderLink();
void attach(App::DocumentObject *pcObj) override;
void reattach(App::DocumentObject *pcObj) override;
bool isSelectable(void) const override;
bool useNewSelectionModel(void) const override {return true;}
void updateData(const App::Property*) override;
void onChanged(const App::Property* prop) override;
std::vector<App::DocumentObject*> claimChildren(void) const override;
bool getElementPicked(const SoPickedPoint *, std::string &) const override;
bool getDetailPath(const char *, SoFullPath *, bool, SoDetail *&) const override;
void finishRestoring() override;
QIcon getIcon(void) const override;
bool canDragObjects() const override;
bool canDragObject(App::DocumentObject*) const override;
void dragObject(App::DocumentObject*) override;
bool canDropObjects() const override;
bool canDragAndDropObject(App::DocumentObject*) const override;
bool canDropObjectEx(App::DocumentObject *obj, App::DocumentObject *owner,
const char *subname, const std::vector<std::string> &elements) const override;
std::string dropObjectEx(App::DocumentObject*, App::DocumentObject*,
const char *subname, const std::vector<std::string> &elements) override;
bool onDelete(const std::vector<std::string> &) override;
bool canDelete(App::DocumentObject* obj) const override;
std::vector<std::string> getDisplayModes(void) const override;
void setupContextMenu(QMenu*, QObject*, const char*) override;
virtual QPixmap getOverlayPixmap() const;
ViewProvider *startEditing(int ModNum) override;
bool doubleClicked() override;
PyObject *getPyObject() override;
PyObject *getPyLinkView();
static void updateLinks(ViewProvider *vp);
void updateDraggingPlacement(const Base::Placement &pla, bool force=false);
Base::Placement currentDraggingPlacement() const;
void enableCenterballDragger(bool enable);
bool isUsingCenterballDragger() const { return useCenterballDragger; }
std::map<std::string, App::Color> getElementColors(const char *subname=0) const override;
void setElementColors(const std::map<std::string, App::Color> &colors) override;
void setOverrideMode(const std::string &mode) override;
virtual void onBeforeChange(const App::Property*) override;
ViewProviderDocumentObject *getChildViewProvider() const {
return childVp;
}
virtual App::Property *getPropertyByName(const char* name) const override;
virtual void getPropertyMap(std::map<std::string,App::Property*> &Map) const override;
virtual void getPropertyList(std::vector<App::Property*> &List) const override;
virtual ViewProviderDocumentObject *getLinkedViewProvider(
std::string *subname=0, bool recursive=false) const override;
protected:
bool setEdit(int ModNum) override;
void setEditViewer(View3DInventorViewer*, int ModNum) override;
void unsetEditViewer(View3DInventorViewer*) override;
bool linkEdit(const App::LinkBaseExtension *ext=0) const;
enum LinkType {
LinkTypeNone,
LinkTypeNormal,
LinkTypeSubs,
};
bool hasElements(const App::LinkBaseExtension *ext = 0) const;
bool isGroup(const App::LinkBaseExtension *ext=0, bool plainGroup=false) const;
const App::LinkBaseExtension *getLinkExtension() const;
App::LinkBaseExtension *getLinkExtension();
void updateDataPrivate(App::LinkBaseExtension *ext, const App::Property*);
void updateElementList(App::LinkBaseExtension *ext);
bool setLinkType(App::LinkBaseExtension *ext);
void onChangeIcon() const;
std::vector<App::DocumentObject*> claimChildrenPrivate() const;
void applyMaterial();
void applyColors();
void checkIcon(const App::LinkBaseExtension *ext=0);
ViewProvider *getLinkedView(bool real,const App::LinkBaseExtension *ext=0) const;
bool initDraggingPlacement();
bool callDraggerProxy(const char *fname, bool update);
private:
static void dragStartCallback(void * data, SoDragger * d);
static void dragFinishCallback(void * data, SoDragger * d);
static void dragMotionCallback(void * data, SoDragger * d);
protected:
LinkView *linkView;
LinkType linkType;
bool hasSubName;
bool hasSubElement;
bool useCenterballDragger;
struct DraggerContext{
Base::Matrix4D preTransform;
Base::Placement initialPlacement;
Base::Matrix4D mat;
Base::BoundBox3d bbox;
bool cmdPending;
};
std::unique_ptr<DraggerContext> dragCtx;
CoinPtr<SoDragger> pcDragger;
ViewProviderDocumentObject *childVp;
LinkInfoPtr childVpLink;
mutable qint64 overlayCacheKey;
};
typedef ViewProviderPythonFeatureT<ViewProviderLink> ViewProviderLinkPython;
} //namespace Gui
#ifdef _MSC_VER
// forward decleration to please VC 2013
void intrusive_ptr_add_ref(Gui::LinkInfo *px);
void intrusive_ptr_release(Gui::LinkInfo *px);
#endif
#endif // GUI_VIEWPROVIDER_LINK_H

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<GenerateModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="generateMetaModel_Module.xsd">
<PythonExport
Father="ViewProviderDocumentObjectPy"
Name="ViewProviderLinkPy"
Twin="ViewProviderLink"
TwinPointer="ViewProviderLink"
Include="Gui/ViewProviderLink.h"
Namespace="Gui"
FatherInclude="Gui/ViewProviderDocumentObjectPy.h"
FatherNamespace="Gui">
<Documentation>
<Author Licence="LGPL" Name="Zheng, Lei" EMail="realthunder.dev@gmail.com" />
<UserDocu>This is the ViewProviderLink class</UserDocu>
</Documentation>
<Attribute Name="DraggingPlacement">
<Documentation>
<UserDocu>Get/set dragger placement during dragging</UserDocu>
</Documentation>
<Parameter Name="DraggingPlacement" Type="Object" />
</Attribute>
<Attribute Name="UseCenterballDragger">
<Documentation>
<UserDocu>Get/set dragger type</UserDocu>
</Documentation>
<Parameter Name="UseCenterballDragger" Type="Boolean" />
</Attribute>
<Attribute Name="LinkView" ReadOnly="true">
<Documentation>
<UserDocu>Get the associated LinkView object</UserDocu>
</Documentation>
<Parameter Name="LinkView" Type="Object" />
</Attribute>
</PythonExport>
</GenerateModel>

View File

@@ -0,0 +1,83 @@
/****************************************************************************
* Copyright (c) 2017 Zheng, Lei (realthunder) <realthunder.dev@gmail.com>*
* *
* 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 *
* *
****************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <sstream>
#endif
#include "Gui/ViewProviderLink.h"
#include <App/DocumentObject.h>
#include <Base/PlacementPy.h>
// inclusion of the generated files (generated out of ViewProviderLinkPy.xml)
#include "ViewProviderLinkPy.h"
#include "ViewProviderLinkPy.cpp"
using namespace Gui;
// returns a string which represents the object e.g. when printed in python
std::string ViewProviderLinkPy::representation(void) const
{
std::stringstream str;
str << "<ViewProviderLink at " << getViewProviderLinkPtr() << ">";
return str.str();
}
Py::Object ViewProviderLinkPy::getDraggingPlacement() const {
return Py::Object(new Base::PlacementPy(new Base::Placement(
getViewProviderLinkPtr()->currentDraggingPlacement())));
}
void ViewProviderLinkPy::setDraggingPlacement(Py::Object arg) {
if(!PyObject_TypeCheck(arg.ptr(),&Base::PlacementPy::Type))
throw Py::TypeError("expects a placement");
getViewProviderLinkPtr()->updateDraggingPlacement(
*static_cast<Base::PlacementPy*>(arg.ptr())->getPlacementPtr());
}
Py::Boolean ViewProviderLinkPy::getUseCenterballDragger() const {
return Py::Boolean(getViewProviderLinkPtr()->isUsingCenterballDragger());
}
void ViewProviderLinkPy::setUseCenterballDragger(Py::Boolean arg) {
try {
getViewProviderLinkPtr()->enableCenterballDragger(arg);
}catch(const Base::Exception &e){
throw Py::Exception(Base::BaseExceptionFreeCADError,e.what());
}
}
Py::Object ViewProviderLinkPy::getLinkView() const {
return Py::Object(getViewProviderLinkPtr()->getPyLinkView(),true);
}
PyObject *ViewProviderLinkPy::getCustomAttributes(const char* /*attr*/) const
{
return 0;
}
int ViewProviderLinkPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/)
{
return 0;
}