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