Salome HOME
2041d5cb0739a3e0815e7ad7837ebb2423dc42cb
[modules/shaper.git] / src / PartSet / PartSet_DocumentDataModel.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 #include "PartSet_DocumentDataModel.h"
4 #include "PartSet_PartDataModel.h"
5 #include "PartSet_Module.h"
6 //#include "XGUI_Tools.h"
7
8 #include <ModelAPI_Session.h>
9 #include <ModelAPI_Document.h>
10 #include <ModelAPI_Feature.h>
11 #include <ModelAPI_Data.h>
12 #include <ModelAPI_ResultPart.h>
13 #include <ModelAPI_Events.h>
14 #include <ModelAPI_Object.h>
15
16 #include <Events_Loop.h>
17
18 #include <Config_FeatureMessage.h>
19 #include <ModuleBase_Tools.h>
20 #include <ModuleBase_ActionInfo.h>
21
22 #include <QIcon>
23 #include <QString>
24 #include <QBrush>
25
26 #include <set>
27
28 #define ACTIVE_COLOR QColor(0,72,140)
29 #define PASSIVE_COLOR Qt::black
30
31 QMap<QString, QString> PartSet_DocumentDataModel::myIcons;
32
33
34 PartSet_DocumentDataModel::PartSet_DocumentDataModel(QObject* theParent)
35     : ModuleBase_IDocumentDataModel(theParent),
36       myActivePart(0), myHistoryBackOffset(0)
37 {
38   // Create a top part of data tree model
39   myModel = new PartSet_TopDataModel(this);
40   myModel->setItemsColor(ACTIVE_COLOR);
41
42   Events_Loop* aLoop = Events_Loop::loop();
43   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
44   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
45   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
46   aLoop->registerListener(this, Events_Loop::eventByName(Config_FeatureMessage::GUI_EVENT()));
47 }
48
49 PartSet_DocumentDataModel::~PartSet_DocumentDataModel()
50 {
51   clearModelIndexes();
52   clearSubModels();
53 }
54
55 void PartSet_DocumentDataModel::processEvent(const std::shared_ptr<Events_Message>& theMessage)
56 {
57   DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
58
59   // Created object event *******************
60   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)) {
61     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
62         std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
63     std::set<ObjectPtr> aObjects = aUpdMsg->objects();
64
65     std::set<ObjectPtr>::const_iterator aIt;
66     for (aIt = aObjects.begin(); aIt != aObjects.end(); ++aIt) {
67       ObjectPtr aObject = (*aIt);
68       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObject);
69       if (aFeature && (!aFeature->isInHistory()))
70         continue;
71
72       DocumentPtr aDoc = aObject->document();
73       if (aDoc == aRootDoc) {  // If root objects
74         if (aObject->groupName() == ModelAPI_ResultPart::group()) {  // Update only Parts group
75             // Add a new part
76           int aStart = myPartModels.size();
77           PartSet_PartDataModel* aModel = new PartSet_PartDataModel(this);
78           aModel->setPartId(myPartModels.count());
79           myPartModels.append(aModel);
80           insertRow(aStart, partFolderNode(0));
81         } else {  // Update top groups (other except parts
82           QModelIndex aIndex = myModel->findParent(aObject);
83           int aStart = myModel->rowCount(aIndex) - 1;
84           if (aStart < 0)
85             aStart = 0;
86           aIndex = createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex));
87           insertRow(aStart, aIndex);
88         }
89       } else {  // if sub-objects of first level nodes
90         PartSet_PartModel* aPartModel = 0;
91         foreach (PartSet_PartModel* aPart, myPartModels) {
92           if (aPart->hasDocument(aDoc)) {
93             aPartModel = aPart;
94             break;
95           }
96         }
97         if (aPartModel) {
98           QModelIndex aIndex = aPartModel->findParent(aObject);
99           int aStart = aPartModel->rowCount(aIndex);  // check this index
100           aIndex = createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex));
101           insertRow(aStart, aIndex);
102         } else
103           reset();
104       }
105     }
106     // Deleted object event ***********************
107   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
108     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aUpdMsg =
109         std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
110     DocumentPtr aDoc = aUpdMsg->document();
111     std::set<std::string> aGroups = aUpdMsg->groups();
112
113     std::set<std::string>::const_iterator aIt;
114     for (aIt = aGroups.begin(); aIt != aGroups.end(); ++aIt) {
115       std::string aGroup = (*aIt);
116       if (aDoc == aRootDoc) {  // If root objects
117         if (aGroup == ModelAPI_ResultPart::group()) {  // Update only Parts group
118           int aStart = myPartModels.size() - 1;
119           if (aStart >= 0) {// MPV: this could be reproduced on close
120             removeSubModel(aStart);
121             removeRow(aStart, partFolderNode(0));
122             if (myActivePart && (!isPartSubModel(myActivePart))) {
123               myActivePart = 0;
124               myActivePartIndex = QModelIndex();
125               myModel->setItemsColor(ACTIVE_COLOR);
126             }
127           }
128         } else {  // Update top groups (other except parts
129           QModelIndex aIndex = myModel->findGroup(aGroup);
130           int aStart = myModel->rowCount(aIndex);
131           aIndex = createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex));
132           removeRow(aStart, aIndex);
133         }
134       } else {
135         PartSet_PartModel* aPartModel = 0;
136         foreach (PartSet_PartModel* aPart, myPartModels) {
137           if (aPart->hasDocument(aDoc)) {
138             aPartModel = aPart;
139             break;
140           }
141         }
142         if (aPartModel) {
143           QModelIndex aIndex = aPartModel->findGroup(aGroup);
144           int aStart = aPartModel->rowCount(aIndex);
145           aIndex = createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex));
146           removeRow(aStart, aIndex);
147         }
148       }
149     }
150     // Deleted object event ***********************
151   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)) {
152     //std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg = std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
153     //ObjectPtr aFeature = aUpdMsg->feature();
154     //DocumentPtr aDoc = aFeature->document();
155
156     // TODO: Identify the necessary index by the modified feature
157     QModelIndex aIndex;
158     emit dataChanged(aIndex, aIndex);
159
160     // Reset whole tree **************************
161   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(Config_FeatureMessage::GUI_EVENT())) {
162     std::shared_ptr<Config_FeatureMessage> aFeatureMsg =
163        std::dynamic_pointer_cast<Config_FeatureMessage>(theMessage);
164     if (!aFeatureMsg->isInternal()) {
165       ActionInfo aFeatureInfo;
166       aFeatureInfo.initFrom(aFeatureMsg);
167       // Remember features icons
168       myIcons[QString::fromStdString(aFeatureMsg->id())] = aFeatureInfo.iconFile;
169     }
170   } else {
171     rebuildDataTree();
172   }
173 }
174
175 void PartSet_DocumentDataModel::rebuildDataTree()
176 {
177   DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
178
179   beginResetModel();
180   clearModelIndexes();
181
182   int aNbParts = aRootDoc->size(ModelAPI_ResultPart::group());
183   if (myPartModels.size() != aNbParts) {  // resize internal models
184     while (myPartModels.size() > aNbParts) {
185       delete myPartModels.last();
186       myPartModels.removeLast();
187     }
188     while (myPartModels.size() < aNbParts) {
189       myPartModels.append(new PartSet_PartDataModel(this));
190     }
191     for (int i = 0; i < myPartModels.size(); i++)
192       myPartModels.at(i)->setPartId(i);
193   }
194   endResetModel();
195 }
196
197 QVariant PartSet_DocumentDataModel::data(const QModelIndex& theIndex, int theRole) const
198 {
199   if (!theIndex.isValid())
200     return QVariant();
201   QModelIndex aParent = theIndex.parent();
202
203   if ((theIndex.column() == 1) ) {
204     if ((theIndex.internalId() == HistoryNode) && (!aParent.isValid())) {
205       switch (theRole) {
206       case Qt::DecorationRole:
207         if (theIndex.row() == lastHistoryRow())
208           return QIcon(":pictures/arrow.png");
209       }
210     }
211     return QVariant();
212   }
213
214   switch (theIndex.internalId()) {
215     case PartsFolder:
216       switch (theRole) {
217         case Qt::DisplayRole:
218           return tr("Parts") + QString(" (%1)").arg(rowCount(theIndex));
219         case Qt::DecorationRole:
220           return QIcon(":pictures/constr_folder.png");
221         case Qt::ToolTipRole:
222           return tr("Parts folder");
223         case Qt::ForegroundRole:
224           if (myActivePart)
225               return QBrush(PASSIVE_COLOR);
226             else
227               return QBrush(ACTIVE_COLOR);
228         default:
229           return QVariant();
230       }
231       break;
232     case HistoryNode:
233       {
234         int aOffset = historyOffset();
235         DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
236         ObjectPtr aObj = aRootDoc->object(ModelAPI_Feature::group(), theIndex.row() - aOffset);
237         FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
238         if (!aFeature)
239           return QVariant();
240         switch (theRole) {
241           case Qt::DisplayRole:
242             if (aFeature)
243               return aFeature->data()->name().c_str();
244             else
245               return QVariant();
246           case Qt::DecorationRole:
247             return featureIcon(aFeature);
248           case Qt::ToolTipRole:
249             return tr("Feature object");
250           case Qt::ForegroundRole:
251             if (theIndex.row() > lastHistoryRow())
252               return QBrush(Qt::lightGray);
253             else {
254               if (myActivePart)
255                 return QBrush(PASSIVE_COLOR);
256               else
257                 return QBrush(ACTIVE_COLOR);
258             }
259           default:
260             return QVariant();
261         }
262       }
263       break;
264   }
265   if (aParent.isValid() && (aParent.internalId() == PartsFolder)) {
266     return myPartModels.at(theIndex.row())->data(QModelIndex(), theRole);
267   }
268   return toSourceModelIndex(theIndex)->data(theRole);
269 }
270
271 QVariant PartSet_DocumentDataModel::headerData(int theSection, Qt::Orientation theOrient,
272                                             int theRole) const
273 {
274   return QVariant();
275 }
276
277 int PartSet_DocumentDataModel::rowCount(const QModelIndex& theParent) const
278 {
279   if (!theParent.isValid()) {
280     DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
281     // Size of external models
282     int aVal = historyOffset();
283     // Plus history size
284     aVal += aRootDoc->size(ModelAPI_Feature::group());
285     return aVal;
286   }
287   if (theParent.internalId() == PartsFolder) {
288     int aSize = myPartModels.size();
289     return myPartModels.size();
290   }
291   if (theParent.internalId() == HistoryNode) {
292     return 0;
293   }
294   QModelIndex* aParent = toSourceModelIndex(theParent);
295   const QAbstractItemModel* aModel = aParent->model();
296   if (!isSubModel(aModel))
297     return 0;
298
299   /*if (isPartSubModel(aModel)) {
300    if (aModel != myActivePart)
301    return 0;
302    }*/
303   return aModel->rowCount(*aParent);
304 }
305
306 int PartSet_DocumentDataModel::columnCount(const QModelIndex& theParent) const
307 {
308   return 1;
309 }
310
311 QModelIndex PartSet_DocumentDataModel::index(int theRow, int theColumn,
312                                           const QModelIndex& theParent) const
313 {
314   QModelIndex aIndex;
315   if (!theParent.isValid()) {
316     int aOffs = myModel->rowCount();
317     if (theRow < aOffs) {
318       aIndex = myModel->index(theRow, theColumn, theParent);
319       aIndex = createIndex(theRow, theColumn, (void*) getModelIndex(aIndex));
320     } else {
321       if (theRow == aOffs)  // Create Parts node
322         aIndex = partFolderNode(theColumn);
323       else
324         // create history node
325         aIndex = createIndex(theRow, theColumn, HistoryNode);
326     }
327   } else {
328     if (theParent.internalId() == PartsFolder) {
329       aIndex = myPartModels.at(theRow)->index(0, theColumn, QModelIndex());
330     } else {
331       QModelIndex* aParent = (QModelIndex*) theParent.internalPointer();
332       aIndex = aParent->model()->index(theRow, theColumn, (*aParent));
333     }
334     aIndex = createIndex(theRow, theColumn, (void*) getModelIndex(aIndex));
335   }
336   return aIndex;
337 }
338
339 QModelIndex PartSet_DocumentDataModel::parent(const QModelIndex& theIndex) const
340 {
341   if ((theIndex.internalId() == PartsFolder) || (theIndex.internalId() == HistoryNode))
342     return QModelIndex();
343
344   QModelIndex* aIndex = toSourceModelIndex(theIndex);
345   const QAbstractItemModel* aModel = aIndex->model();
346   if (!isSubModel(aModel))
347     return QModelIndex();
348
349   if (isPartSubModel(aModel)) {
350     if (!aModel->parent(*aIndex).isValid()) {
351       return partFolderNode(theIndex.column());
352     }
353   }
354
355   QModelIndex aIndex1 = aModel->parent(*aIndex);
356   if (aIndex1.isValid())
357     return createIndex(aIndex1.row(), aIndex1.column(), (void*) getModelIndex(aIndex1));
358   return aIndex1;
359 }
360
361 bool PartSet_DocumentDataModel::hasChildren(const QModelIndex& theParent) const
362 {
363   if (!theParent.isValid())
364     return true;
365   return rowCount(theParent) > 0;
366 }
367
368 QModelIndex* PartSet_DocumentDataModel::toSourceModelIndex(const QModelIndex& theProxy) const
369 {
370   QModelIndex* aIndexPtr = static_cast<QModelIndex*>(theProxy.internalPointer());
371   return aIndexPtr;
372 }
373
374 QModelIndex* PartSet_DocumentDataModel::findModelIndex(const QModelIndex& theIndex) const
375 {
376   QList<QModelIndex*>::const_iterator aIt;
377   for (aIt = myIndexes.constBegin(); aIt != myIndexes.constEnd(); ++aIt) {
378     QModelIndex* aIndex = (*aIt);
379     if ((*aIndex) == theIndex)
380       return aIndex;
381   }
382   return 0;
383 }
384
385 QModelIndex* PartSet_DocumentDataModel::getModelIndex(const QModelIndex& theIndex) const
386 {
387   QModelIndex* aIndexPtr = findModelIndex(theIndex);
388   if (!aIndexPtr) {
389     aIndexPtr = new QModelIndex(theIndex);
390     PartSet_DocumentDataModel* that = (PartSet_DocumentDataModel*) this;
391     that->myIndexes.append(aIndexPtr);
392   }
393   return aIndexPtr;
394 }
395
396 void PartSet_DocumentDataModel::clearModelIndexes()
397 {
398   foreach (QModelIndex* aIndex, myIndexes) 
399     delete aIndex;
400   myIndexes.clear();
401 }
402
403 void PartSet_DocumentDataModel::clearSubModels()
404 {
405   foreach (PartSet_PartModel* aPart, myPartModels) 
406     delete aPart;
407   myPartModels.clear();
408 }
409
410 ObjectPtr PartSet_DocumentDataModel::object(const QModelIndex& theIndex) const
411 {
412   if (theIndex.internalId() == PartsFolder)
413     return ObjectPtr();
414   if (theIndex.internalId() == HistoryNode) {
415     DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
416     int aOffset = historyOffset();
417     return aRootDoc->object(ModelAPI_Feature::group(), theIndex.row() - aOffset);
418   }
419   QModelIndex* aIndex = toSourceModelIndex(theIndex);
420   if (!isSubModel(aIndex->model()))
421     return ObjectPtr();
422
423   const PartSet_FeaturesModel* aModel = dynamic_cast<const PartSet_FeaturesModel*>(aIndex->model());
424   return aModel->object(*aIndex);
425 }
426
427 bool PartSet_DocumentDataModel::insertRows(int theRow, int theCount, const QModelIndex& theParent)
428 {
429   beginInsertRows(theParent, theRow, theRow + theCount - 1);
430   //endInsertRows();
431
432   // Update history
433   QModelIndex aRoot;
434   int aRow = rowCount(aRoot);
435   beginInsertRows(aRoot, aRow, aRow);
436   endInsertRows();
437
438   return true;
439 }
440
441 bool PartSet_DocumentDataModel::removeRows(int theRow, int theCount, const QModelIndex& theParent)
442 {
443   beginRemoveRows(theParent, theRow, theRow + theCount - 1);
444   endRemoveRows();
445   return true;
446 }
447
448 void PartSet_DocumentDataModel::removeSubModel(int theModelId)
449 {
450   PartSet_PartModel* aModel = myPartModels.at(theModelId);
451   QIntList aToRemove;
452   for (int i = 0; i < myIndexes.size(); i++) {
453     if (myIndexes.at(i)->model() == aModel)
454       aToRemove.append(i);
455   }
456   int aId;
457   while (aToRemove.size() > 0) {
458     aId = aToRemove.last();
459     delete myIndexes.at(aId);
460     myIndexes.removeAt(aId);
461     aToRemove.removeLast();
462   }
463   delete aModel;
464   myPartModels.removeAt(theModelId);
465 }
466
467 bool PartSet_DocumentDataModel::isSubModel(const QAbstractItemModel* theModel) const
468 {
469   if (theModel == myModel)
470     return true;
471   return isPartSubModel(theModel);
472 }
473
474 bool PartSet_DocumentDataModel::isPartSubModel(const QAbstractItemModel* theModel) const
475 {
476   return myPartModels.contains((PartSet_PartModel*) theModel);
477 }
478
479 QModelIndex PartSet_DocumentDataModel::partFolderNode(int theColumn) const
480 {
481   int aPos = myModel->rowCount(QModelIndex());
482   return createIndex(aPos, theColumn, PartsFolder);
483 }
484
485 int PartSet_DocumentDataModel::historyOffset() const
486 {
487   // Nb of rows of top model + Parts folder
488   return myModel->rowCount(QModelIndex()) + 1;
489 }
490
491 bool PartSet_DocumentDataModel::activatePart(const QModelIndex& theIndex)
492 {
493   if ((theIndex.internalId() == PartsFolder) || (theIndex.internalId() == HistoryNode))
494     return false;
495
496   QModelIndex* aIndex = toSourceModelIndex(theIndex);
497   if (!aIndex)
498     return false;
499
500   const QAbstractItemModel* aModel = aIndex->model();
501
502   if (isPartSubModel(aModel)) {
503     // if this is root node (Part item index)
504     if (!aIndex->parent().isValid()) {
505       if (myActivePart)
506         myActivePart->setItemsColor(PASSIVE_COLOR);
507
508         if (myActivePart == aModel) {
509           myActivePart = 0;
510           myActivePartIndex = QModelIndex();
511         } else {
512           myActivePart = (PartSet_PartModel*)aModel;
513           myActivePartIndex = theIndex;
514         }
515
516         if (myActivePart) {
517           myActivePart->setItemsColor(ACTIVE_COLOR);
518           myModel->setItemsColor(PASSIVE_COLOR);
519         } else
520           myModel->setItemsColor(ACTIVE_COLOR);
521         return true;
522       }
523     }
524   return false;
525 }
526
527 ResultPartPtr PartSet_DocumentDataModel::activePart() const
528 {
529   if (myActivePart)
530     return myActivePart->part();
531   return ResultPartPtr();
532 }
533
534 void PartSet_DocumentDataModel::deactivatePart()
535 {
536   if (myActivePart)
537     myActivePart->setItemsColor(PASSIVE_COLOR);
538     myActivePart = 0;
539     myActivePartIndex = QModelIndex();
540     myModel->setItemsColor(ACTIVE_COLOR);
541   }
542
543 Qt::ItemFlags PartSet_DocumentDataModel::flags(const QModelIndex& theIndex) const
544 {
545   Qt::ItemFlags aFlags = QAbstractItemModel::flags(theIndex); //Qt::ItemIsSelectable; 
546   if (object(theIndex)) {
547     aFlags |= Qt::ItemIsEditable;
548   }
549   // Disable items which are below of last history row
550   // Do not disable second column
551   //if (theIndex.row() <= lastHistoryRow() || theIndex.column() == 1) {
552   //  aFlags |= Qt::ItemIsEnabled;
553   //}
554   return aFlags;
555 }
556
557 QModelIndex PartSet_DocumentDataModel::partIndex(const ResultPartPtr& theObject) const
558 {
559   int aRow = -1;
560   PartSet_PartModel* aModel = 0;
561   foreach (PartSet_PartModel* aPartModel, myPartModels)
562   {
563     aRow++;
564     if (aPartModel->part() == theObject) {
565       aModel = aPartModel;
566       break;
567     }
568   }
569   if (aModel) {
570     return createIndex(aRow, 0, (void*) getModelIndex(aModel->index(0, 0, QModelIndex())));
571   }
572   return QModelIndex();
573 }
574
575 QModelIndex PartSet_DocumentDataModel::objectIndex(const ObjectPtr theObject) const
576 {
577   // Check that this feature belongs to root document
578   DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
579   DocumentPtr aDoc = theObject->document();
580   if (aDoc == aRootDoc) {
581     // This feature belongs to histrory or top model
582     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
583     if (aFeature) {
584       int aId;
585       int aNb = aRootDoc->size(ModelAPI_Feature::group());
586       for (aId = 0; aId < aNb; aId++) {
587         if (theObject == aRootDoc->object(ModelAPI_Feature::group(), aId))
588           break;
589       }
590       if (aId < aNb)
591         return index(aId + historyOffset(), 0, QModelIndex());
592     } else {
593       QModelIndex aIndex = myModel->objectIndex(theObject);
594       return
595           aIndex.isValid() ?
596               createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex)) :
597               QModelIndex();
598     }
599   } else {
600     PartSet_PartModel* aPartModel = 0;
601     foreach(PartSet_PartModel* aModel, myPartModels)
602     {
603       if (aModel->hasDocument(aDoc)) {
604         aPartModel = aModel;
605         break;
606       }
607     }
608     if (aPartModel) {
609       QModelIndex aIndex = aPartModel->objectIndex(theObject);
610       return
611           aIndex.isValid() ?
612               createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex)) :
613               QModelIndex();
614     }
615   }
616   return QModelIndex();
617 }
618
619
620 void PartSet_DocumentDataModel::clear()
621 {
622   clearModelIndexes();
623   clearSubModels();
624   myActivePart = 0;
625   myActivePartIndex = QModelIndex();
626   myModel->setItemsColor(ACTIVE_COLOR);
627 }
628
629 int PartSet_DocumentDataModel::lastHistoryRow() const
630 {
631   return rowCount() - 1 - myHistoryBackOffset;
632 }
633
634 void PartSet_DocumentDataModel::setLastHistoryItem(const QModelIndex& theIndex)
635 {
636   if (theIndex.internalId() == HistoryNode) {
637     myHistoryBackOffset = rowCount() - 1 - theIndex.row();
638   }
639 }
640
641 QModelIndex PartSet_DocumentDataModel::lastHistoryItem() const
642 {
643   return index(lastHistoryRow(), 1);
644 }
645
646
647 QIcon PartSet_DocumentDataModel::featureIcon(const FeaturePtr& theFeature)
648 {
649   QIcon anIcon;
650
651   std::string aKind = theFeature->getKind();
652   QString aId(aKind.c_str());
653   if (!myIcons.contains(aId))
654     return anIcon;
655
656   QString anIconString = myIcons[aId];
657
658   ModelAPI_ExecState aState = theFeature->data()->execState();
659   switch(aState) {
660     case ModelAPI_StateDone:
661     case ModelAPI_StateNothing: {
662       anIcon = QIcon(anIconString);
663     }
664     break;
665     case ModelAPI_StateMustBeUpdated: {
666       anIcon = ModuleBase_Tools::lighter(anIconString);
667     }
668     break;
669     case ModelAPI_StateExecFailed: {
670       anIcon = ModuleBase_Tools::composite(":icons/exec_state_failed.png", anIconString);
671     }
672     break;
673     case ModelAPI_StateInvalidArgument: {
674       anIcon = ModuleBase_Tools::composite(":icons/exec_state_invalid_parameters.png",
675                                            anIconString);
676     }
677     break;
678     default: break;  
679   }
680   return anIcon;  
681 }
682