Files
create/src/Gui/DlgPropertyLink.cpp

1064 lines
33 KiB
C++

/***************************************************************************
* Copyright (c) 2014 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <algorithm>
# include <sstream>
# include <QTreeWidgetItem>
# include <QMessageBox>
# include <QPushButton>
#endif
#include <QStyledItemDelegate>
#include <Base/Tools.h>
#include <App/Application.h>
#include <App/Document.h>
#include <App/DocumentObject.h>
#include <App/GeoFeature.h>
#include <App/DocumentObserver.h>
#include "Document.h"
#include "View3DInventor.h"
#include "Tree.h"
#include "Selection.h"
#include "PropertyView.h"
#include "BitmapFactory.h"
#include "DlgPropertyLink.h"
#include "Application.h"
#include "ViewProviderDocumentObject.h"
#include "MetaTypes.h"
#include "ui_DlgPropertyLink.h"
using namespace Gui::Dialog;
class ItemDelegate: public QStyledItemDelegate {
public:
ItemDelegate(QObject* parent=0): QStyledItemDelegate(parent) {}
virtual QWidget* createEditor(QWidget *parent,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if(index.column() != 1)
return nullptr;
return QStyledItemDelegate::createEditor(parent, option, index);
}
};
/* TRANSLATOR Gui::Dialog::DlgPropertyLink */
DlgPropertyLink::DlgPropertyLink(QWidget* parent)
: QDialog(parent), SelectionObserver(false,0)
, ui(new Ui_DlgPropertyLink)
{
ui->setupUi(this);
ui->typeTree->hide();
ui->searchBox->installEventFilter(this);
ui->searchBox->setNoProperty(true);
ui->searchBox->setExactMatch(Gui::ExpressionParameter::instance()->isExactMatch());
timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, SIGNAL(timeout()), this, SLOT(onTimer()));
ui->treeWidget->setEditTriggers(QAbstractItemView::DoubleClicked);
ui->treeWidget->setItemDelegate(new ItemDelegate(this));
ui->treeWidget->setMouseTracking(true);
connect(ui->treeWidget, SIGNAL(itemEntered(QTreeWidgetItem*, int)),
this, SLOT(onItemEntered(QTreeWidgetItem*)));
connect(ui->treeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)),
this, SLOT(onItemExpanded(QTreeWidgetItem*)));
connect(ui->treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(onItemSelectionChanged()));
connect(ui->searchBox, SIGNAL(returnPressed()), this, SLOT(onItemSearch()));
connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(onClicked(QAbstractButton*)));
refreshButton = ui->buttonBox->addButton(tr("Reset"), QDialogButtonBox::ActionRole);
resetButton = ui->buttonBox->addButton(tr("Clear"), QDialogButtonBox::ResetRole);
}
/**
* Destroys the object and frees any allocated resources
*/
DlgPropertyLink::~DlgPropertyLink()
{
detachObserver();
// no need to delete child widgets, Qt does it all for us
delete ui;
}
QList<App::SubObjectT> DlgPropertyLink::getLinksFromProperty(const App::PropertyLinkBase *prop)
{
QList<App::SubObjectT> res;
if(!prop)
return res;
std::vector<App::DocumentObject*> objs;
std::vector<std::string> subs;
prop->getLinks(objs,true,&subs,false);
if(subs.empty()) {
for(auto obj : objs)
res.push_back(App::SubObjectT(obj,0));
} else if (objs.size()==1) {
for(auto &sub : subs)
res.push_back(App::SubObjectT(objs.front(),sub.c_str()));
} else {
int i=0;
for(auto obj : objs)
res.push_back(App::SubObjectT(obj,subs[i++].c_str()));
}
return res;
}
QString DlgPropertyLink::formatObject(App::Document *ownerDoc, App::DocumentObject *obj, const char *sub)
{
if(!obj || !obj->getNameInDocument())
return QLatin1String("?");
const char *objName = obj->getNameInDocument();
std::string _objName;
if(ownerDoc && ownerDoc!=obj->getDocument()) {
_objName = obj->getFullName();
objName = _objName.c_str();
}
if(!sub || !sub[0]) {
if(obj->Label.getStrValue() == obj->getNameInDocument())
return QLatin1String(objName);
return QString::fromLatin1("%1 (%2)").arg(QLatin1String(objName),
QString::fromUtf8(obj->Label.getValue()));
}
auto sobj = obj->getSubObject(sub);
if(!sobj || sobj->Label.getStrValue() == sobj->getNameInDocument())
return QString::fromLatin1("%1.%2").arg(QLatin1String(objName),
QString::fromUtf8(sub));
return QString::fromLatin1("%1.%2 (%3)").arg(QLatin1String(objName),
QString::fromUtf8(sub),
QString::fromUtf8(sobj->Label.getValue()));
}
static inline bool isLinkSub(QList<App::SubObjectT> links)
{
for(auto &link : links) {
if(&link == &links.front())
continue;
if(link.getDocumentName() != links.front().getDocumentName()
|| link.getObjectName() != links.front().getObjectName())
{
return false;
}
}
return true;
}
QString DlgPropertyLink::formatLinks(App::Document *ownerDoc, QList<App::SubObjectT> links)
{
if(!ownerDoc || links.empty())
return QString();
auto obj = links.front().getObject();
if(!obj)
return QLatin1String("?");
if(links.size() == 1 && links.front().getSubName().empty())
return formatObject(ownerDoc, links.front());
QStringList list;
if(isLinkSub(links)) {
int i = 0;
for(auto &link : links) {
list << QString::fromUtf8(link.getSubName().c_str());
if( ++i >= 3)
break;
}
return QString::fromLatin1("%1 [%2%3]").arg(formatObject(ownerDoc,obj,0),
list.join(QLatin1String(", ")),
QLatin1String(links.size()>3?" ...":""));
}
int i = 0;
for(auto &link : links) {
list << formatObject(ownerDoc,link);
if( ++i >= 3)
break;
}
return QString::fromLatin1("[%1%2]").arg(list.join(QLatin1String(", ")),
QLatin1String(links.size()>3?" ...":""));
}
void DlgPropertyLink::init(const App::DocumentObjectT &prop, bool tryFilter) {
ui->treeWidget->blockSignals(true);
ui->treeWidget->clear();
ui->treeWidget->blockSignals(false);
ui->typeTree->blockSignals(true);
ui->typeTree->clear();
ui->typeTree->blockSignals(false);
oldLinks.clear();
docItems.clear();
typeItems.clear();
itemMap.clear();
inList.clear();
selectedTypes.clear();
currentObj = nullptr;
searchItem = nullptr;
subSelections.clear();
selections.clear();
objProp = prop;
auto owner = objProp.getObject();
if(!owner || !owner->getNameInDocument())
return;
ui->searchBox->setDocumentObject(owner);
auto propLink = Base::freecad_dynamic_cast<App::PropertyLinkBase>(objProp.getProperty());
if(!propLink)
return;
oldLinks = getLinksFromProperty(propLink);
if(propLink->getScope() != App::LinkScope::Hidden) {
// populate inList to filter out any objects that contains the owner object
// of the editing link property
inList = owner->getInListEx(true);
inList.insert(owner);
}
std::vector<App::Document*> docs;
singleSelect = false;
if(propLink->isDerivedFrom(App::PropertyXLinkSub::getClassTypeId())
|| propLink->isDerivedFrom(App::PropertyLinkSub::getClassTypeId()))
{
allowSubObject = true;
singleParent = true;
} else if (propLink->isDerivedFrom(App::PropertyLink::getClassTypeId())) {
singleSelect = true;
} else if (propLink->isDerivedFrom(App::PropertyLinkSubList::getClassTypeId())) {
allowSubObject = true;
}
if(App::PropertyXLink::supportXLink(propLink)) {
allowSubObject = true;
docs = App::GetApplication().getDocuments();
} else
docs.push_back(owner->getDocument());
bool isLinkList = false;
if (propLink->isDerivedFrom(App::PropertyXLinkList::getClassTypeId())
|| propLink->isDerivedFrom(App::PropertyLinkList::getClassTypeId()))
{
isLinkList = true;
allowSubObject = false;
}
if(singleSelect) {
singleParent = true;
ui->treeWidget->setSelectionMode(QAbstractItemView::SingleSelection);
} else {
ui->treeWidget->setSelectionMode(QAbstractItemView::MultiSelection);
}
ui->checkSubObject->setVisible(allowSubObject);
if(!allowSubObject) {
ui->treeWidget->setColumnCount(1);
} else {
ui->treeWidget->setColumnCount(2);
// make sure to show a horizontal scrollbar if needed
#if QT_VERSION >= 0x050000
ui->treeWidget->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
#else
ui->treeWidget->header()->setResizeMode(0, QHeaderView::ResizeToContents);
#endif
}
std::set<App::Document*> expandDocs;
if(oldLinks.empty()) {
expandDocs.insert(owner->getDocument());
} else {
for(auto &link : oldLinks) {
auto doc = link.getDocument();
if(doc)
expandDocs.insert(doc);
}
}
QPixmap docIcon(Gui::BitmapFactory().pixmap("Document"));
for(auto d : docs) {
auto item = new QTreeWidgetItem(ui->treeWidget);
item->setIcon(0, docIcon);
item->setText(0, QString::fromUtf8(d->Label.getValue()));
item->setData(0, Qt::UserRole, QByteArray(""));
item->setData(0, Qt::UserRole+1, QByteArray(d->getName()));
item->setFlags(Qt::ItemIsEnabled);
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
if(expandDocs.count(d))
item->setExpanded(true);
docItems[d] = item;
}
if(oldLinks.isEmpty())
return;
if(allowSubObject) {
for(auto &link : oldLinks) {
auto sobj = link.getSubObject();
if(sobj && sobj!=link.getObject()) {
ui->checkSubObject->setChecked(true);
break;
}
}
}
// Try to select items corresponding to the current links inside the
// property
ui->treeWidget->blockSignals(true);
for(auto &link : oldLinks) {
onSelectionChanged(Gui::SelectionChanges(SelectionChanges::AddSelection,
link.getDocumentName(),
link.getObjectName(),
link.getSubName()));
}
ui->treeWidget->blockSignals(false);
// For link list type property, try to auto filter type
if(tryFilter && isLinkList) {
Base::Type objType;
for(auto link : oldLinks) {
auto obj = link.getSubObject();
if(!obj)
continue;
if(objType.isBad()) {
objType = obj->getTypeId();
continue;
}
for(;objType != App::DocumentObject::getClassTypeId();
objType = objType.getParent())
{
if(obj->isDerivedFrom(objType))
break;
}
}
Base::Type baseType;
// get only geometric types
if (objType.isDerivedFrom(App::GeoFeature::getClassTypeId()))
baseType = App::GeoFeature::getClassTypeId();
else
baseType = App::DocumentObject::getClassTypeId();
// get the direct base class of App::DocumentObject which 'obj' is derived from
while (!objType.isBad()) {
Base::Type parType = objType.getParent();
if (parType == baseType) {
baseType = objType;
break;
}
objType = parType;
}
if(!baseType.isBad()) {
const char *name = baseType.getName();
auto it = typeItems.find(QByteArray::fromRawData(name,strlen(name)+1));
if(it != typeItems.end())
it->second->setSelected(true);
ui->checkObjectType->setChecked(true);
}
}
}
void DlgPropertyLink::onClicked(QAbstractButton *button) {
if(button == resetButton) {
ui->treeWidget->blockSignals(true);
ui->treeWidget->selectionModel()->clearSelection();
for(auto item : subSelections)
item->setText(1, QString());
ui->treeWidget->blockSignals(false);
subSelections.clear();
Gui::Selection().clearSelection();
} else if (button == refreshButton) {
init(objProp);
}
}
void DlgPropertyLink::hideEvent(QHideEvent *ev) {
detachObserver();
QDialog::hideEvent(ev);
}
void DlgPropertyLink::closeEvent(QCloseEvent *ev) {
detachObserver();
QDialog::closeEvent(ev);
}
void DlgPropertyLink::attachObserver() {
if(isSelectionAttached())
return;
Gui::Selection().selStackPush();
attachSelection();
if(!parentView) {
for(auto p=parent(); p; p=p->parent()) {
auto view = qobject_cast<Gui::PropertyView*>(p);
if(view) {
parentView = view;
for(auto &sel : Gui::Selection().getCompleteSelection(0))
savedSelections.emplace_back(sel.DocName, sel.FeatName, sel.SubName);
break;
}
}
}
auto view = qobject_cast<Gui::PropertyView*>(parentView.data());
if(view)
view->blockSelection(true);
}
void DlgPropertyLink::showEvent(QShowEvent *ev) {
attachObserver();
QDialog::showEvent(ev);
}
void DlgPropertyLink::onItemEntered(QTreeWidgetItem *) {
int timeout = Gui::TreeParams::Instance()->PreSelectionDelay()/2;
if(timeout < 0)
timeout = 1;
timer->start(timeout);
Gui::Selection().rmvPreselect();
}
void DlgPropertyLink::leaveEvent(QEvent *ev) {
Gui::Selection().rmvPreselect();
QDialog::leaveEvent(ev);
}
void DlgPropertyLink::detachObserver() {
if(isSelectionAttached())
detachSelection();
auto view = qobject_cast<Gui::PropertyView*>(parentView.data());
if(view && savedSelections.size()) {
Gui::Selection().clearSelection();
for(auto &sel : savedSelections) {
if(sel.getSubObject())
Gui::Selection().addSelection(sel.getDocumentName().c_str(),
sel.getObjectName().c_str(),
sel.getSubName().c_str());
}
savedSelections.clear();
}
if(view)
view->blockSelection(false);
parentView = nullptr;
}
void DlgPropertyLink::onItemSelectionChanged()
{
auto newSelections = ui->treeWidget->selectedItems();
if(newSelections.isEmpty() || selections.contains(newSelections.back())) {
selections = newSelections;
if(newSelections.isEmpty())
currentObj = 0;
return;
}
selections = newSelections;
auto sobjs = getLinkFromItem(newSelections.back());
App::DocumentObject *obj = sobjs.size()?sobjs.front().getObject():nullptr;
if(!obj) {
Gui::Selection().clearSelection();
return;
}
bool focus = false;
// Do auto view switch if tree view does not do it
if(!TreeParams::Instance()->SyncView()) {
focus = ui->treeWidget->hasFocus();
auto doc = Gui::Application::Instance->getDocument(sobjs.front().getDocumentName().c_str());
if(doc) {
auto vp = Base::freecad_dynamic_cast<Gui::ViewProviderDocumentObject>(
doc->getViewProvider(obj));
if(vp) {
doc->setActiveView(vp, Gui::View3DInventor::getClassTypeId());
}
}
}
// Sync 3d view selection. To give a better visual feedback, we
// only keep the latest selection.
bool blocked = blockSelection(true);
Gui::Selection().clearSelection();
for(auto &sobj : sobjs)
Gui::Selection().addSelection(sobj.getDocumentName().c_str(),
sobj.getObjectName().c_str(),
sobj.getSubName().c_str());
blockSelection(blocked);
// Enforce single parent
if(singleParent && currentObj && currentObj!=obj) {
ui->treeWidget->blockSignals(true);
for(auto item : ui->treeWidget->selectedItems()) {
if(item != selections.back())
item->setSelected(false);
}
auto last = selections.back();
selections.clear();
selections.append(last);
ui->treeWidget->blockSignals(false);
}
currentObj = obj;
if(focus) {
// FIXME: does not work, why?
ui->treeWidget->setFocus();
}
}
QTreeWidgetItem *DlgPropertyLink::findItem(
App::DocumentObject *obj, const char *subname, bool *pfound)
{
if(pfound)
*pfound = false;
if(!obj || !obj->getNameInDocument())
return 0;
std::vector<App::DocumentObject *> sobjs;
if(subname && subname[0]) {
if(!allowSubObject) {
obj = obj->getSubObject(subname);
if(!obj)
return 0;
} else {
sobjs = obj->getSubObjectList(subname);
}
}
auto itDoc = docItems.find(obj->getDocument());
if(itDoc == docItems.end())
return 0;
onItemExpanded(itDoc->second);
auto it = itemMap.find(obj);
if(it == itemMap.end() || it->second->isHidden())
return 0;
if(!allowSubObject) {
if(pfound)
*pfound = true;
return it->second;
}
QTreeWidgetItem *item = it->second;
bool first = true;
for(auto o : sobjs) {
if(first) {
first = false;
continue;
}
onItemExpanded(item);
bool found = false;
for(int i=0,count=item->childCount();i<count;++i) {
auto child = item->child(i);
if(strcmp(o->getNameInDocument(),
child->data(0, Qt::UserRole).toByteArray().constData())==0)
{
item = child;
found = true;
break;
}
}
if(!found)
return item;
}
if(pfound)
*pfound = true;
return item;
}
void DlgPropertyLink::onSelectionChanged(const Gui::SelectionChanges& msg)
{
if (msg.Type != SelectionChanges::AddSelection)
return;
bool found = false;
auto selObj = msg.Object.getObject();
std::pair<std::string,std::string> elementName;
const char *subname = msg.pSubName;
if(!ui->checkSubObject->isChecked()) {
selObj = App::GeoFeature::resolveElement(selObj,subname,elementName);
if(!selObj)
return;
subname = elementName.second.c_str();
}
auto item = findItem(selObj, msg.pSubName, &found);
if(!item || !found)
return;
if(!item->isSelected()) {
ui->treeWidget->blockSignals(true);
if(singleSelect || (singleParent && currentObj && currentObj!=selObj))
ui->treeWidget->selectionModel()->clearSelection();
currentObj = selObj;
item->setSelected(true);
selections.append(item);
ui->treeWidget->blockSignals(false);
}
ui->treeWidget->scrollToItem(item);
if(allowSubObject) {
QString element = QString::fromLatin1(msg.Object.getOldElementName().c_str());
if(element.size()) {
QStringList list;
QString text = item->text(1);
if(text.size())
list = text.split(QLatin1Char(','));
if(list.indexOf(element)<0) {
list << element;
item->setText(1, list.join(QLatin1String(",")));
subSelections.insert(item);
}
} else if (subSelections.erase(item))
item->setText(1, QString());
}
}
void DlgPropertyLink::accept()
{
QDialog::accept();
}
static QTreeWidgetItem *_getLinkFromItem(std::ostringstream &ss, QTreeWidgetItem *item, const char *objName) {
auto parent = item->parent();
assert(parent);
const char *nextName = parent->data(0, Qt::UserRole).toByteArray().constData();
if(!nextName[0])
return item;
item = _getLinkFromItem(ss, parent, nextName);
ss << objName << '.';
return item;
}
QList<App::SubObjectT>
DlgPropertyLink::getLinkFromItem(QTreeWidgetItem *item, bool needSubName) const
{
QList<App::SubObjectT> res;
auto parent = item->parent();
if(!parent)
return res;
std::ostringstream ss;
auto parentItem = _getLinkFromItem(ss, item,
item->data(0,Qt::UserRole).toByteArray().constData());
App::SubObjectT sobj(parentItem->data(0, Qt::UserRole+1).toByteArray().constData(),
parentItem->data(0, Qt::UserRole).toByteArray().constData(),
ss.str().c_str());
QString elements;
if(needSubName && allowSubObject)
elements = item->text(1);
if(elements.isEmpty()) {
res.append(App::SubObjectT());
res.last() = std::move(sobj);
return res;
}
for(const QString &element : elements.split(QLatin1Char(','))) {
res.append(App::SubObjectT());
res.last() = App::SubObjectT(sobj.getDocumentName().c_str(),
sobj.getObjectName().c_str(),
(sobj.getSubName() + element.toLatin1().constData()).c_str());
}
return res;
}
void DlgPropertyLink::onTimer() {
auto item = ui->treeWidget->itemAt(
ui->treeWidget->viewport()->mapFromGlobal(QCursor::pos()));
if(!item)
return;
auto sobjs = getLinkFromItem(item);
if(sobjs.isEmpty())
return;
const auto &sobj = sobjs.front();
Gui::Selection().setPreselect(sobj.getDocumentName().c_str(),
sobj.getObjectName().c_str(),
sobj.getSubName().c_str(),
0,0,0,2);
}
QList<App::SubObjectT> DlgPropertyLink::currentLinks() const
{
auto items = ui->treeWidget->selectedItems();
QList<App::SubObjectT> res;
for(auto item : items)
res.append(getLinkFromItem(item));
return res;
}
QList<App::SubObjectT> DlgPropertyLink::originalLinks() const
{
return oldLinks;
}
QString DlgPropertyLink::linksToPython(QList<App::SubObjectT> links) {
if(links.isEmpty())
return QLatin1String("None");
if(links.size() == 1)
return QString::fromLatin1(links.front().getSubObjectPython(false).c_str());
std::ostringstream ss;
if(isLinkSub(links)) {
ss << '(' << links.front().getObjectPython() << ", [";
for(auto link : links) {
const auto &sub = link.getSubName();
if(sub.size())
ss << "u'" << Base::Tools::escapedUnicodeFromUtf8(sub.c_str()) << "',";
}
ss << "])";
} else {
ss << '[';
for(auto link : links)
ss << link.getSubObjectPython(false) << ',';
ss << ']';
}
return QString::fromLatin1(ss.str().c_str());
}
void DlgPropertyLink::filterObjects()
{
for(int i=0, count=ui->treeWidget->topLevelItemCount(); i<count; ++i) {
auto item = ui->treeWidget->topLevelItem(i);
for(int j=0, c=item->childCount(); j<c; ++j)
filterItem(item->child(j));
}
}
void DlgPropertyLink::filterItem(QTreeWidgetItem *item) {
if(filterType(item)) {
item->setHidden(true);
return;
}
item->setHidden(false);
for(int i=0, count=item->childCount(); i<count; ++i)
filterItem(item->child(i));
}
bool DlgPropertyLink::eventFilter(QObject *obj, QEvent *e) {
if(obj == ui->searchBox
&& e->type() == QEvent::KeyPress
&& static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape)
{
ui->searchBox->setText(QString());
return true;
}
return QDialog::eventFilter(obj,e);
}
void DlgPropertyLink::onItemSearch() {
itemSearch(ui->searchBox->text(), true);
}
void DlgPropertyLink::keyPressEvent(QKeyEvent *ev)
{
if(ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) {
if(ui->searchBox->hasFocus())
return;
}
QDialog::keyPressEvent(ev);
}
void DlgPropertyLink::itemSearch(const QString &text, bool select) {
if(searchItem)
searchItem->setBackground(0, bgBrush);
auto owner = objProp.getObject();
if(!owner)
return;
std::string txt(text.toUtf8().constData());
try {
if(txt.empty())
return;
if(txt.find("<<") == std::string::npos) {
auto pos = txt.find('.');
if(pos==std::string::npos)
txt += '.';
else if(pos!=txt.size()-1) {
txt.insert(pos+1,"<<");
if(txt.back()!='.')
txt += '.';
txt += ">>.";
}
}else if(txt.back() != '.')
txt += '.';
txt += "_self";
auto path = App::ObjectIdentifier::parse(owner,txt);
if(path.getPropertyName() != "_self")
return;
App::DocumentObject *obj = path.getDocumentObject();
if(!obj)
return;
bool found;
const char *subname = path.getSubObjectName().c_str();
QTreeWidgetItem *item = findItem(obj, subname, &found);
if(!item)
return;
if(select) {
if(!found)
return;
Gui::Selection().addSelection(obj->getDocument()->getName(),
obj->getNameInDocument(),subname);
}else{
Selection().setPreselect(obj->getDocument()->getName(),
obj->getNameInDocument(), subname,0,0,0,2);
searchItem = item;
ui->treeWidget->scrollToItem(searchItem);
bgBrush = searchItem->background(0);
searchItem->setBackground(0, QColor(255, 255, 0, 100));
}
} catch(...)
{
}
}
QTreeWidgetItem *DlgPropertyLink::createItem(
App::DocumentObject *obj, QTreeWidgetItem *parent)
{
if(!obj || !obj->getNameInDocument())
return 0;
if(inList.find(obj)!=inList.end())
return 0;
auto vp = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
Application::Instance->getViewProvider(obj));
if(!vp)
return 0;
QTreeWidgetItem* item;
if(parent)
item = new QTreeWidgetItem(parent);
else
item = new QTreeWidgetItem(ui->treeWidget);
item->setIcon(0, vp->getIcon());
item->setText(0, QString::fromUtf8((obj)->Label.getValue()));
item->setData(0, Qt::UserRole, QByteArray(obj->getNameInDocument()));
item->setData(0, Qt::UserRole+1, QByteArray(obj->getDocument()->getName()));
if(allowSubObject) {
item->setChildIndicatorPolicy(obj->getLinkedObject(true)->getOutList().size()?
QTreeWidgetItem::ShowIndicator:QTreeWidgetItem::DontShowIndicator);
item->setFlags(item->flags() | Qt::ItemIsEditable | Qt::ItemIsUserCheckable);
}
const char *typeName = obj->getTypeId().getName();
QByteArray typeData = QByteArray::fromRawData(typeName, strlen(typeName)+1);
item->setData(0, Qt::UserRole+2, typeData);
QByteArray proxyType;
auto prop = Base::freecad_dynamic_cast<App::PropertyPythonObject>(
obj->getPropertyByName("Proxy"));
if(prop) {
Base::PyGILStateLocker lock;
Py::Object proxy = prop->getValue();
if(!proxy.isNone() && !proxy.isString()) {
const char *name = 0;
if (proxy.hasAttr("__class__"))
proxyType = QByteArray(proxy.getAttr("__class__").as_string().c_str());
else {
name = proxy.ptr()->ob_type->tp_name;
proxyType = QByteArray::fromRawData(name, strlen(name)+1);
}
auto it = typeItems.find(proxyType);
if(it != typeItems.end())
proxyType = it->first;
else if (name)
proxyType = QByteArray(name, proxyType.size());
}
}
item->setData(0, Qt::UserRole+3, proxyType);
filterItem(item);
return item;
}
QTreeWidgetItem *DlgPropertyLink::createTypeItem(Base::Type type) {
if(type.isBad())
return 0;
QTreeWidgetItem *item = 0;
if(!type.isBad() && type!=App::DocumentObject::getClassTypeId()) {
Base::Type parentType = type.getParent();
if(!parentType.isBad()) {
const char *name = parentType.getName();
auto typeData = QByteArray::fromRawData(name,strlen(name)+1);
auto &typeItem = typeItems[typeData];
if(!typeItem) {
typeItem = createTypeItem(parentType);
typeItem->setData(0, Qt::UserRole, typeData);
}
item = typeItem;
}
}
if(!item)
item = new QTreeWidgetItem(ui->typeTree);
else
item = new QTreeWidgetItem(item);
item->setExpanded(true);
item->setText(0, QString::fromLatin1(type.getName()));
if(type == App::DocumentObject::getClassTypeId())
item->setFlags(Qt::ItemIsEnabled);
return item;
}
bool DlgPropertyLink::filterType(QTreeWidgetItem *item) {
auto proxyType = item->data(0, Qt::UserRole+3).toByteArray();
QTreeWidgetItem *proxyItem = 0;
if(proxyType.size()) {
auto &pitem = typeItems[proxyType];
if(!pitem) {
pitem = new QTreeWidgetItem(ui->typeTree);
pitem->setText(0,QString::fromLatin1(proxyType));
pitem->setIcon(0,item->icon(0));
pitem->setData(0,Qt::UserRole,proxyType);
}
proxyItem = pitem;
}
auto typeData = item->data(0, Qt::UserRole+2).toByteArray();
Base::Type type = Base::Type::fromName(typeData.constData());
if(type.isBad())
return false;
QTreeWidgetItem *&typeItem = typeItems[typeData];
if(!typeItem) {
typeItem = createTypeItem(type);
typeItem->setData(0, Qt::UserRole, typeData);
}
if(!proxyType.size()) {
QIcon icon = typeItem->icon(0);
if(icon.isNull())
typeItem->setIcon(0, item->icon(0));
}
if(!ui->checkObjectType->isChecked() || selectedTypes.empty())
return false;
if(proxyItem && selectedTypes.count(proxyType))
return false;
for(auto t=type; !t.isBad() && t!=App::DocumentObject::getClassTypeId(); t=t.getParent()) {
const char *name = t.getName();
if(selectedTypes.count(QByteArray::fromRawData(name, strlen(name)+1)))
return false;
}
return true;
}
void DlgPropertyLink::onItemExpanded(QTreeWidgetItem * item) {
if(item->childCount())
return;
const char *docName = item->data(0, Qt::UserRole+1).toByteArray().constData();
auto doc = App::GetApplication().getDocument(docName);
if(!doc)
return;
const char *objName = item->data(0, Qt::UserRole).toByteArray().constData();
if(!objName[0]) {
for(auto obj : doc->getObjects()) {
auto newItem = createItem(obj,item);
if(newItem)
itemMap[obj] = newItem;
}
} else if(allowSubObject) {
auto obj = doc->getObject(objName);
if(!obj) return;
std::set<App::DocumentObject*> childSet;
std::string sub;
for(auto child : obj->getLinkedObject(true)->getOutList()) {
if(!childSet.insert(child).second)
continue;
sub = child->getNameInDocument();
sub += ".";
if(obj->getSubObject(sub.c_str()))
createItem(child,item);
}
}
}
void DlgPropertyLink::on_checkObjectType_toggled(bool on)
{
ui->typeTree->setVisible(on);
filterObjects();
}
void DlgPropertyLink::on_typeTree_itemSelectionChanged() {
selectedTypes.clear();
for(auto item : ui->typeTree->selectedItems())
selectedTypes.insert(item->data(0, Qt::UserRole).toByteArray());
if(ui->checkObjectType->isChecked())
filterObjects();
}
void DlgPropertyLink::on_searchBox_textChanged(const QString& text)
{
itemSearch(text,false);
}
#include "moc_DlgPropertyLink.cpp"