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       logUserEvent( tr( "MODULE_DEACTIVATED" ).arg( myModule->moduleName() ) );
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       logUserEvent( tr( "MODULE_ACTIVATED" ).arg( myModule->moduleName() ) );
482     }
483     else
484     {
485       myModule->setMenuShown( false );
486       myModule->setToolShown( false );
487       QString wrn = tr( "ERROR_ACTIVATE_MODULE_MSG" ).arg( myModule->moduleName() );
488       if ( desktop() && desktop()->isVisible() )
489         SUIT_MessageBox::critical( desktop(), tr( "ERROR_TLT" ), wrn );
490       else
491         qWarning( qPrintable( wrn ) );
492       myModule = 0;
493       return false;
494     }
495   }
496
497   updateCommandsStatus();
498
499   return true;
500 }
501
502 /*!
503   \brief Load module \a modName and activate its operation, corresponding to \a actionId.
504   This method is dedicated to run operations of some module from any other module.
505   \param modName module name
506   \param actionId is a numerical unique operation identifier
507   \return \c true in case of success and \c false otherwise
508 */
509 bool CAM_Application::activateOperation( const QString& modName, int actionId )
510 {
511   CAM_Module* mod = loadModule(modName, false);
512   if (mod) {
513     addModule(mod);
514     return mod->activateOperation(actionId);
515   }
516   return false;
517 }
518
519 /*!
520   \brief Load module \a modName and activate its operation, corresponding to \a actionId.
521   This method is dedicated to run operations of some module from any other module.
522   \param modName module name
523   \param actionId is a string unique operation identifier
524   \return \c true in case of success and \c false otherwise
525 */
526 bool CAM_Application::activateOperation( const QString& modName, const QString& actionId )
527 {
528   CAM_Module* mod = loadModule(modName, false);
529   if (mod) {
530     addModule(mod);
531     return mod->activateOperation(actionId);
532   }
533   return false;
534 }
535
536 /*!
537   \brief Load module \a modName and activate its operation,
538          corresponding to \a actionId and \a pluginName.
539   This method is dedicated to run operations of some module from any other module.
540   \param modName module name
541   \param actionId is a string unique operation identifier
542   \param pluginName is a name of a plugin where the operation is implemented
543   \return \c true in case of success and \c false otherwise
544 */
545 bool CAM_Application::activateOperation( const QString& modName,
546                                          const QString& actionId,
547                                          const QString& pluginName )
548 {
549   CAM_Module* mod = loadModule(modName, false);
550   if (mod) {
551     addModule(mod);
552     return mod->activateOperation(actionId, pluginName);
553   }
554   return false;
555 }
556
557 /*!
558   \brief Create new study.
559   \return study object pointer
560 */
561 SUIT_Study* CAM_Application::createNewStudy()
562 {
563   return new CAM_Study( this );
564 }
565
566 /*!
567   \brief Update menu commands status.
568 */
569 void CAM_Application::updateCommandsStatus()
570 {
571   STD_Application::updateCommandsStatus();
572
573   if ( activeModule() )
574     activeModule()->updateCommandsStatus();
575 }
576
577 /*!
578   \brief Prepare application to study closing.
579
580   Closes all modules in study \a theDoc.
581
582   \param theDoc study
583 */
584 void CAM_Application::beforeCloseDoc( SUIT_Study* theDoc )
585 {
586   for ( QList<CAM_Module*>::iterator it = myModules.begin(); it != myModules.end(); ++it )
587     (*it)->studyClosed( theDoc );
588 }
589
590 void CAM_Application::afterCloseDoc()
591 {
592 }
593
594 /*!
595   \brief Set active study.
596   \param study study to be made active
597 */
598 void CAM_Application::setActiveStudy( SUIT_Study* study )
599 {
600   STD_Application::setActiveStudy( study );
601 }
602
603 /*!
604   \brief Check module availability.
605
606   The method can be redefined in successors. Default implementation returns \c true.
607
608   \param title module title
609   \return \c true if module is accessible; \c false otherwise
610 */
611 bool CAM_Application::checkModule( const QString& )
612 {
613   return true;
614 }
615
616 /*!
617   \brief Callback function, called when the module is added to the application.
618
619   This virtual method can be re-implemented in the successors. Base implementation
620   does nothing.
621
622   \param mod module being added
623 */
624 void CAM_Application::moduleAdded( CAM_Module* /*mod*/ )
625 {
626 }
627
628 /*!
629   \brief Callback function, called when the module is just deactivated.
630
631   This virtual method can be re-implemented in the successors. Base implementation
632   does nothing.
633
634   \param mod module just deactivated
635 */
636 void CAM_Application::moduleDeactivated( CAM_Module* /*mod*/ )
637 {
638 }
639
640 /*!
641   \brief Get module name by its title (user name).
642   \param title module title (user name)
643   \return module name or null QString if module is not found
644 */
645 QString CAM_Application::moduleName( const QString& title )
646 {
647   QString res;
648   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
649   {
650     if ( (*it).title == title )
651       res = (*it).name;
652   }
653   return res;
654 }
655
656 /*!
657   \brief Get module title (user name) by its name.
658   \param name module name
659   \return module title (user name) or null QString if module is not found
660 */
661 QString CAM_Application::moduleTitle( const QString& name )
662 {
663   QString res;
664   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
665   {
666     if ( (*it).name == name )
667       res = (*it).title;
668   }
669   return res;
670 }
671
672 /*!
673   \brief Get module icon name.
674   \param name module name or title
675   \return module icon or null QString if module is not found
676 */
677 QString CAM_Application::moduleIcon( const QString& name )
678 {
679   QString res;
680   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
681   {
682     if ( (*it).name == name || (*it).title == name )
683       res = (*it).icon;
684   }
685   return res;
686 }
687
688 /*!
689   \brief Get module description.
690   \param name module name or title
691   \return module description or null QString if description is not provided in config file.
692 */
693 QString CAM_Application::moduleDescription( const QString& name )
694 {
695   QString res;
696   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isNull(); ++it )
697   {
698     if ( (*it).name == name || (*it).title == name )
699       res = tr((*it).description.toUtf8());
700   }
701   return res;
702 }
703
704 /*!
705   \brief Get module library name by its title (user name).
706   \param title module name or title
707   \param full if \c true, return full library name, otherwise return its internal name
708   \return module library name or null QString if module is not found
709  */
710 QString CAM_Application::moduleLibrary( const QString& name, const bool full )
711 {
712   QString res;
713   for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
714   {
715     if ( (*it).name == name || (*it).title == name )
716       res = (*it).library;
717   }
718   if ( !res.isEmpty() && full )
719     res = SUIT_Tools::library( res );
720   return res;
721 }
722
723 /*!
724   \brief Get displayer proxy for given module, by its title (user name).
725   \param name module name or title
726   \return name of module which provides displayer for requested module
727  */
728 QString CAM_Application::moduleDisplayer( const QString& name )
729 {
730   QString res;
731
732   if ( !name.isEmpty() )
733   {
734     for ( ModuleInfoList::const_iterator it = myInfoList.begin(); it != myInfoList.end() && res.isEmpty(); ++it )
735     {
736       if ( (*it).title == name || (*it).name == name ) {
737         res = (*it).displayer;
738         if ( res.isEmpty() )
739           res = (*it).title;
740       }
741     }
742   }
743
744   return res;
745 }
746
747 /*!
748   \brief Read modules information list
749
750   This function first tries to get the modules names list by parsing
751   the application command line arguments, looking for the
752   "--modules ( <mod_name>[:<mod_name>...] )" option.
753   List of modules is separated by colon symbol (":").
754
755   If "--modules" command line option is not used, the list of modules
756   is retrieved from the application resource file: parameter "modules" of
757   the section "launch".
758
759   Then the information about each module (module title (user name),
760   library name) is retrieved from the corresponding section of resource
761   file with help of resources manager.
762
763   Shows the warning message, if module information list is empty.
764
765   \sa SUIT_ResourceMgr
766 */
767 void CAM_Application::readModuleList()
768 {
769   if ( !myInfoList.isEmpty() )
770     return;
771
772   // we cannot use own resourceMgr() as this method can be called from constructor
773   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
774
775   QStringList modList;
776
777   QString args = QApplication::arguments().join( " " );
778
779   QRegExp rx1("--modules=([\\w,]*)");
780   rx1.setMinimal( false );
781   QRegExp rx2("--modules\\s+\\(\\s*(.*)\\s*\\)");
782   rx2.setMinimal( true );
783   int pos = 0;
784   while ( 1 ) {
785     QString modules;
786     int pos1 = rx1.indexIn( args, pos );
787     int pos2 = rx2.indexIn( args, pos );
788     if ( pos1 != -1 && pos2 != -1 ) {
789       modules = pos1 < pos2 ? rx1.cap( 1 ) : rx2.cap(1);
790       pos = pos1 < pos2 ? pos1 + rx1.matchedLength() : pos2 + rx2.matchedLength();
791     }
792     else if ( pos1 != -1 ) {
793       modules = rx1.cap( 1 );
794       pos = pos1 + rx1.matchedLength();
795     }
796     else if ( pos2 != -1 ) {
797       modules = rx2.cap( 1 );
798       pos = pos2 + rx2.matchedLength();
799     }
800     else {
801       break;
802     }
803
804     modList.clear();
805     QStringList mods = modules.split( QRegExp( "[:|,\\s]" ), QString::SkipEmptyParts );
806     for ( int i = 0; i < mods.count(); i++ ) {
807       if ( !mods[i].trimmed().isEmpty() )
808         modList.append( mods[i].trimmed() );
809     }
810   }
811
812   if ( modList.isEmpty() ) {
813     QString mods = resMgr->stringValue( "launch", "modules", QString() );
814     modList = mods.split( ",", QString::SkipEmptyParts );
815   }
816
817   // extra modules loaded manually on previous session
818   // ...
819
820   foreach ( QString modName, modList )
821     appendModuleInfo( modName.trimmed() );
822
823   if ( myInfoList.isEmpty() ) {
824     if ( desktop() && desktop()->isVisible() )
825       SUIT_MessageBox::warning( desktop(), tr( "Warning" ), tr( "Modules list is empty" ) );
826     else
827       {
828         printf( "****************************************************************\n" );
829         printf( "*    Warning: modules list is empty.\n" );
830         printf( "****************************************************************\n" );
831       }
832   }
833 }
834
835 bool CAM_Application::appendModuleInfo( const QString& modName )
836 {
837   MESSAGE("Start to append module info for a given module name: ");
838   SCRUTE(modName.toStdString());
839
840   if ( modName.isEmpty() )
841     return false;  // empty module name
842
843   if ( !moduleTitle( modName ).isEmpty() )
844     return false;  // already added
845
846   if ( modName == "KERNEL" || modName == "GUI" )
847     return false; // skip KERNEL and GUI modules
848
849   // we cannot use own resourceMgr() as this method can be called from constructor
850   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
851
852   ModuleInfo inf;
853
854   // module internal name
855   inf.name = modName;
856   // module version
857   inf.version = resMgr->stringValue( modName, "version", QString() ).trimmed();
858   // displayer, if module does not have GUI, displayer may be delegated to other module
859   inf.displayer = resMgr->stringValue( modName, "displayer", QString() ).trimmed();
860
861   // "gui" option explicitly says that module has GUI
862   // Now trying to get the "gui" option value, we always get a default one,
863   // then we can't rely on it.
864   bool hasGui = resMgr->booleanValue(modName, "gui", false);
865
866   // Additional check if the module actually has a title and icon.
867   // Module with GUI must explicitly specify title (GUI name).
868   inf.title = resMgr->stringValue(modName, "name", QString()).trimmed();
869   const bool hasTitle = !inf.title.isEmpty();
870   SCRUTE(hasGui);
871   SCRUTE(hasTitle);
872   if (hasGui && !hasTitle)
873   {
874     MESSAGE("Invalid config! The module has gui option, but doesn't have a title.");
875     return false;
876   }
877
878   // While we can't rely on gui option, use a title to make a decision about gui.
879   hasGui = hasTitle;
880
881   // status; if module has GUI, availability will be checked on activation
882   inf.status = hasGui ? stUnknown : stNoGui;
883
884   if ( hasGui )
885   {
886     // icon
887     inf.icon = resMgr->stringValue( modName, "icon", QString() ).trimmed();
888     // description, for Info panel
889     inf.description = resMgr->stringValue( modName, "description", QString() );
890     // library; if not specified, we use internal module name
891     inf.library = SUIT_Tools::libraryName( resMgr->stringValue( modName, "library", QString() ).trimmed() );
892     if ( inf.library.isEmpty() )
893       inf.library = modName;
894   }
895
896   // At this point we should have only valid inf object.
897   myInfoList.append(inf);
898
899   SCRUTE(inf.name.toStdString());
900   SCRUTE(inf.version.toStdString());
901   SCRUTE(inf.displayer.toStdString());
902   SCRUTE(inf.status);
903   SCRUTE(inf.title.toStdString());
904   SCRUTE(inf.icon.toStdString());
905   SCRUTE(inf.description.toStdString());
906   SCRUTE(inf.library.toStdString());
907
908   return true;
909 }
910
911 void CAM_Application::removeModuleInfo( const QString& modName )
912 {
913   QMutableListIterator<ModuleInfo> it( myInfoList );
914   while ( it.hasNext() )
915   {
916     ModuleInfo info = it.next();
917     if ( info.name == modName )
918     {
919       it.remove();
920       break;
921     }
922   }
923 }
924
925 /*!
926   \brief Add common menu items to the popup menu.
927
928   Menu items list is defined by the active module.
929
930   \param type popup menu context
931   \param menu popup menu
932   \param title popup menu title, which can be set by the module if required
933 */
934 void CAM_Application::contextMenuPopup( const QString& type, QMenu* menu, QString& title )
935 {
936   // to do : add common items for popup menu ( if they are exist )
937   if ( activeModule() )
938     activeModule()->contextMenuPopup( type, menu, title );
939 }
940
941 /*!
942   \brief Create new empty study.
943 */
944 void CAM_Application::createEmptyStudy()
945 {
946   /*SUIT_Study* study = */activeStudy();
947   STD_Application::createEmptyStudy();
948 }
949
950 /*!
951   \brief Return information about version of the each module.
952 */
953 CAM_Application::ModuleShortInfoList CAM_Application::getVersionInfo()
954 {
955   ModuleShortInfoList info;
956
957   ModuleShortInfo kernel;
958   kernel.name = "KERNEL";
959   kernel.version = KERNEL_VERSION_STR;
960   info.append(kernel);
961
962   ModuleShortInfo gui;
963   gui.name = "GUI";
964   gui.version = GUI_VERSION_STR;
965   info.append(gui);
966
967   for(int i = 0; i < myInfoList.size(); i++) {
968     ModuleShortInfo infoItem;
969     infoItem.name = myInfoList.at(i).title.isEmpty() ? myInfoList.at(i).name : myInfoList.at(i).title;
970     infoItem.version = myInfoList.at(i).version;
971     info.append(infoItem);
972   }
973   return info;
974 }
975
976 /*!
977   \brief Abort active operations if there are any
978
979   Iterates through all modules and asks each of them if there are pending operations that cannot be aborted.
980
981   \return \c false if some operation cannot be aborted
982 */
983 bool CAM_Application::abortAllOperations()
984 {
985   bool aborted = true;
986   for ( QList<CAM_Module*>::const_iterator it = myModules.begin(); it != myModules.end() && aborted; ++it )
987   {
988     aborted = (*it)->abortAllOperations();
989   }
990   return aborted;
991 }
992
993 /*!
994   \brief Log GUI event.
995   \param eventDescription GUI event description.
996 */
997 void CAM_Application::logUserEvent( const QString& eventDescription )
998 {
999   static QString guiLogFile; // null string means log file was not initialized yet
1000   static QMutex aGUILogMutex;
1001
1002   if ( guiLogFile.isNull() )
1003   {
1004     // log file was not initialized yet, try to do that by parsing command line arguments
1005     guiLogFile = ""; // empty string means initialization was done but log file was not set
1006     QStringList args = QApplication::arguments();
1007     for ( int i = 1; i < args.count(); i++ )
1008     {
1009       QRegExp rxs ( "--gui-log-file=(.+)" );
1010       if ( rxs.indexIn( args[i] ) >= 0 && rxs.capturedTexts().count() > 1 )
1011       {
1012         QString file = rxs.capturedTexts()[1];
1013         QFileInfo fi ( file );
1014         if ( !fi.isDir() && fi.dir().exists() )
1015         {
1016           guiLogFile = fi.absoluteFilePath();
1017           if ( fi.exists() ) {
1018             QFile file ( guiLogFile );
1019             file.remove(); // remove probably existing log file, to start with empty one
1020           }
1021         }
1022         break;
1023       }
1024     }
1025   }
1026   if ( !guiLogFile.isEmpty() ) // non-empty string means log file was already initialized
1027   {
1028     QMutexLocker aLocker( &aGUILogMutex );
1029     QFile file ( guiLogFile );
1030     if ( file.open( QFile::Append ) ) // append to log file
1031     {
1032       QTextStream stream( &file );
1033       stream << eventDescription << endl;
1034       file.close();
1035     }
1036   }
1037 }
1038
1039 /*!
1040   \brief Log given action.
1041   \param action GUI action being logged.
1042   \param moduleName optional name of module, owning an action
1043 */
1044 void CAM_Application::logAction( QAction* action, const QString& moduleName )
1045 {
1046   QString text = action->toolTip();
1047   if ( text.isEmpty() )
1048     text = action->text();
1049   if ( text.isEmpty() )
1050     text = action->iconText();
1051   if ( !text.isEmpty() )
1052   {
1053     QStringList message;
1054     if ( !moduleName.isEmpty() )
1055       message << moduleName;
1056     if ( action->isCheckable() )
1057     {
1058       message << tr( "ACTION_TOGGLED" );
1059       message << ( action->isChecked() ? tr( "ACTION_ON" ) : tr( "ACTION_OFF" ) );
1060     }
1061     else
1062     {
1063       message << tr( "ACTION_TRIGGERED" );
1064     }
1065     message << text;
1066     logUserEvent( message.join( ": " ) );
1067   }
1068 }