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