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