Salome HOME
Merge branch 'BR_internationalization'
[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     DocumentPtr aDoc = aUpdMsg->document();
259     std::string aGroup = aUpdMsg->group();
260
261     QModelIndex aParent;
262     int aStartId = 0;
263     if (aDoc == aRootDoc) {
264       // Update a group under root
265       if (aGroup == myXMLReader->rootType()) // Update objects under root
266         aStartId = foldersCount();
267       else // Update objects in folder under root 
268         aParent = createIndex(folderId(aGroup), 0, -1);
269     } else {
270       // Update a sub-document
271       if (aGroup == myXMLReader->subType()) {
272         // Update sub-document root
273         aParent = findDocumentRootIndex(aDoc.get());
274         aStartId = foldersCount(aDoc.get());
275       } else 
276         // update folder in sub-document
277         aParent = createIndex(folderId(aGroup, aDoc.get()), 0, aDoc.get());
278     }
279     int aChildNb = rowCount(aParent);
280     rebuildBranch(aStartId, aChildNb - aStartId, aParent);
281   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_DOCUMENT_CHANGED)) {
282     DocumentPtr aDoc = ModelAPI_Session::get()->activeDocument();
283     if (aDoc != aRootDoc) {
284       QModelIndex aDocRoot = findDocumentRootIndex(aDoc.get());
285       if (aDocRoot.isValid())
286         emit dataChanged(aDocRoot, aDocRoot);
287       else 
288         // We have got a new document
289         rebuildDataTree();
290     }
291   } 
292 }
293
294 //******************************************************
295 void XGUI_DataModel::clear()
296 {
297
298 }
299
300 //******************************************************
301 void XGUI_DataModel::rebuildDataTree()
302 {
303   beginResetModel();
304   endResetModel();
305   emit treeRebuilt();
306 }
307
308 //******************************************************
309 ObjectPtr XGUI_DataModel::object(const QModelIndex& theIndex) const
310 {
311   if (theIndex.internalId() < 0) // this is a folder
312     return ObjectPtr();
313   ModelAPI_Object* aObj = (ModelAPI_Object*)theIndex.internalPointer();
314   if (getSubDocument(aObj)) // the selected index is a folder of sub-document
315     return ObjectPtr();
316
317   return aObj->data()->owner();
318 }
319
320 //******************************************************
321 QModelIndex XGUI_DataModel::objectIndex(const ObjectPtr theObject) const
322 {
323   std::string aType = theObject->groupName();
324   DocumentPtr aDoc = theObject->document();
325   int aRow = aDoc->index(theObject);
326   if (aRow == -1) {
327     // it could be a part of complex object
328     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
329     if (aFeature.get()) {
330       CompositeFeaturePtr aCompFea = ModelAPI_Tools::compositeOwner(aFeature);
331       if (aCompFea.get()) {
332         for (int i = 0; i < aCompFea->numberOfSubs(true); i++) {
333           if (aCompFea->subFeature(i, true) == theObject) {
334             aRow = i;
335             break;
336           }
337         }
338       }
339     } else {
340       ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(theObject);
341       if (aResult.get()) {
342         ResultCompSolidPtr aCompRes = ModelAPI_Tools::compSolidOwner(aResult);
343         if (aCompRes.get()) {
344           for (int i = 0; i < aCompRes->numberOfSubs(true); i++) {
345             if (aCompRes->subResult(i, true) == theObject) {
346               aRow = i;
347               break;
348             }
349           }
350         }
351       }
352     }
353     if (aRow == -1)
354       return QModelIndex();
355     else 
356       return createIndex(aRow, 0, theObject.get());
357   }
358   SessionPtr aSession = ModelAPI_Session::get();
359   DocumentPtr aRootDoc = aSession->moduleDocument();
360   if (aDoc == aRootDoc && myXMLReader->rootType() == aType) { 
361     // The object from root document
362     aRow += foldersCount();
363   } else if (myXMLReader->subType() == aType) { 
364     // The object from sub document
365     aRow += foldersCount(aDoc.get());
366   }
367   return createIndex(aRow, 0, theObject.get());
368 }
369
370 //******************************************************
371 QVariant XGUI_DataModel::data(const QModelIndex& theIndex, int theRole) const
372 {
373   SessionPtr aSession = ModelAPI_Session::get();
374   DocumentPtr aRootDoc = aSession->moduleDocument();
375   int aNbFolders = foldersCount();
376   int theIndexRow = theIndex.row();
377
378   if ((theRole == Qt::DecorationRole) && (theIndex == lastHistoryIndex()))
379     return QIcon(":pictures/arrow.png");
380
381   if (theIndex.column() == 1)
382     return QVariant();
383
384   int aParentId = theIndex.internalId();
385   if (aParentId == -1) { // root folders
386     switch (theRole) {
387       case Qt::DisplayRole:
388         return QString(myXMLReader->rootFolderName(theIndexRow).c_str()) + 
389           QString(" (%1)").arg(rowCount(theIndex));
390       case Qt::DecorationRole:
391         return QIcon(myXMLReader->rootFolderIcon(theIndexRow).c_str());
392       case Qt::ForegroundRole:
393         {
394           Qt::ItemFlags aFlags = theIndex.flags();
395           if (aFlags == Qt::ItemFlags())
396             return QBrush(DISABLED_COLOR);
397           if (!aFlags.testFlag(Qt::ItemIsEditable))
398             return QBrush(SELECTABLE_COLOR);
399         }
400         return ACTIVE_COLOR;
401     }
402   } else { // an object or sub-document
403     if (theRole == Qt::ForegroundRole) {
404       Qt::ItemFlags aFlags = theIndex.flags();
405       if (aFlags == Qt::ItemFlags())
406         return QBrush(DISABLED_COLOR);
407       if (!aFlags.testFlag(Qt::ItemIsEditable))
408         return QBrush(SELECTABLE_COLOR);
409       return ACTIVE_COLOR;
410     }
411
412     ModelAPI_Document* aSubDoc = getSubDocument(theIndex.internalPointer());
413     if (aSubDoc) { // this is a folder of sub document
414       QIntList aMissedIdx = missedFolderIndexes(aSubDoc);
415       int aRow = theIndexRow;
416       while (aMissedIdx.contains(aRow)) 
417         aRow++;
418
419       switch (theRole) {
420         case Qt::DisplayRole:
421           return QString(myXMLReader->subFolderName(aRow).c_str()) + 
422             QString(" (%1)").arg(rowCount(theIndex));
423         case Qt::DecorationRole:
424           return QIcon(myXMLReader->subFolderIcon(aRow).c_str());
425       }
426     } else {
427       ModelAPI_Object* aObj = (ModelAPI_Object*)theIndex.internalPointer();
428       switch (theRole) {
429       case Qt::DisplayRole:
430         {
431           if (aObj->groupName() == ModelAPI_ResultParameter::group()) {
432             ModelAPI_ResultParameter* aParam = dynamic_cast<ModelAPI_ResultParameter*>(aObj);
433             AttributeDoublePtr aValueAttribute = aParam->data()->real(ModelAPI_ResultParameter::VALUE());
434             QString aVal = QString::number(aValueAttribute->value());
435             QString aTitle = QString(aObj->data()->name().c_str());
436             return aTitle + " = " + aVal;
437           }
438           QString aSuffix;
439           if (aObj->groupName() == myXMLReader->subType()) {
440             ResultPartPtr aPartRes = getPartResult(aObj);
441             if (aPartRes.get()) {
442               if (aPartRes->partDoc().get() == NULL)
443                 aSuffix = " (Not loaded)";
444             }
445           }
446           return aObj->data()->name().c_str() + aSuffix;
447         }
448       case Qt::DecorationRole:
449         return ModuleBase_IconFactory::get()->getIcon(object(theIndex));
450       }
451     }
452   }
453   return QVariant();
454 }
455
456 //******************************************************
457 QVariant XGUI_DataModel::headerData(int theSection, Qt::Orientation theOrient, int theRole) const
458 {
459   return QVariant();
460 }
461
462 //******************************************************
463 int XGUI_DataModel::rowCount(const QModelIndex& theParent) const
464 {
465   SessionPtr aSession = ModelAPI_Session::get();
466   if (!aSession->hasModuleDocument())
467     return 0;
468   DocumentPtr aRootDoc = aSession->moduleDocument();
469
470   if (!theParent.isValid()) {
471     // Return number of items in root
472     int aNbFolders = foldersCount();
473     int aNbItems = 0;
474     std::string aType = myXMLReader->rootType();
475     if (!aType.empty())
476       aNbItems = aRootDoc->size(aType);
477     return aNbFolders + aNbItems;
478   }
479
480   int aId = theParent.internalId();
481   if (aId == -1) { 
482     // this is a folder under root
483     int aParentPos = theParent.row();
484     std::string aType = myXMLReader->rootFolderType(aParentPos);
485     return aRootDoc->size(aType);
486   } else {
487     // It is an object which could have children
488     ModelAPI_Document* aDoc = getSubDocument(theParent.internalPointer());
489     if (aDoc) { 
490       // a folder of sub-document
491       QIntList aMissedIdx = missedFolderIndexes(aDoc);
492       int aRow = theParent.row();
493       while (aMissedIdx.contains(aRow)) 
494         aRow++;
495       std::string aType = myXMLReader->subFolderType(aRow);
496       return aDoc->size(aType);
497     } else {
498       ModelAPI_Object* aObj = (ModelAPI_Object*)theParent.internalPointer();
499       // Check for Part feature
500       ResultPartPtr aPartRes = getPartResult(aObj);
501       if (aPartRes.get()) {
502         DocumentPtr aSubDoc = aPartRes->partDoc();
503         if (!aSubDoc.get())
504           return 0;
505
506         int aNbSubFolders = foldersCount(aSubDoc.get());
507         int aNbSubItems = 0;
508         std::string aSubType = myXMLReader->subType();
509         if (!aSubType.empty())
510           aNbSubItems = aSubDoc->size(aSubType);
511         return aNbSubItems + aNbSubFolders;
512       } else {
513         // Check for composite object
514         ModelAPI_CompositeFeature* aCompFeature = dynamic_cast<ModelAPI_CompositeFeature*>(aObj);
515         if (aCompFeature) 
516           return aCompFeature->numberOfSubs(true);
517         ModelAPI_ResultCompSolid* aCompRes = dynamic_cast<ModelAPI_ResultCompSolid*>(aObj);
518         if (aCompRes) 
519           return aCompRes->numberOfSubs(true);
520       }
521     }
522   }
523   return 0;
524 }
525
526 //******************************************************
527 int XGUI_DataModel::columnCount(const QModelIndex& theParent) const
528 {
529   return 2;
530 }
531
532 //******************************************************
533 QModelIndex XGUI_DataModel::index(int theRow, int theColumn, const QModelIndex &theParent) const
534 {
535   SessionPtr aSession = ModelAPI_Session::get();
536   DocumentPtr aRootDoc = aSession->moduleDocument();
537   int aNbFolders = foldersCount();
538
539   QModelIndex aIndex;
540
541   if (!theParent.isValid()) {
542     if (theRow < aNbFolders) // Return first level folder index
543       return createIndex(theRow, theColumn, -1);
544     else { // return object under root index
545       std::string aType = myXMLReader->rootType();
546       int aObjId = theRow - aNbFolders;
547       if (aObjId < aRootDoc->size(aType)) {
548         ObjectPtr aObj = aRootDoc->object(aType, aObjId);
549         aIndex = objectIndex(aObj);
550       }
551     }
552   } else {
553     int aId = theParent.internalId();
554     int aParentPos = theParent.row();
555     if (aId == -1) { // return object index inside of first level of folders
556       std::string aType = myXMLReader->rootFolderType(aParentPos);
557       if (theRow < aRootDoc->size(aType)) {
558         ObjectPtr aObj = aRootDoc->object(aType, theRow);
559         aIndex = objectIndex(aObj);
560       }
561     } else {
562       // It is an object which could have children
563       ModelAPI_Document* aDoc = getSubDocument(theParent.internalPointer());
564       if (aDoc) { 
565         // It is a folder of sub-document
566         int aParentRow = aParentPos;
567         QIntList aMissedIdx = missedFolderIndexes(aDoc);
568         while (aMissedIdx.contains(aParentRow))
569           aParentRow++;
570         std::string aType = myXMLReader->subFolderType(aParentRow);
571         if (theRow < aDoc->size(aType)) {
572           ObjectPtr aObj = aDoc->object(aType, theRow);
573           aIndex = objectIndex(aObj);
574         }
575       } else {
576         ModelAPI_Object* aParentObj = (ModelAPI_Object*)theParent.internalPointer();
577
578         // Check for Part feature
579         ResultPartPtr aPartRes = getPartResult(aParentObj);
580         if (aPartRes.get()) {
581           DocumentPtr aSubDoc = aPartRes->partDoc();
582           int aNbSubFolders = foldersCount(aSubDoc.get());
583           if (theRow < aNbSubFolders) { // Create a Folder of sub-document
584             aIndex = createIndex(theRow, theColumn, aSubDoc.get());
585           } else {
586             // this is an object under sub document root
587             std::string aType = myXMLReader->subType();
588             ObjectPtr aObj = aSubDoc->object(aType, theRow - aNbSubFolders);
589             aIndex = objectIndex(aObj);
590           }
591         } else {
592           // Check for composite object
593           ModelAPI_CompositeFeature* aCompFeature = dynamic_cast<ModelAPI_CompositeFeature*>(aParentObj);
594           if (aCompFeature) {
595             aIndex = objectIndex(aCompFeature->subFeature(theRow));
596           } else {
597             ModelAPI_ResultCompSolid* aCompRes = dynamic_cast<ModelAPI_ResultCompSolid*>(aParentObj);
598             if (aCompRes) 
599               aIndex = objectIndex(aCompRes->subResult(theRow));
600           }
601         }
602       }
603     }
604   }
605   if (theColumn != 0)
606     return createIndex(aIndex.row(), theColumn, aIndex.internalPointer());
607   return aIndex;
608 }
609
610 //******************************************************
611 static QModelIndex MYLastDeleted;
612 QModelIndex XGUI_DataModel::parent(const QModelIndex& theIndex) const
613 {
614   if (!theIndex.isValid())
615     return QModelIndex();
616   // To avoid additional request about index which was already deleted
617   if (theIndex == MYLastDeleted)
618     return QModelIndex();
619
620   int aId = theIndex.internalId();
621   if (aId != -1) { // The object is not a root folder
622     ModelAPI_Document* aDoc = getSubDocument(theIndex.internalPointer());
623     if (aDoc) { 
624       // It is a folder of sub-document
625       return findDocumentRootIndex(aDoc);
626     }
627     ObjectPtr aObj = object(theIndex);
628     if (!aObj.get()) {
629       // To avoid additional request about index which was already deleted
630       // If deleted it causes a crash on delete object from Part
631       MYLastDeleted = theIndex;
632       return QModelIndex();
633     }
634     // Check is it object a sub-object of a complex object
635     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
636     if (aFeature.get()) {
637       CompositeFeaturePtr aCompFea = ModelAPI_Tools::compositeOwner(aFeature);
638       if (aCompFea.get()) {
639         return objectIndex(aCompFea);
640       }
641     }
642     ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aObj);
643     if (aResult.get()) {
644       ResultCompSolidPtr aCompRes = ModelAPI_Tools::compSolidOwner(aResult);
645       if (aCompRes.get()) {
646         return objectIndex(aCompRes);
647       }
648     }
649     // Use as ordinary object
650     std::string aType = aObj->groupName();
651     SessionPtr aSession = ModelAPI_Session::get();
652     DocumentPtr aRootDoc = aSession->moduleDocument();
653     DocumentPtr aSubDoc = aObj->document();
654     if (aSubDoc == aRootDoc) {
655       if (aType == myXMLReader->rootType())
656         return QModelIndex();
657       else {
658         // return first level of folder index
659         int aFolderId = myXMLReader->rootFolderId(aType);
660         // Items in a one row must have the same parent
661         return createIndex(aFolderId, 0, -1);
662       }
663     } else {
664       if (aType == myXMLReader->subType())
665         return findDocumentRootIndex(aSubDoc.get());
666       else {
667         // return first level of folder index
668         int aFolderId = myXMLReader->subFolderId(aType);
669         // Items in a one row must have the same parent
670         return createIndex(aFolderId, 0, aSubDoc.get());
671       }
672     }
673   } 
674   return QModelIndex();
675 }
676
677 //******************************************************
678 bool XGUI_DataModel::hasChildren(const QModelIndex& theParent) const
679 {
680   return rowCount(theParent) > 0;
681 }
682
683 //******************************************************
684 bool XGUI_DataModel::insertRows(int theRow, int theCount, const QModelIndex& theParent)
685 {
686   beginInsertRows(theParent, theRow, theRow + theCount - 1);
687   endInsertRows();
688
689   return true;
690 }
691
692 //******************************************************
693 bool XGUI_DataModel::removeRows(int theRow, int theCount, const QModelIndex& theParent)
694 {
695   beginRemoveRows(theParent, theRow, theRow + theCount - 1);
696   endRemoveRows();
697   return true;
698 }
699
700 //******************************************************
701 Qt::ItemFlags XGUI_DataModel::flags(const QModelIndex& theIndex) const
702 {
703   qint64 aIt = theIndex.internalId();
704   ModelAPI_Object* aObj = 0;
705   ModelAPI_Document* aDoc = 0;
706   SessionPtr aSession = ModelAPI_Session::get();
707   DocumentPtr aActiveDoc = aSession->activeDocument();
708
709   Qt::ItemFlags aNullFlag;
710   Qt::ItemFlags aDefaultFlag = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
711   Qt::ItemFlags aEditingFlag = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
712
713
714   if (aIt == -1) {
715     // Folders under root
716     DocumentPtr aRootDoc = aSession->moduleDocument();
717     if (aRootDoc != aActiveDoc)
718       return aDefaultFlag;
719   } else {
720     aDoc = getSubDocument(theIndex.internalPointer());
721     if (!aDoc)
722       aObj = (ModelAPI_Object*) theIndex.internalPointer();
723   }
724
725   if (aObj) {
726     // An object
727     if (aObj->isDisabled()) 
728       return theIndex.column() == 1? Qt::ItemIsSelectable : aNullFlag;
729
730     if (aSession->moduleDocument() != aObj->document())
731       if (aActiveDoc != aObj->document())
732         return theIndex.column() == 1? Qt::ItemIsSelectable : aNullFlag;
733     
734     bool isCompositeSub = false;
735     // An object which is sub-object of a composite object can not be accessible in column 1
736     if (theIndex.column() == 1) {
737       ObjectPtr aObjPtr = aObj->data()->owner();
738       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObjPtr);
739       if (aFeature.get()) {
740         CompositeFeaturePtr aCompFea = ModelAPI_Tools::compositeOwner(aFeature);
741         if (aCompFea.get()) 
742           isCompositeSub = true;
743       } else {
744         ResultPtr aResult = std::dynamic_pointer_cast<ModelAPI_Result>(aObjPtr);
745         if (aResult.get()) {
746           ResultCompSolidPtr aCompRes = ModelAPI_Tools::compSolidOwner(aResult);
747           if (aCompRes.get()) 
748             isCompositeSub = true;
749         }
750       }
751     }
752     if (isCompositeSub)
753       return Qt::ItemIsSelectable;
754
755     if (aObj->document() != aActiveDoc) {
756       // The object could be a root of sub-tree
757       ResultPartPtr aPartRes = getPartResult(aObj);
758       if (aPartRes.get()) {
759         if (aPartRes->partDoc() == aActiveDoc)
760           return aEditingFlag;
761       }
762       return aDefaultFlag;
763     }
764   } else if (aDoc) {
765     // A folder under sub-document
766     if (aActiveDoc.get() != aDoc)
767       return aNullFlag;
768   }
769   return aEditingFlag;
770 }
771
772 //******************************************************
773 QModelIndex XGUI_DataModel::findDocumentRootIndex(const ModelAPI_Document* theDoc) const
774 {
775   SessionPtr aSession = ModelAPI_Session::get();
776   DocumentPtr aRootDoc = aSession->moduleDocument();
777   if (myXMLReader->isAttachToResult()) { // If document is attached to result
778     int aNb = aRootDoc->size(ModelAPI_ResultPart::group());
779     ObjectPtr aObj;
780     ResultPartPtr aPartRes;
781     for (int i = 0; i < aNb; i++) {
782       aObj = aRootDoc->object(ModelAPI_ResultPart::group(), i);
783       aPartRes = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aObj);
784       if (aPartRes.get() && (aPartRes->partDoc().get() == theDoc)) {
785         int aRow = i;
786         if (myXMLReader->rootType() == ModelAPI_Feature::group()) {
787           aRow += foldersCount();
788         }
789         return createIndex(aRow, 0, aObj.get());
790       }
791     }
792   } else { // If document is attached to feature
793     int aNb = aRootDoc->size(ModelAPI_Feature::group());
794     ObjectPtr aObj;
795     ResultPartPtr aPartRes;
796     for (int i = 0; i < aNb; i++) {
797       aObj = aRootDoc->object(ModelAPI_Feature::group(), i);
798       aPartRes = getPartResult(aObj.get());
799       if (aPartRes.get() && (aPartRes->partDoc().get() == theDoc)) {
800         int aRow = i;
801         if (myXMLReader->rootType() == ModelAPI_Feature::group())
802           aRow += foldersCount();
803         return createIndex(aRow, 0, aObj.get());
804       }
805     }
806   }
807   return QModelIndex();
808 }
809
810 //******************************************************
811 QModelIndex XGUI_DataModel::documentRootIndex(DocumentPtr theDoc) const
812 {
813   SessionPtr aSession = ModelAPI_Session::get();
814   DocumentPtr aRootDoc = aSession->moduleDocument();
815   if (theDoc == aRootDoc)
816     return QModelIndex();
817   else 
818     return findDocumentRootIndex(theDoc.get());
819 }
820
821 //******************************************************
822 int XGUI_DataModel::foldersCount(ModelAPI_Document* theDoc) const
823 {
824   int aNb = 0;
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         aNb++;
831       else {
832         if (aRootDoc->size(myXMLReader->rootFolderType(i)) > 0)
833           aNb++;
834       }
835     }
836   } else {
837     for (int i = 0; i < myXMLReader->subFoldersNumber(); i++) {
838       if (myXMLReader->subShowEmpty(i))
839         aNb++;
840       else {
841         if (theDoc->size(myXMLReader->subFolderType(i)) > 0)
842           aNb++;
843       }
844     }
845   }
846   return aNb;
847 }
848
849
850 //******************************************************
851 QIntList XGUI_DataModel::missedFolderIndexes(ModelAPI_Document* theDoc) const
852 {
853   QIntList aList;
854   SessionPtr aSession = ModelAPI_Session::get();
855   DocumentPtr aRootDoc = aSession->moduleDocument();
856   if ((theDoc == 0) || (theDoc == aRootDoc.get())) {
857     for (int i = 0; i < myXMLReader->rootFoldersNumber(); i++) {
858       if (!myXMLReader->rootShowEmpty(i)) {
859         if (aRootDoc->size(myXMLReader->rootFolderType(i)) == 0)
860           aList.append(i);
861       }
862     }
863   } else {
864     for (int i = 0; i < myXMLReader->subFoldersNumber(); i++) {
865       if (!myXMLReader->subShowEmpty(i)) {
866         if (theDoc->size(myXMLReader->subFolderType(i)) == 0)
867           aList.append(i);
868       }
869     }
870   }
871   return aList;
872 }
873
874
875 //******************************************************
876 QStringList XGUI_DataModel::listOfShowNotEmptyFolders(bool fromRoot) const
877 {
878   QStringList aResult;
879   if (fromRoot) {
880     for (int i = 0; i < myXMLReader->rootFoldersNumber(); i++) {
881       if (!myXMLReader->rootShowEmpty(i))
882         aResult << myXMLReader->rootFolderType(i).c_str();
883     }
884   } else {
885     for (int i = 0; i < myXMLReader->subFoldersNumber(); i++) {
886       if (!myXMLReader->subShowEmpty(i))
887         aResult << myXMLReader->subFolderType(i).c_str();
888     }
889   }
890   return aResult;
891 }
892
893 //******************************************************
894 QModelIndex XGUI_DataModel::lastHistoryIndex() const
895 {
896   SessionPtr aSession = ModelAPI_Session::get();
897   DocumentPtr aCurDoc = aSession->activeDocument();
898   FeaturePtr aFeature = aCurDoc->currentFeature(true);
899   if (aFeature.get()) {
900     QModelIndex aInd = objectIndex(aFeature);
901     return createIndex(aInd.row(), 1, aInd.internalPointer());
902   } else {
903     if (aCurDoc == aSession->moduleDocument())
904       return createIndex(foldersCount() - 1, 1, -1);
905     else 
906       return createIndex(foldersCount(aCurDoc.get()) - 1, 1, aCurDoc.get());
907   }
908 }
909
910 //******************************************************
911 int XGUI_DataModel::folderId(std::string theType, ModelAPI_Document* theDoc)
912 {
913   SessionPtr aSession = ModelAPI_Session::get();
914   ModelAPI_Document* aDoc = theDoc;
915   if (aDoc == 0)
916     aDoc = aSession->moduleDocument().get();
917
918   bool aUseSubDoc = (aDoc != aSession->moduleDocument().get());
919
920   int aRes = -1;
921   if (aUseSubDoc) {
922     int aId = myXMLReader->subFolderId(theType);
923     aRes = aId;
924     for (int i = 0; i < aId; i++) {
925       if (!myXMLReader->subShowEmpty(i)) {
926         if (aDoc->size(myXMLReader->subFolderType(i)) == 0)
927           aRes--;
928       }
929     }
930   } else {
931     int aId = myXMLReader->rootFolderId(theType);
932     aRes = aId;
933     for (int i = 0; i < aId; i++) {
934       if (!myXMLReader->rootShowEmpty(i)) {
935         if (aDoc->size(myXMLReader->rootFolderType(i)) == 0)
936           aRes--;
937       }
938     }
939   }
940   return aRes;
941 }
942
943 //******************************************************
944 void XGUI_DataModel::rebuildBranch(int theRow, int theCount, const QModelIndex& theParent)
945 {
946   if (theCount > 0) {
947     removeRows(theRow, theCount, theParent);
948     insertRows(theRow, theCount, theParent);
949   }
950 }