Salome HOME
Merge branch 'master' into cgt/devCEA
[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             int aCount = theRow - aNbSubFolders;
648             // To check number of objects before using
649             if (aSubDoc->size(aType) <= aCount)
650               return QModelIndex();
651             ObjectPtr aObj = aSubDoc->object(aType, aCount);
652             aIndex = objectIndex(aObj);
653           }
654         } else {
655           // Check for composite object
656           ModelAPI_CompositeFeature* aCompFeature =
657             dynamic_cast<ModelAPI_CompositeFeature*>(aParentObj);
658           if (aCompFeature) {
659             aIndex = objectIndex(aCompFeature->subFeature(theRow));
660           } else {
661             ModelAPI_ResultCompSolid* aCompRes =
662               dynamic_cast<ModelAPI_ResultCompSolid*>(aParentObj);
663             if (aCompRes)
664               aIndex = objectIndex(aCompRes->subResult(theRow));
665             else {
666               ModelAPI_ResultField* aFieldRes =
667                 dynamic_cast<ModelAPI_ResultField*>(aParentObj);
668               if (aFieldRes) {
669                 aIndex = createIndex(theRow, 0, aFieldRes->step(theRow));
670               }
671             }
672           }
673         }
674       }
675     }
676   }
677   if (theColumn != 0)
678     return createIndex(aIndex.row(), theColumn, aIndex.internalPointer());
679   return aIndex;
680 }
681
682 //******************************************************
683 static QModelIndex MYLastDeleted;
684 QModelIndex XGUI_DataModel::parent(const QModelIndex& theIndex) const
685 {
686   if (!theIndex.isValid())
687     return QModelIndex();
688   // To avoid additional request about index which was already deleted
689   if (theIndex == MYLastDeleted)
690     return QModelIndex();
691
692   SessionPtr aSession = ModelAPI_Session::get();
693   quintptr aId = theIndex.internalId();
694   if (aId != 0) { // The object is not a root folder
695     ModelAPI_Document* aDoc = getSubDocument(theIndex.internalPointer());
696     if (aDoc) {
697       // It is a folder of sub-document
698       return findDocumentRootIndex(aDoc);
699     }
700     ObjectPtr aObj = object(theIndex);
701     if (!aObj.get()) {
702       // It can b e a step of a field
703       ModelAPI_ResultField::ModelAPI_FieldStep* aStep =
704         dynamic_cast<ModelAPI_ResultField::ModelAPI_FieldStep*>
705         ((ModelAPI_Entity*)theIndex.internalPointer());
706       if (aStep) {
707         ModelAPI_ResultField* aField = aStep->field();
708         DocumentPtr aDoc = aSession->activeDocument();
709         ObjectPtr aFld;
710         for(int i = 0; i < aDoc->size(ModelAPI_ResultField::group()); i++) {
711           aFld = aDoc->object(ModelAPI_ResultField::group(), i);
712           if (aFld.get() == aField)
713             return objectIndex(aFld);
714         }
715       }
716       // To avoid additional request about index which was already deleted
717       // If deleted it causes a crash on delete object from Part
718       MYLastDeleted = theIndex;
719       return QModelIndex();
720     }
721     // Check is it object a sub-object of a complex object
722     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
723     if (aFeature.get()) {
724       CompositeFeaturePtr aCompFea = ModelAPI_Tools::compositeOwner(aFeature);
725       if (aCompFea.get()) {
726         return objectIndex(aCompFea);
727       }
728     }
729     ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aObj);
730     if (aResult.get()) {
731       ResultCompSolidPtr aCompRes = ModelAPI_Tools::compSolidOwner(aResult);
732       if (aCompRes.get()) {
733         return objectIndex(aCompRes);
734       }
735     }
736     // Use as ordinary object
737     std::string aType = aObj->groupName();
738     DocumentPtr aRootDoc = aSession->moduleDocument();
739     DocumentPtr aSubDoc = aObj->document();
740     if (aSubDoc == aRootDoc) {
741       if (aType == myXMLReader->rootType())
742         return QModelIndex();
743       else {
744         // return first level of folder index
745         int aFolderId = myXMLReader->rootFolderId(aType);
746         // Items in a one row must have the same parent
747         return createIndex(aFolderId, 0, (void*)Q_NULLPTR);
748       }
749     } else {
750       if (aType == myXMLReader->subType())
751         return findDocumentRootIndex(aSubDoc.get());
752       else {
753         // return first level of folder index
754         int aFolderId = folderId(aType, aSubDoc.get());
755         // Items in a one row must have the same parent
756         return createIndex(aFolderId, 0, aSubDoc.get());
757       }
758     }
759   }
760   return QModelIndex();
761 }
762
763 //******************************************************
764 bool XGUI_DataModel::hasChildren(const QModelIndex& theParent) const
765 {
766   return rowCount(theParent) > 0;
767 }
768
769 //******************************************************
770 bool XGUI_DataModel::insertRows(int theRow, int theCount, const QModelIndex& theParent)
771 {
772   beginInsertRows(theParent, theRow, theRow + theCount - 1);
773   endInsertRows();
774
775   return true;
776 }
777
778 //******************************************************
779 bool XGUI_DataModel::removeRows(int theRow, int theCount, const QModelIndex& theParent)
780 {
781   beginRemoveRows(theParent, theRow, theRow + theCount - 1);
782   endRemoveRows();
783   return true;
784 }
785
786 //******************************************************
787 Qt::ItemFlags XGUI_DataModel::flags(const QModelIndex& theIndex) const
788 {
789   quintptr aIt = theIndex.internalId();
790   ModelAPI_Object* aObj = 0;
791   ModelAPI_Document* aDoc = 0;
792   SessionPtr aSession = ModelAPI_Session::get();
793   DocumentPtr aActiveDoc = aSession->activeDocument();
794
795   Qt::ItemFlags aNullFlag;
796   Qt::ItemFlags aDefaultFlag = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
797   Qt::ItemFlags aEditingFlag = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
798
799
800   if (aIt == 0) {
801     // Folders under root
802     DocumentPtr aRootDoc = aSession->moduleDocument();
803     if (aRootDoc != aActiveDoc)
804       return aDefaultFlag;
805   } else {
806     aDoc = getSubDocument(theIndex.internalPointer());
807     if (!aDoc)
808       aObj = dynamic_cast<ModelAPI_Object*>((ModelAPI_Entity*)theIndex.internalPointer());
809   }
810
811   if (aObj) {
812     // An object
813     if (aObj->isDisabled())
814       return theIndex.column() == 1? Qt::ItemIsSelectable : aNullFlag;
815
816     if (aSession->moduleDocument() != aObj->document())
817       if (aActiveDoc != aObj->document())
818         return theIndex.column() == 1? Qt::ItemIsSelectable : aNullFlag;
819
820     bool isCompositeSub = false;
821     // An object which is sub-object of a composite object can not be accessible in column 1
822     if (theIndex.column() == 1) {
823       ObjectPtr aObjPtr = aObj->data()->owner();
824       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObjPtr);
825       if (aFeature.get()) {
826         CompositeFeaturePtr aCompFea = ModelAPI_Tools::compositeOwner(aFeature);
827         if (aCompFea.get())
828           isCompositeSub = true;
829       } else {
830         ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aObjPtr);
831         if (aResult.get()) {
832           ResultCompSolidPtr aCompRes = ModelAPI_Tools::compSolidOwner(aResult);
833           if (aCompRes.get())
834             isCompositeSub = true;
835         }
836       }
837     }
838     if (isCompositeSub)
839       return Qt::ItemIsSelectable;
840
841     if (aObj->document() != aActiveDoc) {
842       // The object could be a root of sub-tree
843       ResultPartPtr aPartRes = getPartResult(aObj);
844       if (aPartRes.get()) {
845         if (aPartRes->partDoc() == aActiveDoc)
846           return aEditingFlag;
847       }
848       return aDefaultFlag;
849     }
850   } else if (aDoc) {
851     // A folder under sub-document
852     if (aActiveDoc.get() != aDoc)
853       return aNullFlag;
854   }
855   return aEditingFlag;
856 }
857
858 //******************************************************
859 QModelIndex XGUI_DataModel::findDocumentRootIndex(const ModelAPI_Document* theDoc) const
860 {
861   SessionPtr aSession = ModelAPI_Session::get();
862   DocumentPtr aRootDoc = aSession->moduleDocument();
863   if (myXMLReader->isAttachToResult()) { // If document is attached to result
864     int aNb = aRootDoc->size(ModelAPI_ResultPart::group());
865     ObjectPtr aObj;
866     ResultPartPtr aPartRes;
867     for (int i = 0; i < aNb; i++) {
868       aObj = aRootDoc->object(ModelAPI_ResultPart::group(), i);
869       aPartRes = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aObj);
870       if (aPartRes.get() && (aPartRes->partDoc().get() == theDoc)) {
871         int aRow = i;
872         if (myXMLReader->rootType() == ModelAPI_Feature::group()) {
873           aRow += foldersCount();
874         }
875         return createIndex(aRow, 0, aObj.get());
876       }
877     }
878   } else { // If document is attached to feature
879     int aNb = aRootDoc->size(ModelAPI_Feature::group());
880     ObjectPtr aObj;
881     ResultPartPtr aPartRes;
882     for (int i = 0; i < aNb; i++) {
883       aObj = aRootDoc->object(ModelAPI_Feature::group(), i);
884       aPartRes = getPartResult(aObj.get());
885       if (aPartRes.get() && (aPartRes->partDoc().get() == theDoc)) {
886         int aRow = i;
887         if (myXMLReader->rootType() == ModelAPI_Feature::group())
888           aRow += foldersCount();
889         return createIndex(aRow, 0, aObj.get());
890       }
891     }
892   }
893   return QModelIndex();
894 }
895
896 //******************************************************
897 QModelIndex XGUI_DataModel::documentRootIndex(DocumentPtr theDoc) const
898 {
899   SessionPtr aSession = ModelAPI_Session::get();
900   DocumentPtr aRootDoc = aSession->moduleDocument();
901   if (theDoc == aRootDoc)
902     return QModelIndex();
903   else
904     return findDocumentRootIndex(theDoc.get());
905 }
906
907 //******************************************************
908 int XGUI_DataModel::foldersCount(ModelAPI_Document* theDoc) const
909 {
910   int aNb = 0;
911   SessionPtr aSession = ModelAPI_Session::get();
912   DocumentPtr aRootDoc = aSession->moduleDocument();
913   if ((theDoc == 0) || (theDoc == aRootDoc.get())) {
914     for (int i = 0; i < myXMLReader->rootFoldersNumber(); i++) {
915       if (myXMLReader->rootShowEmpty(i))
916         aNb++;
917       else {
918         if (aRootDoc->size(myXMLReader->rootFolderType(i)) > 0)
919           aNb++;
920       }
921     }
922   } else {
923     for (int i = 0; i < myXMLReader->subFoldersNumber(); i++) {
924       if (myXMLReader->subShowEmpty(i))
925         aNb++;
926       else {
927         if (theDoc->size(myXMLReader->subFolderType(i)) > 0)
928           aNb++;
929       }
930     }
931   }
932   return aNb;
933 }
934
935
936 //******************************************************
937 QIntList XGUI_DataModel::missedFolderIndexes(ModelAPI_Document* theDoc) const
938 {
939   QIntList aList;
940   SessionPtr aSession = ModelAPI_Session::get();
941   DocumentPtr aRootDoc = aSession->moduleDocument();
942   if ((theDoc == 0) || (theDoc == aRootDoc.get())) {
943     for (int i = 0; i < myXMLReader->rootFoldersNumber(); i++) {
944       if (!myXMLReader->rootShowEmpty(i)) {
945         if (aRootDoc->size(myXMLReader->rootFolderType(i)) == 0)
946           aList.append(i);
947       }
948     }
949   } else {
950     for (int i = 0; i < myXMLReader->subFoldersNumber(); i++) {
951       if (!myXMLReader->subShowEmpty(i)) {
952         if (theDoc->size(myXMLReader->subFolderType(i)) == 0)
953           aList.append(i);
954       }
955     }
956   }
957   return aList;
958 }
959
960
961 //******************************************************
962 QStringList XGUI_DataModel::listOfShowNotEmptyFolders(bool fromRoot) const
963 {
964   QStringList aResult;
965   if (fromRoot) {
966     for (int i = 0; i < myXMLReader->rootFoldersNumber(); i++) {
967       if (!myXMLReader->rootShowEmpty(i))
968         aResult << myXMLReader->rootFolderType(i).c_str();
969     }
970   } else {
971     for (int i = 0; i < myXMLReader->subFoldersNumber(); i++) {
972       if (!myXMLReader->subShowEmpty(i))
973         aResult << myXMLReader->subFolderType(i).c_str();
974     }
975   }
976   return aResult;
977 }
978
979 //******************************************************
980 QModelIndex XGUI_DataModel::lastHistoryIndex() const
981 {
982   SessionPtr aSession = ModelAPI_Session::get();
983   DocumentPtr aCurDoc = aSession->activeDocument();
984   FeaturePtr aFeature = aCurDoc->currentFeature(true);
985   if (aFeature.get()) {
986     QModelIndex aInd = objectIndex(aFeature);
987     return createIndex(aInd.row(), 1, aInd.internalPointer());
988   } else {
989     if (aCurDoc == aSession->moduleDocument())
990       return createIndex(foldersCount() - 1, 1, -1);
991     else
992       return createIndex(foldersCount(aCurDoc.get()) - 1, 1, aCurDoc.get());
993   }
994 }
995
996 //******************************************************
997 int XGUI_DataModel::folderId(std::string theType, ModelAPI_Document* theDoc) const
998 {
999   SessionPtr aSession = ModelAPI_Session::get();
1000   ModelAPI_Document* aDoc = theDoc;
1001   if (aDoc == 0)
1002     aDoc = aSession->moduleDocument().get();
1003
1004   bool aUseSubDoc = (aDoc != aSession->moduleDocument().get());
1005
1006   int aRes = -1;
1007   if (aUseSubDoc) {
1008     int aId = myXMLReader->subFolderId(theType);
1009     aRes = aId;
1010     for (int i = 0; i < aId; i++) {
1011       if (!myXMLReader->subShowEmpty(i)) {
1012         if (aDoc->size(myXMLReader->subFolderType(i)) == 0)
1013           aRes--;
1014       }
1015     }
1016   } else {
1017     int aId = myXMLReader->rootFolderId(theType);
1018     aRes = aId;
1019     for (int i = 0; i < aId; i++) {
1020       if (!myXMLReader->rootShowEmpty(i)) {
1021         if (aDoc->size(myXMLReader->rootFolderType(i)) == 0)
1022           aRes--;
1023       }
1024     }
1025   }
1026   return aRes;
1027 }
1028
1029 //******************************************************
1030 void XGUI_DataModel::rebuildBranch(int theRow, int theCount, const QModelIndex& theParent)
1031 {
1032   if (theCount > 0) {
1033     removeRows(theRow, theCount, theParent);
1034     insertRows(theRow, theCount, theParent);
1035   }
1036 }
1037
1038 //******************************************************
1039 //bool XGUI_DataModel::blockEventsProcessing(const bool theState)
1040 //{
1041 //  bool aPreviousState = myIsEventsProcessingBlocked;
1042 //  myIsEventsProcessingBlocked = theState;
1043 //  return aPreviousState;
1044 //}