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