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