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