Salome HOME
Replace Close operation by New
[modules/gui.git] / src / SalomeApp / SalomeApp_Study.cxx
1 // Copyright (C) 2007-2016  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 somethere
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 = -1;
185           //int childDataObjCount = aFatherDO->childCount();
186           _PTR(UseCaseIterator) aUseCaseIter = aUseCaseBuilder->GetUseCaseIterator(aFatherSO);
187           for (int cur = 0; aUseCaseIter->More() && pos < 0; cur++, aUseCaseIter->Next()) {
188             if (aUseCaseIter->Value()->GetID() == theID) {
189               pos = cur;
190               break;
191             }
192           }
193
194           aFatherDO->insertChildAtPos(suit_obj, pos);
195           //aFatherDO->insertChild(suit_obj, pos);
196           aFatherDO->updateItem();
197
198           /* Define visibility state */
199           bool isComponent = dynamic_cast<SalomeApp_ModuleObject*>( suit_obj ) != 0;
200           if ( suit_obj && !isComponent && myStudy->visibilityState( theID.c_str() ) == Qtx::UnpresentableState ) {
201             QString moduleTitle = ((CAM_Application*)myStudy->application())->moduleTitle(suit_obj->componentDataType());
202             if (!moduleTitle.isEmpty()) {
203               LightApp_Displayer* aDisplayer = LightApp_Displayer::FindDisplayer(moduleTitle,false);
204               if (aDisplayer) {
205                 if(aDisplayer->canBeDisplayed(theID.c_str())) {
206                   myStudy->setVisibilityState( theID.c_str(), Qtx::HiddenState ); //hide the just added object
207                   //MESSAGE("Object with entry : "<< theID <<" CAN be displayed !!!");
208                 }
209                 //else
210                   //MESSAGE("Object with entry : "<< theID <<" CAN'T be displayed !!!");
211               }
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 = theID.rfind( ":" );
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           QString moduleTitle = ((CAM_Application*)myStudy->application())->moduleTitle(suit_obj->componentDataType());
335           if (!moduleTitle.isEmpty()) {
336             LightApp_Displayer* aDisplayer = LightApp_Displayer::FindDisplayer(moduleTitle,false);
337             if (aDisplayer) {
338               if(aDisplayer->canBeDisplayed(theID.c_str())) {
339                 myStudy->setVisibilityState( theID.c_str(), Qtx::HiddenState );
340                 //MESSAGE("Object with entry : "<< theID <<" CAN be displayed !!!");
341               }
342               //else
343                 //MESSAGE("Object with entry : "<< theID <<" CAN'T be displayed !!!");
344             }
345           }
346         }
347         break;
348       }
349 #ifndef DISABLE_PYCONSOLE
350     case 6: //NoteBook variables were modified
351       {
352         myStudy->onNoteBookVarUpdate( QString( theID.c_str() ) );
353         break;
354       }
355 #endif
356     default:MESSAGE("Unknown event: "  << event);break;
357     } //switch
358   } //notifyObserverID_real
359
360 private:
361   void fillEntryMap()
362   {
363     entry2SuitObject.clear();
364     SUIT_DataObject* o = myStudy->root();
365     while (o) {
366       SalomeApp_DataObject* so = dynamic_cast<SalomeApp_DataObject*>( o );
367       if ( so ) {
368         std::string entry = so->entry().toUtf8().constData();
369         if ( entry.size() )
370           entry2SuitObject[entry] = so;
371       }
372       if ( o->childCount() > 0 ) {
373         // parse the children
374         o = o->firstChild();
375       }
376       else if ( o->nextBrother() > 0 ) {
377         o = o->nextBrother();
378       }
379       else {
380         // step to the next appropriate parent
381         o = o->parent();
382         while ( o ) {
383           if ( o->nextBrother() ) {
384             o = o->nextBrother();
385             break;
386           }
387           o = o->parent();
388         }
389       }
390     }
391   }
392
393 private:
394   SalomeApp_Study* myStudy;
395   EntryMap         entry2SuitObject;
396 };
397
398
399 /*!
400   Constructor.
401 */
402 SalomeApp_Study::SalomeApp_Study( SUIT_Application* app )
403 : LightApp_Study( app ), myObserver( 0 )
404 {
405   myStudyDS = SalomeApp_Application::getStudy();
406 }
407
408 /*!
409   Destructor.
410 */
411 SalomeApp_Study::~SalomeApp_Study()
412 {
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   MESSAGE( "createDocument" );
462
463   setStudyName( QString::fromUtf8(myStudyDS->URL().c_str()) );
464
465   // create myRoot
466   SalomeApp_RootObject* aRoot=new SalomeApp_RootObject( this );
467 #ifdef WITH_SALOMEDS_OBSERVER
468   aRoot->setToSynchronize(false);
469 #endif
470   setRoot( aRoot );
471
472   bool aRet = CAM_Study::createDocument( theStr );
473
474 #ifdef WITH_SALOMEDS_OBSERVER
475   myObserver = new Observer_i(this);
476   //attach an observer to the study with notification of modifications
477   myStudyDS->attach(myObserver->_this(),true);
478 #endif
479
480   emit created( this );
481
482   return aRet;
483 }
484
485 /*!
486   Opens document
487   \param theFileName - name of file
488 */
489 bool SalomeApp_Study::openDocument( const QString& theFileName )
490 {
491   MESSAGE( "openDocument" );
492
493   // read HDF file
494   bool res = false;
495   bool showError = !application()->property("open_study_from_command_line").isValid() ||
496     !application()->property("open_study_from_command_line").toBool();
497   try {
498     res = myStudyDS->Open( theFileName.toUtf8().data() );
499   }
500   catch(const SALOME_Exception& ex) {
501     application()->putInfo(tr(ex.what()));
502     if ( showError )
503       SUIT_MessageBox::critical( SUIT_Session::session()->activeApplication()->desktop(),
504                                  tr("ERR_ERROR"), tr(ex.what()));
505     return false;
506   }
507   catch(...) {
508     application()->putInfo(tr("OPEN_DOCUMENT_PROBLEM"));
509     if ( showError )
510       SUIT_MessageBox::critical( SUIT_Session::session()->activeApplication()->desktop(),
511                                  tr("ERR_ERROR"), tr("OPEN_DOCUMENT_PROBLEM"));
512     return false;
513   }
514
515   if ( !res)
516     return false;
517
518   setRoot( new SalomeApp_RootObject( this ) ); // create myRoot
519
520   // update loaded data models: call open() and update() on them.
521   ModelList dm_s;
522   dataModels( dm_s );
523   QListIterator<CAM_DataModel*> it( dm_s );
524   while ( it.hasNext() )
525     openDataModel( studyName(), it.next() );
526
527   // this will build a SUIT_DataObject-s tree under myRoot member field
528   // passing "false" in order NOT to rebuild existing data models' trees - it was done in previous step
529   // but tree that corresponds to not-loaded data models will be updated any way.
530   ((SalomeApp_Application*)application())->updateObjectBrowser( false );
531
532 #ifdef WITH_SALOMEDS_OBSERVER
533   dynamic_cast<SalomeApp_RootObject*>( root() )->setToSynchronize(false);
534   myObserver = new Observer_i(this);
535   //attach an observer to the study with notification of modifications
536   myStudyDS->attach(myObserver->_this(),true);
537 #endif
538
539   res = CAM_Study::openDocument( theFileName );
540
541   emit opened( this );
542   myStudyDS->IsSaved(true);
543
544   bool restore = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", true );
545   if ( restore ) {
546     std::vector<int> savePoints = getSavePoints();
547     if ( savePoints.size() > 0 )
548       SalomeApp_VisualState( (SalomeApp_Application*)application() ).restoreState( savePoints[savePoints.size()-1] );
549   }
550
551   ((SalomeApp_Application*)application())->updateObjectBrowser( true );
552   return res;
553 }
554
555 /*!
556   Connects GUI study to SALOMEDS one
557   \param theStudyName - name of study
558 */
559 bool SalomeApp_Study::loadDocument( const QString& theStudyName )
560 {
561   MESSAGE( "loadDocument" );
562
563   setRoot( new SalomeApp_RootObject( this ) ); // create myRoot
564
565   //SRN: BugID IPAL9021, put there the same code as in a method openDocument
566
567   // update loaded data models: call open() and update() on them.
568   ModelList dm_s;
569   dataModels( dm_s );
570
571   QListIterator<CAM_DataModel*> it( dm_s );
572   while ( it.hasNext() )
573     openDataModel( studyName(), it.next() );
574
575   bool res = CAM_Study::openDocument( theStudyName );
576
577   //rnv: to fix the "0051779: TC7.2.0: Save operation works incorrectly for study loaded from data server"
578   //     mark study as "not saved" after call openDocument( ... ) method.
579   setIsSaved(false);
580   emit opened( this ); // myRoot is set to Object Browser here
581
582   // this will build a SUIT_DataObject-s tree under myRoot member field
583   // passing "false" in order NOT to rebuild existing data models' trees - it was done in previous step
584   // but tree that corresponds to not-loaded data models will be updated any way.
585   ((SalomeApp_Application*)application())->updateObjectBrowser( false );
586
587 #ifdef WITH_SALOMEDS_OBSERVER
588   dynamic_cast<SalomeApp_RootObject*>( root() )->setToSynchronize(false);
589   myObserver = new Observer_i(this);
590   //attach an observer to the study with notification of modifications
591   myStudyDS->attach(myObserver->_this(),true);
592 #endif
593
594   bool restore = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", true );
595   if ( restore ) {
596     std::vector<int> savePoints = getSavePoints();
597     if ( savePoints.size() > 0 )
598       SalomeApp_VisualState( (SalomeApp_Application*)application() ).restoreState( savePoints[savePoints.size()-1] );
599   }
600
601   //SRN: BugID IPAL9021: End
602   return res;
603 }
604
605 /*!
606   Saves document
607   \param theFileName - name of file
608 */
609 bool SalomeApp_Study::saveDocumentAs( const QString& theFileName )
610 {
611   bool store = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", false );
612   if ( store )
613     SalomeApp_VisualState( (SalomeApp_Application*)application() ).storeState();
614
615   ModelList list; dataModels( list );
616
617   QListIterator<CAM_DataModel*> it( list );
618   QStringList listOfFiles;
619   while ( it.hasNext() ) {
620     // Cast to LightApp class in order to give a chance
621     // to light modules to save their data
622     if ( LightApp_DataModel* aModel = 
623          dynamic_cast<LightApp_DataModel*>( it.next() ) ) {
624       listOfFiles.clear();
625       aModel->saveAs( theFileName, this, listOfFiles );
626       if ( !listOfFiles.isEmpty() )
627         saveModuleData(aModel->module()->name(), listOfFiles);
628     }
629   }
630
631   // save SALOMEDS document
632   SUIT_ResourceMgr* resMgr = application()->resourceMgr();
633   if( !resMgr )
634     return false;
635
636   bool isMultiFile = resMgr->booleanValue( "Study", "multi_file", false );
637   bool isAscii = resMgr->booleanValue( "Study", "ascii_file", false );
638   bool res = studyDS()->SaveAs( theFileName.toUtf8().data(), isMultiFile, isAscii )
639     && CAM_Study::saveDocumentAs( theFileName );
640
641   res = res && saveStudyData(theFileName);
642
643   if ( res )
644     emit saved( this );
645
646   return res;
647 }
648
649 /*!
650   Saves previously opened document
651 */
652 bool SalomeApp_Study::saveDocument()
653 {
654   bool store = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", true );
655   if ( store )
656     SalomeApp_VisualState( (SalomeApp_Application*)application() ).storeState();
657
658   ModelList list; dataModels( list );
659
660   QListIterator<CAM_DataModel*> it( list );
661   QStringList listOfFiles;
662   while ( it.hasNext() ) {
663     // Cast to LightApp class in order to give a chance
664     // to light modules to save their data
665     if ( LightApp_DataModel* aModel = 
666          dynamic_cast<LightApp_DataModel*>( it.next() ) ) {
667       listOfFiles.clear();
668       aModel->save(listOfFiles);
669       if ( !listOfFiles.isEmpty() )
670         saveModuleData(aModel->module()->name(), listOfFiles);
671     }
672   }
673
674   // save SALOMEDS document
675   SUIT_ResourceMgr* resMgr = application()->resourceMgr();
676   if( !resMgr )
677     return false;
678
679   bool isMultiFile = resMgr->booleanValue( "Study", "multi_file", false );
680   bool isAscii = resMgr->booleanValue( "Study", "ascii_file", false );
681   bool res = studyDS()->Save( isMultiFile, isAscii ) && CAM_Study::saveDocument();
682
683   res = res && saveStudyData(studyName());
684   if ( res )
685     emit saved( this );
686
687   return res;
688 }
689
690 /*!
691   Closes document
692 */
693 void SalomeApp_Study::closeDocument(bool permanently)
694 {
695   LightApp_Study::closeDocument(permanently);
696
697   // close SALOMEDS document
698   if ( myObserver )
699     myStudyDS->detach( myObserver->_this() );
700   if ( permanently ) {
701     SUIT_Desktop* desk = SUIT_Session::session()->activeApplication()->desktop();
702     bool isBlocked = desk->signalsBlocked();
703     desk->blockSignals( true );
704     myStudyDS->Clear();
705     desk->blockSignals( isBlocked );
706 #ifndef DISABLE_PYCONSOLE
707     SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( application() );
708     app->getPyInterp()->destroy();
709 #endif
710   }
711 }
712
713 /*!
714   Dump study operation. Writes a Python dump file using
715   SALOMEDS services. Additionally, gives a chance to light modules
716   to participate in dump study operation.
717
718   \param theFileName - full path to the output Python file
719   \param toPublish - if true, all objects are published in a study 
720   by the output script, including those not orignally present 
721   in the current study.
722   \param isMultiFile - if true, each module's dump is written into 
723   a separate Python file, otherwise a single output file is written
724   \param toSaveGUI - if true, the GUI state is written
725
726   \return - true if the operation succeeds, and false otherwise.
727 */
728 bool SalomeApp_Study::dump( const QString& theFileName, 
729                             bool toPublish, 
730                             bool isMultiFile,
731                             bool toSaveGUI )
732 {
733   int savePoint;
734   _PTR(AttributeParameter) ap;
735   _PTR(IParameters) ip = ClientFactory::getIParameters(ap);
736
737   if( ip->isDumpPython() )
738     ip->setDumpPython(); //Unset DumpPython flag.
739
740   if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method
741     ip->setDumpPython();
742     //SRN: create a temporary save point
743     savePoint = SalomeApp_VisualState( 
744       dynamic_cast<SalomeApp_Application*>( application() ) ).storeState(); 
745   }
746
747   // Issue 21377 - Each data model is asked to dump its data not present in SALOMEDS study.
748   // This is an optional but important step, it gives a chance to light modules
749   // to dump their data as a part of common dump study operation
750   ModelList list; 
751   dataModels( list );
752
753   QListIterator<CAM_DataModel*> it( list );
754   QStringList listOfFiles;
755   while ( it.hasNext() ) {
756     if ( LightApp_DataModel* aModel = 
757          dynamic_cast<LightApp_DataModel*>( it.next() ) ) {
758       listOfFiles.clear();
759       if ( aModel->dumpPython( theFileName, this, isMultiFile, listOfFiles ) && 
760            !listOfFiles.isEmpty() )
761         // This call simply passes the data model's dump output to SalomeApp_Engine servant.
762         // This code is shared with persistence mechanism.
763         // NOTE: this should be revised if behavior of saveModuleData() changes!
764         saveModuleData(aModel->module()->name(), listOfFiles);
765     }
766   }
767
768   // Now dump SALOMEDS part that also involves SalomeApp_Engine in case if 
769   // any light module is present in the current configuration
770   QFileInfo aFileInfo( theFileName );
771   bool res = myStudyDS->DumpStudy( aFileInfo.absolutePath().toUtf8().data(),
772                                   aFileInfo.baseName().toUtf8().data(),
773                                   toPublish,
774                                   isMultiFile);
775   if ( toSaveGUI )
776     removeSavePoint( savePoint ); //SRN: remove the created temporary save point.
777
778   // Issue 21377 - Clean up light module data in SalomeApp_Engine servant
779   // This code is shared with persistence mechanism.
780   // NOTE: this should be revised if behavior of saveStudyData() changes!
781   saveStudyData( theFileName );
782
783   return res;
784 }
785
786 /*!
787   \return true, if study is modified in comparison with last open/save
788 */
789 bool SalomeApp_Study::isModified() const
790 {
791   bool isAnyChanged = studyDS() && studyDS()->IsModified();
792   if (!isAnyChanged)
793     isAnyChanged = LightApp_Study::isModified();
794
795   return isAnyChanged;
796 }
797
798 /*!
799   Set study modified to \a on.
800  */
801 void SalomeApp_Study::Modified()
802 {
803   myStudyDS->Modified();
804   LightApp_Study::Modified();
805 }
806
807 /*!
808   \return if data model is saved
809 */
810 bool SalomeApp_Study::isSaved() const
811 {
812   bool isAllSaved = studyDS() && studyDS()->GetPersistentReference().size();
813   if (!isAllSaved)
814     isAllSaved = LightApp_Study::isSaved();
815
816   return isAllSaved;
817 }
818
819 /*!
820   Saves data of module
821   \param theModuleName - name of module
822   \param theListOfFiles - list of files to be saved
823 */
824 void SalomeApp_Study::saveModuleData( QString theModuleName, QStringList theListOfFiles )
825 {
826   int aNb = theListOfFiles.count();
827   if ( aNb == 0 )
828     return;
829
830   std::vector<std::string> aListOfFiles ( aNb );
831   int anIndex = 0;
832   for ( QStringList::Iterator it = theListOfFiles.begin(); it != theListOfFiles.end(); ++it ) {
833     if ( (*it).isEmpty() )
834       continue;
835     aListOfFiles[anIndex] = (*it).toUtf8().data();
836     anIndex++;
837   }
838   SetListOfFiles(theModuleName.toStdString().c_str(), aListOfFiles);
839 }
840
841 /*!
842   Loads data of module
843   \param theModuleName - name of module
844   \param theListOfFiles - list of files to be loaded
845 */
846 void SalomeApp_Study::openModuleData( QString theModuleName, QStringList& theListOfFiles )
847 {
848   std::vector<std::string> aListOfFiles =  GetListOfFiles( theModuleName.toStdString().c_str() );
849
850   int i, aLength = aListOfFiles.size() - 1;
851   if ( aLength < 0 )
852     return;
853
854   //Get a temporary directory for saved a file
855   theListOfFiles.append(aListOfFiles[0].c_str());
856
857   for(i = 0; i < aLength; i++)
858     theListOfFiles.append(aListOfFiles[i+1].c_str());
859 }
860
861 /*!
862   Re-implemented from LightApp_Study, actually does not save anything but
863   simply cleans up light modules' data
864 */
865 bool SalomeApp_Study::saveStudyData( const QString& theFileName )
866 {
867   ModelList list; dataModels( list );
868   QListIterator<CAM_DataModel*> it( list );
869   std::vector<std::string> listOfFiles(0);
870   while ( it.hasNext() ){
871     LightApp_DataModel* aLModel = 
872       dynamic_cast<LightApp_DataModel*>( it.next() );
873     // It is safe to call SetListOfFiles() for any kind of module
874     // because SetListOfFiles() does nothing for full modules :)
875     if ( aLModel )
876       SetListOfFiles(aLModel->module()->name().toStdString().c_str(), listOfFiles);
877   }
878   return true;
879 }
880
881 /*!
882   Loads data for study
883 */
884 bool SalomeApp_Study::openStudyData( const QString& theFileName )
885 {
886  return true;
887 }
888
889 /*!
890   Virtual method re-implemented from LightApp_Study in order to create
891   the module object connected to SALOMEDS - SalomeApp_ModuleObject.
892
893   \param theDataModel - data model instance to create a module object for
894   \param theParent - the module object's parent (normally it's the study root)
895   \return the module object instance
896   \sa LightApp_Study class, LightApp_DataModel class
897 */
898 CAM_ModuleObject* SalomeApp_Study::createModuleObject( LightApp_DataModel* theDataModel, 
899                                                        SUIT_DataObject* theParent ) const
900 {
901   SalomeApp_Study* that = const_cast<SalomeApp_Study*>( this );
902   
903   // Ensure that SComponent instance is published in the study for the given module
904   // This line causes automatic creation of SalomeApp_ModuleObject in case if
905   // WITH_SALOMEDS_OBSERVER is defined
906   that->addComponent( theDataModel );
907   
908   // SalomeApp_ModuleObject might have been created by SALOMEDS observer
909   // or by someone else so check if it exists first of all
910   CAM_ModuleObject* res = 0;
911
912   DataObjectList children = root()->children();
913   DataObjectList::const_iterator anIt = children.begin(), aLast = children.end();
914   for( ; !res && anIt!=aLast; anIt++ )
915   {
916     SalomeApp_ModuleObject* obj = dynamic_cast<SalomeApp_ModuleObject*>( *anIt );
917     if ( obj && obj->componentDataType() == theDataModel->module()->name() )
918       res = obj;
919   }
920
921   if ( !res ){
922     _PTR(SComponent) aComp = myStudyDS->FindComponent(
923       theDataModel->module()->name().toStdString() );
924     if ( !aComp )
925       return res;
926
927     res = new SalomeApp_ModuleObject( theDataModel, aComp, theParent );
928   }
929
930   return res;
931 }
932
933 /*!
934   Insert data model.
935 */
936 void SalomeApp_Study::dataModelInserted (const CAM_DataModel* dm)
937 {
938   MESSAGE("SalomeApp_Study::dataModelInserted() : module name() = " << dm->module()->name().toStdString());
939
940   CAM_Study::dataModelInserted(dm);
941
942   //  addComponent(dm);
943 }
944
945 /*!
946   Create SComponent for module, using default engine (CORBAless)
947 */
948 void SalomeApp_Study::addComponent(const CAM_DataModel* dm)
949 {
950   SalomeApp_Module* aModule = dynamic_cast<SalomeApp_Module*>( dm->module() );
951   // 1. aModule == 0 means that this is a light module (no CORBA enigine)
952   if (!aModule) {
953     // Check SComponent existance
954
955     std::string aCompDataType = dm->module()->name().toStdString();
956
957     _PTR(SComponent) aComp = myStudyDS->FindComponent(aCompDataType);
958     if (!aComp) {
959       // Create SComponent
960       _PTR(StudyBuilder) aBuilder = myStudyDS->NewBuilder();
961       aComp = aBuilder->NewComponent(aCompDataType);
962       aBuilder->SetName(aComp, dm->module()->moduleName().toStdString());
963       QString anIconName = dm->module()->iconName();
964       if (!anIconName.isEmpty()) {
965         _PTR(AttributePixMap) anAttr = aBuilder->FindOrCreateAttribute(aComp, "AttributePixMap");
966         if (anAttr)
967           anAttr->SetPixMap(anIconName.toStdString());
968       }
969
970       // Set default engine IOR
971       // Issue 21377 - using separate engine for each type of light module
972       std::string anEngineIOR = SalomeApp_Engine_i::EngineIORForComponent( aCompDataType.c_str(),
973                                                                            true );
974       aBuilder->DefineComponentInstance(aComp, anEngineIOR);
975       //SalomeApp_DataModel::BuildTree( aComp, root(), this, /*skipExisitng=*/true );
976       SalomeApp_DataModel::synchronize( aComp, this );
977     }
978     else {
979       _PTR(StudyBuilder) aBuilder = myStudyDS->NewBuilder();
980       aBuilder->SetName(aComp, dm->module()->moduleName().toStdString());
981       QString anIconName = dm->module()->iconName();
982       if (!anIconName.isEmpty()) {
983         _PTR(AttributePixMap) anAttr = aBuilder->FindOrCreateAttribute(aComp, "AttributePixMap");
984         if (anAttr)
985           anAttr->SetPixMap(anIconName.toStdString());
986       }
987       // Set default engine IOR
988     }
989   }
990 }
991
992 /*!
993   Open data model
994 */
995 bool SalomeApp_Study::openDataModel( const QString& studyName, CAM_DataModel* dm )
996 {
997   if (!dm)
998     return false;
999
1000   //  SalomeApp_DataModel* aDM = (SalomeApp_DataModel*)(dm);
1001   SalomeApp_Module* aModule = dynamic_cast<SalomeApp_Module*>( dm->module() );
1002   _PTR(SComponent)  aSComp;
1003   QString anEngine;
1004   // 1. aModule == 0 means that this is a light module (no CORBA enigine)
1005   if (!aModule) {
1006     // Issue 21377 - using separate engine for each type of light module
1007     std::string aCompDataType = dm->module()->name().toStdString();
1008     anEngine = SalomeApp_Engine_i::EngineIORForComponent( aCompDataType.c_str(), true ).c_str();
1009     aSComp = myStudyDS->FindComponent( aCompDataType );
1010   }
1011   else {
1012     SalomeApp_DataModel* aDM = dynamic_cast<SalomeApp_DataModel*>( dm );
1013     if ( aDM ) {
1014       QString anId = aDM->getRootEntry( this );
1015       if ( anId.isEmpty() )
1016         return true; // Probably nothing to load
1017       anEngine = aDM->getModule()->engineIOR();
1018       if ( anEngine.isEmpty() )
1019         return false;
1020       aSComp = myStudyDS->FindComponentID( std::string( anId.toLatin1() ) );
1021     }
1022   }
1023   if ( aSComp ) {
1024     _PTR(StudyBuilder) aBuilder( myStudyDS->NewBuilder() );
1025     if ( aBuilder ) {
1026       try {
1027         aBuilder->LoadWith( aSComp, std::string( anEngine.toLatin1() ) );
1028       }
1029       catch( const SALOME::SALOME_Exception& ) {
1030         // Oops, something went wrong while loading -> return an error
1031         return false;
1032       }
1033       // Something has been read -> create data model tree
1034       //SalomeApp_DataModel* aDM = dynamic_cast<SalomeApp_DataModel*>( dm );
1035       // aDM->buildTree( aSComp, 0, this );
1036     }
1037   } else {
1038     // Don't return false here, for there might be no data
1039     // for a given component in the study yet
1040   }
1041   QStringList listOfFiles;
1042   openModuleData(dm->module()->name(), listOfFiles);
1043   if (dm && dm->open(studyName, this, listOfFiles)) {
1044     // Remove the files and temporary directory, created
1045     // for this module by LightApp_Engine_i::Load()
1046     bool isMultiFile = false; // TODO: decide, how to access this parameter
1047     RemoveTemporaryFiles( dm->module()->name().toStdString().c_str(), isMultiFile );
1048     std::vector<std::string> listOfFiles   ;
1049     SetListOfFiles( dm->module()->name().toStdString().c_str(), listOfFiles );
1050
1051     // Something has been read -> create data model tree
1052     LightApp_DataModel* aDM = dynamic_cast<LightApp_DataModel*>( dm );
1053     if ( aDM )
1054       aDM->update(NULL, this);
1055     return true;
1056   }
1057   return false;
1058 }
1059
1060 /*!
1061   Note that this method does not create or activate SalomeApp_Engine_i instance,
1062   therefore it can be called safely for any kind of module, but for full
1063   modules it returns an empty list.
1064   \return list of files used by module: to be used by CORBAless modules
1065   \param theModuleName - name of module
1066 */
1067 std::vector<std::string> SalomeApp_Study::GetListOfFiles( const char* theModuleName  ) const
1068 {
1069   // Issue 21377 - using separate engine for each type of light module
1070   SalomeApp_Engine_i* aDefaultEngine = SalomeApp_Engine_i::GetInstance( theModuleName, false );
1071   if (aDefaultEngine)
1072     return aDefaultEngine->GetListOfFiles();
1073
1074   std::vector<std::string> aListOfFiles;
1075   return aListOfFiles;
1076 }
1077
1078 /*!
1079   Sets list of files used by module: to be used by CORBAless modules.
1080   Note that this method does not create or activate SalomeApp_Engine_i instance,
1081   therefore it can be called safely for any kind of module, but for full
1082   modules it simply does nothing.
1083   \param theModuleName - name of module
1084   \param theListOfFiles - list of files
1085 */
1086 void SalomeApp_Study::SetListOfFiles ( const char* theModuleName,
1087                                        const std::vector<std::string> theListOfFiles )
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     aDefaultEngine->SetListOfFiles(theListOfFiles);
1093 }
1094
1095 /*!
1096   \return temporary directory for saving files of modules
1097 */
1098 std::string SalomeApp_Study::GetTmpDir ( const char* theURL, const bool  isMultiFile )
1099 {
1100   std::string anURLDir = SALOMEDS_Tool::GetDirFromPath(theURL);
1101   std::string aTmpDir = isMultiFile ? anURLDir : SALOMEDS_Tool::GetTmpDir();
1102   return aTmpDir;
1103 }
1104
1105 /*!
1106   Removes temporary files
1107 */
1108 void SalomeApp_Study::RemoveTemporaryFiles ( const char* theModuleName, const bool isMultiFile ) const
1109 {
1110   if (isMultiFile)
1111     return;
1112
1113   SALOMEDS_Tool::ListOfFiles aListOfFiles = GetListOfFiles( theModuleName );
1114   if (aListOfFiles.size() > 0) {
1115     std::string aTmpDir = aListOfFiles[0];
1116
1117     const int n = aListOfFiles.size() - 1;
1118     std::vector<std::string> aSeq;
1119     aSeq.reserve(n);
1120     for (int i = 0; i < n; i++)
1121       aSeq.push_back(CORBA::string_dup(aListOfFiles[i + 1].c_str()));
1122
1123     SALOMEDS_Tool::RemoveTemporaryFiles(aTmpDir.c_str(), aSeq, true);
1124   }
1125 }
1126
1127 #ifndef DISABLE_PYCONSOLE
1128 /*!
1129   Mark the study as saved in the file
1130   \param theFileName - the name of file
1131 */
1132 void SalomeApp_Study::updateFromNotebook( const QString& theFileName, bool isSaved )
1133 {
1134   setStudyName(theFileName);
1135   studyDS()->URL(theFileName.toStdString());
1136   setIsSaved( isSaved );
1137 }
1138 #endif
1139
1140 LightApp_DataObject* SalomeApp_Study::findObjectByEntry( const QString& theEntry )
1141 {
1142   LightApp_DataObject* o = 0;
1143   if ( myObserver ) {
1144     o = dynamic_cast<LightApp_DataObject*>( myObserver->findObject( theEntry.toUtf8().constData() ) );
1145   }
1146   if ( !o ) {
1147     o = LightApp_Study::findObjectByEntry( theEntry );
1148   }
1149   return o;
1150 }
1151
1152 /*!
1153   Deletes all references to object
1154   \param obj - object
1155 */
1156 void SalomeApp_Study::deleteReferencesTo( _PTR( SObject ) obj )
1157 {
1158   _PTR(StudyBuilder) sb = studyDS()->NewBuilder();
1159   std::vector<_PTR(SObject)> aRefs = studyDS()->FindDependances( obj );
1160   for( int i=0, n=aRefs.size(); i<n; i++ )
1161   {
1162     _PTR( SObject ) o = aRefs[i];
1163     if( o->GetFatherComponent()->ComponentDataType()==obj->GetFatherComponent()->ComponentDataType() )
1164     {
1165       sb->RemoveReference( o );
1166       sb->RemoveObjectWithChildren( o );
1167     }
1168   }
1169 }
1170
1171 /*!
1172   \return real entry by entry of reference
1173   \param entry - entry of reference object
1174 */
1175 QString SalomeApp_Study::referencedToEntry( const QString& entry ) const
1176 {
1177   _PTR(SObject) obj = studyDS()->FindObjectID( entry.toStdString() );
1178   _PTR(SObject) refobj;
1179
1180   if( obj && obj->ReferencedObject( refobj ) )
1181     return refobj->GetID().c_str();
1182   return LightApp_Study::referencedToEntry( entry );
1183 }
1184
1185 /*!
1186   \return component data type for entry
1187 */
1188 QString SalomeApp_Study::componentDataType( const QString& entry ) const
1189 {
1190   _PTR(SObject) obj( studyDS()->FindObjectID( entry.toStdString() ) );
1191   if ( !obj )
1192     return LightApp_Study::componentDataType( entry );
1193   return obj->GetFatherComponent()->ComponentDataType().c_str();
1194 }
1195
1196 /*!
1197   \return true if entry corresponds to component
1198 */
1199 bool SalomeApp_Study::isComponent( const QString& entry ) const
1200 {
1201   _PTR(SObject) obj( studyDS()->FindObjectID( entry.toStdString() ) );
1202   return obj && QString( obj->GetID().c_str() ) == obj->GetFatherComponent()->GetID().c_str();
1203 }
1204
1205 /*!
1206   \return entries of object children
1207 */
1208 void SalomeApp_Study::children( const QString& entry, QStringList& child_entries ) const
1209 {
1210   _PTR(SObject) SO = studyDS()->FindObjectID( entry.toStdString() );
1211   _PTR(ChildIterator) anIter ( studyDS()->NewChildIterator( SO ) );
1212   anIter->InitEx( true );
1213   while( anIter->More() )
1214   {
1215     _PTR(SObject) val( anIter->Value() );
1216     child_entries.append( val->GetID().c_str() );
1217     anIter->Next();
1218   }
1219 }
1220
1221 /*!
1222   Fills list with components names
1223   \param comp - list to be filled
1224 */
1225 void SalomeApp_Study::components( QStringList& comps ) const
1226 {
1227   for( _PTR(SComponentIterator) it ( studyDS()->NewComponentIterator() ); it->More(); it->Next() )
1228   {
1229     _PTR(SComponent) aComponent ( it->Value() );
1230     // skip the magic "Interface Applicative" component
1231     if ( aComponent && aComponent->ComponentDataType() != getVisualComponentName().toLatin1().constData() )
1232       comps.append( aComponent->ComponentDataType().c_str() );
1233   }
1234 }
1235
1236 /*!
1237   Get the entry for the given module
1238   \param comp - list to be filled
1239   \return module root's entry
1240 */
1241 QString SalomeApp_Study::centry( const QString& comp ) const
1242 {
1243   QString e;
1244   for( _PTR(SComponentIterator) it ( studyDS()->NewComponentIterator() ); it->More() && e.isEmpty(); it->Next() )
1245   {
1246     _PTR(SComponent) aComponent ( it->Value() );
1247     if ( aComponent && comp == aComponent->ComponentDataType().c_str() )
1248       e = aComponent->GetID().c_str();
1249   }
1250   return e;
1251 }
1252
1253 /*!
1254   \return a list of saved points' IDs
1255 */
1256 std::vector<int> SalomeApp_Study::getSavePoints()
1257 {
1258   std::vector<int> v;
1259
1260   _PTR(SObject) so = studyDS()->FindComponent( getVisualComponentName().toLatin1().constData() );
1261   if(!so) return v;
1262
1263   _PTR(StudyBuilder) builder = studyDS()->NewBuilder();
1264   _PTR(ChildIterator) anIter ( studyDS()->NewChildIterator( so ) );
1265   for(; anIter->More(); anIter->Next())
1266   {
1267     _PTR(SObject) val( anIter->Value() );
1268     _PTR(GenericAttribute) genAttr;
1269     if(builder->FindAttribute(val, genAttr, "AttributeParameter")) v.push_back(val->Tag());
1270   }
1271
1272   return v;
1273 }
1274
1275 /*!
1276   Removes a given save point
1277 */
1278 void SalomeApp_Study::removeSavePoint(int savePoint)
1279 {
1280   if(savePoint <= 0) return;
1281  _PTR(AttributeParameter) AP = studyDS()->GetCommonParameters(getVisualComponentName().toLatin1().constData(), savePoint);
1282   _PTR(SObject) so = AP->GetSObject();
1283   _PTR(StudyBuilder) builder = studyDS()->NewBuilder();
1284   builder->RemoveObjectWithChildren(so);
1285 }
1286
1287 /*!
1288   \return a name of save point
1289 */
1290 QString SalomeApp_Study::getNameOfSavePoint(int savePoint)
1291 {
1292   _PTR(AttributeParameter) AP = studyDS()->GetCommonParameters(getVisualComponentName().toLatin1().constData(), savePoint);
1293   _PTR(IParameters) ip = ClientFactory::getIParameters(AP);
1294   return ip->getProperty("AP_SAVEPOINT_NAME").c_str();
1295 }
1296
1297 /*!
1298   Sets a name of save point
1299 */
1300 void SalomeApp_Study::setNameOfSavePoint(int savePoint, const QString& nameOfSavePoint)
1301 {
1302   _PTR(AttributeParameter) AP = studyDS()->GetCommonParameters(getVisualComponentName().toLatin1().constData(), savePoint);
1303   _PTR(IParameters) ip = ClientFactory::getIParameters(AP);
1304   ip->setProperty("AP_SAVEPOINT_NAME", nameOfSavePoint.toStdString());
1305 }
1306
1307 /*!
1308  * \brief Restores the study state
1309  */
1310 void SalomeApp_Study::restoreState(int savePoint)
1311 {
1312   SalomeApp_VisualState((SalomeApp_Application*)application()).restoreState(savePoint);
1313 }
1314
1315
1316 /*!
1317   Slot: called on change of a root of a data model. Redefined from CAM_Study
1318 */
1319 void SalomeApp_Study::updateModelRoot( const CAM_DataModel* dm )
1320 {
1321   LightApp_Study::updateModelRoot( dm );
1322
1323   // calling updateSavePointDataObjects in order to set correct order of "Gui states" object
1324   // it must always be the last one.
1325   ((SalomeApp_Application*)application())->updateSavePointDataObjects( this );
1326 }