From a1a0f9a6004274ff176b1bcfd77dab7dc2cb0f83 Mon Sep 17 00:00:00 2001 From: akl Date: Fri, 25 Jan 2013 14:19:33 +0000 Subject: [PATCH] IMPLEMENTED. 0021686: EDF 2303 GUI: Move the notebook window below the OB --- doc/salome/gui/images/notebook1.png | Bin 16052 -> 199652 bytes doc/salome/gui/images/studymanagement.png | Bin 8020 -> 258707 bytes doc/salome/gui/input/introduction_to_gui.doc | 1 + .../gui/input/study_management_chapter.doc | 2 - doc/salome/gui/input/using_notebook.doc | 9 +- src/LightApp/LightApp_Application.h | 2 +- src/SalomeApp/CMakeLists.txt | 6 +- src/SalomeApp/Makefile.am | 6 +- src/SalomeApp/SalomeApp_Application.cxx | 3832 +++++++++-------- src/SalomeApp/SalomeApp_Application.h | 31 +- ...NoteBookDlg.cxx => SalomeApp_NoteBook.cxx} | 2079 ++++----- ...App_NoteBookDlg.h => SalomeApp_NoteBook.h} | 356 +- src/SalomeApp/SalomeApp_Study.cxx | 10 + src/SalomeApp/SalomeApp_Study.h | 4 + src/SalomeApp/resources/SalomeApp_msg_en.ts | 8 +- src/SalomeApp/resources/SalomeApp_msg_fr.ts | 2 +- 16 files changed, 3173 insertions(+), 3175 deletions(-) rename src/SalomeApp/{SalomeApp_NoteBookDlg.cxx => SalomeApp_NoteBook.cxx} (77%) rename src/SalomeApp/{SalomeApp_NoteBookDlg.h => SalomeApp_NoteBook.h} (78%) diff --git a/doc/salome/gui/images/notebook1.png b/doc/salome/gui/images/notebook1.png index a984085ccb940b9d800093b5d0c9411e5cf273e7..80b7d1480f8d9399e0659bd10cd13861c3277859 100644 GIT binary patch literal 199652 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 Qr=8coZ^wi0{nJnXKUww12mk;8 literal 8020 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 diff --git a/doc/salome/gui/input/introduction_to_gui.doc b/doc/salome/gui/input/introduction_to_gui.doc index ce2241271..449baa0ce 100644 --- a/doc/salome/gui/input/introduction_to_gui.doc +++ b/doc/salome/gui/input/introduction_to_gui.doc @@ -38,6 +38,7 @@ the appearance of Salome, etc. Among key parts of study window are: management of objects created or imported into the SALOME application, also providing search possibilities with the \subpage using_find_tool_page "Find Tool", and +
  • \subpage using_notebook "Notebook" - allows to predefine study variables.
  • \subpage python_interpreter_page "Python interpreter", used for direct input of python commands and dumping studies into Python scripts.
  • diff --git a/doc/salome/gui/input/study_management_chapter.doc b/doc/salome/gui/input/study_management_chapter.doc index b06d86873..c7600be5b 100644 --- a/doc/salome/gui/input/study_management_chapter.doc +++ b/doc/salome/gui/input/study_management_chapter.doc @@ -78,8 +78,6 @@ saved in the Python file. To confirm your choice click \b Save. -\subpage using_notebook "Notebook" - allows to predefine study variables. - Load Script - allows to load a saved Python Script. \image html loadscript.png diff --git a/doc/salome/gui/input/using_notebook.doc b/doc/salome/gui/input/using_notebook.doc index 009f05f67..dd92dd889 100644 --- a/doc/salome/gui/input/using_notebook.doc +++ b/doc/salome/gui/input/using_notebook.doc @@ -5,11 +5,12 @@ SALOME NoteBook allows to predefine numerical and boolean parameters (variables). -To open the NoteBook choose File > Notebook, the following -dialog box will appear: +By default, the NoteBook takes place in a left tabbed widget. \image html notebook1.png +To hide/show this tab choose View > Windows > Notebook. + Here you can add new variables, remove and rename existing variables and change their values. @@ -17,7 +18,7 @@ If you have modified some variables, which are already used in the current study, you should click Update Study button to apply your changes to the study. -\note The dialog will not be validated until at least one of variables in +\note The study can not be updated until at least one of variables in the table has an invalid name or value (marked by red color). The names of variables should be unique and their values should be numerical (integer or double) or boolean ("True" / "False"). @@ -36,4 +37,4 @@ notebook.set("Flag", True) Later these values can be used as parameters for any operations in various components. -*/ \ No newline at end of file +*/ diff --git a/src/LightApp/LightApp_Application.h b/src/LightApp/LightApp_Application.h index 009432d29..97ee9ab3a 100644 --- a/src/LightApp/LightApp_Application.h +++ b/src/LightApp/LightApp_Application.h @@ -266,7 +266,7 @@ protected: void updatePreference( const QString&, const QString&, const QString& ); QString defaultModule() const; - void currentWindows( QMap& ) const; + virtual void currentWindows( QMap& ) const; void currentViewManagers( QStringList& ) const; void moduleIconNames( QMap& ) const; diff --git a/src/SalomeApp/CMakeLists.txt b/src/SalomeApp/CMakeLists.txt index 0ade4fd05..7f9888e46 100755 --- a/src/SalomeApp/CMakeLists.txt +++ b/src/SalomeApp/CMakeLists.txt @@ -99,7 +99,7 @@ SET(GUI_HEADERS SalomeApp_ListView.h SalomeApp_CheckFileDlg.h SalomeApp_ExitDlg.h - SalomeApp_NoteBookDlg.h + SalomeApp_NoteBook.h SalomeApp_DoubleSpinBox.h SalomeApp_IntSpinBox.h ) @@ -123,7 +123,7 @@ SET(SalomeApp_SOURCES SalomeApp_CheckFileDlg.cxx SalomeApp_VisualState.cxx SalomeApp_ExitDlg.cxx - SalomeApp_NoteBookDlg.cxx + SalomeApp_NoteBook.cxx SalomeApp_DoubleSpinBox.cxx SalomeApp_IntSpinBox.cxx SalomeApp_Engine_i.cxx @@ -158,7 +158,7 @@ SET(COMMON_HEADERS_H SalomeApp_CheckFileDlg.h SalomeApp_VisualState.h SalomeApp_ExitDlg.h - SalomeApp_NoteBookDlg.h + SalomeApp_NoteBook.h SalomeApp_DoubleSpinBox.h SalomeApp_IntSpinBox.h SalomeApp_Engine_i.h diff --git a/src/SalomeApp/Makefile.am b/src/SalomeApp/Makefile.am index 6321634e6..e7aad71dd 100755 --- a/src/SalomeApp/Makefile.am +++ b/src/SalomeApp/Makefile.am @@ -54,7 +54,7 @@ salomeinclude_HEADERS = \ SalomeApp_CheckFileDlg.h \ SalomeApp_VisualState.h \ SalomeApp_ExitDlg.h \ - SalomeApp_NoteBookDlg.h \ + SalomeApp_NoteBook.h \ SalomeApp_DoubleSpinBox.h \ SalomeApp_IntSpinBox.h \ SalomeApp_Engine_i.h @@ -77,7 +77,7 @@ dist_libSalomeApp_la_SOURCES = \ SalomeApp_CheckFileDlg.cxx \ SalomeApp_VisualState.cxx \ SalomeApp_ExitDlg.cxx \ - SalomeApp_NoteBookDlg.cxx \ + SalomeApp_NoteBook.cxx \ SalomeApp_DoubleSpinBox.cxx \ SalomeApp_IntSpinBox.cxx \ SalomeApp_Engine_i.cxx @@ -92,7 +92,7 @@ MOC_FILES = \ SalomeApp_ListView_moc.cxx \ SalomeApp_CheckFileDlg_moc.cxx \ SalomeApp_ExitDlg_moc.cxx \ - SalomeApp_NoteBookDlg_moc.cxx \ + SalomeApp_NoteBook_moc.cxx \ SalomeApp_DoubleSpinBox_moc.cxx \ SalomeApp_IntSpinBox_moc.cxx diff --git a/src/SalomeApp/SalomeApp_Application.cxx b/src/SalomeApp/SalomeApp_Application.cxx index d446757b2..be562aa83 100644 --- a/src/SalomeApp/SalomeApp_Application.cxx +++ b/src/SalomeApp/SalomeApp_Application.cxx @@ -1,1825 +1,2007 @@ -// Copyright (C) 2007-2012 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 -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com -// - -// File: SalomeApp_Application.cxx -// Created: 10/22/2004 3:23:45 PM -// Author: Sergey LITONIN - -#ifdef WNT -// E.A. : On windows with python 2.6, there is a conflict -// E.A. : between pymath.h and Standard_math.h which define -// E.A. : some same symbols : acosh, asinh, ... -#include -#include -#endif - -#include "SalomeApp_PyInterp.h" // WARNING! This include must be the first! -#include "SalomeApp_Application.h" -#include "SalomeApp_Study.h" -#include "SalomeApp_DataModel.h" -#include "SalomeApp_DataObject.h" -#include "SalomeApp_VisualState.h" -#include "SalomeApp_StudyPropertiesDlg.h" -#include "SalomeApp_LoadStudiesDlg.h" -#include "SalomeApp_NoteBookDlg.h" - -#include "SalomeApp_ExitDlg.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -// temporary commented -//#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - - -#include -#include - -#include - -/*!Internal class that updates object browser item properties */ -// temporary commented -/*class SalomeApp_Updater : public OB_Updater -{ -public: - SalomeApp_Updater() : OB_Updater(){}; - virtual ~SalomeApp_Updater(){}; - virtual void update( SUIT_DataObject* theObj, OB_ListItem* theItem ); -}; - -void SalomeApp_Updater::update( SUIT_DataObject* theObj, OB_ListItem* theItem ) -{ - if( !theObj || !theItem ) - return; - - SalomeApp_DataObject* SAObj = dynamic_cast( theObj ); - if( !SAObj ) - return; - - _PTR(SObject) SObj = SAObj->object(); - if( !SObj ) - return; - _PTR( GenericAttribute ) anAttr; - - // Selectable - if ( SObj->FindAttribute( anAttr, "AttributeSelectable" ) ) - { - _PTR(AttributeSelectable) aAttrSel = anAttr; - theItem->setSelectable( aAttrSel->IsSelectable() ); - } - // Expandable - if ( SObj->FindAttribute(anAttr, "AttributeExpandable") ) - { - _PTR(AttributeExpandable) aAttrExpand = anAttr; - theItem->setExpandable( aAttrExpand->IsExpandable() ); - } - // Opened - //this attribute is not supported in the version of SALOME 3.x - //if ( SObj->FindAttribute(anAttr, "AttributeOpened") ) - //{ - // _PTR(AttributeOpened) aAttrOpen = anAttr; - // theItem->setOpen( aAttrOpen->IsOpened() ); - //} -}*/ - -/*!Create new instance of SalomeApp_Application.*/ -extern "C" SALOMEAPP_EXPORT SUIT_Application* createApplication() -{ - return new SalomeApp_Application(); -} - -/*!Constructor.*/ -SalomeApp_Application::SalomeApp_Application() - : LightApp_Application() -{ - connect( desktop(), SIGNAL( windowActivated( SUIT_ViewWindow* ) ), - this, SLOT( onWindowActivated( SUIT_ViewWindow* ) ), Qt::UniqueConnection ); - - setNoteBook(0); -} - -/*!Destructor. - *\li Destroy event filter. - */ -SalomeApp_Application::~SalomeApp_Application() -{ - // Do not destroy. It's a singleton ! - //SALOME_EventFilter::Destroy(); -} - -/*!Start application.*/ -void SalomeApp_Application::start() -{ - LightApp_Application::start(); - - SALOME_EventFilter::Init(); - - static bool isFirst = true; - if ( isFirst ) { - isFirst = false; - - QString hdffile; - QStringList pyfiles; - - for (int i = 1; i < qApp->argc(); i++) { - QRegExp rxs ("--study-hdf=(.+)"); - if ( rxs.indexIn( QString(qApp->argv()[i]) ) >= 0 && rxs.capturedTexts().count() > 1 ) { - QString file = rxs.capturedTexts()[1]; - QFileInfo fi ( file ); - QString extension = fi.suffix().toLower(); - if ( extension == "hdf" && fi.exists() ) - hdffile = fi.absoluteFilePath(); - } - else { - QRegExp rxp ("--pyscript=(.+)"); - if ( rxp.indexIn( QString(qApp->argv()[i]) ) >= 0 && rxp.capturedTexts().count() > 1 ) { - QStringList files = rxp.capturedTexts()[1].split(",",QString::SkipEmptyParts); - pyfiles += files; - } - } - } - - if ( !hdffile.isEmpty() ) // open hdf file given as parameter - onOpenDoc( hdffile ); - else if ( pyfiles.count() > 0 ) // create new study - onNewDoc(); - - // import/execute python scripts - if ( pyfiles.count() > 0 && activeStudy() ) { - SalomeApp_Study* appStudy = dynamic_cast( activeStudy() ); - PyConsole_Console* pyConsole = pythonConsole(); - if ( appStudy && pyConsole ) { - _PTR(Study) aStudy = appStudy->studyDS(); - if ( !aStudy->GetProperties()->IsLocked() ) { - for (uint j = 0; j < pyfiles.count(); j++ ) { - QFileInfo fi ( pyfiles[j] ); - QFileInfo fipy ( pyfiles[j] + ".py" ); - QString command = QString( "execfile(r\"%1\")" ); - if ( fi.isAbsolute() ) { - if ( fi.exists() ) - pyConsole->exec( command.arg( fi.absoluteFilePath() ) ); - else if ( fipy.exists() ) - pyConsole->exec( command.arg( fipy.absoluteFilePath() ) ); - else - qDebug() << "Can't execute file" << pyfiles[j]; - } - else { - bool found = false; - QStringList dirs; - dirs << QDir::currentPath(); - if ( ::getenv( "PYTHONPATH" ) ) - dirs += QString( ::getenv( "PYTHONPATH" ) ).split( QRegExp( "[:|;]" ) ); - foreach( QString dir, dirs ) { - qDebug() << "try" << QFileInfo( dir, pyfiles[j] ).absoluteFilePath(); - qDebug() << "try" << QFileInfo( dir, pyfiles[j] + ".py" ).absoluteFilePath(); - if ( QFileInfo( dir, pyfiles[j] ).exists() ) { - pyConsole->exec( command.arg( QFileInfo( dir, pyfiles[j] ).absoluteFilePath() ) ); - found = true; - break; - } - else if ( QFileInfo( dir, pyfiles[j] + ".py" ).exists() ) { - pyConsole->exec( command.arg( QFileInfo( dir, pyfiles[j] + ".py" ).absoluteFilePath() ) ); - found = true; - break; - } - } - if ( !found ) { - qDebug() << "Can't execute file" << pyfiles[j]; - } - } - } - } - } - } - } -} - -/*!Create actions:*/ -void SalomeApp_Application::createActions() -{ - LightApp_Application::createActions(); - - SUIT_Desktop* desk = desktop(); - - //! Save GUI state - // "Save GUI State" command is moved to VISU module - // createAction( SaveGUIStateId, tr( "TOT_DESK_FILE_SAVE_GUI_STATE" ), QIcon(), - // tr( "MEN_DESK_FILE_SAVE_GUI_STATE" ), tr( "PRP_DESK_FILE_SAVE_GUI_STATE" ), - // 0, desk, false, this, SLOT( onSaveGUIState() ) ); - - //! Dump study - createAction( DumpStudyId, tr( "TOT_DESK_FILE_DUMP_STUDY" ), QIcon(), - tr( "MEN_DESK_FILE_DUMP_STUDY" ), tr( "PRP_DESK_FILE_DUMP_STUDY" ), - Qt::CTRL+Qt::Key_D, desk, false, this, SLOT( onDumpStudy() ) ); - - //! NoteBook - createAction(NoteBookId, tr( "TOT_DESK_FILE_NOTEBOOK" ), QIcon(), - tr( "MEN_DESK_FILE_NOTEBOOK" ), tr( "PRP_DESK_FILE_NOTEBOOK" ), - Qt::CTRL+Qt::Key_K, desk, false, this, SLOT(onNoteBook())); - - //! Load script - createAction( LoadScriptId, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), QIcon(), - tr( "MEN_DESK_FILE_LOAD_SCRIPT" ), tr( "PRP_DESK_FILE_LOAD_SCRIPT" ), - Qt::CTRL+Qt::Key_T, desk, false, this, SLOT( onLoadScript() ) ); - - //! Properties - createAction( PropertiesId, tr( "TOT_DESK_PROPERTIES" ), QIcon(), - tr( "MEN_DESK_PROPERTIES" ), tr( "PRP_DESK_PROPERTIES" ), - Qt::CTRL+Qt::Key_P, desk, false, this, SLOT( onProperties() ) ); - - //! Catalog Generator - createAction( CatalogGenId, tr( "TOT_DESK_CATALOG_GENERATOR" ), QIcon(), - tr( "MEN_DESK_CATALOG_GENERATOR" ), tr( "PRP_DESK_CATALOG_GENERATOR" ), - Qt::ALT+Qt::SHIFT+Qt::Key_G, desk, false, this, SLOT( onCatalogGen() ) ); - - //! Registry Display - createAction( RegDisplayId, tr( "TOT_DESK_REGISTRY_DISPLAY" ), QIcon(), - tr( "MEN_DESK_REGISTRY_DISPLAY" ), tr( "PRP_DESK_REGISTRY_DISPLAY" ), - /*Qt::SHIFT+Qt::Key_D*/0, desk, false, this, SLOT( onRegDisplay() ) ); - - //SRN: BugID IPAL9021, add an action "Load" - 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() ) ); - //SRN: BugID IPAL9021: End - - - int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1 ); - - // "Save GUI State" command is renamed to "Save VISU State" and - // creation of menu item is moved to VISU - // createMenu( SaveGUIStateId, fileMenu, 10, -1 ); - - createMenu( FileLoadId, fileMenu, 0 ); //SRN: BugID IPAL9021, add a menu item "Load" - - createMenu( DumpStudyId, fileMenu, 10, -1 ); - createMenu( NoteBookId, 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 ); - createMenu( separator(), fileMenu, -1, 10, -1 ); - - int toolsMenu = createMenu( tr( "MEN_DESK_TOOLS" ), -1, MenuToolsId, 50 ); - createMenu( CatalogGenId, toolsMenu, 10, -1 ); - createMenu( RegDisplayId, toolsMenu, 10, -1 ); - createMenu( separator(), toolsMenu, -1, 15, -1 ); - - createExtraActions(); - - // import Python module that manages SALOME plugins - PyGILState_STATE gstate = PyGILState_Ensure(); - PyObject* pluginsmanager=PyImport_ImportModule((char*)"salome_pluginsmanager"); - PyObject* 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()); - if(res==NULL) - PyErr_Print(); - Py_XDECREF(res); - PyGILState_Release(gstate); - // end of SALOME plugins loading - -} - - -/*!Set desktop:*/ -void SalomeApp_Application::setDesktop( SUIT_Desktop* desk ) -{ - LightApp_Application::setDesktop( desk ); - - if ( desk ) { - connect( desk, SIGNAL( windowActivated( SUIT_ViewWindow* ) ), - this, SLOT( onWindowActivated( SUIT_ViewWindow* ) ), Qt::UniqueConnection ); - } -} - -/*! - \brief Close application. -*/ -void SalomeApp_Application::onExit() -{ - bool killServers = false; - bool result = true; - - if ( exitConfirmation() ) { - SalomeApp_ExitDlg dlg( desktop() ); - result = dlg.exec() == QDialog::Accepted; - killServers = dlg.isServersShutdown(); - } - - if ( result ) - SUIT_Session::session()->closeSession( SUIT_Session::ASK, killServers ); -} - -/*!SLOT. Load document.*/ -void SalomeApp_Application::onLoadDoc() -{ - QString studyName; - - std::vector List = studyMgr()->GetOpenStudies(); - - SUIT_Session* aSession = SUIT_Session::session(); - QList aAppList = aSession->applications(); - - QStringList unloadedStudies; - - for ( unsigned int ind = 0; ind < List.size(); ind++ ) { - studyName = List[ind].c_str(); - // Add to list only unloaded studies - bool isAlreadyOpen = false; - QListIterator it( aAppList ); - while ( it.hasNext() && !isAlreadyOpen ) { - SUIT_Application* aApp = it.next(); - if( !aApp || !aApp->activeStudy() ) - continue; - if ( aApp->activeStudy()->studyName() == studyName ) - isAlreadyOpen = true; - } - - if ( !isAlreadyOpen ) - unloadedStudies << studyName; - } - - studyName = SalomeApp_LoadStudiesDlg::selectStudy( desktop(), unloadedStudies ); - if ( studyName.isEmpty() ) - return; - -#ifndef WIN32 - // this code replaces marker of windows drive and path become invalid therefore - // defines placed there - studyName.replace( QRegExp(":"), "/" ); -#endif - - if ( onLoadDoc( studyName ) ) { - updateWindows(); - updateViewManagers(); - updateObjectBrowser( true ); - } -} - -/*!SLOT. Create new study and load script*/ -void SalomeApp_Application::onNewWithScript() -{ - QStringList filtersList; - filtersList.append(tr("PYTHON_FILES_FILTER")); - filtersList.append(tr("ALL_FILES_FILTER")); - - QString anInitialPath = ""; - if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() ) - anInitialPath = QDir::currentPath(); - - QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true ); - - if ( !aFile.isEmpty() ) - { - onNewDoc(); - - QString command = QString("execfile(r\"%1\")").arg(aFile); - - PyConsole_Console* pyConsole = pythonConsole(); - - if ( pyConsole ) - pyConsole->exec( command ); - } -} - - -/*!SLOT. Load document with \a aName.*/ -bool SalomeApp_Application::onLoadDoc( const QString& aName ) -{ - bool res = true; - if ( !activeStudy() ) { - // if no study - load in current desktop - res = useStudy( aName ); - } - else { - // if study exists - load in new desktop. Check: is the same file is loaded? - SUIT_Session* aSession = SUIT_Session::session(); - QList aAppList = aSession->applications(); - bool isAlreadyOpen = false; - SalomeApp_Application* aApp = 0; - for ( QList::iterator it = aAppList.begin(); - it != aAppList.end() && !isAlreadyOpen; ++it ) { - aApp = dynamic_cast( *it ); - if ( aApp && aApp->activeStudy()->studyName() == aName ) - isAlreadyOpen = true; - } - if ( !isAlreadyOpen ) { - aApp = dynamic_cast( startApplication( 0, 0 ) ); - if ( aApp ) - res = aApp->useStudy( aName ); - } - else { - aApp->desktop()->activateWindow(); - } - } - - return res; -} - -/*!SLOT. Copy objects to study maneger from selection maneger..*/ -void SalomeApp_Application::onCopy() -{ - SALOME_ListIO list; - LightApp_SelectionMgr* mgr = selectionMgr(); - mgr->selectedObjects(list); - - SalomeApp_Study* study = dynamic_cast(activeStudy()); - if(study == NULL) return; - - _PTR(Study) stdDS = study->studyDS(); - if(!stdDS) return; - - SALOME_ListIteratorOfListIO it( list ); - if(it.More()) - { - _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry()); - try { - studyMgr()->Copy(so); - onSelectionChanged(); - } - catch(...) { - } - } -} - -/*!SLOT. Paste objects to study maneger from selection manager.*/ -void SalomeApp_Application::onPaste() -{ - SALOME_ListIO list; - LightApp_SelectionMgr* mgr = selectionMgr(); - mgr->selectedObjects(list); - - SalomeApp_Study* study = dynamic_cast(activeStudy()); - if(study == NULL) return; - - _PTR(Study) stdDS = study->studyDS(); - if(!stdDS) return; - - if ( stdDS->GetProperties()->IsLocked() ) { - SUIT_MessageBox::warning( desktop(), - QObject::tr("WRN_WARNING"), - QObject::tr("WRN_STUDY_LOCKED") ); - return; - } - - SALOME_ListIteratorOfListIO it( list ); - if(it.More()) - { - _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry()); - try { - studyMgr()->Paste(so); - updateObjectBrowser( true ); - updateActions(); //SRN: BugID IPAL9377, case 3 - } - catch(...) { - } - } -} - -/*!Check the application on closing. - * \retval true if possible, else false - */ -bool SalomeApp_Application::isPossibleToClose( bool& closePermanently ) -{ - return LightApp_Application::isPossibleToClose( closePermanently ); -} - -/*! Check if the study is locked */ -void SalomeApp_Application::onCloseDoc( bool ask ) -{ - SalomeApp_Study* study = dynamic_cast(activeStudy()); - - if (study != NULL) { - _PTR(Study) stdDS = study->studyDS(); - if(stdDS && stdDS->IsStudyLocked()) { - if ( SUIT_MessageBox::question( desktop(), - QObject::tr( "WRN_WARNING" ), - QObject::tr( "CLOSE_LOCKED_STUDY" ), - SUIT_MessageBox::Yes | SUIT_MessageBox::No, - SUIT_MessageBox::No) == SUIT_MessageBox::No ) return; - - } - } - - if(myNoteBook && myNoteBook->isVisible()) - myNoteBook->hide(); - - LightApp_Application::onCloseDoc( ask ); -} - -/*!Sets enable or disable some actions on selection changed.*/ -void SalomeApp_Application::onSelectionChanged() -{ - SALOME_ListIO list; - LightApp_SelectionMgr* mgr = selectionMgr(); - mgr->selectedObjects(list); - - bool canCopy = false; - bool canPaste = false; - - SalomeApp_Study* study = dynamic_cast(activeStudy()); - if (study != NULL) { - _PTR(Study) stdDS = study->studyDS(); - - if (stdDS) { - SALOME_ListIteratorOfListIO it ( list ); - - if (it.More() && list.Extent() == 1) { - _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry()); - - if ( so ) { - canCopy = studyMgr()->CanCopy(so); - canPaste = studyMgr()->CanPaste(so); - } - } - } - } - - action(EditCopyId)->setEnabled(canCopy); - action(EditPasteId)->setEnabled(canPaste); -} - -/*!Delete references.*/ -void SalomeApp_Application::onDeleteInvalidReferences() -{ - SALOME_ListIO aList; - LightApp_SelectionMgr* mgr = selectionMgr(); - mgr->selectedObjects( aList, QString(), false ); - - if( aList.IsEmpty() ) - return; - - SalomeApp_Study* aStudy = dynamic_cast(activeStudy()); - _PTR(Study) aStudyDS = aStudy->studyDS(); - _PTR(StudyBuilder) aStudyBuilder = aStudyDS->NewBuilder(); - _PTR(SObject) anObj; - - for( SALOME_ListIteratorOfListIO it( aList ); it.More(); it.Next() ) - 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() ) - aStudyBuilder->RemoveReference( aSObject ); - } - updateObjectBrowser(); -} - -/*!Private SLOT. */ -void SalomeApp_Application::onOpenWith() -{ - QApplication::setOverrideCursor( Qt::WaitCursor ); - SALOME_ListIO aList; - LightApp_SelectionMgr* mgr = selectionMgr(); - mgr->selectedObjects(aList); - if (aList.Extent() != 1) - { - QApplication::restoreOverrideCursor(); - return; - } - Handle(SALOME_InteractiveObject) aIObj = aList.First(); - QString aModuleName(aIObj->getComponentDataType()); - QString aModuleTitle = moduleTitle(aModuleName); - activateModule(aModuleTitle); - QApplication::restoreOverrideCursor(); -} - -/*! - Creates new study -*/ -SUIT_Study* SalomeApp_Application::createNewStudy() -{ - SalomeApp_Study* aStudy = new SalomeApp_Study( this ); - - // Set up processing of major study-related events - connect( aStudy, SIGNAL( created( SUIT_Study* ) ), this, SLOT( onStudyCreated( SUIT_Study* ) ) ); - connect( aStudy, SIGNAL( opened ( SUIT_Study* ) ), this, SLOT( onStudyOpened ( SUIT_Study* ) ) ); - connect( aStudy, SIGNAL( saved ( SUIT_Study* ) ), this, SLOT( onStudySaved ( SUIT_Study* ) ) ); - connect( aStudy, SIGNAL( closed ( SUIT_Study* ) ), this, SLOT( onStudyClosed ( SUIT_Study* ) ) ); - - return aStudy; -} - -/*! - Enable/Disable menu items and toolbar buttons. Rebuild menu -*/ -void SalomeApp_Application::updateCommandsStatus() -{ - LightApp_Application::updateCommandsStatus(); - - // Dump study menu - QAction* a = action( DumpStudyId ); - if ( a ) - a->setEnabled( activeStudy() ); - - // Note Book - a = action(NoteBookId); - if( a ) - a->setEnabled( activeStudy() ); - - // Load script menu - a = action( LoadScriptId ); - if ( a ) - a->setEnabled( activeStudy() ); - - // Properties menu - a = action( PropertiesId ); - if( a ) - a->setEnabled( activeStudy() ); - - // Save GUI state menu - a = action( SaveGUIStateId ); - if( a ) - a->setEnabled( activeStudy() ); - - // update state of Copy/Paste menu items - onSelectionChanged(); -} - -/*! - \class DumpStudyFileDlg - Private class used in Dump Study operation. Consists 2 check boxes: - "Publish in study" and "Save GUI parameters" -*/ -class DumpStudyFileDlg : public SUIT_FileDlg -{ -public: - DumpStudyFileDlg( QWidget* parent ) : SUIT_FileDlg( parent, false, true, true ) - { - QGridLayout* grid = ::qobject_cast( layout() ); - if ( grid ) - { - QWidget *hB = new QWidget( this ); - myPublishChk = new QCheckBox( tr("PUBLISH_IN_STUDY") ); - myMultiFileChk = new QCheckBox( tr("MULTI_FILE_DUMP") ); - mySaveGUIChk = new QCheckBox( tr("SAVE_GUI_STATE") ); - - QHBoxLayout *layout = new QHBoxLayout; - layout->addWidget(myPublishChk); - layout->addWidget(myMultiFileChk); - layout->addWidget(mySaveGUIChk); - hB->setLayout(layout); - - QPushButton* pb = new QPushButton(this); - - int row = grid->rowCount(); - grid->addWidget( new QLabel("", this), row, 0 ); - grid->addWidget( hB, row, 1, 1, 3 ); - grid->addWidget( pb, row, 5 ); - - pb->hide(); - } - } - QCheckBox* myPublishChk; - QCheckBox* myMultiFileChk; - QCheckBox* mySaveGUIChk; -}; - -class DumpStudyFileValidator : public SUIT_FileValidator -{ - public: - DumpStudyFileValidator( QWidget* parent) : SUIT_FileValidator ( parent ) {}; - virtual ~DumpStudyFileValidator() {}; - virtual bool canSave( const QString& file, bool permissions ); -}; - -bool DumpStudyFileValidator::canSave(const QString& file, bool permissions) -{ - QFileInfo fi( file ); - if ( !QRegExp( "[A-Za-z_][A-Za-z0-9_]*" ).exactMatch( fi.completeBaseName() ) ) { - SUIT_MessageBox::critical( parent(), - QObject::tr("WRN_WARNING"), - QObject::tr("WRN_FILE_NAME_BAD") ); - return false; - } - return SUIT_FileValidator::canSave( file, permissions); -} - -/*!Private SLOT. On dump study.*/ -void SalomeApp_Application::onDumpStudy( ) -{ - SalomeApp_Study* appStudy = dynamic_cast( activeStudy() ); - if ( !appStudy ) return; - _PTR(Study) aStudy = appStudy->studyDS(); - - QStringList aFilters; - aFilters.append( tr( "PYTHON_FILES_FILTER" ) ); - - bool anIsPublish = true; - bool anIsMultiFile = false; - bool anIsSaveGUI = true; - - if ( SUIT_ResourceMgr* aResourceMgr = resourceMgr() ) { - anIsPublish = aResourceMgr->booleanValue( "Study", "pydump_publish", anIsPublish ); - anIsMultiFile = aResourceMgr->booleanValue( "Study", "multi_file_dump", anIsMultiFile ); - anIsSaveGUI = aResourceMgr->booleanValue( "Study", "pydump_save_gui", anIsSaveGUI ); - } - - DumpStudyFileDlg fd( desktop() ); - fd.setValidator( new DumpStudyFileValidator( &fd ) ); - fd.setWindowTitle( tr( "TOT_DESK_FILE_DUMP_STUDY" ) ); - fd.setFilters( aFilters ); - fd.myPublishChk->setChecked( anIsPublish ); - fd.myMultiFileChk->setChecked( anIsMultiFile ); - fd.mySaveGUIChk->setChecked( anIsSaveGUI ); - if ( fd.exec() == QDialog::Accepted ) - { - QString aFileName = fd.selectedFile(); - - bool toPublish = fd.myPublishChk->isChecked(); - bool isMultiFile = fd.myMultiFileChk->isChecked(); - bool toSaveGUI = fd.mySaveGUIChk->isChecked(); - - if ( !aFileName.isEmpty() ) { - QFileInfo aFileInfo(aFileName); - if( aFileInfo.isDir() ) // IPAL19257 - return; - - // Issue 21377 - dump study implementation moved to SalomeApp_Study class - bool res = appStudy->dump( aFileName, toPublish, isMultiFile, toSaveGUI ); - - if ( !res ) - SUIT_MessageBox::warning( desktop(), - QObject::tr("WRN_WARNING"), - tr("WRN_DUMP_STUDY_FAILED") ); - } - } -} - -/*!Private SLOT. On NoteBook*/ -void SalomeApp_Application::onNoteBook() -{ - SalomeApp_Study* appStudy = dynamic_cast( activeStudy() ); - if ( appStudy ) { - _PTR(Study) aStudy = appStudy->studyDS(); - if(!myNoteBook) { - myNoteBook = new SalomeApp_NoteBookDlg(desktop(),aStudy); - } - else if(!myNoteBook->isVisible()){ - myNoteBook->Init(aStudy); - myNoteBook->adjustSize(); - myNoteBook->move((int)(desktop()->x() + desktop()->width()/2 - myNoteBook->frameGeometry().width()/2), - (int)(desktop()->y() + desktop()->height()/2 - myNoteBook->frameGeometry().height()/2)); - } - myNoteBook->show(); - } -} - -/*!Private SLOT. On load script.*/ -void SalomeApp_Application::onLoadScript( ) -{ - SalomeApp_Study* appStudy = dynamic_cast( 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; - } - - QStringList filtersList; - filtersList.append(tr("PYTHON_FILES_FILTER")); - filtersList.append(tr("ALL_FILES_FILTER")); - - QString anInitialPath = ""; - if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() ) - anInitialPath = QDir::currentPath(); - - QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true ); - - if ( !aFile.isEmpty() ) - { - QString command = QString("execfile(r\"%1\")").arg(aFile); - - PyConsole_Console* pyConsole = pythonConsole(); - - if ( pyConsole ) - pyConsole->exec( command ); - } -} - -/*!Private SLOT. On save GUI state.*/ -void SalomeApp_Application::onSaveGUIState() -{ - SalomeApp_Study* study = dynamic_cast( activeStudy() ); - if ( study ) { - SalomeApp_VisualState( this ).storeState(); - updateSavePointDataObjects( study ); - updateObjectBrowser(); - } - updateActions(); -} - -/*!Gets file filter. - *\retval QString "(*.hdf)" - */ -QString SalomeApp_Application::getFileFilter() const -{ - return "(*.hdf)"; -} - -/*!Create window.*/ -QWidget* SalomeApp_Application::createWindow( const int flag ) -{ - QWidget* wid = 0; - if ( flag != WT_PyConsole ) wid = LightApp_Application::createWindow(flag); - - SUIT_ResourceMgr* resMgr = resourceMgr(); - - if ( flag == WT_ObjectBrowser ) - { - SUIT_DataBrowser* ob = qobject_cast( wid ); - if ( ob ) { - // temporary commented - //ob->setUpdater( new SalomeApp_Updater() ); - -#ifdef WITH_SALOMEDS_OBSERVER - //do not activate the automatic update of Qt tree through signal/slot - ob->setAutoUpdate(false); - //activate update of modified objects only - ob->setUpdateModified(true); -#endif - - connect( ob, SIGNAL( doubleClicked( SUIT_DataObject* ) ), this, SLOT( onDblClick( SUIT_DataObject* ) ) ); - - QString - ValueCol = QObject::tr( "VALUE_COLUMN" ), - IORCol = QObject::tr( "IOR_COLUMN" ), - RefCol = QObject::tr( "REFENTRY_COLUMN" ), - EntryCol = QObject::tr( "ENTRY_COLUMN" ); - - SUIT_AbstractModel* treeModel = dynamic_cast( ob->model() ); - treeModel->registerColumn( 0, EntryCol, SalomeApp_DataObject::EntryId ); - treeModel->registerColumn( 0, ValueCol, SalomeApp_DataObject::ValueId ); - treeModel->registerColumn( 0, IORCol, SalomeApp_DataObject::IORId ); - treeModel->registerColumn( 0, RefCol, SalomeApp_DataObject::RefEntryId ); - treeModel->setAppropriate( EntryCol, Qtx::Toggled ); - treeModel->setAppropriate( ValueCol, Qtx::Toggled ); - treeModel->setAppropriate( IORCol, Qtx::Toggled ); - treeModel->setAppropriate( RefCol, Qtx::Toggled ); - - bool autoSize = resMgr->booleanValue( "ObjectBrowser", "auto_size", false ); - bool autoSizeFirst = resMgr->booleanValue( "ObjectBrowser", "auto_size_first", true ); - bool resizeOnExpandItem = resMgr->booleanValue( "ObjectBrowser", "resize_on_expand_item", true ); - - ob->setAutoSizeFirstColumn(autoSizeFirst); - ob->setAutoSizeColumns(autoSize); - ob->setResizeOnExpandItem(resizeOnExpandItem); - ob->setProperty( "shortcut", QKeySequence( "Alt+Shift+O" ) ); - - // temporary commented - /* - for ( int i = SalomeApp_DataObject::ValueIdx; i <= SalomeApp_DataObject::RefEntryIdx; i++ ) - { - ob->addColumn( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i ) ), i ); - ob->setColumnShown( i, resMgr->booleanValue( "ObjectBrowser", - QString().sprintf( "visibility_column_%d", i ), true ) ); - } - */ - - // temporary commented - /* - ob->setWidthMode( autoSize ? QListView::Maximum : QListView::Manual ); - ob->listView()->setColumnWidthMode( 0, autoSizeFirst ? QListView::Maximum : QListView::Manual ); - ob->resize( desktop()->width()/3, ob->height() ); - */ - } - } - else if ( flag == WT_PyConsole ) - { - PyConsole_Console* pyCons = new PyConsole_Console( desktop(), new SalomeApp_PyInterp() ); - pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) ); - pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" )); - pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true )); - pyCons->setProperty( "shortcut", QKeySequence( "Alt+Shift+P" ) ); - wid = pyCons; - //pyCons->resize( pyCons->width(), desktop()->height()/4 ); - pyCons->connectPopupRequest( this, SLOT( onConnectPopupRequest( SUIT_PopupClient*, QContextMenuEvent* ) ) ); - } - return wid; -} - -/*!Create preferences.*/ -void SalomeApp_Application::createPreferences( LightApp_Preferences* pref ) -{ - LightApp_Application::createPreferences(pref); - - if ( !pref ) - return; - - int salomeCat = pref->addPreference( tr( "PREF_CATEGORY_SALOME" ) ); - int obTab = pref->addPreference( tr( "PREF_TAB_OBJBROWSER" ), salomeCat ); - int defCols = pref->addPreference( tr( "PREF_GROUP_DEF_COLUMNS" ), obTab ); - for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ ) - { - pref->addPreference( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i-SalomeApp_DataObject::EntryId ).toLatin1() ), defCols, - LightApp_Preferences::Bool, "ObjectBrowser", QString().sprintf( "visibility_column_id_%d", i-1 ) ); - } - pref->setItemProperty( "orientation", Qt::Vertical, defCols ); - - // adding preference to LightApp_Application handled preferences.. a bit of hacking with resources.. - int genTab = pref->addPreference( LightApp_Application::tr( "PREF_TAB_GENERAL" ), salomeCat ); - int studyGroup = pref->addPreference( LightApp_Application::tr( "PREF_GROUP_STUDY" ), genTab ); - pref->addPreference( tr( "PREF_STORE_VISUAL_STATE" ), studyGroup, LightApp_Preferences::Bool, "Study", "store_visual_state" ); - pref->addPreference( "", studyGroup, LightApp_Preferences::Space ); - pref->addPreference( tr( "PREF_PYDUMP_PUBLISH" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_publish" ); - pref->addPreference( tr( "PREF_PYDUMP_MULTI_FILE" ), studyGroup, LightApp_Preferences::Bool, "Study", "multi_file_dump" ); - pref->addPreference( tr( "PREF_PYDUMP_SAVE_GUI" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_save_gui" ); - pref->addPreference( "", studyGroup, LightApp_Preferences::Space ); - pref->addPreference( "", studyGroup, LightApp_Preferences::Space ); -} - -/*!Update desktop title.*/ -void SalomeApp_Application::updateDesktopTitle() { - QString aTitle = applicationName(); - QString aVer = applicationVersion(); - if ( !aVer.isEmpty() ) - aTitle += QString( " " ) + aVer; - - if ( activeStudy() ) - { - QString sName = SUIT_Tools::file( activeStudy()->studyName().trimmed(), false ); - if ( !sName.isEmpty() ) { - SalomeApp_Study* study = dynamic_cast(activeStudy()); - if ( study ) { - _PTR(Study) stdDS = study->studyDS(); - if(stdDS) { - if ( stdDS->GetProperties()->IsLocked() ) { - aTitle += QString( " - [%1 (%2)]").arg( sName ).arg( tr( "STUDY_LOCKED" ) ); - } else { - aTitle += QString( " - [%1]" ).arg( sName ); - } - } - } - } - } - - desktop()->setWindowTitle( aTitle ); -} - -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; - - return res; -} - -bool SalomeApp_Application::closeAction( const int choice, bool& closePermanently ) -{ - bool res = true; - switch( choice ) - { - case CloseSave: - if ( activeStudy()->isSaved() ) - onSaveDoc(); - else if ( !onSaveAsDoc() ) - res = false; - break; - case CloseDiscard: - break; - case CloseUnload: - closePermanently = false; - break; - case CloseCancel: - default: - res = false; - } - - return res; -} - -int SalomeApp_Application::openChoice( const QString& aName ) -{ - int choice = LightApp_Application::openChoice( aName ); - - if ( QFileInfo( aName ).exists() ) { - if ( choice == OpenNew ) { // The document isn't already open. - bool exist = false; - std::vector lst = studyMgr()->GetOpenStudies(); - for ( uint i = 0; i < lst.size() && !exist; i++ ) { - if ( aName == QString( lst[i].c_str() ) ) - exist = true; - } - // The document already exists in the study manager. - // Do you want to reload it? - if ( exist ) { - int answer = SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "QUE_DOC_ALREADYEXIST" ).arg( aName ), - SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::No ); - if ( answer == SUIT_MessageBox::Yes ) - choice = OpenRefresh; - else - choice = OpenCancel; - } - } - } else { // file is not exist on disk - SUIT_MessageBox::warning( desktop(), - QObject::tr("WRN_WARNING"), - QObject::tr("WRN_FILE_NOT_EXIST").arg(aName.toLatin1().data())); - return false; - } - - return choice; -} - -bool SalomeApp_Application::openAction( const int aChoice, const QString& aName ) -{ - bool res = false; - int choice = aChoice; - switch ( choice ) - { - case OpenRefresh: - { - _PTR(Study) aStudy = studyMgr()->GetStudyByName( aName.toStdString() ); - if ( aStudy ) - { - studyMgr()->Close( aStudy ); - choice = OpenNew; - } - } - default: - res = LightApp_Application::openAction( choice, aName ); - break; - } - - return res; -} - -/*! - \brief Get map of the operations which can be performed - on the module activation. - - The method should return the map of the kind \c {:} - where \c is an integer identifier of the operation and - \c is a title for the button to be added to the - dialog box. After user selects the required operation by the - clicking the corresponding button in the dialog box, its identifier - is passed to the moduleActionSelected() method to process - the made choice. - - \return map of the operations - \sa moduleActionSelected() -*/ -QMap SalomeApp_Application::activateModuleActions() const -{ - QMap opmap = LightApp_Application::activateModuleActions(); - opmap.insert( LoadStudyId, tr( "ACTIVATE_MODULE_OP_LOAD" ) ); - opmap.insert( NewAndScriptId, tr( "ACTIVATE_MODULE_OP_SCRIPT" ) ); - return opmap; -} - -/*! - \brief Called when the used selectes required operation chosen - from "Activate module" dialog box. - - Performs the required operation according to the user choice. - - \param id operation identifier - \sa activateModuleActions() -*/ -void SalomeApp_Application::moduleActionSelected( const int id ) -{ - switch ( id ) { - case LoadStudyId: - onLoadDoc(); - break; - case NewAndScriptId: - onNewWithScript(); - break; - default: - LightApp_Application::moduleActionSelected( id ); - break; - } -} - -/*!Gets CORBA::ORB_var*/ -CORBA::ORB_var SalomeApp_Application::orb() -{ - ORB_INIT& init = *SINGLETON_::Instance(); - static CORBA::ORB_var _orb = init( qApp->argc(), qApp->argv() ); - return _orb; -} - -/*!Create and return SALOMEDS_StudyManager.*/ -SALOMEDSClient_StudyManager* SalomeApp_Application::studyMgr() -{ - static _PTR(StudyManager) _sm; - if(!_sm) _sm = ClientFactory::StudyManager(); - return _sm.get(); -} - -/*!Create and return SALOME_NamingService.*/ -SALOME_NamingService* SalomeApp_Application::namingService() -{ - static SALOME_NamingService _ns(orb()); - return &_ns; -} - -/*!Create and return SALOME_LifeCycleCORBA.*/ -SALOME_LifeCycleCORBA* SalomeApp_Application::lcc() -{ - static SALOME_LifeCycleCORBA _lcc( namingService() ); - return &_lcc; -} - -/*!Private SLOT. On preferences.*/ -void SalomeApp_Application::onProperties() -{ - SalomeApp_Study* study = dynamic_cast( activeStudy() ); - if( !study ) - return; - - _PTR(StudyBuilder) SB = study->studyDS()->NewBuilder(); - SB->NewCommand(); - - SalomeApp_StudyPropertiesDlg aDlg( desktop() ); - int res = aDlg.exec(); - if( res==QDialog::Accepted && aDlg.isChanged() ) - SB->CommitCommand(); - else - SB->AbortCommand(); - - //study->updateCaptions(); - updateDesktopTitle(); - updateActions(); -} - -/*!Insert items in popup, which necessary for current application*/ -void SalomeApp_Application::contextMenuPopup( const QString& type, QMenu* thePopup, QString& title ) -{ - LightApp_SelectionMgr* mgr = selectionMgr(); - bool cacheIsOn = mgr->isSelectionCacheEnabled(); - mgr->setSelectionCacheEnabled( true ); - - LightApp_Application::contextMenuPopup( type, thePopup, title ); - - // temporary commented - /*OB_Browser* ob = objectBrowser(); - if ( !ob || type != ob->popupClientType() ) - return;*/ - - // Get selected objects - SALOME_ListIO aList; - mgr->selectedObjects( aList, QString(), false ); - - // add GUI state commands: restore, rename - if ( aList.Extent() == 1 && aList.First()->hasEntry() && - QString( aList.First()->getEntry() ).startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) { - thePopup->addSeparator(); - thePopup->addAction( tr( "MEN_RESTORE_VS" ), this, SLOT( onRestoreGUIState() ) ); - thePopup->addAction( tr( "MEN_RENAME_VS" ), objectBrowser(), - SLOT( onStartEditing() ), objectBrowser()->shortcutKey(SUIT_DataBrowser::RenameShortcut) ); - thePopup->addAction( tr( "MEN_DELETE_VS" ), this, SLOT( onDeleteGUIState() ) ); - } - - // "Delete reference" item should appear only for invalid references - - // isInvalidRefs will be true, if at least one of selected objects is invalid reference - bool isInvalidRefs = false; - SalomeApp_Study* aStudy = dynamic_cast(activeStudy()); - _PTR(Study) aStudyDS = aStudy->studyDS(); - _PTR(SObject) anObj; - - for( SALOME_ListIteratorOfListIO it( aList ); it.More() && !isInvalidRefs; it.Next() ) - 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; - } - - // 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 extra popup menu (defined in XML) - if ( myExtActions.size() > 0 ) { - // Use only first selected object - SalomeApp_Study* study = dynamic_cast( 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() ) ); - } - } - - mgr->setSelectionCacheEnabled( cacheIsOn ); -} - -/*!Update obect browser: - 1.if 'updateModels' true, update existing data models; - 2. update "non-existing" (not loaded yet) data models; - 3. update object browser if it exists */ -void SalomeApp_Application::updateObjectBrowser( const bool updateModels ) -{ - // update "non-existing" (not loaded yet) data models - SalomeApp_Study* study = dynamic_cast(activeStudy()); - if ( study ) - { - _PTR(Study) stdDS = study->studyDS(); - if( stdDS ) - { - for ( _PTR(SComponentIterator) it ( stdDS->NewComponentIterator() ); it->More(); it->Next() ) - { - _PTR(SComponent) aComponent ( it->Value() ); - -#ifndef WITH_SALOMEDS_OBSERVER - // with GUI observers this check is not needed anymore - if ( aComponent->ComponentDataType() == study->getVisualComponentName().toLatin1().constData() ) - continue; // skip the magic "Interface Applicative" component -#endif - if ( !objectBrowser() ) - getWindow( WT_ObjectBrowser ); - const bool isAutoUpdate = objectBrowser()->autoUpdate(); - objectBrowser()->setAutoUpdate( false ); - SalomeApp_DataModel::synchronize( aComponent, study ); - objectBrowser()->setAutoUpdate( isAutoUpdate ); - } - } - } - - // create data objects that correspond to GUI state save points - if ( study ) updateSavePointDataObjects( study ); - - // update existing data models (already loaded SComponents) - LightApp_Application::updateObjectBrowser( updateModels ); -} - -/*!Display Catalog Genenerator dialog */ -void SalomeApp_Application::onCatalogGen() -{ - ToolsGUI_CatalogGeneratorDlg aDlg( desktop() ); - aDlg.exec(); -} - -/*!Display Registry Display dialog */ -void SalomeApp_Application::onRegDisplay() -{ - CORBA::ORB_var anOrb = orb(); - ToolsGUI_RegWidget* regWnd = ToolsGUI_RegWidget::GetRegWidget( anOrb, desktop() ); - regWnd->show(); - regWnd->raise(); - regWnd->activateWindow(); -} - -/*!find original object by double click on item */ -void SalomeApp_Application::onDblClick( SUIT_DataObject* theObj ) -{ - // Issue 21379: References are supported at LightApp_DataObject level - LightApp_DataObject* obj = dynamic_cast( theObj ); - - if( obj && obj->isReference() ) - { - QString entry = obj->refEntry(); - - SUIT_DataOwnerPtrList aList; - aList.append( new LightApp_DataOwner( entry ) ); - selectionMgr()->setSelected( aList, false ); - - SUIT_DataBrowser* ob = objectBrowser(); - - QModelIndexList aSelectedIndexes = ob->selectedIndexes(); - if ( !aSelectedIndexes.isEmpty() ) - ob->treeView()->scrollTo( aSelectedIndexes.first() ); - } -} - -/*! - Creates new view manager - \param type - type of view manager -*/ -SUIT_ViewManager* SalomeApp_Application::newViewManager(const QString& type) -{ - return createViewManager(type); -} - - -/*!Global utility funciton, returns selected GUI Save point object's ID */ -int getSelectedSavePoint( const LightApp_SelectionMgr* selMgr ) -{ - SALOME_ListIO aList; - selMgr->selectedObjects( aList ); - if( aList.Extent() > 0 ) { - Handle(SALOME_InteractiveObject) aIObj = aList.First(); - QString entry( aIObj->getEntry() ); - QString startStr = QObject::tr( "SAVE_POINT_DEF_NAME" ); - if ( !entry.startsWith( startStr ) ) // it's a "GUI state" object - return -1; - bool ok; // conversion to integer is ok? - int savePoint = entry.right( entry.length() - startStr.length() ).toInt( &ok ); - return ok ? savePoint : -1; - } - return -1; -} - -/*!Called on Restore GUI State popup command*/ -void SalomeApp_Application::onRestoreGUIState() -{ - int savePoint = ::getSelectedSavePoint( selectionMgr() ); - if ( savePoint == -1 ) - return; - SalomeApp_VisualState( this ).restoreState( savePoint ); -} - -/*!Called on Delete GUI State popup command*/ -void SalomeApp_Application::onDeleteGUIState() -{ - int savePoint = ::getSelectedSavePoint( selectionMgr() ); - if ( savePoint == -1 ) - return; - SalomeApp_Study* study = dynamic_cast( activeStudy() ); - if ( !study ) - return; - - study->removeSavePoint( savePoint ); - updateSavePointDataObjects( study ); -} - -/*!Called on New study operation*/ -void SalomeApp_Application::onStudyCreated( SUIT_Study* study ) -{ - LightApp_Application::onStudyCreated( study ); - - connect( this, SIGNAL( viewManagerRemoved( SUIT_ViewManager* ) ), - this, SLOT( onViewManagerRemoved( SUIT_ViewManager* ) ), Qt::UniqueConnection ); - - - objectBrowserColumnsVisibility(); -} - -/*!Called on Save study operation*/ -void SalomeApp_Application::onStudySaved( SUIT_Study* study ) -{ - LightApp_Application::onStudySaved( study ); - - // temporary commented - /*if ( objectBrowser() ) { - updateSavePointDataObjects( dynamic_cast( study ) ); - objectBrowser()->updateTree( study->root() ); - }*/ -} - -/*!Called on Open study operation*/ -void SalomeApp_Application::onStudyOpened( SUIT_Study* study ) -{ - LightApp_Application::onStudyOpened( study ); - - connect( this, SIGNAL( viewManagerRemoved( SUIT_ViewManager* ) ), - this, SLOT( onViewManagerRemoved( SUIT_ViewManager* ) ), Qt::UniqueConnection ); - - objectBrowserColumnsVisibility(); - - // temporary commented - /*if ( objectBrowser() ) { - updateSavePointDataObjects( dynamic_cast( study ) ); - objectBrowser()->updateTree( study->root() ); - }*/ -} - -/*! updateSavePointDataObjects: syncronize data objects that correspond to save points (gui states)*/ -void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study ) -{ - - SUIT_DataBrowser* ob = objectBrowser(); - LightApp_SelectionMgr* selMgr = selectionMgr(); - - if ( !study || !ob || !selMgr ) - return; - - // find GUI states root object - SUIT_DataObject* guiRootObj = 0; - DataObjectList ch; - study->root()->children( ch ); - DataObjectList::const_iterator it = ch.begin(), last = ch.end(); - for ( ; it != last ; ++it ) { - if ( dynamic_cast( *it ) ) { - guiRootObj = *it; - break; - } - } - std::vector savePoints = study->getSavePoints(); - // case 1: no more save points but they existed in study's tree - if ( savePoints.empty() && guiRootObj ) { - //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state" - // : set auto update to true for removing SalomeApp_SavePointRootObject from the SUIT_TreeModel - const bool isAutoUpdate = ob->autoUpdate(); - selMgr->clearSelected(); - ob->setAutoUpdate(true); - DataObjectList ch = guiRootObj->children(); - for( int i = 0; i < ch.size(); i++ ) - delete ch[i]; - delete guiRootObj; - ob->setAutoUpdate(isAutoUpdate); - return; - } - // case 2: no more save points but root does not exist either - if ( savePoints.empty() && !guiRootObj ) - return; - // case 3: save points but no root for them - create it - if ( !savePoints.empty() && !guiRootObj ) - guiRootObj = new SalomeApp_SavePointRootObject( study->root() ); - // case 4: everything already exists.. here may be a problem: we want "GUI states" root object - // to be always the last one in the tree. Here we check - if it is not the last one - remove and - // re-create it. - if ( guiRootObj->nextBrother() ) { - study->root()->removeChild(guiRootObj); - study->root()->appendChild(guiRootObj); - //study->root()->dump(); - } - - // store data objects in a map id-to-DataObject - QMap mapDO; - ch.clear(); - guiRootObj->children( ch ); - for( it = ch.begin(), last = ch.end(); it != last ; ++it ) { - SalomeApp_SavePointObject* dobj = dynamic_cast( *it ); - if ( dobj ) - mapDO[dobj->getId()] = dobj; - } - - // iterate new save points. if DataObject with such ID not found in map - create DataObject - // if in the map - remove it from map. - for ( int i = 0; i < savePoints.size(); i++ ) - if ( !mapDO.contains( savePoints[i] ) ) - new SalomeApp_SavePointObject( guiRootObj, savePoints[i], study ); - else - mapDO.remove( savePoints[i] ); - - // delete DataObjects that are still in the map -- their IDs were not found in data model - if( mapDO.size() > 0) { - //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state" - // : set auto update to true for removing SalomeApp_SavePointObject from the SUIT_TreeModel - selMgr->clearSelected(); - const bool isAutoUpdate = ob->autoUpdate(); - ob->setAutoUpdate(true); - for ( QMap::Iterator it = mapDO.begin(); it != mapDO.end(); ++it ) - delete it.value(); - ob->setAutoUpdate(isAutoUpdate); - } -} - -/*! Check data object */ -bool SalomeApp_Application::checkDataObject(LightApp_DataObject* theObj) -{ - if (theObj) - return true; - - return false; -} - -/*! - Opens other study into active Study. If Study is empty - creates it. - \param theName - name of study -*/ -bool SalomeApp_Application::useStudy( const QString& theName ) -{ - createEmptyStudy(); - SalomeApp_Study* aStudy = dynamic_cast( activeStudy() ); - bool res = false; - if (aStudy) - res = aStudy->loadDocument( theName ); - updateDesktopTitle(); - updateCommandsStatus(); - return res; -} - -/*! Show/hide object browser colums according to preferences */ -void SalomeApp_Application::objectBrowserColumnsVisibility() -{ - if ( objectBrowser() ) - 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 ); - objectBrowser()->treeView()->setColumnHidden( i, !shown ); - } -} - -/*! Set SalomeApp_NoteBookDlg pointer */ -void SalomeApp_Application::setNoteBook(SalomeApp_NoteBookDlg* theNoteBook){ - myNoteBook = theNoteBook; -} - -/*! Return SalomeApp_NoteBookDlg pointer */ -SalomeApp_NoteBookDlg* SalomeApp_Application::getNoteBook() const -{ - return myNoteBook; -} - -/*! - * Define extra actions defined in module definition XML file. - * Additional popup items sections can be defined by parameter "popupitems". - * Supported attributes: - * title - title of menu item, - * attributelocalid - AttributeLocalId defined for selected data item where menu command has to be applied, - * method - method which has to be called when menu item is selected - * Example: - *
    - * - *
    - *
    - * - * - * - *
    - */ -void SalomeApp_Application::createExtraActions() -{ - myExtActions.clear(); - SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); - - QStringList aModules; - modules(aModules, false); - foreach(QString aModile, aModules) { - QString aModName = moduleName(aModile); - QString aSectionStr = resMgr->stringValue(aModName, "popupitems", QString()); - if (!aSectionStr.isNull()) { - QStringList aSections = aSectionStr.split(':'); - foreach(QString aSection, aSections) { - QString aTitle = resMgr->stringValue(aSection, "title", QString()); - QString aId = resMgr->stringValue(aSection, "objectid", QString()); - QString aSlot = resMgr->stringValue(aSection, "method", QString()); - if (aTitle.isEmpty() || aSlot.isEmpty() || aId.isEmpty()) - continue; - - QString aModuleName = resMgr->stringValue(aSection, "module", QString()); - if (aModuleName.isNull()) - aModuleName = aModName; - - QAction* aAction = new QAction(aTitle, this); - QStringList aData; - aData<setData(aData); - connect(aAction, SIGNAL(triggered()), this, SLOT(onExtAction())); - myExtActions[aId] = aAction; - } - } - } -} - -/*! - * Called when extra action is selected - */ -void SalomeApp_Application::onExtAction() -{ - QAction* aAction = ::qobject_cast(sender()); - if (!aAction) - return; - - QVariant aData = aAction->data(); - QStringList aDataList = aData.value(); - if (aDataList.size() != 2) - return; - - LightApp_SelectionMgr* aSelectionMgr = selectionMgr(); - SALOME_ListIO aListIO; - aSelectionMgr->selectedObjects(aListIO); - const Handle(SALOME_InteractiveObject)& anIO = aListIO.First(); - if (aListIO.Extent() < 1) - return; - if (!anIO->hasEntry()) - return; - - QString aEntry(anIO->getEntry()); - - QApplication::setOverrideCursor( Qt::WaitCursor ); - QString aModuleTitle = moduleTitle(aDataList[0]); - activateModule(aModuleTitle); - QApplication::restoreOverrideCursor(); - - QCoreApplication::processEvents(); - - CAM_Module* aModule = activeModule(); - if (!aModule) - return; - - if (!QMetaObject::invokeMethod(aModule, qPrintable(aDataList[1]), Q_ARG(QString, aEntry))) - printf("Error: Can't Invoke method %s\n", qPrintable(aDataList[1])); -} - -/*! - * Called when window activated - */ -void SalomeApp_Application::onWindowActivated( SUIT_ViewWindow* theViewWindow ) -{ - SUIT_DataBrowser* anOB = objectBrowser(); - if( !anOB ) - return; - SUIT_DataObject* rootObj = anOB->root(); - if( !rootObj ) - return; - - DataObjectList listObj = rootObj->children( true ); - - SUIT_ViewModel* vmod = 0; - if ( SUIT_ViewManager* vman = theViewWindow->getViewManager() ) - vmod = vman->getViewModel(); - updateVisibilityState( listObj, vmod ); -} - -/*! - Update visibility state of given objects - */ -void SalomeApp_Application::updateVisibilityState( DataObjectList& theList, - SUIT_ViewModel* theViewModel ) -{ - LightApp_Study* aStudy = dynamic_cast(activeStudy()); - - if(!theViewModel) - return; - - SALOME_View* aView = dynamic_cast( theViewModel ); - - if (theList.isEmpty() || !aView || !aStudy) - return; - - for ( DataObjectList::iterator itr = theList.begin(); itr != theList.end(); ++itr ) { - LightApp_DataObject* obj = dynamic_cast(*itr); - - if (!obj || aStudy->isComponent(obj->entry())) - continue; - - LightApp_Module* anObjModule = dynamic_cast(obj->module()); - Qtx::VisibilityState anObjState = Qtx::UnpresentableState; - - if(anObjModule) { - LightApp_Displayer* aDisplayer = anObjModule->displayer(); - if(aDisplayer) { - 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 ); - } - } -} - -/*! - Called then view manager removed -*/ -void SalomeApp_Application::onViewManagerRemoved( SUIT_ViewManager* ) -{ - ViewManagerList lst; - viewManagers(lst); - if( lst.count() == 1) { // in case if closed last view window - LightApp_Study* aStudy = dynamic_cast(activeStudy()); - if(aStudy) - aStudy->setVisibilityStateForAll(Qtx::UnpresentableState); - } -} - -/*! - Checks that an object can be renamed. - \param entry entry of the object - \brief Return \c true if object can be renamed -*/ -bool SalomeApp_Application::renameAllowed( const QString& entry) const -{ - return entry.startsWith( tr( "SAVE_POINT_DEF_NAME") ); -} - -/*! - Rename object by entry. - \param entry entry of the object - \param name new name of the object - \brief Return \c true if rename operation finished successfully, \c false otherwise. -*/ -bool SalomeApp_Application::renameObject( const QString& entry, const QString& name ) -{ - SalomeApp_Study* aStudy = dynamic_cast( activeStudy() ); - - int savePoint = ::getSelectedSavePoint( selectionMgr() ); - - if(!aStudy || savePoint == -1) - return false; - - if ( !name.isNull() && !name.isEmpty() ) { - aStudy->setNameOfSavePoint( savePoint, name ); - updateSavePointDataObjects( aStudy ); - - //Mark study as modified - aStudy->Modified(); - return true; - } - return false; -} +// Copyright (C) 2007-2012 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 +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +// File: SalomeApp_Application.cxx +// Created: 10/22/2004 3:23:45 PM +// Author: Sergey LITONIN + +#ifdef WNT +// E.A. : On windows with python 2.6, there is a conflict +// E.A. : between pymath.h and Standard_math.h which define +// E.A. : some same symbols : acosh, asinh, ... +#include +#include +#endif + +#include "SalomeApp_PyInterp.h" // WARNING! This include must be the first! +#include "SalomeApp_Application.h" +#include "SalomeApp_Study.h" +#include "SalomeApp_DataModel.h" +#include "SalomeApp_DataObject.h" +#include "SalomeApp_VisualState.h" +#include "SalomeApp_StudyPropertiesDlg.h" +#include "SalomeApp_LoadStudiesDlg.h" +#include "SalomeApp_NoteBook.h" + +#include "SalomeApp_ExitDlg.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +// temporary commented +//#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + + +#include +#include + +#include + +#include + +/*!Internal class that updates object browser item properties */ +// temporary commented +/*class SalomeApp_Updater : public OB_Updater +{ +public: + SalomeApp_Updater() : OB_Updater(){}; + virtual ~SalomeApp_Updater(){}; + virtual void update( SUIT_DataObject* theObj, OB_ListItem* theItem ); +}; + +void SalomeApp_Updater::update( SUIT_DataObject* theObj, OB_ListItem* theItem ) +{ + if( !theObj || !theItem ) + return; + + SalomeApp_DataObject* SAObj = dynamic_cast( theObj ); + if( !SAObj ) + return; + + _PTR(SObject) SObj = SAObj->object(); + if( !SObj ) + return; + _PTR( GenericAttribute ) anAttr; + + // Selectable + if ( SObj->FindAttribute( anAttr, "AttributeSelectable" ) ) + { + _PTR(AttributeSelectable) aAttrSel = anAttr; + theItem->setSelectable( aAttrSel->IsSelectable() ); + } + // Expandable + if ( SObj->FindAttribute(anAttr, "AttributeExpandable") ) + { + _PTR(AttributeExpandable) aAttrExpand = anAttr; + theItem->setExpandable( aAttrExpand->IsExpandable() ); + } + // Opened + //this attribute is not supported in the version of SALOME 3.x + //if ( SObj->FindAttribute(anAttr, "AttributeOpened") ) + //{ + // _PTR(AttributeOpened) aAttrOpen = anAttr; + // theItem->setOpen( aAttrOpen->IsOpened() ); + //} +}*/ + +/*!Create new instance of SalomeApp_Application.*/ +extern "C" SALOMEAPP_EXPORT SUIT_Application* createApplication() +{ + return new SalomeApp_Application(); +} + +/*!Constructor.*/ +SalomeApp_Application::SalomeApp_Application() + : LightApp_Application() +{ + connect( desktop(), SIGNAL( windowActivated( SUIT_ViewWindow* ) ), + this, SLOT( onWindowActivated( SUIT_ViewWindow* ) ), Qt::UniqueConnection ); + + setNoteBook(0); +} + +/*!Destructor. + *\li Destroy event filter. + */ +SalomeApp_Application::~SalomeApp_Application() +{ + // Do not destroy. It's a singleton ! + //SALOME_EventFilter::Destroy(); +} + +/*!Start application.*/ +void SalomeApp_Application::start() +{ + LightApp_Application::start(); + + SALOME_EventFilter::Init(); + + static bool isFirst = true; + if ( isFirst ) { + isFirst = false; + + QString hdffile; + QStringList pyfiles; + + for (int i = 1; i < qApp->argc(); i++) { + QRegExp rxs ("--study-hdf=(.+)"); + if ( rxs.indexIn( QString(qApp->argv()[i]) ) >= 0 && rxs.capturedTexts().count() > 1 ) { + QString file = rxs.capturedTexts()[1]; + QFileInfo fi ( file ); + QString extension = fi.suffix().toLower(); + if ( extension == "hdf" && fi.exists() ) + hdffile = fi.absoluteFilePath(); + } + else { + QRegExp rxp ("--pyscript=(.+)"); + if ( rxp.indexIn( QString(qApp->argv()[i]) ) >= 0 && rxp.capturedTexts().count() > 1 ) { + QStringList files = rxp.capturedTexts()[1].split(",",QString::SkipEmptyParts); + pyfiles += files; + } + } + } + + if ( !hdffile.isEmpty() ) // open hdf file given as parameter + onOpenDoc( hdffile ); + else if ( pyfiles.count() > 0 ) // create new study + onNewDoc(); + + // import/execute python scripts + if ( pyfiles.count() > 0 && activeStudy() ) { + SalomeApp_Study* appStudy = dynamic_cast( activeStudy() ); + PyConsole_Console* pyConsole = pythonConsole(); + if ( appStudy && pyConsole ) { + _PTR(Study) aStudy = appStudy->studyDS(); + if ( !aStudy->GetProperties()->IsLocked() ) { + for (uint j = 0; j < pyfiles.count(); j++ ) { + QFileInfo fi ( pyfiles[j] ); + QFileInfo fipy ( pyfiles[j] + ".py" ); + QString command = QString( "execfile(r\"%1\")" ); + if ( fi.isAbsolute() ) { + if ( fi.exists() ) + pyConsole->exec( command.arg( fi.absoluteFilePath() ) ); + else if ( fipy.exists() ) + pyConsole->exec( command.arg( fipy.absoluteFilePath() ) ); + else + qDebug() << "Can't execute file" << pyfiles[j]; + } + else { + bool found = false; + QStringList dirs; + dirs << QDir::currentPath(); + if ( ::getenv( "PYTHONPATH" ) ) + dirs += QString( ::getenv( "PYTHONPATH" ) ).split( QRegExp( "[:|;]" ) ); + foreach( QString dir, dirs ) { + qDebug() << "try" << QFileInfo( dir, pyfiles[j] ).absoluteFilePath(); + qDebug() << "try" << QFileInfo( dir, pyfiles[j] + ".py" ).absoluteFilePath(); + if ( QFileInfo( dir, pyfiles[j] ).exists() ) { + pyConsole->exec( command.arg( QFileInfo( dir, pyfiles[j] ).absoluteFilePath() ) ); + found = true; + break; + } + else if ( QFileInfo( dir, pyfiles[j] + ".py" ).exists() ) { + pyConsole->exec( command.arg( QFileInfo( dir, pyfiles[j] + ".py" ).absoluteFilePath() ) ); + found = true; + break; + } + } + if ( !found ) { + qDebug() << "Can't execute file" << pyfiles[j]; + } + } + } + } + } + } + } +} + +/*!Create actions:*/ +void SalomeApp_Application::createActions() +{ + LightApp_Application::createActions(); + + SUIT_Desktop* desk = desktop(); + + //! Save GUI state + // "Save GUI State" command is moved to VISU module + // createAction( SaveGUIStateId, tr( "TOT_DESK_FILE_SAVE_GUI_STATE" ), QIcon(), + // tr( "MEN_DESK_FILE_SAVE_GUI_STATE" ), tr( "PRP_DESK_FILE_SAVE_GUI_STATE" ), + // 0, desk, false, this, SLOT( onSaveGUIState() ) ); + + //! Dump study + createAction( DumpStudyId, tr( "TOT_DESK_FILE_DUMP_STUDY" ), QIcon(), + tr( "MEN_DESK_FILE_DUMP_STUDY" ), tr( "PRP_DESK_FILE_DUMP_STUDY" ), + Qt::CTRL+Qt::Key_D, desk, false, this, SLOT( onDumpStudy() ) ); + + //! Load script + createAction( LoadScriptId, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), QIcon(), + tr( "MEN_DESK_FILE_LOAD_SCRIPT" ), tr( "PRP_DESK_FILE_LOAD_SCRIPT" ), + Qt::CTRL+Qt::Key_T, desk, false, this, SLOT( onLoadScript() ) ); + + //! Properties + createAction( PropertiesId, tr( "TOT_DESK_PROPERTIES" ), QIcon(), + tr( "MEN_DESK_PROPERTIES" ), tr( "PRP_DESK_PROPERTIES" ), + Qt::CTRL+Qt::Key_P, desk, false, this, SLOT( onProperties() ) ); + + //! Catalog Generator + createAction( CatalogGenId, tr( "TOT_DESK_CATALOG_GENERATOR" ), QIcon(), + tr( "MEN_DESK_CATALOG_GENERATOR" ), tr( "PRP_DESK_CATALOG_GENERATOR" ), + Qt::ALT+Qt::SHIFT+Qt::Key_G, desk, false, this, SLOT( onCatalogGen() ) ); + + //! Registry Display + createAction( RegDisplayId, tr( "TOT_DESK_REGISTRY_DISPLAY" ), QIcon(), + tr( "MEN_DESK_REGISTRY_DISPLAY" ), tr( "PRP_DESK_REGISTRY_DISPLAY" ), + /*Qt::SHIFT+Qt::Key_D*/0, desk, false, this, SLOT( onRegDisplay() ) ); + + //SRN: BugID IPAL9021, add an action "Load" + 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() ) ); + //SRN: BugID IPAL9021: End + + + int fileMenu = createMenu( tr( "MEN_DESK_FILE" ), -1 ); + + // "Save GUI State" command is renamed to "Save VISU State" and + // creation of menu item is moved to VISU + // createMenu( SaveGUIStateId, fileMenu, 10, -1 ); + + createMenu( FileLoadId, fileMenu, 0 ); //SRN: BugID IPAL9021, add a menu item "Load" + + 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 ); + createMenu( separator(), fileMenu, -1, 10, -1 ); + + int toolsMenu = createMenu( tr( "MEN_DESK_TOOLS" ), -1, MenuToolsId, 50 ); + createMenu( CatalogGenId, toolsMenu, 10, -1 ); + createMenu( RegDisplayId, toolsMenu, 10, -1 ); + createMenu( separator(), toolsMenu, -1, 15, -1 ); + + createExtraActions(); + + // import Python module that manages SALOME plugins + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject* pluginsmanager=PyImport_ImportModule((char*)"salome_pluginsmanager"); + PyObject* 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()); + if(res==NULL) + PyErr_Print(); + Py_XDECREF(res); + PyGILState_Release(gstate); + // end of SALOME plugins loading + +} + + +/*!Set desktop:*/ +void SalomeApp_Application::setDesktop( SUIT_Desktop* desk ) +{ + LightApp_Application::setDesktop( desk ); + + if ( desk ) { + connect( desk, SIGNAL( windowActivated( SUIT_ViewWindow* ) ), + this, SLOT( onWindowActivated( SUIT_ViewWindow* ) ), Qt::UniqueConnection ); + } +} + +/*! + \brief Close application. +*/ +void SalomeApp_Application::onExit() +{ + bool killServers = false; + bool result = true; + + if ( exitConfirmation() ) { + SalomeApp_ExitDlg dlg( desktop() ); + result = dlg.exec() == QDialog::Accepted; + killServers = dlg.isServersShutdown(); + } + + if ( result ) + SUIT_Session::session()->closeSession( SUIT_Session::ASK, killServers ); +} + +/*!SLOT. Load document.*/ +void SalomeApp_Application::onLoadDoc() +{ + QString studyName; + + std::vector List = studyMgr()->GetOpenStudies(); + + SUIT_Session* aSession = SUIT_Session::session(); + QList aAppList = aSession->applications(); + + QStringList unloadedStudies; + + for ( unsigned int ind = 0; ind < List.size(); ind++ ) { + studyName = List[ind].c_str(); + // Add to list only unloaded studies + bool isAlreadyOpen = false; + QListIterator it( aAppList ); + while ( it.hasNext() && !isAlreadyOpen ) { + SUIT_Application* aApp = it.next(); + if( !aApp || !aApp->activeStudy() ) + continue; + if ( aApp->activeStudy()->studyName() == studyName ) + isAlreadyOpen = true; + } + + if ( !isAlreadyOpen ) + unloadedStudies << studyName; + } + + studyName = SalomeApp_LoadStudiesDlg::selectStudy( desktop(), unloadedStudies ); + if ( studyName.isEmpty() ) + return; + +#ifndef WIN32 + // this code replaces marker of windows drive and path become invalid therefore + // defines placed there + studyName.replace( QRegExp(":"), "/" ); +#endif + + if ( onLoadDoc( studyName ) ) { + updateWindows(); + updateViewManagers(); + updateObjectBrowser( true ); + } +} + +/*!SLOT. Create new study and load script*/ +void SalomeApp_Application::onNewWithScript() +{ + QStringList filtersList; + filtersList.append(tr("PYTHON_FILES_FILTER")); + filtersList.append(tr("ALL_FILES_FILTER")); + + QString anInitialPath = ""; + if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() ) + anInitialPath = QDir::currentPath(); + + QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true ); + + if ( !aFile.isEmpty() ) + { + onNewDoc(); + + QString command = QString("execfile(r\"%1\")").arg(aFile); + + PyConsole_Console* pyConsole = pythonConsole(); + + if ( pyConsole ) + pyConsole->exec( command ); + } +} + + +/*!SLOT. Load document with \a aName.*/ +bool SalomeApp_Application::onLoadDoc( const QString& aName ) +{ + bool res = true; + if ( !activeStudy() ) { + // if no study - load in current desktop + res = useStudy( aName ); + } + else { + // if study exists - load in new desktop. Check: is the same file is loaded? + SUIT_Session* aSession = SUIT_Session::session(); + QList aAppList = aSession->applications(); + bool isAlreadyOpen = false; + SalomeApp_Application* aApp = 0; + for ( QList::iterator it = aAppList.begin(); + it != aAppList.end() && !isAlreadyOpen; ++it ) { + aApp = dynamic_cast( *it ); + if ( aApp && aApp->activeStudy()->studyName() == aName ) + isAlreadyOpen = true; + } + if ( !isAlreadyOpen ) { + aApp = dynamic_cast( startApplication( 0, 0 ) ); + if ( aApp ) + res = aApp->useStudy( aName ); + } + else { + aApp->desktop()->activateWindow(); + } + } + + return res; +} + +/*!SLOT. Copy objects to study maneger from selection maneger..*/ +void SalomeApp_Application::onCopy() +{ + SALOME_ListIO list; + LightApp_SelectionMgr* mgr = selectionMgr(); + mgr->selectedObjects(list); + + SalomeApp_Study* study = dynamic_cast(activeStudy()); + if(study == NULL) return; + + _PTR(Study) stdDS = study->studyDS(); + if(!stdDS) return; + + SALOME_ListIteratorOfListIO it( list ); + if(it.More()) + { + _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry()); + try { + studyMgr()->Copy(so); + onSelectionChanged(); + } + catch(...) { + } + } +} + +/*!SLOT. Paste objects to study maneger from selection manager.*/ +void SalomeApp_Application::onPaste() +{ + SALOME_ListIO list; + LightApp_SelectionMgr* mgr = selectionMgr(); + mgr->selectedObjects(list); + + SalomeApp_Study* study = dynamic_cast(activeStudy()); + if(study == NULL) return; + + _PTR(Study) stdDS = study->studyDS(); + if(!stdDS) return; + + if ( stdDS->GetProperties()->IsLocked() ) { + SUIT_MessageBox::warning( desktop(), + QObject::tr("WRN_WARNING"), + QObject::tr("WRN_STUDY_LOCKED") ); + return; + } + + SALOME_ListIteratorOfListIO it( list ); + if(it.More()) + { + _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry()); + try { + studyMgr()->Paste(so); + updateObjectBrowser( true ); + updateActions(); //SRN: BugID IPAL9377, case 3 + } + catch(...) { + } + } +} + +/*!Check the application on closing. + * \retval true if possible, else false + */ +bool SalomeApp_Application::isPossibleToClose( bool& closePermanently ) +{ + return LightApp_Application::isPossibleToClose( closePermanently ); +} + +/*! Check if the study is locked */ +void SalomeApp_Application::onCloseDoc( bool ask ) +{ + SalomeApp_Study* study = dynamic_cast(activeStudy()); + + if (study != NULL) { + _PTR(Study) stdDS = study->studyDS(); + if(stdDS && stdDS->IsStudyLocked()) { + if ( SUIT_MessageBox::question( desktop(), + QObject::tr( "WRN_WARNING" ), + QObject::tr( "CLOSE_LOCKED_STUDY" ), + SUIT_MessageBox::Yes | SUIT_MessageBox::No, + SUIT_MessageBox::No) == SUIT_MessageBox::No ) return; + + } + } + + LightApp_Application::onCloseDoc( ask ); +} + +/*!Sets enable or disable some actions on selection changed.*/ +void SalomeApp_Application::onSelectionChanged() +{ + SALOME_ListIO list; + LightApp_SelectionMgr* mgr = selectionMgr(); + mgr->selectedObjects(list); + + bool canCopy = false; + bool canPaste = false; + + SalomeApp_Study* study = dynamic_cast(activeStudy()); + if (study != NULL) { + _PTR(Study) stdDS = study->studyDS(); + + if (stdDS) { + SALOME_ListIteratorOfListIO it ( list ); + + if (it.More() && list.Extent() == 1) { + _PTR(SObject) so = stdDS->FindObjectID(it.Value()->getEntry()); + + if ( so ) { + canCopy = studyMgr()->CanCopy(so); + canPaste = studyMgr()->CanPaste(so); + } + } + } + } + + action(EditCopyId)->setEnabled(canCopy); + action(EditPasteId)->setEnabled(canPaste); +} + +/*!Delete references.*/ +void SalomeApp_Application::onDeleteInvalidReferences() +{ + SALOME_ListIO aList; + LightApp_SelectionMgr* mgr = selectionMgr(); + mgr->selectedObjects( aList, QString(), false ); + + if( aList.IsEmpty() ) + return; + + SalomeApp_Study* aStudy = dynamic_cast(activeStudy()); + _PTR(Study) aStudyDS = aStudy->studyDS(); + _PTR(StudyBuilder) aStudyBuilder = aStudyDS->NewBuilder(); + _PTR(SObject) anObj; + + for( SALOME_ListIteratorOfListIO it( aList ); it.More(); it.Next() ) + 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() ) + aStudyBuilder->RemoveReference( aSObject ); + } + updateObjectBrowser(); +} + +/*!Private SLOT. */ +void SalomeApp_Application::onOpenWith() +{ + QApplication::setOverrideCursor( Qt::WaitCursor ); + SALOME_ListIO aList; + LightApp_SelectionMgr* mgr = selectionMgr(); + mgr->selectedObjects(aList); + if (aList.Extent() != 1) + { + QApplication::restoreOverrideCursor(); + return; + } + Handle(SALOME_InteractiveObject) aIObj = aList.First(); + QString aModuleName(aIObj->getComponentDataType()); + QString aModuleTitle = moduleTitle(aModuleName); + activateModule(aModuleTitle); + QApplication::restoreOverrideCursor(); +} + +/*! + Creates new study +*/ +SUIT_Study* SalomeApp_Application::createNewStudy() +{ + SalomeApp_Study* aStudy = new SalomeApp_Study( this ); + + // Set up processing of major study-related events + connect( aStudy, SIGNAL( created( SUIT_Study* ) ), this, SLOT( onStudyCreated( SUIT_Study* ) ) ); + connect( aStudy, SIGNAL( opened ( SUIT_Study* ) ), this, SLOT( onStudyOpened ( SUIT_Study* ) ) ); + connect( aStudy, SIGNAL( saved ( SUIT_Study* ) ), this, SLOT( onStudySaved ( SUIT_Study* ) ) ); + connect( aStudy, SIGNAL( closed ( SUIT_Study* ) ), this, SLOT( onStudyClosed ( SUIT_Study* ) ) ); + + //to receive signal in application that NoteBook's variable was modified + connect( aStudy, SIGNAL(notebookVarUpdated(QString)), + this, SIGNAL(notebookVarUpdated(QString)) ); + + return aStudy; +} + +/*! + Enable/Disable menu items and toolbar buttons. Rebuild menu +*/ +void SalomeApp_Application::updateCommandsStatus() +{ + LightApp_Application::updateCommandsStatus(); + + // Dump study menu + QAction* a = action( DumpStudyId ); + if ( a ) + a->setEnabled( activeStudy() ); + + // Load script menu + a = action( LoadScriptId ); + if ( a ) + a->setEnabled( activeStudy() ); + + // Properties menu + a = action( PropertiesId ); + if( a ) + a->setEnabled( activeStudy() ); + + // Save GUI state menu + a = action( SaveGUIStateId ); + if( a ) + a->setEnabled( activeStudy() ); + + // update state of Copy/Paste menu items + onSelectionChanged(); +} + +/*! + \class DumpStudyFileDlg + Private class used in Dump Study operation. Consists 2 check boxes: + "Publish in study" and "Save GUI parameters" +*/ +class DumpStudyFileDlg : public SUIT_FileDlg +{ +public: + DumpStudyFileDlg( QWidget* parent ) : SUIT_FileDlg( parent, false, true, true ) + { + QGridLayout* grid = ::qobject_cast( layout() ); + if ( grid ) + { + QWidget *hB = new QWidget( this ); + myPublishChk = new QCheckBox( tr("PUBLISH_IN_STUDY") ); + myMultiFileChk = new QCheckBox( tr("MULTI_FILE_DUMP") ); + mySaveGUIChk = new QCheckBox( tr("SAVE_GUI_STATE") ); + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(myPublishChk); + layout->addWidget(myMultiFileChk); + layout->addWidget(mySaveGUIChk); + hB->setLayout(layout); + + QPushButton* pb = new QPushButton(this); + + int row = grid->rowCount(); + grid->addWidget( new QLabel("", this), row, 0 ); + grid->addWidget( hB, row, 1, 1, 3 ); + grid->addWidget( pb, row, 5 ); + + pb->hide(); + } + } + QCheckBox* myPublishChk; + QCheckBox* myMultiFileChk; + QCheckBox* mySaveGUIChk; +}; + +class DumpStudyFileValidator : public SUIT_FileValidator +{ + public: + DumpStudyFileValidator( QWidget* parent) : SUIT_FileValidator ( parent ) {}; + virtual ~DumpStudyFileValidator() {}; + virtual bool canSave( const QString& file, bool permissions ); +}; + +bool DumpStudyFileValidator::canSave(const QString& file, bool permissions) +{ + QFileInfo fi( file ); + if ( !QRegExp( "[A-Za-z_][A-Za-z0-9_]*" ).exactMatch( fi.completeBaseName() ) ) { + SUIT_MessageBox::critical( parent(), + QObject::tr("WRN_WARNING"), + QObject::tr("WRN_FILE_NAME_BAD") ); + return false; + } + return SUIT_FileValidator::canSave( file, permissions); +} + +/*!Private SLOT. On dump study.*/ +void SalomeApp_Application::onDumpStudy( ) +{ + SalomeApp_Study* appStudy = dynamic_cast( activeStudy() ); + if ( !appStudy ) return; + _PTR(Study) aStudy = appStudy->studyDS(); + + QStringList aFilters; + aFilters.append( tr( "PYTHON_FILES_FILTER" ) ); + + bool anIsPublish = true; + bool anIsMultiFile = false; + bool anIsSaveGUI = true; + + if ( SUIT_ResourceMgr* aResourceMgr = resourceMgr() ) { + anIsPublish = aResourceMgr->booleanValue( "Study", "pydump_publish", anIsPublish ); + anIsMultiFile = aResourceMgr->booleanValue( "Study", "multi_file_dump", anIsMultiFile ); + anIsSaveGUI = aResourceMgr->booleanValue( "Study", "pydump_save_gui", anIsSaveGUI ); + } + + DumpStudyFileDlg fd( desktop() ); + fd.setValidator( new DumpStudyFileValidator( &fd ) ); + fd.setWindowTitle( tr( "TOT_DESK_FILE_DUMP_STUDY" ) ); + fd.setFilters( aFilters ); + fd.myPublishChk->setChecked( anIsPublish ); + fd.myMultiFileChk->setChecked( anIsMultiFile ); + fd.mySaveGUIChk->setChecked( anIsSaveGUI ); + if ( fd.exec() == QDialog::Accepted ) + { + QString aFileName = fd.selectedFile(); + + bool toPublish = fd.myPublishChk->isChecked(); + bool isMultiFile = fd.myMultiFileChk->isChecked(); + bool toSaveGUI = fd.mySaveGUIChk->isChecked(); + + if ( !aFileName.isEmpty() ) { + QFileInfo aFileInfo(aFileName); + if( aFileInfo.isDir() ) // IPAL19257 + return; + + // Issue 21377 - dump study implementation moved to SalomeApp_Study class + bool res = appStudy->dump( aFileName, toPublish, isMultiFile, toSaveGUI ); + + if ( !res ) + SUIT_MessageBox::warning( desktop(), + QObject::tr("WRN_WARNING"), + tr("WRN_DUMP_STUDY_FAILED") ); + } + } +} + +/*!Private SLOT. On load script.*/ +void SalomeApp_Application::onLoadScript( ) +{ + SalomeApp_Study* appStudy = dynamic_cast( 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; + } + + QStringList filtersList; + filtersList.append(tr("PYTHON_FILES_FILTER")); + filtersList.append(tr("ALL_FILES_FILTER")); + + QString anInitialPath = ""; + if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() ) + anInitialPath = QDir::currentPath(); + + QString aFile = SUIT_FileDlg::getFileName( desktop(), anInitialPath, filtersList, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), true, true ); + + if ( !aFile.isEmpty() ) + { + QString command = QString("execfile(r\"%1\")").arg(aFile); + + PyConsole_Console* pyConsole = pythonConsole(); + + if ( pyConsole ) + pyConsole->exec( command ); + } +} + +/*!Private SLOT. On save GUI state.*/ +void SalomeApp_Application::onSaveGUIState() +{ + SalomeApp_Study* study = dynamic_cast( activeStudy() ); + if ( study ) { + SalomeApp_VisualState( this ).storeState(); + updateSavePointDataObjects( study ); + updateObjectBrowser(); + } + updateActions(); +} + +/*!Gets file filter. + *\retval QString "(*.hdf)" + */ +QString SalomeApp_Application::getFileFilter() const +{ + return "(*.hdf)"; +} + +/*!Create window.*/ +QWidget* SalomeApp_Application::createWindow( const int flag ) +{ + QWidget* wid = 0; + if ( flag != WT_PyConsole ) wid = LightApp_Application::createWindow(flag); + + SUIT_ResourceMgr* resMgr = resourceMgr(); + + if ( flag == WT_ObjectBrowser ) + { + SUIT_DataBrowser* ob = qobject_cast( wid ); + if ( ob ) { + // temporary commented + //ob->setUpdater( new SalomeApp_Updater() ); + +#ifdef WITH_SALOMEDS_OBSERVER + //do not activate the automatic update of Qt tree through signal/slot + ob->setAutoUpdate(false); + //activate update of modified objects only + ob->setUpdateModified(true); +#endif + + connect( ob, SIGNAL( doubleClicked( SUIT_DataObject* ) ), this, SLOT( onDblClick( SUIT_DataObject* ) ) ); + + QString + ValueCol = QObject::tr( "VALUE_COLUMN" ), + IORCol = QObject::tr( "IOR_COLUMN" ), + RefCol = QObject::tr( "REFENTRY_COLUMN" ), + EntryCol = QObject::tr( "ENTRY_COLUMN" ); + + SUIT_AbstractModel* treeModel = dynamic_cast( ob->model() ); + treeModel->registerColumn( 0, EntryCol, SalomeApp_DataObject::EntryId ); + treeModel->registerColumn( 0, ValueCol, SalomeApp_DataObject::ValueId ); + treeModel->registerColumn( 0, IORCol, SalomeApp_DataObject::IORId ); + treeModel->registerColumn( 0, RefCol, SalomeApp_DataObject::RefEntryId ); + treeModel->setAppropriate( EntryCol, Qtx::Toggled ); + treeModel->setAppropriate( ValueCol, Qtx::Toggled ); + treeModel->setAppropriate( IORCol, Qtx::Toggled ); + treeModel->setAppropriate( RefCol, Qtx::Toggled ); + + bool autoSize = resMgr->booleanValue( "ObjectBrowser", "auto_size", false ); + bool autoSizeFirst = resMgr->booleanValue( "ObjectBrowser", "auto_size_first", true ); + bool resizeOnExpandItem = resMgr->booleanValue( "ObjectBrowser", "resize_on_expand_item", true ); + + ob->setAutoSizeFirstColumn(autoSizeFirst); + ob->setAutoSizeColumns(autoSize); + ob->setResizeOnExpandItem(resizeOnExpandItem); + ob->setProperty( "shortcut", QKeySequence( "Alt+Shift+O" ) ); + + // temporary commented + /* + for ( int i = SalomeApp_DataObject::ValueIdx; i <= SalomeApp_DataObject::RefEntryIdx; i++ ) + { + ob->addColumn( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i ) ), i ); + ob->setColumnShown( i, resMgr->booleanValue( "ObjectBrowser", + QString().sprintf( "visibility_column_%d", i ), true ) ); + } + */ + + // temporary commented + /* + ob->setWidthMode( autoSize ? QListView::Maximum : QListView::Manual ); + ob->listView()->setColumnWidthMode( 0, autoSizeFirst ? QListView::Maximum : QListView::Manual ); + ob->resize( desktop()->width()/3, ob->height() ); + */ + } + } + else if ( flag == WT_PyConsole ) + { + PyConsole_Console* pyCons = new PyConsole_Console( desktop(), new SalomeApp_PyInterp() ); + pyCons->setWindowTitle( tr( "PYTHON_CONSOLE" ) ); + pyCons->setFont(resourceMgr()->fontValue( "PyConsole", "font" )); + pyCons->setIsShowBanner(resourceMgr()->booleanValue( "PyConsole", "show_banner", true )); + pyCons->setProperty( "shortcut", QKeySequence( "Alt+Shift+P" ) ); + wid = pyCons; + //pyCons->resize( pyCons->width(), desktop()->height()/4 ); + pyCons->connectPopupRequest( this, SLOT( onConnectPopupRequest( SUIT_PopupClient*, QContextMenuEvent* ) ) ); + } + else if ( flag == WT_NoteBook ) + { + SalomeApp_Study* appStudy = dynamic_cast( activeStudy() ); + if ( appStudy ) { + _PTR(Study) aStudy = appStudy->studyDS(); + myNoteBook = new SalomeApp_NoteBook( desktop(), aStudy ); + //to receive signal in NoteBook that it's variable was modified + connect( this, SIGNAL(notebookVarUpdated(QString)), + myNoteBook, SLOT(onVarUpdate(QString)) ); + } + wid = myNoteBook; + } + return wid; +} + +/*!Create preferences.*/ +void SalomeApp_Application::createPreferences( LightApp_Preferences* pref ) +{ + LightApp_Application::createPreferences(pref); + + if ( !pref ) + return; + + int salomeCat = pref->addPreference( tr( "PREF_CATEGORY_SALOME" ) ); + int obTab = pref->addPreference( tr( "PREF_TAB_OBJBROWSER" ), salomeCat ); + int defCols = pref->addPreference( tr( "PREF_GROUP_DEF_COLUMNS" ), obTab ); + for ( int i = SalomeApp_DataObject::EntryId; i < SalomeApp_DataObject::LastId; i++ ) + { + pref->addPreference( tr( QString().sprintf( "OBJ_BROWSER_COLUMN_%d", i-SalomeApp_DataObject::EntryId ).toLatin1() ), defCols, + LightApp_Preferences::Bool, "ObjectBrowser", QString().sprintf( "visibility_column_id_%d", i-1 ) ); + } + pref->setItemProperty( "orientation", Qt::Vertical, defCols ); + + // adding preference to LightApp_Application handled preferences.. a bit of hacking with resources.. + int genTab = pref->addPreference( LightApp_Application::tr( "PREF_TAB_GENERAL" ), salomeCat ); + int studyGroup = pref->addPreference( LightApp_Application::tr( "PREF_GROUP_STUDY" ), genTab ); + pref->addPreference( tr( "PREF_STORE_VISUAL_STATE" ), studyGroup, LightApp_Preferences::Bool, "Study", "store_visual_state" ); + pref->addPreference( "", studyGroup, LightApp_Preferences::Space ); + pref->addPreference( tr( "PREF_PYDUMP_PUBLISH" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_publish" ); + pref->addPreference( tr( "PREF_PYDUMP_MULTI_FILE" ), studyGroup, LightApp_Preferences::Bool, "Study", "multi_file_dump" ); + pref->addPreference( tr( "PREF_PYDUMP_SAVE_GUI" ), studyGroup, LightApp_Preferences::Bool, "Study", "pydump_save_gui" ); + pref->addPreference( "", studyGroup, LightApp_Preferences::Space ); + pref->addPreference( "", studyGroup, LightApp_Preferences::Space ); +} + +/*!Update desktop title.*/ +void SalomeApp_Application::updateDesktopTitle() { + QString aTitle = applicationName(); + QString aVer = applicationVersion(); + if ( !aVer.isEmpty() ) + aTitle += QString( " " ) + aVer; + + if ( activeStudy() ) + { + QString sName = SUIT_Tools::file( activeStudy()->studyName().trimmed(), false ); + if ( !sName.isEmpty() ) { + SalomeApp_Study* study = dynamic_cast(activeStudy()); + if ( study ) { + _PTR(Study) stdDS = study->studyDS(); + if(stdDS) { + if ( stdDS->GetProperties()->IsLocked() ) { + aTitle += QString( " - [%1 (%2)]").arg( sName ).arg( tr( "STUDY_LOCKED" ) ); + } else { + aTitle += QString( " - [%1]" ).arg( sName ); + } + } + } + } + } + + desktop()->setWindowTitle( aTitle ); +} + +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; + + return res; +} + +bool SalomeApp_Application::closeAction( const int choice, bool& closePermanently ) +{ + bool res = true; + switch( choice ) + { + case CloseSave: + if ( activeStudy()->isSaved() ) + onSaveDoc(); + else if ( !onSaveAsDoc() ) + res = false; + break; + case CloseDiscard: + break; + case CloseUnload: + closePermanently = false; + break; + case CloseCancel: + default: + res = false; + } + + return res; +} + +int SalomeApp_Application::openChoice( const QString& aName ) +{ + int choice = LightApp_Application::openChoice( aName ); + + if ( QFileInfo( aName ).exists() ) { + if ( choice == OpenNew ) { // The document isn't already open. + bool exist = false; + std::vector lst = studyMgr()->GetOpenStudies(); + for ( uint i = 0; i < lst.size() && !exist; i++ ) { + if ( aName == QString( lst[i].c_str() ) ) + exist = true; + } + // The document already exists in the study manager. + // Do you want to reload it? + if ( exist ) { + int answer = SUIT_MessageBox::question( desktop(), tr( "WRN_WARNING" ), tr( "QUE_DOC_ALREADYEXIST" ).arg( aName ), + SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::No ); + if ( answer == SUIT_MessageBox::Yes ) + choice = OpenRefresh; + else + choice = OpenCancel; + } + } + } else { // file is not exist on disk + SUIT_MessageBox::warning( desktop(), + QObject::tr("WRN_WARNING"), + QObject::tr("WRN_FILE_NOT_EXIST").arg(aName.toLatin1().data())); + return false; + } + + return choice; +} + +bool SalomeApp_Application::openAction( const int aChoice, const QString& aName ) +{ + bool res = false; + int choice = aChoice; + switch ( choice ) + { + case OpenRefresh: + { + _PTR(Study) aStudy = studyMgr()->GetStudyByName( aName.toStdString() ); + if ( aStudy ) + { + studyMgr()->Close( aStudy ); + choice = OpenNew; + } + } + default: + res = LightApp_Application::openAction( choice, aName ); + break; + } + + return res; +} + +/*! + \brief Get map of the operations which can be performed + on the module activation. + + The method should return the map of the kind \c {:} + where \c is an integer identifier of the operation and + \c is a title for the button to be added to the + dialog box. After user selects the required operation by the + clicking the corresponding button in the dialog box, its identifier + is passed to the moduleActionSelected() method to process + the made choice. + + \return map of the operations + \sa moduleActionSelected() +*/ +QMap SalomeApp_Application::activateModuleActions() const +{ + QMap opmap = LightApp_Application::activateModuleActions(); + opmap.insert( LoadStudyId, tr( "ACTIVATE_MODULE_OP_LOAD" ) ); + opmap.insert( NewAndScriptId, tr( "ACTIVATE_MODULE_OP_SCRIPT" ) ); + return opmap; +} + +/*! + \brief Called when the used selectes required operation chosen + from "Activate module" dialog box. + + Performs the required operation according to the user choice. + + \param id operation identifier + \sa activateModuleActions() +*/ +void SalomeApp_Application::moduleActionSelected( const int id ) +{ + switch ( id ) { + case LoadStudyId: + onLoadDoc(); + break; + case NewAndScriptId: + onNewWithScript(); + break; + default: + LightApp_Application::moduleActionSelected( id ); + break; + } +} + +/*!Gets CORBA::ORB_var*/ +CORBA::ORB_var SalomeApp_Application::orb() +{ + ORB_INIT& init = *SINGLETON_::Instance(); + static CORBA::ORB_var _orb = init( qApp->argc(), qApp->argv() ); + return _orb; +} + +/*!Create and return SALOMEDS_StudyManager.*/ +SALOMEDSClient_StudyManager* SalomeApp_Application::studyMgr() +{ + static _PTR(StudyManager) _sm; + if(!_sm) _sm = ClientFactory::StudyManager(); + return _sm.get(); +} + +/*!Create and return SALOME_NamingService.*/ +SALOME_NamingService* SalomeApp_Application::namingService() +{ + static SALOME_NamingService _ns(orb()); + return &_ns; +} + +/*!Create and return SALOME_LifeCycleCORBA.*/ +SALOME_LifeCycleCORBA* SalomeApp_Application::lcc() +{ + static SALOME_LifeCycleCORBA _lcc( namingService() ); + return &_lcc; +} + +/*!Private SLOT. On preferences.*/ +void SalomeApp_Application::onProperties() +{ + SalomeApp_Study* study = dynamic_cast( activeStudy() ); + if( !study ) + return; + + _PTR(StudyBuilder) SB = study->studyDS()->NewBuilder(); + SB->NewCommand(); + + SalomeApp_StudyPropertiesDlg aDlg( desktop() ); + int res = aDlg.exec(); + if( res==QDialog::Accepted && aDlg.isChanged() ) + SB->CommitCommand(); + else + SB->AbortCommand(); + + //study->updateCaptions(); + updateDesktopTitle(); + updateActions(); +} + +/*!Insert items in popup, which necessary for current application*/ +void SalomeApp_Application::contextMenuPopup( const QString& type, QMenu* thePopup, QString& title ) +{ + LightApp_SelectionMgr* mgr = selectionMgr(); + bool cacheIsOn = mgr->isSelectionCacheEnabled(); + mgr->setSelectionCacheEnabled( true ); + + LightApp_Application::contextMenuPopup( type, thePopup, title ); + + // temporary commented + /*OB_Browser* ob = objectBrowser(); + if ( !ob || type != ob->popupClientType() ) + return;*/ + + // Get selected objects + SALOME_ListIO aList; + mgr->selectedObjects( aList, QString(), false ); + + // add GUI state commands: restore, rename + if ( aList.Extent() == 1 && aList.First()->hasEntry() && + QString( aList.First()->getEntry() ).startsWith( tr( "SAVE_POINT_DEF_NAME" ) ) ) { + thePopup->addSeparator(); + thePopup->addAction( tr( "MEN_RESTORE_VS" ), this, SLOT( onRestoreGUIState() ) ); + thePopup->addAction( tr( "MEN_RENAME_VS" ), objectBrowser(), + SLOT( onStartEditing() ), objectBrowser()->shortcutKey(SUIT_DataBrowser::RenameShortcut) ); + thePopup->addAction( tr( "MEN_DELETE_VS" ), this, SLOT( onDeleteGUIState() ) ); + } + + // "Delete reference" item should appear only for invalid references + + // isInvalidRefs will be true, if at least one of selected objects is invalid reference + bool isInvalidRefs = false; + SalomeApp_Study* aStudy = dynamic_cast(activeStudy()); + _PTR(Study) aStudyDS = aStudy->studyDS(); + _PTR(SObject) anObj; + + for( SALOME_ListIteratorOfListIO it( aList ); it.More() && !isInvalidRefs; it.Next() ) + 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; + } + + // 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 extra popup menu (defined in XML) + if ( myExtActions.size() > 0 ) { + // Use only first selected object + SalomeApp_Study* study = dynamic_cast( 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() ) ); + } + } + + mgr->setSelectionCacheEnabled( cacheIsOn ); +} + +/*!Update obect browser: + 1.if 'updateModels' true, update existing data models; + 2. update "non-existing" (not loaded yet) data models; + 3. update object browser if it exists */ +void SalomeApp_Application::updateObjectBrowser( const bool updateModels ) +{ + // update "non-existing" (not loaded yet) data models + SalomeApp_Study* study = dynamic_cast(activeStudy()); + if ( study ) + { + _PTR(Study) stdDS = study->studyDS(); + if( stdDS ) + { + for ( _PTR(SComponentIterator) it ( stdDS->NewComponentIterator() ); it->More(); it->Next() ) + { + _PTR(SComponent) aComponent ( it->Value() ); + +#ifndef WITH_SALOMEDS_OBSERVER + // with GUI observers this check is not needed anymore + if ( aComponent->ComponentDataType() == study->getVisualComponentName().toLatin1().constData() ) + continue; // skip the magic "Interface Applicative" component +#endif + if ( !objectBrowser() ) + getWindow( WT_ObjectBrowser ); + const bool isAutoUpdate = objectBrowser()->autoUpdate(); + objectBrowser()->setAutoUpdate( false ); + SalomeApp_DataModel::synchronize( aComponent, study ); + objectBrowser()->setAutoUpdate( isAutoUpdate ); + } + } + } + + // create data objects that correspond to GUI state save points + if ( study ) updateSavePointDataObjects( study ); + + // update existing data models (already loaded SComponents) + LightApp_Application::updateObjectBrowser( updateModels ); +} + +/*!Display Catalog Genenerator dialog */ +void SalomeApp_Application::onCatalogGen() +{ + ToolsGUI_CatalogGeneratorDlg aDlg( desktop() ); + aDlg.exec(); +} + +/*!Display Registry Display dialog */ +void SalomeApp_Application::onRegDisplay() +{ + CORBA::ORB_var anOrb = orb(); + ToolsGUI_RegWidget* regWnd = ToolsGUI_RegWidget::GetRegWidget( anOrb, desktop() ); + regWnd->show(); + regWnd->raise(); + regWnd->activateWindow(); +} + +/*!find original object by double click on item */ +void SalomeApp_Application::onDblClick( SUIT_DataObject* theObj ) +{ + // Issue 21379: References are supported at LightApp_DataObject level + LightApp_DataObject* obj = dynamic_cast( theObj ); + + if( obj && obj->isReference() ) + { + QString entry = obj->refEntry(); + + SUIT_DataOwnerPtrList aList; + aList.append( new LightApp_DataOwner( entry ) ); + selectionMgr()->setSelected( aList, false ); + + SUIT_DataBrowser* ob = objectBrowser(); + + QModelIndexList aSelectedIndexes = ob->selectedIndexes(); + if ( !aSelectedIndexes.isEmpty() ) + ob->treeView()->scrollTo( aSelectedIndexes.first() ); + } +} + +/*! + Creates new view manager + \param type - type of view manager +*/ +SUIT_ViewManager* SalomeApp_Application::newViewManager(const QString& type) +{ + return createViewManager(type); +} + + +/*!Global utility funciton, returns selected GUI Save point object's ID */ +int getSelectedSavePoint( const LightApp_SelectionMgr* selMgr ) +{ + SALOME_ListIO aList; + selMgr->selectedObjects( aList ); + if( aList.Extent() > 0 ) { + Handle(SALOME_InteractiveObject) aIObj = aList.First(); + QString entry( aIObj->getEntry() ); + QString startStr = QObject::tr( "SAVE_POINT_DEF_NAME" ); + if ( !entry.startsWith( startStr ) ) // it's a "GUI state" object + return -1; + bool ok; // conversion to integer is ok? + int savePoint = entry.right( entry.length() - startStr.length() ).toInt( &ok ); + return ok ? savePoint : -1; + } + return -1; +} + +/*!Called on Restore GUI State popup command*/ +void SalomeApp_Application::onRestoreGUIState() +{ + int savePoint = ::getSelectedSavePoint( selectionMgr() ); + if ( savePoint == -1 ) + return; + SalomeApp_VisualState( this ).restoreState( savePoint ); +} + +/*!Called on Delete GUI State popup command*/ +void SalomeApp_Application::onDeleteGUIState() +{ + int savePoint = ::getSelectedSavePoint( selectionMgr() ); + if ( savePoint == -1 ) + return; + SalomeApp_Study* study = dynamic_cast( activeStudy() ); + if ( !study ) + return; + + study->removeSavePoint( savePoint ); + updateSavePointDataObjects( study ); +} + +/*!Called on New study operation*/ +void SalomeApp_Application::onStudyCreated( SUIT_Study* study ) +{ + LightApp_Application::onStudyCreated( study ); + + desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ), + windowDock( getWindow( WT_ObjectBrowser ) ) ); + + loadDockWindowsState(); + + connect( this, SIGNAL( viewManagerRemoved( SUIT_ViewManager* ) ), + this, SLOT( onViewManagerRemoved( SUIT_ViewManager* ) ), Qt::UniqueConnection ); + + + objectBrowserColumnsVisibility(); +} + +/*!Called on Save study operation*/ +void SalomeApp_Application::onStudySaved( SUIT_Study* study ) +{ + LightApp_Application::onStudySaved( study ); + + // temporary commented + /*if ( objectBrowser() ) { + updateSavePointDataObjects( dynamic_cast( study ) ); + objectBrowser()->updateTree( study->root() ); + }*/ +} + +/*!Called on Open study operation*/ +void SalomeApp_Application::onStudyOpened( SUIT_Study* study ) +{ + LightApp_Application::onStudyOpened( study ); + + desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ), + windowDock( getWindow( WT_ObjectBrowser ) ) ); + + loadDockWindowsState(); + + connect( this, SIGNAL( viewManagerRemoved( SUIT_ViewManager* ) ), + this, SLOT( onViewManagerRemoved( SUIT_ViewManager* ) ), Qt::UniqueConnection ); + + objectBrowserColumnsVisibility(); + + // temporary commented + /*if ( objectBrowser() ) { + updateSavePointDataObjects( dynamic_cast( study ) ); + objectBrowser()->updateTree( study->root() ); + }*/ +} + +/*! updateSavePointDataObjects: syncronize data objects that correspond to save points (gui states)*/ +void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study ) +{ + + SUIT_DataBrowser* ob = objectBrowser(); + LightApp_SelectionMgr* selMgr = selectionMgr(); + + if ( !study || !ob || !selMgr ) + return; + + // find GUI states root object + SUIT_DataObject* guiRootObj = 0; + DataObjectList ch; + study->root()->children( ch ); + DataObjectList::const_iterator it = ch.begin(), last = ch.end(); + for ( ; it != last ; ++it ) { + if ( dynamic_cast( *it ) ) { + guiRootObj = *it; + break; + } + } + std::vector savePoints = study->getSavePoints(); + // case 1: no more save points but they existed in study's tree + if ( savePoints.empty() && guiRootObj ) { + //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state" + // : set auto update to true for removing SalomeApp_SavePointRootObject from the SUIT_TreeModel + const bool isAutoUpdate = ob->autoUpdate(); + selMgr->clearSelected(); + ob->setAutoUpdate(true); + DataObjectList ch = guiRootObj->children(); + for( int i = 0; i < ch.size(); i++ ) + delete ch[i]; + delete guiRootObj; + ob->setAutoUpdate(isAutoUpdate); + return; + } + // case 2: no more save points but root does not exist either + if ( savePoints.empty() && !guiRootObj ) + return; + // case 3: save points but no root for them - create it + if ( !savePoints.empty() && !guiRootObj ) + guiRootObj = new SalomeApp_SavePointRootObject( study->root() ); + // case 4: everything already exists.. here may be a problem: we want "GUI states" root object + // to be always the last one in the tree. Here we check - if it is not the last one - remove and + // re-create it. + if ( guiRootObj->nextBrother() ) { + study->root()->removeChild(guiRootObj); + study->root()->appendChild(guiRootObj); + //study->root()->dump(); + } + + // store data objects in a map id-to-DataObject + QMap mapDO; + ch.clear(); + guiRootObj->children( ch ); + for( it = ch.begin(), last = ch.end(); it != last ; ++it ) { + SalomeApp_SavePointObject* dobj = dynamic_cast( *it ); + if ( dobj ) + mapDO[dobj->getId()] = dobj; + } + + // iterate new save points. if DataObject with such ID not found in map - create DataObject + // if in the map - remove it from map. + for ( int i = 0; i < savePoints.size(); i++ ) + if ( !mapDO.contains( savePoints[i] ) ) + new SalomeApp_SavePointObject( guiRootObj, savePoints[i], study ); + else + mapDO.remove( savePoints[i] ); + + // delete DataObjects that are still in the map -- their IDs were not found in data model + if( mapDO.size() > 0) { + //rnv : to fix bug "IPAL22450 TC6.3.0: sigsegv loop deleting the GUI state" + // : set auto update to true for removing SalomeApp_SavePointObject from the SUIT_TreeModel + selMgr->clearSelected(); + const bool isAutoUpdate = ob->autoUpdate(); + ob->setAutoUpdate(true); + for ( QMap::Iterator it = mapDO.begin(); it != mapDO.end(); ++it ) + delete it.value(); + ob->setAutoUpdate(isAutoUpdate); + } +} + +/*! Check data object */ +bool SalomeApp_Application::checkDataObject(LightApp_DataObject* theObj) +{ + if (theObj) + return true; + + return false; +} + +/*! + Opens other study into active Study. If Study is empty - creates it. + \param theName - name of study +*/ +bool SalomeApp_Application::useStudy( const QString& theName ) +{ + createEmptyStudy(); + SalomeApp_Study* aStudy = dynamic_cast( activeStudy() ); + bool res = false; + if (aStudy) + res = aStudy->loadDocument( theName ); + updateDesktopTitle(); + updateCommandsStatus(); + return res; +} + +/*! Show/hide object browser colums according to preferences */ +void SalomeApp_Application::objectBrowserColumnsVisibility() +{ + if ( objectBrowser() ) + 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 ); + objectBrowser()->treeView()->setColumnHidden( i, !shown ); + } +} + +/*! Set SalomeApp_NoteBook pointer */ +void SalomeApp_Application::setNoteBook(SalomeApp_NoteBook* theNoteBook){ + myNoteBook = theNoteBook; +} + +/*! Return SalomeApp_NoteBook pointer */ +SalomeApp_NoteBook* SalomeApp_Application::getNoteBook() const +{ + return myNoteBook; +} + +/*! + * Define extra actions defined in module definition XML file. + * Additional popup items sections can be defined by parameter "popupitems". + * Supported attributes: + * title - title of menu item, + * attributelocalid - AttributeLocalId defined for selected data item where menu command has to be applied, + * method - method which has to be called when menu item is selected + * Example: + *
    + * + *
    + *
    + * + * + * + *
    + */ +void SalomeApp_Application::createExtraActions() +{ + myExtActions.clear(); + SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); + + QStringList aModules; + modules(aModules, false); + foreach(QString aModile, aModules) { + QString aModName = moduleName(aModile); + QString aSectionStr = resMgr->stringValue(aModName, "popupitems", QString()); + if (!aSectionStr.isNull()) { + QStringList aSections = aSectionStr.split(':'); + foreach(QString aSection, aSections) { + QString aTitle = resMgr->stringValue(aSection, "title", QString()); + QString aId = resMgr->stringValue(aSection, "objectid", QString()); + QString aSlot = resMgr->stringValue(aSection, "method", QString()); + if (aTitle.isEmpty() || aSlot.isEmpty() || aId.isEmpty()) + continue; + + QString aModuleName = resMgr->stringValue(aSection, "module", QString()); + if (aModuleName.isNull()) + aModuleName = aModName; + + QAction* aAction = new QAction(aTitle, this); + QStringList aData; + aData<setData(aData); + connect(aAction, SIGNAL(triggered()), this, SLOT(onExtAction())); + myExtActions[aId] = aAction; + } + } + } +} + +/*! + * Called when extra action is selected + */ +void SalomeApp_Application::onExtAction() +{ + QAction* aAction = ::qobject_cast(sender()); + if (!aAction) + return; + + QVariant aData = aAction->data(); + QStringList aDataList = aData.value(); + if (aDataList.size() != 2) + return; + + LightApp_SelectionMgr* aSelectionMgr = selectionMgr(); + SALOME_ListIO aListIO; + aSelectionMgr->selectedObjects(aListIO); + const Handle(SALOME_InteractiveObject)& anIO = aListIO.First(); + if (aListIO.Extent() < 1) + return; + if (!anIO->hasEntry()) + return; + + QString aEntry(anIO->getEntry()); + + QApplication::setOverrideCursor( Qt::WaitCursor ); + QString aModuleTitle = moduleTitle(aDataList[0]); + activateModule(aModuleTitle); + QApplication::restoreOverrideCursor(); + + QCoreApplication::processEvents(); + + CAM_Module* aModule = activeModule(); + if (!aModule) + return; + + if (!QMetaObject::invokeMethod(aModule, qPrintable(aDataList[1]), Q_ARG(QString, aEntry))) + printf("Error: Can't Invoke method %s\n", qPrintable(aDataList[1])); +} + +/*! + * Called when window activated + */ +void SalomeApp_Application::onWindowActivated( SUIT_ViewWindow* theViewWindow ) +{ + SUIT_DataBrowser* anOB = objectBrowser(); + if( !anOB ) + return; + SUIT_DataObject* rootObj = anOB->root(); + if( !rootObj ) + return; + + DataObjectList listObj = rootObj->children( true ); + + SUIT_ViewModel* vmod = 0; + if ( SUIT_ViewManager* vman = theViewWindow->getViewManager() ) + vmod = vman->getViewModel(); + updateVisibilityState( listObj, vmod ); +} + +/*! + Update visibility state of given objects + */ +void SalomeApp_Application::updateVisibilityState( DataObjectList& theList, + SUIT_ViewModel* theViewModel ) +{ + LightApp_Study* aStudy = dynamic_cast(activeStudy()); + + if(!theViewModel) + return; + + SALOME_View* aView = dynamic_cast( theViewModel ); + + if (theList.isEmpty() || !aView || !aStudy) + return; + + for ( DataObjectList::iterator itr = theList.begin(); itr != theList.end(); ++itr ) { + LightApp_DataObject* obj = dynamic_cast(*itr); + + if (!obj || aStudy->isComponent(obj->entry())) + continue; + + LightApp_Module* anObjModule = dynamic_cast(obj->module()); + Qtx::VisibilityState anObjState = Qtx::UnpresentableState; + + if(anObjModule) { + LightApp_Displayer* aDisplayer = anObjModule->displayer(); + if(aDisplayer) { + 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 ); + } + } +} + +/*! + Called then view manager removed +*/ +void SalomeApp_Application::onViewManagerRemoved( SUIT_ViewManager* ) +{ + ViewManagerList lst; + viewManagers(lst); + if( lst.count() == 1) { // in case if closed last view window + LightApp_Study* aStudy = dynamic_cast(activeStudy()); + if(aStudy) + aStudy->setVisibilityStateForAll(Qtx::UnpresentableState); + } +} + +/*! + Checks that an object can be renamed. + \param entry entry of the object + \brief Return \c true if object can be renamed +*/ +bool SalomeApp_Application::renameAllowed( const QString& entry) const +{ + return entry.startsWith( tr( "SAVE_POINT_DEF_NAME") ); +} + +/*! + Rename object by entry. + \param entry entry of the object + \param name new name of the object + \brief Return \c true if rename operation finished successfully, \c false otherwise. +*/ +bool SalomeApp_Application::renameObject( const QString& entry, const QString& name ) +{ + SalomeApp_Study* aStudy = dynamic_cast( activeStudy() ); + + int savePoint = ::getSelectedSavePoint( selectionMgr() ); + + if(!aStudy || savePoint == -1) + return false; + + if ( !name.isNull() && !name.isEmpty() ) { + aStudy->setNameOfSavePoint( savePoint, name ); + updateSavePointDataObjects( aStudy ); + + //Mark study as modified + aStudy->Modified(); + return true; + } + return false; +} + +/*! + \return default windows( Object Browser, Python Console ) + Adds to map \a aMap. + */ +void SalomeApp_Application::defaultWindows( QMap& aMap ) const +{ + LightApp_Application::defaultWindows(aMap); + if(!aMap.contains(WT_NoteBook)) { + if(!myNoteBook) { + aMap.insert( WT_NoteBook, Qt::LeftDockWidgetArea ); + } + } +} + +/*! + Gets current windows. + \param winMap - output current windows map. +*/ +void SalomeApp_Application::currentWindows(QMap& aMap) const { + LightApp_Application::currentWindows(aMap); + if(!aMap.contains(WT_NoteBook) && myNoteBook) + aMap.insert( WT_NoteBook, Qt::LeftDockWidgetArea ); +} + +//============================================================================ +/*! Function : onUpdateStudy + * Purpose : Slot to update the study. + */ +//============================================================================ +void SalomeApp_Application::onUpdateStudy() +{ + QApplication::setOverrideCursor( Qt::WaitCursor ); + + if( !updateStudy() ) + SUIT_MessageBox::warning( desktop(), tr( "ERROR" ), tr( "ERR_UPDATE_STUDY_FAILED" ) ); + + QApplication::restoreOverrideCursor(); +} + +//============================================================================ +/*! Function : updateStudy + * Purpose : Update study by dumping the study to Python script and loading it. + * It is used to apply variable modifications done in NoteBook to created objects. + */ +//============================================================================ +bool SalomeApp_Application::updateStudy() +{ + SalomeApp_Study* study = dynamic_cast( activeStudy() ); + if( !study ) + return false; + + myNoteBook->setIsDumpedStudySaved( study->isSaved() ); + myNoteBook->setDumpedStudyName( study->studyName() ); + + _PTR(Study) studyDS = study->studyDS(); + + // get unique temporary directory name + QString aTmpDir = QString::fromStdString( SALOMEDS_Tool::GetTmpDir() ); + if( aTmpDir.isEmpty() ) + return false; + + if( aTmpDir.right( 1 ).compare( QDir::separator() ) == 0 ) + aTmpDir.remove( aTmpDir.length() - 1, 1 ); + + // dump study to the temporary directory + QString aScriptName( "notebook" ); + bool toPublish = true; + bool isMultiFile = false; + bool toSaveGUI = true; + + int savePoint; + _PTR(AttributeParameter) ap; + _PTR(IParameters) ip = ClientFactory::getIParameters(ap); + if(ip->isDumpPython(studyDS)) ip->setDumpPython(studyDS); //Unset DumpPython flag. + if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method + ip->setDumpPython(studyDS); + savePoint = SalomeApp_VisualState( this ).storeState(); //SRN: create a temporary save point + } + bool ok = studyDS->DumpStudy( aTmpDir.toStdString(), aScriptName.toStdString(), toPublish, isMultiFile ); + if ( toSaveGUI ) + study->removeSavePoint(savePoint); //SRN: remove the created temporary save point. + + if( ok ) + myNoteBook->setDumpedStudyScript( aTmpDir + QDir::separator() + aScriptName + ".py" ); + else + return false; + + QList aList = SUIT_Session::session()->applications(); + int anIndex = aList.indexOf( this ); + + // Disconnect dialog from application desktop in case if: + // 1) Application is not the first application in the session + // 2) Application is the first application in session but not the only. + bool changeDesktop = ((anIndex > 0) || (anIndex == 0 && aList.count() > 1)); + + SalomeApp_Application* app; + if( anIndex > 0 && anIndex < aList.count() ) + app = dynamic_cast( aList[ anIndex - 1 ] ); + else if(anIndex == 0 && aList.count() > 1) + app = dynamic_cast( aList[ 1 ] ); + + if( !app ) + return false; + + if( changeDesktop ) { + // creation a new study and restoring will be done in another application + connect( this, SIGNAL( dumpedStudyClosed( const QString&, const QString&, bool ) ), + app, SLOT( onRestoreStudy( const QString&, const QString&, bool ) ), Qt::UniqueConnection ); + } + + QString aDumpScript = myNoteBook->getDumpedStudyScript(); + QString aStudyName = myNoteBook->getDumpedStudyName(); + bool isStudySaved = myNoteBook->isDumpedStudySaved(); + // clear a study (delete all objects) + onCloseDoc( false ); + + if( !changeDesktop ) { + ok = onRestoreStudy( aDumpScript, + aStudyName, + isStudySaved ); + } + + return ok; +} + +//============================================================================ +/*! Function : onRestoreStudy + * Purpose : Load the dumped study from Python script + */ +//============================================================================ +bool SalomeApp_Application::onRestoreStudy( const QString& theDumpScript, + const QString& theStudyName, + bool theIsStudySaved ) +{ + bool ok = true; + + // create a new study + onNewDoc(); + + // get active application + SalomeApp_Application* app = dynamic_cast( SUIT_Session::session()->activeApplication() ); + + // load study from the temporary directory + QString command = QString( "execfile(r\"%1\")" ).arg( theDumpScript ); + + PyConsole_Console* pyConsole = app->pythonConsole(); + if ( pyConsole ) + pyConsole->execAndWait( command ); + + // remove temporary directory + QFileInfo aScriptInfo = QFileInfo( theDumpScript ); + QString aStudyName = aScriptInfo.baseName(); + QDir aDir = aScriptInfo.absoluteDir(); + QStringList aFiles = aDir.entryList( QStringList( "*.py*" ) ); + for( QStringList::iterator it = aFiles.begin(), itEnd = aFiles.end(); it != itEnd; ++it ) + ok = aDir.remove( *it ) && ok; + if( ok ) + ok = aDir.rmdir( aDir.absolutePath() ); + + if( SalomeApp_Study* newStudy = dynamic_cast( app->activeStudy() ) ) + { + _PTR(Study) aStudyDS = newStudy->studyDS(); + app->getNoteBook()->Init( aStudyDS ); + if( theIsStudySaved ) { + newStudy->markAsSavedIn( theStudyName ); + newStudy->Modified(); + } + } + else + ok = false; + + return ok; +} + +/*! + Close the Application +*/ +void SalomeApp_Application::closeApplication() +{ + // emit signal to restore study from Python script + emit dumpedStudyClosed( myNoteBook->getDumpedStudyScript(), + myNoteBook->getDumpedStudyName(), + myNoteBook->isDumpedStudySaved() ); + + LightApp_Application::closeApplication(); +} diff --git a/src/SalomeApp/SalomeApp_Application.h b/src/SalomeApp/SalomeApp_Application.h index b0d247dfb..fbd778537 100644 --- a/src/SalomeApp/SalomeApp_Application.h +++ b/src/SalomeApp/SalomeApp_Application.h @@ -46,7 +46,7 @@ class LightApp_Preferences; class SalomeApp_Study; -class SalomeApp_NoteBookDlg; +class SalomeApp_NoteBook; class SUIT_Desktop; class SUIT_ViewModel; @@ -69,7 +69,9 @@ class SALOMEAPP_EXPORT SalomeApp_Application : public LightApp_Application public: enum { MenuToolsId = 5 }; enum { DumpStudyId = LightApp_Application::UserID, LoadScriptId, PropertiesId, - CatalogGenId, RegDisplayId, SaveGUIStateId, FileLoadId, NoteBookId, UserID }; + CatalogGenId, RegDisplayId, SaveGUIStateId, FileLoadId, UserID }; + + typedef enum { WT_NoteBook = LightApp_Application::WT_User } WindowTypes; protected: enum { OpenRefresh = LightApp_Application::OpenReload + 1 }; @@ -104,9 +106,10 @@ public: virtual bool useStudy( const QString& ); virtual void updateDesktopTitle(); + virtual void currentWindows( QMap& ) const; - virtual void setNoteBook(SalomeApp_NoteBookDlg* theNoteBook); - virtual SalomeApp_NoteBookDlg* getNoteBook() const; + virtual void setNoteBook(SalomeApp_NoteBook* theNoteBook); + virtual SalomeApp_NoteBook* getNoteBook() const; //! update visibility state of objects void updateVisibilityState( DataObjectList& theList, @@ -125,6 +128,10 @@ public slots: virtual void onCopy(); virtual void onPaste(); void onSaveGUIState();// called from VISU + void onUpdateStudy(); // called from NoteBook + bool onRestoreStudy( const QString& theDumpScript, + const QString& theStudyName, + bool theIsStudySaved ); protected slots: void onStudyCreated( SUIT_Study* ); @@ -153,14 +160,19 @@ protected: virtual QMap activateModuleActions() const; virtual void moduleActionSelected( const int ); + virtual void defaultWindows( QMap& ) const; + void objectBrowserColumnsVisibility(); + bool updateStudy(); + + virtual void closeApplication(); + private slots: void onDeleteInvalidReferences(); void onDblClick( SUIT_DataObject* ); void onProperties(); void onDumpStudy(); - void onNoteBook(); void onLoadScript(); void onDeleteGUIState(); @@ -177,8 +189,15 @@ private: void createExtraActions(); private: - SalomeApp_NoteBookDlg* myNoteBook; + SalomeApp_NoteBook* myNoteBook; QMap myExtActions; // Map + +signals: + void dumpedStudyClosed( const QString& theDumpScript, + const QString& theStudyName, + bool theIsStudySaved ); + void notebookVarUpdated( QString theVarName ); + }; #ifdef WIN32 diff --git a/src/SalomeApp/SalomeApp_NoteBookDlg.cxx b/src/SalomeApp/SalomeApp_NoteBook.cxx similarity index 77% rename from src/SalomeApp/SalomeApp_NoteBookDlg.cxx rename to src/SalomeApp/SalomeApp_NoteBook.cxx index 4a60705de..2c50383af 100644 --- a/src/SalomeApp/SalomeApp_NoteBookDlg.cxx +++ b/src/SalomeApp/SalomeApp_NoteBook.cxx @@ -1,1149 +1,930 @@ -// Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com -// - -// File: SalomeApp_NoteBookDlg.cxx -// Author : Roman NIKOLAEV, Open CASCADE S.A.S. -// Module : GUI -// -#include // this include must be first (see PyInterp_base.h)! -#include - -#include "SalomeApp_NoteBookDlg.h" -#include "SalomeApp_Application.h" -#include "SalomeApp_Study.h" -#include "SalomeApp_VisualState.h" - -#include - -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define DEFAULT_MARGIN 11 -#define DEFAULT_SPACING 6 -#define SPACER_SIZE 120 -#define COLUMN_SIZE 180 - -#define NAME_COLUMN 0 -#define VALUE_COLUMN 1 - - -/////////////////////////////////////////////////////////////////////////// -// NoteBook_TableRow class // -/////////////////////////////////////////////////////////////////////////// -//============================================================================ -/*! Function : NoteBook_TableRow - * Purpose : Constructor - */ -//============================================================================ -NoteBook_TableRow::NoteBook_TableRow(int index, NoteBook_Table* parentTable, QWidget* parent): - QWidget(parent), - myParentTable(parentTable), - myIndex(index), - myRowHeader(new QTableWidgetItem()), - myVariableName(new QTableWidgetItem()), - myVariableValue(new QTableWidgetItem()) -{ -} - -//============================================================================ -/*! Function : ~NoteBook_TableRow - * Purpose : Destructor - */ -//============================================================================ -NoteBook_TableRow::~NoteBook_TableRow() -{ -} - -//============================================================================ -/*! Function : AddToTable - * Purpose : Add this row to the table theTable - */ -//============================================================================ -void NoteBook_TableRow::AddToTable(QTableWidget *theTable) -{ - int aPosition = theTable->rowCount(); - int aRowCount = aPosition+1; - theTable->setRowCount(aRowCount); - myRowHeader->setText(QString::number(aRowCount)); - - theTable->setVerticalHeaderItem(aPosition,myRowHeader); - theTable->setItem(aPosition, NAME_COLUMN, myVariableName); - theTable->setItem(aPosition, VALUE_COLUMN, myVariableValue); -} - -//============================================================================ -/*! Function : SetName - * Purpose : - */ -//============================================================================ -void NoteBook_TableRow::SetName(const QString theName) -{ - myVariableName->setText(theName); -} - -//============================================================================ -/*! Function : SetValue - * Purpose : - */ -//============================================================================ -void NoteBook_TableRow::SetValue(const QString theValue) -{ - myVariableValue->setText(theValue); -} - -//============================================================================ -/*! Function : GetName - * Purpose : Return variable name - */ -//============================================================================ -QString NoteBook_TableRow::GetName() const -{ - return myVariableName->text(); -} - -//============================================================================ -/*! Function : GetValue - * Purpose : Return variable value - */ -//============================================================================ -QString NoteBook_TableRow::GetValue() const -{ - return myVariableValue->text(); -} - -//============================================================================ -/*! Function : CheckName - * Purpose : Return true if variable name correct, otherwise return false - */ -//============================================================================ -bool NoteBook_TableRow::CheckName() -{ - QString aName = GetName(); - int aPos = 0; - QRegExpValidator aValidator( QRegExp("^([a-zA-Z]+)([a-zA-Z0-9_]*)$"), 0 ); - if( aName.isEmpty() || !aValidator.validate( aName, aPos ) ) - return false; - return true; -} - -//============================================================================ -/*! Function : CheckValue - * Purpose : Return true if variable value correct, otherwise return false - */ -//============================================================================ -bool NoteBook_TableRow::CheckValue() -{ - bool aResult = false; - QString aValue = GetValue(); - if(!aValue.isEmpty() && - (IsRealValue(aValue) || - IsIntegerValue(aValue) || - IsBooleanValue(aValue) || - IsValidStringValue(aValue))) - aResult = true; - - return aResult; -} - -//============================================================================ -/*! Function : GetVariableItem - * Purpose : - */ -//============================================================================ -QTableWidgetItem* NoteBook_TableRow::GetVariableItem() -{ - return myVariableValue; -} - -//============================================================================ -/*! Function : GetNameItem - * Purpose : - */ -//============================================================================ -QTableWidgetItem* NoteBook_TableRow::GetNameItem() -{ - return myVariableName; -} - -//============================================================================ -/*! Function : GetHeaderItem - * Purpose : - */ -//============================================================================ -QTableWidgetItem* NoteBook_TableRow::GetHeaderItem() -{ - return myRowHeader; -} - -//============================================================================ -/*! Function : IsRealValue - * Purpose : Return true if theValue string is real value, otherwise return - * false - */ -//============================================================================ -bool NoteBook_TableRow::IsRealValue(const QString theValue, double* theResult) -{ - bool aResult = false; - double aDResult = theValue.toDouble(&aResult); - if(aResult && theResult) - *theResult = aDResult; - - return aResult; -} - -//============================================================================ -/*! Function : IsBooleanValue - * Purpose : Return true if theValue String is boolean value, otherwise return - * false - */ -//============================================================================ -bool NoteBook_TableRow::IsBooleanValue(const QString theValue, bool* theResult){ - bool aResult = false; - bool aBResult; - if(theValue.compare("True") == 0) { - aBResult = true; - aResult = true; - } - else if(theValue.compare("False") == 0) { - aBResult = false; - aResult = true; - } - if(aResult && theResult) - *theResult = aBResult; - - return aResult; -} - -//============================================================================ -/*! Function : IsIntegerValue - * Purpose : Return true if theValue string is integer value, otherwise return - * false - */ -//============================================================================ -bool NoteBook_TableRow::IsIntegerValue(const QString theValue, int* theResult) -{ - bool aResult = false; - int anIResult; - anIResult = theValue.toInt(&aResult); - - if(aResult && theResult) - *theResult = anIResult; - - return aResult; -} - -//============================================================================ -/*! Function : IsValidStringValue - * Purpose : Return true if theValue string is valid, otherwise return - * false - * The string are always valid for the moment - * The whole notebook is verified on apply - */ -//============================================================================ -bool NoteBook_TableRow::IsValidStringValue(const QString theValue) -{ - int aNumRows = myParentTable->myRows.count(); - if( aNumRows == 0 ) - return true; - - bool aLastRowIsEmpty = myParentTable->myRows[ aNumRows - 1 ]->GetName().isEmpty() && - myParentTable->myRows[ aNumRows - 1 ]->GetValue().isEmpty(); - - SalomeApp_Application* app = dynamic_cast( SUIT_Session::session()->activeApplication() ); - PyConsole_Console* pyConsole = app->pythonConsole(); - PyConsole_Interp* pyInterp = pyConsole->getInterp(); - PyLockWrapper aLock = pyInterp->GetLockWrapper(); - std::string command = "import salome_notebook ; "; - command += "salome_notebook.checkThisNoteBook("; - for( int i = 0, n = aLastRowIsEmpty ? aNumRows - 1 : aNumRows; i < n; i++ ) { - command += myParentTable->myRows[i]->GetName().toStdString(); - command += "=\""; - command += myParentTable->myRows[i]->GetValue().toStdString(); - command += "\", "; - } - command += ") "; - - //rnv: fix for bug 21947 WinTC5.1.4: Wrong error management of "Salome NoteBook" - bool oldSuppressValue = pyConsole->isSuppressOutput(); - pyConsole->setIsSuppressOutput(true); - bool aResult = pyInterp->run(command.c_str()); - pyConsole->setIsSuppressOutput(oldSuppressValue); - return !aResult; -} - -/////////////////////////////////////////////////////////////////////////// -// NoteBook_Table class // -/////////////////////////////////////////////////////////////////////////// -//============================================================================ -/*! Function : NoteBook_Table - * Purpose : Constructor - */ -//============================================================================ -NoteBook_Table::NoteBook_Table(QWidget * parent) - :QTableWidget(parent), - isProcessItemChangedSignal(false), - myIsModified(false) -{ - setColumnCount(2); - setSelectionMode(QAbstractItemView::SingleSelection); - - //Add Headers Columns - QFont aFont = QFont(); - aFont.setBold(true); - aFont.setPointSize(10); - - //"Name" column - QTableWidgetItem * aNameHeader = new QTableWidgetItem(); - aNameHeader->setText(tr("VARNAME_COLUMN")); - aNameHeader->setFont(aFont); - setHorizontalHeaderItem(0,aNameHeader); - setColumnWidth ( 0, COLUMN_SIZE); - - //"Value" Column - QTableWidgetItem * aValueHeader = new QTableWidgetItem(); - aValueHeader->setText(tr("VARVALUE_COLUMN")); - aValueHeader->setFont(aFont); - setHorizontalHeaderItem(1,aValueHeader); - setColumnWidth ( 1, COLUMN_SIZE); - setSortingEnabled(false); - - connect(this,SIGNAL(itemChanged(QTableWidgetItem*)),this,SLOT(onItemChanged(QTableWidgetItem*))); -} - -//============================================================================ -/*! Function : ~NoteBook_Table - * Purpose : Destructor - */ -//============================================================================ -NoteBook_Table::~NoteBook_Table(){} - -//============================================================================ -/*! Function : getUniqueIndex - * Purpose : Get a unique index for the new row - */ -//============================================================================ -int NoteBook_Table::getUniqueIndex() const -{ - int anIndex = 0; - if( !myRows.isEmpty() ) - if( NoteBook_TableRow* aRow = myRows.last() ) - anIndex = aRow->GetIndex(); - - int aMaxRemovedRow = 0; - for( QListIterator anIter( myRemovedRows ); anIter.hasNext(); ) - { - int aRemovedRow = anIter.next(); - aMaxRemovedRow = qMax( aRemovedRow, aMaxRemovedRow ); - } - - anIndex = qMax( anIndex, aMaxRemovedRow ) + 1; - return anIndex; -} - -//============================================================================ -/*! Function : Init - * Purpose : Add variables in the table from theStudy - */ -//============================================================================ -void NoteBook_Table::Init(_PTR(Study) theStudy) -{ - isProcessItemChangedSignal = false; - - int aNumRows = myRows.count(); - if( aNumRows > 0 ) - { - for( int i = 0; i < myRows.size(); i++ ) - { - NoteBook_TableRow* aRow = myRows[ i ]; - if( aRow ) - { - delete aRow; - aRow = 0; - } - } - myRows.clear(); - } - setRowCount( 0 ); - - myRemovedRows.clear(); - myVariableMapRef.clear(); - myVariableMap.clear(); - - //Add all variables into the table - std::vector aVariables = theStudy->GetVariableNames(); - for(int iVar = 0; iVar < aVariables.size(); iVar++ ) { - AddRow(QString(aVariables[iVar].c_str()), - Variable2String(aVariables[iVar],theStudy)); - } - - //Add empty row - AddEmptyRow(); - isProcessItemChangedSignal = true; - - ResetMaps(); - - myStudy = theStudy; -} - -//============================================================================ -/*! Function : Variable2String - * Purpose : Convert variable values to QString - */ -//============================================================================ -QString NoteBook_Table::Variable2String(const std::string& theVarName, - _PTR(Study) theStudy) -{ - QString aResult; - if( theStudy->IsReal(theVarName) ) - aResult = QString::number(theStudy->GetReal(theVarName)); - else if( theStudy->IsInteger(theVarName) ) - aResult = QString::number(theStudy->GetInteger(theVarName)); - else if( theStudy->IsBoolean(theVarName) ) - aResult = theStudy->GetBoolean(theVarName) ? QString("True") : QString("False"); - else if( theStudy->IsString(theVarName) ) - aResult = theStudy->GetString(theVarName).c_str(); - - return aResult; -} - -//============================================================================ -/*! Function : IsValid - * Purpose : Check validity of the table data - */ -//============================================================================ -bool NoteBook_Table::IsValid() const -{ - int aNumRows = myRows.count(); - if( aNumRows == 0 ) - return true; - - bool aLastRowIsEmpty = myRows[ aNumRows - 1 ]->GetName().isEmpty() && - myRows[ aNumRows - 1 ]->GetValue().isEmpty(); - - for( int i = 0, n = aLastRowIsEmpty ? aNumRows - 1 : aNumRows; i < n; i++ ) - if( !myRows[i]->CheckName() || !IsUniqueName( myRows[i] ) || !myRows[i]->CheckValue() ) - return false; - - SalomeApp_Application* app = dynamic_cast( SUIT_Session::session()->activeApplication() ); - PyConsole_Console* pyConsole = app->pythonConsole(); - PyConsole_Interp* pyInterp = pyConsole->getInterp(); - PyLockWrapper aLock = pyInterp->GetLockWrapper(); - std::string command = "import salome_notebook ; "; - command += "salome_notebook.checkThisNoteBook("; - for( int i = 0, n = aLastRowIsEmpty ? aNumRows - 1 : aNumRows; i < n; i++ ) - { - command += myRows[i]->GetName().toStdString(); - command += "=\""; - command += myRows[i]->GetValue().toStdString(); - command += "\","; - } - command += ")"; - - //rnv: fix for bug 21947 WinTC5.1.4: Wrong error management of "Salome NoteBook" - bool oldSuppressValue = pyConsole->isSuppressOutput(); - pyConsole->setIsSuppressOutput(true); - bool aResult = pyInterp->run(command.c_str()); - pyConsole->setIsSuppressOutput(oldSuppressValue); - - return !aResult; -} - -//============================================================================ -/*! Function : RenamberRowItems - * Purpose : renumber row items - */ -//============================================================================ -void NoteBook_Table::RenamberRowItems() { - for(int i=0; iGetHeaderItem()->setText(QString::number(i+1)); - } -} - -//============================================================================ -/*! Function : AddRow - * Purpose : Add a row into the table - */ -//============================================================================ -void NoteBook_Table::AddRow(const QString& theName, const QString& theValue) -{ - int anIndex = getUniqueIndex(); - NoteBook_TableRow* aRow = new NoteBook_TableRow(anIndex, this, this); - aRow->SetName(theName); - aRow->SetValue(theValue); - aRow->AddToTable(this); - myRows.append(aRow); - - myVariableMap.insert( anIndex, NoteBoox_Variable( theName, theValue ) ); -} - -//============================================================================ -/*! Function : AddEmptyRow - * Purpose : Add an empty row into the end of the table - */ -//============================================================================ -void NoteBook_Table::AddEmptyRow() -{ - isProcessItemChangedSignal = false; - AddRow(); - isProcessItemChangedSignal = true; -} - -//============================================================================ -/*! Function : GetRowByItem - * Purpose : - */ -//============================================================================ -NoteBook_TableRow* NoteBook_Table::GetRowByItem(const QTableWidgetItem* theItem) const -{ - int aCurrentRow = row(theItem); - - if( (myRows.size() <= aCurrentRow ) && (aCurrentRow < 0)) - return NULL; - else - return myRows.at(aCurrentRow); -} - -//============================================================================ -/*! Function : IsLastRow - * Purpose : Return true if theRow is last row in the table - */ -//============================================================================ -bool NoteBook_Table::IsLastRow(const NoteBook_TableRow* theRow) const -{ - return (myRows.last() == theRow); -} - -//============================================================================ -/*! Function : onItemChanged - * Purpose : [slot] called then table item changed - */ -//============================================================================ -void NoteBook_Table::onItemChanged(QTableWidgetItem* theItem) -{ - if(isProcessItemChangedSignal) { - bool isModified = true; - NoteBook_TableRow* aRow = GetRowByItem(theItem); - if(aRow) { - int aCurrentColumn = column(theItem); - bool IsCorrect = true, IsVariableComplited = false; - QString aMsg; - - if(aCurrentColumn == NAME_COLUMN) { - int anIndex = aRow->GetIndex(); - if( myVariableMap.contains( anIndex ) ) - { - const NoteBoox_Variable& aVariable = myVariableMap[ anIndex ]; - if( !aVariable.Name.isEmpty() && myStudy->IsVariableUsed( std::string( aVariable.Name.toLatin1().constData() ) ) ) - { - if( QMessageBox::warning( parentWidget(), tr( "WARNING" ), - tr( "RENAME_VARIABLE_IS_USED" ).arg( aVariable.Name ), - QMessageBox::Yes, QMessageBox::No ) == QMessageBox::No ) - { - bool isBlocked = blockSignals( true ); - aRow->SetName( aVariable.Name ); - blockSignals( isBlocked ); - return; - } - } - } - } - - //Case then varible name changed. - if(aCurrentColumn == NAME_COLUMN) { - if(!aRow->CheckName()) { - IsCorrect = false; - aMsg = tr( "VARNAME_INCORRECT" ).arg(aRow->GetName()); - } - else if(!IsUniqueName(aRow)) { - IsCorrect = false; - aMsg = tr( "VARNAME_EXISTS" ).arg(aRow->GetName()); - } - else - IsVariableComplited = aRow->CheckValue(); - } - - //Case then varible value changed. - else if(aCurrentColumn == VALUE_COLUMN){ - if(!aRow->CheckValue()) { - IsCorrect = false; - aMsg = tr( "VARVALUE_INCORRECT" ).arg(aRow->GetName()); - } - else - IsVariableComplited = aRow->CheckName() && IsUniqueName(aRow); - } - - if(!IsCorrect && !aMsg.isEmpty()) - SUIT_MessageBox::warning( parentWidget(), tr( "WARNING" ), aMsg ); - - bool isBlocked = blockSignals( true ); - theItem->setForeground( QBrush( IsCorrect ? Qt::black : Qt::red ) ); - blockSignals( isBlocked ); - - int anIndex = aRow->GetIndex(); - if( myVariableMap.contains( anIndex ) ) - { - NoteBoox_Variable& aVariable = myVariableMap[ anIndex ]; - if( aVariable.Name.compare( aRow->GetName() ) != 0 || - aVariable.Value.compare( aRow->GetValue() ) != 0 ) - { - aVariable.Name = aRow->GetName(); - aVariable.Value = aRow->GetValue(); - } - else - isModified = false; - } - - if(IsCorrect && IsVariableComplited && IsLastRow(aRow)) - AddEmptyRow(); - } - - if( !myIsModified ) - myIsModified = isModified; - } -} - -//============================================================================ -/*! Function : IsUniqueName - * Purpose : Return true if theName is unique name of the Variable - */ -//============================================================================ -bool NoteBook_Table::IsUniqueName(const NoteBook_TableRow* theRow) const -{ - for(int i=0; iGetName().compare(theRow->GetName()) == 0) - return false; - } - return true; -} - -//============================================================================ -/*! Function : RemoveSelected - * Purpose : Remove selected rows in the table - */ -//============================================================================ -void NoteBook_Table::RemoveSelected() -{ - isProcessItemChangedSignal = false; - QList aSelectedItems = selectedItems(); - if( !(aSelectedItems.size() > 0)) { - isProcessItemChangedSignal = true; - return; - } - bool removedFromStudy = false; - for(int i=0; i < aSelectedItems.size(); i++ ) { - NoteBook_TableRow* aRow = GetRowByItem(aSelectedItems[i]); - if(aRow) { - if(IsLastRow(aRow)) { - aRow->SetName(QString()); - aRow->SetValue(QString()); - } - else { - int nRow = row(aSelectedItems[i]); - - if( myStudy->IsVariableUsed( std::string( aRow->GetName().toLatin1().constData() ) ) ) - { - if( QMessageBox::warning( parentWidget(), tr( "WARNING" ), - tr( "REMOVE_VARIABLE_IS_USED" ).arg( aRow->GetName() ), - QMessageBox::Yes, QMessageBox::No ) == QMessageBox::No ) - { - isProcessItemChangedSignal = true; - return; - } - } - - int index = aRow->GetIndex(); - QString aVarName = aRow->GetName(); - myRemovedRows.append( index ); - if( myVariableMap.contains( index ) ) - myVariableMap.remove( index ); - removeRow(nRow); - myRows.removeAt(nRow); - if(myStudy->IsVariable(aVarName.toLatin1().constData())) - removedFromStudy = true; - } - } - } - if(removedFromStudy) - myIsModified = true; - RenamberRowItems(); - isProcessItemChangedSignal = true; -} - -//============================================================================ -/*! Function : SetProcessItemChangedSignalFlag - * Purpose : - */ -//============================================================================ -void NoteBook_Table::SetProcessItemChangedSignalFlag(const bool enable) -{ - isProcessItemChangedSignal = enable; -} - -//============================================================================ -/*! Function : GetProcessItemChangedSignalFlag - * Purpose : - */ -//============================================================================ -bool NoteBook_Table::GetProcessItemChangedSignalFlag() const -{ - return isProcessItemChangedSignal; -} - -//============================================================================ -/*! Function : GetRows - * Purpose : - */ -//============================================================================ -QList NoteBook_Table::GetRows() const -{ - return myRows; -} - -//============================================================================ -/*! Function : ResetMaps - * Purpose : Reset variable maps - */ -//============================================================================ -void NoteBook_Table::ResetMaps() -{ - myIsModified = false; - myVariableMapRef = myVariableMap; - myRemovedRows.clear(); -} - -/////////////////////////////////////////////////////////////////////////// -// SalomeApp_NoteBookDlg class // -/////////////////////////////////////////////////////////////////////////// -//============================================================================ -/*! Function : SalomeApp_NoteBookDlg - * Purpose : Constructor - */ -//============================================================================ -SalomeApp_NoteBookDlg::SalomeApp_NoteBookDlg(QWidget * parent, _PTR(Study) theStudy): - QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), - myStudy(theStudy) -{ - setModal(false); - setObjectName("SalomeApp_NoteBookDlg"); - setWindowTitle(tr("NOTEBOOK_TITLE")); - QGridLayout* aLayout = new QGridLayout(this); - aLayout->setMargin(DEFAULT_MARGIN); - aLayout->setSpacing(DEFAULT_SPACING); - - //Table - myTable = new NoteBook_Table(this); - aLayout->addWidget(myTable, 0, 0, 1, 3); - - //Buttons - myRemoveButton = new QPushButton(tr("BUT_REMOVE")); - aLayout->addWidget(myRemoveButton, 1, 0, 1, 1); - - QSpacerItem* spacer = - new QSpacerItem(DEFAULT_SPACING, 5 , QSizePolicy::Expanding, QSizePolicy::Minimum); - aLayout->addItem(spacer, 1, 1, 2, 1); - - myUpdateStudyBtn = new QPushButton(tr("BUT_UPDATE_STUDY")); - aLayout->addWidget(myUpdateStudyBtn, 1, 2, 1, 1); - - QGroupBox* groupBox = new QGroupBox(this); - - QGridLayout* aLayout1 = new QGridLayout(groupBox); - - aLayout1->setMargin(DEFAULT_MARGIN); - aLayout1->setSpacing(DEFAULT_SPACING); - - myOkBtn = new QPushButton(tr("BUT_APPLY_AND_CLOSE")); - aLayout1->addWidget(myOkBtn, 0, 0, 1, 1); - - myApplyBtn = new QPushButton(tr("BUT_APPLY")); - aLayout1->addWidget(myApplyBtn, 0, 1, 1, 1); - - QSpacerItem* spacer1 = - new QSpacerItem(DEFAULT_SPACING, 5, QSizePolicy::Expanding, QSizePolicy::Minimum); - aLayout1->addItem(spacer1, 0, 2, 1, 1); - - myCancelBtn = new QPushButton(tr("BUT_CLOSE")); - aLayout1->addWidget(myCancelBtn, 0, 3, 1, 1); - - myHelpBtn = new QPushButton(tr("BUT_HELP")); - aLayout1->addWidget(myHelpBtn, 0, 4, 1, 1); - - aLayout->addWidget(groupBox, 2, 0, 1, 3); - - QWidgetList aWidgetList; - aWidgetList.append( myTable ); - aWidgetList.append( myOkBtn ); - aWidgetList.append( myApplyBtn ); - aWidgetList.append( myCancelBtn ); - aWidgetList.append( myHelpBtn ); - aWidgetList.append( myUpdateStudyBtn ); - aWidgetList.append( myRemoveButton ); - Qtx::setTabOrder( aWidgetList ); - - connect( myOkBtn, SIGNAL(clicked()), this, SLOT(onOK()) ); - connect( myApplyBtn, SIGNAL(clicked()), this, SLOT(onApply()) ); - connect( myCancelBtn, SIGNAL(clicked()), this, SLOT(onCancel()) ); - connect( myUpdateStudyBtn, SIGNAL(clicked()), this, SLOT(onUpdateStudy()) ); - connect( myRemoveButton, SIGNAL(clicked()), this, SLOT(onRemove())); - connect( myHelpBtn, SIGNAL(clicked()), this, SLOT(onHelp())); - - myTable->Init(myStudy); -} - -//============================================================================ -/*! Function : ~SalomeApp_NoteBookDlg - * Purpose : Destructor - */ -//============================================================================ -SalomeApp_NoteBookDlg::~SalomeApp_NoteBookDlg(){} - - -//============================================================================ -/*! Function : Init() - * Purpose : init variable table - */ -//============================================================================ -void SalomeApp_NoteBookDlg::Init(_PTR(Study) theStudy){ - if(myStudy!= theStudy) - myStudy = theStudy; - myTable->Init(myStudy); -} - - -//============================================================================ -/*! Function : onOK - * Purpose : [slot] - */ -//============================================================================ -void SalomeApp_NoteBookDlg::onOK() -{ - onApply(); - if( myTable->IsValid() ) - accept(); -} - -//============================================================================ -/*! Function : onHelp - * Purpose : [slot] - */ -//============================================================================ -void SalomeApp_NoteBookDlg::onHelp() -{ - QString aHelpFileName("using_notebook.html"); - LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); - if (app) - app->onHelpContextModule("GUI",aHelpFileName); - else { - QString platform; -#ifdef WIN32 - platform = "winapplication"; -#else - platform = "application"; -#endif - SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(aHelpFileName)); - } - -} -//============================================================================ -/*! Function : onApply - * Purpose : [slot] - */ -//============================================================================ -void SalomeApp_NoteBookDlg::onApply() -{ - if( !myTable->IsValid() ) - { - SUIT_MessageBox::warning( this, tr( "WARNING" ), tr( "INCORRECT_DATA" ) ); - return; - } - - double aDVal; - int anIVal; - bool aBVal; - - const QList& aRemovedRows = myTable->GetRemovedRows(); - const VariableMap& aVariableMap = myTable->GetVariableMap(); - const VariableMap& aVariableMapRef = myTable->GetVariableMapRef(); - - for( QListIterator anIter( aRemovedRows ); anIter.hasNext(); ) - { - int anIndex = anIter.next(); - if( aVariableMapRef.contains( anIndex ) ) - { - QString aRemovedVariable = aVariableMapRef[ anIndex ].Name; - myStudy->RemoveVariable( std::string( aRemovedVariable.toLatin1().constData() ) ); - } - } - - VariableMap::const_iterator it = aVariableMap.constBegin(), itEnd = aVariableMap.constEnd(); - for( ; it != itEnd; ++it ) - { - int anIndex = it.key(); - const NoteBoox_Variable& aVariable = it.value(); - QString aName = aVariable.Name; - QString aValue = aVariable.Value; - - if( !aName.isEmpty() && !aValue.isEmpty() ) - { - if( aVariableMapRef.contains( anIndex ) ) - { - const NoteBoox_Variable& aVariableRef = aVariableMapRef[ anIndex ]; - QString aNameRef = aVariableRef.Name; - QString aValueRef = aVariableRef.Value; - - if( !aNameRef.isEmpty() && !aValueRef.isEmpty() && aNameRef != aName ) - { - myStudy->RenameVariable( std::string( aNameRef.toLatin1().constData() ), - std::string( aName.toLatin1().constData() ) ); - } - } - - if( NoteBook_TableRow::IsIntegerValue(aValue,&anIVal) ) - myStudy->SetInteger(std::string(aName.toLatin1().constData()),anIVal); - - else if( NoteBook_TableRow::IsRealValue(aValue,&aDVal) ) - myStudy->SetReal(std::string(aName.toLatin1().constData()),aDVal); - - else if( NoteBook_TableRow::IsBooleanValue(aValue,&aBVal) ) - myStudy->SetBoolean(std::string(aName.toLatin1().constData()),aBVal); - - else - myStudy->SetString(std::string(aName.toLatin1().constData()),aValue.toStdString()); - } - } - myTable->ResetMaps(); - - SalomeApp_Application* app = dynamic_cast( SUIT_Session::session()->activeApplication() ); - if(app) - app->updateActions(); - -} - -//============================================================================ -/*! Function : onCancel - * Purpose : [slot] - */ -//============================================================================ -void SalomeApp_NoteBookDlg::onCancel() -{ - if( myTable->IsModified() ) - { - int answer = QMessageBox::question( this, tr( "CLOSE_CAPTION" ), tr( "CLOSE_DESCRIPTION" ), - QMessageBox::Yes, QMessageBox::No, QMessageBox::Cancel ); - switch( answer ) - { - case QMessageBox::Yes : onOK(); return; - case QMessageBox::No : break; - case QMessageBox::Cancel : return; - default : break; - } - } - reject(); -} - -//============================================================================ -/*! Function : onRemove - * Purpose : [slot] - */ -//============================================================================ -void SalomeApp_NoteBookDlg::onRemove() -{ - myTable->RemoveSelected(); -} - -//============================================================================ -/*! Function : onUpdateStudy - * Purpose : [slot] - */ -//============================================================================ -void SalomeApp_NoteBookDlg::onUpdateStudy() -{ - onApply(); - if( !myTable->IsValid() ) - return; - - QApplication::setOverrideCursor( Qt::WaitCursor ); - - if( !updateStudy() ) - SUIT_MessageBox::warning( this, tr( "ERROR" ), tr( "ERR_UPDATE_STUDY_FAILED" ) ); - - QApplication::restoreOverrideCursor(); -} - -//============================================================================ -/*! Function : updateStudy - * Purpose : - */ -//============================================================================ -bool SalomeApp_NoteBookDlg::updateStudy() -{ - SalomeApp_Application* app = dynamic_cast( SUIT_Session::session()->activeApplication() ); - if( !app ) - return false; - - SalomeApp_Study* study = dynamic_cast( app->activeStudy() ); - if( !study ) - return false; - - bool isStudySaved = study->isSaved(); - QString aStudyName = study->studyName(); - - _PTR(Study) studyDS = study->studyDS(); - - // get unique temporary directory name - QString aTmpDir = QString::fromStdString( SALOMEDS_Tool::GetTmpDir() ); - if( aTmpDir.isEmpty() ) - return false; - - if( aTmpDir.right( 1 ).compare( QDir::separator() ) == 0 ) - aTmpDir.remove( aTmpDir.length() - 1, 1 ); - - // dump study to the temporary directory - QString aFileName( "notebook" ); - bool toPublish = true; - bool isMultiFile = false; - bool toSaveGUI = true; - - int savePoint; - _PTR(AttributeParameter) ap; - _PTR(IParameters) ip = ClientFactory::getIParameters(ap); - if(ip->isDumpPython(studyDS)) ip->setDumpPython(studyDS); //Unset DumpPython flag. - if ( toSaveGUI ) { //SRN: Store a visual state of the study at the save point for DumpStudy method - ip->setDumpPython(studyDS); - savePoint = SalomeApp_VisualState( app ).storeState(); //SRN: create a temporary save point - } - bool ok = studyDS->DumpStudy( aTmpDir.toStdString(), aFileName.toStdString(), toPublish, isMultiFile ); - if ( toSaveGUI ) - study->removeSavePoint(savePoint); //SRN: remove the created temporary save point. - - if( !ok ) - return false; - - // clear a study (delete all objects) - clearStudy(); - - // get active application - app = dynamic_cast( SUIT_Session::session()->activeApplication() ); - - // load study from the temporary directory - QString command = QString( "execfile(r\"%1\")" ).arg( aTmpDir + QDir::separator() + aFileName + ".py" ); - - PyConsole_Console* pyConsole = app->pythonConsole(); - if ( pyConsole ) - pyConsole->execAndWait( command ); - - // remove temporary directory - QDir aDir( aTmpDir ); - QStringList aFiles = aDir.entryList( QStringList( "*.py*" ) ); - for( QStringList::iterator it = aFiles.begin(), itEnd = aFiles.end(); it != itEnd; ++it ) - ok = aDir.remove( *it ) && ok; - if( ok ) - ok = aDir.rmdir( aTmpDir ); - - if( SalomeApp_Study* newStudy = dynamic_cast( app->activeStudy() ) ) - { - myStudy = newStudy->studyDS(); - myTable->Init(myStudy); - if(isStudySaved) { - newStudy->markAsSavedIn(aStudyName); - } - } - else - ok = false; - - return ok; -} - -//============================================================================ -/*! Function : clearStudy - * Purpose : - */ -//============================================================================ -void SalomeApp_NoteBookDlg::clearStudy() -{ - SalomeApp_Application* app = dynamic_cast( SUIT_Session::session()->activeApplication() ); - if( !app ) - return; - - QList aList = SUIT_Session::session()->applications(); - int anIndex = aList.indexOf( app ); - - //Store position and size of the this dialog - int aW = width(); - int aH = height(); - int aX = x(); - int aY = y(); - - // Disconnect dialog from application desktop in case if: - // 1) Application is not the first application in the session - // 2) Application is the first application in session but not the only. - bool changeDesktop = ((anIndex > 0) || (anIndex == 0 && aList.count() > 1)); - - if( changeDesktop ) - setParent( 0 ); - - app->onCloseDoc( false ); - - if( anIndex > 0 && anIndex < aList.count() ) - app = dynamic_cast( aList[ anIndex - 1 ] ); - else if(anIndex == 0 && aList.count() > 1) - app = dynamic_cast( aList[ 1 ] ); - - if( !app ) - return; - - app->onNewDoc(); - - app = dynamic_cast( SUIT_Session::session()->activeApplication() ); - if( changeDesktop && app ) { - setParent( app->desktop(), Qt::Dialog ); - app->setNoteBook(this); - } - //Set position and size of the this dialog - resize( aW, aH ); - move( aX, aY ); - show(); -} +// Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +// File: SalomeApp_NoteBook.cxx +// Author : Roman NIKOLAEV, Open CASCADE S.A.S. +// Module : GUI +// +#include // this include must be first (see PyInterp_base.h)! +#include + +#include "SalomeApp_NoteBook.h" +#include "SalomeApp_Application.h" +#include "SalomeApp_Study.h" +#include "SalomeApp_VisualState.h" + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEFAULT_MARGIN 11 +#define DEFAULT_SPACING 6 +#define SPACER_SIZE 100 +#define COLUMN_SIZE 100 + +#define NAME_COLUMN 0 +#define VALUE_COLUMN 1 + + +/////////////////////////////////////////////////////////////////////////// +// NoteBook_TableRow class // +/////////////////////////////////////////////////////////////////////////// +//============================================================================ +/*! Function : NoteBook_TableRow + * Purpose : Constructor + */ +//============================================================================ +NoteBook_TableRow::NoteBook_TableRow(int index, NoteBook_Table* parentTable, QWidget* parent): + QWidget(parent), + myParentTable(parentTable), + myIndex(index), + myRowHeader(new QTableWidgetItem()), + myVariableName(new QTableWidgetItem()), + myVariableValue(new QTableWidgetItem()) +{ +} + +//============================================================================ +/*! Function : ~NoteBook_TableRow + * Purpose : Destructor + */ +//============================================================================ +NoteBook_TableRow::~NoteBook_TableRow() +{ +} + +//============================================================================ +/*! Function : AddToTable + * Purpose : Add this row to the table theTable + */ +//============================================================================ +void NoteBook_TableRow::AddToTable(QTableWidget *theTable) +{ + int aPosition = theTable->rowCount(); + int aRowCount = aPosition+1; + theTable->setRowCount(aRowCount); + myRowHeader->setText(QString::number(aRowCount)); + + theTable->setVerticalHeaderItem(aPosition,myRowHeader); + theTable->setItem(aPosition, NAME_COLUMN, myVariableName); + theTable->setItem(aPosition, VALUE_COLUMN, myVariableValue); +} + +//============================================================================ +/*! Function : SetName + * Purpose : + */ +//============================================================================ +void NoteBook_TableRow::SetName(const QString theName) +{ + myVariableName->setText(theName); +} + +//============================================================================ +/*! Function : SetValue + * Purpose : + */ +//============================================================================ +void NoteBook_TableRow::SetValue(const QString theValue) +{ + myVariableValue->setText(theValue); +} + +//============================================================================ +/*! Function : GetName + * Purpose : Return variable name + */ +//============================================================================ +QString NoteBook_TableRow::GetName() const +{ + return myVariableName->text(); +} + +//============================================================================ +/*! Function : GetValue + * Purpose : Return variable value + */ +//============================================================================ +QString NoteBook_TableRow::GetValue() const +{ + return myVariableValue->text(); +} + +//============================================================================ +/*! Function : CheckName + * Purpose : Return true if variable name correct, otherwise return false + */ +//============================================================================ +bool NoteBook_TableRow::CheckName() +{ + QString aName = GetName(); + int aPos = 0; + QRegExpValidator aValidator( QRegExp("^([a-zA-Z]+)([a-zA-Z0-9_]*)$"), 0 ); + if( aName.isEmpty() || !aValidator.validate( aName, aPos ) ) + return false; + return true; +} + +//============================================================================ +/*! Function : CheckValue + * Purpose : Return true if variable value correct, otherwise return false + */ +//============================================================================ +bool NoteBook_TableRow::CheckValue() +{ + bool aResult = false; + QString aValue = GetValue(); + if(!aValue.isEmpty() && + (IsRealValue(aValue) || + IsIntegerValue(aValue) || + IsBooleanValue(aValue) || + IsValidStringValue(aValue))) + aResult = true; + + return aResult; +} + +//============================================================================ +/*! Function : GetVariableItem + * Purpose : + */ +//============================================================================ +QTableWidgetItem* NoteBook_TableRow::GetVariableItem() +{ + return myVariableValue; +} + +//============================================================================ +/*! Function : GetNameItem + * Purpose : + */ +//============================================================================ +QTableWidgetItem* NoteBook_TableRow::GetNameItem() +{ + return myVariableName; +} + +//============================================================================ +/*! Function : GetHeaderItem + * Purpose : + */ +//============================================================================ +QTableWidgetItem* NoteBook_TableRow::GetHeaderItem() +{ + return myRowHeader; +} + +//============================================================================ +/*! Function : IsRealValue + * Purpose : Return true if theValue string is real value, otherwise return + * false + */ +//============================================================================ +bool NoteBook_TableRow::IsRealValue(const QString theValue, double* theResult) +{ + bool aResult = false; + double aDResult = theValue.toDouble(&aResult); + if(aResult && theResult) + *theResult = aDResult; + + return aResult; +} + +//============================================================================ +/*! Function : IsBooleanValue + * Purpose : Return true if theValue String is boolean value, otherwise return + * false + */ +//============================================================================ +bool NoteBook_TableRow::IsBooleanValue(const QString theValue, bool* theResult){ + bool aResult = false; + bool aBResult; + if(theValue.compare("True") == 0) { + aBResult = true; + aResult = true; + } + else if(theValue.compare("False") == 0) { + aBResult = false; + aResult = true; + } + if(aResult && theResult) + *theResult = aBResult; + + return aResult; +} + +//============================================================================ +/*! Function : IsIntegerValue + * Purpose : Return true if theValue string is integer value, otherwise return + * false + */ +//============================================================================ +bool NoteBook_TableRow::IsIntegerValue(const QString theValue, int* theResult) +{ + bool aResult = false; + int anIResult; + anIResult = theValue.toInt(&aResult); + + if(aResult && theResult) + *theResult = anIResult; + + return aResult; +} + +//============================================================================ +/*! Function : IsValidStringValue + * Purpose : Return true if theValue string is valid, otherwise return + * false + * The string are always valid for the moment + * The whole notebook is verified on apply + */ +//============================================================================ +bool NoteBook_TableRow::IsValidStringValue(const QString theValue) +{ + int aNumRows = myParentTable->myRows.count(); + if( aNumRows == 0 ) + return true; + + bool aLastRowIsEmpty = myParentTable->myRows[ aNumRows - 1 ]->GetName().isEmpty() && + myParentTable->myRows[ aNumRows - 1 ]->GetValue().isEmpty(); + + SalomeApp_Application* app = dynamic_cast( SUIT_Session::session()->activeApplication() ); + PyConsole_Console* pyConsole = app->pythonConsole(); + PyConsole_Interp* pyInterp = pyConsole->getInterp(); + PyLockWrapper aLock = pyInterp->GetLockWrapper(); + std::string command = "import salome_notebook ; "; + command += "salome_notebook.checkThisNoteBook("; + for( int i = 0, n = aLastRowIsEmpty ? aNumRows - 1 : aNumRows; i < n; i++ ) { + command += myParentTable->myRows[i]->GetName().toStdString(); + command += "=\""; + command += myParentTable->myRows[i]->GetValue().toStdString(); + command += "\", "; + } + command += ") "; + + //rnv: fix for bug 21947 WinTC5.1.4: Wrong error management of "Salome NoteBook" + bool oldSuppressValue = pyConsole->isSuppressOutput(); + pyConsole->setIsSuppressOutput(true); + bool aResult = pyInterp->run(command.c_str()); + pyConsole->setIsSuppressOutput(oldSuppressValue); + return !aResult; +} + +/////////////////////////////////////////////////////////////////////////// +// NoteBook_Table class // +/////////////////////////////////////////////////////////////////////////// +//============================================================================ +/*! Function : NoteBook_Table + * Purpose : Constructor + */ +//============================================================================ +NoteBook_Table::NoteBook_Table(QWidget * parent) + :QTableWidget(parent), + isProcessItemChangedSignal(false), + myIsModified(false) +{ + setColumnCount(2); + setSelectionMode(QAbstractItemView::SingleSelection); + + //Add Headers Columns + QFont aFont = QFont(); + aFont.setPointSize(10); + + //"Name" column + QTableWidgetItem * aNameHeader = new QTableWidgetItem(); + aNameHeader->setText(tr("VARNAME_COLUMN")); + aNameHeader->setFont(aFont); + setHorizontalHeaderItem(0,aNameHeader); + setColumnWidth ( 0, COLUMN_SIZE); + + //"Value" Column + QTableWidgetItem * aValueHeader = new QTableWidgetItem(); + aValueHeader->setText(tr("VARVALUE_COLUMN")); + aValueHeader->setFont(aFont); + setHorizontalHeaderItem(1,aValueHeader); + setColumnWidth ( 1, COLUMN_SIZE); + setSortingEnabled(false); + + connect(this,SIGNAL(itemChanged(QTableWidgetItem*)),this,SLOT(onItemChanged(QTableWidgetItem*))); +} + +//============================================================================ +/*! Function : ~NoteBook_Table + * Purpose : Destructor + */ +//============================================================================ +NoteBook_Table::~NoteBook_Table(){} + +//============================================================================ +/*! Function : getUniqueIndex + * Purpose : Get a unique index for the new row + */ +//============================================================================ +int NoteBook_Table::getUniqueIndex() const +{ + int anIndex = 0; + if( !myRows.isEmpty() ) + if( NoteBook_TableRow* aRow = myRows.last() ) + anIndex = aRow->GetIndex(); + + int aMaxRemovedRow = 0; + for( QListIterator anIter( myRemovedRows ); anIter.hasNext(); ) + { + int aRemovedRow = anIter.next(); + aMaxRemovedRow = qMax( aRemovedRow, aMaxRemovedRow ); + } + + anIndex = qMax( anIndex, aMaxRemovedRow ) + 1; + return anIndex; +} + +//============================================================================ +/*! Function : Init + * Purpose : Add variables in the table from theStudy + */ +//============================================================================ +void NoteBook_Table::Init(_PTR(Study) theStudy) +{ + isProcessItemChangedSignal = false; + + int aNumRows = myRows.count(); + if( aNumRows > 0 ) + { + for( int i = 0; i < myRows.size(); i++ ) + { + NoteBook_TableRow* aRow = myRows[ i ]; + if( aRow ) + { + delete aRow; + aRow = 0; + } + } + myRows.clear(); + } + setRowCount( 0 ); + + myRemovedRows.clear(); + myVariableMapRef.clear(); + myVariableMap.clear(); + + //Add all variables into the table + std::vector aVariables = theStudy->GetVariableNames(); + for(int iVar = 0; iVar < aVariables.size(); iVar++ ) { + AddRow(QString(aVariables[iVar].c_str()), + Variable2String(aVariables[iVar],theStudy)); + } + + //Add empty row + AddEmptyRow(); + isProcessItemChangedSignal = true; + + ResetMaps(); + + myStudy = theStudy; +} + +//============================================================================ +/*! Function : Variable2String + * Purpose : Convert variable values to QString + */ +//============================================================================ +QString NoteBook_Table::Variable2String(const std::string& theVarName, + _PTR(Study) theStudy) +{ + QString aResult; + if( theStudy->IsReal(theVarName) ) + aResult = QString::number(theStudy->GetReal(theVarName)); + else if( theStudy->IsInteger(theVarName) ) + aResult = QString::number(theStudy->GetInteger(theVarName)); + else if( theStudy->IsBoolean(theVarName) ) + aResult = theStudy->GetBoolean(theVarName) ? QString("True") : QString("False"); + else if( theStudy->IsString(theVarName) ) + aResult = theStudy->GetString(theVarName).c_str(); + + return aResult; +} + +//============================================================================ +/*! Function : IsValid + * Purpose : Check validity of the table data + */ +//============================================================================ +bool NoteBook_Table::IsValid() const +{ + int aNumRows = myRows.count(); + if( aNumRows == 0 ) + return true; + + bool aLastRowIsEmpty = myRows[ aNumRows - 1 ]->GetName().isEmpty() && + myRows[ aNumRows - 1 ]->GetValue().isEmpty(); + + for( int i = 0, n = aLastRowIsEmpty ? aNumRows - 1 : aNumRows; i < n; i++ ) + if( !myRows[i]->CheckName() || !IsUniqueName( myRows[i] ) || !myRows[i]->CheckValue() ) + return false; + + SalomeApp_Application* app = dynamic_cast( SUIT_Session::session()->activeApplication() ); + PyConsole_Console* pyConsole = app->pythonConsole(); + PyConsole_Interp* pyInterp = pyConsole->getInterp(); + PyLockWrapper aLock = pyInterp->GetLockWrapper(); + std::string command = "import salome_notebook ; "; + command += "salome_notebook.checkThisNoteBook("; + for( int i = 0, n = aLastRowIsEmpty ? aNumRows - 1 : aNumRows; i < n; i++ ) + { + command += myRows[i]->GetName().toStdString(); + command += "=\""; + command += myRows[i]->GetValue().toStdString(); + command += "\","; + } + command += ")"; + + //rnv: fix for bug 21947 WinTC5.1.4: Wrong error management of "Salome NoteBook" + bool oldSuppressValue = pyConsole->isSuppressOutput(); + pyConsole->setIsSuppressOutput(true); + bool aResult = pyInterp->run(command.c_str()); + pyConsole->setIsSuppressOutput(oldSuppressValue); + + return !aResult; +} + +//============================================================================ +/*! Function : RenumberRowItems + * Purpose : renumber row items + */ +//============================================================================ +void NoteBook_Table::RenumberRowItems() { + for(int i=0; iGetHeaderItem()->setText(QString::number(i+1)); + } +} + +//============================================================================ +/*! Function : AddRow + * Purpose : Add a row into the table + */ +//============================================================================ +void NoteBook_Table::AddRow(const QString& theName, const QString& theValue) +{ + int anIndex = getUniqueIndex(); + NoteBook_TableRow* aRow = new NoteBook_TableRow(anIndex, this, this); + aRow->SetName(theName); + aRow->SetValue(theValue); + aRow->AddToTable(this); + myRows.append(aRow); + + myVariableMap.insert( anIndex, NoteBoox_Variable( theName, theValue ) ); +} + +//============================================================================ +/*! Function : AddEmptyRow + * Purpose : Add an empty row into the end of the table + */ +//============================================================================ +void NoteBook_Table::AddEmptyRow() +{ + isProcessItemChangedSignal = false; + AddRow(); + isProcessItemChangedSignal = true; +} + +//============================================================================ +/*! Function : GetRowByItem + * Purpose : + */ +//============================================================================ +NoteBook_TableRow* NoteBook_Table::GetRowByItem(const QTableWidgetItem* theItem) const +{ + int aCurrentRow = row(theItem); + + if( (myRows.size() <= aCurrentRow ) && (aCurrentRow < 0)) + return NULL; + else + return myRows.at(aCurrentRow); +} + +//============================================================================ +/*! Function : IsLastRow + * Purpose : Return true if theRow is last row in the table + */ +//============================================================================ +bool NoteBook_Table::IsLastRow(const NoteBook_TableRow* theRow) const +{ + return (myRows.last() == theRow); +} + +//============================================================================ +/*! Function : onItemChanged + * Purpose : [slot] called when table item changed + */ +//============================================================================ +void NoteBook_Table::onItemChanged(QTableWidgetItem* theItem) +{ + if(isProcessItemChangedSignal) { + bool isModified = true; + NoteBook_TableRow* aRow = GetRowByItem(theItem); + if(aRow) { + int aCurrentColumn = column(theItem); + bool IsCorrect = true, IsVariableComplited = false; + QString aMsg; + + if(aCurrentColumn == NAME_COLUMN) { + int anIndex = aRow->GetIndex(); + if( myVariableMap.contains( anIndex ) ) + { + const NoteBoox_Variable& aVariable = myVariableMap[ anIndex ]; + if( !aVariable.Name.isEmpty() && myStudy->IsVariableUsed( std::string( aVariable.Name.toLatin1().constData() ) ) ) + { + if( QMessageBox::warning( parentWidget(), tr( "WARNING" ), + tr( "RENAME_VARIABLE_IS_USED" ).arg( aVariable.Name ), + QMessageBox::Yes, QMessageBox::No ) == QMessageBox::No ) + { + bool isBlocked = blockSignals( true ); + aRow->SetName( aVariable.Name ); + blockSignals( isBlocked ); + return; + } + } + } + } + + //Case when variable name was changed. + if(aCurrentColumn == NAME_COLUMN) { + if(!aRow->CheckName()) { + IsCorrect = false; + aMsg = tr( "VARNAME_INCORRECT" ).arg(aRow->GetName()); + } + else if(!IsUniqueName(aRow)) { + IsCorrect = false; + aMsg = tr( "VARNAME_EXISTS" ).arg(aRow->GetName()); + } + else + IsVariableComplited = aRow->CheckValue(); + } + + //Case when variable value was changed. + else if(aCurrentColumn == VALUE_COLUMN){ + if(!aRow->CheckValue()) { + IsCorrect = false; + aMsg = tr( "VARVALUE_INCORRECT" ).arg(aRow->GetName()); + } + else + IsVariableComplited = aRow->CheckName() && IsUniqueName(aRow); + } + + if(!IsCorrect && !aMsg.isEmpty()) + SUIT_MessageBox::warning( parentWidget(), tr( "WARNING" ), aMsg ); + + bool isBlocked = blockSignals( true ); + theItem->setForeground( QBrush( IsCorrect ? Qt::black : Qt::red ) ); + blockSignals( isBlocked ); + + int anIndex = aRow->GetIndex(); + if( myVariableMap.contains( anIndex ) ) + { + NoteBoox_Variable& aVariable = myVariableMap[ anIndex ]; + if( aVariable.Name.compare( aRow->GetName() ) != 0 || + aVariable.Value.compare( aRow->GetValue() ) != 0 ) + { + aVariable.Name = aRow->GetName(); + aVariable.Value = aRow->GetValue(); + } + else + isModified = false; + } + + if(IsCorrect && IsVariableComplited && IsLastRow(aRow)) + AddEmptyRow(); + + if( aRow->CheckName() && aRow->CheckValue() ) + qobject_cast(parentWidget())->onApply(); + } + + if( !myIsModified ) + myIsModified = isModified; + } +} + +//============================================================================ +/*! Function : IsUniqueName + * Purpose : Return true if theName is unique name of the Variable + */ +//============================================================================ +bool NoteBook_Table::IsUniqueName(const NoteBook_TableRow* theRow) const +{ + for(int i=0; iGetName().compare(theRow->GetName()) == 0) + return false; + } + return true; +} + +//============================================================================ +/*! Function : RemoveSelected + * Purpose : Remove selected rows in the table + */ +//============================================================================ +void NoteBook_Table::RemoveSelected() +{ + isProcessItemChangedSignal = false; + QList aSelectedItems = selectedItems(); + if( !(aSelectedItems.size() > 0)) { + isProcessItemChangedSignal = true; + return; + } + bool removedFromStudy = false; + for(int i=0; i < aSelectedItems.size(); i++ ) { + NoteBook_TableRow* aRow = GetRowByItem(aSelectedItems[i]); + if(aRow) { + if(IsLastRow(aRow)) { + aRow->SetName(QString()); + aRow->SetValue(QString()); + } + else { + int nRow = row(aSelectedItems[i]); + + if( myStudy->IsVariableUsed( std::string( aRow->GetName().toLatin1().constData() ) ) ) + { + if( QMessageBox::warning( parentWidget(), tr( "WARNING" ), + tr( "REMOVE_VARIABLE_IS_USED" ).arg( aRow->GetName() ), + QMessageBox::Yes, QMessageBox::No ) == QMessageBox::No ) + { + isProcessItemChangedSignal = true; + return; + } + } + + int index = aRow->GetIndex(); + QString aVarName = aRow->GetName(); + myRemovedRows.append( index ); + if( myVariableMap.contains( index ) ) + myVariableMap.remove( index ); + removeRow(nRow); + myRows.removeAt(nRow); + if(myStudy->IsVariable(aVarName.toLatin1().constData())) + removedFromStudy = true; + } + } + } + if(removedFromStudy) + myIsModified = true; + RenumberRowItems(); + isProcessItemChangedSignal = true; +} + +//============================================================================ +/*! Function : SetProcessItemChangedSignalFlag + * Purpose : + */ +//============================================================================ +void NoteBook_Table::SetProcessItemChangedSignalFlag(const bool enable) +{ + isProcessItemChangedSignal = enable; +} + +//============================================================================ +/*! Function : GetProcessItemChangedSignalFlag + * Purpose : + */ +//============================================================================ +bool NoteBook_Table::GetProcessItemChangedSignalFlag() const +{ + return isProcessItemChangedSignal; +} + +//============================================================================ +/*! Function : GetRows + * Purpose : + */ +//============================================================================ +QList NoteBook_Table::GetRows() const +{ + return myRows; +} + +//============================================================================ +/*! Function : ResetMaps + * Purpose : Reset variable maps + */ +//============================================================================ +void NoteBook_Table::ResetMaps() +{ + myIsModified = false; + myVariableMapRef = myVariableMap; + myRemovedRows.clear(); +} + +/////////////////////////////////////////////////////////////////////////// +// SalomeApp_NoteBook class // +/////////////////////////////////////////////////////////////////////////// +//============================================================================ +/*! Function : SalomeApp_NoteBook + * Purpose : Constructor + */ +//============================================================================ +SalomeApp_NoteBook::SalomeApp_NoteBook(QWidget * parent, _PTR(Study) theStudy): + QWidget(parent), + myStudy(theStudy) +{ + setObjectName("SalomeApp_NoteBook"); + setWindowTitle(tr("NOTEBOOK_TITLE")); + QGridLayout* aLayout = new QGridLayout(this); + + //Table + myTable = new NoteBook_Table(this); + aLayout->addWidget(myTable, 0, 0, 1, 3); + + //Buttons + myRemoveButton = new QPushButton(tr("BUT_REMOVE")); + aLayout->addWidget(myRemoveButton, 1, 0); + + QSpacerItem* spacer = + new QSpacerItem(DEFAULT_SPACING, 5 , QSizePolicy::Expanding, QSizePolicy::Minimum); + aLayout->addItem(spacer, 1, 1); + + myUpdateStudyBtn = new QPushButton(tr("BUT_UPDATE_STUDY")); + aLayout->addWidget(myUpdateStudyBtn, 1, 2); + + QWidgetList aWidgetList; + aWidgetList.append( myTable ); + aWidgetList.append( myUpdateStudyBtn ); + aWidgetList.append( myRemoveButton ); + Qtx::setTabOrder( aWidgetList ); + + connect( myUpdateStudyBtn, SIGNAL(clicked()), this, SLOT(onUpdateStudy()) ); + connect( myRemoveButton, SIGNAL(clicked()), this, SLOT(onRemove())); + + myTable->Init(myStudy); + + myDumpedStudyScript = ""; + myIsDumpedStudySaved = false; +} + +//============================================================================ +/*! Function : ~SalomeApp_NoteBook + * Purpose : Destructor + */ +//============================================================================ +SalomeApp_NoteBook::~SalomeApp_NoteBook(){} + + +//============================================================================ +/*! Function : Init() + * Purpose : init variable table + */ +//============================================================================ +void SalomeApp_NoteBook::Init(_PTR(Study) theStudy){ + if(myStudy!= theStudy) + myStudy = theStudy; + myTable->Init(myStudy); +} + + +//============================================================================ +/*! Function : onVarUpdate + * Purpose : [slot] + */ +//============================================================================ +void SalomeApp_NoteBook::onVarUpdate(QString theVarName) +{ + myTable->Init(myStudy); +} + +//============================================================================ +/*! Function : onApply + * Purpose : [slot] + */ +//============================================================================ +void SalomeApp_NoteBook::onApply() +{ + if( !myTable->IsValid() ) + { + SUIT_MessageBox::warning( this, tr( "WARNING" ), tr( "INCORRECT_DATA" ) ); + return; + } + + double aDVal; + int anIVal; + bool aBVal; + + const QList& aRemovedRows = myTable->GetRemovedRows(); + const VariableMap& aVariableMap = myTable->GetVariableMap(); + const VariableMap& aVariableMapRef = myTable->GetVariableMapRef(); + + for( QListIterator anIter( aRemovedRows ); anIter.hasNext(); ) + { + int anIndex = anIter.next(); + if( aVariableMapRef.contains( anIndex ) ) + { + QString aRemovedVariable = aVariableMapRef[ anIndex ].Name; + myStudy->RemoveVariable( std::string( aRemovedVariable.toLatin1().constData() ) ); + } + } + + VariableMap::const_iterator it = aVariableMap.constBegin(), itEnd = aVariableMap.constEnd(); + for( ; it != itEnd; ++it ) + { + int anIndex = it.key(); + const NoteBoox_Variable& aVariable = it.value(); + QString aName = aVariable.Name; + QString aValue = aVariable.Value; + + if( !aName.isEmpty() && !aValue.isEmpty() ) + { + if( aVariableMapRef.contains( anIndex ) ) + { + const NoteBoox_Variable& aVariableRef = aVariableMapRef[ anIndex ]; + QString aNameRef = aVariableRef.Name; + QString aValueRef = aVariableRef.Value; + + if( !aNameRef.isEmpty() && !aValueRef.isEmpty() && aNameRef != aName ) + { + myStudy->RenameVariable( std::string( aNameRef.toLatin1().constData() ), + std::string( aName.toLatin1().constData() ) ); + } + } + + if( NoteBook_TableRow::IsIntegerValue(aValue,&anIVal) ) + myStudy->SetInteger(std::string(aName.toLatin1().constData()),anIVal); + + else if( NoteBook_TableRow::IsRealValue(aValue,&aDVal) ) + myStudy->SetReal(std::string(aName.toLatin1().constData()),aDVal); + + else if( NoteBook_TableRow::IsBooleanValue(aValue,&aBVal) ) + myStudy->SetBoolean(std::string(aName.toLatin1().constData()),aBVal); + + else + myStudy->SetString(std::string(aName.toLatin1().constData()),aValue.toStdString()); + } + } + myTable->ResetMaps(); + + SalomeApp_Application* app = dynamic_cast( SUIT_Session::session()->activeApplication() ); + if(app) + app->updateActions(); + + myStudy->Modified(); +} + +//============================================================================ +/*! Function : onRemove + * Purpose : [slot] + */ +//============================================================================ +void SalomeApp_NoteBook::onRemove() +{ + myTable->RemoveSelected(); + onApply(); +} + +//============================================================================ +/*! Function : onUpdateStudy + * Purpose : [slot] + */ +//============================================================================ +void SalomeApp_NoteBook::onUpdateStudy() +{ + onApply(); + if( !myTable->IsValid() ) + return; + + SalomeApp_Application* app = dynamic_cast( SUIT_Session::session()->activeApplication() ); + if(!app) + return; + app->onUpdateStudy(); +} diff --git a/src/SalomeApp/SalomeApp_NoteBookDlg.h b/src/SalomeApp/SalomeApp_NoteBook.h similarity index 78% rename from src/SalomeApp/SalomeApp_NoteBookDlg.h rename to src/SalomeApp/SalomeApp_NoteBook.h index 8e8ec0862..dcf0fb3da 100644 --- a/src/SalomeApp/SalomeApp_NoteBookDlg.h +++ b/src/SalomeApp/SalomeApp_NoteBook.h @@ -1,177 +1,179 @@ -// Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com -// - -// File: SalomeApp_NoteBookDlg.h -// Author : Roman NIKOLAEV, Open CASCADE S.A.S. -// Module : GUI -// -#ifndef SALOMEAPP_NOTEBOOKDLG_H -#define SALOMEAPP_NOTEBOOKDLG_H - -#include "SalomeApp.h" - -#include "SALOMEDSClient_ClientFactory.hxx" -#include CORBA_SERVER_HEADER(SALOMEDS) - -#include -#include -#include - -class QWidget; -class QPushButton; -class QTableWidgetItem; -class NoteBook_Table; - -struct NoteBoox_Variable -{ - NoteBoox_Variable() {} - NoteBoox_Variable( const QString& theName, const QString& theValue ) - { - Name = theName; - Value = theValue; - } - QString Name; - QString Value; -}; - -typedef QMap< int, NoteBoox_Variable > VariableMap; - -class SALOMEAPP_EXPORT NoteBook_TableRow : public QWidget -{ - public: - NoteBook_TableRow(int, NoteBook_Table* parentTable, QWidget* parent=0 ); - virtual ~NoteBook_TableRow(); - - int GetIndex() const { return myIndex; } - - void AddToTable(QTableWidget *theTable); - - void SetName(const QString theName); - void SetValue(const QString theValue); - - QString GetValue() const; - QString GetName() const; - - bool CheckName(); - bool CheckValue(); - - QTableWidgetItem* GetVariableItem(); - QTableWidgetItem* GetNameItem(); - QTableWidgetItem* GetHeaderItem(); - - static bool IsRealValue(const QString theValue, double* theResult = 0); - static bool IsIntegerValue(const QString theValue, int* theResult = 0); - static bool IsBooleanValue(const QString theValue, bool* theResult = 0); - bool IsValidStringValue(const QString theName); - - private: - int myIndex; - NoteBook_Table* myParentTable; - QTableWidgetItem* myRowHeader; - QTableWidgetItem* myVariableName; - QTableWidgetItem* myVariableValue; -}; - -class SALOMEAPP_EXPORT NoteBook_Table : public QTableWidget -{ - Q_OBJECT - public: - NoteBook_Table(QWidget * parent = 0); - virtual ~NoteBook_Table(); - - void Init(_PTR(Study) theStudy); - static QString Variable2String(const std::string& theVarName, - _PTR(Study) theStudy); - - bool IsValid() const; - - void AddRow( const QString& theName = QString::null, const QString& theValue = QString::null ); - void AddEmptyRow(); - NoteBook_TableRow* GetRowByItem(const QTableWidgetItem* theItem) const; - bool IsLastRow(const NoteBook_TableRow* aRow) const; - - void RemoveSelected(); - - void SetProcessItemChangedSignalFlag(const bool enable); - bool GetProcessItemChangedSignalFlag()const; - - bool IsUniqueName(const NoteBook_TableRow* theRow) const; - QList GetRows() const; - - const bool IsModified() const { return myIsModified; } - const QList& GetRemovedRows() const { return myRemovedRows; } - const VariableMap& GetVariableMap() const { return myVariableMap; } - const VariableMap& GetVariableMapRef() const { return myVariableMapRef; } - void RenamberRowItems(); - - void ResetMaps(); - - QList myRows; - - public slots: - void onItemChanged(QTableWidgetItem* theItem); - - private: - int getUniqueIndex() const; - - private: - bool isProcessItemChangedSignal; - - bool myIsModified; - QList myRemovedRows; - VariableMap myVariableMapRef; - VariableMap myVariableMap; - - _PTR(Study) myStudy; -}; - -class SALOMEAPP_EXPORT SalomeApp_NoteBookDlg : public QDialog -{ - Q_OBJECT - public: - SalomeApp_NoteBookDlg(QWidget * parent , _PTR(Study) theStudy); - virtual ~SalomeApp_NoteBookDlg(); - - void Init(_PTR(Study) theStudy); - - public slots: - void onOK(); - void onApply(); - void onCancel(); - void onRemove(); - void onUpdateStudy(); - void onHelp(); - - protected: - bool updateStudy(); - void clearStudy(); - - private: - NoteBook_Table* myTable; - QPushButton* myRemoveButton; - QPushButton* myUpdateStudyBtn; - QPushButton* myOkBtn; - QPushButton* myApplyBtn; - QPushButton* myCancelBtn; - QPushButton* myHelpBtn; - - _PTR(Study) myStudy; -}; - -#endif //SALOMEAPP_NOTEBOOKDLG_H +// Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +// File: SalomeApp_NoteBook.h +// Author : Roman NIKOLAEV, Open CASCADE S.A.S. +// Module : GUI +// +#ifndef SALOMEAPP_NOTEBOOK_H +#define SALOMEAPP_NOTEBOOK_H + +#include "SalomeApp.h" + +#include "SALOMEDSClient_ClientFactory.hxx" +#include CORBA_SERVER_HEADER(SALOMEDS) + +#include +#include +#include + +class QWidget; +class QPushButton; +class QTableWidgetItem; +class NoteBook_Table; + +struct NoteBoox_Variable +{ + NoteBoox_Variable() {} + NoteBoox_Variable( const QString& theName, const QString& theValue ) + { + Name = theName; + Value = theValue; + } + QString Name; + QString Value; +}; + +typedef QMap< int, NoteBoox_Variable > VariableMap; + +class SALOMEAPP_EXPORT NoteBook_TableRow : public QWidget +{ + public: + NoteBook_TableRow(int, NoteBook_Table* parentTable, QWidget* parent=0 ); + virtual ~NoteBook_TableRow(); + + int GetIndex() const { return myIndex; } + + void AddToTable(QTableWidget *theTable); + + void SetName(const QString theName); + void SetValue(const QString theValue); + + QString GetValue() const; + QString GetName() const; + + bool CheckName(); + bool CheckValue(); + + QTableWidgetItem* GetVariableItem(); + QTableWidgetItem* GetNameItem(); + QTableWidgetItem* GetHeaderItem(); + + static bool IsRealValue(const QString theValue, double* theResult = 0); + static bool IsIntegerValue(const QString theValue, int* theResult = 0); + static bool IsBooleanValue(const QString theValue, bool* theResult = 0); + bool IsValidStringValue(const QString theName); + + private: + int myIndex; + NoteBook_Table* myParentTable; + QTableWidgetItem* myRowHeader; + QTableWidgetItem* myVariableName; + QTableWidgetItem* myVariableValue; +}; + +class SALOMEAPP_EXPORT NoteBook_Table : public QTableWidget +{ + Q_OBJECT + public: + NoteBook_Table(QWidget * parent = 0); + virtual ~NoteBook_Table(); + + void Init(_PTR(Study) theStudy); + static QString Variable2String(const std::string& theVarName, + _PTR(Study) theStudy); + + bool IsValid() const; + + void AddRow( const QString& theName = QString::null, const QString& theValue = QString::null ); + void AddEmptyRow(); + NoteBook_TableRow* GetRowByItem(const QTableWidgetItem* theItem) const; + bool IsLastRow(const NoteBook_TableRow* aRow) const; + + void RemoveSelected(); + + void SetProcessItemChangedSignalFlag(const bool enable); + bool GetProcessItemChangedSignalFlag()const; + + bool IsUniqueName(const NoteBook_TableRow* theRow) const; + QList GetRows() const; + + const bool IsModified() const { return myIsModified; } + const QList& GetRemovedRows() const { return myRemovedRows; } + const VariableMap& GetVariableMap() const { return myVariableMap; } + const VariableMap& GetVariableMapRef() const { return myVariableMapRef; } + void RenumberRowItems(); + + void ResetMaps(); + + QList myRows; + + public slots: + void onItemChanged(QTableWidgetItem* theItem); + + private: + int getUniqueIndex() const; + + private: + bool isProcessItemChangedSignal; + + bool myIsModified; + QList myRemovedRows; + VariableMap myVariableMapRef; + VariableMap myVariableMap; + + _PTR(Study) myStudy; +}; + +class SALOMEAPP_EXPORT SalomeApp_NoteBook : public QWidget +{ + Q_OBJECT + public: + SalomeApp_NoteBook(QWidget * parent , _PTR(Study) theStudy); + virtual ~SalomeApp_NoteBook(); + + void Init(_PTR(Study) theStudy); + + QString getDumpedStudyName() { return myDumpedStudyName; } + void setDumpedStudyName(QString theName) { myDumpedStudyName = theName; } + + QString getDumpedStudyScript() { return myDumpedStudyScript; } + void setDumpedStudyScript(QString theScript) { myDumpedStudyScript = theScript; } + + bool isDumpedStudySaved() { return myIsDumpedStudySaved; } + void setIsDumpedStudySaved(bool isSaved) { myIsDumpedStudySaved = isSaved; } + + public slots: + void onApply(); + void onRemove(); + void onUpdateStudy(); + void onVarUpdate( QString theVarName ); + + private: + NoteBook_Table* myTable; + QPushButton* myRemoveButton; + QPushButton* myUpdateStudyBtn; + + _PTR(Study) myStudy; + QString myDumpedStudyScript; // path to script of dumped study + QString myDumpedStudyName; + bool myIsDumpedStudySaved; +}; + +#endif //SALOMEAPP_NOTEBOOK_H diff --git a/src/SalomeApp/SalomeApp_Study.cxx b/src/SalomeApp/SalomeApp_Study.cxx index ec630387f..778877564 100644 --- a/src/SalomeApp/SalomeApp_Study.cxx +++ b/src/SalomeApp/SalomeApp_Study.cxx @@ -318,6 +318,11 @@ public: } break; } + case 6: //NoteBook variables were modified + { + myStudy->onNoteBookVarUpdate( QString( theID.c_str() ) ); + break; + } default:MESSAGE("Unknown event: " << event);break; } //switch } //notifyObserverID_real @@ -381,6 +386,11 @@ SalomeApp_Study::~SalomeApp_Study() } } +void SalomeApp_Study::onNoteBookVarUpdate( QString theVarName) +{ + emit notebookVarUpdated( theVarName ); +} + /*! Gets study id. */ diff --git a/src/SalomeApp/SalomeApp_Study.h b/src/SalomeApp/SalomeApp_Study.h index 534426486..35508b0bd 100644 --- a/src/SalomeApp/SalomeApp_Study.h +++ b/src/SalomeApp/SalomeApp_Study.h @@ -106,6 +106,7 @@ protected: SUIT_DataObject* theParent ) const; protected slots: virtual void updateModelRoot( const CAM_DataModel* ); + void onNoteBookVarUpdate( QString theVarName ); private: QString newStudyName() const; @@ -113,6 +114,9 @@ private: private: _PTR(Study) myStudyDS; Observer_i* myObserver; + + signals: + void notebookVarUpdated( QString theVarName ); }; #ifdef WIN32 diff --git a/src/SalomeApp/resources/SalomeApp_msg_en.ts b/src/SalomeApp/resources/SalomeApp_msg_en.ts index dedd0f7c6..285ff023b 100644 --- a/src/SalomeApp/resources/SalomeApp_msg_en.ts +++ b/src/SalomeApp/resources/SalomeApp_msg_en.ts @@ -441,11 +441,11 @@ Do you really want to rename it? VARNAME_COLUMN - Variable Name + Variable VARVALUE_COLUMN - Variable Value + Value VARVALUE_INCORRECT @@ -461,10 +461,10 @@ Do you really want to rename it? - SalomeApp_NoteBookDlg + SalomeApp_NoteBook NOTEBOOK_TITLE - Salome NoteBook + NoteBook BUT_UPDATE_STUDY diff --git a/src/SalomeApp/resources/SalomeApp_msg_fr.ts b/src/SalomeApp/resources/SalomeApp_msg_fr.ts index 9adf4ae95..fdfb53ee3 100755 --- a/src/SalomeApp/resources/SalomeApp_msg_fr.ts +++ b/src/SalomeApp/resources/SalomeApp_msg_fr.ts @@ -461,7 +461,7 @@ Voulez-vous vraiment la renommer? - SalomeApp_NoteBookDlg + SalomeApp_NoteBook NOTEBOOK_TITLE Registre Salome -- 2.39.2