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