]> SALOME platform Git repositories - modules/gui.git/blob - src/SalomeApp/SalomeApp_Study.cxx
Salome HOME
Copyright update 2020
[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 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(), 0, // 0 means persistence file
628                        listOfFiles);
629     }
630   }
631
632   // save SALOMEDS document
633   SUIT_ResourceMgr* resMgr = application()->resourceMgr();
634   if( !resMgr )
635     return false;
636
637   bool isMultiFile = resMgr->booleanValue( "Study", "multi_file", false );
638   bool isAscii = resMgr->booleanValue( "Study", "ascii_file", false );
639   bool res = studyDS()->SaveAs( theFileName.toUtf8().data(), isMultiFile, isAscii )
640     && CAM_Study::saveDocumentAs( theFileName );
641
642   res = res && saveStudyData(theFileName, 0); // 0 means persistence file
643
644   if ( res )
645     emit saved( this );
646
647   return res;
648 }
649
650 /*!
651   Saves previously opened document
652 */
653 bool SalomeApp_Study::saveDocument()
654 {
655   bool store = application()->resourceMgr()->booleanValue( "Study", "store_visual_state", true );
656   if ( store )
657     SalomeApp_VisualState( (SalomeApp_Application*)application() ).storeState();
658
659   ModelList list; dataModels( list );
660
661   QListIterator<CAM_DataModel*> it( list );
662   QStringList listOfFiles;
663   while ( it.hasNext() ) {
664     // Cast to LightApp class in order to give a chance
665     // to light modules to save their data
666     if ( LightApp_DataModel* aModel = 
667          dynamic_cast<LightApp_DataModel*>( it.next() ) ) {
668       listOfFiles.clear();
669       aModel->save(listOfFiles);
670       if ( !listOfFiles.isEmpty() )
671         saveModuleData(aModel->module()->name(), 0, // 0 means persistence file
672                        listOfFiles);
673     }
674   }
675
676   // save SALOMEDS document
677   SUIT_ResourceMgr* resMgr = application()->resourceMgr();
678   if( !resMgr )
679     return false;
680
681   bool isMultiFile = resMgr->booleanValue( "Study", "multi_file", false );
682   bool isAscii = resMgr->booleanValue( "Study", "ascii_file", false );
683   bool res = studyDS()->Save( isMultiFile, isAscii ) && CAM_Study::saveDocument();
684
685   res = res && saveStudyData(studyName(), 0); // 0 means persistence file
686   if ( res )
687     emit saved( this );
688
689   return res;
690 }
691
692 /*!
693   Closes document
694 */
695 void SalomeApp_Study::closeDocument(bool permanently)
696 {
697   LightApp_Study::closeDocument(permanently);
698
699   // close SALOMEDS document
700   if ( myObserver )
701     myStudyDS->detach( myObserver->_this() );
702   if ( permanently ) {
703     SUIT_Desktop* desk = SUIT_Session::session()->activeApplication()->desktop();
704     bool isBlocked = desk->signalsBlocked();
705     desk->blockSignals( true );
706     myStudyDS->Clear();
707     desk->blockSignals( isBlocked );
708 #ifndef DISABLE_PYCONSOLE
709     SalomeApp_Application* app = dynamic_cast<SalomeApp_Application*>( application() );
710     app->getPyInterp()->destroy();
711 #endif
712   }
713 }
714
715 /*!
716   Dump study operation. Writes a Python dump file using
717   SALOMEDS services. Additionally, gives a chance to light modules
718   to participate in dump study operation.
719
720   \param theFileName - full path to the output Python file
721   \param toPublish - if true, all objects are published in a study 
722   by the output script, including those not orignally present 
723   in the current study.
724   \param isMultiFile - if true, each module's dump is written into 
725   a separate Python file, otherwise a single output file is written
726   \param toSaveGUI - if true, the GUI state is written
727
728   \return - true if the operation succeeds, and false otherwise.
729 */
730 bool SalomeApp_Study::dump( const QString& theFileName, 
731                             bool toPublish, 
732                             bool isMultiFile,
733                             bool toSaveGUI )
734 {
735   int savePoint;
736   _PTR(AttributeParameter) ap;
737   _PTR(IParameters) ip = ClientFactory::getIParameters(ap);
738
739   if( ip->isDumpPython() )
740     ip->setDumpPython(); //Unset DumpPython flag.
741
742   if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method
743     ip->setDumpPython();
744     //SRN: create a temporary save point
745     savePoint = SalomeApp_VisualState( 
746       dynamic_cast<SalomeApp_Application*>( application() ) ).storeState(); 
747   }
748
749   // Issue 21377 - Each data model is asked to dump its data not present in SALOMEDS study.
750   // This is an optional but important step, it gives a chance to light modules
751   // to dump their data as a part of common dump study operation
752   ModelList list; 
753   dataModels( list );
754
755   QListIterator<CAM_DataModel*> it( list );
756   QStringList listOfFiles;
757   while ( it.hasNext() ) {
758     if ( LightApp_DataModel* aModel = 
759          dynamic_cast<LightApp_DataModel*>( it.next() ) ) {
760       listOfFiles.clear();
761       if ( aModel->dumpPython( theFileName, this, isMultiFile, listOfFiles ) && 
762            !listOfFiles.isEmpty() )
763         // This call simply passes the data model's dump output to SalomeApp_Engine servant.
764         // This code is shared with persistence mechanism.
765         // NOTE: this should be revised if behavior of saveModuleData() changes!
766         saveModuleData(aModel->module()->name(), 1, // 1 means dump file
767                        listOfFiles);
768     }
769   }
770
771   // Now dump SALOMEDS part that also involves SalomeApp_Engine in case if 
772   // any light module is present in the current configuration
773   QFileInfo aFileInfo( theFileName );
774   bool res = myStudyDS->DumpStudy( aFileInfo.absolutePath().toUtf8().data(),
775                                   aFileInfo.baseName().toUtf8().data(),
776                                   toPublish,
777                                   isMultiFile);
778   if ( toSaveGUI )
779     removeSavePoint( savePoint ); //SRN: remove the created temporary save point.
780
781   // Issue 21377 - Clean up light module data in SalomeApp_Engine servant
782   // This code is shared with persistence mechanism.
783   // NOTE: this should be revised if behavior of saveStudyData() changes!
784   saveStudyData( theFileName, 1 ); // 0 means persistence file
785
786   return res;
787 }
788
789 /*!
790   \return true, if study is modified in comparison with last open/save
791 */
792 bool SalomeApp_Study::isModified() const
793 {
794   bool isAnyChanged = studyDS() && studyDS()->IsModified();
795   if (!isAnyChanged)
796     isAnyChanged = LightApp_Study::isModified();
797
798   return isAnyChanged;
799 }
800
801 /*!
802   Set study modified to \a on.
803  */
804 void SalomeApp_Study::Modified()
805 {
806   myStudyDS->Modified();
807   LightApp_Study::Modified();
808 }
809
810 /*!
811   \return if data model is saved
812 */
813 bool SalomeApp_Study::isSaved() const
814 {
815   bool isAllSaved = studyDS() && studyDS()->GetPersistentReference().size();
816   if (!isAllSaved)
817     isAllSaved = LightApp_Study::isSaved();
818
819   return isAllSaved;
820 }
821
822 /*!
823   Saves data of module
824   \param theModuleName - name of module
825   \param theListOfFiles - list of files to be saved
826 */
827 void SalomeApp_Study::saveModuleData( QString theModuleName, int type, QStringList theListOfFiles )
828 {
829   int aNb = theListOfFiles.count();
830   if ( aNb == 0 )
831     return;
832
833   std::vector<std::string> aListOfFiles ( aNb );
834   int anIndex = 0;
835   for ( QStringList::Iterator it = theListOfFiles.begin(); it != theListOfFiles.end(); ++it ) {
836     if ( (*it).isEmpty() )
837       continue;
838     aListOfFiles[anIndex] = (*it).toUtf8().data();
839     anIndex++;
840   }
841   SetListOfFiles(theModuleName.toStdString().c_str(), type, aListOfFiles);
842 }
843
844 /*!
845   Loads data of module
846   \param theModuleName - name of module
847   \param theListOfFiles - list of files to be loaded
848 */
849 void SalomeApp_Study::openModuleData( QString theModuleName, int type, QStringList& theListOfFiles )
850 {
851   std::vector<std::string> aListOfFiles = GetListOfFiles( theModuleName.toStdString().c_str(), type );
852
853   int i, aLength = aListOfFiles.size() - 1;
854   if ( aLength < 0 )
855     return;
856
857   //Get a temporary directory for saved a file
858   theListOfFiles.append(aListOfFiles[0].c_str());
859
860   for(i = 0; i < aLength; i++)
861     theListOfFiles.append(aListOfFiles[i+1].c_str());
862 }
863
864 /*!
865   Re-implemented from LightApp_Study, actually does not save anything but
866   simply cleans up light modules' data
867 */
868 bool SalomeApp_Study::saveStudyData( const QString& theFileName, int type )
869 {
870   ModelList list; dataModels( list );
871   QListIterator<CAM_DataModel*> it( list );
872   while ( it.hasNext() ){
873     LightApp_DataModel* aLModel = 
874       dynamic_cast<LightApp_DataModel*>( it.next() );
875     // It is safe to call SetListOfFiles() for any kind of module
876     // because SetListOfFiles() does nothing for full modules :)
877     if ( aLModel )
878       SetListOfFiles(aLModel->module()->name().toStdString().c_str(), type, std::vector<std::string>());
879   }
880   return true;
881 }
882
883 /*!
884   Loads data for study
885 */
886 bool SalomeApp_Study::openStudyData( const QString& /*theFileName*/, int /*type*/ )
887 {
888   return true;
889 }
890
891 /*!
892   Virtual method re-implemented from LightApp_Study in order to create
893   the module object connected to SALOMEDS - SalomeApp_ModuleObject.
894
895   \param theDataModel - data model instance to create a module object for
896   \param theParent - the module object's parent (normally it's the study root)
897   \return the module object instance
898   \sa LightApp_Study class, LightApp_DataModel class
899 */
900 CAM_ModuleObject* SalomeApp_Study::createModuleObject( LightApp_DataModel* theDataModel, 
901                                                        SUIT_DataObject* theParent ) const
902 {
903   SalomeApp_Study* that = const_cast<SalomeApp_Study*>( this );
904   
905   // Ensure that SComponent instance is published in the study for the given module
906   // This line causes automatic creation of SalomeApp_ModuleObject in case if
907   // WITH_SALOMEDS_OBSERVER is defined
908   that->addComponent( theDataModel );
909   
910   // SalomeApp_ModuleObject might have been created by SALOMEDS observer
911   // or by someone else so check if it exists first of all
912   CAM_ModuleObject* res = 0;
913
914   DataObjectList children = root()->children();
915   DataObjectList::const_iterator anIt = children.begin(), aLast = children.end();
916   for( ; !res && anIt!=aLast; anIt++ )
917   {
918     SalomeApp_ModuleObject* obj = dynamic_cast<SalomeApp_ModuleObject*>( *anIt );
919     if ( obj && obj->componentDataType() == theDataModel->module()->name() )
920       res = obj;
921   }
922
923   if ( !res ){
924     _PTR(SComponent) aComp = myStudyDS->FindComponent(
925       theDataModel->module()->name().toStdString() );
926     if ( !aComp )
927       return res;
928
929     res = new SalomeApp_ModuleObject( theDataModel, aComp, theParent );
930   }
931
932   return res;
933 }
934
935 /*!
936   Insert data model.
937 */
938 void SalomeApp_Study::dataModelInserted (const CAM_DataModel* dm)
939 {
940   MESSAGE("SalomeApp_Study::dataModelInserted() : module name() = " << dm->module()->name().toStdString());
941
942   CAM_Study::dataModelInserted(dm);
943
944   //  addComponent(dm);
945 }
946
947 /*!
948   Create SComponent for module, using default engine (CORBAless)
949 */
950 void SalomeApp_Study::addComponent(const CAM_DataModel* dm)
951 {
952   SalomeApp_Module* aModule = dynamic_cast<SalomeApp_Module*>( dm->module() );
953   // 1. aModule == 0 means that this is a light module (no CORBA enigine)
954   if (!aModule) {
955     // Check SComponent existance
956
957     std::string aCompDataType = dm->module()->name().toStdString();
958
959     _PTR(SComponent) aComp = myStudyDS->FindComponent(aCompDataType);
960     if (!aComp) {
961       // Create SComponent
962       _PTR(StudyBuilder) aBuilder = myStudyDS->NewBuilder();
963       aComp = aBuilder->NewComponent(aCompDataType);
964       aBuilder->SetName(aComp, dm->module()->moduleName().toStdString());
965       QString anIconName = dm->module()->iconName();
966       if (!anIconName.isEmpty()) {
967         _PTR(AttributePixMap) anAttr = aBuilder->FindOrCreateAttribute(aComp, "AttributePixMap");
968         if (anAttr)
969           anAttr->SetPixMap(anIconName.toStdString());
970       }
971
972       // Set default engine IOR
973       // Issue 21377 - using separate engine for each type of light module
974       std::string anEngineIOR = SalomeApp_Engine_i::EngineIORForComponent( aCompDataType.c_str(),
975                                                                            true );
976       aBuilder->DefineComponentInstance(aComp, anEngineIOR);
977       //SalomeApp_DataModel::BuildTree( aComp, root(), this, /*skipExisitng=*/true );
978       SalomeApp_DataModel::synchronize( aComp, this );
979     }
980     else {
981       _PTR(StudyBuilder) aBuilder = myStudyDS->NewBuilder();
982       aBuilder->SetName(aComp, dm->module()->moduleName().toStdString());
983       QString anIconName = dm->module()->iconName();
984       if (!anIconName.isEmpty()) {
985         _PTR(AttributePixMap) anAttr = aBuilder->FindOrCreateAttribute(aComp, "AttributePixMap");
986         if (anAttr)
987           anAttr->SetPixMap(anIconName.toStdString());
988       }
989       // Set default engine IOR
990     }
991   }
992 }
993
994 /*!
995   Open data model
996 */
997 bool SalomeApp_Study::openDataModel( const QString& studyName, CAM_DataModel* dm )
998 {
999   if (!dm)
1000     return false;
1001
1002   //  SalomeApp_DataModel* aDM = (SalomeApp_DataModel*)(dm);
1003   SalomeApp_Module* aModule = dynamic_cast<SalomeApp_Module*>( dm->module() );
1004   _PTR(SComponent)  aSComp;
1005   QString anEngine;
1006   // 1. aModule == 0 means that this is a light module (no CORBA enigine)
1007   if (!aModule) {
1008     // Issue 21377 - using separate engine for each type of light module
1009     std::string aCompDataType = dm->module()->name().toStdString();
1010     anEngine = SalomeApp_Engine_i::EngineIORForComponent( aCompDataType.c_str(), true ).c_str();
1011     aSComp = myStudyDS->FindComponent( aCompDataType );
1012   }
1013   else {
1014     SalomeApp_DataModel* aDM = dynamic_cast<SalomeApp_DataModel*>( dm );
1015     if ( aDM ) {
1016       QString anId = aDM->getRootEntry( this );
1017       if ( anId.isEmpty() )
1018         return true; // Probably nothing to load
1019       anEngine = aDM->getModule()->engineIOR();
1020       if ( anEngine.isEmpty() )
1021         return false;
1022       aSComp = myStudyDS->FindComponentID( std::string( anId.toLatin1() ) );
1023     }
1024   }
1025   if ( aSComp ) {
1026     _PTR(StudyBuilder) aBuilder( myStudyDS->NewBuilder() );
1027     if ( aBuilder ) {
1028       try {
1029         aBuilder->LoadWith( aSComp, std::string( anEngine.toLatin1() ) );
1030       }
1031       catch( const SALOME::SALOME_Exception& ) {
1032         // Oops, something went wrong while loading -> return an error
1033         return false;
1034       }
1035       // Something has been read -> create data model tree
1036       //SalomeApp_DataModel* aDM = dynamic_cast<SalomeApp_DataModel*>( dm );
1037       // aDM->buildTree( aSComp, 0, this );
1038     }
1039   } else {
1040     // Don't return false here, for there might be no data
1041     // for a given component in the study yet
1042   }
1043   QStringList listOfFiles;
1044   openModuleData(dm->module()->name(), 0, // 0 means persistence file
1045                  listOfFiles);
1046   if (dm && dm->open(studyName, this, listOfFiles)) {
1047     // Remove the files and temporary directory, created
1048     // for this module by LightApp_Engine_i::Load()
1049     bool isMultiFile = false; // TODO: decide, how to access this parameter
1050     RemoveTemporaryFiles( dm->module()->name().toStdString().c_str(), isMultiFile, true );
1051     SetListOfFiles( dm->module()->name().toStdString().c_str(), 0, // 0 means persistence file
1052                     std::vector<std::string>() );
1053
1054     // Something has been read -> create data model tree
1055     LightApp_DataModel* aDM = dynamic_cast<LightApp_DataModel*>( dm );
1056     if ( aDM )
1057       aDM->update(NULL, this);
1058     return true;
1059   }
1060   return false;
1061 }
1062
1063 /*!
1064   Note that this method does not create or activate SalomeApp_Engine_i instance,
1065   therefore it can be called safely for any kind of module, but for full
1066   modules it returns an empty list.
1067   \return list of files used by module: to be used by CORBAless modules
1068   \param theModuleName - name of module
1069 */
1070 std::vector<std::string> SalomeApp_Study::GetListOfFiles( const char* theModuleName, int type ) const
1071 {
1072   // Issue 21377 - using separate engine for each type of light module
1073   SalomeApp_Engine_i* aDefaultEngine = SalomeApp_Engine_i::GetInstance( theModuleName, false );
1074   if (aDefaultEngine)
1075     return aDefaultEngine->GetListOfFiles( type );
1076
1077   return std::vector<std::string>();
1078 }
1079
1080 /*!
1081   Sets list of files used by module: to be used by CORBAless modules.
1082   Note that this method does not create or activate SalomeApp_Engine_i instance,
1083   therefore it can be called safely for any kind of module, but for full
1084   modules it simply does nothing.
1085   \param theModuleName - name of module
1086   \param theListOfFiles - list of files
1087 */
1088 void SalomeApp_Study::SetListOfFiles ( const char* theModuleName, int type,
1089                                        const std::vector<std::string> theListOfFiles )
1090 {
1091   // Issue 21377 - using separate engine for each type of light module
1092   SalomeApp_Engine_i* aDefaultEngine = SalomeApp_Engine_i::GetInstance( theModuleName, false );
1093   if (aDefaultEngine)
1094     aDefaultEngine->SetListOfFiles(type, theListOfFiles);
1095 }
1096
1097 /*!
1098   \return temporary directory for saving files of modules
1099 */
1100 std::string SalomeApp_Study::GetTmpDir ( const char* theURL, const bool  isMultiFile )
1101 {
1102   std::string anURLDir = SALOMEDS_Tool::GetDirFromPath(theURL);
1103   std::string aTmpDir = isMultiFile ? anURLDir : SALOMEDS_Tool::GetTmpDir();
1104   return aTmpDir;
1105 }
1106
1107 /*!
1108   Removes temporary files
1109 */
1110 void SalomeApp_Study::RemoveTemporaryFiles ( const char* theModuleName, bool isMultiFile, bool force )
1111 {
1112   if (isMultiFile)
1113     return;
1114
1115   SALOMEDS_Tool::ListOfFiles aListOfFiles = GetListOfFiles( theModuleName, 0 );
1116   if (aListOfFiles.size() > 0) {
1117     std::string aTmpDir = aListOfFiles[0];
1118
1119     const int n = aListOfFiles.size() - 1;
1120     std::vector<std::string> aSeq;
1121     aSeq.reserve(n);
1122     for (int i = 0; i < n; i++)
1123       aSeq.push_back(CORBA::string_dup(aListOfFiles[i + 1].c_str()));
1124
1125     SalomeApp_Engine_i* engine = SalomeApp_Engine_i::GetInstance( theModuleName, false );
1126     bool toRemove = force || engine && !engine->keepFiles();
1127
1128     if ( toRemove ) {
1129       SALOMEDS_Tool::RemoveTemporaryFiles(aTmpDir.c_str(), aSeq, true);
1130       engine->keepFiles( false );
1131     }
1132   }
1133 }
1134
1135 #ifndef DISABLE_PYCONSOLE
1136 /*!
1137   Mark the study as saved in the file
1138   \param theFileName - the name of file
1139 */
1140 void SalomeApp_Study::updateFromNotebook( const QString& theFileName, bool isSaved )
1141 {
1142   setStudyName(theFileName);
1143   studyDS()->URL(theFileName.toStdString());
1144   setIsSaved( isSaved );
1145 }
1146 #endif
1147
1148 LightApp_DataObject* SalomeApp_Study::findObjectByEntry( const QString& theEntry )
1149 {
1150   LightApp_DataObject* o = 0;
1151   if ( myObserver ) {
1152     o = dynamic_cast<LightApp_DataObject*>( myObserver->findObject( theEntry.toUtf8().constData() ) );
1153   }
1154   if ( !o ) {
1155     o = LightApp_Study::findObjectByEntry( theEntry );
1156   }
1157   return o;
1158 }
1159
1160 /*!
1161   Deletes all references to object
1162   \param obj - object
1163 */
1164 void SalomeApp_Study::deleteReferencesTo( _PTR( SObject ) obj )
1165 {
1166   _PTR(StudyBuilder) sb = studyDS()->NewBuilder();
1167   std::vector<_PTR(SObject)> aRefs = studyDS()->FindDependances( obj );
1168   for( int i=0, n=aRefs.size(); i<n; i++ )
1169   {
1170     _PTR( SObject ) o = aRefs[i];
1171     if( o->GetFatherComponent()->ComponentDataType()==obj->GetFatherComponent()->ComponentDataType() )
1172     {
1173       sb->RemoveReference( o );
1174       sb->RemoveObjectWithChildren( o );
1175     }
1176   }
1177 }
1178
1179 /*!
1180   \return real entry by entry of reference
1181   \param entry - entry of reference object
1182 */
1183 QString SalomeApp_Study::referencedToEntry( const QString& entry ) const
1184 {
1185   _PTR(SObject) obj = studyDS()->FindObjectID( entry.toStdString() );
1186   _PTR(SObject) refobj;
1187
1188   if( obj && obj->ReferencedObject( refobj ) )
1189     return refobj->GetID().c_str();
1190   return LightApp_Study::referencedToEntry( entry );
1191 }
1192
1193 /*!
1194   \return component data type for entry
1195 */
1196 QString SalomeApp_Study::componentDataType( const QString& entry ) const
1197 {
1198   _PTR(SObject) obj( studyDS()->FindObjectID( entry.toStdString() ) );
1199   if ( !obj )
1200     return LightApp_Study::componentDataType( entry );
1201   return obj->GetFatherComponent()->ComponentDataType().c_str();
1202 }
1203
1204 /*!
1205   \return true if entry corresponds to component
1206 */
1207 bool SalomeApp_Study::isComponent( const QString& entry ) const
1208 {
1209   _PTR(SObject) obj( studyDS()->FindObjectID( entry.toStdString() ) );
1210   return obj && QString( obj->GetID().c_str() ) == obj->GetFatherComponent()->GetID().c_str();
1211 }
1212
1213 /*!
1214   \return entries of object children
1215 */
1216 void SalomeApp_Study::children( const QString& entry, QStringList& child_entries ) const
1217 {
1218   _PTR(SObject) SO = studyDS()->FindObjectID( entry.toStdString() );
1219   _PTR(ChildIterator) anIter ( studyDS()->NewChildIterator( SO ) );
1220   anIter->InitEx( true );
1221   while( anIter->More() )
1222   {
1223     _PTR(SObject) val( anIter->Value() );
1224     child_entries.append( val->GetID().c_str() );
1225     anIter->Next();
1226   }
1227 }
1228
1229 /*!
1230   Fills list with components names
1231   \param comp - list to be filled
1232 */
1233 void SalomeApp_Study::components( QStringList& comps ) const
1234 {
1235   for( _PTR(SComponentIterator) it ( studyDS()->NewComponentIterator() ); it->More(); it->Next() )
1236   {
1237     _PTR(SComponent) aComponent ( it->Value() );
1238     // skip the magic "Interface Applicative" component
1239     if ( aComponent && aComponent->ComponentDataType() != getVisualComponentName().toLatin1().constData() )
1240       comps.append( aComponent->ComponentDataType().c_str() );
1241   }
1242 }
1243
1244 /*!
1245   Get the entry for the given module
1246   \param comp - list to be filled
1247   \return module root's entry
1248 */
1249 QString SalomeApp_Study::centry( const QString& comp ) const
1250 {
1251   QString e;
1252   for( _PTR(SComponentIterator) it ( studyDS()->NewComponentIterator() ); it->More() && e.isEmpty(); it->Next() )
1253   {
1254     _PTR(SComponent) aComponent ( it->Value() );
1255     if ( aComponent && comp == aComponent->ComponentDataType().c_str() )
1256       e = aComponent->GetID().c_str();
1257   }
1258   return e;
1259 }
1260
1261 /*!
1262   \return a list of saved points' IDs
1263 */
1264 std::vector<int> SalomeApp_Study::getSavePoints()
1265 {
1266   std::vector<int> v;
1267
1268   _PTR(SObject) so = studyDS()->FindComponent( getVisualComponentName().toLatin1().constData() );
1269   if(!so) return v;
1270
1271   _PTR(StudyBuilder) builder = studyDS()->NewBuilder();
1272   _PTR(ChildIterator) anIter ( studyDS()->NewChildIterator( so ) );
1273   for(; anIter->More(); anIter->Next())
1274   {
1275     _PTR(SObject) val( anIter->Value() );
1276     _PTR(GenericAttribute) genAttr;
1277     if(builder->FindAttribute(val, genAttr, "AttributeParameter")) v.push_back(val->Tag());
1278   }
1279
1280   return v;
1281 }
1282
1283 /*!
1284   Removes a given save point
1285 */
1286 void SalomeApp_Study::removeSavePoint(int savePoint)
1287 {
1288   if(savePoint <= 0) return;
1289  _PTR(AttributeParameter) AP = studyDS()->GetCommonParameters(getVisualComponentName().toLatin1().constData(), savePoint);
1290   _PTR(SObject) so = AP->GetSObject();
1291   _PTR(StudyBuilder) builder = studyDS()->NewBuilder();
1292   builder->RemoveObjectWithChildren(so);
1293 }
1294
1295 /*!
1296   \return a name of save point
1297 */
1298 QString SalomeApp_Study::getNameOfSavePoint(int savePoint)
1299 {
1300   _PTR(AttributeParameter) AP = studyDS()->GetCommonParameters(getVisualComponentName().toLatin1().constData(), savePoint);
1301   _PTR(IParameters) ip = ClientFactory::getIParameters(AP);
1302   return ip->getProperty("AP_SAVEPOINT_NAME").c_str();
1303 }
1304
1305 /*!
1306   Sets a name of save point
1307 */
1308 void SalomeApp_Study::setNameOfSavePoint(int savePoint, const QString& nameOfSavePoint)
1309 {
1310   _PTR(AttributeParameter) AP = studyDS()->GetCommonParameters(getVisualComponentName().toLatin1().constData(), savePoint);
1311   _PTR(IParameters) ip = ClientFactory::getIParameters(AP);
1312   ip->setProperty("AP_SAVEPOINT_NAME", nameOfSavePoint.toStdString());
1313 }
1314
1315 /*!
1316  * \brief Restores the study state
1317  */
1318 void SalomeApp_Study::restoreState(int savePoint)
1319 {
1320   SalomeApp_VisualState((SalomeApp_Application*)application()).restoreState(savePoint);
1321 }
1322
1323
1324 /*!
1325   Slot: called on change of a root of a data model. Redefined from CAM_Study
1326 */
1327 void SalomeApp_Study::updateModelRoot( const CAM_DataModel* dm )
1328 {
1329   LightApp_Study::updateModelRoot( dm );
1330
1331   // calling updateSavePointDataObjects in order to set correct order of "Gui states" object
1332   // it must always be the last one.
1333   ((SalomeApp_Application*)application())->updateSavePointDataObjects( this );
1334 }