/**************************************************************************** * Copyright (c) 2018 Zheng, Lei (realthunder) * * * * 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 #endif #include #include #include #include #include "DlgObjectSelection.h" #include "Application.h" #include "ViewProviderDocumentObject.h" #include "ui_DlgObjectSelection.h" FC_LOG_LEVEL_INIT("Gui",true,true) using namespace Gui; /* TRANSLATOR Gui::DlgObjectSelection */ DlgObjectSelection::DlgObjectSelection( const std::vector &objs, QWidget* parent, Qt::WindowFlags fl) : QDialog(parent, fl), ui(new Ui_DlgObjectSelection) { ui->setupUi(this); // make sure to show a horizontal scrollbar if needed #if QT_VERSION >= 0x050000 ui->depList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); ui->depList->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); ui->depList->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents); ui->depList->header()->setSectionResizeMode(3, QHeaderView::ResizeToContents); ui->treeWidget->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); #else ui->depList->header()->setResizeMode(0, QHeaderView::ResizeToContents); ui->depList->header()->setResizeMode(1, QHeaderView::ResizeToContents); ui->depList->header()->setResizeMode(2, QHeaderView::ResizeToContents); ui->depList->header()->setResizeMode(3, QHeaderView::ResizeToContents); ui->treeWidget->header()->setResizeMode(0, QHeaderView::ResizeToContents); #endif ui->depList->header()->setStretchLastSection(false); ui->depList->headerItem()->setText(0, tr("Dependency")); ui->depList->headerItem()->setText(1, tr("Document")); ui->depList->headerItem()->setText(2, tr("Name")); ui->depList->headerItem()->setText(3, tr("State")); ui->treeWidget->headerItem()->setText(0, tr("Hierarchy")); ui->treeWidget->header()->setStretchLastSection(false); for(auto obj : App::Document::getDependencyList(objs)) { auto &info = objMap[obj]; info.depItem = new QTreeWidgetItem(ui->depList); auto vp = Gui::Application::Instance->getViewProvider(obj); if(vp) info.depItem->setIcon(0, vp->getIcon()); info.depItem->setText(0, QString::fromUtf8((obj)->Label.getValue())); info.depItem->setText(1, QString::fromUtf8(obj->getDocument()->getName())); info.depItem->setText(2, QString::fromLatin1(obj->getNameInDocument())); info.depItem->setText(3, tr("Selected")); info.depItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); info.depItem->setCheckState(0,Qt::Checked); } for(auto obj : objs) { auto &info = objMap[obj]; info.items.push_back(createItem(obj,0)); info.items.back()->setCheckState(0,Qt::Checked); } for(auto &v : objMap) { for(auto obj : v.first->getOutListRecursive()) { if(obj == v.first) continue; auto it = objMap.find(obj); if(it == objMap.end()) continue; v.second.outList[obj] = &it->second; } for(auto obj : v.first->getInListRecursive()) { if(obj == v.first) continue; auto it = objMap.find(obj); if(it == objMap.end()) continue; v.second.inList[obj] = &it->second; } } connect(ui->treeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(onItemExpanded(QTreeWidgetItem*))); connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(onItemChanged(QTreeWidgetItem*,int))); connect(ui->depList, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(onItemChanged(QTreeWidgetItem*,int))); connect(ui->treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(onItemSelectionChanged())); connect(ui->depList, SIGNAL(itemSelectionChanged()), this, SLOT(onDepSelectionChanged())); connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } /** * Destroys the object and frees any allocated resources */ DlgObjectSelection::~DlgObjectSelection() { // no need to delete child widgets, Qt does it all for us delete ui; } QTreeWidgetItem *DlgObjectSelection::createItem(App::DocumentObject *obj, QTreeWidgetItem *parent) { QTreeWidgetItem* item; if(parent) item = new QTreeWidgetItem(parent); else item = new QTreeWidgetItem(ui->treeWidget); auto vp = Gui::Application::Instance->getViewProvider(obj); if(vp) item->setIcon(0, vp->getIcon()); item->setText(0, QString::fromUtf8((obj)->Label.getValue())); item->setData(0, Qt::UserRole, QByteArray(obj->getDocument()->getName())); item->setData(0, Qt::UserRole+1, QByteArray(obj->getNameInDocument())); item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); std::set outSet; for(auto o : obj->getOutList()) { if(objMap.count(o)) outSet.insert(o); } if(outSet.empty()) return item; item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); if(!parent) { bool populate = false; for(auto o : outSet) { if(objMap[o].items.empty()) { populate = true; break; } } if(!populate) return item; for(auto o : outSet) { auto &info = objMap[o]; info.items.push_back(createItem(o,item)); info.items.back()->setCheckState(0,info.checkState); } } return item; } class SignalBlocker { public: SignalBlocker(QTreeWidget *treeWidget) :treeWidget(treeWidget) { treeWidget->blockSignals(true); } ~SignalBlocker() { treeWidget->blockSignals(false); } QTreeWidget *treeWidget; }; App::DocumentObject *DlgObjectSelection::objFromItem(QTreeWidgetItem *item) { std::string name; std::string docName; if(item->treeWidget() == ui->treeWidget) { docName = item->data(0,Qt::UserRole).toByteArray().constData(); name = item->data(0,Qt::UserRole+1).toByteArray().constData(); }else{ docName = qPrintable(item->text(1)); name = qPrintable(item->text(2)); } auto doc = App::GetApplication().getDocument(docName.c_str()); if(!doc) return 0; return doc->getObject(name.c_str()); } void DlgObjectSelection::onItemExpanded(QTreeWidgetItem * item) { if(item->childCount()) return; auto obj = objFromItem(item); if(!obj) return; SignalBlocker blocker(ui->treeWidget); std::set outSet; for(auto o : obj->getOutList()) { if(!objMap.count(obj) || !outSet.insert(o).second) continue; auto &info = objMap[o]; info.items.push_back(createItem(o,item)); info.items.back()->setCheckState(0,info.checkState); } } void DlgObjectSelection::onItemChanged(QTreeWidgetItem * item, int column) { if(column) return; auto obj = objFromItem(item); if(!obj) return; auto state = item->checkState(0); auto it = objMap.find(obj); if(it == objMap.end() || state == it->second.checkState) return; SignalBlocker blocker(ui->treeWidget); SignalBlocker blocker2(ui->depList); auto &info = it->second; info.checkState = state; if(item == info.depItem) { for(auto item : info.items) item->setCheckState(0,state); }else{ info.depItem->setCheckState(0,state); info.depItem->setText(3,state==Qt::Checked?tr("Selected"):QString()); } if(state == Qt::Unchecked) { for(auto &v : info.outList) { if(info.inList.count(v.first)) { // This indicates a dependency loop. The check here is so that // object selection still works despite of the loop continue; } if(v.second->checkState == Qt::Unchecked) continue; v.second->checkState = Qt::Unchecked; v.second->depItem->setText(3,QString()); v.second->depItem->setCheckState(0,Qt::Unchecked); for(auto item : v.second->items) item->setCheckState(0,Qt::Unchecked); } for(auto &v : info.inList) { if(v.second->checkState != Qt::Checked) continue; v.second->checkState = Qt::PartiallyChecked; v.second->depItem->setText(3,tr("Partial")); v.second->depItem->setCheckState(0,Qt::PartiallyChecked); for(auto item : v.second->items) item->setCheckState(0,Qt::PartiallyChecked); } return; } else if(state == Qt::Checked) { for(auto &v : info.outList) { if(info.inList.count(v.first)) { // This indicates a dependency loop. The check here is so that // object selection still works despite of the loop continue; } if(v.second->checkState == Qt::Checked) continue; v.second->checkState = Qt::Checked; v.second->depItem->setText(3,tr("Selected")); v.second->depItem->setCheckState(0,Qt::Checked); for(auto item : v.second->items) item->setCheckState(0,Qt::Checked); } bool touched; do { touched = false; for(auto &v : info.inList) { if(v.second->checkState != Qt::PartiallyChecked) continue; bool partial = false; for(auto &vv : v.second->outList) { if(vv.second->checkState != Qt::Checked) { partial = true; break; } } if(partial) continue; touched = true; v.second->checkState = Qt::Checked; v.second->depItem->setText(3,tr("Selected")); v.second->depItem->setCheckState(0,Qt::Checked); for(auto item : v.second->items) item->setCheckState(0,Qt::Checked); } }while(touched); } } std::vector DlgObjectSelection::getSelections() const { std::vector res; for(auto &v : objMap) { if(v.second.checkState != Qt::Unchecked) res.push_back(v.first); } return res; } void DlgObjectSelection::onItemSelectionChanged() { SignalBlocker block2(ui->treeWidget); SignalBlocker block(ui->depList); QTreeWidgetItem *scroll=0; for(auto &v : objMap) { auto &info = v.second; auto it = sels.find(v.first); auto selected = it==sels.end(); for(auto item : info.items) { if(selected == item->isSelected()) { for(auto item : info.items) item->setSelected(selected); scroll = info.depItem; info.depItem->setSelected(selected); scroll = info.depItem; if(!selected) sels.erase(it); else sels.insert(v.first); break; } } } if(scroll) ui->depList->scrollToItem(scroll); } void DlgObjectSelection::onDepSelectionChanged() { SignalBlocker block2(ui->treeWidget); SignalBlocker block(ui->depList); QTreeWidgetItem *scroll=0; for(auto &v : objMap) { auto &info = v.second; auto it = sels.find(v.first); auto selected = it==sels.end(); if(info.depItem->isSelected()==selected) { for(auto item : info.items) { scroll = item; item->setSelected(selected); } if(!selected) sels.erase(it); else { sels.insert(v.first); for(auto item : info.items) { for(auto parent=item->parent();parent;parent=parent->parent()) parent->setExpanded(true); } } } } if(scroll) ui->treeWidget->scrollToItem(scroll); } void DlgObjectSelection::accept() { QDialog::accept(); } void DlgObjectSelection::reject() { QDialog::reject(); } #include "moc_DlgObjectSelection.cpp"