Salome HOME
e1a9ba2933eebb4c1faae09b7ad9b689a8e9426b
[modules/shaper.git] / src / XGUI / XGUI_DataModel.cpp
1 // Copyright (C) 2014-2022  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "XGUI_DataModel.h"
21 #include "XGUI_ObjectsBrowser.h"
22 #include "XGUI_Workshop.h"
23
24 #include <ModuleBase_IconFactory.h>
25 #include <ModuleBase_ITreeNode.h>
26
27 #include <ModelAPI_Session.h>
28 #include <ModelAPI_ResultField.h>
29 #include <ModelAPI_Tools.h>
30 #include <ModelAPI_CompositeFeature.h>
31
32 #include <Config_FeatureMessage.h>
33
34 #include <Events_Loop.h>
35
36 #include <QMimeData>
37 #include <QMessageBox>
38
39 #include <cassert>
40
41 #ifdef _MSC_VER
42 #pragma warning(disable: 4100)
43 #endif
44
45 // Constructor *************************************************
46 XGUI_DataModel::XGUI_DataModel(QObject* theParent) : QAbstractItemModel(theParent)//,
47   //myIsEventsProcessingBlocked(false)
48 {
49   XGUI_ObjectsBrowser* aOB = qobject_cast<XGUI_ObjectsBrowser*>(theParent);
50   myWorkshop = aOB->workshop();
51
52   Events_Loop* aLoop = Events_Loop::loop();
53   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_CREATED));
54   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_DELETED));
55   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_UPDATED));
56   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_ORDER_UPDATED));
57   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_DOCUMENT_CHANGED));
58   aLoop->registerListener(this, Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY));
59 }
60
61 XGUI_DataModel::~XGUI_DataModel()
62 {
63   clear();
64 }
65
66 //******************************************************
67 void XGUI_DataModel::processEvent(const std::shared_ptr<Events_Message>& theMessage)
68 {
69   if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_CREATED)) {
70     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
71       std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
72     std::set<ObjectPtr> aObjects = aUpdMsg->objects();
73     QObjectPtrList aCreated;
74     std::set<ObjectPtr>::const_iterator aIt;
75     for (aIt = aObjects.cbegin(); aIt != aObjects.cend(); aIt++) {
76       if ((*aIt)->isInHistory())
77         aCreated.append(*aIt);
78       if ((*aIt)->groupName() == ModelAPI_ResultPart::group()) {
79         emit beforeTreeRebuild();
80         myRoot->update();
81         rebuildDataTree();
82         emit treeRebuilt();
83         return;
84       }
85     }
86     if (aCreated.length() == 0)
87       return;
88
89     emit beforeTreeRebuild();
90     QTreeNodesList aNodes = myRoot->objectCreated(aCreated);
91     ModuleBase_ITreeNode* aParent;
92     int aRow = 0;
93     QModelIndex aParentIndex1, aParentIndex2;
94     ObjectPtr aObj;
95     bool aRebuildAll = false;
96
97     foreach(ModuleBase_ITreeNode* aNode, aNodes) {
98       aObj = aNode->object();
99       aParent = aNode->parent();
100       if (aObj.get() && (aObj->groupName() == ModelAPI_Folder::group())) {
101         aParent->update();
102         aRebuildAll = true;
103       }
104       else {
105         aRow = aParent->nodeRow(aNode);
106         aParentIndex1 = getParentIndex(aNode, 0);
107         aParentIndex2 = getParentIndex(aNode, 2);
108         insertRows(aRow, 1, aParentIndex1);
109         dataChanged(aParentIndex1, aParentIndex2);
110       }
111     }
112     if (aRebuildAll)
113       rebuildDataTree();
114
115     emit treeRebuilt();
116   }
117   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_DELETED)) {
118     std::shared_ptr<ModelAPI_ObjectDeletedMessage> aUpdMsg =
119       std::dynamic_pointer_cast<ModelAPI_ObjectDeletedMessage>(theMessage);
120     const std::list<std::pair<std::shared_ptr<ModelAPI_Document>, std::string>>& aMsgGroups =
121       aUpdMsg->groups();
122     QTreeNodesList aList;
123     std::list<std::pair<std::shared_ptr<ModelAPI_Document>, std::string>>::const_iterator aIt;
124     emit beforeTreeRebuild();
125     for (aIt = aMsgGroups.cbegin(); aIt != aMsgGroups.cend(); aIt++) {
126       aList.append(myRoot->objectsDeleted(aIt->first, aIt->second.c_str()));
127     }
128     // Remove obsolete nodes
129     QTreeNodesList aRemaining;
130     foreach(ModuleBase_ITreeNode* aNode, aList) {
131       if (myRoot->hasSubNode(aNode))
132         aRemaining.append(aNode);
133     }
134     // Update remaining nodes
135     foreach(ModuleBase_ITreeNode* aNode, aRemaining) {
136       if (aNode->parent())
137         aNode->parent()->update();
138     }
139     rebuildDataTree();
140     emit treeRebuilt();
141   }
142   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_UPDATED)) {
143     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
144       std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
145     std::set<ObjectPtr> aObjects = aUpdMsg->objects();
146
147     QObjectPtrList aCreated;
148     std::set<ObjectPtr>::const_iterator aIt;
149     bool aRebuildAll = false;
150     emit beforeTreeRebuild();
151     for (aIt = aObjects.cbegin(); aIt != aObjects.cend(); aIt++) {
152       ObjectPtr aObj = (*aIt);
153       if (!aObj->isInHistory())
154         continue;
155
156       if (aObj->data()->isValid()) {
157         if (aObj->groupName() == ModelAPI_Folder::group()) {
158           aRebuildAll = true;
159           break;
160         }
161         aCreated.append(*aIt);
162       }
163     }
164     if (aRebuildAll) {
165       myRoot->update();
166     }
167     else {
168       QSet<ModuleBase_ITreeNode*> aParents;
169       foreach(ObjectPtr aObj, aCreated) {
170         ModuleBase_ITreeNode* aNode = myRoot->subNode(aObj);
171         if (aNode) {
172           if (aNode->parent()) {
173             if (aNode->parent() == myRoot) {
174               aParents.clear();
175               aParents.insert(myRoot);
176               break;
177             }
178             else {
179               aNode = aNode->parent();
180             }
181           }
182           aParents.insert(aNode);
183         }
184       }
185       foreach(ModuleBase_ITreeNode* aNode, aParents) {
186         aNode->update();
187       }
188     }
189     rebuildDataTree();
190     emit treeRebuilt();
191   }
192   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_ORDER_UPDATED)) {
193     std::shared_ptr<ModelAPI_OrderUpdatedMessage> aUpdMsg =
194       std::dynamic_pointer_cast<ModelAPI_OrderUpdatedMessage>(theMessage);
195     if (aUpdMsg->reordered().get()) {
196       DocumentPtr aDoc = aUpdMsg->reordered()->document();
197       std::string aGroup = aUpdMsg->reordered()->group();
198       ModuleBase_ITreeNode* aNode = myRoot->findParent(aDoc, aGroup.c_str());
199       if (aNode) {
200         emit beforeTreeRebuild();
201         aNode->update();
202         rebuildDataTree();
203         emit treeRebuilt();
204       }
205     }
206   }
207   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_DOCUMENT_CHANGED)) {
208     if (ModelAPI_Session::get()->hasModuleDocument()) {
209       DocumentPtr aDoc = ModelAPI_Session::get()->activeDocument();
210       ModuleBase_ITreeNode* aRoot = myRoot->findRoot(aDoc);
211       if (aRoot) {
212         updateSubTree(aRoot);
213       }
214     }
215   }
216   else if (theMessage->eventID() == Events_Loop::loop()->eventByName(EVENT_OBJECT_TO_REDISPLAY)) {
217     std::shared_ptr<ModelAPI_ObjectUpdatedMessage> aUpdMsg =
218       std::dynamic_pointer_cast<ModelAPI_ObjectUpdatedMessage>(theMessage);
219     std::set<ObjectPtr> aObjects = aUpdMsg->objects();
220
221     QObjectPtrList aCreated;
222     std::set<ObjectPtr>::const_iterator aIt;
223     for (aIt = aObjects.cbegin(); aIt != aObjects.cend(); aIt++) {
224       ObjectPtr aObj = (*aIt);
225       if (aObj->groupName() == ModelAPI_ResultField::group()) {
226         aCreated.append(aObj);
227       }
228     }
229     if (aCreated.length() == 0)
230       return;
231     emit beforeTreeRebuild();
232     foreach(ObjectPtr aObj, aCreated) {
233       ModuleBase_ITreeNode* aNode = myRoot->subNode(aObj);
234       if (aNode) {
235         int aOldNb = aNode->childrenCount();
236         aNode->update();
237         int aNewNb = aNode->childrenCount();
238
239         QModelIndex aFirstIdx = getIndex(aNode, 0);
240         QModelIndex aLastIdx = getIndex(aNode, 2);
241
242         if (aNewNb > aOldNb) {
243           insertRows(aOldNb - 1, aNewNb - aOldNb, aFirstIdx);
244         }
245         else if (aNewNb < aOldNb) {
246           if (aNewNb)
247             removeRows(aNewNb - 1, aOldNb - aNewNb, aFirstIdx);
248           else if (aOldNb)
249             removeRows(0, aOldNb, aFirstIdx);
250         }
251         dataChanged(aFirstIdx, aLastIdx);
252       }
253     }
254     emit treeRebuilt();
255   }
256 }
257
258 //******************************************************
259 void XGUI_DataModel::clear()
260 {
261   beginResetModel();
262   endResetModel();
263 }
264
265 //******************************************************
266 void XGUI_DataModel::rebuildDataTree()
267 {
268   beginResetModel();
269   endResetModel();
270 }
271
272 //******************************************************
273 ObjectPtr XGUI_DataModel::object(const QModelIndex& theIndex) const
274 {
275   if (theIndex.isValid()) {
276     ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
277     return aNode->object();
278   }
279   return ObjectPtr();
280 }
281
282 //******************************************************
283 QModelIndex XGUI_DataModel::objectIndex(const ObjectPtr theObject, int theColumn) const
284 {
285   ModuleBase_ITreeNode* aNode = myRoot->subNode(theObject);
286   if (aNode) {
287     return getIndex(aNode, theColumn);
288   }
289   return QModelIndex();
290 }
291
292 //******************************************************
293 QVariant XGUI_DataModel::data(const QModelIndex& theIndex, int theRole) const
294 {
295   if (theIndex.isValid()) {
296     ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
297     return aNode->data(theIndex.column(), theRole);
298   }
299   return QVariant();
300 }
301
302 //******************************************************
303 QVariant XGUI_DataModel::headerData(int theSection, Qt::Orientation theOrient, int theRole) const
304 {
305   return QVariant();
306 }
307
308 //******************************************************
309 int XGUI_DataModel::rowCount(const QModelIndex& theParent) const
310 {
311   ModuleBase_ITreeNode* aParentNode = (theParent.isValid()) ?
312     (ModuleBase_ITreeNode*)theParent.internalPointer() : myRoot;
313   return aParentNode->childrenCount();
314 }
315
316 //******************************************************
317 int XGUI_DataModel::columnCount(const QModelIndex& theParent) const
318 {
319   return 3;
320 }
321
322 //******************************************************
323 QModelIndex XGUI_DataModel::index(int theRow, int theColumn, const QModelIndex &theParent) const
324 {
325   ModuleBase_ITreeNode* aParentNode = (theParent.isValid()) ?
326     (ModuleBase_ITreeNode*)theParent.internalPointer() : myRoot;
327   ModuleBase_ITreeNode* aSubNode = aParentNode->subNode(theRow);
328   assert(aSubNode);
329   return createIndex(theRow, theColumn, aSubNode);
330 }
331
332 //******************************************************
333 QModelIndex XGUI_DataModel::parent(const QModelIndex& theIndex) const
334 {
335   if (theIndex.isValid()) {
336     ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
337     return getParentIndex(aNode, 1);
338   }
339   return QModelIndex();
340 }
341
342 //******************************************************
343 bool XGUI_DataModel::hasChildren(const QModelIndex& theParent) const
344 {
345   ModuleBase_ITreeNode* aParentNode = (theParent.isValid()) ?
346     (ModuleBase_ITreeNode*)theParent.internalPointer() : myRoot;
347   return aParentNode->childrenCount() > 0;
348 }
349
350 //******************************************************
351 bool XGUI_DataModel::insertRows(int theRow, int theCount, const QModelIndex& theParent)
352 {
353   beginInsertRows(theParent, theRow, theRow + theCount - 1);
354   endInsertRows();
355   return true;
356 }
357
358 //******************************************************
359 bool XGUI_DataModel::removeRows(int theRow, int theCount, const QModelIndex& theParent)
360 {
361   beginRemoveRows(theParent, theRow, theRow + theCount - 1);
362   endRemoveRows();
363   return true;
364 }
365
366 //******************************************************
367 Qt::ItemFlags XGUI_DataModel::flags(const QModelIndex& theIndex) const
368 {
369   if (theIndex.isValid()) {
370     ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
371     Qt::ItemFlags aResultFlags = aNode->flags(theIndex.column());
372     // Drag and drop of Part features only if:
373     // - PartSet is active
374     // - active Part feature of PartSet is dragged
375     // - finally if it does not break dependencies between features (but here only drag possibility is checked)
376     SessionPtr aSession = ModelAPI_Session::get();
377     if (aSession->hasModuleDocument() && aSession->moduleDocument() == aSession->activeDocument()) {
378
379       ObjectPtr aNodeObj = aNode->object();
380       if (aNodeObj.get() && aNodeObj->groupName() == ModelAPI_Feature::group())
381       {
382         FeaturePtr aNodeFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aNodeObj);
383         if (aNodeFeature.get() && aNodeFeature->getKind() == "Part" && !aNodeFeature->isDisabled())
384           aResultFlags |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
385       }
386     }
387     return aResultFlags;
388   }
389   return Qt::ItemIsDropEnabled | Qt::ItemFlags();
390 }
391
392 bool XGUI_DataModel::canDropMimeData(const QMimeData *theData, Qt::DropAction theAction,
393   int theRow, int theColumn, const QModelIndex &theParent) const
394 {
395   if (theParent.isValid())
396     return false;
397   ModuleBase_ITreeNode* aSubNode = myRoot->subNode(theRow);
398   if ((aSubNode && aSubNode->object() && aSubNode->object()->groupName() == ModelAPI_Feature::group())
399       || theRow == myRoot->childrenCount()) // into the end of a list of features
400   {
401     return true;
402   }
403
404   return false; // in other cases drop is forbidden
405 }
406
407 QMimeData* XGUI_DataModel::mimeData(const QModelIndexList& theIndexes) const
408 {
409   std::set<int> aRows; // to avoid duplication of rows and for sorting the indices
410   foreach (QModelIndex anIndex, theIndexes) {
411     if (anIndex.isValid() && anIndex.internalPointer())
412       aRows.insert(anIndex.row());
413   }
414   QByteArray anEncodedData;
415   QDataStream aStream(&anEncodedData, QIODevice::WriteOnly);
416   for(std::set<int>::iterator aRIter = aRows.begin(); aRIter != aRows.end(); aRIter++)
417     aStream << *aRIter;
418
419   QMimeData* aMimeData = new QMimeData();
420   aMimeData->setData("xgui/moved.rows", anEncodedData);
421   return aMimeData;
422 }
423
424 bool XGUI_DataModel::dropMimeData(const QMimeData *theData, Qt::DropAction theAction,
425   int theRow, int theColumn, const QModelIndex &theParent)
426 {
427   FeaturePtr aDropAfter; // after this feature it is dropped, NULL if drop the the first place
428   if (theRow > 0)
429   {
430     ModuleBase_ITreeNode* aNode = myRoot->subNode(theRow - 1);
431     if (aNode && aNode->object() && aNode->object()->groupName() == ModelAPI_Feature::group())
432       aDropAfter = std::dynamic_pointer_cast<ModelAPI_Feature>(aNode->object());
433   }
434   SessionPtr aSession = ModelAPI_Session::get();
435   if (aDropAfter.get()) // move to the upper enabled feature
436   {
437     while (aDropAfter.get() && (aDropAfter->isDisabled() || !aDropAfter->isInHistory()))
438       aDropAfter = aDropAfter->document()->nextFeature(aDropAfter, true);
439   }
440   else { // move after invisible items, not the first (which is coordinate system by default)
441     std::list<FeaturePtr> allFeatures = aSession->get()->moduleDocument()->allFeatures();
442     std::list<FeaturePtr>::iterator aFeature = allFeatures.begin();
443     for(; aFeature != allFeatures.end(); aFeature++)
444     {
445       if ((*aFeature)->isInHistory())
446         break;
447       aDropAfter = *aFeature;
448     }
449   }
450   // move after the composite feature memebers, if they are invisible (sub elements of sketch)
451   CompositeFeaturePtr aComposite = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aDropAfter);
452   if (aComposite.get())
453   {
454     FeaturePtr aNext = aDropAfter->document()->nextFeature(aDropAfter);
455     while (aNext.get() && !aNext->isInHistory() && aComposite->isSub(aNext)) {
456       aDropAfter = aNext;
457       aNext = aDropAfter->document()->nextFeature(aNext);
458     }
459   }
460
461   QByteArray anEncodedData = theData->data("xgui/moved.rows");
462   if (anEncodedData.isEmpty())
463     return false; // dropped something alien, decline
464
465   QDataStream stream(&anEncodedData, QIODevice::ReadOnly);
466   std::list<FeaturePtr> aDropped;
467   while (!stream.atEnd()) {
468     int aRow;
469     stream >> aRow;
470     ModuleBase_ITreeNode* aNode = myRoot->subNode(aRow);
471     if (aNode)
472     {
473       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aNode->object());
474       // feature moved after itself is not moved, add only Part feature, other skip
475       if (aFeature.get() && aFeature != aDropAfter && aFeature->getKind() == "Part")
476         aDropped.push_back(aFeature);
477     }
478   }
479   if (aDropped.empty()) // nothing to move
480     return false;
481
482   // check for the movement is valid due to existing dependencies
483   std::wstring anErrorStr = ModelAPI_Tools::validateMovement(aDropAfter, aDropped);
484   if (!anErrorStr.empty())
485   {
486     QMessageBox aMessageBox;
487     aMessageBox.setWindowTitle(QObject::tr("Move part"));
488     aMessageBox.setIcon(QMessageBox::Warning);
489     aMessageBox.setStandardButtons(QMessageBox::Ok);
490     aMessageBox.setDefaultButton(QMessageBox::Ok);
491     QString aMessageText(QObject::tr("Part(s) cannot be moved because of breaking dependencies."));
492     aMessageBox.setText(aMessageText);
493     aMessageBox.setDetailedText(QString::fromStdWString(anErrorStr));
494     aMessageBox.exec();
495     return false;
496   }
497
498   if (aSession->isOperation())
499   {
500     QMessageBox aMessageBox;
501     aMessageBox.setWindowTitle(QObject::tr("Move part"));
502     aMessageBox.setIcon(QMessageBox::Warning);
503     aMessageBox.setStandardButtons(QMessageBox::Ok);
504     aMessageBox.setDefaultButton(QMessageBox::Ok);
505     QString aMessageText(QObject::tr("Cannot move part(s) during another operation."));
506     aMessageBox.setText(aMessageText);
507     aMessageBox.exec();
508     return false;
509   }
510
511   aSession->startOperation("Move Part");
512   DocumentPtr aPartSet = aSession->moduleDocument();
513   for (std::list<FeaturePtr>::iterator aDrop = aDropped.begin(); aDrop != aDropped.end(); aDrop++)
514   {
515     aPartSet->moveFeature(*aDrop, aDropAfter);
516     aDropAfter = *aDrop;
517   }
518   aSession->finishOperation();
519
520   updateSubTree(myRoot);
521   myWorkshop->updateHistory();
522
523   // returns false in any case to avoid calling removeRows after it,
524   return false; // because number of rows stays the same
525 }
526
527 //******************************************************
528 QModelIndex XGUI_DataModel::documentRootIndex(DocumentPtr theDoc, int theColumn) const
529 {
530   SessionPtr aSession = ModelAPI_Session::get();
531   DocumentPtr aRootDoc = aSession->moduleDocument();
532   if (theDoc == aRootDoc)
533     return QModelIndex();
534   else {
535     ModuleBase_ITreeNode* aDocNode = 0;
536     foreach(ModuleBase_ITreeNode* aNode, myRoot->children()) {
537       if (aNode->document() == theDoc) {
538         aDocNode = aNode;
539         break;
540       }
541     }
542     if (aDocNode)
543       return getIndex(aDocNode, theColumn);
544   }
545   return QModelIndex();
546 }
547
548 //******************************************************
549 bool XGUI_DataModel::hasHiddenState(const QModelIndex& theIndex)
550 {
551   if (theIndex.isValid()) {
552     ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
553     return aNode->visibilityState() == ModuleBase_ITreeNode::Hidden;
554   }
555   return false;
556 }
557
558 //******************************************************
559 bool XGUI_DataModel::hasIndex(const QModelIndex& theIndex) const
560 {
561   ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
562   return myRoot->hasSubNode(aNode);
563 }
564
565 //******************************************************
566 QModelIndex XGUI_DataModel::getParentIndex(ModuleBase_ITreeNode* theNode, int thCol) const
567 {
568   ModuleBase_ITreeNode* aParent = theNode->parent();
569   if (aParent == myRoot) {
570     return QModelIndex();
571   } else {
572     return getIndex(aParent, thCol);
573   }
574 }
575
576 //******************************************************
577 QModelIndex XGUI_DataModel::getIndex(ModuleBase_ITreeNode* theNode, int thCol) const
578 {
579   if (theNode == myRoot)
580     return QModelIndex();
581   int aRow = theNode->parent()->nodeRow(theNode);
582   return createIndex(aRow, thCol, theNode);
583 }
584
585
586 //******************************************************
587 void XGUI_DataModel::updateSubTree(ModuleBase_ITreeNode* theParent)
588 {
589   int aRows = theParent->childrenCount();
590   if (aRows) {
591     QModelIndex aParent = getIndex(theParent, 0);
592     QModelIndex aFirstIdx = aParent.child(0, 0);
593     QModelIndex aLastIdx = aParent.child(aRows - 1, 2);
594     dataChanged(aFirstIdx, aLastIdx);
595   }
596 }
597
598
599 //******************************************************
600 DocumentPtr XGUI_DataModel::document(const QModelIndex& theIndex) const
601 {
602   ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
603   return aNode->document();
604 }
605
606
607 //******************************************************
608 bool XGUI_DataModel::hasNode(ModuleBase_ITreeNode* theNode) const
609 {
610   return myRoot->hasSubNode(theNode);
611 }