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