Salome HOME
Issue #2016: Rebuild data tree on update Body to CompSolid
[modules/shaper.git] / src / XGUI / XGUI_DataModel.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 // File:        ModuleBase_IDocumentDataModel.cpp
4 // Created:     28 Apr 2015
5 // Author:      Vitaly SMETANNIKOV
6
7 #include "XGUI_DataModel.h"
8
9 #include <ModuleBase_IconFactory.h>
10
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>
17 #include <ModelAPI_CompositeFeature.h>
18 #include <ModelAPI_ResultCompSolid.h>
19 #include <ModelAPI_ResultField.h>
20 #include <ModelAPI_Tools.h>
21
22 #include <Config_FeatureMessage.h>
23 #include <Config_DataModelReader.h>
24
25 #include <Events_Loop.h>
26
27 #include <QIcon>
28 #include <QBrush>
29
30 #define ACTIVE_COLOR QColor(Qt::black)
31 //#define ACTIVE_COLOR QColor(0,72,140)
32 //#define PASSIVE_COLOR Qt::black
33
34 /// Returns ResultPart object if the given object is a Part feature
35 /// Otherwise returns NULL
36
37 #define SELECTABLE_COLOR QColor(80, 80, 80)
38 #define DISABLED_COLOR QColor(200, 200, 200)
39
40
41 ResultPartPtr getPartResult(ModelAPI_Object* theObj)
42 {
43   ModelAPI_Feature* aFeature = dynamic_cast<ModelAPI_Feature*>(theObj);
44   if (aFeature) {
45     ResultPtr aRes = aFeature->firstResult();
46     if (aRes.get() && (aRes->groupName() == ModelAPI_ResultPart::group())) {
47       ResultPartPtr aPartRes = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aRes);
48       // Use only original parts, not a placement results
49       if (aPartRes == aPartRes->original())
50       return aPartRes;
51     }
52   }
53   return ResultPartPtr();
54 }
55
56 /// Returns pointer on document if the given object is document object
57 ModelAPI_Document* getSubDocument(void* theObj)
58 {
59   ModelAPI_Document* aDoc = dynamic_cast<ModelAPI_Document*>((ModelAPI_Entity*)theObj);
60   return aDoc;
61 }
62
63
64
65
66 // Constructor *************************************************
67 XGUI_DataModel::XGUI_DataModel(QObject* theParent) : QAbstractItemModel(theParent)//,
68   //myIsEventsProcessingBlocked(false)
69 {
70   Events_Loop* aLoop = Events_Loop::loop();
71   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
72   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
73   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
74   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_ORDER_UPDATED));
75   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_DOCUMENT_CHANGED));
76 }
77
78 XGUI_DataModel::~XGUI_DataModel()
79 {
80   clear();
81 }
82
83 //******************************************************
84 void XGUI_DataModel::processEvent(const std::shared_ptr<Events_Message>& theMessage)
85 {
86   //if (myIsEventsProcessingBlocked)
87   //  return;
88   DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
89   std::string aRootType = myXMLReader->rootType();
90   std::string aSubType = myXMLReader->subType();
91   int aNbFolders = foldersCount();
92
93   // Created object event *******************
94   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)) {
95     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
96         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
97     std::set<ObjectPtr> aObjects = aUpdMsg->objects();
98
99     std::set<ObjectPtr>::const_iterator aIt;
100     std::string aObjType;
101     for (aIt = aObjects.begin(); aIt != aObjects.end(); ++aIt) {
102       ObjectPtr aObject = (*aIt);
103       // We do not show objects which does not need to be shown in object browser
104       if (!aObject->isInHistory())
105         continue;
106
107       aObjType = aObject->groupName();
108       DocumentPtr aDoc = aObject->document();
109       if (aDoc == aRootDoc) {
110         // Check that new folders could appear
111         QStringList aNotEmptyFolders = listOfShowNotEmptyFolders();
112         foreach (QString aNotEmptyFolder, aNotEmptyFolders) {
113           if ((aNotEmptyFolder.toStdString() == aObjType) && (aRootDoc->size(aObjType) == 1))
114             // Appears first object in folder which can not be shown empty
115             insertRow(myXMLReader->rootFolderId(aObjType));
116         }
117         // Insert new object
118         int aRow = aRootDoc->size(aObjType) - 1;
119         if (aRow != -1) {
120           if (aObjType == aRootType) {
121             insertRow(aRow + aNbFolders + 1);
122           } else {
123             int aFolderId = myXMLReader->rootFolderId(aObjType);
124             if (aFolderId != -1) {
125               insertRow(aRow, createIndex(aFolderId, 0, (void*)Q_NULLPTR));
126             }
127           }
128         }
129       } else {
130         // Object created in sub-document
131         QModelIndex aDocRoot = findDocumentRootIndex(aDoc.get());
132         if (aDocRoot.isValid()) {
133           // Check that new folders could appear
134           QStringList aNotEmptyFolders = listOfShowNotEmptyFolders(false);
135           foreach (QString aNotEmptyFolder, aNotEmptyFolders) {
136             if ((aNotEmptyFolder.toStdString() == aObjType) && (aDoc->size(aObjType) == 1))
137               // Appears first object in folder which can not be shown empty
138               insertRow(myXMLReader->subFolderId(aObjType), aDocRoot);
139           }
140           int aRow = aDoc->index(aObject);
141           if (aRow != -1) {
142             int aNbSubFolders = foldersCount(aDoc.get());
143             if (aObjType == aSubType) {
144               // List of objects under document root
145               insertRow(aRow + aNbSubFolders, aDocRoot);
146             } else {
147               // List of objects under a folder
148               if (aRow != -1) {
149                 int aFolderId = folderId(aObjType, aDoc.get());
150                 if (aFolderId != -1) {
151                   QModelIndex aParentFolder = createIndex(aFolderId, 0, aDoc.get());
152                   insertRow(aRow, aParentFolder);
153                   emit dataChanged(aParentFolder, aParentFolder);
154                 }
155               }
156             }
157           } else {
158             rebuildDataTree();
159             break;
160           }
161         } else {
162           rebuildDataTree();
163           break;
164         }
165       }
166     }
167     // Deleted object event ***********************
168   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
169     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aUpdMsg =
170         std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
171     DocumentPtr aDoc = aUpdMsg->document();
172     std::set<std::string> aMsgGroups = aUpdMsg->groups();
173
174     /// Sort groups because RootType deletion has to be done after others
175     std::string aType = (aDoc == aRootDoc)? aRootType : aSubType;
176     std::list<std::string> aGroups;
177     std::set<std::string>::const_iterator aSetIt;
178     for (aSetIt = aMsgGroups.begin(); aSetIt != aMsgGroups.end(); ++aSetIt) {
179       std::string aGroup = (*aSetIt);
180       if (aGroup == aType)
181         aGroups.push_back(aGroup);
182       else
183         aGroups.push_front(aGroup);
184     }
185
186     std::list<std::string>::const_iterator aIt;
187     for (aIt = aGroups.begin(); aIt != aGroups.end(); ++aIt) {
188       std::string aGroup = (*aIt);
189       if (aDoc == aRootDoc) {  // If root objects
190         int aRow = aRootDoc->size(aGroup);
191         if (aGroup == aRootType) {
192           // Process root folder
193           removeRow(aRow + aNbFolders);
194           rebuildBranch(aNbFolders, aRow);
195         } else {
196           // Process root sub-folder
197           int aFolderId = myXMLReader->rootFolderId(aGroup);
198           if (aFolderId != -1) {
199             QModelIndex aFolderIndex = createIndex(aFolderId, 0, (void*)Q_NULLPTR);
200             removeRow(aRow, aFolderIndex);
201             //rebuildBranch(0, aRow);
202           }
203         }
204         // Check that some folders could erased
205         QStringList aNotEmptyFolders = listOfShowNotEmptyFolders();
206         foreach (QString aNotEmptyFolder, aNotEmptyFolders) {
207           if ((aNotEmptyFolder.toStdString() == aGroup) && (aRootDoc->size(aGroup) == 0)) {
208             // Appears first object in folder which can not be shown empty
209             removeRow(myXMLReader->rootFolderId(aGroup));
210             //rebuildBranch(0, aNbFolders + aDoc->size(myXMLReader->rootType()));
211             break;
212           }
213         }
214       } else {
215         // Remove row for sub-document
216         QModelIndex aDocRoot = findDocumentRootIndex(aDoc.get());
217         if (aDocRoot.isValid()) {
218           int aRow = aDoc->size(aGroup);
219           int aNbSubFolders = foldersCount(aDoc.get());
220           if (aGroup == aSubType) {
221             // List of objects under document root
222             removeRow(aRow + aNbSubFolders, aDocRoot);
223             rebuildBranch(aNbSubFolders, aRow, aDocRoot);
224           } else {
225             // List of objects under a folder
226             int aFolderId = folderId(aGroup, aDoc.get());
227             if (aFolderId != -1) {
228               QModelIndex aFolderRoot = createIndex(aFolderId, 0, aDoc.get());
229               removeRow(aRow, aFolderRoot);
230               //rebuildBranch(0, aRow, aFolderRoot);
231             }
232           }
233           // Check that some folders could disappear
234           QStringList aNotEmptyFolders = listOfShowNotEmptyFolders(false);
235           int aSize = aDoc->size(aGroup);
236           foreach (QString aNotEmptyFolder, aNotEmptyFolders) {
237             if ((aNotEmptyFolder.toStdString() == aGroup) && (aSize == 0)) {
238               // Appears first object in folder which can not be shown empty
239               removeRow(myXMLReader->subFolderId(aGroup), aDocRoot);
240               //rebuildBranch(0, aNbSubFolders + aDoc->size(myXMLReader->subType()), aDocRoot);
241               break;
242             }
243           }
244         } else {
245           rebuildDataTree();
246           break;
247         }
248       }
249     }
250   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)) {
251     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
252         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
253     std::set<ObjectPtr> aObjects = aUpdMsg->objects();
254
255     std::set<ObjectPtr>::const_iterator aIt;
256     std::string aObjType;
257     for (aIt = aObjects.begin(); aIt != aObjects.end(); ++aIt) {
258       ObjectPtr aObject = (*aIt);
259       if (aObject->data()->isValid()) {
260         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObject);
261         if (aFeature.get() && aFeature->firstResult().get()
262           && (aFeature->firstResult()->groupName() == ModelAPI_ResultField::group())) {
263             ResultFieldPtr aResult =
264               std::dynamic_pointer_cast<ModelAPI_ResultField>(aFeature->firstResult());
265             QModelIndex aIndex = objectIndex(aResult);
266             removeRows(0, aResult->stepsSize(), aIndex);
267         } else {
268           QModelIndex aIndex = objectIndex(aObject);
269           if (aIndex.isValid()) {
270             emit dataChanged(aIndex, aIndex);
271           }
272         }
273       } else {
274         rebuildDataTree();
275         break;
276       }
277     }
278   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_ORDER_UPDATED)) {
279     std::shared_ptr<ModelAPI_OrderUpdatedMessage> aUpdMsg =
280         std::dynamic_pointer_cast<ModelAPI_OrderUpdatedMessage>(theMessage);
281     if (aUpdMsg->reordered().get()) {
282       DocumentPtr aDoc = aUpdMsg->reordered()->document();
283       std::string aGroup = aUpdMsg->reordered()->group();
284
285       QModelIndex aParent;
286       int aStartId = 0;
287       if (aDoc == aRootDoc) {
288         // Update a group under root
289         if (aGroup == myXMLReader->rootType()) // Update objects under root
290           aStartId = foldersCount();
291         else // Update objects in folder under root
292           aParent = createIndex(folderId(aGroup), 0, (void*)Q_NULLPTR);
293       } else {
294         // Update a sub-document
295         if (aGroup == myXMLReader->subType()) {
296           // Update sub-document root
297           aParent = findDocumentRootIndex(aDoc.get());
298           aStartId = foldersCount(aDoc.get());
299         } else
300           // update folder in sub-document
301           aParent = createIndex(folderId(aGroup, aDoc.get()), 0, aDoc.get());
302       }
303       int aChildNb = rowCount(aParent);
304       rebuildBranch(aStartId, aChildNb - aStartId, aParent);
305     } else {
306       rebuildDataTree();
307     }
308   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_DOCUMENT_CHANGED)) {
309     DocumentPtr aDoc = ModelAPI_Session::get()->activeDocument();
310     if (aDoc != aRootDoc) {
311       QModelIndex aDocRoot = findDocumentRootIndex(aDoc.get());
312       if (aDocRoot.isValid())
313         emit dataChanged(aDocRoot, aDocRoot);
314       else
315         // We have got a new document
316         rebuildDataTree();
317     }
318   }
319 }
320
321 //******************************************************
322 void XGUI_DataModel::clear()
323 {
324   beginResetModel();
325   endResetModel();
326 }
327
328 //******************************************************
329 void XGUI_DataModel::rebuildDataTree()
330 {
331   beginResetModel();
332   endResetModel();
333   emit treeRebuilt();
334 }
335
336 //******************************************************
337 ObjectPtr XGUI_DataModel::object(const QModelIndex& theIndex) const
338 {
339   if (theIndex.internalId() == 0) // this is a folder
340     return ObjectPtr();
341   ModelAPI_Object* aObj =
342     dynamic_cast<ModelAPI_Object*>((ModelAPI_Entity*)theIndex.internalPointer());
343   if (!aObj)
344     return ObjectPtr();
345   if (getSubDocument(aObj)) // the selected index is a folder of sub-document
346     return ObjectPtr();
347
348   return aObj->data()->owner();
349 }
350
351 //******************************************************
352 QModelIndex XGUI_DataModel::objectIndex(const ObjectPtr theObject) const
353 {
354   std::string aType = theObject->groupName();
355   DocumentPtr aDoc = theObject->document();
356   int aRow = aDoc->index(theObject);
357   if (aRow == -1) {
358     // it could be a part of complex object
359     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
360     if (aFeature.get()) {
361       CompositeFeaturePtr aCompFea = ModelAPI_Tools::compositeOwner(aFeature);
362       if (aCompFea.get()) {
363         for (int i = 0; i < aCompFea->numberOfSubs(true); i++) {
364           if (aCompFea->subFeature(i, true) == theObject) {
365             aRow = i;
366             break;
367           }
368         }
369       }
370     } else {
371       ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
372       if (aResult.get()) {
373         ResultCompSolidPtr aCompRes = ModelAPI_Tools::compSolidOwner(aResult);
374         if (aCompRes.get()) {
375           for (int i = 0; i < aCompRes->numberOfSubs(true); i++) {
376             if (aCompRes->subResult(i, true) == theObject) {
377               aRow = i;
378               break;
379             }
380           }
381         }
382       }
383     }
384     if (aRow == -1)
385       return QModelIndex();
386     else
387       return createIndex(aRow, 0, theObject.get());
388   }
389   SessionPtr aSession = ModelAPI_Session::get();
390   DocumentPtr aRootDoc = aSession->moduleDocument();
391   if (aDoc == aRootDoc && myXMLReader->rootType() == aType) {
392     // The object from root document
393     aRow += foldersCount();
394   } else if (myXMLReader->subType() == aType) {
395     // The object from sub document
396     aRow += foldersCount(aDoc.get());
397   }
398   return createIndex(aRow, 0, theObject.get());
399 }
400
401 //******************************************************
402 QVariant XGUI_DataModel::data(const QModelIndex& theIndex, int theRole) const
403 {
404   SessionPtr aSession = ModelAPI_Session::get();
405   DocumentPtr aRootDoc = aSession->moduleDocument();
406   int aNbFolders = foldersCount();
407   int theIndexRow = theIndex.row();
408
409   if ((theRole == Qt::DecorationRole) && (theIndex == lastHistoryIndex()))
410     return QIcon(":pictures/arrow.png");
411
412   if (theIndex.column() == 1)
413     return QVariant();
414
415   quintptr aParentId = theIndex.internalId();
416   if (aParentId == 0) { // root folders
417     switch (theRole) {
418       case Qt::DisplayRole:
419         return QString(myXMLReader->rootFolderName(theIndexRow).c_str()) +
420           QString(" (%1)").arg(rowCount(theIndex));
421       case Qt::DecorationRole:
422         return QIcon(myXMLReader->rootFolderIcon(theIndexRow).c_str());
423       case Qt::ForegroundRole:
424         {
425           Qt::ItemFlags aFlags = theIndex.flags();
426           if (aFlags == Qt::ItemFlags())
427             return QBrush(DISABLED_COLOR);
428           if (!aFlags.testFlag(Qt::ItemIsEditable))
429             return QBrush(SELECTABLE_COLOR);
430         }
431         return ACTIVE_COLOR;
432     }
433   } else { // an object or sub-document
434     if (theRole == Qt::ForegroundRole) {
435       Qt::ItemFlags aFlags = theIndex.flags();
436       if (aFlags == Qt::ItemFlags())
437         return QBrush(DISABLED_COLOR);
438       if (!aFlags.testFlag(Qt::ItemIsEditable))
439         return QBrush(SELECTABLE_COLOR);
440       return ACTIVE_COLOR;
441     }
442
443     ModelAPI_Document* aSubDoc = getSubDocument(theIndex.internalPointer());
444     if (aSubDoc) { // this is a folder of sub document
445       QIntList aMissedIdx = missedFolderIndexes(aSubDoc);
446       int aRow = theIndexRow;
447       while (aMissedIdx.contains(aRow))
448         aRow++;
449       if (aRow < myXMLReader->subFoldersNumber()) {
450         switch (theRole) {
451           case Qt::DisplayRole:
452             return QString(myXMLReader->subFolderName(aRow).c_str()) +
453               QString(" (%1)").arg(rowCount(theIndex));
454           case Qt::DecorationRole:
455             return QIcon(myXMLReader->subFolderIcon(aRow).c_str());
456         }
457       }
458     } else {
459       ModelAPI_Object* aObj =
460         dynamic_cast<ModelAPI_Object*>((ModelAPI_Entity*)theIndex.internalPointer());
461       if (aObj) {
462         switch (theRole) {
463         case Qt::DisplayRole:
464           {
465             if (aObj->groupName() == ModelAPI_ResultParameter::group()) {
466               ModelAPI_ResultParameter* aParam = dynamic_cast<ModelAPI_ResultParameter*>(aObj);
467               AttributeDoublePtr aValueAttribute =
468                 aParam->data()->real(ModelAPI_ResultParameter::VALUE());
469               QString aVal = QString::number(aValueAttribute->value());
470               QString aTitle = QString(aObj->data()->name().c_str());
471               return aTitle + " = " + aVal;
472             }
473             QString aSuffix;
474             if (aObj->groupName() == myXMLReader->subType()) {
475               ResultPartPtr aPartRes = getPartResult(aObj);
476               if (aPartRes.get()) {
477                 if (aPartRes->partDoc().get() == NULL)
478                   aSuffix = " (Not loaded)";
479               }
480             }
481             return aObj->data()->name().c_str() + aSuffix;
482           }
483         case Qt::DecorationRole:
484           return ModuleBase_IconFactory::get()->getIcon(object(theIndex));
485         }
486       } else {
487         switch (theRole) {
488         case Qt::DisplayRole:
489           {
490             ModelAPI_ResultField::ModelAPI_FieldStep* aStep =
491               dynamic_cast<ModelAPI_ResultField::ModelAPI_FieldStep*>
492               ((ModelAPI_Entity*)theIndex.internalPointer());
493             if (aStep) {
494               return "Step " + QString::number(aStep->id() + 1) + " " +
495                 aStep->field()->textLine(aStep->id()).c_str();
496             }
497           }
498           break;
499         }
500       }
501     }
502   }
503   return QVariant();
504 }
505
506 //******************************************************
507 QVariant XGUI_DataModel::headerData(int theSection, Qt::Orientation theOrient, int theRole) const
508 {
509   return QVariant();
510 }
511
512 //******************************************************
513 int XGUI_DataModel::rowCount(const QModelIndex& theParent) const
514 {
515   SessionPtr aSession = ModelAPI_Session::get();
516   if (!aSession->hasModuleDocument())
517     return 0;
518   DocumentPtr aRootDoc = aSession->moduleDocument();
519
520   if (!theParent.isValid()) {
521     // Return number of items in root
522     int aNbFolders = foldersCount();
523     int aNbItems = 0;
524     std::string aType = myXMLReader->rootType();
525     if (!aType.empty())
526       aNbItems = aRootDoc->size(aType);
527     return aNbFolders + aNbItems;
528   }
529
530   quintptr aId = theParent.internalId();
531   if (aId == 0) {
532     // this is a folder under root
533     int aParentPos = theParent.row();
534     std::string aType = myXMLReader->rootFolderType(aParentPos);
535     return aRootDoc->size(aType);
536   } else {
537     // It is an object which could have children
538     ModelAPI_Document* aDoc = getSubDocument(theParent.internalPointer());
539     if (aDoc) {
540       // a folder of sub-document
541       QIntList aMissedIdx = missedFolderIndexes(aDoc);
542       int aRow = theParent.row();
543       while (aMissedIdx.contains(aRow))
544         aRow++;
545       if (aRow < myXMLReader->subFoldersNumber()) {
546         std::string aType = myXMLReader->subFolderType(aRow);
547         return aDoc->size(aType);
548       }
549     } else {
550       ModelAPI_Object* aObj =
551         dynamic_cast<ModelAPI_Object*>((ModelAPI_Entity*)theParent.internalPointer());
552       // Check for Part feature
553       ResultPartPtr aPartRes = getPartResult(aObj);
554       if (aPartRes.get()) {
555         DocumentPtr aSubDoc = aPartRes->partDoc();
556         if (!aSubDoc.get())
557           return 0;
558
559         int aNbSubFolders = foldersCount(aSubDoc.get());
560         int aNbSubItems = 0;
561         std::string aSubType = myXMLReader->subType();
562         if (!aSubType.empty())
563           aNbSubItems = aSubDoc->size(aSubType);
564         return aNbSubItems + aNbSubFolders;
565       } else {
566         // Check for composite object
567         ModelAPI_CompositeFeature* aCompFeature = dynamic_cast<ModelAPI_CompositeFeature*>(aObj);
568         if (aCompFeature)
569           return aCompFeature->numberOfSubs(true);
570         ModelAPI_ResultCompSolid* aCompRes = dynamic_cast<ModelAPI_ResultCompSolid*>(aObj);
571         if (aCompRes)
572           return aCompRes->numberOfSubs(true);
573         ModelAPI_ResultField* aFieldRes = dynamic_cast<ModelAPI_ResultField*>(aObj);
574         if (aFieldRes)
575           return aFieldRes->stepsSize();
576       }
577     }
578   }
579   return 0;
580 }
581
582 //******************************************************
583 int XGUI_DataModel::columnCount(const QModelIndex& theParent) const
584 {
585   return 2;
586 }
587
588 //******************************************************
589 QModelIndex XGUI_DataModel::index(int theRow, int theColumn, const QModelIndex &theParent) const
590 {
591   SessionPtr aSession = ModelAPI_Session::get();
592   DocumentPtr aRootDoc = aSession->moduleDocument();
593   int aNbFolders = foldersCount();
594
595   QModelIndex aIndex;
596
597   if (!theParent.isValid()) {
598     if (theRow < aNbFolders) // Return first level folder index
599       return createIndex(theRow, theColumn, (void*)Q_NULLPTR);
600     else { // return object under root index
601       std::string aType = myXMLReader->rootType();
602       int aObjId = theRow - aNbFolders;
603       if (aObjId < aRootDoc->size(aType)) {
604         ObjectPtr aObj = aRootDoc->object(aType, aObjId);
605         aIndex = objectIndex(aObj);
606       }
607     }
608   } else {
609     quintptr aId = theParent.internalId();
610     int aParentPos = theParent.row();
611     if (aId == 0) { // return object index inside of first level of folders
612       std::string aType = myXMLReader->rootFolderType(aParentPos);
613       if (theRow < aRootDoc->size(aType)) {
614         ObjectPtr aObj = aRootDoc->object(aType, theRow);
615         aIndex = objectIndex(aObj);
616       }
617     } else {
618       // It is an object which could have children
619       ModelAPI_Document* aDoc = getSubDocument(theParent.internalPointer());
620       if (aDoc) {
621         // It is a folder of sub-document
622         int aParentRow = aParentPos;
623         QIntList aMissedIdx = missedFolderIndexes(aDoc);
624         while (aMissedIdx.contains(aParentRow))
625           aParentRow++;
626         if (aParentRow < myXMLReader->subFoldersNumber()) {
627           std::string aType = myXMLReader->subFolderType(aParentRow);
628           if (theRow < aDoc->size(aType)) {
629             ObjectPtr aObj = aDoc->object(aType, theRow);
630             aIndex = objectIndex(aObj);
631           }
632         }
633       } else {
634         ModelAPI_Object* aParentObj =
635           dynamic_cast<ModelAPI_Object*>((ModelAPI_Entity*)theParent.internalPointer());
636
637         // Check for Part feature
638         ResultPartPtr aPartRes = getPartResult(aParentObj);
639         if (aPartRes.get()) {
640           DocumentPtr aSubDoc = aPartRes->partDoc();
641           int aNbSubFolders = foldersCount(aSubDoc.get());
642           if (theRow < aNbSubFolders) { // Create a Folder of sub-document
643             aIndex = createIndex(theRow, theColumn, aSubDoc.get());
644           } else {
645             // this is an object under sub document root
646             std::string aType = myXMLReader->subType();
647             ObjectPtr aObj = aSubDoc->object(aType, theRow - aNbSubFolders);
648             aIndex = objectIndex(aObj);
649           }
650         } else {
651           // Check for composite object
652           ModelAPI_CompositeFeature* aCompFeature =
653             dynamic_cast<ModelAPI_CompositeFeature*>(aParentObj);
654           if (aCompFeature) {
655             aIndex = objectIndex(aCompFeature->subFeature(theRow));
656           } else {
657             ModelAPI_ResultCompSolid* aCompRes =
658               dynamic_cast<ModelAPI_ResultCompSolid*>(aParentObj);
659             if (aCompRes)
660               aIndex = objectIndex(aCompRes->subResult(theRow));
661             else {
662               ModelAPI_ResultField* aFieldRes =
663                 dynamic_cast<ModelAPI_ResultField*>(aParentObj);
664               if (aFieldRes) {
665                 aIndex = createIndex(theRow, 0, aFieldRes->step(theRow));
666               }
667             }
668           }
669         }
670       }
671     }
672   }
673   if (theColumn != 0)
674     return createIndex(aIndex.row(), theColumn, aIndex.internalPointer());
675   return aIndex;
676 }
677
678 //******************************************************
679 static QModelIndex MYLastDeleted;
680 QModelIndex XGUI_DataModel::parent(const QModelIndex& theIndex) const
681 {
682   if (!theIndex.isValid())
683     return QModelIndex();
684   // To avoid additional request about index which was already deleted
685   if (theIndex == MYLastDeleted)
686     return QModelIndex();
687
688   SessionPtr aSession = ModelAPI_Session::get();
689   quintptr aId = theIndex.internalId();
690   if (aId != 0) { // The object is not a root folder
691     ModelAPI_Document* aDoc = getSubDocument(theIndex.internalPointer());
692     if (aDoc) {
693       // It is a folder of sub-document
694       return findDocumentRootIndex(aDoc);
695     }
696     ObjectPtr aObj = object(theIndex);
697     if (!aObj.get()) {
698       // It can b e a step of a field
699       ModelAPI_ResultField::ModelAPI_FieldStep* aStep =
700         dynamic_cast<ModelAPI_ResultField::ModelAPI_FieldStep*>
701         ((ModelAPI_Entity*)theIndex.internalPointer());
702       if (aStep) {
703         ModelAPI_ResultField* aField = aStep->field();
704         DocumentPtr aDoc = aSession->activeDocument();
705         ObjectPtr aFld;
706         for(int i = 0; i < aDoc->size(ModelAPI_ResultField::group()); i++) {
707           aFld = aDoc->object(ModelAPI_ResultField::group(), i);
708           if (aFld.get() == aField)
709             return objectIndex(aFld);
710         }
711       }
712       // To avoid additional request about index which was already deleted
713       // If deleted it causes a crash on delete object from Part
714       MYLastDeleted = theIndex;
715       return QModelIndex();
716     }
717     // Check is it object a sub-object of a complex object
718     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
719     if (aFeature.get()) {
720       CompositeFeaturePtr aCompFea = ModelAPI_Tools::compositeOwner(aFeature);
721       if (aCompFea.get()) {
722         return objectIndex(aCompFea);
723       }
724     }
725     ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aObj);
726     if (aResult.get()) {
727       ResultCompSolidPtr aCompRes = ModelAPI_Tools::compSolidOwner(aResult);
728       if (aCompRes.get()) {
729         return objectIndex(aCompRes);
730       }
731     }
732     // Use as ordinary object
733     std::string aType = aObj->groupName();
734     DocumentPtr aRootDoc = aSession->moduleDocument();
735     DocumentPtr aSubDoc = aObj->document();
736     if (aSubDoc == aRootDoc) {
737       if (aType == myXMLReader->rootType())
738         return QModelIndex();
739       else {
740         // return first level of folder index
741         int aFolderId = myXMLReader->rootFolderId(aType);
742         // Items in a one row must have the same parent
743         return createIndex(aFolderId, 0, (void*)Q_NULLPTR);
744       }
745     } else {
746       if (aType == myXMLReader->subType())
747         return findDocumentRootIndex(aSubDoc.get());
748       else {
749         // return first level of folder index
750         int aFolderId = folderId(aType, aSubDoc.get());
751         // Items in a one row must have the same parent
752         return createIndex(aFolderId, 0, aSubDoc.get());
753       }
754     }
755   }
756   return QModelIndex();
757 }
758
759 //******************************************************
760 bool XGUI_DataModel::hasChildren(const QModelIndex& theParent) const
761 {
762   return rowCount(theParent) > 0;
763 }
764
765 //******************************************************
766 bool XGUI_DataModel::insertRows(int theRow, int theCount, const QModelIndex& theParent)
767 {
768   beginInsertRows(theParent, theRow, theRow + theCount - 1);
769   endInsertRows();
770
771   return true;
772 }
773
774 //******************************************************
775 bool XGUI_DataModel::removeRows(int theRow, int theCount, const QModelIndex& theParent)
776 {
777   beginRemoveRows(theParent, theRow, theRow + theCount - 1);
778   endRemoveRows();
779   return true;
780 }
781
782 //******************************************************
783 Qt::ItemFlags XGUI_DataModel::flags(const QModelIndex& theIndex) const
784 {
785   quintptr aIt = theIndex.internalId();
786   ModelAPI_Object* aObj = 0;
787   ModelAPI_Document* aDoc = 0;
788   SessionPtr aSession = ModelAPI_Session::get();
789   DocumentPtr aActiveDoc = aSession->activeDocument();
790
791   Qt::ItemFlags aNullFlag;
792   Qt::ItemFlags aDefaultFlag = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
793   Qt::ItemFlags aEditingFlag = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
794
795
796   if (aIt == 0) {
797     // Folders under root
798     DocumentPtr aRootDoc = aSession->moduleDocument();
799     if (aRootDoc != aActiveDoc)
800       return aDefaultFlag;
801   } else {
802     aDoc = getSubDocument(theIndex.internalPointer());
803     if (!aDoc)
804       aObj = dynamic_cast<ModelAPI_Object*>((ModelAPI_Entity*)theIndex.internalPointer());
805   }
806
807   if (aObj) {
808     // An object
809     if (aObj->isDisabled())
810       return theIndex.column() == 1? Qt::ItemIsSelectable : aNullFlag;
811
812     if (aSession->moduleDocument() != aObj->document())
813       if (aActiveDoc != aObj->document())
814         return theIndex.column() == 1? Qt::ItemIsSelectable : aNullFlag;
815
816     bool isCompositeSub = false;
817     // An object which is sub-object of a composite object can not be accessible in column 1
818     if (theIndex.column() == 1) {
819       ObjectPtr aObjPtr = aObj->data()->owner();
820       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObjPtr);
821       if (aFeature.get()) {
822         CompositeFeaturePtr aCompFea = ModelAPI_Tools::compositeOwner(aFeature);
823         if (aCompFea.get())
824           isCompositeSub = true;
825       } else {
826         ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aObjPtr);
827         if (aResult.get()) {
828           ResultCompSolidPtr aCompRes = ModelAPI_Tools::compSolidOwner(aResult);
829           if (aCompRes.get())
830             isCompositeSub = true;
831         }
832       }
833     }
834     if (isCompositeSub)
835       return Qt::ItemIsSelectable;
836
837     if (aObj->document() != aActiveDoc) {
838       // The object could be a root of sub-tree
839       ResultPartPtr aPartRes = getPartResult(aObj);
840       if (aPartRes.get()) {
841         if (aPartRes->partDoc() == aActiveDoc)
842           return aEditingFlag;
843       }
844       return aDefaultFlag;
845     }
846   } else if (aDoc) {
847     // A folder under sub-document
848     if (aActiveDoc.get() != aDoc)
849       return aNullFlag;
850   }
851   return aEditingFlag;
852 }
853
854 //******************************************************
855 QModelIndex XGUI_DataModel::findDocumentRootIndex(const ModelAPI_Document* theDoc) const
856 {
857   SessionPtr aSession = ModelAPI_Session::get();
858   DocumentPtr aRootDoc = aSession->moduleDocument();
859   if (myXMLReader->isAttachToResult()) { // If document is attached to result
860     int aNb = aRootDoc->size(ModelAPI_ResultPart::group());
861     ObjectPtr aObj;
862     ResultPartPtr aPartRes;
863     for (int i = 0; i < aNb; i++) {
864       aObj = aRootDoc->object(ModelAPI_ResultPart::group(), i);
865       aPartRes = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aObj);
866       if (aPartRes.get() && (aPartRes->partDoc().get() == theDoc)) {
867         int aRow = i;
868         if (myXMLReader->rootType() == ModelAPI_Feature::group()) {
869           aRow += foldersCount();
870         }
871         return createIndex(aRow, 0, aObj.get());
872       }
873     }
874   } else { // If document is attached to feature
875     int aNb = aRootDoc->size(ModelAPI_Feature::group());
876     ObjectPtr aObj;
877     ResultPartPtr aPartRes;
878     for (int i = 0; i < aNb; i++) {
879       aObj = aRootDoc->object(ModelAPI_Feature::group(), i);
880       aPartRes = getPartResult(aObj.get());
881       if (aPartRes.get() && (aPartRes->partDoc().get() == theDoc)) {
882         int aRow = i;
883         if (myXMLReader->rootType() == ModelAPI_Feature::group())
884           aRow += foldersCount();
885         return createIndex(aRow, 0, aObj.get());
886       }
887     }
888   }
889   return QModelIndex();
890 }
891
892 //******************************************************
893 QModelIndex XGUI_DataModel::documentRootIndex(DocumentPtr theDoc) const
894 {
895   SessionPtr aSession = ModelAPI_Session::get();
896   DocumentPtr aRootDoc = aSession->moduleDocument();
897   if (theDoc == aRootDoc)
898     return QModelIndex();
899   else
900     return findDocumentRootIndex(theDoc.get());
901 }
902
903 //******************************************************
904 int XGUI_DataModel::foldersCount(ModelAPI_Document* theDoc) const
905 {
906   int aNb = 0;
907   SessionPtr aSession = ModelAPI_Session::get();
908   DocumentPtr aRootDoc = aSession->moduleDocument();
909   if ((theDoc == 0) || (theDoc == aRootDoc.get())) {
910     for (int i = 0; i < myXMLReader->rootFoldersNumber(); i++) {
911       if (myXMLReader->rootShowEmpty(i))
912         aNb++;
913       else {
914         if (aRootDoc->size(myXMLReader->rootFolderType(i)) > 0)
915           aNb++;
916       }
917     }
918   } else {
919     for (int i = 0; i < myXMLReader->subFoldersNumber(); i++) {
920       if (myXMLReader->subShowEmpty(i))
921         aNb++;
922       else {
923         if (theDoc->size(myXMLReader->subFolderType(i)) > 0)
924           aNb++;
925       }
926     }
927   }
928   return aNb;
929 }
930
931
932 //******************************************************
933 QIntList XGUI_DataModel::missedFolderIndexes(ModelAPI_Document* theDoc) const
934 {
935   QIntList aList;
936   SessionPtr aSession = ModelAPI_Session::get();
937   DocumentPtr aRootDoc = aSession->moduleDocument();
938   if ((theDoc == 0) || (theDoc == aRootDoc.get())) {
939     for (int i = 0; i < myXMLReader->rootFoldersNumber(); i++) {
940       if (!myXMLReader->rootShowEmpty(i)) {
941         if (aRootDoc->size(myXMLReader->rootFolderType(i)) == 0)
942           aList.append(i);
943       }
944     }
945   } else {
946     for (int i = 0; i < myXMLReader->subFoldersNumber(); i++) {
947       if (!myXMLReader->subShowEmpty(i)) {
948         if (theDoc->size(myXMLReader->subFolderType(i)) == 0)
949           aList.append(i);
950       }
951     }
952   }
953   return aList;
954 }
955
956
957 //******************************************************
958 QStringList XGUI_DataModel::listOfShowNotEmptyFolders(bool fromRoot) const
959 {
960   QStringList aResult;
961   if (fromRoot) {
962     for (int i = 0; i < myXMLReader->rootFoldersNumber(); i++) {
963       if (!myXMLReader->rootShowEmpty(i))
964         aResult << myXMLReader->rootFolderType(i).c_str();
965     }
966   } else {
967     for (int i = 0; i < myXMLReader->subFoldersNumber(); i++) {
968       if (!myXMLReader->subShowEmpty(i))
969         aResult << myXMLReader->subFolderType(i).c_str();
970     }
971   }
972   return aResult;
973 }
974
975 //******************************************************
976 QModelIndex XGUI_DataModel::lastHistoryIndex() const
977 {
978   SessionPtr aSession = ModelAPI_Session::get();
979   DocumentPtr aCurDoc = aSession->activeDocument();
980   FeaturePtr aFeature = aCurDoc->currentFeature(true);
981   if (aFeature.get()) {
982     QModelIndex aInd = objectIndex(aFeature);
983     return createIndex(aInd.row(), 1, aInd.internalPointer());
984   } else {
985     if (aCurDoc == aSession->moduleDocument())
986       return createIndex(foldersCount() - 1, 1, -1);
987     else
988       return createIndex(foldersCount(aCurDoc.get()) - 1, 1, aCurDoc.get());
989   }
990 }
991
992 //******************************************************
993 int XGUI_DataModel::folderId(std::string theType, ModelAPI_Document* theDoc) const
994 {
995   SessionPtr aSession = ModelAPI_Session::get();
996   ModelAPI_Document* aDoc = theDoc;
997   if (aDoc == 0)
998     aDoc = aSession->moduleDocument().get();
999
1000   bool aUseSubDoc = (aDoc != aSession->moduleDocument().get());
1001
1002   int aRes = -1;
1003   if (aUseSubDoc) {
1004     int aId = myXMLReader->subFolderId(theType);
1005     aRes = aId;
1006     for (int i = 0; i < aId; i++) {
1007       if (!myXMLReader->subShowEmpty(i)) {
1008         if (aDoc->size(myXMLReader->subFolderType(i)) == 0)
1009           aRes--;
1010       }
1011     }
1012   } else {
1013     int aId = myXMLReader->rootFolderId(theType);
1014     aRes = aId;
1015     for (int i = 0; i < aId; i++) {
1016       if (!myXMLReader->rootShowEmpty(i)) {
1017         if (aDoc->size(myXMLReader->rootFolderType(i)) == 0)
1018           aRes--;
1019       }
1020     }
1021   }
1022   return aRes;
1023 }
1024
1025 //******************************************************
1026 void XGUI_DataModel::rebuildBranch(int theRow, int theCount, const QModelIndex& theParent)
1027 {
1028   if (theCount > 0) {
1029     removeRows(theRow, theCount, theParent);
1030     insertRows(theRow, theCount, theParent);
1031   }
1032 }
1033
1034 //******************************************************
1035 //bool XGUI_DataModel::blockEventsProcessing(const bool theState)
1036 //{
1037 //  bool aPreviousState = myIsEventsProcessingBlocked;
1038 //  myIsEventsProcessingBlocked = theState;
1039 //  return aPreviousState;
1040 //}