Salome HOME
Copyright update 2022
[modules/gui.git] / src / LightApp / LightApp_Application.cxx
index 351397e107c26582630d112b4ee77a3bcdd41325..e86a70914403dd37207f75b4073c550d65590aa7 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2022  CEA/DEN, EDF R&D, OPEN CASCADE
 //
 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
@@ -35,8 +35,8 @@
 #endif
 
 #ifndef DISABLE_PYCONSOLE
-  #include "LightApp_PyEditor.h"
   #include "PyConsole_Interp.h"
+  #include "LightApp_PyEditor.h"
   #include "PyConsole_Console.h"
 #endif
 
@@ -88,6 +88,7 @@
 #include <QtxFontEdit.h>
 #include <QtxToolBar.h>
 #include <QtxTreeView.h>
+#include <QtxInfoPanel.h>
 #include <QtxMRUAction.h>
 #include <QtxDockAction.h>
 #include <QtxDockWidget.h>
 #include <QTreeView>
 #include <QMimeData>
 #include <QShortcut>
+#include <QRegExp>
 
 #include <utilities.h>
 
 #define FIRST_HELP_ID 1000000
+#define HAS_WWW_URL true
+#define HAS_FORUM_URL true
+#define HAS_YOUTUBE_URL true
+#define HAS_TUTORIAL_URL false
 
 #ifndef DISABLE_SALOMEOBJECT
   #include <SALOME_InteractiveObject.hxx>
@@ -230,8 +236,6 @@ static const char* imageEmptyIcon[] = {
 "....................",
 "...................."};
 
-int LightApp_Application::lastStudyId = 0;
-
 // Markers used to parse array with dockable windows and toolbars state.
 // For more details please see the qdockarealayout.cpp && qtoolbararealayout.cpp
 // in the Qt source code.
@@ -261,7 +265,7 @@ namespace
     int inputLen = input.length();
     QDataStream anInputData( &input, QIODevice::ReadOnly );
     while ( tmp < inputLen ) {
-      tmp = input.indexOf( QToolBarMarker, tmp + 1 );
+      tmp = input.indexOf( (uchar)QToolBarMarker, tmp + 1 );
       if ( tmp < 0 )
         break;
       anInputData.device()->seek( tmp );
@@ -301,14 +305,24 @@ namespace
       result = QLocale( lang ).nativeLanguageName();
     return result;
   }
-}
 
-/*!
-  \return last global id of study
-*/
-int LightApp_Application::studyId()
-{
-  return LightApp_Application::lastStudyId;
+  QString getHelpItem( const QString& section, const QString& parameter, const QString& root = QString() )
+  {
+    SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
+    foreach( QString item, resMgr->stringValue( section, parameter ).split( ";;", QString::SkipEmptyParts ) )
+    {
+      if ( item.startsWith( "http", Qt::CaseInsensitive ) )
+        return item;
+      QString path = item;
+      path.remove( QRegExp( "#.*$" ) );
+      QFileInfo fi( path );
+      if ( fi.isRelative() && !root.isEmpty() )
+        path = Qtx::addSlash( root ) + path;
+      if ( QFile::exists( path ) )
+        return item;
+    }
+    return QString();
+  }
 }
 
 /*!Create new instance of LightApp_Application.*/
@@ -463,6 +477,8 @@ void LightApp_Application::start()
   desktop()->statusBar()->showMessage( "" );
 
   LightApp_EventFilter::Init();
+
+  onNewDoc();
 }
 
 /*!Closeapplication.*/
@@ -471,7 +487,7 @@ void LightApp_Application::closeApplication()
 #ifndef DISABLE_QTXWEBBROWSER
   QProcess::startDetached( "HelpBrowser",
                            QStringList() << QString( "--remove=%1" ).arg( QApplication::instance()->applicationPid() ) );
-#endif  
+#endif
   CAM_Application::closeApplication();
 }
 
@@ -533,6 +549,9 @@ bool LightApp_Application::activateModule( const QString& modName )
 
   saveDockWindowsState();
 
+  if ( infoPanel() )
+    infoPanel()->clear();
+
   bool status = CAM_Application::activateModule( modName );
 
   updateModuleActions();
@@ -546,13 +565,14 @@ bool LightApp_Application::activateModule( const QString& modName )
   updateViewManagers();
 
   if ( activeStudy() && activeStudy()->root() && objectBrowser() ) {
-    if ( objectBrowser()->root() != activeStudy()->root() ) 
+    if ( objectBrowser()->root() != activeStudy()->root() )
       objectBrowser()->setRoot( activeStudy()->root() );
     updateObjectBrowser( true );
   }
 
   if ( activeModule() ) activeModule()->updateModuleVisibilityState();
 
+  updateActions();
   return true;
 }
 
@@ -597,47 +617,78 @@ void LightApp_Application::createActions()
                 tr( "MEN_DESK_PREFERENCES" ), tr( "PRP_DESK_PREFERENCES" ),
                 Qt::CTRL+Qt::Key_P, desk, false, this, SLOT( onPreferences() ) );
 
-  // Help menu:
-
-  // - Help for modules
+  // Help menu
 
   int helpMenu = createMenu( tr( "MEN_DESK_HELP" ), -1, -1, 1000 );
-  createMenu( separator(), helpMenu, -1, 10 );
-  
-  // Site && forum
+
   int id = LightApp_Application::UserID + FIRST_HELP_ID;
 
-  QString site = tr ( "SALOME_SITE" );
-  QAction* as = createAction( id, site,
-                             resMgr->loadPixmap( "LightApp", tr( "ICON_WWW" ), false ),
-                             site, site,
-                             0, desk, false, this, SLOT( onHelpOnline() ) );
-  as->setData( "salome-platform.org" );
-  createMenu( as, helpMenu, -1, 0 );
-  id++;
-
-  QString forum = tr ( "SALOME_FORUM" );
-
-  QAction* af = createAction( helpMenu, forum,
-                             resMgr->loadPixmap( "LightApp", tr( "ICON_WWW" ), false ),
-                             forum, forum,
-                             0, desk, false, this, SLOT( onHelpOnline() ) );
-  af->setData( "salome-platform.org/forum" );
-  createMenu( af, helpMenu, -1, 0 );
-  id++;
-
-  createMenu( separator(), helpMenu, -1, 0 );
-
-  // YouTube channel
-  QString video = tr ( "SALOME_VIDEO_TUTORIALS" );
-  QAction* av = createAction( helpMenu, video,
-                             resMgr->loadPixmap( "LightApp", tr( "ICON_LIFE_RIGN" ), false ),
-                             video, video,
-                             0, desk, false, this, SLOT( onHelpOnline() ) );
-  av->setData( "www.youtube.com/playlist?list=PLgvBxFyGVRbZZz4wVvP36xXQL-S81RZsc&disable_polymer=true" );
-  createMenu( av, helpMenu, -1, 0 );
-  id++;
+  QString url;
+
+  // a) Link to web site
+  if ( HAS_WWW_URL ) {
+    url = resMgr->stringValue("GUI", "site_url");
+    if ( !url.isEmpty() ) {
+      QString title = tr ( "SALOME_SITE" );
+      QAction* as = createAction( WebSiteId, title,
+                                  resMgr->loadPixmap( "LightApp", tr( "ICON_WWW" ), false ),
+                                  title, title,
+                                  0, desk, false, this, SLOT( onHelpContentsModule() ) );
+      as->setData( url );
+      createMenu( as, helpMenu, -1, 0 );
+    }
+  }
+
+  // b) Link to Forum
+  if ( HAS_FORUM_URL ) {
+    url = resMgr->stringValue("GUI", "forum_url");
+    if ( !url.isEmpty() ) {
+      QString title = tr ( "SALOME_FORUM" );
+      QAction* af = createAction( ForumId, title,
+                                  resMgr->loadPixmap( "LightApp", tr( "ICON_WWW" ), false ),
+                                  title, title,
+                                  0, desk, false, this, SLOT( onHelpContentsModule() ) );
+      af->setData( url );
+      createMenu( af, helpMenu, -1, 0 );
+    }
+  }
+
+  // c) Link to YouTube channel
+  if ( HAS_YOUTUBE_URL ) {
+    url = resMgr->stringValue("GUI", "channel_url");
+    if ( !url.isEmpty() ) {
+      createMenu( separator(), helpMenu, -1, 0 );
+      QString title = tr ( "SALOME_VIDEO_TUTORIALS" );
+      QAction* av = createAction( VideosId, title,
+                                  resMgr->loadPixmap( "LightApp", tr( "ICON_LIFE_RIGN" ), false ),
+                                  title, tr( "PRP_SALOME_VIDEO_TUTORIALS" ),
+                                  0, desk, false, this, SLOT( onHelpContentsModule() ) );
+      av->setData( url );
+      createMenu( av, helpMenu, -1, 0 );
+    }
+  }
+
+  // d) Link to Tutorials
+  if ( HAS_TUTORIAL_URL ) {
+    url = resMgr->stringValue("GUI", "tutorials_url");
+    if ( !url.isEmpty() ) {
+      QString title = tr ( "SALOME_TUTORIALS" );
+      QAction* as = createAction( TutorialsId, title,
+                                  resMgr->loadPixmap( "LightApp", tr( "ICON_WWW" ), false ),
+                                  title, tr( "PRP_SALOME_TUTORIALS" ),
+                                  0, desk, false, this, SLOT( onHelpContentsModule() ) );
+      as->setData( url );
+      createMenu( as, helpMenu, -1, 0 );
+    }
+  }
 
+  // e) Help for modules
+
+  // - First create top-level menus to preserve correct order
+  QString userGuide = "User's Guide";
+  QString devGuide = "Developer's Guide";
+  createMenu( userGuide, helpMenu, -1, 5 );
+  createMenu( devGuide, helpMenu, -1, 5 );
 
   QStringList aModuleList;
   modules( aModuleList, false );
@@ -652,25 +703,22 @@ void LightApp_Application::createActions()
     QString helpSubMenu;                                             // help submenu name (empty if not needed)
     QString modName = moduleName( aModule );                         // module name
     if ( modName.isEmpty() ) modName = aModule;                      // for KERNEL and GUI
-    QString rootDir = QString( "%1_ROOT_DIR" ).arg( modName );       // module root dir variable
-    QString modDir  = getenv( rootDir.toLatin1().constData() );      // module root dir
+    QString rootDir = QString( "%1_ROOT_DIR" ).arg( modName );       // module root dir env variable
+    QString modDir  = Qtx::getenv( rootDir.toUtf8().constData() );   // module root dir path
     QString docSection;
     if (resMgr->hasValue( modName, "documentation" ) )
       docSection = resMgr->stringValue(modName, "documentation");
     else if ( resMgr->hasSection( modName + "_documentation" ) )
       docSection = modName + "_documentation";
     if ( !docSection.isEmpty() ) {
-      helpSubMenu = resMgr->stringValue( docSection, "sub_menu", "" ).arg( aModule );
-      QStringList listOfParam = resMgr->parameters( docSection );
-      foreach( QString paramName, listOfParam ) {
-        QString valueStr = resMgr->stringValue( docSection, paramName );
-        if ( !valueStr.isEmpty() ) {
-          QFileInfo fi( valueStr );
-          if ( fi.isRelative() && !modDir.isEmpty() )
-            valueStr = Qtx::addSlash( modDir ) + valueStr;
-          if ( QFile::exists( valueStr ) )
-            helpData.insert( paramName.arg( aModule ), valueStr );
-        }
+      helpSubMenu = resMgr->stringValue( docSection, "sub_menu", "" );
+      if ( helpSubMenu.contains( "%1" ) )
+        helpSubMenu = helpSubMenu.arg( aModule );
+      foreach( QString paramName, resMgr->parameters( docSection ) ) {
+        QString key = paramName.contains( "%1" ) ? paramName.arg( aModule ) : paramName;
+        QString helpItem = getHelpItem( docSection, paramName );
+        if ( !helpItem.isEmpty() )
+          helpData.insert( key, helpItem );
       }
     }
 
@@ -683,17 +731,24 @@ void LightApp_Application::createActions()
 
     IMapConstIterator<QString, QString > fileIt;
     for ( fileIt = helpData.begin(); fileIt != helpData.end(); fileIt++ ) {
-      QString helpFileName = fileIt.key();
-      // remove all '//' occurances 
-      while ( helpFileName.contains( "//" ) )
-        helpFileName.replace( "//", "" );
+      QString helpItemPath = fileIt.key();
+      // remove all '//' occurances
+      while ( helpItemPath.contains( "//" ) )
+        helpItemPath.replace( "//", "" );
       // obtain submenus hierarchy if given
-      QStringList smenus = helpFileName.split( "/" );
-      helpFileName = smenus.last();
-      smenus.removeLast();
-      QAction* a = createAction( id, helpFileName,
-                                 resMgr->loadPixmap( "STD", tr( "ICON_HELP" ), false ),
-                                 helpFileName, helpFileName,
+      QStringList smenus = helpItemPath.split( "/" );
+      helpItemPath = smenus.takeLast();
+      // workaround for User's Guide and Developer's Guide to avoid having single item in module's submenu.
+      if ( helpItemPath == userGuide || helpItemPath == devGuide ) {
+       QString menuPath = smenus.join( "/" );
+       QStringList allKeys = helpData.keys();
+       QStringList total = allKeys.filter( QRegExp( QString( "^%1" ).arg( menuPath ) ) );
+       if ( total.count() == 1 && smenus.count() > 0 )
+         helpItemPath = smenus.takeLast();
+      }
+      QPixmap helpIcon = fileIt.value().startsWith( "http", Qt::CaseInsensitive ) ?
+        resMgr->loadPixmap( "STD", tr( "ICON_WWW" ), false ) : resMgr->loadPixmap( "STD", tr( "ICON_HELP" ), false );
+      QAction* a = createAction( id, helpItemPath, helpIcon, helpItemPath, helpItemPath,
                                  0, desk, false, this, SLOT( onHelpContentsModule() ) );
       a->setData( fileIt.value() );
       if ( !helpSubMenu.isEmpty() ) {
@@ -701,29 +756,28 @@ void LightApp_Application::createActions()
       }
       // create sub-menus hierarchy
       int menuId = helpMenu;
-      foreach ( QString subMenu, smenus ) {
-        menuId = createMenu( subMenu, menuId, -1, 0 );
-      }
-      createMenu( a, menuId, -1, 0 );
+      foreach ( QString subMenu, smenus )
+        menuId = createMenu( subMenu, menuId, -1, 5 );
+      createMenu( a, menuId, -1, ( menuId != helpMenu && (helpItemPath == userGuide || helpItemPath == devGuide) ) ? 0 : 5 );
       id++;
     }
   }
 
   // - Additional help items
 
-  createMenu( separator(), helpMenu, -1, 5 );
+  createMenu( separator(), helpMenu, -1, 10 );
 
   QStringList addHelpItems = resMgr->parameters( "add_help" );
-  foreach ( QString addHelpItem, addHelpItems ) {
-    QString valueStr = resMgr->stringValue( "add_help", addHelpItem );
-    if ( !valueStr.isEmpty() && QFile::exists( valueStr ) ) {
-      QAction* a = createAction( id, addHelpItem,
-                                 resMgr->loadPixmap( "STD", tr( "ICON_HELP" ), false ),
-                                 addHelpItem, addHelpItem,
+  foreach ( QString paramName, addHelpItems ) {
+    QString helpItem = getHelpItem( "add_help", paramName );
+    if ( !helpItem.isEmpty() )
+    {
+      QPixmap helpIcon = helpItem.startsWith( "http", Qt::CaseInsensitive ) ?
+        resMgr->loadPixmap( "STD", tr( "ICON_WWW" ), false ) : resMgr->loadPixmap( "STD", tr( "ICON_HELP" ), false );
+      QAction* a = createAction( id++, paramName, helpIcon, paramName, paramName,
                                  0, desk, false, this, SLOT( onHelpContentsModule() ) );
-      a->setData( valueStr );
-      createMenu( a, helpMenu, -1, 5 );
-      id++;
+      a->setData( helpItem );
+      createMenu( a, helpMenu, -1, 10 );
     }
   }
 
@@ -758,9 +812,6 @@ void LightApp_Application::createActions()
     QStringList::Iterator it;
     for ( it = modList.begin(); it != modList.end(); ++it )
     {
-      if ( !isModuleAccessible( *it ) )
-        continue;
-
       QString modName = moduleName( *it );
 
       QString iconName;
@@ -972,14 +1023,12 @@ void LightApp_Application::onNewWindow()
 */
 void LightApp_Application::onNewDoc()
 {
-#ifdef SINGLE_DESKTOP
   if ( !checkExistingDoc() )
     return;
-#endif
 
   //asl: fix for 0020515
   saveDockWindowsState();
-  
+
   CAM_Application::onNewDoc();
 }
 
@@ -990,12 +1039,14 @@ void LightApp_Application::onOpenDoc()
 {
   SUIT_Study* study = activeStudy();
   
-#ifdef SINGLE_DESKTOP
-  if ( !checkExistingDoc() )
+  if ( !checkExistingDoc( false ) )
+    return;
+  
+  QString aName = getFileName( true, QString(), getFileFilter( true ), QString(), 0 );
+  if ( aName.isNull() ) //Cancel
     return;
-#endif
   
-  CAM_Application::onOpenDoc();
+  onOpenDoc( aName );
   
   if ( !study ) // new study will be create in THIS application
   {
@@ -1004,22 +1055,38 @@ void LightApp_Application::onOpenDoc()
   }
 }
 
+bool LightApp_Application::canOpenDoc( const QString& )
+{
+  return true;
+}
+
 /*!
   SLOT: Opens new document.
   \param aName - name of file
 */
 bool LightApp_Application::onOpenDoc( const QString& aName )
 {
-#ifdef SINGLE_DESKTOP
+  if ( !canOpenDoc(aName)) {
+    bool showError = !property("open_study_from_command_line").isValid() ||
+      !property("open_study_from_command_line").toBool();
+
+    putInfo( tr("OPEN_DOCUMENT_PROBLEM") );
+    if ( showError )
+      SUIT_MessageBox::critical( desktop(), tr("ERR_ERROR"), tr("OPEN_DOCUMENT_PROBLEM"));
+
+    return false;
+  }
+
+  closeDoc(false);
+
   if ( !checkExistingDoc() )
     return false;
-#endif
 
   saveDockWindowsState();
 
   // We should take mru action first because this application instance can be deleted later.
   QtxMRUAction* mru = ::qobject_cast<QtxMRUAction*>( action( MRUId ) );
-  
+
   bool res = CAM_Application::onOpenDoc( aName );
 
   if ( mru )
@@ -1127,230 +1194,152 @@ void LightApp_Application::updateCommandsStatus()
 class RunBrowser: public QThread
 {
 public:
-  RunBrowser( LightApp_Application* app,
-              const QString&        theApp,
-              const QString&        theParams,
-              const QString&        theHelpFile,
-              const QString&        theContext = QString(),
-             //For the external browser always specify 'file://' protocol,
-             //because some WEB browsers (for example Mozilla Firefox) can't open local file without protocol.
-             const QString&        theProtocol = QString("file://"),
-             const bool            isFile = true)
-    : myApp( theApp ),
-      myParams( theParams ),
-      myContext( theContext ),
-      myStatus(0),
-      myLApp( app )
+  static void execute( LightApp_Application* application,
+                       const QString& browser,
+                       const QString& parameters,
+                       const QString& url )
+  {
+    (new RunBrowser( application, browser, parameters, url ))->start();
+  }
+
+protected:
+  RunBrowser( LightApp_Application* application,
+              const QString&        browser,
+              const QString&        parameters,
+              const QString&        url)
+    : myApplication( application ),
+      myBrowser( browser ),
+      myParameters( parameters ),
+      myUrl( url )
   {
-    QString path_begin = theProtocol+"%1";
-    QString path_end = theHelpFile;
-    if( isFile ) {
-      path_end = QFileInfo( theHelpFile ).canonicalFilePath();
+    if ( !myUrl.startsWith( "http", Qt::CaseInsensitive ) )
+    {
+      // normalize path
+      if ( myUrl.startsWith( "file://", Qt::CaseInsensitive ) )
+       myUrl = myUrl.remove( 0, QString( "file://" ).count() );
+      // For the external browser we always specify 'file://' protocol,
+      // because some web browsers (e.g. Mozilla Firefox) can't open local file without protocol.
+      myUrl = myUrl.prepend( "file://" );
     }
-    myHelpFile = path_begin.arg( path_end );
+    connect(this, SIGNAL(finished()), SLOT(deleteLater()));
   }
 
   virtual void run()
   {
-    if ( !myApp.isEmpty() && !myHelpFile.isEmpty()) {
-      QString aCommand = QString( "%1 %2 \"%3%4\"" ).arg( myApp, myParams, myHelpFile, myContext.isEmpty() ? QString("") : QString( "#%1" ).arg( myContext ) );
-
+    if ( !myBrowser.isEmpty() && !myUrl.isEmpty() )
+    {
+      QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+#ifdef WIN32
+      QString cmdLine = QString( "\"%1\" %2 \"%3\"" ).arg( myBrowser, myParameters, myUrl );
+#else
+      QString cmdLine = QString( "%1 %2 \"%3\"" ).arg( myBrowser, myParameters, myUrl );
+      // remove LD_LIBRARY_PATH from the environement before starting launcher to avoid bad interactions.
+      // (especially in the case of universal binaries) 
+      env.remove("LD_LIBRARY_PATH");
+#endif
       QProcess* proc = new QProcess();
-
-      proc->start( aCommand );
-      if ( !proc->waitForStarted() ) {
+      proc->setProcessEnvironment(env);
+      proc->start( cmdLine );
+      if ( !proc->waitForStarted() )
+      {
         SALOME_CustomEvent* ce2000 = new SALOME_CustomEvent( 2000 );
-        QString* msg = new QString( QObject::tr( "EXTERNAL_BROWSER_CANNOT_SHOW_PAGE" ).arg( myApp, myHelpFile ) );
+        QString* msg = new QString( QObject::tr( "EXTERNAL_BROWSER_CANNOT_SHOW_PAGE" ).arg( myBrowser, myUrl ) );
         ce2000->setData( msg );
-        QApplication::postEvent( myLApp, ce2000 );
+        QApplication::postEvent( myApplication, ce2000 );
       }
     }
   }
 
 private:
-  QString               myApp;
-  QString               myParams;
-  QString               myHelpFile;
-  QString               myContext;
-  int                   myStatus;
-  LightApp_Application* myLApp;
+  LightApp_Application* myApplication;
+  QString               myBrowser;
+  QString               myParameters;
+  QString               myUrl;
 };
 
-/*!
-  SLOT: Displays help contents for choosen module
-*/
-void LightApp_Application::onHelpContentsModule()
+void LightApp_Application::showHelp( const QString& path )
 {
-  const QAction* a = (QAction*) sender();
-  QString helpFile = a->data().toString();
-  if ( helpFile.isEmpty() ) return;
-
   SUIT_ResourceMgr* resMgr = resourceMgr();
-  QString platform;
-#ifdef WIN32
-  platform = "winapplication";
-#else
-  platform = "application";
-#endif
-  QString anApp = resMgr->stringValue("ExternalBrowser", platform);
-#ifdef WIN32
-  QString quote("\"");
-  anApp.prepend( quote );
-  anApp.append( quote );
-#endif
-  QString aParams = resMgr->stringValue("ExternalBrowser", "parameters");
+
 #if DISABLE_QTXWEBBROWSER
-  bool useExtBrowser = true;
-#else  
-  bool useExtBrowser = resMgr->booleanValue("ExternalBrowser", "use_external_browser", false );
+  bool useExternalBrowser = true;
+#else
+  bool useExternalBrowser = resMgr->booleanValue("ExternalBrowser", "use_external_browser", false );
 #endif
-  
-  if( useExtBrowser ) {
-    if ( !anApp.isEmpty() ) {
-      RunBrowser* rs = new RunBrowser( this, anApp, aParams, helpFile );
-      rs->start();
-    }
-    else {
-      if ( SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "DEFINE_EXTERNAL_BROWSER" ),
-                                      SUIT_MessageBox::Yes | SUIT_MessageBox::No,
-                                      SUIT_MessageBox::Yes ) == SUIT_MessageBox::Yes )
-
-        showPreferences( tr( "PREF_APP" ) );
-    }
-  }
-  else {
-    QStringList parameters;
-    parameters << QString( "--language=%1" ).arg( resMgr->stringValue( "language", "language" ) );
-    parameters << QString( "--add=%1" ).arg( QApplication::instance()->applicationPid() );
-    parameters << helpFile;
-    QProcess::startDetached( "HelpBrowser", parameters );
-  }
-}
-
-/*!
-  SLOT: Displays help contents for choosen dialog
-*/
-void LightApp_Application::onHelpContextModule( const QString& theComponentName,
-                                                const QString& theFileName,
-                                                const QString& theContext )
-{
-  QString fileName = theFileName;
-  QString context  = theContext;
-  if ( !QFile::exists( fileName ) && theContext.isEmpty() ) {
-    // context might be passed within theFileName argument
-    QStringList comps = fileName.split("#");
-    if ( comps.count() > 1 ) {
-      context = comps.last();
-      comps.removeLast();
-      fileName = comps.join("#");
-    }
-  }
-
-  QString homeDir = "";
-  if ( !theComponentName.isEmpty() ) {
-    QString dir = getenv( ( theComponentName + "_ROOT_DIR" ).toLatin1().constData() );
-    if ( !dir.isEmpty() )
-      homeDir = Qtx::addSlash( Qtx::addSlash( dir )      +
-                               Qtx::addSlash( "share" )  +
-                               Qtx::addSlash( "doc" )    +
-                               Qtx::addSlash( "salome" ) +
-                               Qtx::addSlash( "gui" )    +
-                               Qtx::addSlash( theComponentName ) );
-  }
 
-  QString helpFile = QFileInfo( homeDir + fileName ).absoluteFilePath();
-  SUIT_ResourceMgr* resMgr = resourceMgr();
-        QString platform;
+  if ( useExternalBrowser )
+  {
 #ifdef WIN32
-        platform = "winapplication";
+    QString browser = resMgr->stringValue( "ExternalBrowser", "winapplication" ) ;
 #else
-        platform = "application";
-#endif
-        QString anApp = resMgr->stringValue("ExternalBrowser", platform);
-#ifdef WIN32
-        QString quote("\"");
-        anApp.prepend( quote );
-        anApp.append( quote );
+    QString browser = resMgr->stringValue( "ExternalBrowser", "application" );
 #endif
+    QString parameters = resMgr->stringValue("ExternalBrowser", "parameters");
 
-#if DISABLE_QTXWEBBROWSER
-  bool useExtBrowser = true;
-#else  
-  bool useExtBrowser = resMgr->booleanValue("ExternalBrowser", "use_external_browser", false );
-#endif
-
-  if(useExtBrowser) {
-    QString aParams = resMgr->stringValue("ExternalBrowser", "parameters");
-
-    if ( !anApp.isEmpty() ) {
-      RunBrowser* rs = new RunBrowser( this, anApp, aParams, helpFile, context );
-      rs->start();
+    if ( !browser.isEmpty() )
+    {
+      RunBrowser::execute( this, browser, parameters, path );
     }
-    else {
+    else
+    {
       if ( SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "DEFINE_EXTERNAL_BROWSER" ),
                                       SUIT_MessageBox::Yes | SUIT_MessageBox::No,
                                       SUIT_MessageBox::Yes ) == SUIT_MessageBox::Yes )
-        showPreferences( tr( "PREF_APP" ) );
+      {
+        QStringList path;
+        path << tr( "PREF_CATEGORY_SALOME" ) << tr( "PREF_TAB_GENERAL" )
+             << tr( "PREF_GROUP_EXT_BROWSER" ) << tr( "PREF_APP" );
+        showPreferences( path );
+      }
     }
   }
-  else {
-    QStringList parameters;
-    parameters << QString( "--language=%1" ).arg( resMgr->stringValue( "language", "language" ) );
-    parameters << QString( "--add=%1" ).arg( QApplication::instance()->applicationPid() );
-    parameters << QString( "%1#%2" ).arg( helpFile ).arg( context );
-    QProcess::startDetached( "HelpBrowser", parameters );
+  else
+  {
+    QStringList cmdLine;
+    cmdLine << QString( "--language=%1" ).arg( resMgr->stringValue( "language", "language" ) );
+    cmdLine << QString( "--add=%1" ).arg( QApplication::instance()->applicationPid() );
+    cmdLine << path;
+    QProcess::startDetached( "HelpBrowser", cmdLine );
   }
 }
 
 /*!
   SLOT: Displays help contents for choosen module
 */
-void LightApp_Application::onHelpOnline()
+void LightApp_Application::onHelpContentsModule()
 {
   const QAction* a = (QAction*) sender();
-  QString url = a->data().toString();
-  if ( url.isEmpty() ) return;
-
-  SUIT_ResourceMgr* resMgr = resourceMgr();
-  QString platform;
-#ifdef WIN32
-  platform = "winapplication";
-#else
-  platform = "application";
-#endif
-  QString anApp = resMgr->stringValue("ExternalBrowser", platform);
-#ifdef WIN32
-  QString quote("\"");
-  anApp.prepend( quote );
-  anApp.append( quote );
-#endif
-  QString aParams = resMgr->stringValue("ExternalBrowser", "parameters");
-#if DISABLE_QTXWEBBROWSER
-  bool useExtBrowser = true;
-#else  
-  bool useExtBrowser = resMgr->booleanValue("ExternalBrowser", "use_external_browser", false );
-#endif
-  
-  if( useExtBrowser ) {
-    if ( !anApp.isEmpty() ) {
-      RunBrowser* rs = new RunBrowser( this, anApp, aParams, url, "", "http://", false );
-      rs->start();
-    }
-    else {
-      if ( SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "DEFINE_EXTERNAL_BROWSER" ),
-                                      SUIT_MessageBox::Yes | SUIT_MessageBox::No,
-                                      SUIT_MessageBox::Yes ) == SUIT_MessageBox::Yes )
+  QString helpFile = a->data().toString();
+  if ( !helpFile.isEmpty() )
+    showHelp( helpFile );
+}
 
-        showPreferences( tr( "PREF_APP" ) );
+/*!
+  SLOT: Displays contextual help (e.g. for choosen dialog)
+*/
+void LightApp_Application::onHelpContextModule( const QString& component,
+                                                const QString& url,
+                                                const QString& context )
+{
+  QString path = url;
+  if ( !url.startsWith( "http", Qt::CaseInsensitive ) )
+  {
+    // local file path
+    QFileInfo fi( url );
+    if ( fi.isRelative() && !component.isEmpty() )
+    {
+      QString rootDir = Qtx::getenv( (component + "_ROOT_DIR").toLatin1().constData() );
+      if ( !rootDir.isEmpty() )
+      {
+       path = (QStringList() << rootDir << "share" << "doc" << "salome" << "gui" << component << url).join( QDir::separator() );
+      }
     }
   }
-  else {
-    QStringList parameters;
-    parameters << QString( "--language=%1" ).arg( resMgr->stringValue( "language", "language" ) );
-    parameters << QString( "--add=%1" ).arg( QApplication::instance()->applicationPid() );
-    parameters << "http://" + url;
-    QProcess::startDetached( "HelpBrowser", parameters );
+  if ( !context.isEmpty() )
+  {
+    path += QString( "#%1" ).arg( context );
   }
+  showHelp( path );
 }
 
 /*!
@@ -1407,9 +1396,18 @@ void LightApp_Application::insertDockWindow( const int id, QWidget* wid )
   myWin.insert( id, wid );
 
   QtxDockWidget* dock = new QtxDockWidget( true, desktop() );
+  if ( id == WT_InfoPanel ) {
+    // Info panel's position is strongly limited to the right area;
+    // It is not movable and not floatable.
+    dock->setAllowedAreas( Qt::RightDockWidgetArea );
+    dock->setFeatures( QDockWidget::DockWidgetClosable );
+    connect( dock, SIGNAL( aboutToShow()), this, SLOT( onInfoPanelShown() ) );
+  }
+  else {
+    dock->setFeatures( QDockWidget::AllDockWidgetFeatures );
+  }
   connect( dock, SIGNAL(  destroyed( QObject* ) ), this, SLOT( onWCDestroyed( QObject* ) ) );
 
-  dock->setFeatures( QDockWidget::AllDockWidgetFeatures );
   dock->setObjectName( wid->objectName().isEmpty() ? QString( "window_%1" ).arg( id ) :
                        QString( "%1Dock" ).arg( wid->objectName() ) );
   dock->setWidget( wid );
@@ -1455,10 +1453,9 @@ void LightApp_Application::placeDockWindow( const int id, Qt::DockWidgetArea pla
 /*!
   Gets window.
   \param flag - key for window
-  \param studyId - study id
   Flag used how identificator of window in windows list.
 */
-QWidget* LightApp_Application::getWindow( const int flag, const int )
+QWidget* LightApp_Application::getWindow( const int flag)
 {
   QWidget* wid = dockWindow( flag );
   if ( !wid )
@@ -1480,6 +1477,11 @@ SUIT_DataBrowser* LightApp_Application::objectBrowser()
   return qobject_cast<SUIT_DataBrowser*>( dockWindow( WT_ObjectBrowser ) );
 }
 
+QtxInfoPanel* LightApp_Application::infoPanel()
+{
+  return qobject_cast<QtxInfoPanel *>( dockWindow( WT_InfoPanel ));
+}
+
 /*!
   \return Log Window
 */
@@ -1494,7 +1496,7 @@ LogWindow* LightApp_Application::logWindow()
   when you request the python console, this function could return
   null. Then the optional parameter force (default to false) can be
   set to force the creation of the python console if it is not done
-  already. 
+  already.
   \param force - if true, the pythonConsole is created if it does not exist yet
   \return Python Console
 */
@@ -1567,7 +1569,8 @@ SUIT_ViewManager* LightApp_Application::getViewManager( const QString& vmType, c
       aVM = anActiveVM;
     }
 
-  if ( aVM && !aVM->getDetached() && create )
+  bool keepDetached = property("keep_detached").toBool();
+  if ( aVM && (!aVM->getDetached() || keepDetached) && create )
   {
     if ( !aVM->getActiveView() )
       {
@@ -1649,7 +1652,7 @@ SUIT_ViewManager* LightApp_Application::createViewManager( const QString& vmType
       viewMgr->getActiveView()->setFocus();
       return 0;
     } else {
-      viewMgr = new PVViewer_ViewManager( activeStudy(), desktop(), logWindow() );
+      viewMgr = new PVViewer_ViewManager( activeStudy(), desktop() );
     }
   }
 #endif
@@ -1688,6 +1691,7 @@ SUIT_ViewManager* LightApp_Application::createViewManager( const QString& vmType
                                resMgr->doubleValue( "OCCViewer", "focus_value", vm->stereographicFocusValue() ));
     vm->setInterocularDistance( resMgr->integerValue( "OCCViewer", "iod_type", vm->interocularDistanceType() ),
                                 resMgr->doubleValue( "OCCViewer", "iod_value", vm->interocularDistanceValue() ));
+    vm->setSelectionStyle((OCCViewer_ViewWindow::SelectionStyle) resMgr->integerValue( "OCCViewer", "adv_selection_mode", vm->selectionStyle() ) );
 
     vm->setReverseStereo( resMgr->booleanValue( "OCCViewer", "reverse_stereo", vm->isReverseStereo() ) );
     vm->setVSync( resMgr->booleanValue( "OCCViewer", "enable_vsync", vm->isVSync() ) );
@@ -1830,6 +1834,7 @@ void LightApp_Application::onStudyCreated( SUIT_Study* theStudy )
   }
 
   getWindow( WT_ObjectBrowser );
+  getWindow( WT_InfoPanel );
 
   loadDockWindowsState();
 
@@ -1861,6 +1866,7 @@ void LightApp_Application::onStudyOpened( SUIT_Study* theStudy )
   }
 
   getWindow( WT_ObjectBrowser );
+  getWindow( WT_InfoPanel );
 
   loadDockWindowsState();
 
@@ -1891,7 +1897,7 @@ void LightApp_Application::onStudySaved( SUIT_Study* s )
 }
 
 /*!Protected SLOT. On study closed.*/
-void LightApp_Application::onStudyClosed( SUIT_Study* s )
+void LightApp_Application::onStudyClosed( SUIT_Study* /*s*/ )
 {
   /*
   disconnect( this, SIGNAL( viewManagerRemoved( SUIT_ViewManager* ) ),
@@ -2031,7 +2037,12 @@ void LightApp_Application::onPreferences()
 }
 
 /*!Private SLOT. On preferences.*/
-void LightApp_Application::showPreferences( const QString& itemText )
+void LightApp_Application::showPreferences( const QString& path )
+{
+  showPreferences( QStringList() << path );
+}
+
+void LightApp_Application::showPreferences( const QStringList& path )
 {
   QApplication::setOverrideCursor( Qt::WaitCursor );
 
@@ -2042,7 +2053,7 @@ void LightApp_Application::showPreferences( const QString& itemText )
   if ( !prefDlg )
     return;
 
-  preferences()->activateItem( itemText );
+  preferences()->activateItem( path );
 
   if ( ( prefDlg->exec() == QDialog::Accepted || prefDlg->isSaved() ) &&  resourceMgr() )
   {
@@ -2095,8 +2106,6 @@ void LightApp_Application::updateActions()
 */
 SUIT_Study* LightApp_Application::createNewStudy()
 {
-  LightApp_Application::lastStudyId++;
-
   LightApp_Study* aStudy = new LightApp_Study( this );
 
   // Set up processing of major study-related events
@@ -2156,17 +2165,20 @@ QWidget* LightApp_Application::createWindow( const int flag )
 
     // Create OBSelector
     new LightApp_OBSelector( ob, mySelMgr );
-#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
-    ob->treeView()->header()->setResizeMode(SUIT_DataObject::VisibilityId, QHeaderView::Fixed);
-#else
     ob->treeView()->header()->setSectionResizeMode(SUIT_DataObject::VisibilityId, QHeaderView::Fixed);
-#endif
     ob->treeView()->header()->moveSection(SUIT_DataObject::NameId,SUIT_DataObject::VisibilityId);
     ob->treeView()->setColumnWidth(SUIT_DataObject::VisibilityId, VISIBILITY_COLUMN_WIDTH);
     ob->setProperty( "shortcut", QKeySequence( "Alt+Shift+O" ) );
     wid = ob;
     ob->connectPopupRequest( this, SLOT( onConnectPopupRequest( SUIT_PopupClient*, QContextMenuEvent* ) ) );
   }
+  else if ( flag == WT_InfoPanel)
+  {
+    QtxInfoPanel* ipanel = new QtxInfoPanel( desktop() );
+    ipanel->setObjectName( "infoPanel" );
+    ipanel->setWindowTitle( tr( "INFO_PANEL" ) );
+    wid = ipanel;
+  }
 #ifndef DISABLE_PYCONSOLE
   else  if ( flag == WT_PyConsole )
   {
@@ -2183,6 +2195,7 @@ QWidget* LightApp_Application::createWindow( const int flag )
   else if ( flag == WT_LogWindow )
   {
     LogWindow* logWin = new LogWindow( desktop() );
+    logWin->handleQtMessages( true );
     logWin->setObjectName( "logWindow" );
     logWin->setWindowTitle( tr( "LOG_WINDOW" ) );
     logWin->setProperty( "shortcut", QKeySequence( "Alt+Shift+L" ) );
@@ -2203,6 +2216,7 @@ void LightApp_Application::defaultWindows( QMap<int, int>& aMap ) const
 #endif
   if ( activeStudy() ) {
     aMap.insert( WT_ObjectBrowser, Qt::LeftDockWidgetArea );
+    aMap.insert( WT_InfoPanel, Qt::RightDockWidgetArea );
     //  aMap.insert( WT_LogWindow, Qt::DockBottom );
   }
 }
@@ -2251,42 +2265,48 @@ LightApp_Preferences* LightApp_Application::preferences( const bool crt ) const
     if ( !app )
       continue;
 
-    QStringList modNameList;
-    app->modules( modNameList, false );
+    // all modules available in current session
+    QStringList names;
+    app->modules( names, false );
 
-    QMap<QString, QString> iconMap;
-    app->moduleIconNames( iconMap );
+    // icons of modules
+    QMap<QString, QString> icons;
+    app->moduleIconNames( icons );
 
-    for ( QStringList::const_iterator it = modNameList.begin(); it != modNameList.end(); ++it )
+    // step 1: iterate through list of all available modules
+    // and add empty preferences page
+    for ( QStringList::const_iterator it = names.begin(); it != names.end(); ++it )
     {
-      if ( !app->isModuleAccessible( *it ) || _prefs_->hasModule( *it ) )
-        continue;
-
-      int modId = _prefs_->addPreference( *it );
-      if ( iconMap.contains( *it ) )
-        _prefs_->setItemIcon( modId, Qtx::scaleIcon( resMgr->loadPixmap( moduleName( *it ), iconMap[*it], false ), 20 ) );
+      if ( !_prefs_->hasModule( *it ) ) // prevent possible duplications
+      {
+        int modId = _prefs_->addPreference( *it ); // add empty page
+        if ( icons.contains( *it ) )               // set icon
+          _prefs_->setItemIcon( modId, Qtx::scaleIcon( resMgr->loadPixmap( moduleName( *it ),
+                                                                           icons[*it], false ), 20 ) );
+      }
     }
 
-    ModuleList modList;
-    app->modules( modList );
-    QListIterator<CAM_Module*> itr( modList );
+    // step 2: iterate through list of all loaded modules
+    // and initialize their preferences
+    ModuleList loadedModules;
+    app->modules( loadedModules );
+    QListIterator<CAM_Module*> itr( loadedModules );
     while ( itr.hasNext() )
     {
-      LightApp_Module* mod = 0;
+      LightApp_Module* module = 0;
+      CAM_Module* m = itr.next();
+      if ( m->inherits( "LightApp_Module" ) )
+        module = (LightApp_Module*)m;
 
-      CAM_Module* anItem = itr.next();
-      if ( anItem->inherits( "LightApp_Module" ) )
-        mod = (LightApp_Module*)anItem;
-
-      if ( mod && !_prefs_->hasModule( mod->moduleName() ) )
+      if ( module && !_prefs_->hasModule( module->moduleName() ) )
       {
-        _prefs_->addPreference( mod->moduleName() );
-        mod->createPreferences();
-        that->emptyPreferences( mod->moduleName() );
+        _prefs_->addPreference( module->moduleName() ); // add page (for sure, had to be done at step 1)
+        module->createPreferences();                    // initialize preferences
+        that->emptyPreferences( module->moduleName() ); // show dummy page if module does not export any preferences
       }
     }
   }
-  _prefs_->setItemProperty( "info", tr( "PREFERENCES_NOT_LOADED" ) );
+  _prefs_->setItemProperty( "info", tr( "PREFERENCES_NOT_LOADED" ) ); // dummy page for modules which are not loaded yet
 
   return myPrefs;
 }
@@ -2316,8 +2336,6 @@ void LightApp_Application::emptyPreferences( const QString& modName )
   if ( !item || !item->isEmpty() )
     return;
 
-  //  printf( "---------------------> Modify for empty module.\n" );
-
   QtxPagePrefFrameItem* frm = new QtxPagePrefFrameItem( item->title(), item->parentItem() );
   frm->setIcon( item->icon() );
   frm->setStretch( false );
@@ -2378,8 +2396,14 @@ void LightApp_Application::createPreferences( LightApp_Preferences* pref )
   pref->addPreference( tr( "PREF_SHOW_SPLASH" ), lookGroup, LightApp_Preferences::Bool, "launch", "splash" );
   // .... -> opaque resize
   pref->addPreference( tr( "PREF_OPAQUE_RESIZE" ), lookGroup, LightApp_Preferences::Bool, "desktop", "opaque_resize" );
-  // .... -> drop-down buttons 
+  // .... -> drop-down buttons
   pref->addPreference( tr( "PREF_DROP_DOWN_BUTTONS" ), lookGroup, LightApp_Preferences::Bool, "viewers", "drop_down_buttons" );
+  // .... -> Notification timeout
+  int delay = pref->addPreference( tr( "PREF_NOTIFY_TIMEOUT" ), lookGroup, LightApp_Preferences::IntSpin, "notification", "timeout" );
+  pref->setItemProperty( "special", tr("PREF_NOTIFY_TIMEOUT_NONE"), delay );
+  pref->setItemProperty( "min", 0, delay );
+  pref->setItemProperty( "max", 100, delay );
+  pref->setItemProperty( "suffix", " sec", delay );
   // ... "Look and feel" group <<end>>
 
   // ... "Study properties" group <<start>>
@@ -2641,13 +2665,22 @@ void LightApp_Application::createPreferences( LightApp_Preferences* pref )
 
   // ... "Selection" group <<start>>
   int occSelectionGroup = pref->addPreference( tr( "PREF_GROUP_SELECTION" ), occGroup );
-  pref->setItemProperty( "columns", 2, occSelectionGroup );
+  pref->setItemProperty( "columns", 3, occSelectionGroup );
   // .... -> enable preselection
   pref->addPreference( tr( "PREF_ENABLE_PRESELECTION" ), occSelectionGroup,
                        LightApp_Preferences::Bool, "OCCViewer", "enable_preselection" );
   // .... -> enable selection
   pref->addPreference( tr( "PREF_ENABLE_SELECTION" ), occSelectionGroup,
                        LightApp_Preferences::Bool, "OCCViewer", "enable_selection" );
+  // .... -> selection style
+  int aSeleStyle = pref->addPreference( tr( "PREF_SELECTION_STYLE" ), occSelectionGroup,
+                       LightApp_Preferences::Selector, "OCCViewer", "adv_selection_mode" );
+  aValuesList.clear();
+  anIndicesList.clear();
+  aValuesList   << tr("PREF_POLYGON_SELECTION") << tr("PREF_CIRCLE_SELECTION");
+  anIndicesList << 0 << 1;
+  pref->setItemProperty( "strings", aValuesList, aSeleStyle);
+  pref->setItemProperty( "indexes", anIndicesList, aSeleStyle);
   // ... "Selection" group <<end>>
 
   // ... "Clipping" group <<start>>
@@ -3358,6 +3391,27 @@ void LightApp_Application::preferencesChanged( const QString& sec, const QString
   }
 #endif
 
+
+#ifndef DISABLE_OCCVIEWER
+  if (sec == QString("OCCViewer") && param == QString("adv_selection_mode"))
+  {
+    int mode = resMgr->integerValue("OCCViewer", "adv_selection_mode", 0);
+    QList<SUIT_ViewManager*> lst;
+    viewManagers(OCCViewer_Viewer::Type(), lst);
+    QListIterator<SUIT_ViewManager*> it(lst);
+    while (it.hasNext())
+    {
+      SUIT_ViewModel* vm = it.next()->getViewModel();
+      if (!vm || !vm->inherits("OCCViewer_Viewer"))
+        continue;
+
+      OCCViewer_Viewer* occVM = (OCCViewer_Viewer*)vm;
+      occVM->setSelectionStyle((OCCViewer_ViewWindow::SelectionStyle)mode);
+    }
+  }
+#endif
+
+
 #ifndef DISABLE_OCCVIEWER
   if ( sec == QString( "OCCViewer" ) && param == QString( "stereo_type" ) )
   {
@@ -3982,17 +4036,8 @@ void LightApp_Application::afterCloseDoc()
 void LightApp_Application::updateModuleActions()
 {
   QString modName;
-  if ( activeModule() ) {
+  if ( activeModule() )
     modName = activeModule()->moduleName();
-    if ( !isModuleAccessible( modName ) ) {
-      QList<SUIT_Application*> apps = SUIT_Session::session()->applications();
-      foreach( SUIT_Application* app, apps ) {
-        LightApp_Application* lapp = dynamic_cast<LightApp_Application*>( app );
-        if ( lapp && lapp != this )
-          lapp->removeModuleAction( modName );
-      }
-    }
-  }
 
   LightApp_ModuleAction* moduleAction =
     qobject_cast<LightApp_ModuleAction*>( action( ModulesListId ) );
@@ -4023,11 +4068,11 @@ bool LightApp_Application::checkModule( const QString& title )
 
   QStringList paths;
 #if defined(WIN32)
-  paths = QString( ::getenv( "PATH" ) ).split( ";", QString::SkipEmptyParts );
+  paths = QString( Qtx::getenv( "PATH" ) ).split( ";", QString::SkipEmptyParts );
 #elif defined(__APPLE__)
-  paths = QString( ::getenv( "DYLD_LIBRARY_PATH" ) ).split( ":", QString::SkipEmptyParts );
+  paths = QString( Qtx::getenv( "DYLD_LIBRARY_PATH" ) ).split( ":", QString::SkipEmptyParts );
 #else
-  paths = QString( ::getenv( "LD_LIBRARY_PATH" ) ).split( ":", QString::SkipEmptyParts );
+  paths = QString( Qtx::getenv( "LD_LIBRARY_PATH" ) ).split( ":", QString::SkipEmptyParts );
 #endif
 
   bool isFound = false;
@@ -4050,8 +4095,11 @@ bool LightApp_Application::checkModule( const QString& title )
   if ( isPyModule )
   {
     QString pyModule = QString( "%1GUI.py" ).arg( name );
-    paths = QString( ::getenv( "PYTHONPATH" ) ).split( ":", QString::SkipEmptyParts );
-
+#if defined(WIN32)
+    paths = QString( Qtx::getenv( "PYTHONPATH" ) ).split( ";", QString::SkipEmptyParts );
+#else
+    paths = QString( Qtx::getenv( "PYTHONPATH" ) ).split( ":", QString::SkipEmptyParts );
+#endif
     isFound = false;
     for ( it = paths.begin(); it != paths.end() && !isFound; ++it )
     {
@@ -4118,13 +4166,47 @@ void LightApp_Application::updateWindows()
   for ( WinMap::ConstIterator it = myWin.begin(); it != myWin.end(); ++it )
   {
     QWidget* wid = it.value();
+    if ( !wid )
+      continue;
     if ( winMap.contains( it.key() ) )
       wid->setVisible( true );
-    else
+    else if ( !activeStudy() )
       delete wid;
+    else
+      wid->setVisible( false );
   }
 
   loadDockWindowsState();
+
+  if ( !activeModule() && infoPanel() )
+  {
+    infoPanel()->clear();
+    infoPanel()->setTitle( tr( "INFO_WELCOME_TO_SALOME" ) );
+
+    int grp = infoPanel()->addGroup( tr( "INFO_GETTING_STARTED" ) );
+    infoPanel()->addAction( action( FileNewId ), grp );
+    infoPanel()->addLabel( action( FileNewId )->statusTip(), grp );
+    infoPanel()->addAction( action( FileOpenId ), grp );
+    infoPanel()->addLabel( action( FileOpenId )->statusTip(), grp );
+    if ( HAS_TUTORIAL_URL ) {
+      infoPanel()->addAction( action( TutorialsId ), grp );
+      infoPanel()->addLabel( action( TutorialsId )->statusTip(), grp );
+    }
+    infoPanel()->addAction( action( VideosId ), grp );
+    infoPanel()->addLabel( action( VideosId )->statusTip(), grp );
+
+    LightApp_ModuleAction* ma = qobject_cast<LightApp_ModuleAction*>(action(ModulesListId));
+    if ( ma && ma->count() > 0 )
+    {
+      grp = infoPanel()->addGroup( tr( "INFO_AVAILABLE_MODULES" ) );
+      foreach ( QString mname, ma->modules() )
+      {
+        infoPanel()->addAction( ma->moduleAction( mname ), grp );
+        if ( !moduleDescription( mname ).isEmpty() )
+          infoPanel()->addLabel( moduleDescription( mname ), grp );
+      }
+    }
+  }
 }
 
 /*!
@@ -4664,7 +4746,12 @@ bool LightApp_Application::event( QEvent* e )
                                   d ? *d : "",
                                   SUIT_MessageBox::Yes | SUIT_MessageBox::No,
                                   SUIT_MessageBox::Yes ) == SUIT_MessageBox::Yes )
-      showPreferences( tr( "PREF_APP" ) );
+    {
+      QStringList path;
+      path << tr( "PREF_CATEGORY_SALOME" ) << tr( "PREF_TAB_GENERAL" )
+           << tr( "PREF_GROUP_EXT_BROWSER" ) << tr( "PREF_APP" );
+      showPreferences( path );
+    }
     if( d )
       delete d;
     return true;
@@ -4889,9 +4976,9 @@ QString LightApp_Application::browseObjects( const QStringList& theEntryList,
         SUIT_DataOwnerPtrList aList;
 #ifndef DISABLE_SALOMEOBJECT
         Handle(SALOME_InteractiveObject) aSObj = new SALOME_InteractiveObject
-          ( anObject->entry().toLatin1().constData(),
+          ( anObject->entry().toUtf8().constData(),
             anObject->componentDataType().toLatin1().constData(),
-            anObject->name().toLatin1().constData() );
+            anObject->name().toUtf8().constData() );
         LightApp_DataOwner* owner = new LightApp_DataOwner( aSObj  );
 #else
         LightApp_DataOwner* owner = new LightApp_DataOwner( anEntry );
@@ -4931,7 +5018,7 @@ bool LightApp_Application::renameAllowed( const QString& /*entry*/) const {
   \param name new name of the object
   \brief Return \c true if rename operation finished successfully, \c false otherwise.
 */
-bool LightApp_Application::renameObject( const QString& entry, const QString& ) {
+bool LightApp_Application::renameObject( const QString& /*entry*/, const QString& /*name*/ ) {
   return false;
 }
 
@@ -4950,6 +5037,34 @@ void LightApp_Application::onDesktopMessage( const QString& message )
     if ( !vtype.isEmpty() )
       getViewManager( vtype, true );
   }
+  else if ( message.toLower().startsWith("register_module_in_study" ) ) {
+    QString moduleName = message.split( sectionSeparator ).last();
+    // Check name of current activating module name in order to avoid ciclik 
+    // call because of messages
+    if (!property("activateModule").toBool()) {
+      CAM_Module* mod = module(moduleName);
+      if (!mod)
+        mod = module(moduleTitle(moduleName));
+      if (!mod) {
+        mod = loadModule(moduleName);
+        if (!mod)
+          mod = loadModule(moduleTitle(moduleName));
+        if (mod) {
+          addModule(mod);
+        }
+      }
+      if (mod) {
+        CAM_Study* anActiveStudy = dynamic_cast<CAM_Study*>(activeStudy());
+        if (anActiveStudy) {
+          mod->connectToStudy(anActiveStudy);
+          LightApp_DataModel* aDM = dynamic_cast<LightApp_DataModel*>(mod->dataModel());
+          if(aDM) {
+            aDM->initRootObject();
+          }
+        }
+      }
+    }
+  }
   else {
     QStringList data = message.split( sectionSeparator );
     if ( data.count() > 1 ) {
@@ -4980,6 +5095,12 @@ void LightApp_Application::onDesktopMessage( const QString& message )
   }
 }
 
+void LightApp_Application::onInfoPanelShown()
+{
+  if ( activeModule() && activeModule()->inherits( "LightApp_Module" ) )
+    ((LightApp_Module*)activeModule())->updateInfoPanel();
+}
+
 /*!
   Internal method. 
   Returns all top level toolbars.
@@ -5043,7 +5164,7 @@ QByteArray LightApp_Application::processState(QByteArray& input,
       return aRes;
     QDataStream anInputData(&input, QIODevice::ReadOnly);
 
-    int toolBarMarkerIndexDef;
+    int toolBarMarkerIndexDef = 0;
     if(hasDefaultState) {
       toolBarMarkerIndexDef = getToolbarMarkerIndex(defaultState, aNames);
       if(toolBarMarkerIndexDef < 0)
@@ -5147,7 +5268,7 @@ void LightApp_Application::emitOperationFinished( const QString& theModuleName,
   Update visibility state of given objects
 */
 void LightApp_Application::updateVisibilityState( DataObjectList& theList,
-                                                  SUIT_ViewModel*  theViewModel )
+                                                  SUIT_ViewModel* theViewModel )
 {
   if ( !theViewModel || theList.isEmpty() ) return;
 
@@ -5162,18 +5283,66 @@ void LightApp_Application::updateVisibilityState( DataObjectList& theList,
     if ( !obj || aStudy->isComponent( obj->entry() ) )
       continue;
 
-    LightApp_Module* anObjModule = dynamic_cast<LightApp_Module*>(obj->module());
-    if ( anObjModule ) {
-      LightApp_Displayer* aDisplayer = anObjModule->displayer();
-      if ( aDisplayer ) {
-        Qtx::VisibilityState anObjState = Qtx::UnpresentableState;
-        if ( aDisplayer->canBeDisplayed( obj->entry(), theViewModel->getType() ) ) {
-          if ( aView && aDisplayer->IsDisplayed( obj->entry(), aView ) )
-            anObjState = Qtx::ShownState;
-          else
-            anObjState = Qtx::HiddenState;
+    QString mname = aStudy->componentDataType(obj->entry());
+    LightApp_Displayer* aDisplayer = LightApp_Displayer::FindDisplayer(mname, false);
+    if ( aDisplayer ) {
+      Qtx::VisibilityState anObjState = Qtx::UnpresentableState;
+      if ( aDisplayer->canBeDisplayed( obj->entry(), theViewModel->getType() ) ) {
+        if ( aDisplayer->IsDisplayed( obj->entry(), aView ) )
+          anObjState = Qtx::ShownState;
+        else
+          anObjState = Qtx::HiddenState;
+      }
+      aStudy->setVisibilityState( obj->entry(), anObjState );
+    }
+  }
+}
+
+/*!
+  Update presentations of all displayed objects of theComponent in specified viewers
+*/
+void LightApp_Application::updatePresentations( const QString& theComponent,
+                                                const QStringList& theViewManagerTypes )
+{
+  LightApp_Displayer* aDisplayer = LightApp_Displayer::FindDisplayer(theComponent, false);
+  if ( aDisplayer ) {
+    LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>(activeStudy());
+    DataObjectList aComps;
+    bool isFound = false;
+    aStudy->root()->children( aComps );
+    DataObjectList::const_iterator aCompsIt = aComps.begin();
+    for ( ; aCompsIt != aComps.end() && !isFound; aCompsIt++ ) {
+      LightApp_DataObject* aComp = dynamic_cast<LightApp_DataObject*>( *aCompsIt );
+      if ( aComp && aComp->componentDataType() ==  theComponent) {
+        isFound = true;
+        DataObjectList anObjs;
+        aComp->children(anObjs, true);
+
+        QList<SUIT_ViewManager*> aViewMgrs;
+        QStringList::const_iterator itVMTypes = theViewManagerTypes.begin();
+        for ( ; itVMTypes != theViewManagerTypes.end(); ++itVMTypes )
+          viewManagers( *itVMTypes, aViewMgrs );
+
+        DataObjectList::const_iterator itObjs = anObjs.begin();
+        for ( ; itObjs != anObjs.end(); itObjs++ ) {
+          LightApp_DataObject* anObj = dynamic_cast<LightApp_DataObject*>( *itObjs );
+          QString anEntry = anObj->entry();
+
+          QListIterator<SUIT_ViewManager*> itViewMgrs( aViewMgrs );
+          while ( itViewMgrs.hasNext()) {
+            SUIT_ViewModel* aVM = itViewMgrs.next()->getViewModel();
+            if ( aVM ) {
+              SALOME_View* aView = dynamic_cast<SALOME_View*>(aVM);
+              if ( aView ) {
+                bool isDisp = aDisplayer->IsDisplayed( anEntry, aView );
+                aDisplayer->Erase( anEntry, true, false, aView );
+                if ( isDisp ) {
+                  aDisplayer->Display( anEntry, false, aView );
+                }
+              }
+            }
+          }
         }
-        aStudy->setVisibilityState( obj->entry(), anObjState );
       }
     }
   }
@@ -5216,30 +5385,37 @@ void LightApp_Application::onViewManagerRemoved( SUIT_ViewManager* )
 /*!
   Check existing document.
 */
-bool LightApp_Application::checkExistingDoc()
+bool LightApp_Application::checkExistingDoc( bool closeExistingDoc )
 {
   bool result = true;
   if( activeStudy() ) {
-    int answer = SUIT_MessageBox::question( desktop(),
-                                            tr( "APPCLOSE_CAPTION" ),
-                                            tr( "STUDYCLOSE_DESCRIPTION" ),
-                                            tr( "APPCLOSE_SAVE" ),
-                                            tr( "APPCLOSE_CLOSE" ),
-                                            tr( "APPCLOSE_CANCEL" ), 0 );
+    int answer = !activeStudy()->isModified() ? 1 :
+                 SUIT_MessageBox::question( desktop(),
+                                           tr( "APPCLOSE_CAPTION" ),
+                                           tr( "STUDYCLOSE_DESCRIPTION" ),
+                                           tr( "APPCLOSE_SAVE" ),
+                                           tr( "APPCLOSE_CLOSE" ),
+                                           tr( "APPCLOSE_CANCEL" ), 0 );
     if(answer == 0) {
       if ( activeStudy()->isSaved() ) {
         onSaveDoc();
-        closeDoc( false );
+               if (closeExistingDoc) {
+                       closeDoc(false);
+               }
       } else if ( onSaveAsDoc() ) {
-        if( !closeDoc( false ) ) {
-          result = false;
+         if (closeExistingDoc) {
+           if( !closeDoc( false ) ) {
+             result = false;
+           }
         }
       } else {
         result = false;
       }
     }
     else if( answer == 1 ) {
-      closeDoc( false );
+      if (closeExistingDoc) {
+       closeDoc( false );
+      }
     } else if( answer == 2 ) {
       result = false;
     }