1
/***************************************************************************
2
* Copyright (c) 2010 Werner Mayer <wmayer[at]users.sourceforge.net> *
4
* This file is part of the FreeCAD CAx development system. *
6
* This library is free software; you can redistribute it and/or *
7
* modify it under the terms of the GNU Library General Public *
8
* License as published by the Free Software Foundation; either *
9
* version 2 of the License, or (at your option) any later version. *
11
* This library is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU Library General Public License for more details. *
16
* You should have received a copy of the GNU Library General Public *
17
* License along with this library; see the file COPYING.LIB. If not, *
18
* write to the Free Software Foundation, Inc., 59 Temple Place, *
19
* Suite 330, Boston, MA 02111-1307, USA *
21
***************************************************************************/
23
#include "PreCompiled.h"
26
# include <QApplication>
30
#include <App/Document.h>
31
#include <App/DocumentObject.h>
32
#include <App/PropertyLinks.h>
34
#include "DocumentModel.h"
35
#include "Application.h"
36
#include "BitmapFactory.h"
38
#include "ViewProviderDocumentObject.h"
42
namespace sp = std::placeholders;
45
// forward declaration
46
class ViewProviderIndex;
49
class DocumentModelIndex : public Base::BaseClass
51
TYPESYSTEM_HEADER_WITH_OVERRIDE();
54
~DocumentModelIndex() override
55
{ qDeleteAll(childItems); }
57
void setParent(DocumentModelIndex* parent)
58
{ parentItem = parent; }
59
DocumentModelIndex *parent() const
60
{ return parentItem; }
61
void appendChild(DocumentModelIndex *child)
62
{ childItems.append(child); child->setParent(this); }
63
void removeChild(int row)
64
{ childItems.removeAt(row); }
65
QList<DocumentModelIndex*> removeAll()
67
QList<DocumentModelIndex*> list = childItems;
71
DocumentModelIndex *child(int row)
72
{ return childItems.value(row); }
76
return parentItem->childItems.indexOf
77
(const_cast<DocumentModelIndex*>(this));
80
int childCount() const
81
{ return childItems.count(); }
82
virtual QVariant data(int role) const
87
virtual bool setData (const QVariant & value, int role)
90
if (role == Qt::EditRole) {
96
virtual Qt::ItemFlags flags() const
98
return Qt::ItemIsSelectable|Qt::ItemIsEnabled;
103
{ qDeleteAll(childItems); childItems.clear(); }
106
DocumentModelIndex() = default;
107
DocumentModelIndex *parentItem{nullptr};
108
QList<DocumentModelIndex*> childItems;
111
// ------------------------------------------------------------------------
114
class ApplicationIndex : public DocumentModelIndex
116
TYPESYSTEM_HEADER_WITH_OVERRIDE();
119
ApplicationIndex() = default;
120
int findChild(const Gui::Document& d) const;
121
Qt::ItemFlags flags() const override;
122
QVariant data(int role) const override;
125
// ------------------------------------------------------------------------
128
class DocumentIndex : public DocumentModelIndex
130
friend class ViewProviderIndex;
131
TYPESYSTEM_HEADER_WITH_OVERRIDE();
132
static QIcon* documentIcon;
133
using IndexSet = boost::unordered_set<ViewProviderIndex*>;
134
std::map<const ViewProviderDocumentObject*, IndexSet> vp_nodes;
135
void addToDocument(ViewProviderIndex*);
136
void removeFromDocument(ViewProviderIndex*);
139
const Gui::Document& d;
140
explicit DocumentIndex(const Gui::Document& d) : d(d)
143
documentIcon = new QIcon(Gui::BitmapFactory().pixmap("Document"));
145
~DocumentIndex() override
147
qDeleteAll(childItems); childItems.clear();
149
ViewProviderIndex* cloneViewProvider(const ViewProviderDocumentObject&) const;
150
int rowOfViewProvider(const ViewProviderDocumentObject&) const;
151
void findViewProviders(const ViewProviderDocumentObject&, QList<ViewProviderIndex*>&) const;
152
QVariant data(int role) const override;
155
// ------------------------------------------------------------------------
158
class ViewProviderIndex : public DocumentModelIndex
160
TYPESYSTEM_HEADER_WITH_OVERRIDE();
163
const Gui::ViewProviderDocumentObject& v;
164
ViewProviderIndex(const Gui::ViewProviderDocumentObject& v, DocumentIndex* d);
165
~ViewProviderIndex() override;
166
ViewProviderIndex* clone() const;
167
void findViewProviders(const ViewProviderDocumentObject&, QList<ViewProviderIndex*>&) const;
168
QVariant data(int role) const override;
174
// ------------------------------------------------------------------------
176
int ApplicationIndex::findChild(const Gui::Document& d) const
179
QList<DocumentModelIndex*>::const_iterator it;
180
for (it = childItems.begin(); it != childItems.end(); ++it, ++child) {
181
auto doc = static_cast<DocumentIndex*>(*it);
189
Qt::ItemFlags ApplicationIndex::flags() const
191
return Qt::ItemIsEnabled;
194
QVariant ApplicationIndex::data(int role) const
196
if (role == Qt::DecorationRole) {
197
return qApp->windowIcon();
199
else if (role == Qt::DisplayRole) {
200
return DocumentModel::tr("Application");
205
// ------------------------------------------------------------------------
207
QIcon* DocumentIndex::documentIcon = nullptr;
209
void DocumentIndex::addToDocument(ViewProviderIndex* vp)
211
vp_nodes[&vp->v].insert(vp);
214
void DocumentIndex::removeFromDocument(ViewProviderIndex* vp)
216
vp_nodes[&vp->v].erase(vp);
220
DocumentIndex::cloneViewProvider(const ViewProviderDocumentObject& vp) const
222
std::map<const ViewProviderDocumentObject*, boost::unordered_set<ViewProviderIndex*> >::const_iterator it;
223
it = vp_nodes.find(&vp);
224
if (it != vp_nodes.end()) {
225
boost::unordered_set<ViewProviderIndex*>::const_iterator v;
226
if (!it->second.empty()) {
227
v = it->second.begin();
229
return (*v)->clone();
232
return new ViewProviderIndex(vp, const_cast<DocumentIndex*>(this));
235
void DocumentIndex::findViewProviders(const ViewProviderDocumentObject& vp,
236
QList<ViewProviderIndex*>& index) const
238
QList<DocumentModelIndex*>::const_iterator it;
239
for (it = childItems.begin(); it != childItems.end(); ++it) {
240
auto v = static_cast<ViewProviderIndex*>(*it);
241
v->findViewProviders(vp, index);
245
int DocumentIndex::rowOfViewProvider(const ViewProviderDocumentObject& vp) const
247
QList<DocumentModelIndex*>::const_iterator it;
249
for (it = childItems.begin(); it != childItems.end(); ++it, ++index) {
250
auto v = static_cast<ViewProviderIndex*>(*it);
258
QVariant DocumentIndex::data(int role) const
260
if (role == Qt::DecorationRole) {
261
return *documentIcon;
263
else if (role == Qt::DisplayRole) {
264
App::Document* doc = d.getDocument();
265
return QString::fromUtf8(doc->Label.getValue());
267
else if (role == Qt::FontRole) {
268
Document* doc = Application::Instance->activeDocument();
270
font.setBold(doc==&d);
271
return static_cast<QVariant>(font);
277
// ------------------------------------------------------------------------
279
ViewProviderIndex::ViewProviderIndex(const Gui::ViewProviderDocumentObject& v, DocumentIndex* d)
282
if (d) d->addToDocument(this);
285
ViewProviderIndex::~ViewProviderIndex()
287
if (d) d->removeFromDocument(this);
290
ViewProviderIndex* ViewProviderIndex::clone() const
292
auto copy = new ViewProviderIndex(this->v, this->d);
293
for (const auto & childItem : childItems) {
294
ViewProviderIndex* c = static_cast<ViewProviderIndex*>(childItem)->clone();
295
copy->appendChild(c);
300
void ViewProviderIndex::findViewProviders(const ViewProviderDocumentObject& vp,
301
QList<ViewProviderIndex*>& index) const
304
index.push_back(const_cast<ViewProviderIndex*>(this));
305
QList<DocumentModelIndex*>::const_iterator it;
306
for (it = childItems.begin(); it != childItems.end(); ++it) {
307
auto v = static_cast<ViewProviderIndex*>(*it);
308
v->findViewProviders(vp, index);
312
QVariant ViewProviderIndex::data(int role) const
314
if (role == Qt::DecorationRole) {
317
else if (role == Qt::DisplayRole) {
318
App::DocumentObject* obj = v.getObject();
319
return QString::fromUtf8(obj->Label.getValue());
321
else if (role == Qt::FontRole) {
322
App::DocumentObject* obj = v.getObject();
323
App::DocumentObject* act = obj->getDocument()->getActiveObject();
325
font.setBold(obj==act);
326
return static_cast<QVariant>(font);
332
// ------------------------------------------------------------------------
334
TYPESYSTEM_SOURCE_ABSTRACT(Gui::DocumentModelIndex, Base::BaseClass)
335
TYPESYSTEM_SOURCE_ABSTRACT(Gui::ApplicationIndex,Gui::DocumentModelIndex)
336
TYPESYSTEM_SOURCE_ABSTRACT(Gui::DocumentIndex, Gui::DocumentModelIndex)
337
TYPESYSTEM_SOURCE_ABSTRACT(Gui::ViewProviderIndex, Gui::DocumentModelIndex)
339
struct DocumentModelP
342
{ rootItem = new ApplicationIndex(); }
345
ApplicationIndex *rootItem;
349
// -----------------------------------------------------------------
351
DocumentModel::DocumentModel(QObject* parent)
352
: QAbstractItemModel(parent), d(new DocumentModelP)
354
static bool inittype = false;
357
DocumentModelIndex ::init();
358
ApplicationIndex ::init();
359
DocumentIndex ::init();
360
ViewProviderIndex ::init();
365
Application::Instance->signalNewDocument.connect(std::bind(&DocumentModel::slotNewDocument, this, sp::_1));
366
Application::Instance->signalDeleteDocument.connect(std::bind(&DocumentModel::slotDeleteDocument, this, sp::_1));
367
Application::Instance->signalRenameDocument.connect(std::bind(&DocumentModel::slotRenameDocument, this, sp::_1));
368
Application::Instance->signalActiveDocument.connect(std::bind(&DocumentModel::slotActiveDocument, this, sp::_1));
369
Application::Instance->signalRelabelDocument.connect(std::bind(&DocumentModel::slotRelabelDocument, this, sp::_1));
373
DocumentModel::~DocumentModel()
375
delete d; d = nullptr;
378
void DocumentModel::slotNewDocument(const Gui::Document& Doc)
381
Doc.signalNewObject.connect(std::bind(&DocumentModel::slotNewObject, this, sp::_1));
382
Doc.signalDeletedObject.connect(std::bind(&DocumentModel::slotDeleteObject, this, sp::_1));
383
Doc.signalChangedObject.connect(std::bind(&DocumentModel::slotChangeObject, this, sp::_1, sp::_2));
384
Doc.signalRelabelObject.connect(std::bind(&DocumentModel::slotRenameObject, this, sp::_1));
385
Doc.signalActivatedObject.connect(std::bind(&DocumentModel::slotActiveObject, this, sp::_1));
386
Doc.signalInEdit.connect(std::bind(&DocumentModel::slotInEdit, this, sp::_1));
387
Doc.signalResetEdit.connect(std::bind(&DocumentModel::slotResetEdit, this, sp::_1));
390
QModelIndex parent = createIndex(0,0,d->rootItem);
391
int count_docs = d->rootItem->childCount();
392
beginInsertRows(parent, count_docs, count_docs);
393
d->rootItem->appendChild(new DocumentIndex(Doc));
397
void DocumentModel::slotDeleteDocument(const Gui::Document& Doc)
399
int row = d->rootItem->findChild(Doc);
401
QModelIndex parent = createIndex(0,0,d->rootItem);
402
beginRemoveRows(parent, row, row);
403
DocumentModelIndex* item = d->rootItem->child(row);
404
d->rootItem->removeChild(row);
410
void DocumentModel::slotRenameDocument(const Gui::Document& Doc)
416
void DocumentModel::slotRelabelDocument(const Gui::Document& Doc)
418
int row = d->rootItem->findChild(Doc);
420
QModelIndex parent = createIndex(0,0,d->rootItem);
421
QModelIndex item = index (row, 0, parent);
422
Q_EMIT dataChanged(item, item);
426
void DocumentModel::slotActiveDocument(const Gui::Document& /*Doc*/)
428
// don't know which was the previous active document, so check simply all
429
QModelIndex parent = createIndex(0,0,d->rootItem);
430
QModelIndex top = index (0, 0, parent);
431
QModelIndex bottom = index (d->rootItem->childCount()-1, 0, parent);
432
Q_EMIT dataChanged(top, bottom);
435
void DocumentModel::slotInEdit(const Gui::ViewProviderDocumentObject& v)
440
void DocumentModel::slotResetEdit(const Gui::ViewProviderDocumentObject& v)
445
void DocumentModel::slotNewObject(const Gui::ViewProviderDocumentObject& obj)
447
App::Document* doc = obj.getObject()->getDocument();
448
Gui::Document* gdc = Application::Instance->getDocument(doc);
449
int row = d->rootItem->findChild(*gdc);
451
auto index = static_cast<DocumentIndex*>(d->rootItem->child(row));
452
QModelIndex parent = createIndex(index->row(),0,index);
453
int count_obj = index->childCount();
454
beginInsertRows(parent, count_obj, count_obj);
455
index->appendChild(new ViewProviderIndex(obj, index));
460
void DocumentModel::slotDeleteObject(const Gui::ViewProviderDocumentObject& obj)
462
App::Document* doc = obj.getObject()->getDocument();
463
Gui::Document* gdc = Application::Instance->getDocument(doc);
464
int row = d->rootItem->findChild(*gdc);
466
auto doc_index = static_cast<DocumentIndex*>(d->rootItem->child(row));
467
QList<ViewProviderIndex*> views;
468
doc_index->findViewProviders(obj, views);
469
for (auto & view : views) {
470
DocumentModelIndex* parentitem = view->parent();
471
QModelIndex parent = createIndex(doc_index->row(), 0, parentitem);
472
int row = view->row();
473
beginRemoveRows(parent, row, row);
474
parentitem->removeChild(row);
481
void DocumentModel::slotChangeObject(const Gui::ViewProviderDocumentObject& obj, const App::Property& Prop)
483
App::DocumentObject* fea = obj.getObject();
484
if (&fea->Label == &Prop) {
485
App::Document* doc = fea->getDocument();
486
Gui::Document* gdc = Application::Instance->getDocument(doc);
487
int row = d->rootItem->findChild(*gdc);
489
auto doc_index = static_cast<DocumentIndex*>(d->rootItem->child(row));
490
QList<ViewProviderIndex*> views;
491
doc_index->findViewProviders(obj, views);
492
for (const auto & view : qAsConst(views)) {
493
DocumentModelIndex* parentitem = view->parent();
494
QModelIndex parent = createIndex(0,0,parentitem);
495
int row = view->row();
496
QModelIndex item = index (row, 0, parent);
497
Q_EMIT dataChanged(item, item);
501
else if (isPropertyLink(Prop)) {
502
App::Document* doc = fea->getDocument();
503
Gui::Document* gdc = Application::Instance->getDocument(doc);
504
std::vector<ViewProviderDocumentObject*> views = claimChildren(*gdc, obj);
506
int row = d->rootItem->findChild(*gdc);
508
QList<DocumentModelIndex*> del_items;
509
auto doc_index = static_cast<DocumentIndex*>(d->rootItem->child(row));
510
for (const auto & view : views) {
511
int row = doc_index->rowOfViewProvider(*view);
512
// is it a top-level child in the document
514
DocumentModelIndex* child = doc_index->child(row);
515
del_items.push_back(child);
516
QModelIndex parent = createIndex(doc_index->row(), 0, doc_index);
517
beginRemoveRows(parent, row, row);
518
doc_index->removeChild(row);
523
// get all occurrences of the view provider in the tree structure
524
QList<ViewProviderIndex*> obj_index;
525
doc_index->findViewProviders(obj, obj_index);
526
for (const auto & it : qAsConst(obj_index)) {
527
QModelIndex parent = createIndex(it->row(),0,it);
528
int count_obj = it->childCount();
529
beginRemoveRows(parent, 0, count_obj);
530
// remove all children but do not yet delete them
531
QList<DocumentModelIndex*> items = it->removeAll();
534
beginInsertRows(parent, 0, (int)views.size());
535
for (const auto & view : views) {
536
ViewProviderIndex* clone = doc_index->cloneViewProvider(*view);
537
it->appendChild(clone);
541
del_items.append(items);
544
qDeleteAll(del_items);
549
void DocumentModel::slotRenameObject(const Gui::ViewProviderDocumentObject& obj)
552
// renaming of objects not supported at the moment
555
void DocumentModel::slotActiveObject(const Gui::ViewProviderDocumentObject& obj)
558
// do nothing here because this is automatically done by calling
559
// ViewProviderIndex::data()
562
const Document* DocumentModel::getDocument(const QModelIndex& index) const
564
if (!index.isValid())
566
Base::BaseClass* item = nullptr;
567
item = static_cast<Base::BaseClass*>(index.internalPointer());
568
if (item->is<DocumentIndex>()) {
569
const Gui::Document& d = static_cast<DocumentIndex*>(item)->d;
576
bool DocumentModel::isPropertyLink(const App::Property& prop) const
578
if (prop.isDerivedFrom(App::PropertyLink::getClassTypeId()))
580
if (prop.isDerivedFrom(App::PropertyLinkSub::getClassTypeId()))
582
if (prop.isDerivedFrom(App::PropertyLinkList::getClassTypeId()))
584
if (prop.isDerivedFrom(App::PropertyLinkSubList::getClassTypeId()))
589
std::vector<ViewProviderDocumentObject*>
590
DocumentModel::claimChildren(const Gui::Document& doc, const ViewProviderDocumentObject& obj) const
592
std::vector<ViewProviderDocumentObject*> views;
593
std::vector<App::DocumentObject*> childs = obj.claimChildren();
594
for (const auto & child : childs) {
595
ViewProvider* view = doc.getViewProvider(child);
596
if (view && view->isDerivedFrom<ViewProviderDocumentObject>())
597
views.push_back(static_cast<ViewProviderDocumentObject*>(view));
603
int DocumentModel::columnCount (const QModelIndex & /*parent*/) const
608
QVariant DocumentModel::data (const QModelIndex & index, int role) const
610
if (!index.isValid())
612
return static_cast<DocumentModelIndex*>(index.internalPointer())->data(role);
615
bool DocumentModel::setData(const QModelIndex& index, const QVariant & value, int role)
617
if (!index.isValid())
619
return static_cast<DocumentModelIndex*>(index.internalPointer())->setData(value, role);
622
Qt::ItemFlags DocumentModel::flags(const QModelIndex &index) const
624
//if (index.internalPointer() == d->rootItem)
625
// return Qt::ItemIsEnabled;
626
//return QAbstractItemModel::flags(index);
627
if (!index.isValid())
629
return static_cast<DocumentModelIndex*>(index.internalPointer())->flags();
632
QModelIndex DocumentModel::index (int row, int column, const QModelIndex & parent) const
634
DocumentModelIndex* item = nullptr;
635
if (!parent.isValid())
638
item = static_cast<DocumentModelIndex*>(parent.internalPointer())->child(row);
641
return createIndex(row, column, item);
644
QModelIndex DocumentModel::parent (const QModelIndex & index) const
646
if (!index.isValid() || index.internalPointer() == d->rootItem)
648
DocumentModelIndex* item = nullptr;
649
item = static_cast<DocumentModelIndex*>(index.internalPointer());
650
DocumentModelIndex* parent = item->parent();
651
return createIndex(parent->row(), 0, parent);
654
int DocumentModel::rowCount (const QModelIndex & parent) const
656
if (!parent.isValid())
657
return 1; // the root item
658
DocumentModelIndex* item = nullptr;
659
item = static_cast<DocumentModelIndex*>(parent.internalPointer());
660
return item->childCount();
663
QVariant DocumentModel::headerData (int section, Qt::Orientation orientation, int role) const
666
if (orientation == Qt::Horizontal) {
667
if (role != Qt::DisplayRole)
669
return tr("Labels & Attributes");
675
bool DocumentModel::setHeaderData (int, Qt::Orientation, const QVariant &, int)