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