1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
3 // File: ModuleBase_IDocumentDataModel.cpp
4 // Created: 28 Apr 2015
5 // Author: Vitaly SMETANNIKOV
7 #include "XGUI_DataModel.h"
9 #include <ModuleBase_IconFactory.h>
11 #include <ModelAPI_Session.h>
12 #include <ModelAPI_Events.h>
13 #include <ModelAPI_ResultParameter.h>
14 #include <ModelAPI_AttributeDouble.h>
15 #include <ModelAPI_ResultPart.h>
16 #include <ModelAPI_Feature.h>
18 #include <Config_FeatureMessage.h>
20 #include <Events_Loop.h>
21 #include <Events_Error.h>
26 #define ACTIVE_COLOR QColor(0,72,140)
27 #define PASSIVE_COLOR Qt::black
29 /// Returns ResultPart object if the given object is a Part feature
30 /// Otherwise returns NULL
31 ResultPartPtr getPartResult(ModelAPI_Object* theObj)
33 ModelAPI_Feature* aFeature = dynamic_cast<ModelAPI_Feature*>(theObj);
35 ResultPtr aRes = aFeature->firstResult();
36 if (aRes.get() && (aRes->groupName() == ModelAPI_ResultPart::group())) {
37 return std::dynamic_pointer_cast<ModelAPI_ResultPart>(aRes);
40 return ResultPartPtr();
43 /// Returns pointer on document if the given object is document object
44 ModelAPI_Document* getSubDocument(void* theObj)
46 ModelAPI_Document* aDoc = dynamic_cast<ModelAPI_Document*>((ModelAPI_Entity*)theObj);
53 // Constructor *************************************************
54 XGUI_DataModel::XGUI_DataModel(QObject* theParent) : QAbstractItemModel(theParent)
56 myXMLReader.readAll();
58 Events_Loop* aLoop = Events_Loop::loop();
59 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
60 aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
63 //******************************************************
64 void XGUI_DataModel::processEvent(const std::shared_ptr<Events_Message>& theMessage)
66 DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
67 std::string aRootType = myXMLReader.rootType();
68 std::string aSubType = myXMLReader.subType();
69 int aNbFolders = foldersCount();
71 // Created object event *******************
72 if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)) {
73 std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
74 std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
75 std::set<ObjectPtr> aObjects = aUpdMsg->objects();
77 std::set<ObjectPtr>::const_iterator aIt;
79 for (aIt = aObjects.begin(); aIt != aObjects.end(); ++aIt) {
80 ObjectPtr aObject = (*aIt);
81 aObjType = aObject->groupName();
82 DocumentPtr aDoc = aObject->document();
83 if (aDoc == aRootDoc) {
84 // Check that new folders could appear
85 QStringList aNotEmptyFolders = listOfShowNotEmptyFolders();
86 foreach (QString aNotEmptyFolder, aNotEmptyFolders) {
87 if ((aNotEmptyFolder.toStdString() == aObjType) && (aRootDoc->size(aObjType) == 1))
88 // Appears first object in folder which can not be shown empty
89 insertRow(myXMLReader.rootFolderId(aObjType));
92 int aRow = aRootDoc->size(aObjType) - 1;
93 if (aObjType == aRootType) {
94 insertRow(aRow + aNbFolders + 1);
96 int aFolderId = myXMLReader.rootFolderId(aObjType);
97 if (aFolderId != -1) {
98 insertRow(aRow, createIndex(aFolderId, 0, -1));
102 // Object created in sub-document
103 QModelIndex aDocRoot = findDocumentRootIndex(aDoc.get());
104 if (aDocRoot.isValid()) {
105 // Check that new folders could appear
106 QStringList aNotEmptyFolders = listOfShowNotEmptyFolders(false);
107 foreach (QString aNotEmptyFolder, aNotEmptyFolders) {
108 if ((aNotEmptyFolder.toStdString() == aObjType) && (aDoc->size(aObjType) == 1))
109 // Appears first object in folder which can not be shown empty
110 insertRow(myXMLReader.subFolderId(aObjType), aDocRoot);
112 int aRow = aDoc->size(aObjType) - 1;
113 int aNbSubFolders = foldersCount(aDoc.get());
114 if (aObjType == aSubType) {
115 // List of objects under document root
116 insertRow(aRow + aNbSubFolders, aDocRoot);
118 // List of objects under a folder
120 int aFolderId = myXMLReader.subFolderId(aObjType);
121 if (aFolderId != -1) {
122 insertRow(aRow, createIndex(aFolderId, 0, aDoc.get()));
129 Events_Error::send("Problem with Data Model definition of sub-document");
134 // Deleted object event ***********************
135 } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
136 std::shared_ptr<ModelAPI_ObjectDeletedMessage> aUpdMsg =
137 std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
138 DocumentPtr aDoc = aUpdMsg->document();
139 std::set<std::string> aGroups = aUpdMsg->groups();
140 std::set<std::string>::const_iterator aIt;
141 for (aIt = aGroups.begin(); aIt != aGroups.end(); ++aIt) {
142 std::string aGroup = (*aIt);
143 if (aDoc == aRootDoc) { // If root objects
144 int aRow = aRootDoc->size(aGroup);
145 if (aGroup == aRootType) {
146 removeRow(aRow + aNbFolders);
148 int aFolderId = myXMLReader.rootFolderId(aGroup);
149 if (aFolderId != -1) {
150 QModelIndex aFolderIndex = createIndex(aFolderId, 0, -1);
151 removeRow(aRow, aFolderIndex);
154 // Check that some folders could erased
155 QStringList aNotEmptyFolders = listOfShowNotEmptyFolders();
156 foreach (QString aNotEmptyFolder, aNotEmptyFolders) {
157 if ((aNotEmptyFolder.toStdString() == aGroup) && (aRootDoc->size(aGroup) == 0))
158 // Appears first object in folder which can not be shown empty
159 removeRow(myXMLReader.rootFolderId(aGroup));
162 // Remove row for sub-document
163 QModelIndex aDocRoot = findDocumentRootIndex(aDoc.get());
164 if (aDocRoot.isValid()) {
165 int aRow = aDoc->size(aGroup);
166 int aNbSubFolders = foldersCount(aDoc.get());
167 if (aGroup == aSubType) {
168 // List of objects under document root
169 removeRow(aRow + aNbSubFolders, aDocRoot);
171 // List of objects under a folder
172 int aFolderId = myXMLReader.subFolderId(aGroup);
173 if (aFolderId != -1) {
174 removeRow(aRow, createIndex(aFolderId, 0, aDoc.get()));
177 // Check that some folders could disappear
178 QStringList aNotEmptyFolders = listOfShowNotEmptyFolders(false);
179 foreach (QString aNotEmptyFolder, aNotEmptyFolders) {
180 if ((aNotEmptyFolder.toStdString() == aGroup) && (aDoc->size(aGroup) == 1))
181 // Appears first object in folder which can not be shown empty
182 removeRow(myXMLReader.subFolderId(aGroup), aDocRoot);
187 Events_Error::send("Problem with Data Model definition of sub-document");
195 //******************************************************
196 void XGUI_DataModel::clear()
201 //******************************************************
202 void XGUI_DataModel::rebuildDataTree()
207 //******************************************************
208 ObjectPtr XGUI_DataModel::object(const QModelIndex& theIndex) const
210 if (theIndex.internalId() < 0) // this is a folder
212 ModelAPI_Object* aObj = (ModelAPI_Object*)theIndex.internalPointer();
213 if (getSubDocument(aObj)) // the selected index is a folder of sub-document
216 // We can not create the ObjectPtr directly because the pointer will be deleted
217 // with deletion of the ObjectPtr because its counter become to 0.
218 DocumentPtr aDoc = aObj->document();
219 std::string aType = aObj->groupName();
222 for (int i = 0; i < aDoc->size(aType); i++) {
223 aObjPtr = aDoc->object(aType, i);
224 if (aObjPtr.get() == aObj)
230 //******************************************************
231 QModelIndex XGUI_DataModel::objectIndex(const ObjectPtr theObject) const
233 std::string aType = theObject->groupName();
234 DocumentPtr aDoc = theObject->document();
235 int aRow = aDoc->index(theObject);
237 return QModelIndex();
239 SessionPtr aSession = ModelAPI_Session::get();
240 DocumentPtr aRootDoc = aSession->moduleDocument();
241 if (aDoc == aRootDoc && myXMLReader.rootType() == aType) {
242 // The object from root document
243 aRow += foldersCount();
244 } else if (myXMLReader.subType() == aType) {
245 // The object from sub document
246 aRow += foldersCount(aDoc.get());
248 return createIndex(aRow, 0, theObject.get());
251 //******************************************************
252 QVariant XGUI_DataModel::data(const QModelIndex& theIndex, int theRole) const
254 SessionPtr aSession = ModelAPI_Session::get();
255 DocumentPtr aRootDoc = aSession->moduleDocument();
256 int aNbFolders = foldersCount();
257 int theIndexRow = theIndex.row();
259 if ((theRole == Qt::DecorationRole) && (theIndex == lastHistoryIndex()))
260 return QIcon(":pictures/arrow.png");
262 if (theIndex.column() == 1)
265 int aParentId = theIndex.internalId();
266 if (aParentId == -1) { // root folders
268 case Qt::DisplayRole:
269 return QString(myXMLReader.rootFolderName(theIndexRow).c_str()) +
270 QString(" (%1)").arg(rowCount(theIndex));
271 case Qt::DecorationRole:
272 return QIcon(myXMLReader.rootFolderIcon(theIndexRow).c_str());
273 case Qt::ForegroundRole:
274 if (aSession->activeDocument() == aRootDoc)
275 return QBrush(ACTIVE_COLOR);
277 return QBrush(PASSIVE_COLOR);
279 } else { // an object or sub-document
280 ModelAPI_Document* aSubDoc = getSubDocument(theIndex.internalPointer());
282 if (theRole == Qt::ForegroundRole) {
283 bool aIsActive = false;
285 aIsActive = (aSession->activeDocument().get() == aSubDoc);
287 ModelAPI_Object* aObj = (ModelAPI_Object*)theIndex.internalPointer();
288 if (aObj->isDisabled())
289 return QBrush(Qt::lightGray);
290 aIsActive = (aSession->activeDocument() == aObj->document());
293 return QBrush(ACTIVE_COLOR);
295 return QBrush(PASSIVE_COLOR);
298 if (aSubDoc) { // this is a folder of sub document
300 case Qt::DisplayRole:
301 return QString(myXMLReader.subFolderName(theIndexRow).c_str()) +
302 QString(" (%1)").arg(rowCount(theIndex));
303 case Qt::DecorationRole:
304 return QIcon(myXMLReader.subFolderIcon(theIndexRow).c_str());
307 ModelAPI_Object* aObj = (ModelAPI_Object*)theIndex.internalPointer();
309 case Qt::DisplayRole:
310 if (aObj->groupName() == ModelAPI_ResultParameter::group()) {
311 ModelAPI_ResultParameter* aParam = dynamic_cast<ModelAPI_ResultParameter*>(aObj);
312 AttributeDoublePtr aValueAttribute = aParam->data()->real(ModelAPI_ResultParameter::VALUE());
313 QString aVal = QString::number(aValueAttribute->value());
314 QString aTitle = QString(aObj->data()->name().c_str());
315 return aTitle + " = " + aVal;
317 return aObj->data()->name().c_str();
318 case Qt::DecorationRole:
319 return ModuleBase_IconFactory::get()->getIcon(object(theIndex));
326 //******************************************************
327 QVariant XGUI_DataModel::headerData(int theSection, Qt::Orientation theOrient, int theRole) const
332 //******************************************************
333 int XGUI_DataModel::rowCount(const QModelIndex& theParent) const
335 SessionPtr aSession = ModelAPI_Session::get();
336 if (!aSession->hasModuleDocument())
338 DocumentPtr aRootDoc = aSession->moduleDocument();
340 if (!theParent.isValid()) {
341 // Return number of items in root
342 int aNbFolders = foldersCount();
344 std::string aType = myXMLReader.rootType();
346 aNbItems = aRootDoc->size(aType);
347 return aNbFolders + aNbItems;
350 int aId = theParent.internalId();
352 // this is a folder under root
353 int aParentPos = theParent.row();
354 std::string aType = myXMLReader.rootFolderType(aParentPos);
355 //qDebug("### %s = %i\n", aType.c_str(), aRootDoc->size(aType));
356 return aRootDoc->size(aType);
358 // It is an object which could have children
359 ModelAPI_Document* aDoc = getSubDocument(theParent.internalPointer());
361 // a folder of sub-document
362 std::string aType = myXMLReader.subFolderType(theParent.row());
363 return aDoc->size(aType);
365 // Check for Part feature
366 ModelAPI_Object* aObj = (ModelAPI_Object*)theParent.internalPointer();
367 ResultPartPtr aPartRes = getPartResult(aObj);
368 if (aPartRes.get()) {
369 DocumentPtr aSubDoc = aPartRes->partDoc();
370 int aNbSubFolders = foldersCount(aSubDoc.get());
372 std::string aSubType = myXMLReader.subType();
373 if (!aSubType.empty())
374 aNbSubItems = aSubDoc->size(aSubType);
375 return aNbSubItems + aNbSubFolders;
382 //******************************************************
383 int XGUI_DataModel::columnCount(const QModelIndex& theParent) const
388 //******************************************************
389 QModelIndex XGUI_DataModel::index(int theRow, int theColumn, const QModelIndex &theParent) const
391 SessionPtr aSession = ModelAPI_Session::get();
392 DocumentPtr aRootDoc = aSession->moduleDocument();
393 int aNbFolders = foldersCount();
395 if (!theParent.isValid()) {
396 if (theRow < aNbFolders) // Return first level folder index
397 return createIndex(theRow, theColumn, -1);
398 else { // return object under root index
399 std::string aType = myXMLReader.rootType();
400 int aObjId = theRow - aNbFolders;
401 if (aObjId < aRootDoc->size(aType)) {
402 ObjectPtr aObj = aRootDoc->object(aType, aObjId);
403 QModelIndex aIndex = objectIndex(aObj);
405 return createIndex(aIndex.row(), theColumn, aIndex.internalPointer());
408 return QModelIndex();
411 int aId = theParent.internalId();
412 int aParentPos = theParent.row();
413 if (aId == -1) { // return object index inside of first level of folders
414 std::string aType = myXMLReader.rootFolderType(aParentPos);
415 if (theRow < aRootDoc->size(aType)) {
416 ObjectPtr aObj = aRootDoc->object(aType, theRow);
417 QModelIndex aIndex = objectIndex(aObj);
419 return createIndex(aIndex.row(), theColumn, aIndex.internalPointer());
423 // It is an object which could have children
424 ModelAPI_Document* aDoc = getSubDocument(theParent.internalPointer());
426 // It is a folder of sub-document
427 std::string aType = myXMLReader.subFolderType(aParentPos);
428 if (theRow < aDoc->size(aType)) {
429 ObjectPtr aObj = aDoc->object(aType, theRow);
430 QModelIndex aIndex = objectIndex(aObj);
432 return createIndex(aIndex.row(), theColumn, aIndex.internalPointer());
436 ModelAPI_Object* aParentObj = (ModelAPI_Object*)theParent.internalPointer();
438 // Check for Part feature
439 ResultPartPtr aPartRes = getPartResult(aParentObj);
440 if (aPartRes.get()) {
441 DocumentPtr aSubDoc = aPartRes->partDoc();
442 int aNbSubFolders = foldersCount(aSubDoc.get());
443 if (theRow < aNbSubFolders) { // Create a Folder of sub-document
444 return createIndex(theRow, theColumn, aSubDoc.get());
446 // this is an object under sub document root
447 std::string aType = myXMLReader.subType();
448 ObjectPtr aObj = aSubDoc->object(aType, theRow - aNbSubFolders);
449 QModelIndex aIndex = objectIndex(aObj);
451 return createIndex(aIndex.row(), theColumn, aIndex.internalPointer());
457 return QModelIndex();
460 //******************************************************
461 QModelIndex XGUI_DataModel::parent(const QModelIndex& theIndex) const
463 int aId = theIndex.internalId();
464 if (aId != -1) { // The object is not a root folder
465 ModelAPI_Document* aDoc = getSubDocument(theIndex.internalPointer());
467 // It is a folder of sub-document
468 return findDocumentRootIndex(aDoc);
470 ModelAPI_Object* aObj = (ModelAPI_Object*) theIndex.internalPointer();
471 std::string aType = aObj->groupName();
472 SessionPtr aSession = ModelAPI_Session::get();
473 DocumentPtr aRootDoc = aSession->moduleDocument();
474 DocumentPtr aSubDoc = aObj->document();
475 if (aSubDoc == aRootDoc) {
476 if (aType == myXMLReader.rootType())
477 return QModelIndex();
479 // return first level of folder index
480 int aFolderId = myXMLReader.rootFolderId(aType);
481 // Items in a one row must have the same parent
482 return createIndex(aFolderId, 0, -1);
485 if (aType == myXMLReader.subType())
486 return findDocumentRootIndex(aSubDoc.get());
488 // return first level of folder index
489 int aFolderId = myXMLReader.subFolderId(aType);
490 // Items in a one row must have the same parent
491 return createIndex(aFolderId, 0, aSubDoc.get());
495 return QModelIndex();
498 //******************************************************
499 bool XGUI_DataModel::hasChildren(const QModelIndex& theParent) const
501 if (!theParent.isValid()) {
502 int aNbFolders = foldersCount();
505 SessionPtr aSession = ModelAPI_Session::get();
506 DocumentPtr aRootDoc = aSession->moduleDocument();
507 return aRootDoc->size(myXMLReader.rootType()) > 0;
509 if (theParent.internalId() == -1) {
510 std::string aType = myXMLReader.rootFolderType(theParent.row());
511 if (!aType.empty()) {
512 SessionPtr aSession = ModelAPI_Session::get();
513 DocumentPtr aRootDoc = aSession->moduleDocument();
514 return aRootDoc->size(aType) > 0;
517 ModelAPI_Document* aDoc = getSubDocument(theParent.internalPointer());
519 // a folder of sub-document
520 std::string aType = myXMLReader.subFolderType(theParent.row());
521 return aDoc->size(aType) > 0;
523 // Check that it could be an object with children
524 ModelAPI_Object* aObj = (ModelAPI_Object*)theParent.internalPointer();
526 // Check for Part feature
527 ResultPartPtr aPartRes = getPartResult(aObj);
535 //******************************************************
536 bool XGUI_DataModel::insertRows(int theRow, int theCount, const QModelIndex& theParent)
538 beginInsertRows(theParent, theRow, theRow + theCount - 1);
544 //******************************************************
545 bool XGUI_DataModel::removeRows(int theRow, int theCount, const QModelIndex& theParent)
547 beginRemoveRows(theParent, theRow, theRow + theCount - 1);
552 //******************************************************
553 Qt::ItemFlags XGUI_DataModel::flags(const QModelIndex& theIndex) const
555 Qt::ItemFlags aFlags = Qt::ItemIsSelectable;
557 ModelAPI_Object* aObj = 0;
558 if (theIndex.internalId() != -1) {
559 if (!getSubDocument(theIndex.internalPointer()))
560 aObj = (ModelAPI_Object*) theIndex.internalPointer();
563 aFlags |= Qt::ItemIsEditable;
565 if (!aObj->isDisabled())
566 aFlags |= Qt::ItemIsEnabled;
568 aFlags |= Qt::ItemIsEnabled;
572 //******************************************************
573 QModelIndex XGUI_DataModel::findDocumentRootIndex(const ModelAPI_Document* theDoc) const
575 SessionPtr aSession = ModelAPI_Session::get();
576 DocumentPtr aRootDoc = aSession->moduleDocument();
577 if (myXMLReader.isAttachToResult()) { // If document is attached to result
578 int aNb = aRootDoc->size(ModelAPI_ResultPart::group());
580 ResultPartPtr aPartRes;
581 for (int i = 0; i < aNb; i++) {
582 aObj = aRootDoc->object(ModelAPI_ResultPart::group(), i);
583 aPartRes = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aObj);
584 if (aPartRes.get() && (aPartRes->partDoc().get() == theDoc)) {
586 if (myXMLReader.rootType() == ModelAPI_Feature::group())
587 aRow += foldersCount();
588 return createIndex(aRow, 0, aObj.get());
591 } else { // If document is attached to feature
592 int aNb = aRootDoc->size(ModelAPI_Feature::group());
594 ResultPartPtr aPartRes;
595 for (int i = 0; i < aNb; i++) {
596 aObj = aRootDoc->object(ModelAPI_Feature::group(), i);
597 aPartRes = getPartResult(aObj.get());
598 if (aPartRes.get() && (aPartRes->partDoc().get() == theDoc)) {
600 if (myXMLReader.rootType() == ModelAPI_Feature::group())
601 aRow += foldersCount();
602 return createIndex(aRow, 0, aObj.get());
606 return QModelIndex();
609 //******************************************************
610 QModelIndex XGUI_DataModel::documentRootIndex(DocumentPtr theDoc) const
612 SessionPtr aSession = ModelAPI_Session::get();
613 DocumentPtr aRootDoc = aSession->moduleDocument();
614 if (theDoc == aRootDoc)
615 return QModelIndex();
617 return findDocumentRootIndex(theDoc.get());
620 //******************************************************
621 int XGUI_DataModel::foldersCount(ModelAPI_Document* theDoc) const
624 SessionPtr aSession = ModelAPI_Session::get();
625 DocumentPtr aRootDoc = aSession->moduleDocument();
626 if ((theDoc == 0) || (theDoc == aRootDoc.get())) {
627 for (int i = 0; i < myXMLReader.rootFoldersNumber(); i++) {
628 if (myXMLReader.rootShowEmpty(i))
631 if (aRootDoc->size(myXMLReader.rootFolderType(i)) > 0)
636 for (int i = 0; i < myXMLReader.subFoldersNumber(); i++) {
637 if (myXMLReader.subShowEmpty(i))
640 if (theDoc->size(myXMLReader.subFolderType(i)) > 0)
648 //******************************************************
649 QStringList XGUI_DataModel::listOfShowNotEmptyFolders(bool fromRoot) const
653 for (int i = 0; i < myXMLReader.rootFoldersNumber(); i++) {
654 if (!myXMLReader.rootShowEmpty(i))
655 aResult << myXMLReader.rootFolderType(i).c_str();
658 for (int i = 0; i < myXMLReader.subFoldersNumber(); i++) {
659 if (!myXMLReader.subShowEmpty(i))
660 aResult << myXMLReader.subFolderType(i).c_str();
666 //******************************************************
667 QModelIndex XGUI_DataModel::lastHistoryIndex() const
669 SessionPtr aSession = ModelAPI_Session::get();
670 DocumentPtr aCurDoc = aSession->activeDocument();
671 FeaturePtr aFeature = aCurDoc->currentFeature(true);
672 if (aFeature.get()) {
673 QModelIndex aInd = objectIndex(aFeature);
674 return createIndex(aInd.row(), 1, aInd.internalPointer());
676 if (aCurDoc == aSession->moduleDocument())
677 return createIndex(foldersCount() - 1, 1, -1);
679 return createIndex(foldersCount(aCurDoc.get()) - 1, 1, aCurDoc.get());