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