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"
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*>;
64
class LinkParamsP: public ParameterGrp::ObserverType {
67
ParameterGrp::handle handle;
70
std::unordered_map<const char *,void(*)(LinkParamsP*),App::CStringHasher,App::CStringHasher> funcs;
72
bool CopyOnChangeApplyToAll;
76
handle = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Link");
79
CopyOnChangeApplyToAll = handle->GetBool("CopyOnChangeApplyToAll", true);
80
funcs["CopyOnChangeApplyToAll"] = &LinkParamsP::updateCopyOnChangeApplyToAll;
84
~LinkParamsP() override = default;
87
void OnChange(Base::Subject<const char*> &, const char* sReason) override {
90
auto it = funcs.find(sReason);
98
static void updateCopyOnChangeApplyToAll(LinkParamsP *self) {
99
self->CopyOnChangeApplyToAll = self->handle->GetBool("CopyOnChangeApplyToAll", true);
104
LinkParamsP *instance() {
105
static LinkParamsP *inst = new LinkParamsP;
112
ParameterGrp::handle LinkParams::getHandle() {
113
return instance()->handle;
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");
124
const bool & LinkParams::getCopyOnChangeApplyToAll() {
125
return instance()->CopyOnChangeApplyToAll;
129
const bool & LinkParams::defaultCopyOnChangeApplyToAll() {
130
static const bool def = true;
135
void LinkParams::setCopyOnChangeApplyToAll(const bool &v) {
136
instance()->handle->SetBool("CopyOnChangeApplyToAll",v);
137
instance()->CopyOnChangeApplyToAll = v;
141
void LinkParams::removeCopyOnChangeApplyToAll() {
142
instance()->handle->RemoveBool("CopyOnChangeApplyToAll");
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())){
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 PropLinkCopyOnChangeTouched:
241
case PropLinkCopyOnChangeSource:
242
case PropLinkCopyOnChangeGroup:
243
prop->setStatus(Property::Hidden, true);
245
case PropLinkTransform:
246
case PropLinkPlacement:
248
if(getLinkTransformProperty() &&
249
getLinkPlacementProperty() &&
250
getPlacementProperty())
252
bool transform = getLinkTransformValue();
253
getPlacementProperty()->setStatus(Property::Hidden, transform);
254
getLinkPlacementProperty()->setStatus(Property::Hidden, !transform);
257
case PropElementList:
258
getElementListProperty()->setScope(LinkScope::Global);
259
getElementListProperty()->setStatus(Property::Hidden, true);
261
case PropLinkedObject:
264
if(getElementListProperty())
265
getElementListProperty()->setStatus(
266
Property::Immutable, getLinkedObjectProperty() != nullptr);
268
case PropVisibilityList:
269
getVisibilityListProperty()->setStatus(Property::Immutable, true);
270
getVisibilityListProperty()->setStatus(Property::Hidden, true);
274
if(FC_LOG_INSTANCE.isEnabled(FC_LOGLEVEL_TRACE)) {
275
const char *propName;
278
else if(prop->getContainer())
279
propName = prop->getName();
281
propName = extensionGetPropertyName(prop);
282
if(!Property::isValidName(propName))
284
FC_TRACE("set property " << infos[idx].name << ": " << propName);
288
static const char _GroupPrefix[] = "Configuration (";
290
App::DocumentObjectExecReturn *LinkBaseExtension::extensionExecute() {
294
_LinkTouched.touch();
296
if(getLinkedObjectProperty()) {
297
DocumentObject *linked = getTrueLinkedObject(true);
299
std::ostringstream ss;
300
ss << "Link broken!";
301
auto xlink = Base::freecad_dynamic_cast<PropertyXLink>(
302
getLinkedObjectProperty());
304
const char *objname = xlink->getObjectName();
305
if (objname && objname[0])
306
ss << "\nObject: " << objname;
307
const char *filename = xlink->getFilePath();
308
if (filename && filename[0])
309
ss << "\nFile: " << filename;
311
return new App::DocumentObjectExecReturn(ss.str().c_str());
314
App::DocumentObject *container = getContainer();
315
auto source = getLinkCopyOnChangeSourceValue();
316
if (source && getLinkCopyOnChangeValue() == CopyOnChangeTracking
317
&& getLinkCopyOnChangeTouchedValue())
323
linked = getTrueLinkedObject(true);
325
return new App::DocumentObjectExecReturn("Error in processing variable link");
328
PropertyPythonObject *proxy = nullptr;
329
if(getLinkExecuteProperty()
330
&& !boost::iequals(getLinkExecuteValue(), "none")
331
&& (!_LinkOwner.getValue()
332
|| !container->getDocument()->getObjectByID(_LinkOwner.getValue())))
336
proxy = Base::freecad_dynamic_cast<PropertyPythonObject>(
337
linked->getPropertyByName("Proxy"));
340
Base::PyGILStateLocker lock;
341
const char *errMsg = "Linked proxy execute failed";
344
Py::Object proxyValue = proxy->getValue();
345
const char *method = getLinkExecuteValue();
346
if(!method || !method[0])
347
method = "appLinkExecute";
348
if(proxyValue.hasAttr(method)) {
349
Py::Object attr = proxyValue.getAttr(method);
350
if(attr.ptr() && attr.isCallable()) {
352
args.setItem(0, Py::asObject(linked->getPyObject()));
353
args.setItem(1, Py::asObject(container->getPyObject()));
354
if(!_getElementCountValue()) {
355
Py::Callable(attr).apply(args);
357
const auto &elements = _getElementListValue();
358
for(int i=0; i<_getElementCountValue(); ++i) {
359
args.setItem(2, Py::Int(i));
360
if(i < (int)elements.size())
361
args.setItem(3, Py::asObject(elements[i]->getPyObject()));
363
args.setItem(3, Py::Object());
364
Py::Callable(attr).apply(args);
369
} catch (Py::Exception &) {
372
return new App::DocumentObjectExecReturn(errMsg);
373
} catch (Base::Exception &e) {
375
return new App::DocumentObjectExecReturn(errMsg);
379
auto parent = getContainer();
380
setupCopyOnChange(parent);
382
if(hasCopyOnChange && getLinkCopyOnChangeValue()==CopyOnChangeDisabled) {
383
hasCopyOnChange = false;
384
std::vector<Property*> props;
385
parent->getPropertyList(props);
386
for(auto prop : props) {
387
if(isCopyOnChangeProperty(parent, *prop)) {
389
parent->removeDynamicProperty(prop->getName());
390
} catch (Base::Exception &e) {
398
return inherited::extensionExecute();
401
short LinkBaseExtension::extensionMustExecute() {
402
auto link = getLink();
405
return link->mustExecute();
408
std::vector<App::DocumentObject*>
409
LinkBaseExtension::getOnChangeCopyObjects(
410
std::vector<App::DocumentObject *> *excludes,
411
App::DocumentObject *src)
413
auto parent = getContainer();
415
src = getLinkCopyOnChangeSourceValue();
416
if (!src || getLinkCopyOnChangeValue() == CopyOnChangeDisabled)
419
auto res = Document::getDependencyList({src}, Document::DepSort);
420
for (auto it=res.begin(); it!=res.end();) {
426
auto prop = Base::freecad_dynamic_cast<PropertyMap>(
427
obj->getPropertyByName("_CopyOnChangeControl"));
428
static std::map<std::string, std::string> dummy;
429
const auto & map = prop && prop->getContainer()==obj ? prop->getValues() : dummy;
431
if (src->getDocument() != obj->getDocument())
433
auto iter = map.find("*");
434
if (iter != map.end())
435
v = iter->second.c_str();
436
else if ((iter = map.find(parent->getNameInDocument())) != map.end())
437
v = iter->second.c_str();
438
if (boost::equals(v, "-")) {
440
excludes->push_back(obj);
451
void LinkBaseExtension::setOnChangeCopyObject(
452
App::DocumentObject *obj, OnChangeCopyOptions options)
454
auto parent = getContainer();
455
Base::Flags<OnChangeCopyOptions> flags(options);
456
bool exclude = flags.testFlag(OnChangeCopyOptions::Exclude);
457
bool external = parent->getDocument() != obj->getDocument();
458
auto prop = Base::freecad_dynamic_cast<PropertyMap>(
459
obj->getPropertyByName("_CopyOnChangeControl"));
461
if (external == exclude && !prop)
466
prop = static_cast<PropertyMap*>(
467
obj->addDynamicProperty("App::PropertyMap", "_CopyOnChangeControl"));
468
} catch (Base::Exception &e) {
472
FC_ERR("Failed to setup copy on change object " << obj->getFullName());
477
const char *key = flags.testFlag(OnChangeCopyOptions::ApplyAll) ? "*" : parent->getDagKey();
479
prop->setValue(key, exclude ? "" : "+");
481
prop->setValue(key, exclude ? "-" : "");
487
void LinkBaseExtension::syncCopyOnChange()
489
if (!getLinkCopyOnChangeValue())
491
auto linkProp = getLinkedObjectProperty();
492
auto srcProp = getLinkCopyOnChangeSourceProperty();
493
auto srcTouched = getLinkCopyOnChangeTouchedProperty();
497
|| !srcProp->getValue()
498
|| !linkProp->getValue()
499
|| srcProp->getValue() == linkProp->getValue())
502
auto parent = getContainer();
504
auto linked = linkProp->getValue();
506
std::vector<App::DocumentObjectT> oldObjs;
507
std::vector<App::DocumentObject*> objs;
512
LinkGroup *copyOnChangeGroup = nullptr;
513
if (auto prop = getLinkCopyOnChangeGroupProperty()) {
514
copyOnChangeGroup = Base::freecad_dynamic_cast<LinkGroup>(prop->getValue());
515
if (!copyOnChangeGroup) {
517
auto group = new LinkGroup;
518
group->LinkMode.setValue(LinkModeAutoDelete);
519
parent->getDocument()->addObject(group, "CopyOnChangeGroup");
520
prop->setValue(group);
525
objs = copyOnChangeGroup->ElementList.getValues();
526
for (auto obj : objs) {
527
if (!obj->isAttachedToDocument())
529
auto prop = Base::freecad_dynamic_cast<PropertyUUID>(
530
obj->getPropertyByName("_SourceUUID"));
531
if (prop && prop->getContainer() == obj)
532
oldObjs.emplace_back(prop);
534
oldObjs.emplace_back(obj);
536
std::sort(objs.begin(), objs.end());
542
auto srcObjs = getOnChangeCopyObjects();
544
monitorOnChangeCopyObjects(srcObjs);
549
auto copiedObjs = parent->getDocument()->copyObject(srcObjs);
550
if(copiedObjs.empty())
555
auto newLinked = copiedObjs.back();
560
std::vector<App::Property*> propList;
561
linked->getPropertyList(propList);
562
for (auto prop : propList) {
563
if(!prop->testStatus(Property::CopyOnChange)
564
|| prop->getContainer()!=linked)
566
auto p = newLinked->getPropertyByName(prop->getName());
567
if (p && p->getTypeId() == prop->getTypeId()) {
568
std::unique_ptr<Property> pCopy(prop->Copy());
573
if (copyOnChangeGroup) {
578
std::reverse(copiedObjs.begin(), copiedObjs.end());
579
copyOnChangeGroup->ElementList.setValues(copiedObjs);
589
std::map<Base::Uuid, App::DocumentObjectT> newObjs;
590
for (auto obj : copiedObjs) {
591
auto prop = Base::freecad_dynamic_cast<PropertyUUID>(
592
obj->getPropertyByName("_SourceUUID"));
594
newObjs.insert(std::make_pair(prop->getValue(), obj));
597
std::vector<std::pair<App::DocumentObject*, App::DocumentObject*> > replacements;
598
for (const auto &objT : oldObjs) {
599
auto prop = Base::freecad_dynamic_cast<PropertyUUID>(objT.getProperty());
602
auto it = newObjs.find(prop->getValue());
603
if (it == newObjs.end())
605
auto oldObj = objT.getObject();
606
auto newObj = it->second.getObject();
607
if (oldObj && newObj)
608
replacements.emplace_back(oldObj, newObj);
611
std::vector<std::pair<App::DocumentObjectT, std::unique_ptr<App::Property> > > propChanges;
612
if (!replacements.empty()) {
613
std::sort(copiedObjs.begin(), copiedObjs.end());
618
for(auto doc : App::GetApplication().getDocuments()) {
619
for(auto o : doc->getObjects()) {
621
|| std::binary_search(objs.begin(), objs.end(), o)
622
|| std::binary_search(copiedObjs.begin(), copiedObjs.end(), o))
625
o->getPropertyList(propList);
626
for(auto prop : propList) {
627
if (prop->getContainer() != o)
629
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
632
for (const auto &v : replacements) {
633
std::unique_ptr<App::Property> copy(
634
linkProp->CopyOnLinkReplace(parent,v.first,v.second));
637
propChanges.emplace_back(App::DocumentObjectT(prop),std::move(copy));
644
Base::StateLocker guard(pauseCopyOnChange);
645
linkProp->setValue(newLinked);
646
newLinked->Visibility.setValue(false);
647
srcTouched->setValue(false);
650
for(const auto &v : propChanges) {
651
auto prop = v.first.getProperty();
653
prop->Paste(*v.second.get());
660
for (const auto &objT : oldObjs) {
661
auto obj = objT.getObject();
662
if (obj && std::binary_search(objs.begin(), objs.end(), obj))
663
obj->getDocument()->removeObject(obj->getNameInDocument());
667
bool LinkBaseExtension::isLinkedToConfigurableObject() const
669
if (auto linked = getLinkedObjectValue()) {
670
std::vector<App::Property*> propList;
671
linked->getPropertyList(propList);
672
for (auto prop : propList) {
673
if(prop->testStatus(Property::CopyOnChange)
674
&& prop->getContainer()==linked)
681
bool LinkBaseExtension::isCopyOnChangeProperty(DocumentObject *obj, const App::Property &prop) {
682
if(obj!=prop.getContainer() || !prop.testStatus(App::Property::PropDynamic))
684
auto group = prop.getGroup();
685
return group && boost::starts_with(group,_GroupPrefix);
688
void LinkBaseExtension::setupCopyOnChange(DocumentObject *parent, bool checkSource) {
689
copyOnChangeConns.clear();
690
copyOnChangeSrcConns.clear();
692
auto linked = getTrueLinkedObject(false);
693
if(!linked || getLinkCopyOnChangeValue()==CopyOnChangeDisabled)
696
if (checkSource && !pauseCopyOnChange) {
697
PropertyLink *source = getLinkCopyOnChangeSourceProperty();
699
source->setValue(linked);
700
if (auto touched = getLinkCopyOnChangeTouchedProperty())
701
touched->setValue(false);
705
hasCopyOnChange = setupCopyOnChange(parent,linked,©OnChangeConns,hasCopyOnChange);
706
if (hasCopyOnChange && getLinkCopyOnChangeValue() == CopyOnChangeOwned
707
&& getLinkedObjectValue()
708
&& getLinkedObjectValue() == getLinkCopyOnChangeSourceValue())
714
bool LinkBaseExtension::setupCopyOnChange(DocumentObject *parent, DocumentObject *linked,
715
std::vector<boost::signals2::scoped_connection> *copyOnChangeConns, bool checkExisting)
717
if(!parent || !linked)
722
std::unordered_map<Property*, Property*> newProps;
723
std::vector<Property*> props;
724
linked->getPropertyList(props);
725
for(auto prop : props) {
726
if(!prop->testStatus(Property::CopyOnChange)
727
|| prop->getContainer()!=linked)
732
const char* linkedGroupName = prop->getGroup();
733
if(!linkedGroupName || !linkedGroupName[0])
734
linkedGroupName = "Base";
736
std::string groupName;
737
groupName = _GroupPrefix;
738
if(boost::starts_with(linkedGroupName,_GroupPrefix))
739
groupName += linkedGroupName + sizeof(_GroupPrefix)-1;
741
groupName += linkedGroupName;
745
auto p = parent->getPropertyByName(prop->getName());
747
if(p->getContainer()!=parent)
750
const char* otherGroupName = p->getGroup();
751
if(!otherGroupName || !boost::starts_with(otherGroupName, _GroupPrefix)) {
752
FC_WARN(p->getFullName() << " shadows another CopyOnChange property "
753
<< prop->getFullName());
756
if(p->getTypeId() != prop->getTypeId() || groupName != otherGroupName) {
757
parent->removeDynamicProperty(p->getName());
763
p = parent->addDynamicProperty(prop->getTypeId().getName(),
764
prop->getName(), groupName.c_str(), prop->getDocumentation());
765
std::unique_ptr<Property> pcopy(prop->Copy());
766
Base::ObjectStatusLocker<Property::Status,Property> guard(Property::User3, p);
770
p->setStatusValue(prop->getStatus());
777
parent->getPropertyList(props);
778
for(auto prop : props) {
779
if(prop->getContainer()!=parent)
781
auto gname = prop->getGroup();
782
if(!gname || !boost::starts_with(gname, _GroupPrefix))
784
if(!newProps.count(prop))
785
parent->removeDynamicProperty(prop->getName());
789
if(!copyOnChangeConns)
792
for(const auto &v : newProps) {
794
copyOnChangeConns->push_back(v.second->signalChanged.connect([parent](const Property &prop) {
795
if(!prop.testStatus(Property::CopyOnChange))
797
auto p = parent->getPropertyByName(prop.getName());
798
if(p && p->getTypeId()==prop.getTypeId()) {
799
std::unique_ptr<Property> pcopy(prop.Copy());
801
p->setStatus(Property::Output, true);
803
Base::ObjectStatusLocker<Property::Status,Property> guard(Property::User3, p);
806
p->setStatusValue(prop.getStatus());
814
void LinkBaseExtension::checkCopyOnChange(App::DocumentObject *parent, const App::Property &prop)
816
if(!parent || !parent->getDocument()
817
|| parent->getDocument()->isPerformingTransaction())
820
auto linked = getLinkedObjectValue();
821
if(!linked || getLinkCopyOnChangeValue()==CopyOnChangeDisabled
822
|| !isCopyOnChangeProperty(parent,prop))
825
if(getLinkCopyOnChangeValue() == CopyOnChangeOwned ||
826
(getLinkCopyOnChangeValue() == CopyOnChangeTracking
827
&& linked != getLinkCopyOnChangeSourceValue()))
829
auto p = linked->getPropertyByName(prop.getName());
830
if(p && p->getTypeId()==prop.getTypeId()) {
831
std::unique_ptr<Property> pcopy(prop.Copy());
838
auto linkedProp = linked->getPropertyByName(prop.getName());
839
if(!linkedProp || linkedProp->getTypeId()!=prop.getTypeId() || linkedProp->isSame(prop))
842
auto copied = makeCopyOnChange();
844
linkedProp = copied->getPropertyByName(prop.getName());
845
if(linkedProp && linkedProp->getTypeId()==prop.getTypeId()) {
846
std::unique_ptr<Property> pcopy(prop.Copy());
848
linkedProp->Paste(*pcopy);
853
App::DocumentObject *LinkBaseExtension::makeCopyOnChange() {
854
auto linked = getLinkedObjectValue();
855
if (pauseCopyOnChange || !linked)
857
auto parent = getContainer();
858
auto srcobjs = getOnChangeCopyObjects(nullptr, linked);
859
for (auto obj : srcobjs) {
860
if (obj->testStatus(App::PartialObject)) {
861
FC_THROWM(Base::RuntimeError, "Cannot copy partial loaded object: "
862
<< obj->getFullName());
865
auto objs = parent->getDocument()->copyObject(srcobjs);
869
monitorOnChangeCopyObjects(srcobjs);
871
linked = objs.back();
872
linked->Visibility.setValue(false);
874
Base::StateLocker guard(pauseCopyOnChange);
875
getLinkedObjectProperty()->setValue(linked);
876
if (getLinkCopyOnChangeValue() == CopyOnChangeEnabled)
877
getLinkCopyOnChangeProperty()->setValue(CopyOnChangeOwned);
879
if (auto prop = getLinkCopyOnChangeGroupProperty()) {
880
if (auto obj = prop->getValue()) {
881
if (obj->isAttachedToDocument() && obj->getDocument())
882
obj->getDocument()->removeObject(obj->getNameInDocument());
884
auto group = new LinkGroup;
885
group->LinkMode.setValue(LinkModeAutoDelete);
886
getContainer()->getDocument()->addObject(group, "CopyOnChangeGroup");
887
prop->setValue(group);
893
std::reverse(objs.begin(), objs.end());
894
group->ElementList.setValues(objs);
900
void LinkBaseExtension::monitorOnChangeCopyObjects(
901
const std::vector<App::DocumentObject*> &objs)
903
copyOnChangeSrcConns.clear();
904
if (getLinkCopyOnChangeValue() == CopyOnChangeDisabled)
906
for(auto obj : objs) {
907
obj->setStatus(App::ObjectStatus::TouchOnColorChange, true);
908
copyOnChangeSrcConns.emplace_back(obj->signalChanged.connect(
909
[this](const DocumentObject &, const Property &) {
910
if (auto prop = this->getLinkCopyOnChangeTouchedProperty()) {
911
if (this->getLinkCopyOnChangeValue() != CopyOnChangeDisabled)
912
prop->setValue(true);
918
App::GroupExtension *LinkBaseExtension::linkedPlainGroup() const {
919
if(!mySubElements.empty() && !mySubElements[0].empty())
921
auto linked = getTrueLinkedObject(false);
924
return linked->getExtensionByType<GroupExtension>(true,false);
927
App::PropertyLinkList *LinkBaseExtension::_getElementListProperty() const {
928
auto group = linkedPlainGroup();
930
return &group->Group;
931
return const_cast<PropertyLinkList*>(getElementListProperty());
934
const std::vector<App::DocumentObject*> &LinkBaseExtension::_getElementListValue() const {
935
if(_ChildCache.getSize())
936
return _ChildCache.getValues();
937
if(getElementListProperty())
938
return getElementListProperty()->getValues();
939
static const std::vector<DocumentObject*> empty;
943
App::PropertyBool *LinkBaseExtension::_getShowElementProperty() const {
944
auto prop = getShowElementProperty();
945
if(prop && !linkedPlainGroup())
946
return const_cast<App::PropertyBool*>(prop);
950
bool LinkBaseExtension::_getShowElementValue() const {
951
auto prop = _getShowElementProperty();
953
return prop->getValue();
957
App::PropertyInteger *LinkBaseExtension::_getElementCountProperty() const {
958
auto prop = getElementCountProperty();
959
if(prop && !linkedPlainGroup())
960
return const_cast<App::PropertyInteger*>(prop);
964
int LinkBaseExtension::_getElementCountValue() const {
965
auto prop = _getElementCountProperty();
967
return prop->getValue();
971
bool LinkBaseExtension::extensionHasChildElement() const {
972
if(!_getElementListValue().empty()
973
|| (_getElementCountValue() && _getShowElementValue()))
975
if (getLinkClaimChildValue())
977
DocumentObject *linked = getTrueLinkedObject(false);
979
if(linked->hasChildElement())
985
int LinkBaseExtension::extensionSetElementVisible(const char *element, bool visible) {
986
int index = _getShowElementValue()?getElementIndex(element):getArrayIndex(element);
988
auto propElementVis = getVisibilityListProperty();
989
if(!propElementVis || !element || !element[0])
991
if(propElementVis->getSize()<=index) {
994
propElementVis->setSize(index+1, true);
996
propElementVis->setStatus(Property::User3,true);
997
propElementVis->set1Value(index,visible);
998
propElementVis->setStatus(Property::User3,false);
999
const auto &elements = _getElementListValue();
1000
if(index<(int)elements.size()) {
1002
myHiddenElements.insert(elements[index]);
1004
myHiddenElements.erase(elements[index]);
1008
DocumentObject *linked = getTrueLinkedObject(false);
1010
return linked->setElementVisible(element,visible);
1014
int LinkBaseExtension::extensionIsElementVisible(const char *element) {
1015
int index = _getShowElementValue()?getElementIndex(element):getArrayIndex(element);
1017
auto propElementVis = getVisibilityListProperty();
1018
if(propElementVis) {
1019
if(propElementVis->getSize()<=index || propElementVis->getValues()[index])
1025
DocumentObject *linked = getTrueLinkedObject(false);
1027
return linked->isElementVisible(element);
1031
const DocumentObject *LinkBaseExtension::getContainer() const {
1032
auto ext = getExtendedContainer();
1033
if(!ext || !ext->isDerivedFrom(DocumentObject::getClassTypeId()))
1034
LINK_THROW(Base::RuntimeError,"Link: container not derived from document object");
1035
return static_cast<const DocumentObject *>(ext);
1038
DocumentObject *LinkBaseExtension::getContainer(){
1039
auto ext = getExtendedContainer();
1040
if(!ext || !ext->isDerivedFrom(DocumentObject::getClassTypeId()))
1041
LINK_THROW(Base::RuntimeError,"Link: container not derived from document object");
1042
return static_cast<DocumentObject *>(ext);
1045
DocumentObject *LinkBaseExtension::getLink(int depth) const{
1046
if (!GetApplication().checkLinkDepth(depth, MessageOption::Error))
1048
if(getLinkedObjectProperty())
1049
return getLinkedObjectValue();
1053
int LinkBaseExtension::getArrayIndex(const char *subname, const char **psubname) {
1054
if(!subname || Data::isMappedElement(subname))
1056
const char *dot = strchr(subname,'.');
1057
if(!dot) dot= subname+strlen(subname);
1061
for(const char *c=subname;c!=dot;++c) {
1064
idx = idx*10 + *c -'0';
1075
int LinkBaseExtension::getElementIndex(const char *subname, const char **psubname) const {
1076
if(!subname || Data::isMappedElement(subname))
1079
const char *dot = strchr(subname,'.');
1080
if(!dot) dot= subname+strlen(subname);
1082
if(isdigit(subname[0])) {
1084
idx = getArrayIndex(subname,nullptr);
1087
if(_getElementCountProperty()) {
1088
if(idx>=_getElementCountValue())
1090
}else if(idx>=(int)_getElementListValue().size())
1092
}else if(!_getShowElementValue() && _getElementCountValue()) {
1095
const char *name = subname[0]=='$'?subname+1:subname;
1096
auto owner = getContainer();
1097
if(owner && owner->isAttachedToDocument()) {
1098
std::string ownerName(owner->getNameInDocument());
1100
if(boost::algorithm::starts_with(name,ownerName.c_str())) {
1101
for(const char *txt=dot-1;txt>=name+ownerName.size();--txt) {
1103
idx = getArrayIndex(txt+1,nullptr);
1104
if(idx<0 || idx>=_getElementCountValue())
1116
auto linked = getTrueLinkedObject(false);
1117
if(!linked || !linked->isAttachedToDocument())
1119
if(subname[0]=='$') {
1120
CharRange sub(subname+1, dot);
1121
if (boost::equals(sub, linked->Label.getValue()))
1124
CharRange sub(subname, dot);
1125
if (boost::equals(sub, linked->getNameInDocument()))
1130
auto sobj = linked->getSubObject(std::string(subname, dot-subname+1).c_str());
1134
*psubname = subname;
1138
}else if(subname[0]!='$') {
1140
std::string name(subname,dot);
1141
if(_ChildCache.getSize()) {
1142
auto obj=_ChildCache.find(name,&idx);
1144
auto group = obj->getExtensionByType<GroupExtension>(true,false);
1146
int nidx = getElementIndex(dot+1,psubname);
1151
} else if(getElementListProperty())
1152
getElementListProperty()->find(name.c_str(),&idx);
1158
std::string name(subname,dot-subname);
1159
const auto &elements = _getElementListValue();
1160
if(enableLabelCache) {
1161
if(myLabelCache.empty())
1163
auto it = myLabelCache.find(name);
1164
if(it == myLabelCache.end())
1169
for(auto element : elements) {
1170
if(element->Label.getStrValue() == name)
1175
if(idx<0 || idx>=(int)elements.size())
1177
auto obj = elements[idx];
1178
if(obj && _ChildCache.getSize()) {
1179
auto group = obj->getExtensionByType<GroupExtension>(true,false);
1181
int nidx = getElementIndex(dot+1,psubname);
1188
*psubname = dot[0]?dot+1:dot;
1192
void LinkBaseExtension::elementNameFromIndex(int idx, std::ostream &ss) const {
1193
const auto &elements = _getElementListValue();
1194
if(idx < 0 || idx >= (int)elements.size())
1197
auto obj = elements[idx];
1198
if(_ChildCache.getSize()) {
1199
auto group = GroupExtension::getGroupOfObject(obj);
1200
if(group && _ChildCache.find(group->getNameInDocument(),&idx))
1201
elementNameFromIndex(idx,ss);
1203
ss << obj->getNameInDocument() << '.';
1206
Base::Vector3d LinkBaseExtension::getScaleVector() const {
1207
if(getScaleVectorProperty())
1208
return getScaleVectorValue();
1209
double s = getScaleValue();
1210
return Base::Vector3d(s,s,s);
1213
Base::Matrix4D LinkBaseExtension::getTransform(bool transform) const {
1216
if(getLinkPlacementProperty())
1217
mat = getLinkPlacementValue().toMatrix();
1218
else if(getPlacementProperty())
1219
mat = getPlacementValue().toMatrix();
1221
if(getScaleProperty() || getScaleVectorProperty()) {
1223
s.scale(getScaleVector());
1229
bool LinkBaseExtension::extensionGetSubObjects(std::vector<std::string> &ret, int reason) const {
1230
if(!getLinkedObjectProperty() && getElementListProperty()) {
1231
for(auto obj : getElementListProperty()->getValues()) {
1232
if(obj && obj->isAttachedToDocument()) {
1233
std::string name(obj->getNameInDocument());
1235
ret.push_back(name);
1240
if(mySubElements.empty() || mySubElements[0].empty()) {
1241
DocumentObject *linked = getTrueLinkedObject(true);
1243
if(!_getElementCountValue())
1244
ret = linked->getSubObjects(reason);
1247
for(int i=0,count=_getElementCountValue();i<count;++i) {
1248
snprintf(index,sizeof(index),"%d.",i);
1249
ret.emplace_back(index);
1253
} else if(mySubElements.size()>1) {
1254
ret = mySubElements;
1259
bool LinkBaseExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
1260
PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
1263
auto obj = getContainer();
1264
if(!subname || !subname[0]) {
1265
ret = const_cast<DocumentObject*>(obj);
1266
Base::Matrix4D _mat;
1277
if(getLinkPlacementProperty())
1278
*mat *= getLinkPlacementValue().toMatrix();
1279
else if(getPlacementProperty())
1280
*mat *= getPlacementValue().toMatrix();
1285
if(pyObj && !_getElementCountValue()
1286
&& _getElementListValue().empty() && mySubElements.size()<=1)
1289
if(getScaleProperty() || getScaleVectorProperty()) {
1291
s.scale(getScaleVector());
1294
auto linked = getTrueLinkedObject(false,&_mat,depth);
1295
if(linked && linked!=obj) {
1296
linked->getSubObject(mySubElements.empty()?nullptr:mySubElements.front().c_str(),
1297
pyObj,&_mat,false,depth+1);
1298
checkGeoElementMap(obj,linked,pyObj,nullptr);
1304
if(mat) *mat *= getTransform(transform);
1307
bool isElement = false;
1308
int idx = getElementIndex(subname,&subname);
1310
const auto &elements = _getElementListValue();
1311
if(!elements.empty()) {
1312
if(idx>=(int)elements.size() || !elements[idx] || !elements[idx]->isAttachedToDocument())
1314
ret = elements[idx]->getSubObject(subname,pyObj,mat,true,depth+1);
1316
if(!subname || Data::isMappedElement(subname) || !strchr(subname,'.'))
1317
ret = elements[idx];
1321
int elementCount = _getElementCountValue();
1322
if(idx>=elementCount)
1326
auto placementList = getPlacementListProperty();
1327
if(placementList && placementList->getSize()>idx)
1328
*mat *= (*placementList)[idx].toMatrix();
1329
auto scaleList = getScaleListProperty();
1330
if(scaleList && scaleList->getSize()>idx) {
1332
s.scale((*scaleList)[idx]);
1338
auto linked = getTrueLinkedObject(false,mat,depth);
1339
if(!linked || linked==obj)
1342
Base::Matrix4D matNext;
1348
if (const char* dot=strchr(subname,'.')) {
1349
auto group = getLinkCopyOnChangeGroupValue();
1350
if (subname[0] == '$') {
1351
CharRange sub(subname+1,dot);
1352
if (group && boost::equals(sub, group->Label.getValue()))
1354
else if(!boost::equals(sub, linked->Label.getValue()))
1357
CharRange sub(subname,dot);
1358
if (group && boost::equals(sub, group->getNameInDocument()))
1360
else if (!boost::equals(sub, linked->getNameInDocument()))
1369
if(mat) matNext = *mat;
1370
ret = linked->getSubObject(dot+1,pyObj,mat?&matNext:nullptr,false,depth+1);
1377
if(mat) matNext = *mat;
1378
ret = linked->getSubObject(subname,pyObj,mat?&matNext:nullptr,false,depth+1);
1381
std::string postfix;
1384
if(subname && !Data::isMappedElement(subname) && strchr(subname,'.')) {
1392
else if(!isElement) {
1393
ret = const_cast<DocumentObject*>(obj);
1397
postfix = Data::POSTFIX_INDEX;
1398
postfix += std::to_string(idx);
1404
checkGeoElementMap(obj,linked,pyObj,!postfix.empty()?postfix.c_str():nullptr);
1408
void LinkBaseExtension::checkGeoElementMap(const App::DocumentObject *obj,
1409
const App::DocumentObject *linked, PyObject **pyObj, const char *postfix) const
1411
if(!pyObj || !*pyObj || (!postfix && obj->getDocument()==linked->getDocument()) ||
1412
!PyObject_TypeCheck(*pyObj, &Data::ComplexGeoDataPy::Type))
1419
void LinkBaseExtension::onExtendedUnsetupObject() {
1420
if(!getElementListProperty())
1423
if (auto obj = getLinkCopyOnChangeGroupValue()) {
1424
if(obj->isAttachedToDocument() && !obj->isRemoving())
1425
obj->getDocument()->removeObject(obj->getNameInDocument());
1429
DocumentObject *LinkBaseExtension::getTrueLinkedObject(
1430
bool recurse, Base::Matrix4D *mat, int depth, bool noElement) const
1432
if(noElement && extensionIsDerivedFrom(LinkElement::getExtensionClassTypeId())
1433
&& !static_cast<const LinkElement*>(this)->canDelete())
1438
auto ret = getLink(depth);
1441
bool transform = linkTransform();
1442
const char *subname = getSubName();
1443
if(subname || (mat && transform)) {
1444
ret = ret->getSubObject(subname,nullptr,mat,transform,depth+1);
1448
ret = ret->getLinkedObject(recurse,mat,transform,depth+1);
1449
if(ret && !ret->isAttachedToDocument())
1454
bool LinkBaseExtension::extensionGetLinkedObject(DocumentObject *&ret,
1455
bool recurse, Base::Matrix4D *mat, bool transform, int depth) const
1458
*mat *= getTransform(transform);
1460
if(!_getElementCountValue())
1461
ret = getTrueLinkedObject(recurse,mat,depth);
1463
ret = const_cast<DocumentObject*>(getContainer());
1468
void LinkBaseExtension::extensionOnChanged(const Property *prop) {
1469
auto parent = getContainer();
1470
if(parent && !parent->isRestoring() && prop && !prop->testStatus(Property::User3))
1471
update(parent,prop);
1472
inherited::extensionOnChanged(prop);
1475
void LinkBaseExtension::parseSubName() const {
1480
bool hasSubElement = !mySubElements.empty();
1481
mySubElements.clear();
1483
auto xlink = freecad_dynamic_cast<const PropertyXLink>(getLinkedObjectProperty());
1484
if(!xlink || xlink->getSubValues().empty()) {
1486
mySubElements.emplace_back("");
1489
const auto &subs = xlink->getSubValues();
1490
auto subname = subs.front().c_str();
1491
auto element = Data::findElementName(subname);
1492
if(!element || !element[0]) {
1493
mySubName = subs[0];
1495
mySubElements.emplace_back("");
1498
mySubElements.emplace_back(element);
1499
mySubName = std::string(subname,element-subname);
1500
for(std::size_t i=1;i<subs.size();++i) {
1501
auto &sub = subs[i];
1502
element = Data::findElementName(sub.c_str());
1503
if(element && element[0] && boost::starts_with(sub,mySubName))
1504
mySubElements.emplace_back(element);
1508
void LinkBaseExtension::slotChangedPlainGroup(const App::DocumentObject &obj, const App::Property &prop) {
1509
auto group = obj.getExtensionByType<GroupExtension>(true,false);
1510
if(group && &prop == &group->Group)
1514
void LinkBaseExtension::updateGroup() {
1515
std::vector<GroupExtension*> groups;
1516
std::unordered_set<const App::DocumentObject*> groupSet;
1517
auto group = linkedPlainGroup();
1519
groups.push_back(group);
1520
groupSet.insert(group->getExtendedObject());
1522
for(auto o : getElementListProperty()->getValues()) {
1523
if(!o || !o->isAttachedToDocument())
1525
auto ext = o->getExtensionByType<GroupExtension>(true,false);
1527
groups.push_back(ext);
1532
std::vector<App::DocumentObject*> children;
1533
if(!groups.empty()) {
1534
children = getElementListValue();
1535
std::set<DocumentObject*> childSet(children.begin(),children.end());
1536
for(auto ext : groups) {
1537
auto group = ext->getExtendedObject();
1538
auto &conn = plainGroupConns[group];
1539
if(!conn.connected()) {
1540
FC_LOG("new group connection " << getExtendedObject()->getFullName()
1541
<< " -> " << group->getFullName());
1543
conn = group->signalChanged.connect(
1544
std::bind(&LinkBaseExtension::slotChangedPlainGroup,this,sp::_1,sp::_2));
1547
std::size_t count = children.size();
1548
ext->getAllChildren(children,childSet);
1549
for(;count<children.size();++count) {
1550
auto child = children[count];
1551
if(!child->getExtensionByType<GroupExtension>(true,false))
1553
groupSet.insert(child);
1554
auto &conn = plainGroupConns[child];
1555
if(!conn.connected()) {
1556
FC_LOG("new group connection " << getExtendedObject()->getFullName()
1557
<< " -> " << child->getFullName());
1559
conn = child->signalChanged.connect(
1560
std::bind(&LinkBaseExtension::slotChangedPlainGroup,this,sp::_1,sp::_2));
1566
for(auto it=plainGroupConns.begin();it!=plainGroupConns.end();) {
1567
if(!groupSet.count(it->first))
1568
it = plainGroupConns.erase(it);
1572
if(children != _ChildCache.getValues())
1573
_ChildCache.setValue(children);
1576
void LinkBaseExtension::update(App::DocumentObject *parent, const Property *prop) {
1580
if(prop == getLinkPlacementProperty() || prop == getPlacementProperty()) {
1581
auto src = getLinkPlacementProperty();
1582
auto dst = getPlacementProperty();
1583
if(src!=prop) std::swap(src,dst);
1585
dst->setStatus(Property::User3,true);
1586
dst->setValue(src->getValue());
1587
dst->setStatus(Property::User3,false);
1589
}else if(prop == getScaleProperty()) {
1590
if(!prop->testStatus(Property::User3) && getScaleVectorProperty()) {
1591
auto s = getScaleValue();
1592
auto p = getScaleVectorProperty();
1593
p->setStatus(Property::User3,true);
1595
p->setStatus(Property::User3,false);
1597
}else if(prop == getScaleVectorProperty()) {
1598
if(!prop->testStatus(Property::User3) && getScaleProperty()) {
1599
const auto &v = getScaleVectorValue();
1600
if(v.x == v.y && v.x == v.z) {
1601
auto p = getScaleProperty();
1602
p->setStatus(Property::User3,true);
1604
p->setStatus(Property::User3,false);
1607
}else if(prop == _getShowElementProperty()) {
1608
if(_getShowElementValue())
1609
update(parent,_getElementCountProperty());
1611
auto objs = getElementListValue();
1614
std::vector<Base::Placement> placements;
1615
placements.reserve(objs.size());
1616
std::vector<Base::Vector3d> scales;
1617
scales.reserve(objs.size());
1618
for(auto obj : objs) {
1619
auto element = freecad_dynamic_cast<LinkElement>(obj);
1621
placements.push_back(element->Placement.getValue());
1622
scales.push_back(element->getScaleVector());
1624
placements.emplace_back();
1625
scales.emplace_back(1,1,1);
1630
getShowElementProperty()->setStatus(App::Property::User3, true);
1631
getShowElementProperty()->touch();
1632
getShowElementProperty()->setStatus(App::Property::User3, false);
1634
getElementListProperty()->setValues(std::vector<App::DocumentObject*>());
1636
if(getPlacementListProperty()) {
1637
getPlacementListProperty()->setStatus(Property::User3, getScaleListProperty() != nullptr);
1638
getPlacementListProperty()->setValue(placements);
1639
getPlacementListProperty()->setStatus(Property::User3, false);
1641
if(getScaleListProperty())
1642
getScaleListProperty()->setValue(scales);
1644
for(auto obj : objs) {
1645
if(obj && obj->isAttachedToDocument())
1646
obj->getDocument()->removeObject(obj->getNameInDocument());
1649
}else if(prop == _getElementCountProperty()) {
1650
size_t elementCount = getElementCountValue()<0?0:(size_t)getElementCountValue();
1652
auto propVis = getVisibilityListProperty();
1654
if(propVis->getSize()>(int)elementCount)
1655
propVis->setSize(getElementCountValue(),true);
1658
if(!_getShowElementValue()) {
1659
if(getScaleListProperty()) {
1660
auto scales = getScaleListValue();
1661
scales.resize(elementCount,Base::Vector3d(1,1,1));
1662
getScaleListProperty()->setStatus(Property::User3,true);
1663
getScaleListProperty()->setValue(scales);
1664
getScaleListProperty()->setStatus(Property::User3,false);
1666
if(getPlacementListProperty()) {
1667
auto placements = getPlacementListValue();
1668
if(placements.size()<elementCount) {
1669
for(size_t i=placements.size();i<elementCount;++i)
1670
placements.emplace_back(Base::Vector3d(i%10,(i/10)%10,i/100),Base::Rotation());
1672
placements.resize(elementCount);
1673
getPlacementListProperty()->setStatus(Property::User3,true);
1674
getPlacementListProperty()->setValue(placements);
1675
getPlacementListProperty()->setStatus(Property::User3,false);
1677
}else if(getElementListProperty()) {
1678
auto objs = getElementListValue();
1679
if(elementCount>objs.size()) {
1680
std::string name = parent->getNameInDocument();
1681
auto doc = parent->getDocument();
1683
name = doc->getUniqueObjectName(name.c_str());
1684
if(name[name.size()-1] != 'i')
1686
auto offset = name.size();
1687
auto placementProp = getPlacementListProperty();
1688
auto scaleProp = getScaleListProperty();
1689
const auto &vis = getVisibilityListValue();
1691
auto owner = getContainer();
1692
long ownerID = owner?owner->getID():0;
1694
for(size_t i=objs.size();i<elementCount;++i) {
1695
name.resize(offset);
1696
name += std::to_string(i);
1701
auto obj = freecad_dynamic_cast<LinkElement>(doc->getObject(name.c_str()));
1702
if(obj && (!obj->_LinkOwner.getValue() || obj->_LinkOwner.getValue()==ownerID)) {
1703
obj->Visibility.setValue(false);
1705
obj = new LinkElement;
1706
parent->getDocument()->addObject(obj,name.c_str());
1709
if(vis.size()>i && !vis[i])
1710
myHiddenElements.insert(obj);
1712
if(placementProp && placementProp->getSize()>(int)i)
1713
obj->Placement.setValue(placementProp->getValues()[i]);
1715
Base::Placement pla(Base::Vector3d(i%10,(i/10)%10,i/100),Base::Rotation());
1716
obj->Placement.setValue(pla);
1718
if(scaleProp && scaleProp->getSize()>(int)i)
1719
obj->Scale.setValue(scaleProp->getValues()[i].x);
1721
obj->Scale.setValue(1);
1722
objs.push_back(obj);
1724
if(getPlacementListProperty())
1725
getPlacementListProperty()->setSize(0);
1726
if(getScaleListProperty())
1727
getScaleListProperty()->setSize(0);
1729
getElementListProperty()->setValue(objs);
1731
}else if(elementCount<objs.size()){
1732
std::vector<App::DocumentObject*> tmpObjs;
1733
auto owner = getContainer();
1734
long ownerID = owner?owner->getID():0;
1735
while(objs.size()>elementCount) {
1736
auto element = freecad_dynamic_cast<LinkElement>(objs.back());
1737
if(element && element->_LinkOwner.getValue()==ownerID)
1738
tmpObjs.push_back(objs.back());
1741
getElementListProperty()->setValue(objs);
1742
for(auto obj : tmpObjs) {
1743
if(obj && obj->isAttachedToDocument())
1744
obj->getDocument()->removeObject(obj->getNameInDocument());
1748
}else if(prop == getVisibilityListProperty()) {
1749
if(_getShowElementValue()) {
1750
const auto &elements = _getElementListValue();
1751
const auto &vis = getVisibilityListValue();
1752
myHiddenElements.clear();
1753
for(size_t i=0;i<vis.size();++i) {
1754
if(i>=elements.size())
1757
myHiddenElements.insert(elements[i]);
1760
}else if(prop == getElementListProperty() || prop == &_ChildCache) {
1762
if(prop == getElementListProperty()) {
1763
_ChildCache.setStatus(Property::User3,true);
1765
_ChildCache.setStatus(Property::User3,false);
1768
const auto &elements = _getElementListValue();
1770
if(enableLabelCache)
1771
myLabelCache.clear();
1774
if(_getShowElementValue() && getVisibilityListProperty()) {
1775
if(parent->getDocument()->isPerformingTransaction()) {
1776
update(parent,getVisibilityListProperty());
1778
boost::dynamic_bitset<> vis;
1779
vis.resize(elements.size(),true);
1780
std::unordered_set<const App::DocumentObject *> hiddenElements;
1781
for(size_t i=0;i<elements.size();++i) {
1782
if(myHiddenElements.find(elements[i])!=myHiddenElements.end()) {
1783
hiddenElements.insert(elements[i]);
1787
myHiddenElements.swap(hiddenElements);
1788
if(vis != getVisibilityListValue()) {
1789
auto propVis = getVisibilityListProperty();
1790
propVis->setStatus(Property::User3,true);
1791
propVis->setValue(vis);
1792
propVis->setStatus(Property::User3,false);
1797
if(_getShowElementValue()
1798
&& _getElementCountProperty()
1799
&& getElementListProperty()
1800
&& getElementCountValue()!=getElementListProperty()->getSize())
1802
getElementCountProperty()->setValue(
1803
getElementListProperty()->getSize());
1805
}else if(prop == getLinkedObjectProperty()) {
1806
auto group = linkedPlainGroup();
1807
if(getShowElementProperty())
1808
getShowElementProperty()->setStatus(Property::Hidden, !!group);
1809
if(getElementCountProperty())
1810
getElementCountProperty()->setStatus(Property::Hidden, !!group);
1813
else if(_ChildCache.getSize())
1814
_ChildCache.setValue();
1818
if(getLinkCopyOnChangeValue()==CopyOnChangeOwned
1819
&& !pauseCopyOnChange
1820
&& !parent->getDocument()->isPerformingTransaction())
1821
getLinkCopyOnChangeProperty()->setValue(CopyOnChangeEnabled);
1823
setupCopyOnChange(parent, true);
1825
}else if(prop == getLinkCopyOnChangeProperty()) {
1826
setupCopyOnChange(parent, getLinkCopyOnChangeSourceValue() == nullptr);
1827
} else if (prop == getLinkCopyOnChangeSourceProperty()) {
1828
if (auto source = getLinkCopyOnChangeSourceValue()) {
1829
this->connCopyOnChangeSource = source->signalChanged.connect(
1830
[this](const DocumentObject & obj, const Property &prop) {
1831
auto src = getLinkCopyOnChangeSourceValue();
1832
if (src != &obj || getLinkCopyOnChangeValue()==CopyOnChangeDisabled)
1834
if (App::Document::isAnyRestoring()
1835
|| obj.testStatus(ObjectStatus::NoTouch)
1836
|| (prop.getType() & Prop_Output)
1837
|| prop.testStatus(Property::Output))
1839
if (auto propTouch = getLinkCopyOnChangeTouchedProperty())
1840
propTouch->setValue(true);
1843
this->connCopyOnChangeSource.disconnect();
1845
}else if(prop == getLinkTransformProperty()) {
1846
auto linkPlacement = getLinkPlacementProperty();
1847
auto placement = getPlacementProperty();
1848
if(linkPlacement && placement) {
1849
bool transform = getLinkTransformValue();
1850
placement->setStatus(Property::Hidden,transform);
1851
linkPlacement->setStatus(Property::Hidden,!transform);
1856
checkCopyOnChange(parent, *prop);
1860
void LinkBaseExtension::cacheChildLabel(int enable) const {
1861
enableLabelCache = enable?true:false;
1862
myLabelCache.clear();
1867
for(auto child : _getElementListValue()) {
1868
if(child && child->isAttachedToDocument())
1869
myLabelCache[child->Label.getStrValue()] = idx;
1874
bool LinkBaseExtension::linkTransform() const {
1875
if(!getLinkTransformProperty() &&
1876
!getLinkPlacementProperty() &&
1877
!getPlacementProperty())
1879
return getLinkTransformValue();
1882
void LinkBaseExtension::syncElementList() {
1883
auto transform = getLinkTransformProperty();
1884
auto link = getLinkedObjectProperty();
1885
auto xlink = freecad_dynamic_cast<const PropertyXLink>(link);
1887
auto owner = getContainer();
1888
auto ownerID = owner?owner->getID():0;
1889
auto elements = getElementListValue();
1890
for (auto i : elements) {
1891
auto element = freecad_dynamic_cast<LinkElement>(i);
1893
|| (element->_LinkOwner.getValue()
1894
&& element->_LinkOwner.getValue() != ownerID))
1897
element->_LinkOwner.setValue(ownerID);
1899
element->LinkTransform.setStatus(Property::Hidden, transform != nullptr);
1900
element->LinkTransform.setStatus(Property::Immutable, transform != nullptr);
1901
if (transform && element->LinkTransform.getValue() != transform->getValue())
1902
element->LinkTransform.setValue(transform->getValue());
1904
element->LinkedObject.setStatus(Property::Hidden, link != nullptr);
1905
element->LinkedObject.setStatus(Property::Immutable, link != nullptr);
1906
if (element->LinkCopyOnChange.getValue() == 2)
1909
if (element->LinkedObject.getValue() != xlink->getValue()
1910
|| element->LinkedObject.getSubValues() != xlink->getSubValues()) {
1911
element->LinkedObject.setValue(xlink->getValue(), xlink->getSubValues());
1914
else if (element->LinkedObject.getValue() != link->getValue()
1915
|| !element->LinkedObject.getSubValues().empty()) {
1916
element->setLink(-1, link->getValue());
1921
void LinkBaseExtension::onExtendedDocumentRestored() {
1922
inherited::onExtendedDocumentRestored();
1923
myHiddenElements.clear();
1924
auto parent = getContainer();
1927
if(hasOldSubElement) {
1928
hasOldSubElement = false;
1931
auto xlink = freecad_dynamic_cast<PropertyXLink>(getLinkedObjectProperty());
1933
FC_ERR("Failed to restore SubElements for " << parent->getFullName());
1934
else if(!xlink->getValue())
1935
FC_ERR("Discard SubElements of " << parent->getFullName() << " due to null link");
1936
else if(xlink->getSubValues().size() > 1)
1937
FC_ERR("Failed to restore SubElements for " << parent->getFullName()
1938
<< " due to conflict subnames");
1939
else if(xlink->getSubValues().empty()) {
1940
auto subs = xlink->getSubValues();
1941
xlink->setSubValues(std::move(subs));
1943
std::set<std::string> subset(mySubElements.begin(),mySubElements.end());
1944
auto sub = xlink->getSubValues().front();
1945
auto element = Data::findElementName(sub.c_str());
1946
if(element && element[0]) {
1947
subset.insert(element);
1948
sub.resize(element - sub.c_str());
1950
std::vector<std::string> subs;
1951
for(const auto &s : subset)
1952
subs.push_back(sub + s);
1953
xlink->setSubValues(std::move(subs));
1956
if(getScaleVectorProperty() && getScaleProperty()) {
1958
const auto &v = getScaleVectorValue();
1959
double s = getScaleValue();
1960
if(v.x == v.y && v.x == v.z && v.x != s)
1961
getScaleVectorProperty()->setValue(s,s,s);
1963
update(parent,getVisibilityListProperty());
1964
if (auto prop = getLinkedObjectProperty()) {
1965
Base::StateLocker guard(pauseCopyOnChange);
1966
update(parent,prop);
1968
update(parent,getLinkCopyOnChangeSourceProperty());
1969
update(parent,getElementListProperty());
1970
if (getLinkCopyOnChangeValue() != CopyOnChangeDisabled)
1971
monitorOnChangeCopyObjects(getOnChangeCopyObjects());
1974
void LinkBaseExtension::_handleChangedPropertyName(
1975
Base::XMLReader &reader, const char * TypeName, const char *PropName)
1977
if(strcmp(PropName,"SubElements")==0
1978
&& strcmp(TypeName,PropertyStringList::getClassTypeId().getName())==0)
1980
PropertyStringList prop;
1981
prop.setContainer(getContainer());
1982
prop.Restore(reader);
1983
if(prop.getSize()) {
1984
mySubElements = prop.getValues();
1985
hasOldSubElement = true;
1990
void LinkBaseExtension::setLink(int index, DocumentObject *obj,
1991
const char *subname, const std::vector<std::string> &subElements)
1993
auto parent = getContainer();
1995
LINK_THROW(Base::RuntimeError,"No parent container");
1997
if(obj && !App::Document::isAnyRestoring()) {
1998
auto inSet = parent->getInListEx(true);
1999
inSet.insert(parent);
2000
if(inSet.find(obj)!=inSet.end())
2001
LINK_THROW(Base::RuntimeError,"Cyclic dependency");
2004
auto linkProp = getLinkedObjectProperty();
2009
if(index<0 && obj && !linkProp && getElementListProperty())
2010
index = getElementListProperty()->getSize();
2015
if(linkProp || !getElementListProperty())
2016
LINK_THROW(Base::RuntimeError,"Cannot set link element");
2018
DocumentObject *old = nullptr;
2019
const auto &elements = getElementListProperty()->getValues();
2021
if(index>=(int)elements.size())
2022
LINK_THROW(Base::ValueError,"Link element index out of bound");
2023
std::vector<DocumentObject*> objs;
2024
old = elements[index];
2025
for(int i=0;i<(int)elements.size();++i) {
2027
objs.push_back(elements[i]);
2029
getElementListProperty()->setValue(objs);
2030
}else if(!obj->isAttachedToDocument())
2031
LINK_THROW(Base::ValueError,"Invalid object");
2033
if(index>(int)elements.size())
2034
LINK_THROW(Base::ValueError,"Link element index out of bound");
2036
if(index < (int)elements.size())
2037
old = elements[index];
2040
if(getLinkModeValue()>=LinkModeAutoLink ||
2041
(subname && subname[0]) ||
2042
!subElements.empty() ||
2043
obj->getDocument()!=parent->getDocument() ||
2044
(getElementListProperty()->find(obj->getNameInDocument(),&idx) && idx!=index))
2046
std::string name = parent->getDocument()->getUniqueObjectName("Link");
2047
auto link = new Link;
2048
link->_LinkOwner.setValue(parent->getID());
2049
parent->getDocument()->addObject(link,name.c_str());
2050
link->setLink(-1,obj,subname,subElements);
2051
auto linked = link->getTrueLinkedObject(true);
2053
link->Label.setValue(linked->Label.getValue());
2054
auto pla = freecad_dynamic_cast<PropertyPlacement>(obj->getPropertyByName("Placement"));
2056
link->Placement.setValue(pla->getValue());
2057
link->Visibility.setValue(false);
2064
getElementListProperty()->set1Value(index,obj);
2074
if(obj || !getElementListProperty())
2075
LINK_THROW(Base::RuntimeError,"No PropertyLink or PropertyLinkList configured");
2082
auto xlink = freecad_dynamic_cast<PropertyXLink>(linkProp);
2084
if(!obj->isAttachedToDocument())
2085
LINK_THROW(Base::ValueError,"Invalid document object");
2087
if(parent && obj->getDocument()!=parent->getDocument())
2088
LINK_THROW(Base::ValueError,"Cannot link to external object without PropertyXLink");
2093
if(!subElements.empty() || (subname && subname[0]))
2094
LINK_THROW(Base::RuntimeError,"SubName/SubElement link requires PropertyXLink");
2095
linkProp->setValue(obj);
2099
std::vector<std::string> subs;
2100
if(!subElements.empty()) {
2101
subs.reserve(subElements.size());
2102
for(const auto &s : subElements) {
2103
subs.emplace_back(subname?subname:"");
2106
} else if(subname && subname[0])
2107
subs.emplace_back(subname);
2108
xlink->setValue(obj,std::move(subs));
2111
void LinkBaseExtension::detachElements()
2113
std::vector<App::DocumentObjectT> objs;
2114
for (auto obj : getElementListValue())
2115
objs.emplace_back(obj);
2116
getElementListProperty()->setValue();
2117
for(const auto &objT : objs)
2118
detachElement(objT.getObject());
2121
void LinkBaseExtension::detachElement(DocumentObject *obj) {
2122
if(!obj || !obj->isAttachedToDocument() || obj->isRemoving())
2124
auto ext = obj->getExtensionByType<LinkBaseExtension>(true);
2125
auto owner = getContainer();
2126
long ownerID = owner?owner->getID():0;
2127
if(getLinkModeValue()==LinkModeAutoUnlink) {
2128
if(!ext || ext->_LinkOwner.getValue()!=ownerID)
2130
}else if(getLinkModeValue()!=LinkModeAutoDelete) {
2131
if(ext && ext->_LinkOwner.getValue()==ownerID)
2132
ext->_LinkOwner.setValue(0);
2135
obj->getDocument()->removeObject(obj->getNameInDocument());
2138
std::vector<App::DocumentObject*> LinkBaseExtension::getLinkedChildren(bool filter) const{
2140
return _getElementListValue();
2141
std::vector<App::DocumentObject*> ret;
2142
for(auto o : _getElementListValue()) {
2143
if(!o->hasExtension(GroupExtension::getExtensionClassTypeId(),false))
2149
const char *LinkBaseExtension::flattenSubname(const char *subname) const {
2150
if(subname && _ChildCache.getSize()) {
2151
const char *sub = subname;
2153
for(const char* dot=strchr(sub,'.');dot;sub=dot+1,dot=strchr(sub,'.')) {
2154
DocumentObject *obj = nullptr;
2156
s.append(sub,dot+1);
2157
extensionGetSubObject(obj,s.c_str());
2160
if(!obj->hasExtension(GroupExtension::getExtensionClassTypeId(),false))
2167
void LinkBaseExtension::expandSubname(std::string &subname) const {
2168
if(!_ChildCache.getSize())
2171
const char *pos = nullptr;
2172
int index = getElementIndex(subname.c_str(),&pos);
2175
std::ostringstream ss;
2176
elementNameFromIndex(index,ss);
2181
static bool isExcludedProperties(const char *name) {
2182
#define CHECK_EXCLUDE_PROP(_name) if(strcmp(name,#_name)==0) return true;
2183
CHECK_EXCLUDE_PROP(Shape);
2184
CHECK_EXCLUDE_PROP(Proxy);
2185
CHECK_EXCLUDE_PROP(Placement);
2189
Property *LinkBaseExtension::extensionGetPropertyByName(const char* name) const {
2190
if (checkingProperty)
2191
return inherited::extensionGetPropertyByName(name);
2192
Base::StateLocker guard(checkingProperty);
2193
if(isExcludedProperties(name))
2195
auto owner = getContainer();
2197
App::Property *prop = owner->getPropertyByName(name);
2200
if(owner->canLinkProperties()) {
2201
auto linked = getTrueLinkedObject(true);
2203
return linked->getPropertyByName(name);
2209
bool LinkBaseExtension::isLinkMutated() const
2211
return getLinkCopyOnChangeValue() != CopyOnChangeDisabled
2212
&& getLinkedObjectValue()
2213
&& (!getLinkCopyOnChangeSourceValue()
2214
|| (getLinkedObjectValue() != getLinkCopyOnChangeSourceValue()));
2220
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::LinkBaseExtensionPython, App::LinkBaseExtension)
2223
template class AppExport ExtensionPythonT<LinkBaseExtension>;
2229
EXTENSION_PROPERTY_SOURCE(App::LinkExtension, App::LinkBaseExtension)
2231
LinkExtension::LinkExtension()
2233
initExtensionType(LinkExtension::getExtensionClassTypeId());
2235
LINK_PROPS_ADD_EXTENSION(LINK_PARAMS_EXT);
2241
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::LinkExtensionPython, App::LinkExtension)
2244
template class AppExport ExtensionPythonT<App::LinkExtension>;
2250
PROPERTY_SOURCE_WITH_EXTENSIONS(App::Link, App::DocumentObject)
2253
LINK_PROPS_ADD(LINK_PARAMS_LINK);
2254
LinkExtension::initExtension(this);
2255
static const PropertyIntegerConstraint::Constraints s_constraints = {0,INT_MAX,1};
2256
ElementCount.setConstraints(&s_constraints);
2259
bool Link::canLinkProperties() const {
2266
PROPERTY_SOURCE_TEMPLATE(App::LinkPython, App::Link)
2267
template<> const char* App::LinkPython::getViewProviderName() const {
2268
return "Gui::ViewProviderLinkPython";
2270
template class AppExport FeaturePythonT<App::Link>;
2275
PROPERTY_SOURCE_WITH_EXTENSIONS(App::LinkElement, App::DocumentObject)
2277
LinkElement::LinkElement() {
2278
LINK_PROPS_ADD(LINK_PARAMS_ELEMENT);
2279
LinkBaseExtension::initExtension(this);
2282
bool LinkElement::canDelete() const {
2283
if(!_LinkOwner.getValue())
2286
auto owner = getContainer();
2287
return !owner || !owner->getDocument()->getObjectByID(_LinkOwner.getValue());
2293
PROPERTY_SOURCE_TEMPLATE(App::LinkElementPython, App::LinkElement)
2294
template<> const char* App::LinkElementPython::getViewProviderName() const {
2295
return "Gui::ViewProviderLinkPython";
2297
template class AppExport FeaturePythonT<App::LinkElement>;
2302
PROPERTY_SOURCE_WITH_EXTENSIONS(App::LinkGroup, App::DocumentObject)
2304
LinkGroup::LinkGroup() {
2305
LINK_PROPS_ADD(LINK_PARAMS_GROUP);
2306
LinkBaseExtension::initExtension(this);
2312
PROPERTY_SOURCE_TEMPLATE(App::LinkGroupPython, App::LinkGroup)
2313
template<> const char* App::LinkGroupPython::getViewProviderName() const {
2314
return "Gui::ViewProviderLinkPython";
2316
template class AppExport FeaturePythonT<App::LinkGroup>;
2319
#if defined(__clang__)
2320
# pragma clang diagnostic pop