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