Salome HOME
c680652323e8e61e629fc0da38948e23930b00cc
[modules/gui.git] / src / SalomeApp / SalomeApp_Study.cxx
1 // Copyright (C) 2007-2020  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             QString moduleTitle = ((CAM_Application*)myStudy->application())->moduleTitle(suit_obj->componentDataType());
194             if (!moduleTitle.isEmpty()) {
195               LightApp_Displayer* aDisplayer = LightApp_Displayer::FindDisplayer(moduleTitle,false);
196               if (aDisplayer) {
197                 if(aDisplayer->canBeDisplayed(theID.c_str())) {
198                   myStudy->setVisibilityState( theID.c_str(), Qtx::HiddenState ); //hide the just added object
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           }
206         } // END: work with tree nodes structure
207         else { // BEGIN: work with study structure
208           EntryMapIter it = entry2SuitObject.find( theID );
209           if ( it != entry2SuitObject.end() ) {
210             MESSAGE("Entry " << theID << " is already added. Problem ??");
211             return;
212           }
213
214           int last2Pnt_pos = (int)theID.rfind( ":" ); //!< TODO: conversion from size_t to int
215           std::string parent_id = theID.substr( 0, last2Pnt_pos );
216           int tag = atoi( theID.substr( last2Pnt_pos+1 ).c_str() );
217
218           if ( parent_id.length() == 3 ) // "0:1" - root item?
219           {
220             // It's probably a SComponent
221             if ( theID == aSComp->GetID() )
222               suit_obj = new SalomeApp_ModuleObject( aSComp );
223             else
224               suit_obj = new SalomeApp_DataObject( aSObj );
225           }
226           else
227           {
228             suit_obj = new SalomeApp_DataObject( aSObj );
229           }
230
231           it = entry2SuitObject.find( parent_id );
232           if ( it != entry2SuitObject.end() ) {
233             SalomeApp_DataObject* father = it->second;
234             father->insertChildAtTag( suit_obj, tag );
235           }
236           else {
237             if ( parent_id.length() == 3 ) // "0:1" - root item?
238             {
239               // This should be for a module
240               SUIT_DataObject* father=myStudy->root();
241               father->appendChild(suit_obj);
242             }
243             else
244             {
245               MESSAGE("SHOULD NEVER GET HERE!!!");
246
247               //Try to find the SalomeApp_DataObject object parent
248               std::string root_id = parent_id.substr( 0, 4 );
249               std::string obj_id = parent_id.substr( 4 );
250
251               std::string anID;
252               std::string::size_type debut = 0;
253               std::string::size_type fin;
254               SalomeApp_DataObject* anObj = dynamic_cast<SalomeApp_DataObject*>( myStudy->root() );
255               while ( anObj ) {
256                 fin = obj_id.find_first_of( ':', debut );
257                 if ( fin == std::string::npos ) {
258                   //last id
259                   anObj = dynamic_cast<SalomeApp_DataObject*>(anObj->childObject(atoi(obj_id.substr(debut).c_str())-1));
260                   if ( anObj )
261                     entry2SuitObject[parent_id] = anObj;
262                   break;
263                 }
264                 anID = root_id + obj_id.substr( 0, fin );
265                 EntryMapIter it2 = entry2SuitObject.find( anID );
266                 if ( it2 == entry2SuitObject.end() ) {
267                   //the ID is not known in entry2SuitObject
268                   anObj = dynamic_cast<SalomeApp_DataObject*>(anObj->childObject(atoi(obj_id.substr(debut, fin-debut).c_str())-1));
269                   if ( anObj )
270                     entry2SuitObject[anID] = anObj;
271                 }
272                 else
273                   anObj = it2->second;
274                 debut = fin+1;
275               }
276               if ( anObj )
277                 anObj->insertChildAtTag( suit_obj, tag );
278             }
279           }
280           entry2SuitObject[theID] = suit_obj;
281         } // END: work with study structure
282         break;
283       }
284     case 2:
285       { // Remove sobject
286         EntryMapIter it = entry2SuitObject.find( theID );
287         if ( it != entry2SuitObject.end() )
288         {
289           suit_obj = it->second;
290           suit_obj->updateItem();
291           SUIT_DataObject* father=suit_obj->parent();
292           if(father)
293             father->removeChild(suit_obj);
294           entry2SuitObject.erase(it);
295         }
296         else
297         {
298           MESSAGE("Want to remove an unknown object" << theID);
299         }
300         break;
301       }
302     case 0:
303       { //modify sobject
304         //MESSAGE("Want to modify an object "  << theID);
305         EntryMapIter it = entry2SuitObject.find( theID );
306         if ( it != entry2SuitObject.end() )
307         {
308           suit_obj = it->second;
309           suit_obj->updateItem();
310         }
311         else
312         {
313           MESSAGE("Want to modify an unknown object"  << theID);
314         }
315         break;
316       }
317     case 5: //IOR of the object modified
318       {
319         EntryMapIter it = entry2SuitObject.find( theID );
320         if ( it != entry2SuitObject.end() )
321           suit_obj = it->second;
322
323         /* Define visibility state */
324         bool isComponent = dynamic_cast<SalomeApp_ModuleObject*>( suit_obj ) != 0;
325         if ( suit_obj && !isComponent ) {
326           QString moduleTitle = ((CAM_Application*)myStudy->application())->moduleTitle(suit_obj->componentDataType());
327           if (!moduleTitle.isEmpty()) {
328             LightApp_Displayer* aDisplayer = LightApp_Displayer::FindDisplayer(moduleTitle,false);
329             if (aDisplayer) {
330               if(aDisplayer->canBeDisplayed(theID.c_str())) {
331                 myStudy->setVisibilityState( theID.c_str(), Qtx::HiddenState );
332                 //MESSAGE("Object with entry : "<< theID <<" CAN be displayed !!!");
333               }
334               //else
335                 //MESSAGE("Object with entry : "<< theID <<" CAN'T be displayed !!!");
336             }
337           }
338         }
339         break;
340       }
341 #ifndef DISABLE_PYCONSOLE
342     case 6: //NoteBook variables were modified
343       {
344         myStudy->onNoteBookVarUpdate( QString( theID.c_str() ) );
345         break;
346       }
347 #endif
348     default:MESSAGE("Unknown event: "  << event);break;
349     } //switch
350   } //notifyObserverID_real
351
352 private:
353   void fillEntryMap()
354   {
355     entry2SuitObject.clear();
356     SUIT_DataObject* o = myStudy->root();
357     while (o) {
358       SalomeApp_DataObject* so = dynamic_cast<SalomeApp_DataObject*>( o );
359       if ( so ) {
360         std::string entry = so->entry().toUtf8().constData();
361         if ( entry.size() )
362           entry2SuitObject[entry] = so;
363       }
364       if ( o->childCount() > 0 ) {
365         // parse the children
366         o = o->firstChild();
367       }
368       else if ( o->nextBrother() ) {
369         o = o->nextBrother();
370       }
371       else {
372         // step to the next appropriate parent
373         o = o->parent();
374         while ( o ) {
375           if ( o->nextBrother() ) {
376             o = o->nextBrother();
377             break;
378           }
379           o = o->parent();
380         }
381       }
382     }
383   }
384
385 private:
386   SalomeApp_Study* myStudy;
387   EntryMap         entry2SuitObject;
388 };
389
390
391 /*!
392   Constructor.
393 */
394 SalomeApp_Study::SalomeApp_Study( SUIT_Application* app )
395 : LightApp_Study( app ), myObserver( 0 )
396 {
397   myStudyDS = SalomeApp_Application::getStudy();
398 }
399
400 /*!
401   Destructor.
402 */
403 SalomeApp_Study::~SalomeApp_Study()
404 {
405   if ( myObserver ) {
406     PortableServer::ObjectId_var oid = myObserver->_default_POA()->servant_to_id( myObserver );
407     myObserver->_default_POA()->deactivate_object( oid.in() );
408     myObserver = 0;
409   }
410 }
411
412 #ifndef DISABLE_PYCONSOLE
413 void SalomeApp_Study::onNoteBookVarUpdate( QString theVarName)
414 {
415   emit notebookVarUpdated( theVarName );
416 }
417 #endif
418
419 /*!
420   Get study name.
421 */
422 QString SalomeApp_Study::studyName() const
423 {
424   // redefined from SUIT_Study to update study name properly since
425   // it can be changed outside of GUI
426   // TEMPORARILY SOLUTION: better to be implemented with help of SALOMEDS observers
427   if ( studyDS() ) {
428     QString newName = QString::fromUtf8(studyDS()->URL().c_str());
429     if ( newName.isEmpty() )
430       newName = QString::fromUtf8(studyDS()->Name().c_str());
431     if ( LightApp_Study::studyName() != newName ) {
432       SalomeApp_Study* that = const_cast<SalomeApp_Study*>( this );
433       that->setStudyName( newName );
434       ((SalomeApp_Application*)application())->updateDesktopTitle();
435     }
436   }
437   return LightApp_Study::studyName();
438 }
439
440 /*!
441   Gets studyDS pointer.
442 */
443 _PTR(Study) SalomeApp_Study::studyDS() const
444 {
445   return myStudyDS;
446 }
447
448 /*!
449   Create document.
450 */
451 bool SalomeApp_Study::createDocument( const QString& theStr )
452 {
453   MESSAGE( "createDocument" );
454
455   setStudyName( QString::fromUtf8(myStudyDS->URL().c_str()) );
456
457   // create myRoot
458   SalomeApp_RootObject* aRoot=new SalomeApp_RootObject( this );
459 #ifdef WITH_SALOMEDS_OBSERVER
460   aRoot->setToSynchronize(false);
461 #endif
462   setRoot( aRoot );
463
464   bool aRet = CAM_Study::createDocument( theStr );
465
466 #ifdef WITH_SALOMEDS_OBSERVER
467   myObserver = new Observer_i(this);
468   //attach an observer to the study with notification of modifications
469   myStudyDS->attach(myObserver->_this(),true);
470 #endif
471
472   emit created( this );
473
474   return aRet;
475 }
476
477 /*!
478   Opens document
479   \param theFileName - name of file
480 */
481 bool SalomeApp_Study::openDocument( const QString& theFileName )
482 {
483   MESSAGE( "openDocument" );
484
485   // read HDF file
486   bool res = false;
487   bool showError = !application()->property("open_study_from_command_line").isValid() ||
488     !application()->property("open_study_from_command_line").toBool();
489   try {
490     res = myStudyDS->Open( theFileName.toUtf8().data() );
491   }
492   catch(const SALOME_Exception& ex) {
493     application()->putInfo(tr(ex.what()));
494     if ( showError )
495       SUIT_MessageBox::critical( SUIT_Session::session()->activeApplication()->desktop(),
496                                  tr("ERR_ERROR"), tr(ex.what()));
497     return false;
498   }
499   catch(...) {
500     application()->putInfo(tr("OPEN_DOCUMENT_PROBLEM"));
501     if ( showError )
502       SUIT_MessageBox::critical( SUIT_Session::session()->activeApplication()->desktop(),
503                                  tr("ERR_ERROR"), tr("OPEN_DOCUMENT_PROBLEM"));
504     return false;
505   }
506
507   if ( !res)
508     return false;
509
510   setRoot( new SalomeApp_RootObject( this ) ); // create myRoot
511
512   // update loaded data models: call open() and update() on them.
513   ModelList dm_s;
514   dataModels( dm_s );
515   QListIterator<CAM_DataModel*> it( dm_s );
516   while ( it.hasNext() )
517     openDataModel( studyName(), it.next() );
518
519   // this will build a SUIT_DataObject-s tree under myRoot member field
520   // passing "false" in order NOT to rebuild existing data models' trees - it was done in previous step
521   // but tree that corresponds to not-loaded data models will be updated any way.
522   ((SalomeApp_Application*)application())->updateObjectBrowser( false );
523
524 #ifdef WITH_SALOMEDS_OBSERVER
525   dynamic_cast<SalomeApp_RootObject*>( root() )->setToSynchronize(false);
526   myObserver = new Observer_i(this);
527   //attach an observer to the study with notification of modifications
528   myStudyDS->attach(myObserver->_this(),true);
529 #endif
530
531   res = CAM_Study::openDocument( theFileName );
532
533   emit opened( this );
534   myStudyDS->IsSaved(true);
535
536   bool restore = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", true );
537   if ( restore ) {
538     std::vector<int> savePoints = getSavePoints();
539     if ( savePoints.size() > 0 )
540       SalomeApp_VisualState( (SalomeApp_Application*)application() ).restoreState( savePoints[savePoints.size()-1] );
541   }
542
543   ((SalomeApp_Application*)application())->updateObjectBrowser( true );
544   return res;
545 }
546
547 /*!
548   Connects GUI study to SALOMEDS one
549   \param theStudyName - name of study
550 */
551 bool SalomeApp_Study::loadDocument( const QString& theStudyName )
552 {
553   MESSAGE( "loadDocument" );
554
555   setRoot( new SalomeApp_RootObject( this ) ); // create myRoot
556
557   //SRN: BugID IPAL9021, put there the same code as in a method openDocument
558
559   // update loaded data models: call open() and update() on them.
560   ModelList dm_s;
561   dataModels( dm_s );
562
563   QListIterator<CAM_DataModel*> it( dm_s );
564   while ( it.hasNext() )
565     openDataModel( studyName(), it.next() );
566
567   bool res = CAM_Study::openDocument( theStudyName );
568
569   //rnv: to fix the "0051779: TC7.2.0: Save operation works incorrectly for study loaded from data server"
570   //     mark study as "not saved" after call openDocument( ... ) method.
571   setIsSaved(false);
572   emit opened( this ); // myRoot is set to Object Browser here
573
574   // this will build a SUIT_DataObject-s tree under myRoot member field
575   // passing "false" in order NOT to rebuild existing data models' trees - it was done in previous step
576   // but tree that corresponds to not-loaded data models will be updated any way.
577   ((SalomeApp_Application*)application())->updateObjectBrowser( false );
578
579 #ifdef WITH_SALOMEDS_OBSERVER
580   dynamic_cast<SalomeApp_RootObject*>( root() )->setToSynchronize(false);
581   myObserver = new Observer_i(this);
582   //attach an observer to the study with notification of modifications
583   myStudyDS->attach(myObserver->_this(),true);
584 #endif
585
586   bool restore = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", true );
587   if ( restore ) {
588     std::vector<int> savePoints = getSavePoints();
589     if ( savePoints.size() > 0 )
590       SalomeApp_VisualState( (SalomeApp_Application*)application() ).restoreState( savePoints[savePoints.size()-1] );
591   }
592
593   //SRN: BugID IPAL9021: End
594   return res;
595 }
596
597 /*!
598   Saves document
599   \param theFileName - name of file
600 */
601 bool SalomeApp_Study::saveDocumentAs( const QString& theFileName )
602 {
603   bool store = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", false );
604   if ( store )
605     SalomeApp_VisualState( (SalomeApp_Application*)application() ).storeState();
606
607   ModelList list; dataModels( list );
608
609   QListIterator<CAM_DataModel*> it( list );
610   QStringList listOfFiles;
611   while ( it.hasNext() ) {
612     // Cast to LightApp class in order to give a chance
613     // to light modules to save their data
614     if ( LightApp_DataModel* aModel = 
615          dynamic_cast<LightApp_DataModel*>( it.next() ) ) {
616       listOfFiles.clear();
617       aModel->saveAs( theFileName, this, listOfFiles );
618       if ( !listOfFiles.isEmpty() )
619         saveModuleData(aModel->module()->name(), 0, // 0 means persistence file
620                        listOfFiles);
621     }
622   }
623
624   // save SALOMEDS document
625   SUIT_ResourceMgr* resMgr = application()->resourceMgr();
626   if( !resMgr )
627     return false;
628
629   bool isMultiFile = resMgr->booleanValue( "Study", "multi_file", false );
630   bool isAscii = resMgr->booleanValue( "Study", "ascii_file", false );
631   bool res = studyDS()->SaveAs( theFileName.toUtf8().data(), isMultiFile, isAscii )
632     && CAM_Study::saveDocumentAs( theFileName );
633
634   res = res && saveStudyData(theFileName, 0); // 0 means persistence file
635
636   if ( res )
637     emit saved( this );
638
639   return res;
640 }
641
642 /*!
643   Saves previously opened document
644 */
645 bool SalomeApp_Study::saveDocument()
646 {
647   bool store = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", true );
648   if ( store )
649     SalomeApp_VisualState( (SalomeApp_Application*)application() ).storeState();
650
651   ModelList list; dataModels( list );
652
653   QListIterator<CAM_DataModel*> it( list );
654   QStringList listOfFiles;
655   while ( it.hasNext() ) {
656     // Cast to LightApp class in order to give a chance
657     // to light modules to save their data
658     if ( LightApp_DataModel* aModel = 
659          dynamic_cast<LightApp_DataModel*>( it.next() ) ) {
660       listOfFiles.clear();
661       aModel->save(listOfFiles);
662       if ( !listOfFiles.isEmpty() )
663         saveModuleData(aModel->module()->name(), 0, // 0 means persistence file
664                        listOfFiles);
665     }
666   }
667
668   // save SALOMEDS document
669   SUIT_ResourceMgr* resMgr = application()->resourceMgr();
670   if( !resMgr )
671     return false;
672
673   bool isMultiFile = resMgr->booleanValue( "Study", "multi_file", false );
674   bool isAscii = resMgr->booleanValue( "Study", "ascii_file", false );
675   bool res = studyDS()->Save( isMultiFile, isAscii ) && CAM_Study::saveDocument();
676
677   res = res && saveStudyData(studyName(), 0); // 0 means persistence file
678   if ( res )
679     emit saved( this );
680
681   return res;
682 }
683
684 /*!
685   Closes document
686 */
687 void SalomeApp_Study::closeDocument(bool permanently)
688 {
689   LightApp_Study::closeDocument(permanently);
690
691   // close SALOMEDS document
692   if ( myObserver )
693     myStudyDS->detach( myObserver->_this() );
694   if ( permanently ) {
695     SUIT_Desktop* desk = SUIT_Session::session()->activeApplication()->desktop();
696     bool isBlocked = desk->signalsBlocked();
697     desk->blockSignals( true );
698     myStudyDS->Clear();
699     desk->blockSignals( isBlocked );
700 #ifndef DISABLE_PYCONSOLE
701     SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( application() );
702     app->getPyInterp()->destroy();
703 #endif
704   }
705 }
706
707 /*!
708   Dump study operation. Writes a Python dump file using
709   SALOMEDS services. Additionally, gives a chance to light modules
710   to participate in dump study operation.
711
712   \param theFileName - full path to the output Python file
713   \param toPublish - if true, all objects are published in a study 
714   by the output script, including those not orignally present 
715   in the current study.
716   \param isMultiFile - if true, each module's dump is written into 
717   a separate Python file, otherwise a single output file is written
718   \param toSaveGUI - if true, the GUI state is written
719
720   \return - true if the operation succeeds, and false otherwise.
721 */
722 bool SalomeApp_Study::dump( const QString& theFileName, 
723                             bool toPublish, 
724                             bool isMultiFile,
725                             bool toSaveGUI )
726 {
727   int savePoint = 0;
728   _PTR(AttributeParameter) ap;
729   _PTR(IParameters) ip = ClientFactory::getIParameters(ap);
730
731   if( ip->isDumpPython() )
732     ip->setDumpPython(); //Unset DumpPython flag.
733
734   if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method
735     ip->setDumpPython();
736     //SRN: create a temporary save point
737     savePoint = SalomeApp_VisualState( 
738       dynamic_cast<SalomeApp_Application*>( application() ) ).storeState(); 
739   }
740
741   // Issue 21377 - Each data model is asked to dump its data not present in SALOMEDS study.
742   // This is an optional but important step, it gives a chance to light modules
743   // to dump their data as a part of common dump study operation
744   ModelList list; 
745   dataModels( list );
746
747   QListIterator<CAM_DataModel*> it( list );
748   QStringList listOfFiles;
749   while ( it.hasNext() ) {
750     if ( LightApp_DataModel* aModel = 
751          dynamic_cast<LightApp_DataModel*>( it.next() ) ) {
752       listOfFiles.clear();
753       if ( aModel->dumpPython( theFileName, this, isMultiFile, listOfFiles ) && 
754            !listOfFiles.isEmpty() )
755         // This call simply passes the data model's dump output to SalomeApp_Engine servant.
756         // This code is shared with persistence mechanism.
757         // NOTE: this should be revised if behavior of saveModuleData() changes!
758         saveModuleData(aModel->module()->name(), 1, // 1 means dump file
759                        listOfFiles);
760     }
761   }
762
763   // Now dump SALOMEDS part that also involves SalomeApp_Engine in case if 
764   // any light module is present in the current configuration
765   QFileInfo aFileInfo( theFileName );
766   bool res = myStudyDS->DumpStudy( aFileInfo.absolutePath().toUtf8().data(),
767                                   aFileInfo.baseName().toUtf8().data(),
768                                   toPublish,
769                                   isMultiFile);
770   if ( toSaveGUI )
771     removeSavePoint( savePoint ); //SRN: remove the created temporary save point.
772
773   // Issue 21377 - Clean up light module data in SalomeApp_Engine servant
774   // This code is shared with persistence mechanism.
775   // NOTE: this should be revised if behavior of saveStudyData() changes!
776   saveStudyData( theFileName, 1 ); // 0 means persistence file
777
778   return res;
779 }
780
781 /*!
782   \return true, if study is modified in comparison with last open/save
783 */
784 bool SalomeApp_Study::isModified() const
785 {
786   bool isAnyChanged = studyDS() && studyDS()->IsModified();
787   if (!isAnyChanged)
788     isAnyChanged = LightApp_Study::isModified();
789
790   return isAnyChanged;
791 }
792
793 /*!
794   Set study modified to \a on.
795  */
796 void SalomeApp_Study::Modified()
797 {
798   myStudyDS->Modified();
799   LightApp_Study::Modified();
800 }
801
802 /*!
803   \return if data model is saved
804 */
805 bool SalomeApp_Study::isSaved() const
806 {
807   bool isAllSaved = studyDS() && studyDS()->GetPersistentReference().size();
808   if (!isAllSaved)
809     isAllSaved = LightApp_Study::isSaved();
810
811   return isAllSaved;
812 }
813
814 /*!
815   Saves data of module
816   \param theModuleName - name of module
817   \param theListOfFiles - list of files to be saved
818 */
819 void SalomeApp_Study::saveModuleData( QString theModuleName, int type, QStringList theListOfFiles )
820 {
821   int aNb = theListOfFiles.count();
822   if ( aNb == 0 )
823     return;
824
825   std::vector<std::string> aListOfFiles ( aNb );
826   int anIndex = 0;
827   for ( QStringList::Iterator it = theListOfFiles.begin(); it != theListOfFiles.end(); ++it ) {
828     if ( (*it).isEmpty() )
829       continue;
830     aListOfFiles[anIndex] = (*it).toUtf8().data();
831     anIndex++;
832   }
833   SetListOfFiles(theModuleName.toStdString().c_str(), type, aListOfFiles);
834 }
835
836 /*!
837   Loads data of module
838   \param theModuleName - name of module
839   \param theListOfFiles - list of files to be loaded
840 */
841 void SalomeApp_Study::openModuleData( QString theModuleName, int type, QStringList& theListOfFiles )
842 {
843   std::vector<std::string> aListOfFiles = GetListOfFiles( theModuleName.toStdString().c_str(), type );
844
845   int i, aLength = (int)aListOfFiles.size() - 1; //!< TODO: conversion from size_t to int
846   if ( aLength < 0 )
847     return;
848
849   //Get a temporary directory for saved a file
850   theListOfFiles.append(aListOfFiles[0].c_str());
851
852   for(i = 0; i < aLength; i++)
853     theListOfFiles.append(aListOfFiles[i+1].c_str());
854 }
855
856 /*!
857   Re-implemented from LightApp_Study, actually does not save anything but
858   simply cleans up light modules' data
859 */
860 bool SalomeApp_Study::saveStudyData( const QString& /*theFileName*/, int type )
861 {
862   ModelList list; dataModels( list );
863   QListIterator<CAM_DataModel*> it( list );
864   while ( it.hasNext() ){
865     LightApp_DataModel* aLModel = 
866       dynamic_cast<LightApp_DataModel*>( it.next() );
867     // It is safe to call SetListOfFiles() for any kind of module
868     // because SetListOfFiles() does nothing for full modules :)
869     if ( aLModel )
870       SetListOfFiles(aLModel->module()->name().toStdString().c_str(), type, std::vector<std::string>());
871   }
872   return true;
873 }
874
875 /*!
876   Loads data for study
877 */
878 bool SalomeApp_Study::openStudyData( const QString& /*theFileName*/, int /*type*/ )
879 {
880   return true;
881 }
882
883 /*!
884   Virtual method re-implemented from LightApp_Study in order to create
885   the module object connected to SALOMEDS - SalomeApp_ModuleObject.
886
887   \param theDataModel - data model instance to create a module object for
888   \param theParent - the module object's parent (normally it's the study root)
889   \return the module object instance
890   \sa LightApp_Study class, LightApp_DataModel class
891 */
892 CAM_ModuleObject* SalomeApp_Study::createModuleObject( LightApp_DataModel* theDataModel, 
893                                                        SUIT_DataObject* theParent ) const
894 {
895   SalomeApp_Study* that = const_cast<SalomeApp_Study*>( this );
896   
897   // Ensure that SComponent instance is published in the study for the given module
898   // This line causes automatic creation of SalomeApp_ModuleObject in case if
899   // WITH_SALOMEDS_OBSERVER is defined
900   that->addComponent( theDataModel );
901   
902   // SalomeApp_ModuleObject might have been created by SALOMEDS observer
903   // or by someone else so check if it exists first of all
904   CAM_ModuleObject* res = 0;
905
906   DataObjectList children = root()->children();
907   DataObjectList::const_iterator anIt = children.begin(), aLast = children.end();
908   for( ; !res && anIt!=aLast; anIt++ )
909   {
910     SalomeApp_ModuleObject* obj = dynamic_cast<SalomeApp_ModuleObject*>( *anIt );
911     if ( obj && obj->componentDataType() == theDataModel->module()->name() )
912       res = obj;
913   }
914
915   if ( !res ){
916     _PTR(SComponent) aComp = myStudyDS->FindComponent(
917       theDataModel->module()->name().toStdString() );
918     if ( !aComp )
919       return res;
920
921     res = new SalomeApp_ModuleObject( theDataModel, aComp, theParent );
922   }
923
924   return res;
925 }
926
927 /*!
928   Insert data model.
929 */
930 void SalomeApp_Study::dataModelInserted (const CAM_DataModel* dm)
931 {
932   MESSAGE("SalomeApp_Study::dataModelInserted() : module name() = " << dm->module()->name().toStdString());
933
934   CAM_Study::dataModelInserted(dm);
935
936   //  addComponent(dm);
937 }
938
939 /*!
940   Create SComponent for module, using default engine (CORBAless)
941 */
942 void SalomeApp_Study::addComponent(const CAM_DataModel* dm)
943 {
944   SalomeApp_Module* aModule = dynamic_cast<SalomeApp_Module*>( dm->module() );
945   // 1. aModule == 0 means that this is a light module (no CORBA enigine)
946   if (!aModule) {
947     // Check SComponent existance
948
949     std::string aCompDataType = dm->module()->name().toStdString();
950
951     _PTR(SComponent) aComp = myStudyDS->FindComponent(aCompDataType);
952     if (!aComp) {
953       // Create SComponent
954       _PTR(StudyBuilder) aBuilder = myStudyDS->NewBuilder();
955       aComp = aBuilder->NewComponent(aCompDataType);
956       aBuilder->SetName(aComp, dm->module()->moduleName().toStdString());
957       QString anIconName = dm->module()->iconName();
958       if (!anIconName.isEmpty()) {
959         _PTR(AttributePixMap) anAttr = aBuilder->FindOrCreateAttribute(aComp, "AttributePixMap");
960         if (anAttr)
961           anAttr->SetPixMap(anIconName.toStdString());
962       }
963
964       // Set default engine IOR
965       // Issue 21377 - using separate engine for each type of light module
966       std::string anEngineIOR = SalomeApp_Engine_i::EngineIORForComponent( aCompDataType.c_str(),
967                                                                            true );
968       aBuilder->DefineComponentInstance(aComp, anEngineIOR);
969       //SalomeApp_DataModel::BuildTree( aComp, root(), this, /*skipExisitng=*/true );
970       SalomeApp_DataModel::synchronize( aComp, this );
971     }
972     else {
973       _PTR(StudyBuilder) aBuilder = myStudyDS->NewBuilder();
974       aBuilder->SetName(aComp, dm->module()->moduleName().toStdString());
975       QString anIconName = dm->module()->iconName();
976       if (!anIconName.isEmpty()) {
977         _PTR(AttributePixMap) anAttr = aBuilder->FindOrCreateAttribute(aComp, "AttributePixMap");
978         if (anAttr)
979           anAttr->SetPixMap(anIconName.toStdString());
980       }
981       // Set default engine IOR
982     }
983   }
984 }
985
986 /*!
987   Open data model
988 */
989 bool SalomeApp_Study::openDataModel( const QString& studyName, CAM_DataModel* dm )
990 {
991   if (!dm)
992     return false;
993
994   //  SalomeApp_DataModel* aDM = (SalomeApp_DataModel*)(dm);
995   SalomeApp_Module* aModule = dynamic_cast<SalomeApp_Module*>( dm->module() );
996   _PTR(SComponent)  aSComp;
997   QString anEngine;
998   // 1. aModule == 0 means that this is a light module (no CORBA enigine)
999   if (!aModule) {
1000     // Issue 21377 - using separate engine for each type of light module
1001     std::string aCompDataType = dm->module()->name().toStdString();
1002     anEngine = SalomeApp_Engine_i::EngineIORForComponent( aCompDataType.c_str(), true ).c_str();
1003     aSComp = myStudyDS->FindComponent( aCompDataType );
1004   }
1005   else {
1006     SalomeApp_DataModel* aDM = dynamic_cast<SalomeApp_DataModel*>( dm );
1007     if ( aDM ) {
1008       QString anId = aDM->getRootEntry( this );
1009       if ( anId.isEmpty() )
1010         return true; // Probably nothing to load
1011       anEngine = aDM->getModule()->engineIOR();
1012       if ( anEngine.isEmpty() )
1013         return false;
1014       aSComp = myStudyDS->FindComponentID( std::string( anId.toLatin1() ) );
1015     }
1016   }
1017   if ( aSComp ) {
1018     _PTR(StudyBuilder) aBuilder( myStudyDS->NewBuilder() );
1019     if ( aBuilder ) {
1020       try {
1021         aBuilder->LoadWith( aSComp, std::string( anEngine.toLatin1() ) );
1022       }
1023       catch( const SALOME::SALOME_Exception& ) {
1024         // Oops, something went wrong while loading -> return an error
1025         return false;
1026       }
1027       // Something has been read -> create data model tree
1028       //SalomeApp_DataModel* aDM = dynamic_cast<SalomeApp_DataModel*>( dm );
1029       // aDM->buildTree( aSComp, 0, this );
1030     }
1031   } else {
1032     // Don't return false here, for there might be no data
1033     // for a given component in the study yet
1034   }
1035   QStringList listOfFiles;
1036   openModuleData(dm->module()->name(), 0, // 0 means persistence file
1037                  listOfFiles);
1038   if (dm && dm->open(studyName, this, listOfFiles)) {
1039     // Remove the files and temporary directory, created
1040     // for this module by LightApp_Engine_i::Load()
1041     bool isMultiFile = false; // TODO: decide, how to access this parameter
1042     RemoveTemporaryFiles( dm->module()->name().toStdString().c_str(), isMultiFile, true );
1043     SetListOfFiles( dm->module()->name().toStdString().c_str(), 0, // 0 means persistence file
1044                     std::vector<std::string>() );
1045
1046     // Something has been read -> create data model tree
1047     LightApp_DataModel* aDM = dynamic_cast<LightApp_DataModel*>( dm );
1048     if ( aDM )
1049       aDM->update(NULL, this);
1050     return true;
1051   }
1052   return false;
1053 }
1054
1055 /*!
1056   Note that this method does not create or activate SalomeApp_Engine_i instance,
1057   therefore it can be called safely for any kind of module, but for full
1058   modules it returns an empty list.
1059   \return list of files used by module: to be used by CORBAless modules
1060   \param theModuleName - name of module
1061 */
1062 std::vector<std::string> SalomeApp_Study::GetListOfFiles( const char* theModuleName, int type ) const
1063 {
1064   // Issue 21377 - using separate engine for each type of light module
1065   SalomeApp_Engine_i* aDefaultEngine = SalomeApp_Engine_i::GetInstance( theModuleName, false );
1066   if (aDefaultEngine)
1067     return aDefaultEngine->GetListOfFiles( type );
1068
1069   return std::vector<std::string>();
1070 }
1071
1072 /*!
1073   Sets list of files used by module: to be used by CORBAless modules.
1074   Note that this method does not create or activate SalomeApp_Engine_i instance,
1075   therefore it can be called safely for any kind of module, but for full
1076   modules it simply does nothing.
1077   \param theModuleName - name of module
1078   \param theListOfFiles - list of files
1079 */
1080 void SalomeApp_Study::SetListOfFiles ( const char* theModuleName, int type,
1081                                        const std::vector<std::string> theListOfFiles )
1082 {
1083   // Issue 21377 - using separate engine for each type of light module
1084   SalomeApp_Engine_i* aDefaultEngine = SalomeApp_Engine_i::GetInstance( theModuleName, false );
1085   if (aDefaultEngine)
1086     aDefaultEngine->SetListOfFiles(type, theListOfFiles);
1087 }
1088
1089 /*!
1090   \return temporary directory for saving files of modules
1091 */
1092 std::string SalomeApp_Study::GetTmpDir ( const char* theURL, const bool  isMultiFile )
1093 {
1094   std::string anURLDir = SALOMEDS_Tool::GetDirFromPath(theURL);
1095   std::string aTmpDir = isMultiFile ? anURLDir : SALOMEDS_Tool::GetTmpDir();
1096   return aTmpDir;
1097 }
1098
1099 /*!
1100   Removes temporary files
1101 */
1102 void SalomeApp_Study::RemoveTemporaryFiles ( const char* theModuleName, bool isMultiFile, bool force )
1103 {
1104   if (isMultiFile)
1105     return;
1106
1107   SALOMEDS_Tool::ListOfFiles aListOfFiles = GetListOfFiles( theModuleName, 0 );
1108   if (aListOfFiles.size() > 0) {
1109     std::string aTmpDir = aListOfFiles[0];
1110
1111     const int n = (int)aListOfFiles.size() - 1; //!< TODO: conversion from size_t to int
1112     std::vector<std::string> aSeq;
1113     aSeq.reserve(n);
1114     for (int i = 0; i < n; i++)
1115       aSeq.push_back(CORBA::string_dup(aListOfFiles[i + 1].c_str()));
1116
1117     SalomeApp_Engine_i* engine = SalomeApp_Engine_i::GetInstance( theModuleName, false );
1118     bool toRemove = force || ( engine && !engine->keepFiles() );
1119
1120     if ( toRemove ) {
1121       SALOMEDS_Tool::RemoveTemporaryFiles(aTmpDir.c_str(), aSeq, true);
1122       engine->keepFiles( false );
1123     }
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( size_t 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 }