Salome HOME
refs #1458: disable chained panning on operations
[modules/gui.git] / src / SalomeApp / SalomeApp_Engine_i.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 //  SalomeApp_Engine_i : implementation of SalomeApp_Engine.idl
24 //  File   : SalomeApp_Engine_i.cxx
25 //  Author : Alexander SLADKOV
26
27 #include "SalomeApp_Engine_i.h"
28 #include "SalomeApp_Application.h"
29
30 #include <SALOME_NamingService.hxx>
31 #include <SALOMEDS_Tool.hxx>
32 #include <Utils_ORB_INIT.hxx>
33 #include <Utils_SINGLETON.hxx>
34 #include <Utils_SALOME_Exception.hxx>
35 #include <utilities.h>
36
37 #include <QApplication>
38 #include <QDir>
39 #include <QFile>
40
41 #include <iostream>
42
43 /*!
44   Constructor
45 */
46 SalomeApp_Engine_i::SalomeApp_Engine_i( const char* theComponentName )
47 {
48   myComponentName = theComponentName;
49   MESSAGE("SalomeApp_Engine_i::SalomeApp_Engine_i(): myComponentName = " <<
50           myComponentName << ", this = " << this);
51 }
52
53 /*!
54   Destructor
55 */
56 SalomeApp_Engine_i::~SalomeApp_Engine_i()
57 {
58   MESSAGE("SalomeApp_Engine_i::~SalomeApp_Engine_i(): myComponentName = " << 
59           myComponentName << ", this = " << this);
60 }
61
62 SALOMEDS::TMPFile* SalomeApp_Engine_i::Save (SALOMEDS::SComponent_ptr theComponent,
63                                              const char* theURL,
64                                              bool isMultiFile)
65 {
66   SALOMEDS::TMPFile_var aStreamFile = new SALOMEDS::TMPFile;
67
68   if (CORBA::is_nil(theComponent) || CORBA::is_nil(theComponent->GetStudy()))
69     return aStreamFile._retn();
70
71   const int studyId = theComponent->GetStudy()->StudyId();
72
73   // Get a temporary directory to store a file
74   //std::string aTmpDir = isMultiFile ? theURL : SALOMEDS_Tool::GetTmpDir();
75
76   if (myMap.count(studyId)) {
77     std::string componentName (theComponent->ComponentDataType());
78
79     // Error somewhere outside - Save() called with
80     // wrong SComponent instance
81     if ( myComponentName != componentName )
82       return aStreamFile._retn();
83
84     const ListOfFiles& listOfFiles = myMap[studyId];
85
86     // listOfFiles must contain temporary directory name in its first item
87     // and names of files (relatively the temporary directory) in the others
88     const int n = listOfFiles.size() - 1;
89
90     if (n > 0) { // there are some files, containing persistent data of the component
91       std::string aTmpDir = listOfFiles[0];
92
93       // Create a list to store names of created files
94       SALOMEDS::ListOfFileNames_var aSeq = new SALOMEDS::ListOfFileNames;
95       aSeq->length(n);
96       for (int i = 0; i < n; i++)
97         aSeq[i] = CORBA::string_dup(listOfFiles[i + 1].c_str());
98
99       // Convert a file to the byte stream
100       aStreamFile = SALOMEDS_Tool::PutFilesToStream(aTmpDir.c_str(), aSeq.in(), isMultiFile);
101
102       // Remove the files and tmp directory, created by the component storage procedure
103       if (!isMultiFile) SALOMEDS_Tool::RemoveTemporaryFiles(aTmpDir.c_str(), aSeq.in(), true);
104     }
105   }
106
107   return aStreamFile._retn();
108 }
109
110 CORBA::Boolean SalomeApp_Engine_i::Load (SALOMEDS::SComponent_ptr theComponent,
111                                          const SALOMEDS::TMPFile& theFile,
112                                          const char* theURL,
113                                          bool isMultiFile)
114 {
115   std::cout << "SalomeApp_Engine_i::Load() isMultiFile = " << isMultiFile << std::endl;
116   if (CORBA::is_nil(theComponent) || CORBA::is_nil(theComponent->GetStudy()))
117     return false;
118
119   // Error somewhere outside - Load() called with
120   // wrong SComponent instance
121   std::string componentName (theComponent->ComponentDataType());
122   if ( myComponentName != componentName )
123     return false;
124
125   const int studyId = theComponent->GetStudy()->StudyId();
126
127   // Create a temporary directory for the component's data files
128   std::string aTmpDir = isMultiFile ? theURL : SALOMEDS_Tool::GetTmpDir();
129
130   // Convert the byte stream theStream to a files and place them in the tmp directory.
131   // The files and temporary directory must be deleted by the component loading procedure.
132   SALOMEDS::ListOfFileNames_var aSeq =
133     SALOMEDS_Tool::PutStreamToFiles(theFile, aTmpDir.c_str(), isMultiFile);
134
135   // Store list of file names to be used by the component loading procedure
136   const int n = aSeq->length() + 1;
137   ListOfFiles listOfFiles (n);
138   listOfFiles[0] = aTmpDir;
139   for (int i = 1; i < n; i++)
140     listOfFiles[i] = std::string(aSeq[i - 1]);
141
142   SetListOfFiles(listOfFiles, studyId);
143
144   return true;
145 }
146
147 SalomeApp_Engine_i::ListOfFiles SalomeApp_Engine_i::GetListOfFiles (const int theStudyId)
148 {
149   ListOfFiles aListOfFiles;
150
151   if (myMap.find(theStudyId) != myMap.end())
152   {
153     aListOfFiles = myMap[theStudyId];
154   }
155
156   return aListOfFiles;
157 }
158
159 void SalomeApp_Engine_i::SetListOfFiles (const ListOfFiles& theListOfFiles,
160                                          const int          theStudyId)
161 {
162   myMap[theStudyId] = theListOfFiles;
163 }
164
165 /*! 
166  *  DumpPython implementation for light modules
167  */
168 Engines::TMPFile* SalomeApp_Engine_i::DumpPython(CORBA::Object_ptr theStudy, 
169                                                  CORBA::Boolean isPublished, 
170                                                  CORBA::Boolean isMultiFile, 
171                                                  CORBA::Boolean& isValidScript)
172 {
173   MESSAGE("SalomeApp_Engine_i::DumpPython(): myComponentName = "<<
174           myComponentName << ", this = " << this);
175   
176   // Temporary solution: returning a non-empty sequence
177   // even if there's nothing to dump, to avoid crashes in SALOMEDS
178   // TODO: Improve SALOMEDSImpl_Study::DumpStudy() by skipping the components 
179   // with isValidScript == false, and initialize isValidScript by false below.
180   Engines::TMPFile_var aStreamFile = new Engines::TMPFile(1);
181   aStreamFile->length( 1 );
182   aStreamFile[0] = '\0';
183   isValidScript = true;
184
185   if (CORBA::is_nil(theStudy))
186     return aStreamFile._retn();
187
188   SALOMEDS::Study_var studyDS = SALOMEDS::Study::_narrow( theStudy );
189   const int studyId = studyDS->StudyId();
190
191   if (!myMap.count(studyId))
192     return aStreamFile._retn();
193
194   ListOfFiles listOfFiles = myMap[studyId];
195
196   // listOfFiles must contain temporary directory name in its first item
197   // and names of files (relatively the temporary directory) in the others
198   if ( listOfFiles.size() < 2 ) 
199     return aStreamFile._retn();
200
201   // there are some files, containing persistent data of the component
202   QString aTmpPath( listOfFiles.front().c_str() );
203   QDir aTmpDir( aTmpPath );
204   if ( !aTmpDir.exists() )
205     return aStreamFile._retn();    
206
207   // Calculate file sizes
208   QStringList aFilePaths;
209   QList<qint64> aFileSizes;
210   qint64 aBuffSize = 0;
211   ListOfFiles::const_iterator aFIt  = listOfFiles.begin();
212   ListOfFiles::const_iterator aFEnd = listOfFiles.end();
213   aFIt++;
214   for (; aFIt != aFEnd; aFIt++){
215     QString aFileName( (*aFIt).c_str() );
216     if ( !aTmpDir.exists( aFileName ) ){
217       continue;
218     }
219
220     QFile aFile( aTmpDir.filePath( aFileName ) );
221     if ( !aFile.open( QIODevice::ReadOnly ) ){
222       continue;
223     }
224
225     aFilePaths.push_back( aTmpDir.filePath( aFileName ) );
226     aFileSizes.push_back( aFile.size() );
227     aBuffSize += aFileSizes.back();
228
229     aFile.close();
230   }
231
232   if ( !aFilePaths.size() || !aBuffSize )
233     return aStreamFile._retn(); 
234     
235   char* aBuffer = new char[aBuffSize + 1];
236   if ( !aBuffer )
237     return aStreamFile._retn();
238
239   // Convert the file(s) to the byte stream, multiple files are simply
240   // concatenated
241   // TODO: imporve multi-script support if necessary...
242   qint64 aCurrPos = 0;
243   QStringList::const_iterator aFileIt  = aFilePaths.begin();
244   QStringList::const_iterator aFileEnd = aFilePaths.end();
245   QList<qint64>::const_iterator   aSIt = aFileSizes.begin();
246   for ( ; aFileIt != aFileEnd; aFileIt++, aSIt++ ){
247     QFile aFile( aTmpDir.filePath( *aFileIt ) );
248     if ( !aFile.open( QIODevice::ReadOnly ) ){
249       continue;
250     }
251
252     // Incorrect size of file
253     // Do not remove the bad file to have some diagnostic means
254     if ( aFile.read( aBuffer + aCurrPos, *aSIt ) != *aSIt ){
255       aFile.close();      
256       return aStreamFile._retn();
257     }
258
259     aCurrPos += (*aSIt); 
260     aFile.remove();   
261   }
262
263   // Here we should end up with empty aTmpDir
264   // TODO: Handle QDir::rmdir() error status somehow...
265   aTmpDir.rmdir( aTmpPath );
266
267   aBuffer[aBuffSize] = '\0';
268   CORBA::Octet* anOctetBuf =  (CORBA::Octet*)aBuffer;
269   aStreamFile = new Engines::TMPFile(aBuffSize + 1, aBuffSize + 1, anOctetBuf, 1); 
270
271   return aStreamFile._retn();
272 }
273
274 /*!
275   \return Component data type string for this instance of the engine
276 */
277 char* SalomeApp_Engine_i::ComponentDataType()
278 {
279   return const_cast<char*>( myComponentName.c_str() );
280 }
281
282 /*!
283   \return Component version
284 */
285 char* SalomeApp_Engine_i::getVersion()
286 {
287   SalomeApp_Application::ModuleShortInfoList versions = SalomeApp_Application::getVersionInfo();
288   QString version;
289   SalomeApp_Application::ModuleShortInfo version_info;
290   foreach ( version_info, versions ) {
291     if ( SalomeApp_Application::moduleName( version_info.name ) == myComponentName.c_str() ) {
292       version = version_info.version;
293       break;
294     }
295   }
296   
297   return CORBA::string_dup( version.toLatin1().constData() );
298 }
299
300 /*!
301   \return 
302 */
303 CORBA::ORB_var SalomeApp_Engine_i::orb()
304 {
305   static CORBA::ORB_var _orb;
306
307   if ( CORBA::is_nil( _orb ) ) {
308     Qtx::CmdLineArgs args;
309     ORB_INIT& init = *SINGLETON_<ORB_INIT>::Instance();
310     _orb = init( args.argc(), args.argv() );
311   }
312
313   return _orb;
314 }
315
316 /*!
317   \return 
318 */
319 PortableServer::POA_var SalomeApp_Engine_i::poa()
320 {
321   static PortableServer::POA_var _poa;
322   if ( CORBA::is_nil( _poa ) ){
323     CORBA::Object_var obj = orb()->resolve_initial_references( "RootPOA" );
324     _poa = PortableServer::POA::_narrow( obj );
325   }
326   return _poa;
327 }
328
329 /*!
330   \return 
331 */
332 SALOME_NamingService* SalomeApp_Engine_i::namingService()
333 {
334   static SALOME_NamingService _ns(orb());
335   return &_ns;
336 }
337
338 /*!
339   Internal method, creates a CORBA engine for a light SALOME module
340   with the given "component data type" string,
341   activates it and registers in SALOME naming service with
342   /SalomeAppEngine/comp_data_type path. If the engine is already in the 
343   naming service, simply returns and object reference to it.
344   \param theComponentName - synthetic "component data type" used to identify a given light module
345   \return Object reference to the CORBA engine
346 */
347 CORBA::Object_ptr SalomeApp_Engine_i::EngineForComponent( const char* theComponentName,
348                                                           bool toCreate )
349 {
350   CORBA::Object_var anEngine;
351   if ( !theComponentName || !strlen( theComponentName ) )
352     return anEngine._retn();
353
354   if ( SalomeApp_Application::moduleTitle( theComponentName ).isEmpty() )
355     return anEngine._retn();
356
357   std::string aPath( "/SalomeAppEngine/" );
358   aPath += theComponentName;
359   anEngine = namingService()->Resolve( aPath.c_str() );
360
361   // Activating a new instance of the servant
362   if ( toCreate && CORBA::is_nil( anEngine ) ){
363     try {
364       SalomeApp_Engine_i* aServant    = new SalomeApp_Engine_i( theComponentName );
365       PortableServer::ObjectId_var id = poa()->activate_object( aServant );
366       anEngine = aServant->_this();
367       aServant->_remove_ref();
368       namingService()->Register( anEngine.in(), aPath.c_str() );
369     }
370     catch (CORBA::SystemException&) {
371       INFOS("Caught CORBA::SystemException.");
372     }
373     catch (CORBA::Exception&) {
374       INFOS("Caught CORBA::Exception.");
375     }
376     catch (...) {
377       INFOS("Caught unknown exception.");
378     }
379   }
380
381   return anEngine._retn();
382 }
383
384 /*!
385   \param theComponentName - synthetic "component data type" used to identify a given light module
386   \return IOR string for the CORBA engine for a light SALOME module
387   with the given "component data type" string
388   \sa GetInstance( const char* theComponentName )
389 */
390 std::string SalomeApp_Engine_i::EngineIORForComponent( const char* theComponentName,
391                                                        bool toCreate )
392 {
393   std::string anIOR( "" );
394   CORBA::Object_var anEngine = EngineForComponent( theComponentName, toCreate );
395   if ( !CORBA::is_nil( anEngine ) )
396   {
397     CORBA::String_var objStr = orb()->object_to_string( anEngine.in() );
398     anIOR = std::string( objStr.in() );
399   }
400   return anIOR;
401 }
402
403 /*!
404   \param theComponentName - synthetic "component data type" used to identify a given light module
405   \return A pointer to corresponding C++ engine instance, null means some internal problems.
406   \sa EngineIORForComponent( const char* theComponentName )
407 */
408 SalomeApp_Engine_i* SalomeApp_Engine_i::GetInstance( const char* theComponentName,
409                                                      bool toCreate )
410 {
411   SalomeApp_Engine_i* aServant = 0;
412   CORBA::Object_var anEngine = EngineForComponent( theComponentName, toCreate );
413   if ( !CORBA::is_nil( anEngine ) )
414   {
415     PortableServer::Servant aServantBase = poa()->reference_to_servant( anEngine.in() );
416     aServant = dynamic_cast<SalomeApp_Engine_i*>( aServantBase );
417   } 
418   MESSAGE("SalomeApp_Engine_i::GetInstance(): theComponentName = " <<
419           theComponentName << ", aServant = " << aServant);
420   return aServant;
421 }