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