Salome HOME
Merge remote-tracking branch 'remotes/origin/master'
[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 <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), myActivePart(0)
29 {
30   // Register in event loop
31   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
32   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
33   Events_Loop::loop()->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
34
35   // Create a top part of data tree model
36   myModel = new XGUI_TopDataModel(this);
37   myModel->setItemsColor(ACTIVE_COLOR);
38 }
39
40
41 XGUI_DocumentDataModel::~XGUI_DocumentDataModel()
42 {
43   clearModelIndexes();
44 }
45
46
47 void XGUI_DocumentDataModel::processEvent(const Events_Message* theMessage)
48 {
49   DocumentPtr aRootDoc = ModelAPI_PluginManager::get()->rootDocument();
50
51   // Created object event *******************
52   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)) {
53     const ModelAPI_ObjectUpdatedMessage* aUpdMsg = 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) aStart = 0;
76           aIndex = createIndex(aIndex.row(), aIndex.column(), (void*)getModelIndex(aIndex));
77           insertRow(aStart, aIndex);
78         }
79       } else { // if sub-objects of first level nodes
80         XGUI_PartModel* aPartModel = 0;
81         QList<XGUI_PartModel*>::const_iterator aIt;
82         for (aIt = myPartModels.constBegin(); aIt != myPartModels.constEnd(); ++aIt) {
83           if ((*aIt)->hasDocument(aDoc)) {
84             aPartModel = (*aIt);
85             break;
86           }
87         }
88         if (aPartModel) {
89           QModelIndex aIndex = aPartModel->findParent(aObject);
90           int aStart = aPartModel->rowCount(aIndex); // check this index
91           aIndex = createIndex(aIndex.row(), aIndex.column(), (void*)getModelIndex(aIndex));
92           insertRow(aStart, aIndex);
93         }
94       }
95     }
96   // Deleted object event ***********************
97   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
98     const ModelAPI_ObjectDeletedMessage* aUpdMsg = dynamic_cast<const ModelAPI_ObjectDeletedMessage*>(theMessage);
99     DocumentPtr aDoc = aUpdMsg->document();
100     std::set<std::string> aGroups = aUpdMsg->groups();
101
102     std::set<std::string>::const_iterator aIt;
103     for (aIt = aGroups.begin(); aIt != aGroups.end(); ++aIt) {
104       std::string aGroup = (*aIt);
105       if (aDoc == aRootDoc) {  // If root objects
106         if (aGroup == ModelAPI_ResultPart::group()) { // Update only Parts group
107           int aStart = myPartModels.size() - 1;
108           removeSubModel(aStart);
109           removeRow(aStart, partFolderNode());
110           if (myActivePart && (!isPartSubModel(myActivePart))) {
111             myActivePart = 0;
112             myActivePartIndex = QModelIndex();
113             myModel->setItemsColor(ACTIVE_COLOR);
114           }
115         } else { // Update top groups (other except parts
116           QModelIndex aIndex = myModel->findGroup(aGroup);
117           int aStart = myModel->rowCount(aIndex);
118           aIndex = createIndex(aIndex.row(), aIndex.column(), (void*)getModelIndex(aIndex));
119           removeRow(aStart, aIndex);
120         }
121       } else {
122         XGUI_PartModel* aPartModel = 0;
123         QList<XGUI_PartModel*>::const_iterator aIt;
124         for (aIt = myPartModels.constBegin(); aIt != myPartModels.constEnd(); ++aIt) {
125           if ((*aIt)->hasDocument(aDoc)) {
126             aPartModel = (*aIt);
127             break;
128           }
129         }
130         if (aPartModel) {
131           QModelIndex aIndex = aPartModel->findGroup(aGroup);
132           int aStart = aPartModel->rowCount(aIndex);
133           aIndex = createIndex(aIndex.row(), aIndex.column(), (void*)getModelIndex(aIndex));
134           removeRow(aStart, aIndex);
135         }
136       }
137     }
138   // Deleted object event ***********************
139   } else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)) {
140     //const ModelAPI_ObjectUpdatedMessage* aUpdMsg = dynamic_cast<const ModelAPI_ObjectUpdatedMessage*>(theMessage);
141     //ObjectPtr aFeature = aUpdMsg->feature();
142     //DocumentPtr aDoc = aFeature->document();
143     
144     // TODO: Identify the necessary index by the modified feature
145     QModelIndex aIndex;
146     emit dataChanged(aIndex, aIndex);
147
148   // Reset whole tree **************************
149   } else {  
150     rebuildDataTree();
151   }
152 }
153
154 void XGUI_DocumentDataModel::rebuildDataTree()
155 {
156   DocumentPtr aRootDoc = ModelAPI_PluginManager::get()->rootDocument();
157
158   beginResetModel();
159   clearModelIndexes();
160
161   int aNbParts = aRootDoc->size(ModelAPI_ResultPart::group());
162   if (myPartModels.size() != aNbParts) { // resize internal models
163     while (myPartModels.size() > aNbParts) {
164       delete myPartModels.last();
165       myPartModels.removeLast();
166     }
167     while (myPartModels.size() < aNbParts) {
168       myPartModels.append(new XGUI_PartDataModel(this));
169     }
170     for (int i = 0; i < myPartModels.size(); i++)
171       myPartModels.at(i)->setPartId(i);
172   }
173   endResetModel();
174 }
175
176 QVariant XGUI_DocumentDataModel::data(const QModelIndex& theIndex, int theRole) const
177 {
178   if (!theIndex.isValid())
179     return QVariant();
180   switch (theIndex.internalId()) {
181   case PartsFolder:
182     switch (theRole) {
183     case Qt::DisplayRole:
184       return tr("Parts") + QString(" (%1)").arg(rowCount(theIndex));
185     case Qt::DecorationRole:
186       return QIcon(":pictures/constr_folder.png");
187     case Qt::ToolTipRole:
188       return tr("Parts folder");
189     case Qt::ForegroundRole:
190       if (myActivePart)
191         return QBrush(PASSIVE_COLOR);
192       else
193         return QBrush(ACTIVE_COLOR);
194     default:
195       return QVariant();
196     }
197     break;
198   case HistoryNode:
199     {
200       int aOffset = historyOffset();
201       DocumentPtr aRootDoc = ModelAPI_PluginManager::get()->rootDocument();
202       ObjectPtr aObj = aRootDoc->object(ModelAPI_Feature::group(), theIndex.row() - aOffset);
203       FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(aObj);
204       if (!aFeature)
205         return QVariant();
206       switch (theRole) {
207       case Qt::DisplayRole:
208         if (aFeature)
209           return aFeature->data()->name().c_str();
210         else 
211           return QVariant();
212       case Qt::DecorationRole:
213         return QIcon(XGUI_Workshop::featureIcon(aFeature->getKind()));
214       case Qt::ToolTipRole:
215         return tr("Feature object");
216       case Qt::ForegroundRole:
217         if (myActivePart)
218           return QBrush(PASSIVE_COLOR);
219         else
220           return QBrush(ACTIVE_COLOR);
221       default:
222         return QVariant();
223       }
224     }
225     break;
226   }
227   QModelIndex aParent = theIndex.parent();
228   if (aParent.isValid() && (aParent.internalId() == PartsFolder)) {
229     return myPartModels.at(theIndex.row())->data(QModelIndex(), theRole);
230   }
231   return toSourceModelIndex(theIndex)->data(theRole);
232 }
233
234
235 QVariant XGUI_DocumentDataModel::headerData(int theSection, Qt::Orientation theOrient, int theRole) const
236 {
237   return QVariant();
238 }
239
240 int XGUI_DocumentDataModel::rowCount(const QModelIndex& theParent) const
241 {
242   if (!theParent.isValid()) {
243     DocumentPtr aRootDoc = ModelAPI_PluginManager::get()->rootDocument();
244     // Size of external models
245     int aVal = historyOffset();
246     // Plus history size
247     aVal += aRootDoc->size(ModelAPI_Feature::group());
248     return aVal;
249   }
250   if (theParent.internalId() == PartsFolder) {
251     int aSize = myPartModels.size();
252     return myPartModels.size();
253   }
254   if (theParent.internalId() == HistoryNode) {
255     return 0;
256   }
257   QModelIndex* aParent = toSourceModelIndex(theParent);
258   const QAbstractItemModel* aModel = aParent->model();
259   if (!isSubModel(aModel)) 
260     return 0;
261
262   /*if (isPartSubModel(aModel)) {
263     if (aModel != myActivePart)
264       return 0;
265   }*/
266   return aModel->rowCount(*aParent);
267 }
268
269 int XGUI_DocumentDataModel::columnCount(const QModelIndex& theParent) const
270 {
271   return 1;
272 }
273
274 QModelIndex XGUI_DocumentDataModel::index(int theRow, int theColumn, const QModelIndex& theParent) const
275 {
276   QModelIndex aIndex;
277   if (!theParent.isValid()) {
278     int aOffs = myModel->rowCount();
279     if (theRow < aOffs) {
280       aIndex = myModel->index(theRow, theColumn, theParent);
281       aIndex = createIndex(theRow, theColumn, (void*)getModelIndex(aIndex));
282     } else {
283       if (theRow == aOffs)  // Create Parts node
284         aIndex = partFolderNode();
285       else // create history node
286         aIndex = createIndex(theRow, theColumn, HistoryNode);
287     }
288   } else {
289     if (theParent.internalId() == PartsFolder) {
290       aIndex = myPartModels.at(theRow)->index(0, theColumn, QModelIndex());
291     } else {
292       QModelIndex* aParent = (QModelIndex*)theParent.internalPointer();
293       aIndex = aParent->model()->index(theRow, theColumn, (*aParent));
294     }
295     aIndex = createIndex(theRow, theColumn, (void*)getModelIndex(aIndex));
296   }
297   return aIndex;
298 }
299
300
301 QModelIndex XGUI_DocumentDataModel::parent(const QModelIndex& theIndex) const
302 {
303   if ((theIndex.internalId() == PartsFolder) || (theIndex.internalId() == HistoryNode))
304     return QModelIndex();
305
306   QModelIndex* aIndex = toSourceModelIndex(theIndex);
307   const QAbstractItemModel* aModel = aIndex->model();
308   if (!isSubModel(aModel)) 
309     return QModelIndex();
310
311   if (isPartSubModel(aModel)) {
312     if (!aModel->parent(*aIndex).isValid()) {
313       return partFolderNode();
314     }
315   }
316
317   QModelIndex aIndex1 = aModel->parent(*aIndex);
318   if (aIndex1.isValid())
319     return createIndex(aIndex1.row(), aIndex1.column(), (void*)getModelIndex(aIndex1));
320   return aIndex1;
321 }
322
323
324 bool XGUI_DocumentDataModel::hasChildren(const QModelIndex& theParent) const
325 {
326   if (!theParent.isValid())
327     return true;
328   return rowCount(theParent) > 0;
329 }
330
331
332 QModelIndex* XGUI_DocumentDataModel::toSourceModelIndex(const QModelIndex& theProxy) const
333 {
334   QModelIndex* aIndexPtr = static_cast<QModelIndex*>(theProxy.internalPointer());
335   return aIndexPtr;
336 }
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_PluginManager::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
408 void XGUI_DocumentDataModel::removeSubModel(int theModelId)
409 {
410   XGUI_PartModel* aModel = myPartModels.at(theModelId);
411   QIntList aToRemove;
412   for (int i = 0; i < myIndexes.size(); i++) {
413     if (myIndexes.at(i)->model() == aModel)
414       aToRemove.append(i);
415   }
416   int aId;
417   while(aToRemove.size() > 0) {
418     aId = aToRemove.last();
419     delete myIndexes.at(aId);
420     myIndexes.removeAt(aId);
421     aToRemove.removeLast();
422   }
423   delete aModel;
424   myPartModels.removeAt(theModelId);
425 }
426
427 bool XGUI_DocumentDataModel::isSubModel(const QAbstractItemModel* theModel) const
428 {
429   if (theModel == myModel)
430     return true;
431   return isPartSubModel(theModel);
432 }
433
434 bool XGUI_DocumentDataModel::isPartSubModel(const QAbstractItemModel* theModel) const
435 {
436   return myPartModels.contains((XGUI_PartModel*)theModel);
437 }
438
439 QModelIndex XGUI_DocumentDataModel::partFolderNode() const
440 {
441   int aPos = myModel->rowCount(QModelIndex());
442   return createIndex(aPos, columnCount() - 1, PartsFolder);
443 }
444
445 int XGUI_DocumentDataModel::historyOffset() const
446 {
447   // Nb of rows of top model + Parts folder
448   return myModel->rowCount(QModelIndex()) + 1;
449 }
450
451 bool XGUI_DocumentDataModel::activatedIndex(const QModelIndex& theIndex)
452 {
453   if ((theIndex.internalId() == PartsFolder) || (theIndex.internalId() == HistoryNode))
454     return false;
455
456   QModelIndex* aIndex = toSourceModelIndex(theIndex);
457   if (!aIndex)
458     return false;
459
460   const QAbstractItemModel* aModel = aIndex->model();
461
462   if (isPartSubModel(aModel)) {
463     // if this is root node (Part item index)
464     if (!aIndex->parent().isValid()) {
465       if (myActivePart) 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     aRow++;
517     if (aPartModel->part() == theObject) {
518       aModel = aPartModel;
519       break;
520     }
521   }
522   if (aModel) {
523     return createIndex(aRow, 0, (void*)getModelIndex(aModel->index(0, 0, QModelIndex())));
524   }
525   return QModelIndex();
526 }
527
528 QModelIndex XGUI_DocumentDataModel::objectIndex(const ObjectPtr theObject) const
529 {
530   // Check that this feature belongs to root document
531   DocumentPtr aRootDoc = ModelAPI_PluginManager::get()->rootDocument();
532   DocumentPtr aDoc = theObject->document();
533   if (aDoc == aRootDoc) {
534     // This feature belongs to histrory or top model
535     FeaturePtr aFeature = boost::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
536     if (aFeature) {
537       int aId;
538       int aNb = aRootDoc->size(ModelAPI_Feature::group());
539       for (aId = 0; aId < aNb; aId++) {
540         if (theObject == aRootDoc->object(ModelAPI_Feature::group(), aId))
541           break;
542       }
543       Q_ASSERT(aId < aNb);
544       return index(aId + historyOffset(), 0, QModelIndex());
545     } else {
546       QModelIndex aIndex = myModel->objectIndex(theObject);
547       return aIndex.isValid()? 
548         createIndex(aIndex.row(), aIndex.column(), (void*)getModelIndex(aIndex)) :
549         QModelIndex();
550     }
551   } else {
552     XGUI_PartModel* aPartModel = 0;
553     foreach(XGUI_PartModel* aModel, myPartModels) {
554       if (aModel->hasDocument(aDoc)) {
555         aPartModel = aModel;
556         break;
557       }
558     }
559     if (aPartModel) {
560       QModelIndex aIndex = aPartModel->objectIndex(theObject);
561       return aIndex.isValid()? 
562         createIndex(aIndex.row(), aIndex.column(), (void*)getModelIndex(aIndex)) :
563         QModelIndex();
564     }
565   }
566   return QModelIndex();
567 }