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