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