FreeCAD

Форк
0
/
DocumentObject.cpp 
1331 строка · 42.0 Кб
1
/***************************************************************************
2
 *   Copyright (c) 2011 Jürgen Riegel <juergen.riegel@web.de>              *
3
 *   Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net>     *
4
 *                                                                         *
5
 *   This file is part of the FreeCAD CAx development system.              *
6
 *                                                                         *
7
 *   This library is free software; you can redistribute it and/or         *
8
 *   modify it under the terms of the GNU Library General Public           *
9
 *   License as published by the Free Software Foundation; either          *
10
 *   version 2 of the License, or (at your option) any later version.      *
11
 *                                                                         *
12
 *   This library  is distributed in the hope that it will be useful,      *
13
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15
 *   GNU Library General Public License for more details.                  *
16
 *                                                                         *
17
 *   You should have received a copy of the GNU Library General Public     *
18
 *   License along with this library; see the file COPYING.LIB. If not,    *
19
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
20
 *   Suite 330, Boston, MA  02111-1307, USA                                *
21
 *                                                                         *
22
 ***************************************************************************/
23

24

25
#include "PreCompiled.h"
26
#ifndef _PreComp_
27
#include <stack>
28
#endif
29

30
#include <App/DocumentObjectPy.h>
31
#include <Base/Console.h>
32
#include <Base/Matrix.h>
33
#include <Base/Tools.h>
34
#include <Base/Writer.h>
35

36
#include "Application.h"
37
#include "ElementNamingUtils.h"
38
#include "Document.h"
39
#include "DocumentObject.h"
40
#include "DocumentObjectExtension.h"
41
#include "DocumentObjectGroup.h"
42
#include "GeoFeatureGroupExtension.h"
43
#include "ObjectIdentifier.h"
44
#include "PropertyExpressionEngine.h"
45
#include "PropertyLinks.h"
46

47

48
FC_LOG_LEVEL_INIT("App",true,true)
49

50
using namespace App;
51

52
/** \defgroup DocObject Document Object
53
    \ingroup APP
54
    \brief Base class of all objects handled in the Document
55
*/
56

57
PROPERTY_SOURCE(App::DocumentObject, App::TransactionalObject)
58

59
DocumentObjectExecReturn *DocumentObject::StdReturn = nullptr;
60

61
//===========================================================================
62
// DocumentObject
63
//===========================================================================
64

65
DocumentObject::DocumentObject()
66
    : ExpressionEngine()
67
{
68
    // define Label of type 'Output' to avoid being marked as touched after relabeling
69
    ADD_PROPERTY_TYPE(Label,("Unnamed"),"Base",Prop_Output,"User name of the object (UTF8)");
70
    ADD_PROPERTY_TYPE(Label2,(""),"Base",Prop_Hidden,"User description of the object (UTF8)");
71
    Label2.setStatus(App::Property::Output,true);
72
    ADD_PROPERTY_TYPE(ExpressionEngine,(),"Base",Prop_Hidden,"Property expressions");
73

74
    ADD_PROPERTY(Visibility, (true));
75

76
    // default set Visibility status to hidden and output (no touch) for
77
    // compatibitily reason. We use setStatus instead of PropertyType to 
78
    // allow user to change its status later
79
    Visibility.setStatus(Property::Output,true);
80
    Visibility.setStatus(Property::Hidden,true);
81
    Visibility.setStatus(Property::NoModify,true);
82
}
83

84
DocumentObject::~DocumentObject()
85
{
86
    if (!PythonObject.is(Py::_None())){
87
        Base::PyGILStateLocker lock;
88
        // Remark: The API of Py::Object has been changed to set whether the wrapper owns the passed
89
        // Python object or not. In the constructor we forced the wrapper to own the object so we need
90
        // not to dec'ref the Python object any more.
91
        // But we must still invalidate the Python object because it need not to be
92
        // destructed right now because the interpreter can own several references to it.
93
        Base::PyObjectBase* obj = static_cast<Base::PyObjectBase*>(PythonObject.ptr());
94
        // Call before decrementing the reference counter, otherwise a heap error can occur
95
        obj->setInvalid();
96
    }
97
}
98

99
void DocumentObject::printInvalidLinks() const
100
{
101
    try {
102
        // Get objects that have invalid link scope, and print their names.
103
        // Truncate the invalid object list name strings for readability, if they happen to be very long.
104
        std::vector<App::DocumentObject*> invalid_linkobjs;
105
        std::string objnames, scopenames;
106
        GeoFeatureGroupExtension::getInvalidLinkObjects(this, invalid_linkobjs);
107
        for (auto& obj : invalid_linkobjs) {
108
            objnames += obj->getNameInDocument();
109
            objnames += " ";
110
            for (auto& scope : obj->getParents()) {
111
                if (scopenames.length() > 80) {
112
                    scopenames += "... ";
113
                    break;
114
                }
115

116
                scopenames += scope.first->getNameInDocument();
117
                scopenames += " ";
118
            }
119

120
            if (objnames.length() > 80) {
121
                objnames += "... ";
122
                break;
123
            }
124
        }
125

126
        if (objnames.empty()) {
127
            objnames = "N/A";
128
        }
129
        else {
130
            objnames.pop_back();
131
        }
132

133
        if (scopenames.empty()) {
134
            scopenames = "N/A";
135
        }
136
        else {
137
            scopenames.pop_back();
138
        }
139

140
        Base::Console().Warning("%s: Link(s) to object(s) '%s' go out of the allowed scope '%s'. Instead, the linked object(s) reside within '%s'.\n",
141
                                getTypeId().getName(), objnames.c_str(), getNameInDocument(), scopenames.c_str());
142
    }
143
    catch (const Base::Exception& e) {
144
        e.ReportException();
145
    }
146
}
147

148
App::DocumentObjectExecReturn *DocumentObject::recompute()
149
{
150
    //check if the links are valid before making the recompute
151
    if (!GeoFeatureGroupExtension::areLinksValid(this)) {
152
        printInvalidLinks();
153
    }
154

155
    // set/unset the execution bit
156
    Base::ObjectStatusLocker<ObjectStatus, DocumentObject> exe(App::Recompute, this);
157

158
    // mark the object to recompute its extensions
159
    this->setStatus(App::RecomputeExtension, true);
160

161
    auto ret = this->execute();
162
    if (ret == StdReturn) {
163
        // most feature classes don't call the execute() method of its base class
164
        // so execute the extensions now
165
        if (this->testStatus(App::RecomputeExtension)) {
166
            ret = executeExtensions();
167
        }
168
    }
169

170
    return ret;
171
}
172

173
DocumentObjectExecReturn *DocumentObject::execute()
174
{
175
    return executeExtensions();
176
}
177

178
App::DocumentObjectExecReturn* DocumentObject::executeExtensions()
179
{
180
    //execute extensions but stop on error
181
    this->setStatus(App::RecomputeExtension, false); // reset the flag
182
    auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
183
    for(auto ext : vector) {
184
        auto ret = ext->extensionExecute();
185
        if (ret != StdReturn)
186
            return ret;
187
    }
188

189
    return StdReturn;
190
}
191

192
bool DocumentObject::recomputeFeature(bool recursive)
193
{
194
    Document* doc = this->getDocument();
195
    if (doc)
196
        return doc->recomputeFeature(this,recursive);
197
    return isValid();
198
}
199

200
/**
201
 * @brief Set this document object touched.
202
 * Touching a document object does not mean to recompute it, it only means that
203
 * other document objects that link it (i.e. its InList) will be recomputed.
204
 * If it should be forced to recompute a document object then use
205
 * \ref enforceRecompute() instead.
206
 */
207
void DocumentObject::touch(bool noRecompute)
208
{
209
    if(!noRecompute)
210
        StatusBits.set(ObjectStatus::Enforce);
211
    StatusBits.set(ObjectStatus::Touch);
212
    if (_pDoc)
213
        _pDoc->signalTouchedObject(*this);
214
}
215

216
/**
217
 * @brief Set this document object freezed.
218
 * A freezed document object does not recompute ever.
219
 */
220
void DocumentObject::freeze()
221
{
222
    StatusBits.set(ObjectStatus::Freeze);
223
    // use the signalTouchedObject to refresh the Gui
224
    if (_pDoc)
225
        _pDoc->signalTouchedObject(*this);
226
}
227

228
/**
229
 * @brief Set this document object unfreezed.
230
 * A freezed document object does not recompute ever.
231
 */
232
void DocumentObject::unfreeze(bool noRecompute)
233
{
234
    StatusBits.set(ObjectStatus::Freeze, false);
235
    touch(noRecompute);
236
}
237

238
/**
239
 * @brief Check whether the document object is touched or not.
240
 * @return true if document object is touched, false if not.
241
 */
242
bool DocumentObject::isTouched() const
243
{
244
    return ExpressionEngine.isTouched() || StatusBits.test(ObjectStatus::Touch);
245
}
246

247
/**
248
 * @brief Enforces this document object to be recomputed.
249
 * This can be useful to recompute the feature without
250
 * having to change one of its input properties.
251
 */
252
void DocumentObject::enforceRecompute()
253
{
254
    touch(false);
255
}
256

257
/**
258
 * @brief Check whether the document object must be recomputed or not.
259
 * This means that the 'Enforce' flag is set or that \ref mustExecute()
260
 * returns a value > 0.
261
 * @return true if document object must be recomputed, false if not.
262
 */
263
bool DocumentObject::mustRecompute() const
264
{
265
    if (StatusBits.test(ObjectStatus::Freeze))
266
        return false;
267

268
    if (StatusBits.test(ObjectStatus::Enforce))
269
        return true;
270

271
    return mustExecute() > 0;
272
}
273

274
short DocumentObject::mustExecute() const
275
{
276
    if (ExpressionEngine.isTouched())
277
        return 1;
278

279
    //ask all extensions
280
    auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
281
    for(auto ext : vector) {
282
        if (ext->extensionMustExecute())
283
            return 1;
284
    }
285

286
    return 0;
287
}
288

289
const char* DocumentObject::getStatusString() const
290
{
291
    if (isError()) {
292
        const char* text = getDocument()->getErrorDescription(this);
293
        return text ? text : "Error";
294
    }
295
    else if (isTouched())
296
        return "Touched";
297
    else
298
        return "Valid";
299
}
300

301
std::string DocumentObject::getFullName() const {
302
    if(!getDocument() || !isAttachedToDocument())
303
        return "?";
304
    std::string name(getDocument()->getName());
305
    name += '#';
306
    name += *pcNameInDocument;
307
    return name;
308
}
309

310
std::string DocumentObject::getFullLabel() const {
311
    if(!getDocument())
312
        return "?";
313

314
    auto name = getDocument()->Label.getStrValue();
315
    name += "#";
316
    name += Label.getStrValue();
317
    return name;
318
}
319

320
const char* DocumentObject::getDagKey() const
321
{
322
    if(!pcNameInDocument)
323
    {
324
        return nullptr;
325
    }
326
    return pcNameInDocument->c_str();
327
}
328

329
const char *DocumentObject::getNameInDocument() const
330
{
331
    // Note: It can happen that we query the internal name of an object even if it is not
332
    // part of a document (anymore). This is the case e.g. if we have a reference in Python
333
    // to an object that has been removed from the document. In this case we should rather
334
    // return 0.
335
    //assert(pcNameInDocument);
336
    if (!pcNameInDocument)
337
        return nullptr;
338
    return pcNameInDocument->c_str();
339
}
340

341
int DocumentObject::isExporting() const {
342
    if(!getDocument() || !isAttachedToDocument())
343
        return 0;
344
    return getDocument()->isExporting(this);
345
}
346

347
std::string DocumentObject::getExportName(bool forced) const {
348
    if(!isAttachedToDocument())
349
        return {};
350

351
    if(!forced && !isExporting())
352
        return *pcNameInDocument;
353

354
    // '@' is an invalid character for an internal name, which ensures the
355
    // following returned name will be unique in any document. Saving external
356
    // object like that shall only happens in Document::exportObjects(). We
357
    // shall strip out this '@' and the following document name during restoring.
358
    return *pcNameInDocument + '@' + getDocument()->getName();
359
}
360

361
bool DocumentObject::isAttachedToDocument() const
362
{
363
    return (pcNameInDocument != nullptr);
364
}
365

366
const char* DocumentObject::detachFromDocument()
367
{
368
    const std::string* name = pcNameInDocument;
369
    pcNameInDocument = nullptr;
370
    return name ? name->c_str() : nullptr;
371
}
372

373
const std::vector<DocumentObject*> &DocumentObject::getOutList() const {
374
    if(!_outListCached) {
375
        _outList.clear();
376
        getOutList(0,_outList);
377
        _outListCached = true;
378
    }
379
    return _outList;
380
}
381

382
std::vector<DocumentObject*> DocumentObject::getOutList(int options) const
383
{
384
    std::vector<DocumentObject*> res;
385
    getOutList(options,res);
386
    return res;
387
}
388

389
void DocumentObject::getOutList(int options, std::vector<DocumentObject*> &res) const {
390
    if(_outListCached && !options) {
391
        res.insert(res.end(),_outList.begin(),_outList.end());
392
        return;
393
    }
394
    std::vector<Property*> props;
395
    getPropertyList(props);
396
    bool noHidden = !!(options & OutListNoHidden);
397
    std::size_t size = res.size();
398
    for(auto prop : props) {
399
        auto link = dynamic_cast<PropertyLinkBase*>(prop);
400
        if(link)
401
            link->getLinks(res,noHidden);
402
    }
403
    if(!(options & OutListNoExpression))
404
        ExpressionEngine.getLinks(res);
405

406
    if(options & OutListNoXLinked) {
407
        for(auto it=res.begin()+size;it!=res.end();) {
408
            auto obj = *it;
409
            if(obj && obj->getDocument()!=getDocument())
410
                it = res.erase(it);
411
            else
412
                ++it;
413
        }
414
    }
415
}
416

417
std::vector<App::DocumentObject*> DocumentObject::getOutListOfProperty(App::Property* prop) const
418
{
419
    std::vector<DocumentObject*> ret;
420
    if (!prop || prop->getContainer() != this)
421
        return ret;
422

423
    auto link = dynamic_cast<PropertyLinkBase*>(prop);
424
    if(link)
425
        link->getLinks(ret);
426
    return ret;
427
}
428

429
#ifdef USE_OLD_DAG
430
std::vector<App::DocumentObject*> DocumentObject::getInList(void) const
431
{
432
    if (_pDoc)
433
        return _pDoc->getInList(this);
434
    else
435
        return std::vector<App::DocumentObject*>();
436
}
437

438
#else // ifndef USE_OLD_DAG
439

440
const std::vector<App::DocumentObject*> &DocumentObject::getInList() const
441
{
442
    return _inList;
443
}
444

445
#endif // if USE_OLD_DAG
446

447

448
// The original algorithm is highly inefficient in some special case.
449
// Considering an object is linked by every other objects. After excluding this
450
// object, there is another object linked by every other of the remaining
451
// objects, and so on.  The vector 'result' above will be of magnitude n^2.
452
// Even if we replace the vector with a set, we still need to visit that amount
453
// of objects. And this may not be the worst case. getInListEx() has no such
454
// problem.
455

456
std::vector<App::DocumentObject*> DocumentObject::getInListRecursive() const {
457
    std::set<App::DocumentObject*> inSet;
458
    std::vector<App::DocumentObject*> res;
459
    getInListEx(inSet,true,&res);
460
    return res;
461
}
462

463

464
// More efficient algorithm to find the recursive inList of an object,
465
// including possible external parents.  One shortcoming of this algorithm is
466
// it does not detect cyclic reference, althgouth it won't crash either.
467
void DocumentObject::getInListEx(std::set<App::DocumentObject*> &inSet, 
468
        bool recursive, std::vector<App::DocumentObject*> *inList) const
469
{
470
#ifdef USE_OLD_DAG
471
    std::map<DocumentObject*,std::set<App::DocumentObject*> > outLists;
472

473
    // Old DAG does not have pre-built InList, and must calculate The InList by
474
    // going through all objects' OutLists. So we collect all objects and their
475
    // outLists first here.
476
    for(auto doc : GetApplication().getDocuments()) {
477
        for(auto obj : doc->getObjects()) {
478
            if(!obj || !obj->isAttachedToDocument() || obj==this)
479
                continue;
480
            const auto &outList = obj->getOutList();
481
            outLists[obj].insert(outList.begin(),outList.end());
482
        }
483
    }
484

485
    std::stack<DocumentObject*> pendings;
486
    pendings.push(const_cast<DocumentObject*>(this));
487
    while(pendings.size()) {
488
        auto obj = pendings.top();
489
        pendings.pop();
490
        for(auto &v : outLists) {
491
            if(v.first == obj) continue;
492
            auto &outList = v.second;
493
            // Check the outList to see if the object is there, and pend the
494
            // object for recursive check if it's not already in the inList
495
            if(outList.find(obj)!=outList.end() && 
496
               inSet.insert(v.first).second &&
497
               recursive)
498
            {
499
                pendings.push(v.first);
500
            }
501
        }
502
    }
503
#else // USE_OLD_DAG
504

505
    if(!recursive) {
506
        inSet.insert(_inList.begin(),_inList.end());
507
        if(inList)
508
            *inList = _inList;
509
        return;
510
    }
511

512
    std::stack<DocumentObject*> pendings;
513
    pendings.push(const_cast<DocumentObject*>(this));
514
    while(!pendings.empty()) {
515
        auto obj = pendings.top();
516
        pendings.pop();
517
        for(auto o : obj->getInList()) {
518
            if(o && o->isAttachedToDocument() && inSet.insert(o).second) {
519
                pendings.push(o);
520
                if(inList)
521
                    inList->push_back(o);
522
            }
523
        }
524
    }
525

526
#endif
527
}
528

529
std::set<App::DocumentObject*> DocumentObject::getInListEx(bool recursive) const {
530
    std::set<App::DocumentObject*> ret;
531
    getInListEx(ret,recursive);
532
    return ret;
533
}
534

535
void _getOutListRecursive(std::set<DocumentObject*>& objSet,
536
                          const DocumentObject* obj,
537
                          const DocumentObject* checkObj, int depth)
538
{
539
    for (const auto objIt : obj->getOutList()) {
540
        // if the check object is in the recursive inList we have a cycle!
541
        if (objIt == checkObj || depth <= 0) {
542
            throw Base::BadGraphError("DocumentObject::getOutListRecursive(): cyclic dependency detected!");
543
        }
544

545
        // if the element was already in the set then there is no need to process it again
546
        auto pair = objSet.insert(objIt);
547
        if (pair.second)
548
            _getOutListRecursive(objSet, objIt, checkObj, depth-1);
549
    }
550
}
551

552
std::vector<App::DocumentObject*> DocumentObject::getOutListRecursive() const
553
{
554
    // number of objects in document is a good estimate in result size
555
    int maxDepth = GetApplication().checkLinkDepth(0);
556
    std::set<App::DocumentObject*> result;
557

558
    // using a recursive helper to collect all OutLists
559
    _getOutListRecursive(result, this, this, maxDepth);
560

561
    std::vector<App::DocumentObject*> array;
562
    array.insert(array.begin(), result.begin(), result.end());
563
    return array;
564
}
565

566
// helper for isInInListRecursive()
567
bool _isInInListRecursive(const DocumentObject* act,
568
                          const DocumentObject* checkObj, int depth)
569
{
570
#ifndef  USE_OLD_DAG
571
    for (auto obj : act->getInList()) {
572
        if (obj == checkObj)
573
            return true;
574
        // if we reach the depth limit we have a cycle!
575
        if (depth <= 0) {
576
            throw Base::BadGraphError("DocumentObject::isInInListRecursive(): cyclic dependency detected!");
577
        }
578

579
        if (_isInInListRecursive(obj, checkObj, depth - 1))
580
            return true;
581
    }
582
#else
583
    (void)act;
584
    (void)checkObj;
585
    (void)depth;
586
#endif
587

588
    return false;
589
}
590

591
bool DocumentObject::isInInListRecursive(DocumentObject *linkTo) const
592
{
593
    return this==linkTo || getInListEx(true).count(linkTo);
594
}
595

596
bool DocumentObject::isInInList(DocumentObject *linkTo) const
597
{
598
#ifndef  USE_OLD_DAG
599
    if (std::find(_inList.begin(), _inList.end(), linkTo) != _inList.end())
600
        return true;
601
    else
602
        return false;
603
#else
604
    (void)linkTo;
605
    return false;
606
#endif
607
}
608

609
// helper for isInOutListRecursive()
610
bool _isInOutListRecursive(const DocumentObject* act,
611
                           const DocumentObject* checkObj, int depth)
612
{
613
#ifndef  USE_OLD_DAG
614
    for (auto obj : act->getOutList()) {
615
        if (obj == checkObj)
616
            return true;
617
        // if we reach the depth limit we have a cycle!
618
        if (depth <= 0) {
619
            throw Base::BadGraphError("DocumentObject::isInOutListRecursive(): cyclic dependency detected!");
620
        }
621

622
        if (_isInOutListRecursive(obj, checkObj, depth - 1))
623
            return true;
624
    }
625
#else
626
    (void)act;
627
    (void)checkObj;
628
    (void)depth;
629
#endif
630

631
    return false;
632
}
633

634
bool DocumentObject::isInOutListRecursive(DocumentObject *linkTo) const
635
{
636
    int maxDepth = getDocument()->countObjects() + 2;
637
    return _isInOutListRecursive(this, linkTo, maxDepth);
638
}
639

640
std::vector<std::list<App::DocumentObject*> >
641
DocumentObject::getPathsByOutList(App::DocumentObject* to) const
642
{
643
    return _pDoc->getPathsByOutList(this, to);
644
}
645

646
DocumentObjectGroup* DocumentObject::getGroup() const
647
{
648
    return dynamic_cast<DocumentObjectGroup*>(GroupExtension::getGroupOfObject(this));
649
}
650

651
bool DocumentObject::testIfLinkDAGCompatible(DocumentObject *linkTo) const
652
{
653
    std::vector<App::DocumentObject*> linkTo_in_vector;
654
    linkTo_in_vector.push_back(linkTo);
655
    return this->testIfLinkDAGCompatible(linkTo_in_vector);
656
}
657

658
bool DocumentObject::testIfLinkDAGCompatible(const std::vector<DocumentObject *> &linksTo) const
659
{
660
    auto inLists = getInListEx(true);
661
    inLists.emplace(const_cast<DocumentObject*>(this));
662
    for(auto obj : linksTo)
663
        if(inLists.count(obj))
664
            return false;
665
    return true;
666
}
667

668
bool DocumentObject::testIfLinkDAGCompatible(PropertyLinkSubList &linksTo) const
669
{
670
    const std::vector<App::DocumentObject*> &linksTo_in_vector = linksTo.getValues();
671
    return this->testIfLinkDAGCompatible(linksTo_in_vector);
672
}
673

674
bool DocumentObject::testIfLinkDAGCompatible(PropertyLinkSub &linkTo) const
675
{
676
    std::vector<App::DocumentObject*> linkTo_in_vector;
677
    linkTo_in_vector.reserve(1);
678
    linkTo_in_vector.push_back(linkTo.getValue());
679
    return this->testIfLinkDAGCompatible(linkTo_in_vector);
680
}
681

682
void DocumentObject::onLostLinkToObject(DocumentObject*)
683
{
684

685
}
686

687
App::Document *DocumentObject::getDocument() const
688
{
689
    return _pDoc;
690
}
691

692
void DocumentObject::setDocument(App::Document* doc)
693
{
694
    _pDoc=doc;
695
    onSettingDocument();
696
}
697

698
bool DocumentObject::removeDynamicProperty(const char* name)
699
{
700
    if (!_pDoc || testStatus(ObjectStatus::Destroy)) 
701
        return false;
702

703
    Property* prop = getDynamicPropertyByName(name);
704
    if(!prop || prop->testStatus(App::Property::LockDynamic))
705
        return false;
706

707
    if(prop->isDerivedFrom(PropertyLinkBase::getClassTypeId()))
708
        clearOutListCache();
709

710
    _pDoc->addOrRemovePropertyOfObject(this, prop, false);
711

712
    auto expressions = ExpressionEngine.getExpressions();
713
    std::vector<App::ObjectIdentifier> removeExpr;
714

715
    for (const auto& it : expressions) {
716
        if (it.first.getProperty() == prop) {
717
            removeExpr.push_back(it.first);
718
        }
719
    }
720

721
    for (const auto& it : removeExpr) {
722
        ExpressionEngine.setValue(it, std::shared_ptr<Expression>());
723
    }
724

725
    return TransactionalObject::removeDynamicProperty(name);
726
}
727

728
App::Property* DocumentObject::addDynamicProperty(
729
    const char* type, const char* name, const char* group, const char* doc,
730
    short attr, bool ro, bool hidden)
731
{
732
    auto prop = TransactionalObject::addDynamicProperty(type,name,group,doc,attr,ro,hidden);
733
    if(prop && _pDoc)
734
        _pDoc->addOrRemovePropertyOfObject(this, prop, true);
735
    return prop;
736
}
737

738
void DocumentObject::onBeforeChange(const Property* prop)
739
{
740
    // Store current name in oldLabel, to be able to easily retrieve old name of document object later
741
    // when renaming expressions.
742
    if (prop == &Label)
743
        oldLabel = Label.getStrValue();
744

745
    if (_pDoc)
746
        onBeforeChangeProperty(_pDoc, prop);
747

748
    signalBeforeChange(*this,*prop);
749
}
750

751
void DocumentObject::onEarlyChange(const Property *prop)
752
{
753
    if(GetApplication().isClosingAll())
754
        return;
755

756
    if(!GetApplication().isRestoring() &&
757
        !prop->testStatus(Property::PartialTrigger) &&
758
        getDocument() &&
759
        getDocument()->testStatus(Document::PartialDoc))
760
    {
761
        static App::Document *warnedDoc;
762
        if(warnedDoc != getDocument()) {
763
            warnedDoc = getDocument();
764
            FC_WARN("Changes to partial loaded document will not be saved: "
765
                    << getFullName() << '.' << prop->getName());
766
        }
767
    }
768

769
    signalEarlyChanged(*this, *prop);
770
}
771

772
/// get called by the container when a Property was changed
773
void DocumentObject::onChanged(const Property* prop)
774
{
775
    if (isFreezed())
776
        return;
777

778
    if(GetApplication().isClosingAll())
779
        return;
780

781
    if(!GetApplication().isRestoring() && 
782
       !prop->testStatus(Property::PartialTrigger) &&
783
       getDocument() && 
784
       getDocument()->testStatus(Document::PartialDoc))
785
    {
786
        static App::Document *warnedDoc;
787
        if(warnedDoc != getDocument()) {
788
            warnedDoc = getDocument();
789
            FC_WARN("Changes to partial loaded document will not be saved: "
790
                    << getFullName() << '.' << prop->getName());
791
        }
792
    }
793

794
    // Delay signaling view provider until the document object has handled the
795
    // change
796
    // if (_pDoc)
797
    //     _pDoc->onChangedProperty(this,prop);
798

799
    if (prop == &Label && _pDoc && oldLabel != Label.getStrValue())
800
        _pDoc->signalRelabelObject(*this);
801

802
    // set object touched if it is an input property
803
    if (!testStatus(ObjectStatus::NoTouch) 
804
            && !(prop->getType() & Prop_Output) 
805
            && !prop->testStatus(Property::Output)) 
806
    {
807
        if(!StatusBits.test(ObjectStatus::Touch)) {
808
            FC_TRACE("touch '" << getFullName() << "' on change of '" << prop->getName() << "'");
809
            StatusBits.set(ObjectStatus::Touch);
810
        }
811
        // must execute on document recompute
812
        if (!(prop->getType() & Prop_NoRecompute))
813
            StatusBits.set(ObjectStatus::Enforce);
814
    }
815

816
    //call the parent for appropriate handling
817
    TransactionalObject::onChanged(prop);
818

819
    // Now signal the view provider
820
    if (_pDoc)
821
        _pDoc->onChangedProperty(this,prop);
822

823
    signalChanged(*this,*prop);
824
}
825

826
void DocumentObject::clearOutListCache() const {
827
    _outList.clear();
828
    _outListMap.clear();
829
    _outListCached = false;
830
}
831

832
PyObject *DocumentObject::getPyObject()
833
{
834
    if (PythonObject.is(Py::_None())) {
835
        // ref counter is set to 1
836
        PythonObject = Py::Object(new DocumentObjectPy(this),true);
837
    }
838
    return Py::new_reference_to(PythonObject);
839
}
840

841
DocumentObject *DocumentObject::getSubObject(const char *subname,
842
        PyObject **pyObj, Base::Matrix4D *mat, bool transform, int depth) const
843
{
844
    DocumentObject *ret = nullptr;
845
    auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
846
    for(auto ext : exts) {
847
        if(ext->extensionGetSubObject(ret,subname,pyObj,mat,transform, depth))
848
            return ret;
849
    }
850

851
    std::string name;
852
    const char *dot=nullptr;
853
    if(!subname || !(dot=strchr(subname,'.'))) {
854
        ret = const_cast<DocumentObject*>(this);
855
    }else if(subname[0]=='$') {
856
        name = std::string(subname+1,dot);
857
        for(auto obj : getOutList()) {
858
            if(name == obj->Label.getValue()) {
859
                ret = obj;
860
                break;
861
            }
862
        }
863
    }else{
864
        name = std::string(subname,dot);
865
        const auto &outList = getOutList();
866
        if(outList.size()!=_outListMap.size()) {
867
            _outListMap.clear();
868
            for(auto obj : outList)
869
                _outListMap[obj->getDagKey()] = obj;
870
        }
871
        auto it = _outListMap.find(name.c_str());
872
        if(it != _outListMap.end())
873
            ret = it->second;
874
    }
875

876
    // TODO: By right, normal object's placement does not transform its sub
877
    // objects (think of the claimed children of a Fusion). But I do think we
878
    // should change that.
879
    if(transform && mat) {
880
        auto pla = Base::freecad_dynamic_cast<PropertyPlacement>(getPropertyByName("Placement"));
881
        if(pla)
882
            *mat *= pla->getValue().toMatrix();
883
    }
884

885
    if(ret && dot)
886
        return ret->getSubObject(dot+1,pyObj,mat,true,depth+1);
887
    return ret;
888
}
889

890
std::vector<DocumentObject*> DocumentObject::getSubObjectList(const char *subname) const {
891
    std::vector<DocumentObject*> res;
892
    res.push_back(const_cast<DocumentObject*>(this));
893
    if(!subname || !subname[0])
894
        return res;
895
    std::string sub(subname);
896
    for(auto pos=sub.find('.');pos!=std::string::npos;pos=sub.find('.',pos+1)) {
897
        char c = sub[pos+1];
898
        sub[pos+1] = 0;
899
        auto sobj = getSubObject(sub.c_str());
900
        if(!sobj || !sobj->isAttachedToDocument())
901
            break;
902
        res.push_back(sobj);
903
        sub[pos+1] = c;
904
    }
905
    return res;
906
}
907

908
std::vector<std::string> DocumentObject::getSubObjects(int reason) const {
909
    std::vector<std::string> ret;
910
    auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
911
    for(auto ext : exts) {
912
        if(ext->extensionGetSubObjects(ret,reason))
913
            return ret;
914
    }
915
    return ret;
916
}
917

918
std::vector<std::pair<App::DocumentObject *,std::string>> DocumentObject::getParents(int depth) const {
919
    std::vector<std::pair<App::DocumentObject *, std::string>> ret;
920
    if (!isAttachedToDocument() || !GetApplication().checkLinkDepth(depth, MessageOption::Throw)) {
921
        return ret;
922
    }
923

924
    std::string name(getNameInDocument());
925
    name += ".";
926
    for (auto parent : getInList()) {
927
        if (!parent || !parent->isAttachedToDocument()) {
928
            continue;
929
        }
930

931
        if (!parent->hasChildElement() &&
932
            !parent->hasExtension(GeoFeatureGroupExtension::getExtensionClassTypeId())) {
933
            continue;
934
        }
935

936
        if (!parent->getSubObject(name.c_str())) {
937
            continue;
938
        }
939

940
        auto links = GetApplication().getLinksTo(parent, App::GetLinkRecursive);
941
        links.insert(parent);
942

943
        for (auto parent : links) {
944
            auto parents = parent->getParents(depth + 1);
945
            if (parents.empty()) {
946
                parents.emplace_back(parent, std::string());
947
            }
948

949
            for (auto &v : parents) {
950
                ret.emplace_back(v.first, v.second + name);
951
            }
952
        }
953
    }
954

955
    return ret;
956
}
957

958
App::DocumentObject* DocumentObject::getFirstParent() const
959
{
960
    for (auto obj : getInList()) {
961
        if (obj->hasExtension(App::GroupExtension::getExtensionClassTypeId(), true)) {
962
            return obj;
963
        }
964
    }
965

966
    return nullptr;
967
}
968

969
DocumentObject *DocumentObject::getLinkedObject(
970
        bool recursive, Base::Matrix4D *mat, bool transform, int depth) const 
971
{
972
    DocumentObject *ret = nullptr;
973
    auto exts = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
974
    for(auto ext : exts) {
975
        if(ext->extensionGetLinkedObject(ret,recursive,mat,transform,depth))
976
            return ret;
977
    }
978
    if(transform && mat) {
979
        auto pla = dynamic_cast<PropertyPlacement*>(getPropertyByName("Placement"));
980
        if(pla)
981
            *mat *= pla->getValue().toMatrix();
982
    }
983
    return const_cast<DocumentObject*>(this);
984
}
985

986
void DocumentObject::Save (Base::Writer &writer) const
987
{
988
    if (this->isAttachedToDocument())
989
        writer.ObjectName = this->getNameInDocument();
990
    App::ExtensionContainer::Save(writer);
991
}
992

993
/**
994
 * @brief Associate the expression \expr with the object identifier \a path in this document object.
995
 * @param path Target object identifier for the result of the expression
996
 * @param expr Expression tree
997
 */
998

999
void DocumentObject::setExpression(const ObjectIdentifier &path, std::shared_ptr<Expression> expr)
1000
{
1001
    ExpressionEngine.setValue(path, expr);
1002
}
1003

1004
/**
1005
 * @brief Clear the expression of the object identifier \a path in this document object.
1006
 * @param path Target object identifier
1007
 */
1008

1009
void DocumentObject::clearExpression(const ObjectIdentifier & path)
1010
{
1011
    setExpression(path, std::shared_ptr<Expression>());
1012
}
1013

1014
/**
1015
 * @brief Get expression information associated with \a path.
1016
 * @param path Object identifier
1017
 * @return Expression info, containing expression and optional comment.
1018
 */
1019

1020
const PropertyExpressionEngine::ExpressionInfo DocumentObject::getExpression(const ObjectIdentifier &path) const
1021
{
1022
    boost::any value = ExpressionEngine.getPathValue(path);
1023

1024
    if (value.type() == typeid(PropertyExpressionEngine::ExpressionInfo))
1025
        return boost::any_cast<PropertyExpressionEngine::ExpressionInfo>(value);
1026
    else
1027
        return PropertyExpressionEngine::ExpressionInfo();
1028
}
1029

1030
/**
1031
 * @brief Invoke ExpressionEngine's renameObjectIdentifier, to possibly rewrite expressions using
1032
 * the \a paths map with current and new identifiers.
1033
 *
1034
 * @param paths
1035
 */
1036

1037
void DocumentObject::renameObjectIdentifiers(const std::map<ObjectIdentifier, ObjectIdentifier> &paths)
1038
{
1039
    ExpressionEngine.renameObjectIdentifiers(paths);
1040
}
1041

1042
void DocumentObject::onDocumentRestored()
1043
{
1044
    //call all extensions
1045
    auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
1046
    for(auto ext : vector)
1047
        ext->onExtendedDocumentRestored();
1048
    if(Visibility.testStatus(Property::Output))
1049
        Visibility.setStatus(Property::NoModify,true);
1050
}
1051

1052
void DocumentObject::onUndoRedoFinished()
1053
{
1054

1055
}
1056

1057
void DocumentObject::onSettingDocument()
1058
{
1059
    //call all extensions
1060
    auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
1061
    for(auto ext : vector)
1062
        ext->onExtendedSettingDocument();
1063
}
1064

1065
void DocumentObject::setupObject()
1066
{
1067
    //call all extensions
1068
    auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
1069
    for(auto ext : vector)
1070
        ext->onExtendedSetupObject();
1071
}
1072

1073
void DocumentObject::unsetupObject()
1074
{
1075
    //call all extensions
1076
    auto vector = getExtensionsDerivedFromType<App::DocumentObjectExtension>();
1077
    for(auto ext : vector)
1078
        ext->onExtendedUnsetupObject();
1079
}
1080

1081
void App::DocumentObject::_removeBackLink(DocumentObject* rmvObj)
1082
{
1083
#ifndef USE_OLD_DAG
1084
    //do not use erase-remove idom, as this erases ALL entries that match. we only want to remove a
1085
    //single one.
1086
    auto it = std::find(_inList.begin(), _inList.end(), rmvObj);
1087
    if(it != _inList.end())
1088
        _inList.erase(it);
1089
#else
1090
    (void)rmvObj;
1091
#endif
1092
}
1093

1094
void App::DocumentObject::_addBackLink(DocumentObject* newObj)
1095
{
1096
#ifndef USE_OLD_DAG
1097
    //we need to add all links, even if they are available multiple times. The reason for this is the
1098
    //removal: If a link loses this object it removes the backlink. If we would have added it only once
1099
    //this removal would clear the object from the inlist, even though there may be other link properties 
1100
    //from this object that link to us.
1101
    _inList.push_back(newObj);
1102
#else
1103
    (void)newObj;
1104
#endif //USE_OLD_DAG    
1105
}
1106

1107
int DocumentObject::setElementVisible(const char *element, bool visible) {
1108
    for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
1109
        int ret = ext->extensionSetElementVisible(element,visible);
1110
        if(ret>=0)
1111
            return ret;
1112
    }
1113

1114
    return -1;
1115
}
1116

1117
int DocumentObject::isElementVisible(const char *element) const {
1118
    for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
1119
        int ret = ext->extensionIsElementVisible(element);
1120
        if(ret>=0)
1121
            return ret;
1122
    }
1123

1124
    return -1;
1125
}
1126

1127
bool DocumentObject::hasChildElement() const {
1128
    for(auto ext : getExtensionsDerivedFromType<DocumentObjectExtension>()) {
1129
        if(ext->extensionHasChildElement())
1130
            return true;
1131
    }
1132
    return false;
1133
}
1134

1135
DocumentObject *DocumentObject::resolve(const char *subname, 
1136
        App::DocumentObject **parent, std::string *childName, const char **subElement, 
1137
        PyObject **pyObj, Base::Matrix4D *pmat, bool transform, int depth) const
1138
{
1139
    auto self = const_cast<DocumentObject*>(this);
1140
    if(parent) *parent = nullptr;
1141
    if(subElement) *subElement = nullptr;
1142

1143
    auto obj = getSubObject(subname,pyObj,pmat,transform,depth);
1144
    if(!obj || !subname || *subname==0)
1145
        return self;
1146

1147
    if(!parent && !subElement)
1148
        return obj;
1149

1150
    // NOTE, the convention of '.' separated SubName demands a mandatory ending
1151
    // '.' for each object name in SubName, even if there is no subelement
1152
    // following it. So finding the last dot will give us the end of the last
1153
    // object name.
1154
    const char *dot=nullptr;
1155
    if(Data::isMappedElement(subname) ||
1156
       !(dot=strrchr(subname,'.')) ||
1157
       dot == subname) 
1158
    {
1159
        if(subElement)
1160
            *subElement = dot?dot+1:subname;
1161
        return obj; // this means no parent object reference in SubName
1162
    }
1163

1164
    if(parent)
1165
        *parent = self;
1166

1167
    bool elementMapChecked = false;
1168
    const char *lastDot = dot;
1169
    for(--dot;;--dot) {
1170
        // check for the second last dot, which is the end of the last parent object
1171
        if(*dot == '.' || dot == subname) {
1172
            // We can't get parent object by its name, because the object may be
1173
            // externally linked (i.e. in a different document). So go through
1174
            // getSubObject again.
1175
            if(!elementMapChecked) {
1176
                elementMapChecked = true;
1177
                const char *sub = dot==subname?dot:dot+1;
1178
                if(Data::isMappedElement(sub)) {
1179
                    lastDot = dot;
1180
                    if(dot==subname) 
1181
                        break;
1182
                    else
1183
                        continue;
1184
                }
1185
            }
1186
            if(dot==subname)
1187
                break;
1188
            auto sobj = getSubObject(std::string(subname,dot-subname+1).c_str());
1189
            if(sobj!=obj) {
1190
                if(parent) {
1191
                    // Link/LinkGroup has special visibility handling of plain
1192
                    // group, so keep ascending
1193
                    if(!sobj->hasExtension(GroupExtension::getExtensionClassTypeId(),false)) {
1194
                        *parent = sobj;
1195
                        break;
1196
                    }
1197
                    for(auto ddot=dot-1;ddot!=subname;--ddot) {
1198
                        if(*ddot != '.') continue;
1199
                        auto sobj = getSubObject(std::string(subname,ddot-subname+1).c_str());
1200
                        if(!sobj->hasExtension(GroupExtension::getExtensionClassTypeId(),false)) {
1201
                            *parent = sobj;
1202
                            break;
1203
                        }
1204
                    }
1205
                }
1206
                break;
1207
            }
1208
        }
1209
    }
1210
    if(childName && lastDot!=dot) {
1211
        if(*dot == '.')
1212
            ++dot;
1213
        const char *nextDot = strchr(dot,'.');
1214
        assert(nextDot);
1215
        *childName = std::string(dot,nextDot-dot);
1216
    }
1217
    if(subElement)
1218
        *subElement = *lastDot=='.'?lastDot+1:lastDot;
1219
    return obj;
1220
}
1221

1222
DocumentObject *DocumentObject::resolveRelativeLink(std::string &subname,
1223
        DocumentObject *&link, std::string &linkSub) const
1224
{
1225
    if(!link || !link->isAttachedToDocument() || !isAttachedToDocument())
1226
        return nullptr;
1227
    auto ret = const_cast<DocumentObject*>(this);
1228
    if(link != ret) {
1229
        auto sub = subname.c_str();
1230
        auto nextsub = sub;
1231
        for(auto dot=strchr(nextsub,'.');dot;nextsub=dot+1,dot=strchr(nextsub,'.')) {
1232
            std::string subcheck(sub,nextsub-sub);
1233
            subcheck += link->getNameInDocument();
1234
            subcheck += '.';
1235
            if(getSubObject(subcheck.c_str())==link) {
1236
                ret = getSubObject(std::string(sub,dot+1-sub).c_str());
1237
                if(!ret) 
1238
                    return nullptr;
1239
                subname = std::string(dot+1);
1240
                break;
1241
            }
1242
        }
1243
        return ret;
1244
    }
1245

1246
    size_t pos=0,linkPos=0;
1247
    std::string linkssub,ssub;
1248
    do {
1249
        linkPos = linkSub.find('.',linkPos);
1250
        if(linkPos == std::string::npos) {
1251
            link = nullptr;
1252
            return nullptr;
1253
        }
1254
        ++linkPos;
1255
        pos = subname.find('.',pos);
1256
        if(pos == std::string::npos) {
1257
            subname.clear();
1258
            ret = nullptr;
1259
            break;
1260
        }
1261
        ++pos;
1262
    }while(subname.compare(0,pos,linkSub,0,linkPos)==0);
1263

1264
    if(pos != std::string::npos) {
1265
        ret = getSubObject(subname.substr(0,pos).c_str());
1266
        if(!ret) {
1267
            link = nullptr;
1268
            return nullptr;
1269
        }
1270
        subname = subname.substr(pos);
1271
    }
1272
    if(linkPos) {
1273
        link = link->getSubObject(linkSub.substr(0,linkPos).c_str());
1274
        if(!link)
1275
            return nullptr;
1276
        linkSub = linkSub.substr(linkPos);
1277
    }
1278
    return ret;
1279
}
1280

1281
bool DocumentObject::adjustRelativeLinks(
1282
        const std::set<App::DocumentObject *> &inList,
1283
        std::set<App::DocumentObject *> *visited)
1284
{
1285
    if(visited)
1286
        visited->insert(this);
1287

1288
    bool touched = false;
1289
    std::vector<Property*> props;
1290
    getPropertyList(props);
1291
    for(auto prop : props) {
1292
        auto linkProp = Base::freecad_dynamic_cast<PropertyLinkBase>(prop);
1293
        if(linkProp && linkProp->adjustLink(inList))
1294
            touched = true;
1295
    }
1296
    if(visited) {
1297
        for(auto obj : getOutList()) {
1298
            if(!visited->count(obj))  {
1299
                if(obj->adjustRelativeLinks(inList,visited))
1300
                    touched = true;
1301
            }
1302
        }
1303
    }
1304
    return touched;
1305
}
1306

1307
const std::string &DocumentObject::hiddenMarker() {
1308
    static std::string marker("!hide");
1309
    return marker;
1310
}
1311

1312
const char *DocumentObject::hasHiddenMarker(const char *subname) {
1313
    if(!subname)
1314
        return nullptr;
1315
    const char *marker = strrchr(subname,'.');
1316
    if(!marker)
1317
        marker = subname;
1318
    else
1319
        ++marker;
1320
    return hiddenMarker()==marker?marker:nullptr;
1321
}
1322

1323
bool DocumentObject::redirectSubName(std::ostringstream &, DocumentObject *, DocumentObject *) const {
1324
    return false;
1325
}
1326

1327
void DocumentObject::onPropertyStatusChanged(const Property &prop, unsigned long oldStatus) {
1328
    (void)oldStatus;
1329
    if(!Document::isAnyRestoring() && isAttachedToDocument() && getDocument())
1330
        getDocument()->signalChangePropertyEditor(*getDocument(),prop);
1331
}
1332

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.