Salome HOME
bos #29467 SALOME GUI logger
[modules/gui.git] / src / CAM / CAM_Application.cxx
1 // Copyright (C) 2007-2023  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 #include "CAM_Application.h"
24
25 #include "CAM_Study.h"
26 #include "CAM_Module.h"
27
28 #include <SUIT_Tools.h>
29 #include <SUIT_Desktop.h>
30 #include <SUIT_Session.h>
31 #include <SUIT_MessageBox.h>
32 #include <SUIT_ResourceMgr.h>
33
34 #include <KERNEL_version.h>
35 #include <GUI_version.h>
36
37 #include <QAction>
38 #include <QApplication>
39 #include <QDir>
40 #include <QFileInfo>
41 #include <QMutex>
42 #include <QMutexLocker>
43 #include <QRegExp>
44 #include <QTextStream>
45
46 #ifdef WIN32
47 #include <windows.h>
48 #else
49 #include <dlfcn.h>
50 #endif
51
52 #include <cstdio>
53 #include <iostream>
54
55 #include <utilities.h>
56
57 namespace
58 {
59 class BusyLocker
60 {
61 public:
62   BusyLocker( bool& busy ) : myPrev( busy ), myBusy( busy ) { myBusy = true; }
63   ~BusyLocker() { myBusy = myPrev; }
64 private:
65   bool  myPrev;
66   bool& myBusy;
67 };
68 }
69
70 /*!
71   \brief Create new instance of CAM_Application.
72   \return new instance of CAM_Application class
73 */
74 extern "C" CAM_EXPORT SUIT_Application* createApplication()
75 {
76   return new CAM_Application();
77 }
78
79 /*!
80   \class CAM_Application
81   \brief Introduces an application class which provides modular architecture.
82
83   This class defines multi-modular application configuration and behaviour.
84   Each module (CAM_Module) can have own data model, document windows and
85   viewers, etc.
86
87   An application provides all necessary functionality for modules management,
88   like
89   - loading of modules
90   - modules activation/deactivation
91   - etc
92 */
93
94 CAM_Application::ModuleInfoList CAM_Application::myInfoList;
95
96 /*!
97   \brief Constructor.
98
99   Read modules list (from command line or from resource file).
100   If \a autoLoad parameter is \c true all the modules will be loaded
101   immediately after application starting, otherwise each module will
102   be loaded by demand (with activateModule()).
103
104   \param autoLoad auto loading flag
105 */
106 CAM_Application::CAM_Application( const bool autoLoad )
107 : STD_Application(),
108   myModule( 0 ),
109   myAutoLoad( autoLoad ),
110   myBlocked( false )
111 {
112   readModuleList();
113 }
114
115 /*!
116   \brief Destructor.
117
118   Does nothing currently.
119 */
120 CAM_Application::~CAM_Application()
121 {
122   for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end(); ++it )
123     delete *it;
124   myModules.clear();
125 }
126
127 /*!
128   \brief Start an application.
129
130   Load all modules, if "auto loading" flag has been set to \c true.
131
132   \sa CAM_Application()
133 */
134 void CAM_Application::start()
135 {
136   // check modules
137   for ( ModuleInfoList::iterator it = myInfoList.begin();
138         it != myInfoList.end(); ++it )
139   {
140     if ( (*it).status == stUnknown )
141       (*it).status = checkModule( (*it).title ) ? stReady : stInaccessible;
142   }
143
144   // auto-load modules
145   if ( myAutoLoad )
146     loadModules();
147
148   STD_Application::start();
149 }
150
151 /*!
152   \brief Get active module.
153   \return active module or 0 if there are no any
154 */
155 CAM_Module* CAM_Application::activeModule() const
156 {
157   return myModule;
158 }
159
160 /*!
161   \brief Get the module with specified name.
162   \return module or 0 if not found
163 */
164 CAM_Module* CAM_Application::module(  const QString& modName ) const
165 {
166   CAM_Module* mod = 0;
167   for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
168         it != myModules.end() && !mod; ++it )
169     if ( (*it)->moduleName() == modName )
170       mod = *it;
171   return mod;
172 }
173
174 /*!
175   \brief Get all loaded modules.
176   \return list of modules
177 */
178 CAM_Application::ModuleList CAM_Application::modules() const
179 {
180   return myModules;
181 }
182
183 /*!
184   \brief Get all loaded modules.
185   \param returning list of modules
186 */
187 void CAM_Application::modules( CAM_Application::ModuleList& out ) const
188 {
189   out.clear();
190
191   for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
192         it != myModules.end(); ++it )
193     out.append( *it );
194 }
195
196 /*!
197   \brief Get names of all modules.
198
199   Get loaded modules names if \a loaded is \c true,
200   otherwise get all avaiable modules names.
201
202   \param lst output list of modules names
203   \param loaded boolean flag, defines what modules names to return
204 */
205 void CAM_Application::modules( QStringList& lst, const bool loaded ) const
206 {
207   lst.clear();
208
209   if ( loaded )
210   {
211     for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
212           it != myModules.end(); ++it )
213       lst.append( (*it)->moduleName() );
214   }
215   else
216   {
217     for ( ModuleInfoList::const_iterator it = myInfoList.begin();
218           it != myInfoList.end(); ++it )
219       if ( (*it).status != stNoGui )
220         lst.append( (*it).title );
221   }
222 }
223
224 /*!
225   \brief Add module \a mod to the modules list.
226
227   Performes module initialization. Does nothing if the module
228   is already added.
229
230   \param mod module being added
231   \sa CAM_Module::initialize()
232 */
233 void CAM_Application::addModule( CAM_Module* mod )
234 {
235   if ( !mod || myModules.contains( mod ) )
236     return;
237
238   mod->initialize( this );
239
240   QMap<CAM_Module*, int> map;
241
242   ModuleList newList;
243   for ( ModuleInfoList::const_iterator it = myInfoList.begin();
244         it != myInfoList.end(); ++it )
245   {
246     if ( (*it).title == mod->moduleName() )
247       newList.append( mod );
248     else
249     {
250       CAM_Module* curMod = module( (*it).title );
251       if ( curMod )
252         newList.append( curMod );
253     }
254   }
255
256   for ( QList<CAM_Module*>::const_iterator it = myModules.begin();
257         it != myModules.end(); ++it )
258   {
259     if ( !newList.contains( *it ) )
260       newList.append( *it );
261   }
262
263   if ( !newList.contains( mod ) )
264       newList.append( mod );
265
266   myModules = newList;
267
268   moduleAdded( mod );
269 }
270
271 /*!
272   \brief Load modules from the modules information list.
273
274   If some module can not be loaded, an error message is shown.
275 */
276 void CAM_Application::loadModules()
277 {
278   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end(); ++it )
279   {
280     CAM_Module* mod = loadModule( (*it).title );
281     if ( mod )
282       addModule( mod );
283     else {
284       QString wrn = tr( "Can not load module %1" ).arg( (*it).title );
285       if ( desktop() && desktop()->isVisible() )
286         SUIT_MessageBox::critical( desktop(), tr( "Loading modules" ), wrn );
287       else
288         qWarning( qPrintable( wrn ) );
289     }
290   }
291 }
292
293 /*!
294   \brief Load module \a modName.
295
296   The function prints warning message if:
297   - modules information list is empty
298   - modules information list does not include specified module info
299   - module library can not be loaded by some reason
300
301   \param modName module name
302   \return module object pointer or 0 if module could not be loaded
303 */
304 CAM_Module* CAM_Application::loadModule( const QString& modName, const bool showMsg )
305 {
306   if ( myInfoList.isEmpty() )
307   {
308     qWarning( qPrintable( tr( "Modules configuration is not defined." ) ) );
309     return 0;
310   }
311
312   QString libName = moduleLibrary( modName );
313   if ( libName.isEmpty() )
314   {
315     qWarning( qPrintable( tr( "Information about module \"%1\" doesn't exist." ).arg( modName ) ) );
316     return 0;
317   }
318
319   QString err;
320   GET_MODULE_FUNC crtInst = 0;
321   GET_VERSION_FUNC getVersion = 0;
322
323 #ifdef WIN32
324 #ifdef UNICODE
325   LPTSTR str_libname = new TCHAR[libName.length() + 1];
326   str_libname[libName.toWCharArray(str_libname)] = '\0';
327 #else
328   QByteArray arr = libName.toLatin1();
329   LPTSTR str_libname = arr.constData();
330 #endif
331   HINSTANCE modLib = ::LoadLibrary( str_libname );
332 #ifdef UNICODE
333   delete str_libname;
334 #endif
335   if ( !modLib )
336   {
337     LPVOID lpMsgBuf;
338     ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
339                      FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
340 #ifdef UNICODE
341         QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
342 #else
343         QString out_err = (LPTSTR)lpMsgBuf;
344 #endif
345     err = QString( "Failed to load  %1. %2" ).arg( libName ).arg(out_err);
346     ::LocalFree( lpMsgBuf );
347   }
348   else
349   {
350     crtInst = (GET_MODULE_FUNC)::GetProcAddress( modLib, GET_MODULE_NAME );
351     if ( !crtInst )
352     {
353       LPVOID lpMsgBuf;
354       ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
355                        FORMAT_MESSAGE_IGNORE_INSERTS, 0, ::GetLastError(), 0, (LPTSTR)&lpMsgBuf, 0, 0 );
356 #ifdef UNICODE
357           QString out_err = QString::fromWCharArray((LPTSTR)lpMsgBuf);
358 #else
359           QString out_err = (LPTSTR)lpMsgBuf;
360 #endif
361
362           err = QString( "Failed to find  %1 function. %2" ).arg( GET_MODULE_NAME ).arg( out_err );
363           ::LocalFree( lpMsgBuf );
364     }
365
366     getVersion = (GET_VERSION_FUNC)::GetProcAddress( modLib, GET_VERSION_NAME );
367   }
368 #else
369   void* modLib = dlopen( libName.toUtf8(), RTLD_LAZY | RTLD_GLOBAL );
370   if ( !modLib )
371     err = QString( "Can not load library %1. %2" ).arg( libName ).arg( dlerror() );
372   else
373   {
374     crtInst = (GET_MODULE_FUNC)dlsym( modLib, GET_MODULE_NAME );
375     if ( !crtInst )
376       err = QString( "Failed to find function %1. %2" ).arg( GET_MODULE_NAME ).arg( dlerror() );
377
378     getVersion = (GET_VERSION_FUNC)dlsym( modLib, GET_VERSION_NAME );
379   }
380 #endif
381
382   CAM_Module* module = crtInst ? crtInst() : 0;
383   if ( module )
384   {
385     module->setModuleName( modName );
386     module->setName( moduleName( modName ) );
387   }
388
389   if ( !err.isEmpty() && showMsg ) {
390     if ( desktop() && desktop()->isVisible() )
391       SUIT_MessageBox::warning( desktop(), tr( "Error" ), err );
392     else
393       qWarning( qPrintable( err ) );
394   }
395
396   char* version = getVersion ? getVersion() : 0;
397
398   if ( version ) {
399     for ( ModuleInfoList::iterator it = myInfoList.begin(); it != myInfoList.end(); ++it ) {
400       if ( (*it).title == modName ) {
401         if( (*it).version.isEmpty() ) {
402           (*it).version = QString(version);
403         }
404         break;
405       }
406     }
407   }
408
409   return module;
410 }
411
412 /*!
413   \brief Activate module \a modName.
414   \param modName module name
415   \return \c true, if module is loaded and activated successfully and \c false otherwise
416 */
417 bool CAM_Application::activateModule( const QString& modName )
418 {
419   if ( (!modName.isEmpty() && !activeStudy()) || myBlocked )
420     return false;
421
422   // VSR 25/10/2011: prevent nested activation/deactivation
423   // See issues 0021307, 0021373
424   BusyLocker lock( myBlocked );
425
426   QString name = modName;
427   if ( !name.isEmpty() && !moduleTitle( modName ).isEmpty() )
428     name = moduleTitle( modName );
429
430   bool res = false;
431   if ( !name.isEmpty() )
432   {
433     setProperty("activateModule", true);
434     CAM_Module* mod = module( name );
435     if ( !mod )
436       mod = loadModule( name );
437     addModule( mod );
438
439     if ( mod )
440       res = activateModule( mod );
441     setProperty("activateModule", QVariant());
442   }
443   else
444     res = activateModule( 0 );
445
446   return res;
447 }
448
449 /*!
450   \brief Activate module \a mod.
451
452   Shows error message if module could not be activated in the current study.
453
454   \param mod module object pointer
455   \return \c true, if module is loaded and activated successfully and \c false otherwise
456 */
457 bool CAM_Application::activateModule( CAM_Module* mod )
458 {
459   if ( mod && !activeStudy() )
460     return false;
461
462   if ( myModule == mod )
463     return true;
464
465   if ( myModule )
466   {
467     if ( !myModule->deactivateModule( activeStudy() ) )
468     {
469       // ???
470     }
471     moduleDeactivated( myModule );
472   }
473   myModule = mod;
474
475   if ( myModule )
476   {
477     // Connect the module to the active study
478     myModule->connectToStudy( dynamic_cast<CAM_Study*>( activeStudy() ) );
479     if ( !myModule->activateModule( activeStudy() ) )
480     {
481       myModule->setMenuShown( false );
482       myModule->setToolShown( false );
483       QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() );
484       if ( desktop() && desktop()->isVisible() )
485         SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn );
486       else
487         qWarning( qPrintable( wrn ) );
488       myModule = 0;
489       return false;
490     }
491   }
492
493   updateCommandsStatus();
494
495   return true;
496 }
497
498 /*!
499   \brief Load module \a modName and activate its operation, corresponding to \a actionId.
500   This method is dedicated to run operations of some module from any other module.
501   \param modName module name
502   \param actionId is a numerical unique operation identifier
503   \return \c true in case of success and \c false otherwise
504 */
505 bool CAM_Application::activateOperation( const QString& modName, int actionId )
506 {
507   CAM_Module* mod = loadModule(modName, false);
508   if (mod) {
509     addModule(mod);
510     return mod->activateOperation(actionId);
511   }
512   return false;
513 }
514
515 /*!
516   \brief Load module \a modName and activate its operation, corresponding to \a actionId.
517   This method is dedicated to run operations of some module from any other module.
518   \param modName module name
519   \param actionId is a string unique operation identifier
520   \return \c true in case of success and \c false otherwise
521 */
522 bool CAM_Application::activateOperation( const QString& modName, const QString& actionId )
523 {
524   CAM_Module* mod = loadModule(modName, false);
525   if (mod) {
526     addModule(mod);
527     return mod->activateOperation(actionId);
528   }
529   return false;
530 }
531
532 /*!
533   \brief Load module \a modName and activate its operation,
534          corresponding to \a actionId and \a pluginName.
535   This method is dedicated to run operations of some module from any other module.
536   \param modName module name
537   \param actionId is a string unique operation identifier
538   \param pluginName is a name of a plugin where the operation is implemented
539   \return \c true in case of success and \c false otherwise
540 */
541 bool CAM_Application::activateOperation( const QString& modName,
542                                          const QString& actionId,
543                                          const QString& pluginName )
544 {
545   CAM_Module* mod = loadModule(modName, false);
546   if (mod) {
547     addModule(mod);
548     return mod->activateOperation(actionId, pluginName);
549   }
550   return false;
551 }
552
553 /*!
554   \brief Create new study.
555   \return study object pointer
556 */
557 SUIT_Study* CAM_Application::createNewStudy()
558 {
559   return new CAM_Study( this );
560 }
561
562 /*!
563   \brief Update menu commands status.
564 */
565 void CAM_Application::updateCommandsStatus()
566 {
567   STD_Application::updateCommandsStatus();
568
569   if ( activeModule() )
570     activeModule()->updateCommandsStatus();
571 }
572
573 /*!
574   \brief Prepare application to study closing.
575
576   Closes all modules in study \a theDoc.
577
578   \param theDoc study
579 */
580 void CAM_Application::beforeCloseDoc( SUIT_Study* theDoc )
581 {
582   for ( QList<CAM_Module*>::iterator it = myModules.begin(); it != myModules.end(); ++it )
583     (*it)->studyClosed( theDoc );
584 }
585
586 void CAM_Application::afterCloseDoc()
587 {
588 }
589
590 /*!
591   \brief Set active study.
592   \param study study to be made active
593 */
594 void CAM_Application::setActiveStudy( SUIT_Study* study )
595 {
596   STD_Application::setActiveStudy( study );
597 }
598
599 /*!
600   \brief Check module availability.
601
602   The method can be redefined in successors. Default implementation returns \c true.
603
604   \param title module title
605   \return \c true if module is accessible; \c false otherwise
606 */
607 bool CAM_Application::checkModule( const QString& )
608 {
609   return true;
610 }
611
612 /*!
613   \brief Callback function, called when the module is added to the application.
614
615   This virtual method can be re-implemented in the successors. Base implementation
616   does nothing.
617
618   \param mod module being added
619 */
620 void CAM_Application::moduleAdded( CAM_Module* /*mod*/ )
621 {
622 }
623
624 /*!
625   \brief Callback function, called when the module is just deactivated.
626
627   This virtual method can be re-implemented in the successors. Base implementation
628   does nothing.
629
630   \param mod module just deactivated
631 */
632 void CAM_Application::moduleDeactivated( CAM_Module* /*mod*/ )
633 {
634 }
635
636 /*!
637   \brief Get module name by its title (user name).
638   \param title module title (user name)
639   \return module name or null QString if module is not found
640 */
641 QString CAM_Application::moduleName( const QString& title )
642 {
643   QString res;
644   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
645   {
646     if ( (*it).title == title )
647       res = (*it).name;
648   }
649   return res;
650 }
651
652 /*!
653   \brief Get module title (user name) by its name.
654   \param name module name
655   \return module title (user name) or null QString if module is not found
656 */
657 QString CAM_Application::moduleTitle( const QString& name )
658 {
659   QString res;
660   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
661   {
662     if ( (*it).name == name )
663       res = (*it).title;
664   }
665   return res;
666 }
667
668 /*!
669   \brief Get module icon name.
670   \param name module name or title
671   \return module icon or null QString if module is not found
672 */
673 QString CAM_Application::moduleIcon( const QString& name )
674 {
675   QString res;
676   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
677   {
678     if ( (*it).name == name || (*it).title == name )
679       res = (*it).icon;
680   }
681   return res;
682 }
683
684 /*!
685   \brief Get module description.
686   \param name module name or title
687   \return module description or null QString if description is not provided in config file.
688 */
689 QString CAM_Application::moduleDescription( const QString& name )
690 {
691   QString res;
692   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
693   {
694     if ( (*it).name == name || (*it).title == name )
695       res = tr((*it).description.toUtf8());
696   }
697   return res;
698 }
699
700 /*!
701   \brief Get module library name by its title (user name).
702   \param title module name or title
703   \param full if \c true, return full library name, otherwise return its internal name
704   \return module library name or null QString if module is not found
705  */
706 QString CAM_Application::moduleLibrary( const QString& name, const bool full )
707 {
708   QString res;
709   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
710   {
711     if ( (*it).name == name || (*it).title == name )
712       res = (*it).library;
713   }
714   if ( !res.isEmpty() && full )
715     res = SUIT_Tools::library( res );
716   return res;
717 }
718
719 /*!
720   \brief Get displayer proxy for given module, by its title (user name).
721   \param name module name or title
722   \return name of module which provides displayer for requested module
723  */
724 QString CAM_Application::moduleDisplayer( const QString& name )
725 {
726   QString res;
727
728   if ( !name.isEmpty() )
729   {
730     for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
731     {
732       if ( (*it).title == name || (*it).name == name ) {
733         res = (*it).displayer;
734         if ( res.isEmpty() )
735           res = (*it).title;
736       }
737     }
738   }
739
740   return res;
741 }
742
743 /*!
744   \brief Read modules information list
745
746   This function first tries to get the modules names list by parsing
747   the application command line arguments, looking for the
748   "--modules ( <mod_name>[:<mod_name>...] )" option.
749   List of modules is separated by colon symbol (":").
750
751   If "--modules" command line option is not used, the list of modules
752   is retrieved from the application resource file: parameter "modules" of
753   the section "launch".
754
755   Then the information about each module (module title (user name),
756   library name) is retrieved from the corresponding section of resource
757   file with help of resources manager.
758
759   Shows the warning message, if module information list is empty.
760
761   \sa SUIT_ResourceMgr
762 */
763 void CAM_Application::readModuleList()
764 {
765   if ( !myInfoList.isEmpty() )
766     return;
767
768   // we cannot use own resourceMgr() as this method can be called from constructor
769   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
770
771   QStringList modList;
772
773   QString args = QApplication::arguments().join( " " );
774
775   QRegExp rx1("--modules=([\\w,]*)");
776   rx1.setMinimal( false );
777   QRegExp rx2("--modules\\s+\\(\\s*(.*)\\s*\\)");
778   rx2.setMinimal( true );
779   int pos = 0;
780   while ( 1 ) {
781     QString modules;
782     int pos1 = rx1.indexIn( args, pos );
783     int pos2 = rx2.indexIn( args, pos );
784     if ( pos1 != -1 && pos2 != -1 ) {
785       modules = pos1 < pos2 ? rx1.cap( 1 ) : rx2.cap(1);
786       pos = pos1 < pos2 ? pos1 + rx1.matchedLength() : pos2 + rx2.matchedLength();
787     }
788     else if ( pos1 != -1 ) {
789       modules = rx1.cap( 1 );
790       pos = pos1 + rx1.matchedLength();
791     }
792     else if ( pos2 != -1 ) {
793       modules = rx2.cap( 1 );
794       pos = pos2 + rx2.matchedLength();
795     }
796     else {
797       break;
798     }
799
800     modList.clear();
801     QStringList mods = modules.split( QRegExp( "[:|,\\s]" ), QString::SkipEmptyParts );
802     for ( int i = 0; i < mods.count(); i++ ) {
803       if ( !mods[i].trimmed().isEmpty() )
804         modList.append( mods[i].trimmed() );
805     }
806   }
807
808   if ( modList.isEmpty() ) {
809     QString mods = resMgr->stringValue( "launch", "modules", QString() );
810     modList = mods.split( ",", QString::SkipEmptyParts );
811   }
812
813   // extra modules loaded manually on previous session
814   // ...
815
816   foreach ( QString modName, modList )
817     appendModuleInfo( modName.trimmed() );
818
819   if ( myInfoList.isEmpty() ) {
820     if ( desktop() && desktop()->isVisible() )
821       SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) );
822     else
823       {
824         printf( "****************************************************************\n" );
825         printf( "*    Warning: modules list is empty.\n" );
826         printf( "****************************************************************\n" );
827       }
828   }
829 }
830
831 bool CAM_Application::appendModuleInfo( const QString& modName )
832 {
833   MESSAGE("Start to append module info for a given module name: ");
834   SCRUTE(modName.toStdString());
835
836   if ( modName.isEmpty() )
837     return false;  // empty module name
838
839   if ( !moduleTitle( modName ).isEmpty() )
840     return false;  // already added
841
842   if ( modName == "KERNEL" || modName == "GUI" )
843     return false; // skip KERNEL and GUI modules
844
845   // we cannot use own resourceMgr() as this method can be called from constructor
846   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
847
848   ModuleInfo inf;
849
850   // module internal name
851   inf.name = modName;
852   // module version
853   inf.version = resMgr->stringValue( modName, "version", QString() ).trimmed();
854   // displayer, if module does not have GUI, displayer may be delegated to other module
855   inf.displayer = resMgr->stringValue( modName, "displayer", QString() ).trimmed();
856
857   // "gui" option explicitly says that module has GUI
858   // Now trying to get the "gui" option value, we always get a default one,
859   // then we can't rely on it.
860   bool hasGui = resMgr->booleanValue(modName, "gui", false);
861
862   // Additional check if the module actually has a title and icon.
863   // Module with GUI must explicitly specify title (GUI name).
864   inf.title = resMgr->stringValue(modName, "name", QString()).trimmed();
865   const bool hasTitle = !inf.title.isEmpty();
866   SCRUTE(hasGui);
867   SCRUTE(hasTitle);
868   if (hasGui && !hasTitle)
869   {
870     MESSAGE("Invalid config! The module has gui option, but doesn't have a title.");
871     return false;
872   }
873
874   // While we can't rely on gui option, use a title to make a decision about gui.
875   hasGui = hasTitle;
876
877   // status; if module has GUI, availability will be checked on activation
878   inf.status = hasGui ? stUnknown : stNoGui;
879
880   if ( hasGui )
881   {
882     // icon
883     inf.icon = resMgr->stringValue( modName, "icon", QString() ).trimmed();
884     // description, for Info panel
885     inf.description = resMgr->stringValue( modName, "description", QString() );
886     // library; if not specified, we use internal module name
887     inf.library = SUIT_Tools::libraryName( resMgr->stringValue( modName, "library", QString() ).trimmed() );
888     if ( inf.library.isEmpty() )
889       inf.library = modName;
890   }
891
892   // At this point we should have only valid inf object.
893   myInfoList.append(inf);
894
895   SCRUTE(inf.name.toStdString());
896   SCRUTE(inf.version.toStdString());
897   SCRUTE(inf.displayer.toStdString());
898   SCRUTE(inf.status);
899   SCRUTE(inf.title.toStdString());
900   SCRUTE(inf.icon.toStdString());
901   SCRUTE(inf.description.toStdString());
902   SCRUTE(inf.library.toStdString());
903
904   return true;
905 }
906
907 void CAM_Application::removeModuleInfo( const QString& modName )
908 {
909   QMutableListIterator<ModuleInfo> it( myInfoList );
910   while ( it.hasNext() )
911   {
912     ModuleInfo info = it.next();
913     if ( info.name == modName )
914     {
915       it.remove();
916       break;
917     }
918   }
919 }
920
921 /*!
922   \brief Add common menu items to the popup menu.
923
924   Menu items list is defined by the active module.
925
926   \param type popup menu context
927   \param menu popup menu
928   \param title popup menu title, which can be set by the module if required
929 */
930 void CAM_Application::contextMenuPopup( const QString& type, QMenu* menu, QString& title )
931 {
932   // to do : add common items for popup menu ( if they are exist )
933   if ( activeModule() )
934     activeModule()->contextMenuPopup( type, menu, title );
935 }
936
937 /*!
938   \brief Create new empty study.
939 */
940 void CAM_Application::createEmptyStudy()
941 {
942   /*SUIT_Study* study = */activeStudy();
943   STD_Application::createEmptyStudy();
944 }
945
946 /*!
947   \brief Return information about version of the each module.
948 */
949 CAM_Application::ModuleShortInfoList CAM_Application::getVersionInfo()
950 {
951   ModuleShortInfoList info;
952
953   ModuleShortInfo kernel;
954   kernel.name = "KERNEL";
955   kernel.version = KERNEL_VERSION_STR;
956   info.append(kernel);
957
958   ModuleShortInfo gui;
959   gui.name = "GUI";
960   gui.version = GUI_VERSION_STR;
961   info.append(gui);
962
963   for(int i = 0; i < myInfoList.size(); i++) {
964     ModuleShortInfo infoItem;
965     infoItem.name = myInfoList.at(i).title.isEmpty() ? myInfoList.at(i).name : myInfoList.at(i).title;
966     infoItem.version = myInfoList.at(i).version;
967     info.append(infoItem);
968   }
969   return info;
970 }
971
972 /*!
973   \brief Abort active operations if there are any
974
975   Iterates through all modules and asks each of them if there are pending operations that cannot be aborted.
976
977   \return \c false if some operation cannot be aborted
978 */
979 bool CAM_Application::abortAllOperations()
980 {
981   bool aborted = true;
982   for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end() && aborted; ++it )
983   {
984     aborted = (*it)->abortAllOperations();
985   }
986   return aborted;
987 }
988
989 /*!
990   \brief Log GUI event.
991   \param eventDescription GUI event description.
992 */
993 void CAM_Application::logUserEvent( const QString& eventDescription )
994 {
995   static QString guiLogFile; // null string means log file was not initialized yet
996   static QMutex aGUILogMutex;
997
998   if ( guiLogFile.isNull() )
999   {
1000     // log file was not initialized yet, try to do that by parsing command line arguments
1001     guiLogFile = ""; // empty string means initialization was done but log file was not set
1002     QStringList args = QApplication::arguments();
1003     for ( int i = 1; i < args.count(); i++ )
1004     {
1005       QRegExp rxs ( "--gui-log-file=(.+)" );
1006       if ( rxs.indexIn( args[i] ) >= 0 && rxs.capturedTexts().count() > 1 )
1007       {
1008         QString file = rxs.capturedTexts()[1];
1009         QFileInfo fi ( file );
1010         if ( !fi.isDir() && fi.dir().exists() )
1011         {
1012           guiLogFile = fi.absoluteFilePath();
1013           if ( fi.exists() ) {
1014             QFile file ( guiLogFile );
1015             file.remove(); // remove probably existing log file, to start with empty one
1016           }
1017         }
1018         break;
1019       }
1020     }
1021   }
1022   if ( !guiLogFile.isEmpty() ) // non-empty string means log file was already initialized
1023   {
1024     QMutexLocker aLocker( &aGUILogMutex );
1025     QFile file ( guiLogFile );
1026     if ( file.open( QFile::Append ) ) // append to log file
1027     {
1028       QTextStream stream( &file );
1029       stream << eventDescription << endl;
1030       file.close();
1031     }
1032   }
1033 }
1034
1035 /*!
1036   \brief Log given action.
1037   \param action GUI action being logged.
1038   \param moduleName optional name of module, owning an action
1039 */
1040 void CAM_Application::logAction( QAction* action, const QString& moduleName )
1041 {
1042   QString text = action->toolTip();
1043   if ( text.isEmpty() )
1044     text = action->text();
1045   if ( text.isEmpty() )
1046     text = action->iconText();
1047   if ( !text.isEmpty() )
1048   {
1049     QStringList message;
1050     if ( !moduleName.isEmpty() )
1051       message << moduleName;
1052     if ( action->isCheckable() )
1053     {
1054       message << tr( "ACTION_TOGGLED" );
1055       message << ( action->isChecked() ? tr( "ACTION_ON" ) : tr( "ACTION_OFF" ) );
1056     }
1057     else
1058     {
1059       message << tr( "ACTION_TRIGGERED" );
1060     }
1061     message << text;
1062     logUserEvent( message.join( ": " ) );
1063   }
1064 }