FreeCAD

Форк
0
/
Selection.cpp 
2476 строк · 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 <array>
28
# include <boost/algorithm/string/predicate.hpp>
29
# include <QApplication>
30
#endif
31

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

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

57

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

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

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

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

84
//////////////////////////////////////////////////////////////////////////////////////////
85

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

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

106

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

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

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

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

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

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

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

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

173
// -------------------------------------------
174

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

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

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

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

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

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

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

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

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

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

233
        temp.push_back(tempSelObj);
234
    }
235

236
    return temp;
237
}
238

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

256
    return false;
257
}
258

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

278
    return false;
279
}
280

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

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

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

309
    return temp;
310
}
311

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

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

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

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

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

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

377
    return temp;
378
}
379

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

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

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

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

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

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

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

482
    if (!obj)
483
        return nullptr;
484

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

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

491
    return obj;
492
}
493

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

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

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

516
    return temp;
517
}
518

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

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

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

542
    return iNbr;
543
}
544

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

553

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

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

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

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

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

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

614
    rmvPreselect();
615

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

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

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

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

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

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

681
    notify(Chng);
682

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

804
    FC_TRACE("rmv preselect");
805

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

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

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

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

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

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

842

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

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

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

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

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

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

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

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

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

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

956
    if(clearPreselect)
957
        rmvPreselect();
958

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

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

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

967
    getMainWindow()->updateActions();
968

969
    rmvPreselect(true);
970

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1164
    return true;
1165
}
1166

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

1195

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

1217
    if(!pDocName)
1218
        return;
1219

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

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

1238
        It->log(true);
1239

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1425
        if (!touched)
1426
            return;
1427

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

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

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

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

1450
    if(clearPreSelect)
1451
        rmvPreselect();
1452

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

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

1461
    _SelList.clear();
1462

1463
    SelectionChanges Chng(SelectionChanges::ClrSelection);
1464

1465
    FC_LOG("Clear selection");
1466

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1642
//**************************************************************************
1643
// Construction/Destruction
1644

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

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

1670
SelectionSingleton* SelectionSingleton::_pcSingleton = nullptr;
1671

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

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

1686
//**************************************************************************
1687
// Python stuff
1688

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

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

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

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

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

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

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

1975
    return nullptr;
1976
}
1977

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

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

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

1997
    Py_Return;
1998
}
1999

2000

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

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

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

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

2028
    Py_Return;
2029
}
2030

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

2043
    Py_Return;
2044
}
2045

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

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

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

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

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

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

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

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

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

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

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

2143
    Py_Return;
2144
}
2145

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

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

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

2170
    return nullptr;
2171
}
2172

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

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

2181
    return obj.getPyObject();
2182
}
2183

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

2189
    Selection().rmvPreselect();
2190

2191
    Py_Return;
2192
}
2193

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2383
    return nullptr;
2384
}
2385

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

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

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

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

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

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

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

2425
    Py_Return;
2426
}
2427

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

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

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

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

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

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

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

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

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

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

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