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