1
/***************************************************************************
2
* Copyright (c) 2014 Werner Mayer <wmayer[at]users.sourceforge.net> *
4
* This file is part of the FreeCAD CAx development system. *
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. *
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. *
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 *
21
***************************************************************************/
23
#include "PreCompiled.h"
27
# include <QStyledItemDelegate>
28
# include <QTreeWidgetItem>
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>
39
#include "DlgPropertyLink.h"
40
#include "ui_DlgPropertyLink.h"
41
#include "Application.h"
43
#include "BitmapFactory.h"
44
#include "PropertyView.h"
47
#include "TreeParams.h"
48
#include "View3DInventor.h"
49
#include "ViewProviderDocumentObject.h"
52
using namespace Gui::Dialog;
54
class ItemDelegate: public QStyledItemDelegate {
56
explicit ItemDelegate(QObject* parent=nullptr): QStyledItemDelegate(parent) {}
58
QWidget* createEditor(QWidget *parent,
59
const QStyleOptionViewItem &option, const QModelIndex &index) const override
61
if(index.column() != 1)
63
return QStyledItemDelegate::createEditor(parent, option, index);
67
/* TRANSLATOR Gui::Dialog::DlgPropertyLink */
69
DlgPropertyLink::DlgPropertyLink(QWidget* parent)
70
: QDialog(parent), SelectionObserver(false, ResolveMode::NoResolve)
71
, ui(new Ui_DlgPropertyLink)
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);
82
ui->searchBox->installEventFilter(this);
83
ui->searchBox->setNoProperty(true);
84
ui->searchBox->setExactMatch(Gui::ExpressionParameter::instance()->isExactMatch());
86
timer = new QTimer(this);
87
timer->setSingleShot(true);
89
connect(timer, &QTimer::timeout, this, &DlgPropertyLink::onTimer);
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);
97
connect(ui->treeWidget, &QTreeWidget::itemExpanded,
98
this, &DlgPropertyLink::onItemExpanded);
100
connect(ui->treeWidget, &QTreeWidget::itemSelectionChanged, this, &DlgPropertyLink::onItemSelectionChanged);
102
connect(ui->searchBox, &QLineEdit::returnPressed, this, &DlgPropertyLink::onItemSearch);
104
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &DlgPropertyLink::onClicked);
106
refreshButton = ui->buttonBox->addButton(tr("Reset"), QDialogButtonBox::ActionRole);
107
resetButton = ui->buttonBox->addButton(tr("Clear"), QDialogButtonBox::ResetRole);
111
* Destroys the object and frees any allocated resources
113
DlgPropertyLink::~DlgPropertyLink()
117
// no need to delete child widgets, Qt does it all for us
121
QList<App::SubObjectT> DlgPropertyLink::getLinksFromProperty(const App::PropertyLinkBase *prop)
123
QList<App::SubObjectT> res;
127
std::vector<App::DocumentObject*> objs;
128
std::vector<std::string> subs;
129
prop->getLinks(objs,true,&subs,false);
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()));
139
res.push_back(App::SubObjectT(obj,subs[i++].c_str()));
144
QString DlgPropertyLink::formatObject(App::Document *ownerDoc, App::DocumentObject *obj, const char *sub)
146
if(!obj || !obj->isAttachedToDocument())
147
return QLatin1String("?");
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();
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()));
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));
168
return QString::fromLatin1("%1.%2 (%3)").arg(QLatin1String(objName),
169
QString::fromUtf8(sub),
170
QString::fromUtf8(sobj->Label.getValue()));
173
static inline bool isLinkSub(const QList<App::SubObjectT>& links)
175
for(const auto &link : links) {
176
if(&link == &links.front())
178
if(link.getDocumentName() != links.front().getDocumentName()
179
|| link.getObjectName() != links.front().getObjectName())
187
QString DlgPropertyLink::formatLinks(App::Document *ownerDoc, QList<App::SubObjectT> links)
189
if(!ownerDoc || links.empty())
192
auto obj = links.front().getObject();
194
return QLatin1String("?");
196
if(links.size() == 1 && links.front().getSubName().empty())
197
return formatObject(ownerDoc, links.front());
200
if(isLinkSub(links)) {
202
for(auto &link : links) {
203
list << QString::fromUtf8(link.getSubName().c_str());
207
return QString::fromLatin1("%1 [%2%3]").arg(formatObject(ownerDoc,obj,nullptr),
208
list.join(QLatin1String(", ")),
209
QLatin1String(links.size()>3?" ...":""));
213
for(auto &link : links) {
214
list << formatObject(ownerDoc,link);
218
return QString::fromLatin1("[%1%2]").arg(list.join(QLatin1String(", ")),
219
QLatin1String(links.size()>3?" ...":""));
222
void DlgPropertyLink::init(const App::DocumentObjectT &prop, bool tryFilter) {
223
ui->treeWidget->blockSignals(true);
224
ui->treeWidget->clear();
225
ui->treeWidget->blockSignals(false);
227
ui->typeTree->blockSignals(true);
228
ui->typeTree->clear();
229
ui->typeTree->blockSignals(false);
236
selectedTypes.clear();
237
currentObj = nullptr;
238
searchItem = nullptr;
239
subSelections.clear();
243
auto owner = objProp.getObject();
244
if(!owner || !owner->isAttachedToDocument())
247
ui->searchBox->setDocumentObject(owner);
249
auto propLink = Base::freecad_dynamic_cast<App::PropertyLinkBase>(objProp.getProperty());
253
oldLinks = getLinksFromProperty(propLink);
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);
262
std::vector<App::Document*> docs;
264
singleSelect = false;
265
if(propLink->isDerivedFrom(App::PropertyXLinkSub::getClassTypeId())
266
|| propLink->isDerivedFrom(App::PropertyLinkSub::getClassTypeId()))
268
allowSubObject = true;
270
} else if (propLink->isDerivedFrom(App::PropertyLink::getClassTypeId())) {
272
} else if (propLink->isDerivedFrom(App::PropertyLinkSubList::getClassTypeId())) {
273
allowSubObject = true;
276
if(App::PropertyXLink::supportXLink(propLink)) {
277
allowSubObject = true;
278
docs = App::GetApplication().getDocuments();
280
docs.push_back(owner->getDocument());
282
bool isLinkList = false;
283
if (propLink->isDerivedFrom(App::PropertyXLinkList::getClassTypeId())
284
|| propLink->isDerivedFrom(App::PropertyLinkList::getClassTypeId()))
287
allowSubObject = false;
292
ui->treeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
294
ui->treeWidget->setSelectionMode(QAbstractItemView::MultiSelection);
297
ui->checkSubObject->setVisible(allowSubObject);
299
if(!allowSubObject) {
300
ui->treeWidget->setColumnCount(1);
302
ui->treeWidget->setColumnCount(2);
304
// make sure to show a horizontal scrollbar if needed
305
ui->treeWidget->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
308
std::set<App::Document*> expandDocs;
310
if(oldLinks.empty()) {
311
expandDocs.insert(owner->getDocument());
313
for(auto &link : oldLinks) {
314
auto doc = link.getDocument();
316
expandDocs.insert(doc);
320
QPixmap docIcon(Gui::BitmapFactory().pixmap("Document"));
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);
335
if (propLink->testFlag(App::PropertyLinkBase::LinkSyncSubObject))
336
ui->checkSubObject->setChecked(true);
338
for(auto &link : oldLinks) {
339
auto sobj = link.getSubObject();
340
if(sobj && sobj!=link.getObject()) {
341
ui->checkSubObject->setChecked(true);
348
if(oldLinks.isEmpty())
351
// Try to select items corresponding to the current links inside the
353
ui->treeWidget->blockSignals(true);
354
for(auto &link : oldLinks) {
355
onSelectionChanged(Gui::SelectionChanges(SelectionChanges::AddSelection,
356
link.getDocumentName(),
357
link.getObjectName(),
360
ui->treeWidget->blockSignals(false);
362
// For link list type property, try to auto filter type
363
if(tryFilter && isLinkList) {
365
for(const auto& link : qAsConst(oldLinks)) {
366
auto obj = link.getSubObject();
369
if(objType.isBad()) {
370
objType = obj->getTypeId();
373
for(;objType != App::DocumentObject::getClassTypeId();
374
objType = objType.getParent())
376
if(obj->isDerivedFrom(objType))
382
// get only geometric types
383
if (objType.isDerivedFrom(App::GeoFeature::getClassTypeId()))
384
baseType = App::GeoFeature::getClassTypeId();
386
baseType = App::DocumentObject::getClassTypeId();
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) {
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);
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) {
422
void DlgPropertyLink::hideEvent(QHideEvent *ev) {
424
QDialog::hideEvent(ev);
427
void DlgPropertyLink::closeEvent(QCloseEvent *ev) {
429
QDialog::closeEvent(ev);
432
void DlgPropertyLink::attachObserver() {
433
if(isSelectionAttached())
436
Gui::Selection().selStackPush();
440
for(auto p=parent(); p; p=p->parent()) {
441
auto view = qobject_cast<Gui::PropertyView*>(p);
444
for(auto &sel : Gui::Selection().getCompleteSelection(ResolveMode::NoResolve))
445
savedSelections.emplace_back(sel.DocName, sel.FeatName, sel.SubName);
450
auto view = qobject_cast<Gui::PropertyView*>(parentView.data());
452
view->blockSelection(true);
455
void DlgPropertyLink::showEvent(QShowEvent *ev) {
457
QDialog::showEvent(ev);
460
void DlgPropertyLink::onItemEntered(QTreeWidgetItem *) {
461
int timeout = Gui::TreeParams::getPreSelectionDelay()/2;
464
timer->start(timeout);
465
Gui::Selection().rmvPreselect();
468
void DlgPropertyLink::leaveEvent(QEvent *ev) {
469
Gui::Selection().rmvPreselect();
470
QDialog::leaveEvent(ev);
473
void DlgPropertyLink::detachObserver() {
474
if(isSelectionAttached())
477
auto view = qobject_cast<Gui::PropertyView*>(parentView.data());
478
if(view && !savedSelections.empty()) {
480
Gui::Selection().clearSelection();
482
catch (Py::Exception& e) {
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());
491
savedSelections.clear();
494
view->blockSelection(false);
496
parentView = nullptr;
499
void DlgPropertyLink::onItemSelectionChanged()
501
auto newSelections = ui->treeWidget->selectedItems();
503
if(newSelections.isEmpty() || selections.contains(newSelections.back())) {
504
selections = newSelections;
505
if(newSelections.isEmpty())
506
currentObj = nullptr;
510
selections = newSelections;
512
auto sobjs = getLinkFromItem(newSelections.back());
513
App::DocumentObject *obj = !sobjs.empty()?sobjs.front().getObject():nullptr;
515
Gui::Selection().clearSelection();
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());
525
auto vp = Base::freecad_dynamic_cast<Gui::ViewProviderDocumentObject>(
526
doc->getViewProvider(obj));
528
// If the view provider uses a special window for rendering, switch to it
529
MDIView *view = vp->getMDIView();
531
doc->setActiveWindow(view);
534
doc->setActiveView(vp, Gui::View3DInventor::getClassTypeId());
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);
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);
558
auto last = selections.back();
560
selections.append(last);
561
ui->treeWidget->blockSignals(false);
566
// FIXME: does not work, why?
567
ui->treeWidget->setFocus();
571
QTreeWidgetItem *DlgPropertyLink::findItem(
572
App::DocumentObject *obj, const char *subname, bool *pfound)
577
if(!obj || !obj->isAttachedToDocument())
580
std::vector<App::DocumentObject *> sobjs;
581
if(subname && subname[0]) {
582
if(!allowSubObject) {
583
obj = obj->getSubObject(subname);
587
sobjs = obj->getSubObjectList(subname);
591
auto itDoc = docItems.find(obj->getDocument());
592
if(itDoc == docItems.end())
594
onItemExpanded(itDoc->second);
596
auto it = itemMap.find(obj);
597
if(it == itemMap.end() || it->second->isHidden())
600
if(!allowSubObject) {
606
QTreeWidgetItem *item = it->second;
609
for(auto o : sobjs) {
614
onItemExpanded(item);
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)
634
void DlgPropertyLink::onSelectionChanged(const Gui::SelectionChanges& msg)
636
if (msg.Type != SelectionChanges::AddSelection)
640
auto selObj = msg.Object.getObject();
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);
648
subname = elementName.second.c_str();
651
auto item = findItem(selObj, msg.pSubName, &found);
655
if(!item->isSelected()) {
656
ui->treeWidget->blockSignals(true);
657
if(singleSelect || (singleParent && currentObj && currentObj!=selObj))
658
ui->treeWidget->selectionModel()->clearSelection();
660
item->setSelected(true);
661
selections.append(item);
662
ui->treeWidget->blockSignals(false);
665
ui->treeWidget->scrollToItem(item);
667
QString element = QString::fromLatin1(msg.Object.getOldElementName().c_str());
670
QString text = item->text(1);
672
list = text.split(QLatin1Char(','));
673
if(list.indexOf(element)<0) {
675
item->setText(1, list.join(QLatin1String(",")));
676
subSelections.insert(item);
678
} else if (subSelections.erase(item))
679
item->setText(1, QString());
683
void DlgPropertyLink::accept()
688
static QTreeWidgetItem *_getLinkFromItem(std::ostringstream &ss, QTreeWidgetItem *item, const char *objName) {
689
auto parent = item->parent();
691
QByteArray nextName = parent->data(0, Qt::UserRole).toByteArray();
692
if (nextName.isEmpty())
695
item = _getLinkFromItem(ss, parent, nextName);
696
ss << objName << '.';
700
QList<App::SubObjectT>
701
DlgPropertyLink::getLinkFromItem(QTreeWidgetItem *item, bool needSubName) const
703
QList<App::SubObjectT> res;
705
auto parent = item->parent();
709
std::ostringstream ss;
710
auto parentItem = _getLinkFromItem(ss, item,
711
item->data(0,Qt::UserRole).toByteArray().constData());
713
App::SubObjectT sobj(parentItem->data(0, Qt::UserRole+1).toByteArray().constData(),
714
parentItem->data(0, Qt::UserRole).toByteArray().constData(),
718
if(needSubName && allowSubObject)
719
elements = item->text(1);
721
if(elements.isEmpty()) {
722
res.append(App::SubObjectT());
723
res.last() = std::move(sobj);
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());
737
void DlgPropertyLink::onTimer() {
738
auto item = ui->treeWidget->itemAt(
739
ui->treeWidget->viewport()->mapFromGlobal(QCursor::pos()));
742
auto sobjs = getLinkFromItem(item);
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);
752
QList<App::SubObjectT> DlgPropertyLink::currentLinks() const
754
auto items = ui->treeWidget->selectedItems();
755
QList<App::SubObjectT> res;
756
for(auto item : items)
757
res.append(getLinkFromItem(item));
761
QList<App::SubObjectT> DlgPropertyLink::originalLinks() const
766
QString DlgPropertyLink::linksToPython(const QList<App::SubObjectT>& links) {
768
return QLatin1String("None");
770
if(links.size() == 1)
771
return QString::fromLatin1(links.front().getSubObjectPython(false).c_str());
773
std::ostringstream ss;
775
if(isLinkSub(links)) {
776
ss << '(' << links.front().getObjectPython() << ", [";
777
for(const auto& link : links) {
778
const auto &sub = link.getSubName();
780
ss << "u'" << Base::Tools::escapedUnicodeFromUtf8(sub.c_str()) << "',";
785
for(const auto& link : links)
786
ss << link.getSubObjectPython(false) << ',';
790
return QString::fromLatin1(ss.str().c_str());
793
void DlgPropertyLink::filterObjects()
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));
802
void DlgPropertyLink::filterItem(QTreeWidgetItem *item) {
803
if(filterType(item)) {
804
item->setHidden(true);
807
item->setHidden(false);
808
for(int i=0, count=item->childCount(); i<count; ++i)
809
filterItem(item->child(i));
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)
817
ui->searchBox->setText(QString());
820
return QDialog::eventFilter(obj,e);
823
void DlgPropertyLink::onItemSearch() {
824
itemSearch(ui->searchBox->text(), true);
827
void DlgPropertyLink::keyPressEvent(QKeyEvent *ev)
829
if(ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
830
if(ui->searchBox->hasFocus())
833
QDialog::keyPressEvent(ev);
836
void DlgPropertyLink::itemSearch(const QString &text, bool select) {
838
searchItem->setBackground(0, bgBrush);
840
auto owner = objProp.getObject();
844
std::string txt(text.toUtf8().constData());
848
if(txt.find("<<") == std::string::npos) {
849
auto pos = txt.find('.');
850
if(pos==std::string::npos)
852
else if(pos!=txt.size()-1) {
853
txt.insert(pos+1,"<<");
858
}else if(txt.back() != '.')
861
auto path = App::ObjectIdentifier::parse(owner,txt);
862
if(path.getPropertyName() != "_self")
865
App::DocumentObject *obj = path.getDocumentObject();
870
const char *subname = path.getSubObjectName().c_str();
871
QTreeWidgetItem *item = findItem(obj, subname, &found);
878
Gui::Selection().addSelection(obj->getDocument()->getName(),
879
obj->getNameInDocument(),subname);
881
Selection().setPreselect(obj->getDocument()->getName(),
882
obj->getNameInDocument(), subname, 0, 0, 0,
883
Gui::SelectionChanges::MsgSource::TreeView);
885
ui->treeWidget->scrollToItem(searchItem);
886
bgBrush = searchItem->background(0);
887
searchItem->setBackground(0, QColor(255, 255, 0, 100));
894
QTreeWidgetItem *DlgPropertyLink::createItem(
895
App::DocumentObject *obj, QTreeWidgetItem *parent)
897
if(!obj || !obj->isAttachedToDocument())
900
if(inList.find(obj)!=inList.end())
903
auto vp = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
904
Application::Instance->getViewProvider(obj));
908
QTreeWidgetItem* item;
910
item = new QTreeWidgetItem(parent);
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()));
919
item->setChildIndicatorPolicy(!obj->getLinkedObject(true)->getOutList().empty()?
920
QTreeWidgetItem::ShowIndicator:QTreeWidgetItem::DontShowIndicator);
921
item->setFlags(item->flags() | Qt::ItemIsEditable | Qt::ItemIsUserCheckable);
924
const char *typeName = obj->getTypeId().getName();
925
QByteArray typeData = QByteArray::fromRawData(typeName, strlen(typeName)+1);
926
item->setData(0, Qt::UserRole+2, typeData);
928
QByteArray proxyType;
929
auto prop = Base::freecad_dynamic_cast<App::PropertyPythonObject>(
930
obj->getPropertyByName("Proxy"));
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());
939
name = proxy.ptr()->ob_type->tp_name;
940
proxyType = QByteArray::fromRawData(name, strlen(name)+1);
942
auto it = typeItems.find(proxyType);
943
if(it != typeItems.end())
944
proxyType = it->first;
946
proxyType = QByteArray(name, proxyType.size());
949
item->setData(0, Qt::UserRole+3, proxyType);
955
QTreeWidgetItem *DlgPropertyLink::createTypeItem(Base::Type type) {
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];
967
typeItem = createTypeItem(parentType);
968
typeItem->setData(0, Qt::UserRole, typeData);
975
item = new QTreeWidgetItem(ui->typeTree);
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);
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];
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);
999
auto typeData = item->data(0, Qt::UserRole+2).toByteArray();
1000
Base::Type type = Base::Type::fromName(typeData.constData());
1004
QTreeWidgetItem *&typeItem = typeItems[typeData];
1006
typeItem = createTypeItem(type);
1007
typeItem->setData(0, Qt::UserRole, typeData);
1010
if(!proxyType.size()) {
1011
QIcon icon = typeItem->icon(0);
1013
typeItem->setIcon(0, item->icon(0));
1016
if(!ui->checkObjectType->isChecked() || selectedTypes.empty())
1019
if(proxyItem && selectedTypes.count(proxyType))
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)))
1031
void DlgPropertyLink::onItemExpanded(QTreeWidgetItem * item) {
1032
if(item->childCount())
1035
QByteArray docName = item->data(0, Qt::UserRole+1).toByteArray();
1036
auto doc = App::GetApplication().getDocument(docName);
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);
1045
itemMap[obj] = newItem;
1047
} else if(allowSubObject) {
1048
auto obj = doc->getObject(objName);
1051
std::set<App::DocumentObject*> childSet;
1053
for(auto child : obj->getLinkedObject(true)->getOutList()) {
1054
if(!childSet.insert(child).second)
1056
sub = child->getNameInDocument();
1058
if(obj->getSubObject(sub.c_str()))
1059
createItem(child,item);
1064
void DlgPropertyLink::onObjectTypeToggled(bool on)
1066
ui->typeTree->setVisible(on);
1070
void DlgPropertyLink::onTypeTreeItemSelectionChanged() {
1072
selectedTypes.clear();
1073
const auto items = ui->typeTree->selectedItems();
1074
for(auto item : items)
1075
selectedTypes.insert(item->data(0, Qt::UserRole).toByteArray());
1077
if(ui->checkObjectType->isChecked())
1081
void DlgPropertyLink::onSearchBoxTextChanged(const QString& text)
1083
itemSearch(text,false);
1086
#include "moc_DlgPropertyLink.cpp"