-// 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
// 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
- <Ctrl><C> : copy
- <Ctrl><X> : cut
- <Ctrl><V> : 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 <PyInterp_Dispatcher.h>
+#include "PyConsole_Event.h"
+#include "PyInterp_Event.h"
+#include "PyInterp_Dispatcher.h"
+#include "PyConsole_Request.h"
#include <SUIT_Tools.h>
#include <SUIT_FileDlg.h>
#include <QTextCursor>
#include <QTextDocument>
#include <QTextStream>
+#include <QChar>
-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
{
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.
myCmdInHistory( -1 ),
myEventLoop( 0 ),
myShowBanner( true ),
- myIsSync( false ),
+ myIsSync( true ),
myIsSuppressOutput( false )
{
QString fntSet( "" );
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 );
/*!
\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;
}
/*!
}
}
+/*!
+ \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
\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();
}
\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 ( !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 );
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;
+ }
}
/*!
*/
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 );
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();
}
//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 );
}
// 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 );
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--;
// - with <Ctrl>+<Shift> 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 );
}
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;
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;
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 );
}
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();
}
}
else {
- cur.setPosition( document()->end().previous().position() + PROMPT_SIZE );
+ cur.setPosition( document()->end().previous().position() + promptSize() );
setTextCursor( cur );
horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
}
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();
}
}
else {
- cur.setPosition( document()->end().previous().position() + PROMPT_SIZE );
+ cur.setPosition( document()->end().previous().position() + promptSize() );
setTextCursor( cur );
horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
}
{
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:
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() );
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();
}
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<myHistory.count(); i++ ) {
- out<<myHistory[i]<<endl;
+ for ( int i=0; i<myHistory.count(); i++ ) {
+ out << myHistory[i] << endl;
+ }
+ file.close();
+ }
+}
+/*!
+ \brief "Start log" operation.
+ */
+void PyConsole_Editor::startLog()
+{
+ QStringList aFilters;
+ aFilters.append( tr( "LOG_FILES_FILTER" ) );
+
+ while (1) {
+ QString fileName = SUIT_FileDlg::getFileName( this, QString(),
+ aFilters, tr( "TOT_SAVE_PYLOG" ),
+ false, true );
+ if ( !fileName.isEmpty() ) {
+ if ( startLog( fileName ) ) {
+ break;
+ }
+ else {
+ SUIT_MessageBox::critical( this,
+ QObject::tr("ERR_ERROR"),
+ QObject::tr("ERR_FILE_NOT_WRITABLE") );
+ }
+ }
+ else {
+ break;
+ }
+ }
+}
+
+/*!
+ \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
+ */
+void PyConsole_Editor::putLog( const QString& s )
+{
+ if ( !myLogFile.isEmpty() ) {
+ QFile file( myLogFile );
+ if ( !file.open( QFile::Append ) )
+ return;
+
+ QTextStream out (&file);
+ out << s;
+
file.close();
}
}