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