Salome HOME
Moved some functionality to VTKViewer_Utilities.h
[modules/kernel.git] / src / SALOMEGUI / QAD_ResourceMgr.cxx
1 //  SALOME SALOMEGUI : implementation of desktop and GUI kernel
2 //
3 //  Copyright (C) 2003  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.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
21 //
22 //
23 //
24 //  File   : QAD_ResourceMgr.cxx
25 //  Author : UI team
26 //  Module : SALOME
27 //  $Header$
28
29 /*!
30   \class QAD_ResourceMgr QAD_ResourceMgr.h
31   \brief ResourceMgr QAD-based application.
32 */
33
34 #include "QAD.h"
35 #include "QAD_Tools.h"
36 #include "QAD_MessageBox.h"
37 #include "QAD_ResourceMgr.h"
38
39 #include "utilities.h"
40
41 #include <qfile.h>
42 #include <stdlib.h>
43 #include <qtranslator.h>
44 #include <qapplication.h>
45 #include <qfileinfo.h>
46
47 #include <Standard.hxx>
48 using namespace std;
49
50 /* configuration file */
51 static const char* CONFIG_FILE = "config";
52
53 /* config keys */
54 static const char* RES_DIR      = "res";            
55 static const char* RES_DOCS     = "docs";
56 static const char* RES_PIXMAPS  = "icons";
57 static const char* RES_STRINGS  = "strings";
58 static const char* RES_LANGUAGE = "language";
59 static const char* RES_FILES    = "resources";
60
61 static const char* SEPARATOR    = ":";
62
63 /*!
64     Constructor
65 */
66 QAD_ResourceMgr::QAD_ResourceMgr() :
67 myRes( 5, false )
68 {    
69     myRes.setAutoDelete( true );    
70 }
71
72 /*!
73     Destructor
74 */
75 QAD_ResourceMgr::~QAD_ResourceMgr()
76 {
77      myRes.clear();
78 }
79
80 /*!
81     Removes icons and messages from 'prefix'_msg_'lang'
82     and 'prefix'_icons' files. Returns 'true' if OK. 
83     Each application which has its own resources must
84     have a unique 'prefix' ( prefix "QAD" is reserved )
85 */
86 bool QAD_ResourceMgr::removeResources( const char* prefix )
87 {    
88   return myRes.remove(prefix);
89 }
90
91 /*!
92     Loads icons and messages from 'prefix'_msg_'lang'
93     and 'prefix'_icons' files. Returns 'true' if OK. 
94     Each application which has its own resources must
95     have a unique 'prefix' ( prefix "QAD" is reserved )
96 */
97 bool QAD_ResourceMgr::loadResources( const char* prefix, QString &msg )
98 {    
99   bool allLoaded = true;    
100   if ( !myRes[ prefix ] ) {
101     QCString dir ;
102     
103     /*  We read the settings once and keep them. 
104         The resources are loaded consequently from the end of directory list
105         which ( see collectDirs() method description ). This allows to override
106         resources when it is necessary.
107     */
108     
109     ResourceSettings* settings = new ResourceSettings();
110     StringDict& conf = settings->config();
111     myRes.insert( prefix, settings );
112     
113     // settings->config().insert( RES_DIR, new QString( resDir ) );
114     
115     /* we search language definition : we read it in config file
116        If not found, we use default : English
117     */
118     conf.insert( RES_LANGUAGE, new QString( "en" ) );
119     
120     /* Read configuration file */
121     
122     /* WE MUST HAVE ONE CONFIGURATION FILE FOR ALL SALOME !!!
123        I DON'T KNOW WHERE READ IT AND SAVE ITS CONTENTS FOR ALL GUI
124        ALL GUI HAS SAME LANGUAGE AND HAVE DEFAULT (en) IF SPECIFIED 
125        IS NOT FOUND !!
126     */
127     QString resDirs = collectDirs( prefix );
128     conf.insert( RES_DIR, new QString( resDirs ) );
129     QString fileConfig = QString( prefix ) + QString( "." ) + QString( CONFIG_FILE );
130     fileConfig = path( fileConfig, prefix, 0 ) ;
131     //MESSAGE("QAD_ResourceMgr::loadresources : config : "<<fileConfig);
132     if ( fileConfig.isEmpty() )
133       fileConfig = path( CONFIG_FILE, prefix, 0 ) ;
134     if ( !fileConfig.isEmpty() ) {
135       QFile configFile( fileConfig );
136       if ( !configFile.exists() || !configFile.open( IO_ReadOnly ) ) {
137         QString warnMsg;
138         warnMsg.sprintf( "Cannot open configuration file: %s\nDefault settings will be used.",
139                          configFile.name().latin1() );
140         msg = warnMsg;
141 //      removeResources( prefix );
142 //      return false;
143       } 
144       else {     
145         /* read 'config' file */
146         const int MAX_LINE = 512;
147         while ( !configFile.atEnd() ) {
148           QString line;
149           if ( configFile.readLine( line, MAX_LINE ) > 0 ) {
150             int index;
151             if ( ( index = line.find( "=" ) ) > 0 ) {
152               QString key = line.left(index).stripWhiteSpace();
153               QString value = line.mid( index+1 ).stripWhiteSpace();                    
154               conf.replace( key, new QString( value ) );
155             }
156           }
157         }
158         configFile.close();                        
159       }
160     }
161     
162     /* Load the resources */
163     QString stFile( prefix );
164     stFile = stFile + "_msg_" +  *( conf[ RES_LANGUAGE ] ) + ".qm" ;
165     QString imagesFile( prefix );
166     imagesFile = imagesFile + "_" + RES_PIXMAPS + ".qm";
167     if ( conf[ RES_STRINGS ] && !conf[ RES_STRINGS ]->isEmpty() )
168       stFile = QAD_Tools::addSlash( *conf[ RES_STRINGS ] ) + stFile;
169     if ( conf[ RES_PIXMAPS ] && !conf[ RES_PIXMAPS ]->isEmpty() )
170       imagesFile = QAD_Tools::addSlash( *conf[ RES_PIXMAPS ] ) + imagesFile;
171
172     bool bLoadString = false;
173     bool bLoadImages = false;
174
175     QStringList resFiles = conf[ RES_FILES ] ? QStringList::split( SEPARATOR, *( conf[ RES_FILES ] ), false ) : QStringList();
176     
177     // first load main module's resources
178     QString fileString = path( stFile, prefix, 0 );
179     QString fileImage  = path( imagesFile, prefix, 0 );
180     
181     if ( !fileString.isEmpty() && settings->load( fileString ) ) {
182       bLoadString = true;
183     }
184     if ( !fileImage.isEmpty() && settings->load( fileImage ) ) {
185       bLoadImages = true;
186     }
187
188     // then load additional module's resources if any are given
189     for ( int j = 0; j < resFiles.count(); j++ ) {
190       QString stFileAdd = resFiles[j] + "_msg_" +  *( conf[ RES_LANGUAGE ] ) + ".qm" ;
191       QString imagesFileAdd = resFiles[j] + "_" + RES_PIXMAPS + ".qm";
192       if ( conf[ RES_STRINGS ] && !conf[ RES_STRINGS ]->isEmpty() )
193         stFileAdd = QAD_Tools::addSlash( *conf[ RES_STRINGS ] ) + stFileAdd;
194       if ( conf[ RES_PIXMAPS ] && !conf[ RES_PIXMAPS ]->isEmpty() )
195         imagesFileAdd = QAD_Tools::addSlash( *conf[ RES_PIXMAPS ] ) + imagesFileAdd;
196       
197       QString fileStringAdd = path( stFileAdd, prefix, 0 );
198       QString fileImageAdd  = path( imagesFileAdd, prefix, 0 );
199       
200       if ( !fileStringAdd.isEmpty() )
201         settings->load( fileStringAdd );
202       if ( !fileImageAdd.isEmpty() )
203         settings->load( fileImageAdd );
204     }
205
206     if ( !bLoadString ) {
207       QString warnMsg;
208       warnMsg.sprintf( "String resources for module %s not found.\n"
209                        "Please, check your settings.", 
210                        prefix );
211       msg = warnMsg;
212 //      removeResources( prefix );
213       return false;
214     }
215     if ( !bLoadImages ) {
216       QString warnMsg;
217       warnMsg.sprintf( "Icons resources for module %s not found.\n"
218                        "Please, check your settings.", 
219                        prefix );
220       msg = warnMsg;
221 //      removeResources( prefix );
222       return false;
223     }
224     allLoaded = bLoadString && bLoadImages;
225   }
226   return allLoaded;
227 }
228
229 /*!
230   Returns language setting for the module 'prefix' ( e.g. "en" )
231 */
232 QString QAD_ResourceMgr::language( const char* prefix ) const
233 {
234   QString ret;
235   ResourceSettings* rs = myRes[ prefix ];
236   if ( rs ) 
237     {
238       StringDict& conf = rs->config();
239       ret = *(conf[RES_LANGUAGE]);
240     }
241   return ret;
242 }
243
244 /*!
245   Returns list of directories where resources can be located
246   See collectDirs() method description for more detail
247 */
248 QString QAD_ResourceMgr::resources( const char* prefix ) const
249 {
250   QString ret;
251   ResourceSettings* rs = myRes[ prefix ];
252   if ( rs ) 
253     {
254       StringDict& conf = rs->config();
255       ret = *(conf[RES_DIR]);
256     }
257   return ret;
258 }
259
260 /*!
261   Collects list of directories, separated by ';' where resources for module 'prefix'
262   can be situated
263   The order is following : 
264   - <prefix>_ROOT_DIR/share/salome/resources directory
265   - SALOME_<prefix>Resources env.var directory ( or directory list )
266   - ${HOME}/.salome/resources directory
267   - ${SALOME_SITE_DIR}/share/${SALOME_SITE_NAME}/resources directory (for SALOME-based applications)
268   - ${KERNEL_ROOT_DIR}/share/salome/resources directory
269 */
270 QString QAD_ResourceMgr::collectDirs( const QString& prefix ) const
271 {
272   QString dirList;
273   QCString envVar;
274   QString dir;
275   char* cenv;
276   
277   if ( !prefix.isEmpty() ) {
278     envVar = prefix.latin1() + QCString( "_ROOT_DIR" );
279     cenv = getenv( ( const char* ) envVar );
280     if ( cenv ) {
281       dir.sprintf( "%s", cenv );
282       if ( !dir.isEmpty() ) {
283         dir = QAD_Tools::addSlash(dir) ;
284         dir = dir + "share" ;
285         dir = QAD_Tools::addSlash(dir) ;
286         dir = dir + "salome" ;
287         dir = QAD_Tools::addSlash(dir) ;
288         dir = dir + "resources" ;
289         dir = QAD_Tools::addSlash(dir) ;
290         dirList.append( dirList.isEmpty() ? dir : ( QString( SEPARATOR ) + dir ) );
291       }
292     }
293   }
294
295   // Try SALOME_<prefix>Resources env.var directory ( or directory list )
296   if ( !prefix.isEmpty() ) {
297     envVar = QCString( "SALOME_" ) + prefix.latin1() + QCString( "Resources" );
298     cenv = getenv( ( const char* ) envVar );
299     if ( cenv ) {
300       dir.sprintf( "%s", cenv );
301       if ( !dir.isEmpty() )
302         dirList.append( dirList.isEmpty() ? dir : ( QString( SEPARATOR ) + dir ) );
303     }
304   }
305   // Try ${HOME}/.salome/resources directory
306   cenv = getenv( "HOME" );
307   if ( cenv ) {
308     dir.sprintf( "%s", cenv );
309     if ( !dir.isEmpty() ) {
310       dir = QAD_Tools::addSlash(dir) ;
311       dir = dir + ".salome" ;
312       dir = QAD_Tools::addSlash(dir) ;
313       dir = dir + "resources" ;
314       dir = QAD_Tools::addSlash(dir) ;
315       dirList.append( dirList.isEmpty() ? dir : ( QString( SEPARATOR ) + dir ) );
316     }
317   }
318   // Try ${SALOME_SITE_DIR}/share/${SALOME_SITE_NAME}/resources directory
319   cenv = getenv( "SALOME_SITE_DIR" );
320   if ( cenv ) {
321     dir.sprintf( "%s", cenv );
322     if ( !dir.isEmpty() ) {
323       dir = QAD_Tools::addSlash(dir) ;
324       dir = dir + "share" ;
325       dir = QAD_Tools::addSlash(dir) ;
326       cenv = getenv( "SALOME_SITE_NAME" );
327       if ( cenv ) 
328         dir = dir + cenv ;
329       else
330         dir = dir + "salome" ;
331       dir = QAD_Tools::addSlash(dir) ;
332       dir = dir + "resources" ;
333       dir = QAD_Tools::addSlash(dir) ;
334       dirList.append( dirList.isEmpty() ? dir : ( QString( SEPARATOR ) + dir ) );
335     }
336   }
337   // Try ${KERNEL_ROOT_DIR}/share/salome/resources directory
338   cenv = getenv( "KERNEL_ROOT_DIR" );
339   if ( cenv ) {
340     dir.sprintf( "%s", cenv );
341     if ( !dir.isEmpty() ) {
342       dir = QAD_Tools::addSlash(dir) ;
343       dir = dir + "share" ;
344       dir = QAD_Tools::addSlash(dir) ;
345       dir = dir + "salome" ;
346       dir = QAD_Tools::addSlash(dir) ;
347       dir = dir + "resources" ;
348       dir = QAD_Tools::addSlash(dir) ;
349       dirList.append( dirList.isEmpty() ? dir : ( QString( SEPARATOR ) + dir ) );
350     }
351   }
352   //MESSAGE("QAD_ResourceMgr::collectDirs : "<<dirList.latin1()) ;
353   return dirList;
354 }
355
356 /*!
357     Returns a directory where 'filename' is located (filename is relative 
358     of the application identified by 'prefix' or empty string if file not found
359     Search is processed in different location : see collectDirs() method description
360 */
361 QString QAD_ResourceMgr::getFile( const QString& filename, const char* prefix ) const
362 {  
363   QFileInfo fi( path( filename, prefix, 0 ) );
364   if ( fi.isFile() && fi.exists() )
365     return fi.dirPath();
366   return QString();
367 }
368
369 /*!
370     Returns a directory where 'filename' is located (filename is relative 
371     of the application identified by 'prefix' or empty string if file not found
372     Search is processed in different location : see collectDirs() method description
373     The difference from above method that this function is used when resources 
374     is not yet actually loaded by application.
375 */
376 QString QAD_ResourceMgr::findFile( const QString& filename, const char* prefix ) const
377 {  
378   QString resDirs = collectDirs( prefix );
379   QStringList dirList = QStringList::split( SEPARATOR, resDirs, false ); // skip empty entries
380   for ( int i = 0; i < dirList.count(); i++ ) {
381     QString dir = dirList[ i ];
382     QFileInfo fi( QAD_Tools::addSlash( dir ) + filename );
383     if ( fi.isFile() && fi.exists() )
384       return fi.dirPath();
385   }
386   return QString();
387 }
388
389 /*!
390     Returns a path to file 'filename' (filename is relative 
391     of the application identified by 'prefix' and subdirectory identified by 'key'
392     or empty string if file not found.
393     Search is processed in different location : see collectDirs() method description
394
395     Returns a directory 'key' resource of the application 
396     identified by 'prefix'    
397 */
398 QString QAD_ResourceMgr::path( const QString& filename, const char* prefix, const char* key ) const
399 {   
400   ResourceSettings* rs = myRes[ prefix ];
401   if ( rs ) {
402     StringDict& conf = rs->config();
403     QString resDirs = QString( *( conf[ RES_DIR ] ) );
404     if ( !resDirs.isEmpty() ) {
405       //MESSAGE("QAD_ResourceMgr::resDirs : <"<<resDirs<<">") ;
406       QStringList dirList = QStringList::split( SEPARATOR, resDirs, false ); // skip empty entries
407       for ( int i = 0; i < dirList.count(); i++ ) {
408         QString dir = dirList[ i ];
409         dir = QAD_Tools::addSlash( dir );
410         if ( key && conf[ key ] ) {
411           dir = dir + QString( *( conf[ key ] ) );
412           dir = QAD_Tools::addSlash( dir );
413         }
414         dir = dir + filename;
415         QFileInfo fileInfo( dir );
416         if ( fileInfo.isFile() && fileInfo.exists() ) {
417           return fileInfo.filePath();
418         }
419       }
420     }
421   }
422   //MESSAGE("QAD_ResourceMgr::path : <"<<filename.latin1()<<"> : "<<filePath.latin1()) ;
423   return QString::null;
424 }
425
426 /*!
427     Loads a pixmap from 'resname' resources 
428     and indetified by 'id'
429 */
430 QPixmap QAD_ResourceMgr::loadPixmap( const char* resname, 
431                                      const QString& id ) const
432 {
433   return QPixmap( path( id, resname, RES_PIXMAPS ) );
434 }
435
436 /*!
437     Loads a doc page from 'resname' resources
438     and indetified by 'id'
439 */
440 bool QAD_ResourceMgr::loadDoc( const char* resname,
441                                const QString& id ) const
442 {    
443   QString docPath = path( id, resname, RES_DOCS );
444   return true;
445 }
446
447 /************************************************************************
448 **  
449 **  Class QAD_ResourceMgr::ResourceSettings ( internal )
450 **  
451 *************************************************************************/
452
453 /*!
454     Loads a resource 'file'. 
455     Returns 'false' if 'file' can't be loaded( not found etc. ),
456     'true' if loaded or reloaded OK. 
457 */
458 bool QAD_ResourceMgr::ResourceSettings::load( const QString& file )
459 {   
460 #if QT_VERSION >= 0x030000 // VSR: workaround - crash on qt3.0.5 ==========
461   static const int magic_length = 16;        // length of *.qm file header (qtranslator.cpp)
462   static const uchar magic[magic_length] = { // magic number for the file
463     0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95,
464     0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd };
465   QFile f(file);
466   if ( !f.exists() || f.size() < magic_length)
467     return false;
468   char buf[magic_length];
469   if ( !f.open(IO_ReadOnly) )
470     return false;
471   bool bOk = ( f.readBlock(buf, magic_length) == magic_length );
472   f.close();
473   if (!bOk)
474     return false;
475   if ( memcmp( (const void *)buf, magic, magic_length ) )
476     return false;
477   if ( f.size() == magic_length)
478     return true;
479 #endif // VSR =============================================================
480   QTranslator* strTbl = new QTranslator( 0 );    
481   try {
482     if ( !strTbl->load( file, "" ) ) {
483       delete strTbl;
484       return false;
485     }    
486   }
487   catch (...) {
488     return false;
489   }
490   QAD_ASSERT_DEBUG_ONLY( qApp );
491   qApp->installTranslator( strTbl );    
492   return true;
493 }