X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FPyConsole%2FPyConsole_Editor.cxx;h=2311b58d46bb29a3e2b48be8b4b7cd2020572401;hb=a6c6f1e04c7c1a22e856db2d6538bf5197f86c6c;hp=3414e26a62b56da147bc90712864a82c07231fdc;hpb=bb8609caf7881d966fbb88dec0a7822736da93f5;p=modules%2Fgui.git diff --git a/src/PyConsole/PyConsole_Editor.cxx b/src/PyConsole/PyConsole_Editor.cxx index 3414e26a6..2311b58d4 100644 --- a/src/PyConsole/PyConsole_Editor.cxx +++ b/src/PyConsole/PyConsole_Editor.cxx @@ -1,4 +1,4 @@ -// Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE // // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS @@ -6,7 +6,7 @@ // 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. +// 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 @@ -86,16 +86,14 @@ - : copy - : cut - : paste - - TODO: - - paste multiline text: process each line as separate command - (including mouse middle-button click pasting) - - the same for drag-n-drop of multiline text */ #include "PyConsole_Interp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!! #include "PyConsole_Editor.h" -#include +#include "PyConsole_Event.h" +#include "PyInterp_Event.h" +#include "PyInterp_Dispatcher.h" +#include "PyConsole_Request.h" #include #include @@ -113,13 +111,26 @@ #include #include #include +#include -static QString READY_PROMPT = ">>> "; -static QString DOTS_PROMPT = "... "; -#define PROMPT_SIZE myPrompt.length() +//VSR: uncomment below macro to support unicode text properly in SALOME +// current commented out due to regressions +//#define PAL22528_UNICODE -#define PRINT_EVENT 65432 +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 = "... "; class DumpCommandsFileValidator : public SUIT_FileValidator { @@ -141,95 +152,25 @@ bool DumpCommandsFileValidator::canSave(const QString& file, bool permissions) return SUIT_FileValidator::canSave( file, permissions); } -/*! - \class ExecCommand - \brief Python command execution request. - \internal -*/ - -class ExecCommand : public PyInterp_LockRequest +void staticCallbackStdout( void* data, char* c ) { -public: - /*! - \brief Constructor. - - Creates new python command execution request. - \param theInterp python interpreter - \param theCommand python command - \param theListener widget to get the notification messages - \param sync if True the request is processed synchronously - */ - ExecCommand( PyInterp_Interp* theInterp, - const QString& theCommand, - PyConsole_Editor* theListener, - bool sync = false ) - : PyInterp_LockRequest( theInterp, theListener, sync ), - myCommand( theCommand ), myState( PyInterp_Event::ES_OK ) - {} - -protected: - /*! - \brief Execute the python command in the interpreter and - get its execution status. - */ - virtual void execute() - { - if ( myCommand != "" ) - { - int ret = getInterp()->run( myCommand.toUtf8().data() ); - if ( ret < 0 ) - myState = PyInterp_Event::ES_ERROR; - else if ( ret > 0 ) - myState = PyInterp_Event::ES_INCOMPLETE; - } - } - - /*! - \brief Create and return a notification event. - \return new notification event - */ - virtual QEvent* createEvent() const - { - if ( IsSync() ) - QCoreApplication::sendPostedEvents( listener(), PRINT_EVENT ); - return new PyInterp_Event( myState, (PyInterp_Request*)this ); + if(!((PyConsole_Editor*)data)->isSuppressOutput()) { + PyConsole_Editor* e = (PyConsole_Editor*)data; + e->putLog( fromUtf8(c) ); + QApplication::postEvent( e, new PrintEvent( fromUtf8(c), false ) ); } +} -private: - QString myCommand; //!< Python command - int myState; //!< Python command execution status -}; - -/*! - \class PrintEvent - \brief Python command output backend event. - \internal -*/ - -class PrintEvent : public QEvent -{ -public: - /*! - \brief Constructor - \param c message text (python trace) - */ - PrintEvent( const char* c ) : QEvent( (QEvent::Type)PRINT_EVENT ), myText( c ) {} - /*! - \brief Get message - \return message text (python trace) - */ - QString text() const { return myText; } - -private: - QString myText; //!< Event message (python trace) -}; - -void staticCallback( void* data, char* c ) +void staticCallbackStderr( void* data, char* c ) { - if(!((PyConsole_Editor*)data)->isSuppressOutput()) - QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( 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. @@ -244,7 +185,7 @@ PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp, myCmdInHistory( -1 ), myEventLoop( 0 ), myShowBanner( true ), - myIsSync( false ), + myIsSync( true ), myIsSuppressOutput( false ) { QString fntSet( "" ); @@ -257,8 +198,8 @@ PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp, setWordWrapMode( QTextOption::WrapAnywhere ); setAcceptRichText( false ); - theInterp->setvoutcb( staticCallback, this ); - theInterp->setverrcb( staticCallback, this ); + theInterp->setvoutcb( staticCallbackStdout, this ); + theInterp->setverrcb( staticCallbackStderr, this ); // san - This is necessary for troubleless initialization onPyInterpChanged( theInterp ); @@ -266,11 +207,18 @@ PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp, /*! \brief Destructor. - - Does nothing for the moment. */ PyConsole_Editor::~PyConsole_Editor() { + myInterp = 0; +} + +/*! + \brief Get Python interpreter +*/ +PyConsole_Interp* PyConsole_Editor::getInterp() const +{ + return myInterp; } /*! @@ -350,6 +298,17 @@ void PyConsole_Editor::setIsShowBanner( const bool on ) } } +/*! + \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 @@ -366,14 +325,23 @@ QSize PyConsole_Editor::sizeHint() const \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 newBlock, + const bool isError) { + QTextCursor theCursor(textCursor()); + QTextCharFormat cf; + moveCursor( QTextCursor::End ); if ( newBlock ) - textCursor().insertBlock(); - textCursor().insertText( str ); + theCursor.insertBlock(); + if (isError) + cf.setForeground(QBrush(Qt::red)); + else + cf.setForeground(QBrush(Qt::black)); + theCursor.insertText( str, cf); moveCursor( QTextCursor::End ); ensureCursorVisible(); } @@ -382,6 +350,8 @@ void PyConsole_Editor::addText( const QString& str, \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 ) { @@ -409,6 +379,7 @@ void PyConsole_Editor::exec( const QString& command ) 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 ); @@ -445,14 +416,21 @@ void PyConsole_Editor::execAndWait( const QString& command ) return; // create new event loop - myEventLoop = new QEventLoop( this ); + bool sync = isSync(); + if ( !sync ) { + myEventLoop = new QEventLoop( this ); + } + // execute command exec( command ); - // run event loop - myEventLoop->exec(); - // delete event loop after command is processed - delete myEventLoop; - myEventLoop = 0; + + if ( !sync ) { + // run event loop + myEventLoop->exec(); + // delete event loop after command is processed + delete myEventLoop; + myEventLoop = 0; + } } /*! @@ -462,17 +440,23 @@ void PyConsole_Editor::execAndWait( const QString& command ) */ 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, PROMPT_SIZE ); + 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 ); @@ -499,7 +483,7 @@ void PyConsole_Editor::dropEvent( QDropEvent* event ) 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() + PROMPT_SIZE ) { + if ( cur.position() < document()->end().previous().position() + promptSize() ) { moveCursor( QTextCursor::End ); pos = cursorRect().center(); } @@ -529,21 +513,18 @@ void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event ) //copy(); } else if ( event->button() == Qt::MidButton ) { - QString text; - if ( QApplication::clipboard()->supportsSelection() ) - text = QApplication::clipboard()->text( QClipboard::Selection ); - if ( text.isEmpty() ) - text = QApplication::clipboard()->text( QClipboard::Clipboard ); 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() + PROMPT_SIZE ) { + if ( cur.position() < document()->end().previous().position() + promptSize() ) { moveCursor( QTextCursor::End ); } else { setTextCursor( cur ); } - textCursor().clearSelection(); - textCursor().insertText( text ); + const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ? + QClipboard::Selection : QClipboard::Clipboard ); + if ( md ) + insertFromMimeData( md ); } else { QTextEdit::mouseReleaseEvent( event ); @@ -612,13 +593,15 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) } // check for printed key - aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0; + // #### 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 < PROMPT_SIZE ) { + if ( curLine < endLine || curCol < promptSize() ) { moveCursor( QTextCursor::End ); } QTextEdit::keyPressEvent( event ); @@ -654,7 +637,7 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) myCmdInHistory = myHistory.count(); // remember current command QTextBlock par = document()->end().previous(); - myCurrentCommand = par.text().remove( 0, PROMPT_SIZE ); + myCurrentCommand = par.text().remove( 0, promptSize() ); } if ( myCmdInHistory > 0 ) { myCmdInHistory--; @@ -724,7 +707,7 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) // - with + modifier keys pressed: move one word left with selection { QString txt = textCursor().block().text(); - if ( !shftPressed && isCommand( txt ) && curCol <= PROMPT_SIZE ) { + if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) { moveCursor( QTextCursor::Up ); moveCursor( QTextCursor::EndOfBlock ); } @@ -743,15 +726,15 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) QString txt = textCursor().block().text(); if ( !shftPressed ) { if ( curCol < txt.length() ) { - if ( isCommand( txt ) && curCol < PROMPT_SIZE ) { - cur.setPosition( cur.block().position() + PROMPT_SIZE ); + 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() + PROMPT_SIZE+1 ); + cur.setPosition( cur.position() + promptSize()+1 ); setTextCursor( cur ); horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); break; @@ -798,7 +781,7 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) myCmdInHistory = myHistory.count(); // remember current command QTextBlock par = document()->end().previous(); - myCurrentCommand = par.text().remove( 0, PROMPT_SIZE ); + myCurrentCommand = par.text().remove( 0, promptSize() ); } if ( myCmdInHistory > 0 ) { myCmdInHistory = 0; @@ -876,14 +859,14 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) QString txt = textCursor().block().text(); if ( isCommand( txt ) ) { if ( shftPressed ) { - if ( curCol > PROMPT_SIZE ) { + if ( curCol > promptSize() ) { cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor ); - cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, PROMPT_SIZE ); + cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() ); } } else { cur.movePosition( QTextCursor::StartOfLine ); - cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PROMPT_SIZE ); + cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() ); } setTextCursor( cur ); } @@ -916,13 +899,13 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) if ( cur.hasSelection() ) { cut(); } - else if ( cur.position() > document()->end().previous().position() + PROMPT_SIZE ) { + 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() + PROMPT_SIZE, + cur.setPosition( document()->end().previous().position() + promptSize(), QTextCursor::KeepAnchor ); setTextCursor( cur ); textCursor().removeSelectedText(); @@ -932,7 +915,7 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) } } else { - cur.setPosition( document()->end().previous().position() + PROMPT_SIZE ); + cur.setPosition( document()->end().previous().position() + promptSize() ); setTextCursor( cur ); horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); } @@ -948,7 +931,7 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) if ( cur.hasSelection() ) { cut(); } - else if ( cur.position() > document()->end().previous().position() + PROMPT_SIZE-1 ) { + else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) { if ( shftPressed ) { moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor ); textCursor().removeSelectedText(); @@ -962,7 +945,7 @@ void PyConsole_Editor::keyPressEvent( QKeyEvent* event ) } } else { - cur.setPosition( document()->end().previous().position() + PROMPT_SIZE ); + cur.setPosition( document()->end().previous().position() + promptSize() ); setTextCursor( cur ); horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() ); } @@ -994,10 +977,10 @@ void PyConsole_Editor::customEvent( QEvent* event ) { switch( event->type() ) { - case PRINT_EVENT: + case PrintEvent::EVENT_ID: { PrintEvent* pe=(PrintEvent*)event; - addText( pe->text() ); + addText( pe->text(), false, pe->isError()); return; } case PyInterp_Event::ES_OK: @@ -1115,11 +1098,11 @@ void PyConsole_Editor::cut() if ( cur.hasSelection() ) { QApplication::clipboard()->setText( cur.selectedText() ); int startSelection = cur.selectionStart(); - if ( startSelection < document()->end().previous().position() + PROMPT_SIZE ) - startSelection = document()->end().previous().position() + PROMPT_SIZE; + if ( startSelection < document()->end().previous().position() + promptSize() ) + startSelection = document()->end().previous().position() + promptSize(); int endSelection = cur.selectionEnd(); - if ( endSelection < document()->end().previous().position() + PROMPT_SIZE ) - endSelection = document()->end().previous().position() + PROMPT_SIZE; + 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() ); @@ -1139,18 +1122,18 @@ void PyConsole_Editor::paste() QTextCursor cur = textCursor(); if ( cur.hasSelection() ) { int startSelection = cur.selectionStart(); - if ( startSelection < document()->end().previous().position() + PROMPT_SIZE ) - startSelection = document()->end().previous().position() + PROMPT_SIZE; + if ( startSelection < document()->end().previous().position() + promptSize() ) + startSelection = document()->end().previous().position() + promptSize(); int endSelection = cur.selectionEnd(); - if ( endSelection < document()->end().previous().position() + PROMPT_SIZE ) - endSelection = document()->end().previous().position() + PROMPT_SIZE; + 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() + PROMPT_SIZE ) + if ( textCursor().position() < document()->end().previous().position() + promptSize() ) moveCursor( QTextCursor::End ); QTextEdit::paste(); } @@ -1179,18 +1162,90 @@ void PyConsole_Editor::dump() 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 != "" ) { + aFilters, tr( "TOT_DUMP_PYCOMMANDS" ), + false, true, new DumpCommandsFileValidator( this ) ); + if ( !fileName.isEmpty() ) { QFile file( fileName ); if ( !file.open( QFile::WriteOnly ) ) return; QTextStream out (&file); - for( int i=0; i