From e5f3e29f0ae622f71877e3d296775d4063c185f1 Mon Sep 17 00:00:00 2001 From: Anthony Geay Date: Mon, 22 Feb 2016 14:44:49 +0100 Subject: [PATCH] Split PyConsole in order to be used easily outside GUI of SALOME. --- CMakeLists.txt | 2 +- SalomeGUIConfig.cmake.in | 1 + adm_local/cmake_files/FindGUI.cmake | 1 + .../cmake_files/deprecated/FindGUI.cmake | 1 + src/CMakeLists.txt | 1 + src/LightApp/CMakeLists.txt | 1 + src/LightApp/resources/LightApp.xml | 1 + src/PyConsole/CMakeLists.txt | 19 +- src/PyConsole/PyConsole_Console.cxx | 318 +---- src/PyConsole/PyConsole_Console.h | 62 +- src/PyConsole/PyConsole_Editor.cxx | 1088 +------------- src/PyConsole/PyConsole_Editor.h | 66 +- src/PyConsole/PyConsole_EnhEditor.cxx | 450 +----- src/PyConsole/PyConsole_EnhEditor.h | 58 +- src/PyConsole/resources/PyConsole_msg_en.ts | 35 - src/PyConsole/resources/PyConsole_msg_fr.ts | 35 - src/PyConsole/resources/PyConsole_msg_ja.ts | 35 - src/PyConsoleBase/CMakeLists.txt | 97 ++ src/PyConsoleBase/PyConsoleBase.h | 48 + src/PyConsoleBase/PyConsole_ConsoleBase.cxx | 413 ++++++ src/PyConsoleBase/PyConsole_ConsoleBase.h | 127 ++ src/PyConsoleBase/PyConsole_EditorBase.cxx | 1260 +++++++++++++++++ src/PyConsoleBase/PyConsole_EditorBase.h | 110 ++ src/PyConsoleBase/PyConsole_EnhEditorBase.cxx | 491 +++++++ src/PyConsoleBase/PyConsole_EnhEditorBase.h | 95 ++ .../PyConsole_EnhInterp.cxx | 2 +- .../PyConsole_EnhInterp.h | 4 +- .../PyConsole_Event.cxx | 0 .../PyConsole_Event.h | 2 +- .../PyConsole_Interp.cxx | 0 .../PyConsole_Interp.h | 4 +- .../PyConsole_Request.cxx | 0 .../PyConsole_Request.h | 0 .../resources/PyConsoleBase_msg_en.ts | 70 + .../resources/PyConsoleBase_msg_fr.ts | 70 + .../resources/PyConsoleBase_msg_ja.ts | 70 + src/Qtx/Qtx.cxx | 11 + src/Qtx/Qtx.h | 3 + src/SALOME_PYQT/SalomePyQt/CMakeLists.txt | 1 + src/SUIT/SUIT_Tools.cxx | 11 - src/SUIT/SUIT_Tools.h | 1 - src/SalomeApp/CMakeLists.txt | 1 + 42 files changed, 2964 insertions(+), 2101 deletions(-) mode change 100755 => 100644 src/PyConsole/resources/PyConsole_msg_fr.ts create mode 100755 src/PyConsoleBase/CMakeLists.txt create mode 100644 src/PyConsoleBase/PyConsoleBase.h create mode 100644 src/PyConsoleBase/PyConsole_ConsoleBase.cxx create mode 100644 src/PyConsoleBase/PyConsole_ConsoleBase.h create mode 100644 src/PyConsoleBase/PyConsole_EditorBase.cxx create mode 100644 src/PyConsoleBase/PyConsole_EditorBase.h create mode 100644 src/PyConsoleBase/PyConsole_EnhEditorBase.cxx create mode 100644 src/PyConsoleBase/PyConsole_EnhEditorBase.h rename src/{PyConsole => PyConsoleBase}/PyConsole_EnhInterp.cxx (99%) rename src/{PyConsole => PyConsoleBase}/PyConsole_EnhInterp.h (95%) rename src/{PyConsole => PyConsoleBase}/PyConsole_Event.cxx (100%) rename src/{PyConsole => PyConsoleBase}/PyConsole_Event.h (98%) rename src/{PyConsole => PyConsoleBase}/PyConsole_Interp.cxx (100%) rename src/{PyConsole => PyConsoleBase}/PyConsole_Interp.h (94%) rename src/{PyConsole => PyConsoleBase}/PyConsole_Request.cxx (100%) rename src/{PyConsole => PyConsoleBase}/PyConsole_Request.h (100%) create mode 100644 src/PyConsoleBase/resources/PyConsoleBase_msg_en.ts create mode 100644 src/PyConsoleBase/resources/PyConsoleBase_msg_fr.ts create mode 100644 src/PyConsoleBase/resources/PyConsoleBase_msg_ja.ts diff --git a/CMakeLists.txt b/CMakeLists.txt index ceb25ef0a..ea19bc0a7 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -389,7 +389,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/adm_local/cmake_files/FindGUI.cmake b/adm_local/cmake_files/FindGUI.cmake index 7a185c84a..ef6ffa9c4 100644 --- a/adm_local/cmake_files/FindGUI.cmake +++ b/adm_local/cmake_files/FindGUI.cmake @@ -36,6 +36,7 @@ FIND_LIBRARY(OCCViewer OCCViewer ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(OpenGLUtils OpenGLUtils ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(Plot2d Plot2d ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(PyConsole PyConsole ${GUI_ROOT_DIR}/lib/salome) +FIND_LIBRARY(PyConsoleBase PyConsoleBase ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(PyInterp PyInterp ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(QDS QDS ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(qtx qtx ${GUI_ROOT_DIR}/lib/salome) diff --git a/adm_local/cmake_files/deprecated/FindGUI.cmake b/adm_local/cmake_files/deprecated/FindGUI.cmake index cf4277198..ff29e7b16 100644 --- a/adm_local/cmake_files/deprecated/FindGUI.cmake +++ b/adm_local/cmake_files/deprecated/FindGUI.cmake @@ -32,6 +32,7 @@ FIND_LIBRARY(OCCViewer OCCViewer ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(OpenGLUtils OpenGLUtils ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(Plot2d Plot2d ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(PyConsole PyConsole ${GUI_ROOT_DIR}/lib/salome) +FIND_LIBRARY(PyConsoleBase PyConsoleBase ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(PyInterp PyInterp ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(QDS QDS ${GUI_ROOT_DIR}/lib/salome) FIND_LIBRARY(qtx qtx ${GUI_ROOT_DIR}/lib/salome) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6435a1ccf..ba919fa87 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -124,6 +124,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/LightApp/resources/LightApp.xml b/src/LightApp/resources/LightApp.xml index 8489c9f5a..892c453e2 100644 --- a/src/LightApp/resources/LightApp.xml +++ b/src/LightApp/resources/LightApp.xml @@ -73,6 +73,7 @@ + diff --git a/src/PyConsole/CMakeLists.txt b/src/PyConsole/CMakeLists.txt index 6dc393da5..0bf638f98 100755 --- a/src/PyConsole/CMakeLists.txt +++ b/src/PyConsole/CMakeLists.txt @@ -25,6 +25,8 @@ INCLUDE(UseQtExt) 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 @@ -35,7 +37,7 @@ 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 --- @@ -49,10 +51,6 @@ SET(_moc_HEADERS # 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 @@ -60,27 +58,23 @@ SET(PyConsole_HEADERS ${_moc_HEADERS} ${_other_HEADERS}) # --- resources --- +# --- sources --- # 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_Console.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 @@ -94,4 +88,3 @@ INSTALL(TARGETS PyConsole EXPORT ${PROJECT_NAME}TargetGroup DESTINATION ${SALOME 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 e684bf1e6..43e9cb6a4 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" @@ -42,6 +43,12 @@ #include #include +PyConsole_EditorBase *PyConsole_Console::PyConsole_Interp_Creator::createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const +{ return new PyConsole_Editor(interp,console); } + +PyConsole_Interp *PyConsole_Console::PyConsole_Interp_Creator::createInterp( ) const +{ return new PyConsole_Interp; } + /*! \brief Constructor. @@ -50,34 +57,19 @@ \param interp python interpreter */ PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* interp ) -: QWidget( parent ) + : PyConsole_ConsoleBase( parent, interp, 0 ) { - 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(); + PyConsole_Interp_Creator crea; + defaultConstructor(interp,crea); } /** * Protected constructor. */ -PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* /*i*/, PyConsole_Editor* e ) - : QWidget (parent), myEditor(e) -{} +PyConsole_Console::PyConsole_Console( QWidget* parent, PyConsole_Interp* i, PyConsole_Editor* e ) + : PyConsole_ConsoleBase(parent,i,e) +{ +} /*! \brief Destructor. @@ -88,128 +80,16 @@ PyConsole_Console::~PyConsole_Console() { } -PyConsole_Interp* PyConsole_Console::getInterp() const -{ - return myEditor ? myEditor->getInterp() : 0; -} - /*! - \brief Execute python command in the interpreter. - \param command string with command and arguments -*/ -void PyConsole_Console::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_Console::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_Console::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_Console::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_Console::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_Console::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_Console::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_Console::setIsShowBanner( const bool on ) -{ - myEditor->setIsShowBanner( on ); -} + \brief Create the context popup menu. -/*! - \brief Change the python console's font. - \param f new font -*/ -void PyConsole_Console::setFont( const QFont& f ) -{ - if( myEditor ) - myEditor->setFont( f ); -} + Fill in the popup menu with the commands. -/*! - \brief Get python console font. - \return current python console's font + \param menu context popup menu */ -QFont PyConsole_Console::font() const +void PyConsole_Console::contextMenuPopup( QMenu *menu ) { - QFont res; - if( myEditor ) - res = myEditor->font(); - return res; + PyConsole_ConsoleBase::contextMenuPopup(menu); } /*! @@ -231,143 +111,11 @@ bool PyConsole_Console::eventFilter( QObject* o, QEvent* e ) return QWidget::eventFilter( o, e ); } -/*! - \brief Create the context popup menu. +PyConsole_EditorBase *PyConsole_EnhConsole::PyConsole_Interp_EnhCreator::createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const +{ return new PyConsole_EnhEditor(interp,console); } - Fill in the popup menu with the commands. - - \param menu context popup menu -*/ -void PyConsole_Console::contextMenuPopup( QMenu* menu ) -{ - if ( myEditor->isReadOnly() ) - return; - - menu->addAction( myActions[CopyId] ); - menu->addAction( myActions[PasteId] ); - menu->addAction( myActions[ClearId] ); - menu->addSeparator(); - menu->addAction( myActions[SelectAllId] ); - menu->addSeparator(); - menu->addAction( myActions[DumpCommandsId] ); - if ( !myEditor->isLogging() ) - menu->addAction( myActions[StartLogId] ); - else - menu->addAction( myActions[StopLogId] ); - - Qtx::simplifySeparators( menu ); - - updateActions(); -} - -/*! - \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_Console::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_Console::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_Console::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_Console::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_Console::startLog( const QString& fileName ) -{ - myEditor->startLog( fileName ); -} - -/*! - \brief Stop python trace logging -*/ -void PyConsole_Console::stopLog() -{ - myEditor->stopLog(); -} +PyConsole_Interp *PyConsole_EnhConsole::PyConsole_Interp_EnhCreator::createInterp( ) const +{ return new PyConsole_EnhInterp; } /** * Similar to constructor of the base class but using enhanced objects. @@ -378,22 +126,6 @@ void PyConsole_Console::stopLog() PyConsole_EnhConsole::PyConsole_EnhConsole( QWidget* parent, PyConsole_Interp* interp ) : PyConsole_Console( 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(); + PyConsole_Interp_EnhCreator crea; + defaultConstructor(interp,crea); } diff --git a/src/PyConsole/PyConsole_Console.h b/src/PyConsole/PyConsole_Console.h index ce8f36467..1aa68a4d6 100644 --- a/src/PyConsole/PyConsole_Console.h +++ b/src/PyConsole/PyConsole_Console.h @@ -27,7 +27,7 @@ #define PYCONSOLE_CONSOLE_H #include "PyConsole.h" - +#include #include #include #include @@ -35,65 +35,26 @@ class PyConsole_Interp; class PyConsole_Editor; -class PYCONSOLE_EXPORT PyConsole_Console : public QWidget, public SUIT_PopupClient +class PYCONSOLE_EXPORT PyConsole_Console : public PyConsole_ConsoleBase, public SUIT_PopupClient { Q_OBJECT - public: - //! Context popup menu actions flags - enum + + struct PyConsole_Interp_Creator : public PyConsole_Interp_CreatorBase { - 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 + virtual PyConsole_EditorBase *createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const; + virtual PyConsole_Interp *createInterp( ) const; }; public: PyConsole_Console( QWidget* parent, PyConsole_Interp* interp = 0 ); virtual ~PyConsole_Console(); - - //! \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& ); - - virtual bool eventFilter( QObject*, QEvent* ); - //! \brief Get popup client symbolic name virtual QString popupClientType() const { return QString( "PyConsole" ); } virtual void contextMenuPopup( QMenu* ); - - void setMenuActions( const int ); - int menuActions() const; - - void startLog( const QString& ); - void stopLog(); - + virtual bool eventFilter( QObject*, QEvent* ); protected: - void createActions(); - void updateActions(); - PyConsole_Console( QWidget* parent, PyConsole_Interp*, PyConsole_Editor*); - - PyConsole_Editor* myEditor; //!< python console editor widget - QMap myActions; //!< menu actions list }; /** @@ -101,9 +62,16 @@ protected: * Similar to PyConsole_Console except that an enhanced interpreter and enhanced editor * are encapsulated. */ -class PYCONSOLE_EXPORT PyConsole_EnhConsole: public PyConsole_Console +class PYCONSOLE_EXPORT PyConsole_EnhConsole : public PyConsole_Console { Q_OBJECT +public: + + struct PyConsole_Interp_EnhCreator : public PyConsole_Interp_CreatorBase + { + virtual PyConsole_EditorBase *createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const; + virtual PyConsole_Interp *createInterp( ) const; + }; public: PyConsole_EnhConsole( QWidget* parent, PyConsole_Interp* interp = 0 ); diff --git a/src/PyConsole/PyConsole_Editor.cxx b/src/PyConsole/PyConsole_Editor.cxx index 2311b58d4..2371eecae 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,991 +151,43 @@ 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 ) +void PyConsole_Editor::StaticDumpSlot(PyConsole_EditorBase *base) { - 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. + QStringList aFilters; + aFilters.append( tr( "PYTHON_FILES_FILTER" ) ); - 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 ); + QString fileName = SUIT_FileDlg::getFileName( base, QString(), + aFilters, tr( "TOT_DUMP_PYCOMMANDS" ), + false, true, new DumpCommandsFileValidator( base ) ); + base->dumpImpl(fileName); } /*! \brief "Dump commands" operation. */ -void PyConsole_Editor::dump() +void PyConsole_Editor::dumpSlot() { - QStringList aFilters; - aFilters.append( tr( "PYTHON_FILES_FILTER" ) ); - - 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; istartLogImpl( fileName ) ) { break; } else { - SUIT_MessageBox::critical( this, + SUIT_MessageBox::critical( base, QObject::tr("ERR_ERROR"), QObject::tr("ERR_FILE_NOT_WRITABLE") ); } @@ -1206,46 +199,9 @@ void PyConsole_Editor::startLog() } /*! - \brief Start python trace logging - \param fileName the path to the log file - \sa stopLog() - */ -bool PyConsole_Editor::startLog( const QString& fileName ) -{ - bool ok = false; - if ( !fileName.isEmpty() ) { - QFile file( fileName ); - if ( file.open( QFile::WriteOnly ) ) { - file.close(); - myLogFile = fileName; - ok = true; - } - } - return ok; -} - -/*! - \brief "Stop log" operation. - \sa startLog() - */ -void PyConsole_Editor::stopLog() -{ - myLogFile = QString(); -} - -/*! - \brief Put string to the log file + \brief "Start log" operation. */ -void PyConsole_Editor::putLog( const QString& s ) +void PyConsole_Editor::startLogSlot() { - if ( !myLogFile.isEmpty() ) { - QFile file( myLogFile ); - if ( !file.open( QFile::Append ) ) - return; - - QTextStream out (&file); - out << s; - - file.close(); - } + StaticStartLogSlot(this); } diff --git a/src/PyConsole/PyConsole_Editor.h b/src/PyConsole/PyConsole_Editor.h index 5dbc8d15e..a8821992e 100644 --- a/src/PyConsole/PyConsole_Editor.h +++ b/src/PyConsole/PyConsole_Editor.h @@ -28,6 +28,7 @@ #define PYCONSOLE_EDITOR_H #include "PyConsole.h" +#include "PyConsole_EditorBase.h" #include @@ -35,72 +36,17 @@ 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& ); - + static void StaticDumpSlot(PyConsole_EditorBase *base); + static void StaticStartLogSlot(PyConsole_EditorBase *base); 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 + virtual void dumpSlot(); + virtual void startLogSlot(); }; #endif // PYCONSOLE_EDITOR_H diff --git a/src/PyConsole/PyConsole_EnhEditor.cxx b/src/PyConsole/PyConsole_EnhEditor.cxx index d4cf274a2..dd4619b1e 100644 --- a/src/PyConsole/PyConsole_EnhEditor.cxx +++ b/src/PyConsole/PyConsole_EnhEditor.cxx @@ -30,14 +30,7 @@ #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])); +#include "PyConsole_Editor.h" /** * Constructor. @@ -45,447 +38,16 @@ 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 + PyConsole_EnhEditorBase(interp, parent) { - 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 ) +void PyConsole_EnhEditor::dumpSlot() { - 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; - } - } + PyConsole_Editor::StaticDumpSlot(this); } -/** - * Extract the common leading part of all strings in matches. - * @param matches - * @param result - */ -void PyConsole_EnhEditor::extractCommon(const QStringList& matches, QString& result) const +void PyConsole_EnhEditor::startLogSlot() { - 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) -{ - // 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(); - } + PyConsole_Editor::StaticStartLogSlot(this); } diff --git a/src/PyConsole/PyConsole_EnhEditor.h b/src/PyConsole/PyConsole_EnhEditor.h index f505066d3..f14402e3b 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,15 @@ /** * 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; - + virtual void dumpSlot(); + virtual void startLogSlot(); }; #endif /* PYCONSOLE_ENHEDITOR_H_ */ diff --git a/src/PyConsole/resources/PyConsole_msg_en.ts b/src/PyConsole/resources/PyConsole_msg_en.ts index 3a3b89bd9..a2d4aae39 100644 --- a/src/PyConsole/resources/PyConsole_msg_en.ts +++ b/src/PyConsole/resources/PyConsole_msg_en.ts @@ -1,41 +1,6 @@ - - PyConsole_Console - - - 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 diff --git a/src/PyConsole/resources/PyConsole_msg_fr.ts b/src/PyConsole/resources/PyConsole_msg_fr.ts old mode 100755 new mode 100644 index b4f16bd0c..905d68f7c --- a/src/PyConsole/resources/PyConsole_msg_fr.ts +++ b/src/PyConsole/resources/PyConsole_msg_fr.ts @@ -1,41 +1,6 @@ - - PyConsole_Console - - - 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 diff --git a/src/PyConsole/resources/PyConsole_msg_ja.ts b/src/PyConsole/resources/PyConsole_msg_ja.ts index 85e2cc732..dd8b48e3e 100644 --- a/src/PyConsole/resources/PyConsole_msg_ja.ts +++ b/src/PyConsole/resources/PyConsole_msg_ja.ts @@ -1,41 +1,6 @@ - - PyConsole_Console - - - 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 diff --git a/src/PyConsoleBase/CMakeLists.txt b/src/PyConsoleBase/CMakeLists.txt new file mode 100755 index 000000000..3e06a3fee --- /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/Qtx + ${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 qtx) + +# --- 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_EnhInterp.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/PyConsoleBase_msg_en.ts + resources/PyConsoleBase_msg_fr.ts + resources/PyConsoleBase_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..306dd998c --- /dev/null +++ b/src/PyConsoleBase/PyConsole_ConsoleBase.cxx @@ -0,0 +1,413 @@ +// 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 "Qtx.h" + +#include +#include +#include +#include +#include +#include +#include + +PyConsole_EditorBase *PyConsole_ConsoleBase::PyConsole_Interp_CreatorBase::createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const +{ return new PyConsole_EditorBase(interp,console); } + +PyConsole_Interp *PyConsole_ConsoleBase::PyConsole_Interp_CreatorBase::createInterp( ) const +{ return new PyConsole_Interp; } + +/*! + \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_ConsoleBase::PyConsole_Interp_CreatorBase crea; + defaultConstructor(interp,crea); +} + +/*! + * MUST BE NON VIRTUAL ! (called from constructor !!!!) + */ +void PyConsole_ConsoleBase::defaultConstructor( PyConsole_Interp* interp, const PyConsole_Interp_CreatorBase& crea ) +{ + PyConsole_Interp *anInterp = interp ? interp : crea.createInterp(); + + // initialize Python interpretator + anInterp->initialize(); + + // create editor console + QVBoxLayout* lay = new QVBoxLayout( this ); + lay->setMargin( 0 ); + myEditor = crea.createEditor( 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(); +} + +/*! + \brief Create the context popup menu. + + Fill in the popup menu with the commands. + + \param menu context popup menu +*/ +void PyConsole_ConsoleBase::contextMenuPopup( QMenu *menu ) +{ + if ( myEditor->isReadOnly() ) + return; + + menu->addAction( myActions[CopyId] ); + menu->addAction( myActions[PasteId] ); + menu->addAction( myActions[ClearId] ); + menu->addSeparator(); + menu->addAction( myActions[SelectAllId] ); + menu->addSeparator(); + menu->addAction( myActions[DumpCommandsId] ); + if ( !myEditor->isLogging() ) + menu->addAction( myActions[StartLogId] ); + else + menu->addAction( myActions[StopLogId] ); + + Qtx::simplifySeparators( menu ); + + updateActions(); +} + +PyConsole_EditorBase *PyConsole_EnhConsoleBase::PyConsole_Interp_EnhCreatorBase::createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const +{ return new PyConsole_EnhEditorBase(interp,console); } + +PyConsole_Interp *PyConsole_EnhConsoleBase::PyConsole_Interp_EnhCreatorBase::createInterp( ) const +{ return new PyConsole_EnhInterp; } + +/** + * 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_EnhCreatorBase crea; + defaultConstructor(interp,crea); +} + +/*! + \brief Event handler. + + Handles context menu request event. + + \param o object + \param e event + \return True if the event is processed and further processing should be stopped +*/ +bool PyConsole_EnhConsoleBase::eventFilter( QObject* o, QEvent* e ) +{ + if ( o == myEditor->viewport() && e->type() == QEvent::ContextMenu ) + { + contextMenuRequest( (QContextMenuEvent*)e ); + return true; + } + return QWidget::eventFilter( o, e ); +} + +void PyConsole_EnhConsoleBase::contextMenuRequest( QContextMenuEvent * e ) +{ + QMenu *menu(new QMenu(this)); + contextMenuPopup(menu); + menu->move(e->globalPos()); + menu->show(); +} diff --git a/src/PyConsoleBase/PyConsole_ConsoleBase.h b/src/PyConsoleBase/PyConsole_ConsoleBase.h new file mode 100644 index 000000000..b0a07273d --- /dev/null +++ b/src/PyConsoleBase/PyConsole_ConsoleBase.h @@ -0,0 +1,127 @@ +// 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 +#include + +class PyConsole_Interp; +class PyConsole_EditorBase; + +class PYCONSOLEBASE_EXPORT PyConsole_ConsoleBase : public QWidget +{ + Q_OBJECT + +public: + + struct PyConsole_Interp_CreatorBase + { + virtual PyConsole_EditorBase *createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const; + virtual PyConsole_Interp *createInterp( ) const; + }; + +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(); + + virtual void contextMenuPopup( QMenu* ); + +protected: + void createActions(); + void updateActions(); + //! MUST BE NON VIRTUAL ! (called from constructor !!!!) + void defaultConstructor( PyConsole_Interp* interp, const PyConsole_Interp_CreatorBase& crea ); + 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: + + struct PyConsole_Interp_EnhCreatorBase : public PyConsole_Interp_CreatorBase + { + virtual PyConsole_EditorBase *createEditor( PyConsole_Interp *interp, PyConsole_ConsoleBase *console ) const; + virtual PyConsole_Interp *createInterp( ) const; + }; + +public: + PyConsole_EnhConsoleBase( QWidget* parent, PyConsole_Interp* interp = 0 ); + virtual ~PyConsole_EnhConsoleBase() {} + virtual bool eventFilter( QObject * o, QEvent * e ); + virtual void contextMenuRequest( QContextMenuEvent * e ) ; +}; + +#endif // PYCONSOLE_CONSOLEBASE_H diff --git a/src/PyConsoleBase/PyConsole_EditorBase.cxx b/src/PyConsoleBase/PyConsole_EditorBase.cxx new file mode 100644 index 000000000..a8bdd058f --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EditorBase.cxx @@ -0,0 +1,1260 @@ +// 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 + +#include +#include +#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 ( Qtx::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; idump( fileName ); + else + QMessageBox::warning(this,tr("WARNING"),tr("Python file has not been written")); +} + +/*! + \brief Start python trace logging + \param fileName the path to the log file + \sa stopLog() + */ +bool PyConsole_EditorBase::startLogImpl( const QString& fileName ) +{ + bool ok = false; + if ( !fileName.isEmpty() ) { + QFile file( fileName ); + if ( file.open( QFile::WriteOnly ) ) { + file.close(); + myLogFile = fileName; + ok = true; + } + } + return ok; +} + + +/*! + \brief Start python trace logging + \param fileName the path to the log file + \sa stopLog() + */ +bool PyConsole_EditorBase::startLog( const QString& fileName ) +{ + return startLogImpl(fileName); +} + +/*! + \brief Start python trace logging + \sa stopLog() + */ +void PyConsole_EditorBase::startLog() +{ + startLogSlot(); +} + +void PyConsole_EditorBase::startLogSlot() +{ + while (1) + { + QString fileName(QFileDialog::getSaveFileName(this,tr("Choose python file where to store log"),QString(),tr("Log files ext (*.log *.txt)"))); + if ( !fileName.isEmpty() ) + { + if ( startLogImpl( fileName ) ) + break; + else + QMessageBox::warning(this,tr("WARNING"),tr("Log file is not writable")); + } + else + break; + } +} + +/*! + \brief "Stop log" operation. + \sa startLog() + */ +void PyConsole_EditorBase::stopLog() +{ + myLogFile = QString(); +} + +/*! + \brief Put string to the log file + */ +void PyConsole_EditorBase::putLog( const QString& s ) +{ + if ( !myLogFile.isEmpty() ) { + QFile file( myLogFile ); + if ( !file.open( QFile::Append ) ) + return; + + QTextStream out (&file); + out << s; + + file.close(); + } +} diff --git a/src/PyConsoleBase/PyConsole_EditorBase.h b/src/PyConsoleBase/PyConsole_EditorBase.h new file mode 100644 index 000000000..c875e7684 --- /dev/null +++ b/src/PyConsoleBase/PyConsole_EditorBase.h @@ -0,0 +1,110 @@ +// 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.h +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) +// +#ifndef PYCONSOLE_EDITORBASE_H +#define PYCONSOLE_EDITORBASE_H + +#include "PyConsoleBase.h" + +#include + +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); + void dump(); + void startLog(); + 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 void dumpSlot(); + virtual void startLogSlot(); + + 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/PyConsole/PyConsole_EnhInterp.cxx b/src/PyConsoleBase/PyConsole_EnhInterp.cxx similarity index 99% rename from src/PyConsole/PyConsole_EnhInterp.cxx rename to src/PyConsoleBase/PyConsole_EnhInterp.cxx index bb3230466..eab44af6f 100644 --- a/src/PyConsole/PyConsole_EnhInterp.cxx +++ b/src/PyConsoleBase/PyConsole_EnhInterp.cxx @@ -20,7 +20,7 @@ // Created on: 4 avr. 2013 -#include "PyConsole.h" +#include "PyConsoleBase.h" #include "PyConsole_EnhInterp.h" diff --git a/src/PyConsole/PyConsole_EnhInterp.h b/src/PyConsoleBase/PyConsole_EnhInterp.h similarity index 95% rename from src/PyConsole/PyConsole_EnhInterp.h rename to src/PyConsoleBase/PyConsole_EnhInterp.h index b8fb04a77..57a483b34 100644 --- a/src/PyConsole/PyConsole_EnhInterp.h +++ b/src/PyConsoleBase/PyConsole_EnhInterp.h @@ -23,7 +23,7 @@ #ifndef PYCONSOLE_ENHINTERP_H_ #define PYCONSOLE_ENHINTERP_H_ -#include "PyConsole.h" +#include "PyConsoleBase.h" #include #include "PyConsole_Interp.h" @@ -35,7 +35,7 @@ * 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 +class PYCONSOLEBASE_EXPORT PyConsole_EnhInterp: public PyConsole_Interp { public: PyConsole_EnhInterp(); diff --git a/src/PyConsole/PyConsole_Event.cxx b/src/PyConsoleBase/PyConsole_Event.cxx similarity index 100% rename from src/PyConsole/PyConsole_Event.cxx rename to src/PyConsoleBase/PyConsole_Event.cxx diff --git a/src/PyConsole/PyConsole_Event.h b/src/PyConsoleBase/PyConsole_Event.h similarity index 98% rename from src/PyConsole/PyConsole_Event.h rename to src/PyConsoleBase/PyConsole_Event.h index 6ec1d51bb..28db82430 100644 --- a/src/PyConsole/PyConsole_Event.h +++ b/src/PyConsoleBase/PyConsole_Event.h @@ -24,7 +24,7 @@ #ifndef PYCONSOLE_EVENT_H #define PYCONSOLE_EVENT_H -#include "PyConsole.h" +#include "PyConsoleBase.h" #include #include diff --git a/src/PyConsole/PyConsole_Interp.cxx b/src/PyConsoleBase/PyConsole_Interp.cxx similarity index 100% rename from src/PyConsole/PyConsole_Interp.cxx rename to src/PyConsoleBase/PyConsole_Interp.cxx diff --git a/src/PyConsole/PyConsole_Interp.h b/src/PyConsoleBase/PyConsole_Interp.h similarity index 94% rename from src/PyConsole/PyConsole_Interp.h rename to src/PyConsoleBase/PyConsole_Interp.h index 1aac73683..34f251d26 100644 --- a/src/PyConsole/PyConsole_Interp.h +++ b/src/PyConsoleBase/PyConsole_Interp.h @@ -25,12 +25,12 @@ #ifndef PYCONSOLE_INTERP_H #define PYCONSOLE_INTERP_H -#include "PyConsole.h" +#include "PyConsoleBase.h" #include "PyInterp_Interp.h" /// !!! WARNING !!! THIS INCLUDE MUST BE VERY FIRST !!! #include -class PYCONSOLE_EXPORT PyConsole_Interp : public PyInterp_Interp +class PYCONSOLEBASE_EXPORT PyConsole_Interp : public PyInterp_Interp { public: PyConsole_Interp(); diff --git a/src/PyConsole/PyConsole_Request.cxx b/src/PyConsoleBase/PyConsole_Request.cxx similarity index 100% rename from src/PyConsole/PyConsole_Request.cxx rename to src/PyConsoleBase/PyConsole_Request.cxx diff --git a/src/PyConsole/PyConsole_Request.h b/src/PyConsoleBase/PyConsole_Request.h similarity index 100% rename from src/PyConsole/PyConsole_Request.h rename to src/PyConsoleBase/PyConsole_Request.h diff --git a/src/PyConsoleBase/resources/PyConsoleBase_msg_en.ts b/src/PyConsoleBase/resources/PyConsoleBase_msg_en.ts new file mode 100644 index 000000000..1586df266 --- /dev/null +++ b/src/PyConsoleBase/resources/PyConsoleBase_msg_en.ts @@ -0,0 +1,70 @@ + + + + + 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 + + Choose python file where to store + Choose python file where to store dump + + + Python scripts ext (*.py) + Python scripts (*.py) + + + WARNING + WARNING ! + + + Python file has not been written + Python file has not been written ! + + + Choose python file where to store log + Choose python file where to store log + + + Log files ext (*.log *.txt) + Log files extension (*.log *.txt) + + + Log file is not writable + Log file is not writable ! + + + diff --git a/src/PyConsoleBase/resources/PyConsoleBase_msg_fr.ts b/src/PyConsoleBase/resources/PyConsoleBase_msg_fr.ts new file mode 100644 index 000000000..ff40a32c5 --- /dev/null +++ b/src/PyConsoleBase/resources/PyConsoleBase_msg_fr.ts @@ -0,0 +1,70 @@ + + + + + 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_EditorBase + + Choose python file where to store + Choisissez un fichier python où sauver le dump + + + Python scripts ext (*.py) + Scripts Python (*.py) + + + WARNING + Attention ! + + + Python file has not been written + Le fichier Python n'a pas été écrit ! + + + Choose python file where to store log + Choisissez un fichier python où sauver le log + + + Log files ext (*.log *.txt) + Extensions possible des fichiers log (*.log *.txt) + + + Log file is not writable + Le fichier log sélectionné n'est pas writable ! + + + diff --git a/src/PyConsoleBase/resources/PyConsoleBase_msg_ja.ts b/src/PyConsoleBase/resources/PyConsoleBase_msg_ja.ts new file mode 100644 index 000000000..ca1aacee3 --- /dev/null +++ b/src/PyConsoleBase/resources/PyConsoleBase_msg_ja.ts @@ -0,0 +1,70 @@ + + + + + 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_EditorBase + + Choose python file where to store + Choose python file where to store dump + + + Python scripts ext (*.py) + Python scripts (*.py) + + + WARNING + WARNING ! + + + Python file has not been written + Python file has not been written ! + + + Choose python file where to store log + Choose python file where to store log + + + Log files ext (*.log *.txt) + Log files extension (*.log *.txt) + + + Log file is not writable + Log file is not writable ! + + + diff --git a/src/Qtx/Qtx.cxx b/src/Qtx/Qtx.cxx index cf3dc958f..b7e4e775f 100755 --- a/src/Qtx/Qtx.cxx +++ b/src/Qtx/Qtx.cxx @@ -2095,6 +2095,17 @@ QString Qtx::qtDir( const QString& context ) return qtPath; } +/*! + Creates font from string description +*/ +QFont Qtx::stringToFont( const QString& fontDescription ) +{ + QFont font; + if ( fontDescription.trimmed().isEmpty() || !font.fromString( fontDescription ) ) + font = QFont( "Courier", 11 ); + return font; +} + #ifndef WIN32 #include diff --git a/src/Qtx/Qtx.h b/src/Qtx/Qtx.h index 807002e74..20eac5f92 100755 --- a/src/Qtx/Qtx.h +++ b/src/Qtx/Qtx.h @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -276,6 +277,8 @@ public: static QString qtDir( const QString& = QString()); + static QFont stringToFont( const QString& fontDescription ); + #ifndef WIN32 static void* getDisplay(); static Qt::HANDLE getVisual(); 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/SUIT/SUIT_Tools.cxx b/src/SUIT/SUIT_Tools.cxx index 11a8ddb92..2d5ab0162 100755 --- a/src/SUIT/SUIT_Tools.cxx +++ b/src/SUIT/SUIT_Tools.cxx @@ -62,17 +62,6 @@ QRect SUIT_Tools::makeRect( const int x1, const int y1, const int x2, const int return QRect( qMin( x1, x2 ), qMin( y1, y2 ), qAbs( x2 - x1 ), qAbs( y2 - y1 ) ); } -/*! - Creates font from string description -*/ -QFont SUIT_Tools::stringToFont( const QString& fontDescription ) -{ - QFont font; - if ( fontDescription.trimmed().isEmpty() || !font.fromString( fontDescription ) ) - font = QFont( "Courier", 11 ); - return font; -} - /*! Creates font's string description */ diff --git a/src/SUIT/SUIT_Tools.h b/src/SUIT/SUIT_Tools.h index dbb64a7da..40da4718a 100755 --- a/src/SUIT/SUIT_Tools.h +++ b/src/SUIT/SUIT_Tools.h @@ -42,7 +42,6 @@ public: static QRect makeRect( const int x1, const int y1, const int x2, const int y2 ); static QString fontToString( const QFont& font ); - static QFont stringToFont( const QString& fontDescription ); static void centerWidget( QWidget* src, const QWidget* ref ); }; 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() -- 2.39.2