1 // Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
23 // SALOME SALOMEGUI : implementation of desktop and GUI kernel
24 // File : PyConsole_Editor.cxx
25 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
28 \class PyConsole_Editor
29 \brief Python command line interpreter front-end GUI widget.
31 This class provides simple GUI interface to the Python interpreter, including basic
32 navigation operations, executing commands (both interactively and programmatically),
33 copy-paste operations, history of the commands and so on.
35 Here below is the shortcut keyboard boundings used for navigation and other operations:
36 - <Enter> : execute current command
37 - <Ctrl><Break> : clear current command
38 - <Escape> : clear current command
39 - <Up> : previous command in the history
40 - <Shift><Up> : move cursor one row up with selection
41 - <Ctrl><Up> : move cursor one row up without selection
42 - <Ctrl><Shift><Up> : move cursor one row up with selection
43 - <Down> : next command in the history
44 - <Shift><Down> : move cursor one row down with selection
45 - <Ctrl><Down> : move cursor one row down without selection
46 - <Ctrl><Shift><Down> : move cursor one row down with selection
47 - <Left> : move one symbol left without selection
48 - <Shift><Left> : move one symbol left with selection
49 - <Ctrl><Left> : move one word left without selection
50 - <Ctrl><Shift><Left> : move one word left with selection
51 - <Right> : move one symbol right without selection
52 - <Shift><Right> : move one symbol right with selection
53 - <Ctrl><Right> : move one word right without selection
54 - <Ctrl><Shift><Right> : move one word right with selection
55 - <PgUp> : first command in the history
56 - <Shift><PgUp> : move one page up with selection
57 - <Ctrl><PgUp> : move one page up without selection
58 - <Ctrl><Shift><PgUp> : scroll one page up
59 - <PgDn> : last command in the history
60 - <Shift><PgDn> : move one page down with selection
61 - <Ctrl><PgDn> : move one page down without selection
62 - <Ctrl><Shift><PgDn> : scroll one page down
63 - <Home> : move to the beginning of the line without selection
64 - <Shift><Home> : move to the beginning of the line with selection
65 - <Ctrl><Home> : move to the very first symbol without selection
66 - <Ctrl><Shift><Home> : move to the very first symbol with selection
67 - <End> : move to the end of the line without selection
68 - <Shift><End> : move to the end of the line with selection
69 - <Ctrl><End> : move to the very last symbol without selection
70 - <Ctrl><Shift><End> : move to the very last symbol with selection
71 - <Backspace> : delete symbol before the cursor
72 / remove selected text and put it to the clipboard (cut)
73 - <Shift><Backspace> : delete previous word
74 / remove selected text and put it to the clipboard (cut)
75 - <Ctrl><Backspace> : delete text from the cursor to the beginning of the line
76 / remove selected text and put it to the clipboard (cut)
77 - <Delete> : delete symbol after the cursor
78 / remove selected text and put it to the clipboard (cut)
79 - <Shift><Delete> : delete next word
80 / remove selected text and put it to the clipboard (cut)
81 - <Ctrl><Delete> : delete text from the cursor to the end of the line
82 / remove selected text and put it to the clipboard (cut)
83 - <Ctrl><Insert> : copy
84 - <Shift><Insert> : paste
91 #include "PyConsole_Interp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
92 #include "PyConsole_EditorBase.h"
93 #include "PyConsole_Event.h"
94 #include "PyInterp_Event.h"
95 #include "PyInterp_Dispatcher.h"
96 #include "PyConsole_Request.h"
100 #include <QApplication>
101 #include <QClipboard>
102 #include <QDropEvent>
105 #include <QMouseEvent>
106 #include <QScrollBar>
107 #include <QTextBlock>
108 #include <QTextCursor>
109 #include <QTextDocument>
110 #include <QTextStream>
113 #include <QFileDialog>
114 #include <QMessageBox>
116 //VSR: uncomment below macro to support unicode text properly in SALOME
117 // current commented out due to regressions
118 //#define PAL22528_UNICODE
122 QString fromUtf8( const char* txt )
124 #ifdef PAL22528_UNICODE
125 return QString::fromUtf8( txt );
127 return QString( txt );
132 static QString READY_PROMPT = ">>> ";
133 static QString DOTS_PROMPT = "... ";
135 void staticCallbackStdout( void* data, char* c )
137 if(!((PyConsole_EditorBase*)data)->isSuppressOutput()) {
138 PyConsole_EditorBase* e = (PyConsole_EditorBase*)data;
139 e->putLog( fromUtf8(c) );
140 QApplication::postEvent( e, new PrintEvent( fromUtf8(c), false ) );
144 void staticCallbackStderr( void* data, char* c )
146 if(!((PyConsole_EditorBase*)data)->isSuppressOutput()) {
147 PyConsole_EditorBase* e = (PyConsole_EditorBase*)data;
148 e->putLog( fromUtf8(c) );
149 QApplication::postEvent( e, new PrintEvent( fromUtf8(c), true ) );
157 Creates python editor window.
158 \param theInterp python interper
159 \param theParent parent widget
161 PyConsole_EditorBase::PyConsole_EditorBase( PyConsole_Interp* theInterp,
163 : QTextEdit( theParent ),
165 myCmdInHistory( -1 ),
167 myShowBanner( true ),
169 myIsSuppressOutput( false )
171 QString fntSet( "" );
172 QFont aFont ( Qtx::stringToFont(fntSet) );
174 setUndoRedoEnabled( false );
176 myPrompt = READY_PROMPT;
177 setLineWrapMode( QTextEdit::WidgetWidth );
178 setWordWrapMode( QTextOption::WrapAnywhere );
179 setAcceptRichText( false );
181 theInterp->setvoutcb( staticCallbackStdout, this );
182 theInterp->setverrcb( staticCallbackStderr, this );
184 // san - This is necessary for troubleless initialization
185 onPyInterpChanged( theInterp );
191 PyConsole_EditorBase::~PyConsole_EditorBase()
197 \brief Get Python interpreter
199 PyConsole_Interp* PyConsole_EditorBase::getInterp() const
205 \brief Get synchronous mode flag value.
208 \return True if python console works in synchronous mode
210 bool PyConsole_EditorBase::isSync() const
216 \brief Set synchronous mode flag value.
218 In synhronous mode the Python commands are executed in the GUI thread
219 and the GUI is blocked until the command is finished. In the asynchronous
220 mode each Python command is executed in the separate thread that does not
221 block the main GUI loop.
223 \param on synhronous mode flag
225 void PyConsole_EditorBase::setIsSync( const bool on )
231 \brief Get suppress output flag value.
233 \sa setIsSuppressOutput()
234 \return \c true if python console output is suppressed.
236 bool PyConsole_EditorBase::isSuppressOutput() const
238 return myIsSuppressOutput;
242 \brief Set suppress output flag value.
244 In case if suppress output flag is true, the python
245 console output suppressed.
247 \param on suppress output flag
249 void PyConsole_EditorBase::setIsSuppressOutput( const bool on )
251 myIsSuppressOutput = on;
255 \brief Get 'show banner' flag value.
257 \sa setIsShowBanner()
258 \return \c true if python console shows banner
260 bool PyConsole_EditorBase::isShowBanner() const
266 \brief Set 'show banner' flag value.
268 The banner is shown in the top of the python console window.
271 \param on 'show banner' flag
273 void PyConsole_EditorBase::setIsShowBanner( const bool on )
275 if ( myShowBanner != on ) {
282 \brief Check if trace logging is switched on.
284 \sa startLog(), stopLog()
285 \return \c true if trace logging is switched on
287 bool PyConsole_EditorBase::isLogging() const
289 return !myLogFile.isEmpty();
293 \brief Get size hint for the Python console window
294 \return size hint value
296 QSize PyConsole_EditorBase::sizeHint() const
298 QFontMetrics fm( font() );
299 int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1;
300 QSize s(100, fm.lineSpacing()*nbLines);
305 \brief Put the string \a str to the python editor.
306 \param str string to be put in the command line of the editor
307 \param newBlock if True, then the string is printed on a new line
308 \param isError if true, the text is printed in dark red
310 void PyConsole_EditorBase::addText( const QString& str,
314 QTextCursor theCursor(textCursor());
317 moveCursor( QTextCursor::End );
319 theCursor.insertBlock();
321 cf.setForeground(QBrush(Qt::red));
323 cf.setForeground(QBrush(Qt::black));
324 theCursor.insertText( str, cf);
325 moveCursor( QTextCursor::End );
326 ensureCursorVisible();
330 \brief Convenient method for executing a Python command,
331 as if the user typed it manually.
332 \param command python command to be executed
334 !!! WARNING: doesn't work properly with multi-line commands. !!!
336 void PyConsole_EditorBase::exec( const QString& command )
338 if ( isReadOnly() ) {
339 // some interactive command is being executed in this editor...
340 // shedule the command to the queue
341 myQueue.push_back( command );
345 moveCursor( QTextCursor::End );
346 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
347 textCursor().removeSelectedText();
348 // set "ready" prompt
349 myPrompt = READY_PROMPT;
350 // clear command buffer
351 myCommandBuffer.truncate( 0 );
352 // unset history browsing mode
354 // print command line by line
355 QString cmd = command;
356 if ( !cmd.endsWith( "\n" ) ) cmd += "\n";
357 QStringList lines = command.split( "\n" );
358 for ( int i = 0; i < lines.size(); i++ ) {
359 if ( !lines[i].trimmed().isEmpty() )
360 myHistory.push_back( lines[i] );
361 addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 );
362 putLog( QString( "%1%2\n" ).arg( i == 0 ? READY_PROMPT : DOTS_PROMPT ).arg( lines[i] ) );
366 // set read-only mode
369 setCursor( Qt::BusyCursor );
371 // post a request to execute Python command;
372 // editor will be informed via a custom event that execution has been completed
373 PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) );
377 \brief Create request to the python dispatcher for the command execution.
379 \param command python command to be executed
381 PyInterp_Request* PyConsole_EditorBase::createRequest( const QString& command )
383 return new ExecCommand( myInterp, command, this, isSync() );
387 \brief Execute command in the python interpreter
388 and wait until it is finished.
390 \param command python command to be executed
392 void PyConsole_EditorBase::execAndWait( const QString& command )
398 // create new event loop
399 bool sync = isSync();
401 myEventLoop = new QEventLoop( this );
410 // delete event loop after command is processed
417 \brief Process "Enter" key press event.
419 Execute the command entered by the user.
421 void PyConsole_EditorBase::handleReturn()
423 // Position cursor at the end
424 QTextCursor curs(textCursor());
425 curs.movePosition(QTextCursor::End);
429 QTextBlock par = document()->end().previous();
430 if ( !par.isValid() ) return;
433 QString cmd = par.text().remove( 0, promptSize() );
434 // extend the command buffer with the current command
435 myCommandBuffer.append( cmd );
436 // add command to the history
437 if ( !cmd.trimmed().isEmpty() )
438 myHistory.push_back( cmd );
439 putLog( QString( "%1%2\n" ).arg( myPrompt ).arg( cmd ) );
444 // set read-only mode
447 setCursor( Qt::BusyCursor );
449 // post a request to execute Python command;
450 // editor will be informed via a custom event that execution has been completed
451 PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) );
455 \brief Process drop event.
458 \param event drop event
460 void PyConsole_EditorBase::dropEvent( QDropEvent* event )
462 // get the initial drop position
463 QPoint pos = event->pos();
464 QTextCursor cur = cursorForPosition( event->pos() );
465 // if the position is not in the last line move it to the end of the command line
466 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
467 moveCursor( QTextCursor::End );
468 pos = cursorRect().center();
470 // create new drop event and use it instead of the original
472 event->possibleActions(),
474 event->mouseButtons(),
475 event->keyboardModifiers(),
477 QTextEdit::dropEvent( &de );
478 // accept the original event
479 event->acceptProposedAction();
483 \brief Process mouse button release event.
485 Left mouse button: copy selection to the clipboard.
486 Middle mouse button: paste clipboard's contents.
487 \param event mouse event
489 void PyConsole_EditorBase::mouseReleaseEvent( QMouseEvent* event )
491 if ( event->button() == Qt::LeftButton ) {
492 QTextEdit::mouseReleaseEvent( event );
495 else if ( event->button() == Qt::MidButton ) {
496 QTextCursor cur = cursorForPosition( event->pos() );
497 // if the position is not in the last line move it to the end of the command line
498 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
499 moveCursor( QTextCursor::End );
502 setTextCursor( cur );
504 const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ?
505 QClipboard::Selection : QClipboard::Clipboard );
507 insertFromMimeData( md );
510 QTextEdit::mouseReleaseEvent( event );
515 \brief Check if the string is command.
517 Return True if the string \a str is likely to be the command
518 (i.e. it is started from the '>>>' or '...').
519 \param str string to be checked
521 bool PyConsole_EditorBase::isCommand( const QString& str ) const
523 return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT );
527 \brief Handle keyboard event.
529 Implement navigation, history browsing, copy/paste and other common
531 \param event keyboard event
533 void PyConsole_EditorBase::keyPressEvent( QKeyEvent* event )
535 // get cursor position
536 QTextCursor cur = textCursor();
537 int curLine = cur.blockNumber();
538 int curCol = cur.columnNumber();
540 // get last edited line
541 int endLine = document()->blockCount()-1;
543 // get pressed key code
544 int aKey = event->key();
546 // check if <Ctrl> is pressed
547 bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
548 // check if <Shift> is pressed
549 bool shftPressed = event->modifiers() & Qt::ShiftModifier;
551 if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) {
552 // process <Ctrl>+<Break> key-binding and <Escape> key: clear current command
553 myCommandBuffer.truncate( 0 );
554 myPrompt = READY_PROMPT;
555 addText( myPrompt, true );
556 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
559 else if ( ctrlPressed && aKey == Qt::Key_C ) {
560 // process <Ctrl>+<C> key-binding : copy
564 else if ( ctrlPressed && aKey == Qt::Key_X ) {
565 // process <Ctrl>+<X> key-binding : cut
569 else if ( ctrlPressed && aKey == Qt::Key_V ) {
570 // process <Ctrl>+<V> key-binding : paste
575 // check for printed key
576 // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0;
578 aKey = !(QChar(aKey).isPrint()) ? aKey : 0;
582 // any printed key: just print it
584 if ( curLine < endLine || curCol < promptSize() ) {
585 moveCursor( QTextCursor::End );
587 QTextEdit::keyPressEvent( event );
592 // <Enter> key: process the current command
598 // <Up> arrow key: process as follows:
599 // - without <Ctrl>, <Shift> modifiers: previous command in history
600 // - with <Ctrl> modifier key pressed: move cursor one row up without selection
601 // - with <Shift> modifier key pressed: move cursor one row up with selection
602 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
604 if ( ctrlPressed && shftPressed ) {
605 int value = verticalScrollBar()->value();
606 int spacing = fontMetrics().lineSpacing();
607 verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 );
609 else if ( shftPressed || ctrlPressed ) {
611 moveCursor( QTextCursor::Up,
612 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
615 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
616 // set history browsing mode
617 myCmdInHistory = myHistory.count();
618 // remember current command
619 QTextBlock par = document()->end().previous();
620 myCurrentCommand = par.text().remove( 0, promptSize() );
622 if ( myCmdInHistory > 0 ) {
624 // get previous command in the history
625 QString previousCommand = myHistory.at( myCmdInHistory );
626 // print previous command
627 moveCursor( QTextCursor::End );
628 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
629 textCursor().removeSelectedText();
630 addText( myPrompt + previousCommand );
631 // move cursor to the end
632 moveCursor( QTextCursor::End );
638 // <Down> arrow key: process as follows:
639 // - without <Ctrl>, <Shift> modifiers: next command in history
640 // - with <Ctrl> modifier key pressed: move cursor one row down without selection
641 // - with <Shift> modifier key pressed: move cursor one row down with selection
642 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
644 if ( ctrlPressed && shftPressed ) {
645 int value = verticalScrollBar()->value();
646 int maxval = verticalScrollBar()->maximum();
647 int spacing = fontMetrics().lineSpacing();
648 verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval );
650 else if ( shftPressed || ctrlPressed) {
651 if ( curLine < endLine )
652 moveCursor( QTextCursor::Down,
653 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
656 if ( myCmdInHistory >= 0 ) {
657 // get next command in the history
660 if ( myCmdInHistory < myHistory.count() ) {
661 // next command in history
662 nextCommand = myHistory.at( myCmdInHistory );
665 // end of history is reached
666 // last printed command
667 nextCommand = myCurrentCommand;
668 // unset history browsing mode
671 // print next or current command
672 moveCursor( QTextCursor::End );
673 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
674 textCursor().removeSelectedText();
675 addText( myPrompt + nextCommand );
676 // move cursor to the end
677 moveCursor( QTextCursor::End );
683 // <Left> arrow key: process as follows:
684 // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
685 // - with <Ctrl> modifier key pressed: move one word left (taking into account prompt)
686 // - with <Shift> modifier key pressed: move one symbol left with selection
687 // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
689 QString txt = textCursor().block().text();
690 if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) {
691 moveCursor( QTextCursor::Up );
692 moveCursor( QTextCursor::EndOfBlock );
695 QTextEdit::keyPressEvent( event );
700 // <Right> arrow key: process as follows:
701 // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
702 // - with <Ctrl> modifier key pressed: move one word right (taking into account prompt)
703 // - with <Shift> modifier key pressed: move one symbol right with selection
704 // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
706 QString txt = textCursor().block().text();
707 if ( !shftPressed ) {
708 if ( curCol < txt.length() ) {
709 if ( isCommand( txt ) && curCol < promptSize() ) {
710 cur.setPosition( cur.block().position() + promptSize() );
711 setTextCursor( cur );
716 if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) {
717 cur.setPosition( cur.position() + promptSize()+1 );
718 setTextCursor( cur );
719 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
724 QTextEdit::keyPressEvent( event );
728 // <PageUp> key: process as follows:
729 // - without <Ctrl>, <Shift> modifiers: first command in history
730 // - with <Ctrl> modifier key pressed: move cursor one page up without selection
731 // - with <Shift> modifier key pressed: move cursor one page up with selection
732 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
734 if ( ctrlPressed && shftPressed ) {
735 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
737 else if ( shftPressed || ctrlPressed ) {
739 qreal lastY = cursorRect( cur ).top();
741 // move using movePosition to keep the cursor's x
743 qreal y = cursorRect( cur ).top();
744 distance += qAbs( y - lastY );
746 moved = cur.movePosition( QTextCursor::Up,
747 shftPressed ? QTextCursor::KeepAnchor :
748 QTextCursor::MoveAnchor );
749 } while ( moved && distance < viewport()->height() );
751 cur.movePosition( QTextCursor::Down,
752 shftPressed ? QTextCursor::KeepAnchor :
753 QTextCursor::MoveAnchor );
754 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
756 setTextCursor( cur );
759 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
760 // set history browsing mode
761 myCmdInHistory = myHistory.count();
762 // remember current command
763 QTextBlock par = document()->end().previous();
764 myCurrentCommand = par.text().remove( 0, promptSize() );
766 if ( myCmdInHistory > 0 ) {
768 // get very first command in the history
769 QString firstCommand = myHistory.at( myCmdInHistory );
770 // print first command
771 moveCursor( QTextCursor::End );
772 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
773 textCursor().removeSelectedText();
774 addText( myPrompt + firstCommand );
775 // move cursor to the end
776 moveCursor( QTextCursor::End );
781 case Qt::Key_PageDown:
782 // <PageDown> key: process as follows:
783 // - without <Ctrl>, <Shift> modifiers: last command in history
784 // - with <Ctrl> modifier key pressed: move cursor one page down without selection
785 // - with <Shift> modifier key pressed: move cursor one page down with selection
786 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
788 if ( ctrlPressed && shftPressed ) {
789 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
791 else if ( shftPressed || ctrlPressed ) {
793 qreal lastY = cursorRect( cur ).top();
795 // move using movePosition to keep the cursor's x
797 qreal y = cursorRect( cur ).top();
798 distance += qAbs( y - lastY );
800 moved = cur.movePosition( QTextCursor::Down,
801 shftPressed ? QTextCursor::KeepAnchor :
802 QTextCursor::MoveAnchor );
803 } while ( moved && distance < viewport()->height() );
805 cur.movePosition( QTextCursor::Up,
806 shftPressed ? QTextCursor::KeepAnchor :
807 QTextCursor::MoveAnchor );
808 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
810 setTextCursor( cur );
813 if ( myCmdInHistory >= 0 ) {
814 // unset history browsing mode
816 // print current command
817 moveCursor( QTextCursor::End );
818 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
819 textCursor().removeSelectedText();
820 addText( myPrompt + myCurrentCommand );
821 // move cursor to the end
822 moveCursor( QTextCursor::End );
828 // <Home> key: process as follows:
829 // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
830 // - with <Ctrl> modifier key pressed: move cursor to the very first symbol without selection
831 // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
832 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
835 moveCursor( QTextCursor::Start,
836 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
839 QString txt = textCursor().block().text();
840 if ( isCommand( txt ) ) {
842 if ( curCol > promptSize() ) {
843 cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor );
844 cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() );
848 cur.movePosition( QTextCursor::StartOfLine );
849 cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() );
851 setTextCursor( cur );
854 moveCursor( QTextCursor::StartOfBlock,
855 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
857 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
862 // <End> key: process as follows:
863 // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
864 // - with <Ctrl> modifier key pressed: move cursor to the very last symbol without selection
865 // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
866 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
868 moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock,
869 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
872 case Qt::Key_Backspace :
873 // <Backspace> key: process as follows
874 // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
875 // - with <Shift> modifier key pressed: delete previous word
876 // - with <Ctrl> modifier key pressed: delete text from the cursor to the line beginning
877 // works only for last (command) line
879 if ( cur.hasSelection() ) {
882 else if ( cur.position() > document()->end().previous().position() + promptSize() ) {
884 moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor );
885 textCursor().removeSelectedText();
887 else if ( ctrlPressed ) {
888 cur.setPosition( document()->end().previous().position() + promptSize(),
889 QTextCursor::KeepAnchor );
890 setTextCursor( cur );
891 textCursor().removeSelectedText();
894 QTextEdit::keyPressEvent( event );
898 cur.setPosition( document()->end().previous().position() + promptSize() );
899 setTextCursor( cur );
900 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
904 case Qt::Key_Delete :
905 // <Delete> key: process as follows
906 // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
907 // - with <Shift> modifier key pressed: delete next word
908 // - with <Ctrl> modifier key pressed: delete text from the cursor to the end of line
909 // works only for last (command) line
911 if ( cur.hasSelection() ) {
914 else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) {
916 moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor );
917 textCursor().removeSelectedText();
919 else if ( ctrlPressed ) {
920 moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
921 textCursor().removeSelectedText();
924 QTextEdit::keyPressEvent( event );
928 cur.setPosition( document()->end().previous().position() + promptSize() );
929 setTextCursor( cur );
930 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
934 case Qt::Key_Insert :
935 // <Insert> key: process as follows
936 // - with <Ctrl> modifier key pressed: copy()
937 // - with <Shift> modifier key pressed: paste() to the command line
942 else if ( shftPressed ) {
946 QTextEdit::keyPressEvent( event );
953 \brief Handle notification event coming from Python dispatcher.
954 \param event notification event
956 void PyConsole_EditorBase::customEvent( QEvent* event )
958 switch( event->type() )
960 case PrintEvent::EVENT_ID:
962 PrintEvent* pe=(PrintEvent*)event;
963 addText( pe->text(), false, pe->isError());
966 case PyInterp_Event::ES_OK:
967 case PyInterp_Event::ES_ERROR:
969 // clear command buffer
970 myCommandBuffer.truncate( 0 );
971 // add caret return line if necessary
972 QTextBlock par = document()->end().previous();
973 QString txt = par.text();
974 txt.truncate( txt.length() - 1 );
975 // IPAL19397 : addText moved to handleReturn() method
976 //if ( !txt.isEmpty() )
977 // addText( "", true );
978 // set "ready" prompt
979 myPrompt = READY_PROMPT;
983 // stop event loop (if running)
988 case PyInterp_Event::ES_INCOMPLETE:
990 // extend command buffer (multi-line command)
991 myCommandBuffer.append( "\n" );
992 // add caret return line if necessary
993 QTextBlock par = document()->end().previous();
994 QString txt = par.text();
995 txt.truncate( txt.length() - 1 );
996 // IPAL19397 : addText moved to handleReturn() method
997 //if ( !txt.isEmpty() )
998 // addText( "", true );
1000 myPrompt = DOTS_PROMPT;
1001 addText( myPrompt/*, true*/ ); // IPAL19397
1002 // unset busy cursor
1004 // stop event loop (if running)
1006 myEventLoop->exit();
1010 QTextEdit::customEvent( event );
1013 // unset read-only state
1014 setReadOnly( false );
1015 // unset history browsing mode
1016 myCmdInHistory = -1;
1018 if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 )
1020 // process the next sheduled command from the queue (if there is any)
1021 QString nextcmd = myQueue[0];
1022 myQueue.pop_front();
1028 \brief Handle Python interpreter change.
1030 Perform initialization actions if the interpreter is changed.
1031 \param interp python interpreter is being set
1033 void PyConsole_EditorBase::onPyInterpChanged( PyConsole_Interp* interp )
1035 if ( myInterp != interp
1036 // Force read-only state and wait cursor when myInterp is NULL
1041 myBanner = myInterp->getbanner().c_str();
1042 if ( isShowBanner() )
1043 addText( myBanner );
1044 // clear command buffer
1045 myCommandBuffer.truncate(0);
1046 // unset read-only state
1047 setReadOnly( false );
1048 // unset history browsing mode
1049 myCmdInHistory = -1;
1051 addText( myPrompt );
1052 // unset busy cursor
1053 viewport()->unsetCursor();
1054 // stop event loop (if running)
1056 myEventLoop->exit();
1061 // set read-only state
1062 setReadOnly( true );
1064 setCursor( Qt::WaitCursor );
1070 \brief "Copy" operation.
1072 Reimplemented from Qt.
1073 Warning! In Qt4 this method is not virtual.
1075 void PyConsole_EditorBase::cut()
1077 QTextCursor cur = textCursor();
1078 if ( cur.hasSelection() ) {
1079 QApplication::clipboard()->setText( cur.selectedText() );
1080 int startSelection = cur.selectionStart();
1081 if ( startSelection < document()->end().previous().position() + promptSize() )
1082 startSelection = document()->end().previous().position() + promptSize();
1083 int endSelection = cur.selectionEnd();
1084 if ( endSelection < document()->end().previous().position() + promptSize() )
1085 endSelection = document()->end().previous().position() + promptSize();
1086 cur.setPosition( startSelection );
1087 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1088 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1089 setTextCursor( cur );
1090 textCursor().removeSelectedText();
1095 \brief "Paste" operation.
1097 Reimplemented from Qt.
1098 Warning! In Qt4 this method is not virtual.
1100 void PyConsole_EditorBase::paste()
1102 QTextCursor cur = textCursor();
1103 if ( cur.hasSelection() ) {
1104 int startSelection = cur.selectionStart();
1105 if ( startSelection < document()->end().previous().position() + promptSize() )
1106 startSelection = document()->end().previous().position() + promptSize();
1107 int endSelection = cur.selectionEnd();
1108 if ( endSelection < document()->end().previous().position() + promptSize() )
1109 endSelection = document()->end().previous().position() + promptSize();
1110 cur.setPosition( startSelection );
1111 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1112 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1113 setTextCursor( cur );
1114 textCursor().removeSelectedText();
1116 if ( textCursor().position() < document()->end().previous().position() + promptSize() )
1117 moveCursor( QTextCursor::End );
1122 \brief "Clear" operation.
1124 Reimplemented from Qt.
1125 Warning! In Qt4 this method is not virtual.
1127 void PyConsole_EditorBase::clear()
1130 if ( isShowBanner() )
1131 addText( myBanner );
1132 myPrompt = READY_PROMPT;
1133 addText( myPrompt );
1137 \brief "Dump commands" operation.
1139 void PyConsole_EditorBase::dumpImpl(const QString& fileName)
1141 if ( !fileName.isEmpty() ) {
1142 QFile file( fileName );
1143 if ( !file.open( QFile::WriteOnly ) )
1146 QTextStream out (&file);
1148 for ( int i=0; i<myHistory.count(); i++ ) {
1149 out << myHistory[i] << endl;
1156 \brief "Dump commands" operation.
1158 void PyConsole_EditorBase::dump(const QString& fileName)
1164 \brief "Dump commands" operation.
1166 void PyConsole_EditorBase::dump()
1171 void PyConsole_EditorBase::dumpSlot()
1173 QString fileName(QFileDialog::getSaveFileName(this,tr("Choose python file where to store"),QString(),tr("Python scripts ext (*.py)")));
1174 if ( !fileName.isEmpty() )
1175 this->dump( fileName );
1177 QMessageBox::warning(this,tr("WARNING"),tr("Python file has not been written"));
1181 \brief Start python trace logging
1182 \param fileName the path to the log file
1185 bool PyConsole_EditorBase::startLogImpl( const QString& fileName )
1188 if ( !fileName.isEmpty() ) {
1189 QFile file( fileName );
1190 if ( file.open( QFile::WriteOnly ) ) {
1192 myLogFile = fileName;
1201 \brief Start python trace logging
1202 \param fileName the path to the log file
1205 bool PyConsole_EditorBase::startLog( const QString& fileName )
1207 return startLogImpl(fileName);
1211 \brief Start python trace logging
1214 void PyConsole_EditorBase::startLog()
1219 void PyConsole_EditorBase::startLogSlot()
1223 QString fileName(QFileDialog::getSaveFileName(this,tr("Choose python file where to store log"),QString(),tr("Log files ext (*.log *.txt)")));
1224 if ( !fileName.isEmpty() )
1226 if ( startLogImpl( fileName ) )
1229 QMessageBox::warning(this,tr("WARNING"),tr("Log file is not writable"));
1237 \brief "Stop log" operation.
1240 void PyConsole_EditorBase::stopLog()
1242 myLogFile = QString();
1246 \brief Put string to the log file
1248 void PyConsole_EditorBase::putLog( const QString& s )
1250 if ( !myLogFile.isEmpty() ) {
1251 QFile file( myLogFile );
1252 if ( !file.open( QFile::Append ) )
1255 QTextStream out (&file);