1 // Copyright (C) 2014-2024 CEA, EDF
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include "XGUI_DataModel.h"
21 #include "XGUI_ObjectsBrowser.h"
22 #include "XGUI_Workshop.h"
24 #include <ModuleBase_IconFactory.h>
25 #include <ModuleBase_ITreeNode.h>
27 #include <ModelAPI_Session.h>
28 #include <ModelAPI_ResultField.h>
29 #include <ModelAPI_Tools.h>
30 #include <ModelAPI_CompositeFeature.h>
32 #include <Config_FeatureMessage.h>
34 #include <Events_Loop.h>
37 #include <QMessageBox>
42 #pragma warning(disable: 4100)
45 static bool isValidNode(const ModuleBase_ITreeNode* theNode)
47 ModuleBase_ITreeNode* aParent = 0;
49 aParent = theNode->parent();
55 return isValidNode(aParent);
59 // Constructor *************************************************
60 XGUI_DataModel::XGUI_DataModel(QObject* theParent) : QAbstractItemModel(theParent)//,
61 //myIsEventsProcessingBlocked(false)
63 XGUI_ObjectsBrowser* aOB = qobject_cast<XGUI_ObjectsBrowser*>(theParent);
64 myWorkshop = aOB->workshop();
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));
75 XGUI_DataModel::~XGUI_DataModel()
80 //******************************************************
81 void XGUI_DataModel::processEvent(const std::shared_ptr<Events_Message>& theMessage)
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();
100 if (aCreated.length() == 0)
103 emit beforeTreeRebuild();
104 QTreeNodesList aNodes = myRoot->objectCreated(aCreated);
105 ModuleBase_ITreeNode* aParent;
107 QModelIndex aParentIndex1, aParentIndex2;
109 bool aRebuildAll = false;
111 foreach(ModuleBase_ITreeNode* aNode, aNodes) {
112 aObj = aNode->object();
113 aParent = aNode->parent();
114 if (aObj.get() && (aObj->groupName() == ModelAPI_Folder::group())) {
119 aRow = aParent->nodeRow(aNode);
120 aParentIndex1 = getParentIndex(aNode, 0);
121 aParentIndex2 = getParentIndex(aNode, 2);
122 insertRows(aRow, 1, aParentIndex1);
123 dataChanged(aParentIndex1, aParentIndex2);
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 =
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()));
142 // Remove obsolete nodes
143 QTreeNodesList aRemaining;
144 foreach(ModuleBase_ITreeNode* aNode, aList) {
145 if (myRoot->hasSubNode(aNode))
146 aRemaining.append(aNode);
148 // Update remaining nodes
149 foreach(ModuleBase_ITreeNode* aNode, aRemaining) {
151 aNode->parent()->update();
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();
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())
170 if (aObj->data()->isValid()) {
171 if (aObj->groupName() == ModelAPI_Folder::group()) {
175 aCreated.append(*aIt);
182 QSet<ModuleBase_ITreeNode*> aParents;
183 foreach(ObjectPtr aObj, aCreated) {
184 ModuleBase_ITreeNode* aNode = myRoot->subNode(aObj);
186 if (aNode->parent()) {
187 if (aNode->parent() == myRoot) {
189 aParents.insert(myRoot);
193 aNode = aNode->parent();
196 aParents.insert(aNode);
199 foreach(ModuleBase_ITreeNode* aNode, aParents) {
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());
214 emit beforeTreeRebuild();
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);
226 updateSubTree(aRoot);
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();
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);
243 if (aCreated.length() == 0)
245 emit beforeTreeRebuild();
246 foreach(ObjectPtr aObj, aCreated) {
247 ModuleBase_ITreeNode* aNode = myRoot->subNode(aObj);
249 int aOldNb = aNode->childrenCount();
251 int aNewNb = aNode->childrenCount();
253 QModelIndex aFirstIdx = getIndex(aNode, 0);
254 QModelIndex aLastIdx = getIndex(aNode, 2);
256 if (aNewNb > aOldNb) {
257 insertRows(aOldNb - 1, aNewNb - aOldNb, aFirstIdx);
259 else if (aNewNb < aOldNb) {
261 removeRows(aNewNb - 1, aOldNb - aNewNb, aFirstIdx);
263 removeRows(0, aOldNb, aFirstIdx);
265 dataChanged(aFirstIdx, aLastIdx);
272 //******************************************************
273 void XGUI_DataModel::clear()
279 //******************************************************
280 void XGUI_DataModel::rebuildDataTree()
286 //******************************************************
287 ObjectPtr XGUI_DataModel::object(const QModelIndex& theIndex) const
289 if (theIndex.isValid()) {
290 ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
291 return aNode->object();
296 //******************************************************
297 QModelIndex XGUI_DataModel::objectIndex(const ObjectPtr theObject, int theColumn) const
299 ModuleBase_ITreeNode* aNode = myRoot->subNode(theObject);
301 return getIndex(aNode, theColumn);
303 return QModelIndex();
306 //******************************************************
307 QVariant XGUI_DataModel::data(const QModelIndex& theIndex, int theRole) const
309 if (theIndex.isValid()) {
310 ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
311 return aNode->data(theIndex.column(), theRole);
316 //******************************************************
317 QVariant XGUI_DataModel::headerData(int theSection, Qt::Orientation theOrient, int theRole) const
322 //******************************************************
323 int XGUI_DataModel::rowCount(const QModelIndex& theParent) const
325 ModuleBase_ITreeNode* aParentNode = (theParent.isValid()) ?
326 (ModuleBase_ITreeNode*)theParent.internalPointer() : myRoot;
327 return aParentNode->childrenCount();
330 //******************************************************
331 int XGUI_DataModel::columnCount(const QModelIndex& theParent) const
336 //******************************************************
337 QModelIndex XGUI_DataModel::index(int theRow, int theColumn, const QModelIndex &theParent) const
339 ModuleBase_ITreeNode* aParentNode = (theParent.isValid()) ?
340 (ModuleBase_ITreeNode*)theParent.internalPointer() : myRoot;
341 ModuleBase_ITreeNode* aSubNode = aParentNode->subNode(theRow);
343 return createIndex(theRow, theColumn, aSubNode);
346 //******************************************************
347 QModelIndex XGUI_DataModel::parent(const QModelIndex& theIndex) const
349 if (theIndex.isValid()) {
350 ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
351 return getParentIndex(aNode, 1);
353 return QModelIndex();
356 //******************************************************
357 bool XGUI_DataModel::hasChildren(const QModelIndex& theParent) const
359 ModuleBase_ITreeNode* aParentNode = (theParent.isValid()) ?
360 (ModuleBase_ITreeNode*)theParent.internalPointer() : myRoot;
361 return aParentNode->childrenCount() > 0;
364 //******************************************************
365 bool XGUI_DataModel::insertRows(int theRow, int theCount, const QModelIndex& theParent)
367 beginInsertRows(theParent, theRow, theRow + theCount - 1);
370 //bos #40645 [CEA] Automatically expand tree in Object Browser
371 if (myWorkshop->objectBrowser())
373 XGUI_DataTree* aTreeView = myWorkshop->objectBrowser()->treeView();
374 if(!aTreeView->isExpanded(theParent))
376 aTreeView->setExpanded(theParent, true);
382 //******************************************************
383 bool XGUI_DataModel::removeRows(int theRow, int theCount, const QModelIndex& theParent)
385 beginRemoveRows(theParent, theRow, theRow + theCount - 1);
390 //******************************************************
391 Qt::ItemFlags XGUI_DataModel::flags(const QModelIndex& theIndex) const
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()) {
406 ObjectPtr aNodeObj = aNode->object();
407 if (aNodeObj.get() && aNodeObj->groupName() == ModelAPI_Feature::group())
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;
416 return Qt::ItemIsDropEnabled | Qt::ItemFlags();
419 bool XGUI_DataModel::canDropMimeData(const QMimeData *theData, Qt::DropAction theAction,
420 int theRow, int theColumn, const QModelIndex &theParent) const
422 if (theParent.isValid())
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
431 return false; // in other cases drop is forbidden
434 QMimeData* XGUI_DataModel::mimeData(const QModelIndexList& theIndexes) const
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());
441 QByteArray anEncodedData;
442 QDataStream aStream(&anEncodedData, QIODevice::WriteOnly);
443 for(std::set<int>::iterator aRIter = aRows.begin(); aRIter != aRows.end(); aRIter++)
446 QMimeData* aMimeData = new QMimeData();
447 aMimeData->setData("xgui/moved.rows", anEncodedData);
451 bool XGUI_DataModel::dropMimeData(const QMimeData *theData, Qt::DropAction theAction,
452 int theRow, int theColumn, const QModelIndex &theParent)
454 FeaturePtr aDropAfter; // after this feature it is dropped, NULL if drop the the first place
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());
461 SessionPtr aSession = ModelAPI_Session::get();
462 if (aDropAfter.get()) // move to the upper enabled feature
464 while (aDropAfter.get() && (aDropAfter->isDisabled() || !aDropAfter->isInHistory()))
465 aDropAfter = aDropAfter->document()->nextFeature(aDropAfter, true);
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++)
472 if ((*aFeature)->isInHistory())
474 aDropAfter = *aFeature;
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())
481 FeaturePtr aNext = aDropAfter->document()->nextFeature(aDropAfter);
482 while (aNext.get() && !aNext->isInHistory() && aComposite->isSub(aNext)) {
484 aNext = aDropAfter->document()->nextFeature(aNext);
488 QByteArray anEncodedData = theData->data("xgui/moved.rows");
489 if (anEncodedData.isEmpty())
490 return false; // dropped something alien, decline
492 QDataStream stream(&anEncodedData, QIODevice::ReadOnly);
493 std::list<FeaturePtr> aDropped;
494 while (!stream.atEnd()) {
497 ModuleBase_ITreeNode* aNode = myRoot->subNode(aRow);
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);
506 if (aDropped.empty()) // nothing to move
509 // check for the movement is valid due to existing dependencies
510 std::wstring anErrorStr = ModelAPI_Tools::validateMovement(aDropAfter, aDropped);
511 if (!anErrorStr.empty())
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));
525 if (aSession->isOperation())
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);
538 aSession->startOperation("Move Part");
539 DocumentPtr aPartSet = aSession->moduleDocument();
540 for (std::list<FeaturePtr>::iterator aDrop = aDropped.begin(); aDrop != aDropped.end(); aDrop++)
542 aPartSet->moveFeature(*aDrop, aDropAfter);
545 aSession->finishOperation();
547 updateSubTree(myRoot);
548 myWorkshop->updateHistory();
550 // returns false in any case to avoid calling removeRows after it,
551 return false; // because number of rows stays the same
554 //******************************************************
555 QModelIndex XGUI_DataModel::documentRootIndex(DocumentPtr theDoc, int theColumn) const
557 SessionPtr aSession = ModelAPI_Session::get();
558 DocumentPtr aRootDoc = aSession->moduleDocument();
559 if (theDoc == aRootDoc)
560 return QModelIndex();
562 ModuleBase_ITreeNode* aDocNode = 0;
563 foreach(ModuleBase_ITreeNode* aNode, myRoot->children()) {
564 if (aNode->document() == theDoc) {
570 return getIndex(aDocNode, theColumn);
572 return QModelIndex();
575 //******************************************************
576 bool XGUI_DataModel::hasHiddenState(const QModelIndex& theIndex)
578 if (theIndex.isValid()) {
579 ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
580 return aNode->visibilityState() == ModuleBase_ITreeNode::Hidden;
585 //******************************************************
586 bool XGUI_DataModel::hasIndex(const QModelIndex& theIndex) const
588 ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
589 return myRoot->hasSubNode(aNode);
592 //******************************************************
593 QModelIndex XGUI_DataModel::getParentIndex(ModuleBase_ITreeNode* theNode, int thCol) const
595 ModuleBase_ITreeNode* aParent = theNode->parent();
596 if (aParent == myRoot) {
597 return QModelIndex();
599 return getIndex(aParent, thCol);
603 //******************************************************
604 QModelIndex XGUI_DataModel::getIndex(ModuleBase_ITreeNode* theNode, int thCol) const
606 if (theNode == myRoot)
607 return QModelIndex();
608 int aRow = theNode->parent()->nodeRow(theNode);
609 return createIndex(aRow, thCol, theNode);
613 //******************************************************
614 void XGUI_DataModel::updateSubTree(ModuleBase_ITreeNode* theParent)
616 int aRows = theParent->childrenCount();
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);
626 //******************************************************
627 DocumentPtr XGUI_DataModel::document(const QModelIndex& theIndex) const
629 ModuleBase_ITreeNode* aNode = (ModuleBase_ITreeNode*)theIndex.internalPointer();
630 return aNode->document();
634 //******************************************************
635 bool XGUI_DataModel::hasNode(ModuleBase_ITreeNode* theNode) const
637 return myRoot->hasSubNode(theNode);