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