1
/****************************************************************************
2
* Copyright (c) 2017 Zheng Lei (realthunder) <realthunder.dev@gmail.com> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
****************************************************************************/
23
#include "PreCompiled.h"
24
#include <boost/property_map/property_map.hpp>
26
#include <boost/range.hpp>
27
#include <boost/algorithm/string/predicate.hpp>
28
#include <Base/Tools.h>
31
#include "Application.h"
32
#include "ElementNamingUtils.h"
33
#include "ComplexGeoDataPy.h"
35
#include "DocumentObserver.h"
36
#include "GeoFeatureGroupExtension.h"
38
#include "LinkBaseExtensionPy.h"
40
//FIXME: ISO C++11 requires at least one argument for the "..." in a variadic macro
42
# pragma clang diagnostic push
43
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
46
FC_LOG_LEVEL_INIT("App::Link", true,true)
50
namespace sp = std::placeholders;
52
using CharRange = boost::iterator_range<const char*>;
54
////////////////////////////////////////////////////////////////////////
63
// Auto generated code. See class document of LinkParams.
64
class LinkParamsP: public ParameterGrp::ObserverType {
66
// Auto generated code. See class document of LinkParams.
67
ParameterGrp::handle handle;
69
// Auto generated code. See class document of LinkParams.
70
std::unordered_map<const char *,void(*)(LinkParamsP*),App::CStringHasher,App::CStringHasher> funcs;
72
bool CopyOnChangeApplyToAll; // Auto generated code. See class document of LinkParams.
74
// Auto generated code. See class document of LinkParams.
76
handle = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Link");
79
CopyOnChangeApplyToAll = handle->GetBool("CopyOnChangeApplyToAll", true);
80
funcs["CopyOnChangeApplyToAll"] = &LinkParamsP::updateCopyOnChangeApplyToAll;
83
// Auto generated code. See class document of LinkParams.
84
~LinkParamsP() override = default;
86
// Auto generated code. See class document of LinkParams.
87
void OnChange(Base::Subject<const char*> &, const char* sReason) override {
90
auto it = funcs.find(sReason);
97
// Auto generated code. See class document of LinkParams.
98
static void updateCopyOnChangeApplyToAll(LinkParamsP *self) {
99
self->CopyOnChangeApplyToAll = self->handle->GetBool("CopyOnChangeApplyToAll", true);
103
// Auto generated code. See class document of LinkParams.
104
LinkParamsP *instance() {
105
static LinkParamsP *inst = new LinkParamsP;
109
} // Anonymous namespace
111
// Auto generated code. See class document of LinkParams.
112
ParameterGrp::handle LinkParams::getHandle() {
113
return instance()->handle;
116
// Auto generated code. See class document of LinkParams.
117
const char *LinkParams::docCopyOnChangeApplyToAll() {
118
return QT_TRANSLATE_NOOP("LinkParams",
119
"Stores the last user choice of whether to apply CopyOnChange setup to all links\n"
120
"that reference the same configurable object");
123
// Auto generated code. See class document of LinkParams.
124
const bool & LinkParams::getCopyOnChangeApplyToAll() {
125
return instance()->CopyOnChangeApplyToAll;
128
// Auto generated code. See class document of LinkParams.
129
const bool & LinkParams::defaultCopyOnChangeApplyToAll() {
130
static const bool def = true;
134
// Auto generated code. See class document of LinkParams.
135
void LinkParams::setCopyOnChangeApplyToAll(const bool &v) {
136
instance()->handle->SetBool("CopyOnChangeApplyToAll",v);
137
instance()->CopyOnChangeApplyToAll = v;
140
// Auto generated code. See class document of LinkParams.
141
void LinkParams::removeCopyOnChangeApplyToAll() {
142
instance()->handle->RemoveBool("CopyOnChangeApplyToAll");
146
///////////////////////////////////////////////////////////////////////////////
148
EXTENSION_PROPERTY_SOURCE(App::LinkBaseExtension, App::DocumentObjectExtension)
150
LinkBaseExtension::LinkBaseExtension()
152
initExtensionType(LinkBaseExtension::getExtensionClassTypeId());
153
EXTENSION_ADD_PROPERTY_TYPE(_LinkTouched, (false), " Link",
154
PropertyType(Prop_Hidden|Prop_NoPersist),0);
155
EXTENSION_ADD_PROPERTY_TYPE(_ChildCache, (), " Link",
156
PropertyType(Prop_Hidden|Prop_NoPersist|Prop_ReadOnly),0);
157
_ChildCache.setScope(LinkScope::Global);
158
EXTENSION_ADD_PROPERTY_TYPE(_LinkOwner, (0), " Link",
159
PropertyType(Prop_Hidden|Prop_Output),0);
160
props.resize(PropMax,nullptr);
163
PyObject* LinkBaseExtension::getExtensionPyObject() {
164
if (ExtensionPythonObject.is(Py::_None())){
165
// ref counter is set to 1
166
ExtensionPythonObject = Py::Object(new LinkBaseExtensionPy(this),true);
168
return Py::new_reference_to(ExtensionPythonObject);
171
const std::vector<LinkBaseExtension::PropInfo> &LinkBaseExtension::getPropertyInfo() const {
172
static std::vector<LinkBaseExtension::PropInfo> PropsInfo;
173
if(PropsInfo.empty()) {
174
BOOST_PP_SEQ_FOR_EACH(LINK_PROP_INFO,PropsInfo,LINK_PARAMS);
179
const LinkBaseExtension::PropInfoMap &LinkBaseExtension::getPropertyInfoMap() const {
180
static PropInfoMap PropsMap;
181
if(PropsMap.empty()) {
182
const auto &infos = getPropertyInfo();
183
for(const auto &info : infos)
184
PropsMap[info.name] = info;
189
Property *LinkBaseExtension::getProperty(int idx) {
190
if(idx>=0 && idx<(int)props.size())
195
Property *LinkBaseExtension::getProperty(const char *name) {
196
const auto &info = getPropertyInfoMap();
197
auto it = info.find(name);
200
return getProperty(it->second.index);
203
void LinkBaseExtension::setProperty(int idx, Property *prop) {
204
const auto &infos = getPropertyInfo();
205
if(idx<0 || idx>=(int)infos.size())
206
LINK_THROW(Base::RuntimeError,"App::LinkBaseExtension: property index out of range");
209
props[idx]->setStatus(Property::LockDynamic,false);
210
props[idx] = nullptr;
214
if(!prop->isDerivedFrom(infos[idx].type)) {
215
std::ostringstream str;
216
str << "App::LinkBaseExtension: expected property type '" <<
217
infos[idx].type.getName() << "', instead of '" <<
218
prop->getClassTypeId().getName() << "'";
219
LINK_THROW(Base::TypeError,str.str().c_str());
223
props[idx]->setStatus(Property::LockDynamic,true);
227
static const char *linkModeEnums[] = {"None","Auto Delete","Auto Link","Auto Unlink",nullptr};
228
auto propLinkMode = static_cast<PropertyEnumeration*>(prop);
229
if(!propLinkMode->hasEnums())
230
propLinkMode->setEnums(linkModeEnums);
233
case PropLinkCopyOnChange: {
234
static const char *enums[] = {"Disabled","Enabled","Owned","Tracking",nullptr};
235
auto propEnum = static_cast<PropertyEnumeration*>(prop);
236
if(!propEnum->hasEnums())
237
propEnum->setEnums(enums);
240
case PropLinkCopyOnChangeSource:
241
case PropLinkCopyOnChangeGroup:
242
if (auto linkProp = Base::freecad_dynamic_cast<PropertyLinkBase>(prop)) {
243
linkProp->setScope(LinkScope::Global);
246
case PropLinkCopyOnChangeTouched:
247
prop->setStatus(Property::Hidden, true);
249
case PropLinkTransform:
250
case PropLinkPlacement:
252
if(getLinkTransformProperty() &&
253
getLinkPlacementProperty() &&
254
getPlacementProperty())
256
bool transform = getLinkTransformValue();
257
getPlacementProperty()->setStatus(Property::Hidden, transform);
258
getLinkPlacementProperty()->setStatus(Property::Hidden, !transform);
261
case PropElementList:
262
getElementListProperty()->setScope(LinkScope::Global);
263
getElementListProperty()->setStatus(Property::Hidden, true);
265
case PropLinkedObject:
266
// Make ElementList as read-only if we are not a group (i.e. having
267
// LinkedObject property), because it is for holding array elements.
268
if(getElementListProperty()) {
269
getElementListProperty()->setStatus(
270
Property::Immutable, getLinkedObjectProperty() != nullptr);
272
if (auto linkProp = getLinkedObjectProperty()) {
273
linkProp->setScope(LinkScope::Global);
276
case PropVisibilityList:
277
getVisibilityListProperty()->setStatus(Property::Immutable, true);
278
getVisibilityListProperty()->setStatus(Property::Hidden, true);
282
if(FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_TRACE)) {
283
const char *propName;
286
else if(prop->getContainer())
287
propName = prop->getName();
289
propName = extensionGetPropertyName(prop);
290
if(!Property::isValidName(propName))
292
FC_TRACE("set property " << infos[idx].name << ": " << propName);
296
static const char _GroupPrefix[] = "Configuration (";
298
App::DocumentObjectExecReturn *LinkBaseExtension::extensionExecute() {
299
// The actual value of LinkTouched is not important, just to notify view
300
// provider that the link (in fact, its dependents, i.e. linked ones) have
302
_LinkTouched.touch();
304
if(getLinkedObjectProperty()) {
305
DocumentObject *linked = getTrueLinkedObject(true);
307
std::ostringstream ss;
308
ss << "Link broken!";
309
auto xlink = Base::freecad_dynamic_cast<PropertyXLink>(
310
getLinkedObjectProperty());
312
const char *objname = xlink->getObjectName();
313
if (objname && objname[0])
314
ss << "\nObject: " << objname;
315
const char *filename = xlink->getFilePath();
316
if (filename && filename[0])
317
ss << "\nFile: " << filename;
319
return new App::DocumentObjectExecReturn(ss.str().c_str());
322
App::DocumentObject *container = getContainer();
323
auto source = getLinkCopyOnChangeSourceValue();
324
if (source && getLinkCopyOnChangeValue() == CopyOnChangeTracking
325
&& getLinkCopyOnChangeTouchedValue())
330
// the previous linked object could be deleted by syncCopyOnChange - #12281
331
linked = getTrueLinkedObject(true);
333
return new App::DocumentObjectExecReturn("Error in processing variable link");
336
PropertyPythonObject *proxy = nullptr;
337
if(getLinkExecuteProperty()
338
&& !boost::iequals(getLinkExecuteValue(), "none")
339
&& (!_LinkOwner.getValue()
340
|| !container->getDocument()->getObjectByID(_LinkOwner.getValue())))
342
// Check if this is an element link. Do not invoke appLinkExecute()
343
// if so, because it will be called from the link array.
344
proxy = Base::freecad_dynamic_cast<PropertyPythonObject>(
345
linked->getPropertyByName("Proxy"));
348
Base::PyGILStateLocker lock;
349
const char *errMsg = "Linked proxy execute failed";
352
Py::Object proxyValue = proxy->getValue();
353
const char *method = getLinkExecuteValue();
354
if(!method || !method[0])
355
method = "appLinkExecute";
356
if(proxyValue.hasAttr(method)) {
357
Py::Object attr = proxyValue.getAttr(method);
358
if(attr.ptr() && attr.isCallable()) {
360
args.setItem(0, Py::asObject(linked->getPyObject()));
361
args.setItem(1, Py::asObject(container->getPyObject()));
362
if(!_getElementCountValue()) {
363
Py::Callable(attr).apply(args);
365
const auto &elements = _getElementListValue();
366
for(int i=0; i<_getElementCountValue(); ++i) {
367
args.setItem(2, Py::Int(i));
368
if(i < (int)elements.size())
369
args.setItem(3, Py::asObject(elements[i]->getPyObject()));
371
args.setItem(3, Py::Object());
372
Py::Callable(attr).apply(args);
377
} catch (Py::Exception &) {
380
return new App::DocumentObjectExecReturn(errMsg);
381
} catch (Base::Exception &e) {
383
return new App::DocumentObjectExecReturn(errMsg);
387
auto parent = getContainer();
388
setupCopyOnChange(parent);
390
if(hasCopyOnChange && getLinkCopyOnChangeValue()==CopyOnChangeDisabled) {
391
hasCopyOnChange = false;
392
std::vector<Property*> props;
393
parent->getPropertyList(props);
394
for(auto prop : props) {
395
if(isCopyOnChangeProperty(parent, *prop)) {
397
parent->removeDynamicProperty(prop->getName());
398
} catch (Base::Exception &e) {
406
return inherited::extensionExecute();
409
short LinkBaseExtension::extensionMustExecute() {
410
auto link = getLink();
413
return link->mustExecute();
416
std::vector<App::DocumentObject*>
417
LinkBaseExtension::getOnChangeCopyObjects(
418
std::vector<App::DocumentObject *> *excludes,
419
App::DocumentObject *src)
421
auto parent = getContainer();
423
src = getLinkCopyOnChangeSourceValue();
424
if (!src || getLinkCopyOnChangeValue() == CopyOnChangeDisabled)
427
auto res = Document::getDependencyList({src}, Document::DepSort);
428
for (auto it=res.begin(); it!=res.end();) {
434
auto prop = Base::freecad_dynamic_cast<PropertyMap>(
435
obj->getPropertyByName("_CopyOnChangeControl"));
436
static std::map<std::string, std::string> dummy;
437
const auto & map = prop && prop->getContainer()==obj ? prop->getValues() : dummy;
439
if (src->getDocument() != obj->getDocument())
441
auto iter = map.find("*");
442
if (iter != map.end())
443
v = iter->second.c_str();
444
else if ((iter = map.find(parent->getNameInDocument())) != map.end())
445
v = iter->second.c_str();
446
if (boost::equals(v, "-")) {
448
excludes->push_back(obj);
459
void LinkBaseExtension::setOnChangeCopyObject(
460
App::DocumentObject *obj, OnChangeCopyOptions options)
462
auto parent = getContainer();
463
Base::Flags<OnChangeCopyOptions> flags(options);
464
bool exclude = flags.testFlag(OnChangeCopyOptions::Exclude);
465
bool external = parent->getDocument() != obj->getDocument();
466
auto prop = Base::freecad_dynamic_cast<PropertyMap>(
467
obj->getPropertyByName("_CopyOnChangeControl"));
469
if (external == exclude && !prop)
474
prop = static_cast<PropertyMap*>(
475
obj->addDynamicProperty("App::PropertyMap", "_CopyOnChangeControl"));
476
} catch (Base::Exception &e) {
480
FC_ERR("Failed to setup copy on change object " << obj->getFullName());
485
const char *key = flags.testFlag(OnChangeCopyOptions::ApplyAll) ? "*" : parent->getDagKey();
487
prop->setValue(key, exclude ? "" : "+");
489
prop->setValue(key, exclude ? "-" : "");
492
// The purpose of this function is to synchronize the mutated copy to the
493
// original linked CopyOnChange object. It will make a new copy if any of the
494
// non-CopyOnChange property of the original object has changed.
495
void LinkBaseExtension::syncCopyOnChange()
497
if (!getLinkCopyOnChangeValue())
499
auto linkProp = getLinkedObjectProperty();
500
auto srcProp = getLinkCopyOnChangeSourceProperty();
501
auto srcTouched = getLinkCopyOnChangeTouchedProperty();
505
|| !srcProp->getValue()
506
|| !linkProp->getValue()
507
|| srcProp->getValue() == linkProp->getValue())
510
auto parent = getContainer();
512
auto linked = linkProp->getValue();
514
std::vector<App::DocumentObjectT> oldObjs;
515
std::vector<App::DocumentObject*> objs;
517
// CopyOnChangeGroup is a hidden dynamic property for holding a LinkGroup
518
// for holding the mutated copy of the original linked object and all its
520
LinkGroup *copyOnChangeGroup = nullptr;
521
if (auto prop = getLinkCopyOnChangeGroupProperty()) {
522
copyOnChangeGroup = Base::freecad_dynamic_cast<LinkGroup>(prop->getValue());
523
if (!copyOnChangeGroup) {
524
// Create the LinkGroup if not exist
525
auto group = new LinkGroup;
526
group->LinkMode.setValue(LinkModeAutoDelete);
527
parent->getDocument()->addObject(group, "CopyOnChangeGroup");
528
prop->setValue(group);
530
// If it exists, then obtain all copied objects. Note that we stores
531
// the dynamic property _SourceUUID in oldObjs if possible, in order
532
// to match the possible new copy later.
533
objs = copyOnChangeGroup->ElementList.getValues();
534
for (auto obj : objs) {
535
if (!obj->isAttachedToDocument())
537
auto prop = Base::freecad_dynamic_cast<PropertyUUID>(
538
obj->getPropertyByName("_SourceUUID"));
539
if (prop && prop->getContainer() == obj)
540
oldObjs.emplace_back(prop);
542
oldObjs.emplace_back(obj);
544
std::sort(objs.begin(), objs.end());
548
// Obtain the original linked object and its dependency in depending order.
549
// The last being the original linked object.
550
auto srcObjs = getOnChangeCopyObjects();
551
// Refresh signal connection to monitor changes
552
monitorOnChangeCopyObjects(srcObjs);
554
// Copy the objects. Document::export/importObjects() (called by
555
// copyObject()) will generate a _ObjectUUID for each source object and
556
// match it with a _SourceUUID in the copy.
557
auto copiedObjs = parent->getDocument()->copyObject(srcObjs);
558
if(copiedObjs.empty())
561
// copyObject() will return copy in order of the same order of the input,
562
// so the last object will be the copy of the original linked object
563
auto newLinked = copiedObjs.back();
565
// We are copying from the original linked object and we've already mutated
566
// it, so we need to copy all CopyOnChange properties from the mutated
567
// object to the new copy.
568
std::vector<App::Property*> propList;
569
linked->getPropertyList(propList);
570
for (auto prop : propList) {
571
if(!prop->testStatus(Property::CopyOnChange)
572
|| prop->getContainer()!=linked)
574
auto p = newLinked->getPropertyByName(prop->getName());
575
if (p && p->getTypeId() == prop->getTypeId()) {
576
std::unique_ptr<Property> pCopy(prop->Copy());
581
if (copyOnChangeGroup) {
582
// The order of the copied objects is in dependency order (because of
583
// getOnChangeCopyObjects()). We reverse it here so that we can later
584
// on delete it in reverse order to avoid error (because some parent
585
// objects may want to delete their own children).
586
std::reverse(copiedObjs.begin(), copiedObjs.end());
587
copyOnChangeGroup->ElementList.setValues(copiedObjs);
590
// Create a map to find the corresponding replacement of the new copies to
591
// the mutated object. The reason for doing so is that we are copying from
592
// the original linked object and its dependency, not the mutated objects
593
// which are old copies. There could be arbitrary changes in the originals
594
// which may add or remove or change depending orders, while the
595
// replacement happens between the new and old copies.
597
std::map<Base::Uuid, App::DocumentObjectT> newObjs;
598
for (auto obj : copiedObjs) {
599
auto prop = Base::freecad_dynamic_cast<PropertyUUID>(
600
obj->getPropertyByName("_SourceUUID"));
602
newObjs.insert(std::make_pair(prop->getValue(), obj));
605
std::vector<std::pair<App::DocumentObject*, App::DocumentObject*> > replacements;
606
for (const auto &objT : oldObjs) {
607
auto prop = Base::freecad_dynamic_cast<PropertyUUID>(objT.getProperty());
610
auto it = newObjs.find(prop->getValue());
611
if (it == newObjs.end())
613
auto oldObj = objT.getObject();
614
auto newObj = it->second.getObject();
615
if (oldObj && newObj)
616
replacements.emplace_back(oldObj, newObj);
619
std::vector<std::pair<App::DocumentObjectT, std::unique_ptr<App::Property> > > propChanges;
620
if (!replacements.empty()) {
621
std::sort(copiedObjs.begin(), copiedObjs.end());
623
// Global search for links affected by the replacement. We accumulate
624
// the changes in propChanges without applying, in order to avoid any
625
// side effect of changing while searching.
626
for(auto doc : App::GetApplication().getDocuments()) {
627
for(auto o : doc->getObjects()) {
629
|| std::binary_search(objs.begin(), objs.end(), o)
630
|| std::binary_search(copiedObjs.begin(), copiedObjs.end(), o))
633
o->getPropertyList(propList);
634
for(auto prop : propList) {
635
if (prop->getContainer() != o)
637
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
640
for (const auto &v : replacements) {
641
std::unique_ptr<App::Property> copy(
642
linkProp->CopyOnLinkReplace(parent,v.first,v.second));
645
propChanges.emplace_back(App::DocumentObjectT(prop),std::move(copy));
652
Base::StateLocker guard(pauseCopyOnChange);
653
linkProp->setValue(newLinked);
654
newLinked->Visibility.setValue(false);
655
srcTouched->setValue(false);
657
// Apply the global link changes.
658
for(const auto &v : propChanges) {
659
auto prop = v.first.getProperty();
661
prop->Paste(*v.second.get());
664
// Finally, remove all old copies. oldObjs stores type of DocumentObjectT
665
// which stores the object name. Before removing, we need to make sure the
666
// object exists, and it is not some new object with the same name, by
667
// checking objs which stores DocumentObject pointer.
668
for (const auto &objT : oldObjs) {
669
auto obj = objT.getObject();
670
if (obj && std::binary_search(objs.begin(), objs.end(), obj))
671
obj->getDocument()->removeObject(obj->getNameInDocument());
675
bool LinkBaseExtension::isLinkedToConfigurableObject() const
677
if (auto linked = getLinkedObjectValue()) {
678
std::vector<App::Property*> propList;
679
linked->getPropertyList(propList);
680
for (auto prop : propList) {
681
if(prop->testStatus(Property::CopyOnChange)
682
&& prop->getContainer()==linked)
689
bool LinkBaseExtension::isCopyOnChangeProperty(DocumentObject *obj, const App::Property &prop) {
690
if(obj!=prop.getContainer() || !prop.testStatus(App::Property::PropDynamic))
692
auto group = prop.getGroup();
693
return group && boost::starts_with(group,_GroupPrefix);
696
void LinkBaseExtension::setupCopyOnChange(DocumentObject *parent, bool checkSource) {
697
copyOnChangeConns.clear();
698
copyOnChangeSrcConns.clear();
700
auto linked = getTrueLinkedObject(false);
701
if(!linked || getLinkCopyOnChangeValue()==CopyOnChangeDisabled)
704
if (checkSource && !pauseCopyOnChange) {
705
PropertyLink *source = getLinkCopyOnChangeSourceProperty();
707
source->setValue(linked);
708
if (auto touched = getLinkCopyOnChangeTouchedProperty())
709
touched->setValue(false);
713
hasCopyOnChange = setupCopyOnChange(parent,linked,©OnChangeConns,hasCopyOnChange);
714
if (hasCopyOnChange && getLinkCopyOnChangeValue() == CopyOnChangeOwned
715
&& getLinkedObjectValue()
716
&& getLinkedObjectValue() == getLinkCopyOnChangeSourceValue())
722
bool LinkBaseExtension::setupCopyOnChange(DocumentObject *parent, DocumentObject *linked,
723
std::vector<boost::signals2::scoped_connection> *copyOnChangeConns, bool checkExisting)
725
if(!parent || !linked)
730
std::unordered_map<Property*, Property*> newProps;
731
std::vector<Property*> props;
732
linked->getPropertyList(props);
733
for(auto prop : props) {
734
if(!prop->testStatus(Property::CopyOnChange)
735
|| prop->getContainer()!=linked)
740
const char* linkedGroupName = prop->getGroup();
741
if(!linkedGroupName || !linkedGroupName[0])
742
linkedGroupName = "Base";
744
std::string groupName;
745
groupName = _GroupPrefix;
746
if(boost::starts_with(linkedGroupName,_GroupPrefix))
747
groupName += linkedGroupName + sizeof(_GroupPrefix)-1;
749
groupName += linkedGroupName;
753
auto p = parent->getPropertyByName(prop->getName());
755
if(p->getContainer()!=parent)
758
const char* otherGroupName = p->getGroup();
759
if(!otherGroupName || !boost::starts_with(otherGroupName, _GroupPrefix)) {
760
FC_WARN(p->getFullName() << " shadows another CopyOnChange property "
761
<< prop->getFullName());
764
if(p->getTypeId() != prop->getTypeId() || groupName != otherGroupName) {
765
parent->removeDynamicProperty(p->getName());
771
p = parent->addDynamicProperty(prop->getTypeId().getName(),
772
prop->getName(), groupName.c_str(), prop->getDocumentation());
773
std::unique_ptr<Property> pcopy(prop->Copy());
774
Base::ObjectStatusLocker<Property::Status,Property> guard(Property::User3, p);
778
p->setStatusValue(prop->getStatus());
785
parent->getPropertyList(props);
786
for(auto prop : props) {
787
if(prop->getContainer()!=parent)
789
auto gname = prop->getGroup();
790
if(!gname || !boost::starts_with(gname, _GroupPrefix))
792
if(!newProps.count(prop))
793
parent->removeDynamicProperty(prop->getName());
797
if(!copyOnChangeConns)
800
for(const auto &v : newProps) {
801
// sync configuration properties
802
copyOnChangeConns->push_back(v.second->signalChanged.connect([parent](const Property &prop) {
803
if(!prop.testStatus(Property::CopyOnChange))
805
auto p = parent->getPropertyByName(prop.getName());
806
if(p && p->getTypeId()==prop.getTypeId()) {
807
std::unique_ptr<Property> pcopy(prop.Copy());
808
// temperoray set Output to prevent touching
809
p->setStatus(Property::Output, true);
810
// temperoray block copy on change
811
Base::ObjectStatusLocker<Property::Status,Property> guard(Property::User3, p);
814
p->setStatusValue(prop.getStatus());
822
void LinkBaseExtension::checkCopyOnChange(App::DocumentObject *parent, const App::Property &prop)
824
if(!parent || !parent->getDocument()
825
|| parent->getDocument()->isPerformingTransaction())
828
auto linked = getLinkedObjectValue();
829
if(!linked || getLinkCopyOnChangeValue()==CopyOnChangeDisabled
830
|| !isCopyOnChangeProperty(parent,prop))
833
if(getLinkCopyOnChangeValue() == CopyOnChangeOwned ||
834
(getLinkCopyOnChangeValue() == CopyOnChangeTracking
835
&& linked != getLinkCopyOnChangeSourceValue()))
837
auto p = linked->getPropertyByName(prop.getName());
838
if(p && p->getTypeId()==prop.getTypeId()) {
839
std::unique_ptr<Property> pcopy(prop.Copy());
846
auto linkedProp = linked->getPropertyByName(prop.getName());
847
if(!linkedProp || linkedProp->getTypeId()!=prop.getTypeId() || linkedProp->isSame(prop))
850
auto copied = makeCopyOnChange();
852
linkedProp = copied->getPropertyByName(prop.getName());
853
if(linkedProp && linkedProp->getTypeId()==prop.getTypeId()) {
854
std::unique_ptr<Property> pcopy(prop.Copy());
856
linkedProp->Paste(*pcopy);
861
App::DocumentObject *LinkBaseExtension::makeCopyOnChange() {
862
auto linked = getLinkedObjectValue();
863
if (pauseCopyOnChange || !linked)
865
auto parent = getContainer();
866
auto srcobjs = getOnChangeCopyObjects(nullptr, linked);
867
for (auto obj : srcobjs) {
868
if (obj->testStatus(App::PartialObject)) {
869
FC_THROWM(Base::RuntimeError, "Cannot copy partial loaded object: "
870
<< obj->getFullName());
873
auto objs = parent->getDocument()->copyObject(srcobjs);
877
monitorOnChangeCopyObjects(srcobjs);
879
linked = objs.back();
880
linked->Visibility.setValue(false);
882
Base::StateLocker guard(pauseCopyOnChange);
883
getLinkedObjectProperty()->setValue(linked);
884
if (getLinkCopyOnChangeValue() == CopyOnChangeEnabled)
885
getLinkCopyOnChangeProperty()->setValue(CopyOnChangeOwned);
887
if (auto prop = getLinkCopyOnChangeGroupProperty()) {
888
if (auto obj = prop->getValue()) {
889
if (obj->isAttachedToDocument() && obj->getDocument())
890
obj->getDocument()->removeObject(obj->getNameInDocument());
892
auto group = new LinkGroup;
893
group->LinkMode.setValue(LinkModeAutoDelete);
894
getContainer()->getDocument()->addObject(group, "CopyOnChangeGroup");
895
prop->setValue(group);
897
// The order of the copied objects is in dependency order (because of
898
// getOnChangeCopyObjects()). We reverse it here so that we can later
899
// on delete it in reverse order to avoid error (because some parent
900
// objects may want to delete their own children).
901
std::reverse(objs.begin(), objs.end());
902
group->ElementList.setValues(objs);
908
void LinkBaseExtension::monitorOnChangeCopyObjects(
909
const std::vector<App::DocumentObject*> &objs)
911
copyOnChangeSrcConns.clear();
912
if (getLinkCopyOnChangeValue() == CopyOnChangeDisabled)
914
for(auto obj : objs) {
915
obj->setStatus(App::ObjectStatus::TouchOnColorChange, true);
916
copyOnChangeSrcConns.emplace_back(obj->signalChanged.connect(
917
[this](const DocumentObject &, const Property &) {
918
if (auto prop = this->getLinkCopyOnChangeTouchedProperty()) {
919
if (this->getLinkCopyOnChangeValue() != CopyOnChangeDisabled)
920
prop->setValue(true);
926
App::GroupExtension *LinkBaseExtension::linkedPlainGroup() const {
927
if(!mySubElements.empty() && !mySubElements[0].empty())
929
auto linked = getTrueLinkedObject(false);
932
return linked->getExtensionByType<GroupExtension>(true,false);
935
App::PropertyLinkList *LinkBaseExtension::_getElementListProperty() const {
936
auto group = linkedPlainGroup();
938
return &group->Group;
939
return const_cast<PropertyLinkList*>(getElementListProperty());
942
const std::vector<App::DocumentObject*> &LinkBaseExtension::_getElementListValue() const {
943
if(_ChildCache.getSize())
944
return _ChildCache.getValues();
945
if(getElementListProperty())
946
return getElementListProperty()->getValues();
947
static const std::vector<DocumentObject*> empty;
951
App::PropertyBool *LinkBaseExtension::_getShowElementProperty() const {
952
auto prop = getShowElementProperty();
953
if(prop && !linkedPlainGroup())
954
return const_cast<App::PropertyBool*>(prop);
958
bool LinkBaseExtension::_getShowElementValue() const {
959
auto prop = _getShowElementProperty();
961
return prop->getValue();
965
App::PropertyInteger *LinkBaseExtension::_getElementCountProperty() const {
966
auto prop = getElementCountProperty();
967
if(prop && !linkedPlainGroup())
968
return const_cast<App::PropertyInteger*>(prop);
972
int LinkBaseExtension::_getElementCountValue() const {
973
auto prop = _getElementCountProperty();
975
return prop->getValue();
979
bool LinkBaseExtension::extensionHasChildElement() const {
980
if(!_getElementListValue().empty()
981
|| (_getElementCountValue() && _getShowElementValue()))
983
if (getLinkClaimChildValue())
985
DocumentObject *linked = getTrueLinkedObject(false);
987
if(linked->hasChildElement())
993
int LinkBaseExtension::extensionSetElementVisible(const char *element, bool visible) {
994
int index = _getShowElementValue()?getElementIndex(element):getArrayIndex(element);
996
auto propElementVis = getVisibilityListProperty();
997
if(!propElementVis || !element || !element[0])
999
if(propElementVis->getSize()<=index) {
1002
propElementVis->setSize(index+1, true);
1004
propElementVis->setStatus(Property::User3,true);
1005
propElementVis->set1Value(index,visible);
1006
propElementVis->setStatus(Property::User3,false);
1007
const auto &elements = _getElementListValue();
1008
if(index<(int)elements.size()) {
1010
myHiddenElements.insert(elements[index]);
1012
myHiddenElements.erase(elements[index]);
1016
DocumentObject *linked = getTrueLinkedObject(false);
1018
return linked->setElementVisible(element,visible);
1022
int LinkBaseExtension::extensionIsElementVisible(const char *element) {
1023
int index = _getShowElementValue()?getElementIndex(element):getArrayIndex(element);
1025
auto propElementVis = getVisibilityListProperty();
1026
if(propElementVis) {
1027
if(propElementVis->getSize()<=index || propElementVis->getValues()[index])
1033
DocumentObject *linked = getTrueLinkedObject(false);
1035
return linked->isElementVisible(element);
1039
const DocumentObject *LinkBaseExtension::getContainer() const {
1040
auto ext = getExtendedContainer();
1041
if(!ext || !ext->isDerivedFrom(DocumentObject::getClassTypeId()))
1042
LINK_THROW(Base::RuntimeError,"Link: container not derived from document object");
1043
return static_cast<const DocumentObject *>(ext);
1046
DocumentObject *LinkBaseExtension::getContainer(){
1047
auto ext = getExtendedContainer();
1048
if(!ext || !ext->isDerivedFrom(DocumentObject::getClassTypeId()))
1049
LINK_THROW(Base::RuntimeError,"Link: container not derived from document object");
1050
return static_cast<DocumentObject *>(ext);
1053
DocumentObject *LinkBaseExtension::getLink(int depth) const{
1054
if (!GetApplication().checkLinkDepth(depth, MessageOption::Error))
1056
if(getLinkedObjectProperty())
1057
return getLinkedObjectValue();
1061
int LinkBaseExtension::getArrayIndex(const char *subname, const char **psubname) {
1062
if(!subname || Data::isMappedElement(subname))
1064
const char *dot = strchr(subname,'.');
1065
if(!dot) dot= subname+strlen(subname);
1069
for(const char *c=subname;c!=dot;++c) {
1072
idx = idx*10 + *c -'0';
1083
int LinkBaseExtension::getElementIndex(const char *subname, const char **psubname) const {
1084
if(!subname || Data::isMappedElement(subname))
1087
const char *dot = strchr(subname,'.');
1088
if(!dot) dot= subname+strlen(subname);
1090
if(isdigit(subname[0])) {
1091
// If the name start with digits, treat as index reference
1092
idx = getArrayIndex(subname,nullptr);
1095
if(_getElementCountProperty()) {
1096
if(idx>=_getElementCountValue())
1098
}else if(idx>=(int)_getElementListValue().size())
1100
}else if(!_getShowElementValue() && _getElementCountValue()) {
1101
// If elements are collapsed, we check first for LinkElement naming
1102
// pattern, which is the owner object name + "_i" + index
1103
const char *name = subname[0]=='$'?subname+1:subname;
1104
auto owner = getContainer();
1105
if(owner && owner->isAttachedToDocument()) {
1106
std::string ownerName(owner->getNameInDocument());
1108
if(boost::algorithm::starts_with(name,ownerName.c_str())) {
1109
for(const char *txt=dot-1;txt>=name+ownerName.size();--txt) {
1111
idx = getArrayIndex(txt+1,nullptr);
1112
if(idx<0 || idx>=_getElementCountValue())
1122
// Then check for the actual linked object's name or label, and
1123
// redirect that reference to the first array element
1124
auto linked = getTrueLinkedObject(false);
1125
if(!linked || !linked->isAttachedToDocument())
1127
if(subname[0]=='$') {
1128
CharRange sub(subname+1, dot);
1129
if (boost::equals(sub, linked->Label.getValue()))
1132
CharRange sub(subname, dot);
1133
if (boost::equals(sub, linked->getNameInDocument()))
1137
// Lastly, try to get sub object directly from the linked object
1138
auto sobj = linked->getSubObject(std::string(subname, dot-subname+1).c_str());
1142
*psubname = subname;
1146
}else if(subname[0]!='$') {
1147
// Try search by element objects' name
1148
std::string name(subname,dot);
1149
if(_ChildCache.getSize()) {
1150
auto obj=_ChildCache.findUsingMap(name,&idx);
1152
auto group = obj->getExtensionByType<GroupExtension>(true,false);
1154
int nidx = getElementIndex(dot+1,psubname);
1159
} else if(getElementListProperty())
1160
getElementListProperty()->find(name.c_str(),&idx);
1164
// Try search by label if the reference name start with '$'
1166
std::string name(subname,dot-subname);
1167
const auto &elements = _getElementListValue();
1168
if(enableLabelCache) {
1169
if(myLabelCache.empty())
1171
auto it = myLabelCache.find(name);
1172
if(it == myLabelCache.end())
1177
for(auto element : elements) {
1178
if(element->Label.getStrValue() == name)
1183
if(idx<0 || idx>=(int)elements.size())
1185
auto obj = elements[idx];
1186
if(obj && _ChildCache.getSize()) {
1187
auto group = obj->getExtensionByType<GroupExtension>(true,false);
1189
int nidx = getElementIndex(dot+1,psubname);
1196
*psubname = dot[0]?dot+1:dot;
1200
void LinkBaseExtension::elementNameFromIndex(int idx, std::ostream &ss) const {
1201
const auto &elements = _getElementListValue();
1202
if(idx < 0 || idx >= (int)elements.size())
1205
auto obj = elements[idx];
1206
if(_ChildCache.getSize()) {
1207
auto group = GroupExtension::getGroupOfObject(obj);
1208
if(group && _ChildCache.find(group->getNameInDocument(),&idx))
1209
elementNameFromIndex(idx,ss);
1211
ss << obj->getNameInDocument() << '.';
1214
Base::Vector3d LinkBaseExtension::getScaleVector() const {
1215
if(getScaleVectorProperty())
1216
return getScaleVectorValue();
1217
double s = getScaleValue();
1218
return Base::Vector3d(s,s,s);
1221
Base::Matrix4D LinkBaseExtension::getTransform(bool transform) const {
1224
if(getLinkPlacementProperty())
1225
mat = getLinkPlacementValue().toMatrix();
1226
else if(getPlacementProperty())
1227
mat = getPlacementValue().toMatrix();
1229
if(getScaleProperty() || getScaleVectorProperty()) {
1231
s.scale(getScaleVector());
1237
bool LinkBaseExtension::extensionGetSubObjects(std::vector<std::string> &ret, int reason) const {
1238
if(!getLinkedObjectProperty() && getElementListProperty()) {
1239
for(auto obj : getElementListProperty()->getValues()) {
1240
if(obj && obj->isAttachedToDocument()) {
1241
std::string name(obj->getNameInDocument());
1243
ret.push_back(name);
1248
if(mySubElements.empty() || mySubElements[0].empty()) {
1249
DocumentObject *linked = getTrueLinkedObject(true);
1251
if(!_getElementCountValue())
1252
ret = linked->getSubObjects(reason);
1255
for(int i=0,count=_getElementCountValue();i<count;++i) {
1256
snprintf(index,sizeof(index),"%d.",i);
1257
ret.emplace_back(index);
1261
} else if(mySubElements.size()>1) {
1262
ret = mySubElements;
1267
bool LinkBaseExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
1268
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
1271
auto obj = getContainer();
1272
if(!subname || !subname[0]) {
1273
ret = const_cast<DocumentObject*>(obj);
1274
Base::Matrix4D _mat;
1276
// 'mat' here is used as an output to return the accumulated
1277
// transformation up until this object. Since 'subname' is empty
1278
// here, it means the we are at the end of the hierarchy. We shall
1279
// not include scale in the output transformation.
1281
// Think of it this way, the transformation along object hierarchy
1282
// is public, while transformation through linkage is private to
1285
if(getLinkPlacementProperty())
1286
*mat *= getLinkPlacementValue().toMatrix();
1287
else if(getPlacementProperty())
1288
*mat *= getPlacementValue().toMatrix();
1293
if(pyObj && !_getElementCountValue()
1294
&& _getElementListValue().empty() && mySubElements.size()<=1)
1296
// Scale will be included here
1297
if(getScaleProperty() || getScaleVectorProperty()) {
1299
s.scale(getScaleVector());
1302
auto linked = getTrueLinkedObject(false,&_mat,depth);
1303
if(linked && linked!=obj) {
1304
linked->getSubObject(mySubElements.empty()?nullptr:mySubElements.front().c_str(),
1305
pyObj,&_mat,false,depth+1);
1306
checkGeoElementMap(obj,linked,pyObj,nullptr);
1312
if(mat) *mat *= getTransform(transform);
1314
//DocumentObject *element = 0;
1315
bool isElement = false;
1316
int idx = getElementIndex(subname,&subname);
1318
const auto &elements = _getElementListValue();
1319
if(!elements.empty()) {
1320
if(idx>=(int)elements.size() || !elements[idx] || !elements[idx]->isAttachedToDocument())
1322
ret = elements[idx]->getSubObject(subname,pyObj,mat,true,depth+1);
1323
// do not resolve the link if this element is the last referenced object
1324
if(!subname || Data::isMappedElement(subname) || !strchr(subname,'.'))
1325
ret = elements[idx];
1329
int elementCount = _getElementCountValue();
1330
if(idx>=elementCount)
1334
auto placementList = getPlacementListProperty();
1335
if(placementList && placementList->getSize()>idx)
1336
*mat *= (*placementList)[idx].toMatrix();
1337
auto scaleList = getScaleListProperty();
1338
if(scaleList && scaleList->getSize()>idx) {
1340
s.scale((*scaleList)[idx]);
1346
auto linked = getTrueLinkedObject(false,mat,depth);
1347
if(!linked || linked==obj)
1350
Base::Matrix4D matNext;
1352
// Because of the addition of LinkClaimChild, the linked object may be
1353
// claimed as the first child. Regardless of the current value of
1354
// LinkClaimChild, we must accept sub-object path that contains the linked
1355
// object, because other link property may store such reference.
1356
if (const char* dot=strchr(subname,'.')) {
1357
auto group = getLinkCopyOnChangeGroupValue();
1358
if (subname[0] == '$') {
1359
CharRange sub(subname+1,dot);
1360
if (group && boost::equals(sub, group->Label.getValue()))
1362
else if(!boost::equals(sub, linked->Label.getValue()))
1365
CharRange sub(subname,dot);
1366
if (group && boost::equals(sub, group->getNameInDocument()))
1368
else if (!boost::equals(sub, linked->getNameInDocument()))
1372
// Because of external linked object, It is possible for and
1373
// child object to have the exact same internal name or label
1374
// as the parent object. To resolve this potential ambiguity,
1375
// try assuming the current subname is referring to the parent
1376
// (i.e. the linked object), and if it fails, try again below.
1377
if(mat) matNext = *mat;
1378
ret = linked->getSubObject(dot+1,pyObj,mat?&matNext:nullptr,false,depth+1);
1385
if(mat) matNext = *mat;
1386
ret = linked->getSubObject(subname,pyObj,mat?&matNext:nullptr,false,depth+1);
1389
std::string postfix;
1391
// do not resolve the link if we are the last referenced object
1392
if(subname && !Data::isMappedElement(subname) && strchr(subname,'.')) {
1396
// This is a useless check as 'element' is never set to a value other than null
1397
//else if(element) {
1400
else if(!isElement) {
1401
ret = const_cast<DocumentObject*>(obj);
1405
postfix = Data::POSTFIX_INDEX;
1406
postfix += std::to_string(idx);
1412
checkGeoElementMap(obj,linked,pyObj,!postfix.empty()?postfix.c_str():nullptr);
1416
void LinkBaseExtension::checkGeoElementMap(const App::DocumentObject *obj,
1417
const App::DocumentObject *linked, PyObject **pyObj, const char *postfix) const
1419
if(!pyObj || !*pyObj || (!postfix && obj->getDocument()==linked->getDocument()) ||
1420
!PyObject_TypeCheck(*pyObj, &Data::ComplexGeoDataPy::Type))
1423
// auto geoData = static_cast<Data::ComplexGeoDataPy*>(*pyObj)->getComplexGeoDataPtr();
1424
// geoData->reTagElementMap(obj->getID(),obj->getDocument()->Hasher,postfix);
1426
auto geoData = static_cast<Data::ComplexGeoDataPy*>(*pyObj)->getComplexGeoDataPtr();
1427
std::string _postfix;
1428
if (linked && obj && linked->getDocument() != obj->getDocument()) {
1429
_postfix = Data::POSTFIX_EXTERNAL_TAG;
1431
if (!boost::starts_with(postfix, Data::ComplexGeoData::elementMapPrefix()))
1432
_postfix += Data::ComplexGeoData::elementMapPrefix();
1433
_postfix += postfix;
1435
postfix = _postfix.c_str();
1437
geoData->reTagElementMap(obj->getID(),obj->getDocument()->getStringHasher(),postfix);
1441
void LinkBaseExtension::onExtendedUnsetupObject() {
1442
if(!getElementListProperty())
1445
if (auto obj = getLinkCopyOnChangeGroupValue()) {
1446
if(obj->isAttachedToDocument() && !obj->isRemoving())
1447
obj->getDocument()->removeObject(obj->getNameInDocument());
1451
DocumentObject *LinkBaseExtension::getTrueLinkedObject(
1452
bool recurse, Base::Matrix4D *mat, int depth, bool noElement) const
1454
if(noElement && extensionIsDerivedFrom(LinkElement::getExtensionClassTypeId())
1455
&& !static_cast<const LinkElement*>(this)->canDelete())
1460
auto ret = getLink(depth);
1463
bool transform = linkTransform();
1464
const char *subname = getSubName();
1465
if(subname || (mat && transform)) {
1466
ret = ret->getSubObject(subname,nullptr,mat,transform,depth+1);
1470
ret = ret->getLinkedObject(recurse,mat,transform,depth+1);
1471
if(ret && !ret->isAttachedToDocument())
1476
bool LinkBaseExtension::extensionGetLinkedObject(DocumentObject *&ret,
1477
bool recurse, Base::Matrix4D *mat, bool transform, int depth) const
1480
*mat *= getTransform(transform);
1482
if(!_getElementCountValue())
1483
ret = getTrueLinkedObject(recurse,mat,depth);
1485
ret = const_cast<DocumentObject*>(getContainer());
1486
// always return true to indicate we've handled getLinkObject() call
1490
void LinkBaseExtension::extensionOnChanged(const Property *prop) {
1491
auto parent = getContainer();
1492
if(parent && !parent->isRestoring() && prop && !prop->testStatus(Property::User3))
1493
update(parent,prop);
1494
inherited::extensionOnChanged(prop);
1497
void LinkBaseExtension::parseSubName() const {
1498
// If user has ever linked to some sub-element, the Link shall always accept
1499
// sub-element linking in the future, which affects how ViewProviderLink
1500
// dropObjectEx() behave. So we will push an empty string later even if no
1501
// sub-element linking this time.
1502
bool hasSubElement = !mySubElements.empty();
1503
mySubElements.clear();
1505
auto xlink = freecad_dynamic_cast<const PropertyXLink>(getLinkedObjectProperty());
1506
if(!xlink || xlink->getSubValues().empty()) {
1508
mySubElements.emplace_back("");
1511
const auto &subs = xlink->getSubValues();
1512
auto subname = subs.front().c_str();
1513
auto element = Data::findElementName(subname);
1514
if(!element || !element[0]) {
1515
mySubName = subs[0];
1517
mySubElements.emplace_back("");
1520
mySubElements.emplace_back(element);
1521
mySubName = std::string(subname,element-subname);
1522
for(std::size_t i=1;i<subs.size();++i) {
1523
auto &sub = subs[i];
1524
element = Data::findElementName(sub.c_str());
1525
if(element && element[0] && boost::starts_with(sub,mySubName))
1526
mySubElements.emplace_back(element);
1530
void LinkBaseExtension::slotChangedPlainGroup(const App::DocumentObject &obj, const App::Property &prop) {
1531
auto group = obj.getExtensionByType<GroupExtension>(true,false);
1532
if(group && &prop == &group->Group)
1536
void LinkBaseExtension::updateGroup() {
1537
std::vector<GroupExtension*> groups;
1538
std::unordered_set<const App::DocumentObject*> groupSet;
1539
auto group = linkedPlainGroup();
1541
groups.push_back(group);
1542
groupSet.insert(group->getExtendedObject());
1544
for(auto o : getElementListProperty()->getValues()) {
1545
if(!o || !o->isAttachedToDocument())
1547
auto ext = o->getExtensionByType<GroupExtension>(true,false);
1549
groups.push_back(ext);
1554
std::vector<App::DocumentObject*> children;
1555
if(!groups.empty()) {
1556
children = getElementListValue();
1557
std::set<DocumentObject*> childSet(children.begin(),children.end());
1558
for(auto ext : groups) {
1559
auto group = ext->getExtendedObject();
1560
auto &conn = plainGroupConns[group];
1561
if(!conn.connected()) {
1562
FC_LOG("new group connection " << getExtendedObject()->getFullName()
1563
<< " -> " << group->getFullName());
1565
conn = group->signalChanged.connect(
1566
std::bind(&LinkBaseExtension::slotChangedPlainGroup,this,sp::_1,sp::_2));
1569
std::size_t count = children.size();
1570
ext->getAllChildren(children,childSet);
1571
for(;count<children.size();++count) {
1572
auto child = children[count];
1573
if(!child->getExtensionByType<GroupExtension>(true,false))
1575
groupSet.insert(child);
1576
auto &conn = plainGroupConns[child];
1577
if(!conn.connected()) {
1578
FC_LOG("new group connection " << getExtendedObject()->getFullName()
1579
<< " -> " << child->getFullName());
1581
conn = child->signalChanged.connect(
1582
std::bind(&LinkBaseExtension::slotChangedPlainGroup,this,sp::_1,sp::_2));
1588
for(auto it=plainGroupConns.begin();it!=plainGroupConns.end();) {
1589
if(!groupSet.count(it->first))
1590
it = plainGroupConns.erase(it);
1594
if(children != _ChildCache.getValues())
1595
_ChildCache.setValue(children);
1598
void LinkBaseExtension::update(App::DocumentObject *parent, const Property *prop) {
1602
if(prop == getLinkPlacementProperty() || prop == getPlacementProperty()) {
1603
auto src = getLinkPlacementProperty();
1604
auto dst = getPlacementProperty();
1605
if(src!=prop) std::swap(src,dst);
1607
dst->setStatus(Property::User3,true);
1608
dst->setValue(src->getValue());
1609
dst->setStatus(Property::User3,false);
1611
}else if(prop == getScaleProperty()) {
1612
if(!prop->testStatus(Property::User3) && getScaleVectorProperty()) {
1613
auto s = getScaleValue();
1614
auto p = getScaleVectorProperty();
1615
p->setStatus(Property::User3,true);
1617
p->setStatus(Property::User3,false);
1619
}else if(prop == getScaleVectorProperty()) {
1620
if(!prop->testStatus(Property::User3) && getScaleProperty()) {
1621
const auto &v = getScaleVectorValue();
1622
if(v.x == v.y && v.x == v.z) {
1623
auto p = getScaleProperty();
1624
p->setStatus(Property::User3,true);
1626
p->setStatus(Property::User3,false);
1629
}else if(prop == _getShowElementProperty()) {
1630
if(_getShowElementValue())
1631
update(parent,_getElementCountProperty());
1633
auto objs = getElementListValue();
1635
// preserve element properties in ourself
1636
std::vector<Base::Placement> placements;
1637
placements.reserve(objs.size());
1638
std::vector<Base::Vector3d> scales;
1639
scales.reserve(objs.size());
1640
for(auto obj : objs) {
1641
auto element = freecad_dynamic_cast<LinkElement>(obj);
1643
placements.push_back(element->Placement.getValue());
1644
scales.push_back(element->getScaleVector());
1646
placements.emplace_back();
1647
scales.emplace_back(1,1,1);
1650
// touch the property again to make sure view provider has been
1651
// signaled before clearing the elements
1652
getShowElementProperty()->setStatus(App::Property::User3, true);
1653
getShowElementProperty()->touch();
1654
getShowElementProperty()->setStatus(App::Property::User3, false);
1656
getElementListProperty()->setValues(std::vector<App::DocumentObject*>());
1658
if(getPlacementListProperty()) {
1659
getPlacementListProperty()->setStatus(Property::User3, getScaleListProperty() != nullptr);
1660
getPlacementListProperty()->setValue(placements);
1661
getPlacementListProperty()->setStatus(Property::User3, false);
1663
if(getScaleListProperty())
1664
getScaleListProperty()->setValue(scales);
1666
for(auto obj : objs) {
1667
if(obj && obj->isAttachedToDocument())
1668
obj->getDocument()->removeObject(obj->getNameInDocument());
1671
}else if(prop == _getElementCountProperty()) {
1672
size_t elementCount = getElementCountValue()<0?0:(size_t)getElementCountValue();
1674
auto propVis = getVisibilityListProperty();
1676
if(propVis->getSize()>(int)elementCount)
1677
propVis->setSize(getElementCountValue(),true);
1680
if(!_getShowElementValue()) {
1681
if(getScaleListProperty()) {
1682
auto scales = getScaleListValue();
1683
scales.resize(elementCount,Base::Vector3d(1,1,1));
1684
getScaleListProperty()->setStatus(Property::User3,true);
1685
getScaleListProperty()->setValue(scales);
1686
getScaleListProperty()->setStatus(Property::User3,false);
1688
if(getPlacementListProperty()) {
1689
auto placements = getPlacementListValue();
1690
if(placements.size()<elementCount) {
1691
for(size_t i=placements.size();i<elementCount;++i)
1692
placements.emplace_back(Base::Vector3d(i%10,(i/10)%10,i/100),Base::Rotation());
1694
placements.resize(elementCount);
1695
getPlacementListProperty()->setStatus(Property::User3,true);
1696
getPlacementListProperty()->setValue(placements);
1697
getPlacementListProperty()->setStatus(Property::User3,false);
1699
}else if(getElementListProperty()) {
1700
auto objs = getElementListValue();
1701
if(elementCount>objs.size()) {
1702
std::string name = parent->getNameInDocument();
1703
auto doc = parent->getDocument();
1705
name = doc->getUniqueObjectName(name.c_str());
1706
if(name[name.size()-1] != 'i')
1708
auto offset = name.size();
1709
auto placementProp = getPlacementListProperty();
1710
auto scaleProp = getScaleListProperty();
1711
const auto &vis = getVisibilityListValue();
1713
auto owner = getContainer();
1714
long ownerID = owner?owner->getID():0;
1716
for(size_t i=objs.size();i<elementCount;++i) {
1717
name.resize(offset);
1718
name += std::to_string(i);
1720
// It is possible to have orphan LinkElement here due to,
1721
// for example, undo and redo. So we try to re-claim the
1722
// children element first.
1723
auto obj = freecad_dynamic_cast<LinkElement>(doc->getObject(name.c_str()));
1724
if(obj && (!obj->_LinkOwner.getValue() || obj->_LinkOwner.getValue()==ownerID)) {
1725
obj->Visibility.setValue(false);
1727
obj = new LinkElement;
1728
parent->getDocument()->addObject(obj,name.c_str());
1731
if(vis.size()>i && !vis[i])
1732
myHiddenElements.insert(obj);
1734
if(placementProp && placementProp->getSize()>(int)i)
1735
obj->Placement.setValue(placementProp->getValues()[i]);
1737
Base::Placement pla(Base::Vector3d(i%10,(i/10)%10,i/100),Base::Rotation());
1738
obj->Placement.setValue(pla);
1740
if(scaleProp && scaleProp->getSize()>(int)i)
1741
obj->Scale.setValue(scaleProp->getValues()[i].x);
1743
obj->Scale.setValue(1);
1744
objs.push_back(obj);
1746
if(getPlacementListProperty())
1747
getPlacementListProperty()->setSize(0);
1748
if(getScaleListProperty())
1749
getScaleListProperty()->setSize(0);
1751
getElementListProperty()->setValue(objs);
1753
}else if(elementCount<objs.size()){
1754
std::vector<App::DocumentObject*> tmpObjs;
1755
auto owner = getContainer();
1756
long ownerID = owner?owner->getID():0;
1757
while(objs.size()>elementCount) {
1758
auto element = freecad_dynamic_cast<LinkElement>(objs.back());
1759
if(element && element->_LinkOwner.getValue()==ownerID)
1760
tmpObjs.push_back(objs.back());
1763
getElementListProperty()->setValue(objs);
1764
for(auto obj : tmpObjs) {
1765
if(obj && obj->isAttachedToDocument())
1766
obj->getDocument()->removeObject(obj->getNameInDocument());
1770
}else if(prop == getVisibilityListProperty()) {
1771
if(_getShowElementValue()) {
1772
const auto &elements = _getElementListValue();
1773
const auto &vis = getVisibilityListValue();
1774
myHiddenElements.clear();
1775
for(size_t i=0;i<vis.size();++i) {
1776
if(i>=elements.size())
1779
myHiddenElements.insert(elements[i]);
1782
}else if(prop == getElementListProperty() || prop == &_ChildCache) {
1784
if(prop == getElementListProperty()) {
1785
_ChildCache.setStatus(Property::User3,true);
1787
_ChildCache.setStatus(Property::User3,false);
1790
const auto &elements = _getElementListValue();
1792
if(enableLabelCache)
1793
myLabelCache.clear();
1795
// Element list changed, we need to sychrnoize VisibilityList.
1796
if(_getShowElementValue() && getVisibilityListProperty()) {
1797
if(parent->getDocument()->isPerformingTransaction()) {
1798
update(parent,getVisibilityListProperty());
1800
boost::dynamic_bitset<> vis;
1801
vis.resize(elements.size(),true);
1802
std::unordered_set<const App::DocumentObject *> hiddenElements;
1803
for(size_t i=0;i<elements.size();++i) {
1804
if(myHiddenElements.find(elements[i])!=myHiddenElements.end()) {
1805
hiddenElements.insert(elements[i]);
1809
myHiddenElements.swap(hiddenElements);
1810
if(vis != getVisibilityListValue()) {
1811
auto propVis = getVisibilityListProperty();
1812
propVis->setStatus(Property::User3,true);
1813
propVis->setValue(vis);
1814
propVis->setStatus(Property::User3,false);
1819
if(_getShowElementValue()
1820
&& _getElementCountProperty()
1821
&& getElementListProperty()
1822
&& getElementCountValue()!=getElementListProperty()->getSize())
1824
getElementCountProperty()->setValue(
1825
getElementListProperty()->getSize());
1827
}else if(prop == getLinkedObjectProperty()) {
1828
auto group = linkedPlainGroup();
1829
if(getShowElementProperty())
1830
getShowElementProperty()->setStatus(Property::Hidden, !!group);
1831
if(getElementCountProperty())
1832
getElementCountProperty()->setStatus(Property::Hidden, !!group);
1835
else if(_ChildCache.getSize())
1836
_ChildCache.setValue();
1840
if(getLinkCopyOnChangeValue()==CopyOnChangeOwned
1841
&& !pauseCopyOnChange
1842
&& !parent->getDocument()->isPerformingTransaction())
1843
getLinkCopyOnChangeProperty()->setValue(CopyOnChangeEnabled);
1845
setupCopyOnChange(parent, true);
1847
}else if(prop == getLinkCopyOnChangeProperty()) {
1848
setupCopyOnChange(parent, getLinkCopyOnChangeSourceValue() == nullptr);
1849
} else if (prop == getLinkCopyOnChangeSourceProperty()) {
1850
if (auto source = getLinkCopyOnChangeSourceValue()) {
1851
this->connCopyOnChangeSource = source->signalChanged.connect(
1852
[this](const DocumentObject & obj, const Property &prop) {
1853
auto src = getLinkCopyOnChangeSourceValue();
1854
if (src != &obj || getLinkCopyOnChangeValue()==CopyOnChangeDisabled)
1856
if (App::Document::isAnyRestoring()
1857
|| obj.testStatus(ObjectStatus::NoTouch)
1858
|| (prop.getType() & Prop_Output)
1859
|| prop.testStatus(Property::Output))
1861
if (auto propTouch = getLinkCopyOnChangeTouchedProperty())
1862
propTouch->setValue(true);
1865
this->connCopyOnChangeSource.disconnect();
1867
}else if(prop == getLinkTransformProperty()) {
1868
auto linkPlacement = getLinkPlacementProperty();
1869
auto placement = getPlacementProperty();
1870
if(linkPlacement && placement) {
1871
bool transform = getLinkTransformValue();
1872
placement->setStatus(Property::Hidden,transform);
1873
linkPlacement->setStatus(Property::Hidden,!transform);
1878
checkCopyOnChange(parent, *prop);
1882
void LinkBaseExtension::cacheChildLabel(int enable) const {
1883
enableLabelCache = enable?true:false;
1884
myLabelCache.clear();
1889
for(auto child : _getElementListValue()) {
1890
if(child && child->isAttachedToDocument())
1891
myLabelCache[child->Label.getStrValue()] = idx;
1896
bool LinkBaseExtension::linkTransform() const {
1897
if(!getLinkTransformProperty() &&
1898
!getLinkPlacementProperty() &&
1899
!getPlacementProperty())
1901
return getLinkTransformValue();
1904
void LinkBaseExtension::syncElementList() {
1905
auto transform = getLinkTransformProperty();
1906
auto link = getLinkedObjectProperty();
1907
auto xlink = freecad_dynamic_cast<const PropertyXLink>(link);
1909
auto owner = getContainer();
1910
auto ownerID = owner?owner->getID():0;
1911
auto elements = getElementListValue();
1912
for (auto i : elements) {
1913
auto element = freecad_dynamic_cast<LinkElement>(i);
1915
|| (element->_LinkOwner.getValue()
1916
&& element->_LinkOwner.getValue() != ownerID))
1919
element->_LinkOwner.setValue(ownerID);
1921
element->LinkTransform.setStatus(Property::Hidden, transform != nullptr);
1922
element->LinkTransform.setStatus(Property::Immutable, transform != nullptr);
1923
if (transform && element->LinkTransform.getValue() != transform->getValue())
1924
element->LinkTransform.setValue(transform->getValue());
1926
element->LinkedObject.setStatus(Property::Hidden, link != nullptr);
1927
element->LinkedObject.setStatus(Property::Immutable, link != nullptr);
1928
if (element->LinkCopyOnChange.getValue() == 2)
1931
if (element->LinkedObject.getValue() != xlink->getValue()
1932
|| element->LinkedObject.getSubValues() != xlink->getSubValues()) {
1933
element->LinkedObject.setValue(xlink->getValue(), xlink->getSubValues());
1936
else if (element->LinkedObject.getValue() != link->getValue()
1937
|| !element->LinkedObject.getSubValues().empty()) {
1938
element->setLink(-1, link->getValue());
1943
void LinkBaseExtension::onExtendedDocumentRestored() {
1944
inherited::onExtendedDocumentRestored();
1945
myHiddenElements.clear();
1946
auto parent = getContainer();
1949
if(hasOldSubElement) {
1950
hasOldSubElement = false;
1951
// SubElements was stored as a PropertyStringList. It is now migrated to be
1952
// stored inside PropertyXLink.
1953
auto xlink = freecad_dynamic_cast<PropertyXLink>(getLinkedObjectProperty());
1955
FC_ERR("Failed to restore SubElements for " << parent->getFullName());
1956
else if(!xlink->getValue())
1957
FC_ERR("Discard SubElements of " << parent->getFullName() << " due to null link");
1958
else if(xlink->getSubValues().size() > 1)
1959
FC_ERR("Failed to restore SubElements for " << parent->getFullName()
1960
<< " due to conflict subnames");
1961
else if(xlink->getSubValues().empty()) {
1962
auto subs = xlink->getSubValues();
1963
xlink->setSubValues(std::move(subs));
1965
std::set<std::string> subset(mySubElements.begin(),mySubElements.end());
1966
auto sub = xlink->getSubValues().front();
1967
auto element = Data::findElementName(sub.c_str());
1968
if(element && element[0]) {
1969
subset.insert(element);
1970
sub.resize(element - sub.c_str());
1972
std::vector<std::string> subs;
1973
for(const auto &s : subset)
1974
subs.push_back(sub + s);
1975
xlink->setSubValues(std::move(subs));
1978
if(getScaleVectorProperty() && getScaleProperty()) {
1979
// Scale vector is added later. The code here is for migration.
1980
const auto &v = getScaleVectorValue();
1981
double s = getScaleValue();
1982
if(v.x == v.y && v.x == v.z && v.x != s)
1983
getScaleVectorProperty()->setValue(s,s,s);
1985
update(parent,getVisibilityListProperty());
1986
if (auto prop = getLinkedObjectProperty()) {
1987
Base::StateLocker guard(pauseCopyOnChange);
1988
update(parent,prop);
1990
update(parent,getLinkCopyOnChangeSourceProperty());
1991
update(parent,getElementListProperty());
1992
if (getLinkCopyOnChangeValue() != CopyOnChangeDisabled)
1993
monitorOnChangeCopyObjects(getOnChangeCopyObjects());
1996
void LinkBaseExtension::_handleChangedPropertyName(
1997
Base::XMLReader &reader, const char * TypeName, const char *PropName)
1999
if(strcmp(PropName,"SubElements")==0
2000
&& strcmp(TypeName,PropertyStringList::getClassTypeId().getName())==0)
2002
PropertyStringList prop;
2003
prop.setContainer(getContainer());
2004
prop.Restore(reader);
2005
if(prop.getSize()) {
2006
mySubElements = prop.getValues();
2007
hasOldSubElement = true;
2012
void LinkBaseExtension::setLink(int index, DocumentObject *obj,
2013
const char *subname, const std::vector<std::string> &subElements)
2015
auto parent = getContainer();
2017
LINK_THROW(Base::RuntimeError,"No parent container");
2019
if(obj && !App::Document::isAnyRestoring()) {
2020
auto inSet = parent->getInListEx(true);
2021
inSet.insert(parent);
2022
if(inSet.find(obj)!=inSet.end())
2023
LINK_THROW(Base::RuntimeError,"Cyclic dependency");
2026
auto linkProp = getLinkedObjectProperty();
2028
// If we are a group (i.e. no LinkObject property), and the index is
2029
// negative with a non-zero 'obj' assignment, we treat this as group
2030
// expansion by changing the index to one pass the existing group size
2031
if(index<0 && obj && !linkProp && getElementListProperty())
2032
index = getElementListProperty()->getSize();
2035
// LinkGroup assignment
2037
if(linkProp || !getElementListProperty())
2038
LINK_THROW(Base::RuntimeError,"Cannot set link element");
2040
DocumentObject *old = nullptr;
2041
const auto &elements = getElementListProperty()->getValues();
2043
if(index>=(int)elements.size())
2044
LINK_THROW(Base::ValueError,"Link element index out of bound");
2045
std::vector<DocumentObject*> objs;
2046
old = elements[index];
2047
for(int i=0;i<(int)elements.size();++i) {
2049
objs.push_back(elements[i]);
2051
getElementListProperty()->setValue(objs);
2052
}else if(!obj->isAttachedToDocument())
2053
LINK_THROW(Base::ValueError,"Invalid object");
2055
if(index>(int)elements.size())
2056
LINK_THROW(Base::ValueError,"Link element index out of bound");
2058
if(index < (int)elements.size())
2059
old = elements[index];
2062
if(getLinkModeValue()>=LinkModeAutoLink ||
2063
(subname && subname[0]) ||
2064
!subElements.empty() ||
2065
obj->getDocument()!=parent->getDocument() ||
2066
(getElementListProperty()->find(obj->getNameInDocument(),&idx) && idx!=index))
2068
std::string name = parent->getDocument()->getUniqueObjectName("Link");
2069
auto link = new Link;
2070
link->_LinkOwner.setValue(parent->getID());
2071
parent->getDocument()->addObject(link,name.c_str());
2072
link->setLink(-1,obj,subname,subElements);
2073
auto linked = link->getTrueLinkedObject(true);
2075
link->Label.setValue(linked->Label.getValue());
2076
auto pla = freecad_dynamic_cast<PropertyPlacement>(obj->getPropertyByName("Placement"));
2078
link->Placement.setValue(pla->getValue());
2079
link->Visibility.setValue(false);
2086
getElementListProperty()->set1Value(index,obj);
2093
// Reaching here means, we are group (i.e. no LinkedObject), and
2094
// index<0, and 'obj' is zero. We shall clear the whole group
2096
if(obj || !getElementListProperty())
2097
LINK_THROW(Base::RuntimeError,"No PropertyLink or PropertyLinkList configured");
2102
// Here means we are assigning a Link
2104
auto xlink = freecad_dynamic_cast<PropertyXLink>(linkProp);
2106
if(!obj->isAttachedToDocument())
2107
LINK_THROW(Base::ValueError,"Invalid document object");
2109
if(parent && obj->getDocument()!=parent->getDocument())
2110
LINK_THROW(Base::ValueError,"Cannot link to external object without PropertyXLink");
2115
if(!subElements.empty() || (subname && subname[0]))
2116
LINK_THROW(Base::RuntimeError,"SubName/SubElement link requires PropertyXLink");
2117
linkProp->setValue(obj);
2121
std::vector<std::string> subs;
2122
if(!subElements.empty()) {
2123
subs.reserve(subElements.size());
2124
for(const auto &s : subElements) {
2125
subs.emplace_back(subname?subname:"");
2128
} else if(subname && subname[0])
2129
subs.emplace_back(subname);
2130
xlink->setValue(obj,std::move(subs));
2133
void LinkBaseExtension::detachElements()
2135
std::vector<App::DocumentObjectT> objs;
2136
for (auto obj : getElementListValue())
2137
objs.emplace_back(obj);
2138
getElementListProperty()->setValue();
2139
for(const auto &objT : objs)
2140
detachElement(objT.getObject());
2143
void LinkBaseExtension::detachElement(DocumentObject *obj) {
2144
if(!obj || !obj->isAttachedToDocument() || obj->isRemoving())
2146
auto ext = obj->getExtensionByType<LinkBaseExtension>(true);
2147
auto owner = getContainer();
2148
long ownerID = owner?owner->getID():0;
2149
if(getLinkModeValue()==LinkModeAutoUnlink) {
2150
if(!ext || ext->_LinkOwner.getValue()!=ownerID)
2152
}else if(getLinkModeValue()!=LinkModeAutoDelete) {
2153
if(ext && ext->_LinkOwner.getValue()==ownerID)
2154
ext->_LinkOwner.setValue(0);
2157
obj->getDocument()->removeObject(obj->getNameInDocument());
2160
std::vector<App::DocumentObject*> LinkBaseExtension::getLinkedChildren(bool filter) const{
2162
return _getElementListValue();
2163
std::vector<App::DocumentObject*> ret;
2164
for(auto o : _getElementListValue()) {
2165
if(!o->hasExtension(GroupExtension::getExtensionClassTypeId(),false))
2171
const char *LinkBaseExtension::flattenSubname(const char *subname) const {
2172
if(subname && _ChildCache.getSize()) {
2173
const char *sub = subname;
2175
for(const char* dot=strchr(sub,'.');dot;sub=dot+1,dot=strchr(sub,'.')) {
2176
DocumentObject *obj = nullptr;
2178
s.append(sub,dot+1);
2179
extensionGetSubObject(obj,s.c_str());
2182
if(!obj->hasExtension(GroupExtension::getExtensionClassTypeId(),false))
2189
void LinkBaseExtension::expandSubname(std::string &subname) const {
2190
if(!_ChildCache.getSize())
2193
const char *pos = nullptr;
2194
int index = getElementIndex(subname.c_str(),&pos);
2197
std::ostringstream ss;
2198
elementNameFromIndex(index,ss);
2203
static bool isExcludedProperties(const char *name) {
2204
#define CHECK_EXCLUDE_PROP(_name) if(strcmp(name,#_name)==0) return true;
2205
CHECK_EXCLUDE_PROP(Shape);
2206
CHECK_EXCLUDE_PROP(Proxy);
2207
CHECK_EXCLUDE_PROP(Placement);
2211
Property *LinkBaseExtension::extensionGetPropertyByName(const char* name) const {
2212
if (checkingProperty)
2213
return inherited::extensionGetPropertyByName(name);
2214
Base::StateLocker guard(checkingProperty);
2215
if(isExcludedProperties(name))
2217
auto owner = getContainer();
2219
App::Property *prop = owner->getPropertyByName(name);
2222
if(owner->canLinkProperties()) {
2223
auto linked = getTrueLinkedObject(true);
2225
return linked->getPropertyByName(name);
2231
bool LinkBaseExtension::isLinkMutated() const
2233
return getLinkCopyOnChangeValue() != CopyOnChangeDisabled
2234
&& getLinkedObjectValue()
2235
&& (!getLinkCopyOnChangeSourceValue()
2236
|| (getLinkedObjectValue() != getLinkCopyOnChangeSourceValue()));
2239
///////////////////////////////////////////////////////////////////////////////////////////
2242
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::LinkBaseExtensionPython, App::LinkBaseExtension)
2244
// explicit template instantiation
2245
template class AppExport ExtensionPythonT<LinkBaseExtension>;
2249
//////////////////////////////////////////////////////////////////////////////
2251
EXTENSION_PROPERTY_SOURCE(App::LinkExtension, App::LinkBaseExtension)
2253
LinkExtension::LinkExtension()
2255
initExtensionType(LinkExtension::getExtensionClassTypeId());
2257
LINK_PROPS_ADD_EXTENSION(LINK_PARAMS_EXT);
2260
///////////////////////////////////////////////////////////////////////////////////////////
2263
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::LinkExtensionPython, App::LinkExtension)
2265
// explicit template instantiation
2266
template class AppExport ExtensionPythonT<App::LinkExtension>;
2270
///////////////////////////////////////////////////////////////////////////////////////////
2272
PROPERTY_SOURCE_WITH_EXTENSIONS(App::Link, App::DocumentObject)
2275
LINK_PROPS_ADD(LINK_PARAMS_LINK);
2276
LinkExtension::initExtension(this);
2277
static const PropertyIntegerConstraint::Constraints s_constraints = {0,INT_MAX,1};
2278
ElementCount.setConstraints(&s_constraints);
2281
bool Link::canLinkProperties() const {
2285
bool Link::isLink() const
2287
return ElementCount.getValue() == 0;
2290
bool Link::isLinkGroup() const
2292
return ElementCount.getValue() > 0;
2295
//////////////////////////////////////////////////////////////////////////////////////////
2298
PROPERTY_SOURCE_TEMPLATE(App::LinkPython, App::Link)
2299
template<> const char* App::LinkPython::getViewProviderName() const {
2300
return "Gui::ViewProviderLinkPython";
2302
template class AppExport FeaturePythonT<App::Link>;
2305
//////////////////////////////////////////////////////////////////////////////////////////
2307
PROPERTY_SOURCE_WITH_EXTENSIONS(App::LinkElement, App::DocumentObject)
2309
LinkElement::LinkElement() {
2310
LINK_PROPS_ADD(LINK_PARAMS_ELEMENT);
2311
LinkBaseExtension::initExtension(this);
2314
bool LinkElement::canDelete() const {
2315
if(!_LinkOwner.getValue())
2318
auto owner = getContainer();
2319
return !owner || !owner->getDocument()->getObjectByID(_LinkOwner.getValue());
2322
bool LinkElement::isLink() const
2327
App::Link* LinkElement::getLinkGroup() const
2329
std::vector<App::DocumentObject*> inList = getInList();
2330
for (auto* obj : inList) {
2331
auto* link = dynamic_cast<App::Link*>(obj);
2335
std::vector<App::DocumentObject*> elts = link->ElementList.getValues();
2336
for (auto* elt : elts) {
2345
//////////////////////////////////////////////////////////////////////////////////////////
2348
PROPERTY_SOURCE_TEMPLATE(App::LinkElementPython, App::LinkElement)
2349
template<> const char* App::LinkElementPython::getViewProviderName() const {
2350
return "Gui::ViewProviderLinkPython";
2352
template class AppExport FeaturePythonT<App::LinkElement>;
2355
//////////////////////////////////////////////////////////////////////////////////////////
2357
PROPERTY_SOURCE_WITH_EXTENSIONS(App::LinkGroup, App::DocumentObject)
2359
LinkGroup::LinkGroup() {
2360
LINK_PROPS_ADD(LINK_PARAMS_GROUP);
2361
LinkBaseExtension::initExtension(this);
2364
//////////////////////////////////////////////////////////////////////////////////////////
2367
PROPERTY_SOURCE_TEMPLATE(App::LinkGroupPython, App::LinkGroup)
2368
template<> const char* App::LinkGroupPython::getViewProviderName() const {
2369
return "Gui::ViewProviderLinkPython";
2371
template class AppExport FeaturePythonT<App::LinkGroup>;
2374
#if defined(__clang__)
2375
# pragma clang diagnostic pop