FreeCAD

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

23
#include "PreCompiled.h"
24
#ifndef _PreComp_
25
# include <algorithm>
26
# include <sstream>
27
# include <QStyledItemDelegate>
28
# include <QTreeWidgetItem>
29
#endif
30

31
#include <App/Document.h>
32
#include <App/DocumentObject.h>
33
#include <App/GeoFeature.h>
34
#include <App/ObjectIdentifier.h>
35
#include <App/PropertyPythonObject.h>
36
#include <Base/Interpreter.h>
37
#include <Base/Tools.h>
38

39
#include "DlgPropertyLink.h"
40
#include "ui_DlgPropertyLink.h"
41
#include "Application.h"
42
#include "Document.h"
43
#include "BitmapFactory.h"
44
#include "PropertyView.h"
45
#include "Selection.h"
46
#include "Tree.h"
47
#include "TreeParams.h"
48
#include "View3DInventor.h"
49
#include "ViewProviderDocumentObject.h"
50

51

52
using namespace Gui::Dialog;
53

54
class ItemDelegate: public QStyledItemDelegate {
55
public:
56
    explicit ItemDelegate(QObject* parent=nullptr): QStyledItemDelegate(parent) {}
57

58
    QWidget* createEditor(QWidget *parent,
59
            const QStyleOptionViewItem &option, const QModelIndex &index) const override
60
    {
61
        if(index.column() != 1)
62
            return nullptr;
63
        return QStyledItemDelegate::createEditor(parent, option, index);
64
    }
65
};
66

67
/* TRANSLATOR Gui::Dialog::DlgPropertyLink */
68

69
DlgPropertyLink::DlgPropertyLink(QWidget* parent)
70
  : QDialog(parent), SelectionObserver(false, ResolveMode::NoResolve)
71
  , ui(new Ui_DlgPropertyLink)
72
{
73
    ui->setupUi(this);
74
    connect(ui->checkObjectType, &QCheckBox::toggled,
75
            this, &DlgPropertyLink::onObjectTypeToggled);
76
    connect(ui->typeTree, &QTreeWidget::itemSelectionChanged,
77
            this, &DlgPropertyLink::onTypeTreeItemSelectionChanged);
78
    connect(ui->searchBox, &ExpressionLineEdit::textChanged,
79
            this, &DlgPropertyLink::onSearchBoxTextChanged);
80

81
    ui->typeTree->hide();
82
    ui->searchBox->installEventFilter(this);
83
    ui->searchBox->setNoProperty(true);
84
    ui->searchBox->setExactMatch(Gui::ExpressionParameter::instance()->isExactMatch());
85

86
    timer = new QTimer(this);
87
    timer->setSingleShot(true);
88

89
    connect(timer, &QTimer::timeout, this, &DlgPropertyLink::onTimer);
90

91
    ui->treeWidget->setEditTriggers(QAbstractItemView::DoubleClicked);
92
    ui->treeWidget->setItemDelegate(new ItemDelegate(this));
93
    ui->treeWidget->setMouseTracking(true);
94
    connect(ui->treeWidget, &QTreeWidget::itemEntered,
95
            this, &DlgPropertyLink::onItemEntered);
96

97
    connect(ui->treeWidget, &QTreeWidget::itemExpanded,
98
            this, &DlgPropertyLink::onItemExpanded);
99

100
    connect(ui->treeWidget, &QTreeWidget::itemSelectionChanged, this, &DlgPropertyLink::onItemSelectionChanged);
101

102
    connect(ui->searchBox, &QLineEdit::returnPressed, this, &DlgPropertyLink::onItemSearch);
103

104
    connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &DlgPropertyLink::onClicked);
105

106
    refreshButton = ui->buttonBox->addButton(tr("Reset"), QDialogButtonBox::ActionRole);
107
    resetButton = ui->buttonBox->addButton(tr("Clear"), QDialogButtonBox::ResetRole);
108
}
109

110
/**
111
 *  Destroys the object and frees any allocated resources
112
 */
113
DlgPropertyLink::~DlgPropertyLink()
114
{
115
    detachObserver();
116

117
    // no need to delete child widgets, Qt does it all for us
118
    delete ui;
119
}
120

121
QList<App::SubObjectT> DlgPropertyLink::getLinksFromProperty(const App::PropertyLinkBase *prop)
122
{
123
    QList<App::SubObjectT> res;
124
    if(!prop)
125
        return res;
126

127
    std::vector<App::DocumentObject*> objs;
128
    std::vector<std::string> subs;
129
    prop->getLinks(objs,true,&subs,false);
130
    if(subs.empty()) {
131
        for(auto obj : objs)
132
            res.push_back(App::SubObjectT(obj,nullptr));
133
    } else if (objs.size()==1) {
134
        for(auto &sub : subs)
135
            res.push_back(App::SubObjectT(objs.front(),sub.c_str()));
136
    } else {
137
        int i=0;
138
        for(auto obj : objs)
139
            res.push_back(App::SubObjectT(obj,subs[i++].c_str()));
140
    }
141
    return res;
142
}
143

144
QString DlgPropertyLink::formatObject(App::Document *ownerDoc, App::DocumentObject *obj, const char *sub)
145
{
146
    if(!obj || !obj->isAttachedToDocument())
147
        return QLatin1String("?");
148

149
    const char *objName = obj->getNameInDocument();
150
    std::string _objName;
151
    if(ownerDoc && ownerDoc!=obj->getDocument()) {
152
        _objName = obj->getFullName();
153
        objName = _objName.c_str();
154
    }
155

156
    if(!sub || !sub[0]) {
157
        if(obj->Label.getStrValue() == obj->getNameInDocument())
158
            return QLatin1String(objName);
159
        return QString::fromLatin1("%1 (%2)").arg(QLatin1String(objName),
160
                                                  QString::fromUtf8(obj->Label.getValue()));
161
    }
162

163
    auto sobj = obj->getSubObject(sub);
164
    if(!sobj || sobj->Label.getStrValue() == sobj->getNameInDocument())
165
        return QString::fromLatin1("%1.%2").arg(QLatin1String(objName),
166
                                                QString::fromUtf8(sub));
167

168
    return QString::fromLatin1("%1.%2 (%3)").arg(QLatin1String(objName),
169
                                                 QString::fromUtf8(sub),
170
                                                 QString::fromUtf8(sobj->Label.getValue()));
171
}
172

173
static inline bool isLinkSub(const QList<App::SubObjectT>& links)
174
{
175
    for(const auto &link : links) {
176
        if(&link == &links.front())
177
            continue;
178
        if(link.getDocumentName() != links.front().getDocumentName()
179
                || link.getObjectName() != links.front().getObjectName())
180
        {
181
            return false;
182
        }
183
    }
184
    return true;
185
}
186

187
QString DlgPropertyLink::formatLinks(App::Document *ownerDoc, QList<App::SubObjectT> links)
188
{
189
    if(!ownerDoc || links.empty())
190
        return {};
191

192
    auto obj = links.front().getObject();
193
    if(!obj)
194
        return QLatin1String("?");
195

196
    if(links.size() == 1 && links.front().getSubName().empty())
197
        return formatObject(ownerDoc, links.front());
198

199
    QStringList list;
200
    if(isLinkSub(links)) {
201
        int i = 0;
202
        for(auto &link : links) {
203
            list << QString::fromUtf8(link.getSubName().c_str());
204
            if( ++i >= 3)
205
                break;
206
        }
207
        return QString::fromLatin1("%1 [%2%3]").arg(formatObject(ownerDoc,obj,nullptr),
208
                                                    list.join(QLatin1String(", ")),
209
                                                    QLatin1String(links.size()>3?" ...":""));
210
    }
211

212
    int i = 0;
213
    for(auto &link : links) {
214
        list << formatObject(ownerDoc,link);
215
        if( ++i >= 3)
216
            break;
217
    }
218
    return QString::fromLatin1("[%1%2]").arg(list.join(QLatin1String(", ")),
219
                                             QLatin1String(links.size()>3?" ...":""));
220
}
221

222
void DlgPropertyLink::init(const App::DocumentObjectT &prop, bool tryFilter) {
223
    ui->treeWidget->blockSignals(true);
224
    ui->treeWidget->clear();
225
    ui->treeWidget->blockSignals(false);
226

227
    ui->typeTree->blockSignals(true);
228
    ui->typeTree->clear();
229
    ui->typeTree->blockSignals(false);
230

231
    oldLinks.clear();
232
    docItems.clear();
233
    typeItems.clear();
234
    itemMap.clear();
235
    inList.clear();
236
    selectedTypes.clear();
237
    currentObj = nullptr;
238
    searchItem = nullptr;
239
    subSelections.clear();
240
    selections.clear();
241

242
    objProp  = prop;
243
    auto owner = objProp.getObject();
244
    if(!owner || !owner->isAttachedToDocument())
245
        return;
246

247
    ui->searchBox->setDocumentObject(owner);
248

249
    auto propLink = Base::freecad_dynamic_cast<App::PropertyLinkBase>(objProp.getProperty());
250
    if(!propLink)
251
        return;
252

253
    oldLinks = getLinksFromProperty(propLink);
254

255
    if(propLink->getScope() != App::LinkScope::Hidden) {
256
        // populate inList to filter out any objects that contains the owner object
257
        // of the editing link property
258
        inList = owner->getInListEx(true);
259
        inList.insert(owner);
260
    }
261

262
    std::vector<App::Document*> docs;
263

264
    singleSelect = false;
265
    if(propLink->isDerivedFrom(App::PropertyXLinkSub::getClassTypeId())
266
            || propLink->isDerivedFrom(App::PropertyLinkSub::getClassTypeId()))
267
    {
268
        allowSubObject = true;
269
        singleParent = true;
270
    } else if (propLink->isDerivedFrom(App::PropertyLink::getClassTypeId())) {
271
        singleSelect = true;
272
    } else if (propLink->isDerivedFrom(App::PropertyLinkSubList::getClassTypeId())) {
273
        allowSubObject = true;
274
    }
275

276
    if(App::PropertyXLink::supportXLink(propLink)) {
277
        allowSubObject = true;
278
        docs = App::GetApplication().getDocuments();
279
    } else
280
        docs.push_back(owner->getDocument());
281

282
    bool isLinkList = false;
283
    if (propLink->isDerivedFrom(App::PropertyXLinkList::getClassTypeId())
284
            || propLink->isDerivedFrom(App::PropertyLinkList::getClassTypeId()))
285
    {
286
        isLinkList = true;
287
        allowSubObject = false;
288
    }
289

290
    if(singleSelect) {
291
        singleParent = true;
292
        ui->treeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
293
    } else {
294
        ui->treeWidget->setSelectionMode(QAbstractItemView::MultiSelection);
295
    }
296

297
    ui->checkSubObject->setVisible(allowSubObject);
298

299
    if(!allowSubObject) {
300
        ui->treeWidget->setColumnCount(1);
301
    } else {
302
        ui->treeWidget->setColumnCount(2);
303

304
        // make sure to show a horizontal scrollbar if needed
305
        ui->treeWidget->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
306
    }
307

308
    std::set<App::Document*> expandDocs;
309

310
    if(oldLinks.empty()) {
311
        expandDocs.insert(owner->getDocument());
312
    } else {
313
        for(auto &link : oldLinks) {
314
            auto doc = link.getDocument();
315
            if(doc)
316
                expandDocs.insert(doc);
317
        }
318
    }
319

320
    QPixmap docIcon(Gui::BitmapFactory().pixmap("Document"));
321
    for(auto d : docs) {
322
        auto item = new QTreeWidgetItem(ui->treeWidget);
323
        item->setIcon(0, docIcon);
324
        item->setText(0, QString::fromUtf8(d->Label.getValue()));
325
        item->setData(0, Qt::UserRole, QByteArray(""));
326
        item->setData(0, Qt::UserRole+1, QByteArray(d->getName()));
327
        item->setFlags(Qt::ItemIsEnabled);
328
        item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
329
        if(expandDocs.count(d))
330
            item->setExpanded(true);
331
        docItems[d] = item;
332
    }
333

334
    if(allowSubObject) {
335
        if (propLink->testFlag(App::PropertyLinkBase::LinkSyncSubObject))
336
            ui->checkSubObject->setChecked(true);
337
        else {
338
            for(auto &link : oldLinks) {
339
                auto sobj = link.getSubObject();
340
                if(sobj && sobj!=link.getObject()) {
341
                    ui->checkSubObject->setChecked(true);
342
                    break;
343
                }
344
            }
345
        }
346
    }
347

348
    if(oldLinks.isEmpty())
349
        return;
350

351
    // Try to select items corresponding to the current links inside the
352
    // property
353
    ui->treeWidget->blockSignals(true);
354
    for(auto &link : oldLinks) {
355
        onSelectionChanged(Gui::SelectionChanges(SelectionChanges::AddSelection,
356
                                                 link.getDocumentName(),
357
                                                 link.getObjectName(),
358
                                                 link.getSubName()));
359
    }
360
    ui->treeWidget->blockSignals(false);
361

362
    // For link list type property, try to auto filter type
363
    if(tryFilter && isLinkList) {
364
        Base::Type objType;
365
        for(const auto& link : qAsConst(oldLinks)) {
366
            auto obj = link.getSubObject();
367
            if(!obj)
368
                continue;
369
            if(objType.isBad()) {
370
                objType = obj->getTypeId();
371
                continue;
372
            }
373
            for(;objType != App::DocumentObject::getClassTypeId();
374
                 objType = objType.getParent())
375
            {
376
                if(obj->isDerivedFrom(objType))
377
                    break;
378
            }
379
        }
380

381
        Base::Type baseType;
382
        // get only geometric types
383
        if (objType.isDerivedFrom(App::GeoFeature::getClassTypeId()))
384
            baseType = App::GeoFeature::getClassTypeId();
385
        else
386
            baseType = App::DocumentObject::getClassTypeId();
387

388
        // get the direct base class of App::DocumentObject which 'obj' is derived from
389
        while (!objType.isBad()) {
390
            Base::Type parType = objType.getParent();
391
            if (parType == baseType) {
392
                baseType = objType;
393
                break;
394
            }
395
            objType = parType;
396
        }
397

398
        if(!baseType.isBad()) {
399
            const char *name = baseType.getName();
400
            auto it = typeItems.find(QByteArray::fromRawData(name,strlen(name)+1));
401
            if(it != typeItems.end())
402
                it->second->setSelected(true);
403
            ui->checkObjectType->setChecked(true);
404
        }
405
    }
406
}
407

408
void DlgPropertyLink::onClicked(QAbstractButton *button) {
409
    if(button == resetButton) {
410
        ui->treeWidget->blockSignals(true);
411
        ui->treeWidget->selectionModel()->clearSelection();
412
        for(auto item : subSelections)
413
            item->setText(1, QString());
414
        ui->treeWidget->blockSignals(false);
415
        subSelections.clear();
416
        Gui::Selection().clearSelection();
417
    } else if (button == refreshButton) {
418
        init(objProp);
419
    }
420
}
421

422
void DlgPropertyLink::hideEvent(QHideEvent *ev) {
423
    detachObserver();
424
    QDialog::hideEvent(ev);
425
}
426

427
void DlgPropertyLink::closeEvent(QCloseEvent *ev) {
428
    detachObserver();
429
    QDialog::closeEvent(ev);
430
}
431

432
void DlgPropertyLink::attachObserver() {
433
    if(isSelectionAttached())
434
        return;
435

436
    Gui::Selection().selStackPush();
437
    attachSelection();
438

439
    if(!parentView) {
440
        for(auto p=parent(); p; p=p->parent()) {
441
            auto view = qobject_cast<Gui::PropertyView*>(p);
442
            if(view) {
443
                parentView = view;
444
                for(auto &sel : Gui::Selection().getCompleteSelection(ResolveMode::NoResolve))
445
                    savedSelections.emplace_back(sel.DocName, sel.FeatName, sel.SubName);
446
                break;
447
            }
448
        }
449
    }
450
    auto view = qobject_cast<Gui::PropertyView*>(parentView.data());
451
    if(view)
452
        view->blockSelection(true);
453
}
454

455
void DlgPropertyLink::showEvent(QShowEvent *ev) {
456
    attachObserver();
457
    QDialog::showEvent(ev);
458
}
459

460
void DlgPropertyLink::onItemEntered(QTreeWidgetItem *) {
461
    int timeout = Gui::TreeParams::getPreSelectionDelay()/2;
462
    if(timeout < 0)
463
        timeout = 1;
464
    timer->start(timeout);
465
    Gui::Selection().rmvPreselect();
466
}
467

468
void DlgPropertyLink::leaveEvent(QEvent *ev) {
469
    Gui::Selection().rmvPreselect();
470
    QDialog::leaveEvent(ev);
471
}
472

473
void DlgPropertyLink::detachObserver() {
474
    if(isSelectionAttached())
475
        detachSelection();
476

477
    auto view = qobject_cast<Gui::PropertyView*>(parentView.data());
478
    if(view && !savedSelections.empty()) {
479
        try {
480
            Gui::Selection().clearSelection();
481
        }
482
        catch (Py::Exception& e) {
483
            e.clear();
484
        }
485
        for(auto &sel : savedSelections) {
486
            if(sel.getSubObject())
487
                Gui::Selection().addSelection(sel.getDocumentName().c_str(),
488
                                              sel.getObjectName().c_str(),
489
                                              sel.getSubName().c_str());
490
        }
491
        savedSelections.clear();
492
    }
493
    if(view)
494
        view->blockSelection(false);
495

496
    parentView = nullptr;
497
}
498

499
void DlgPropertyLink::onItemSelectionChanged()
500
{
501
    auto newSelections = ui->treeWidget->selectedItems();
502

503
    if(newSelections.isEmpty() || selections.contains(newSelections.back())) {
504
        selections = newSelections;
505
        if(newSelections.isEmpty())
506
            currentObj = nullptr;
507
        return;
508
    }
509

510
    selections = newSelections;
511

512
    auto sobjs = getLinkFromItem(newSelections.back());
513
    App::DocumentObject *obj = !sobjs.empty()?sobjs.front().getObject():nullptr;
514
    if(!obj) {
515
        Gui::Selection().clearSelection();
516
        return;
517
    }
518

519
    bool focus = false;
520
    // Do auto view switch if tree view does not do it
521
    if(!TreeParams::getSyncView()) {
522
        focus = ui->treeWidget->hasFocus();
523
        auto doc = Gui::Application::Instance->getDocument(sobjs.front().getDocumentName().c_str());
524
        if(doc) {
525
            auto vp = Base::freecad_dynamic_cast<Gui::ViewProviderDocumentObject>(
526
                    doc->getViewProvider(obj));
527
            if(vp) {
528
                // If the view provider uses a special window for rendering, switch to it
529
                MDIView *view = vp->getMDIView();
530
                if (view) {
531
                    doc->setActiveWindow(view);
532
                }
533
                else {
534
                    doc->setActiveView(vp, Gui::View3DInventor::getClassTypeId());
535
                }
536
            }
537
        }
538
    }
539

540
    // Sync 3d view selection. To give a better visual feedback, we
541
    // only keep the latest selection.
542
    bool blocked = blockSelection(true);
543
    Gui::Selection().clearSelection();
544
    for(auto &sobj : sobjs)
545
        Gui::Selection().addSelection(sobj.getDocumentName().c_str(),
546
                                      sobj.getObjectName().c_str(),
547
                                      sobj.getSubName().c_str());
548
    blockSelection(blocked);
549

550
    // Enforce single parent
551
    if(singleParent && currentObj && currentObj!=obj) {
552
        ui->treeWidget->blockSignals(true);
553
        const auto items = ui->treeWidget->selectedItems();
554
        for(auto item : items) {
555
            if(item != selections.back())
556
                item->setSelected(false);
557
        }
558
        auto last = selections.back();
559
        selections.clear();
560
        selections.append(last);
561
        ui->treeWidget->blockSignals(false);
562
    }
563
    currentObj = obj;
564

565
    if(focus) {
566
        // FIXME: does not work, why?
567
        ui->treeWidget->setFocus();
568
    }
569
}
570

571
QTreeWidgetItem *DlgPropertyLink::findItem(
572
        App::DocumentObject *obj, const char *subname, bool *pfound)
573
{
574
    if(pfound)
575
        *pfound = false;
576

577
    if(!obj || !obj->isAttachedToDocument())
578
        return nullptr;
579

580
    std::vector<App::DocumentObject *> sobjs;
581
    if(subname && subname[0]) {
582
        if(!allowSubObject) {
583
            obj = obj->getSubObject(subname);
584
            if(!obj)
585
                return nullptr;
586
        } else {
587
            sobjs = obj->getSubObjectList(subname);
588
        }
589
    }
590

591
    auto itDoc = docItems.find(obj->getDocument());
592
    if(itDoc == docItems.end())
593
        return nullptr;
594
    onItemExpanded(itDoc->second);
595

596
    auto it = itemMap.find(obj);
597
    if(it == itemMap.end() || it->second->isHidden())
598
        return nullptr;
599

600
    if(!allowSubObject) {
601
        if(pfound)
602
            *pfound = true;
603
        return it->second;
604
    }
605

606
    QTreeWidgetItem *item = it->second;
607

608
    bool first = true;
609
    for(auto o : sobjs) {
610
        if(first) {
611
            first = false;
612
            continue;
613
        }
614
        onItemExpanded(item);
615
        bool found = false;
616
        for(int i=0,count=item->childCount();i<count;++i) {
617
            auto child = item->child(i);
618
            if(strcmp(o->getNameInDocument(),
619
                        child->data(0, Qt::UserRole).toByteArray().constData())==0)
620
            {
621
                item = child;
622
                found = true;
623
                break;
624
            }
625
        }
626
        if(!found)
627
            return item;
628
    }
629
    if(pfound)
630
        *pfound = true;
631
    return item;
632
}
633

634
void DlgPropertyLink::onSelectionChanged(const Gui::SelectionChanges& msg)
635
{
636
    if (msg.Type != SelectionChanges::AddSelection)
637
        return;
638

639
    bool found = false;
640
    auto selObj = msg.Object.getObject();
641

642
    std::pair<std::string,std::string> elementName;
643
    const char *subname = msg.pSubName;
644
    if(!ui->checkSubObject->isChecked()) {
645
        selObj = App::GeoFeature::resolveElement(selObj,subname,elementName);
646
        if(!selObj)
647
            return;
648
        subname = elementName.second.c_str();
649
    }
650

651
    auto item = findItem(selObj, msg.pSubName, &found);
652
    if(!item || !found)
653
        return;
654

655
    if(!item->isSelected()) {
656
        ui->treeWidget->blockSignals(true);
657
        if(singleSelect || (singleParent && currentObj && currentObj!=selObj))
658
            ui->treeWidget->selectionModel()->clearSelection();
659
        currentObj = selObj;
660
        item->setSelected(true);
661
        selections.append(item);
662
        ui->treeWidget->blockSignals(false);
663
    }
664

665
    ui->treeWidget->scrollToItem(item);
666
    if(allowSubObject) {
667
        QString element = QString::fromLatin1(msg.Object.getOldElementName().c_str());
668
        if(element.size()) {
669
            QStringList list;
670
            QString text = item->text(1);
671
            if(text.size())
672
                list = text.split(QLatin1Char(','));
673
            if(list.indexOf(element)<0) {
674
                list << element;
675
                item->setText(1, list.join(QLatin1String(",")));
676
                subSelections.insert(item);
677
            }
678
        } else if (subSelections.erase(item))
679
            item->setText(1, QString());
680
    }
681
}
682

683
void DlgPropertyLink::accept()
684
{
685
    QDialog::accept();
686
}
687

688
static QTreeWidgetItem *_getLinkFromItem(std::ostringstream &ss, QTreeWidgetItem *item, const char *objName) {
689
    auto parent = item->parent();
690
    assert(parent);
691
    QByteArray nextName = parent->data(0, Qt::UserRole).toByteArray();
692
    if (nextName.isEmpty())
693
        return item;
694

695
    item = _getLinkFromItem(ss, parent, nextName);
696
    ss << objName << '.';
697
    return item;
698
}
699

700
QList<App::SubObjectT>
701
DlgPropertyLink::getLinkFromItem(QTreeWidgetItem *item, bool needSubName) const
702
{
703
    QList<App::SubObjectT> res;
704

705
    auto parent = item->parent();
706
    if(!parent)
707
        return res;
708

709
    std::ostringstream ss;
710
    auto parentItem = _getLinkFromItem(ss, item,
711
            item->data(0,Qt::UserRole).toByteArray().constData());
712

713
    App::SubObjectT sobj(parentItem->data(0, Qt::UserRole+1).toByteArray().constData(),
714
                         parentItem->data(0, Qt::UserRole).toByteArray().constData(),
715
                         ss.str().c_str());
716

717
    QString elements;
718
    if(needSubName && allowSubObject)
719
        elements = item->text(1);
720

721
    if(elements.isEmpty()) {
722
        res.append(App::SubObjectT());
723
        res.last() = std::move(sobj);
724
        return res;
725
    }
726

727
    const auto split = elements.split(QLatin1Char(','));
728
    for(const QString &element : split) {
729
        res.append(App::SubObjectT());
730
        res.last() = App::SubObjectT(sobj.getDocumentName().c_str(),
731
                                     sobj.getObjectName().c_str(),
732
                                     (sobj.getSubName() + element.toLatin1().constData()).c_str());
733
    }
734
    return res;
735
}
736

737
void DlgPropertyLink::onTimer() {
738
    auto item = ui->treeWidget->itemAt(
739
            ui->treeWidget->viewport()->mapFromGlobal(QCursor::pos()));
740
    if(!item)
741
        return;
742
    auto sobjs = getLinkFromItem(item);
743
    if(sobjs.isEmpty())
744
        return;
745
    const auto &sobj = sobjs.front();
746
    Gui::Selection().setPreselect(sobj.getDocumentName().c_str(),
747
                                  sobj.getObjectName().c_str(),
748
                                  sobj.getSubName().c_str(),
749
                                  0, 0, 0, Gui::SelectionChanges::MsgSource::TreeView);
750
}
751

752
QList<App::SubObjectT> DlgPropertyLink::currentLinks() const
753
{
754
    auto items = ui->treeWidget->selectedItems();
755
    QList<App::SubObjectT> res;
756
    for(auto item : items)
757
        res.append(getLinkFromItem(item));
758
    return res;
759
}
760

761
QList<App::SubObjectT> DlgPropertyLink::originalLinks() const
762
{
763
    return oldLinks;
764
}
765

766
QString DlgPropertyLink::linksToPython(const QList<App::SubObjectT>& links) {
767
    if(links.isEmpty())
768
        return QLatin1String("None");
769

770
    if(links.size() == 1)
771
        return QString::fromLatin1(links.front().getSubObjectPython(false).c_str());
772

773
    std::ostringstream ss;
774

775
    if(isLinkSub(links)) {
776
        ss << '(' << links.front().getObjectPython() << ", [";
777
        for(const auto& link : links) {
778
            const auto &sub = link.getSubName();
779
            if(!sub.empty())
780
                ss << "u'" << Base::Tools::escapedUnicodeFromUtf8(sub.c_str()) << "',";
781
        }
782
        ss << "])";
783
    } else {
784
        ss << '[';
785
        for(const auto& link : links)
786
            ss << link.getSubObjectPython(false) << ',';
787
        ss << ']';
788
    }
789

790
    return QString::fromLatin1(ss.str().c_str());
791
}
792

793
void DlgPropertyLink::filterObjects()
794
{
795
    for(int i=0, count=ui->treeWidget->topLevelItemCount(); i<count; ++i) {
796
        auto item = ui->treeWidget->topLevelItem(i);
797
        for(int j=0, c=item->childCount(); j<c; ++j)
798
            filterItem(item->child(j));
799
    }
800
}
801

802
void DlgPropertyLink::filterItem(QTreeWidgetItem *item) {
803
    if(filterType(item)) {
804
        item->setHidden(true);
805
        return;
806
    }
807
    item->setHidden(false);
808
    for(int i=0, count=item->childCount(); i<count; ++i)
809
        filterItem(item->child(i));
810
}
811

812
bool DlgPropertyLink::eventFilter(QObject *obj, QEvent *e) {
813
    if(obj == ui->searchBox
814
            && e->type() == QEvent::KeyPress
815
            && static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape)
816
    {
817
        ui->searchBox->setText(QString());
818
        return true;
819
    }
820
    return QDialog::eventFilter(obj,e);
821
}
822

823
void DlgPropertyLink::onItemSearch() {
824
    itemSearch(ui->searchBox->text(), true);
825
}
826

827
void DlgPropertyLink::keyPressEvent(QKeyEvent *ev)
828
{
829
    if(ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
830
        if(ui->searchBox->hasFocus())
831
            return;
832
    }
833
    QDialog::keyPressEvent(ev);
834
}
835

836
void DlgPropertyLink::itemSearch(const QString &text, bool select) {
837
    if(searchItem)
838
        searchItem->setBackground(0, bgBrush);
839

840
    auto owner = objProp.getObject();
841
    if(!owner)
842
        return;
843

844
    std::string txt(text.toUtf8().constData());
845
    try {
846
        if(txt.empty())
847
            return;
848
        if(txt.find("<<") == std::string::npos) {
849
            auto pos = txt.find('.');
850
            if(pos==std::string::npos)
851
                txt += '.';
852
            else if(pos!=txt.size()-1) {
853
                txt.insert(pos+1,"<<");
854
                if(txt.back()!='.')
855
                    txt += '.';
856
                txt += ">>.";
857
            }
858
        }else if(txt.back() != '.')
859
            txt += '.';
860
        txt += "_self";
861
        auto path = App::ObjectIdentifier::parse(owner,txt);
862
        if(path.getPropertyName() != "_self")
863
            return;
864

865
        App::DocumentObject *obj = path.getDocumentObject();
866
        if(!obj)
867
            return;
868

869
        bool found;
870
        const char *subname = path.getSubObjectName().c_str();
871
        QTreeWidgetItem *item = findItem(obj, subname, &found);
872
        if(!item)
873
            return;
874

875
        if(select) {
876
            if(!found)
877
                return;
878
            Gui::Selection().addSelection(obj->getDocument()->getName(),
879
                    obj->getNameInDocument(),subname);
880
        }else{
881
            Selection().setPreselect(obj->getDocument()->getName(),
882
                    obj->getNameInDocument(), subname, 0, 0, 0,
883
                    Gui::SelectionChanges::MsgSource::TreeView);
884
            searchItem = item;
885
            ui->treeWidget->scrollToItem(searchItem);
886
            bgBrush = searchItem->background(0);
887
            searchItem->setBackground(0, QColor(255, 255, 0, 100));
888
        }
889
    } catch(...)
890
    {
891
    }
892
}
893

894
QTreeWidgetItem *DlgPropertyLink::createItem(
895
        App::DocumentObject *obj, QTreeWidgetItem *parent)
896
{
897
    if(!obj || !obj->isAttachedToDocument())
898
        return nullptr;
899

900
    if(inList.find(obj)!=inList.end())
901
        return nullptr;
902

903
    auto vp = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
904
            Application::Instance->getViewProvider(obj));
905
    if(!vp)
906
        return nullptr;
907

908
    QTreeWidgetItem* item;
909
    if(parent)
910
        item = new QTreeWidgetItem(parent);
911
    else
912
        item = new QTreeWidgetItem(ui->treeWidget);
913
    item->setIcon(0, vp->getIcon());
914
    item->setText(0, QString::fromUtf8((obj)->Label.getValue()));
915
    item->setData(0, Qt::UserRole, QByteArray(obj->getNameInDocument()));
916
    item->setData(0, Qt::UserRole+1, QByteArray(obj->getDocument()->getName()));
917

918
    if(allowSubObject) {
919
        item->setChildIndicatorPolicy(!obj->getLinkedObject(true)->getOutList().empty()?
920
                QTreeWidgetItem::ShowIndicator:QTreeWidgetItem::DontShowIndicator);
921
        item->setFlags(item->flags() | Qt::ItemIsEditable | Qt::ItemIsUserCheckable);
922
    }
923

924
    const char *typeName = obj->getTypeId().getName();
925
    QByteArray typeData = QByteArray::fromRawData(typeName, strlen(typeName)+1);
926
    item->setData(0, Qt::UserRole+2, typeData);
927

928
    QByteArray proxyType;
929
    auto prop = Base::freecad_dynamic_cast<App::PropertyPythonObject>(
930
            obj->getPropertyByName("Proxy"));
931
    if(prop) {
932
        Base::PyGILStateLocker lock;
933
        Py::Object proxy = prop->getValue();
934
        if(!proxy.isNone() && !proxy.isString()) {
935
            const char *name = nullptr;
936
            if (proxy.hasAttr("__class__"))
937
                proxyType = QByteArray(proxy.getAttr("__class__").as_string().c_str());
938
            else {
939
                name = proxy.ptr()->ob_type->tp_name;
940
                proxyType = QByteArray::fromRawData(name, strlen(name)+1);
941
            }
942
            auto it = typeItems.find(proxyType);
943
            if(it != typeItems.end())
944
                proxyType = it->first;
945
            else if (name)
946
                proxyType = QByteArray(name, proxyType.size());
947
        }
948
    }
949
    item->setData(0, Qt::UserRole+3, proxyType);
950

951
    filterItem(item);
952
    return item;
953
}
954

955
QTreeWidgetItem *DlgPropertyLink::createTypeItem(Base::Type type) {
956
    if(type.isBad())
957
        return nullptr;
958

959
    QTreeWidgetItem *item = nullptr;
960
    if(!type.isBad() && type!=App::DocumentObject::getClassTypeId()) {
961
        Base::Type parentType = type.getParent();
962
        if(!parentType.isBad()) {
963
            const char *name = parentType.getName();
964
            auto typeData = QByteArray::fromRawData(name,strlen(name)+1);
965
            auto &typeItem = typeItems[typeData];
966
            if(!typeItem) {
967
                typeItem = createTypeItem(parentType);
968
                typeItem->setData(0, Qt::UserRole, typeData);
969
            }
970
            item = typeItem;
971
        }
972
    }
973

974
    if(!item)
975
        item = new QTreeWidgetItem(ui->typeTree);
976
    else
977
        item = new QTreeWidgetItem(item);
978
    item->setExpanded(true);
979
    item->setText(0, QString::fromLatin1(type.getName()));
980
    if(type == App::DocumentObject::getClassTypeId())
981
        item->setFlags(Qt::ItemIsEnabled);
982
    return item;
983
}
984

985
bool DlgPropertyLink::filterType(QTreeWidgetItem *item) {
986
    auto proxyType = item->data(0, Qt::UserRole+3).toByteArray();
987
    QTreeWidgetItem *proxyItem = nullptr;
988
    if(proxyType.size()) {
989
        auto &pitem = typeItems[proxyType];
990
        if(!pitem) {
991
            pitem = new QTreeWidgetItem(ui->typeTree);
992
            pitem->setText(0,QString::fromLatin1(proxyType));
993
            pitem->setIcon(0,item->icon(0));
994
            pitem->setData(0,Qt::UserRole,proxyType);
995
        }
996
        proxyItem = pitem;
997
    }
998

999
    auto typeData = item->data(0, Qt::UserRole+2).toByteArray();
1000
    Base::Type type = Base::Type::fromName(typeData.constData());
1001
    if(type.isBad())
1002
        return false;
1003

1004
    QTreeWidgetItem *&typeItem = typeItems[typeData];
1005
    if(!typeItem)  {
1006
        typeItem = createTypeItem(type);
1007
        typeItem->setData(0, Qt::UserRole, typeData);
1008
    }
1009

1010
    if(!proxyType.size()) {
1011
        QIcon icon = typeItem->icon(0);
1012
        if(icon.isNull())
1013
            typeItem->setIcon(0, item->icon(0));
1014
    }
1015

1016
    if(!ui->checkObjectType->isChecked() || selectedTypes.empty())
1017
        return false;
1018

1019
    if(proxyItem && selectedTypes.count(proxyType))
1020
        return false;
1021

1022
    for(auto t=type; !t.isBad() && t!=App::DocumentObject::getClassTypeId(); t=t.getParent()) {
1023
        const char *name = t.getName();
1024
        if(selectedTypes.count(QByteArray::fromRawData(name, strlen(name)+1)))
1025
            return false;
1026
    }
1027

1028
    return true;
1029
}
1030

1031
void DlgPropertyLink::onItemExpanded(QTreeWidgetItem * item) {
1032
    if(item->childCount())
1033
        return;
1034

1035
    QByteArray docName = item->data(0, Qt::UserRole+1).toByteArray();
1036
    auto doc = App::GetApplication().getDocument(docName);
1037
    if (!doc)
1038
        return;
1039

1040
    QByteArray objName = item->data(0, Qt::UserRole).toByteArray();
1041
    if (objName.isEmpty()) {
1042
        for(auto obj : doc->getObjects()) {
1043
            auto newItem = createItem(obj,item);
1044
            if(newItem)
1045
                itemMap[obj] = newItem;
1046
        }
1047
    } else if(allowSubObject) {
1048
        auto obj = doc->getObject(objName);
1049
        if(!obj)
1050
            return;
1051
        std::set<App::DocumentObject*> childSet;
1052
        std::string sub;
1053
        for(auto child : obj->getLinkedObject(true)->getOutList()) {
1054
            if(!childSet.insert(child).second)
1055
                continue;
1056
            sub = child->getNameInDocument();
1057
            sub += ".";
1058
            if(obj->getSubObject(sub.c_str()))
1059
                createItem(child,item);
1060
        }
1061
    }
1062
}
1063

1064
void DlgPropertyLink::onObjectTypeToggled(bool on)
1065
{
1066
    ui->typeTree->setVisible(on);
1067
    filterObjects();
1068
}
1069

1070
void DlgPropertyLink::onTypeTreeItemSelectionChanged() {
1071

1072
    selectedTypes.clear();
1073
    const auto items = ui->typeTree->selectedItems();
1074
    for(auto item : items)
1075
        selectedTypes.insert(item->data(0, Qt::UserRole).toByteArray());
1076

1077
    if(ui->checkObjectType->isChecked())
1078
        filterObjects();
1079
}
1080

1081
void DlgPropertyLink::onSearchBoxTextChanged(const QString& text)
1082
{
1083
    itemSearch(text,false);
1084
}
1085

1086
#include "moc_DlgPropertyLink.cpp"
1087

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

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

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

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