Salome HOME
Updated copyright comment
[modules/gui.git] / src / SUITApp / SUITApp.cxx
1 // Copyright (C) 2007-2024  CEA, EDF, 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 #ifndef DISABLE_PYCONSOLE
24 #include "SUITApp_init_python.hxx"
25 #endif
26
27 #include "GUI_version.h"
28 #include "SUITApp_Application.h"
29 #include "SUIT_Desktop.h"
30 #include "SUIT_LicenseDlg.h"
31 #include "SUIT_ResourceMgr.h"
32 #include "SUIT_Session.h"
33 #ifdef USE_SALOME_STYLE
34 #include "Style_Salome.h"
35 #endif // USE_SALOME_STYLE
36 #include "QtxSplash.h"
37
38 #include <QDir>
39 #include <QFile>
40 #include <QLocale>
41 #include <QRegExp>
42 #include <QString>
43 #include <QStringList>
44
45 #include <stdlib.h>
46
47 namespace
48 {
49   //! Get version of SALOME GUI module
50   static QString salomeVersion()
51   {
52     return GUI_VERSION_STR;
53   }
54
55   //! Extract application name from the library filename
56   static QString getAppName( const QString& libName )
57   {
58     QString appName = QFileInfo( libName ).baseName();
59     if ( appName.startsWith( "lib" ) )
60       appName = appName.mid( 3 );
61     return appName;
62   }
63
64   //! Custom resources manager, that allows customization of application name/version
65   //  via configuration/translation files.
66   class ResourceMgr : public SUIT_ResourceMgr
67   {
68   public:
69     ResourceMgr( bool iniFormat = false, const QString& appName = "LightApp" )
70     : SUIT_ResourceMgr( appName, "%1Config" )
71     {
72       customize( iniFormat, appName ); // activate customization
73       setCurrentFormat( iniFormat ? "ini" : "xml" );
74       setOption( "translators", QString( "%P_msg_%L.qm|%P_icons.qm|%P_images.qm" ) );
75     }
76
77     QString customName() const { return myCustomAppName; }
78     QString version() const { return myCustomAppVersion; }
79
80   private:
81     static void customize( bool iniFormat, const QString& appName )
82     {
83       // Try to retrieve actual application name and version from translation files.
84       // We create temporary resource manager and load translations.
85       // This procedure is supposed to be done only once, at first call.
86       if ( myCustomAppName.isNull() ) {
87         SUIT_ResourceMgr mgr( appName, "%1Config" );
88         mgr.setCurrentFormat( iniFormat ? "ini" : "xml" );
89         mgr.setWorkingMode( IgnoreUserValues ); // prevent reading data from user's file
90         mgr.loadLanguage( appName, "en" );
91
92         // actual application name can be customized via APP_NAME resource key
93         myCustomAppName = QObject::tr( "APP_NAME" ).trimmed();
94         if ( myCustomAppName == "APP_NAME" || myCustomAppName.toLower() == "salome" ) 
95           myCustomAppName = appName; // fallback name
96
97         // actual application name can be customized via APP_VERSION resource key
98         myCustomAppVersion = QObject::tr( "APP_VERSION" ).trimmed();
99         if ( myCustomAppVersion == "APP_VERSION" )
100           myCustomAppVersion = myCustomAppName == appName ? salomeVersion() : ""; // fallback version
101       }
102     }
103
104   protected:
105     QString userFileName( const QString& /*appName*/, const bool forLoad ) const
106     {
107       if ( version().isEmpty() ) return ""; 
108       return SUIT_ResourceMgr::userFileName( myCustomAppName, forLoad );
109     }
110
111     virtual long userFileId( const QString& _fname ) const
112     {
113       //////////////////////////////////////////////////////////////////////////////////////////////
114       // In SALOME and SALOME-based applications the user preferences file is named as
115       // - <AppName>.xml.<AppVersion> on Windows
116       // - <AppName>rc.<AppVersion> on Linux
117       // where
118       //   * AppName is application name, defaults to LightApp. Can be customized in SALOME-based
119       //     applications, see ResourceMgr above for more details.
120       //   * AppVersion is application version, defaults to current version of SALOME GUI module
121       //     if AppName is not customize, otherwise empty. Can be customized in SALOME-based
122       //     applications, see ResourceMgr above for more details.
123       //
124       // Since version 6.5.0 of SALOME, user file is stored in the ~/.config/salome
125       // directory. For backward compatibility, when user preferences from nearest
126       // version of application is searched, user home directory is also looked through,
127       // with lower priority.
128       // 
129       // Since version 6.6.0 of SALOME, user file name on Linux is no more prefixed by dot
130       // symbol since it is stored in the hidden ~/.config/salome directory. However, dot-prefixed
131       // files are also taken into account (with lower priority) for backward compatibility.
132       //
133       // Notes:
134       // - Currently the following format of version number is supported:
135       //   <major>[.<minor>[.<release>[<type><dev>]]]
136       //   Parts in square brackets are considered optional. Here:
137       //   * major   - major version id
138       //   * minor   - minor version id
139       //   * release - maintenance version id
140       //   * type    - dev or patch marker; it can be either one alphabetical symbol (from 'a' to 'z')
141       //               or 'rc' to point release candidate (case-insensitive)
142       //   * dev     - dev version or patch number
143       //   All numerical values must be of range [1-99].
144       //   Examples: 1.0, 6.5.0, 1.2.0a1, 3.3.3rc3 (release candidate 3), 11.0.0p1 (patch 1)
145       //
146       // - Versioning approach can be customized by implementing and using own resource manager class,
147       //   see QtxResurceMgr, SUIT_ResourceMgr classes, and ResourceMgr class above in this file.
148       //////////////////////////////////////////////////////////////////////////////////////////////
149
150       long id = -1;
151       if ( !myCustomAppName.isEmpty() ) {
152 #ifdef WIN32
153         // On Windows, user file name is something like LightApp.xml.6.5.0 where
154         // - LightApp is an application name (can be customized)
155         // - xml is a file format (xml or ini)
156         // - 6.5.0 is an application version, can include alfa/beta/rc marks, e.g. 6.5.0a3, 6.5.0rc1
157         QRegExp exp( QString( "%1\\.%2\\.([a-zA-Z0-9.]+)" ).arg( myCustomAppName ).arg( currentFormat() ) );
158 #else
159         // On Linux, user file name is something like LightApprc.6.5.0 where
160         // - LightApp is an application name (can be customized)
161         // - 6.5.0 is an application version, can include alfa/beta/rc marks, e.g. 6.5.0a3, 6.5.0rc1
162
163         // VSR 24/09/2012: issue 0021781: since version 6.6.0 user filename is not prepended with "."
164         // when it is stored in the ~/.config/<appname> directory;
165         // for backward compatibility we also check files prepended with "." with lower priority
166         QRegExp exp( QString( "\\.?%1rc\\.([a-zA-Z0-9.]+)" ).arg( myCustomAppName ) );
167 #endif
168         QString fname = QFileInfo( _fname ).fileName();
169         if ( exp.exactMatch( fname ) ) {
170           long fid = Qtx::versionToId( exp.cap( 1 ) );
171           if ( fid > 0 ) id = fid;
172         }
173       }
174       return id;
175     }
176
177   private:
178     static QString myCustomAppName;
179     static QString myCustomAppVersion;
180   };
181   QString ResourceMgr::myCustomAppName;
182   QString ResourceMgr::myCustomAppVersion;
183
184   //! Custom session, to use custom resource manager class.
185   class Session : public SUIT_Session
186   {
187   public:
188     Session( bool theIniFormat = false ) : SUIT_Session(), myIniFormat( theIniFormat ) {}
189     virtual SUIT_ResourceMgr* createResourceMgr( const QString& appName ) const
190     {
191       return new ResourceMgr( myIniFormat, appName );
192     }
193   private:
194     bool myIniFormat;
195   };
196 } // end of anonymous namespace
197
198 int main( int argc, char* argv[] )
199 {
200   // Set-up application settings configuration (as for QSettings)
201   // Note: these are default settings which can be customized (see below)
202   QApplication::setOrganizationName( "salome" );
203   QApplication::setApplicationName( "salome" );
204   QApplication::setApplicationVersion( salomeVersion() );
205
206   // Add <qtdir>/plugins dir to the pluins search path for image plugins
207   QString qtdir = Qtx::qtDir( "plugins" );
208   if ( !qtdir.isEmpty() )
209     QApplication::addLibraryPath( qtdir );
210
211   // Add application library path (to search style plugin etc...)
212   QString path = Qtx::addSlash( Qtx::getenv( "GUI_ROOT_DIR" ) ) + "bin/salome";
213   QApplication::addLibraryPath( QDir::toNativeSeparators( path ) );
214
215   // QSurfaceFormat should be set before creation of QApplication,  
216   // so to avoid conflicts beetween SALOME and ParaView QSurfaceFormats we should merge theirs formats
217   // (see void Qtx::initDefaultSurfaceFormat()) and set the resultant format here.
218   Qtx::initDefaultSurfaceFormat(); 
219
220   // Create Qt application instance: this should be done as early as possible!
221   // Note: QApplication forces setting locale LC_ALL to system one: setlocale(LC_ALL, "").
222   SUITApp_Application app( argc, argv );
223
224   // Initialize Python (if necessary)
225   // Note: Python forces setting locale LC_CTYPE to system one: setlocale(LC_CTYPE, "").
226 #ifndef DISABLE_PYCONSOLE
227   char* py_argv[] = {(char*)""};
228   SUIT_PYTHON::init_python( 1, py_argv );
229 #endif
230
231   // Treat command line arguments
232   bool debugExceptions  = false;
233   bool iniFormat        = false;
234   bool noSplash         = false;
235   bool useLicense       = false;
236   QStringList args;
237   foreach( QString arg, QApplication::arguments().mid(1) ) // omit 1st argument: app executable
238   {
239     if ( arg == "--no-exception-handler" )
240       debugExceptions = true;
241     else if ( arg == "--format=ini" )
242       iniFormat = true;
243     else if ( arg == "--no-splash" )
244       noSplash = true;
245     else if ( arg == "--show-license" )
246       useLicense = true;
247     else if ( !arg.startsWith( "-" ) )
248       args << arg;
249   }
250   if ( args.empty() )
251     args << "LightApp"; // fallback application library
252
253   QString appName = getAppName( args.first() );
254
255   // Create auxiliary resource manager to access application settings
256   ResourceMgr resMgr( iniFormat, appName );
257   resMgr.setWorkingMode( ResourceMgr::IgnoreUserValues );
258   resMgr.loadLanguage( appName, "en" );
259
260   // Set-up application settings configuration possible customized via resources
261   if ( resMgr.customName() != "LightApp" ) {
262     QApplication::setApplicationName( resMgr.customName() );
263     QApplication::setApplicationVersion( resMgr.version() );
264   }
265
266   // Force default "C" locale if requested via user's preferences
267   // Note: this does not change whole application locale (changed via setlocale() function),
268   // but only affects GUI behavior
269   resMgr.setWorkingMode( ResourceMgr::AllowUserValues ); // we must take into account user preferences
270   if ( resMgr.booleanValue( "language", "locale", true ) )
271     QLocale::setDefault( QLocale::c() );
272   resMgr.setWorkingMode( ResourceMgr::IgnoreUserValues );
273
274 #if defined(GLOBAL_DOUBLE_CONVERSION)
275   // VSR 30/03/2021: moved here from QtxDoubleSpinBox/QtxIntSpinBox because of crash on Qt 5.12.
276   // Disable thousands separator for spin box
277   // see issue 14540 (old id 21219)
278   QLocale locale;
279   locale.setNumberOptions(locale.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator);
280   QLocale::setDefault(locale);
281 #endif
282
283   if ( !debugExceptions )
284     debugExceptions = resMgr.booleanValue( "launch", "noexcepthandler", false );
285   if ( !noSplash )
286     noSplash = !resMgr.booleanValue( "launch", "splash", true );
287   if ( !useLicense )
288     useLicense = resMgr.booleanValue( "launch", "license", false );
289
290   // If asked, read the text from a file show a license dialog
291   // TODO: path to license file, and option to check license, may be defined in XML cfg file.
292   if ( useLicense ) {
293     QFile file( QDir::home().filePath( "ReadLicense.log" ) );
294     if ( !file.exists() ) {
295       SUIT_LicenseDlg aLicense;
296       if ( aLicense.exec() != QDialog::Accepted ) 
297         return 1; // license is not accepted!
298     }
299   }
300
301   // If not disabled, show splash screen
302   QtxSplash* splash = 0;
303   if ( !noSplash ) 
304   {
305     splash = QtxSplash::splash( QPixmap() );
306     splash->readSettings( &resMgr );
307     if ( splash->pixmap().isNull() )
308       splash->setPixmap( resMgr.loadPixmap( appName, QObject::tr( "ABOUT_SPLASH" ) ) );
309     if ( splash->pixmap().isNull() )
310       splash->setPixmap( resMgr.loadPixmap( "LightApp", QObject::tr( "ABOUT_SPLASH" ) ) );
311     if ( splash->pixmap().isNull() ) {
312       delete splash;
313       splash = 0;
314     }
315     else {
316       splash->setOption( "%A", QObject::tr( "APP_NAME" ) );
317       splash->setOption( "%V", QObject::tr( "ABOUT_VERSION" ).arg( resMgr.version() ) );
318       splash->setOption( "%L", QObject::tr( "ABOUT_LICENSE" ) );
319       splash->setOption( "%C", QObject::tr( "ABOUT_COPYRIGHT" ) );
320       splash->show();
321       QApplication::instance()->processEvents();
322     }
323   }
324
325   // Create session
326   Session session( iniFormat );
327
328   // Initialize and start application supplied by the library specified via the parameter
329   SUIT_Application* sessionApp = session.startApplication( appName );
330   if ( sessionApp )
331   {
332 #ifdef USE_SALOME_STYLE
333     Style_Salome::initialize( session->resourceMgr() );
334     if ( session->resourceMgr()->booleanValue( "Style", "use_salome_style", true ) )
335       Style_Salome::apply();
336 #endif // USE_SALOME_STYLE
337
338     if ( !debugExceptions )
339       app.setHandler( session.handler() );
340
341     if ( splash )
342       splash->finish( sessionApp->desktop() );
343
344     return app.exec();
345   }
346
347   return 1;
348 }