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