Salome HOME
7f110ccd2c98e27c8ef5721551a0c883540ab243
[modules/shaper.git] / src / XGUI / XGUI_DataModel.cpp
1 // Copyright (C) 2014-2024  CEA, EDF
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   
370   //bos #40645 [CEA] Automatically expand tree in Object Browser
371   if (myWorkshop->objectBrowser()) 
372   {
373     XGUI_DataTree* aTreeView = myWorkshop->objectBrowser()->treeView();
374     if(!aTreeView->isExpanded(theParent))
375     {
376       aTreeView->setExpanded(theParent, true);
377     }
378   }
379   return true;
380 }
381
382 //******************************************************
383 bool XGUI_DataModel::removeRows(int theRow, int theCount, const QModelIndex& theParent)
384 {
385   beginRemoveRows(theParent, theRow, theRow + theCount - 1);
386   endRemoveRows();
387   return true;
388 }
389
390 //******************************************************
391 Qt::ItemFlags XGUI_DataModel::flags(const QModelIndex& theIndex) const
392 {
393   if (theIndex.isValid()) {
394     ModuleBase_ITreeNode* aNode = static_cast<ModuleBase_ITreeNode*>(theIndex.internalPointer());
395     // Check that the pointer is Valid
396     if (!isValidNode(aNode))
397       return Qt::NoItemFlags;
398     Qt::ItemFlags aResultFlags = aNode->flags(theIndex.column());
399     // Drag and drop of Part features only if:
400     // - PartSet is active
401     // - active Part feature of PartSet is dragged
402     // - finally if it does not break dependencies between features (but here only drag possibility is checked)
403     SessionPtr aSession = ModelAPI_Session::get();
404     if (aSession->hasModuleDocument() && aSession->moduleDocument() == aSession->activeDocument()) {
405
406       ObjectPtr aNodeObj = aNode->object();
407       if (aNodeObj.get() && aNodeObj->groupName() == ModelAPI_Feature::group())
408       {
409         FeaturePtr aNodeFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aNodeObj);
410         if (aNodeFeature.get() && aNodeFeature->getKind() == "Part" && !aNodeFeature->isDisabled())
411           aResultFlags |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
412       }
413     }
414     return aResultFlags;
415   }
416   return Qt::ItemIsDropEnabled | Qt::ItemFlags();
417 }
418
419 bool XGUI_DataModel::canDropMimeData(const QMimeData *theData, Qt::DropAction theAction,
420   int theRow, int theColumn, const QModelIndex &theParent) const
421 {
422   if (theParent.isValid())
423     return false;
424   ModuleBase_ITreeNode* aSubNode = myRoot->subNode(theRow);
425   if ((aSubNode && aSubNode->object() && aSubNode->object()->groupName() == ModelAPI_Feature::group())
426       || theRow == myRoot->childrenCount()) // into the end of a list of features
427   {
428     return true;
429   }
430
431   return false; // in other cases drop is forbidden
432 }
433
434 QMimeData* XGUI_DataModel::mimeData(const QModelIndexList& theIndexes) const
435 {
436   std::set<int> aRows; // to avoid duplication of rows and for sorting the indices
437   foreach (QModelIndex anIndex, theIndexes) {
438     if (anIndex.isValid() && anIndex.internalPointer())
439       aRows.insert(anIndex.row());
440   }
441   QByteArray anEncodedData;
442   QDataStream aStream(&anEncodedData, QIODevice::WriteOnly);
443   for(std::set<int>::iterator aRIter = aRows.begin(); aRIter != aRows.end(); aRIter++)
444     aStream << *aRIter;
445
446   QMimeData* aMimeData = new QMimeData();
447   aMimeData->setData("xgui/moved.rows", anEncodedData);
448   return aMimeData;
449 }
450
451 bool XGUI_DataModel::dropMimeData(const QMimeData *theData, Qt::DropAction theAction,
452   int theRow, int theColumn, const QModelIndex &theParent)
453 {
454   FeaturePtr aDropAfter; // after this feature it is dropped, NULL if drop the the first place
455   if (theRow > 0)
456   {
457     ModuleBase_ITreeNode* aNode = myRoot->subNode(theRow - 1);
458     if (aNode && aNode->object() && aNode->object()->groupName() == ModelAPI_Feature::group())
459       aDropAfter = std::dynamic_pointer_cast<ModelAPI_Feature>(aNode->object());
460   }
461   SessionPtr aSession = ModelAPI_Session::get();
462   if (aDropAfter.get()) // move to the upper enabled feature
463   {
464     while (aDropAfter.get() && (aDropAfter->isDisabled() || !aDropAfter->isInHistory()))
465       aDropAfter = aDropAfter->document()->nextFeature(aDropAfter, true);
466   }
467   else { // move after invisible items, not the first (which is coordinate system by default)
468     std::list<FeaturePtr> allFeatures = aSession->get()->moduleDocument()->allFeatures();
469     std::list<FeaturePtr>::iterator aFeature = allFeatures.begin();
470     for(; aFeature != allFeatures.end(); aFeature++)
471     {
472       if ((*aFeature)->isInHistory())
473         break;
474       aDropAfter = *aFeature;
475     }
476   }
477   // move after the composite feature memebers, if they are invisible (sub elements of sketch)
478   CompositeFeaturePtr aComposite = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(aDropAfter);
479   if (aComposite.get())
480   {
481     FeaturePtr aNext = aDropAfter->document()->nextFeature(aDropAfter);
482     while (aNext.get() && !aNext->isInHistory() && aComposite->isSub(aNext)) {
483       aDropAfter = aNext;
484       aNext = aDropAfter->document()->nextFeature(aNext);
485     }
486   }
487
488   QByteArray anEncodedData = theData->data("xgui/moved.rows");
489   if (anEncodedData.isEmpty())
490     return false; // dropped something alien, decline
491
492   QDataStream stream(&anEncodedData, QIODevice::ReadOnly);
493   std::list<FeaturePtr> aDropped;
494   while (!stream.atEnd()) {
495     int aRow;
496     stream >> aRow;
497     ModuleBase_ITreeNode* aNode = myRoot->subNode(aRow);
498     if (aNode)
499     {
500       FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aNode->object());
501       // feature moved after itself is not moved, add only Part feature, other skip
502       if (aFeature.get() && aFeature != aDropAfter && aFeature->getKind() == "Part")
503         aDropped.push_back(aFeature);
504     }
505   }
506   if (aDropped.empty()) // nothing to move
507     return false;
508
509   // check for the movement is valid due to existing dependencies
510   std::wstring anErrorStr = ModelAPI_Tools::validateMovement(aDropAfter, aDropped);
511   if (!anErrorStr.empty())
512   {
513     QMessageBox aMessageBox;
514     aMessageBox.setWindowTitle(QObject::tr("Move part"));
515     aMessageBox.setIcon(QMessageBox::Warning);
516     aMessageBox.setStandardButtons(QMessageBox::Ok);
517     aMessageBox.setDefaultButton(QMessageBox::Ok);
518     QString aMessageText(QObject::tr("Part(s) cannot be moved because of breaking dependencies."));
519     aMessageBox.setText(aMessageText);
520     aMessageBox.setDetailedText(QString::fromStdWString(anErrorStr));
521     aMessageBox.exec();
522     return false;
523   }
524
525   if (aSession->isOperation())
526   {
527     QMessageBox aMessageBox;
528     aMessageBox.setWindowTitle(QObject::tr("Move part"));
529     aMessageBox.setIcon(QMessageBox::Warning);
530     aMessageBox.setStandardButtons(QMessageBox::Ok);
531     aMessageBox.setDefaultButton(QMessageBox::Ok);
532     QString aMessageText(QObject::tr("Cannot move part(s) during another operation."));
533     aMessageBox.setText(aMessageText);
534     aMessageBox.exec();
535     return false;
536   }
537
538   aSession->startOperation("Move Part");
539   DocumentPtr aPartSet = aSession->moduleDocument();
540   for (std::list<FeaturePtr>::iterator aDrop = aDropped.begin(); aDrop != aDropped.end(); aDrop++)
541   {
542     aPartSet->moveFeature(*aDrop, aDropAfter);
543     aDropAfter = *aDrop;
544   }
545   aSession->finishOperation();
546
547   updateSubTree(myRoot);
548   myWorkshop->updateHistory();
549
550   // returns false in any case to avoid calling removeRows after it,
551   return false; // because number of rows stays the same
552 }
553
554 //******************************************************
555 QModelIndex XGUI_DataModel::documentRootIndex(DocumentPtr theDoc, int theColumn) const
556 {
557   SessionPtr aSession = ModelAPI_Session::get();
558   DocumentPtr aRootDoc = aSession->moduleDocument();
559   if (theDoc == aRootDoc)
560     return QModelIndex();
561   else {
562     ModuleBase_ITreeNode* aDocNode = 0;
563     foreach(ModuleBase_ITreeNode* aNode, myRoot->children()) {
564       if (aNode->document() == theDoc) {
565         aDocNode = aNode;
566         break;
567       }
568     }
569     if (aDocNode)
570       return getIndex(aDocNode, theColumn);
571   }
572   return QModelIndex();
573 }
574
575 //******************************************************
576 bool XGUI_DataModel::hasHiddenState(const QModelIndex& theIndex)
577 {
578   if (theIndex.isValid()) {
579     ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
580     return aNode->visibilityState() == ModuleBase_ITreeNode::Hidden;
581   }
582   return false;
583 }
584
585 //******************************************************
586 bool XGUI_DataModel::hasIndex(const QModelIndex& theIndex) const
587 {
588   ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
589   return myRoot->hasSubNode(aNode);
590 }
591
592 //******************************************************
593 QModelIndex XGUI_DataModel::getParentIndex(ModuleBase_ITreeNode* theNode, int thCol) const
594 {
595   ModuleBase_ITreeNode* aParent = theNode->parent();
596   if (aParent == myRoot) {
597     return QModelIndex();
598   } else {
599     return getIndex(aParent, thCol);
600   }
601 }
602
603 //******************************************************
604 QModelIndex XGUI_DataModel::getIndex(ModuleBase_ITreeNode* theNode, int thCol) const
605 {
606   if (theNode == myRoot)
607     return QModelIndex();
608   int aRow = theNode->parent()->nodeRow(theNode);
609   return createIndex(aRow, thCol, theNode);
610 }
611
612
613 //******************************************************
614 void XGUI_DataModel::updateSubTree(ModuleBase_ITreeNode* theParent)
615 {
616   int aRows = theParent->childrenCount();
617   if (aRows) {
618     QModelIndex aParent = getIndex(theParent, 0);
619     QModelIndex aFirstIdx = aParent.child(0, 0);
620     QModelIndex aLastIdx = aParent.child(aRows - 1, 2);
621     dataChanged(aFirstIdx, aLastIdx);
622   }
623 }
624
625
626 //******************************************************
627 DocumentPtr XGUI_DataModel::document(const QModelIndex& theIndex) const
628 {
629   ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
630   return aNode->document();
631 }
632
633
634 //******************************************************
635 bool XGUI_DataModel::hasNode(ModuleBase_ITreeNode* theNode) const
636 {
637   return myRoot->hasSubNode(theNode);
638 }