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