Salome HOME
6d97460513d09610720625cf81714964b819b9fb
[modules/gui.git] / src / SalomeApp / SalomeApp_Study.cxx
1 // Copyright (C) 2007-2023  CEA, EDF, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 #ifndef DISABLE_PYCONSOLE
24   #include "SalomeApp_PyInterp.h" // WARNING! This include must be the first!
25 #endif
26
27 #include "SalomeApp_Study.h"
28
29 #include "SalomeApp_Module.h"
30 #include "SalomeApp_DataObject.h"
31 #include "SalomeApp_DataModel.h"
32 #include "SalomeApp_Application.h"
33 #include "SalomeApp_Engine_i.h"
34 #include "SalomeApp_VisualState.h"
35
36 // temporary commented
37 //#include <OB_Browser.h>
38
39 #include <QCoreApplication>
40 #include <QEvent>
41 #include <QFileInfo>
42 #include "SALOME_Event.h"
43 #include "Basics_Utils.hxx"
44
45 #include <SUIT_ResourceMgr.h>
46 #include <SUIT_TreeModel.h>
47 #include <SUIT_DataBrowser.h>
48 #include <SUIT_MessageBox.h>
49 #include <SUIT_Session.h>
50 #include <SUIT_Desktop.h>
51
52 #include <LightApp_Displayer.h>
53
54 #include "utilities.h"
55
56 #include "SALOMEDS_Tool.hxx"
57
58 #include "SALOMEDSClient_ClientFactory.hxx"
59
60 #include <SALOMEconfig.h>
61 #include CORBA_SERVER_HEADER(SALOME_Exception)
62
63 //---------------------------------------------------------
64 #define USE_DEBUG
65 //#define MB_IGNORE_QT
66 //#define MB_FULL_DUMP
67 #define MBCLASSNAME "SalomeApp_Study"
68 #include "MBDebug.h"
69 // <-- insert includes for addtional debug headers here!
70 //---------------------------------------------------------
71
72 //#define NOTIFY_BY_EVENT
73
74 class ObserverEvent : public QEvent
75 {
76 public:
77   ObserverEvent(const char* theID, CORBA::Long event):QEvent(QEvent::User)
78   {
79     _anID=theID;
80     _event=event;
81   }
82
83   std::string _anID;
84   CORBA::Long _event;
85 };
86
87 class SalomeApp_Study::Observer_i : public virtual POA_SALOMEDS::Observer, QObject
88 {
89   typedef std::map<std::string, SalomeApp_DataObject*>           EntryMap;
90   typedef std::map<std::string, SalomeApp_DataObject*>::iterator EntryMapIter;
91
92 public:
93
94   Observer_i( SalomeApp_Study* aStudy):QObject(aStudy)
95   {
96     myStudy=aStudy;
97     fillEntryMap();
98   }
99
100   SUIT_DataObject* findObject( const char* theID ) const
101   {
102     EntryMap::const_iterator it = entry2SuitObject.find( theID );
103     return it != entry2SuitObject.end() ? it->second : 0;
104   }
105
106   virtual void notifyObserverID(const char* theID, CORBA::Long event)
107   {
108 #ifdef NOTIFY_BY_EVENT
109     QCoreApplication::postEvent(this,new ObserverEvent(theID,event));
110 #else
111     notifyObserverID_real(theID,event);
112 #endif
113   }
114
115   virtual bool event(QEvent *event)
116   {
117     if (event->type() == QEvent::User )
118     {
119       //START_TIMING(notify);
120       notifyObserverID_real(static_cast<ObserverEvent *>(event)->_anID.c_str(),static_cast<ObserverEvent *>(event)->_event);
121       //END_TIMING(notify,100);
122     }
123     return true;
124   }
125
126   void notifyObserverID_real(const std::string& theID, long event)
127   {
128     SalomeApp_DataObject* suit_obj = 0;
129
130     switch(event) {
131     case 1:
132       { //Add sobject
133         _PTR(SObject) aSObj = SalomeApp_Application::getStudy()->FindObjectID(theID);
134         _PTR(SComponent) aSComp = aSObj->GetFatherComponent();
135
136         if (!aSComp || aSComp->IsNull()) {
137           MESSAGE("Entry " << theID << " has not father component. Problem ??");
138           return;
139         }
140
141         // Mantis issue 0020136: Drag&Drop in OB
142         _PTR(UseCaseBuilder) aUseCaseBuilder = SalomeApp_Application::getStudy()->GetUseCaseBuilder();
143         if (aUseCaseBuilder->IsUseCaseNode(aSComp)) { // BEGIN: work with tree nodes structure
144           if (!aUseCaseBuilder->IsUseCaseNode(aSObj)) {
145             // tree node is not yet set, it is a normal situation
146             return;
147           }
148
149           _PTR(SObject) aFatherSO = aUseCaseBuilder->GetFather(aSObj);
150           if (!aFatherSO || aFatherSO->IsNull()) {
151             MESSAGE("Father SObject is not found. Problem ??");
152             return;
153           }
154
155           std::string parent_id = aFatherSO->GetID();
156           EntryMapIter it = entry2SuitObject.find(parent_id.c_str());
157
158           if (it == entry2SuitObject.end()) {
159             MESSAGE("Father data object is not found. Problem ??");
160             return;
161           }
162
163           SalomeApp_DataObject* aFatherDO = it->second;
164
165           it = entry2SuitObject.find(theID);
166           if (it != entry2SuitObject.end()) { // this SOobject is already added somewhere
167             suit_obj = it->second;
168             SUIT_DataObject* oldFather = suit_obj->parent();
169             if (oldFather) {
170               oldFather->removeChild(suit_obj, false);
171               SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( myStudy->application() );
172 //              MESSAGE("myStudy: " << myStudy->id() << " app " << app);
173 //              MESSAGE("objectBrowser: "<< app->objectBrowser());
174               if (!app->objectBrowser()) {
175                 MESSAGE("Object Browser not found. Problem ??");
176                 return;
177               }
178               SUIT_AbstractModel* model = dynamic_cast<SUIT_AbstractModel*>(app->objectBrowser()->model());
179               model->forgetObject( suit_obj );
180                 
181               if (SalomeApp_DataObject* oldFatherSA = dynamic_cast<SalomeApp_DataObject*>(oldFather)) {
182                 oldFatherSA->updateItem();
183               }
184             }
185           }
186           else {
187             suit_obj = new SalomeApp_DataObject(aSObj);
188             entry2SuitObject[theID] = suit_obj;
189           }
190
191           suit_obj->updateItem();
192           // define position in the data tree (in aFatherDO) to insert the aSObj
193           int pos = aUseCaseBuilder->GetIndexInFather(aFatherSO, aSObj);
194
195           aFatherDO->insertChildAtPos(suit_obj, pos);
196           //aFatherDO->insertChild(suit_obj, pos);
197           aFatherDO->updateItem();
198
199           /* Define visibility state */
200           bool isComponent = dynamic_cast<SalomeApp_ModuleObject*>( suit_obj ) != 0;
201           if ( suit_obj && !isComponent && myStudy->visibilityState( theID.c_str() ) == Qtx::UnpresentableState ) {
202             LightApp_Displayer* aDisplayer = LightApp_Displayer::FindDisplayer
203               (suit_obj->componentDataType(),false);
204             if (aDisplayer) {
205               if (aDisplayer->canBeDisplayed(theID.c_str())) {
206                 //hide the just added object
207                 myStudy->setVisibilityState( theID.c_str(), Qtx::HiddenState );
208                 //MESSAGE("Object with entry : "<< theID <<" CAN be displayed !!!");
209               }
210               //else
211               //  MESSAGE("Object with entry : "<< theID <<" CAN'T be displayed !!!");
212             }
213           }
214         } // END: work with tree nodes structure
215         else { // BEGIN: work with study structure
216           EntryMapIter it = entry2SuitObject.find( theID );
217           if ( it != entry2SuitObject.end() ) {
218             MESSAGE("Entry " << theID << " is already added. Problem ??");
219             return;
220           }
221
222           int last2Pnt_pos = (int)theID.rfind( ":" ); //!< TODO: conversion from size_t to int
223           std::string parent_id = theID.substr( 0, last2Pnt_pos );
224           int tag = atoi( theID.substr( last2Pnt_pos+1 ).c_str() );
225
226           if ( parent_id.length() == 3 ) // "0:1" - root item?
227           {
228             // It's probably a SComponent
229             if ( theID == aSComp->GetID() )
230               suit_obj = new SalomeApp_ModuleObject( aSComp );
231             else
232               suit_obj = new SalomeApp_DataObject( aSObj );
233           }
234           else
235           {
236             suit_obj = new SalomeApp_DataObject( aSObj );
237           }
238
239           it = entry2SuitObject.find( parent_id );
240           if ( it != entry2SuitObject.end() ) {
241             SalomeApp_DataObject* father = it->second;
242             father->insertChildAtTag( suit_obj, tag );
243           }
244           else {
245             if ( parent_id.length() == 3 ) // "0:1" - root item?
246             {
247               // This should be for a module
248               SUIT_DataObject* father=myStudy->root();
249               father->appendChild(suit_obj);
250             }
251             else
252             {
253               MESSAGE("SHOULD NEVER GET HERE!!!");
254
255               //Try to find the SalomeApp_DataObject object parent
256               std::string root_id = parent_id.substr( 0, 4 );
257               std::string obj_id = parent_id.substr( 4 );
258
259               std::string anID;
260               std::string::size_type debut = 0;
261               std::string::size_type fin;
262               SalomeApp_DataObject* anObj = dynamic_cast<SalomeApp_DataObject*>( myStudy->root() );
263               while ( anObj ) {
264                 fin = obj_id.find_first_of( ':', debut );
265                 if ( fin == std::string::npos ) {
266                   //last id
267                   anObj = dynamic_cast<SalomeApp_DataObject*>(anObj->childObject(atoi(obj_id.substr(debut).c_str())-1));
268                   if ( anObj )
269                     entry2SuitObject[parent_id] = anObj;
270                   break;
271                 }
272                 anID = root_id + obj_id.substr( 0, fin );
273                 EntryMapIter it2 = entry2SuitObject.find( anID );
274                 if ( it2 == entry2SuitObject.end() ) {
275                   //the ID is not known in entry2SuitObject
276                   anObj = dynamic_cast<SalomeApp_DataObject*>(anObj->childObject(atoi(obj_id.substr(debut, fin-debut).c_str())-1));
277                   if ( anObj )
278                     entry2SuitObject[anID] = anObj;
279                 }
280                 else
281                   anObj = it2->second;
282                 debut = fin+1;
283               }
284               if ( anObj )
285                 anObj->insertChildAtTag( suit_obj, tag );
286             }
287           }
288           entry2SuitObject[theID] = suit_obj;
289         } // END: work with study structure
290         break;
291       }
292     case 2:
293       { // Remove sobject
294         EntryMapIter it = entry2SuitObject.find( theID );
295         if ( it != entry2SuitObject.end() )
296         {
297           suit_obj = it->second;
298           suit_obj->updateItem();
299           SUIT_DataObject* father=suit_obj->parent();
300           if(father)
301             father->removeChild(suit_obj);
302           entry2SuitObject.erase(it);
303         }
304         else
305         {
306           MESSAGE("Want to remove an unknown object" << theID);
307         }
308         break;
309       }
310     case 0:
311       { //modify sobject
312         //MESSAGE("Want to modify an object "  << theID);
313         EntryMapIter it = entry2SuitObject.find( theID );
314         if ( it != entry2SuitObject.end() )
315         {
316           suit_obj = it->second;
317           suit_obj->updateItem();
318         }
319         else
320         {
321           MESSAGE("Want to modify an unknown object"  << theID);
322         }
323         break;
324       }
325     case 5: //IOR of the object modified
326       {
327         EntryMapIter it = entry2SuitObject.find( theID );
328         if ( it != entry2SuitObject.end() )
329           suit_obj = it->second;
330
331         /* Define visibility state */
332         bool isComponent = dynamic_cast<SalomeApp_ModuleObject*>( suit_obj ) != 0;
333         if ( suit_obj && !isComponent ) {
334           LightApp_Displayer* aDisplayer = LightApp_Displayer::FindDisplayer
335             (suit_obj->componentDataType(),false);
336           if (aDisplayer) {
337             if (aDisplayer->canBeDisplayed(theID.c_str())) {
338               myStudy->setVisibilityState( theID.c_str(), Qtx::HiddenState );
339               //MESSAGE("Object with entry : "<< theID <<" CAN be displayed !!!");
340             }
341             //else
342             //  MESSAGE("Object with entry : "<< theID <<" CAN'T be displayed !!!");
343           }
344         }
345         break;
346       }
347 #ifndef DISABLE_PYCONSOLE
348     case 6: //NoteBook variables were modified
349       {
350         myStudy->onNoteBookVarUpdate( QString( theID.c_str() ) );
351         break;
352       }
353 #endif
354     default:MESSAGE("Unknown event: "  << event);break;
355     } //switch
356   } //notifyObserverID_real
357
358 private:
359   void fillEntryMap()
360   {
361     entry2SuitObject.clear();
362     SUIT_DataObject* o = myStudy->root();
363     while (o) {
364       SalomeApp_DataObject* so = dynamic_cast<SalomeApp_DataObject*>( o );
365       if ( so ) {
366         std::string entry = so->entry().toUtf8().constData();
367         if ( entry.size() )
368           entry2SuitObject[entry] = so;
369       }
370       if ( o->childCount() > 0 ) {
371         // parse the children
372         o = o->firstChild();
373       }
374       else if ( o->nextBrother() ) {
375         o = o->nextBrother();
376       }
377       else {
378         // step to the next appropriate parent
379         o = o->parent();
380         while ( o ) {
381           if ( o->nextBrother() ) {
382             o = o->nextBrother();
383             break;
384           }
385           o = o->parent();
386         }
387       }
388     }
389   }
390
391 private:
392   SalomeApp_Study* myStudy;
393   EntryMap         entry2SuitObject;
394 };
395
396
397 /*!
398   Constructor.
399 */
400 SalomeApp_Study::SalomeApp_Study( SUIT_Application* app )
401 : LightApp_Study( app ), myObserver( 0 )
402 {
403   DBG_FUN();
404   myStudyDS = SalomeApp_Application::getStudy();
405 }
406
407 /*!
408   Destructor.
409 */
410 SalomeApp_Study::~SalomeApp_Study()
411 {
412   DBG_FUN();
413   if ( myObserver ) {
414     PortableServer::ObjectId_var oid = myObserver->_default_POA()->servant_to_id( myObserver );
415     myObserver->_default_POA()->deactivate_object( oid.in() );
416     myObserver = 0;
417   }
418 }
419
420 #ifndef DISABLE_PYCONSOLE
421 void SalomeApp_Study::onNoteBookVarUpdate( QString theVarName)
422 {
423   emit notebookVarUpdated( theVarName );
424 }
425 #endif
426
427 /*!
428   Get study name.
429 */
430 QString SalomeApp_Study::studyName() const
431 {
432   // redefined from SUIT_Study to update study name properly since
433   // it can be changed outside of GUI
434   // TEMPORARILY SOLUTION: better to be implemented with help of SALOMEDS observers
435   if ( studyDS() ) {
436     QString newName = QString::fromUtf8(studyDS()->URL().c_str());
437     if ( newName.isEmpty() )
438       newName = QString::fromUtf8(studyDS()->Name().c_str());
439     if ( LightApp_Study::studyName() != newName ) {
440       SalomeApp_Study* that = const_cast<SalomeApp_Study*>( this );
441       that->setStudyName( newName );
442       ((SalomeApp_Application*)application())->updateDesktopTitle();
443     }
444   }
445   return LightApp_Study::studyName();
446 }
447
448 /*!
449   Gets studyDS pointer.
450 */
451 _PTR(Study) SalomeApp_Study::studyDS() const
452 {
453   return myStudyDS;
454 }
455
456 /*!
457   Create document.
458 */
459 bool SalomeApp_Study::createDocument( const QString& theStr )
460 {
461   DBG_FUN();
462   ARG(theStr);
463   MESSAGE( "createDocument" );
464
465   setStudyName( QString::fromUtf8(myStudyDS->URL().c_str()) );
466
467   // create myRoot
468   SalomeApp_RootObject* aRoot=new SalomeApp_RootObject( this );
469 #ifdef WITH_SALOMEDS_OBSERVER
470   aRoot->setToSynchronize(false);
471 #endif
472   setRoot( aRoot );
473
474   bool aRet = CAM_Study::createDocument( theStr );
475
476 #ifdef WITH_SALOMEDS_OBSERVER
477   myObserver = new Observer_i(this);
478   //attach an observer to the study with notification of modifications
479   myStudyDS->attach(myObserver->_this(),true);
480 #endif
481
482   emit created( this );
483
484   return aRet;
485 }
486
487 /*!
488   Opens document
489   \param theFileName - name of file
490 */
491 bool SalomeApp_Study::openDocument( const QString& theFileName )
492 {
493   DBG_FUN();
494   ARG(theFileName);
495   MESSAGE( "openDocument" );
496
497   // read HDF file
498   bool res = false;
499   bool showError = !application()->property("open_study_from_command_line").isValid() ||
500     !application()->property("open_study_from_command_line").toBool();
501   try {
502     res = myStudyDS->Open( theFileName.toUtf8().data() );
503   }
504   catch(const SALOME_Exception& ex) {
505     application()->putInfo(tr(ex.what()));
506     if ( showError )
507       SUIT_MessageBox::critical( SUIT_Session::session()->activeApplication()->desktop(),
508                                  tr("ERR_ERROR"), tr(ex.what()));
509     return false;
510   }
511   catch(...) {
512     application()->putInfo(tr("OPEN_DOCUMENT_PROBLEM"));
513     if ( showError )
514       SUIT_MessageBox::critical( SUIT_Session::session()->activeApplication()->desktop(),
515                                  tr("ERR_ERROR"), tr("OPEN_DOCUMENT_PROBLEM"));
516     return false;
517   }
518
519   if ( !res)
520     return false;
521
522   setRoot( new SalomeApp_RootObject( this ) ); // create myRoot
523
524   // update loaded data models: call open() and update() on them.
525   ModelList dm_s;
526   dataModels( dm_s );
527   QListIterator<CAM_DataModel*> it( dm_s );
528   while ( it.hasNext() )
529     openDataModel( studyName(), it.next() );
530
531   // this will build a SUIT_DataObject-s tree under myRoot member field
532   // passing "false" in order NOT to rebuild existing data models' trees - it was done in previous step
533   // but tree that corresponds to not-loaded data models will be updated any way.
534   ((SalomeApp_Application*)application())->updateObjectBrowser( false );
535
536 #ifdef WITH_SALOMEDS_OBSERVER
537   dynamic_cast<SalomeApp_RootObject*>( root() )->setToSynchronize(false);
538   myObserver = new Observer_i(this);
539   //attach an observer to the study with notification of modifications
540   myStudyDS->attach(myObserver->_this(),true);
541 #endif
542
543   res = CAM_Study::openDocument( theFileName );
544
545   emit opened( this );
546   myStudyDS->IsSaved(true);
547
548   bool restore = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", true );
549   if ( restore ) {
550     std::vector<int> savePoints = getSavePoints();
551     if ( savePoints.size() > 0 )
552       SalomeApp_VisualState( (SalomeApp_Application*)application() ).restoreState( savePoints[savePoints.size()-1] );
553   }
554
555   ((SalomeApp_Application*)application())->updateObjectBrowser( true );
556   return res;
557 }
558
559 /*!
560   Connects GUI study to SALOMEDS one
561   \param theStudyName - name of study
562 */
563 bool SalomeApp_Study::loadDocument( const QString& theStudyName )
564 {
565   DBG_FUN();
566   ARG(theStudyName);
567   MESSAGE( "loadDocument" );
568
569   setRoot( new SalomeApp_RootObject( this ) ); // create myRoot
570
571   //SRN: BugID IPAL9021, put there the same code as in a method openDocument
572
573   // update loaded data models: call open() and update() on them.
574   ModelList dm_s;
575   dataModels( dm_s );
576
577   QListIterator<CAM_DataModel*> it( dm_s );
578   while ( it.hasNext() )
579     openDataModel( studyName(), it.next() );
580
581   bool res = CAM_Study::openDocument( theStudyName );
582
583   //rnv: to fix the "0051779: TC7.2.0: Save operation works incorrectly for study loaded from data server"
584   //     mark study as "not saved" after call openDocument( ... ) method.
585   setIsSaved(false);
586   emit opened( this ); // myRoot is set to Object Browser here
587
588   // this will build a SUIT_DataObject-s tree under myRoot member field
589   // passing "false" in order NOT to rebuild existing data models' trees - it was done in previous step
590   // but tree that corresponds to not-loaded data models will be updated any way.
591   ((SalomeApp_Application*)application())->updateObjectBrowser( false );
592
593 #ifdef WITH_SALOMEDS_OBSERVER
594   dynamic_cast<SalomeApp_RootObject*>( root() )->setToSynchronize(false);
595   myObserver = new Observer_i(this);
596   //attach an observer to the study with notification of modifications
597   myStudyDS->attach(myObserver->_this(),true);
598 #endif
599
600   bool restore = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", true );
601   if ( restore ) {
602     std::vector<int> savePoints = getSavePoints();
603     if ( savePoints.size() > 0 )
604       SalomeApp_VisualState( (SalomeApp_Application*)application() ).restoreState( savePoints[savePoints.size()-1] );
605   }
606
607   //SRN: BugID IPAL9021: End
608   return res;
609 }
610
611 /*!
612   Saves document
613   \param theFileName - name of file
614 */
615 bool SalomeApp_Study::saveDocumentAs( const QString& theFileName, bool isBackup/*=false*/ )
616 {
617   DBG_FUN();
618   ARG(theFileName);
619   ARG(isBackup);
620
621   bool store = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", false );
622   if ( store && !isBackup )
623     SalomeApp_VisualState( (SalomeApp_Application*)application() ).storeState();
624
625   ModelList list; dataModels( list );
626
627   QListIterator<CAM_DataModel*> it( list );
628   QStringList listOfFiles;
629   while ( it.hasNext() ) {
630     // Cast to LightApp class in order to give a chance
631     // to light modules to save their data
632     if ( LightApp_DataModel* aModel = 
633          dynamic_cast<LightApp_DataModel*>( it.next() ) ) {
634       listOfFiles.clear();
635       aModel->saveAs( theFileName, this, listOfFiles, isBackup );
636       if ( !listOfFiles.isEmpty() )
637         saveModuleData(aModel->module()->name(), 0, // 0 means persistence file
638                        listOfFiles);
639     }
640   }
641
642   // save SALOMEDS document
643   SUIT_ResourceMgr* resMgr = application()->resourceMgr();
644   if( !resMgr )
645     return false;
646
647   bool isMultiFile = resMgr->booleanValue( "Study", "multi_file", false );
648   bool isAscii = resMgr->booleanValue( "Study", "ascii_file", false );
649   bool res = studyDS()->SaveAs( theFileName.toUtf8().data(), isMultiFile, isAscii )
650     && CAM_Study::saveDocumentAs( theFileName );
651
652   res = res && saveStudyData(theFileName, 0); // 0 means persistence file
653
654   if ( res && !isBackup)
655     emit saved( this );
656
657   return res;
658 }
659
660 /*!
661   Saves previously opened document
662 */
663 bool SalomeApp_Study::saveDocument()
664 {
665   DBG_FUN();
666   bool store = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", true );
667   if ( store )
668     SalomeApp_VisualState( (SalomeApp_Application*)application() ).storeState();
669
670   ModelList list; dataModels( list );
671
672   QListIterator<CAM_DataModel*> it( list );
673   QStringList listOfFiles;
674   while ( it.hasNext() ) {
675     // Cast to LightApp class in order to give a chance
676     // to light modules to save their data
677     if ( LightApp_DataModel* aModel = 
678          dynamic_cast<LightApp_DataModel*>( it.next() ) ) {
679       listOfFiles.clear();
680       aModel->save(listOfFiles);
681       if ( !listOfFiles.isEmpty() )
682         saveModuleData(aModel->module()->name(), 0, // 0 means persistence file
683                        listOfFiles);
684     }
685   }
686
687   // save SALOMEDS document
688   SUIT_ResourceMgr* resMgr = application()->resourceMgr();
689   if( !resMgr )
690     return false;
691
692   bool isMultiFile = resMgr->booleanValue( "Study", "multi_file", false );
693   bool isAscii = resMgr->booleanValue( "Study", "ascii_file", false );
694   bool res = studyDS()->Save( isMultiFile, isAscii ) && CAM_Study::saveDocument();
695
696   res = res && saveStudyData(studyName(), 0); // 0 means persistence file
697   if ( res )
698     emit saved( this );
699
700   return res;
701 }
702
703 /*!
704   Closes document
705 */
706 void SalomeApp_Study::closeDocument(bool permanently)
707 {
708   DBG_FUN();
709   LightApp_Study::closeDocument(permanently);
710
711   // close SALOMEDS document
712   if ( myObserver )
713     myStudyDS->detach( myObserver->_this() );
714   if ( permanently ) {
715     SUIT_Desktop* desk = SUIT_Session::session()->activeApplication()->desktop();
716     bool isBlocked = desk->signalsBlocked();
717     desk->blockSignals( true );
718     myStudyDS->Clear();
719     desk->blockSignals( isBlocked );
720 #ifndef DISABLE_PYCONSOLE
721     SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( application() );
722     app->getPyInterp()->destroy();
723 #endif
724   }
725 }
726
727 /*!
728   Dump study operation. Writes a Python dump file using
729   SALOMEDS services. Additionally, gives a chance to light modules
730   to participate in dump study operation.
731
732   \param theFileName - full path to the output Python file
733   \param toPublish - if true, all objects are published in a study 
734   by the output script, including those not orignally present 
735   in the current study.
736   \param isMultiFile - if true, each module's dump is written into 
737   a separate Python file, otherwise a single output file is written
738   \param toSaveGUI - if true, the GUI state is written
739
740   \return - true if the operation succeeds, and false otherwise.
741 */
742 bool SalomeApp_Study::dump( const QString& theFileName, 
743                             bool toPublish, 
744                             bool isMultiFile,
745                             bool toSaveGUI )
746 {
747   DBG_FUN();
748   ARG(theFileName);
749   ARG(toPublish);
750   ARG(isMultiFile);
751   ARG(toSaveGUI);
752   int savePoint = 0;
753   _PTR(AttributeParameter) ap;
754   _PTR(IParameters) ip = ClientFactory::getIParameters(ap);
755
756   if( ip->isDumpPython() )
757     ip->setDumpPython(); //Unset DumpPython flag.
758
759   if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method
760     ip->setDumpPython();
761     //SRN: create a temporary save point
762     savePoint = SalomeApp_VisualState( 
763       dynamic_cast<SalomeApp_Application*>( application() ) ).storeState(); 
764   }
765
766   // Issue 21377 - Each data model is asked to dump its data not present in SALOMEDS study.
767   // This is an optional but important step, it gives a chance to light modules
768   // to dump their data as a part of common dump study operation
769   ModelList list; 
770   dataModels( list );
771
772   QListIterator<CAM_DataModel*> it( list );
773   QStringList listOfFiles;
774   while ( it.hasNext() ) {
775     if ( LightApp_DataModel* aModel = 
776          dynamic_cast<LightApp_DataModel*>( it.next() ) ) {
777       listOfFiles.clear();
778       if ( aModel->dumpPython( theFileName, this, isMultiFile, listOfFiles ) && 
779            !listOfFiles.isEmpty() )
780         // This call simply passes the data model's dump output to SalomeApp_Engine servant.
781         // This code is shared with persistence mechanism.
782         // NOTE: this should be revised if behavior of saveModuleData() changes!
783         saveModuleData(aModel->module()->name(), 1, // 1 means dump file
784                        listOfFiles);
785     }
786   }
787
788   // Now dump SALOMEDS part that also involves SalomeApp_Engine in case if 
789   // any light module is present in the current configuration
790   QFileInfo aFileInfo( theFileName );
791   bool res = myStudyDS->DumpStudy( aFileInfo.absolutePath().toUtf8().data(),
792                                   aFileInfo.baseName().toUtf8().data(),
793                                   toPublish,
794                                   isMultiFile);
795   if ( toSaveGUI )
796     removeSavePoint( savePoint ); //SRN: remove the created temporary save point.
797
798   // Issue 21377 - Clean up light module data in SalomeApp_Engine servant
799   // This code is shared with persistence mechanism.
800   // NOTE: this should be revised if behavior of saveStudyData() changes!
801   saveStudyData( theFileName, 1 ); // 0 means persistence file
802
803   return res;
804 }
805
806 /*!
807   \return true, if study is modified in comparison with last open/save
808 */
809 bool SalomeApp_Study::isModified() const
810 {
811   bool isAnyChanged = studyDS() && studyDS()->IsModified();
812   if (!isAnyChanged)
813     isAnyChanged = LightApp_Study::isModified();
814
815   return isAnyChanged;
816 }
817
818 /*!
819   Set study modified to \a on.
820  */
821 void SalomeApp_Study::Modified()
822 {
823   myStudyDS->Modified();
824   LightApp_Study::Modified();
825 }
826
827 /*!
828   \return if data model is saved
829 */
830 bool SalomeApp_Study::isSaved() const
831 {
832   bool isAllSaved = studyDS() && studyDS()->GetPersistentReference().size();
833   if (!isAllSaved)
834     isAllSaved = LightApp_Study::isSaved();
835
836   return isAllSaved;
837 }
838
839 /*!
840   Saves data of module
841   \param theModuleName - name of module
842   \param theListOfFiles - list of files to be saved
843 */
844 void SalomeApp_Study::saveModuleData( QString theModuleName, int type, QStringList theListOfFiles )
845 {
846   int aNb = theListOfFiles.count();
847   if ( aNb == 0 )
848     return;
849
850   std::vector<std::string> aListOfFiles ( aNb );
851   int anIndex = 0;
852   for ( QStringList::Iterator it = theListOfFiles.begin(); it != theListOfFiles.end(); ++it ) {
853     if ( (*it).isEmpty() )
854       continue;
855     aListOfFiles[anIndex] = (*it).toUtf8().data();
856     anIndex++;
857   }
858   SetListOfFiles(theModuleName.toStdString().c_str(), type, aListOfFiles);
859 }
860
861 /*!
862   Loads data of module
863   \param theModuleName - name of module
864   \param theListOfFiles - list of files to be loaded
865 */
866 void SalomeApp_Study::openModuleData( QString theModuleName, int type, QStringList& theListOfFiles )
867 {
868   std::vector<std::string> aListOfFiles = GetListOfFiles( theModuleName.toStdString().c_str(), type );
869
870   int i, aLength = (int)aListOfFiles.size() - 1; //!< TODO: conversion from size_t to int
871   if ( aLength < 0 )
872     return;
873
874   //Get a temporary directory for saved a file
875   theListOfFiles.append(aListOfFiles[0].c_str());
876
877   for(i = 0; i < aLength; i++)
878     theListOfFiles.append(aListOfFiles[i+1].c_str());
879 }
880
881 /*!
882   Re-implemented from LightApp_Study, actually does not save anything but
883   simply cleans up light modules' data
884 */
885 bool SalomeApp_Study::saveStudyData( const QString& /*theFileName*/, int type )
886 {
887   ModelList list; dataModels( list );
888   QListIterator<CAM_DataModel*> it( list );
889   while ( it.hasNext() ){
890     LightApp_DataModel* aLModel = 
891       dynamic_cast<LightApp_DataModel*>( it.next() );
892     // It is safe to call SetListOfFiles() for any kind of module
893     // because SetListOfFiles() does nothing for full modules :)
894     if ( aLModel )
895       SetListOfFiles(aLModel->module()->name().toStdString().c_str(), type, std::vector<std::string>());
896   }
897   return true;
898 }
899
900 /*!
901   Loads data for study
902 */
903 bool SalomeApp_Study::openStudyData( const QString& /*theFileName*/, int /*type*/ )
904 {
905   return true;
906 }
907
908 /*!
909   Virtual method re-implemented from LightApp_Study in order to create
910   the module object connected to SALOMEDS - SalomeApp_ModuleObject.
911
912   \param theDataModel - data model instance to create a module object for
913   \param theParent - the module object's parent (normally it's the study root)
914   \return the module object instance
915   \sa LightApp_Study class, LightApp_DataModel class
916 */
917 CAM_ModuleObject* SalomeApp_Study::createModuleObject( LightApp_DataModel* theDataModel, 
918                                                        SUIT_DataObject* theParent ) const
919 {
920   SalomeApp_Study* that = const_cast<SalomeApp_Study*>( this );
921   
922   // Ensure that SComponent instance is published in the study for the given module
923   // This line causes automatic creation of SalomeApp_ModuleObject in case if
924   // WITH_SALOMEDS_OBSERVER is defined
925   that->addComponent( theDataModel );
926   
927   // SalomeApp_ModuleObject might have been created by SALOMEDS observer
928   // or by someone else so check if it exists first of all
929   CAM_ModuleObject* res = 0;
930
931   DataObjectList children = root()->children();
932   DataObjectList::const_iterator anIt = children.begin(), aLast = children.end();
933   for( ; !res && anIt!=aLast; anIt++ )
934   {
935     SalomeApp_ModuleObject* obj = dynamic_cast<SalomeApp_ModuleObject*>( *anIt );
936     if ( obj && obj->componentDataType() == theDataModel->module()->name() )
937       res = obj;
938   }
939
940   if ( !res ){
941     _PTR(SComponent) aComp = myStudyDS->FindComponent(
942       theDataModel->module()->name().toStdString() );
943     if ( !aComp )
944       return res;
945
946     res = new SalomeApp_ModuleObject( theDataModel, aComp, theParent );
947   }
948
949   return res;
950 }
951
952 /*!
953   Insert data model.
954 */
955 void SalomeApp_Study::dataModelInserted (const CAM_DataModel* dm)
956 {
957   MESSAGE("SalomeApp_Study::dataModelInserted() : module name() = " << dm->module()->name().toStdString());
958
959   CAM_Study::dataModelInserted(dm);
960
961   //  addComponent(dm);
962 }
963
964 /*!
965   Create SComponent for module, using default engine (CORBAless)
966 */
967 void SalomeApp_Study::addComponent(const CAM_DataModel* dm)
968 {
969   SalomeApp_Module* aModule = dynamic_cast<SalomeApp_Module*>( dm->module() );
970   // 1. aModule == 0 means that this is a light module (no CORBA enigine)
971   if (!aModule) {
972     // Check SComponent existance
973
974     std::string aCompDataType = dm->module()->name().toStdString();
975
976     _PTR(SComponent) aComp = myStudyDS->FindComponent(aCompDataType);
977     if (!aComp) {
978       // Create SComponent
979       _PTR(StudyBuilder) aBuilder = myStudyDS->NewBuilder();
980       aComp = aBuilder->NewComponent(aCompDataType);
981       aBuilder->SetName(aComp, dm->module()->moduleName().toStdString());
982       QString anIconName = dm->module()->iconName();
983       if (!anIconName.isEmpty()) {
984         _PTR(AttributePixMap) anAttr = aBuilder->FindOrCreateAttribute(aComp, "AttributePixMap");
985         if (anAttr)
986           anAttr->SetPixMap(anIconName.toStdString());
987       }
988
989       // Set default engine IOR
990       // Issue 21377 - using separate engine for each type of light module
991       std::string anEngineIOR = SalomeApp_Engine_i::EngineIORForComponent( aCompDataType.c_str(),
992                                                                            true );
993       aBuilder->DefineComponentInstance(aComp, anEngineIOR);
994       //SalomeApp_DataModel::BuildTree( aComp, root(), this, /*skipExisitng=*/true );
995       SalomeApp_DataModel::synchronize( aComp, this );
996     }
997     else {
998       _PTR(StudyBuilder) aBuilder = myStudyDS->NewBuilder();
999       aBuilder->SetName(aComp, dm->module()->moduleName().toStdString());
1000       QString anIconName = dm->module()->iconName();
1001       if (!anIconName.isEmpty()) {
1002         _PTR(AttributePixMap) anAttr = aBuilder->FindOrCreateAttribute(aComp, "AttributePixMap");
1003         if (anAttr)
1004           anAttr->SetPixMap(anIconName.toStdString());
1005       }
1006       // Set default engine IOR
1007     }
1008   }
1009 }
1010
1011 /*!
1012   Open data model
1013 */
1014 bool SalomeApp_Study::openDataModel( const QString& studyName, CAM_DataModel* dm )
1015 {
1016   if (!dm)
1017     return false;
1018
1019   //  SalomeApp_DataModel* aDM = (SalomeApp_DataModel*)(dm);
1020   SalomeApp_Module* aModule = dynamic_cast<SalomeApp_Module*>( dm->module() );
1021   _PTR(SComponent)  aSComp;
1022   QString anEngine;
1023   // 1. aModule == 0 means that this is a light module (no CORBA enigine)
1024   if (!aModule) {
1025     // Issue 21377 - using separate engine for each type of light module
1026     std::string aCompDataType = dm->module()->name().toStdString();
1027     anEngine = SalomeApp_Engine_i::EngineIORForComponent( aCompDataType.c_str(), true ).c_str();
1028     aSComp = myStudyDS->FindComponent( aCompDataType );
1029   }
1030   else {
1031     SalomeApp_DataModel* aDM = dynamic_cast<SalomeApp_DataModel*>( dm );
1032     if ( aDM ) {
1033       QString anId = aDM->getRootEntry( this );
1034       if ( anId.isEmpty() )
1035         return true; // Probably nothing to load
1036       anEngine = aDM->getModule()->engineIOR();
1037       if ( anEngine.isEmpty() )
1038         return false;
1039       aSComp = myStudyDS->FindComponentID( std::string( anId.toLatin1() ) );
1040     }
1041   }
1042   if ( aSComp ) {
1043     _PTR(StudyBuilder) aBuilder( myStudyDS->NewBuilder() );
1044     if ( aBuilder ) {
1045       try {
1046         aBuilder->LoadWith( aSComp, std::string( anEngine.toLatin1() ) );
1047       }
1048       catch( const SALOME::SALOME_Exception& ) {
1049         // Oops, something went wrong while loading -> return an error
1050         return false;
1051       }
1052       // Something has been read -> create data model tree
1053       //SalomeApp_DataModel* aDM = dynamic_cast<SalomeApp_DataModel*>( dm );
1054       // aDM->buildTree( aSComp, 0, this );
1055     }
1056   } else {
1057     // Don't return false here, for there might be no data
1058     // for a given component in the study yet
1059   }
1060   QStringList listOfFiles;
1061   openModuleData(dm->module()->name(), 0, // 0 means persistence file
1062                  listOfFiles);
1063   if (dm && dm->open(studyName, this, listOfFiles)) {
1064     // Remove the files and temporary directory, created
1065     // for this module by LightApp_Engine_i::Load()
1066     bool isMultiFile = false; // TODO: decide, how to access this parameter
1067     RemoveTemporaryFiles( dm->module()->name().toStdString().c_str(), isMultiFile, true );
1068     SetListOfFiles( dm->module()->name().toStdString().c_str(), 0, // 0 means persistence file
1069                     std::vector<std::string>() );
1070
1071     // Something has been read -> create data model tree
1072     LightApp_DataModel* aDM = dynamic_cast<LightApp_DataModel*>( dm );
1073     if ( aDM )
1074       aDM->update(NULL, this);
1075     return true;
1076   }
1077   return false;
1078 }
1079
1080 /*!
1081   Note that this method does not create or activate SalomeApp_Engine_i instance,
1082   therefore it can be called safely for any kind of module, but for full
1083   modules it returns an empty list.
1084   \return list of files used by module: to be used by CORBAless modules
1085   \param theModuleName - name of module
1086 */
1087 std::vector<std::string> SalomeApp_Study::GetListOfFiles( const char* theModuleName, int type ) const
1088 {
1089   // Issue 21377 - using separate engine for each type of light module
1090   SalomeApp_Engine_i* aDefaultEngine = SalomeApp_Engine_i::GetInstance( theModuleName, false );
1091   if (aDefaultEngine)
1092     return aDefaultEngine->GetListOfFiles( type );
1093
1094   return std::vector<std::string>();
1095 }
1096
1097 /*!
1098   Sets list of files used by module: to be used by CORBAless modules.
1099   Note that this method does not create or activate SalomeApp_Engine_i instance,
1100   therefore it can be called safely for any kind of module, but for full
1101   modules it simply does nothing.
1102   \param theModuleName - name of module
1103   \param theListOfFiles - list of files
1104 */
1105 void SalomeApp_Study::SetListOfFiles ( const char* theModuleName, int type,
1106                                        const std::vector<std::string> theListOfFiles )
1107 {
1108   // Issue 21377 - using separate engine for each type of light module
1109   SalomeApp_Engine_i* aDefaultEngine = SalomeApp_Engine_i::GetInstance( theModuleName, false );
1110   if (aDefaultEngine)
1111     aDefaultEngine->SetListOfFiles(type, theListOfFiles);
1112 }
1113
1114 /*!
1115   \return temporary directory for saving files of modules
1116 */
1117 std::string SalomeApp_Study::GetTmpDir ( const char* theURL, const bool  isMultiFile )
1118 {
1119   std::string anURLDir = SALOMEDS_Tool::GetDirFromPath(theURL);
1120   std::string aTmpDir = isMultiFile ? anURLDir : SALOMEDS_Tool::GetTmpDir();
1121   return aTmpDir;
1122 }
1123
1124 /*!
1125   Removes temporary files
1126 */
1127 void SalomeApp_Study::RemoveTemporaryFiles ( const char* theModuleName, bool isMultiFile, bool force )
1128 {
1129   if (isMultiFile)
1130     return;
1131
1132   SALOMEDS_Tool::ListOfFiles aListOfFiles = GetListOfFiles( theModuleName, 0 );
1133   if (aListOfFiles.size() > 0) {
1134     std::string aTmpDir = aListOfFiles[0];
1135
1136     const int n = (int)aListOfFiles.size() - 1; //!< TODO: conversion from size_t to int
1137     std::vector<std::string> aSeq;
1138     aSeq.reserve(n);
1139     for (int i = 0; i < n; i++)
1140       aSeq.push_back(CORBA::string_dup(aListOfFiles[i + 1].c_str()));
1141
1142     SalomeApp_Engine_i* engine = SalomeApp_Engine_i::GetInstance( theModuleName, false );
1143     bool toRemove = force || ( engine && !engine->keepFiles() );
1144
1145     if ( toRemove ) {
1146       SALOMEDS_Tool::RemoveTemporaryFiles(aTmpDir.c_str(), aSeq, true);
1147       engine->keepFiles( false );
1148     }
1149   }
1150 }
1151
1152 #ifndef DISABLE_PYCONSOLE
1153 /*!
1154   Mark the study as saved in the file
1155   \param theFileName - the name of file
1156 */
1157 void SalomeApp_Study::updateFromNotebook( const QString& theFileName, bool isSaved )
1158 {
1159   setStudyName(theFileName);
1160   studyDS()->URL(theFileName.toStdString());
1161   setIsSaved( isSaved );
1162 }
1163 #endif
1164
1165 LightApp_DataObject* SalomeApp_Study::findObjectByEntry( const QString& theEntry )
1166 {
1167   LightApp_DataObject* o = 0;
1168   if ( myObserver ) {
1169     o = dynamic_cast<LightApp_DataObject*>( myObserver->findObject( theEntry.toUtf8().constData() ) );
1170   }
1171   if ( !o ) {
1172     o = LightApp_Study::findObjectByEntry( theEntry );
1173   }
1174   return o;
1175 }
1176
1177 /*!
1178   Deletes all references to object
1179   \param obj - object
1180 */
1181 void SalomeApp_Study::deleteReferencesTo( _PTR( SObject ) obj )
1182 {
1183   _PTR(StudyBuilder) sb = studyDS()->NewBuilder();
1184   std::vector<_PTR(SObject)> aRefs = studyDS()->FindDependances( obj );
1185   for( size_t i=0, n=aRefs.size(); i<n; i++ )
1186   {
1187     _PTR( SObject ) o = aRefs[i];
1188     if( o->GetFatherComponent()->ComponentDataType()==obj->GetFatherComponent()->ComponentDataType() )
1189     {
1190       sb->RemoveReference( o );
1191       sb->RemoveObjectWithChildren( o );
1192     }
1193   }
1194 }
1195
1196 /*!
1197   \return real entry by entry of reference
1198   \param entry - entry of reference object
1199 */
1200 QString SalomeApp_Study::referencedToEntry( const QString& entry ) const
1201 {
1202   _PTR(SObject) obj = studyDS()->FindObjectID( entry.toStdString() );
1203   _PTR(SObject) refobj;
1204
1205   if( obj && obj->ReferencedObject( refobj ) )
1206     return refobj->GetID().c_str();
1207   return LightApp_Study::referencedToEntry( entry );
1208 }
1209
1210 /*!
1211   \return component data type for entry
1212 */
1213 QString SalomeApp_Study::componentDataType( const QString& entry ) const
1214 {
1215   _PTR(SObject) obj( studyDS()->FindObjectID( entry.toStdString() ) );
1216   if ( !obj )
1217     return LightApp_Study::componentDataType( entry );
1218   return obj->GetFatherComponent()->ComponentDataType().c_str();
1219 }
1220
1221 /*!
1222   \return true if entry corresponds to component
1223 */
1224 bool SalomeApp_Study::isComponent( const QString& entry ) const
1225 {
1226   _PTR(SObject) obj( studyDS()->FindObjectID( entry.toStdString() ) );
1227   return obj && QString( obj->GetID().c_str() ) == obj->GetFatherComponent()->GetID().c_str();
1228 }
1229
1230 /*!
1231   \return entries of object children
1232 */
1233 void SalomeApp_Study::children( const QString& entry, QStringList& child_entries ) const
1234 {
1235   _PTR(SObject) SO = studyDS()->FindObjectID( entry.toStdString() );
1236   _PTR(ChildIterator) anIter ( studyDS()->NewChildIterator( SO ) );
1237   anIter->InitEx( true );
1238   while( anIter->More() )
1239   {
1240     _PTR(SObject) val( anIter->Value() );
1241     child_entries.append( val->GetID().c_str() );
1242     anIter->Next();
1243   }
1244 }
1245
1246 /*!
1247   Fills list with components names
1248   \param comp - list to be filled
1249 */
1250 void SalomeApp_Study::components( QStringList& comps ) const
1251 {
1252   for( _PTR(SComponentIterator) it ( studyDS()->NewComponentIterator() ); it->More(); it->Next() )
1253   {
1254     _PTR(SComponent) aComponent ( it->Value() );
1255     // skip the magic "Interface Applicative" component
1256     if ( aComponent && aComponent->ComponentDataType() != getVisualComponentName().toLatin1().constData() )
1257       comps.append( aComponent->ComponentDataType().c_str() );
1258   }
1259 }
1260
1261 /*!
1262   Get the entry for the given module
1263   \param comp - list to be filled
1264   \return module root's entry
1265 */
1266 QString SalomeApp_Study::centry( const QString& comp ) const
1267 {
1268   QString e;
1269   for( _PTR(SComponentIterator) it ( studyDS()->NewComponentIterator() ); it->More() && e.isEmpty(); it->Next() )
1270   {
1271     _PTR(SComponent) aComponent ( it->Value() );
1272     if ( aComponent && comp == aComponent->ComponentDataType().c_str() )
1273       e = aComponent->GetID().c_str();
1274   }
1275   return e;
1276 }
1277
1278 /*!
1279   \return a list of saved points' IDs
1280 */
1281 std::vector<int> SalomeApp_Study::getSavePoints()
1282 {
1283   std::vector<int> v;
1284
1285   _PTR(SObject) so = studyDS()->FindComponent( getVisualComponentName().toLatin1().constData() );
1286   if(!so) return v;
1287
1288   _PTR(StudyBuilder) builder = studyDS()->NewBuilder();
1289   _PTR(ChildIterator) anIter ( studyDS()->NewChildIterator( so ) );
1290   for(; anIter->More(); anIter->Next())
1291   {
1292     _PTR(SObject) val( anIter->Value() );
1293     _PTR(GenericAttribute) genAttr;
1294     if(builder->FindAttribute(val, genAttr, "AttributeParameter")) v.push_back(val->Tag());
1295   }
1296
1297   return v;
1298 }
1299
1300 /*!
1301   Removes a given save point
1302 */
1303 void SalomeApp_Study::removeSavePoint(int savePoint)
1304 {
1305   if(savePoint <= 0) return;
1306  _PTR(AttributeParameter) AP = studyDS()->GetCommonParameters(getVisualComponentName().toLatin1().constData(), savePoint);
1307   _PTR(SObject) so = AP->GetSObject();
1308   _PTR(StudyBuilder) builder = studyDS()->NewBuilder();
1309   builder->RemoveObjectWithChildren(so);
1310 }
1311
1312 /*!
1313   \return a name of save point
1314 */
1315 QString SalomeApp_Study::getNameOfSavePoint(int savePoint)
1316 {
1317   _PTR(AttributeParameter) AP = studyDS()->GetCommonParameters(getVisualComponentName().toLatin1().constData(), savePoint);
1318   _PTR(IParameters) ip = ClientFactory::getIParameters(AP);
1319   return ip->getProperty("AP_SAVEPOINT_NAME").c_str();
1320 }
1321
1322 /*!
1323   Sets a name of save point
1324 */
1325 void SalomeApp_Study::setNameOfSavePoint(int savePoint, const QString& nameOfSavePoint)
1326 {
1327   _PTR(AttributeParameter) AP = studyDS()->GetCommonParameters(getVisualComponentName().toLatin1().constData(), savePoint);
1328   _PTR(IParameters) ip = ClientFactory::getIParameters(AP);
1329   ip->setProperty("AP_SAVEPOINT_NAME", nameOfSavePoint.toStdString());
1330 }
1331
1332 /*!
1333  * \brief Restores the study state
1334  */
1335 void SalomeApp_Study::restoreState(int savePoint)
1336 {
1337   SalomeApp_VisualState((SalomeApp_Application*)application()).restoreState(savePoint);
1338 }
1339
1340
1341 /*!
1342   Slot: called on change of a root of a data model. Redefined from CAM_Study
1343 */
1344 void SalomeApp_Study::updateModelRoot( const CAM_DataModel* dm )
1345 {
1346   LightApp_Study::updateModelRoot( dm );
1347
1348   // calling updateSavePointDataObjects in order to set correct order of "Gui states" object
1349   // it must always be the last one.
1350   ((SalomeApp_Application*)application())->updateSavePointDataObjects( this );
1351 }