1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
3 #include "PartSet_DocumentDataModel.h"
4 #include "PartSet_PartDataModel.h"
5 #include "PartSet_Module.h"
6 //#include "XGUI_Tools.h"
8 #include <ModelAPI_Session.h>
9 #include <ModelAPI_Document.h>
10 #include <ModelAPI_Feature.h>
11 #include <ModelAPI_Data.h>
12 #include <ModelAPI_ResultPart.h>
13 #include <ModelAPI_Events.h>
14 #include <ModelAPI_Object.h>
16 #include <Events_Loop.h>
18 #include <Config_FeatureMessage.h>
19 #include <ModuleBase_Tools.h>
20 #include <ModuleBase_ActionInfo.h>
28 #define ACTIVE_COLOR QColor(0,72,140)
29 #define PASSIVE_COLOR Qt::black
31 QMap<QString, QString> PartSet_DocumentDataModel::myIcons;
34 PartSet_DocumentDataModel::PartSet_DocumentDataModel(QObject* theParent)
35 : ModuleBase_IDocumentDataModel(theParent),
36 myActivePart(0), myHistoryBackOffset(0)
38 // Create a top part of data tree model
39 myModel = new PartSet_TopDataModel(this);
40 myModel->setItemsColor(ACTIVE_COLOR);
42 Events_Loop* aLoop = Events_Loop::loop();
43 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
44 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
45 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
46 aLoop->registerListener(this, Events_Loop::eventByName(Config_FeatureMessage::GUI_EVENT()));
49 PartSet_DocumentDataModel::~PartSet_DocumentDataModel()
55 void PartSet_DocumentDataModel::processEvent(const std::shared_ptr<Events_Message>& theMessage)
57 DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
59 // Created object event *******************
60 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)) {
61 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
62 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
63 std::set<ObjectPtr> aObjects = aUpdMsg->objects();
65 std::set<ObjectPtr>::const_iterator aIt;
66 for (aIt = aObjects.begin(); aIt != aObjects.end(); ++aIt) {
67 ObjectPtr aObject = (*aIt);
68 FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObject);
69 if (aFeature && (!aFeature->isInHistory()))
72 DocumentPtr aDoc = aObject->document();
73 if (aDoc == aRootDoc) { // If root objects
74 if (aObject->groupName() == ModelAPI_ResultPart::group()) { // Update only Parts group
76 int aStart = myPartModels.size();
77 PartSet_PartDataModel* aModel = new PartSet_PartDataModel(this);
78 aModel->setPartId(myPartModels.count());
79 myPartModels.append(aModel);
80 insertRow(aStart, partFolderNode(0));
81 } else { // Update top groups (other except parts
82 QModelIndex aIndex = myModel->findParent(aObject);
83 int aStart = myModel->rowCount(aIndex) - 1;
86 aIndex = createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex));
87 insertRow(aStart, aIndex);
89 } else { // if sub-objects of first level nodes
90 PartSet_PartModel* aPartModel = 0;
91 foreach (PartSet_PartModel* aPart, myPartModels) {
92 if (aPart->hasDocument(aDoc)) {
98 QModelIndex aIndex = aPartModel->findParent(aObject);
99 int aStart = aPartModel->rowCount(aIndex); // check this index
100 aIndex = createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex));
101 insertRow(aStart, aIndex);
106 // Deleted object event ***********************
107 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
108 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aUpdMsg =
109 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
110 DocumentPtr aDoc = aUpdMsg->document();
111 std::set<std::string> aGroups = aUpdMsg->groups();
113 std::set<std::string>::const_iterator aIt;
114 for (aIt = aGroups.begin(); aIt != aGroups.end(); ++aIt) {
115 std::string aGroup = (*aIt);
116 if (aDoc == aRootDoc) { // If root objects
117 if (aGroup == ModelAPI_ResultPart::group()) { // Update only Parts group
118 int aStart = myPartModels.size() - 1;
119 if (aStart >= 0) {// MPV: this could be reproduced on close
120 removeSubModel(aStart);
121 removeRow(aStart, partFolderNode(0));
122 if (myActivePart && (!isPartSubModel(myActivePart))) {
124 myActivePartIndex = QModelIndex();
125 myModel->setItemsColor(ACTIVE_COLOR);
128 } else { // Update top groups (other except parts
129 QModelIndex aIndex = myModel->findGroup(aGroup);
130 int aStart = myModel->rowCount(aIndex);
131 aIndex = createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex));
132 removeRow(aStart, aIndex);
135 PartSet_PartModel* aPartModel = 0;
136 foreach (PartSet_PartModel* aPart, myPartModels) {
137 if (aPart->hasDocument(aDoc)) {
143 QModelIndex aIndex = aPartModel->findGroup(aGroup);
144 int aStart = aPartModel->rowCount(aIndex);
145 aIndex = createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex));
146 removeRow(aStart, aIndex);
150 // Deleted object event ***********************
151 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)) {
152 //std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg = std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
153 //ObjectPtr aFeature = aUpdMsg->feature();
154 //DocumentPtr aDoc = aFeature->document();
156 // TODO: Identify the necessary index by the modified feature
158 emit dataChanged(aIndex, aIndex);
160 // Reset whole tree **************************
161 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(Config_FeatureMessage::GUI_EVENT())) {
162 std::shared_ptr<Config_FeatureMessage> aFeatureMsg =
163 std::dynamic_pointer_cast<Config_FeatureMessage>(theMessage);
164 if (!aFeatureMsg->isInternal()) {
165 ActionInfo aFeatureInfo;
166 aFeatureInfo.initFrom(aFeatureMsg);
167 // Remember features icons
168 myIcons[QString::fromStdString(aFeatureMsg->id())] = aFeatureInfo.iconFile;
175 void PartSet_DocumentDataModel::rebuildDataTree()
177 DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
182 int aNbParts = aRootDoc->size(ModelAPI_ResultPart::group());
183 if (myPartModels.size() != aNbParts) { // resize internal models
184 while (myPartModels.size() > aNbParts) {
185 delete myPartModels.last();
186 myPartModels.removeLast();
188 while (myPartModels.size() < aNbParts) {
189 myPartModels.append(new PartSet_PartDataModel(this));
191 for (int i = 0; i < myPartModels.size(); i++)
192 myPartModels.at(i)->setPartId(i);
197 QVariant PartSet_DocumentDataModel::data(const QModelIndex& theIndex, int theRole) const
199 if (!theIndex.isValid())
201 QModelIndex aParent = theIndex.parent();
203 if ((theIndex.column() == 1) ) {
204 if ((theIndex.internalId() == HistoryNode) && (!aParent.isValid())) {
206 case Qt::DecorationRole:
207 if (theIndex.row() == lastHistoryRow())
208 return QIcon(":pictures/arrow.png");
214 switch (theIndex.internalId()) {
217 case Qt::DisplayRole:
218 return tr("Parts") + QString(" (%1)").arg(rowCount(theIndex));
219 case Qt::DecorationRole:
220 return QIcon(":pictures/constr_folder.png");
221 case Qt::ToolTipRole:
222 return tr("Parts folder");
223 case Qt::ForegroundRole:
225 return QBrush(PASSIVE_COLOR);
227 return QBrush(ACTIVE_COLOR);
234 int aOffset = historyOffset();
235 DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
236 ObjectPtr aObj = aRootDoc->object(ModelAPI_Feature::group(), theIndex.row() - aOffset);
237 FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
241 case Qt::DisplayRole:
243 return aFeature->data()->name().c_str();
246 case Qt::DecorationRole:
247 return featureIcon(aFeature);
248 case Qt::ToolTipRole:
249 return tr("Feature object");
250 case Qt::ForegroundRole:
251 if (theIndex.row() > lastHistoryRow())
252 return QBrush(Qt::lightGray);
255 return QBrush(PASSIVE_COLOR);
257 return QBrush(ACTIVE_COLOR);
265 if (aParent.isValid() && (aParent.internalId() == PartsFolder)) {
266 return myPartModels.at(theIndex.row())->data(QModelIndex(), theRole);
268 return toSourceModelIndex(theIndex)->data(theRole);
271 QVariant PartSet_DocumentDataModel::headerData(int theSection, Qt::Orientation theOrient,
277 int PartSet_DocumentDataModel::rowCount(const QModelIndex& theParent) const
279 if (!theParent.isValid()) {
280 DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
281 // Size of external models
282 int aVal = historyOffset();
284 aVal += aRootDoc->size(ModelAPI_Feature::group());
287 if (theParent.internalId() == PartsFolder) {
288 int aSize = myPartModels.size();
289 return myPartModels.size();
291 if (theParent.internalId() == HistoryNode) {
294 QModelIndex* aParent = toSourceModelIndex(theParent);
295 const QAbstractItemModel* aModel = aParent->model();
296 if (!isSubModel(aModel))
299 /*if (isPartSubModel(aModel)) {
300 if (aModel != myActivePart)
303 return aModel->rowCount(*aParent);
306 int PartSet_DocumentDataModel::columnCount(const QModelIndex& theParent) const
311 QModelIndex PartSet_DocumentDataModel::index(int theRow, int theColumn,
312 const QModelIndex& theParent) const
315 if (!theParent.isValid()) {
316 int aOffs = myModel->rowCount();
317 if (theRow < aOffs) {
318 aIndex = myModel->index(theRow, theColumn, theParent);
319 aIndex = createIndex(theRow, theColumn, (void*) getModelIndex(aIndex));
321 if (theRow == aOffs) // Create Parts node
322 aIndex = partFolderNode(theColumn);
324 // create history node
325 aIndex = createIndex(theRow, theColumn, HistoryNode);
328 if (theParent.internalId() == PartsFolder) {
329 aIndex = myPartModels.at(theRow)->index(0, theColumn, QModelIndex());
331 QModelIndex* aParent = (QModelIndex*) theParent.internalPointer();
332 aIndex = aParent->model()->index(theRow, theColumn, (*aParent));
334 aIndex = createIndex(theRow, theColumn, (void*) getModelIndex(aIndex));
339 QModelIndex PartSet_DocumentDataModel::parent(const QModelIndex& theIndex) const
341 if ((theIndex.internalId() == PartsFolder) || (theIndex.internalId() == HistoryNode))
342 return QModelIndex();
344 QModelIndex* aIndex = toSourceModelIndex(theIndex);
345 const QAbstractItemModel* aModel = aIndex->model();
346 if (!isSubModel(aModel))
347 return QModelIndex();
349 if (isPartSubModel(aModel)) {
350 if (!aModel->parent(*aIndex).isValid()) {
351 return partFolderNode(theIndex.column());
355 QModelIndex aIndex1 = aModel->parent(*aIndex);
356 if (aIndex1.isValid())
357 return createIndex(aIndex1.row(), aIndex1.column(), (void*) getModelIndex(aIndex1));
361 bool PartSet_DocumentDataModel::hasChildren(const QModelIndex& theParent) const
363 if (!theParent.isValid())
365 return rowCount(theParent) > 0;
368 QModelIndex* PartSet_DocumentDataModel::toSourceModelIndex(const QModelIndex& theProxy) const
370 QModelIndex* aIndexPtr = static_cast<QModelIndex*>(theProxy.internalPointer());
374 QModelIndex* PartSet_DocumentDataModel::findModelIndex(const QModelIndex& theIndex) const
376 QList<QModelIndex*>::const_iterator aIt;
377 for (aIt = myIndexes.constBegin(); aIt != myIndexes.constEnd(); ++aIt) {
378 QModelIndex* aIndex = (*aIt);
379 if ((*aIndex) == theIndex)
385 QModelIndex* PartSet_DocumentDataModel::getModelIndex(const QModelIndex& theIndex) const
387 QModelIndex* aIndexPtr = findModelIndex(theIndex);
389 aIndexPtr = new QModelIndex(theIndex);
390 PartSet_DocumentDataModel* that = (PartSet_DocumentDataModel*) this;
391 that->myIndexes.append(aIndexPtr);
396 void PartSet_DocumentDataModel::clearModelIndexes()
398 foreach (QModelIndex* aIndex, myIndexes)
403 void PartSet_DocumentDataModel::clearSubModels()
405 foreach (PartSet_PartModel* aPart, myPartModels)
407 myPartModels.clear();
410 ObjectPtr PartSet_DocumentDataModel::object(const QModelIndex& theIndex) const
412 if (theIndex.internalId() == PartsFolder)
414 if (theIndex.internalId() == HistoryNode) {
415 DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
416 int aOffset = historyOffset();
417 return aRootDoc->object(ModelAPI_Feature::group(), theIndex.row() - aOffset);
419 QModelIndex* aIndex = toSourceModelIndex(theIndex);
420 if (!isSubModel(aIndex->model()))
423 const PartSet_FeaturesModel* aModel = dynamic_cast<const PartSet_FeaturesModel*>(aIndex->model());
424 return aModel->object(*aIndex);
427 bool PartSet_DocumentDataModel::insertRows(int theRow, int theCount, const QModelIndex& theParent)
429 beginInsertRows(theParent, theRow, theRow + theCount - 1);
434 int aRow = rowCount(aRoot);
435 beginInsertRows(aRoot, aRow, aRow);
441 bool PartSet_DocumentDataModel::removeRows(int theRow, int theCount, const QModelIndex& theParent)
443 beginRemoveRows(theParent, theRow, theRow + theCount - 1);
448 void PartSet_DocumentDataModel::removeSubModel(int theModelId)
450 PartSet_PartModel* aModel = myPartModels.at(theModelId);
452 for (int i = 0; i < myIndexes.size(); i++) {
453 if (myIndexes.at(i)->model() == aModel)
457 while (aToRemove.size() > 0) {
458 aId = aToRemove.last();
459 delete myIndexes.at(aId);
460 myIndexes.removeAt(aId);
461 aToRemove.removeLast();
464 myPartModels.removeAt(theModelId);
467 bool PartSet_DocumentDataModel::isSubModel(const QAbstractItemModel* theModel) const
469 if (theModel == myModel)
471 return isPartSubModel(theModel);
474 bool PartSet_DocumentDataModel::isPartSubModel(const QAbstractItemModel* theModel) const
476 return myPartModels.contains((PartSet_PartModel*) theModel);
479 QModelIndex PartSet_DocumentDataModel::partFolderNode(int theColumn) const
481 int aPos = myModel->rowCount(QModelIndex());
482 return createIndex(aPos, theColumn, PartsFolder);
485 int PartSet_DocumentDataModel::historyOffset() const
487 // Nb of rows of top model + Parts folder
488 return myModel->rowCount(QModelIndex()) + 1;
491 bool PartSet_DocumentDataModel::activatePart(const QModelIndex& theIndex)
493 if ((theIndex.internalId() == PartsFolder) || (theIndex.internalId() == HistoryNode))
496 QModelIndex* aIndex = toSourceModelIndex(theIndex);
500 const QAbstractItemModel* aModel = aIndex->model();
502 if (isPartSubModel(aModel)) {
503 // if this is root node (Part item index)
504 if (!aIndex->parent().isValid()) {
506 myActivePart->setItemsColor(PASSIVE_COLOR);
508 if (myActivePart == aModel) {
510 myActivePartIndex = QModelIndex();
512 myActivePart = (PartSet_PartModel*)aModel;
513 myActivePartIndex = theIndex;
517 myActivePart->setItemsColor(ACTIVE_COLOR);
518 myModel->setItemsColor(PASSIVE_COLOR);
520 myModel->setItemsColor(ACTIVE_COLOR);
527 ResultPartPtr PartSet_DocumentDataModel::activePart() const
530 return myActivePart->part();
531 return ResultPartPtr();
534 void PartSet_DocumentDataModel::deactivatePart()
537 myActivePart->setItemsColor(PASSIVE_COLOR);
539 myActivePartIndex = QModelIndex();
540 myModel->setItemsColor(ACTIVE_COLOR);
543 Qt::ItemFlags PartSet_DocumentDataModel::flags(const QModelIndex& theIndex) const
545 Qt::ItemFlags aFlags = QAbstractItemModel::flags(theIndex); //Qt::ItemIsSelectable;
546 if (object(theIndex)) {
547 aFlags |= Qt::ItemIsEditable;
549 // Disable items which are below of last history row
550 // Do not disable second column
551 //if (theIndex.row() <= lastHistoryRow() || theIndex.column() == 1) {
552 // aFlags |= Qt::ItemIsEnabled;
557 QModelIndex PartSet_DocumentDataModel::partIndex(const ResultPartPtr& theObject) const
560 PartSet_PartModel* aModel = 0;
561 foreach (PartSet_PartModel* aPartModel, myPartModels)
564 if (aPartModel->part() == theObject) {
570 return createIndex(aRow, 0, (void*) getModelIndex(aModel->index(0, 0, QModelIndex())));
572 return QModelIndex();
575 QModelIndex PartSet_DocumentDataModel::objectIndex(const ObjectPtr theObject) const
577 // Check that this feature belongs to root document
578 DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
579 DocumentPtr aDoc = theObject->document();
580 if (aDoc == aRootDoc) {
581 // This feature belongs to histrory or top model
582 FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
585 int aNb = aRootDoc->size(ModelAPI_Feature::group());
586 for (aId = 0; aId < aNb; aId++) {
587 if (theObject == aRootDoc->object(ModelAPI_Feature::group(), aId))
591 return index(aId + historyOffset(), 0, QModelIndex());
593 QModelIndex aIndex = myModel->objectIndex(theObject);
596 createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex)) :
600 PartSet_PartModel* aPartModel = 0;
601 foreach(PartSet_PartModel* aModel, myPartModels)
603 if (aModel->hasDocument(aDoc)) {
609 QModelIndex aIndex = aPartModel->objectIndex(theObject);
612 createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex)) :
616 return QModelIndex();
620 void PartSet_DocumentDataModel::clear()
625 myActivePartIndex = QModelIndex();
626 myModel->setItemsColor(ACTIVE_COLOR);
629 int PartSet_DocumentDataModel::lastHistoryRow() const
631 return rowCount() - 1 - myHistoryBackOffset;
634 void PartSet_DocumentDataModel::setLastHistoryItem(const QModelIndex& theIndex)
636 if (theIndex.internalId() == HistoryNode) {
637 myHistoryBackOffset = rowCount() - 1 - theIndex.row();
641 QModelIndex PartSet_DocumentDataModel::lastHistoryItem() const
643 return index(lastHistoryRow(), 1);
647 QIcon PartSet_DocumentDataModel::featureIcon(const FeaturePtr& theFeature)
651 std::string aKind = theFeature->getKind();
652 QString aId(aKind.c_str());
653 if (!myIcons.contains(aId))
656 QString anIconString = myIcons[aId];
658 ModelAPI_ExecState aState = theFeature->data()->execState();
660 case ModelAPI_StateDone:
661 case ModelAPI_StateNothing: {
662 anIcon = QIcon(anIconString);
665 case ModelAPI_StateMustBeUpdated: {
666 anIcon = ModuleBase_Tools::lighter(anIconString);
669 case ModelAPI_StateExecFailed: {
670 anIcon = ModuleBase_Tools::composite(":icons/exec_state_failed.png", anIconString);
673 case ModelAPI_StateInvalidArgument: {
674 anIcon = ModuleBase_Tools::composite(":icons/exec_state_invalid_parameters.png",