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