From 83a447f43f4699a665d21096061734acf9593cec Mon Sep 17 00:00:00 2001 From: wmayer Date: Sat, 11 May 2013 22:16:02 +0200 Subject: [PATCH] MVC for tree view --- src/Gui/DocumentModel.cpp | 278 ++++++++++++++++++++---------- src/Gui/DocumentModel.h | 4 +- src/Mod/Sandbox/Gui/Workbench.cpp | 30 ++++ src/Mod/Sandbox/Gui/Workbench.h | 1 + 4 files changed, 222 insertions(+), 91 deletions(-) diff --git a/src/Gui/DocumentModel.cpp b/src/Gui/DocumentModel.cpp index f65a58965..856200b99 100644 --- a/src/Gui/DocumentModel.cpp +++ b/src/Gui/DocumentModel.cpp @@ -30,6 +30,8 @@ # include #endif +#include + #include "DocumentModel.h" #include "Application.h" #include "BitmapFactory.h" @@ -54,8 +56,6 @@ namespace Gui { virtual ~DocumentModelIndex() { qDeleteAll(childItems); } - void reset() - { qDeleteAll(childItems); childItems.clear(); } void setParent(DocumentModelIndex* parent) { parentItem = parent; } DocumentModelIndex *parent() const @@ -64,7 +64,12 @@ namespace Gui { { childItems.append(child); child->setParent(this); } void removeChild(int row) { childItems.removeAt(row); } - + QList removeAll() + { + QList list = childItems; + childItems.clear(); + return list; + } DocumentModelIndex *child(int row) { return childItems.value(row); } int row() const @@ -91,11 +96,18 @@ namespace Gui { return Qt::ItemIsSelectable|Qt::ItemIsEnabled; } + protected: + void reset() + { qDeleteAll(childItems); childItems.clear(); } + protected: DocumentModelIndex() : parentItem(0) {} DocumentModelIndex *parentItem; QList childItems; }; + + // ------------------------------------------------------------------------ + // Root node class ApplicationIndex : public DocumentModelIndex { @@ -104,24 +116,22 @@ namespace Gui { public: ApplicationIndex(){} int findChild(const Gui::Document& d) const; - Qt::ItemFlags flags() const - { return Qt::ItemIsEnabled; } - QVariant data(int role) const - { - if (role == Qt::DecorationRole) { - return qApp->windowIcon(); - } - else if (role == Qt::DisplayRole) { - return DocumentModel::tr("Application"); - } - return QVariant(); - } + Qt::ItemFlags flags() const; + QVariant data(int role) const; }; + + // ------------------------------------------------------------------------ + // Document nodes class DocumentIndex : public DocumentModelIndex { + friend class ViewProviderIndex; TYPESYSTEM_HEADER(); static QIcon* documentIcon; + typedef boost::unordered_set IndexSet; + std::map vp_nodes; + void addToDocument(ViewProviderIndex*); + void removeFromDocument(ViewProviderIndex*); public: const Gui::Document& d; @@ -130,30 +140,18 @@ namespace Gui { if (!documentIcon) documentIcon = new QIcon(Gui::BitmapFactory().pixmap("Document")); } - int findViewProvider(const ViewProvider&) const; - void findViewProviders(const ViewProvider&, QList&) const; - QVariant data(int role) const + ~DocumentIndex() { - if (role == Qt::DecorationRole) { - return *documentIcon; - } - else if (role == Qt::DisplayRole) { - App::Document* doc = d.getDocument(); - return QString::fromUtf8(doc->Label.getValue()); - } - else if (role == Qt::FontRole) { - Document* doc = Application::Instance->activeDocument(); - QFont font; - font.setBold(doc==&d); - QVariant variant; - variant.setValue(font); - return variant; - } - - return QVariant(); + qDeleteAll(childItems); childItems.clear(); } + ViewProviderIndex* cloneViewProvider(const ViewProviderDocumentObject&) const; + int rowOfViewProvider(const ViewProviderDocumentObject&) const; + void findViewProviders(const ViewProviderDocumentObject&, QList&) const; + QVariant data(int role) const; }; + // ------------------------------------------------------------------------ + // Object nodes class ViewProviderIndex : public DocumentModelIndex { @@ -161,31 +159,18 @@ namespace Gui { public: const Gui::ViewProviderDocumentObject& v; - ViewProviderIndex(const Gui::ViewProviderDocumentObject& v) : v(v){} - void findViewProviders(const ViewProvider&, QList&) const; - QVariant data(int role) const - { - if (role == Qt::DecorationRole) { - return v.getIcon(); - } - else if (role == Qt::DisplayRole) { - App::DocumentObject* obj = v.getObject(); - return QString::fromUtf8(obj->Label.getValue()); - } - else if (role == Qt::FontRole) { - App::DocumentObject* obj = v.getObject(); - App::DocumentObject* act = obj->getDocument()->getActiveObject(); - QFont font; - font.setBold(obj==act); - QVariant variant; - variant.setValue(font); - return variant; - } + ViewProviderIndex(const Gui::ViewProviderDocumentObject& v, DocumentIndex* d); + ~ViewProviderIndex(); + ViewProviderIndex* clone() const; + void findViewProviders(const ViewProviderDocumentObject&, QList&) const; + QVariant data(int role) const; - return QVariant(); - } + private: + DocumentIndex* d; }; + // ------------------------------------------------------------------------ + int ApplicationIndex::findChild(const Gui::Document& d) const { int child=0; @@ -199,9 +184,50 @@ namespace Gui { return -1; } + Qt::ItemFlags ApplicationIndex::flags() const + { + return Qt::ItemIsEnabled; + } + + QVariant ApplicationIndex::data(int role) const + { + if (role == Qt::DecorationRole) { + return qApp->windowIcon(); + } + else if (role == Qt::DisplayRole) { + return DocumentModel::tr("Application"); + } + return QVariant(); + } + + // ------------------------------------------------------------------------ + QIcon* DocumentIndex::documentIcon = 0; - void DocumentIndex::findViewProviders(const ViewProvider& vp, + void DocumentIndex::addToDocument(ViewProviderIndex* vp) + { + vp_nodes[&vp->v].insert(vp); + } + + void DocumentIndex::removeFromDocument(ViewProviderIndex* vp) + { + vp_nodes[&vp->v].erase(vp); + } + + ViewProviderIndex* + DocumentIndex::cloneViewProvider(const ViewProviderDocumentObject& vp) const + { + std::map >::const_iterator it; + it = vp_nodes.find(&vp); + if (it != vp_nodes.end()) { + boost::unordered_set::const_iterator v; + v = it->second.begin(); + return (*v)->clone(); + } + return new ViewProviderIndex(vp, const_cast(this)); + } + + void DocumentIndex::findViewProviders(const ViewProviderDocumentObject& vp, QList& index) const { QList::const_iterator it; @@ -211,7 +237,7 @@ namespace Gui { } } - int DocumentIndex::findViewProvider(const ViewProvider& vp) const + int DocumentIndex::rowOfViewProvider(const ViewProviderDocumentObject& vp) const { QList::const_iterator it; int index=0; @@ -224,8 +250,52 @@ namespace Gui { return -1; } - void ViewProviderIndex::findViewProviders(const ViewProvider& vp, - QList& index) const + QVariant DocumentIndex::data(int role) const + { + if (role == Qt::DecorationRole) { + return *documentIcon; + } + else if (role == Qt::DisplayRole) { + App::Document* doc = d.getDocument(); + return QString::fromUtf8(doc->Label.getValue()); + } + else if (role == Qt::FontRole) { + Document* doc = Application::Instance->activeDocument(); + QFont font; + font.setBold(doc==&d); + QVariant variant; + variant.setValue(font); + return variant; + } + + return QVariant(); + } + + // ------------------------------------------------------------------------ + + ViewProviderIndex::ViewProviderIndex(const Gui::ViewProviderDocumentObject& v, DocumentIndex* d) + : v(v),d(d) + { + if (d) d->addToDocument(this); + } + + ViewProviderIndex::~ViewProviderIndex() + { + if (d) d->removeFromDocument(this); + } + + ViewProviderIndex* ViewProviderIndex::clone() const + { + ViewProviderIndex* copy = new ViewProviderIndex(this->v, this->d); + for (QList::const_iterator it = childItems.begin(); it != childItems.end(); ++it) { + ViewProviderIndex* c = static_cast(*it)->clone(); + copy->appendChild(c); + } + return copy; + } + + void ViewProviderIndex::findViewProviders(const ViewProviderDocumentObject& vp, + QList& index) const { if (&this->v == &vp) index.push_back(const_cast(this)); @@ -236,6 +306,30 @@ namespace Gui { } } + QVariant ViewProviderIndex::data(int role) const + { + if (role == Qt::DecorationRole) { + return v.getIcon(); + } + else if (role == Qt::DisplayRole) { + App::DocumentObject* obj = v.getObject(); + return QString::fromUtf8(obj->Label.getValue()); + } + else if (role == Qt::FontRole) { + App::DocumentObject* obj = v.getObject(); + App::DocumentObject* act = obj->getDocument()->getActiveObject(); + QFont font; + font.setBold(obj==act); + QVariant variant; + variant.setValue(font); + return variant; + } + + return QVariant(); + } + + // ------------------------------------------------------------------------ + TYPESYSTEM_SOURCE_ABSTRACT(Gui::DocumentModelIndex, Base::BaseClass); TYPESYSTEM_SOURCE_ABSTRACT(Gui::ApplicationIndex,Gui::DocumentModelIndex); TYPESYSTEM_SOURCE_ABSTRACT(Gui::DocumentIndex, Gui::DocumentModelIndex); @@ -350,7 +444,7 @@ void DocumentModel::slotNewObject(const Gui::ViewProviderDocumentObject& obj) QModelIndex parent = createIndex(index->row(),0,index); int count_obj = index->childCount(); beginInsertRows(parent, count_obj, count_obj); - index->appendChild(new ViewProviderIndex(obj)); + index->appendChild(new ViewProviderIndex(obj, index)); endInsertRows(); } } @@ -399,35 +493,47 @@ void DocumentModel::slotChangeObject(const Gui::ViewProviderDocumentObject& obj, else if (isPropertyLink(Prop)) { App::Document* doc = fea->getDocument(); Gui::Document* gdc = Application::Instance->getDocument(doc); - std::vector views = getLinkedObjects(*gdc, Prop); + std::vector views = claimChildren(*gdc, obj); int row = d->rootItem->findChild(*gdc); if (row > -1) { + QList del_items; DocumentIndex* doc_index = static_cast(d->rootItem->child(row)); - QList obj_index; - doc_index->findViewProviders(obj, obj_index); - - // remove from top level in document for (std::vector::iterator vp = views.begin(); vp != views.end(); ++vp) { - int row = doc_index->findViewProvider(**vp); + int row = doc_index->rowOfViewProvider(**vp); + // is it a top-level child in the document if (row >= 0) { DocumentModelIndex* child = doc_index->child(row); + del_items.push_back(child); QModelIndex parent = createIndex(doc_index->row(), 0, doc_index); beginRemoveRows(parent, row, row); doc_index->removeChild(row); - delete child; endRemoveRows(); } } + // get all occurrences of the view provider in the tree structure + QList obj_index; + doc_index->findViewProviders(obj, obj_index); for (QList::iterator it = obj_index.begin(); it != obj_index.end(); ++it) { QModelIndex parent = createIndex((*it)->row(),0,*it); int count_obj = (*it)->childCount(); - beginInsertRows(parent, count_obj, count_obj + (int)views.size()); - for (std::vector::iterator jt = views.begin(); jt != views.end(); ++jt) - (*it)->appendChild(new ViewProviderIndex(**jt)); + beginRemoveRows(parent, 0, count_obj); + // remove all children but do not yet delete them + QList items = (*it)->removeAll(); + endRemoveRows(); + + beginInsertRows(parent, 0, (int)views.size()); + for (std::vector::iterator vp = views.begin(); vp != views.end(); ++vp) { + ViewProviderIndex* clone = doc_index->cloneViewProvider(**vp); + (*it)->appendChild(clone); + } endInsertRows(); + + del_items.append(items); } + + qDeleteAll(del_items); } } } @@ -461,33 +567,27 @@ bool DocumentModel::isPropertyLink(const App::Property& prop) const { if (prop.isDerivedFrom(App::PropertyLink::getClassTypeId())) return true; + if (prop.isDerivedFrom(App::PropertyLinkSub::getClassTypeId())) + return true; if (prop.isDerivedFrom(App::PropertyLinkList::getClassTypeId())) return true; + if (prop.isDerivedFrom(App::PropertyLinkSubList::getClassTypeId())) + return true; return false; } std::vector -DocumentModel::getLinkedObjects(const Gui::Document& doc, const App::Property& prop) const +DocumentModel::claimChildren(const Gui::Document& doc, const ViewProviderDocumentObject& obj) const { - std::vector links; - if (prop.isDerivedFrom(App::PropertyLink::getClassTypeId())) { - App::DocumentObject* obj; - obj = static_cast(prop).getValue(); - ViewProvider* view = doc.getViewProvider(obj); + std::vector views; + std::vector childs = obj.claimChildren(); + for (std::vector::iterator it = childs.begin(); it != childs.end(); ++it) { + ViewProvider* view = doc.getViewProvider(*it); if (view && view->getTypeId().isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) - links.push_back(static_cast(view)); - } - else if (prop.isDerivedFrom(App::PropertyLinkList::getClassTypeId())) { - const std::vector& refs = static_cast - (prop).getValues(); - for (std::vector::const_iterator it = refs.begin();it != refs.end(); ++it) { - ViewProvider* view = doc.getViewProvider(*it); - if (view && view->getTypeId().isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) - links.push_back(static_cast(view)); - } + views.push_back(static_cast(view)); } - return links; + return views; } int DocumentModel::columnCount (const QModelIndex & /*parent*/) const diff --git a/src/Gui/DocumentModel.h b/src/Gui/DocumentModel.h index aaaa89ef0..fafe3587b 100644 --- a/src/Gui/DocumentModel.h +++ b/src/Gui/DocumentModel.h @@ -67,8 +67,8 @@ private: const Document* getDocument(const QModelIndex&) const; bool isPropertyLink(const App::Property&) const; - std::vector getLinkedObjects - (const Gui::Document&, const App::Property&) const; + std::vector claimChildren + (const Document&, const ViewProviderDocumentObject&) const; private: struct DocumentModelP *d; diff --git a/src/Mod/Sandbox/Gui/Workbench.cpp b/src/Mod/Sandbox/Gui/Workbench.cpp index c87385295..de5753616 100644 --- a/src/Mod/Sandbox/Gui/Workbench.cpp +++ b/src/Mod/Sandbox/Gui/Workbench.cpp @@ -36,6 +36,10 @@ #include "Workbench.h" #include #include +#include +#include +#include +#include using namespace SandboxGui; @@ -44,6 +48,24 @@ TYPESYSTEM_SOURCE(SandboxGui::Workbench, Gui::StdWorkbench) Workbench::Workbench() { + // Tree view + Gui::DockWindow* tree = new Gui::DockWindow(0, Gui::getMainWindow()); + tree->setWindowTitle(QString::fromAscii("Tree view")); + Gui::TreeView* treeWidget = new Gui::TreeView(tree); + treeWidget->setRootIsDecorated(false); + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/TreeView"); + treeWidget->setIndentation(hGrp->GetInt("Indentation", treeWidget->indentation())); + + QGridLayout* pLayout = new QGridLayout(tree); + pLayout->setSpacing(0); + pLayout->setMargin (0); + pLayout->addWidget(treeWidget, 0, 0); + + tree->setObjectName + (QString::fromAscii(QT_TRANSLATE_NOOP("QDockWidget","Tree view (MVC)"))); + tree->setMinimumWidth(210); + Gui::DockWindowManager* pDockMgr = Gui::DockWindowManager::instance(); + pDockMgr->registerDockWindow("Std_TreeViewMVC", tree); } Workbench::~Workbench() @@ -109,6 +131,14 @@ Gui::ToolBarItem* Workbench::setupCommandBars() const return 0; } +Gui::DockWindowItems* Workbench::setupDockWindows() const +{ + Gui::DockWindowItems* root = Gui::StdWorkbench::setupDockWindows(); + root->setVisibility(false); // hide all dock windows by default + root->addDockWidget("Std_TreeViewMVC", Qt::RightDockWidgetArea, true, true); + return root; +} + // ---------------------------------------------------- diff --git a/src/Mod/Sandbox/Gui/Workbench.h b/src/Mod/Sandbox/Gui/Workbench.h index f923c433a..cdc20d370 100644 --- a/src/Mod/Sandbox/Gui/Workbench.h +++ b/src/Mod/Sandbox/Gui/Workbench.h @@ -43,6 +43,7 @@ protected: Gui::MenuItem* setupMenuBar() const; Gui::ToolBarItem* setupToolBars() const; Gui::ToolBarItem* setupCommandBars() const; + Gui::DockWindowItems* setupDockWindows() const; }; class SoWidgetShape : public SoShape {