FreeCAD

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

23

24
#include "PreCompiled.h"
25

26
#include <Base/Tools.h>
27

28
#include "Document.h"
29
#include "GeoFeatureGroupExtension.h"
30
#include "GroupExtensionPy.h"
31

32

33
using namespace App;
34
namespace sp = std::placeholders;
35

36
EXTENSION_PROPERTY_SOURCE(App::GroupExtension, App::DocumentObjectExtension)
37

38
namespace App {
39
EXTENSION_PROPERTY_SOURCE_TEMPLATE(App::GroupExtensionPython, App::GroupExtension)
40

41
// explicit template instantiation
42
template class AppExport ExtensionPythonT<GroupExtensionPythonT<GroupExtension>>;
43
}
44

45
GroupExtension::GroupExtension()
46
{
47
    initExtensionType(GroupExtension::getExtensionClassTypeId());
48
    
49
    EXTENSION_ADD_PROPERTY_TYPE(Group,(nullptr),"Base",(App::PropertyType)(Prop_None),"List of referenced objects");
50

51
    EXTENSION_ADD_PROPERTY_TYPE(_GroupTouched, (false), "Base", 
52
            PropertyType(Prop_Hidden|Prop_Transient),0);
53
}
54

55
GroupExtension::~GroupExtension() = default;
56

57
DocumentObject* GroupExtension::addObject(const char* sType, const char* pObjectName)
58
{
59
    DocumentObject* obj = getExtendedObject()->getDocument()->addObject(sType, pObjectName);
60
    if(!allowObject(obj)) {
61
        getExtendedObject()->getDocument()->removeObject(obj->getNameInDocument());
62
        return nullptr;
63
    }
64
    addObject(obj);
65
    return obj;
66
}
67

68
std::vector<DocumentObject*> GroupExtension::addObject(DocumentObject* obj)
69
{
70
    std::vector<DocumentObject*> vec = {obj};
71
    return addObjects(vec);
72
}
73

74
std::vector< DocumentObject* > GroupExtension::addObjects(std::vector< DocumentObject* > objs) {
75
    
76
    std::vector<DocumentObject*> added;
77
    std::vector<DocumentObject*> grp = Group.getValues();
78
    for(auto obj : objs) {
79
            
80
        if(!allowObject(obj))
81
            continue;
82
        
83
        if (hasObject(obj))
84
            continue;
85
            
86
        //only one group per object. Note that it is allowed to be in a group and geofeaturegroup. However,
87
        //getGroupOfObject() returns only normal groups, no GeoFeatureGroups. Hence this works.
88
        auto *group = App::GroupExtension::getGroupOfObject(obj);
89
        if(group && group != getExtendedObject())
90
            group->getExtensionByType<App::GroupExtension>()->removeObject(obj);
91
        
92
        //if we are in a geofeaturegroup we need to ensure the object is too
93
        auto geogrp = GeoFeatureGroupExtension::getGroupOfObject(getExtendedObject());
94
        auto objgrp = GeoFeatureGroupExtension::getGroupOfObject(obj);
95
        if( geogrp != objgrp ) {
96
            //what to do depends on if we are in  geofeature group or not
97
            if(geogrp)
98
                geogrp->getExtensionByType<GeoFeatureGroupExtension>()->addObject(obj);
99
            else 
100
                objgrp->getExtensionByType<GeoFeatureGroupExtension>()->removeObject(obj);
101
        }
102
        
103
        grp.push_back(obj);
104
        added.push_back(obj);
105
    }
106
    
107
    Group.setValues(grp);
108
    
109
    return added;
110
}
111

112
std::vector< DocumentObject* > GroupExtension::setObjects(std::vector< DocumentObject* > obj) {
113

114
    Group.setValues(std::vector< DocumentObject* > ());
115
    return addObjects(obj);
116
}
117

118
std::vector<DocumentObject*> GroupExtension::removeObject(DocumentObject* obj)
119
{
120
    std::vector<DocumentObject*> vec = {obj};
121
    return removeObjects(vec);
122
}
123

124
std::vector< DocumentObject* > GroupExtension::removeObjects(std::vector< DocumentObject* > objs) {
125

126
    const std::vector<DocumentObject*> & grp = Group.getValues();
127
    std::vector<DocumentObject*> newGrp = grp;
128
    std::vector<DocumentObject*> removed;
129

130
    std::vector<DocumentObject*>::iterator end = newGrp.end();
131
    for(auto obj : objs) {       
132
       auto res = std::remove(newGrp.begin(), end, obj);
133
       if(res != end) {
134
           end = res;
135
           removed.push_back(obj);
136
       }
137
    }
138
    
139
    newGrp.erase(end, newGrp.end());
140
    if (grp.size() != newGrp.size()) {
141
        Group.setValues (newGrp);
142
    }
143
    
144
    return removed;
145
}
146

147
void GroupExtension::removeObjectsFromDocument()
148
{
149
    while (Group.getSize() > 0) {
150
        // Remove the objects step by step because it can happen
151
        // that an object is part of several groups and thus a
152
        // double destruction could be possible
153
        const std::vector<DocumentObject*> & grp = Group.getValues();
154
        removeObjectFromDocument(grp.front());
155
    }
156
}
157

158
void GroupExtension::removeObjectFromDocument(DocumentObject* obj)
159
{
160
    // check that object is not invalid
161
    if (!obj || !obj->isAttachedToDocument())
162
        return;
163

164
    // remove all children
165
    if (obj->hasExtension(GroupExtension::getExtensionClassTypeId())) {
166
        GroupExtension *grp = static_cast<GroupExtension*>(obj->getExtension(GroupExtension::getExtensionClassTypeId()));
167

168
        // recursive call to remove all subgroups
169
        grp->removeObjectsFromDocument();
170
    }
171

172
    getExtendedObject()->getDocument()->removeObject(obj->getNameInDocument());
173
}
174

175
DocumentObject *GroupExtension::getObject(const char *Name) const
176
{
177
    DocumentObject* obj = getExtendedObject()->getDocument()->getObject(Name);
178
    if (obj && hasObject(obj))
179
        return obj;
180
    return nullptr;
181
}
182

183
bool GroupExtension::hasObject(const DocumentObject* obj, bool recursive) const
184
{
185
    if (obj == getExtendedObject()) {
186
        return false;
187
    }
188

189
    try {
190
        const std::vector<DocumentObject*>& grp = Group.getValues();
191
        for (auto child : grp) {
192

193
            if (!child)
194
                continue;
195

196
            if (child == obj) {
197
                return true;
198
            }
199
            else if (child == getExtendedObject()) {
200
                throw Base::RuntimeError("Cyclic dependencies detected: Search cannot be performed");
201
            }
202
            else if ( recursive && child->hasExtension(GroupExtension::getExtensionClassTypeId()) ) {
203
                App::GroupExtension *subGroup = static_cast<App::GroupExtension *> (
204
                                        child->getExtension(GroupExtension::getExtensionClassTypeId()));
205
                std::vector<const GroupExtension*> history;
206
                history.push_back(this);
207

208
                if (subGroup->recursiveHasObject (obj, subGroup, history)) {
209
                    return true;
210
                }
211
            }
212
        }
213

214
        return false;
215
    }
216
    catch (const Base::RuntimeError& e) {
217
        e.ReportException();
218
        return false;
219
    }
220
}
221

222
bool GroupExtension::recursiveHasObject(const DocumentObject* obj, const GroupExtension* group, 
223
                                        std::vector< const GroupExtension* > history) const {
224

225
    //the purpose is to prevent infinite recursion when groups form a cyclic graph. To do this 
226
    //we store every group we processed on the current leave of the tree, and if we reach an 
227
    //already processed group we know that it not really is a tree but a cycle.
228
    history.push_back(this);
229

230
    //we use hasObject with out recursion to allow override in derived classes
231
    if(group->hasObject(obj, false))
232
        return true;
233

234
    //we checked for the searched object already with hasObject and did not find it, now we need to
235
    //do the same for all subgroups
236
    for (auto child : group->Group.getValues()) {
237

238
        if(!child)
239
            continue;
240

241
        if ( child->hasExtension(GroupExtension::getExtensionClassTypeId()) ) {
242

243
            auto ext = child->getExtensionByType<GroupExtension>();
244
            
245
            if (std::find(history.begin(), history.end(), ext) != history.end()) {
246
                throw Base::RuntimeError("Cyclic dependencies detected: Search cannot be performed");
247
            }
248

249
            if (recursiveHasObject(obj, ext, history)) {
250
                return true;
251
            }
252
        }
253
    }
254
    return false;
255
}
256

257
bool GroupExtension::isChildOf(const GroupExtension* group, bool recursive) const
258
{
259
    return group->hasObject(getExtendedObject(), recursive);
260
}
261

262
const std::vector<DocumentObject*> &GroupExtension::getObjects() const
263
{
264
    return Group.getValues();
265
}
266

267
std::vector<DocumentObject*> GroupExtension::getObjectsOfType(const Base::Type& typeId) const
268
{
269
    std::vector<DocumentObject*> type;
270
    const std::vector<DocumentObject*>& grp = Group.getValues();
271
    for (auto it : grp) {
272
        if (it->getTypeId().isDerivedFrom(typeId))
273
            type.push_back(it);
274
    }
275

276
    return type;
277
}
278

279
int GroupExtension::countObjectsOfType(const Base::Type& typeId) const
280
{
281
    int type=0;
282
    const std::vector<DocumentObject*>& grp = Group.getValues();
283
    for (auto it : grp) {
284
        if ( it->getTypeId().isDerivedFrom(typeId))
285
            type++;
286
    }
287

288
    return type;
289
}
290

291
DocumentObject* GroupExtension::getGroupOfObject(const DocumentObject* obj)
292
{
293
    //note that we return here only Groups, but nothing derived from it, e.g. no GeoFeatureGroups.
294
    //That is important as there are clear differences between groups/geofeature groups (e.g. an object
295
    //can be in only one group, and only one geofeaturegroup, however, it can be in both at the same time)
296
    for (auto o : obj->getInList()) {
297
        if (o->hasExtension(App::GroupExtension::getExtensionClassTypeId(), false))
298
            return o;
299
        if (o->hasExtension(App::GroupExtensionPython::getExtensionClassTypeId(), false))
300
            return o;
301
    }
302

303
    return nullptr;
304
}
305

306
PyObject* GroupExtension::getExtensionPyObject() {
307

308
    if (ExtensionPythonObject.is(Py::_None())){
309
        // ref counter is set to 1
310
        auto grp = new GroupExtensionPy(this);
311
        ExtensionPythonObject = Py::Object(grp,true);
312
    }
313
    return Py::new_reference_to(ExtensionPythonObject);
314
}
315

316
void GroupExtension::extensionOnChanged(const Property* p) {
317

318
    //objects are only allowed in a single group. Note that this check must only be done for normal
319
    //groups, not any derived classes
320
    if((this->getExtensionTypeId() == GroupExtension::getExtensionClassTypeId())
321
        && p == &Group && !Group.testStatus(Property::User3)) 
322
    {
323
        if(!getExtendedObject()->isRestoring() &&
324
           !getExtendedObject()->getDocument()->isPerformingTransaction()) {
325
            
326
            bool error = false;
327
            auto corrected = Group.getValues();
328
            for(auto obj : Group.getValues()) {
329

330
                //we have already set the obj into the group, so in a case of multiple groups getGroupOfObject
331
                //would return anyone of it and hence it is possible that we miss an error. We need a custom check
332
                auto list = obj->getInList();
333
                for (auto in : list) {
334
                    if(in->hasExtension(App::GroupExtension::getExtensionClassTypeId(), false) &&
335
                        in != getExtendedObject()) {
336
                        error = true;
337
                        corrected.erase(std::remove(corrected.begin(), corrected.end(), obj), corrected.end());
338
                    }
339
                }
340
            }
341

342
            //if an error was found we need to correct the values and inform the user
343
            if(error) {
344
                Base::ObjectStatusLocker<Property::Status, Property> guard(Property::User3, &Group);
345
                Group.setValues(corrected);
346
                throw Base::RuntimeError("Object can only be in a single Group");
347
            }
348
        }
349
    }
350

351
    if(p == &Group) {
352
        _Conns.clear();
353
        for(auto obj : Group.getValue()) {
354
            if(obj && obj->isAttachedToDocument()) {
355
                //NOLINTBEGIN
356
                _Conns[obj] = obj->signalChanged.connect(std::bind(
357
                            &GroupExtension::slotChildChanged,this,sp::_1, sp::_2));
358
                //NOLINTEND
359
            }
360
        }
361
    }
362

363
    App::Extension::extensionOnChanged(p);
364
}
365

366
void GroupExtension::slotChildChanged(const DocumentObject &obj, const Property &prop) {
367
    if(&prop == &obj.Visibility)
368
        _GroupTouched.touch();
369
}
370

371
bool GroupExtension::extensionGetSubObject(DocumentObject *&ret, const char *subname,
372
        PyObject **pyObj, Base::Matrix4D *mat, bool /*transform*/, int depth) const 
373
{
374
    const char *dot;
375
    if(!subname || *subname==0) {
376
        auto obj = Base::freecad_dynamic_cast<const DocumentObject>(getExtendedContainer());
377
        ret = const_cast<DocumentObject*>(obj);
378
        return true;
379
    }
380
    dot=strchr(subname,'.');
381
    if(!dot)
382
        return false;
383
    if(subname[0]!='$')
384
        ret = Group.findUsingMap(std::string(subname,dot));
385
    else{
386
        std::string name = std::string(subname+1,dot);
387
        for(auto child : Group.getValues()) {
388
            if(name == child->Label.getStrValue()){
389
                ret = child;
390
                break;
391
            }
392
        }
393
    }
394
    if(!ret) 
395
        return false;
396
    return ret->getSubObject(dot+1,pyObj,mat,true,depth+1);
397
}
398

399
bool GroupExtension::extensionGetSubObjects(std::vector<std::string> &ret, int) const {
400
    for(auto obj : Group.getValues()) {
401
        if(obj && obj->isAttachedToDocument())
402
            ret.push_back(std::string(obj->getNameInDocument())+'.');
403
    }
404
    return true;
405
}
406

407
App::DocumentObjectExecReturn *GroupExtension::extensionExecute() {
408
    // This touch property is for propagating changes to upper group
409
    _GroupTouched.touch();
410
    return inherited::extensionExecute();
411
}
412

413
std::vector<App::DocumentObject*> GroupExtension::getAllChildren() const {
414
    std::vector<DocumentObject*> res;
415
    std::set<DocumentObject*> rset;
416
    getAllChildren(res,rset);
417
    return res;
418
}
419

420
void GroupExtension::getAllChildren(std::vector<App::DocumentObject*> &res,
421
        std::set<App::DocumentObject*> &rset) const
422
{
423
    for(auto obj : Group.getValues()) {
424
        if(!obj || !obj->isAttachedToDocument())
425
            continue;
426
        if(!rset.insert(obj).second)
427
            continue;
428
        res.push_back(obj);
429
        auto ext = obj->getExtensionByType<GroupExtension>(true,false);
430
        if(ext) 
431
            ext->getAllChildren(res,rset);
432
    }
433
}
434

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

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

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

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