From: Anthony Geay Date: Mon, 22 Feb 2016 17:08:14 +0000 (+0100) Subject: Step 3 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=b71987d324f35b2f8bcac57c2a36a292eb0c371c;p=modules%2Fgui.git Step 3 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 16a00d96b..251f60645 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -310,7 +310,7 @@ INCLUDE(CMakePackageConfigHelpers) # They all have to be INSTALL'd with the option "EXPORT ${PROJECT_NAME}TargetGroup" SET(_${PROJECT_NAME}_exposed_targets caf CAM CASCatch DDS Event LightApp LogWindow ObjBrowser - QDS qtx SalomePrs SalomeStyle std SUITApp suit ViewerTools ViewerData + QDS qtx suitt SalomePrs SalomeStyle std SUITApp suit ViewerTools ViewerData ImageComposer ) @@ -387,7 +387,7 @@ ENDIF() # Python-based packages specific targets: IF(SALOME_USE_PYCONSOLE) LIST(APPEND _${PROJECT_NAME}_exposed_targets - PyInterp PyConsole SalomePyQtGUILight) + PyInterp PyConsoleBase PyConsole SalomePyQtGUILight) IF(SALOME_USE_PLOT2DVIEWER) LIST(APPEND _${PROJECT_NAME}_exposed_targets SalomePyQt) ENDIF() diff --git a/SalomeGUIConfig.cmake.in b/SalomeGUIConfig.cmake.in index d99916d85..d52f42693 100644 --- a/SalomeGUIConfig.cmake.in +++ b/SalomeGUIConfig.cmake.in @@ -198,6 +198,7 @@ SET(GUI_ObjBrowser ObjBrowser) SET(GUI_OCCViewer OCCViewer) SET(GUI_OpenGLUtils OpenGLUtils) SET(GUI_Plot2d Plot2d) +SET(GUI_PyConsoleBase PyConsoleBase) SET(GUI_PyConsole PyConsole) SET(GUI_PyInterp PyInterp) SET(GUI_PyEditor PyEditor) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6435a1ccf..05d331cc1 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,7 @@ # Common packages ## ADD_SUBDIRECTORY(CASCatch) +ADD_SUBDIRECTORY(SUITT) ADD_SUBDIRECTORY(Qtx) ADD_SUBDIRECTORY(HelpBrowser) ADD_SUBDIRECTORY(Style) @@ -124,6 +125,7 @@ ENDIF(SALOME_USE_PYVIEWER) ## IF(SALOME_USE_PYCONSOLE) ADD_SUBDIRECTORY(PyInterp) + ADD_SUBDIRECTORY(PyConsoleBase) ADD_SUBDIRECTORY(PyConsole) ADD_SUBDIRECTORY(SALOME_PYQT) ENDIF(SALOME_USE_PYCONSOLE) diff --git a/src/LightApp/CMakeLists.txt b/src/LightApp/CMakeLists.txt index 04e309b4a..3171c6c93 100755 --- a/src/LightApp/CMakeLists.txt +++ b/src/LightApp/CMakeLists.txt @@ -82,6 +82,7 @@ IF(SALOME_USE_PYCONSOLE) INCLUDE_DIRECTORIES( ${PYTHON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/src/PyConsole + ${PROJECT_SOURCE_DIR}/src/PyConsoleBase ${PROJECT_SOURCE_DIR}/src/PyInterp ${PROJECT_SOURCE_DIR}/src/SUITApp ) diff --git a/src/PyConsole/CMakeLists.txt b/src/PyConsole/CMakeLists.txt index aebd5fd6f..8516a1280 100755 --- a/src/PyConsole/CMakeLists.txt +++ b/src/PyConsole/CMakeLists.txt @@ -26,6 +26,7 @@ INCLUDE_DIRECTORIES( ${QT_INCLUDES} ${PYTHON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/src/PyConsole + ${PROJECT_SOURCE_DIR}/src/PyConsoleBase ${PROJECT_SOURCE_DIR}/src/Qtx ${PROJECT_SOURCE_DIR}/src/SUIT ${PROJECT_SOURCE_DIR}/src/Event @@ -36,25 +37,20 @@ INCLUDE_DIRECTORIES( ADD_DEFINITIONS(${QT_DEFINITIONS} ${PYTHON_DEFINITIONS}) # libraries to link to -SET(_link_LIBRARIES ${QT_LIBRARIES} ${PYTHON_LIBRARIES} qtx suit PyInterp Event) +SET(_link_LIBRARIES ${QT_LIBRARIES} ${PYTHON_LIBRARIES} qtx suit PyConsoleBase) # --- headers --- # header files / to be processed by moc SET(_moc_HEADERS PyConsole_Console.h - PyConsole_ConsoleBase.h PyConsole_Editor.h PyConsole_EnhEditor.h ) # header files / no moc processing SET(_other_HEADERS - PyConsole.h - PyConsole_EnhInterp.h - PyConsole_Event.h - PyConsole_Interp.h - PyConsole_Request.h + ) # header files / to install @@ -62,13 +58,6 @@ SET(PyConsole_HEADERS ${_moc_HEADERS} ${_other_HEADERS}) # --- resources --- -# resource files / to be processed by lrelease -SET(_ts_RESOURCES - resources/PyConsole_msg_en.ts - resources/PyConsole_msg_fr.ts - resources/PyConsole_msg_ja.ts -) - # --- sources --- # sources / moc wrappings @@ -77,13 +66,8 @@ QT_WRAP_MOC(_moc_SOURCES ${_moc_HEADERS}) # sources / static SET(_other_SOURCES PyConsole_Console.cxx - PyConsole_ConsoleBase.cxx - PyConsole_Editor.cxx PyConsole_EnhEditor.cxx - PyConsole_EnhInterp.cxx - PyConsole_Event.cxx - PyConsole_Interp.cxx - PyConsole_Request.cxx + PyConsole_Editor.cxx ) # sources / to compile @@ -96,5 +80,4 @@ TARGET_LINK_LIBRARIES(PyConsole ${_link_LIBRARIES}) INSTALL(TARGETS PyConsole EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME_INSTALL_LIBS}) INSTALL(FILES ${PyConsole_HEADERS} DESTINATION ${SALOME_INSTALL_HEADERS}) -QT_INSTALL_TS_RESOURCES("${_ts_RESOURCES}" "${SALOME_GUI_INSTALL_RES_DATA}") diff --git a/src/PyConsole/PyConsole_Console.cxx b/src/PyConsole/PyConsole_Console.cxx index f315800e6..ad41a0aa8 100644 --- a/src/PyConsole/PyConsole_Console.cxx +++ b/src/PyConsole/PyConsole_Console.cxx @@ -30,6 +30,7 @@ #include "PyConsole_Interp.h" /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!! #include "PyConsole_Console.h" +#include "PyConsole_Editor.h" #include "PyConsole_EnhEditor.h" #include "PyConsole_EnhInterp.h" diff --git a/src/PyConsole/PyConsole_ConsoleBase.cxx b/src/PyConsole/PyConsole_ConsoleBase.cxx deleted file mode 100644 index 79b717087..000000000 --- a/src/PyConsole/PyConsole_ConsoleBase.cxx +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE -// -// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -// -// 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, or (at your option) any later version. -// -// 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 : PyConsole_ConsoleBase.cxx -// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) -// -/*! - \class PyConsole_Console - \brief Python console widget. -*/ - -#include "PyConsole_Interp.h" /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!! -#include "PyConsole_ConsoleBase.h" -#include "PyConsole_EnhEditor.h" -#include "PyConsole_EnhInterp.h" - -#include -#include -#include -#include -#include -#include - -/*! - \brief Constructor. - - Creates new python console widget. - \param parent parent widget - \param interp python interpreter -*/ -PyConsole_ConsoleBase::PyConsole_ConsoleBase( QWidget* parent, PyConsole_Interp* interp ) -: QWidget( parent ) -{ - PyConsole_Interp* anInterp = interp ? interp : new PyConsole_Interp(); - - // initialize Python interpretator - anInterp->initialize(); - - // create editor console - QVBoxLayout* lay = new QVBoxLayout( this ); - lay->setMargin( 0 ); - myEditor = new PyConsole_Editor( anInterp, this ); - char* synchronous = getenv("PYTHON_CONSOLE_SYNC"); - if (synchronous && atoi(synchronous)) - { - myEditor->setIsSync(true); - } - myEditor->viewport()->installEventFilter( this ); - lay->addWidget( myEditor ); - - createActions(); -} - -/** - * Protected constructor. - */ -PyConsole_ConsoleBase::PyConsole_ConsoleBase( QWidget* parent, PyConsole_Interp* /*i*/, PyConsole_Editor* e ) - : QWidget (parent), myEditor(e) -{} - -/*! - \brief Destructor. - - Does nothing for the moment. -*/ -PyConsole_ConsoleBase::~PyConsole_ConsoleBase() -{ -} - -PyConsole_Interp* PyConsole_ConsoleBase::getInterp() const -{ - return myEditor ? myEditor->getInterp() : 0; -} - -/*! - \brief Execute python command in the interpreter. - \param command string with command and arguments -*/ -void PyConsole_ConsoleBase::exec( const QString& command ) -{ - if ( myEditor ) - myEditor->exec( command ); -} - -/*! - \brief Execute python command in the interpreter - and wait until it is finished. - - Block execution of main application until the python command is executed. - \param command string with command and arguments -*/ -void PyConsole_ConsoleBase::execAndWait( const QString& command ) -{ - if ( myEditor ) - myEditor->execAndWait( command ); -} - -/*! - \brief Get synchronous mode flag value. - - \sa setIsSync() - \return True if python console works in synchronous mode -*/ -bool PyConsole_ConsoleBase::isSync() const -{ - return myEditor->isSync(); -} - -/*! - \brief Set synchronous mode flag value. - - In synhronous mode the Python commands are executed in the GUI thread - and the GUI is blocked until the command is finished. In the asynchronous - mode each Python command is executed in the separate thread that does not - block the main GUI loop. - - \param on synhronous mode flag -*/ -void PyConsole_ConsoleBase::setIsSync( const bool on ) -{ - myEditor->setIsSync( on ); -} - -/*! - \brief Get suppress output flag value. - - \sa setIsSuppressOutput() - \return True if python console output is suppressed. -*/ -bool PyConsole_ConsoleBase::isSuppressOutput() const -{ - return myEditor->isSuppressOutput(); -} - -/*! - \brief Set suppress output flag value. - - In case if suppress output flag is true, the python - console output suppressed. - - \param on suppress output flag -*/ -void PyConsole_ConsoleBase::setIsSuppressOutput( const bool on ) -{ - myEditor->setIsSuppressOutput(on); -} - -/*! - \brief Get 'show banner' flag value. - - \sa setIsShowBanner() - \return \c true if python console shows banner -*/ -bool PyConsole_ConsoleBase::isShowBanner() const -{ - return myEditor->isShowBanner(); -} - -/*! - \brief Set 'show banner' flag value. - - The banner is shown in the top of the python console window. - - \sa isShowBanner() - \param on 'show banner' flag -*/ -void PyConsole_ConsoleBase::setIsShowBanner( const bool on ) -{ - myEditor->setIsShowBanner( on ); -} - -/*! - \brief Change the python console's font. - \param f new font -*/ -void PyConsole_ConsoleBase::setFont( const QFont& f ) -{ - if( myEditor ) - myEditor->setFont( f ); -} - -/*! - \brief Get python console font. - \return current python console's font -*/ -QFont PyConsole_ConsoleBase::font() const -{ - QFont res; - if( myEditor ) - res = myEditor->font(); - return res; -} - -/*! - \brief Set actions to be visible in the context popup menu. - - Actions, which IDs are set in \a flags parameter, will be shown in the - context popup menu. Other actions will not be shown. - - \param flags ORed together actions flags -*/ -void PyConsole_ConsoleBase::setMenuActions( const int flags ) -{ - myActions[CopyId]->setVisible( flags & CopyId ); - myActions[PasteId]->setVisible( flags & PasteId ); - myActions[ClearId]->setVisible( flags & ClearId ); - myActions[SelectAllId]->setVisible( flags & SelectAllId ); - myActions[DumpCommandsId]->setVisible( flags & DumpCommandsId ); - myActions[StartLogId]->setVisible( flags & StartLogId ); - myActions[StopLogId]->setVisible( flags & StopLogId ); -} - -/*! - \brief Get menu actions which are currently visible in the context popup menu. - \return ORed together actions flags - \sa setMenuActions() -*/ -int PyConsole_ConsoleBase::menuActions() const -{ - int ret = 0; - ret = ret | ( myActions[CopyId]->isVisible() ? CopyId : 0 ); - ret = ret | ( myActions[PasteId]->isVisible() ? PasteId : 0 ); - ret = ret | ( myActions[ClearId]->isVisible() ? ClearId : 0 ); - ret = ret | ( myActions[SelectAllId]->isVisible() ? SelectAllId : 0 ); - ret = ret | ( myActions[DumpCommandsId]->isVisible() ? DumpCommandsId : 0 ); - ret = ret | ( myActions[StartLogId]->isVisible() ? StartLogId : 0 ); - ret = ret | ( myActions[StopLogId]->isVisible() ? StopLogId : 0 ); - return ret; -} - -/*! - \brief Create menu actions. - - Create context popup menu actions. -*/ -void PyConsole_ConsoleBase::createActions() -{ - QAction* a = new QAction( tr( "EDIT_COPY_CMD" ), this ); - a->setStatusTip( tr( "EDIT_COPY_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) ); - myActions.insert( CopyId, a ); - - a = new QAction( tr( "EDIT_PASTE_CMD" ), this ); - a->setStatusTip( tr( "EDIT_PASTE_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) ); - myActions.insert( PasteId, a ); - - a = new QAction( tr( "EDIT_CLEAR_CMD" ), this ); - a->setStatusTip( tr( "EDIT_CLEAR_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( clear() ) ); - myActions.insert( ClearId, a ); - - a = new QAction( tr( "EDIT_SELECTALL_CMD" ), this ); - a->setStatusTip( tr( "EDIT_SELECTALL_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) ); - myActions.insert( SelectAllId, a ); - - a = new QAction( tr( "EDIT_DUMPCOMMANDS_CMD" ), this ); - a->setStatusTip( tr( "EDIT_DUMPCOMMANDS_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( dump() ) ); - myActions.insert( DumpCommandsId, a ); - - a = new QAction( tr( "EDIT_STARTLOG_CMD" ), this ); - a->setStatusTip( tr( "EDIT_STARTLOG_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( startLog() ) ); - myActions.insert( StartLogId, a ); - - a = new QAction( tr( "EDIT_STOPLOG_CMD" ), this ); - a->setStatusTip( tr( "EDIT_STOPLOG_CMD" ) ); - connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( stopLog() ) ); - myActions.insert( StopLogId, a ); -} - -/*! - \brief Update menu actions. - - Update context popup menu action state. -*/ -void PyConsole_ConsoleBase::updateActions() -{ - myActions[CopyId]->setEnabled( myEditor->textCursor().hasSelection() ); - myActions[PasteId]->setEnabled( !myEditor->isReadOnly() && !QApplication::clipboard()->text().isEmpty() ); - myActions[SelectAllId]->setEnabled( !myEditor->document()->isEmpty() ); -} - -/*! - \brief Start python trace logging - \param fileName the path to the log file -*/ -void PyConsole_ConsoleBase::startLog( const QString& fileName ) -{ - myEditor->startLog( fileName ); -} - -/*! - \brief Stop python trace logging -*/ -void PyConsole_ConsoleBase::stopLog() -{ - myEditor->stopLog(); -} - -/** - * Similar to constructor of the base class but using enhanced objects. - * TODO: this should really be done in a factory to avoid code duplication. - * @param parent - * @param interp - */ -PyConsole_EnhConsoleBase::PyConsole_EnhConsoleBase( QWidget* parent, PyConsole_Interp* interp ) - : PyConsole_ConsoleBase( parent, interp, 0 ) -{ - PyConsole_Interp* anInterp = interp ? interp : new PyConsole_EnhInterp(); - - // initialize Python interpretator - anInterp->initialize(); - - // create editor console - QVBoxLayout* lay = new QVBoxLayout( this ); - lay->setMargin( 0 ); - myEditor = new PyConsole_EnhEditor( anInterp, this ); - char* synchronous = getenv("PYTHON_CONSOLE_SYNC"); - if (synchronous && atoi(synchronous)) - { - myEditor->setIsSync(true); - } - myEditor->viewport()->installEventFilter( this ); - lay->addWidget( myEditor ); - - createActions(); -} diff --git a/src/PyConsole/PyConsole_ConsoleBase.h b/src/PyConsole/PyConsole_ConsoleBase.h deleted file mode 100644 index 2add981b7..000000000 --- a/src/PyConsole/PyConsole_ConsoleBase.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE -// -// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -// -// 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, or (at your option) any later version. -// -// 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 : PyConsole_Console.h -// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) -// -#ifndef PYCONSOLE_CONSOLEBASE_H -#define PYCONSOLE_CONSOLEBASE_H - -#include "PyConsole.h" - -#include -#include -#include - -class PyConsole_Interp; -class PyConsole_Editor; - -class PYCONSOLE_EXPORT PyConsole_ConsoleBase : public QWidget -{ - Q_OBJECT - -public: - //! Context popup menu actions flags - enum - { - CopyId = 0x01, //!< "Copy" menu action - PasteId = 0x02, //!< "Paste" menu action - ClearId = 0x04, //!< "Clear" menu action - SelectAllId = 0x08, //!< "Select All" menu action - DumpCommandsId = 0x10, //!< "DumpCommands" menu action - StartLogId = 0x20, //!< "Start log" menu action - StopLogId = 0x40, //!< "Stop log" menu action - All = 0xFF, //!< all menu actions - }; - -public: - PyConsole_ConsoleBase( QWidget* parent, PyConsole_Interp* interp = 0 ); - virtual ~PyConsole_ConsoleBase(); - - //! \brief Get python interperter - PyConsole_Interp* getInterp() const; - QFont font() const; - virtual void setFont( const QFont& ); - - bool isSync() const; - void setIsSync( const bool ); - - bool isSuppressOutput() const; - void setIsSuppressOutput( const bool ); - - bool isShowBanner() const; - void setIsShowBanner( const bool ); - - void exec( const QString& ); - void execAndWait( const QString& ); - - void setMenuActions( const int ); - int menuActions() const; - - void startLog( const QString& ); - void stopLog(); - -protected: - void createActions(); - void updateActions(); - - PyConsole_ConsoleBase( QWidget* parent, PyConsole_Interp*, PyConsole_Editor*); - - PyConsole_Editor* myEditor; //!< python console editor widget - QMap myActions; //!< menu actions list -}; - -/** - * Enhance console object providing auto-completion. - * Similar to PyConsole_Console except that an enhanced interpreter and enhanced editor - * are encapsulated. - */ -class PYCONSOLE_EXPORT PyConsole_EnhConsoleBase : public PyConsole_ConsoleBase -{ - Q_OBJECT - -public: - PyConsole_EnhConsoleBase( QWidget* parent, PyConsole_Interp* interp = 0 ); - virtual ~PyConsole_EnhConsoleBase() {} -}; - -#endif // PYCONSOLE_CONSOLE_H diff --git a/src/PyConsole/PyConsole_Editor.cxx b/src/PyConsole/PyConsole_Editor.cxx index 2311b58d4..7dbc93f8c 100644 --- a/src/PyConsole/PyConsole_Editor.cxx +++ b/src/PyConsole/PyConsole_Editor.cxx @@ -95,10 +95,9 @@ #include "PyInterp_Dispatcher.h" #include "PyConsole_Request.h" -#include -#include -#include -#include +#include "SUIT_FileValidator.h" +#include "SUIT_MessageBox.h" +#include "SUIT_FileDlg.h" #include #include @@ -112,25 +111,7 @@ #include #include #include - -//VSR: uncomment below macro to support unicode text properly in SALOME -// current commented out due to regressions -//#define PAL22528_UNICODE - -namespace -{ - QString fromUtf8( const char* txt ) - { -#ifdef PAL22528_UNICODE - return QString::fromUtf8( txt ); -#else - return QString( txt ); -#endif - } -} - -static QString READY_PROMPT = ">>> "; -static QString DOTS_PROMPT = "... "; +#include class DumpCommandsFileValidator : public SUIT_FileValidator { @@ -152,25 +133,6 @@ bool DumpCommandsFileValidator::canSave(const QString& file, bool permissions) return SUIT_FileValidator::canSave( file, permissions); } -void staticCallbackStdout( void* data, char* c ) -{ - if(!((PyConsole_Editor*)data)->isSuppressOutput()) { - PyConsole_Editor* e = (PyConsole_Editor*)data; - e->putLog( fromUtf8(c) ); - QApplication::postEvent( e, new PrintEvent( fromUtf8(c), false ) ); - } -} - -void staticCallbackStderr( void* data, char* c ) -{ - if(!((PyConsole_Editor*)data)->isSuppressOutput()) { - PyConsole_Editor* e = (PyConsole_Editor*)data; - e->putLog( fromUtf8(c) ); - QApplication::postEvent( e, new PrintEvent( fromUtf8(c), true ) ); - } -} - - /*! \brief Constructor. @@ -180,29 +142,8 @@ void staticCallbackStderr( void* data, char* c ) */ PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp, QWidget* theParent ) -: QTextEdit( theParent ), - myInterp( 0 ), - myCmdInHistory( -1 ), - myEventLoop( 0 ), - myShowBanner( true ), - myIsSync( true ), - myIsSuppressOutput( false ) + : PyConsole_EditorBase(theInterp,theParent) { - QString fntSet( "" ); - QFont aFont = SUIT_Tools::stringToFont( fntSet ); - setFont( aFont ); - setUndoRedoEnabled( false ); - - myPrompt = READY_PROMPT; - setLineWrapMode( QTextEdit::WidgetWidth ); - setWordWrapMode( QTextOption::WrapAnywhere ); - setAcceptRichText( false ); - - theInterp->setvoutcb( staticCallbackStdout, this ); - theInterp->setverrcb( staticCallbackStderr, this ); - - // san - This is necessary for troubleless initialization - onPyInterpChanged( theInterp ); } /*! @@ -210,947 +151,6 @@ PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp, */ PyConsole_Editor::~PyConsole_Editor() { - myInterp = 0; -} - -/*! - \brief Get Python interpreter -*/ -PyConsole_Interp* PyConsole_Editor::getInterp() const -{ - return myInterp; -} - -/*! - \brief Get synchronous mode flag value. - - \sa setIsSync() - \return True if python console works in synchronous mode -*/ -bool PyConsole_Editor::isSync() const -{ - return myIsSync; -} - -/*! - \brief Set synchronous mode flag value. - - In synhronous mode the Python commands are executed in the GUI thread - and the GUI is blocked until the command is finished. In the asynchronous - mode each Python command is executed in the separate thread that does not - block the main GUI loop. - - \param on synhronous mode flag -*/ -void PyConsole_Editor::setIsSync( const bool on ) -{ - myIsSync = on; -} - -/*! - \brief Get suppress output flag value. - - \sa setIsSuppressOutput() - \return \c true if python console output is suppressed. -*/ -bool PyConsole_Editor::isSuppressOutput() const -{ - return myIsSuppressOutput; -} - -/*! - \brief Set suppress output flag value. - - In case if suppress output flag is true, the python - console output suppressed. - - \param on suppress output flag -*/ -void PyConsole_Editor::setIsSuppressOutput( const bool on ) -{ - myIsSuppressOutput = on; -} - -/*! - \brief Get 'show banner' flag value. - - \sa setIsShowBanner() - \return \c true if python console shows banner -*/ -bool PyConsole_Editor::isShowBanner() const -{ - return myShowBanner; -} - -/*! - \brief Set 'show banner' flag value. - - The banner is shown in the top of the python console window. - - \sa isShowBanner() - \param on 'show banner' flag -*/ -void PyConsole_Editor::setIsShowBanner( const bool on ) -{ - if ( myShowBanner != on ) { - myShowBanner = on; - clear(); - } -} - -/*! - \brief Check if trace logging is switched on. - - \sa startLog(), stopLog() - \return \c true if trace logging is switched on -*/ -bool PyConsole_Editor::isLogging() const -{ - return !myLogFile.isEmpty(); -} - -/*! - \brief Get size hint for the Python console window - \return size hint value -*/ -QSize PyConsole_Editor::sizeHint() const -{ - QFontMetrics fm( font() ); - int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1; - QSize s(100, fm.lineSpacing()*nbLines); - return s; -} - -/*! - \brief Put the string \a str to the python editor. - \param str string to be put in the command line of the editor - \param newBlock if True, then the string is printed on a new line - \param isError if true, the text is printed in dark red -*/ -void PyConsole_Editor::addText( const QString& str, - const bool newBlock, - const bool isError) -{ - QTextCursor theCursor(textCursor()); - QTextCharFormat cf; - - moveCursor( QTextCursor::End ); - if ( newBlock ) - theCursor.insertBlock(); - if (isError) - cf.setForeground(QBrush(Qt::red)); - else - cf.setForeground(QBrush(Qt::black)); - theCursor.insertText( str, cf); - moveCursor( QTextCursor::End ); - ensureCursorVisible(); -} - -/*! - \brief Convenient method for executing a Python command, - as if the user typed it manually. - \param command python command to be executed - - !!! WARNING: doesn't work properly with multi-line commands. !!! -*/ -void PyConsole_Editor::exec( const QString& command ) -{ - if ( isReadOnly() ) { - // some interactive command is being executed in this editor... - // shedule the command to the queue - myQueue.push_back( command ); - return; - } - // remove last line - moveCursor( QTextCursor::End ); - moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - // set "ready" prompt - myPrompt = READY_PROMPT; - // clear command buffer - myCommandBuffer.truncate( 0 ); - // unset history browsing mode - myCmdInHistory = -1; - // print command line by line - QString cmd = command; - if ( !cmd.endsWith( "\n" ) ) cmd += "\n"; - QStringList lines = command.split( "\n" ); - for ( int i = 0; i < lines.size(); i++ ) { - if ( !lines[i].trimmed().isEmpty() ) - myHistory.push_back( lines[i] ); - addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 ); - putLog( QString( "%1%2\n" ).arg( i == 0 ? READY_PROMPT : DOTS_PROMPT ).arg( lines[i] ) ); - } - // IPAL20182 - addText( "", true ); - // set read-only mode - setReadOnly( true ); - // set busy cursor - setCursor( Qt::BusyCursor ); - - // post a request to execute Python command; - // editor will be informed via a custom event that execution has been completed - PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) ); -} - -/*! - \brief Create request to the python dispatcher for the command execution. - - \param command python command to be executed - */ -PyInterp_Request* PyConsole_Editor::createRequest( const QString& command ) -{ - return new ExecCommand( myInterp, command, this, isSync() ); -} - -/*! - \brief Execute command in the python interpreter - and wait until it is finished. - - \param command python command to be executed - */ -void PyConsole_Editor::execAndWait( const QString& command ) -{ - // already running ? - if( myEventLoop ) - return; - - // create new event loop - bool sync = isSync(); - if ( !sync ) { - myEventLoop = new QEventLoop( this ); - } - - // execute command - exec( command ); - - if ( !sync ) { - // run event loop - myEventLoop->exec(); - // delete event loop after command is processed - delete myEventLoop; - myEventLoop = 0; - } -} - -/*! - \brief Process "Enter" key press event. - - Execute the command entered by the user. -*/ -void PyConsole_Editor::handleReturn() -{ - // Position cursor at the end - QTextCursor curs(textCursor()); - curs.movePosition(QTextCursor::End); - setTextCursor(curs); - - // get last line - QTextBlock par = document()->end().previous(); - if ( !par.isValid() ) return; - - // get command - QString cmd = par.text().remove( 0, promptSize() ); - // extend the command buffer with the current command - myCommandBuffer.append( cmd ); - // add command to the history - if ( !cmd.trimmed().isEmpty() ) - myHistory.push_back( cmd ); - putLog( QString( "%1%2\n" ).arg( myPrompt ).arg( cmd ) ); - - // IPAL19397 - addText( "", true ); - - // set read-only mode - setReadOnly( true ); - // set busy cursor - setCursor( Qt::BusyCursor ); - - // post a request to execute Python command; - // editor will be informed via a custom event that execution has been completed - PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) ); -} - -/*! - \brief Process drop event. - - Paste dragged text. - \param event drop event -*/ -void PyConsole_Editor::dropEvent( QDropEvent* event ) -{ - // get the initial drop position - QPoint pos = event->pos(); - QTextCursor cur = cursorForPosition( event->pos() ); - // if the position is not in the last line move it to the end of the command line - if ( cur.position() < document()->end().previous().position() + promptSize() ) { - moveCursor( QTextCursor::End ); - pos = cursorRect().center(); - } - // create new drop event and use it instead of the original - QDropEvent de( pos, - event->possibleActions(), - event->mimeData(), - event->mouseButtons(), - event->keyboardModifiers(), - event->type() ); - QTextEdit::dropEvent( &de ); - // accept the original event - event->acceptProposedAction(); -} - -/*! - \brief Process mouse button release event. - - Left mouse button: copy selection to the clipboard. - Middle mouse button: paste clipboard's contents. - \param event mouse event -*/ -void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event ) -{ - if ( event->button() == Qt::LeftButton ) { - QTextEdit::mouseReleaseEvent( event ); - //copy(); - } - else if ( event->button() == Qt::MidButton ) { - QTextCursor cur = cursorForPosition( event->pos() ); - // if the position is not in the last line move it to the end of the command line - if ( cur.position() < document()->end().previous().position() + promptSize() ) { - moveCursor( QTextCursor::End ); - } - else { - setTextCursor( cur ); - } - const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ? - QClipboard::Selection : QClipboard::Clipboard ); - if ( md ) - insertFromMimeData( md ); - } - else { - QTextEdit::mouseReleaseEvent( event ); - } -} - -/*! - \brief Check if the string is command. - - Return True if the string \a str is likely to be the command - (i.e. it is started from the '>>>' or '...'). - \param str string to be checked -*/ -bool PyConsole_Editor::isCommand( const QString& str ) const -{ - return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT ); -} - -/*! - \brief Handle keyboard event. - - Implement navigation, history browsing, copy/paste and other common - operations. - \param event keyboard event -*/ -void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) -{ - // get cursor position - QTextCursor cur = textCursor(); - int curLine = cur.blockNumber(); - int curCol = cur.columnNumber(); - - // get last edited line - int endLine = document()->blockCount()-1; - - // get pressed key code - int aKey = event->key(); - - // check if is pressed - bool ctrlPressed = event->modifiers() & Qt::ControlModifier; - // check if is pressed - bool shftPressed = event->modifiers() & Qt::ShiftModifier; - - if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) { - // process + key-binding and key: clear current command - myCommandBuffer.truncate( 0 ); - myPrompt = READY_PROMPT; - addText( myPrompt, true ); - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - return; - } - else if ( ctrlPressed && aKey == Qt::Key_C ) { - // process + key-binding : copy - copy(); - return; - } - else if ( ctrlPressed && aKey == Qt::Key_X ) { - // process + key-binding : cut - cut(); - return; - } - else if ( ctrlPressed && aKey == Qt::Key_V ) { - // process + key-binding : paste - paste(); - return; - } - - // check for printed key - // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0; - // Better: - aKey = !(QChar(aKey).isPrint()) ? aKey : 0; - - switch ( aKey ) { - case 0 : - // any printed key: just print it - { - if ( curLine < endLine || curCol < promptSize() ) { - moveCursor( QTextCursor::End ); - } - QTextEdit::keyPressEvent( event ); - break; - } - case Qt::Key_Return: - case Qt::Key_Enter: - // key: process the current command - { - handleReturn(); - break; - } - case Qt::Key_Up: - // arrow key: process as follows: - // - without , modifiers: previous command in history - // - with modifier key pressed: move cursor one row up without selection - // - with modifier key pressed: move cursor one row up with selection - // - with + modifier keys pressed: scroll one row up - { - if ( ctrlPressed && shftPressed ) { - int value = verticalScrollBar()->value(); - int spacing = fontMetrics().lineSpacing(); - verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 ); - } - else if ( shftPressed || ctrlPressed ) { - if ( curLine > 0 ) - moveCursor( QTextCursor::Up, - shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); - } - else { - if ( myCmdInHistory < 0 && myHistory.count() > 0 ) { - // set history browsing mode - myCmdInHistory = myHistory.count(); - // remember current command - QTextBlock par = document()->end().previous(); - myCurrentCommand = par.text().remove( 0, promptSize() ); - } - if ( myCmdInHistory > 0 ) { - myCmdInHistory--; - // get previous command in the history - QString previousCommand = myHistory.at( myCmdInHistory ); - // print previous command - moveCursor( QTextCursor::End ); - moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - addText( myPrompt + previousCommand ); - // move cursor to the end - moveCursor( QTextCursor::End ); - } - } - break; - } - case Qt::Key_Down: - // arrow key: process as follows: - // - without , modifiers: next command in history - // - with modifier key pressed: move cursor one row down without selection - // - with modifier key pressed: move cursor one row down with selection - // - with + modifier keys pressed: scroll one row down - { - if ( ctrlPressed && shftPressed ) { - int value = verticalScrollBar()->value(); - int maxval = verticalScrollBar()->maximum(); - int spacing = fontMetrics().lineSpacing(); - verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval ); - } - else if ( shftPressed || ctrlPressed) { - if ( curLine < endLine ) - moveCursor( QTextCursor::Down, - shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); - } - else { - if ( myCmdInHistory >= 0 ) { - // get next command in the history - myCmdInHistory++; - QString nextCommand; - if ( myCmdInHistory < myHistory.count() ) { - // next command in history - nextCommand = myHistory.at( myCmdInHistory ); - } - else { - // end of history is reached - // last printed command - nextCommand = myCurrentCommand; - // unset history browsing mode - myCmdInHistory = -1; - } - // print next or current command - moveCursor( QTextCursor::End ); - moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - addText( myPrompt + nextCommand ); - // move cursor to the end - moveCursor( QTextCursor::End ); - } - } - break; - } - case Qt::Key_Left: - // arrow key: process as follows: - // - without , modifiers: move one symbol left (taking into account prompt) - // - with modifier key pressed: move one word left (taking into account prompt) - // - with modifier key pressed: move one symbol left with selection - // - with + modifier keys pressed: move one word left with selection - { - QString txt = textCursor().block().text(); - if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) { - moveCursor( QTextCursor::Up ); - moveCursor( QTextCursor::EndOfBlock ); - } - else { - QTextEdit::keyPressEvent( event ); - } - break; - } - case Qt::Key_Right: - // arrow key: process as follows: - // - without , modifiers: move one symbol right (taking into account prompt) - // - with modifier key pressed: move one word right (taking into account prompt) - // - with modifier key pressed: move one symbol right with selection - // - with + modifier keys pressed: move one word right with selection - { - QString txt = textCursor().block().text(); - if ( !shftPressed ) { - if ( curCol < txt.length() ) { - if ( isCommand( txt ) && curCol < promptSize() ) { - cur.setPosition( cur.block().position() + promptSize() ); - setTextCursor( cur ); - break; - } - } - else { - if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) { - cur.setPosition( cur.position() + promptSize()+1 ); - setTextCursor( cur ); - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - break; - } - } - } - QTextEdit::keyPressEvent( event ); - break; - } - case Qt::Key_PageUp: - // key: process as follows: - // - without , modifiers: first command in history - // - with modifier key pressed: move cursor one page up without selection - // - with modifier key pressed: move cursor one page up with selection - // - with + modifier keys pressed: scroll one page up - { - if ( ctrlPressed && shftPressed ) { - verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); - } - else if ( shftPressed || ctrlPressed ) { - bool moved = false; - qreal lastY = cursorRect( cur ).top(); - qreal distance = 0; - // move using movePosition to keep the cursor's x - do { - qreal y = cursorRect( cur ).top(); - distance += qAbs( y - lastY ); - lastY = y; - moved = cur.movePosition( QTextCursor::Up, - shftPressed ? QTextCursor::KeepAnchor : - QTextCursor::MoveAnchor ); - } while ( moved && distance < viewport()->height() ); - if ( moved ) { - cur.movePosition( QTextCursor::Down, - shftPressed ? QTextCursor::KeepAnchor : - QTextCursor::MoveAnchor ); - verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub ); - } - setTextCursor( cur ); - } - else { - if ( myCmdInHistory < 0 && myHistory.count() > 0 ) { - // set history browsing mode - myCmdInHistory = myHistory.count(); - // remember current command - QTextBlock par = document()->end().previous(); - myCurrentCommand = par.text().remove( 0, promptSize() ); - } - if ( myCmdInHistory > 0 ) { - myCmdInHistory = 0; - // get very first command in the history - QString firstCommand = myHistory.at( myCmdInHistory ); - // print first command - moveCursor( QTextCursor::End ); - moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - addText( myPrompt + firstCommand ); - // move cursor to the end - moveCursor( QTextCursor::End ); - } - } - break; - } - case Qt::Key_PageDown: - // key: process as follows: - // - without , modifiers: last command in history - // - with modifier key pressed: move cursor one page down without selection - // - with modifier key pressed: move cursor one page down with selection - // - with + modifier keys pressed: scroll one page down - { - if ( ctrlPressed && shftPressed ) { - verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); - } - else if ( shftPressed || ctrlPressed ) { - bool moved = false; - qreal lastY = cursorRect( cur ).top(); - qreal distance = 0; - // move using movePosition to keep the cursor's x - do { - qreal y = cursorRect( cur ).top(); - distance += qAbs( y - lastY ); - lastY = y; - moved = cur.movePosition( QTextCursor::Down, - shftPressed ? QTextCursor::KeepAnchor : - QTextCursor::MoveAnchor ); - } while ( moved && distance < viewport()->height() ); - if ( moved ) { - cur.movePosition( QTextCursor::Up, - shftPressed ? QTextCursor::KeepAnchor : - QTextCursor::MoveAnchor ); - verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub ); - } - setTextCursor( cur ); - } - else { - if ( myCmdInHistory >= 0 ) { - // unset history browsing mode - myCmdInHistory = -1; - // print current command - moveCursor( QTextCursor::End ); - moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - addText( myPrompt + myCurrentCommand ); - // move cursor to the end - moveCursor( QTextCursor::End ); - } - } - break; - } - case Qt::Key_Home: - // key: process as follows: - // - without , modifiers: move cursor to the beginning of the current line without selection - // - with modifier key pressed: move cursor to the very first symbol without selection - // - with modifier key pressed: move cursor to the beginning of the current line with selection - // - with + modifier keys pressed: move cursor to the very first symbol with selection - { - if ( ctrlPressed ) { - moveCursor( QTextCursor::Start, - shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); - } - else { - QString txt = textCursor().block().text(); - if ( isCommand( txt ) ) { - if ( shftPressed ) { - if ( curCol > promptSize() ) { - cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor ); - cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() ); - } - } - else { - cur.movePosition( QTextCursor::StartOfLine ); - cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() ); - } - setTextCursor( cur ); - } - else { - moveCursor( QTextCursor::StartOfBlock, - shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); - } - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - } - break; - } - case Qt::Key_End: - // key: process as follows: - // - without , modifiers: move cursor to the end of the current line without selection - // - with modifier key pressed: move cursor to the very last symbol without selection - // - with modifier key pressed: move cursor to the end of the current line with selection - // - with + modifier keys pressed: move cursor to the very last symbol with selection - { - moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock, - shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); - break; - } - case Qt::Key_Backspace : - // key: process as follows - // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt) - // - with modifier key pressed: delete previous word - // - with modifier key pressed: delete text from the cursor to the line beginning - // works only for last (command) line - { - if ( cur.hasSelection() ) { - cut(); - } - else if ( cur.position() > document()->end().previous().position() + promptSize() ) { - if ( shftPressed ) { - moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - } - else if ( ctrlPressed ) { - cur.setPosition( document()->end().previous().position() + promptSize(), - QTextCursor::KeepAnchor ); - setTextCursor( cur ); - textCursor().removeSelectedText(); - } - else { - QTextEdit::keyPressEvent( event ); - } - } - else { - cur.setPosition( document()->end().previous().position() + promptSize() ); - setTextCursor( cur ); - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - } - break; - } - case Qt::Key_Delete : - // key: process as follows - // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt) - // - with modifier key pressed: delete next word - // - with modifier key pressed: delete text from the cursor to the end of line - // works only for last (command) line - { - if ( cur.hasSelection() ) { - cut(); - } - else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) { - if ( shftPressed ) { - moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - } - else if ( ctrlPressed ) { - moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); - textCursor().removeSelectedText(); - } - else { - QTextEdit::keyPressEvent( event ); - } - } - else { - cur.setPosition( document()->end().previous().position() + promptSize() ); - setTextCursor( cur ); - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - } - break; - } - case Qt::Key_Insert : - // key: process as follows - // - with modifier key pressed: copy() - // - with modifier key pressed: paste() to the command line - { - if ( ctrlPressed ) { - copy(); - } - else if ( shftPressed ) { - paste(); - } - else - QTextEdit::keyPressEvent( event ); - break; - } - } -} - -/*! - \brief Handle notification event coming from Python dispatcher. - \param event notification event -*/ -void PyConsole_Editor::customEvent( QEvent* event ) -{ - switch( event->type() ) - { - case PrintEvent::EVENT_ID: - { - PrintEvent* pe=(PrintEvent*)event; - addText( pe->text(), false, pe->isError()); - return; - } - case PyInterp_Event::ES_OK: - case PyInterp_Event::ES_ERROR: - { - // clear command buffer - myCommandBuffer.truncate( 0 ); - // add caret return line if necessary - QTextBlock par = document()->end().previous(); - QString txt = par.text(); - txt.truncate( txt.length() - 1 ); - // IPAL19397 : addText moved to handleReturn() method - //if ( !txt.isEmpty() ) - // addText( "", true ); - // set "ready" prompt - myPrompt = READY_PROMPT; - addText( myPrompt ); - // unset busy cursor - unsetCursor(); - // stop event loop (if running) - if ( myEventLoop ) - myEventLoop->exit(); - break; - } - case PyInterp_Event::ES_INCOMPLETE: - { - // extend command buffer (multi-line command) - myCommandBuffer.append( "\n" ); - // add caret return line if necessary - QTextBlock par = document()->end().previous(); - QString txt = par.text(); - txt.truncate( txt.length() - 1 ); - // IPAL19397 : addText moved to handleReturn() method - //if ( !txt.isEmpty() ) - // addText( "", true ); - // set "dot" prompt - myPrompt = DOTS_PROMPT; - addText( myPrompt/*, true*/ ); // IPAL19397 - // unset busy cursor - unsetCursor(); - // stop event loop (if running) - if ( myEventLoop ) - myEventLoop->exit(); - break; - } - default: - QTextEdit::customEvent( event ); - } - - // unset read-only state - setReadOnly( false ); - // unset history browsing mode - myCmdInHistory = -1; - - if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 ) - { - // process the next sheduled command from the queue (if there is any) - QString nextcmd = myQueue[0]; - myQueue.pop_front(); - exec( nextcmd ); - } -} - -/*! - \brief Handle Python interpreter change. - - Perform initialization actions if the interpreter is changed. - \param interp python interpreter is being set -*/ -void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp ) -{ - if ( myInterp != interp - // Force read-only state and wait cursor when myInterp is NULL - || !myInterp ) { - myInterp = interp; - if ( myInterp ) { - // print banner - myBanner = myInterp->getbanner().c_str(); - if ( isShowBanner() ) - addText( myBanner ); - // clear command buffer - myCommandBuffer.truncate(0); - // unset read-only state - setReadOnly( false ); - // unset history browsing mode - myCmdInHistory = -1; - // add prompt - addText( myPrompt ); - // unset busy cursor - viewport()->unsetCursor(); - // stop event loop (if running) - if( myEventLoop) - myEventLoop->exit(); - } - else { - // clear contents - clear(); - // set read-only state - setReadOnly( true ); - // set busy cursor - setCursor( Qt::WaitCursor ); - } - } -} - -/*! - \brief "Copy" operation. - - Reimplemented from Qt. - Warning! In Qt4 this method is not virtual. - */ -void PyConsole_Editor::cut() -{ - QTextCursor cur = textCursor(); - if ( cur.hasSelection() ) { - QApplication::clipboard()->setText( cur.selectedText() ); - int startSelection = cur.selectionStart(); - if ( startSelection < document()->end().previous().position() + promptSize() ) - startSelection = document()->end().previous().position() + promptSize(); - int endSelection = cur.selectionEnd(); - if ( endSelection < document()->end().previous().position() + promptSize() ) - endSelection = document()->end().previous().position() + promptSize(); - cur.setPosition( startSelection ); - cur.setPosition( endSelection, QTextCursor::KeepAnchor ); - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - setTextCursor( cur ); - textCursor().removeSelectedText(); - } -} - -/*! - \brief "Paste" operation. - - Reimplemented from Qt. - Warning! In Qt4 this method is not virtual. - */ -void PyConsole_Editor::paste() -{ - QTextCursor cur = textCursor(); - if ( cur.hasSelection() ) { - int startSelection = cur.selectionStart(); - if ( startSelection < document()->end().previous().position() + promptSize() ) - startSelection = document()->end().previous().position() + promptSize(); - int endSelection = cur.selectionEnd(); - if ( endSelection < document()->end().previous().position() + promptSize() ) - endSelection = document()->end().previous().position() + promptSize(); - cur.setPosition( startSelection ); - cur.setPosition( endSelection, QTextCursor::KeepAnchor ); - horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); - setTextCursor( cur ); - textCursor().removeSelectedText(); - } - if ( textCursor().position() < document()->end().previous().position() + promptSize() ) - moveCursor( QTextCursor::End ); - QTextEdit::paste(); -} - -/*! - \brief "Clear" operation. - - Reimplemented from Qt. - Warning! In Qt4 this method is not virtual. - */ -void PyConsole_Editor::clear() -{ - QTextEdit::clear(); - if ( isShowBanner() ) - addText( myBanner ); - myPrompt = READY_PROMPT; - addText( myPrompt ); } /*! @@ -1164,18 +164,7 @@ void PyConsole_Editor::dump() QString fileName = SUIT_FileDlg::getFileName( this, QString(), aFilters, tr( "TOT_DUMP_PYCOMMANDS" ), false, true, new DumpCommandsFileValidator( this ) ); - if ( !fileName.isEmpty() ) { - QFile file( fileName ); - if ( !file.open( QFile::WriteOnly ) ) - return; - - QTextStream out (&file); - - for ( int i=0; i @@ -35,72 +36,16 @@ class PyConsole_Interp; class PyInterp_Request; class QEventLoop; -class PYCONSOLE_EXPORT PyConsole_Editor : public QTextEdit +class PYCONSOLE_EXPORT PyConsole_Editor : public PyConsole_EditorBase { Q_OBJECT; public: PyConsole_Editor( PyConsole_Interp* theInterp, QWidget *theParent = 0 ); ~PyConsole_Editor(); - - PyConsole_Interp* getInterp() const; - - virtual void addText( const QString& str, const bool newBlock = false, const bool isError = false ); - bool isCommand( const QString& str ) const; - - virtual void exec( const QString& command ); - void execAndWait( const QString& command ); - - bool isSync() const; - void setIsSync( const bool ); - - bool isSuppressOutput() const; - void setIsSuppressOutput(const bool); - - bool isShowBanner() const; - void setIsShowBanner( const bool ); - - bool isLogging() const; - - virtual QSize sizeHint() const; - public slots: - void cut(); - void paste(); - void clear(); - void handleReturn(); - void onPyInterpChanged( PyConsole_Interp* ); void dump(); - bool startLog( const QString& ); void startLog(); - void stopLog(); - void putLog( const QString& ); - -protected: - virtual void dropEvent( QDropEvent* event ); - virtual void mouseReleaseEvent( QMouseEvent* event ); - virtual void keyPressEvent ( QKeyEvent* event); - virtual void customEvent( QEvent* event); - - virtual PyInterp_Request* createRequest( const QString& ); - - /** Convenience function */ - inline int promptSize() const { return myPrompt.size(); } - - PyConsole_Interp* myInterp; //!< python interpreter - - QString myCommandBuffer; //!< python command buffer - QString myCurrentCommand; //!< currently being printed command - QString myPrompt; //!< current command line prompt - int myCmdInHistory; //!< current history command index - QString myLogFile; //!< current output log - QStringList myHistory; //!< commands history buffer - QEventLoop* myEventLoop; //!< internal event loop - QString myBanner; //!< current banner - bool myShowBanner; //!< 'show banner' flag - QStringList myQueue; //!< python commands queue - bool myIsSync; //!< synchronous mode flag - bool myIsSuppressOutput; //!< suppress output flag }; #endif // PYCONSOLE_EDITOR_H diff --git a/src/PyConsole/PyConsole_EnhEditor.cxx b/src/PyConsole/PyConsole_EnhEditor.cxx index d4cf274a2..ffca890af 100644 --- a/src/PyConsole/PyConsole_EnhEditor.cxx +++ b/src/PyConsole/PyConsole_EnhEditor.cxx @@ -30,14 +30,6 @@ #include #include "PyConsole_EnhEditor.h" -#include "PyConsole_EnhInterp.h" -#include "PyConsole_Request.h" -#include "PyInterp_Dispatcher.h" - -// Initialize list of valid separators -static const char * tmp_a[] = {" ", "(", "[","+", "-", "*", "/", ";", "^", "="}; -const std::vector PyConsole_EnhEditor::SEPARATORS = \ - std::vector(tmp_a, tmp_a + sizeof(tmp_a)/sizeof(tmp_a[0])); /** * Constructor. @@ -45,447 +37,6 @@ const std::vector PyConsole_EnhEditor::SEPARATORS = \ * @param parent parent widget */ PyConsole_EnhEditor::PyConsole_EnhEditor(PyConsole_Interp* interp, QWidget* parent) : - PyConsole_Editor(interp, parent), - _tab_mode(false), - _cursor_pos(-1), - _multi_line_paste(false), - _multi_line_content() -{ - document()->setUndoRedoEnabled(true); -} - -/** - * Overrides. Catches the TAB and Ctrl+TAB combinations. - * @param event - */ -void PyConsole_EnhEditor::keyPressEvent ( QKeyEvent* event) -{ - // check if is pressed - bool ctrlPressed = event->modifiers() & Qt::ControlModifier; - // check if is pressed - bool shftPressed = event->modifiers() & Qt::ShiftModifier; - - if (event->key() == Qt::Key_Tab && !shftPressed) - { - if (!ctrlPressed) - handleTab(); - else - { - clearCompletion(); - handleBackTab(); - } - PyConsole_Editor::keyPressEvent(event); - } - else - { - // If ctrl is not pressed (and sth else is pressed with it), - // or if ctrl is not pressed alone - if (!ctrlPressed || (ctrlPressed && event->key() != Qt::Key_Control)) - { - clearCompletion(); - _cursor_pos = -1; - } - // Discard ctrl pressed alone: - if (event->key() != Qt::Key_Control) - PyConsole_Editor::keyPressEvent(event); - } -} - -/** - * Whenever the mouse is clicked, clear the completion. - * @param e - */ -void PyConsole_EnhEditor::mousePressEvent(QMouseEvent* e) -{ - clearCompletion(); - _cursor_pos = -1; - PyConsole_Editor::mousePressEvent(e); -} - -/** - * Clear in the editor the block of text displayed after having hit . - */ -void PyConsole_EnhEditor::clearCompletion() -{ - // Delete completion text if present - if (_tab_mode) - { - // Remove completion display - document()->undo(); - // Remove trailing line return: - QTextCursor tc(textCursor()); - tc.setPosition(document()->characterCount()-1); - setTextCursor(tc); - textCursor().deletePreviousChar(); - // TODO: before wait for any TAB event to be completed - if ( myInterp ) - myInterp->clearCompletion(); - } - _tab_mode = false; -} - -/** - * Handle the sequence of events after having hit - */ -void PyConsole_EnhEditor::handleTab() -{ - if (_tab_mode) - { - // Already tab mode - nothing to do ! - return; - } - - QTextCursor cursor(textCursor()); - - // Cursor at end of input - cursor.movePosition(QTextCursor::End); - setTextCursor(cursor); - - // Save cursor position if needed - if (_cursor_pos == -1) - _cursor_pos = textCursor().position(); - - // get last line - QTextBlock par = document()->end().previous(); - if ( !par.isValid() ) return; - - // Switch to completion mode - _tab_mode = true; - - QString cmd = par.text().mid(promptSize()); - - // Post completion request - // Editor will be informed via a custom event that completion has been run - PyInterp_Request* req = createTabRequest(cmd); - PyInterp_Dispatcher::Get()->Exec(req); -} - -/** - * Handles what happens after hitting Ctrl-TAB - */ -void PyConsole_EnhEditor::handleBackTab() -{ - QTextCursor cursor(textCursor()); - - if (_cursor_pos == -1) - { - // Invalid cursor position - we can't do anything: - return; - } - // Ensure cursor is at the end of command line - cursor.setPosition(_cursor_pos); - cursor.movePosition(QTextCursor::EndOfLine); - //setCursor(cursor); - - // Delete last completed text - int i = cursor.position() - _cursor_pos; - cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, i); - cursor.removeSelectedText(); - _cursor_pos = -1; -} - -/** - * Create the Python requested that will be posted to the interpreter to - * get the completions. - * @param input line typed by the user at the time TAB was hit - * @return a CompletionCommand - * @sa CompletionCommand - */ -PyInterp_Request* PyConsole_EnhEditor::createTabRequest( const QString& input ) -{ - // Parse input to extract on what part the dir() has to be executed - QString input2(input); - - // Split up to the last syntaxical separator - int lastSp = -1; - for (std::vector::const_iterator i = SEPARATORS.begin(); i != SEPARATORS.end(); i++) - { - int j = input2.lastIndexOf(*i); - if (j > lastSp) - lastSp = j; - } - if (lastSp >= 0) - input2 = input.mid(lastSp+1); - - // Detect a qualified name (with a point) - int lastPt = input2.lastIndexOf(QString(".")); - - // Split the 2 surrounding parts of the qualified name - if (lastPt != -1) - { - _compl_before_point = input2.left(lastPt); - _compl_after_point = input2.mid(lastPt+1); - } - else - { - // No point found - do a global matching - - // (the following will call dir() with an empty string) - _compl_after_point = input2; - _compl_before_point = QString(""); - } - - return new CompletionCommand( myInterp, _compl_before_point, - _compl_after_point, this, isSync() ); -} - -/** - * Format completion results - this is where we should create 3 columns etc ... - * @param matches list of possible completions - * @param result return value - */ -void PyConsole_EnhEditor::formatCompletion(const QStringList& matches, QString& result) const -{ - int sz = matches.size(); - - if (sz > MAX_COMPLETIONS) - { - sz = MAX_COMPLETIONS; - result.append("[Too many matches! Displaying first ones only ...]\n"); - } - - for (int i = 0; i < sz; ++i) - { - result.append(matches[i]); - result.append("\n"); - } -} - -/** - * Override. Catches the events generated by the enhanced interpreter after the execution - * of a completion request. - * @param event - */ -void PyConsole_EnhEditor::customEvent( QEvent* event ) -{ - QStringList matches; - QString first_match, comple_text, doc, base; - QTextCursor cursor(textCursor()); - QTextBlockFormat bf; - QTextCharFormat cf; - int cursorPos; - - switch( event->type() ) - { - case PyInterp_Event::ES_TAB_COMPLETE_OK: - { - // Extract corresponding matches from the interpreter - matches = getInterp()->getLastMatches(); - doc = getInterp()->getDocStr(); - - if (matches.size() == 0) - { - // Completion successful but nothing returned. - _tab_mode = false; - _cursor_pos = -1; - return; - } - - // Only one match - complete directly and update doc string window - if (matches.size() == 1) - { - first_match = matches[0].mid(_compl_after_point.size()); - cursor.insertText(first_match); - _tab_mode = false; - if (doc.isEmpty()) - emit updateDoc(formatDocHTML("(no documentation available)\n")); - else - emit updateDoc(formatDocHTML(doc)); - } - else - { - // Detect if there is a common base to all available completion - // In this case append this base to the text already - extractCommon(matches, base); - first_match = base.mid(_compl_after_point.size()); - cursor.insertText(first_match); - - // If this happens to match exaclty the first completion - // also provide doc - if (base == matches[0]) - { - doc = formatDocHTML(doc); - emit updateDoc(doc); - } - - // Print all matching completion in a "undo-able" block - cursorPos = cursor.position(); - cursor.insertBlock(); - cursor.beginEditBlock(); - - // Insert all matches - QTextCharFormat cf; - cf.setForeground(QBrush(Qt::darkGreen)); - cursor.setCharFormat(cf); - formatCompletion(matches, comple_text); - cursor.insertText(comple_text); - cursor.endEditBlock(); - - // Position cursor where it was before inserting the completion list: - cursor.setPosition(cursorPos); - setTextCursor(cursor); - } - break; - } - case PyInterp_Event::ES_TAB_COMPLETE_ERR: - { - // Tab completion was unsuccessful, switch off mode: - _tab_mode = false; - _cursor_pos = -1; - break; - } - case PyInterp_Event::ES_OK: - case PyInterp_Event::ES_ERROR: - case PyInterp_Event::ES_INCOMPLETE: - { - // Before everything else, call super() - PyConsole_Editor::customEvent(event); - // If we are in multi_paste_mode, process the next item: - multiLineProcessNextLine(); - break; - } - default: - { - PyConsole_Editor::customEvent( event ); - break; - } - } -} - -/** - * Extract the common leading part of all strings in matches. - * @param matches - * @param result - */ -void PyConsole_EnhEditor::extractCommon(const QStringList& matches, QString& result) const -{ - result = ""; - int charIdx = 0; - - if (matches.size() < 2) - return; - - while (true) - { - if (charIdx >= matches[0].size()) - return; - QChar ch = matches[0][charIdx]; - for (int j = 1; j < matches.size(); j++) - if (charIdx >= matches[j].size() || matches[j][charIdx] != ch) - return; - result += ch; - charIdx++; - } -} - -/** - * Format the doc string in HTML format with the first line in bold blue - * @param doc initial doc string - * @return HTML string - */ -QString PyConsole_EnhEditor::formatDocHTML(const QString & doc) const -{ - QString templ = QString("\n ") + - QString(" ") + - QString(" ") + - QString("\n") + - QString("

") + - QString("%1

") + - QString("

%2

") + - QString(""); - - QString fst, rest(""); - - // Extract first line of doc - int idx = doc.indexOf("\n"); - if (idx > 0) - { - fst = doc.left(idx); - rest = doc.mid(idx+1); - } - else - { - fst = doc; - } - - fst = fst.replace("\n", " "); - rest = rest.replace("\n", " "); - return templ.arg(fst).arg(rest); -} - -/** - * Handle properly multi-line pasting. Qt4 doc recommends overriding this function. - * If the pasted text doesn't contain a line return, no special treatment is done. - * @param source - */ -void PyConsole_EnhEditor::insertFromMimeData(const QMimeData* source) -{ - if (_multi_line_paste) - return; - - if (source->hasText()) - { - QString s = source->text(); - if (s.contains("\n")) - multilinePaste(s); - else - PyConsole_Editor::insertFromMimeData(source); - } - else - { - PyConsole_Editor::insertFromMimeData(source); - } -} - - -void PyConsole_EnhEditor::multilinePaste(const QString & s) + PyConsole_EnhEditorBase(interp, parent) { - // Turn on multi line pasting mode - _multi_line_paste = true; - - // Split the string: - QString s2 = s; - s2.replace("\r", ""); // Windows string format converted to Unix style - - QStringList lst = s2.split(QChar('\n'), QString::KeepEmptyParts); - - // Perform the proper paste operation for the first line to handle the case where - // sth was already there: - QMimeData source; - source.setText(lst[0]); - PyConsole_Editor::insertFromMimeData(&source); - - // Prepare what will have to be executed after the first line: - _multi_line_content = std::queue(); - for (int i = 1; i < lst.size(); ++i) - _multi_line_content.push(lst[i]); - - // Trigger the execution of the first (mixed) line - handleReturn(); - - // See customEvent() and multiLineProcessNext() for the rest of the handling. -} - -/** - * Process the next line in the queue of a multiple copy/paste: - */ -void PyConsole_EnhEditor::multiLineProcessNextLine() -{ - if (!_multi_line_paste) - return; - - QString line(_multi_line_content.front()); - _multi_line_content.pop(); - if (!_multi_line_content.size()) - { - // last line in the queue, just paste it - addText(line, false, false); - _multi_line_paste = false; - } - else - { - // paste the line and simulate a key stroke - addText(line, false, false); - handleReturn(); - } } diff --git a/src/PyConsole/PyConsole_EnhEditor.h b/src/PyConsole/PyConsole_EnhEditor.h index f505066d3..ea20d1b3f 100644 --- a/src/PyConsole/PyConsole_EnhEditor.h +++ b/src/PyConsole/PyConsole_EnhEditor.h @@ -23,7 +23,7 @@ #define PYCONSOLE_ENHEDITOR_H_ #include "PyConsole.h" -#include "PyConsole_Editor.h" +#include "PyConsole_EnhEditorBase.h" #include #include @@ -31,65 +31,12 @@ /** * Enhanced Python editor handling tab completion. */ -class PYCONSOLE_EXPORT PyConsole_EnhEditor: public PyConsole_Editor +class PYCONSOLE_EXPORT PyConsole_EnhEditor : public PyConsole_EnhEditorBase { Q_OBJECT; - public: PyConsole_EnhEditor(PyConsole_Interp* interp, QWidget* parent = 0); virtual ~PyConsole_EnhEditor() {} - -signals: - /** - * Signal emitted by the editor widget when the doc string should be updated. - * @param doc a HTML block with the formatted doc string. - * TODO: for now this signal is left uncaught. - */ - void updateDoc(QString doc); - -protected: - /** List of separators identifying the last parsable token for completion */ - static const std::vector SEPARATORS; - - /** Maximum number of completions shown at once */ - static const int MAX_COMPLETIONS = 70; - - /** Are we in completion mode */ - bool _tab_mode; - - /** String on which the dir() comamnd is executed */ - QString _compl_before_point; - /** String on which the results of the dir() are matched */ - QString _compl_after_point; - - /** Cursor position when is hit */ - int _cursor_pos; - - /** Are we currently pasting several lines */ - bool _multi_line_paste; - - /** Queue of lines being pasted */ - std::queue _multi_line_content; - - // Overrides: - virtual void keyPressEvent ( QKeyEvent* event); - virtual void customEvent( QEvent* event); - virtual void mousePressEvent( QMouseEvent* event ); - virtual void insertFromMimeData(const QMimeData* source); - - virtual PyInterp_Request* createTabRequest( const QString& input ); - virtual void handleTab(); - virtual void handleBackTab(); - virtual void clearCompletion(); - virtual void formatCompletion(const QStringList& matches, QString& result) const; - virtual QString formatDocHTML(const QString & doc) const; - - virtual void multilinePaste(const QString & s); - virtual void multiLineProcessNextLine(); - -private: - void extractCommon(const QStringList& matches, QString& result) const; - }; #endif /* PYCONSOLE_ENHEDITOR_H_ */ diff --git a/src/PyConsole/PyConsole_EnhInterp.cxx b/src/PyConsole/PyConsole_EnhInterp.cxx deleted file mode 100644 index bb3230466..000000000 --- a/src/PyConsole/PyConsole_EnhInterp.cxx +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (C) 2007-2015 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, or (at your option) any later version. -// -// 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 -// -// Author : Adrien Bruneton (CEA/DEN) -// Created on: 4 avr. 2013 - - -#include "PyConsole.h" - -#include "PyConsole_EnhInterp.h" - -#include -#include -#include - -static const char * tmp_k[] = {"and", "as", "assert", "break", "class", - "continue", "def", "del", - "elif", "else", "except", "exec", "finally", "for", "from", "global", "if", - "import", "in", "is", "lambda", "not", "or", "pass", "print", "raise", - "return", "try", "while", "with", "yield"}; - -const std::vector PyConsole_EnhInterp::PYTHON_KEYWORDS = \ - std::vector(tmp_k, tmp_k+sizeof(tmp_k)/sizeof(tmp_k[0])); - -/*! - \brief Constructor -*/ -PyConsole_EnhInterp::PyConsole_EnhInterp() - : PyConsole_Interp() -{ -} - -/*! - \brief Destructor -*/ -PyConsole_EnhInterp::~PyConsole_EnhInterp() -{ -} - -QStringList PyConsole_EnhInterp::getLastMatches() const -{ - return _last_matches; -} - -QString PyConsole_EnhInterp::getDocStr() const -{ - return _doc_str; -} - -/*! - \brief Run Python dir() command and saves the result internally in _lastPy - \param dirArgument Python expression to pass to the dir command. The parsing of what the - user actually started typing is dedicated to the caller - \param startMatch string representing the begining of the patter to be completed. For example when - the user types "a_string_variable.rsp ", this is "rsp". - \return command exit status - 0 = success -*/ -int PyConsole_EnhInterp::runDirCommand(const QString& dirArgument, const QString& startMatch) -{ - int ret; - std::vector v; - - clearCompletion(); - if ( (ret = runDirAndExtract(dirArgument, startMatch, _last_matches)) ) - return ret; - - // If dirArgument is empty, we append the __builtins__ - if (dirArgument.isEmpty()) - { - if ( (ret = runDirAndExtract(QString("__builtins__"), startMatch, _last_matches, false)) ) - return ret; - - // ... and we match on Python's keywords as well: - for (std::vector::const_iterator it = PYTHON_KEYWORDS.begin(); it != PYTHON_KEYWORDS.end(); it++) - if ((*it).startsWith(startMatch)) - _last_matches.push_back(*it); - } - - // Try to get doc string of the first match - if (_last_matches.size() > 0) - { - QString cmd(""); - if (dirArgument.trimmed() != "") - cmd = dirArgument + "."; - cmd += _last_matches[0] + ".__doc__"; - PyObject * str = PyRun_String(cmd.toStdString().c_str(), Py_eval_input, _global_context, _local_context); - if (!str || str == Py_None || !PyString_Check(str)) - { - if (!str) - PyErr_Clear(); - _doc_str = ""; - } - else - _doc_str = QString(PyString_AsString(str)); - Py_XDECREF(str); - } - - // The command has been successfully executed - return 0; -} - -/** - * See runDirCommand(). - * @param dirArgument see runDirCommand() - * @param startMatch see runDirCommand() - * @param[out] result the list of matches - * @param discardSwig if true a regular expression is used to discard all static method generated - * by SWIG. typically: MEDCouplingUMesh_Blabla - * @return -1 if the call to dir() or the parsing of the result failed, 0 otherwise. - */ -int PyConsole_EnhInterp::runDirAndExtract(const QString& dirArgument, - const QString& startMatch, - QStringList& result, - bool discardSwig) const -{ - QRegExp re("^[A-Z].+_[A-Z]+[a-z]+.+$"); // discard SWIG static method, e.g. MEDCouplingUMesh_Blabla - QString command("dir(" + dirArgument + ")"); - PyObject * plst = PyRun_String(command.toStdString().c_str(), Py_eval_input, _global_context, _local_context); - if(!plst || plst == Py_None) { - if(!plst) - PyErr_Clear(); - - Py_XDECREF(plst); - return -1; - } - - // Extract the returned list and convert it to a vector<> - if (!PySequence_Check(plst)) - { - // Should never happen ... - //std::cerr << "not a list!" << std::endl; - Py_XDECREF(plst); - return -1; - } - - // Convert plst to a vector of QString - int n = PySequence_Length(plst); - for (int i = 0; i < n; i++) - { - PyObject * it; - it = PySequence_GetItem(plst, i); - QString s(PyString_AsString(it)); - // if the method is not from swig, not static (guessed from the reg exp) and matches - // what is already there - if (s.startsWith(startMatch)) - if(!discardSwig || (!re.exactMatch(s) && !s.contains("swig"))) - result.push_back(s); - Py_DECREF(it); - } - Py_DECREF(plst); - - return 0; -} - -/** - * Clear internal members containing the last completion results. - */ -void PyConsole_EnhInterp::clearCompletion() -{ - _last_matches.clear(); - _doc_str = ""; -} diff --git a/src/PyConsole/PyConsole_EnhInterp.h b/src/PyConsole/PyConsole_EnhInterp.h deleted file mode 100644 index b8fb04a77..000000000 --- a/src/PyConsole/PyConsole_EnhInterp.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2007-2015 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, or (at your option) any later version. -// -// 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 -// -// Author : Adrien Bruneton (CEA/DEN) -// Created on: 4 avr. 2013 - - -#ifndef PYCONSOLE_ENHINTERP_H_ -#define PYCONSOLE_ENHINTERP_H_ - -#include "PyConsole.h" - -#include -#include "PyConsole_Interp.h" - -#include -#include - -/** - * Enhanced Python interpreter used for auto-completion. - * This extends PyConsole_Interp with an API wrapping the Python dir() command nicely. - */ -class PYCONSOLE_EXPORT PyConsole_EnhInterp: public PyConsole_Interp -{ -public: - PyConsole_EnhInterp(); - virtual ~PyConsole_EnhInterp(); - - virtual QStringList getLastMatches() const; - virtual QString getDocStr() const; - - virtual int runDirCommand(const QString& dirArgument, const QString& startMatch); - virtual void clearCompletion(); - -protected: - /** Hard coded list of Python keywords */ - static const std::vector PYTHON_KEYWORDS; - - /** Last computed matches */ - QStringList _last_matches; - /** Doc string of the first match - when only one match it will be displayed by the Editor*/ - QString _doc_str; - - virtual int runDirAndExtract(const QString& dirArgument, const QString& startMatch, - QStringList& result, bool discardSwig=true) const; -}; - -#endif /* PYCONSOLE_ENHINTERP_H_ */ diff --git a/src/PyConsole/PyConsole_Event.cxx b/src/PyConsole/PyConsole_Event.cxx deleted file mode 100644 index aaeb2576f..000000000 --- a/src/PyConsole/PyConsole_Event.cxx +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE -// -// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -// -// 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, or (at your option) any later version. -// -// 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 -// -// Author : Vadim SANDLER (Open CASCADE S.A.S), Adrien Bruneton (CEA/DEN) - -#include "PyConsole_Event.h" diff --git a/src/PyConsole/PyConsole_Event.h b/src/PyConsole/PyConsole_Event.h deleted file mode 100644 index 6ec1d51bb..000000000 --- a/src/PyConsole/PyConsole_Event.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE -// -// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -// -// 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, or (at your option) any later version. -// -// 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 -// -// Author : Vadim SANDLER (Open CASCADE S.A.S), Adrien Bruneton (CEA/DEN) - -#ifndef PYCONSOLE_EVENT_H -#define PYCONSOLE_EVENT_H - -#include "PyConsole.h" - -#include -#include - -/*! - \class PrintEvent - \brief Python command output backend event. - \internal -*/ -class PrintEvent : public QEvent -{ -public: - static const int EVENT_ID = 65432; - - /*! - \brief Constructor - \param c message text (python trace) - \param isError default to false - if true indicates that an error is being printed. - */ - PrintEvent( const QString& c, bool isError = false) : - QEvent( (QEvent::Type)EVENT_ID ), myText( c ), errorFlag(isError) - {} - - /*! - \brief Get message - \return message text (python trace) - */ - QString text() const { return myText; } - - /** - * @return true if this is an error message - */ - bool isError() const { return errorFlag; } - -protected: - QString myText; //!< Event message (python trace) - - /** Set to true if an error msg is to be displayed */ - bool errorFlag; -}; - -#endif // PYCONSOLE_EVENT_H diff --git a/src/PyConsole/PyConsole_Interp.cxx b/src/PyConsole/PyConsole_Interp.cxx deleted file mode 100644 index ca459b582..000000000 --- a/src/PyConsole/PyConsole_Interp.cxx +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE -// -// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -// -// 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, or (at your option) any later version. -// -// 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 : PyConsole_Interp.cxx -// Author : Nicolas REJNERI, Adrien BRUNETON - -#include "PyConsole_Interp.h" - -/*! - \class PyConsole_Interp - \brief Python interpreter to be embedded to the SALOME study's GUI. - - There is only one Python interpreter for the whole SALOME environment. - - Call the initialize() method defined in the base class PyInterp_Interp, - to initialize the interpreter after instance creation. - - The method initialize() calls virtuals methods - - initPython() to initialize global Python interpreter - - initContext() to initialize interpreter internal context - - initRun() to prepare interpreter for running commands -*/ - -/*! - \brief Constructor. - - Creates new python interpreter. -*/ -PyConsole_Interp::PyConsole_Interp(): PyInterp_Interp() -{ -} - -/*! - \brief Destructor. - - Does nothing for the moment. -*/ -PyConsole_Interp::~PyConsole_Interp() -{ -} - -/*! Sets the variable "__IN_SALOME_GUI_CONSOLE" to True. -* This is not attached to a module (like salome_iapp.IN_SALOME_GUI_CONSOLE) -* since modules are shared across all interpreters in SALOME. -* -* (GIL is already acquired here) -*/ -int PyConsole_Interp::beforeRun() -{ - return PyRun_SimpleString("__builtins__.__IN_SALOME_GUI_CONSOLE=True"); -} - -int PyConsole_Interp::afterRun() -{ - return PyRun_SimpleString("__builtins__.__IN_SALOME_GUI_CONSOLE=False"); -} - -QStringList PyConsole_Interp::getLastMatches() const -{ - return QStringList(); -} - -QString PyConsole_Interp::getDocStr() const -{ - return QString(); -} - -int PyConsole_Interp::runDirCommand(const QString&, const QString& ) -{ - return 0; -} - -void PyConsole_Interp::clearCompletion() -{ -} diff --git a/src/PyConsole/PyConsole_Interp.h b/src/PyConsole/PyConsole_Interp.h deleted file mode 100644 index 1aac73683..000000000 --- a/src/PyConsole/PyConsole_Interp.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE -// -// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS -// -// 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, or (at your option) any later version. -// -// 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 : PyConsole_Interp.h -// Author : Nicolas REJNERI, Adrien BRUNETON - -#ifndef PYCONSOLE_INTERP_H -#define PYCONSOLE_INTERP_H - -#include "PyConsole.h" -#include "PyInterp_Interp.h" /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!! - -#include - -class PYCONSOLE_EXPORT PyConsole_Interp : public PyInterp_Interp -{ -public: - PyConsole_Interp(); - ~PyConsole_Interp(); - - virtual int afterRun(); - virtual int beforeRun(); - - virtual QStringList getLastMatches() const; - virtual QString getDocStr() const; - - virtual int runDirCommand(const QString&, const QString&); - virtual void clearCompletion(); -}; - -#endif // PYCONSOLE_INTERP_H diff --git a/src/PyConsole/PyConsole_Request.cxx b/src/PyConsole/PyConsole_Request.cxx deleted file mode 100644 index 5c67b8bbb..000000000 --- a/src/PyConsole/PyConsole_Request.cxx +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (C) 2007-2015 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, or (at your option) any later version. -// -// 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 -// -// Author : Adrien Bruneton (CEA/DEN) -// Created on: 3 avr. 2013 - -#include "PyConsole_Request.h" -#include "PyConsole_Interp.h" -#include "PyConsole_Event.h" -#include "PyInterp_Event.h" - -#include - -/** - * Constructor. - * @param theInterp interpreter that will execute the command - * @param theCommand command text - * @param theListener editor object that will receive the response events after execution - * of the request - * @param sync - */ -ExecCommand::ExecCommand( PyInterp_Interp* theInterp, - const QString& theCommand, - QObject* theListener, - bool theSync ) - : PyInterp_LockRequest( theInterp, theListener, theSync ), - myCommand( theCommand ), myState( PyInterp_Event::ES_OK ) -{} - -/** - * Execute the command by calling the run() method of the embedded interpreter. - */ -void ExecCommand::execute() -{ - if ( myCommand != "" ) - { - int ret = getInterp()->run( myCommand.toLatin1().data() ); - if ( ret < 0 ) - myState = PyInterp_Event::ES_ERROR; - else if ( ret > 0 ) - myState = PyInterp_Event::ES_INCOMPLETE; - } -} - -/** - * Create the event indicating the status of the request execution. - * @return a QEvent - */ -QEvent* ExecCommand::createEvent() -{ - if ( IsSync() ) - QCoreApplication::sendPostedEvents( listener(), PrintEvent::EVENT_ID ); - return new PyInterp_Event( myState, this ); -} - - -/*! - Constructor. - Creates a new python completion request. - \param theInterp python interpreter - \param input string containing the dir() command to be executed - \param startMatch part to be matched with the results of the dir() command - \param theListener widget to get the notification messages - \param sync if True the request is processed synchronously -*/ -CompletionCommand::CompletionCommand( PyInterp_Interp* theInterp, - const QString& theInput, - const QString& theStartMatch, - QObject* theListener, - bool theSync ) - : PyInterp_LockRequest( theInterp, theListener, theSync ), - _tabSuccess(false), _dirArg(theInput), _startMatch(theStartMatch) -{} - -/** - * Execute the completion command by wrapping the runDirCommand() of the - * embedded enhanced interpreter. - */ -void CompletionCommand::execute() -{ - int ret = static_cast(getInterp())->runDirCommand( _dirArg, _startMatch ); - _tabSuccess = ret == 0; -} - -/** - * Create the event indicating the return value of the completion command. - * @return - */ -QEvent* CompletionCommand::createEvent() -{ - int typ = _tabSuccess ? PyInterp_Event::ES_TAB_COMPLETE_OK : PyInterp_Event::ES_TAB_COMPLETE_ERR; - return new PyInterp_Event( typ, this); -} diff --git a/src/PyConsole/PyConsole_Request.h b/src/PyConsole/PyConsole_Request.h deleted file mode 100644 index a0ed598eb..000000000 --- a/src/PyConsole/PyConsole_Request.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2007-2015 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, or (at your option) any later version. -// -// 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 -// -// Author : Adrien Bruneton (CEA/DEN) -// Created on: 3 avr. 2013 - - -#ifndef PYCONSOLE_REQUEST_H_ -#define PYCONSOLE_REQUEST_H_ - -#include "PyInterp_Request.h" - -#include -#include -#include - -class PyInterp_Interp; - -/*! - \class ExecCommand - \brief Python command execution request. - \internal -*/ -class ExecCommand : public PyInterp_LockRequest -{ -public: - /*! - \brief Constructor. - - Creates new python command execution request. - \param theInterp python interpreter - \param theCommand python command - \param theListener widget to get the notification messages - \param sync if True the request is processed synchronously - */ - ExecCommand( PyInterp_Interp* theInterp, - const QString& theCommand, - QObject* theListener, - bool theSync = false ); - -protected: - /*! - \brief Execute the python command in the interpreter and - get its execution status. - */ - virtual void execute(); - - /*! - \brief Create and return a notification event. - \return new notification event - */ - virtual QEvent* createEvent(); - -private: - QString myCommand; //!< Python command - int myState; //!< Python command execution status -}; - -class CompletionCommand : public PyInterp_LockRequest -{ -public: - CompletionCommand( PyInterp_Interp* theInterp, - const QString& theInput, - const QString& theStartMatch, - QObject* theListener, - bool theSync = false ); - - -protected: - /** List of separators identifying the last parsable token for completion */ - static const std::vector SEPARATORS; - - /** String to be passed to the dir() command */ - QString _dirArg; - /** Begining of the command (as typed by the user) */ - QString _startMatch; - /** was the completion command successful */ - bool _tabSuccess; - - virtual void execute(); - virtual QEvent* createEvent(); -}; - -#endif /* PYCONSOLE_REQUEST_H_ */ diff --git a/src/PyConsole/resources/PyConsole_msg_en.ts b/src/PyConsole/resources/PyConsole_msg_en.ts deleted file mode 100644 index f8ce3050a..000000000 --- a/src/PyConsole/resources/PyConsole_msg_en.ts +++ /dev/null @@ -1,62 +0,0 @@ - - - - - PyConsole_ConsoleBase - - - EDIT_COPY_CMD - &Copy - - - - EDIT_PASTE_CMD - &Paste - - - - EDIT_CLEAR_CMD - Clea&r - - - - EDIT_SELECTALL_CMD - Select &All - - - EDIT_DUMPCOMMANDS_CMD - D&ump Commands - - - EDIT_STARTLOG_CMD - Start &Log - - - EDIT_STOPLOG_CMD - Stop &Log - - - - PyConsole_Editor - - TOT_DUMP_PYCOMMANDS - Dump commands - - - TOT_SAVE_PYLOG - Save log - - - PYTHON_FILES_FILTER - PYTHON Files (*.py) - - - LOG_FILES_FILTER - Log files (*.log *.txt) - - - ERR_FILE_NOT_WRITABLE - File is not writable! - - - diff --git a/src/PyConsole/resources/PyConsole_msg_fr.ts b/src/PyConsole/resources/PyConsole_msg_fr.ts deleted file mode 100755 index bab57bfa6..000000000 --- a/src/PyConsole/resources/PyConsole_msg_fr.ts +++ /dev/null @@ -1,62 +0,0 @@ - - - - - PyConsole_ConsoleBase - - - EDIT_COPY_CMD - &Copier - - - - EDIT_PASTE_CMD - C&oller - - - - EDIT_CLEAR_CMD - &Effacer - - - - EDIT_SELECTALL_CMD - &Tout sélectionner - - - EDIT_DUMPCOMMANDS_CMD - &Générer le script des commandes - - - EDIT_STARTLOG_CMD - Démarrer une &trace - - - EDIT_STOPLOG_CMD - Arrêter la &trace - - - - PyConsole_Editor - - TOT_DUMP_PYCOMMANDS - &Générer le script des commandes - - - TOT_SAVE_PYLOG - Sauver la trace - - - PYTHON_FILES_FILTER - Fichiers PYTHON (*.py) - - - LOG_FILES_FILTER - Fichiers de trace (*.log *.txt) - - - ERR_FILE_NOT_WRITABLE - Le fichier n'est pas accessible en écriture! - - - diff --git a/src/PyConsole/resources/PyConsole_msg_ja.ts b/src/PyConsole/resources/PyConsole_msg_ja.ts deleted file mode 100644 index c36cf6ac3..000000000 --- a/src/PyConsole/resources/PyConsole_msg_ja.ts +++ /dev/null @@ -1,62 +0,0 @@ - - - - - PyConsole_ConsoleBase - - - EDIT_COPY_CMD - コピー(&C) - - - - EDIT_PASTE_CMD - 貼り付け(&P) - - - - EDIT_CLEAR_CMD - 削除(&r) - - - - EDIT_SELECTALL_CMD - すべて選択します。(&A) - - - EDIT_DUMPCOMMANDS_CMD - スクリプト コマンドを生成します。(&u) - - - EDIT_STARTLOG_CMD - ログの開始 (&L) - - - EDIT_STOPLOG_CMD - ログの停止 (&L) - - - - PyConsole_Editor - - TOT_DUMP_PYCOMMANDS - スクリプト コマンドを生成します。 - - - TOT_SAVE_PYLOG - ログの保存 - - - PYTHON_FILES_FILTER - ファイル (*.py) PYTHON - - - LOG_FILES_FILTER - ログファイル (*.log *.txt) - - - ERR_FILE_NOT_WRITABLE - ファイルが書き込み禁止です! - - - diff --git a/src/PyConsoleBase/CMakeLists.txt b/src/PyConsoleBase/CMakeLists.txt new file mode 100755 index 000000000..d70defa36 --- /dev/null +++ b/src/PyConsoleBase/CMakeLists.txt @@ -0,0 +1,97 @@ +# Copyright (C) 2012-2015 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, or (at your option) any later version. +# +# 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 +# + +INCLUDE(UseQtExt) + +# --- options --- + +# additional include directories +INCLUDE_DIRECTORIES( + ${QT_INCLUDES} + ${PYTHON_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR}/src/SUITT + ${PROJECT_SOURCE_DIR}/src/PyConsoleBase + ${PROJECT_SOURCE_DIR}/src/Event + ${PROJECT_SOURCE_DIR}/src/PyInterp +) + +# additional preprocessor / compiler flags +ADD_DEFINITIONS(${QT_DEFINITIONS} ${PYTHON_DEFINITIONS}) + +# libraries to link to +SET(_link_LIBRARIES ${QT_LIBRARIES} ${PYTHON_LIBRARIES} PyInterp Event suitt) + +# --- headers --- + +# header files / to be processed by moc +SET(_moc_HEADERS + PyConsole_ConsoleBase.h + PyConsole_EditorBase.h + PyConsole_EnhEditorBase.h +) + +# header files / no moc processing +SET(_other_HEADERS + PyConsoleBase.h + PyConsole_EnhInterpBase.h + PyConsole_Event.h + PyConsole_Interp.h + PyConsole_Request.h +) + +# header files / to install +SET(PyConsoleBase_HEADERS ${_moc_HEADERS} ${_other_HEADERS}) + +# --- resources --- + +# resource files / to be processed by lrelease +SET(_ts_RESOURCES + resources/PyConsole_msg_en.ts + resources/PyConsole_msg_fr.ts + resources/PyConsole_msg_ja.ts +) + +# --- sources --- + +# sources / moc wrappings +QT_WRAP_MOC(_moc_SOURCES ${_moc_HEADERS}) + +# sources / static +SET(_other_SOURCES + PyConsole_ConsoleBase.cxx + PyConsole_EnhInterp.cxx + PyConsole_Event.cxx + PyConsole_Interp.cxx + PyConsole_Request.cxx + PyConsole_EditorBase.cxx + PyConsole_EnhEditorBase.cxx +) + +# sources / to compile +SET(PyConsoleBase_SOURCES ${_other_SOURCES} ${_moc_SOURCES}) + +# --- rules --- + +ADD_LIBRARY(PyConsoleBase ${PyConsoleBase_SOURCES}) +TARGET_LINK_LIBRARIES(PyConsoleBase ${_link_LIBRARIES}) +INSTALL(TARGETS PyConsoleBase EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME_INSTALL_LIBS}) + +INSTALL(FILES ${PyConsoleBase_HEADERS} DESTINATION ${SALOME_INSTALL_HEADERS}) +QT_INSTALL_TS_RESOURCES("${_ts_RESOURCES}" "${SALOME_GUI_INSTALL_RES_DATA}") + diff --git a/src/PyConsoleBase/PyConsoleBase.h b/src/PyConsoleBase/PyConsoleBase.h new file mode 100644 index 000000000..fcef126cc --- /dev/null +++ b/src/PyConsoleBase/PyConsoleBase.h @@ -0,0 +1,48 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 : PyConsoleBase.h +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// +#if !defined ( PYCONSOLEBASE_H ) +#define PYCONSOLEBASE_H + +// ======================================================== +// set dllexport type for Win platform +#ifdef WIN32 +# if defined PYCONSOLEBASE_EXPORTS || defined PyConsoleBase_EXPORTS +# define PYCONSOLEBASE_EXPORT __declspec(dllexport) +# else +# define PYCONSOLEBASE_EXPORT __declspec(dllimport) +# endif +#else // WIN32 +# define PYCONSOLEBASE_EXPORT +#endif // WIN32 + +// ======================================================== +// avoid warning messages +#ifdef WIN32 +#pragma warning (disable : 4786) +#pragma warning (disable : 4251) +#endif + +#endif // PYCONSOLEBASE_H diff --git a/src/PyConsoleBase/PyConsole_ConsoleBase.cxx b/src/PyConsoleBase/PyConsole_ConsoleBase.cxx new file mode 100644 index 000000000..3e49f2f24 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_ConsoleBase.cxx @@ -0,0 +1,349 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 : PyConsole_ConsoleBase.cxx +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// +/*! + \class PyConsole_Console + \brief Python console widget. +*/ + +#include "PyConsole_Interp.h" /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!! +#include "PyConsole_ConsoleBase.h" +#include "PyConsole_EnhEditorBase.h" +#include "PyConsole_EnhInterp.h" + +#include +#include +#include +#include +#include +#include + +/*! + \brief Constructor. + + Creates new python console widget. + \param parent parent widget + \param interp python interpreter +*/ +PyConsole_ConsoleBase::PyConsole_ConsoleBase( QWidget* parent, PyConsole_Interp* interp ) +: QWidget( parent ) +{ + PyConsole_Interp* anInterp = interp ? interp : new PyConsole_Interp(); + + // initialize Python interpretator + anInterp->initialize(); + + // create editor console + QVBoxLayout* lay = new QVBoxLayout( this ); + lay->setMargin( 0 ); + myEditor = new PyConsole_EditorBase( anInterp, this ); + char* synchronous = getenv("PYTHON_CONSOLE_SYNC"); + if (synchronous && atoi(synchronous)) + { + myEditor->setIsSync(true); + } + myEditor->viewport()->installEventFilter( this ); + lay->addWidget( myEditor ); + + createActions(); +} + +/** + * Protected constructor. + */ +PyConsole_ConsoleBase::PyConsole_ConsoleBase( QWidget* parent, PyConsole_Interp* /*i*/, PyConsole_EditorBase* e ) + : QWidget (parent), myEditor(e) +{} + +/*! + \brief Destructor. + + Does nothing for the moment. +*/ +PyConsole_ConsoleBase::~PyConsole_ConsoleBase() +{ +} + +PyConsole_Interp* PyConsole_ConsoleBase::getInterp() const +{ + return myEditor ? myEditor->getInterp() : 0; +} + +/*! + \brief Execute python command in the interpreter. + \param command string with command and arguments +*/ +void PyConsole_ConsoleBase::exec( const QString& command ) +{ + if ( myEditor ) + myEditor->exec( command ); +} + +/*! + \brief Execute python command in the interpreter + and wait until it is finished. + + Block execution of main application until the python command is executed. + \param command string with command and arguments +*/ +void PyConsole_ConsoleBase::execAndWait( const QString& command ) +{ + if ( myEditor ) + myEditor->execAndWait( command ); +} + +/*! + \brief Get synchronous mode flag value. + + \sa setIsSync() + \return True if python console works in synchronous mode +*/ +bool PyConsole_ConsoleBase::isSync() const +{ + return myEditor->isSync(); +} + +/*! + \brief Set synchronous mode flag value. + + In synhronous mode the Python commands are executed in the GUI thread + and the GUI is blocked until the command is finished. In the asynchronous + mode each Python command is executed in the separate thread that does not + block the main GUI loop. + + \param on synhronous mode flag +*/ +void PyConsole_ConsoleBase::setIsSync( const bool on ) +{ + myEditor->setIsSync( on ); +} + +/*! + \brief Get suppress output flag value. + + \sa setIsSuppressOutput() + \return True if python console output is suppressed. +*/ +bool PyConsole_ConsoleBase::isSuppressOutput() const +{ + return myEditor->isSuppressOutput(); +} + +/*! + \brief Set suppress output flag value. + + In case if suppress output flag is true, the python + console output suppressed. + + \param on suppress output flag +*/ +void PyConsole_ConsoleBase::setIsSuppressOutput( const bool on ) +{ + myEditor->setIsSuppressOutput(on); +} + +/*! + \brief Get 'show banner' flag value. + + \sa setIsShowBanner() + \return \c true if python console shows banner +*/ +bool PyConsole_ConsoleBase::isShowBanner() const +{ + return myEditor->isShowBanner(); +} + +/*! + \brief Set 'show banner' flag value. + + The banner is shown in the top of the python console window. + + \sa isShowBanner() + \param on 'show banner' flag +*/ +void PyConsole_ConsoleBase::setIsShowBanner( const bool on ) +{ + myEditor->setIsShowBanner( on ); +} + +/*! + \brief Change the python console's font. + \param f new font +*/ +void PyConsole_ConsoleBase::setFont( const QFont& f ) +{ + if( myEditor ) + myEditor->setFont( f ); +} + +/*! + \brief Get python console font. + \return current python console's font +*/ +QFont PyConsole_ConsoleBase::font() const +{ + QFont res; + if( myEditor ) + res = myEditor->font(); + return res; +} + +/*! + \brief Set actions to be visible in the context popup menu. + + Actions, which IDs are set in \a flags parameter, will be shown in the + context popup menu. Other actions will not be shown. + + \param flags ORed together actions flags +*/ +void PyConsole_ConsoleBase::setMenuActions( const int flags ) +{ + myActions[CopyId]->setVisible( flags & CopyId ); + myActions[PasteId]->setVisible( flags & PasteId ); + myActions[ClearId]->setVisible( flags & ClearId ); + myActions[SelectAllId]->setVisible( flags & SelectAllId ); + myActions[DumpCommandsId]->setVisible( flags & DumpCommandsId ); + myActions[StartLogId]->setVisible( flags & StartLogId ); + myActions[StopLogId]->setVisible( flags & StopLogId ); +} + +/*! + \brief Get menu actions which are currently visible in the context popup menu. + \return ORed together actions flags + \sa setMenuActions() +*/ +int PyConsole_ConsoleBase::menuActions() const +{ + int ret = 0; + ret = ret | ( myActions[CopyId]->isVisible() ? CopyId : 0 ); + ret = ret | ( myActions[PasteId]->isVisible() ? PasteId : 0 ); + ret = ret | ( myActions[ClearId]->isVisible() ? ClearId : 0 ); + ret = ret | ( myActions[SelectAllId]->isVisible() ? SelectAllId : 0 ); + ret = ret | ( myActions[DumpCommandsId]->isVisible() ? DumpCommandsId : 0 ); + ret = ret | ( myActions[StartLogId]->isVisible() ? StartLogId : 0 ); + ret = ret | ( myActions[StopLogId]->isVisible() ? StopLogId : 0 ); + return ret; +} + +/*! + \brief Create menu actions. + + Create context popup menu actions. +*/ +void PyConsole_ConsoleBase::createActions() +{ + QAction* a = new QAction( tr( "EDIT_COPY_CMD" ), this ); + a->setStatusTip( tr( "EDIT_COPY_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( copy() ) ); + myActions.insert( CopyId, a ); + + a = new QAction( tr( "EDIT_PASTE_CMD" ), this ); + a->setStatusTip( tr( "EDIT_PASTE_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( paste() ) ); + myActions.insert( PasteId, a ); + + a = new QAction( tr( "EDIT_CLEAR_CMD" ), this ); + a->setStatusTip( tr( "EDIT_CLEAR_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( clear() ) ); + myActions.insert( ClearId, a ); + + a = new QAction( tr( "EDIT_SELECTALL_CMD" ), this ); + a->setStatusTip( tr( "EDIT_SELECTALL_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( selectAll() ) ); + myActions.insert( SelectAllId, a ); + + a = new QAction( tr( "EDIT_DUMPCOMMANDS_CMD" ), this ); + a->setStatusTip( tr( "EDIT_DUMPCOMMANDS_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( dump() ) ); + myActions.insert( DumpCommandsId, a ); + + a = new QAction( tr( "EDIT_STARTLOG_CMD" ), this ); + a->setStatusTip( tr( "EDIT_STARTLOG_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( startLog() ) ); + myActions.insert( StartLogId, a ); + + a = new QAction( tr( "EDIT_STOPLOG_CMD" ), this ); + a->setStatusTip( tr( "EDIT_STOPLOG_CMD" ) ); + connect( a, SIGNAL( triggered( bool ) ), myEditor, SLOT( stopLog() ) ); + myActions.insert( StopLogId, a ); +} + +/*! + \brief Update menu actions. + + Update context popup menu action state. +*/ +void PyConsole_ConsoleBase::updateActions() +{ + myActions[CopyId]->setEnabled( myEditor->textCursor().hasSelection() ); + myActions[PasteId]->setEnabled( !myEditor->isReadOnly() && !QApplication::clipboard()->text().isEmpty() ); + myActions[SelectAllId]->setEnabled( !myEditor->document()->isEmpty() ); +} + +/*! + \brief Start python trace logging + \param fileName the path to the log file +*/ +void PyConsole_ConsoleBase::startLog( const QString& fileName ) +{ + myEditor->startLog( fileName ); +} + +/*! + \brief Stop python trace logging +*/ +void PyConsole_ConsoleBase::stopLog() +{ + myEditor->stopLog(); +} + +/** + * Similar to constructor of the base class but using enhanced objects. + * TODO: this should really be done in a factory to avoid code duplication. + * @param parent + * @param interp + */ +PyConsole_EnhConsoleBase::PyConsole_EnhConsoleBase( QWidget* parent, PyConsole_Interp* interp ) + : PyConsole_ConsoleBase( parent, interp, 0 ) +{ + PyConsole_Interp* anInterp = interp ? interp : new PyConsole_EnhInterp(); + + // initialize Python interpretator + anInterp->initialize(); + + // create editor console + QVBoxLayout* lay = new QVBoxLayout( this ); + lay->setMargin( 0 ); + myEditor = new PyConsole_EnhEditorBase( anInterp, this ); + char* synchronous = getenv("PYTHON_CONSOLE_SYNC"); + if (synchronous && atoi(synchronous)) + { + myEditor->setIsSync(true); + } + myEditor->viewport()->installEventFilter( this ); + lay->addWidget( myEditor ); + + createActions(); +} diff --git a/src/PyConsoleBase/PyConsole_ConsoleBase.h b/src/PyConsoleBase/PyConsole_ConsoleBase.h new file mode 100644 index 000000000..615400d41 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_ConsoleBase.h @@ -0,0 +1,106 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 : PyConsole_Console.h +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// +#ifndef PYCONSOLE_CONSOLEBASE_H +#define PYCONSOLE_CONSOLEBASE_H + +#include "PyConsoleBase.h" + +#include +#include + +class PyConsole_Interp; +class PyConsole_EditorBase; + +class PYCONSOLEBASE_EXPORT PyConsole_ConsoleBase : public QWidget +{ + Q_OBJECT + +public: + //! Context popup menu actions flags + enum + { + CopyId = 0x01, //!< "Copy" menu action + PasteId = 0x02, //!< "Paste" menu action + ClearId = 0x04, //!< "Clear" menu action + SelectAllId = 0x08, //!< "Select All" menu action + DumpCommandsId = 0x10, //!< "DumpCommands" menu action + StartLogId = 0x20, //!< "Start log" menu action + StopLogId = 0x40, //!< "Stop log" menu action + All = 0xFF, //!< all menu actions + }; + +public: + PyConsole_ConsoleBase( QWidget* parent, PyConsole_Interp* interp = 0 ); + virtual ~PyConsole_ConsoleBase(); + + //! \brief Get python interperter + PyConsole_Interp* getInterp() const; + QFont font() const; + virtual void setFont( const QFont& ); + + bool isSync() const; + void setIsSync( const bool ); + + bool isSuppressOutput() const; + void setIsSuppressOutput( const bool ); + + bool isShowBanner() const; + void setIsShowBanner( const bool ); + + void exec( const QString& ); + void execAndWait( const QString& ); + + void setMenuActions( const int ); + int menuActions() const; + + void startLog( const QString& ); + void stopLog(); + +protected: + void createActions(); + void updateActions(); + + PyConsole_ConsoleBase( QWidget* parent, PyConsole_Interp*, PyConsole_EditorBase*); + + PyConsole_EditorBase* myEditor; //!< python console editor widget + QMap myActions; //!< menu actions list +}; + +/** + * Enhance console object providing auto-completion. + * Similar to PyConsole_Console except that an enhanced interpreter and enhanced editor + * are encapsulated. + */ +class PYCONSOLEBASE_EXPORT PyConsole_EnhConsoleBase : public PyConsole_ConsoleBase +{ + Q_OBJECT + +public: + PyConsole_EnhConsoleBase( QWidget* parent, PyConsole_Interp* interp = 0 ); + virtual ~PyConsole_EnhConsoleBase() {} +}; + +#endif // PYCONSOLE_CONSOLEBASE_H diff --git a/src/PyConsoleBase/PyConsole_EditorBase.cxx b/src/PyConsoleBase/PyConsole_EditorBase.cxx new file mode 100644 index 000000000..cf95a290d --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EditorBase.cxx @@ -0,0 +1,1217 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 +// + +// SALOME SALOMEGUI : implementation of desktop and GUI kernel +// File : PyConsole_Editor.cxx +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// +/*! + \class PyConsole_Editor + \brief Python command line interpreter front-end GUI widget. + + This class provides simple GUI interface to the Python interpreter, including basic + navigation operations, executing commands (both interactively and programmatically), + copy-paste operations, history of the commands and so on. + + Here below is the shortcut keyboard boundings used for navigation and other operations: + - : execute current command + - : clear current command + - : clear current command + - : previous command in the history + - : move cursor one row up with selection + - : move cursor one row up without selection + - : move cursor one row up with selection + - : next command in the history + - : move cursor one row down with selection + - : move cursor one row down without selection + - : move cursor one row down with selection + - : move one symbol left without selection + - : move one symbol left with selection + - : move one word left without selection + - : move one word left with selection + - : move one symbol right without selection + - : move one symbol right with selection + - : move one word right without selection + - : move one word right with selection + - : first command in the history + - : move one page up with selection + - : move one page up without selection + - : scroll one page up + - : last command in the history + - : move one page down with selection + - : move one page down without selection + - : scroll one page down + - : move to the beginning of the line without selection + - : move to the beginning of the line with selection + - : move to the very first symbol without selection + - : move to the very first symbol with selection + - : move to the end of the line without selection + - : move to the end of the line with selection + - : move to the very last symbol without selection + - : move to the very last symbol with selection + - : delete symbol before the cursor + / remove selected text and put it to the clipboard (cut) + - : delete previous word + / remove selected text and put it to the clipboard (cut) + - : delete text from the cursor to the beginning of the line + / remove selected text and put it to the clipboard (cut) + - : delete symbol after the cursor + / remove selected text and put it to the clipboard (cut) + - : delete next word + / remove selected text and put it to the clipboard (cut) + - : delete text from the cursor to the end of the line + / remove selected text and put it to the clipboard (cut) + - : copy + - : paste + - : paste + - : copy + - : cut + - : paste +*/ + +#include "PyConsole_Interp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!! +#include "PyConsole_EditorBase.h" +#include "PyConsole_Event.h" +#include "PyInterp_Event.h" +#include "PyInterp_Dispatcher.h" +#include "PyConsole_Request.h" + +#include "SUIT_FileValidator.h" +#include "SUIT_MessageBox.h" +#include "Qtxx.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//VSR: uncomment below macro to support unicode text properly in SALOME +// current commented out due to regressions +//#define PAL22528_UNICODE + +namespace +{ + QString fromUtf8( const char* txt ) + { +#ifdef PAL22528_UNICODE + return QString::fromUtf8( txt ); +#else + return QString( txt ); +#endif + } +} + +static QString READY_PROMPT = ">>> "; +static QString DOTS_PROMPT = "... "; + +void staticCallbackStdout( void* data, char* c ) +{ + if(!((PyConsole_EditorBase*)data)->isSuppressOutput()) { + PyConsole_EditorBase* e = (PyConsole_EditorBase*)data; + e->putLog( fromUtf8(c) ); + QApplication::postEvent( e, new PrintEvent( fromUtf8(c), false ) ); + } +} + +void staticCallbackStderr( void* data, char* c ) +{ + if(!((PyConsole_EditorBase*)data)->isSuppressOutput()) { + PyConsole_EditorBase* e = (PyConsole_EditorBase*)data; + e->putLog( fromUtf8(c) ); + QApplication::postEvent( e, new PrintEvent( fromUtf8(c), true ) ); + } +} + + +/*! + \brief Constructor. + + Creates python editor window. + \param theInterp python interper + \param theParent parent widget +*/ +PyConsole_EditorBase::PyConsole_EditorBase( PyConsole_Interp* theInterp, + QWidget* theParent ) +: QTextEdit( theParent ), + myInterp( 0 ), + myCmdInHistory( -1 ), + myEventLoop( 0 ), + myShowBanner( true ), + myIsSync( true ), + myIsSuppressOutput( false ) +{ + QString fntSet( "" ); + QFont aFont ( Qtxx::stringToFont(fntSet) ); + setFont( aFont ); + setUndoRedoEnabled( false ); + + myPrompt = READY_PROMPT; + setLineWrapMode( QTextEdit::WidgetWidth ); + setWordWrapMode( QTextOption::WrapAnywhere ); + setAcceptRichText( false ); + + theInterp->setvoutcb( staticCallbackStdout, this ); + theInterp->setverrcb( staticCallbackStderr, this ); + + // san - This is necessary for troubleless initialization + onPyInterpChanged( theInterp ); +} + +/*! + \brief Destructor. +*/ +PyConsole_EditorBase::~PyConsole_EditorBase() +{ + myInterp = 0; +} + +/*! + \brief Get Python interpreter +*/ +PyConsole_Interp* PyConsole_EditorBase::getInterp() const +{ + return myInterp; +} + +/*! + \brief Get synchronous mode flag value. + + \sa setIsSync() + \return True if python console works in synchronous mode +*/ +bool PyConsole_EditorBase::isSync() const +{ + return myIsSync; +} + +/*! + \brief Set synchronous mode flag value. + + In synhronous mode the Python commands are executed in the GUI thread + and the GUI is blocked until the command is finished. In the asynchronous + mode each Python command is executed in the separate thread that does not + block the main GUI loop. + + \param on synhronous mode flag +*/ +void PyConsole_EditorBase::setIsSync( const bool on ) +{ + myIsSync = on; +} + +/*! + \brief Get suppress output flag value. + + \sa setIsSuppressOutput() + \return \c true if python console output is suppressed. +*/ +bool PyConsole_EditorBase::isSuppressOutput() const +{ + return myIsSuppressOutput; +} + +/*! + \brief Set suppress output flag value. + + In case if suppress output flag is true, the python + console output suppressed. + + \param on suppress output flag +*/ +void PyConsole_EditorBase::setIsSuppressOutput( const bool on ) +{ + myIsSuppressOutput = on; +} + +/*! + \brief Get 'show banner' flag value. + + \sa setIsShowBanner() + \return \c true if python console shows banner +*/ +bool PyConsole_EditorBase::isShowBanner() const +{ + return myShowBanner; +} + +/*! + \brief Set 'show banner' flag value. + + The banner is shown in the top of the python console window. + + \sa isShowBanner() + \param on 'show banner' flag +*/ +void PyConsole_EditorBase::setIsShowBanner( const bool on ) +{ + if ( myShowBanner != on ) { + myShowBanner = on; + clear(); + } +} + +/*! + \brief Check if trace logging is switched on. + + \sa startLog(), stopLog() + \return \c true if trace logging is switched on +*/ +bool PyConsole_EditorBase::isLogging() const +{ + return !myLogFile.isEmpty(); +} + +/*! + \brief Get size hint for the Python console window + \return size hint value +*/ +QSize PyConsole_EditorBase::sizeHint() const +{ + QFontMetrics fm( font() ); + int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1; + QSize s(100, fm.lineSpacing()*nbLines); + return s; +} + +/*! + \brief Put the string \a str to the python editor. + \param str string to be put in the command line of the editor + \param newBlock if True, then the string is printed on a new line + \param isError if true, the text is printed in dark red +*/ +void PyConsole_EditorBase::addText( const QString& str, + const bool newBlock, + const bool isError) +{ + QTextCursor theCursor(textCursor()); + QTextCharFormat cf; + + moveCursor( QTextCursor::End ); + if ( newBlock ) + theCursor.insertBlock(); + if (isError) + cf.setForeground(QBrush(Qt::red)); + else + cf.setForeground(QBrush(Qt::black)); + theCursor.insertText( str, cf); + moveCursor( QTextCursor::End ); + ensureCursorVisible(); +} + +/*! + \brief Convenient method for executing a Python command, + as if the user typed it manually. + \param command python command to be executed + + !!! WARNING: doesn't work properly with multi-line commands. !!! +*/ +void PyConsole_EditorBase::exec( const QString& command ) +{ + if ( isReadOnly() ) { + // some interactive command is being executed in this editor... + // shedule the command to the queue + myQueue.push_back( command ); + return; + } + // remove last line + moveCursor( QTextCursor::End ); + moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + // set "ready" prompt + myPrompt = READY_PROMPT; + // clear command buffer + myCommandBuffer.truncate( 0 ); + // unset history browsing mode + myCmdInHistory = -1; + // print command line by line + QString cmd = command; + if ( !cmd.endsWith( "\n" ) ) cmd += "\n"; + QStringList lines = command.split( "\n" ); + for ( int i = 0; i < lines.size(); i++ ) { + if ( !lines[i].trimmed().isEmpty() ) + myHistory.push_back( lines[i] ); + addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 ); + putLog( QString( "%1%2\n" ).arg( i == 0 ? READY_PROMPT : DOTS_PROMPT ).arg( lines[i] ) ); + } + // IPAL20182 + addText( "", true ); + // set read-only mode + setReadOnly( true ); + // set busy cursor + setCursor( Qt::BusyCursor ); + + // post a request to execute Python command; + // editor will be informed via a custom event that execution has been completed + PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) ); +} + +/*! + \brief Create request to the python dispatcher for the command execution. + + \param command python command to be executed + */ +PyInterp_Request* PyConsole_EditorBase::createRequest( const QString& command ) +{ + return new ExecCommand( myInterp, command, this, isSync() ); +} + +/*! + \brief Execute command in the python interpreter + and wait until it is finished. + + \param command python command to be executed + */ +void PyConsole_EditorBase::execAndWait( const QString& command ) +{ + // already running ? + if( myEventLoop ) + return; + + // create new event loop + bool sync = isSync(); + if ( !sync ) { + myEventLoop = new QEventLoop( this ); + } + + // execute command + exec( command ); + + if ( !sync ) { + // run event loop + myEventLoop->exec(); + // delete event loop after command is processed + delete myEventLoop; + myEventLoop = 0; + } +} + +/*! + \brief Process "Enter" key press event. + + Execute the command entered by the user. +*/ +void PyConsole_EditorBase::handleReturn() +{ + // Position cursor at the end + QTextCursor curs(textCursor()); + curs.movePosition(QTextCursor::End); + setTextCursor(curs); + + // get last line + QTextBlock par = document()->end().previous(); + if ( !par.isValid() ) return; + + // get command + QString cmd = par.text().remove( 0, promptSize() ); + // extend the command buffer with the current command + myCommandBuffer.append( cmd ); + // add command to the history + if ( !cmd.trimmed().isEmpty() ) + myHistory.push_back( cmd ); + putLog( QString( "%1%2\n" ).arg( myPrompt ).arg( cmd ) ); + + // IPAL19397 + addText( "", true ); + + // set read-only mode + setReadOnly( true ); + // set busy cursor + setCursor( Qt::BusyCursor ); + + // post a request to execute Python command; + // editor will be informed via a custom event that execution has been completed + PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) ); +} + +/*! + \brief Process drop event. + + Paste dragged text. + \param event drop event +*/ +void PyConsole_EditorBase::dropEvent( QDropEvent* event ) +{ + // get the initial drop position + QPoint pos = event->pos(); + QTextCursor cur = cursorForPosition( event->pos() ); + // if the position is not in the last line move it to the end of the command line + if ( cur.position() < document()->end().previous().position() + promptSize() ) { + moveCursor( QTextCursor::End ); + pos = cursorRect().center(); + } + // create new drop event and use it instead of the original + QDropEvent de( pos, + event->possibleActions(), + event->mimeData(), + event->mouseButtons(), + event->keyboardModifiers(), + event->type() ); + QTextEdit::dropEvent( &de ); + // accept the original event + event->acceptProposedAction(); +} + +/*! + \brief Process mouse button release event. + + Left mouse button: copy selection to the clipboard. + Middle mouse button: paste clipboard's contents. + \param event mouse event +*/ +void PyConsole_EditorBase::mouseReleaseEvent( QMouseEvent* event ) +{ + if ( event->button() == Qt::LeftButton ) { + QTextEdit::mouseReleaseEvent( event ); + //copy(); + } + else if ( event->button() == Qt::MidButton ) { + QTextCursor cur = cursorForPosition( event->pos() ); + // if the position is not in the last line move it to the end of the command line + if ( cur.position() < document()->end().previous().position() + promptSize() ) { + moveCursor( QTextCursor::End ); + } + else { + setTextCursor( cur ); + } + const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ? + QClipboard::Selection : QClipboard::Clipboard ); + if ( md ) + insertFromMimeData( md ); + } + else { + QTextEdit::mouseReleaseEvent( event ); + } +} + +/*! + \brief Check if the string is command. + + Return True if the string \a str is likely to be the command + (i.e. it is started from the '>>>' or '...'). + \param str string to be checked +*/ +bool PyConsole_EditorBase::isCommand( const QString& str ) const +{ + return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT ); +} + +/*! + \brief Handle keyboard event. + + Implement navigation, history browsing, copy/paste and other common + operations. + \param event keyboard event +*/ +void PyConsole_EditorBase::keyPressEvent( QKeyEvent* event ) +{ + // get cursor position + QTextCursor cur = textCursor(); + int curLine = cur.blockNumber(); + int curCol = cur.columnNumber(); + + // get last edited line + int endLine = document()->blockCount()-1; + + // get pressed key code + int aKey = event->key(); + + // check if is pressed + bool ctrlPressed = event->modifiers() & Qt::ControlModifier; + // check if is pressed + bool shftPressed = event->modifiers() & Qt::ShiftModifier; + + if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) { + // process + key-binding and key: clear current command + myCommandBuffer.truncate( 0 ); + myPrompt = READY_PROMPT; + addText( myPrompt, true ); + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + return; + } + else if ( ctrlPressed && aKey == Qt::Key_C ) { + // process + key-binding : copy + copy(); + return; + } + else if ( ctrlPressed && aKey == Qt::Key_X ) { + // process + key-binding : cut + cut(); + return; + } + else if ( ctrlPressed && aKey == Qt::Key_V ) { + // process + key-binding : paste + paste(); + return; + } + + // check for printed key + // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0; + // Better: + aKey = !(QChar(aKey).isPrint()) ? aKey : 0; + + switch ( aKey ) { + case 0 : + // any printed key: just print it + { + if ( curLine < endLine || curCol < promptSize() ) { + moveCursor( QTextCursor::End ); + } + QTextEdit::keyPressEvent( event ); + break; + } + case Qt::Key_Return: + case Qt::Key_Enter: + // key: process the current command + { + handleReturn(); + break; + } + case Qt::Key_Up: + // arrow key: process as follows: + // - without , modifiers: previous command in history + // - with modifier key pressed: move cursor one row up without selection + // - with modifier key pressed: move cursor one row up with selection + // - with + modifier keys pressed: scroll one row up + { + if ( ctrlPressed && shftPressed ) { + int value = verticalScrollBar()->value(); + int spacing = fontMetrics().lineSpacing(); + verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 ); + } + else if ( shftPressed || ctrlPressed ) { + if ( curLine > 0 ) + moveCursor( QTextCursor::Up, + shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); + } + else { + if ( myCmdInHistory < 0 && myHistory.count() > 0 ) { + // set history browsing mode + myCmdInHistory = myHistory.count(); + // remember current command + QTextBlock par = document()->end().previous(); + myCurrentCommand = par.text().remove( 0, promptSize() ); + } + if ( myCmdInHistory > 0 ) { + myCmdInHistory--; + // get previous command in the history + QString previousCommand = myHistory.at( myCmdInHistory ); + // print previous command + moveCursor( QTextCursor::End ); + moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + addText( myPrompt + previousCommand ); + // move cursor to the end + moveCursor( QTextCursor::End ); + } + } + break; + } + case Qt::Key_Down: + // arrow key: process as follows: + // - without , modifiers: next command in history + // - with modifier key pressed: move cursor one row down without selection + // - with modifier key pressed: move cursor one row down with selection + // - with + modifier keys pressed: scroll one row down + { + if ( ctrlPressed && shftPressed ) { + int value = verticalScrollBar()->value(); + int maxval = verticalScrollBar()->maximum(); + int spacing = fontMetrics().lineSpacing(); + verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval ); + } + else if ( shftPressed || ctrlPressed) { + if ( curLine < endLine ) + moveCursor( QTextCursor::Down, + shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); + } + else { + if ( myCmdInHistory >= 0 ) { + // get next command in the history + myCmdInHistory++; + QString nextCommand; + if ( myCmdInHistory < myHistory.count() ) { + // next command in history + nextCommand = myHistory.at( myCmdInHistory ); + } + else { + // end of history is reached + // last printed command + nextCommand = myCurrentCommand; + // unset history browsing mode + myCmdInHistory = -1; + } + // print next or current command + moveCursor( QTextCursor::End ); + moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + addText( myPrompt + nextCommand ); + // move cursor to the end + moveCursor( QTextCursor::End ); + } + } + break; + } + case Qt::Key_Left: + // arrow key: process as follows: + // - without , modifiers: move one symbol left (taking into account prompt) + // - with modifier key pressed: move one word left (taking into account prompt) + // - with modifier key pressed: move one symbol left with selection + // - with + modifier keys pressed: move one word left with selection + { + QString txt = textCursor().block().text(); + if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) { + moveCursor( QTextCursor::Up ); + moveCursor( QTextCursor::EndOfBlock ); + } + else { + QTextEdit::keyPressEvent( event ); + } + break; + } + case Qt::Key_Right: + // arrow key: process as follows: + // - without , modifiers: move one symbol right (taking into account prompt) + // - with modifier key pressed: move one word right (taking into account prompt) + // - with modifier key pressed: move one symbol right with selection + // - with + modifier keys pressed: move one word right with selection + { + QString txt = textCursor().block().text(); + if ( !shftPressed ) { + if ( curCol < txt.length() ) { + if ( isCommand( txt ) && curCol < promptSize() ) { + cur.setPosition( cur.block().position() + promptSize() ); + setTextCursor( cur ); + break; + } + } + else { + if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) { + cur.setPosition( cur.position() + promptSize()+1 ); + setTextCursor( cur ); + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + break; + } + } + } + QTextEdit::keyPressEvent( event ); + break; + } + case Qt::Key_PageUp: + // key: process as follows: + // - without , modifiers: first command in history + // - with modifier key pressed: move cursor one page up without selection + // - with modifier key pressed: move cursor one page up with selection + // - with + modifier keys pressed: scroll one page up + { + if ( ctrlPressed && shftPressed ) { + verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub); + } + else if ( shftPressed || ctrlPressed ) { + bool moved = false; + qreal lastY = cursorRect( cur ).top(); + qreal distance = 0; + // move using movePosition to keep the cursor's x + do { + qreal y = cursorRect( cur ).top(); + distance += qAbs( y - lastY ); + lastY = y; + moved = cur.movePosition( QTextCursor::Up, + shftPressed ? QTextCursor::KeepAnchor : + QTextCursor::MoveAnchor ); + } while ( moved && distance < viewport()->height() ); + if ( moved ) { + cur.movePosition( QTextCursor::Down, + shftPressed ? QTextCursor::KeepAnchor : + QTextCursor::MoveAnchor ); + verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub ); + } + setTextCursor( cur ); + } + else { + if ( myCmdInHistory < 0 && myHistory.count() > 0 ) { + // set history browsing mode + myCmdInHistory = myHistory.count(); + // remember current command + QTextBlock par = document()->end().previous(); + myCurrentCommand = par.text().remove( 0, promptSize() ); + } + if ( myCmdInHistory > 0 ) { + myCmdInHistory = 0; + // get very first command in the history + QString firstCommand = myHistory.at( myCmdInHistory ); + // print first command + moveCursor( QTextCursor::End ); + moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + addText( myPrompt + firstCommand ); + // move cursor to the end + moveCursor( QTextCursor::End ); + } + } + break; + } + case Qt::Key_PageDown: + // key: process as follows: + // - without , modifiers: last command in history + // - with modifier key pressed: move cursor one page down without selection + // - with modifier key pressed: move cursor one page down with selection + // - with + modifier keys pressed: scroll one page down + { + if ( ctrlPressed && shftPressed ) { + verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd); + } + else if ( shftPressed || ctrlPressed ) { + bool moved = false; + qreal lastY = cursorRect( cur ).top(); + qreal distance = 0; + // move using movePosition to keep the cursor's x + do { + qreal y = cursorRect( cur ).top(); + distance += qAbs( y - lastY ); + lastY = y; + moved = cur.movePosition( QTextCursor::Down, + shftPressed ? QTextCursor::KeepAnchor : + QTextCursor::MoveAnchor ); + } while ( moved && distance < viewport()->height() ); + if ( moved ) { + cur.movePosition( QTextCursor::Up, + shftPressed ? QTextCursor::KeepAnchor : + QTextCursor::MoveAnchor ); + verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub ); + } + setTextCursor( cur ); + } + else { + if ( myCmdInHistory >= 0 ) { + // unset history browsing mode + myCmdInHistory = -1; + // print current command + moveCursor( QTextCursor::End ); + moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + addText( myPrompt + myCurrentCommand ); + // move cursor to the end + moveCursor( QTextCursor::End ); + } + } + break; + } + case Qt::Key_Home: + // key: process as follows: + // - without , modifiers: move cursor to the beginning of the current line without selection + // - with modifier key pressed: move cursor to the very first symbol without selection + // - with modifier key pressed: move cursor to the beginning of the current line with selection + // - with + modifier keys pressed: move cursor to the very first symbol with selection + { + if ( ctrlPressed ) { + moveCursor( QTextCursor::Start, + shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); + } + else { + QString txt = textCursor().block().text(); + if ( isCommand( txt ) ) { + if ( shftPressed ) { + if ( curCol > promptSize() ) { + cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor ); + cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() ); + } + } + else { + cur.movePosition( QTextCursor::StartOfLine ); + cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() ); + } + setTextCursor( cur ); + } + else { + moveCursor( QTextCursor::StartOfBlock, + shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); + } + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + } + break; + } + case Qt::Key_End: + // key: process as follows: + // - without , modifiers: move cursor to the end of the current line without selection + // - with modifier key pressed: move cursor to the very last symbol without selection + // - with modifier key pressed: move cursor to the end of the current line with selection + // - with + modifier keys pressed: move cursor to the very last symbol with selection + { + moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock, + shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor ); + break; + } + case Qt::Key_Backspace : + // key: process as follows + // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt) + // - with modifier key pressed: delete previous word + // - with modifier key pressed: delete text from the cursor to the line beginning + // works only for last (command) line + { + if ( cur.hasSelection() ) { + cut(); + } + else if ( cur.position() > document()->end().previous().position() + promptSize() ) { + if ( shftPressed ) { + moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + } + else if ( ctrlPressed ) { + cur.setPosition( document()->end().previous().position() + promptSize(), + QTextCursor::KeepAnchor ); + setTextCursor( cur ); + textCursor().removeSelectedText(); + } + else { + QTextEdit::keyPressEvent( event ); + } + } + else { + cur.setPosition( document()->end().previous().position() + promptSize() ); + setTextCursor( cur ); + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + } + break; + } + case Qt::Key_Delete : + // key: process as follows + // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt) + // - with modifier key pressed: delete next word + // - with modifier key pressed: delete text from the cursor to the end of line + // works only for last (command) line + { + if ( cur.hasSelection() ) { + cut(); + } + else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) { + if ( shftPressed ) { + moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + } + else if ( ctrlPressed ) { + moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); + textCursor().removeSelectedText(); + } + else { + QTextEdit::keyPressEvent( event ); + } + } + else { + cur.setPosition( document()->end().previous().position() + promptSize() ); + setTextCursor( cur ); + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + } + break; + } + case Qt::Key_Insert : + // key: process as follows + // - with modifier key pressed: copy() + // - with modifier key pressed: paste() to the command line + { + if ( ctrlPressed ) { + copy(); + } + else if ( shftPressed ) { + paste(); + } + else + QTextEdit::keyPressEvent( event ); + break; + } + } +} + +/*! + \brief Handle notification event coming from Python dispatcher. + \param event notification event +*/ +void PyConsole_EditorBase::customEvent( QEvent* event ) +{ + switch( event->type() ) + { + case PrintEvent::EVENT_ID: + { + PrintEvent* pe=(PrintEvent*)event; + addText( pe->text(), false, pe->isError()); + return; + } + case PyInterp_Event::ES_OK: + case PyInterp_Event::ES_ERROR: + { + // clear command buffer + myCommandBuffer.truncate( 0 ); + // add caret return line if necessary + QTextBlock par = document()->end().previous(); + QString txt = par.text(); + txt.truncate( txt.length() - 1 ); + // IPAL19397 : addText moved to handleReturn() method + //if ( !txt.isEmpty() ) + // addText( "", true ); + // set "ready" prompt + myPrompt = READY_PROMPT; + addText( myPrompt ); + // unset busy cursor + unsetCursor(); + // stop event loop (if running) + if ( myEventLoop ) + myEventLoop->exit(); + break; + } + case PyInterp_Event::ES_INCOMPLETE: + { + // extend command buffer (multi-line command) + myCommandBuffer.append( "\n" ); + // add caret return line if necessary + QTextBlock par = document()->end().previous(); + QString txt = par.text(); + txt.truncate( txt.length() - 1 ); + // IPAL19397 : addText moved to handleReturn() method + //if ( !txt.isEmpty() ) + // addText( "", true ); + // set "dot" prompt + myPrompt = DOTS_PROMPT; + addText( myPrompt/*, true*/ ); // IPAL19397 + // unset busy cursor + unsetCursor(); + // stop event loop (if running) + if ( myEventLoop ) + myEventLoop->exit(); + break; + } + default: + QTextEdit::customEvent( event ); + } + + // unset read-only state + setReadOnly( false ); + // unset history browsing mode + myCmdInHistory = -1; + + if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 ) + { + // process the next sheduled command from the queue (if there is any) + QString nextcmd = myQueue[0]; + myQueue.pop_front(); + exec( nextcmd ); + } +} + +/*! + \brief Handle Python interpreter change. + + Perform initialization actions if the interpreter is changed. + \param interp python interpreter is being set +*/ +void PyConsole_EditorBase::onPyInterpChanged( PyConsole_Interp* interp ) +{ + if ( myInterp != interp + // Force read-only state and wait cursor when myInterp is NULL + || !myInterp ) { + myInterp = interp; + if ( myInterp ) { + // print banner + myBanner = myInterp->getbanner().c_str(); + if ( isShowBanner() ) + addText( myBanner ); + // clear command buffer + myCommandBuffer.truncate(0); + // unset read-only state + setReadOnly( false ); + // unset history browsing mode + myCmdInHistory = -1; + // add prompt + addText( myPrompt ); + // unset busy cursor + viewport()->unsetCursor(); + // stop event loop (if running) + if( myEventLoop) + myEventLoop->exit(); + } + else { + // clear contents + clear(); + // set read-only state + setReadOnly( true ); + // set busy cursor + setCursor( Qt::WaitCursor ); + } + } +} + +/*! + \brief "Copy" operation. + + Reimplemented from Qt. + Warning! In Qt4 this method is not virtual. + */ +void PyConsole_EditorBase::cut() +{ + QTextCursor cur = textCursor(); + if ( cur.hasSelection() ) { + QApplication::clipboard()->setText( cur.selectedText() ); + int startSelection = cur.selectionStart(); + if ( startSelection < document()->end().previous().position() + promptSize() ) + startSelection = document()->end().previous().position() + promptSize(); + int endSelection = cur.selectionEnd(); + if ( endSelection < document()->end().previous().position() + promptSize() ) + endSelection = document()->end().previous().position() + promptSize(); + cur.setPosition( startSelection ); + cur.setPosition( endSelection, QTextCursor::KeepAnchor ); + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + setTextCursor( cur ); + textCursor().removeSelectedText(); + } +} + +/*! + \brief "Paste" operation. + + Reimplemented from Qt. + Warning! In Qt4 this method is not virtual. + */ +void PyConsole_EditorBase::paste() +{ + QTextCursor cur = textCursor(); + if ( cur.hasSelection() ) { + int startSelection = cur.selectionStart(); + if ( startSelection < document()->end().previous().position() + promptSize() ) + startSelection = document()->end().previous().position() + promptSize(); + int endSelection = cur.selectionEnd(); + if ( endSelection < document()->end().previous().position() + promptSize() ) + endSelection = document()->end().previous().position() + promptSize(); + cur.setPosition( startSelection ); + cur.setPosition( endSelection, QTextCursor::KeepAnchor ); + horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); + setTextCursor( cur ); + textCursor().removeSelectedText(); + } + if ( textCursor().position() < document()->end().previous().position() + promptSize() ) + moveCursor( QTextCursor::End ); + QTextEdit::paste(); +} + +/*! + \brief "Clear" operation. + + Reimplemented from Qt. + Warning! In Qt4 this method is not virtual. + */ +void PyConsole_EditorBase::clear() +{ + QTextEdit::clear(); + if ( isShowBanner() ) + addText( myBanner ); + myPrompt = READY_PROMPT; + addText( myPrompt ); +} + +/*! + \brief "Dump commands" operation. + */ +void PyConsole_EditorBase::dumpImpl(const QString& fileName) +{ + if ( !fileName.isEmpty() ) { + QFile file( fileName ); + if ( !file.open( QFile::WriteOnly ) ) + return; + + QTextStream out (&file); + + for ( int i=0; i + +class PyConsole_Interp; +class PyInterp_Request; +class QEventLoop; + +class PYCONSOLEBASE_EXPORT PyConsole_EditorBase : public QTextEdit +{ + Q_OBJECT; + +public: + PyConsole_EditorBase( PyConsole_Interp* theInterp, QWidget *theParent = 0 ); + ~PyConsole_EditorBase(); + + PyConsole_Interp* getInterp() const; + + virtual void addText( const QString& str, const bool newBlock = false, const bool isError = false ); + bool isCommand( const QString& str ) const; + + virtual void exec( const QString& command ); + void execAndWait( const QString& command ); + + bool isSync() const; + void setIsSync( const bool ); + + bool isSuppressOutput() const; + void setIsSuppressOutput(const bool); + + bool isShowBanner() const; + void setIsShowBanner( const bool ); + + bool isLogging() const; + + virtual QSize sizeHint() const; + void dumpImpl(const QString& fileName); + bool startLogImpl( const QString& ); +public slots: + void cut(); + void paste(); + void clear(); + void handleReturn(); + void onPyInterpChanged( PyConsole_Interp* ); + void dump(const QString& fileName); + bool startLog( const QString& ); + void stopLog(); + void putLog( const QString& ); + +protected: + virtual void dropEvent( QDropEvent* event ); + virtual void mouseReleaseEvent( QMouseEvent* event ); + virtual void keyPressEvent ( QKeyEvent* event); + virtual void customEvent( QEvent* event); + + virtual PyInterp_Request* createRequest( const QString& ); + + /** Convenience function */ + inline int promptSize() const { return myPrompt.size(); } + + PyConsole_Interp* myInterp; //!< python interpreter + + QString myCommandBuffer; //!< python command buffer + QString myCurrentCommand; //!< currently being printed command + QString myPrompt; //!< current command line prompt + int myCmdInHistory; //!< current history command index + QString myLogFile; //!< current output log + QStringList myHistory; //!< commands history buffer + QEventLoop* myEventLoop; //!< internal event loop + QString myBanner; //!< current banner + bool myShowBanner; //!< 'show banner' flag + QStringList myQueue; //!< python commands queue + bool myIsSync; //!< synchronous mode flag + bool myIsSuppressOutput; //!< suppress output flag +}; + +#endif // PYCONSOLE_EDITORBASE_H diff --git a/src/PyConsoleBase/PyConsole_EnhEditorBase.cxx b/src/PyConsoleBase/PyConsole_EnhEditorBase.cxx new file mode 100644 index 000000000..54bf7fd23 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EnhEditorBase.cxx @@ -0,0 +1,491 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 +// +// Author : Adrien Bruneton (CEA/DEN) +// Created on: 4 avr. 2013 + +#include "PyConsoleBase.h" +#include + +#include +#include +#include +#include +#include +#include + +#include "PyConsole_EnhEditorBase.h" +#include "PyConsole_EnhInterp.h" +#include "PyConsole_Request.h" +#include "PyInterp_Dispatcher.h" + +// Initialize list of valid separators +static const char * tmp_a[] = {" ", "(", "[","+", "-", "*", "/", ";", "^", "="}; +const std::vector PyConsole_EnhEditorBase::SEPARATORS = \ + std::vector(tmp_a, tmp_a + sizeof(tmp_a)/sizeof(tmp_a[0])); + +/** + * Constructor. + * @param interp the interpreter linked to the editor + * @param parent parent widget + */ +PyConsole_EnhEditorBase::PyConsole_EnhEditorBase(PyConsole_Interp* interp, QWidget* parent) : + PyConsole_EditorBase(interp, parent), + _tab_mode(false), + _cursor_pos(-1), + _multi_line_paste(false), + _multi_line_content() +{ + document()->setUndoRedoEnabled(true); +} + +/** + * Overrides. Catches the TAB and Ctrl+TAB combinations. + * @param event + */ +void PyConsole_EnhEditorBase::keyPressEvent ( QKeyEvent* event) +{ + // check if is pressed + bool ctrlPressed = event->modifiers() & Qt::ControlModifier; + // check if is pressed + bool shftPressed = event->modifiers() & Qt::ShiftModifier; + + if (event->key() == Qt::Key_Tab && !shftPressed) + { + if (!ctrlPressed) + handleTab(); + else + { + clearCompletion(); + handleBackTab(); + } + PyConsole_EditorBase::keyPressEvent(event); + } + else + { + // If ctrl is not pressed (and sth else is pressed with it), + // or if ctrl is not pressed alone + if (!ctrlPressed || (ctrlPressed && event->key() != Qt::Key_Control)) + { + clearCompletion(); + _cursor_pos = -1; + } + // Discard ctrl pressed alone: + if (event->key() != Qt::Key_Control) + PyConsole_EditorBase::keyPressEvent(event); + } +} + +/** + * Whenever the mouse is clicked, clear the completion. + * @param e + */ +void PyConsole_EnhEditorBase::mousePressEvent(QMouseEvent* e) +{ + clearCompletion(); + _cursor_pos = -1; + PyConsole_EditorBase::mousePressEvent(e); +} + +/** + * Clear in the editor the block of text displayed after having hit . + */ +void PyConsole_EnhEditorBase::clearCompletion() +{ + // Delete completion text if present + if (_tab_mode) + { + // Remove completion display + document()->undo(); + // Remove trailing line return: + QTextCursor tc(textCursor()); + tc.setPosition(document()->characterCount()-1); + setTextCursor(tc); + textCursor().deletePreviousChar(); + // TODO: before wait for any TAB event to be completed + if ( myInterp ) + myInterp->clearCompletion(); + } + _tab_mode = false; +} + +/** + * Handle the sequence of events after having hit + */ +void PyConsole_EnhEditorBase::handleTab() +{ + if (_tab_mode) + { + // Already tab mode - nothing to do ! + return; + } + + QTextCursor cursor(textCursor()); + + // Cursor at end of input + cursor.movePosition(QTextCursor::End); + setTextCursor(cursor); + + // Save cursor position if needed + if (_cursor_pos == -1) + _cursor_pos = textCursor().position(); + + // get last line + QTextBlock par = document()->end().previous(); + if ( !par.isValid() ) return; + + // Switch to completion mode + _tab_mode = true; + + QString cmd = par.text().mid(promptSize()); + + // Post completion request + // Editor will be informed via a custom event that completion has been run + PyInterp_Request* req = createTabRequest(cmd); + PyInterp_Dispatcher::Get()->Exec(req); +} + +/** + * Handles what happens after hitting Ctrl-TAB + */ +void PyConsole_EnhEditorBase::handleBackTab() +{ + QTextCursor cursor(textCursor()); + + if (_cursor_pos == -1) + { + // Invalid cursor position - we can't do anything: + return; + } + // Ensure cursor is at the end of command line + cursor.setPosition(_cursor_pos); + cursor.movePosition(QTextCursor::EndOfLine); + //setCursor(cursor); + + // Delete last completed text + int i = cursor.position() - _cursor_pos; + cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, i); + cursor.removeSelectedText(); + _cursor_pos = -1; +} + +/** + * Create the Python requested that will be posted to the interpreter to + * get the completions. + * @param input line typed by the user at the time TAB was hit + * @return a CompletionCommand + * @sa CompletionCommand + */ +PyInterp_Request* PyConsole_EnhEditorBase::createTabRequest( const QString& input ) +{ + // Parse input to extract on what part the dir() has to be executed + QString input2(input); + + // Split up to the last syntaxical separator + int lastSp = -1; + for (std::vector::const_iterator i = SEPARATORS.begin(); i != SEPARATORS.end(); i++) + { + int j = input2.lastIndexOf(*i); + if (j > lastSp) + lastSp = j; + } + if (lastSp >= 0) + input2 = input.mid(lastSp+1); + + // Detect a qualified name (with a point) + int lastPt = input2.lastIndexOf(QString(".")); + + // Split the 2 surrounding parts of the qualified name + if (lastPt != -1) + { + _compl_before_point = input2.left(lastPt); + _compl_after_point = input2.mid(lastPt+1); + } + else + { + // No point found - do a global matching - + // (the following will call dir() with an empty string) + _compl_after_point = input2; + _compl_before_point = QString(""); + } + + return new CompletionCommand( myInterp, _compl_before_point, + _compl_after_point, this, isSync() ); +} + +/** + * Format completion results - this is where we should create 3 columns etc ... + * @param matches list of possible completions + * @param result return value + */ +void PyConsole_EnhEditorBase::formatCompletion(const QStringList& matches, QString& result) const +{ + int sz = matches.size(); + + if (sz > MAX_COMPLETIONS) + { + sz = MAX_COMPLETIONS; + result.append("[Too many matches! Displaying first ones only ...]\n"); + } + + for (int i = 0; i < sz; ++i) + { + result.append(matches[i]); + result.append("\n"); + } +} + +/** + * Override. Catches the events generated by the enhanced interpreter after the execution + * of a completion request. + * @param event + */ +void PyConsole_EnhEditorBase::customEvent( QEvent* event ) +{ + QStringList matches; + QString first_match, comple_text, doc, base; + QTextCursor cursor(textCursor()); + QTextBlockFormat bf; + QTextCharFormat cf; + int cursorPos; + + switch( event->type() ) + { + case PyInterp_Event::ES_TAB_COMPLETE_OK: + { + // Extract corresponding matches from the interpreter + matches = getInterp()->getLastMatches(); + doc = getInterp()->getDocStr(); + + if (matches.size() == 0) + { + // Completion successful but nothing returned. + _tab_mode = false; + _cursor_pos = -1; + return; + } + + // Only one match - complete directly and update doc string window + if (matches.size() == 1) + { + first_match = matches[0].mid(_compl_after_point.size()); + cursor.insertText(first_match); + _tab_mode = false; + if (doc.isEmpty()) + emit updateDoc(formatDocHTML("(no documentation available)\n")); + else + emit updateDoc(formatDocHTML(doc)); + } + else + { + // Detect if there is a common base to all available completion + // In this case append this base to the text already + extractCommon(matches, base); + first_match = base.mid(_compl_after_point.size()); + cursor.insertText(first_match); + + // If this happens to match exaclty the first completion + // also provide doc + if (base == matches[0]) + { + doc = formatDocHTML(doc); + emit updateDoc(doc); + } + + // Print all matching completion in a "undo-able" block + cursorPos = cursor.position(); + cursor.insertBlock(); + cursor.beginEditBlock(); + + // Insert all matches + QTextCharFormat cf; + cf.setForeground(QBrush(Qt::darkGreen)); + cursor.setCharFormat(cf); + formatCompletion(matches, comple_text); + cursor.insertText(comple_text); + cursor.endEditBlock(); + + // Position cursor where it was before inserting the completion list: + cursor.setPosition(cursorPos); + setTextCursor(cursor); + } + break; + } + case PyInterp_Event::ES_TAB_COMPLETE_ERR: + { + // Tab completion was unsuccessful, switch off mode: + _tab_mode = false; + _cursor_pos = -1; + break; + } + case PyInterp_Event::ES_OK: + case PyInterp_Event::ES_ERROR: + case PyInterp_Event::ES_INCOMPLETE: + { + // Before everything else, call super() + PyConsole_EditorBase::customEvent(event); + // If we are in multi_paste_mode, process the next item: + multiLineProcessNextLine(); + break; + } + default: + { + PyConsole_EditorBase::customEvent( event ); + break; + } + } +} + +/** + * Extract the common leading part of all strings in matches. + * @param matches + * @param result + */ +void PyConsole_EnhEditorBase::extractCommon(const QStringList& matches, QString& result) const +{ + result = ""; + int charIdx = 0; + + if (matches.size() < 2) + return; + + while (true) + { + if (charIdx >= matches[0].size()) + return; + QChar ch = matches[0][charIdx]; + for (int j = 1; j < matches.size(); j++) + if (charIdx >= matches[j].size() || matches[j][charIdx] != ch) + return; + result += ch; + charIdx++; + } +} + +/** + * Format the doc string in HTML format with the first line in bold blue + * @param doc initial doc string + * @return HTML string + */ +QString PyConsole_EnhEditorBase::formatDocHTML(const QString & doc) const +{ + QString templ = QString("\n ") + + QString(" ") + + QString(" ") + + QString("\n") + + QString("

") + + QString("%1

") + + QString("

%2

") + + QString(""); + + QString fst, rest(""); + + // Extract first line of doc + int idx = doc.indexOf("\n"); + if (idx > 0) + { + fst = doc.left(idx); + rest = doc.mid(idx+1); + } + else + { + fst = doc; + } + + fst = fst.replace("\n", " "); + rest = rest.replace("\n", " "); + return templ.arg(fst).arg(rest); +} + +/** + * Handle properly multi-line pasting. Qt4 doc recommends overriding this function. + * If the pasted text doesn't contain a line return, no special treatment is done. + * @param source + */ +void PyConsole_EnhEditorBase::insertFromMimeData(const QMimeData* source) +{ + if (_multi_line_paste) + return; + + if (source->hasText()) + { + QString s = source->text(); + if (s.contains("\n")) + multilinePaste(s); + else + PyConsole_EditorBase::insertFromMimeData(source); + } + else + { + PyConsole_EditorBase::insertFromMimeData(source); + } +} + + +void PyConsole_EnhEditorBase::multilinePaste(const QString & s) +{ + // Turn on multi line pasting mode + _multi_line_paste = true; + + // Split the string: + QString s2 = s; + s2.replace("\r", ""); // Windows string format converted to Unix style + + QStringList lst = s2.split(QChar('\n'), QString::KeepEmptyParts); + + // Perform the proper paste operation for the first line to handle the case where + // sth was already there: + QMimeData source; + source.setText(lst[0]); + PyConsole_EditorBase::insertFromMimeData(&source); + + // Prepare what will have to be executed after the first line: + _multi_line_content = std::queue(); + for (int i = 1; i < lst.size(); ++i) + _multi_line_content.push(lst[i]); + + // Trigger the execution of the first (mixed) line + handleReturn(); + + // See customEvent() and multiLineProcessNext() for the rest of the handling. +} + +/** + * Process the next line in the queue of a multiple copy/paste: + */ +void PyConsole_EnhEditorBase::multiLineProcessNextLine() +{ + if (!_multi_line_paste) + return; + + QString line(_multi_line_content.front()); + _multi_line_content.pop(); + if (!_multi_line_content.size()) + { + // last line in the queue, just paste it + addText(line, false, false); + _multi_line_paste = false; + } + else + { + // paste the line and simulate a key stroke + addText(line, false, false); + handleReturn(); + } +} diff --git a/src/PyConsoleBase/PyConsole_EnhEditorBase.h b/src/PyConsoleBase/PyConsole_EnhEditorBase.h new file mode 100644 index 000000000..6c91a3d8e --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EnhEditorBase.h @@ -0,0 +1,95 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 +// +// Author : Adrien Bruneton (CEA/DEN) +// Created on: 4 avr. 2013 + +#ifndef PYCONSOLE_ENHEDITORBASE_H_ +#define PYCONSOLE_ENHEDITORBASE_H_ + +#include "PyConsoleBase.h" +#include "PyConsole_EditorBase.h" + +#include +#include + +/** + * Enhanced Python editor handling tab completion. + */ +class PYCONSOLEBASE_EXPORT PyConsole_EnhEditorBase : public PyConsole_EditorBase +{ + Q_OBJECT; + +public: + PyConsole_EnhEditorBase(PyConsole_Interp* interp, QWidget* parent = 0); + virtual ~PyConsole_EnhEditorBase() {} + +signals: + /** + * Signal emitted by the editor widget when the doc string should be updated. + * @param doc a HTML block with the formatted doc string. + * TODO: for now this signal is left uncaught. + */ + void updateDoc(QString doc); + +protected: + /** List of separators identifying the last parsable token for completion */ + static const std::vector SEPARATORS; + + /** Maximum number of completions shown at once */ + static const int MAX_COMPLETIONS = 70; + + /** Are we in completion mode */ + bool _tab_mode; + + /** String on which the dir() comamnd is executed */ + QString _compl_before_point; + /** String on which the results of the dir() are matched */ + QString _compl_after_point; + + /** Cursor position when is hit */ + int _cursor_pos; + + /** Are we currently pasting several lines */ + bool _multi_line_paste; + + /** Queue of lines being pasted */ + std::queue _multi_line_content; + + // Overrides: + virtual void keyPressEvent ( QKeyEvent* event); + virtual void customEvent( QEvent* event); + virtual void mousePressEvent( QMouseEvent* event ); + virtual void insertFromMimeData(const QMimeData* source); + + virtual PyInterp_Request* createTabRequest( const QString& input ); + virtual void handleTab(); + virtual void handleBackTab(); + virtual void clearCompletion(); + virtual void formatCompletion(const QStringList& matches, QString& result) const; + virtual QString formatDocHTML(const QString & doc) const; + + virtual void multilinePaste(const QString & s); + virtual void multiLineProcessNextLine(); + +private: + void extractCommon(const QStringList& matches, QString& result) const; + +}; + +#endif /* PYCONSOLE_ENHEDITORBASE_H_ */ diff --git a/src/PyConsoleBase/PyConsole_EnhInterp.cxx b/src/PyConsoleBase/PyConsole_EnhInterp.cxx new file mode 100644 index 000000000..eab44af6f --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EnhInterp.cxx @@ -0,0 +1,177 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 +// +// Author : Adrien Bruneton (CEA/DEN) +// Created on: 4 avr. 2013 + + +#include "PyConsoleBase.h" + +#include "PyConsole_EnhInterp.h" + +#include +#include +#include + +static const char * tmp_k[] = {"and", "as", "assert", "break", "class", + "continue", "def", "del", + "elif", "else", "except", "exec", "finally", "for", "from", "global", "if", + "import", "in", "is", "lambda", "not", "or", "pass", "print", "raise", + "return", "try", "while", "with", "yield"}; + +const std::vector PyConsole_EnhInterp::PYTHON_KEYWORDS = \ + std::vector(tmp_k, tmp_k+sizeof(tmp_k)/sizeof(tmp_k[0])); + +/*! + \brief Constructor +*/ +PyConsole_EnhInterp::PyConsole_EnhInterp() + : PyConsole_Interp() +{ +} + +/*! + \brief Destructor +*/ +PyConsole_EnhInterp::~PyConsole_EnhInterp() +{ +} + +QStringList PyConsole_EnhInterp::getLastMatches() const +{ + return _last_matches; +} + +QString PyConsole_EnhInterp::getDocStr() const +{ + return _doc_str; +} + +/*! + \brief Run Python dir() command and saves the result internally in _lastPy + \param dirArgument Python expression to pass to the dir command. The parsing of what the + user actually started typing is dedicated to the caller + \param startMatch string representing the begining of the patter to be completed. For example when + the user types "a_string_variable.rsp ", this is "rsp". + \return command exit status - 0 = success +*/ +int PyConsole_EnhInterp::runDirCommand(const QString& dirArgument, const QString& startMatch) +{ + int ret; + std::vector v; + + clearCompletion(); + if ( (ret = runDirAndExtract(dirArgument, startMatch, _last_matches)) ) + return ret; + + // If dirArgument is empty, we append the __builtins__ + if (dirArgument.isEmpty()) + { + if ( (ret = runDirAndExtract(QString("__builtins__"), startMatch, _last_matches, false)) ) + return ret; + + // ... and we match on Python's keywords as well: + for (std::vector::const_iterator it = PYTHON_KEYWORDS.begin(); it != PYTHON_KEYWORDS.end(); it++) + if ((*it).startsWith(startMatch)) + _last_matches.push_back(*it); + } + + // Try to get doc string of the first match + if (_last_matches.size() > 0) + { + QString cmd(""); + if (dirArgument.trimmed() != "") + cmd = dirArgument + "."; + cmd += _last_matches[0] + ".__doc__"; + PyObject * str = PyRun_String(cmd.toStdString().c_str(), Py_eval_input, _global_context, _local_context); + if (!str || str == Py_None || !PyString_Check(str)) + { + if (!str) + PyErr_Clear(); + _doc_str = ""; + } + else + _doc_str = QString(PyString_AsString(str)); + Py_XDECREF(str); + } + + // The command has been successfully executed + return 0; +} + +/** + * See runDirCommand(). + * @param dirArgument see runDirCommand() + * @param startMatch see runDirCommand() + * @param[out] result the list of matches + * @param discardSwig if true a regular expression is used to discard all static method generated + * by SWIG. typically: MEDCouplingUMesh_Blabla + * @return -1 if the call to dir() or the parsing of the result failed, 0 otherwise. + */ +int PyConsole_EnhInterp::runDirAndExtract(const QString& dirArgument, + const QString& startMatch, + QStringList& result, + bool discardSwig) const +{ + QRegExp re("^[A-Z].+_[A-Z]+[a-z]+.+$"); // discard SWIG static method, e.g. MEDCouplingUMesh_Blabla + QString command("dir(" + dirArgument + ")"); + PyObject * plst = PyRun_String(command.toStdString().c_str(), Py_eval_input, _global_context, _local_context); + if(!plst || plst == Py_None) { + if(!plst) + PyErr_Clear(); + + Py_XDECREF(plst); + return -1; + } + + // Extract the returned list and convert it to a vector<> + if (!PySequence_Check(plst)) + { + // Should never happen ... + //std::cerr << "not a list!" << std::endl; + Py_XDECREF(plst); + return -1; + } + + // Convert plst to a vector of QString + int n = PySequence_Length(plst); + for (int i = 0; i < n; i++) + { + PyObject * it; + it = PySequence_GetItem(plst, i); + QString s(PyString_AsString(it)); + // if the method is not from swig, not static (guessed from the reg exp) and matches + // what is already there + if (s.startsWith(startMatch)) + if(!discardSwig || (!re.exactMatch(s) && !s.contains("swig"))) + result.push_back(s); + Py_DECREF(it); + } + Py_DECREF(plst); + + return 0; +} + +/** + * Clear internal members containing the last completion results. + */ +void PyConsole_EnhInterp::clearCompletion() +{ + _last_matches.clear(); + _doc_str = ""; +} diff --git a/src/PyConsoleBase/PyConsole_EnhInterp.h b/src/PyConsoleBase/PyConsole_EnhInterp.h new file mode 100644 index 000000000..57a483b34 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EnhInterp.h @@ -0,0 +1,63 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 +// +// Author : Adrien Bruneton (CEA/DEN) +// Created on: 4 avr. 2013 + + +#ifndef PYCONSOLE_ENHINTERP_H_ +#define PYCONSOLE_ENHINTERP_H_ + +#include "PyConsoleBase.h" + +#include +#include "PyConsole_Interp.h" + +#include +#include + +/** + * Enhanced Python interpreter used for auto-completion. + * This extends PyConsole_Interp with an API wrapping the Python dir() command nicely. + */ +class PYCONSOLEBASE_EXPORT PyConsole_EnhInterp: public PyConsole_Interp +{ +public: + PyConsole_EnhInterp(); + virtual ~PyConsole_EnhInterp(); + + virtual QStringList getLastMatches() const; + virtual QString getDocStr() const; + + virtual int runDirCommand(const QString& dirArgument, const QString& startMatch); + virtual void clearCompletion(); + +protected: + /** Hard coded list of Python keywords */ + static const std::vector PYTHON_KEYWORDS; + + /** Last computed matches */ + QStringList _last_matches; + /** Doc string of the first match - when only one match it will be displayed by the Editor*/ + QString _doc_str; + + virtual int runDirAndExtract(const QString& dirArgument, const QString& startMatch, + QStringList& result, bool discardSwig=true) const; +}; + +#endif /* PYCONSOLE_ENHINTERP_H_ */ diff --git a/src/PyConsoleBase/PyConsole_Event.cxx b/src/PyConsoleBase/PyConsole_Event.cxx new file mode 100644 index 000000000..aaeb2576f --- /dev/null +++ b/src/PyConsoleBase/PyConsole_Event.cxx @@ -0,0 +1,24 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 +// +// Author : Vadim SANDLER (Open CASCADE S.A.S), Adrien Bruneton (CEA/DEN) + +#include "PyConsole_Event.h" diff --git a/src/PyConsoleBase/PyConsole_Event.h b/src/PyConsoleBase/PyConsole_Event.h new file mode 100644 index 000000000..28db82430 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_Event.h @@ -0,0 +1,69 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 +// +// Author : Vadim SANDLER (Open CASCADE S.A.S), Adrien Bruneton (CEA/DEN) + +#ifndef PYCONSOLE_EVENT_H +#define PYCONSOLE_EVENT_H + +#include "PyConsoleBase.h" + +#include +#include + +/*! + \class PrintEvent + \brief Python command output backend event. + \internal +*/ +class PrintEvent : public QEvent +{ +public: + static const int EVENT_ID = 65432; + + /*! + \brief Constructor + \param c message text (python trace) + \param isError default to false - if true indicates that an error is being printed. + */ + PrintEvent( const QString& c, bool isError = false) : + QEvent( (QEvent::Type)EVENT_ID ), myText( c ), errorFlag(isError) + {} + + /*! + \brief Get message + \return message text (python trace) + */ + QString text() const { return myText; } + + /** + * @return true if this is an error message + */ + bool isError() const { return errorFlag; } + +protected: + QString myText; //!< Event message (python trace) + + /** Set to true if an error msg is to be displayed */ + bool errorFlag; +}; + +#endif // PYCONSOLE_EVENT_H diff --git a/src/PyConsoleBase/PyConsole_Interp.cxx b/src/PyConsoleBase/PyConsole_Interp.cxx new file mode 100644 index 000000000..ca459b582 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_Interp.cxx @@ -0,0 +1,93 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 : PyConsole_Interp.cxx +// Author : Nicolas REJNERI, Adrien BRUNETON + +#include "PyConsole_Interp.h" + +/*! + \class PyConsole_Interp + \brief Python interpreter to be embedded to the SALOME study's GUI. + + There is only one Python interpreter for the whole SALOME environment. + + Call the initialize() method defined in the base class PyInterp_Interp, + to initialize the interpreter after instance creation. + + The method initialize() calls virtuals methods + - initPython() to initialize global Python interpreter + - initContext() to initialize interpreter internal context + - initRun() to prepare interpreter for running commands +*/ + +/*! + \brief Constructor. + + Creates new python interpreter. +*/ +PyConsole_Interp::PyConsole_Interp(): PyInterp_Interp() +{ +} + +/*! + \brief Destructor. + + Does nothing for the moment. +*/ +PyConsole_Interp::~PyConsole_Interp() +{ +} + +/*! Sets the variable "__IN_SALOME_GUI_CONSOLE" to True. +* This is not attached to a module (like salome_iapp.IN_SALOME_GUI_CONSOLE) +* since modules are shared across all interpreters in SALOME. +* +* (GIL is already acquired here) +*/ +int PyConsole_Interp::beforeRun() +{ + return PyRun_SimpleString("__builtins__.__IN_SALOME_GUI_CONSOLE=True"); +} + +int PyConsole_Interp::afterRun() +{ + return PyRun_SimpleString("__builtins__.__IN_SALOME_GUI_CONSOLE=False"); +} + +QStringList PyConsole_Interp::getLastMatches() const +{ + return QStringList(); +} + +QString PyConsole_Interp::getDocStr() const +{ + return QString(); +} + +int PyConsole_Interp::runDirCommand(const QString&, const QString& ) +{ + return 0; +} + +void PyConsole_Interp::clearCompletion() +{ +} diff --git a/src/PyConsoleBase/PyConsole_Interp.h b/src/PyConsoleBase/PyConsole_Interp.h new file mode 100644 index 000000000..34f251d26 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_Interp.h @@ -0,0 +1,49 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 : PyConsole_Interp.h +// Author : Nicolas REJNERI, Adrien BRUNETON + +#ifndef PYCONSOLE_INTERP_H +#define PYCONSOLE_INTERP_H + +#include "PyConsoleBase.h" +#include "PyInterp_Interp.h" /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!! + +#include + +class PYCONSOLEBASE_EXPORT PyConsole_Interp : public PyInterp_Interp +{ +public: + PyConsole_Interp(); + ~PyConsole_Interp(); + + virtual int afterRun(); + virtual int beforeRun(); + + virtual QStringList getLastMatches() const; + virtual QString getDocStr() const; + + virtual int runDirCommand(const QString&, const QString&); + virtual void clearCompletion(); +}; + +#endif // PYCONSOLE_INTERP_H diff --git a/src/PyConsoleBase/PyConsole_Request.cxx b/src/PyConsoleBase/PyConsole_Request.cxx new file mode 100644 index 000000000..5c67b8bbb --- /dev/null +++ b/src/PyConsoleBase/PyConsole_Request.cxx @@ -0,0 +1,108 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 +// +// Author : Adrien Bruneton (CEA/DEN) +// Created on: 3 avr. 2013 + +#include "PyConsole_Request.h" +#include "PyConsole_Interp.h" +#include "PyConsole_Event.h" +#include "PyInterp_Event.h" + +#include + +/** + * Constructor. + * @param theInterp interpreter that will execute the command + * @param theCommand command text + * @param theListener editor object that will receive the response events after execution + * of the request + * @param sync + */ +ExecCommand::ExecCommand( PyInterp_Interp* theInterp, + const QString& theCommand, + QObject* theListener, + bool theSync ) + : PyInterp_LockRequest( theInterp, theListener, theSync ), + myCommand( theCommand ), myState( PyInterp_Event::ES_OK ) +{} + +/** + * Execute the command by calling the run() method of the embedded interpreter. + */ +void ExecCommand::execute() +{ + if ( myCommand != "" ) + { + int ret = getInterp()->run( myCommand.toLatin1().data() ); + if ( ret < 0 ) + myState = PyInterp_Event::ES_ERROR; + else if ( ret > 0 ) + myState = PyInterp_Event::ES_INCOMPLETE; + } +} + +/** + * Create the event indicating the status of the request execution. + * @return a QEvent + */ +QEvent* ExecCommand::createEvent() +{ + if ( IsSync() ) + QCoreApplication::sendPostedEvents( listener(), PrintEvent::EVENT_ID ); + return new PyInterp_Event( myState, this ); +} + + +/*! + Constructor. + Creates a new python completion request. + \param theInterp python interpreter + \param input string containing the dir() command to be executed + \param startMatch part to be matched with the results of the dir() command + \param theListener widget to get the notification messages + \param sync if True the request is processed synchronously +*/ +CompletionCommand::CompletionCommand( PyInterp_Interp* theInterp, + const QString& theInput, + const QString& theStartMatch, + QObject* theListener, + bool theSync ) + : PyInterp_LockRequest( theInterp, theListener, theSync ), + _tabSuccess(false), _dirArg(theInput), _startMatch(theStartMatch) +{} + +/** + * Execute the completion command by wrapping the runDirCommand() of the + * embedded enhanced interpreter. + */ +void CompletionCommand::execute() +{ + int ret = static_cast(getInterp())->runDirCommand( _dirArg, _startMatch ); + _tabSuccess = ret == 0; +} + +/** + * Create the event indicating the return value of the completion command. + * @return + */ +QEvent* CompletionCommand::createEvent() +{ + int typ = _tabSuccess ? PyInterp_Event::ES_TAB_COMPLETE_OK : PyInterp_Event::ES_TAB_COMPLETE_ERR; + return new PyInterp_Event( typ, this); +} diff --git a/src/PyConsoleBase/PyConsole_Request.h b/src/PyConsoleBase/PyConsole_Request.h new file mode 100644 index 000000000..a0ed598eb --- /dev/null +++ b/src/PyConsoleBase/PyConsole_Request.h @@ -0,0 +1,99 @@ +// Copyright (C) 2007-2015 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, or (at your option) any later version. +// +// 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 +// +// Author : Adrien Bruneton (CEA/DEN) +// Created on: 3 avr. 2013 + + +#ifndef PYCONSOLE_REQUEST_H_ +#define PYCONSOLE_REQUEST_H_ + +#include "PyInterp_Request.h" + +#include +#include +#include + +class PyInterp_Interp; + +/*! + \class ExecCommand + \brief Python command execution request. + \internal +*/ +class ExecCommand : public PyInterp_LockRequest +{ +public: + /*! + \brief Constructor. + + Creates new python command execution request. + \param theInterp python interpreter + \param theCommand python command + \param theListener widget to get the notification messages + \param sync if True the request is processed synchronously + */ + ExecCommand( PyInterp_Interp* theInterp, + const QString& theCommand, + QObject* theListener, + bool theSync = false ); + +protected: + /*! + \brief Execute the python command in the interpreter and + get its execution status. + */ + virtual void execute(); + + /*! + \brief Create and return a notification event. + \return new notification event + */ + virtual QEvent* createEvent(); + +private: + QString myCommand; //!< Python command + int myState; //!< Python command execution status +}; + +class CompletionCommand : public PyInterp_LockRequest +{ +public: + CompletionCommand( PyInterp_Interp* theInterp, + const QString& theInput, + const QString& theStartMatch, + QObject* theListener, + bool theSync = false ); + + +protected: + /** List of separators identifying the last parsable token for completion */ + static const std::vector SEPARATORS; + + /** String to be passed to the dir() command */ + QString _dirArg; + /** Begining of the command (as typed by the user) */ + QString _startMatch; + /** was the completion command successful */ + bool _tabSuccess; + + virtual void execute(); + virtual QEvent* createEvent(); +}; + +#endif /* PYCONSOLE_REQUEST_H_ */ diff --git a/src/PyConsoleBase/resources/PyConsole_msg_en.ts b/src/PyConsoleBase/resources/PyConsole_msg_en.ts new file mode 100644 index 000000000..af33f0b3b --- /dev/null +++ b/src/PyConsoleBase/resources/PyConsole_msg_en.ts @@ -0,0 +1,62 @@ + + + + + PyConsole_ConsoleBase + + + EDIT_COPY_CMD + &Copy + + + + EDIT_PASTE_CMD + &Paste + + + + EDIT_CLEAR_CMD + Clea&r + + + + EDIT_SELECTALL_CMD + Select &All + + + EDIT_DUMPCOMMANDS_CMD + D&ump Commands + + + EDIT_STARTLOG_CMD + Start &Log + + + EDIT_STOPLOG_CMD + Stop &Log + + + + PyConsole_EditorBase + + TOT_DUMP_PYCOMMANDS + Dump commands + + + TOT_SAVE_PYLOG + Save log + + + PYTHON_FILES_FILTER + PYTHON Files (*.py) + + + LOG_FILES_FILTER + Log files (*.log *.txt) + + + ERR_FILE_NOT_WRITABLE + File is not writable! + + + diff --git a/src/PyConsoleBase/resources/PyConsole_msg_fr.ts b/src/PyConsoleBase/resources/PyConsole_msg_fr.ts new file mode 100755 index 000000000..bab57bfa6 --- /dev/null +++ b/src/PyConsoleBase/resources/PyConsole_msg_fr.ts @@ -0,0 +1,62 @@ + + + + + PyConsole_ConsoleBase + + + EDIT_COPY_CMD + &Copier + + + + EDIT_PASTE_CMD + C&oller + + + + EDIT_CLEAR_CMD + &Effacer + + + + EDIT_SELECTALL_CMD + &Tout sélectionner + + + EDIT_DUMPCOMMANDS_CMD + &Générer le script des commandes + + + EDIT_STARTLOG_CMD + Démarrer une &trace + + + EDIT_STOPLOG_CMD + Arrêter la &trace + + + + PyConsole_Editor + + TOT_DUMP_PYCOMMANDS + &Générer le script des commandes + + + TOT_SAVE_PYLOG + Sauver la trace + + + PYTHON_FILES_FILTER + Fichiers PYTHON (*.py) + + + LOG_FILES_FILTER + Fichiers de trace (*.log *.txt) + + + ERR_FILE_NOT_WRITABLE + Le fichier n'est pas accessible en écriture! + + + diff --git a/src/PyConsoleBase/resources/PyConsole_msg_ja.ts b/src/PyConsoleBase/resources/PyConsole_msg_ja.ts new file mode 100644 index 000000000..c36cf6ac3 --- /dev/null +++ b/src/PyConsoleBase/resources/PyConsole_msg_ja.ts @@ -0,0 +1,62 @@ + + + + + PyConsole_ConsoleBase + + + EDIT_COPY_CMD + コピー(&C) + + + + EDIT_PASTE_CMD + 貼り付け(&P) + + + + EDIT_CLEAR_CMD + 削除(&r) + + + + EDIT_SELECTALL_CMD + すべて選択します。(&A) + + + EDIT_DUMPCOMMANDS_CMD + スクリプト コマンドを生成します。(&u) + + + EDIT_STARTLOG_CMD + ログの開始 (&L) + + + EDIT_STOPLOG_CMD + ログの停止 (&L) + + + + PyConsole_Editor + + TOT_DUMP_PYCOMMANDS + スクリプト コマンドを生成します。 + + + TOT_SAVE_PYLOG + ログの保存 + + + PYTHON_FILES_FILTER + ファイル (*.py) PYTHON + + + LOG_FILES_FILTER + ログファイル (*.log *.txt) + + + ERR_FILE_NOT_WRITABLE + ファイルが書き込み禁止です! + + + diff --git a/src/SALOME_PYQT/SalomePyQt/CMakeLists.txt b/src/SALOME_PYQT/SalomePyQt/CMakeLists.txt index bba191dd1..ee6a6feae 100755 --- a/src/SALOME_PYQT/SalomePyQt/CMakeLists.txt +++ b/src/SALOME_PYQT/SalomePyQt/CMakeLists.txt @@ -41,6 +41,7 @@ INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}/src/OBJECT ${PROJECT_SOURCE_DIR}/src/ObjBrowser ${PROJECT_SOURCE_DIR}/src/PyInterp + ${PROJECT_SOURCE_DIR}/src/PyConsoleBase ${PROJECT_SOURCE_DIR}/src/PyConsole ${PROJECT_SOURCE_DIR}/src/Qtx ${PROJECT_SOURCE_DIR}/src/SALOME_PYQT/SALOME_PYQT_GUILight diff --git a/src/SUITT/CMakeLists.txt b/src/SUITT/CMakeLists.txt new file mode 100644 index 000000000..fcff36a2c --- /dev/null +++ b/src/SUITT/CMakeLists.txt @@ -0,0 +1,95 @@ +# Copyright (C) 2012-2015 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, or (at your option) any later version. +# +# 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 +# + +INCLUDE(UseQtExt) + +# --- options --- + +# additional include directories +INCLUDE_DIRECTORIES( + ${QT_INCLUDES} +) + +# additional preprocessor / compiler flags +ADD_DEFINITIONS(${QT_DEFINITIONS}) +IF(ENABLE_TESTRECORDER) + ADD_DEFINITIONS("-DENABLE_TESTRECORDER ${TESTRECORDER_DEFINITIONS}") +ENDIF() + +# libraries to link to +SET(_link_LIBRARIES ${PLATFORM_LIBS} ${QT_LIBRARIES} qtx ObjBrowser) +IF(ENABLE_TESTRECORDER) + LIST(APPEND _link_LIBRARIES ${TESTRECORDER_LIBS}) +ENDIF() + +# --- headers --- + +# header files / to be processed by moc +SET(_moc_HEADERS + SUIT_MessageBox.h +) + +# header files / no moc processing +SET(_other_HEADERS + SUIT_OverrideCursor.h + SUIT_FileValidator.h + Qtxx.h + SUIT.h +) + +# header files / to install +SET(suitt_HEADERS ${_moc_HEADERS} ${_other_HEADERS}) + +# --- resources --- + +# resource files / to be processed by lrelease + +# resource files / static +SET(_other_RESOURCES + resources/icon_visibility_on.png + resources/icon_visibility_off.png + resources/view_sync.png +) + +# --- sources --- + +# sources / moc wrappings +QT_WRAP_MOC(_moc_SOURCES ${_moc_HEADERS}) + +# sources / static +SET(_other_SOURCES + SUIT_OverrideCursor.cxx + SUIT_MessageBox.cxx + SUIT_FileValidator.cxx + Qtxx.cxx +) + +# sources / to compile +SET(suitt_SOURCES ${_other_SOURCES} ${_moc_SOURCES}) + +# --- rules --- + +ADD_LIBRARY(suitt ${suitt_SOURCES}) +TARGET_LINK_LIBRARIES(suitt ${_link_LIBRARIES}) +INSTALL(TARGETS suitt EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME_INSTALL_LIBS}) + +INSTALL(FILES ${suitt_HEADERS} DESTINATION ${SALOME_INSTALL_HEADERS}) + +INSTALL(FILES ${_other_RESOURCES} DESTINATION ${SALOME_GUI_INSTALL_RES_DATA}) + diff --git a/src/SUITT/Qtxx.cxx b/src/SUITT/Qtxx.cxx new file mode 100755 index 000000000..3186a1148 --- /dev/null +++ b/src/SUITT/Qtxx.cxx @@ -0,0 +1,75 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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: Qtxx.cxx +// Author: Sergey TELKOV +// +#include "Qtxx.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/*! + \brief Return directory part of the file path. + + If the file path does not include directory part (the file is in the + current directory), null string is returned. + + \param path file path + \param abs if true (default) \a path parameter is treated as absolute file path + \return directory part of the file path +*/ +QString Qtxx::dir( const QString& path, const bool abs ) +{ + QDir aDir = QFileInfo( path ).dir(); + QString dirPath = abs ? aDir.absolutePath() : aDir.path(); + if ( dirPath == QString( "." ) ) + dirPath = QString(); + return dirPath; +} + +/*! + Creates font from string description +*/ +QFont Qtxx::stringToFont( const QString& fontDescription ) +{ + QFont font; + if ( fontDescription.trimmed().isEmpty() || !font.fromString( fontDescription ) ) + font = QFont( "Courier", 11 ); + return font; +} diff --git a/src/SUITT/Qtxx.h b/src/SUITT/Qtxx.h new file mode 100755 index 000000000..5d31747db --- /dev/null +++ b/src/SUITT/Qtxx.h @@ -0,0 +1,68 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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: Qtx.h +// Author: Sergey TELKOV +// +#ifndef QTXX_H +#define QTXX_H + +#if defined WIN32 +# if defined QTXX_EXPORTS || defined qtxx_EXPORTS +# define QTX_EXPORT _declspec( dllexport ) +# else +# define QTX_EXPORT _declspec( dllimport ) +# endif +#else +# define QTX_EXPORT +#endif + +#if defined SOLARIS +#define bool int +#define false 0 +#define true 1 +#endif + +#include +#include +#include +#include +#include +#include + +class QObject; +class QWidget; +class QCompleter; + +typedef QList QIntList; //!< list of int values +typedef QList QShortList; //!< list of short int values +typedef QList QDoubleList; //!< list of double values +typedef QList QColorList; //!< list of colors + +class QTX_EXPORT Qtxx +{ +public: + static QString dir( const QString&, const bool = true ); + static QFont stringToFont( const QString& fontDescription ); +}; + +#endif diff --git a/src/SUITT/SUIT.h b/src/SUITT/SUIT.h new file mode 100755 index 000000000..392e98719 --- /dev/null +++ b/src/SUITT/SUIT.h @@ -0,0 +1,58 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 : SUIT.h +// Author : +// +#ifndef SUIT_H +#define SUIT_H + +#if defined WIN32 +# if defined SUIT_EXPORTS || defined suit_EXPORTS +# define SUIT_EXPORT __declspec( dllexport ) +# else +# define SUIT_EXPORT __declspec( dllimport ) +# endif +#else +# define SUIT_EXPORT +#endif + +#if defined SOLARIS +#define bool int +#define false 0 +#define true 1 +#endif + +#if defined WIN32 +#pragma warning ( disable: 4251 ) +#endif + +#if defined ( _DEBUG ) || defined ( DEBUG ) +#include +#define SUIT_VERIFY(x) assert( x ); +#define SUIT_ASSERT(x) assert( x ); +#else +#define SUIT_VERIFY(x) x +#define SUIT_ASSERT(x) +#endif + +#endif // SUIT_H diff --git a/src/SUITT/SUIT_FileValidator.cxx b/src/SUITT/SUIT_FileValidator.cxx new file mode 100755 index 000000000..1fc1b402a --- /dev/null +++ b/src/SUITT/SUIT_FileValidator.cxx @@ -0,0 +1,201 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 : SUIT_FileValidator.cxx +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// +#include "SUIT_FileValidator.h" +#include "SUIT_MessageBox.h" +#include "Qtxx.h" +#include + +/*! + \class SUIT_FileValidator + \brief Provides functionality to check the file or directory + existance and permissions. + \sa SUIT_FileDlg class +*/ + +/*! + \brief Constructor. + \param parent parent widget (used as parent when displaying + information message boxes) +*/ +SUIT_FileValidator::SUIT_FileValidator( QWidget* parent ) +: myParent( parent ) +{ +} + +/*! + \brief Check if the specified file exists and (optionally) can be read. + + If file does not exists or can not be read (if \a checkPermission is \c true) + and parent() is not null, shows error message box. + + \param fileName file path + \param checkPermission if \c true (default) check also file permissions + \return \c false if file does not exist or if it does not have + read permissions (if \a checkPermission is \c true) +*/ +bool SUIT_FileValidator::canOpen( const QString& fileName, bool checkPermission ) +{ + if ( !QFile::exists( fileName ) ) { + if ( parent() ) + SUIT_MessageBox::critical( parent(), QObject::tr( "ERR_ERROR" ), + QObject::tr( "ERR_FILE_NOT_EXIST" ).arg( fileName ) ); + return false; + } + if ( checkPermission && !QFileInfo( fileName ).isReadable() ) { + if ( parent() ) + SUIT_MessageBox::critical( parent(), QObject::tr( "ERR_ERROR" ), + QObject::tr( "ERR_OPEN_PERMISSION_DENIED" ).arg( fileName ) ); + return false; + } + return true; +} + +/*! + \brief Check if the specified file can be written. + + If file already exists and parent() is not null, prompts + question message box to the user to confirm file overwriting. + + If file can not be written (if \a checkPermission is \c true) + and parent() is not null, shows error message box. + + \param fileName file path + \param checkPermission if \c true (default) check also file permissions + \return \c false if file exists and user rejects file overwriting + or if file does not have write permissions (if \a checkPermission is \c true) +*/ +bool SUIT_FileValidator::canSave( const QString& fileName, bool checkPermission ) +{ + if ( QFile::exists( fileName ) ) { + if ( parent() ) + if ( SUIT_MessageBox::question( parent(), QObject::tr( "WRN_WARNING" ), + QObject::tr( "QUE_DOC_FILEEXISTS" ).arg( fileName ), + SUIT_MessageBox::Yes | SUIT_MessageBox::No, + SUIT_MessageBox::No ) != SUIT_MessageBox::Yes ) + return false; + + if ( checkPermission && !QFileInfo( fileName ).isWritable() ) { + if ( parent() ) + SUIT_MessageBox::critical( myParent, QObject::tr( "ERR_ERROR" ), + QObject::tr( "ERR_PERMISSION_DENIED" ).arg( fileName ) ); + return false; + } + } + else { + QString dirName = Qtxx::dir( fileName ); + if ( !QFile::exists( dirName ) ) { + SUIT_MessageBox::critical( parent(), QObject::tr( "WRN_WARNING" ), + QObject::tr( "ERR_DIR_NOT_EXIST" ).arg( dirName ) ); + return false; + } + if ( checkPermission && !QFileInfo( dirName ).isWritable() ) { + if ( parent() ) + SUIT_MessageBox::critical( parent(), QObject::tr( "ERR_ERROR" ), + QObject::tr( "ERR_PERMISSION_DENIED" ).arg( fileName ) ); + return false; + } + } + return true; +} + +/*! + \brief Check if the specified directory exists and (optionally) can be read. + + If directory does not exists or can not be read (if \a checkPermission is \c true) + and parent() is not null, shows error message box. + + \param dirName directory path + \param checkPermission if \c true (default) check also directory permissions + \return \c false if directory does not exist or if it does not have + read permissions (if \a checkPermission is \c true) +*/ +bool SUIT_FileValidator::canReadDir( const QString& dirName, bool checkPermission ) +{ + QFileInfo info( dirName ); + if ( !info.exists() ) { + if ( parent() ) + SUIT_MessageBox::critical( parent(), QObject::tr( "ERR_ERROR" ), + QObject::tr( "ERR_DIR_NOT_EXIST" ).arg( dirName ) ); + return false; + } + if ( !info.isDir() ) { + if ( parent() ) + SUIT_MessageBox::critical( parent(), QObject::tr( "ERR_ERROR" ), + QObject::tr( "ERR_FILE_NOT_DIR" ).arg( dirName ) ); + return false; + } + if ( checkPermission && !info.isReadable() ) { + if ( parent() ) + SUIT_MessageBox::critical( parent(), QObject::tr( "ERR_ERROR" ), + QObject::tr( "ERR_DIR_READ_PERMISSION_DENIED" ).arg( dirName ) ); + return false; + } + return true; +} + +/*! + \brief Check if the specified directory can be written. + + If directory does not exists or can not be modified (if \a checkPermission is \c true) + and parent() is not null, shows error message box. + + \param dirName directory path + \param checkPermission if \c true (default) check also directory permissions + \return \c false if directory does not exist or if it does not have + write permissions (if \a checkPermission is \c true) +*/ +bool SUIT_FileValidator::canWriteDir( const QString& dirName, bool checkPermission ) +{ + QFileInfo info( dirName ); + if ( !info.exists() ) { + if ( parent() ) + SUIT_MessageBox::critical( parent(), QObject::tr( "ERR_ERROR" ), + QObject::tr( "ERR_DIR_NOT_EXIST" ).arg( dirName ) ); + return false; + } + if ( !info.isDir() ) { + if ( parent() ) + SUIT_MessageBox::critical( parent(), QObject::tr( "ERR_ERROR" ), + QObject::tr( "ERR_FILE_NOT_DIR" ).arg( dirName ) ); + return false; + } + if ( checkPermission && !info.isWritable() ) { + if ( parent() ) + SUIT_MessageBox::critical( parent(), QObject::tr( "ERR_ERROR" ), + QObject::tr( "ERR_DIR_WRITE_PERMISSION_DENIED" ).arg( dirName ) ); + return false; + } + return true; +} + +/*! + \brief Get parent widget. + \return parent widget +*/ +QWidget* SUIT_FileValidator::parent() const +{ + return myParent; +} diff --git a/src/SUITT/SUIT_FileValidator.h b/src/SUITT/SUIT_FileValidator.h new file mode 100755 index 000000000..4c59b02d3 --- /dev/null +++ b/src/SUITT/SUIT_FileValidator.h @@ -0,0 +1,51 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 : SUIT_FileValidator.h +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// +#ifndef SUIT_FILEVALIDATOR_H +#define SUIT_FILEVALIDATOR_H + +#include "SUIT.h" + +class QWidget; +class QString; + +class SUIT_EXPORT SUIT_FileValidator +{ +public: + SUIT_FileValidator( QWidget* = 0 ); + + virtual bool canOpen( const QString&, bool = true ); + virtual bool canSave( const QString&, bool = true ); + + virtual bool canReadDir( const QString&, bool = true ); + virtual bool canWriteDir( const QString&, bool = true ); + + QWidget* parent() const; + +private: + QWidget* myParent; +}; + +#endif // SUIT_FILEVALIDATOR_H diff --git a/src/SUITT/SUIT_MessageBox.cxx b/src/SUITT/SUIT_MessageBox.cxx new file mode 100755 index 000000000..348d8a864 --- /dev/null +++ b/src/SUITT/SUIT_MessageBox.cxx @@ -0,0 +1,1131 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 +// + +#include "SUIT_MessageBox.h" + +#include "SUIT_OverrideCursor.h" + +#include +#include +#include +#include + +#include + +/*! + \class SUIT_MessageBox + \brief Message dialog box for SUIT-based application + + The class provides a modal dialog with a short message, an icon, + and buttons laid out depending on the current style. + + Message boxes are used to provide informative messages and to ask + simple questions. + + The easiest way to pop up a message box is to call one of the static + functions information(), question(), critical(), and warning(). + + The class provides the static functions to show message boxes with + standard buttons (like \c Ok, \c Cancel, \c Apply, \c Close, \c Yes, + \c No, \c Abort, \c Retry, etc). These methods accept ORed buttons + flags as one of the parameters. The buttons layouting type and order + is system-dependant and defined by the current style. In addition, + these methods allow to define default button (where input focus is + set by default and which is clicked when user presses \c Enter key). + + Another set of static functions allows to show message boxes with + up to four user-defined buttons. It is possible to define default + and escape buttons using additional parameters. + + And finally, the last group of static functions allow displaying + the message boxes with an arbitrary number of buttons. + + For example: + \code + // show question message box with two standard buttons + int result = SUIT_MessageBox::question(desktop(), "Error", + "File already exists? Overwrite?", + SUIT_MessageBox::Yes | SUIT_MessageBox::No, + SUIT_MessageBox::No ); + if ( result == SUIT_MessageBox::Yes ) + overwriteFileFunction(); + + // show critical message box with user-defined buttons + // default is second button and escape is third button + int result = SUIT_MessageBox::critical(desktop(), "Hazard!", + "The situation is critical! What to do?", + "Hide", "Run Away", "Stand still", 1, 2); + switch ( result ) + { + case 0: + hideMyself(); break; + case 1: + runAwayFromHere(); break; + case 2: + sitDownAndPray(); break; + default: + break; + } + + // show message box with 6 buttons + // both default and escape buttons are set to first button ("Zero") + int result = SUIT_MessageBox::information(desktop(), "Question", + "Select your favourite number", + 0, 0, + "Zero", "One", "Two", "Three", "Four", "Five"); + useMyFavouriteNumberSomewhere( result ); + \endcode +*/ + +/*! + \brief Constructor. + \param parent parent widget +*/ +SUIT_MessageBox::SUIT_MessageBox( QWidget* parent ) +: QMessageBox( parent ) +{ +} + +/*! + \brief Constructor. + \param icon message box icon (QMessageBox::Icon) + \param title message box title + \param text message box text + \param buttons ORed message box standard buttons (QMessageBox::StandardButton) + \param parent parent widget + \param f window flags +*/ +SUIT_MessageBox::SUIT_MessageBox( Icon icon, const QString& title, const QString& text, + StandardButtons buttons, QWidget* parent, Qt::WindowFlags f ) +: QMessageBox( icon, title, text, buttons, parent, f ) +{ +} + +/*! + \brief Destructor. +*/ +SUIT_MessageBox::~SUIT_MessageBox() +{ +} + +/*! + \brief Get the standard button text. + \param btn standard button id + \return button text +*/ +QString SUIT_MessageBox::buttonText( StandardButton btn ) const +{ + QString res; + QAbstractButton* b = button( btn ); + if ( b ) + res = b->text(); + return res; +} + +/*! + \brief Set the standard button text. + \param btn standard button id + \param text new button text +*/ +void SUIT_MessageBox::setButtonText( StandardButton btn, const QString& text ) +{ + QAbstractButton* b = button( btn ); + if ( b ) + b->setText( text ); +} + +/*! + \brief Show critical message box with specified standard buttons. + \param parent parent widget + \param title message box title + \param text message box text + \param buttons ORed message box buttons (QMessageBox::StandardButton) + \param defaultButton default button (QMessageBox::StandardButton) + \return button id clicked by the user (QMessageBox::StandardButton) +*/ +SUIT_MessageBox::StandardButton SUIT_MessageBox::critical( QWidget* parent, const QString& title, const QString& text, + StandardButtons buttons, StandardButton defaultButton ) +{ + SUIT_OverrideCursor cw( parent ? parent->cursor() : Qt::ArrowCursor ); + return QMessageBox::critical( parent, title, text, buttons, defaultButton ); +} + +/*! + \brief Show warning message box with specified standard buttons. + \param parent parent widget + \param title message box title + \param text message box text + \param buttons ORed message box buttons (QMessageBox::StandardButton) + \param defaultButton default button (QMessageBox::StandardButton) + \return button id clicked by the user (QMessageBox::StandardButton) +*/ +SUIT_MessageBox::StandardButton SUIT_MessageBox::warning( QWidget* parent, const QString& title, const QString& text, + SUIT_MessageBox::StandardButtons buttons, StandardButton defaultButton ) +{ + SUIT_OverrideCursor cw( parent ? parent->cursor() : Qt::ArrowCursor ); + return QMessageBox::warning( parent, title, text, buttons, defaultButton ); +} + +/*! + \brief Show information message box with specified standard buttons. + \param parent parent widget + \param title message box title + \param text message box text + \param buttons ORed message box buttons (QMessageBox::StandardButton) + \param defaultButton default button (QMessageBox::StandardButton) + \return button id clicked by the user (QMessageBox::StandardButton) +*/ +SUIT_MessageBox::StandardButton SUIT_MessageBox::information( QWidget* parent, const QString& title, const QString& text, + StandardButtons buttons, StandardButton defaultButton ) +{ + SUIT_OverrideCursor cw( parent ? parent->cursor() : Qt::ArrowCursor ); + return QMessageBox::information( parent, title, text, buttons, defaultButton ); +} + +/*! + \brief Show question message box with specified standard buttons. + \param parent parent widget + \param title message box title + \param text message box text + \param buttons ORed message box buttons (QMessageBox::StandardButton) + \param defaultButton default button (QMessageBox::StandardButton) + \return button id clicked by the user (QMessageBox::StandardButton) +*/ +SUIT_MessageBox::StandardButton SUIT_MessageBox::question( QWidget* parent, const QString& title, const QString& text, + StandardButtons buttons, StandardButton defaultButton ) +{ + SUIT_OverrideCursor cw( parent ? parent->cursor() : Qt::ArrowCursor ); + return QMessageBox::question( parent, title, text, buttons, defaultButton ); +} + +/*! + \brief Show critical message box with one custom button. + + Specified button becomes "default" button and "escape" button, i.e. + pressing \c Return or \c Enter and \c Escape keys is equivalent to clicking + this button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button button text + \return button id clicked by the user (QMessageBox::StandardButton) +*/ +int SUIT_MessageBox::critical( QWidget* parent, const QString& title, const QString& text, + const QString& button ) +{ + ButtonInfos lst; + lst.append( ButtonInfo( 0, button ) ); + return messageBox( SUIT_MessageBox::Critical, parent, title, text, lst ); +} + +/*! + \brief Show warning message box with one custom button. + + Specified button becomes "default" button and "escape" button, i.e. + pressing \c Return or \c Enter and \c Escape keys is equivalent to clicking + this button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button button text + \return button id clicked by the user (QMessageBox::StandardButton) +*/ +int SUIT_MessageBox::warning( QWidget* parent, const QString& title, const QString& text, + const QString& button ) +{ + ButtonInfos lst; + lst.append( ButtonInfo( 0, button ) ); + return messageBox( SUIT_MessageBox::Warning, parent, title, text, lst ); +} + +/*! + \brief Show information message box with one custom button. + + Specified button becomes "default" button and "escape" button, i.e. + pressing \c Return or \c Enter and \c Escape keys is equivalent to clicking + this button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button button text + \return button id clicked by the user (QMessageBox::StandardButton) +*/ +int SUIT_MessageBox::information( QWidget* parent, const QString& title, const QString& text, + const QString& button ) +{ + ButtonInfos lst; + lst.append( ButtonInfo( 0, button ) ); + return messageBox( SUIT_MessageBox::Information, parent, title, text, lst ); +} + +/*! + \brief Show question message box with one custom button. + + \warning This function does not make a lot of sense because it provides + message box with only one button, i.e. it is impossible to give several + answers for the question (at least 'yes'/'no'). + This function is implemented only for completeness. + + Specified button becomes "default" button and "escape" button, i.e. + pressing \c Return or \c Enter and \c Escape keys is equivalent to clicking + this button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button button text + \return button id clicked by the user (QMessageBox::StandardButton) +*/ +int SUIT_MessageBox::question( QWidget* parent, const QString& title, const QString& text, + const QString& button ) +{ + ButtonInfos lst; + lst.append( ButtonInfo( 0, button ) ); + return messageBox( SUIT_MessageBox::Question, parent, title, text, lst ); +} + +/*! + \brief Show critical message box with two custom buttons. + + Parameters \a button1 and \a button2 specify the buttons text. + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the second one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button1 first button text + \param button2 second button text + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::critical( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + lst.append( ButtonInfo( id++, button1 ) ); + lst.append( ButtonInfo( id++, button2 ) ); + return messageBox( SUIT_MessageBox::Critical, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show warning message box with two custom buttons. + + Parameters \a button1 and \a button2 specify the buttons text. + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the second one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button1 first button text + \param button2 second button text + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::warning( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + lst.append( ButtonInfo( id++, button1 ) ); + lst.append( ButtonInfo( id++, button2 ) ); + return messageBox( SUIT_MessageBox::Warning, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show information message box with two custom buttons. + + Parameters \a button1 and \a button2 specify the buttons text. + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the second one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button1 first button text + \param button2 second button text + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::information( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + lst.append( ButtonInfo( id++, button1 ) ); + lst.append( ButtonInfo( id++, button2 ) ); + return messageBox( SUIT_MessageBox::Information, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show question message box with two custom buttons. + + Parameters \a button1 and \a button2 specify the buttons text. + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the second one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button1 first button text + \param button2 second button text + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::question( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + lst.append( ButtonInfo( id++, button1 ) ); + lst.append( ButtonInfo( id++, button2 ) ); + return messageBox( SUIT_MessageBox::Question, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show critical message box with three custom buttons. + + Parameters \a button1, \a button2 and \a button3 specify the buttons text. + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button1 first button text + \param button2 second button text + \param button3 third button text + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::critical( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const QString& button3, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + lst.append( ButtonInfo( id++, button1 ) ); + lst.append( ButtonInfo( id++, button2 ) ); + lst.append( ButtonInfo( id++, button3 ) ); + return messageBox( SUIT_MessageBox::Critical, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show warning message box with three custom buttons. + + Parameters \a button1, \a button2 and \a button3 specify the buttons text. + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button1 first button text + \param button2 second button text + \param button3 third button text + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::warning( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const QString& button3, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + lst.append( ButtonInfo( id++, button1 ) ); + lst.append( ButtonInfo( id++, button2 ) ); + lst.append( ButtonInfo( id++, button3 ) ); + return messageBox( SUIT_MessageBox::Warning, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show information message box with three custom buttons. + + Parameters \a button1, \a button2 and \a button3 specify the buttons text. + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button1 first button text + \param button2 second button text + \param button3 third button text + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::information( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const QString& button3, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + lst.append( ButtonInfo( id++, button1 ) ); + lst.append( ButtonInfo( id++, button2 ) ); + lst.append( ButtonInfo( id++, button3 ) ); + return messageBox( SUIT_MessageBox::Information, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show question message box with three custom buttons. + + Parameters \a button1, \a button2 and \a button3 specify the buttons text. + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button1 first button text + \param button2 second button text + \param button3 third button text + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::question( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const QString& button3, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + lst.append( ButtonInfo( id++, button1 ) ); + lst.append( ButtonInfo( id++, button2 ) ); + lst.append( ButtonInfo( id++, button3 ) ); + return messageBox( SUIT_MessageBox::Question, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show critical message box with four custom buttons. + + Parameters \a button1, \a button2, \a button3 and \a button4 specify + the buttons text. + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button1 first button text + \param button2 second button text + \param button3 third button text + \param button4 fourth button text + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::critical( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const QString& button3, const QString& button4, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + lst.append( ButtonInfo( id++, button1 ) ); + lst.append( ButtonInfo( id++, button2 ) ); + lst.append( ButtonInfo( id++, button3 ) ); + lst.append( ButtonInfo( id++, button4 ) ); + return messageBox( SUIT_MessageBox::Critical, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show warning message box with four custom buttons. + + Parameters \a button1, \a button2, \a button3 and \a button4 specify + the buttons text. + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button1 first button text + \param button2 second button text + \param button3 third button text + \param button4 fourth button text + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::warning( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const QString& button3, const QString& button4, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + lst.append( ButtonInfo( id++, button1 ) ); + lst.append( ButtonInfo( id++, button2 ) ); + lst.append( ButtonInfo( id++, button3 ) ); + lst.append( ButtonInfo( id++, button4 ) ); + return messageBox( SUIT_MessageBox::Warning, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show information message box with four custom buttons. + + Parameters \a button1, \a button2, \a button3 and \a button4 specify + the buttons text. + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button1 first button text + \param button2 second button text + \param button3 third button text + \param button4 fourth button text + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::information( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const QString& button3, const QString& button4, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + lst.append( ButtonInfo( id++, button1 ) ); + lst.append( ButtonInfo( id++, button2 ) ); + lst.append( ButtonInfo( id++, button3 ) ); + lst.append( ButtonInfo( id++, button4 ) ); + return messageBox( SUIT_MessageBox::Information, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show question message box with four custom buttons. + + Parameters \a button1, \a button2, \a button3 and \a button4 specify + the buttons text. + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param button1 first button text + \param button2 second button text + \param button3 third button text + \param button4 fourth button text + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::question( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const QString& button3, const QString& button4, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + lst.append( ButtonInfo( id++, button1 ) ); + lst.append( ButtonInfo( id++, button2 ) ); + lst.append( ButtonInfo( id++, button3 ) ); + lst.append( ButtonInfo( id++, button4 ) ); + return messageBox( SUIT_MessageBox::Question, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show critical message box with arbitrary number of user-specified + buttons. + + List of buttons to be shown is specified via \a buttons parameter. + + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param defaultButton default button + \param escapeButton escape button + \param buttons list of buttons to be shown + \return button used button id +*/ +int SUIT_MessageBox::critical( QWidget* parent, const QString& title, const QString& text, + const QStringList& buttons, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + Q_FOREACH( QString button, buttons ) + lst.append( ButtonInfo( id++, button ) ); + return messageBox( SUIT_MessageBox::Critical, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show warning message box with arbitrary number of user-specified + buttons. + + List of buttons to be shown is specified via \a buttons parameter. + + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param defaultButton default button + \param escapeButton escape button + \param buttons list of buttons to be shown + \return button used button id +*/ +int SUIT_MessageBox::warning( QWidget* parent, const QString& title, const QString& text, + const QStringList& buttons, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + Q_FOREACH( QString button, buttons ) + lst.append( ButtonInfo( id++, button ) ); + return messageBox( SUIT_MessageBox::Warning, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show information message box with arbitrary number of user-specified + buttons. + + List of buttons to be shown is specified via \a buttons parameter. + + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param defaultButton default button + \param escapeButton escape button + \param buttons list of buttons to be shown + \return button used button id +*/ +int SUIT_MessageBox::information( QWidget* parent, const QString& title, const QString& text, + const QStringList& buttons, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + Q_FOREACH( QString button, buttons ) + lst.append( ButtonInfo( id++, button ) ); + return messageBox( SUIT_MessageBox::Information, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show question message box with arbitrary number of user-specified + buttons. + + List of buttons to be shown is specified via \a buttons parameter. + + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param defaultButton default button + \param escapeButton escape button + \param buttons list of buttons to be shown + \return button used button id +*/ +int SUIT_MessageBox::question( QWidget* parent, const QString& title, const QString& text, + const QStringList& buttons, + const int defaultButton, const int escapeButton ) +{ + ButtonInfos lst; + int id = 0; + Q_FOREACH( QString button, buttons ) + lst.append( ButtonInfo( id++, button ) ); + return messageBox( SUIT_MessageBox::Question, parent, title, text, lst, + defaultButton, escapeButton ); +} + +/*! + \brief Show critical message box with arbitrary number of user-specified + buttons. + + The function accepts arbitrary number of parameters. Each parameter starting + from \a btn should be of type const char* to specify the button text. + After the last button parameter and additional 0 (zero) value should be + specified. + + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param defaultButton default button + \param escapeButton escape button + \param btn first button text + \return button used button id +*/ +int SUIT_MessageBox::critical( QWidget* parent, const QString& title, const QString& text, + const int defaultButton, const int escapeButton, + char* btn, ... ) +{ + va_list args; + va_start( args, btn ); + return messageBox( SUIT_MessageBox::Critical, parent, title, text, + messageList( btn, args ), + defaultButton, escapeButton ); +} + +/*! + \brief Show warning message box with arbitrary number of user-specified + buttons. + + The function accepts arbitrary number of parameters. Each parameter starting + from \a btn should be of type const char* to specify the button text. + After the last button parameter and additional 0 (zero) value should be + specified. + + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param defaultButton default button + \param escapeButton escape button + \param btn first button text + \return button used button id +*/ +int SUIT_MessageBox::warning( QWidget* parent, const QString& title, const QString& text, + const int defaultButton, const int escapeButton, + char* btn, ... ) +{ + va_list args; + va_start( args, btn ); + return messageBox( SUIT_MessageBox::Warning, parent, title, text, + messageList( btn, args ), + defaultButton, escapeButton ); +} + +/*! + \brief Show information message box with arbitrary number of user-specified + buttons. + + The function accepts arbitrary number of parameters. Each parameter starting + from \a btn should be of type const char* to specify the button text. + After the last button parameter and additional 0 (zero) value should be + specified. + + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param defaultButton default button + \param escapeButton escape button + \param btn first button text + \return button used button id +*/ +int SUIT_MessageBox::information( QWidget* parent, const QString& title, const QString& text, + const int defaultButton, const int escapeButton, + char* btn, ... ) +{ + va_list args; + va_start( args, btn ); + return messageBox( SUIT_MessageBox::Information, parent, title, text, + messageList( btn, args ), + defaultButton, escapeButton ); +} + +/*! + \brief Show question message box with arbitrary number of user-specified + buttons. + + The function accepts arbitrary number of parameters. Each parameter starting + from \a btn should be of type const char* to specify the button text. + After the last button parameter and additional 0 (zero) value should be + specified. + + The function returns clicked button id. The identifiers for the buttons + are assigned automatically. The first button is identified as 0, the + second one as 1, etc. + + The \a defaultButton parameter allows to specify the button which is assigned + for the \c Return or \c Enter key. Similarly, \a escapeButton parameter + allows specifing the button which is assigned for \c Escape key. + If these parameters are not specified (-1 by default), the first button + is set as default button and the last one is defined as escape button. + + \param parent parent widget + \param title message box title + \param text message box text + \param defaultButton default button + \param escapeButton escape button + \param btn first button text + \return button used button id +*/ +int SUIT_MessageBox::question( QWidget* parent, const QString& title, const QString& text, + const int defaultButton, const int escapeButton, + char* btn, ... ) +{ + va_list args; + va_start( args, btn ); + return messageBox( SUIT_MessageBox::Question, parent, title, text, + messageList( btn, args ), + defaultButton, escapeButton ); +} + +/*! + \brief Parse arbitrary arguments list. + + The last parameter in a sequence should be 0 (zero) value. + + \param txt first argument which starts arbitrary sequence + \param args arguments list from the stack + \return list of buttons infos +*/ +SUIT_MessageBox::ButtonInfos SUIT_MessageBox::messageList( char* txt, va_list& args ) +{ + int i = 0; + ButtonInfos lst; + char* cur = txt; + while ( cur ) + { + lst.append( ButtonInfo( i++, cur ) ); + cur = va_arg( args, char* ); + } + + va_end( args ); + + return lst; +} + +/*! + \brief Create and show the message box. + \param icon icon type + \param parent parent widget + \param title message box title + \param text message box text + \param lst list of buttons infos + \param defaultButton default button + \param escapeButton escape button + \return button used button id +*/ +int SUIT_MessageBox::messageBox( Icon icon, QWidget* parent, + const QString& title, const QString& text, + const ButtonInfos& lst, + const int defaultButton, + const int escapeButton ) +{ + SUIT_MessageBox msgBox( icon, title, text, NoButton, parent ); + + QMap bm; + for ( int i = 0; i < lst.count(); i++ ) + { + int btn = lst[i].id(); + QString txt = lst[i].text(); + ButtonRole role = lst[i].role(); + + QPushButton* pb = msgBox.addButton( txt, role ); + bm.insert( pb, btn ); + + if ( ( defaultButton == -1 && i == 0 ) || btn == defaultButton ) + msgBox.setDefaultButton( pb ); + if ( ( escapeButton == -1 && i == lst.count() - 1 ) || btn == escapeButton ) + msgBox.setEscapeButton( pb ); + } + + SUIT_OverrideCursor cw( parent ? parent->cursor() : Qt::ArrowCursor ); + + int res = msgBox.exec(); + if ( res != -1 ) + res = bm[msgBox.clickedButton()]; + + QApplication::processEvents(); + + return res; +} + diff --git a/src/SUITT/SUIT_MessageBox.h b/src/SUITT/SUIT_MessageBox.h new file mode 100755 index 000000000..b00040ceb --- /dev/null +++ b/src/SUITT/SUIT_MessageBox.h @@ -0,0 +1,167 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 +// + +#ifndef SUIT_MESSAGEBOX_H +#define SUIT_MESSAGEBOX_H + +#include "SUIT.h" + +#include +#include +#include + +class SUIT_EXPORT SUIT_MessageBox : public QMessageBox +{ + Q_OBJECT +public: + // construction/destruction + SUIT_MessageBox( QWidget* = 0 ); + SUIT_MessageBox( Icon, const QString&, const QString&, StandardButtons buttons = NoButton, + QWidget* = 0, Qt::WindowFlags = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint ); + ~SUIT_MessageBox(); + + // customize the standard buttons text + QString buttonText( StandardButton ) const; + void setButtonText( StandardButton, const QString& ); + + // message box with standard buttons + static StandardButton critical( QWidget* parent, const QString& title, const QString& text, + StandardButtons buttons = Ok, StandardButton defaultButton = NoButton ); + static StandardButton warning( QWidget* parent, const QString& title, const QString& text, + StandardButtons buttons = Ok, StandardButton defaultButton = NoButton ); + static StandardButton information( QWidget* parent, const QString& title, const QString& text, + StandardButtons buttons = Ok, StandardButton defaultButton = NoButton ); + static StandardButton question( QWidget* parent, const QString& title, const QString& text, + StandardButtons buttons = Ok, StandardButton defaultButton = NoButton ); + + // message boxes with one custom button + static int critical( QWidget* parent, const QString& title, const QString& text, + const QString& button ); + static int warning( QWidget* parent, const QString& title, const QString& text, + const QString& button ); + static int information( QWidget* parent, const QString& title, const QString& text, + const QString& button ); + static int question( QWidget* parent, const QString& title, const QString& text, + const QString& button ); + + // message boxes with two custom buttons + static int critical( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const int defaultButton = -1, const int escapeButton = -1 ); + static int warning( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const int defaultButton = -1, const int escapeButton = -1 ); + static int information( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const int defaultButton = -1, const int escapeButton = -1 ); + static int question( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const int defaultButton = -1, const int escapeButton = -1 ); + + // message boxes with three custom buttons + static int critical( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, const QString& button3, + const int defaultButton = -1, const int escapeButton = -1 ); + static int warning( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, const QString& button3, + const int defaultButton = -1, const int escapeButton = -1 ); + static int information( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, const QString& button3, + const int defaultButton = -1, const int escapeButton = -1 ); + static int question( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, const QString& button3, + const int defaultButton = -1, const int escapeButton = -1 ); + + // message boxes with four custom buttons + static int critical( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const QString& button3, const QString& button4, + const int defaultButton = -1, const int escapeButton = -1 ); + static int warning( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const QString& button3, const QString& button4, + const int defaultButton = -1, const int escapeButton = -1 ); + static int information( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const QString& button3, const QString& button4, + const int defaultButton = -1, const int escapeButton = -1 ); + static int question( QWidget* parent, const QString& title, const QString& text, + const QString& button1, const QString& button2, + const QString& button3, const QString& button4, + const int defaultButton = -1, const int escapeButton = -1 ); + + // message boxes with arbitrary number of buttons + static int critical( QWidget* parent, const QString& title, const QString& text, + const QStringList& buttons, + const int defaultButton = -1, const int escapeButton = -1 ); + static int warning( QWidget* parent, const QString& title, const QString& text, + const QStringList& buttons, + const int defaultButton = -1, const int escapeButton = -1 ); + static int information( QWidget* parent, const QString& title, const QString& text, + const QStringList& buttons, + const int defaultButton = -1, const int escapeButton = -1 ); + static int question( QWidget* parent, const QString& title, const QString& text, + const QStringList& buttons, + const int defaultButton = -1, const int escapeButton = -1 ); + + static int critical( QWidget* parent, const QString& title, const QString& text, + const int defaultButton, const int escapeButton, + char*, ... ); + static int warning( QWidget* parent, const QString& title, const QString& text, + const int defaultButton, const int escapeButton, + char*, ... ); + static int information( QWidget* parent, const QString& title, const QString& text, + const int defaultButton, const int escapeButton, + char*, ... ); + static int question( QWidget* parent, const QString& title, const QString& text, + const int defaultButton, const int escapeButton, + char*, ... ); + +private: + class ButtonInfo + { + public: + ButtonInfo( const int id, + const QString& text, + const ButtonRole role = ActionRole ) + : myId( id ), myText( text ), myRole( role ) {} + int id() const { return myId; } + QString text() const { return myText; } + ButtonRole role() const { return myRole; } + private: + int myId; //!< button id + QString myText; //!< button text + ButtonRole myRole; //!< button role + }; + + typedef QList ButtonInfos; + +private: + static int messageBox( SUIT_MessageBox::Icon icon, QWidget* parent, + const QString& title, const QString& text, + const ButtonInfos& lst, + const int defaultButton = -1, + const int escapeButton = -1 ); + static ButtonInfos messageList( char*, va_list& ); +}; + +#endif diff --git a/src/SUITT/SUIT_OverrideCursor.cxx b/src/SUITT/SUIT_OverrideCursor.cxx new file mode 100755 index 000000000..aaf50903a --- /dev/null +++ b/src/SUITT/SUIT_OverrideCursor.cxx @@ -0,0 +1,74 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 +// + +#include "SUIT_OverrideCursor.h" + +#include + +/*!Constructor. Initialize wait cursor.*/ +SUIT_OverrideCursor::SUIT_OverrideCursor() +{ + QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) ); +} + +/*!Copy constructor.*/ +SUIT_OverrideCursor::SUIT_OverrideCursor( const QCursor& cursor ) +{ + QApplication::setOverrideCursor( cursor ); +} + +/*!Destructor. restoring override cursor.*/ +SUIT_OverrideCursor::~SUIT_OverrideCursor() +{ + QApplication::restoreOverrideCursor(); +} + +/*! Check cursors is empty */ +bool SUIT_OverrideCursor::isActive() const +{ + return myCursors.isEmpty(); +} + +/*!Suspend cursors.*/ +void SUIT_OverrideCursor::suspend() +{ + if ( !isActive() ) + return; + + while ( QApplication::overrideCursor() ) + { + myCursors.prepend( *QApplication::overrideCursor() ); + QApplication::restoreOverrideCursor(); + } +} + +/*!Resume cursors.*/ +void SUIT_OverrideCursor::resume() +{ + if ( isActive() ) + return; + + for ( QList::const_iterator it = myCursors.begin(); it != myCursors.end(); ++it ) + QApplication::setOverrideCursor( *it ); + + myCursors.clear(); +} diff --git a/src/SUITT/SUIT_OverrideCursor.h b/src/SUITT/SUIT_OverrideCursor.h new file mode 100755 index 000000000..d5f4d5b70 --- /dev/null +++ b/src/SUITT/SUIT_OverrideCursor.h @@ -0,0 +1,47 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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, or (at your option) any later version. +// +// 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 +// + +#ifndef SUIT_OVERRIDECURSOR_H +#define SUIT_OVERRIDECURSOR_H + +#include "SUIT.h" + +#include +#include + +/*! \brief Class used for management cursors.*/ +class SUIT_EXPORT SUIT_OverrideCursor +{ +public: + SUIT_OverrideCursor(); + SUIT_OverrideCursor( const QCursor& ); + virtual ~SUIT_OverrideCursor(); + + bool isActive() const; + void suspend(); + void resume(); + +private: + QList myCursors; +}; + +#endif diff --git a/src/SalomeApp/CMakeLists.txt b/src/SalomeApp/CMakeLists.txt index 8c29843b4..5e8ff2e0b 100755 --- a/src/SalomeApp/CMakeLists.txt +++ b/src/SalomeApp/CMakeLists.txt @@ -57,6 +57,7 @@ IF(SALOME_USE_PYCONSOLE) INCLUDE_DIRECTORIES( ${PYTHON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/src/PyConsole + ${PROJECT_SOURCE_DIR}/src/PyConsoleBase ${PROJECT_SOURCE_DIR}/src/PyInterp ) ENDIF()