It is possible that a newly reated document object already claims other existing objects. The tree needs to be recomputed in this case. This happens for example for the origin, which creates its planes and axis first and claims them before its own viewprovider is created.
1515 lines
59 KiB
C++
1515 lines
59 KiB
C++
/***************************************************************************
|
|
* Copyright (c) 2004 Jürgen Riegel <juergen.riegel@web.de> *
|
|
* *
|
|
* 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 <boost/signals.hpp>
|
|
# include <boost/bind.hpp>
|
|
# include <QAction>
|
|
# include <QActionGroup>
|
|
# include <QApplication>
|
|
# include <qcursor.h>
|
|
# include <qlayout.h>
|
|
# include <qstatusbar.h>
|
|
# include <QContextMenuEvent>
|
|
# include <QMenu>
|
|
# include <QPixmap>
|
|
# include <QTimer>
|
|
# include <QToolTip>
|
|
# include <QHeaderView>
|
|
#endif
|
|
|
|
#include <Base/Console.h>
|
|
#include <App/Document.h>
|
|
#include <App/DocumentObject.h>
|
|
#include <App/DocumentObjectGroup.h>
|
|
|
|
#include "Tree.h"
|
|
#include "Document.h"
|
|
#include "BitmapFactory.h"
|
|
#include "ViewProviderDocumentObject.h"
|
|
#include "MenuManager.h"
|
|
#include "Application.h"
|
|
#include "MainWindow.h"
|
|
|
|
using namespace Gui;
|
|
|
|
QPixmap* TreeWidget::documentPixmap = 0;
|
|
const int TreeWidget::DocumentType = 1000;
|
|
const int TreeWidget::ObjectType = 1001;
|
|
|
|
|
|
/* TRANSLATOR Gui::TreeWidget */
|
|
TreeWidget::TreeWidget(QWidget* parent)
|
|
: QTreeWidget(parent), fromOutside(false)
|
|
{
|
|
this->setDragEnabled(true);
|
|
this->setAcceptDrops(true);
|
|
this->setDropIndicatorShown(false);
|
|
this->setRootIsDecorated(false);
|
|
|
|
this->createGroupAction = new QAction(this);
|
|
this->createGroupAction->setText(tr("Create group..."));
|
|
this->createGroupAction->setStatusTip(tr("Create a group"));
|
|
connect(this->createGroupAction, SIGNAL(triggered()),
|
|
this, SLOT(onCreateGroup()));
|
|
this->relabelObjectAction = new QAction(this);
|
|
this->relabelObjectAction->setText(tr("Rename"));
|
|
this->relabelObjectAction->setStatusTip(tr("Rename object"));
|
|
this->relabelObjectAction->setShortcut(Qt::Key_F2);
|
|
connect(this->relabelObjectAction, SIGNAL(triggered()),
|
|
this, SLOT(onRelabelObject()));
|
|
this->finishEditingAction = new QAction(this);
|
|
this->finishEditingAction->setText(tr("Finish editing"));
|
|
this->finishEditingAction->setStatusTip(tr("Finish editing object"));
|
|
connect(this->finishEditingAction, SIGNAL(triggered()),
|
|
this, SLOT(onFinishEditing()));
|
|
|
|
// Setup connections
|
|
Application::Instance->signalNewDocument.connect(boost::bind(&TreeWidget::slotNewDocument, this, _1));
|
|
Application::Instance->signalDeleteDocument.connect(boost::bind(&TreeWidget::slotDeleteDocument, this, _1));
|
|
Application::Instance->signalRenameDocument.connect(boost::bind(&TreeWidget::slotRenameDocument, this, _1));
|
|
Application::Instance->signalActiveDocument.connect(boost::bind(&TreeWidget::slotActiveDocument, this, _1));
|
|
Application::Instance->signalRelabelDocument.connect(boost::bind(&TreeWidget::slotRelabelDocument, this, _1));
|
|
|
|
QStringList labels;
|
|
labels << tr("Labels & Attributes");
|
|
this->setHeaderLabels(labels);
|
|
// make sure to show a horizontal scrollbar if needed
|
|
this->header()->setResizeMode(0, QHeaderView::ResizeToContents);
|
|
this->header()->setStretchLastSection(false);
|
|
|
|
// Add the first main label
|
|
this->rootItem = new QTreeWidgetItem(this);
|
|
this->rootItem->setText(0, tr("Application"));
|
|
this->rootItem->setFlags(Qt::ItemIsEnabled);
|
|
this->expandItem(this->rootItem);
|
|
this->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
#if QT_VERSION >= 0x040200
|
|
// causes unexpected drop events (possibly only with Qt4.1.x)
|
|
this->setMouseTracking(true); // needed for itemEntered() to work
|
|
#endif
|
|
|
|
this->statusTimer = new QTimer(this);
|
|
|
|
connect(this->statusTimer, SIGNAL(timeout()),
|
|
this, SLOT(onTestStatus()));
|
|
connect(this, SIGNAL(itemEntered(QTreeWidgetItem*, int)),
|
|
this, SLOT(onItemEntered(QTreeWidgetItem*)));
|
|
connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)),
|
|
this, SLOT(onItemCollapsed(QTreeWidgetItem*)));
|
|
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)),
|
|
this, SLOT(onItemExpanded(QTreeWidgetItem*)));
|
|
connect(this, SIGNAL(itemSelectionChanged()),
|
|
this, SLOT(onItemSelectionChanged()));
|
|
|
|
this->statusTimer->setSingleShot(true);
|
|
this->statusTimer->start(300);
|
|
documentPixmap = new QPixmap(Gui::BitmapFactory().pixmap("Document"));
|
|
}
|
|
|
|
TreeWidget::~TreeWidget()
|
|
{
|
|
}
|
|
|
|
void TreeWidget::contextMenuEvent (QContextMenuEvent * e)
|
|
{
|
|
// ask workbenches and view provider, ...
|
|
MenuItem view;
|
|
Gui::Application::Instance->setupContextMenu("Tree", &view);
|
|
|
|
QMenu contextMenu;
|
|
QMenu subMenu;
|
|
QMenu editMenu;
|
|
QActionGroup subMenuGroup(&subMenu);
|
|
subMenuGroup.setExclusive(true);
|
|
connect(&subMenuGroup, SIGNAL(triggered(QAction*)),
|
|
this, SLOT(onActivateDocument(QAction*)));
|
|
MenuManager::getInstance()->setupContextMenu(&view, contextMenu);
|
|
|
|
// get the current item
|
|
this->contextItem = itemAt(e->pos());
|
|
if (this->contextItem && this->contextItem->type() == DocumentType) {
|
|
if (!contextMenu.actions().isEmpty())
|
|
contextMenu.addSeparator();
|
|
contextMenu.addAction(this->createGroupAction);
|
|
}
|
|
else if (this->contextItem && this->contextItem->type() == ObjectType) {
|
|
DocumentObjectItem* objitem = static_cast<DocumentObjectItem*>
|
|
(this->contextItem);
|
|
if (objitem->object()->getObject()->isDerivedFrom(App::DocumentObjectGroup
|
|
::getClassTypeId())) {
|
|
QList<QAction*> acts = contextMenu.actions();
|
|
if (!acts.isEmpty()) {
|
|
QAction* first = acts.front();
|
|
QAction* sep = contextMenu.insertSeparator(first);
|
|
contextMenu.insertAction(sep, this->createGroupAction);
|
|
}
|
|
else
|
|
contextMenu.addAction(this->createGroupAction);
|
|
}
|
|
if (!contextMenu.actions().isEmpty())
|
|
contextMenu.addSeparator();
|
|
contextMenu.addAction(this->relabelObjectAction);
|
|
|
|
// if only one item is selected setup the edit menu
|
|
if (this->selectedItems().size() == 1) {
|
|
objitem->object()->setupContextMenu(&editMenu, this, SLOT(onStartEditing()));
|
|
QList<QAction*> editAct = editMenu.actions();
|
|
if (!editAct.isEmpty()) {
|
|
QAction* topact = contextMenu.actions().front();
|
|
for (QList<QAction*>::iterator it = editAct.begin(); it != editAct.end(); ++it)
|
|
contextMenu.insertAction(topact, *it);
|
|
QAction* first = editAct.front();
|
|
contextMenu.setDefaultAction(first);
|
|
if (objitem->object()->isEditing())
|
|
contextMenu.insertAction(topact, this->finishEditingAction);
|
|
contextMenu.insertSeparator(topact);
|
|
}
|
|
}
|
|
}
|
|
|
|
// add a submenu to active a document if two or more exist
|
|
std::vector<App::Document*> docs = App::GetApplication().getDocuments();
|
|
if (docs.size() >= 2) {
|
|
App::Document* activeDoc = App::GetApplication().getActiveDocument();
|
|
subMenu.setTitle(tr("Activate document"));
|
|
contextMenu.addMenu(&subMenu);
|
|
QAction* active = 0;
|
|
for (std::vector<App::Document*>::iterator it = docs.begin(); it != docs.end(); ++it) {
|
|
QString label = QString::fromUtf8((*it)->Label.getValue());
|
|
QAction* action = subMenuGroup.addAction(label);
|
|
action->setCheckable(true);
|
|
action->setStatusTip(tr("Activate document %1").arg(label));
|
|
action->setData(QByteArray((*it)->getName()));
|
|
if (*it == activeDoc) active = action;
|
|
}
|
|
|
|
if (active)
|
|
active->setChecked(true);
|
|
subMenu.addActions(subMenuGroup.actions());
|
|
}
|
|
|
|
if (contextMenu.actions().count() > 0)
|
|
contextMenu.exec(QCursor::pos());
|
|
}
|
|
|
|
void TreeWidget::onCreateGroup()
|
|
{
|
|
QString name = tr("Group");
|
|
if (this->contextItem->type() == DocumentType) {
|
|
DocumentItem* docitem = static_cast<DocumentItem*>(this->contextItem);
|
|
App::Document* doc = docitem->document()->getDocument();
|
|
QString cmd = QString::fromLatin1("App.getDocument(\"%1\").addObject"
|
|
"(\"App::DocumentObjectGroup\",\"%2\")")
|
|
.arg(QString::fromLatin1(doc->getName())).arg(name);
|
|
Gui::Document* gui = Gui::Application::Instance->getDocument(doc);
|
|
gui->openCommand("Create group");
|
|
Gui::Application::Instance->runPythonCode(cmd.toUtf8());
|
|
gui->commitCommand();
|
|
}
|
|
else if (this->contextItem->type() == ObjectType) {
|
|
DocumentObjectItem* objitem = static_cast<DocumentObjectItem*>
|
|
(this->contextItem);
|
|
App::DocumentObject* obj = objitem->object()->getObject();
|
|
App::Document* doc = obj->getDocument();
|
|
QString cmd = QString::fromLatin1("App.getDocument(\"%1\").getObject(\"%2\")"
|
|
".newObject(\"App::DocumentObjectGroup\",\"%3\")")
|
|
.arg(QString::fromLatin1(doc->getName()))
|
|
.arg(QString::fromLatin1(obj->getNameInDocument()))
|
|
.arg(name);
|
|
Gui::Document* gui = Gui::Application::Instance->getDocument(doc);
|
|
gui->openCommand("Create group");
|
|
Gui::Application::Instance->runPythonCode(cmd.toUtf8());
|
|
gui->commitCommand();
|
|
}
|
|
}
|
|
|
|
void TreeWidget::onRelabelObject()
|
|
{
|
|
QTreeWidgetItem* item = currentItem();
|
|
if (item)
|
|
editItem(item);
|
|
}
|
|
|
|
void TreeWidget::onStartEditing()
|
|
{
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
if (action) {
|
|
if (this->contextItem && this->contextItem->type() == ObjectType) {
|
|
DocumentObjectItem* objitem = static_cast<DocumentObjectItem*>
|
|
(this->contextItem);
|
|
int edit = action->data().toInt();
|
|
App::DocumentObject* obj = objitem->object()->getObject();
|
|
if (!obj) return;
|
|
Gui::Document* doc = Gui::Application::Instance->getDocument(obj->getDocument());
|
|
MDIView *view = doc->getActiveView();
|
|
if (view) getMainWindow()->setActiveWindow(view);
|
|
|
|
// Always open a transaction here doesn't make much sense because:
|
|
// - many objects open transactions when really changing some properties
|
|
// - this leads to certain inconsistencies with the doubleClicked() method
|
|
// So, only the view provider class should decide what to do
|
|
#if 0
|
|
// open a transaction before starting edit mode
|
|
std::string cmd("Edit ");
|
|
cmd += obj->Label.getValue();
|
|
doc->openCommand(cmd.c_str());
|
|
bool ok = doc->setEdit(objitem->object(), edit);
|
|
if (!ok) doc->abortCommand();
|
|
#else
|
|
doc->setEdit(objitem->object(), edit);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void TreeWidget::onFinishEditing()
|
|
{
|
|
if (this->contextItem && this->contextItem->type() == ObjectType) {
|
|
DocumentObjectItem* objitem = static_cast<DocumentObjectItem*>
|
|
(this->contextItem);
|
|
App::DocumentObject* obj = objitem->object()->getObject();
|
|
if (!obj) return;
|
|
Gui::Document* doc = Gui::Application::Instance->getDocument(obj->getDocument());
|
|
doc->commitCommand();
|
|
doc->resetEdit();
|
|
doc->getDocument()->recompute();
|
|
}
|
|
}
|
|
|
|
void TreeWidget::onActivateDocument(QAction* active)
|
|
{
|
|
// activate the specified document
|
|
QByteArray docname = active->data().toByteArray();
|
|
Gui::Document* doc = Application::Instance->getDocument((const char*)docname);
|
|
if (!doc) return;
|
|
MDIView *view = doc->getActiveView();
|
|
if (!view) return;
|
|
getMainWindow()->setActiveWindow(view);
|
|
}
|
|
|
|
Qt::DropActions TreeWidget::supportedDropActions () const
|
|
{
|
|
return QTreeWidget::supportedDropActions();
|
|
}
|
|
|
|
bool TreeWidget::event(QEvent *e)
|
|
{
|
|
#if 0
|
|
if (e->type() == QEvent::ShortcutOverride) {
|
|
QKeyEvent* ke = static_cast<QKeyEvent *>(e);
|
|
switch (ke->key()) {
|
|
case Qt::Key_Delete:
|
|
ke->accept();
|
|
}
|
|
}
|
|
#endif
|
|
return QTreeWidget::event(e);
|
|
}
|
|
|
|
void TreeWidget::keyPressEvent(QKeyEvent *event)
|
|
{
|
|
#if 0
|
|
if (event && event->matches(QKeySequence::Delete)) {
|
|
event->ignore();
|
|
}
|
|
#endif
|
|
QTreeWidget::keyPressEvent(event);
|
|
}
|
|
|
|
void TreeWidget::mouseDoubleClickEvent (QMouseEvent * event)
|
|
{
|
|
QTreeWidgetItem* item = itemAt(event->pos());
|
|
if (!item) return;
|
|
if (item->type() == TreeWidget::DocumentType) {
|
|
//QTreeWidget::mouseDoubleClickEvent(event);
|
|
const Gui::Document* doc = static_cast<DocumentItem*>(item)->document();
|
|
if (!doc) return;
|
|
MDIView *view = doc->getActiveView();
|
|
if (!view) return;
|
|
getMainWindow()->setActiveWindow(view);
|
|
}
|
|
else if (item->type() == TreeWidget::ObjectType) {
|
|
DocumentObjectItem* objitem = static_cast<DocumentObjectItem*>(item);
|
|
App::DocumentObject* obj = objitem->object()->getObject();
|
|
Gui::Document* doc = Gui::Application::Instance->getDocument(obj->getDocument());
|
|
MDIView *view = doc->getActiveView();
|
|
if (view) getMainWindow()->setActiveWindow(view);
|
|
if (!objitem->object()->doubleClicked())
|
|
QTreeWidget::mouseDoubleClickEvent(event);
|
|
}
|
|
}
|
|
|
|
void TreeWidget::startDrag(Qt::DropActions supportedActions)
|
|
{
|
|
QTreeWidget::startDrag(supportedActions);
|
|
}
|
|
|
|
QMimeData * TreeWidget::mimeData (const QList<QTreeWidgetItem *> items) const
|
|
{
|
|
// all selected items must reference an object from the same document
|
|
App::Document* doc=0;
|
|
for (QList<QTreeWidgetItem *>::ConstIterator it = items.begin(); it != items.end(); ++it) {
|
|
if ((*it)->type() != TreeWidget::ObjectType)
|
|
return 0;
|
|
App::DocumentObject* obj = static_cast<DocumentObjectItem *>(*it)->object()->getObject();
|
|
if (!doc)
|
|
doc = obj->getDocument();
|
|
else if (doc != obj->getDocument())
|
|
return 0;
|
|
// Now check for object with a parent that is an ObjectType, too.
|
|
// If this object is *not* selected and *not* a group we are not allowed to remove
|
|
// its child (e.g. the sketch of a pad).
|
|
QTreeWidgetItem* parent = (*it)->parent();
|
|
if (parent && parent->type() == TreeWidget::ObjectType) {
|
|
// fix issue #0001456
|
|
if (!items.contains(parent)) {
|
|
Gui::ViewProvider* vp = static_cast<DocumentObjectItem *>(parent)->object();
|
|
if (!vp->canDragObjects()) {
|
|
return 0;
|
|
}
|
|
else if (!vp->canDragObject(obj)) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return QTreeWidget::mimeData(items);
|
|
}
|
|
|
|
bool TreeWidget::dropMimeData(QTreeWidgetItem *parent, int index,
|
|
const QMimeData *data, Qt::DropAction action)
|
|
{
|
|
return QTreeWidget::dropMimeData(parent, index, data, action);
|
|
}
|
|
|
|
void TreeWidget::dragEnterEvent(QDragEnterEvent * event)
|
|
{
|
|
QTreeWidget::dragEnterEvent(event);
|
|
}
|
|
|
|
void TreeWidget::dragLeaveEvent(QDragLeaveEvent * event)
|
|
{
|
|
QTreeWidget::dragLeaveEvent(event);
|
|
}
|
|
|
|
void TreeWidget::dragMoveEvent(QDragMoveEvent *event)
|
|
{
|
|
QTreeWidget::dragMoveEvent(event);
|
|
if (!event->isAccepted())
|
|
return;
|
|
|
|
QTreeWidgetItem* targetitem = itemAt(event->pos());
|
|
if (!targetitem || this->isItemSelected(targetitem)) {
|
|
event->ignore();
|
|
}
|
|
else if (targetitem->type() == TreeWidget::DocumentType) {
|
|
QList<QModelIndex> idxs = selectedIndexes();
|
|
App::Document* doc = static_cast<DocumentItem*>(targetitem)->
|
|
document()->getDocument();
|
|
for (QList<QModelIndex>::Iterator it = idxs.begin(); it != idxs.end(); ++it) {
|
|
QTreeWidgetItem* item = itemFromIndex(*it);
|
|
if (item->type() != TreeWidget::ObjectType) {
|
|
event->ignore();
|
|
return;
|
|
}
|
|
App::DocumentObject* obj = static_cast<DocumentObjectItem*>(item)->
|
|
object()->getObject();
|
|
if (doc != obj->getDocument()) {
|
|
event->ignore();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else if (targetitem->type() == TreeWidget::ObjectType) {
|
|
|
|
DocumentObjectItem* targetItemObj = static_cast<DocumentObjectItem*>(targetitem);
|
|
Gui::ViewProviderDocumentObject* vp = targetItemObj->object();
|
|
|
|
if (!vp->canDropObjects()) {
|
|
event->ignore();
|
|
}
|
|
|
|
QList<QTreeWidgetItem *> children;
|
|
for (int i=0; i<targetitem->childCount(); i++)
|
|
children << targetitem->child(i);
|
|
|
|
App::DocumentObject* grp = vp->getObject();
|
|
App::Document* doc = grp->getDocument();
|
|
QList<QModelIndex> idxs = selectedIndexes();
|
|
|
|
std::vector<const App::DocumentObject*> dropObjects;
|
|
dropObjects.reserve(idxs.size());
|
|
|
|
for (QList<QModelIndex>::Iterator it = idxs.begin(); it != idxs.end(); ++it) {
|
|
QTreeWidgetItem* item = itemFromIndex(*it);
|
|
if (item->type() != TreeWidget::ObjectType) {
|
|
event->ignore();
|
|
return;
|
|
}
|
|
App::DocumentObject* obj = static_cast<DocumentObjectItem*>(item)->
|
|
object()->getObject();
|
|
if (doc != obj->getDocument()) {
|
|
event->ignore();
|
|
return;
|
|
}
|
|
|
|
dropObjects.push_back(obj);
|
|
|
|
// To avoid a cylic dependency it must be made sure to not allow to
|
|
// drag'n'drop a tree item onto a child or grandchild item of it.
|
|
if (static_cast<DocumentObjectItem*>(targetitem)->isChildOfItem(
|
|
static_cast<DocumentObjectItem*>(item))) {
|
|
event->ignore();
|
|
return;
|
|
}
|
|
|
|
// if the item is already a child of the target item there is nothing to do
|
|
if (children.contains(item)) {
|
|
event->ignore();
|
|
return;
|
|
}
|
|
|
|
// let the view provider decide to accept the object or ignore it
|
|
if (!vp->canDropObject(obj)) {
|
|
event->ignore();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
event->ignore();
|
|
}
|
|
}
|
|
|
|
void TreeWidget::dropEvent(QDropEvent *event)
|
|
{
|
|
//FIXME: This should actually be done inside dropMimeData
|
|
|
|
QTreeWidgetItem* targetitem = itemAt(event->pos());
|
|
// not dropped onto an item
|
|
if (!targetitem)
|
|
return;
|
|
// one of the source items is also the destination item, that's not allowed
|
|
if (this->isItemSelected(targetitem))
|
|
return;
|
|
|
|
// filter out the selected items we cannot handle
|
|
QList<QTreeWidgetItem*> items;
|
|
QList<QModelIndex> idxs = selectedIndexes();
|
|
for (QList<QModelIndex>::Iterator it = idxs.begin(); it != idxs.end(); ++it) {
|
|
// ignore child elements if the parent is selected
|
|
QModelIndex parent = (*it).parent();
|
|
if (idxs.contains(parent))
|
|
continue;
|
|
QTreeWidgetItem* item = itemFromIndex(*it);
|
|
if (item == targetitem)
|
|
continue;
|
|
if (item->parent() == targetitem)
|
|
continue;
|
|
items.push_back(item);
|
|
}
|
|
|
|
if (items.isEmpty())
|
|
return; // nothing needs to be done
|
|
|
|
if (targetitem->type() == TreeWidget::ObjectType) {
|
|
// add object to group
|
|
DocumentObjectItem* targetItemObj = static_cast<DocumentObjectItem*>(targetitem);
|
|
Gui::ViewProviderDocumentObject* vp = targetItemObj->object();
|
|
App::DocumentObject* grp = vp->getObject();
|
|
if (!vp->canDropObjects()) {
|
|
return; // no group like object
|
|
}
|
|
|
|
std::vector<const App::DocumentObject*> dropObjects;
|
|
dropObjects.reserve(idxs.size());
|
|
|
|
// Open command
|
|
App::Document* doc = grp->getDocument();
|
|
Gui::Document* gui = Gui::Application::Instance->getDocument(doc);
|
|
|
|
for (QList<QTreeWidgetItem*>::Iterator it = items.begin(); it != items.end(); ++it) {
|
|
Gui::ViewProviderDocumentObject* vpc = static_cast<DocumentObjectItem*>(*it)->object();
|
|
App::DocumentObject* obj = vpc->getObject();
|
|
|
|
dropObjects.push_back(obj);
|
|
|
|
// does this have a parent object
|
|
QTreeWidgetItem* parent = (*it)->parent();
|
|
if (parent && parent->type() == TreeWidget::ObjectType) {
|
|
Gui::ViewProvider* vpp = static_cast<DocumentObjectItem *>(parent)->object();
|
|
vpp->dragObject(obj);
|
|
}
|
|
|
|
// now add the object to the target object
|
|
vp->dropObject(obj);
|
|
}
|
|
targetItemObj->drop(dropObjects,event->keyboardModifiers(),event->mouseButtons(),event->pos());
|
|
}
|
|
else if (targetitem->type() == TreeWidget::DocumentType) {
|
|
// Open command
|
|
App::Document* doc = static_cast<DocumentItem*>(targetitem)->document()->getDocument();
|
|
Gui::Document* gui = Gui::Application::Instance->getDocument(doc);
|
|
gui->openCommand("Move object");
|
|
for (QList<QTreeWidgetItem*>::Iterator it = items.begin(); it != items.end(); ++it) {
|
|
Gui::ViewProviderDocumentObject* vpc = static_cast<DocumentObjectItem*>(*it)->object();
|
|
App::DocumentObject* obj = vpc->getObject();
|
|
|
|
// does this have a parent object
|
|
QTreeWidgetItem* parent = (*it)->parent();
|
|
if (parent && parent->type() == TreeWidget::ObjectType) {
|
|
Gui::ViewProvider* vpp = static_cast<DocumentObjectItem *>(parent)->object();
|
|
vpp->dragObject(obj);
|
|
}
|
|
}
|
|
gui->commitCommand();
|
|
}
|
|
}
|
|
|
|
void TreeWidget::drawRow(QPainter *painter, const QStyleOptionViewItem &options, const QModelIndex &index) const
|
|
{
|
|
QTreeWidget::drawRow(painter, options, index);
|
|
// Set the text and highlighted text color of a hidden object to a dark
|
|
//QTreeWidgetItem * item = itemFromIndex(index);
|
|
//if (item->type() == ObjectType && !(static_cast<DocumentObjectItem*>(item)->previousStatus & 1)) {
|
|
// QStyleOptionViewItem opt(options);
|
|
// opt.state ^= QStyle::State_Enabled;
|
|
// QColor c = opt.palette.color(QPalette::Inactive, QPalette::Dark);
|
|
// opt.palette.setColor(QPalette::Inactive, QPalette::Text, c);
|
|
// opt.palette.setColor(QPalette::Inactive, QPalette::HighlightedText, c);
|
|
// QTreeWidget::drawRow(painter, opt, index);
|
|
//}
|
|
//else {
|
|
// QTreeWidget::drawRow(painter, options, index);
|
|
//}
|
|
}
|
|
|
|
void TreeWidget::slotNewDocument(const Gui::Document& Doc)
|
|
{
|
|
DocumentItem* item = new DocumentItem(&Doc, this->rootItem);
|
|
this->expandItem(item);
|
|
item->setIcon(0, *documentPixmap);
|
|
item->setText(0, QString::fromUtf8(Doc.getDocument()->Label.getValue()));
|
|
DocumentMap[ &Doc ] = item;
|
|
}
|
|
|
|
void TreeWidget::slotDeleteDocument(const Gui::Document& Doc)
|
|
{
|
|
std::map<const Gui::Document*, DocumentItem*>::iterator it = DocumentMap.find(&Doc);
|
|
if (it != DocumentMap.end()) {
|
|
this->rootItem->takeChild(this->rootItem->indexOfChild(it->second));
|
|
delete it->second;
|
|
DocumentMap.erase(it);
|
|
}
|
|
}
|
|
|
|
void TreeWidget::slotRenameDocument(const Gui::Document& Doc)
|
|
{
|
|
// do nothing here
|
|
}
|
|
|
|
void TreeWidget::slotRelabelDocument(const Gui::Document& Doc)
|
|
{
|
|
std::map<const Gui::Document*, DocumentItem*>::iterator it = DocumentMap.find(&Doc);
|
|
if (it != DocumentMap.end()) {
|
|
it->second->setText(0, QString::fromUtf8(Doc.getDocument()->Label.getValue()));
|
|
}
|
|
}
|
|
|
|
void TreeWidget::slotActiveDocument(const Gui::Document& Doc)
|
|
{
|
|
std::map<const Gui::Document*, DocumentItem*>::iterator jt = DocumentMap.find(&Doc);
|
|
if (jt == DocumentMap.end())
|
|
return; // signal is emitted before the item gets created
|
|
for (std::map<const Gui::Document*, DocumentItem*>::iterator it = DocumentMap.begin();
|
|
it != DocumentMap.end(); ++it)
|
|
{
|
|
QFont f = it->second->font(0);
|
|
f.setBold(it == jt);
|
|
it->second->setFont(0,f);
|
|
}
|
|
}
|
|
|
|
|
|
void TreeWidget::onTestStatus(void)
|
|
{
|
|
if (isVisible()) {
|
|
std::map<const Gui::Document*,DocumentItem*>::iterator pos;
|
|
for (pos = DocumentMap.begin();pos!=DocumentMap.end();++pos) {
|
|
pos->second->testStatus();
|
|
}
|
|
}
|
|
|
|
this->statusTimer->setSingleShot(true);
|
|
this->statusTimer->start(300);
|
|
}
|
|
|
|
void TreeWidget::onItemEntered(QTreeWidgetItem * item)
|
|
{
|
|
// object item selected
|
|
if ( item && item->type() == TreeWidget::ObjectType ) {
|
|
DocumentObjectItem* obj = static_cast<DocumentObjectItem*>(item);
|
|
obj->displayStatusInfo();
|
|
}
|
|
}
|
|
|
|
void TreeWidget::onItemCollapsed(QTreeWidgetItem * item)
|
|
{
|
|
// object item collapsed
|
|
if (item && item->type() == TreeWidget::ObjectType) {
|
|
DocumentObjectItem* obj = static_cast<DocumentObjectItem*>(item);
|
|
obj->setExpandedStatus(false);
|
|
}
|
|
}
|
|
|
|
void TreeWidget::onItemExpanded(QTreeWidgetItem * item)
|
|
{
|
|
// object item expanded
|
|
if (item && item->type() == TreeWidget::ObjectType) {
|
|
DocumentObjectItem* obj = static_cast<DocumentObjectItem*>(item);
|
|
obj->setExpandedStatus(true);
|
|
}
|
|
}
|
|
|
|
void TreeWidget::scrollItemToTop(Gui::Document* doc)
|
|
{
|
|
std::map<const Gui::Document*,DocumentItem*>::iterator it;
|
|
it = DocumentMap.find(doc);
|
|
if (it != DocumentMap.end()) {
|
|
DocumentItem* root = it->second;
|
|
QTreeWidgetItemIterator it(root, QTreeWidgetItemIterator::Selected);
|
|
for (; *it; ++it) {
|
|
if ((*it)->type() == TreeWidget::ObjectType) {
|
|
this->scrollToItem(*it, QAbstractItemView::PositionAtTop);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TreeWidget::changeEvent(QEvent *e)
|
|
{
|
|
if (e->type() == QEvent::LanguageChange) {
|
|
this->headerItem()->setText(0, tr("Labels & Attributes"));
|
|
this->rootItem->setText(0, tr("Application"));
|
|
}
|
|
|
|
QTreeWidget::changeEvent(e);
|
|
}
|
|
|
|
void TreeWidget::onItemSelectionChanged ()
|
|
{
|
|
// we already got notified by the selection to update the tree items
|
|
if (this->isConnectionBlocked())
|
|
return;
|
|
|
|
// block tmp. the connection to avoid to notify us ourself
|
|
bool lock = this->blockConnection(true);
|
|
std::map<const Gui::Document*,DocumentItem*>::iterator pos;
|
|
for (pos = DocumentMap.begin();pos!=DocumentMap.end();++pos) {
|
|
pos->second->updateSelection();
|
|
}
|
|
this->blockConnection(lock);
|
|
}
|
|
|
|
void TreeWidget::onSelectionChanged(const SelectionChanges& msg)
|
|
{
|
|
switch (msg.Type)
|
|
{
|
|
case SelectionChanges::AddSelection:
|
|
{
|
|
Gui::Document* pDoc = Application::Instance->getDocument(msg.pDocName);
|
|
std::map<const Gui::Document*, DocumentItem*>::iterator it;
|
|
it = DocumentMap.find(pDoc);
|
|
bool lock = this->blockConnection(true);
|
|
if (it!= DocumentMap.end())
|
|
it->second->setObjectSelected(msg.pObjectName,true);
|
|
this->blockConnection(lock);
|
|
} break;
|
|
case SelectionChanges::RmvSelection:
|
|
{
|
|
Gui::Document* pDoc = Application::Instance->getDocument(msg.pDocName);
|
|
std::map<const Gui::Document*, DocumentItem*>::iterator it;
|
|
it = DocumentMap.find(pDoc);
|
|
bool lock = this->blockConnection(true);
|
|
if (it!= DocumentMap.end())
|
|
it->second->setObjectSelected(msg.pObjectName,false);
|
|
this->blockConnection(lock);
|
|
} break;
|
|
case SelectionChanges::SetSelection:
|
|
{
|
|
Gui::Document* pDoc = Application::Instance->getDocument(msg.pDocName);
|
|
std::map<const Gui::Document*, DocumentItem*>::iterator it;
|
|
it = DocumentMap.find(pDoc);
|
|
// we get notified from the selection and must only update the selection on the tree,
|
|
// thus no need to notify again the selection. See also onItemSelectionChanged().
|
|
if (it != DocumentMap.end()) {
|
|
bool lock = this->blockConnection(true);
|
|
it->second->selectItems();
|
|
this->blockConnection(lock);
|
|
}
|
|
} break;
|
|
case SelectionChanges::ClrSelection:
|
|
{
|
|
// clears the complete selection
|
|
if (strcmp(msg.pDocName,"") == 0) {
|
|
this->clearSelection ();
|
|
}
|
|
else {
|
|
// clears the selection of the given document
|
|
Gui::Document* pDoc = Application::Instance->getDocument(msg.pDocName);
|
|
std::map<const Gui::Document*, DocumentItem*>::iterator it;
|
|
it = DocumentMap.find(pDoc);
|
|
if (it != DocumentMap.end()) {
|
|
it->second->clearSelection();
|
|
}
|
|
}
|
|
this->update();
|
|
} break;
|
|
case SelectionChanges::SetPreselect:
|
|
{
|
|
Gui::Document* pDoc = Application::Instance->getDocument(msg.pDocName);
|
|
std::map<const Gui::Document*, DocumentItem*>::iterator it;
|
|
it = DocumentMap.find(pDoc);
|
|
if (it!= DocumentMap.end())
|
|
it->second->setObjectHighlighted(msg.pObjectName,true);
|
|
} break;
|
|
case SelectionChanges::RmvPreselect:
|
|
{
|
|
Gui::Document* pDoc = Application::Instance->getDocument(msg.pDocName);
|
|
std::map<const Gui::Document*, DocumentItem*>::iterator it;
|
|
it = DocumentMap.find(pDoc);
|
|
if (it!= DocumentMap.end())
|
|
it->second->setObjectHighlighted(msg.pObjectName,false);
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TreeWidget::setItemsSelected (const QList<QTreeWidgetItem *> items, bool select)
|
|
{
|
|
if (items.isEmpty())
|
|
return;
|
|
QItemSelection range;
|
|
for (QList<QTreeWidgetItem*>::const_iterator it = items.begin(); it != items.end(); ++it)
|
|
range.select(this->indexFromItem(*it),this->indexFromItem(*it));
|
|
selectionModel()->select(range, select ?
|
|
QItemSelectionModel::Select :
|
|
QItemSelectionModel::Deselect);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/* TRANSLATOR Gui::TreeDockWidget */
|
|
TreeDockWidget::TreeDockWidget(Gui::Document* pcDocument,QWidget *parent)
|
|
: DockWindow(pcDocument,parent)
|
|
{
|
|
setWindowTitle(tr("Tree view"));
|
|
this->treeWidget = new TreeWidget(this);
|
|
this->treeWidget->setRootIsDecorated(false);
|
|
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/TreeView");
|
|
this->treeWidget->setIndentation(hGrp->GetInt("Indentation", this->treeWidget->indentation()));
|
|
|
|
QGridLayout* pLayout = new QGridLayout(this);
|
|
pLayout->setSpacing(0);
|
|
pLayout->setMargin (0);
|
|
pLayout->addWidget(this->treeWidget, 0, 0 );
|
|
}
|
|
|
|
TreeDockWidget::~TreeDockWidget()
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
DocumentItem::DocumentItem(const Gui::Document* doc, QTreeWidgetItem * parent)
|
|
: QTreeWidgetItem(parent, TreeWidget::DocumentType), pDocument(doc)
|
|
{
|
|
// Setup connections
|
|
connectNewObject = doc->signalNewObject.connect(boost::bind(&DocumentItem::slotNewObject, this, _1));
|
|
connectDelObject = doc->signalDeletedObject.connect(boost::bind(&DocumentItem::slotDeleteObject, this, _1));
|
|
connectChgObject = doc->signalChangedObject.connect(boost::bind(&DocumentItem::slotChangeObject, this, _1));
|
|
connectRenObject = doc->signalRelabelObject.connect(boost::bind(&DocumentItem::slotRenameObject, this, _1));
|
|
connectActObject = doc->signalActivatedObject.connect(boost::bind(&DocumentItem::slotActiveObject, this, _1));
|
|
connectEdtObject = doc->signalInEdit.connect(boost::bind(&DocumentItem::slotInEdit, this, _1));
|
|
connectResObject = doc->signalResetEdit.connect(boost::bind(&DocumentItem::slotResetEdit, this, _1));
|
|
connectHltObject = doc->signalHighlightObject.connect(boost::bind(&DocumentItem::slotHighlightObject, this, _1,_2,_3));
|
|
connectExpObject = doc->signalExpandObject.connect(boost::bind(&DocumentItem::slotExpandObject, this, _1,_2));
|
|
|
|
setFlags(Qt::ItemIsEnabled/*|Qt::ItemIsEditable*/);
|
|
}
|
|
|
|
DocumentItem::~DocumentItem()
|
|
{
|
|
connectNewObject.disconnect();
|
|
connectDelObject.disconnect();
|
|
connectChgObject.disconnect();
|
|
connectRenObject.disconnect();
|
|
connectActObject.disconnect();
|
|
connectEdtObject.disconnect();
|
|
connectResObject.disconnect();
|
|
connectHltObject.disconnect();
|
|
connectExpObject.disconnect();
|
|
}
|
|
|
|
|
|
|
|
void DocumentItem::slotInEdit(const Gui::ViewProviderDocumentObject& v)
|
|
{
|
|
std::string name (v.getObject()->getNameInDocument());
|
|
std::map<std::string, DocumentObjectItem*>::iterator it = ObjectMap.find(name);
|
|
if (it != ObjectMap.end())
|
|
it->second->setBackgroundColor(0,Qt::yellow);
|
|
}
|
|
|
|
void DocumentItem::slotResetEdit(const Gui::ViewProviderDocumentObject& v)
|
|
{
|
|
std::string name (v.getObject()->getNameInDocument());
|
|
std::map<std::string, DocumentObjectItem*>::iterator it = ObjectMap.find(name);
|
|
if (it != ObjectMap.end()) {
|
|
it->second->setData(0, Qt::BackgroundColorRole,QVariant());
|
|
}
|
|
}
|
|
|
|
void DocumentItem::slotNewObject(const Gui::ViewProviderDocumentObject& obj)
|
|
{
|
|
if (obj.showInTree()){
|
|
std::string displayName = obj.getObject()->Label.getValue();
|
|
std::string objectName = obj.getObject()->getNameInDocument();
|
|
std::map<std::string, DocumentObjectItem*>::iterator it = ObjectMap.find(objectName);
|
|
if (it == ObjectMap.end()) {
|
|
// cast to non-const object
|
|
DocumentObjectItem* item = new DocumentObjectItem(
|
|
const_cast<Gui::ViewProviderDocumentObject*>(&obj), this);
|
|
item->setIcon(0, obj.getIcon());
|
|
item->setText(0, QString::fromUtf8(displayName.c_str()));
|
|
ObjectMap[objectName] = item;
|
|
|
|
// it may be possible that the new object claims already existing objects. If this is the
|
|
// case we need to make sure this is shown by the tree
|
|
if(!obj.claimChildren().empty())
|
|
slotChangeObject(obj);
|
|
}else {
|
|
Base::Console().Warning("DocumentItem::slotNewObject: Cannot add view provider twice.\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void DocumentItem::slotDeleteObject(const Gui::ViewProviderDocumentObject& view)
|
|
{
|
|
App::DocumentObject* obj = view.getObject();
|
|
std::string objectName = obj->getNameInDocument();
|
|
std::map<std::string, DocumentObjectItem*>::iterator it = ObjectMap.find(objectName);
|
|
if (it != ObjectMap.end()) {
|
|
QTreeWidgetItem* parent = it->second->parent();
|
|
if (it->second->childCount() > 0) {
|
|
// When removing an object check if there are multiple parents of its children
|
|
//
|
|
// this removes the children from their parent
|
|
QList<QTreeWidgetItem*> children = it->second->takeChildren();
|
|
for (QList<QTreeWidgetItem*>::iterator jt = children.begin(); jt != children.end(); ++jt) {
|
|
std::vector<DocumentObjectItem*> parents = getAllParents(static_cast<DocumentObjectItem*>(*jt));
|
|
for (std::vector<DocumentObjectItem*>::iterator kt = parents.begin(); kt != parents.end(); ++kt) {
|
|
if (*kt != it->second) {
|
|
// there is another parent object of this child
|
|
(*kt)->addChild(*jt);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if there are still children, move them to the document item (#0001905)
|
|
QList<QTreeWidgetItem*> freeChildren;
|
|
for (QList<QTreeWidgetItem*>::iterator jt = children.begin(); jt != children.end(); ++jt) {
|
|
if (!(*jt)->parent())
|
|
freeChildren << *jt;
|
|
}
|
|
|
|
if (!freeChildren.isEmpty())
|
|
this->addChildren(freeChildren);
|
|
}
|
|
|
|
parent->takeChild(parent->indexOfChild(it->second));
|
|
delete it->second;
|
|
ObjectMap.erase(it);
|
|
}
|
|
}
|
|
|
|
void DocumentItem::slotChangeObject(const Gui::ViewProviderDocumentObject& view)
|
|
{
|
|
// As we immediately add a newly created object to the tree we check here which
|
|
// item (this or a DocumentObjectItem) is the parent of the associated item of 'view'
|
|
App::DocumentObject* obj = view.getObject();
|
|
std::string objectName = obj->getNameInDocument();
|
|
std::map<std::string, DocumentObjectItem*>::iterator it = ObjectMap.find(objectName);
|
|
if (it != ObjectMap.end()) {
|
|
// use new grouping style
|
|
DocumentObjectItem* parent_of_group = it->second;
|
|
std::set<QTreeWidgetItem*> children;
|
|
std::vector<App::DocumentObject*> group = view.claimChildren();
|
|
int group_index = 0; // counter of children inserted to the tree
|
|
for (std::vector<App::DocumentObject*>::iterator jt = group.begin(); jt != group.end(); ++jt) {
|
|
if ((*jt) && view.getObject()->getDocument()->isIn(*jt)){
|
|
// Note: It is possible that we receive an invalid pointer from claimChildren(), e.g. if multiple properties
|
|
// were changed in a transaction and slotChangedObject() is triggered by one property being reset
|
|
// before the invalid pointer has been removed from another. Currently this happens for PartDesign::Body
|
|
// when cancelling a new feature in the dialog. First the new feature is deleted, then the Tip property is
|
|
// reset, but claimChildren() accesses the Model property which still contains the pointer to the deleted feature
|
|
const char* internalName = (*jt)->getNameInDocument();
|
|
if (internalName) {
|
|
std::map<std::string, DocumentObjectItem*>::iterator kt = ObjectMap.find(internalName);
|
|
if (kt != ObjectMap.end()) {
|
|
DocumentObjectItem* child_of_group = kt->second;
|
|
children.insert(child_of_group);
|
|
QTreeWidgetItem* parent_of_child = child_of_group->parent();
|
|
|
|
if (parent_of_child) {
|
|
if (parent_of_child != parent_of_group) {
|
|
if (parent_of_group != child_of_group) {
|
|
// This child's parent must be adjusted
|
|
parent_of_child->removeChild(child_of_group);
|
|
// Insert the child at the correct position according to the order of the children returned
|
|
// by claimChildren
|
|
if (group_index <= parent_of_group->childCount())
|
|
parent_of_group->insertChild(group_index, child_of_group);
|
|
else
|
|
parent_of_group->addChild(child_of_group);
|
|
group_index++;
|
|
} else {
|
|
Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Object references to itself.\n");
|
|
}
|
|
} else {
|
|
// The child already in the right group, but we may need to ajust it's index to follow the order of claimChildren
|
|
int index=parent_of_group->indexOfChild (child_of_group);
|
|
if (index>group_index) {
|
|
parent_of_group->takeChild (index);
|
|
parent_of_group->insertChild (group_index, child_of_group);
|
|
}
|
|
group_index++;
|
|
}
|
|
} else {
|
|
Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): "
|
|
"'%s' claimed a top level object '%s' to be it's child.\n", objectName.c_str(), internalName);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Cannot reparent unknown object.\n");
|
|
}
|
|
}
|
|
else {
|
|
Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Group references unknown object.\n");
|
|
}
|
|
}
|
|
|
|
// move all children which are not part of the group anymore to this item
|
|
int count = parent_of_group->childCount();
|
|
for (int i=0; i < count; i++) {
|
|
QTreeWidgetItem* child = parent_of_group->child(i);
|
|
if (children.find(child) == children.end()) {
|
|
parent_of_group->takeChild(i);
|
|
this->addChild(child);
|
|
}
|
|
}
|
|
|
|
// set the text label
|
|
std::string displayName = obj->Label.getValue();
|
|
parent_of_group->setText(0, QString::fromUtf8(displayName.c_str()));
|
|
}
|
|
else {
|
|
Base::Console().Warning("Gui::DocumentItem::slotChangedObject(): Cannot change unknown object.\n");
|
|
}
|
|
}
|
|
|
|
void DocumentItem::slotRenameObject(const Gui::ViewProviderDocumentObject& obj)
|
|
{
|
|
// Do nothing here because the Label is set in slotChangeObject
|
|
}
|
|
|
|
void DocumentItem::slotActiveObject(const Gui::ViewProviderDocumentObject& obj)
|
|
{
|
|
std::string objectName = obj.getObject()->getNameInDocument();
|
|
std::map<std::string, DocumentObjectItem*>::iterator jt = ObjectMap.find(objectName);
|
|
if (jt == ObjectMap.end())
|
|
return; // signal is emitted before the item gets created
|
|
for (std::map<std::string, DocumentObjectItem*>::iterator it = ObjectMap.begin();
|
|
it != ObjectMap.end(); ++it)
|
|
{
|
|
QFont f = it->second->font(0);
|
|
f.setBold(it == jt);
|
|
it->second->setFont(0,f);
|
|
}
|
|
}
|
|
|
|
void DocumentItem::slotHighlightObject (const Gui::ViewProviderDocumentObject& obj,const Gui::HighlightMode& high,bool set)
|
|
{
|
|
std::string objectName = obj.getObject()->getNameInDocument();
|
|
std::map<std::string, DocumentObjectItem*>::iterator jt = ObjectMap.find(objectName);
|
|
if (jt == ObjectMap.end())
|
|
return; // signal is emitted before the item gets created
|
|
|
|
QFont f = jt->second->font(0);
|
|
switch (high) {
|
|
case Gui::Bold: f.setBold(set); break;
|
|
case Gui::Italic: f.setItalic(set); break;
|
|
case Gui::Underlined: f.setUnderline(set); break;
|
|
case Gui::Overlined: f.setOverline(set); break;
|
|
case Gui::Blue:
|
|
if(set)
|
|
jt->second->setBackgroundColor(0,QColor(200,200,255));
|
|
else
|
|
jt->second->setData(0, Qt::BackgroundColorRole,QVariant());
|
|
break;
|
|
case Gui::LightBlue:
|
|
if(set)
|
|
jt->second->setBackgroundColor(0,QColor(230,230,255));
|
|
else
|
|
jt->second->setData(0, Qt::BackgroundColorRole,QVariant());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
jt->second->setFont(0,f);
|
|
|
|
}
|
|
|
|
void DocumentItem::slotExpandObject (const Gui::ViewProviderDocumentObject& obj,const Gui::TreeItemMode& mode)
|
|
{
|
|
std::string objectName = obj.getObject()->getNameInDocument();
|
|
std::map<std::string, DocumentObjectItem*>::iterator jt = ObjectMap.find(objectName);
|
|
if (jt == ObjectMap.end())
|
|
return; // signal is emitted before the item gets created
|
|
|
|
switch (mode) {
|
|
case Gui::Expand:
|
|
jt->second->setExpanded(true);
|
|
break;
|
|
case Gui::Collapse:
|
|
jt->second->setExpanded(false);
|
|
break;
|
|
case Gui::Toggle:
|
|
if (jt->second->isExpanded())
|
|
jt->second->setExpanded(false);
|
|
else
|
|
jt->second->setExpanded(true);
|
|
break;
|
|
|
|
default:
|
|
// not defined enum
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
const Gui::Document* DocumentItem::document() const
|
|
{
|
|
return this->pDocument;
|
|
}
|
|
|
|
//void DocumentItem::markItem(const App::DocumentObject* Obj,bool mark)
|
|
//{
|
|
// // never call without Object!
|
|
// assert(Obj);
|
|
//
|
|
//
|
|
// std::map<std::string,DocumentObjectItem*>::iterator pos;
|
|
// pos = ObjectMap.find(Obj->getNameInDocument());
|
|
// if (pos != ObjectMap.end()) {
|
|
// QFont f = pos->second->font(0);
|
|
// f.setUnderline(mark);
|
|
// pos->second->setFont(0,f);
|
|
// }
|
|
//}
|
|
|
|
//void DocumentItem::markItem(const App::DocumentObject* Obj,bool mark)
|
|
//{
|
|
// // never call without Object!
|
|
// assert(Obj);
|
|
//
|
|
//
|
|
// std::map<std::string,DocumentObjectItem*>::iterator pos;
|
|
// pos = ObjectMap.find(Obj->getNameInDocument());
|
|
// if (pos != ObjectMap.end()) {
|
|
// QFont f = pos->second->font(0);
|
|
// f.setUnderline(mark);
|
|
// pos->second->setFont(0,f);
|
|
// }
|
|
//}
|
|
|
|
void DocumentItem::testStatus(void)
|
|
{
|
|
for (std::map<std::string,DocumentObjectItem*>::iterator pos = ObjectMap.begin();pos!=ObjectMap.end();++pos) {
|
|
pos->second->testStatus();
|
|
}
|
|
}
|
|
|
|
void DocumentItem::setData (int column, int role, const QVariant & value)
|
|
{
|
|
if (role == Qt::EditRole) {
|
|
QString label = value.toString();
|
|
pDocument->getDocument()->Label.setValue((const char*)label.toUtf8());
|
|
}
|
|
|
|
QTreeWidgetItem::setData(column, role, value);
|
|
}
|
|
|
|
void DocumentItem::setObjectHighlighted(const char* name, bool select)
|
|
{
|
|
std::map<std::string,DocumentObjectItem*>::iterator pos;
|
|
pos = ObjectMap.find(name);
|
|
if (pos != ObjectMap.end()) {
|
|
//pos->second->setData(0, Qt::TextColorRole, QVariant(Qt::red));
|
|
//treeWidget()->setItemSelected(pos->second, select);
|
|
}
|
|
}
|
|
|
|
void DocumentItem::setObjectSelected(const char* name, bool select)
|
|
{
|
|
std::map<std::string,DocumentObjectItem*>::iterator pos;
|
|
pos = ObjectMap.find(name);
|
|
if (pos != ObjectMap.end()) {
|
|
treeWidget()->setItemSelected(pos->second, select);
|
|
}
|
|
}
|
|
|
|
void DocumentItem::clearSelection(void)
|
|
{
|
|
// Block signals here otherwise we get a recursion and quadratic runtime
|
|
bool ok = treeWidget()->blockSignals(true);
|
|
for (std::map<std::string,DocumentObjectItem*>::iterator pos = ObjectMap.begin();pos!=ObjectMap.end();++pos) {
|
|
pos->second->setSelected(false);
|
|
}
|
|
treeWidget()->blockSignals(ok);
|
|
}
|
|
|
|
void DocumentItem::updateSelection(void)
|
|
{
|
|
std::vector<App::DocumentObject*> sel;
|
|
for (std::map<std::string,DocumentObjectItem*>::iterator pos = ObjectMap.begin();pos!=ObjectMap.end();++pos) {
|
|
if (treeWidget()->isItemSelected(pos->second)) {
|
|
sel.push_back(pos->second->object()->getObject());
|
|
}
|
|
}
|
|
|
|
Gui::Selection().setSelection(pDocument->getDocument()->getName(), sel);
|
|
}
|
|
|
|
namespace Gui {
|
|
struct ObjectItem_Less : public std::binary_function<DocumentObjectItem*,
|
|
DocumentObjectItem*, bool>
|
|
{
|
|
bool operator()(DocumentObjectItem* x, DocumentObjectItem* y) const
|
|
{
|
|
return x->object()->getObject() < y->object()->getObject();
|
|
}
|
|
};
|
|
|
|
struct ObjectItem_Equal : public std::binary_function<DocumentObjectItem*,
|
|
App::DocumentObject*, bool>
|
|
{
|
|
bool operator()(DocumentObjectItem* x, App::DocumentObject* y) const
|
|
{
|
|
return x->object()->getObject() == y;
|
|
}
|
|
};
|
|
}
|
|
|
|
void DocumentItem::selectItems(void)
|
|
{
|
|
// get an array of all tree items of the document and sort it in ascending order
|
|
// with regard to their document object
|
|
std::vector<DocumentObjectItem*> items;
|
|
for (std::map<std::string,DocumentObjectItem*>::iterator it = ObjectMap.begin(); it != ObjectMap.end(); ++it) {
|
|
items.push_back(it->second);
|
|
}
|
|
std::sort(items.begin(), items.end(), ObjectItem_Less());
|
|
|
|
// get and sort all selected document objects of the given document
|
|
std::vector<App::DocumentObject*> objs;
|
|
std::vector<SelectionSingleton::SelObj> obj = Selection().getSelection(pDocument->getDocument()->getName());
|
|
for (std::vector<SelectionSingleton::SelObj>::iterator jt = obj.begin(); jt != obj.end(); ++jt) {
|
|
objs.push_back(jt->pObject);
|
|
}
|
|
std::sort(objs.begin(), objs.end());
|
|
|
|
// The document objects in 'objs' is a subset of the document objects stored
|
|
// in 'items'. Since both arrays are sorted we get the wanted tree items in
|
|
// linear time.
|
|
std::vector<DocumentObjectItem*> common;
|
|
std::vector<DocumentObjectItem*>::iterator item_it = items.begin();
|
|
for (std::vector<App::DocumentObject*>::iterator it = objs.begin(); it != objs.end(); ++it) {
|
|
item_it = std::find_if(item_it, items.end(), std::bind2nd(ObjectItem_Equal(), *it));
|
|
if (item_it == items.end())
|
|
break; // should never ever happen
|
|
common.push_back(*item_it);
|
|
}
|
|
|
|
// get all unselected items of the given document
|
|
std::sort(common.begin(), common.end());
|
|
std::sort(items.begin(), items.end());
|
|
std::vector<DocumentObjectItem*> diff;
|
|
std::back_insert_iterator<std::vector<DocumentObjectItem*> > biit(diff);
|
|
std::set_difference(items.begin(), items.end(), common.begin(), common.end(), biit);
|
|
|
|
// select the appropriate items
|
|
QList<QTreeWidgetItem *> selitems;
|
|
for (std::vector<DocumentObjectItem*>::iterator it = common.begin(); it != common.end(); ++it)
|
|
selitems.append(*it);
|
|
static_cast<TreeWidget*>(treeWidget())->setItemsSelected(selitems, true);
|
|
// deselect the appropriate items
|
|
QList<QTreeWidgetItem *> deselitems;
|
|
for (std::vector<DocumentObjectItem*>::iterator it = diff.begin(); it != diff.end(); ++it)
|
|
deselitems.append(*it);
|
|
static_cast<TreeWidget*>(treeWidget())->setItemsSelected(deselitems, false);
|
|
}
|
|
|
|
std::vector<DocumentObjectItem*> DocumentItem::getAllParents(DocumentObjectItem* item) const
|
|
{
|
|
std::vector<DocumentObjectItem*> parents;
|
|
App::DocumentObject* obj = item->object()->getObject();
|
|
std::vector<App::DocumentObject*> inlist = obj->getInList();
|
|
|
|
for (std::vector<App::DocumentObject*>::iterator it = inlist.begin(); it != inlist.end(); ++it) {
|
|
Gui::ViewProvider* vp = pDocument->getViewProvider(*it);
|
|
if(!vp)
|
|
continue;
|
|
std::vector<App::DocumentObject*> child = vp->claimChildren();
|
|
for (std::vector<App::DocumentObject*>::iterator jt = child.begin(); jt != child.end(); ++jt) {
|
|
if (*jt == obj) {
|
|
std::map<std::string, DocumentObjectItem*>::const_iterator kt;
|
|
kt = ObjectMap.find((*it)->getNameInDocument());
|
|
if (kt != ObjectMap.end()) {
|
|
parents.push_back(kt->second);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return parents;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
DocumentObjectItem::DocumentObjectItem(Gui::ViewProviderDocumentObject* pcViewProvider,
|
|
QTreeWidgetItem* parent)
|
|
: QTreeWidgetItem(parent, TreeWidget::ObjectType), previousStatus(-1), viewObject(pcViewProvider)
|
|
{
|
|
setFlags(flags()|Qt::ItemIsEditable);
|
|
// Setup connections
|
|
connectIcon = pcViewProvider->signalChangeIcon.connect(boost::bind(&DocumentObjectItem::slotChangeIcon, this));
|
|
connectTool = pcViewProvider->signalChangeToolTip.connect(boost::bind(&DocumentObjectItem::slotChangeToolTip, this, _1));
|
|
connectStat = pcViewProvider->signalChangeStatusTip.connect(boost::bind(&DocumentObjectItem::slotChangeStatusTip, this, _1));
|
|
}
|
|
|
|
DocumentObjectItem::~DocumentObjectItem()
|
|
{
|
|
connectIcon.disconnect();
|
|
connectTool.disconnect();
|
|
connectStat.disconnect();
|
|
}
|
|
|
|
Gui::ViewProviderDocumentObject* DocumentObjectItem::object() const
|
|
{
|
|
return viewObject;
|
|
}
|
|
|
|
void DocumentObjectItem::testStatus()
|
|
{
|
|
App::DocumentObject* pObject = viewObject->getObject();
|
|
|
|
// if status has changed then continue
|
|
int currentStatus =
|
|
((pObject->isError() ? 1 : 0) << 2) |
|
|
((pObject->mustExecute() == 1 ? 1 : 0) << 1) |
|
|
(viewObject->isShow() ? 1 : 0);
|
|
if (previousStatus == currentStatus)
|
|
return;
|
|
previousStatus = currentStatus;
|
|
|
|
QPixmap px;
|
|
if (currentStatus & 4) {
|
|
// object is in error state
|
|
static const char * const feature_error_xpm[]={
|
|
"9 9 3 1",
|
|
". c None",
|
|
"# c #ff0000",
|
|
"a c #ffffff",
|
|
"...###...",
|
|
".##aaa##.",
|
|
".##aaa##.",
|
|
"###aaa###",
|
|
"###aaa###",
|
|
"#########",
|
|
".##aaa##.",
|
|
".##aaa##.",
|
|
"...###..."};
|
|
px = QPixmap(feature_error_xpm);
|
|
}
|
|
else if (currentStatus & 2) {
|
|
// object must be recomputed
|
|
static const char * const feature_recompute_xpm[]={
|
|
"9 9 3 1",
|
|
". c None",
|
|
"# c #0000ff",
|
|
"a c #ffffff",
|
|
"...###...",
|
|
".######aa",
|
|
".#####aa.",
|
|
"#####aa##",
|
|
"#aa#aa###",
|
|
"#aaaa####",
|
|
".#aa####.",
|
|
".#######.",
|
|
"...###..."};
|
|
px = QPixmap(feature_recompute_xpm);
|
|
}
|
|
|
|
QIcon::Mode mode = QIcon::Normal;
|
|
if (currentStatus & 1) { // visible
|
|
// Note: By default the foreground, i.e. text color is invalid
|
|
// to make use of the default color of the tree widget's palette.
|
|
// If we temporarily set this color to dark and reset to an invalid
|
|
// color again we cannot do it with setTextColor() or setForeground(),
|
|
// respectively, because for any reason the color would always switch
|
|
// to black which will lead to unreadable text if the system background
|
|
// hss already a dark color.
|
|
// However, it works if we set the appropriate role to an empty QVariant().
|
|
#if QT_VERSION >= 0x040200
|
|
this->setData(0, Qt::ForegroundRole,QVariant());
|
|
#else
|
|
this->setData(0, Qt::TextColorRole,QVariant());
|
|
#endif
|
|
}
|
|
else { // invisible
|
|
QStyleOptionViewItem opt;
|
|
opt.initFrom(this->treeWidget());
|
|
#if QT_VERSION >= 0x040200
|
|
this->setForeground(0, opt.palette.color(QPalette::Disabled,QPalette::Text));
|
|
#else
|
|
this->setTextColor(0, opt.palette.color(QPalette::Disabled,QPalette::Text);
|
|
#endif
|
|
mode = QIcon::Disabled;
|
|
}
|
|
|
|
// get the original icon set
|
|
QIcon icon_org = viewObject->getIcon();
|
|
QIcon icon_mod;
|
|
int w = QApplication::style()->pixelMetric(QStyle::PM_ListViewIconSize);
|
|
|
|
// if needed show small pixmap inside
|
|
if (!px.isNull()) {
|
|
icon_mod.addPixmap(BitmapFactory().merge(icon_org.pixmap(w, w, mode, QIcon::Off),
|
|
px,BitmapFactoryInst::TopRight), QIcon::Normal, QIcon::Off);
|
|
icon_mod.addPixmap(BitmapFactory().merge(icon_org.pixmap(w, w, mode, QIcon::On ),
|
|
px,BitmapFactoryInst::TopRight), QIcon::Normal, QIcon::Off);
|
|
}
|
|
else {
|
|
icon_mod.addPixmap(icon_org.pixmap(w, w, mode, QIcon::Off), QIcon::Normal, QIcon::Off);
|
|
icon_mod.addPixmap(icon_org.pixmap(w, w, mode, QIcon::On ), QIcon::Normal, QIcon::On );
|
|
}
|
|
|
|
this->setIcon(0, icon_mod);
|
|
}
|
|
|
|
bool DocumentObjectItem::allowDrop(const std::vector<const App::DocumentObject*> &objList,Qt::KeyboardModifiers keys,Qt::MouseButtons mouseBts,const QPoint &pos)
|
|
{
|
|
return viewObject->allowDrop(objList,keys,mouseBts,pos);
|
|
}
|
|
void DocumentObjectItem::drop(const std::vector<const App::DocumentObject*> &objList,Qt::KeyboardModifiers keys,Qt::MouseButtons mouseBts,const QPoint &pos)
|
|
{
|
|
viewObject->drop(objList,keys,mouseBts,pos);
|
|
}
|
|
|
|
void DocumentObjectItem::displayStatusInfo()
|
|
{
|
|
App::DocumentObject* Obj = viewObject->getObject();
|
|
|
|
QString info = QString::fromLatin1(Obj->getStatusString());
|
|
if ( Obj->mustExecute() == 1 )
|
|
info += QString::fromLatin1(" (but must be executed)");
|
|
getMainWindow()->showMessage( info );
|
|
|
|
if (Obj->isError()) {
|
|
QTreeWidget* tree = this->treeWidget();
|
|
QPoint pos = tree->visualItemRect(this).topRight();
|
|
QToolTip::showText(tree->mapToGlobal(pos), info);
|
|
}
|
|
}
|
|
|
|
void DocumentObjectItem::setExpandedStatus(bool on)
|
|
{
|
|
App::DocumentObject* Obj = viewObject->getObject();
|
|
Obj->setStatus(App::Expand, on);
|
|
}
|
|
|
|
void DocumentObjectItem::setData (int column, int role, const QVariant & value)
|
|
{
|
|
QTreeWidgetItem::setData(column, role, value);
|
|
if (role == Qt::EditRole) {
|
|
QString label = value.toString();
|
|
viewObject->getObject()->Label.setValue((const char*)label.toUtf8());
|
|
}
|
|
}
|
|
|
|
bool DocumentObjectItem::isChildOfItem(DocumentObjectItem* item)
|
|
{
|
|
int numChild = item->childCount();
|
|
for (int i=0; i<numChild; i++) {
|
|
QTreeWidgetItem* child = item->child(i);
|
|
if (child == this)
|
|
return true;
|
|
if (child->type() == TreeWidget::ObjectType) {
|
|
DocumentObjectItem* obj = static_cast<DocumentObjectItem*>(child);
|
|
if (this->isChildOfItem(obj))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void DocumentObjectItem::slotChangeIcon()
|
|
{
|
|
previousStatus = -1;
|
|
testStatus();
|
|
}
|
|
|
|
void DocumentObjectItem::slotChangeToolTip(const QString& tip)
|
|
{
|
|
this->setToolTip(0, tip);
|
|
}
|
|
|
|
void DocumentObjectItem::slotChangeStatusTip(const QString& tip)
|
|
{
|
|
this->setStatusTip(0, tip);
|
|
}
|
|
|
|
|
|
|
|
#include "moc_Tree.cpp"
|
|
|