FreeCAD

Форк
0
/
Selection.cpp 
2475 строк · 85.2 Кб
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
#include "PreCompiled.h"
25

26
#ifndef _PreComp_
27
# include <boost/algorithm/string/predicate.hpp>
28
# include <QApplication>
29
#endif
30

31
#include <App/Application.h>
32
#include <App/Document.h>
33
#include <App/DocumentObject.h>
34
#include <App/DocumentObjectPy.h>
35
#include <App/GeoFeature.h>
36
#include <Base/Console.h>
37
#include <Base/Exception.h>
38
#include <Base/Interpreter.h>
39
#include <Base/Tools.h>
40
#include <Base/PyWrapParseTupleAndKeywords.h>
41
#include <Base/UnitsApi.h>
42

43
#include "Selection.h"
44
#include "SelectionObject.h"
45
#include "Application.h"
46
#include "Document.h"
47
#include "Macro.h"
48
#include "MainWindow.h"
49
#include "MDIView.h"
50
#include "SelectionFilter.h"
51
#include "SelectionFilterPy.h"
52
#include "SelectionObserverPython.h"
53
#include "Tree.h"
54
#include "ViewProviderDocumentObject.h"
55

56

57
FC_LOG_LEVEL_INIT("Selection",false,true,true)
58

59
using namespace Gui;
60
using namespace std;
61
namespace sp = std::placeholders;
62

63
SelectionGateFilterExternal::SelectionGateFilterExternal(const char *docName, const char *objName) {
64
    if(docName) {
65
        DocName = docName;
66
        if(objName)
67
            ObjName = objName;
68
    }
69
}
70

71
bool SelectionGateFilterExternal::allow(App::Document *doc ,App::DocumentObject *obj, const char*) {
72
    if(!doc || !obj)
73
        return true;
74
    if(!DocName.empty() && doc->getName()!=DocName)
75
        notAllowedReason = "Cannot select external object";
76
    else if(!ObjName.empty() && ObjName==obj->getNameInDocument())
77
        notAllowedReason = "Cannot select self";
78
    else
79
        return true;
80
    return false;
81
}
82

83
//////////////////////////////////////////////////////////////////////////////////////////
84

85
SelectionObserver::SelectionObserver(bool attach, ResolveMode resolve)
86
    : resolve(resolve)
87
    , blockedSelection(false)
88
{
89
    if (attach)
90
        attachSelection();
91
}
92

93
SelectionObserver::SelectionObserver(const ViewProviderDocumentObject *vp, bool attach, ResolveMode resolve)
94
    : resolve(resolve)
95
    , blockedSelection(false)
96
{
97
    if (vp && vp->getObject() && vp->getObject()->getDocument()) {
98
        filterDocName = vp->getObject()->getDocument()->getName();
99
        filterObjName = vp->getObject()->getNameInDocument();
100
    }
101
    if (attach)
102
        attachSelection();
103
}
104

105

106
SelectionObserver::~SelectionObserver()
107
{
108
    detachSelection();
109
}
110

111
bool SelectionObserver::blockSelection(bool block)
112
{
113
    bool ok = blockedSelection;
114
    blockedSelection = block;
115
    return ok;
116
}
117

118
bool SelectionObserver::isSelectionBlocked() const
119
{
120
    return blockedSelection;
121
}
122

123
bool SelectionObserver::isSelectionAttached() const
124
{
125
    return connectSelection.connected();
126
}
127

128
void SelectionObserver::attachSelection()
129
{
130
    if (!connectSelection.connected()) {
131
        bool newStyle = (resolve >= ResolveMode::NewStyleElement);
132
        bool oldStyle = (resolve == ResolveMode::OldStyleElement);
133
        auto &signal = newStyle ? Selection().signalSelectionChanged3 :
134
                       oldStyle ? Selection().signalSelectionChanged2 :
135
                                  Selection().signalSelectionChanged  ;
136
        //NOLINTBEGIN
137
        connectSelection = signal.connect(std::bind
138
            (&SelectionObserver::_onSelectionChanged, this, sp::_1));
139
        //NOLINTEND
140

141
        if (!filterDocName.empty()) {
142
            Selection().addSelectionGate(
143
                    new SelectionGateFilterExternal(filterDocName.c_str(),filterObjName.c_str()));
144
        }
145
    }
146
}
147

148
void SelectionObserver::_onSelectionChanged(const SelectionChanges& msg) {
149
    try {
150
        if (blockedSelection)
151
            return;
152
        onSelectionChanged(msg);
153
    } catch (Base::Exception &e) {
154
        e.ReportException();
155
        FC_ERR("Unhandled Base::Exception caught in selection observer: ");
156
    } catch (std::exception &e) {
157
        FC_ERR("Unhandled std::exception caught in selection observer: " << e.what());
158
    } catch (...) {
159
        FC_ERR("Unhandled unknown exception caught in selection observer");
160
    }
161
}
162

163
void SelectionObserver::detachSelection()
164
{
165
    if (connectSelection.connected()) {
166
        connectSelection.disconnect();
167
        if (!filterDocName.empty())
168
            Selection().rmvSelectionGate();
169
    }
170
}
171

172
// -------------------------------------------
173

174
bool SelectionSingleton::hasSelection() const
175
{
176
    return !_SelList.empty();
177
}
178

179
bool SelectionSingleton::hasPreselection() const {
180
    return !CurrentPreselection.Object.getObjectName().empty();
181
}
182

183
std::vector<SelectionSingleton::SelObj> SelectionSingleton::getCompleteSelection(ResolveMode resolve) const
184
{
185
    return getSelection("*", resolve);
186
}
187

188
std::vector<SelectionSingleton::SelObj> SelectionSingleton::getSelection(const char* pDocName, ResolveMode resolve, bool single) const
189
{
190
    std::vector<SelObj> temp;
191
    if (single)
192
        temp.reserve(1);
193
    SelObj tempSelObj;
194

195
    App::Document *pcDoc = nullptr;
196
    if(!pDocName || strcmp(pDocName,"*") != 0) {
197
        pcDoc = getDocument(pDocName);
198
        if (!pcDoc)
199
            return temp;
200
    }
201

202
    std::map<App::DocumentObject*,std::set<std::string> > objMap;
203

204
    for(auto &sel : _SelList) {
205
        if (!sel.pDoc)
206
            continue;
207
        const char *subelement = nullptr;
208
        auto obj = getObjectOfType(sel, App::DocumentObject::getClassTypeId(), resolve, &subelement);
209
        if (!obj || (pcDoc && sel.pObject->getDocument() != pcDoc))
210
            continue;
211

212
        // In case we are resolving objects, make sure no duplicates
213
        if (resolve != ResolveMode::NoResolve && !objMap[obj].insert(std::string(subelement ? subelement : "")).second)
214
            continue;
215

216
        if (single && !temp.empty()) {
217
            temp.clear();
218
            break;
219
        }
220

221
        tempSelObj.DocName  = obj->getDocument()->getName();
222
        tempSelObj.FeatName = obj->getNameInDocument();
223
        tempSelObj.SubName = subelement;
224
        tempSelObj.TypeName = obj->getTypeId().getName();
225
        tempSelObj.pObject  = obj;
226
        tempSelObj.pResolvedObject  = sel.pResolvedObject;
227
        tempSelObj.pDoc     = obj->getDocument();
228
        tempSelObj.x        = sel.x;
229
        tempSelObj.y        = sel.y;
230
        tempSelObj.z        = sel.z;
231

232
        temp.push_back(tempSelObj);
233
    }
234

235
    return temp;
236
}
237

238
bool SelectionSingleton::hasSelection(const char* doc, ResolveMode resolve) const
239
{
240
    App::Document *pcDoc = nullptr;
241
    if (!doc || strcmp(doc,"*") != 0) {
242
        pcDoc = getDocument(doc);
243
        if (!pcDoc)
244
            return false;
245
    }
246
    for(auto &sel : _SelList) {
247
        if (!sel.pDoc)
248
            continue;
249
        auto obj = getObjectOfType(sel, App::DocumentObject::getClassTypeId(), resolve);
250
        if (obj && (!pcDoc || sel.pObject->getDocument()==pcDoc)) {
251
            return true;
252
        }
253
    }
254

255
    return false;
256
}
257

258
bool SelectionSingleton::hasSubSelection(const char* doc, bool subElement) const
259
{
260
    App::Document *pcDoc = nullptr;
261
    if(!doc || strcmp(doc,"*")!=0) {
262
        pcDoc = getDocument(doc);
263
        if (!pcDoc)
264
            return false;
265
    }
266
    for(auto &sel : _SelList) {
267
        if(pcDoc && pcDoc != sel.pDoc)
268
            continue;
269
        if(sel.SubName.empty())
270
            continue;
271
        if(subElement && sel.SubName.back()!='.')
272
            return true;
273
        if(sel.pObject != sel.pResolvedObject)
274
            return true;
275
    }
276

277
    return false;
278
}
279

280
std::vector<SelectionSingleton::SelObj> SelectionSingleton::getPickedList(const char* pDocName) const
281
{
282
    std::vector<SelObj> temp;
283
    SelObj tempSelObj;
284

285
    App::Document *pcDoc = nullptr;
286
    if(!pDocName || strcmp(pDocName,"*") != 0) {
287
        pcDoc = getDocument(pDocName);
288
        if (!pcDoc)
289
            return temp;
290
    }
291

292
    for(std::list<_SelObj>::const_iterator It = _PickedList.begin();It != _PickedList.end();++It) {
293
        if (!pcDoc || It->pDoc == pcDoc) {
294
            tempSelObj.DocName  = It->DocName.c_str();
295
            tempSelObj.FeatName = It->FeatName.c_str();
296
            tempSelObj.SubName  = It->SubName.c_str();
297
            tempSelObj.TypeName = It->TypeName.c_str();
298
            tempSelObj.pObject  = It->pObject;
299
            tempSelObj.pResolvedObject  = It->pResolvedObject;
300
            tempSelObj.pDoc     = It->pDoc;
301
            tempSelObj.x        = It->x;
302
            tempSelObj.y        = It->y;
303
            tempSelObj.z        = It->z;
304
            temp.push_back(tempSelObj);
305
        }
306
    }
307

308
    return temp;
309
}
310

311
std::vector<SelectionObject> SelectionSingleton::getSelectionEx(const char* pDocName, Base::Type typeId,
312
                                                                ResolveMode resolve, bool single) const
313
{
314
    return getObjectList(pDocName, typeId, _SelList, resolve, single);
315
}
316

317
std::vector<SelectionObject> SelectionSingleton::getPickedListEx(const char* pDocName, Base::Type typeId) const
318
{
319
    return getObjectList(pDocName, typeId, _PickedList, ResolveMode::NoResolve);
320
}
321

322
std::vector<SelectionObject> SelectionSingleton::getObjectList(const char* pDocName, Base::Type typeId,
323
                                                               std::list<_SelObj> &objList,
324
                                                               ResolveMode resolve, bool single) const
325
{
326
    std::vector<SelectionObject> temp;
327
    if (single)
328
        temp.reserve(1);
329
    std::map<App::DocumentObject*,size_t> SortMap;
330

331
    // check the type
332
    if (typeId == Base::Type::badType())
333
        return temp;
334

335
    App::Document *pcDoc = nullptr;
336
    if (!pDocName || strcmp(pDocName,"*") != 0) {
337
        pcDoc = getDocument(pDocName);
338
        if (!pcDoc)
339
            return temp;
340
    }
341

342
    for (auto &sel : objList) {
343
        if(!sel.pDoc)
344
            continue;
345
        const char *subelement = nullptr;
346
        auto obj = getObjectOfType(sel,typeId,resolve,&subelement);
347
        if (!obj || (pcDoc && sel.pObject->getDocument() != pcDoc))
348
            continue;
349
        auto it = SortMap.find(obj);
350
        if(it!=SortMap.end()) {
351
            // only add sub-element
352
            if (subelement && *subelement) {
353
                if (resolve != ResolveMode::NoResolve && !temp[it->second]._SubNameSet.insert(subelement).second)
354
                    continue;
355
                temp[it->second].SubNames.emplace_back(subelement);
356
                temp[it->second].SelPoses.emplace_back(sel.x,sel.y,sel.z);
357
            }
358
        }
359
        else {
360
            if (single && !temp.empty()) {
361
                temp.clear();
362
                break;
363
            }
364
            // create a new entry
365
            temp.emplace_back(obj);
366
            if (subelement && *subelement) {
367
                temp.back().SubNames.emplace_back(subelement);
368
                temp.back().SelPoses.emplace_back(sel.x,sel.y,sel.z);
369
                if (resolve != ResolveMode::NoResolve)
370
                    temp.back()._SubNameSet.insert(subelement);
371
            }
372
            SortMap.insert(std::make_pair(obj,temp.size()-1));
373
        }
374
    }
375

376
    return temp;
377
}
378

379
bool SelectionSingleton::needPickedList() const
380
{
381
    return _needPickedList;
382
}
383

384
void SelectionSingleton::enablePickedList(bool enable)
385
{
386
    if(enable != _needPickedList) {
387
        _needPickedList = enable;
388
        _PickedList.clear();
389
        notify(SelectionChanges(SelectionChanges::PickedListChanged));
390
    }
391
}
392

393
void SelectionSingleton::notify(SelectionChanges &&Chng)
394
{
395
    if(Notifying) {
396
        NotificationQueue.push_back(std::move(Chng));
397
        return;
398
    }
399
    Base::FlagToggler<bool> flag(Notifying);
400
    NotificationQueue.push_back(std::move(Chng));
401
    while(!NotificationQueue.empty()) {
402
        const auto &msg = NotificationQueue.front();
403
        bool notify;
404
        switch(msg.Type) {
405
        case SelectionChanges::AddSelection:
406
            notify = isSelected(msg.pDocName, msg.pObjectName, msg.pSubName, ResolveMode::NoResolve);
407
            break;
408
        case SelectionChanges::RmvSelection:
409
            notify = !isSelected(msg.pDocName, msg.pObjectName, msg.pSubName, ResolveMode::NoResolve);
410
            break;
411
        case SelectionChanges::SetPreselect:
412
            notify = CurrentPreselection.Type==SelectionChanges::SetPreselect
413
                && CurrentPreselection.Object == msg.Object;
414
            break;
415
        case SelectionChanges::RmvPreselect:
416
            notify = CurrentPreselection.Type==SelectionChanges::ClrSelection;
417
            break;
418
        default:
419
            notify = true;
420
        }
421
        if(notify) {
422
            Notify(msg);
423
            try {
424
                signalSelectionChanged(msg);
425
            }
426
            catch (const boost::exception&) {
427
                // reported by code analyzers
428
                Base::Console().Warning("notify: Unexpected boost exception\n");
429
            }
430
        }
431
        NotificationQueue.pop_front();
432
    }
433
}
434

435
bool SelectionSingleton::hasPickedList() const
436
{
437
    return !_PickedList.empty();
438
}
439

440
int SelectionSingleton::getAsPropertyLinkSubList(App::PropertyLinkSubList &prop) const
441
{
442
    std::vector<Gui::SelectionObject> sel = this->getSelectionEx();
443
    std::vector<App::DocumentObject*> objs; objs.reserve(sel.size() * 2);
444
    std::vector<std::string> subs; subs.reserve(sel.size()*2);
445
    for (auto & selitem : sel) {
446
        App::DocumentObject* obj = selitem.getObject();
447
        const std::vector<std::string> &subnames = selitem.getSubNames();
448

449
        //whole object is selected
450
        if (subnames.empty()){
451
            objs.push_back(obj);
452
            subs.emplace_back();
453
        }
454
        else {
455
            for (const auto & subname : subnames) {
456
                objs.push_back(obj);
457
                subs.push_back(subname);
458
            }
459
        }
460
    }
461
    assert(objs.size()==subs.size());
462
    prop.setValues(objs, subs);
463
    return objs.size();
464
}
465

466
App::DocumentObject *SelectionSingleton::getObjectOfType(_SelObj &sel, Base::Type typeId,
467
                                                         ResolveMode resolve, const char **subelement)
468
{
469
    auto obj = sel.pObject;
470
    if(!obj || !obj->isAttachedToDocument())
471
        return nullptr;
472
    const char *subname = sel.SubName.c_str();
473
    if (resolve != ResolveMode::NoResolve) {
474
        obj = sel.pResolvedObject;
475
        if (resolve == ResolveMode::NewStyleElement && !sel.elementName.first.empty())
476
            subname = sel.elementName.first.c_str();
477
        else
478
            subname = sel.elementName.second.c_str();
479
    }
480

481
    if (!obj)
482
        return nullptr;
483

484
    if (!obj->isDerivedFrom(typeId) && (resolve != ResolveMode::FollowLink || !obj->getLinkedObject(true)->isDerivedFrom(typeId)))
485
        return nullptr;
486

487
    if (subelement)
488
        *subelement = subname;
489

490
    return obj;
491
}
492

493
vector<App::DocumentObject*> SelectionSingleton::getObjectsOfType(const Base::Type& typeId, const char* pDocName, ResolveMode resolve) const
494
{
495
    std::vector<App::DocumentObject*> temp;
496

497
    App::Document *pcDoc = nullptr;
498
    if (!pDocName || strcmp(pDocName,"*") != 0) {
499
        pcDoc = getDocument(pDocName);
500
        if (!pcDoc)
501
            return temp;
502
    }
503

504
    std::set<App::DocumentObject*> objs;
505
    for(auto &sel : _SelList) {
506
        if(pcDoc && pcDoc!=sel.pDoc) continue;
507
        App::DocumentObject *pObject = getObjectOfType(sel, typeId, resolve);
508
        if (pObject) {
509
            auto ret = objs.insert(pObject);
510
            if(ret.second)
511
                temp.push_back(pObject);
512
        }
513
    }
514

515
    return temp;
516
}
517

518
std::vector<App::DocumentObject*> SelectionSingleton::getObjectsOfType(const char* typeName, const char* pDocName, ResolveMode resolve) const
519
{
520
    Base::Type typeId = Base::Type::fromName(typeName);
521
    if (typeId == Base::Type::badType())
522
        return {};
523
    return getObjectsOfType(typeId, pDocName, resolve);
524
}
525

526
unsigned int SelectionSingleton::countObjectsOfType(const Base::Type& typeId, const char* pDocName, ResolveMode resolve) const
527
{
528
    unsigned int iNbr=0;
529
    App::Document *pcDoc = nullptr;
530
    if(!pDocName || strcmp(pDocName,"*") != 0) {
531
        pcDoc = getDocument(pDocName);
532
        if (!pcDoc)
533
            return 0;
534
    }
535

536
    for (auto &sel : _SelList) {
537
        if((!pcDoc||pcDoc==sel.pDoc) && getObjectOfType(sel, typeId, resolve))
538
            iNbr++;
539
    }
540

541
    return iNbr;
542
}
543

544
unsigned int SelectionSingleton::countObjectsOfType(const char* typeName, const char* pDocName, ResolveMode resolve) const
545
{
546
    Base::Type typeId = Base::Type::fromName(typeName);
547
    if (typeId == Base::Type::badType())
548
        return 0;
549
    return countObjectsOfType(typeId, pDocName, resolve);
550
}
551

552

553
void SelectionSingleton::slotSelectionChanged(const SelectionChanges& msg)
554
{
555
    if(msg.Type == SelectionChanges::SetPreselectSignal ||
556
       msg.Type == SelectionChanges::ShowSelection ||
557
       msg.Type == SelectionChanges::HideSelection)
558
        return;
559

560
    if(!msg.Object.getSubName().empty()) {
561
        auto pParent = msg.Object.getObject();
562
        if(!pParent)
563
            return;
564
        std::pair<std::string,std::string> elementName;
565
        auto &newElementName = elementName.first;
566
        auto &oldElementName = elementName.second;
567
        auto pObject = App::GeoFeature::resolveElement(pParent,msg.pSubName,elementName);
568
        if (!pObject)
569
            return;
570
        SelectionChanges msg2(msg.Type,pObject->getDocument()->getName(),
571
                pObject->getNameInDocument(),
572
                !newElementName.empty()?newElementName.c_str():oldElementName.c_str(),
573
                pObject->getTypeId().getName(), msg.x,msg.y,msg.z);
574

575
        try {
576
            msg2.pOriginalMsg = &msg;
577
            signalSelectionChanged3(msg2);
578

579
            msg2.Object.setSubName(oldElementName.c_str());
580
            msg2.pSubName = msg2.Object.getSubName().c_str();
581
            signalSelectionChanged2(msg2);
582
        }
583
        catch (const boost::exception&) {
584
            // reported by code analyzers
585
            Base::Console().Warning("slotSelectionChanged: Unexpected boost exception\n");
586
        }
587
    }
588
    else {
589
        try {
590
            signalSelectionChanged3(msg);
591
            signalSelectionChanged2(msg);
592
        }
593
        catch (const boost::exception&) {
594
            // reported by code analyzers
595
            Base::Console().Warning("slotSelectionChanged: Unexpected boost exception\n");
596
        }
597
    }
598
}
599

600
int SelectionSingleton::setPreselect(const char* pDocName, const char* pObjectName, const char* pSubName,
601
                                     float x, float y, float z, SelectionChanges::MsgSource signal)
602
{
603
    if (!pDocName || !pObjectName) {
604
        rmvPreselect();
605
        return 0;
606
    }
607
    if (!pSubName) pSubName = "";
608

609
    if (DocName==pDocName && FeatName==pObjectName && SubName==pSubName) {
610
        return -1;
611
    }
612

613
    rmvPreselect();
614

615
    if (ActiveGate && signal != SelectionChanges::MsgSource::Internal) {
616
        App::Document* pDoc = getDocument(pDocName);
617
        if (!pDoc || !pObjectName)
618
            return 0;
619
        std::pair<std::string,std::string> elementName;
620
        auto pObject = pDoc->getObject(pObjectName);
621
        if(!pObject)
622
            return 0;
623

624
        const char *subelement = pSubName;
625
        if (gateResolve != ResolveMode::NoResolve) {
626
            auto &newElementName = elementName.first;
627
            auto &oldElementName = elementName.second;
628
            pObject = App::GeoFeature::resolveElement(pObject,pSubName,elementName);
629
            if (!pObject)
630
                return 0;
631
            if (gateResolve > ResolveMode::OldStyleElement)
632
                subelement = !newElementName.empty() ? newElementName.c_str() : oldElementName.c_str();
633
            else
634
                subelement = oldElementName.c_str();
635
        }
636
        if (!ActiveGate->allow(pObject->getDocument(), pObject, subelement)) {
637
            QString msg;
638
            if (ActiveGate->notAllowedReason.length() > 0){
639
                msg = QObject::tr(ActiveGate->notAllowedReason.c_str());
640
            } else {
641
                msg = QCoreApplication::translate("SelectionFilter","Not allowed:");
642
            }
643
            msg.append(QString::fromLatin1(" %1.%2.%3 ")
644
                  .arg(QString::fromLatin1(pDocName),
645
                       QString::fromLatin1(pObjectName),
646
                       QString::fromLatin1(pSubName)));
647

648
            if (getMainWindow()) {
649
                getMainWindow()->showMessage(msg);
650
                Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView();
651
                mdi->setOverrideCursor(QCursor(Qt::ForbiddenCursor));
652
            }
653
            return 0;
654
        }
655
        Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView();
656
        mdi->restoreOverrideCursor();
657
    }
658

659
    DocName = pDocName;
660
    FeatName= pObjectName;
661
    SubName = pSubName;
662
    hx = x;
663
    hy = y;
664
    hz = z;
665

666
    // set up the change object
667
    SelectionChanges Chng(signal == SelectionChanges::MsgSource::Internal
668
                                  ? SelectionChanges::SetPreselectSignal
669
                                  : SelectionChanges::SetPreselect,
670
            DocName,FeatName,SubName,std::string(),x,y,z,signal);
671

672
    if (Chng.Type==SelectionChanges::SetPreselect) {
673
        CurrentPreselection = Chng;
674
        FC_TRACE("preselect "<<DocName<<'#'<<FeatName<<'.'<<SubName);
675
    }
676
    else {
677
        FC_TRACE("preselect signal "<<DocName<<'#'<<FeatName<<'.'<<SubName);
678
    }
679

680
    notify(Chng);
681

682
    if (signal == SelectionChanges::MsgSource::Internal && !DocName.empty()) {
683
        FC_TRACE("preselect "<<DocName<<'#'<<FeatName<<'.'<<SubName);
684
        Chng.Type = SelectionChanges::SetPreselect;
685
        CurrentPreselection = Chng;
686
        notify(std::move(Chng));
687
    }
688

689
    // It is possible the preselect is removed during notification
690
    return DocName.empty()?0:1;
691
}
692

693
namespace Gui {
694
std::array<std::pair<double, std::string>, 3> schemaTranslatePoint(double x, double y, double z, double precision)
695
{
696
    Base::Quantity mmx(Base::Quantity::MilliMetre);
697
    mmx.setValue(fabs(x) > precision ? x : 0.0);
698
    Base::Quantity mmy(Base::Quantity::MilliMetre);
699
    mmy.setValue(fabs(y) > precision ? y : 0.0);
700
    Base::Quantity mmz(Base::Quantity::MilliMetre);
701
    mmz.setValue(fabs(z) > precision ? z : 0.0);
702

703
    double xfactor, yfactor, zfactor;
704
    QString xunit, yunit, zunit;
705

706
    Base::UnitsApi::schemaTranslate(mmx, xfactor, xunit);
707
    Base::UnitsApi::schemaTranslate(mmy, yfactor, yunit);
708
    Base::UnitsApi::schemaTranslate(mmz, zfactor, zunit);
709

710
    double xuser = fabs(x) > precision ? x / xfactor : 0.0;
711
    double yuser = fabs(y) > precision ? y / yfactor : 0.0;
712
    double zuser = fabs(z) > precision ? z / zfactor : 0.0;
713

714
    std::array<std::pair<double, std::string>, 3> ret = {std::make_pair(xuser, xunit.toUtf8().constBegin()),
715
                                                         std::make_pair(yuser, yunit.toUtf8().constBegin()),
716
                                                         std::make_pair(zuser, zunit.toUtf8().constBegin())};
717
    return ret;
718
}
719

720
QString getPreselectionInfo(const char* documentName,
721
                            const char* objectName,
722
                            const char* subElementName,
723
                            float x, float y, float z,
724
                            double precision)
725
{
726
    auto pts = schemaTranslatePoint(x, y, z, precision);
727

728
    int numberDecimals = std::min(6, Base::UnitsApi::getDecimals());
729

730
    QString message = QStringLiteral("Preselected: %1.%2.%3 (%4 %5, %6 %7, %8 %9)")
731
        .arg(QString::fromUtf8(documentName))
732
        .arg(QString::fromUtf8(objectName))
733
        .arg(QString::fromUtf8(subElementName))
734
        .arg(QString::number(pts[0].first, 'f', numberDecimals))
735
        .arg(QString::fromStdString(pts[0].second))
736
        .arg(QString::number(pts[1].first, 'f', numberDecimals))
737
        .arg(QString::fromStdString(pts[1].second))
738
        .arg(QString::number(pts[2].first, 'f', numberDecimals))
739
        .arg(QString::fromStdString(pts[2].second));
740
    return message;
741
}
742

743
void printPreselectionInfo(const char* documentName,
744
                           const char* objectName,
745
                           const char* subElementName,
746
                           float x, float y, float z,
747
                           double precision)
748
{
749
    if (getMainWindow()) {
750
        QString message = getPreselectionInfo(documentName,
751
                                              objectName,
752
                                              subElementName,
753
                                              x, y, z, precision);
754
        getMainWindow()->showMessage(message);
755
    }
756
}
757
}
758

759
void SelectionSingleton::setPreselectCoord( float x, float y, float z)
760
{
761
    // if nothing is in preselect ignore
762
    if(CurrentPreselection.Object.getObjectName().empty())
763
        return;
764

765
    CurrentPreselection.x = x;
766
    CurrentPreselection.y = y;
767
    CurrentPreselection.z = z;
768

769
    printPreselectionInfo(CurrentPreselection.pDocName,
770
                          CurrentPreselection.pObjectName,
771
                          CurrentPreselection.pSubName,
772
                          x, y, z, 0.0);
773
}
774

775
void SelectionSingleton::rmvPreselect(bool signal)
776
{
777
    if (DocName.empty())
778
        return;
779

780
    if(signal) {
781
        SelectionChanges Chng(SelectionChanges::RmvPreselectSignal,DocName,FeatName,SubName);
782
        notify(std::move(Chng));
783
        return;
784
    }
785

786
    SelectionChanges Chng(SelectionChanges::RmvPreselect,DocName,FeatName,SubName);
787

788
    // reset the current preselection
789
    CurrentPreselection = SelectionChanges();
790

791
    DocName = "";
792
    FeatName= "";
793
    SubName = "";
794
    hx = 0;
795
    hy = 0;
796
    hz = 0;
797

798
    if (ActiveGate && getMainWindow()) {
799
        Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView();
800
        mdi->restoreOverrideCursor();
801
    }
802

803
    FC_TRACE("rmv preselect");
804

805
    // notify observing objects
806
    notify(std::move(Chng));
807
}
808

809
const SelectionChanges &SelectionSingleton::getPreselection() const
810
{
811
    return CurrentPreselection;
812
}
813

814
// add a SelectionGate to control what is selectable
815
void SelectionSingleton::addSelectionGate(Gui::SelectionGate *gate, ResolveMode resolve)
816
{
817
    if (ActiveGate)
818
        rmvSelectionGate();
819

820
    ActiveGate = gate;
821
    gateResolve = resolve;
822
}
823

824
// remove the active SelectionGate
825
void SelectionSingleton::rmvSelectionGate()
826
{
827
    if (ActiveGate) {
828
        delete ActiveGate;
829
        ActiveGate = nullptr;
830

831
        Gui::Document* doc = Gui::Application::Instance->activeDocument();
832
        if (doc) {
833
            // if a document is about to be closed it has no MDI view any more
834
            Gui::MDIView* mdi = doc->getActiveView();
835
            if (mdi)
836
                mdi->restoreOverrideCursor();
837
        }
838
    }
839
}
840

841

842
App::Document* SelectionSingleton::getDocument(const char* pDocName) const
843
{
844
    if (pDocName && pDocName[0])
845
        return App::GetApplication().getDocument(pDocName);
846
    else
847
        return App::GetApplication().getActiveDocument();
848
}
849

850
int SelectionSingleton::disableCommandLog() {
851
    if(!logDisabled)
852
        logHasSelection = hasSelection();
853
    return ++logDisabled;
854
}
855

856
int SelectionSingleton::enableCommandLog(bool silent) {
857
    --logDisabled;
858
    if(!logDisabled && !silent) {
859
        auto manager = Application::Instance->macroManager();
860
        if(!hasSelection()) {
861
            if(logHasSelection)
862
                manager->addLine(MacroManager::Cmt, "Gui.Selection.clearSelection()");
863
        }else{
864
            for(auto &sel : _SelList)
865
                sel.log();
866
        }
867
    }
868
    return logDisabled;
869
}
870

871
void SelectionSingleton::_SelObj::log(bool remove, bool clearPreselect) {
872
    if(logged && !remove)
873
        return;
874
    logged = true;
875
    std::ostringstream ss;
876
    ss << "Gui.Selection." << (remove?"removeSelection":"addSelection")
877
        << "('" << DocName  << "','" << FeatName << "'";
878
    if(!SubName.empty()) {
879
        if(!elementName.second.empty() && !elementName.first.empty())
880
            ss << ",'" << SubName.substr(0,SubName.size()-elementName.first.size())
881
                << elementName.second << "'";
882
        else
883
            ss << ",'" << SubName << "'";
884
    }
885
    if(!remove && (x || y || z || !clearPreselect)) {
886
        if(SubName.empty())
887
            ss << ",''";
888
        ss << ',' << x << ',' << y << ',' << z;
889
        if(!clearPreselect)
890
            ss << ",False";
891
    }
892
    ss << ')';
893
    Application::Instance->macroManager()->addLine(MacroManager::Cmt, ss.str().c_str());
894
}
895

896
bool SelectionSingleton::addSelection(const char* pDocName, const char* pObjectName,
897
        const char* pSubName, float x, float y, float z,
898
        const std::vector<SelObj> *pickedList, bool clearPreselect)
899
{
900
    if(pickedList) {
901
        _PickedList.clear();
902
        for(const auto &sel : *pickedList) {
903
            _PickedList.emplace_back();
904
            auto &s = _PickedList.back();
905
            s.DocName = sel.DocName;
906
            s.FeatName = sel.FeatName;
907
            s.SubName = sel.SubName;
908
            s.TypeName = sel.TypeName;
909
            s.pObject = sel.pObject;
910
            s.pDoc = sel.pDoc;
911
            s.x = sel.x;
912
            s.y = sel.y;
913
            s.z = sel.z;
914
        }
915
        notify(SelectionChanges(SelectionChanges::PickedListChanged));
916
    }
917

918
    _SelObj temp;
919
    int ret = checkSelection(pDocName, pObjectName, pSubName, ResolveMode::NoResolve, temp);
920
    if (ret!=0)
921
        return false;
922

923
    temp.x        = x;
924
    temp.y        = y;
925
    temp.z        = z;
926

927
    // check for a Selection Gate
928
    if (ActiveGate) {
929
        const char *subelement = nullptr;
930
        auto pObject = getObjectOfType(temp,App::DocumentObject::getClassTypeId(),gateResolve,&subelement);
931
        if (!ActiveGate->allow(pObject?pObject->getDocument():temp.pDoc,pObject,subelement)) {
932
            if (getMainWindow()) {
933
                QString msg;
934
                if (ActiveGate->notAllowedReason.length() > 0) {
935
                    msg = QObject::tr(ActiveGate->notAllowedReason.c_str());
936
                } else {
937
                    msg = QCoreApplication::translate("SelectionFilter","Selection not allowed by filter");
938
                }
939
                getMainWindow()->showMessage(msg);
940
                Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView();
941
                mdi->setOverrideCursor(Qt::ForbiddenCursor);
942
            }
943
            ActiveGate->notAllowedReason.clear();
944
            QApplication::beep();
945
            return false;
946
        }
947
    }
948

949
    if(!logDisabled)
950
        temp.log(false,clearPreselect);
951

952
    _SelList.push_back(temp);
953
    _SelStackForward.clear();
954

955
    if(clearPreselect)
956
        rmvPreselect();
957

958
    SelectionChanges Chng(SelectionChanges::AddSelection,
959
            temp.DocName,temp.FeatName,temp.SubName,temp.TypeName, x,y,z);
960

961
    FC_LOG("Add Selection "<<Chng.pDocName<<'#'<<Chng.pObjectName<<'.'<<Chng.pSubName
962
            << " (" << x << ", " << y << ", " << z << ')');
963

964
    notify(std::move(Chng));
965

966
    getMainWindow()->updateActions();
967

968
    rmvPreselect(true);
969

970
    // There is a possibility that some observer removes or clears selection
971
    // inside signal handler, hence the check here
972
    return isSelected(temp.DocName.c_str(),temp.FeatName.c_str(), temp.SubName.c_str());
973
}
974

975
void SelectionSingleton::selStackPush(bool clearForward, bool overwrite) {
976
    static int stackSize;
977
    if(!stackSize) {
978
        stackSize = App::GetApplication().GetParameterGroupByPath
979
                ("User parameter:BaseApp/Preferences/View")->GetInt("SelectionStackSize",100);
980
    }
981
    if(clearForward)
982
        _SelStackForward.clear();
983
    if(_SelList.empty())
984
        return;
985
    if((int)_SelStackBack.size() >= stackSize)
986
        _SelStackBack.pop_front();
987
    SelStackItem item;
988
    for(auto &sel : _SelList)
989
        item.emplace(sel.DocName.c_str(),sel.FeatName.c_str(),sel.SubName.c_str());
990
    if(!_SelStackBack.empty() && _SelStackBack.back()==item)
991
        return;
992
    if(!overwrite || _SelStackBack.empty())
993
        _SelStackBack.emplace_back();
994
    _SelStackBack.back() = std::move(item);
995
}
996

997
void SelectionSingleton::selStackGoBack(int count) {
998
    if((int)_SelStackBack.size()<count)
999
        count = _SelStackBack.size();
1000
    if(count<=0)
1001
        return;
1002
    if(!_SelList.empty()) {
1003
        selStackPush(false,true);
1004
        clearCompleteSelection();
1005
    } else
1006
        --count;
1007
    for(int i=0;i<count;++i) {
1008
        _SelStackForward.push_front(std::move(_SelStackBack.back()));
1009
        _SelStackBack.pop_back();
1010
    }
1011
    std::deque<SelStackItem> tmpStack;
1012
    _SelStackForward.swap(tmpStack);
1013
    while(!_SelStackBack.empty()) {
1014
        bool found = false;
1015
        for(auto &sobjT : _SelStackBack.back()) {
1016
            if(sobjT.getSubObject()) {
1017
                addSelection(sobjT.getDocumentName().c_str(),
1018
                             sobjT.getObjectName().c_str(),
1019
                             sobjT.getSubName().c_str());
1020
                found = true;
1021
            }
1022
        }
1023
        if(found)
1024
            break;
1025
        tmpStack.push_front(std::move(_SelStackBack.back()));
1026
        _SelStackBack.pop_back();
1027
    }
1028
    _SelStackForward = std::move(tmpStack);
1029
    getMainWindow()->updateActions();
1030
}
1031

1032
void SelectionSingleton::selStackGoForward(int count) {
1033
    if((int)_SelStackForward.size()<count)
1034
        count = _SelStackForward.size();
1035
    if(count<=0)
1036
        return;
1037
    if(!_SelList.empty()) {
1038
        selStackPush(false,true);
1039
        clearCompleteSelection();
1040
    }
1041
    for(int i=0;i<count;++i) {
1042
        _SelStackBack.push_back(_SelStackForward.front());
1043
        _SelStackForward.pop_front();
1044
    }
1045
    std::deque<SelStackItem> tmpStack;
1046
    _SelStackForward.swap(tmpStack);
1047
    while(true) {
1048
        bool found = false;
1049
        for(auto &sobjT : _SelStackBack.back()) {
1050
            if(sobjT.getSubObject()) {
1051
                addSelection(sobjT.getDocumentName().c_str(),
1052
                             sobjT.getObjectName().c_str(),
1053
                             sobjT.getSubName().c_str());
1054
                found = true;
1055
            }
1056
        }
1057
        if(found || tmpStack.empty())
1058
            break;
1059
        _SelStackBack.push_back(tmpStack.front());
1060
        tmpStack.pop_front();
1061
    }
1062
    _SelStackForward = std::move(tmpStack);
1063
    getMainWindow()->updateActions();
1064
}
1065

1066
std::vector<SelectionObject> SelectionSingleton::selStackGet(const char* pDocName, ResolveMode resolve, int index) const
1067
{
1068
    const SelStackItem *item = nullptr;
1069
    if (index >= 0) {
1070
        if(index >= (int)_SelStackBack.size())
1071
            return {};
1072
        item = &_SelStackBack[_SelStackBack.size()-1-index];
1073
    }
1074
    else {
1075
        index = -index-1;
1076
        if(index>=(int)_SelStackForward.size())
1077
            return {};
1078
        item = &_SelStackBack[_SelStackForward.size()-1-index];
1079
    }
1080

1081
    std::list<_SelObj> selList;
1082
    for(auto &sobjT : *item) {
1083
        _SelObj sel;
1084
        if(checkSelection(sobjT.getDocumentName().c_str(),
1085
                          sobjT.getObjectName().c_str(),
1086
                          sobjT.getSubName().c_str(),
1087
                          ResolveMode::NoResolve,
1088
                          sel,
1089
                          &selList)==0)
1090
        {
1091
            selList.push_back(sel);
1092
        }
1093
    }
1094

1095
    return getObjectList(pDocName,App::DocumentObject::getClassTypeId(), selList, resolve);
1096
}
1097

1098
bool SelectionSingleton::addSelections(const char* pDocName, const char* pObjectName, const std::vector<std::string>& pSubNames)
1099
{
1100
    if(!_PickedList.empty()) {
1101
        _PickedList.clear();
1102
        notify(SelectionChanges(SelectionChanges::PickedListChanged));
1103
    }
1104

1105
    bool update = false;
1106
    for(const auto & pSubName : pSubNames) {
1107
        _SelObj temp;
1108
        int ret = checkSelection(pDocName, pObjectName, pSubName.c_str(), ResolveMode::NoResolve, temp);
1109
        if (ret!=0)
1110
            continue;
1111

1112
        temp.x        = 0;
1113
        temp.y        = 0;
1114
        temp.z        = 0;
1115

1116
        _SelList.push_back(temp);
1117
        _SelStackForward.clear();
1118

1119
        SelectionChanges Chng(SelectionChanges::AddSelection,
1120
                temp.DocName,temp.FeatName,temp.SubName,temp.TypeName);
1121

1122
        FC_LOG("Add Selection "<<Chng.pDocName<<'#'<<Chng.pObjectName<<'.'<<Chng.pSubName);
1123

1124
        notify(std::move(Chng));
1125
        update = true;
1126
    }
1127

1128
    if(update)
1129
        getMainWindow()->updateActions();
1130
    return true;
1131
}
1132

1133
bool SelectionSingleton::updateSelection(bool show, const char* pDocName,
1134
                            const char* pObjectName, const char* pSubName)
1135
{
1136
    if(!pDocName || !pObjectName)
1137
        return false;
1138
    if(!pSubName)
1139
        pSubName = "";
1140
    if(DocName==pDocName && FeatName==pObjectName && SubName==pSubName) {
1141
        if(show) {
1142
            FC_TRACE("preselect signal");
1143
            notify(SelectionChanges(SelectionChanges::SetPreselectSignal,DocName,FeatName,SubName));
1144
        }else
1145
            rmvPreselect();
1146
    }
1147
    auto pDoc = getDocument(pDocName);
1148
    if(!pDoc)
1149
        return false;
1150
    auto pObject = pDoc->getObject(pObjectName);
1151
    if(!pObject)
1152
        return false;
1153
    if (!isSelected(pObject, pSubName, ResolveMode::NoResolve))
1154
        return false;
1155

1156
    SelectionChanges Chng(show?SelectionChanges::ShowSelection:SelectionChanges::HideSelection,
1157
            pDocName,pObjectName,pSubName,pObject->getTypeId().getName());
1158

1159
    FC_LOG("Update Selection "<<Chng.pDocName << '#' << Chng.pObjectName << '.' <<Chng.pSubName);
1160

1161
    notify(std::move(Chng));
1162

1163
    return true;
1164
}
1165

1166
bool SelectionSingleton::addSelection(const SelectionObject& obj, bool clearPreselect)
1167
{
1168
    const std::vector<std::string>& subNames = obj.getSubNames();
1169
    const std::vector<Base::Vector3d> points = obj.getPickedPoints();
1170
    if (!subNames.empty() && subNames.size() == points.size()) {
1171
        bool ok = true;
1172
        for (std::size_t i=0; i<subNames.size(); i++) {
1173
            const std::string& name = subNames[i];
1174
            const Base::Vector3d& pnt = points[i];
1175
            ok &= addSelection(obj.getDocName(), obj.getFeatName(), name.c_str(),
1176
                               static_cast<float>(pnt.x),
1177
                               static_cast<float>(pnt.y),
1178
                               static_cast<float>(pnt.z),nullptr,clearPreselect);
1179
        }
1180
        return ok;
1181
    }
1182
    else if (!subNames.empty()) {
1183
        bool ok = true;
1184
        for (const std::string& name : subNames) {
1185
            ok &= addSelection(obj.getDocName(), obj.getFeatName(), name.c_str());
1186
        }
1187
        return ok;
1188
    }
1189
    else {
1190
        return addSelection(obj.getDocName(), obj.getFeatName());
1191
    }
1192
}
1193

1194

1195
void SelectionSingleton::rmvSelection(const char* pDocName, const char* pObjectName, const char* pSubName,
1196
        const std::vector<SelObj> *pickedList)
1197
{
1198
    if(pickedList) {
1199
        _PickedList.clear();
1200
        for(const auto &sel : *pickedList) {
1201
            _PickedList.emplace_back();
1202
            auto &s = _PickedList.back();
1203
            s.DocName = sel.DocName;
1204
            s.FeatName = sel.FeatName;
1205
            s.SubName = sel.SubName;
1206
            s.TypeName = sel.TypeName;
1207
            s.pObject = sel.pObject;
1208
            s.pDoc = sel.pDoc;
1209
            s.x = sel.x;
1210
            s.y = sel.y;
1211
            s.z = sel.z;
1212
        }
1213
        notify(SelectionChanges(SelectionChanges::PickedListChanged));
1214
    }
1215

1216
    if(!pDocName)
1217
        return;
1218

1219
    _SelObj temp;
1220
    int ret = checkSelection(pDocName, pObjectName, pSubName, ResolveMode::NoResolve, temp);
1221
    if (ret<0)
1222
        return;
1223

1224
    std::vector<SelectionChanges> changes;
1225
    for(auto It=_SelList.begin(),ItNext=It;It!=_SelList.end();It=ItNext) {
1226
        ++ItNext;
1227
        if(It->DocName!=temp.DocName || It->FeatName!=temp.FeatName)
1228
            continue;
1229
        // if no subname is specified, remove all subobjects of the matching object
1230
        if(!temp.SubName.empty()) {
1231
            // otherwise, match subojects with common prefix, separated by '.'
1232
            if(!boost::starts_with(It->SubName,temp.SubName) ||
1233
               (It->SubName.length()!=temp.SubName.length() && It->SubName[temp.SubName.length()-1]!='.'))
1234
                continue;
1235
        }
1236

1237
        It->log(true);
1238

1239
        changes.emplace_back(SelectionChanges::RmvSelection,
1240
                It->DocName,It->FeatName,It->SubName,It->TypeName);
1241

1242
        // destroy the _SelObj item
1243
        _SelList.erase(It);
1244
    }
1245

1246
    // NOTE: It can happen that there are nested calls of rmvSelection()
1247
    // so that it's not safe to invoke the notifications inside the loop
1248
    // as this can invalidate the iterators and thus leads to undefined
1249
    // behaviour.
1250
    // So, the notification is done after the loop, see also #0003469
1251
    if(!changes.empty()) {
1252
        for(auto &Chng : changes) {
1253
            FC_LOG("Rmv Selection "<<Chng.pDocName<<'#'<<Chng.pObjectName<<'.'<<Chng.pSubName);
1254
            notify(std::move(Chng));
1255
        }
1256
        getMainWindow()->updateActions();
1257
    }
1258
}
1259

1260
struct SelInfo {
1261
    std::string DocName;
1262
    std::string FeatName;
1263
    std::string SubName;
1264
    SelInfo(const std::string &docName,
1265
            const std::string &featName,
1266
            const std::string &subName)
1267
        :DocName(docName)
1268
        ,FeatName(featName)
1269
        ,SubName(subName)
1270
    {}
1271
};
1272

1273
void SelectionSingleton::setVisible(VisibleState vis) {
1274
    std::set<std::pair<App::DocumentObject*,App::DocumentObject*> > filter;
1275
    int visible;
1276
    switch(vis) {
1277
    case VisShow:
1278
        visible = 1;
1279
        break;
1280
    case VisToggle:
1281
        visible = -1;
1282
        break;
1283
    default:
1284
        visible = 0;
1285
    }
1286

1287
    // Copy the selection in case it changes during this function
1288
    std::vector<SelInfo> sels;
1289
    sels.reserve(_SelList.size());
1290
    for(auto &sel : _SelList) {
1291
        if(sel.DocName.empty() || sel.FeatName.empty() || !sel.pObject)
1292
            continue;
1293
        sels.emplace_back(sel.DocName,sel.FeatName,sel.SubName);
1294
    }
1295

1296
    for(auto &sel : sels) {
1297
        App::Document *doc = App::GetApplication().getDocument(sel.DocName.c_str());
1298
        if(!doc) continue;
1299
        App::DocumentObject *obj = doc->getObject(sel.FeatName.c_str());
1300
        if(!obj) continue;
1301

1302
        // get parent object
1303
        App::DocumentObject *parent = nullptr;
1304
        std::string elementName;
1305
        obj = obj->resolve(sel.SubName.c_str(),&parent,&elementName);
1306
        if (!obj || !obj->isAttachedToDocument() || (parent && !parent->isAttachedToDocument()))
1307
            continue;
1308
        // try call parent object's setElementVisible
1309
        if (parent) {
1310
            // prevent setting the same object visibility more than once
1311
            if (!filter.insert(std::make_pair(obj,parent)).second)
1312
                continue;
1313
            int visElement = parent->isElementVisible(elementName.c_str());
1314
            if (visElement >= 0) {
1315
                if (visElement > 0)
1316
                    visElement = 1;
1317
                if (visible >= 0) {
1318
                    if (visElement == visible)
1319
                        continue;
1320
                    visElement = visible;
1321
                }
1322
                else {
1323
                    visElement = !visElement;
1324
                }
1325

1326
                if (!visElement)
1327
                    updateSelection(false,sel.DocName.c_str(),sel.FeatName.c_str(), sel.SubName.c_str());
1328
                parent->setElementVisible(elementName.c_str(), visElement ? true : false);
1329
                if (visElement)
1330
                    updateSelection(true,sel.DocName.c_str(),sel.FeatName.c_str(), sel.SubName.c_str());
1331
                continue;
1332
            }
1333

1334
            // Fall back to direct object visibility setting
1335
        }
1336
        if(!filter.insert(std::make_pair(obj,static_cast<App::DocumentObject*>(nullptr))).second){
1337
            continue;
1338
        }
1339

1340
        auto vp = Application::Instance->getViewProvider(obj);
1341

1342
        if(vp) {
1343
            bool visObject;
1344
            if(visible>=0)
1345
                visObject = visible ? true : false;
1346
            else
1347
                visObject = !vp->isShow();
1348

1349
            if(visObject) {
1350
                vp->show();
1351
                updateSelection(visObject,sel.DocName.c_str(),sel.FeatName.c_str(), sel.SubName.c_str());
1352
            } else {
1353
                updateSelection(visObject,sel.DocName.c_str(),sel.FeatName.c_str(), sel.SubName.c_str());
1354
                vp->hide();
1355
            }
1356
        }
1357
    }
1358
}
1359

1360
void SelectionSingleton::setSelection(const char* pDocName, const std::vector<App::DocumentObject*>& sel)
1361
{
1362
    App::Document *pcDoc;
1363
    pcDoc = getDocument(pDocName);
1364
    if (!pcDoc)
1365
        return;
1366

1367
    if(!_PickedList.empty()) {
1368
        _PickedList.clear();
1369
        notify(SelectionChanges(SelectionChanges::PickedListChanged));
1370
    }
1371

1372
    bool touched = false;
1373
    for(auto obj : sel) {
1374
        if(!obj || !obj->isAttachedToDocument())
1375
            continue;
1376
        _SelObj temp;
1377
        int ret = checkSelection(pDocName,obj->getNameInDocument(), nullptr, ResolveMode::NoResolve, temp);
1378
        if (ret!=0)
1379
            continue;
1380
        touched = true;
1381
        _SelList.push_back(temp);
1382
    }
1383

1384
    if(touched) {
1385
        _SelStackForward.clear();
1386
        notify(SelectionChanges(SelectionChanges::SetSelection, pDocName));
1387
        getMainWindow()->updateActions();
1388
    }
1389
}
1390

1391
void SelectionSingleton::clearSelection(const char* pDocName, bool clearPreSelect)
1392
{
1393
    // Because the introduction of external editing, it is best to make
1394
    // clearSelection(0) behave as clearCompleteSelection(), which is the same
1395
    // behavior of python Selection.clearSelection(None)
1396
    if (!pDocName || !pDocName[0] || strcmp(pDocName,"*")==0) {
1397
        clearCompleteSelection(clearPreSelect);
1398
        return;
1399
    }
1400

1401
    if (!_PickedList.empty()) {
1402
        _PickedList.clear();
1403
        notify(SelectionChanges(SelectionChanges::PickedListChanged));
1404
    }
1405

1406
    App::Document* pDoc;
1407
    pDoc = getDocument(pDocName);
1408
    if (pDoc) {
1409
        std::string docName = pDocName;
1410
        if (clearPreSelect && DocName == docName)
1411
            rmvPreselect();
1412

1413
        bool touched = false;
1414
        for (auto it=_SelList.begin();it!=_SelList.end();) {
1415
            if (it->DocName == docName) {
1416
                touched = true;
1417
                it = _SelList.erase(it);
1418
            }
1419
            else {
1420
                ++it;
1421
            }
1422
        }
1423

1424
        if (!touched)
1425
            return;
1426

1427
        if (!logDisabled) {
1428
            std::ostringstream ss;
1429
            ss << "Gui.Selection.clearSelection('" << docName << "'";
1430
            if (!clearPreSelect)
1431
                ss << ", False";
1432
            ss << ')';
1433
            Application::Instance->macroManager()->addLine(MacroManager::Cmt,ss.str().c_str());
1434
        }
1435

1436
        notify(SelectionChanges(SelectionChanges::ClrSelection,docName.c_str()));
1437

1438
        getMainWindow()->updateActions();
1439
    }
1440
}
1441

1442
void SelectionSingleton::clearCompleteSelection(bool clearPreSelect)
1443
{
1444
    if(!_PickedList.empty()) {
1445
        _PickedList.clear();
1446
        notify(SelectionChanges(SelectionChanges::PickedListChanged));
1447
    }
1448

1449
    if(clearPreSelect)
1450
        rmvPreselect();
1451

1452
    if(_SelList.empty())
1453
        return;
1454

1455
    if(!logDisabled)
1456
        Application::Instance->macroManager()->addLine(MacroManager::Cmt,
1457
                clearPreSelect?"Gui.Selection.clearSelection()"
1458
                              :"Gui.Selection.clearSelection(False)");
1459

1460
    _SelList.clear();
1461

1462
    SelectionChanges Chng(SelectionChanges::ClrSelection);
1463

1464
    FC_LOG("Clear selection");
1465

1466
    notify(std::move(Chng));
1467
    getMainWindow()->updateActions();
1468
}
1469

1470
bool SelectionSingleton::isSelected(const char* pDocName, const char* pObjectName,
1471
                                    const char* pSubName, ResolveMode resolve) const
1472
{
1473
    _SelObj sel;
1474
    return checkSelection(pDocName, pObjectName, pSubName, resolve, sel, &_SelList) > 0;
1475
}
1476

1477
bool SelectionSingleton::isSelected(App::DocumentObject* pObject, const char* pSubName, ResolveMode resolve) const
1478
{
1479
    if (!pObject || !pObject->isAttachedToDocument() || !pObject->getDocument())
1480
        return false;
1481
    _SelObj sel;
1482
    return checkSelection(pObject->getDocument()->getName(),
1483
            pObject->getNameInDocument(), pSubName, resolve, sel, &_SelList) > 0;
1484
}
1485

1486
int SelectionSingleton::checkSelection(const char *pDocName, const char *pObjectName, const char *pSubName,
1487
                                       ResolveMode resolve, _SelObj &sel, const std::list<_SelObj> *selList) const
1488
{
1489
    sel.pDoc = getDocument(pDocName);
1490
    if(!sel.pDoc) {
1491
        if(!selList)
1492
            FC_ERR("Cannot find document");
1493
        return -1;
1494
    }
1495
    pDocName = sel.pDoc->getName();
1496
    sel.DocName = pDocName;
1497

1498
    if(pObjectName)
1499
        sel.pObject = sel.pDoc->getObject(pObjectName);
1500
    else
1501
        sel.pObject = nullptr;
1502
    if (!sel.pObject) {
1503
        if(!selList)
1504
            FC_ERR("Object not found");
1505
        return -1;
1506
    }
1507
    if (sel.pObject->testStatus(App::ObjectStatus::Remove))
1508
        return -1;
1509
    if (pSubName)
1510
       sel.SubName = pSubName;
1511
    if (resolve == ResolveMode::NoResolve)
1512
        TreeWidget::checkTopParent(sel.pObject,sel.SubName);
1513
    pSubName = !sel.SubName.empty()?sel.SubName.c_str():nullptr;
1514
    sel.FeatName = sel.pObject->getNameInDocument();
1515
    sel.TypeName = sel.pObject->getTypeId().getName();
1516
    const char *element = nullptr;
1517
    sel.pResolvedObject = App::GeoFeature::resolveElement(sel.pObject,
1518
            pSubName,sel.elementName,false,App::GeoFeature::Normal,nullptr,&element);
1519
    if(!sel.pResolvedObject) {
1520
        if(!selList)
1521
            FC_ERR("Sub-object " << sel.DocName << '#' << sel.FeatName << '.' << sel.SubName << " not found");
1522
        return -1;
1523
    }
1524
    if(sel.pResolvedObject->testStatus(App::ObjectStatus::Remove))
1525
        return -1;
1526
    std::string subname;
1527
    std::string prefix;
1528
    if(pSubName && element) {
1529
        prefix = std::string(pSubName, element-pSubName);
1530
        if(!sel.elementName.first.empty()) {
1531
            // make sure the selected sub name is a new style if available
1532
            subname = prefix + sel.elementName.first;
1533
            pSubName = subname.c_str();
1534
            sel.SubName = subname;
1535
        }
1536
    }
1537
    if(!selList)
1538
        selList = &_SelList;
1539

1540
    if(!pSubName)
1541
        pSubName = "";
1542

1543
    for (auto &s : *selList) {
1544
        if (s.DocName==pDocName && s.FeatName==sel.FeatName) {
1545
            if(s.SubName==pSubName)
1546
                return 1;
1547
            if (resolve > ResolveMode::OldStyleElement && boost::starts_with(s.SubName,prefix))
1548
                return 1;
1549
        }
1550
    }
1551
    if (resolve == ResolveMode::OldStyleElement) {
1552
        for(auto &s : *selList) {
1553
            if(s.pResolvedObject != sel.pResolvedObject)
1554
                continue;
1555
            if(!pSubName[0])
1556
                return 1;
1557
            if (!s.elementName.first.empty()) {
1558
                if (s.elementName.first == sel.elementName.first)
1559
                    return 1;
1560
            }
1561
            else if(s.SubName == sel.elementName.second)
1562
                return 1;
1563
        }
1564
    }
1565
    return 0;
1566
}
1567

1568
const char *SelectionSingleton::getSelectedElement(App::DocumentObject *obj, const char* pSubName) const
1569
{
1570
    if (!obj)
1571
        return nullptr;
1572

1573
    for(list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) {
1574
        if (It->pObject == obj) {
1575
            auto len = It->SubName.length();
1576
            if(!len)
1577
                return "";
1578
            if (pSubName && strncmp(pSubName,It->SubName.c_str(),It->SubName.length())==0){
1579
                if(pSubName[len]==0 || pSubName[len-1] == '.')
1580
                    return It->SubName.c_str();
1581
            }
1582
        }
1583
    }
1584
    return nullptr;
1585
}
1586

1587
void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj)
1588
{
1589
    if(!Obj.isAttachedToDocument())
1590
        return;
1591

1592
    // For safety reason, don't bother checking
1593
    rmvPreselect();
1594

1595
    // Remove also from the selection, if selected
1596
    // We don't walk down the hierarchy for each selection, so there may be stray selection
1597
    std::vector<SelectionChanges> changes;
1598
    for(auto it=_SelList.begin(),itNext=it;it!=_SelList.end();it=itNext) {
1599
        ++itNext;
1600
        if(it->pResolvedObject == &Obj || it->pObject==&Obj) {
1601
            changes.emplace_back(SelectionChanges::RmvSelection,
1602
                    it->DocName,it->FeatName,it->SubName,it->TypeName);
1603
            _SelList.erase(it);
1604
        }
1605
    }
1606
    if(!changes.empty()) {
1607
        for(auto &Chng : changes) {
1608
            FC_LOG("Rmv Selection "<<Chng.pDocName<<'#'<<Chng.pObjectName<<'.'<<Chng.pSubName);
1609
            notify(std::move(Chng));
1610
        }
1611
        getMainWindow()->updateActions();
1612
    }
1613

1614
    if(!_PickedList.empty()) {
1615
        bool changed = false;
1616
        for(auto it=_PickedList.begin(),itNext=it;it!=_PickedList.end();it=itNext) {
1617
            ++itNext;
1618
            auto &sel = *it;
1619
            if(sel.DocName == Obj.getDocument()->getName() &&
1620
               sel.FeatName == Obj.getNameInDocument())
1621
            {
1622
                changed = true;
1623
                _PickedList.erase(it);
1624
            }
1625
        }
1626
        if(changed)
1627
            notify(SelectionChanges(SelectionChanges::PickedListChanged));
1628
    }
1629
}
1630

1631
void SelectionSingleton::setSelectionStyle(SelectionStyle selStyle)
1632
{
1633
    selectionStyle = selStyle;
1634
}
1635

1636
SelectionSingleton::SelectionStyle SelectionSingleton::getSelectionStyle()
1637
{
1638
    return selectionStyle;
1639
}
1640

1641
//**************************************************************************
1642
// Construction/Destruction
1643

1644
/**
1645
 * A constructor.
1646
 * A more elaborate description of the constructor.
1647
 */
1648
SelectionSingleton::SelectionSingleton() :
1649
    CurrentPreselection(SelectionChanges::ClrSelection),
1650
    selectionStyle(SelectionStyle::NormalSelection)
1651
{
1652
    hx = 0;
1653
    hy = 0;
1654
    hz = 0;
1655
    ActiveGate = nullptr;
1656
    gateResolve = ResolveMode::OldStyleElement;
1657
    //NOLINTBEGIN
1658
    App::GetApplication().signalDeletedObject.connect(std::bind(&Gui::SelectionSingleton::slotDeletedObject, this, sp::_1));
1659
    signalSelectionChanged.connect(std::bind(&Gui::SelectionSingleton::slotSelectionChanged, this, sp::_1));
1660
    //NOLINTEND
1661
}
1662

1663
/**
1664
 * A destructor.
1665
 * A more elaborate description of the destructor.
1666
 */
1667
SelectionSingleton::~SelectionSingleton() = default;
1668

1669
SelectionSingleton* SelectionSingleton::_pcSingleton = nullptr;
1670

1671
SelectionSingleton& SelectionSingleton::instance()
1672
{
1673
    if (!_pcSingleton)
1674
        _pcSingleton = new SelectionSingleton;
1675
    return *_pcSingleton;
1676
}
1677

1678
void SelectionSingleton::destruct ()
1679
{
1680
    if (_pcSingleton)
1681
        delete _pcSingleton;
1682
    _pcSingleton = nullptr;
1683
}
1684

1685
//**************************************************************************
1686
// Python stuff
1687

1688
// SelectionSingleton Methods  // Methods structure
1689
PyMethodDef SelectionSingleton::Methods[] = {
1690
    {"addSelection",         (PyCFunction) SelectionSingleton::sAddSelection, METH_VARARGS,
1691
     "addSelection(docName, objName, subName, x=0, y=0, z=0, clear=True) -> None\n"
1692
     "addSelection(obj, subName, x=0, y=0, z=0, clear=True) -> None\n"
1693
     "addSelection(obj, subNames, clear=True) -> None\n"
1694
     "\n"
1695
     "Add an object to the selection.\n"
1696
     "\n"
1697
     "docName : str\n    Name of the `App.Document`.\n"
1698
     "objName : str\n    Name of the `App.DocumentObject` to add.\n"
1699
     "obj : App.DocumentObject\n    Object to add.\n"
1700
     "subName : str\n    Subelement name.\n"
1701
     "x : float\n    Coordinate `x` of the point to pick.\n"
1702
     "y : float\n    Coordinate `y` of the point to pick.\n"
1703
     "z : float\n    Coordinate `z` of the point to pick.\n"
1704
     "subNames : list of str\n    List of subelement names.\n"
1705
     "clear : bool\n    Clear preselection."},
1706
    {"updateSelection",      (PyCFunction) SelectionSingleton::sUpdateSelection, METH_VARARGS,
1707
     "updateSelection(show, obj, subName) -> None\n"
1708
     "\n"
1709
     "Update an object in the selection.\n"
1710
     "\n"
1711
     "show : bool\n    Show or hide the selection.\n"
1712
     "obj : App.DocumentObject\n    Object to update.\n"
1713
     "subName : str\n    Name of the subelement to update."},
1714
    {"removeSelection",      (PyCFunction) SelectionSingleton::sRemoveSelection, METH_VARARGS,
1715
     "removeSelection(obj, subName) -> None\n"
1716
     "removeSelection(docName, objName, subName) -> None\n"
1717
     "\n"
1718
     "Remove an object from the selection.\n"
1719
     "\n"
1720
     "docName : str\n    Name of the `App.Document`.\n"
1721
     "objName : str\n    Name of the `App.DocumentObject` to remove.\n"
1722
     "obj : App.DocumentObject\n    Object to remove.\n"
1723
     "subName : str\n    Name of the subelement to remove."},
1724
    {"clearSelection"  ,     (PyCFunction) SelectionSingleton::sClearSelection, METH_VARARGS,
1725
     "clearSelection(docName, clearPreSelect=True) -> None\n"
1726
     "clearSelection(clearPreSelect=True) -> None\n"
1727
     "\n"
1728
     "Clear the selection in the given document. If no document is\n"
1729
     "given the complete selection is cleared.\n"
1730
     "\n"
1731
     "docName : str\n    Name of the `App.Document`.\n"
1732
     "clearPreSelect : bool\n    Clear preselection."},
1733
    {"isSelected",           (PyCFunction) SelectionSingleton::sIsSelected, METH_VARARGS,
1734
     "isSelected(obj, subName, resolve=ResolveMode.OldStyleElement) -> bool\n"
1735
     "\n"
1736
     "Check if a given object is selected.\n"
1737
     "\n"
1738
     "obj : App.DocumentObject\n    Object to check.\n"
1739
     "subName : str\n    Name of the subelement.\n"
1740
     "resolve : int\n    Resolve subelement reference."},
1741
    {"setPreselection",      reinterpret_cast<PyCFunction>(reinterpret_cast<void (*) ()>( SelectionSingleton::sSetPreselection )), METH_VARARGS|METH_KEYWORDS,
1742
     "setPreselection(obj, subName, x=0, y=0, z=0, type=1) -> None\n"
1743
     "\n"
1744
     "Set preselected object.\n"
1745
     "\n"
1746
     "obj : App.DocumentObject\n    Object to preselect.\n"
1747
     "subName : str\n    Subelement name.\n"
1748
     "x : float\n    Coordinate `x` of the point to preselect.\n"
1749
     "y : float\n    Coordinate `y` of the point to preselect.\n"
1750
     "z : float\n    Coordinate `z` of the point to preselect.\n"
1751
     "type : int"},
1752
    {"getPreselection",      (PyCFunction) SelectionSingleton::sGetPreselection, METH_VARARGS,
1753
    "getPreselection() -> Gui.SelectionObject\n"
1754
    "\n"
1755
    "Get preselected object."},
1756
    {"clearPreselection",   (PyCFunction) SelectionSingleton::sRemPreselection, METH_VARARGS,
1757
     "clearPreselection() -> None\n"
1758
     "\n"
1759
     "Clear the preselection."},
1760
    {"countObjectsOfType",   (PyCFunction) SelectionSingleton::sCountObjectsOfType, METH_VARARGS,
1761
     "countObjectsOfType(type, docName, resolve=ResolveMode.OldStyleElement) -> int\n"
1762
     "\n"
1763
     "Get the number of selected objects. If no document name is given the\n"
1764
     "active document is used and '*' means all documents.\n"
1765
     "\n"
1766
     "type : str\n    Object type id name.\n"
1767
     "docName : str\n    Name of the `App.Document`.\n"
1768
     "resolve : int"},
1769
    {"getSelection",         (PyCFunction) SelectionSingleton::sGetSelection, METH_VARARGS,
1770
     "getSelection(docName, resolve=ResolveMode.OldStyleElement, single=False) -> list\n"
1771
     "\n"
1772
     "Return a list of selected objects. If no document name is given\n"
1773
     "the active document is used and '*' means all documents.\n"
1774
     "\n"
1775
     "docName : str\n    Name of the `App.Document`.\n"
1776
     "resolve : int\n    Resolve the subname references.\n"
1777
     "    0: do not resolve, 1: resolve, 2: resolve with element map.\n"
1778
     "single : bool\n    Only return if there is only one selection."},
1779
    {"getPickedList",         (PyCFunction) SelectionSingleton::sGetPickedList, 1,
1780
     "getPickedList(docName) -> list of Gui.SelectionObject\n"
1781
     "\n"
1782
     "Return a list of SelectionObjects generated by the last mouse click.\n"
1783
     "If no document name is given the active document is used and '*'\n"
1784
     "means all documents.\n"
1785
     "\n"
1786
     "docName : str\n    Name of the `App.Document`."},
1787
    {"enablePickedList",      (PyCFunction) SelectionSingleton::sEnablePickedList, METH_VARARGS,
1788
     "enablePickedList(enable=True) -> None\n"
1789
     "\n"
1790
     "Enable/disable pick list.\n"
1791
     "\n"
1792
     "enable : bool"},
1793
    {"getCompleteSelection", (PyCFunction) SelectionSingleton::sGetCompleteSelection, METH_VARARGS,
1794
     "getCompleteSelection(resolve=ResolveMode.OldStyleElement) -> list\n"
1795
     "\n"
1796
     "Return a list of selected objects across all documents.\n"
1797
     "\n"
1798
     "resolve : int"},
1799
    {"getSelectionEx",         (PyCFunction) SelectionSingleton::sGetSelectionEx, METH_VARARGS,
1800
     "getSelectionEx(docName, resolve=ResolveMode.OldStyleElement, single=False) -> list of Gui.SelectionObject\n"
1801
     "\n"
1802
     "Return a list of SelectionObjects. If no document name is given the\n"
1803
     "active document is used and '*' means all documents.\n"
1804
     "The SelectionObjects contain a variety of information about the selection,\n"
1805
     "e.g. subelement names.\n"
1806
     "\n"
1807
     "docName : str\n    Name of the `App.Document`.\n"
1808
     "resolve : int\n    Resolve the subname references.\n"
1809
     "    0: do not resolve, 1: resolve, 2: resolve with element map.\n"
1810
     "single : bool\n    Only return if there is only one selection."},
1811
    {"getSelectionObject",  (PyCFunction) SelectionSingleton::sGetSelectionObject, METH_VARARGS,
1812
     "getSelectionObject(docName, objName, subName, point) -> Gui.SelectionObject\n"
1813
     "\n"
1814
     "Return a SelectionObject.\n"
1815
     "\n"
1816
     "docName : str\n    Name of the `App.Document`.\n"
1817
     "objName : str\n    Name of the `App.DocumentObject` to select.\n"
1818
     "subName : str\n    Subelement name.\n"
1819
     "point : tuple\n    Coordinates of the point to pick."},
1820
    {"setSelectionStyle",         (PyCFunction) SelectionSingleton::sSetSelectionStyle, METH_VARARGS,
1821
     "setSelectionStyle(selectionStyle) -> None\n"
1822
     "\n"
1823
     "Change the selection style. 0 for normal selection, 1 for greedy selection\n"
1824
     "\n"
1825
     "selectionStyle : int"},
1826
    {"addObserver",         (PyCFunction) SelectionSingleton::sAddSelObserver, METH_VARARGS,
1827
     "addObserver(object, resolve=ResolveMode.OldStyleElement) -> None\n"
1828
     "\n"
1829
     "Install an observer.\n"
1830
     "\n"
1831
     "object : object\n    Python object instance.\n"
1832
     "resolve : int"},
1833
    {"removeObserver",      (PyCFunction) SelectionSingleton::sRemSelObserver, METH_VARARGS,
1834
     "removeObserver(object) -> None\n"
1835
     "\n"
1836
     "Uninstall an observer.\n"
1837
     "\n"
1838
     "object : object\n    Python object instance."},
1839
    {"addSelectionGate",      (PyCFunction) SelectionSingleton::sAddSelectionGate, METH_VARARGS,
1840
     "addSelectionGate(filter, resolve=ResolveMode.OldStyleElement) -> None\n"
1841
     "\n"
1842
     "Activate the selection gate.\n"
1843
     "The selection gate will prohibit all selections that do not match\n"
1844
     "the given selection criteria.\n"
1845
     "\n"
1846
     "filter : str, SelectionFilter, object\n"
1847
     "resolve : int\n"
1848
     "\n"
1849
     "Examples strings are:\n"
1850
     "Gui.Selection.addSelectionGate('SELECT Part::Feature SUBELEMENT Edge')\n"
1851
     "Gui.Selection.addSelectionGate('SELECT Robot::RobotObject')\n"
1852
     "\n"
1853
     "An instance of SelectionFilter can also be set:\n"
1854
     "filter = Gui.Selection.Filter('SELECT Part::Feature SUBELEMENT Edge')\n"
1855
     "Gui.Selection.addSelectionGate(filter)\n"
1856
     "\n"
1857
     "The most flexible approach is to write a selection gate class that\n"
1858
     "implements the method 'allow':\n"
1859
     "class Gate:\n"
1860
     "    def allow(self,doc,obj,sub):\n"
1861
     "        return (sub[0:4] == 'Face')\n"
1862
     "Gui.Selection.addSelectionGate(Gate())"},
1863
    {"removeSelectionGate",      (PyCFunction) SelectionSingleton::sRemoveSelectionGate, METH_VARARGS,
1864
     "removeSelectionGate() -> None\n"
1865
     "\n"
1866
     "Remove the active selection gate."},
1867
    {"setVisible",            (PyCFunction) SelectionSingleton::sSetVisible, METH_VARARGS,
1868
     "setVisible(visible=None) -> None\n"
1869
     "\n"
1870
     "Set visibility of all selection items.\n"
1871
     "\n"
1872
     "visible : bool, None\n    If None, then toggle visibility."},
1873
    {"pushSelStack",      (PyCFunction) SelectionSingleton::sPushSelStack, METH_VARARGS,
1874
     "pushSelStack(clearForward=True, overwrite=False) -> None\n"
1875
     "\n"
1876
     "Push current selection to stack.\n"
1877
     "\n"
1878
     "clearForward : bool\n    Clear the forward selection stack.\n"
1879
     "overwrite : bool\n    Overwrite the top back selection stack with current selection."},
1880
    {"hasSelection",      (PyCFunction) SelectionSingleton::sHasSelection, METH_VARARGS,
1881
     "hasSelection(docName, resolve=ResolveMode.NoResolve) -> bool\n"
1882
     "\n"
1883
     "Check if there is any selection. If no document name is given,\n"
1884
     "checks selections in all documents.\n"
1885
     "\n"
1886
     "docName : str\n    Name of the `App.Document`.\n"
1887
     "resolve : int"},
1888
    {"hasSubSelection",   (PyCFunction) SelectionSingleton::sHasSubSelection, METH_VARARGS,
1889
     "hasSubSelection(docName, subElement=False) -> bool\n"
1890
     "\n"
1891
     "Check if there is any selection with subname. If no document name\n"
1892
     "is given the active document is used and '*' means all documents.\n"
1893
     "\n"
1894
     "docName : str\n    Name of the `App.Document`.\n"
1895
     "subElement : bool"},
1896
    {"getSelectionFromStack",(PyCFunction) SelectionSingleton::sGetSelectionFromStack, METH_VARARGS,
1897
     "getSelectionFromStack(docName, resolve=ResolveMode.OldStyleElement, index=0) -> list of Gui.SelectionObject\n"
1898
     "\n"
1899
     "Return SelectionObjects from selection stack. If no document name is given\n"
1900
     "the active document is used and '*' means all documents.\n"
1901
     "\n"
1902
     "docName : str\n    Name of the `App.Document`.\n"
1903
     "resolve : int\n    Resolve the subname references.\n"
1904
     "    0: do not resolve, 1: resolve, 2: resolve with element map.\n"
1905
     "index : int\n    Select stack index.\n"
1906
     "    0: last pushed selection, > 0: trace back, < 0: trace forward."},
1907
    {nullptr, nullptr, 0, nullptr}  /* Sentinel */
1908
};
1909

1910
PyObject *SelectionSingleton::sAddSelection(PyObject * /*self*/, PyObject *args)
1911
{
1912
    SelectionLogDisabler disabler(true);
1913
    PyObject *clearPreselect = Py_True;
1914
    char *objname;
1915
    char *docname;
1916
    char* subname = nullptr;
1917
    float x = 0, y = 0, z = 0;
1918
    if (PyArg_ParseTuple(args, "ss|sfffO!", &docname, &objname ,
1919
                &subname,&x,&y,&z,&PyBool_Type,&clearPreselect)) {
1920
        Selection().addSelection(docname, objname, subname, x, y, z, nullptr, Base::asBoolean(clearPreselect));
1921
        Py_Return;
1922
    }
1923

1924
    PyErr_Clear();
1925
    PyObject *object;
1926
    subname = nullptr;
1927
    x = 0, y = 0, z = 0;
1928
    if (PyArg_ParseTuple(args, "O!|sfffO!", &(App::DocumentObjectPy::Type),&object,
1929
                &subname,&x,&y,&z,&PyBool_Type,&clearPreselect)) {
1930
        auto docObjPy = static_cast<App::DocumentObjectPy*>(object);
1931
        App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr();
1932
        if (!docObj || !docObj->isAttachedToDocument()) {
1933
            PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot check invalid object");
1934
            return nullptr;
1935
        }
1936

1937
        Selection().addSelection(docObj->getDocument()->getName(),
1938
                                 docObj->getNameInDocument(),
1939
                                 subname, x, y, z, nullptr, Base::asBoolean(clearPreselect));
1940
        Py_Return;
1941
    }
1942

1943
    PyErr_Clear();
1944
    PyObject *sequence;
1945
    if (PyArg_ParseTuple(args, "O!O|O!", &(App::DocumentObjectPy::Type),&object,
1946
                &sequence,&PyBool_Type,&clearPreselect))
1947
    {
1948
        auto docObjPy = static_cast<App::DocumentObjectPy*>(object);
1949
        App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr();
1950
        if (!docObj || !docObj->isAttachedToDocument()) {
1951
            PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot check invalid object");
1952
            return nullptr;
1953
        }
1954

1955
        try {
1956
            if (PyTuple_Check(sequence) || PyList_Check(sequence)) {
1957
                Py::Sequence list(sequence);
1958
                for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) {
1959
                    std::string subname = static_cast<std::string>(Py::String(*it));
1960
                    Selection().addSelection(docObj->getDocument()->getName(),
1961
                                             docObj->getNameInDocument(),
1962
                                             subname.c_str(), 0, 0, 0, nullptr, Base::asBoolean(clearPreselect));
1963
                }
1964
                Py_Return;
1965
            }
1966
        }
1967
        catch (const Py::Exception&) {
1968
            // do nothing here
1969
        }
1970
    }
1971

1972
    PyErr_SetString(PyExc_ValueError, "type must be 'DocumentObject[,subname[,x,y,z]]' or 'DocumentObject, list or tuple of subnames'");
1973

1974
    return nullptr;
1975
}
1976

1977
PyObject *SelectionSingleton::sUpdateSelection(PyObject * /*self*/, PyObject *args)
1978
{
1979
    PyObject *show;
1980
    PyObject *object;
1981
    char* subname=nullptr;
1982
    if(!PyArg_ParseTuple(args, "O!O!|s", &PyBool_Type, &show, &(App::DocumentObjectPy::Type),
1983
            &object, &subname))
1984
        return nullptr;
1985

1986
    auto docObjPy = static_cast<App::DocumentObjectPy*>(object);
1987
    App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr();
1988
    if (!docObj || !docObj->isAttachedToDocument()) {
1989
        PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot check invalid object");
1990
        return nullptr;
1991
    }
1992

1993
    Selection().updateSelection(Base::asBoolean(show),
1994
            docObj->getDocument()->getName(), docObj->getNameInDocument(), subname);
1995

1996
    Py_Return;
1997
}
1998

1999

2000
PyObject *SelectionSingleton::sRemoveSelection(PyObject * /*self*/, PyObject *args)
2001
{
2002
    SelectionLogDisabler disabler(true);
2003
    char *docname, *objname;
2004
    char* subname = nullptr;
2005
    if(PyArg_ParseTuple(args, "ss|s", &docname,&objname,&subname)) {
2006
        Selection().rmvSelection(docname,objname,subname);
2007
        Py_Return;
2008
    }
2009

2010
    PyErr_Clear();
2011
    PyObject *object;
2012
    subname = nullptr;
2013
    if (!PyArg_ParseTuple(args, "O!|s", &(App::DocumentObjectPy::Type),&object,&subname))
2014
        return nullptr;
2015

2016
    auto docObjPy = static_cast<App::DocumentObjectPy*>(object);
2017
    App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr();
2018
    if (!docObj || !docObj->isAttachedToDocument()) {
2019
        PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot check invalid object");
2020
        return nullptr;
2021
    }
2022

2023
    Selection().rmvSelection(docObj->getDocument()->getName(),
2024
                             docObj->getNameInDocument(),
2025
                             subname);
2026

2027
    Py_Return;
2028
}
2029

2030
PyObject *SelectionSingleton::sClearSelection(PyObject * /*self*/, PyObject *args)
2031
{
2032
    SelectionLogDisabler disabler(true);
2033
    PyObject *clearPreSelect = Py_True;
2034
    char *documentName = nullptr;
2035
    if (!PyArg_ParseTuple(args, "|O!", &PyBool_Type, &clearPreSelect)) {
2036
        PyErr_Clear();
2037
        if (!PyArg_ParseTuple(args, "|sO!", &documentName, &PyBool_Type, &clearPreSelect))
2038
            return nullptr;
2039
    }
2040
    Selection().clearSelection(documentName, Base::asBoolean(clearPreSelect));
2041

2042
    Py_Return;
2043
}
2044

2045
namespace {
2046
ResolveMode toEnum(int value) {
2047
    switch (value) {
2048
    case 0:
2049
        return ResolveMode::NoResolve;
2050
    case 1:
2051
        return ResolveMode::OldStyleElement;
2052
    case 2:
2053
        return ResolveMode::NewStyleElement;
2054
    case 3:
2055
        return ResolveMode::FollowLink;
2056
    default:
2057
        throw Base::ValueError("Wrong enum value");
2058
    }
2059
}
2060
}
2061

2062
PyObject *SelectionSingleton::sIsSelected(PyObject * /*self*/, PyObject *args)
2063
{
2064
    PyObject *object;
2065
    char* subname = nullptr;
2066
    int resolve = 1;
2067
    if (!PyArg_ParseTuple(args, "O!|si", &(App::DocumentObjectPy::Type), &object, &subname, &resolve))
2068
        return nullptr;
2069

2070
    try {
2071
        auto docObj = static_cast<App::DocumentObjectPy*>(object);
2072
        bool ok = Selection().isSelected(docObj->getDocumentObjectPtr(), subname, toEnum(resolve));
2073

2074
        return Py_BuildValue("O", (ok ? Py_True : Py_False));
2075
    }
2076
    catch (const Base::Exception& e) {
2077
        e.setPyException();
2078
        return nullptr;
2079
    }
2080
}
2081

2082
PyObject *SelectionSingleton::sCountObjectsOfType(PyObject * /*self*/, PyObject *args)
2083
{
2084
    char* objecttype;
2085
    char* document = nullptr;
2086
    int resolve = 1;
2087
    if (!PyArg_ParseTuple(args, "s|si", &objecttype, &document,&resolve))
2088
        return nullptr;
2089

2090
    try {
2091
        unsigned int count = Selection().countObjectsOfType(objecttype, document, toEnum(resolve));
2092
        return PyLong_FromLong(count);
2093
    }
2094
    catch (const Base::Exception& e) {
2095
        e.setPyException();
2096
        return nullptr;
2097
    }
2098
}
2099

2100
PyObject *SelectionSingleton::sGetSelection(PyObject * /*self*/, PyObject *args)
2101
{
2102
    char *documentName = nullptr;
2103
    int resolve = 1;
2104
    PyObject *single = Py_False;
2105
    if (!PyArg_ParseTuple(args, "|siO!", &documentName, &resolve, &PyBool_Type, &single))
2106
        return nullptr;
2107

2108
    try {
2109
        std::vector<SelectionSingleton::SelObj> sel;
2110
        sel = Selection().getSelection(documentName, toEnum(resolve), Base::asBoolean(single));
2111

2112
        std::set<App::DocumentObject*> noduplicates;
2113
        std::vector<App::DocumentObject*> selectedObjects; // keep the order of selection
2114
        Py::List list;
2115
        for (const auto & it : sel) {
2116
            if (noduplicates.insert(it.pObject).second) {
2117
                selectedObjects.push_back(it.pObject);
2118
            }
2119
        }
2120
        for (const auto & selectedObject : selectedObjects) {
2121
            list.append(Py::asObject(selectedObject->getPyObject()));
2122
        }
2123
        return Py::new_reference_to(list);
2124
    }
2125
    catch (const Base::Exception& e) {
2126
        e.setPyException();
2127
        return nullptr;
2128
    }
2129
    catch (Py::Exception&) {
2130
        return nullptr;
2131
    }
2132
}
2133

2134
PyObject *SelectionSingleton::sEnablePickedList(PyObject * /*self*/, PyObject *args)
2135
{
2136
    PyObject *enable = Py_True;
2137
    if (!PyArg_ParseTuple(args, "|O!", &PyBool_Type, &enable))
2138
        return nullptr;
2139

2140
    Selection().enablePickedList(Base::asBoolean(enable));
2141

2142
    Py_Return;
2143
}
2144

2145
PyObject *SelectionSingleton::sSetPreselection(PyObject * /*self*/, PyObject *args, PyObject *kwd)
2146
{
2147
    PyObject *object;
2148
    const char* subname = nullptr;
2149
    float x = 0, y = 0, z = 0;
2150
    int type = 1;
2151
    static const std::array<const char *, 7> kwlist{"obj", "subname", "x", "y", "z", "tp", nullptr};
2152
    if (Base::Wrapped_ParseTupleAndKeywords(args, kwd, "O!|sfffi", kwlist, &(App::DocumentObjectPy::Type), &object,
2153
                                            &subname, &x, &y, &z, &type)) {
2154
        auto docObjPy = static_cast<App::DocumentObjectPy*>(object);
2155
        App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr();
2156
        if (!docObj || !docObj->isAttachedToDocument()) {
2157
            PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot check invalid object");
2158
            return nullptr;
2159
        }
2160

2161
        Selection().setPreselect(docObj->getDocument()->getName(),
2162
                                 docObj->getNameInDocument(),
2163
                                 subname,x,y,z, static_cast<SelectionChanges::MsgSource>(type));
2164
        Py_Return;
2165
    }
2166

2167
    PyErr_SetString(PyExc_ValueError, "type must be 'DocumentObject[,subname[,x,y,z]]'");
2168

2169
    return nullptr;
2170
}
2171

2172
PyObject *SelectionSingleton::sGetPreselection(PyObject * /*self*/, PyObject *args)
2173
{
2174
    if (!PyArg_ParseTuple(args, ""))
2175
        return nullptr;
2176

2177
    const SelectionChanges& sel = Selection().getPreselection();
2178
    SelectionObject obj(sel);
2179

2180
    return obj.getPyObject();
2181
}
2182

2183
PyObject *SelectionSingleton::sRemPreselection(PyObject * /*self*/, PyObject *args)
2184
{
2185
    if (!PyArg_ParseTuple(args, ""))
2186
        return nullptr;
2187

2188
    Selection().rmvPreselect();
2189

2190
    Py_Return;
2191
}
2192

2193
PyObject *SelectionSingleton::sGetCompleteSelection(PyObject * /*self*/, PyObject *args)
2194
{
2195
    int resolve = 1;
2196
    if (!PyArg_ParseTuple(args, "|i",&resolve))
2197
        return nullptr;
2198

2199
    try {
2200
        std::vector<SelectionSingleton::SelObj> sel;
2201
        sel = Selection().getCompleteSelection(toEnum(resolve));
2202

2203
        Py::List list;
2204
        for (const auto & it : sel) {
2205
            SelectionObject obj(SelectionChanges(SelectionChanges::AddSelection,
2206
                                                 it.DocName,
2207
                                                 it.FeatName,
2208
                                                 it.SubName,
2209
                                                 it.TypeName,
2210
                                                 it.x, it.y, it.z));
2211
            list.append(Py::asObject(obj.getPyObject()));
2212
        }
2213
        return Py::new_reference_to(list);
2214
    }
2215
    catch (const Base::Exception& e) {
2216
        e.setPyException();
2217
        return nullptr;
2218
    }
2219
    catch (Py::Exception&) {
2220
        return nullptr;
2221
    }
2222
}
2223

2224
PyObject *SelectionSingleton::sGetSelectionEx(PyObject * /*self*/, PyObject *args)
2225
{
2226
    char *documentName = nullptr;
2227
    int resolve = 1;
2228
    PyObject *single = Py_False;
2229
    if (!PyArg_ParseTuple(args, "|siO!", &documentName, &resolve, &PyBool_Type, &single))
2230
        return nullptr;
2231

2232
    try {
2233
        std::vector<SelectionObject> sel;
2234
        sel = Selection().getSelectionEx(documentName,
2235
                App::DocumentObject::getClassTypeId(), toEnum(resolve), Base::asBoolean(single));
2236

2237
        Py::List list;
2238
        for (auto & it : sel) {
2239
            list.append(Py::asObject(it.getPyObject()));
2240
        }
2241
        return Py::new_reference_to(list);
2242
    }
2243
    catch (const Base::Exception& e) {
2244
        e.setPyException();
2245
        return nullptr;
2246
    }
2247
    catch (Py::Exception&) {
2248
        return nullptr;
2249
    }
2250
}
2251

2252
PyObject *SelectionSingleton::sGetPickedList(PyObject * /*self*/, PyObject *args)
2253
{
2254
    char *documentName = nullptr;
2255
    if (!PyArg_ParseTuple(args, "|s", &documentName))
2256
        return nullptr;
2257

2258
    std::vector<SelectionObject> sel;
2259
    sel = Selection().getPickedListEx(documentName);
2260

2261
    try {
2262
        Py::List list;
2263
        for (auto & it : sel) {
2264
            list.append(Py::asObject(it.getPyObject()));
2265
        }
2266
        return Py::new_reference_to(list);
2267
    }
2268
    catch (Py::Exception&) {
2269
        return nullptr;
2270
    }
2271
}
2272

2273
PyObject *SelectionSingleton::sGetSelectionObject(PyObject * /*self*/, PyObject *args)
2274
{
2275
    char *docName, *objName, *subName;
2276
    PyObject* tuple = nullptr;
2277
    if (!PyArg_ParseTuple(args, "sss|O!", &docName, &objName, &subName, &PyTuple_Type, &tuple))
2278
        return nullptr;
2279

2280
    try {
2281
        SelectionObject selObj;
2282
        selObj.DocName  = docName;
2283
        selObj.FeatName = objName;
2284
        std::string sub = subName;
2285
        if (!sub.empty()) {
2286
            selObj.SubNames.push_back(sub);
2287
            if (tuple) {
2288
                Py::Tuple t(tuple);
2289
                double x = (double)Py::Float(t.getItem(0));
2290
                double y = (double)Py::Float(t.getItem(1));
2291
                double z = (double)Py::Float(t.getItem(2));
2292
                selObj.SelPoses.emplace_back(x,y,z);
2293
            }
2294
        }
2295

2296
        return selObj.getPyObject();
2297
    }
2298
    catch (const Py::Exception&) {
2299
        return nullptr;
2300
    }
2301
    catch (const Base::Exception& e) {
2302
        e.setPyException();
2303
        return nullptr;
2304
    }
2305
}
2306

2307
PyObject *SelectionSingleton::sSetSelectionStyle(PyObject * /*self*/, PyObject *args)
2308
{
2309
    int selStyle = 0;
2310
    if (!PyArg_ParseTuple(args, "i", &selStyle))
2311
        return nullptr;
2312

2313
    PY_TRY {
2314
        Selection().setSelectionStyle(selStyle == 0 ? SelectionStyle::NormalSelection : SelectionStyle::GreedySelection);
2315
        Py_Return;
2316
    }
2317
    PY_CATCH;
2318
}
2319

2320
PyObject *SelectionSingleton::sAddSelObserver(PyObject * /*self*/, PyObject *args)
2321
{
2322
    PyObject* o;
2323
    int resolve = 1;
2324
    if (!PyArg_ParseTuple(args, "O|i", &o, &resolve))
2325
        return nullptr;
2326

2327
    PY_TRY {
2328
        SelectionObserverPython::addObserver(Py::Object(o), toEnum(resolve));
2329
        Py_Return;
2330
    }
2331
    PY_CATCH;
2332
}
2333

2334
PyObject *SelectionSingleton::sRemSelObserver(PyObject * /*self*/, PyObject *args)
2335
{
2336
    PyObject* o;
2337
    if (!PyArg_ParseTuple(args, "O", &o))
2338
        return nullptr;
2339

2340
    PY_TRY {
2341
        SelectionObserverPython::removeObserver(Py::Object(o));
2342
        Py_Return;
2343
    }
2344
    PY_CATCH;
2345
}
2346

2347
PyObject *SelectionSingleton::sAddSelectionGate(PyObject * /*self*/, PyObject *args)
2348
{
2349
    char* filter;
2350
    int resolve = 1;
2351
    if (PyArg_ParseTuple(args, "s|i", &filter, &resolve)) {
2352
        PY_TRY {
2353
            Selection().addSelectionGate(new SelectionFilterGate(filter), toEnum(resolve));
2354
            Py_Return;
2355
        }
2356
        PY_CATCH;
2357
    }
2358

2359
    PyErr_Clear();
2360
    PyObject* filterPy;
2361
    if (PyArg_ParseTuple(args, "O!|i",SelectionFilterPy::type_object(),&filterPy,resolve)) {
2362
        PY_TRY {
2363
            Selection().addSelectionGate(new SelectionFilterGatePython(
2364
                        SelectionFilterPy::cast(filterPy)), toEnum(resolve));
2365
            Py_Return;
2366
        }
2367
        PY_CATCH;
2368
    }
2369

2370
    PyErr_Clear();
2371
    PyObject* gate;
2372
    if (PyArg_ParseTuple(args, "O|i",&gate,&resolve)) {
2373
        PY_TRY {
2374
            Selection().addSelectionGate(new SelectionGatePython(Py::Object(gate, false)), toEnum(resolve));
2375
            Py_Return;
2376
        }
2377
         PY_CATCH;
2378
    }
2379

2380
    PyErr_SetString(PyExc_ValueError, "Argument is neither string nor SelectionFiler nor SelectionGate");
2381

2382
    return nullptr;
2383
}
2384

2385
PyObject *SelectionSingleton::sRemoveSelectionGate(PyObject * /*self*/, PyObject *args)
2386
{
2387
    if (!PyArg_ParseTuple(args, ""))
2388
        return nullptr;
2389

2390
    PY_TRY {
2391
        Selection().rmvSelectionGate();
2392
        Py_Return;
2393
    }
2394
    PY_CATCH;
2395
}
2396

2397
PyObject *SelectionSingleton::sSetVisible(PyObject * /*self*/, PyObject *args)
2398
{
2399
    PyObject *visible = Py_None;
2400
    if (!PyArg_ParseTuple(args, "|O", &visible))
2401
        return nullptr;
2402

2403
    PY_TRY {
2404
        VisibleState vis = VisToggle;
2405
        Base::PyTypeCheck(&visible, &PyBool_Type);
2406
        if (visible)
2407
            vis = PyObject_IsTrue(visible) ? VisShow : VisHide;
2408

2409
        Selection().setVisible(vis);
2410
        Py_Return;
2411
    }
2412
    PY_CATCH;
2413
}
2414

2415
PyObject *SelectionSingleton::sPushSelStack(PyObject * /*self*/, PyObject *args)
2416
{
2417
    PyObject *clear = Py_True;
2418
    PyObject *overwrite = Py_False;
2419
    if (!PyArg_ParseTuple(args, "|O!O!", &PyBool_Type, &clear, &PyBool_Type, &overwrite))
2420
        return nullptr;
2421

2422
    Selection().selStackPush(Base::asBoolean(clear), Base::asBoolean(overwrite));
2423

2424
    Py_Return;
2425
}
2426

2427
PyObject *SelectionSingleton::sHasSelection(PyObject * /*self*/, PyObject *args)
2428
{
2429
    const char *doc = nullptr;
2430
    int resolve = 0;
2431
    if (!PyArg_ParseTuple(args, "|si", &doc, &resolve))
2432
        return nullptr;
2433

2434
    PY_TRY {
2435
        bool ret;
2436
        if (doc || resolve > 0)
2437
            ret = Selection().hasSelection(doc, toEnum(resolve));
2438
        else
2439
            ret = Selection().hasSelection();
2440

2441
        return Py::new_reference_to(Py::Boolean(ret));
2442
    }
2443
    PY_CATCH;
2444
}
2445

2446
PyObject *SelectionSingleton::sHasSubSelection(PyObject * /*self*/, PyObject *args)
2447
{
2448
    const char *doc = nullptr;
2449
    PyObject *subElement = Py_False;
2450
    if (!PyArg_ParseTuple(args, "|sO!",&doc,&PyBool_Type,&subElement))
2451
        return nullptr;
2452

2453
    PY_TRY {
2454
        return Py::new_reference_to(
2455
               Py::Boolean(Selection().hasSubSelection(doc, Base::asBoolean(subElement))));
2456
    }
2457
    PY_CATCH;
2458
}
2459

2460
PyObject *SelectionSingleton::sGetSelectionFromStack(PyObject * /*self*/, PyObject *args)
2461
{
2462
    char *documentName = nullptr;
2463
    int resolve = 1;
2464
    int index = 0;
2465
    if (!PyArg_ParseTuple(args, "|sii", &documentName, &resolve, &index))
2466
        return nullptr;
2467

2468
    PY_TRY {
2469
        Py::List list;
2470
        for(auto &sel : Selection().selStackGet(documentName, toEnum(resolve), index))
2471
            list.append(Py::asObject(sel.getPyObject()));
2472
        return Py::new_reference_to(list);
2473
    }
2474
    PY_CATCH;
2475
}
2476

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

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

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

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