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