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