Salome HOME
On re-creating of the Object browser, show columns in accordance with preferences.
[modules/gui.git] / src / SalomeApp / SalomeApp_Application.cxx
old mode 100755 (executable)
new mode 100644 (file)
index 4182bfc..b867e85
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
+// Copyright (C) 2007-2015  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
@@ -84,6 +84,7 @@
 #include <SALOME_LifeCycleCORBA.hxx>
 
 #include <QApplication>
+#include <QWidget>
 #include <QAction>
 #include <QRegExp>
 #include <QCheckBox>
@@ -98,7 +99,6 @@
 #include <Basics_Utils.hxx>
 
 #include <SALOME_ListIO.hxx>
-#include <SALOME_ListIteratorOfListIO.hxx>
 #include <SALOME_Prs.h>
 
 
@@ -162,11 +162,9 @@ extern "C" SALOMEAPP_EXPORT SUIT_Application* createApplication()
 
 /*!Constructor.*/
 SalomeApp_Application::SalomeApp_Application()
-  : LightApp_Application()
+  : LightApp_Application(),
+    myIsCloseFromExit( false )
 {
-  connect( desktop(), SIGNAL( message( const QString& ) ),
-           this, SLOT( onLoadDocMessage( const QString& ) ), Qt::UniqueConnection );
-  myIsSiman = false; // default
 }
 
 /*!Destructor.
@@ -178,6 +176,25 @@ SalomeApp_Application::~SalomeApp_Application()
   //SALOME_EventFilter::Destroy();
 }
 
+QStringList __getArgsList(QString argsString)
+{
+  // Special process if some items of 'args:' list are themselves lists
+  // Note that an item can be a list, but not a list of lists...
+  // So we can have something like this:
+  // myscript.py args:['file1','file2'],val1,"done",[1,2,3],[True,False],"ok"
+  // With such a call, argsString variable contains the string representing "[file1,file2]", "val1", "done", "[1,2,3]", "[True,False]", "ok"
+  // We have to split argsString to obtain: [[file1,file2],val1,done,[1,2,3],[True,False],ok]
+  argsString.replace("\\\"", "'"); // replace escaped double quotes by simple quotes
+  bool containsList = (QRegExp("(\\[[^\\]]*\\])").indexIn(argsString) >= 0);
+  if (containsList) {
+    QStringList sl = argsString.split("\"", QString::SkipEmptyParts);
+    sl.removeAll(", ");
+    return sl;
+  }
+  else
+    return argsString.split(",", QString::SkipEmptyParts);
+}
+
 /*!Start application.*/
 void SalomeApp_Application::start()
 {
@@ -190,9 +207,9 @@ void SalomeApp_Application::start()
     QStringList pyfiles;
     QString loadStudy;
 
-    for (int i = 1; i < qApp->argc(); i++) {
+    for (int i = 1; i < qApp->arguments().size(); i++) {
       QRegExp rxs ("--study-hdf=(.+)");
-      if ( rxs.indexIn( QString(qApp->argv()[i]) ) >= 0 && rxs.capturedTexts().count() > 1 ) {
+      if ( rxs.indexIn( qApp->arguments()[i] ) >= 0 && rxs.capturedTexts().count() > 1 ) {
         QString file = rxs.capturedTexts()[1];
         QFileInfo fi ( file );
         QString extension = fi.suffix().toLower();
@@ -201,9 +218,8 @@ void SalomeApp_Application::start()
       }
       else {
         QRegExp rxp ("--pyscript=\\[(.+)\\]");
-       QRegExp rxl ("--siman-study=(.+)");
-        if ( rxp.indexIn( QString(qApp->argv()[i]) ) >= 0 && rxp.capturedTexts().count() > 1 ) {
-         // pyscript
+        if ( rxp.indexIn( qApp->arguments()[i] ) >= 0 && rxp.capturedTexts().count() > 1 ) {
+          // pyscript
           QStringList dictList = rxp.capturedTexts()[1].split("},", QString::SkipEmptyParts);
           for (int k = 0; k < dictList.count(); ++k) {
             QRegExp rxd ("[\\s]*\\{?([^\\{\\}]+)\\}?[\\s]*");
@@ -214,13 +230,6 @@ void SalomeApp_Application::start()
             }
           }
         }
-#ifdef WITH_SIMANIO
-       if ( rxl.indexIn( QString(qApp->argv()[i]) ) >= 0 && rxl.capturedTexts().count() > 1 ) {
-         // siman
-         loadStudy = rxl.capturedTexts()[1];
-         myIsSiman = true;
-       }
-#endif
       }
     }
     // Here pyfiles elements are: "script_name": [list_of_"arg"s]
@@ -229,6 +238,7 @@ void SalomeApp_Application::start()
     LightApp_Application::start();
     SALOME_EventFilter::Init();
 
+    setProperty("open_study_from_command_line", true);
     if ( !hdffile.isEmpty() )       // open hdf file given as parameter
       onOpenDoc( hdffile );
     else if ( pyfiles.count() > 0 ) // create new study
@@ -237,6 +247,7 @@ void SalomeApp_Application::start()
       if (onLoadDoc(loadStudy))
         updateObjectBrowser(true);
     }
+    setProperty("open_study_from_command_line", QVariant());
 
 #ifndef DISABLE_PYCONSOLE
     // import/execute python scripts
@@ -254,7 +265,7 @@ void SalomeApp_Application::start()
             if ( rxp.indexIn( pyfiles[j] ) >= 0 && rxp.capturedTexts().count() == 3 ) {
               QString script = rxp.capturedTexts()[1];
               QString args = "";
-              QStringList argList = rxp.capturedTexts()[2].split(",", QString::SkipEmptyParts);
+              QStringList argList = __getArgsList(rxp.capturedTexts()[2]);
               for (uint k = 0; k < argList.count(); k++ ) {
                 QString arg = argList[k].trimmed();
                 arg.remove( QRegExp("^[\"]") );
@@ -320,25 +331,14 @@ void SalomeApp_Application::createActions()
                 tr( "MEN_DESK_REGISTRY_DISPLAY" ), tr( "PRP_DESK_REGISTRY_DISPLAY" ),
                 /*Qt::SHIFT+Qt::Key_D*/0, desk, false, this, SLOT( onRegDisplay() ) );
 
-  //rnv commented : implementation of the mono-study in GUI
-  //
-  //createAction( FileLoadId, tr( "TOT_DESK_FILE_LOAD" ),
-  //              resourceMgr()->loadPixmap( "STD", tr( "ICON_FILE_OPEN" ) ),
-  //              tr( "MEN_DESK_FILE_LOAD" ), tr( "PRP_DESK_FILE_LOAD" ),
-  //              Qt::CTRL+Qt::Key_L, desk, false, this, SLOT( onLoadDoc() ) );
-
-
-#ifdef WITH_SIMANIO
-  if (myIsSiman) {
-    // check-in operations for SIMAN study  
-    createAction( SimanCheckInId, tr( "TOT_SIMAN_CHECK_IN" ), QIcon(),
-                tr( "MEN_SIMAN_CHECK_IN" ), tr( "PRP_SIMAN_CHECK_IN" ),
-                0, desk, false, this, SLOT( onCheckIn() ) );
-    createAction( SimanLocalCheckInId, tr( "TOT_SIMAN_LOCAL_CHECK_IN" ), QIcon(),
-                tr( "MEN_SIMAN_LOCAL_CHECK_IN" ), tr( "PRP_SIMAN_LOCAL_CHECK_IN" ),
-                0, desk, false, this, SLOT( onLocalCheckIn() ) );
-  }
-#endif
+  createAction( ConnectId, tr( "TOT_DESK_CONNECT_STUDY" ), QIcon(),
+                tr( "MEN_DESK_CONNECT" ), tr( "PRP_DESK_CONNECT" ),
+                Qt::CTRL+Qt::Key_L, desk, false, this, SLOT( onLoadDoc() ) );
+
+  createAction( DisconnectId, tr( "TOT_DESK_DISCONNECT_STUDY" ), QIcon(),
+                tr( "MEN_DESK_DISCONNECT" ), tr( "PRP_DESK_DISCONNECT" ),
+                Qt::CTRL+Qt::Key_U, desk, false, this, SLOT( onUnloadDoc() ) );
+
 
   int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1 );
 
@@ -346,19 +346,11 @@ void SalomeApp_Application::createActions()
   // creation of menu item is moved to VISU
   //  createMenu( SaveGUIStateId, fileMenu, 10, -1 );
 
-  // createMenu( FileLoadId,   fileMenu, 0 );
+  createMenu( ConnectId,    fileMenu, 5 );
+  createMenu( DisconnectId, fileMenu, 5 );
+  createMenu( separator(),  fileMenu, -1, 5 );
 
-#ifdef WITH_SIMANIO
-  if (myIsSiman) {
-    // check-in operation for SIMAN study
-    // last argument "5" locates this just after "Save As" but certain constant is bad => insert after the separator
-    createMenu( SimanCheckInId, fileMenu, 5);
-    createMenu( SimanLocalCheckInId, fileMenu, 5);
-    createMenu( separator(), fileMenu, 5 );
-  }
-#endif
   createMenu( DumpStudyId, fileMenu, 10, -1 );
-  createMenu( separator(), fileMenu, -1, 10, -1 );
   createMenu( LoadScriptId, fileMenu, 10, -1 );
   createMenu( separator(), fileMenu, -1, 10, -1 );
   createMenu( PropertiesId, fileMenu, 10, -1 );
@@ -377,7 +369,7 @@ void SalomeApp_Application::createActions()
   {
     PyLockWrapper lck; // acquire GIL
     PyObjWrapper pluginsmanager = PyImport_ImportModule((char*)"salome_pluginsmanager");
-    PyObjWrapper res = PyObject_CallMethod( pluginsmanager, (char*)"initialize", (char*)"isss",0,"salome",tr("MEN_DESK_PLUGINS_TOOLS").toStdString().c_str(),tr("MEN_DESK_PLUGINS").toStdString().c_str());
+    PyObjWrapper res = PyObject_CallMethod( pluginsmanager, (char*)"initialize", (char*)"isss",0,"salome",tr("MEN_DESK_TOOLS").toUtf8().data(),tr("MEN_DESK_PLUGINS").toUtf8().data());
     if ( !res )
       PyErr_Print();
   }
@@ -387,18 +379,6 @@ void SalomeApp_Application::createActions()
 
 }
 
-
-/*!Set desktop:*/
-void SalomeApp_Application::setDesktop( SUIT_Desktop* desk )
-{
-  LightApp_Application::setDesktop( desk );
-
-  if ( desk ) {
-    connect( desk, SIGNAL( message( const QString& ) ),
-             this, SLOT( onLoadDocMessage( const QString& ) ), Qt::UniqueConnection );
-  }
-}
-
 /*!
   \brief Close application.
 */
@@ -413,8 +393,11 @@ void SalomeApp_Application::onExit()
     killServers = dlg.isServersShutdown();
   }
 
-  if ( result )
+  if ( result ) {
+    if ( !killServers ) myIsCloseFromExit = true;
     SUIT_Session::session()->closeSession( SUIT_Session::ASK, killServers );
+    if ( SUIT_Session::session()->applications().count() > 0 ) myIsCloseFromExit = false;
+  }
 }
 
 /*!SLOT. Load document.*/
@@ -425,7 +408,7 @@ void SalomeApp_Application::onLoadDoc()
   std::vector<std::string> List = studyMgr()->GetOpenStudies();
 
   // rnv: According to the single-study approach on the server side
-  //      can be only one study. So if it is exists connect to them,  
+  //      can be only one study. So if it is exists connect to them,
   //      overwise show warning message: "No active study on the server"
 
   /*
@@ -461,7 +444,7 @@ void SalomeApp_Application::onLoadDoc()
                               QObject::tr("WRN_NO_STUDY_ON SERV") );
     return;
   }
-  
+
   studyName = List[0].c_str();
 
 #ifndef WIN32
@@ -477,6 +460,31 @@ void SalomeApp_Application::onLoadDoc()
   }
 }
 
+/*!SLOT. Unload document.*/
+void SalomeApp_Application::onUnloadDoc( bool ask )
+{
+  if ( ask ) {
+    activeStudy()->abortAllOperations();
+    if ( activeStudy()->isModified() ) {
+      QString docName = activeStudy()->studyName().trimmed();
+      int answer = SUIT_MessageBox::question( desktop(), tr( "DISCONNECT_CAPTION" ),
+                                            tr( "DISCONNECT_DESCRIPTION" ),
+                                            tr( "DISCONNECT_SAVE" ),
+                                            tr( "DISCONNECT_WO_SAVE" ),
+                                            tr( "APPCLOSE_CANCEL" ), 0 );
+      if ( answer == 0 ) { // save before unload
+        if ( activeStudy()->isSaved() )
+          onSaveDoc();
+        else if ( !onSaveAsDoc() )
+          return;
+      }
+      else if ( answer == 2 ) // Cancel
+        return;
+    }
+  }
+  closeActiveDoc( false );
+}
+
 /*!SLOT. Create new study and load script*/
 void SalomeApp_Application::onNewWithScript()
 {
@@ -543,18 +551,29 @@ bool SalomeApp_Application::onLoadDoc( const QString& aName )
   return res;
 }
 
-/*!SLOT. Load document with a name, specified in \a aMessage.*/
-void SalomeApp_Application::onLoadDocMessage(const QString& aMessage)
+/*!SLOT. Parse message for desktop.*/
+void SalomeApp_Application::onDesktopMessage( const QString& message )
 {
-  if (aMessage.indexOf("simanCheckoutDone ") == 0) {
-#ifdef WITH_SIMANIO
-    onLoadDoc(aMessage.section(' ', 1));
-#else
-    printf( "****************************************************************\n" );
-    printf( "*    Warning: SALOME is built without SIMAN support.\n" );
-    printf( "****************************************************************\n" );
-#endif
+  if (message.indexOf("studyCreated:") == 0) {
+    // Enable 'Connect' action
+    updateCommandsStatus();
+  }
+  else if (message.indexOf("studyClosed:") == 0) {
+    /* message also contains ID of the closed study,
+       but as soon as SALOME is mono-study application for the moment,
+       this ID is not needed now.*/
+    //long aStudyId = message.section(':', 1).toLong();
+    // Disconnect GUI from active study, because it was closed on DS side.
+    closeActiveDoc( false );
+    // Disable 'Connect' action
+    QAction* a = action( ConnectId );
+    if ( a )
+      a->setEnabled( false );
   }
+  else if ( message.toLower() == "connect_to_study" ) {
+    onLoadDoc();
+  }
+  LightApp_Application::onDesktopMessage( message );
 }
 
 /*!SLOT. Copy objects to study maneger from selection maneger..*/
@@ -641,7 +660,6 @@ void SalomeApp_Application::onCloseDoc( bool ask )
 
     }
   }
-
   LightApp_Application::onCloseDoc( ask );
 }
 
@@ -655,8 +673,15 @@ void SalomeApp_Application::onSelectionChanged()
    bool canCopy  = false;
    bool canPaste = false;
 
+   LightApp_Module* m = dynamic_cast<LightApp_Module*>( activeModule() );
+
+   if ( m ) {
+     canCopy  = m->canCopy();
+     canPaste = m->canPaste();
+   }
+
    SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(activeStudy());
-   if (study != NULL) {
+   if (study) {
      _PTR(Study) stdDS = study->studyDS();
 
      if (stdDS) {
@@ -666,8 +691,8 @@ void SalomeApp_Application::onSelectionChanged()
          _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry());
 
          if ( so ) {
-             canCopy = studyMgr()->CanCopy(so);
-             canPaste = studyMgr()->CanPaste(so);
+           canCopy  = canCopy  || studyMgr()->CanCopy(so);
+           canPaste = canPaste || studyMgr()->CanPaste(so);
          }
        }
      }
@@ -758,10 +783,12 @@ void SalomeApp_Application::updateCommandsStatus()
   if ( a )
     a->setEnabled( activeStudy() );
 
+#ifndef DISABLE_PYCONSOLE
   // Load script menu
   a = action( LoadScriptId );
-  if ( a )
-    a->setEnabled( activeStudy() );
+  if( a )
+    a->setEnabled( pythonConsole() );
+#endif
 
   // Properties menu
   a = action( PropertiesId );
@@ -773,6 +800,16 @@ void SalomeApp_Application::updateCommandsStatus()
   if( a )
     a->setEnabled( activeStudy() );
 
+  // Connect study menu
+  a = action( ConnectId );
+  if( a )
+    a->setEnabled( !activeStudy() && studyMgr()->GetOpenStudies().size() > 0 );
+
+  // Disconnect study menu
+  a = action( DisconnectId );
+  if( a )
+    a->setEnabled( activeStudy() );
+
   // update state of Copy/Paste menu items
   onSelectionChanged();
 }
@@ -859,7 +896,7 @@ void SalomeApp_Application::onDumpStudy( )
   DumpStudyFileDlg fd( desktop() );
   fd.setValidator( new DumpStudyFileValidator( &fd ) );
   fd.setWindowTitle( tr( "TOT_DESK_FILE_DUMP_STUDY" ) );
-  fd.setFilters( aFilters );
+  fd.setNameFilters( aFilters );
   fd.myPublishChk->setChecked( anIsPublish );
   fd.myMultiFileChk->setChecked( anIsMultiFile );
   fd.mySaveGUIChk->setChecked( anIsSaveGUI );
@@ -894,14 +931,14 @@ void SalomeApp_Application::onDumpStudy( )
 void SalomeApp_Application::onLoadScript( )
 {
   SalomeApp_Study* appStudy = dynamic_cast<SalomeApp_Study*>( activeStudy() );
-  if ( !appStudy ) return;
-  _PTR(Study) aStudy = appStudy->studyDS();
-
-  if ( aStudy->GetProperties()->IsLocked() ) {
-    SUIT_MessageBox::warning( desktop(),
-                              QObject::tr("WRN_WARNING"),
-                              QObject::tr("WRN_STUDY_LOCKED") );
-    return;
+  if ( appStudy ) {
+    _PTR(Study) aStudy = appStudy->studyDS();
+    if ( aStudy->GetProperties()->IsLocked() ) {
+      SUIT_MessageBox::warning( desktop(),
+                                QObject::tr("WRN_WARNING"),
+                                QObject::tr("WRN_STUDY_LOCKED") );
+      return;
+    }
   }
 
   QStringList filtersList;
@@ -911,17 +948,6 @@ void SalomeApp_Application::onLoadScript( )
   QString anInitialPath = "";
   if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
     anInitialPath = QDir::currentPath();
-  
-#ifdef WITH_SIMANIO
-  // MPV: if it is SIMAN study, make the initial path as the path to the Siman scripts storage
-  if (myIsSiman) {
-    SALOMEDSClient_StudyManager* aMgr = studyMgr();
-    aMgr->GetSimanStudy()->StudyId();
-    anInitialPath = QString(QDir::separator()) + "tmp" + QDir::separator() + "SimanSalome" + QDir::separator() + 
-      aMgr->GetSimanStudy()->StudyId().c_str() + QDir::separator() +
-      aMgr->GetSimanStudy()->ScenarioId().c_str() + QDir::separator() + aMgr->GetSimanStudy()->UserId().c_str();
-  }
-#endif
 
   QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true );
 
@@ -950,38 +976,16 @@ void SalomeApp_Application::onSaveGUIState()
   updateActions();
 }
 
-/*!Public SLOT. On SIMAN check in operation.*/
-void SalomeApp_Application::onCheckIn()
-{
-#ifdef WITH_SIMANIO
-  setMenuShown(SimanCheckInId, false); // check in may be performed only once
-  setMenuShown(SimanLocalCheckInId, false);
-  SALOMEDSClient_StudyManager* aMgr = studyMgr();
-  aMgr->GetSimanStudy()->CheckIn("");
-#else
-  printf( "****************************************************************\n" );
-  printf( "*    Warning: SALOME is built without SIMAN support.\n" );
-  printf( "****************************************************************\n" );
-#endif
-}
-
-/*!Public SLOT. On SIMAN local check in operation.*/
-void SalomeApp_Application::onLocalCheckIn()
+/*!Public SLOT. Performs some actions when dockable windows are triggered.*/
+void SalomeApp_Application::onDockWindowVisibilityChanged( bool theIsVisible )
 {
-#ifdef WITH_SIMANIO
-  // get the active module
-  CAM_Module* aModule = activeModule();
-  if (!aModule) return; // there is no active module
-  
-  setMenuShown(SimanCheckInId, false); // check in may be performed only once
-  setMenuShown(SimanLocalCheckInId, false);
-  SALOMEDSClient_StudyManager* aMgr = studyMgr();
-  aMgr->GetSimanStudy()->CheckIn(aModule->name().toLatin1().data());
-#else
-  printf( "****************************************************************\n" );
-  printf( "*    Warning: SALOME is built without SIMAN support.\n" );
-  printf( "****************************************************************\n" );
-#endif
+  LightApp_Application::onDockWindowVisibilityChanged( theIsVisible );
+  QAction* send = ::qobject_cast<QAction*>( sender() );
+  if ( !send )
+    return;
+  QString aWinName = send->data().toString();
+  if ( theIsVisible && aWinName == "objectBrowser" )
+    objectBrowserColumnsVisibility();
 }
 
 /*!Gets file filter.
@@ -1045,6 +1049,12 @@ QWidget* SalomeApp_Application::createWindow( const int flag )
       ob->setResizeOnExpandItem(resizeOnExpandItem);
       ob->setProperty( "shortcut", QKeySequence( "Alt+Shift+O" ) );
 
+      for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ )
+      {
+        bool shown = resourceMgr()->booleanValue( "ObjectBrowser", QString( "visibility_column_id_%1" ).arg( i-1 ), true );
+        ob->treeView()->setColumnHidden( i, !shown );
+      }
+
       // temporary commented
       /*
       for ( int i = SalomeApp_DataObject::ValueIdx; i <= SalomeApp_DataObject::RefEntryIdx; i++ )
@@ -1066,7 +1076,8 @@ QWidget* SalomeApp_Application::createWindow( const int flag )
 #ifndef DISABLE_PYCONSOLE
   else if ( flag == WT_PyConsole )
   {
-    PyConsole_Console* pyCons = new PyConsole_EnhConsole( desktop(), new SalomeApp_PyInterp() );
+    PyConsole_Console* pyCons = new PyConsole_EnhConsole( desktop(), getPyInterp() );
+    pyCons->setObjectName( "pythonConsole" );
     pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) );
     pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" ));
     pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true ));
@@ -1086,6 +1097,7 @@ QWidget* SalomeApp_Application::createWindow( const int flag )
                getNoteBook(), SLOT( onVarUpdate( QString ) ) );
     }
     wid = getNoteBook();
+    wid->setObjectName( "noteBook" );
   }
 #endif
   return wid;
@@ -1151,20 +1163,25 @@ void SalomeApp_Application::updateDesktopTitle() {
 
 int SalomeApp_Application::closeChoice( const QString& docName )
 {
-  int answer = SUIT_MessageBox::question( desktop(), tr( "APPCLOSE_CAPTION" ), tr( "APPCLOSE_DESCRIPTION" ).arg( docName ),
-                                          tr ("APPCLOSE_SAVE"), tr ("APPCLOSE_CLOSE"),
-                                         //tr ("APPCLOSE_UNLOAD"), 
-                                         tr ("APPCLOSE_CANCEL"), 0 );
-
-  int res = CloseCancel;
-  if ( answer == 0 )
-    res = CloseSave;
-  else if ( answer == 1 )
-    res = CloseDiscard;
-  // else if ( answer == 2 )
-  //   res = CloseUnload;
+  QStringList buttons;
+  QMap<int, int> choices;
+  int idx = 0;
+  buttons << tr ("APPCLOSE_SAVE");                // Save & Close
+  choices.insert( idx++, CloseSave );             // ...
+  buttons << tr ("APPCLOSE_CLOSE");               // Close w/o saving
+  choices.insert( idx++, CloseDiscard );          // ...
+  if ( myIsCloseFromExit ) {
+    buttons << tr ("APPCLOSE_UNLOAD_SAVE");       // Save & Disconnect
+    choices.insert( idx++, CloseDisconnectSave );     // ...
+    buttons << tr ("APPCLOSE_UNLOAD");            // Disconnect
+    choices.insert( idx++, CloseDisconnect );         // ...
+  }
+  buttons << tr ("APPCLOSE_CANCEL");              // Cancel
+  choices.insert( idx++, CloseCancel );           // ...
 
-  return res;
+  int answer = SUIT_MessageBox::question( desktop(), tr( "APPCLOSE_CAPTION" ),
+                                          tr( "APPCLOSE_DESCRIPTION" ), buttons, 0 );
+  return choices[answer];
 }
 
 bool SalomeApp_Application::closeAction( const int choice, bool& closePermanently )
@@ -1180,14 +1197,19 @@ bool SalomeApp_Application::closeAction( const int choice, bool& closePermanentl
     break;
   case CloseDiscard:
     break;
-  case CloseUnload:
+  case CloseDisconnectSave:
+    if ( activeStudy()->isSaved() )
+      onSaveDoc();
+    else if ( !onSaveAsDoc() )
+      res = false;
+  case CloseDisconnect:
+    closeActiveDoc( false );
     closePermanently = false;
     break;
   case CloseCancel:
   default:
     res = false;
   }
-
   return res;
 }
 
@@ -1266,8 +1288,7 @@ QMap<int, QString> SalomeApp_Application::activateModuleActions() const
 {
   QMap<int, QString> opmap = LightApp_Application::activateModuleActions();
 
-  // rnv commented : implementation of the mono-study in GUI
-  // opmap.insert( LoadStudyId,     tr( "ACTIVATE_MODULE_OP_LOAD" ) );
+  opmap.insert( LoadStudyId,     tr( "ACTIVATE_MODULE_OP_LOAD" ) );
 
   opmap.insert( NewAndScriptId,  tr( "ACTIVATE_MODULE_OP_SCRIPT" ) );
   return opmap;
@@ -1300,8 +1321,14 @@ void SalomeApp_Application::moduleActionSelected( const int id )
 /*!Gets CORBA::ORB_var*/
 CORBA::ORB_var SalomeApp_Application::orb()
 {
-  ORB_INIT& init = *SINGLETON_<ORB_INIT>::Instance();
-  static CORBA::ORB_var _orb = init( qApp->argc(), qApp->argv() );
+  static CORBA::ORB_var _orb;
+
+  if ( CORBA::is_nil( _orb ) ) {
+    Qtx::CmdLineArgs args;
+    ORB_INIT& init = *SINGLETON_<ORB_INIT>::Instance();
+    _orb = init( args.argc(), args.argv() );
+  }
+
   return _orb;
 }
 
@@ -1382,67 +1409,71 @@ void SalomeApp_Application::contextMenuPopup( const QString& type, QMenu* thePop
   // isInvalidRefs will be true, if at least one of selected objects is invalid reference
   bool isInvalidRefs = false;
   SalomeApp_Study* aStudy = dynamic_cast<SalomeApp_Study*>(activeStudy());
-  _PTR(Study) aStudyDS = aStudy->studyDS();
-  _PTR(SObject) anObj;
+  if ( aStudy ) {
+    _PTR(Study) aStudyDS = aStudy->studyDS();
+    _PTR(SObject) anObj;
 
-  for( SALOME_ListIteratorOfListIO it( aList ); it.More() && !isInvalidRefs; it.Next() )
-    if( it.Value()->hasEntry() )
+    for( SALOME_ListIteratorOfListIO it( aList ); it.More() && !isInvalidRefs; it.Next() )
     {
-      _PTR(SObject) aSObject = aStudyDS->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
-      while( aRefObj && aRefObj->ReferencedObject( anObj ) )
-        aRefObj = anObj;
+      if( it.Value()->hasEntry() )
+      {
+        _PTR(SObject) aSObject = aStudyDS->FindObjectID( it.Value()->getEntry() ), aRefObj = aSObject;
+        while( aRefObj && aRefObj->ReferencedObject( anObj ) )
+          aRefObj = anObj;
 
-      if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
-        isInvalidRefs = true;
+        if( aRefObj && aRefObj!=aSObject && QString( aRefObj->GetName().c_str() ).isEmpty() )
+          isInvalidRefs = true;
+      }
     }
 
-  // Add "Delete reference" item to popup
-  if ( isInvalidRefs )
-  {
-    thePopup->addSeparator();
-    thePopup->addAction( tr( "MEN_DELETE_INVALID_REFERENCE" ), this, SLOT( onDeleteInvalidReferences() ) );
-    return;
-  }
-
-  // "Activate module" item should appear only if it's necessary
-  if ( aList.Extent() == 1 ) {
-    aList.Clear();
-    mgr->selectedObjects( aList );
-
-    Handle(SALOME_InteractiveObject) aIObj = aList.First();
+    // Add "Delete reference" item to popup
+    if ( isInvalidRefs )
+    {
+      thePopup->addSeparator();
+      thePopup->addAction( tr( "MEN_DELETE_INVALID_REFERENCE" ), this, SLOT( onDeleteInvalidReferences() ) );
+      return;
+    }
 
-    // add extra popup menu (defined in XML)
-    if ( myExtActions.size() > 0 ) {
-      // Use only first selected object
-      SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
-      if ( study ) {
-        _PTR(Study) stdDS = study->studyDS();
-        if ( stdDS ) {
-          _PTR(SObject) aSO = stdDS->FindObjectID( aIObj->getEntry() );
-          if ( aSO ) {
-            _PTR( GenericAttribute ) anAttr;
-            std::string auid = "AttributeUserID";
-            auid += Kernel_Utils::GetGUID(Kernel_Utils::ObjectdID);
-            if ( aSO->FindAttribute( anAttr, auid ) ) {
-              _PTR(AttributeUserID) aAttrID = anAttr;
-              QString aId = aAttrID->Value().c_str();
-              if ( myExtActions.contains( aId ) ) {
-                thePopup->addAction(myExtActions[aId]);
+    // "Activate module" item should appear only if it's necessary
+    if ( aList.Extent() == 1 ) {
+      aList.Clear();
+      mgr->selectedObjects( aList );
+
+      Handle(SALOME_InteractiveObject) aIObj = aList.First();
+
+      // add extra popup menu (defined in XML)
+      if ( myExtActions.size() > 0 ) {
+        // Use only first selected object
+        SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>( activeStudy() );
+        if ( study ) {
+          _PTR(Study) stdDS = study->studyDS();
+          if ( stdDS ) {
+            _PTR(SObject) aSO = stdDS->FindObjectID( aIObj->getEntry() );
+            if ( aSO ) {
+              _PTR( GenericAttribute ) anAttr;
+              std::string auid = "AttributeUserID";
+              auid += Kernel_Utils::GetGUID(Kernel_Utils::ObjectdID);
+              if ( aSO->FindAttribute( anAttr, auid ) ) {
+                _PTR(AttributeUserID) aAttrID = anAttr;
+                QString aId = aAttrID->Value().c_str();
+                if ( myExtActions.contains( aId ) ) {
+                  thePopup->addAction(myExtActions[aId]);
+                }
               }
             }
           }
         }
       }
-    }
 
-    // check if item is a "GUI state" item (also a first level object)
-    QString entry( aIObj->getEntry() );
-    if ( !entry.startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
-      QString aModuleName( aIObj->getComponentDataType() );
-      QString aModuleTitle = moduleTitle( aModuleName );
-      CAM_Module* currentModule = activeModule();
-      if ( ( !currentModule || currentModule->moduleName() != aModuleTitle ) && !aModuleTitle.isEmpty() )
-        thePopup->addAction( tr( "MEN_OPENWITH" ).arg( aModuleTitle ), this, SLOT( onOpenWith() ) );
+      // check if item is a "GUI state" item (also a first level object)
+      QString entry( aIObj->getEntry() );
+      if ( !entry.startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) {
+        QString aModuleName( aIObj->getComponentDataType() );
+        QString aModuleTitle = moduleTitle( aModuleName );
+        CAM_Module* currentModule = activeModule();
+        if ( ( !currentModule || currentModule->moduleName() != aModuleTitle ) && !aModuleTitle.isEmpty() )
+          thePopup->addAction( tr( "MEN_OPENWITH" ).arg( aModuleTitle ), this, SLOT( onOpenWith() ) );
+      }
     }
   }
 
@@ -2048,45 +2079,28 @@ void SalomeApp_Application::afterCloseDoc()
 /*
   Asks to close existing document.
 */
-bool SalomeApp_Application::checkExistingDoc() {
-  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 );
-    if(answer == 0) {
-      if ( activeStudy()->isSaved() ) {
-       onSaveDoc();
-       closeDoc( false );
-      } else if ( onSaveAsDoc() ) {
-       if( !closeDoc( false ) ) {
-         result = false;
-       }
-      } else {
-       result = false;
-      }        
-    }
-    else if( answer == 1 ) {
-      closeDoc( false );
-    } else if( answer == 2 ) {
-      result = false;
-    }
-  } else {
+bool SalomeApp_Application::checkExistingDoc()
+{
+  bool result = LightApp_Application::checkExistingDoc();
+  if ( result && !activeStudy() ) {
     SALOMEDSClient_StudyManager* aMgr = studyMgr();
-    if( aMgr ) {
+    if ( aMgr ) {
       std::vector<std::string> List = studyMgr()->GetOpenStudies();
       if( List.size() > 0 ) {
-        int answer = SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "QUE_ACTIVEDOC_LOAD" ),
-                                                SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::No );
-        if ( answer == SUIT_MessageBox::Yes ) {
-         onLoadDoc();
-       }
-       result = false;
+        SUIT_MessageBox::critical( desktop(), tr( "WRN_WARNING" ), tr( "ERR_ACTIVEDOC_LOAD" ));
+        result = false;
       }
     }
   }
   return result;
 }
+
+
+#ifndef DISABLE_PYCONSOLE
+
+PyConsole_Interp* SalomeApp_Application::createPyInterp()
+{
+  return new SalomeApp_PyInterp();
+}
+
+#endif // DISABLE_PYCONSOLE