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