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