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