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