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