Salome HOME
1ed348e1c3d0d3c2347661c529b9582b96de8122
[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
37   // Create a top part of data tree model
38   myModel = new XGUI_TopDataModel(this);
39   myModel->setItemsColor(ACTIVE_COLOR);
40 }
41
42 XGUI_DocumentDataModel::~XGUI_DocumentDataModel()
43 {
44   clearModelIndexes();
45   clearSubModels();
46 }
47
48 void XGUI_DocumentDataModel::processEvent(const std::shared_ptr<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     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
55         std::dynamic_pointer_cast<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 = std::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         foreach (XGUI_PartModel* aPart, myPartModels) {
85           if (aPart->hasDocument(aDoc)) {
86             aPartModel = aPart;
87             break;
88           }
89         }
90         if (aPartModel) {
91           QModelIndex aIndex = aPartModel->findParent(aObject);
92           int aStart = aPartModel->rowCount(aIndex);  // check this index
93           aIndex = createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex));
94           insertRow(aStart, aIndex);
95         }
96       }
97     }
98     // Deleted object event ***********************
99   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
100     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aUpdMsg =
101         std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
102     DocumentPtr aDoc = aUpdMsg->document();
103     std::set<std::string> aGroups = aUpdMsg->groups();
104
105     std::set<std::string>::const_iterator aIt;
106     for (aIt = aGroups.begin(); aIt != aGroups.end(); ++aIt) {
107       std::string aGroup = (*aIt);
108       if (aDoc == aRootDoc) {  // If root objects
109         if (aGroup == ModelAPI_ResultPart::group()) {  // Update only Parts group
110           int aStart = myPartModels.size() - 1;
111           if (aStart >= 0) {// MPV: this could be reproduced on close
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           }
120         } else {  // Update top groups (other except parts
121           QModelIndex aIndex = myModel->findGroup(aGroup);
122           int aStart = myModel->rowCount(aIndex);
123           aIndex = createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex));
124           removeRow(aStart, aIndex);
125         }
126       } else {
127         XGUI_PartModel* aPartModel = 0;
128         foreach (XGUI_PartModel* aPart, myPartModels) {
129           if (aPart->hasDocument(aDoc)) {
130             aPartModel = aPart;
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     //std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg = std::dynamic_pointer_cast<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 = std::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   foreach (QModelIndex* aIndex, myIndexes) 
366     delete aIndex;
367   myIndexes.clear();
368 }
369
370 void XGUI_DocumentDataModel::clearSubModels()
371 {
372   foreach (XGUI_PartModel* aPart, myPartModels) 
373     delete aPart;
374   myPartModels.clear();
375 }
376
377 ObjectPtr XGUI_DocumentDataModel::object(const QModelIndex& theIndex) const
378 {
379   if (theIndex.internalId() == PartsFolder)
380     return ObjectPtr();
381   if (theIndex.internalId() == HistoryNode) {
382     DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
383     int aOffset = historyOffset();
384     return aRootDoc->object(ModelAPI_Feature::group(), theIndex.row() - aOffset);
385   }
386   QModelIndex* aIndex = toSourceModelIndex(theIndex);
387   if (!isSubModel(aIndex->model()))
388     return ObjectPtr();
389
390   const XGUI_FeaturesModel* aModel = dynamic_cast<const XGUI_FeaturesModel*>(aIndex->model());
391   return aModel->object(*aIndex);
392 }
393
394 bool XGUI_DocumentDataModel::insertRows(int theRow, int theCount, const QModelIndex& theParent)
395 {
396   beginInsertRows(theParent, theRow, theRow + theCount - 1);
397   //endInsertRows();
398
399   // Update history
400   QModelIndex aRoot;
401   int aRow = rowCount(aRoot);
402   beginInsertRows(aRoot, aRow, aRow);
403   endInsertRows();
404
405   return true;
406 }
407
408 bool XGUI_DocumentDataModel::removeRows(int theRow, int theCount, const QModelIndex& theParent)
409 {
410   beginRemoveRows(theParent, theRow, theRow + theCount - 1);
411   endRemoveRows();
412   return true;
413 }
414
415 void XGUI_DocumentDataModel::removeSubModel(int theModelId)
416 {
417   XGUI_PartModel* aModel = myPartModels.at(theModelId);
418   QIntList aToRemove;
419   for (int i = 0; i < myIndexes.size(); i++) {
420     if (myIndexes.at(i)->model() == aModel)
421       aToRemove.append(i);
422   }
423   int aId;
424   while (aToRemove.size() > 0) {
425     aId = aToRemove.last();
426     delete myIndexes.at(aId);
427     myIndexes.removeAt(aId);
428     aToRemove.removeLast();
429   }
430   delete aModel;
431   myPartModels.removeAt(theModelId);
432 }
433
434 bool XGUI_DocumentDataModel::isSubModel(const QAbstractItemModel* theModel) const
435 {
436   if (theModel == myModel)
437     return true;
438   return isPartSubModel(theModel);
439 }
440
441 bool XGUI_DocumentDataModel::isPartSubModel(const QAbstractItemModel* theModel) const
442 {
443   return myPartModels.contains((XGUI_PartModel*) theModel);
444 }
445
446 QModelIndex XGUI_DocumentDataModel::partFolderNode() const
447 {
448   int aPos = myModel->rowCount(QModelIndex());
449   return createIndex(aPos, columnCount() - 1, PartsFolder);
450 }
451
452 int XGUI_DocumentDataModel::historyOffset() const
453 {
454   // Nb of rows of top model + Parts folder
455   return myModel->rowCount(QModelIndex()) + 1;
456 }
457
458 bool XGUI_DocumentDataModel::activatedIndex(const QModelIndex& theIndex)
459 {
460   if ((theIndex.internalId() == PartsFolder) || (theIndex.internalId() == HistoryNode))
461     return false;
462
463   QModelIndex* aIndex = toSourceModelIndex(theIndex);
464   if (!aIndex)
465     return false;
466
467   const QAbstractItemModel* aModel = aIndex->model();
468
469   if (isPartSubModel(aModel)) {
470     // if this is root node (Part item index)
471     if (!aIndex->parent().isValid()) {
472       if (myActivePart)
473         myActivePart->setItemsColor(PASSIVE_COLOR);
474
475         if (myActivePart == aModel) {
476           myActivePart = 0;
477           myActivePartIndex = QModelIndex();
478         } else {
479           myActivePart = (XGUI_PartModel*)aModel;
480           myActivePartIndex = theIndex;
481         }
482
483         if (myActivePart) {
484           myActivePart->setItemsColor(ACTIVE_COLOR);
485           myModel->setItemsColor(PASSIVE_COLOR);
486         } else
487           myModel->setItemsColor(ACTIVE_COLOR);
488         return true;
489       }
490     }
491   return false;
492 }
493
494 ResultPartPtr XGUI_DocumentDataModel::activePart() const
495 {
496   if (myActivePart)
497     return myActivePart->part();
498   return ResultPartPtr();
499 }
500
501 void XGUI_DocumentDataModel::deactivatePart()
502 {
503   if (myActivePart)
504     myActivePart->setItemsColor(PASSIVE_COLOR);
505     myActivePart = 0;
506     myActivePartIndex = QModelIndex();
507     myModel->setItemsColor(ACTIVE_COLOR);
508   }
509
510 Qt::ItemFlags XGUI_DocumentDataModel::flags(const QModelIndex& theIndex) const
511 {
512   Qt::ItemFlags aFlags = QAbstractItemModel::flags(theIndex);
513   if (object(theIndex)) {
514     aFlags |= Qt::ItemIsEditable;
515   }
516   return aFlags;
517 }
518
519 QModelIndex XGUI_DocumentDataModel::partIndex(const ResultPartPtr& theObject) const
520 {
521   int aRow = -1;
522   XGUI_PartModel* aModel = 0;
523   foreach (XGUI_PartModel* aPartModel, myPartModels)
524   {
525     aRow++;
526     if (aPartModel->part() == theObject) {
527       aModel = aPartModel;
528       break;
529     }
530   }
531   if (aModel) {
532     return createIndex(aRow, 0, (void*) getModelIndex(aModel->index(0, 0, QModelIndex())));
533   }
534   return QModelIndex();
535 }
536
537 QModelIndex XGUI_DocumentDataModel::objectIndex(const ObjectPtr theObject) const
538 {
539   // Check that this feature belongs to root document
540   DocumentPtr aRootDoc = ModelAPI_Session::get()->moduleDocument();
541   DocumentPtr aDoc = theObject->document();
542   if (aDoc == aRootDoc) {
543     // This feature belongs to histrory or top model
544     FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
545     if (aFeature) {
546       int aId;
547       int aNb = aRootDoc->size(ModelAPI_Feature::group());
548       for (aId = 0; aId < aNb; aId++) {
549         if (theObject == aRootDoc->object(ModelAPI_Feature::group(), aId))
550           break;
551       }
552       if (aId < aNb)
553         return index(aId + historyOffset(), 0, QModelIndex());
554     } else {
555       QModelIndex aIndex = myModel->objectIndex(theObject);
556       return
557           aIndex.isValid() ?
558               createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex)) :
559               QModelIndex();
560     }
561   } else {
562     XGUI_PartModel* aPartModel = 0;
563     foreach(XGUI_PartModel* aModel, myPartModels)
564     {
565       if (aModel->hasDocument(aDoc)) {
566         aPartModel = aModel;
567         break;
568       }
569     }
570     if (aPartModel) {
571       QModelIndex aIndex = aPartModel->objectIndex(theObject);
572       return
573           aIndex.isValid() ?
574               createIndex(aIndex.row(), aIndex.column(), (void*) getModelIndex(aIndex)) :
575               QModelIndex();
576     }
577   }
578   return QModelIndex();
579 }
580
581
582 void XGUI_DocumentDataModel::clear()
583 {
584   clearModelIndexes();
585   clearSubModels();
586   myActivePart = 0;
587   myActivePartIndex = QModelIndex();
588   myModel->setItemsColor(ACTIVE_COLOR);
589 }