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