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