Salome HOME
dab8161792d418f6694d4951da9faea858ae1b52
[modules/shaper.git] / src / PartSet / PartSet_TreeNodes.cpp
1 // Copyright (C) 2014-2021  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 "PartSet_TreeNodes.h"
21 #include "PartSet_Tools.h"
22
23 #include <ModuleBase_IconFactory.h>
24 #include <ModuleBase_IWorkshop.h>
25 #include <ModuleBase_Tools.h>
26
27 #include <PartSetPlugin_Part.h>
28
29 #include <ModelAPI_Session.h>
30 #include <ModelAPI_ResultParameter.h>
31 #include <ModelAPI_ResultField.h>
32 #include <ModelAPI_ResultGroup.h>
33 #include <ModelAPI_ResultConstruction.h>
34 #include <ModelAPI_ResultPart.h>
35 #include <ModelAPI_ResultBody.h>
36 #include <ModelAPI_Tools.h>
37 #include <ModelAPI_ResultBody.h>
38 #include <ModelAPI_CompositeFeature.h>
39 #include <ModelAPI_AttributeDouble.h>
40 #include <ModelAPI_Folder.h>
41 #include <ModelAPI_AttributeReference.h>
42
43 #include <Config_PropManager.h>
44
45 #include <QBrush>
46 #include <QMap>
47
48
49 #define ACTIVE_COLOR QColor(Qt::black)
50 #define SELECTABLE_COLOR QColor(100, 100, 100)
51 #define DISABLED_COLOR QColor(200, 200, 200)
52
53 Qt::ItemFlags aNullFlag;
54 Qt::ItemFlags aDefaultFlag = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
55 Qt::ItemFlags aEditingFlag = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
56
57
58 ResultPartPtr getPartResult(const ObjectPtr& theObj)
59 {
60   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObj);
61   if (aFeature) {
62     ResultPtr aRes = aFeature->firstResult();
63     if (aRes.get() && (aRes->groupName() == ModelAPI_ResultPart::group())) {
64       ResultPartPtr aPartRes = std::dynamic_pointer_cast<ModelAPI_ResultPart>(aRes);
65       // Use only original parts, not a placement results
66       if (aPartRes == aPartRes->original())
67         return aPartRes;
68     }
69   }
70   return ResultPartPtr();
71 }
72
73 bool isCurrentFeature(const ObjectPtr& theObj)
74 {
75   SessionPtr aSession = ModelAPI_Session::get();
76   DocumentPtr aCurDoc = aSession->activeDocument();
77   FeaturePtr aFeature = aCurDoc->currentFeature(true);
78   return aFeature == theObj;
79 }
80
81 //////////////////////////////////////////////////////////////////////////////////
82 QVariant PartSet_TreeNode::data(int theColumn, int theRole) const
83 {
84   if ((theColumn == 1) && (theRole == Qt::ForegroundRole)) {
85     Qt::ItemFlags aFlags = flags(theColumn);
86     if (aFlags == Qt::ItemFlags())
87       return QBrush(DISABLED_COLOR);
88     if (!aFlags.testFlag(Qt::ItemIsEditable))
89       return QBrush(SELECTABLE_COLOR);
90     return activeItemColor();
91   }
92   return ModuleBase_ITreeNode::data(theColumn, theRole);
93 }
94
95 QColor PartSet_TreeNode::activeItemColor() const
96 {
97   return ACTIVE_COLOR;
98 }
99
100
101 //////////////////////////////////////////////////////////////////////////////////
102 QVariant PartSet_ObjectNode::data(int theColumn, int theRole) const
103 {
104   switch (theRole) {
105   case Qt::DisplayRole:
106     if (theColumn == 1) {
107       if (myObject->groupName() == ModelAPI_ResultParameter::group()) {
108         ResultParameterPtr aParam = std::dynamic_pointer_cast<ModelAPI_ResultParameter>(myObject);
109         AttributeDoublePtr aValueAttribute =
110           aParam->data()->real(ModelAPI_ResultParameter::VALUE());
111         QString aVal = QString::number(aValueAttribute->value());
112         QString aTitle = QString::fromStdWString(myObject->data()->name());
113         return aTitle + " = " + aVal;
114       }
115       return QString::fromStdWString(myObject->data()->name());
116     }
117     break;
118   case Qt::DecorationRole:
119     switch (theColumn) {
120     case 0:
121       switch (visibilityState()) {
122       case NoneState:
123         return QIcon();
124       case Visible:
125         return QIcon(":pictures/eyeopen.png");
126       case SemiVisible:
127         return QIcon(":pictures/eyemiclosed.png");
128       case Hidden:
129         return QIcon(":pictures/eyeclosed.png");
130       }
131     case 1:
132       return ModuleBase_IconFactory::get()->getIcon(myObject);
133     case 2:
134       if (isCurrentFeature(myObject))
135         return QIcon(":pictures/arrow.png");
136       else
137         return QIcon();
138     }
139   }
140   return PartSet_TreeNode::data(theColumn, theRole);
141 }
142
143 QColor PartSet_ObjectNode::activeItemColor() const
144 {
145   if (myObject.get() && myObject->groupName() == ModelAPI_Feature::group()) {
146     std::vector<int> aColor =
147       Config_PropManager::color("Visualization", "feature_objectbrowser_color");
148     return QColor(aColor[0], aColor[1], aColor[2]);
149   }
150   return PartSet_TreeNode::activeItemColor();
151 }
152
153
154 Qt::ItemFlags PartSet_ObjectNode::flags(int theColumn) const
155 {
156   if (myObject->isDisabled()) {
157     return (theColumn == 2) ? Qt::ItemIsSelectable : aNullFlag;
158   } else {
159     DocumentPtr aDoc = myObject->document();
160     SessionPtr aSession = ModelAPI_Session::get();
161     if (aSession->activeDocument() == aDoc)
162       return aEditingFlag;
163   }
164   return aDefaultFlag;
165 }
166
167 PartSet_ObjectNode::VisibilityState PartSet_ObjectNode::visibilityState() const
168 {
169   Qt::ItemFlags aFlags = flags(1);
170   if (aFlags == Qt::ItemFlags())
171     return NoneState;
172
173   if (myObject->groupName() == ModelAPI_ResultParameter::group())
174     return NoneState;
175   ResultPtr aResObj = std::dynamic_pointer_cast<ModelAPI_Result>(myObject);
176   if (aResObj.get()) {
177     ModuleBase_IWorkshop* aWork = workshop();
178     ResultBodyPtr aCompRes = std::dynamic_pointer_cast<ModelAPI_ResultBody>(aResObj);
179     if (aCompRes.get()) {
180       std::list<ResultPtr> aResultsList;
181       ModelAPI_Tools::allSubs(aCompRes, aResultsList);
182       VisibilityState aState = aResultsList.size() == 0 ?
183         (aWork->isVisible(aCompRes) ? Visible : Hidden) : NoneState;
184
185       std::list<ResultPtr>::const_iterator aIt;
186       ResultBodyPtr aCompSub;
187       for (aIt = aResultsList.cbegin(); aIt != aResultsList.cend(); aIt++) {
188         ResultPtr aSubRes = (*aIt);
189         aCompSub = std::dynamic_pointer_cast<ModelAPI_ResultBody>(aSubRes);
190         if (!(aCompSub.get() && aCompSub->numberOfSubs() > 0)) {
191           VisibilityState aS = aWork->isVisible(aSubRes) ? Visible : Hidden;
192           if (aState == NoneState)
193             aState = aS;
194           else if (aState != aS) {
195             aState = SemiVisible;
196             break;
197           }
198         }
199       }
200       return aState;
201     } else {
202       if (aWork->isVisible(aResObj))
203         return Visible;
204       else
205         return Hidden;
206     }
207   }
208   return NoneState;
209 }
210
211 int PartSet_ObjectNode::numberOfSubs() const
212 {
213   ResultBodyPtr aCompRes = std::dynamic_pointer_cast<ModelAPI_ResultBody>(myObject);
214   if (aCompRes.get())
215    return aCompRes->numberOfSubs(true);
216   else {
217     CompositeFeaturePtr aCompFeature =
218       std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(myObject);
219     if (aCompFeature.get() && aCompFeature->data()->isValid())
220       return aCompFeature->numberOfSubs(true);
221     else {
222       ResultFieldPtr aFieldRes = std::dynamic_pointer_cast<ModelAPI_ResultField>(myObject);
223       if (aFieldRes.get())
224         return aFieldRes->stepsSize();
225     }
226   }
227   return 0;
228 }
229
230
231 ObjectPtr PartSet_ObjectNode::subObject(int theId) const
232 {
233   ResultBodyPtr aCompRes = std::dynamic_pointer_cast<ModelAPI_ResultBody>(myObject);
234   if (aCompRes.get())
235     return aCompRes->subResult(theId, true);
236   else {
237     CompositeFeaturePtr aCompFeature =
238       std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(myObject);
239     if (aCompFeature.get())
240       return aCompFeature->subFeature(theId, true);
241   }
242   return ObjectPtr();
243 }
244
245 void PartSet_ObjectNode::update()
246 {
247   int aNb = numberOfSubs();
248   if (aNb > 0) {
249     ResultFieldPtr aFieldRes = std::dynamic_pointer_cast<ModelAPI_ResultField>(myObject);
250
251     // If the object is a field result then delete extra sub-objects
252     if (aFieldRes.get()) {
253       // Call shape in order to update content of Field.
254       // It is necessary to do for cases when field was created by script when module is inactive.
255       aFieldRes->shape();
256       while (myChildren.size() > aNb) {
257         ModuleBase_ITreeNode* aNode = myChildren.last();
258         myChildren.removeAll(aNode);
259         delete aNode;
260       }
261     }
262     else {
263       ObjectPtr aObj;
264       ModuleBase_ITreeNode* aNode;
265       int aId = 0;
266       while (aId < myChildren.size()) {
267         aNode = myChildren.at(aId);
268         aObj = subObject(aId);
269         if (aNode->object() != aObj) {
270           myChildren.removeAll(aNode);
271           delete aNode;
272         }
273         else
274           aId++;
275       }
276     }
277
278     ModuleBase_ITreeNode* aNode;
279     ObjectPtr aBody;
280     int i;
281     for (i = 0; i < aNb; i++) {
282       aBody = subObject(i);
283       if (aBody.get()) {
284         if (i < myChildren.size()) {
285           aNode = myChildren.at(i);
286           if (aNode->object() != aBody) {
287             ((PartSet_ObjectNode*)aNode)->setObject(aBody);
288           }
289         }
290         else {
291           aNode = new PartSet_ObjectNode(aBody, this);
292           myChildren.append(aNode);
293           aNode->update();
294         }
295       }
296       else if (aFieldRes.get()) {
297         FieldStepPtr aStep = aFieldRes->step(i);
298         if (aStep.get()) {
299           if (i < myChildren.size()) {
300             PartSet_StepNode* aStepNode = static_cast<PartSet_StepNode*>(myChildren.at(i));
301             if (aStepNode->object() != aStep) {
302               aStepNode->setObject(aStep);
303             }
304           }
305           else {
306             aNode = new PartSet_StepNode(aStep, this);
307             myChildren.append(aNode);
308           }
309         }
310       }
311     }
312     // Delete extra objects
313     while (myChildren.size() > aNb) {
314       aNode = myChildren.takeLast();
315       delete aNode;
316     }
317     foreach(ModuleBase_ITreeNode* aChildNode, myChildren) {
318       aChildNode->update();
319     }
320   }
321   else {
322     deleteChildren();
323   }
324 }
325
326 QTreeNodesList PartSet_ObjectNode::objectCreated(const QObjectPtrList& theObjects)
327 {
328   QTreeNodesList aResult;
329   int aNb = numberOfSubs();
330   if (aNb > 0) {
331     ModuleBase_ITreeNode* aNode;
332     ResultFieldPtr aFieldRes = std::dynamic_pointer_cast<ModelAPI_ResultField>(myObject);
333     ObjectPtr aBody;
334     int i;
335     // Call shape in order to update content of Field.
336     // It is necessary to do for cases when field was created by script when module is inactive.
337     if (aFieldRes.get())
338       aFieldRes->shape();
339     for (i = 0; i < aNb; i++) {
340       aBody = subObject(i);
341       if (aBody.get()) {
342         if (i < myChildren.size()) {
343           aNode = myChildren.at(i);
344           if (aNode->object() != aBody) {
345             ((PartSet_ObjectNode*)aNode)->setObject(aBody);
346             aResult.append(aNode);
347           }
348         }
349         else {
350           aNode = new PartSet_ObjectNode(aBody, this);
351           myChildren.append(aNode);
352           aResult.append(aNode);
353           aNode->update();
354         }
355       }
356       else {
357         FieldStepPtr aStep = aFieldRes->step(i);
358         if (aStep.get()) {
359           if (i < myChildren.size()) {
360             PartSet_StepNode* aStepNode = static_cast<PartSet_StepNode*>(myChildren.at(i));
361             if (aStepNode->object() != aStep) {
362               aStepNode->setObject(aStep);
363             }
364           }
365           else {
366             aNode = new PartSet_StepNode(aStep, this);
367             myChildren.append(aNode);
368           }
369         }
370       }
371     }
372     foreach(ModuleBase_ITreeNode* aChildNode, myChildren) {
373       aResult.append(aChildNode->objectCreated(theObjects));
374     }
375   }
376   return aResult;
377 }
378
379 QTreeNodesList PartSet_ObjectNode::objectsDeleted(
380   const DocumentPtr& theDoc, const QString& theGroup)
381 {
382   QTreeNodesList aResult;
383   int aNb = numberOfSubs();
384   if (aNb != myChildren.size()) {
385     if (aNb == 0) {
386       deleteChildren();
387       aResult.append(this);
388     }
389     else {
390       // Delete extra objects
391       bool isDeleted = false;
392       ObjectPtr aObj;
393       ModuleBase_ITreeNode* aNode;
394       int aId = 0;
395       while (aId < myChildren.size()) {
396         aNode = myChildren.at(aId);
397         aObj = subObject(aId);
398         if (aNode->object() != aObj) {
399           myChildren.removeAll(aNode);
400           delete aNode;
401           isDeleted = true;
402         }
403         else
404           aId++;
405       }
406       if (isDeleted)
407         aResult.append(this);
408       int i = 0;
409       ObjectPtr aBody;
410       foreach(ModuleBase_ITreeNode* aChildNode, myChildren) {
411         aBody = subObject(i);
412         ((PartSet_ObjectNode*)aChildNode)->setObject(aBody);
413         aResult.append(aChildNode->objectsDeleted(theDoc, theGroup));
414         i++;
415       }
416     }
417   }
418   return aResult;
419 }
420 //////////////////////////////////////////////////////////////////////////////////
421 PartSet_FolderNode::PartSet_FolderNode(ModuleBase_ITreeNode* theParent,
422   FolderType theType)
423   : PartSet_TreeNode(theParent), myType(theType)
424 {
425 }
426
427 QString PartSet_FolderNode::name() const
428 {
429   switch (myType) {
430   case ParametersFolder:
431     return QObject::tr("Parameters");
432   case ConstructionFolder:
433     return QObject::tr("Constructions");
434   case PartsFolder:
435     return QObject::tr("Parts");
436   case ResultsFolder:
437     return QObject::tr("Results");
438   case FieldsFolder:
439     return QObject::tr("Fields");
440   case GroupsFolder:
441     return QObject::tr("Groups");
442   }
443   return "NoName";
444 }
445
446
447 QVariant PartSet_FolderNode::data(int theColumn, int theRole) const
448 {
449   static QIcon aParamsIco(":pictures/params_folder.png");
450   static QIcon aConstrIco(":pictures/constr_folder.png");
451
452   if (theColumn == 1) {
453     switch (theRole) {
454     case Qt::DisplayRole:
455       return name() + QString(" (%1)").arg(childrenCount());
456     case Qt::DecorationRole:
457       switch (myType) {
458       case ParametersFolder:
459         return aParamsIco;
460       case ConstructionFolder:
461         return aConstrIco;
462       case PartsFolder:
463         return aConstrIco;
464       case ResultsFolder:
465         return aConstrIco;
466       case FieldsFolder:
467         return aConstrIco;
468       case GroupsFolder:
469         return aConstrIco;
470       }
471     }
472   }
473   if ((theColumn == 2) && (theRole == Qt::DecorationRole)) {
474     if (document().get()) {
475       SessionPtr aSession = ModelAPI_Session::get();
476       if (document() != aSession->activeDocument())
477           return QIcon();
478
479       FeaturePtr aFeature = document()->currentFeature(true);
480       if (!aFeature.get()) { // There is no current feature
481         ModuleBase_ITreeNode* aLastFolder = 0;
482         foreach(ModuleBase_ITreeNode* aNode, parent()->children()) {
483           if (aNode->type() == PartSet_FolderNode::typeId())
484             aLastFolder = aNode;
485           else
486             break;
487         }
488         if (aLastFolder == this)
489           return QIcon(":pictures/arrow.png");
490         else
491           return QIcon();
492       }
493     }
494   }
495   return PartSet_TreeNode::data(theColumn, theRole);
496 }
497
498 Qt::ItemFlags PartSet_FolderNode::flags(int theColumn) const
499 {
500   SessionPtr aSession = ModelAPI_Session::get();
501   DocumentPtr aActiveDoc = aSession->activeDocument();
502   if (theColumn == 1) {
503     if (document() == aActiveDoc)
504       return aEditingFlag;
505   }
506   return aDefaultFlag;
507 }
508
509 ModuleBase_ITreeNode* PartSet_FolderNode::createNode(const ObjectPtr& theObj)
510 {
511   //ResultCompSolidPtr aCompRes = std::dynamic_pointer_cast<ModelAPI_ResultCompSolid>(theObj);
512   //if (aCompRes.get())
513   //  return new PartSet_CompsolidNode(theObj, this);
514   ModuleBase_ITreeNode* aNode = new PartSet_ObjectNode(theObj, this);
515   aNode->update();
516   return aNode;
517 }
518
519 void PartSet_FolderNode::update()
520 {
521   DocumentPtr aDoc = document();
522   if (!aDoc.get())
523     return;
524
525   // Remove extra sub-nodes
526   int aIndex;
527   int aId = 0;
528   while (aId < myChildren.size()) {
529     ModuleBase_ITreeNode* aNode = myChildren.at(aId);
530     aIndex = aDoc->index(aNode->object(), true);
531     if ((aIndex == -1) || (aId != aIndex)) {
532       myChildren.removeAll(aNode);
533       delete aNode;
534     } else
535       aId++;
536   }
537
538   // Add new nodes
539   std::string aGroup = groupName();
540   int aSize = aDoc->size(aGroup, true);
541   for (int i = 0; i < aSize; i++) {
542     ObjectPtr aObj = aDoc->object(aGroup, i, true);
543     if (i < myChildren.size()) {
544       if (myChildren.at(i)->object() != aObj) {
545         ModuleBase_ITreeNode* aNode = createNode(aObj);
546         myChildren.insert(i, aNode);
547       }
548     } else {
549       ModuleBase_ITreeNode* aNode = createNode(aObj);
550       myChildren.append(aNode);
551     }
552   }
553
554   foreach(ModuleBase_ITreeNode* aNode, myChildren) {
555     aNode->update();
556   }
557 }
558
559 std::string PartSet_FolderNode::groupName() const
560 {
561   switch (myType) {
562   case ParametersFolder:
563     return ModelAPI_ResultParameter::group();
564   case ConstructionFolder:
565     return ModelAPI_ResultConstruction::group();
566   case PartsFolder:
567     return ModelAPI_ResultPart::group();
568   case ResultsFolder:
569     return ModelAPI_ResultBody::group();
570   case FieldsFolder:
571     return ModelAPI_ResultField::group();
572   case GroupsFolder:
573     return ModelAPI_ResultGroup::group();
574   }
575   return "";
576 }
577
578 QTreeNodesList PartSet_FolderNode::objectCreated(const QObjectPtrList& theObjects)
579 {
580   QTreeNodesList aResult;
581   std::string aName = groupName();
582   DocumentPtr aDoc = document();
583   int aIdx = -1;
584   QMap<int, ModuleBase_ITreeNode*> aNewNodes;
585   foreach(ObjectPtr aObj, theObjects) {
586     if ((aObj->document() == aDoc) && (aObj->groupName() == aName)) {
587       aIdx = aDoc->index(aObj, true);
588       if (aIdx != -1) {
589         bool aHasObject = (aIdx < myChildren.size()) && (myChildren.at(aIdx)->object() == aObj);
590         if (!aHasObject) {
591           ModuleBase_ITreeNode* aNode = createNode(aObj);
592           aNewNodes[aIdx] = aNode;
593           aResult.append(aNode);
594           aNode->update();
595         }
596       }
597     }
598   }
599   // Add nodes in correct order
600   if (aNewNodes.size() > 0) {
601     int i;
602     for (i = 0; i < myChildren.size(); i++) {
603       if (aNewNodes.contains(i)) {
604         myChildren.insert(i, aNewNodes[i]);
605         aNewNodes.remove(i);
606       }
607     }
608     while (aNewNodes.size()) {
609       i = myChildren.size();
610       myChildren.append(aNewNodes[i]);
611       aNewNodes.remove(i);
612     }
613   }
614   foreach(ModuleBase_ITreeNode* aNode, myChildren) {
615     aResult.append(aNode->objectCreated(theObjects));
616   }
617   return aResult;
618 }
619
620 QTreeNodesList PartSet_FolderNode::objectsDeleted(const DocumentPtr& theDoc,
621   const QString& theGroup)
622 {
623   DocumentPtr aDoc = document();
624   QTreeNodesList aResult;
625   if ((theGroup.toStdString() == groupName()) && (theDoc == aDoc)) {
626     QTreeNodesList aDelList;
627     int aIndex;
628     int aId = 0;
629     bool aRemoved = false;
630     bool aToSort = false;
631     while (aId < myChildren.size()) {
632       ModuleBase_ITreeNode* aNode = myChildren.at(aId);
633       aIndex = aDoc->index(aNode->object(), true);
634       aToSort |= ((aIndex != -1) && (aId != aIndex));
635       if (aIndex == -1) {
636         myChildren.removeAll(aNode);
637         delete aNode;
638         aRemoved = true;
639       }
640       else
641         aId++;
642     }
643     if (aRemoved)
644       aResult.append(this);
645     if (aToSort)
646       sortChildren();
647     foreach(ModuleBase_ITreeNode* aNode, myChildren) {
648       aResult.append(aNode->objectsDeleted(theDoc, theGroup));
649     }
650   }
651   return aResult;
652 }
653
654 //////////////////////////////////////////////////////////////////////////////////
655 QTreeNodesList PartSet_FeatureFolderNode::objectCreated(const QObjectPtrList& theObjects)
656 {
657   QTreeNodesList aResult;
658   // Process the root sub-objects
659   DocumentPtr aDoc = document();
660   int aIdx = -1;
661   int aNb = numberOfFolders();
662   QMap<int, ModuleBase_ITreeNode*> aNewNodes;
663   foreach(ObjectPtr aObj, theObjects) {
664     if (aDoc == aObj->document()) {
665       if ((aObj->groupName() == ModelAPI_Feature::group()) ||
666         (aObj->groupName() == ModelAPI_Folder::group())){
667         aIdx = aDoc->index(aObj, true);
668         if (aIdx != -1) {
669           ModuleBase_ITreeNode* aNode = createNode(aObj);
670           aIdx += aNb;
671           bool aHasObject = (aIdx < myChildren.size()) && (myChildren.at(aIdx)->object() == aObj);
672           if (!aHasObject) {
673             aNewNodes[aIdx] = aNode;
674             aResult.append(aNode);
675             aNode->update();
676           }
677         }
678       }
679     }
680   }
681   // To add in correct order
682   if (aNewNodes.size() > 0) {
683     int i;
684     for (i = 0; i < myChildren.size(); i++) {
685       if (aNewNodes.contains(i)) {
686         myChildren.insert(i, aNewNodes[i]);
687         aNewNodes.remove(i);
688       }
689     }
690     while (aNewNodes.size()) {
691       i = myChildren.size();
692       if (aNewNodes.contains(i)) {
693         myChildren.append(aNewNodes[i]);
694         aNewNodes.remove(i);
695       }
696     }
697   }
698   // Update sub-folders
699   foreach(ModuleBase_ITreeNode* aNode, myChildren) {
700     aResult.append(aNode->objectCreated(theObjects));
701   }
702   return aResult;
703 }
704
705 QTreeNodesList PartSet_FeatureFolderNode::objectsDeleted(const DocumentPtr& theDoc,
706   const QString& theGroup)
707 {
708   QTreeNodesList aResult;
709
710   // Process sub-folders
711   foreach(ModuleBase_ITreeNode* aNode, myChildren) {
712     if (aNode->childrenCount() > 0) { // aFolder node
713       QTreeNodesList aList = aNode->objectsDeleted(theDoc, theGroup);
714       if (aList.size() > 0)
715         aResult.append(aList);
716     }
717   }
718
719   // Process root
720   DocumentPtr aDoc = document();
721   int aNb = numberOfFolders();
722   bool isGroup = ((theGroup.toStdString() == ModelAPI_Feature::group()) ||
723     (theGroup.toStdString() == ModelAPI_Folder::group()));
724   if ((theDoc == aDoc) && isGroup) {
725     int aIndex;
726     int aId = 0;
727     bool aRemoved = false;
728     bool aToSort = false;
729     while (aId < myChildren.size()) {
730       ModuleBase_ITreeNode* aNode = myChildren.at(aId);
731       if (aNode->object().get()) {
732         aIndex = aDoc->index(aNode->object(), true);
733         aToSort |= ((aIndex != -1) && (aId != (aIndex + aNb)));
734         if (aIndex == -1) {
735           myChildren.removeAll(aNode);
736           delete aNode;
737           aRemoved = true;
738           continue;
739         }
740       }
741       aId++;
742     }
743     if (aRemoved)
744       aResult.append(this);
745     if (aToSort)
746       sortChildren();
747   }
748   return aResult;
749 }
750
751 ModuleBase_ITreeNode* PartSet_FeatureFolderNode::findParent(const DocumentPtr& theDoc,
752   QString theGroup)
753 {
754   ModuleBase_ITreeNode* aResult = 0;
755   foreach(ModuleBase_ITreeNode* aNode, myChildren) {
756     aResult = aNode->findParent(theDoc, theGroup);
757     if (aResult) {
758       return aResult;
759     }
760   }
761   bool isGroup = ((theGroup.toStdString() == ModelAPI_Feature::group()) ||
762     (theGroup.toStdString() == ModelAPI_Folder::group()));
763   if ((theDoc == document()) && isGroup)
764     return this;
765   return 0;
766 }
767
768
769 //////////////////////////////////////////////////////////////////////////////////
770 PartSet_RootNode::PartSet_RootNode() : PartSet_FeatureFolderNode(0), myWorkshop(0)
771 {
772   SessionPtr aSession = ModelAPI_Session::get();
773   DocumentPtr aDoc = aSession->moduleDocument();
774
775   myParamsFolder = new PartSet_FolderNode(this, PartSet_FolderNode::ParametersFolder);
776   myConstrFolder = new PartSet_FolderNode(this, PartSet_FolderNode::ConstructionFolder);
777   myPartsFolder = new PartSet_FolderNode(this, PartSet_FolderNode::PartsFolder);
778
779   myChildren.append(myParamsFolder);
780   myChildren.append(myConstrFolder);
781   myChildren.append(myPartsFolder);
782
783   update();
784 }
785
786
787 void PartSet_RootNode::update()
788 {
789   myParamsFolder->update();
790   myConstrFolder->update();
791   myPartsFolder->update();
792
793   // Update features content
794   DocumentPtr aDoc = document();
795   int aNb = numberOfFolders();
796
797   // Remove extra sub-nodes
798   int aIndex;
799   int aId = 0;
800   while (aId < myChildren.size()) {
801     ModuleBase_ITreeNode* aNode = myChildren.at(aId);
802     if (aNode->object().get()) {
803       aIndex = aDoc->index(aNode->object(), true);
804       if ((aIndex == -1) || (aId != (aIndex + aNb))) {
805         myChildren.removeAll(aNode);
806         delete aNode;
807         continue;
808       }
809     }
810     aId++;
811   }
812
813   // Add new nodes
814   std::string aGroup = ModelAPI_Feature::group();
815   int aSize = aDoc->size(aGroup, true);
816   FeaturePtr aFeature;
817   for (int i = 0; i < aSize; i++) {
818     ObjectPtr aObj = aDoc->object(aGroup, i, true);
819     aId = i + aNb; // Take into account existing folders
820     if (aId < myChildren.size()) {
821       if (myChildren.at(aId)->object() != aObj) {
822         ModuleBase_ITreeNode* aNode = createNode(aObj);
823         myChildren.insert(aId, aNode);
824       }
825     } else {
826       ModuleBase_ITreeNode* aNode = createNode(aObj);
827       myChildren.append(aNode);
828     }
829   }
830   // Update sub-folders
831   foreach(ModuleBase_ITreeNode* aNode, myChildren) {
832     if ((aNode->type() == PartSet_ObjectFolderNode::typeId()) ||
833       (aNode->type() == PartSet_PartRootNode::typeId()))
834       aNode->update();
835   }
836 }
837
838 DocumentPtr PartSet_RootNode::document() const
839 {
840   return ModelAPI_Session::get()->moduleDocument();
841 }
842
843 ModuleBase_ITreeNode* PartSet_RootNode::createNode(const ObjectPtr& theObj)
844 {
845   if (theObj->groupName() == ModelAPI_Folder::group())
846     return new PartSet_ObjectFolderNode(theObj, this);
847
848   FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObj);
849   if (aFeature->getKind() == PartSetPlugin_Part::ID())
850     return new PartSet_PartRootNode(theObj, this);
851
852   PartSet_ObjectNode* aNode = new PartSet_ObjectNode(theObj, this);
853   aNode->update();
854   return aNode;
855 }
856
857 //////////////////////////////////////////////////////////////////////////////////
858 PartSet_PartRootNode::PartSet_PartRootNode(const ObjectPtr& theObj, ModuleBase_ITreeNode* theParent)
859   : PartSet_FeatureFolderNode(theParent), myObject(theObj)
860 {
861   myParamsFolder = new PartSet_FolderNode(this, PartSet_FolderNode::ParametersFolder);
862   myConstrFolder = new PartSet_FolderNode(this, PartSet_FolderNode::ConstructionFolder);
863   myResultsFolder = new PartSet_FolderNode(this, PartSet_FolderNode::ResultsFolder);
864   myFieldsFolder = new PartSet_FolderNode(this, PartSet_FolderNode::FieldsFolder);
865   myGroupsFolder = new PartSet_FolderNode(this, PartSet_FolderNode::GroupsFolder);
866
867   myChildren.append(myParamsFolder);
868   myChildren.append(myConstrFolder);
869   myChildren.append(myResultsFolder);
870
871   update();
872 }
873
874 void PartSet_PartRootNode::deleteChildren()
875 {
876   if (!myFieldsFolder->childrenCount()) {
877     delete myFieldsFolder;
878   }
879   if (!myGroupsFolder->childrenCount()) {
880     delete myGroupsFolder;
881   }
882   PartSet_FeatureFolderNode::deleteChildren();
883 }
884
885
886 void PartSet_PartRootNode::update()
887 {
888   DocumentPtr aDoc = document();
889   if (!aDoc.get())
890     return;
891
892   myParamsFolder->update();
893   myConstrFolder->update();
894   myResultsFolder->update();
895   myFieldsFolder->update();
896   myGroupsFolder->update();
897
898   bool aHasFields = myFieldsFolder->childrenCount() > 0;
899   bool aHasGroups = myGroupsFolder->childrenCount() > 0;
900   if (aHasFields) {
901     if (!myChildren.contains(myFieldsFolder)) {
902       myChildren.insert(3, myFieldsFolder);
903     }
904   } else if (myChildren.contains(myFieldsFolder)) {
905     myChildren.removeAll(myFieldsFolder);
906   }
907   if (aHasGroups) {
908     if (!myChildren.contains(myGroupsFolder)) {
909       myChildren.insert(aHasFields ? 4 : 3, myGroupsFolder);
910     }
911   } else if (myChildren.contains(myGroupsFolder)) {
912     myChildren.removeAll(myGroupsFolder);
913   }
914
915   // Update features content
916   int aRows = numberOfFolders();
917
918   // Remove extra sub-nodes
919   int aIndex = -1;
920   int aId = aRows;
921   QMap<int, ModuleBase_ITreeNode*> aExistingNodes;
922   while (aId < myChildren.size()) {
923     ModuleBase_ITreeNode* aNode = myChildren.at(aId);
924     if (aNode->object().get()) {
925       aIndex = aDoc->index(aNode->object(), true);
926       if ((aIndex == -1) || (aId != (aIndex + aRows))) {
927         myChildren.removeAll(aNode);
928         if (aIndex == -1)
929           delete aNode;
930         else
931           aExistingNodes[aIndex + aRows] = aNode;
932         continue;
933       }
934     }
935     aId++;
936   }
937
938   std::string aGroup = ModelAPI_Feature::group();
939   int aSize = aDoc->size(aGroup, true);
940   FeaturePtr aFeature;
941   for (int i = 0; i < aSize; i++) {
942     ObjectPtr aObj = aDoc->object(aGroup, i, true);
943     aId = i + aRows; // Take into account existing folders
944     if (aId < myChildren.size()) {
945       if (myChildren.at(aId)->object() != aObj) {
946         if (aExistingNodes.contains(aId)) {
947           myChildren.insert(aId, aExistingNodes[aId]);
948           aExistingNodes.remove(aId);
949         }
950         else {
951           myChildren.insert(aId, createNode(aObj));
952         }
953       }
954     } else {
955       if (aExistingNodes.contains(myChildren.size()))
956         myChildren.append(aExistingNodes[myChildren.size()]);
957       else
958         myChildren.append(createNode(aObj));
959     }
960   }
961   // Update sub-folders
962   foreach(ModuleBase_ITreeNode* aNode, myChildren) {
963     if (aNode->type() == PartSet_ObjectFolderNode::typeId())
964       aNode->update();
965   }
966 }
967
968 DocumentPtr PartSet_PartRootNode::document() const
969 {
970   ResultPartPtr aPartRes = getPartResult(myObject);
971   if (aPartRes.get())
972     return aPartRes->partDoc();
973   return DocumentPtr();
974 }
975
976 QVariant PartSet_PartRootNode::data(int theColumn, int theRole) const
977 {
978   switch (theColumn) {
979   case 1:
980     switch (theRole) {
981     case Qt::DisplayRole:
982     {
983       ResultPartPtr aPartRes = getPartResult(myObject);
984       if (aPartRes.get()) {
985         if (aPartRes->partDoc().get() == NULL)
986           return QString::fromStdWString(myObject->data()->name()) + " (Not loaded)";
987       }
988       return QString::fromStdWString(myObject->data()->name());
989     }
990     case Qt::DecorationRole:
991       return ModuleBase_IconFactory::get()->getIcon(myObject);
992     }
993   case 2:
994     if (theRole == Qt::DecorationRole) {
995       if (isCurrentFeature(myObject))
996         return QIcon(":pictures/arrow.png");
997       else
998         return QIcon();
999     }
1000   }
1001   return PartSet_TreeNode::data(theColumn, theRole);
1002 }
1003
1004 Qt::ItemFlags PartSet_PartRootNode::flags(int theColumn) const
1005 {
1006   if (myObject->isDisabled())
1007     return (theColumn == 2) ? Qt::ItemIsSelectable : aNullFlag;
1008
1009   SessionPtr aSession = ModelAPI_Session::get();
1010   DocumentPtr aActiveDoc = aSession->activeDocument();
1011   if ((aActiveDoc == document()) || (myObject->document() == aActiveDoc))
1012     return aEditingFlag;
1013   return aDefaultFlag;
1014 }
1015
1016 ModuleBase_ITreeNode* PartSet_PartRootNode::createNode(const ObjectPtr& theObj)
1017 {
1018   if (theObj->groupName() == ModelAPI_Folder::group())
1019     return new PartSet_ObjectFolderNode(theObj, this);
1020   PartSet_ObjectNode* aNode = new PartSet_ObjectNode(theObj, this);
1021   aNode->update();
1022   return aNode;
1023 }
1024
1025 int PartSet_PartRootNode::numberOfFolders() const
1026 {
1027   int aNb = 3;
1028   if (myFieldsFolder->childrenCount() > 0)
1029     aNb++;
1030   if (myGroupsFolder->childrenCount() > 0)
1031     aNb++;
1032   return aNb;
1033 }
1034
1035 QTreeNodesList PartSet_PartRootNode::objectCreated(const QObjectPtrList& theObjects)
1036 {
1037   QTreeNodesList aResult = PartSet_FeatureFolderNode::objectCreated(theObjects);
1038   if (!myFieldsFolder->childrenCount()) {
1039     QTreeNodesList aList = myFieldsFolder->objectCreated(theObjects);
1040     if (aList.size()) {
1041       myChildren.insert(3, myFieldsFolder);
1042       aResult.append(myFieldsFolder);
1043       aResult.append(aList);
1044     }
1045   }
1046   if (!myGroupsFolder->childrenCount()) {
1047     QTreeNodesList aList = myGroupsFolder->objectCreated(theObjects);
1048     if (aList.size()) {
1049       myChildren.insert(myFieldsFolder->childrenCount()? 4 : 3, myGroupsFolder);
1050       aResult.append(myGroupsFolder);
1051       aResult.append(aList);
1052     }
1053   }
1054   return aResult;
1055 }
1056
1057 QTreeNodesList PartSet_PartRootNode::objectsDeleted(const DocumentPtr& theDoc,
1058   const QString& theGroup)
1059 {
1060   QTreeNodesList aResult;
1061   if (myFieldsFolder->childrenCount()) {
1062     QTreeNodesList aList = myFieldsFolder->objectsDeleted(theDoc, theGroup);
1063     if (aList.size()) {
1064       aResult.append(aList);
1065       if (!myFieldsFolder->childrenCount())
1066         myChildren.removeAll(myFieldsFolder);
1067     }
1068   }
1069   if (myGroupsFolder->childrenCount()) {
1070     QTreeNodesList aList = myGroupsFolder->objectsDeleted(theDoc, theGroup);
1071     if (aList.size()) {
1072       aResult.append(aList);
1073       if (!myGroupsFolder->childrenCount())
1074         myChildren.removeAll(myGroupsFolder);
1075     }
1076   }
1077   aResult.append(PartSet_FeatureFolderNode::objectsDeleted(theDoc, theGroup));
1078   return aResult;
1079 }
1080
1081 //////////////////////////////////////////////////////////////////////////////////
1082 void PartSet_ObjectFolderNode::update()
1083 {
1084   int aFirst = -1, aLast = -1;
1085   PartSet_Tools::getFirstAndLastIndexInFolder(myObject, aFirst, aLast);
1086   if ((aFirst == -1) || (aLast == -1)) {
1087     deleteChildren();
1088     return;
1089   }
1090
1091   int aNbItems = aLast - aFirst + 1;
1092   if (!aNbItems) {
1093     deleteChildren();
1094     return;
1095   }
1096
1097   DocumentPtr aDoc = myObject->document();
1098   if (aNbItems < myChildren.size()) {
1099     // Delete obsolete nodes
1100     int aId = 0;
1101     int aNbOfFeatures = aDoc->size(ModelAPI_Feature::group(), true);
1102     while (aId < myChildren.size()) {
1103       ModuleBase_ITreeNode* aNode = myChildren.at(aId);
1104       if ((aId < aNbItems) && ((aFirst + aId) < aNbOfFeatures)) {
1105         if (aNode->object() != aDoc->object(ModelAPI_Feature::group(), aFirst + aId)) {
1106           myChildren.removeAll(aNode);
1107           delete aNode;
1108           continue;
1109         }
1110       }
1111       else {
1112         myChildren.removeAll(aNode);
1113         delete aNode;
1114         continue;
1115       }
1116       aId++;
1117     }
1118   }
1119   if (aNbItems > myChildren.size()) {
1120     // Add new nodes
1121     ModuleBase_ITreeNode* aNode;
1122     for (int i = 0; i < aNbItems; i++) {
1123       ObjectPtr aObj = aDoc->object(ModelAPI_Feature::group(), aFirst + i);
1124       if (i < myChildren.size()) {
1125         if (aObj != myChildren.at(i)->object()) {
1126           aNode = new PartSet_ObjectNode(aObj, this);
1127           myChildren.insert(i, aNode);
1128           aNode->update();
1129         }
1130       }
1131       else {
1132         aNode = new PartSet_ObjectNode(aObj, this);
1133         myChildren.append(aNode);
1134         aNode->update();
1135       }
1136     }
1137   }
1138 }
1139
1140 QTreeNodesList PartSet_ObjectFolderNode::objectCreated(const QObjectPtrList& /*theObjects*/)
1141 {
1142   QTreeNodesList aResult;
1143   int aFirst = -1, aLast = -1;
1144   PartSet_Tools::getFirstAndLastIndexInFolder(myObject, aFirst, aLast);
1145   if ((aFirst == -1) || (aLast == -1)) {
1146     return aResult;
1147   }
1148   int aNbItems = aLast - aFirst + 1;
1149   if (!aNbItems) {
1150     return aResult;
1151   }
1152   DocumentPtr aDoc = myObject->document();
1153   // Add new nodes
1154   ModuleBase_ITreeNode* aNode;
1155   for (int i = 0; i < aNbItems; i++) {
1156     ObjectPtr aObj = aDoc->object(ModelAPI_Feature::group(), aFirst + i);
1157     if (i < myChildren.size()) {
1158       if (aObj != myChildren.at(i)->object()) {
1159         aNode = new PartSet_ObjectNode(aObj, this);
1160         myChildren.insert(i, aNode);
1161         aResult.append(aNode);
1162         aNode->update();
1163       }
1164     } else {
1165       aNode = new PartSet_ObjectNode(aObj, this);
1166       myChildren.append(aNode);
1167       aResult.append(aNode);
1168       aNode->update();
1169     }
1170   }
1171   return aResult;
1172 }
1173
1174 QTreeNodesList PartSet_ObjectFolderNode::objectsDeleted(const DocumentPtr& /*theDoc*/,
1175   const QString& /*theGroup*/)
1176 {
1177   QTreeNodesList aResult;
1178   int aFirst = -1, aLast = -1;
1179   PartSet_Tools::getFirstAndLastIndexInFolder(myObject, aFirst, aLast);
1180   if ((aFirst == -1) || (aLast == -1)) {
1181     return aResult;
1182   }
1183   int aNbItems = aLast - aFirst + 1;
1184   if (!aNbItems) {
1185     return aResult;
1186   }
1187   if (aNbItems >= myChildren.size()) // Nothing was deleted here
1188     return aResult;
1189
1190   DocumentPtr aDoc = myObject->document();
1191   // Delete obsolete nodes
1192   bool aRemoved = false;
1193   int aId = 0;
1194   int aNbOfFeatures = aDoc->size(ModelAPI_Feature::group(), true);
1195   while (aId < myChildren.size()) {
1196     ModuleBase_ITreeNode* aNode = myChildren.at(aId);
1197     if ((aFirst + aId) < aNbOfFeatures) {
1198       if (aNode->object() != aDoc->object(ModelAPI_Feature::group(), aFirst + aId)) {
1199         myChildren.removeAll(aNode);
1200         delete aNode;
1201         aRemoved = true;
1202         continue;
1203       }
1204     }
1205     else {
1206       myChildren.removeAll(aNode);
1207       aResult.removeAll(aNode);
1208       delete aNode;
1209       aRemoved = true;
1210       continue;
1211     }
1212     aId++;
1213   }
1214   if (aRemoved) {
1215     aResult.append(this);
1216   }
1217   return aResult;
1218 }
1219
1220 QVariant PartSet_ObjectFolderNode::data(int theColumn, int theRole) const
1221 {
1222   if (theRole == Qt::ForegroundRole) {
1223     if (!myObject->isDisabled()) {
1224       std::vector<int> aColor =
1225         Config_PropManager::color("Visualization", "feature_objectbrowser_color");
1226       return QColor(aColor[0], aColor[1], aColor[2]);
1227     }
1228   }
1229   return PartSet_ObjectNode::data(theColumn, theRole);
1230
1231 }
1232
1233 //////////////////////////////////////////////////////////////////////////////////
1234 QVariant PartSet_StepNode::data(int theColumn, int theRole) const
1235 {
1236   if ((theColumn == 1) && (theRole == Qt::DisplayRole)) {
1237     FieldStepPtr aStep =
1238       std::dynamic_pointer_cast<ModelAPI_ResultField::ModelAPI_FieldStep>(myObject);
1239
1240     return "Step " + QString::number(aStep->id() + 1) + " " +
1241       aStep->field()->textLine(aStep->id()).c_str();
1242   }
1243   return PartSet_ObjectNode::data(theColumn, theRole);
1244 }
1245
1246 ModuleBase_ITreeNode::VisibilityState PartSet_StepNode::visibilityState() const
1247 {
1248   Qt::ItemFlags aFlags = flags(1);
1249   if (aFlags == Qt::ItemFlags())
1250     return NoneState;
1251
1252   ModuleBase_IWorkshop* aWork = workshop();
1253   if (aWork->isVisible(myObject))
1254     return Visible;
1255   else
1256     return Hidden;
1257 }