3 \class PyConsole_Editor
4 \brief Python command line interpreter front-end GUI widget.
6 This class provides simple GUI interface to the Python interpreter, including basic
7 navigation operations, executing commands (both interactively and programmatically),
8 copy-paste operations, history of the commands and so on.
10 Here below is the shortcut keyboard boundings used for navigation and other operations:
11 - <Enter> : execute current command
12 - <Ctrl><Break> : clear current command
13 - <Escape> : clear current command
14 - <Up> : previous command in the history
15 - <Shift><Up> : move cursor one row up with selection
16 - <Ctrl><Up> : move cursor one row up without selection
17 - <Ctrl><Shift><Up> : move cursor one row up with selection
18 - <Down> : next command in the history
19 - <Shift><Down> : move cursor one row down with selection
20 - <Ctrl><Down> : move cursor one row down without selection
21 - <Ctrl><Shift><Down> : move cursor one row down with selection
22 - <Left> : move one symbol left without selection
23 - <Shift><Left> : move one symbol left with selection
24 - <Ctrl><Left> : move one word left without selection
25 - <Ctrl><Shift><Left> : move one word left with selection
26 - <Right> : move one symbol right without selection
27 - <Shift><Right> : move one symbol right with selection
28 - <Ctrl><Right> : move one word right without selection
29 - <Ctrl><Shift><Right> : move one word right with selection
30 - <PgUp> : first command in the history
31 - <Shift><PgUp> : move one page up with selection
32 - <Ctrl><PgUp> : move one page up without selection
33 - <Ctrl><Shift><PgUp> : scroll one page up
34 - <PgDn> : last command in the history
35 - <Shift><PgDn> : move one page down with selection
36 - <Ctrl><PgDn> : move one page down without selection
37 - <Ctrl><Shift><PgDn> : scroll one page down
38 - <Home> : move to the beginning of the line without selection
39 - <Shift><Home> : move to the beginning of the line with selection
40 - <Ctrl><Home> : move to the very first symbol without selection
41 - <Ctrl><Shift><Home> : move to the very first symbol with selection
42 - <End> : move to the end of the line without selection
43 - <Shift><End> : move to the end of the line with selection
44 - <Ctrl><End> : move to the very last symbol without selection
45 - <Ctrl><Shift><End> : move to the very last symbol with selection
46 - <Backspace> : delete symbol before the cursor
47 / remove selected text and put it to the clipboard (cut)
48 - <Shift><Backspace> : delete previous word
49 / remove selected text and put it to the clipboard (cut)
50 - <Ctrl><Backspace> : delete text from the cursor to the beginning of the line
51 / remove selected text and put it to the clipboard (cut)
52 - <Delete> : delete symbol after the cursor
53 / remove selected text and put it to the clipboard (cut)
54 - <Shift><Delete> : delete next word
55 / remove selected text and put it to the clipboard (cut)
56 - <Ctrl><Delete> : delete text from the cursor to the end of the line
57 / remove selected text and put it to the clipboard (cut)
58 - <Ctrl><Insert> : copy
59 - <Shift><Insert> : paste
66 #include "PyConsole_Interp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
67 #include "PyConsole_Editor.h"
68 #include "PyConsole_Event.h"
69 #include "PyInterp_Event.h"
70 #include "PyInterp_Dispatcher.h"
71 #include "PyConsole_Request.h"
73 //#include <SUIT_Tools.h>
74 //#include <SUIT_FileDlg.h>
75 //#include <SUIT_MessageBox.h>
76 //#include <SUIT_FileValidator.h>
78 #include <QApplication>
83 #include <QMouseEvent>
86 #include <QTextCursor>
87 #include <QTextDocument>
88 #include <QTextStream>
91 static QString READY_PROMPT = ">>> ";
92 static QString DOTS_PROMPT = "... ";
94 /*class DumpCommandsFileValidator : public SUIT_FileValidator
97 DumpCommandsFileValidator( QWidget* parent = 0 ) : SUIT_FileValidator ( parent ) {};
98 virtual ~DumpCommandsFileValidator() {};
99 virtual bool canSave( const QString& file, bool permissions );
102 bool DumpCommandsFileValidator::canSave(const QString& file, bool permissions)
104 QFileInfo fi( file );
105 if ( !QRegExp( "[A-Za-z_][A-Za-z0-9_]*" ).exactMatch( fi.completeBaseName() ) ) {
106 SUIT_MessageBox::critical( parent(),
107 QObject::tr("WRN_WARNING"),
108 QObject::tr("WRN_FILE_NAME_BAD") );
111 return SUIT_FileValidator::canSave( file, permissions);
114 void staticCallbackStdout( void* data, char* c )
116 if(!((PyConsole_Editor*)data)->isSuppressOutput())
117 QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c, false ) );
120 void staticCallbackStderr( void* data, char* c )
122 if(!((PyConsole_Editor*)data)->isSuppressOutput())
123 QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c, true ) );
130 Creates python editor window.
131 \param theInterp python interper
132 \param theParent parent widget
134 PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp,
136 : QTextEdit( theParent ),
138 myCmdInHistory( -1 ),
140 myShowBanner( true ),
142 myIsSuppressOutput( false )
144 //QString fntSet( "" );
145 QFont aFont = QFont( "Courier", 11 );//SUIT_Tools::stringToFont( fntSet );
147 setUndoRedoEnabled( false );
149 myPrompt = READY_PROMPT;
150 setLineWrapMode( QTextEdit::WidgetWidth );
151 setWordWrapMode( QTextOption::WrapAnywhere );
152 setAcceptRichText( false );
154 theInterp->setvoutcb( staticCallbackStdout, this );
155 theInterp->setverrcb( staticCallbackStderr, this );
157 // san - This is necessary for troubleless initialization
158 onPyInterpChanged( theInterp );
164 Does nothing for the moment.
166 PyConsole_Editor::~PyConsole_Editor()
171 \brief Get synchronous mode flag value.
174 \return True if python console works in synchronous mode
176 bool PyConsole_Editor::isSync() const
182 \brief Set synchronous mode flag value.
184 In synhronous mode the Python commands are executed in the GUI thread
185 and the GUI is blocked until the command is finished. In the asynchronous
186 mode each Python command is executed in the separate thread that does not
187 block the main GUI loop.
189 \param on synhronous mode flag
191 void PyConsole_Editor::setIsSync( const bool on )
197 \brief Get suppress output flag value.
199 \sa setIsSuppressOutput()
200 \return \c true if python console output is suppressed.
202 bool PyConsole_Editor::isSuppressOutput() const
204 return myIsSuppressOutput;
208 \brief Set suppress output flag value.
210 In case if suppress output flag is true, the python
211 console output suppressed.
213 \param on suppress output flag
215 void PyConsole_Editor::setIsSuppressOutput( const bool on )
217 myIsSuppressOutput = on;
221 \brief Get 'show banner' flag value.
223 \sa setIsShowBanner()
224 \return \c true if python console shows banner
226 bool PyConsole_Editor::isShowBanner() const
232 \brief Set 'show banner' flag value.
234 The banner is shown in the top of the python console window.
237 \param on 'show banner' flag
239 void PyConsole_Editor::setIsShowBanner( const bool on )
241 if ( myShowBanner != on ) {
248 \brief Get size hint for the Python console window
249 \return size hint value
251 QSize PyConsole_Editor::sizeHint() const
253 QFontMetrics fm( font() );
254 int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1;
255 QSize s(100, fm.lineSpacing()*nbLines);
260 \brief Put the string \a str to the python editor.
261 \param str string to be put in the command line of the editor
262 \param newBlock if True, then the string is printed on a new line
263 \param isError if true, the text is printed in dark red
265 void PyConsole_Editor::addText( const QString& str,
269 QTextCursor theCursor(textCursor());
272 moveCursor( QTextCursor::End );
274 theCursor.insertBlock();
276 cf.setForeground(QBrush(Qt::red));
278 cf.setForeground(QBrush(Qt::black));
279 theCursor.insertText( str, cf);
280 moveCursor( QTextCursor::End );
281 ensureCursorVisible();
285 \brief Convenient method for executing a Python command,
286 as if the user typed it manually.
287 \param command python command to be executed
289 !!! WARNING: doesn't work properly with multi-line commands. !!!
291 void PyConsole_Editor::exec( const QString& command )
293 if ( isReadOnly() ) {
294 // some interactive command is being executed in this editor...
295 // shedule the command to the queue
296 myQueue.push_back( command );
300 moveCursor( QTextCursor::End );
301 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
302 textCursor().removeSelectedText();
303 // set "ready" prompt
304 myPrompt = READY_PROMPT;
305 // clear command buffer
306 myCommandBuffer.truncate( 0 );
307 // unset history browsing mode
309 // print command line by line
310 QString cmd = command;
311 if ( !cmd.endsWith( "\n" ) ) cmd += "\n";
312 QStringList lines = command.split( "\n" );
313 for ( int i = 0; i < lines.size(); i++ ) {
314 if ( !lines[i].trimmed().isEmpty() )
315 myHistory.push_back( lines[i] );
316 addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 );
320 // set read-only mode
323 setCursor( Qt::BusyCursor );
325 // post a request to execute Python command;
326 // editor will be informed via a custom event that execution has been completed
327 PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) );
331 \brief Create request to the python dispatcher for the command execution.
333 \param command python command to be executed
335 PyInterp_Request* PyConsole_Editor::createRequest( const QString& command )
337 return new ExecCommand( myInterp, command, this, isSync() );
341 \brief Execute command in the python interpreter
342 and wait until it is finished.
344 \param command python command to be executed
346 void PyConsole_Editor::execAndWait( const QString& command )
352 // create new event loop
353 myEventLoop = new QEventLoop( this );
358 // delete event loop after command is processed
364 \brief Process "Enter" key press event.
366 Execute the command entered by the user.
368 void PyConsole_Editor::handleReturn()
370 // Position cursor at the end
371 QTextCursor curs(textCursor());
372 curs.movePosition(QTextCursor::End);
376 QTextBlock par = document()->end().previous();
377 if ( !par.isValid() ) return;
380 QString cmd = par.text().remove( 0, promptSize() );
381 // extend the command buffer with the current command
382 myCommandBuffer.append( cmd );
383 // add command to the history
384 if ( !cmd.trimmed().isEmpty() )
385 myHistory.push_back( cmd );
390 // set read-only mode
393 setCursor( Qt::BusyCursor );
395 // post a request to execute Python command;
396 // editor will be informed via a custom event that execution has been completed
397 PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) );
401 \brief Process drop event.
404 \param event drop event
406 void PyConsole_Editor::dropEvent( QDropEvent* event )
408 // get the initial drop position
409 QPoint pos = event->pos();
410 QTextCursor cur = cursorForPosition( event->pos() );
411 // if the position is not in the last line move it to the end of the command line
412 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
413 moveCursor( QTextCursor::End );
414 pos = cursorRect().center();
416 // create new drop event and use it instead of the original
418 event->possibleActions(),
420 event->mouseButtons(),
421 event->keyboardModifiers(),
423 QTextEdit::dropEvent( &de );
424 // accept the original event
425 event->acceptProposedAction();
429 \brief Process mouse button release event.
431 Left mouse button: copy selection to the clipboard.
432 Middle mouse button: paste clipboard's contents.
433 \param event mouse event
435 void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event )
437 if ( event->button() == Qt::LeftButton ) {
438 QTextEdit::mouseReleaseEvent( event );
441 else if ( event->button() == Qt::MidButton ) {
442 QTextCursor cur = cursorForPosition( event->pos() );
443 // if the position is not in the last line move it to the end of the command line
444 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
445 moveCursor( QTextCursor::End );
448 setTextCursor( cur );
450 const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ?
451 QClipboard::Selection : QClipboard::Clipboard );
453 insertFromMimeData( md );
456 QTextEdit::mouseReleaseEvent( event );
461 \brief Check if the string is command.
463 Return True if the string \a str is likely to be the command
464 (i.e. it is started from the '>>>' or '...').
465 \param str string to be checked
467 bool PyConsole_Editor::isCommand( const QString& str ) const
469 return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT );
473 \brief Handle keyboard event.
475 Implement navigation, history browsing, copy/paste and other common
477 \param event keyboard event
479 void PyConsole_Editor::keyPressEvent( QKeyEvent* event )
481 // get cursor position
482 QTextCursor cur = textCursor();
483 int curLine = cur.blockNumber();
484 int curCol = cur.columnNumber();
486 // get last edited line
487 int endLine = document()->blockCount()-1;
489 // get pressed key code
490 int aKey = event->key();
492 // check if <Ctrl> is pressed
493 bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
494 // check if <Shift> is pressed
495 bool shftPressed = event->modifiers() & Qt::ShiftModifier;
497 if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) {
498 // process <Ctrl>+<Break> key-binding and <Escape> key: clear current command
499 myCommandBuffer.truncate( 0 );
500 myPrompt = READY_PROMPT;
501 addText( myPrompt, true );
502 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
505 else if ( ctrlPressed && aKey == Qt::Key_C ) {
506 // process <Ctrl>+<C> key-binding : copy
510 else if ( ctrlPressed && aKey == Qt::Key_X ) {
511 // process <Ctrl>+<X> key-binding : cut
515 else if ( ctrlPressed && aKey == Qt::Key_V ) {
516 // process <Ctrl>+<V> key-binding : paste
521 // check for printed key
522 // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0;
524 aKey = !(QChar(aKey).isPrint()) ? aKey : 0;
528 // any printed key: just print it
530 if ( curLine < endLine || curCol < promptSize() ) {
531 moveCursor( QTextCursor::End );
533 QTextEdit::keyPressEvent( event );
538 // <Enter> key: process the current command
544 // <Up> arrow key: process as follows:
545 // - without <Ctrl>, <Shift> modifiers: previous command in history
546 // - with <Ctrl> modifier key pressed: move cursor one row up without selection
547 // - with <Shift> modifier key pressed: move cursor one row up with selection
548 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
550 if ( ctrlPressed && shftPressed ) {
551 int value = verticalScrollBar()->value();
552 int spacing = fontMetrics().lineSpacing();
553 verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 );
555 else if ( shftPressed || ctrlPressed ) {
557 moveCursor( QTextCursor::Up,
558 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
561 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
562 // set history browsing mode
563 myCmdInHistory = myHistory.count();
564 // remember current command
565 QTextBlock par = document()->end().previous();
566 myCurrentCommand = par.text().remove( 0, promptSize() );
568 if ( myCmdInHistory > 0 ) {
570 // get previous command in the history
571 QString previousCommand = myHistory.at( myCmdInHistory );
572 // print previous command
573 moveCursor( QTextCursor::End );
574 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
575 textCursor().removeSelectedText();
576 addText( myPrompt + previousCommand );
577 // move cursor to the end
578 moveCursor( QTextCursor::End );
584 // <Down> arrow key: process as follows:
585 // - without <Ctrl>, <Shift> modifiers: next command in history
586 // - with <Ctrl> modifier key pressed: move cursor one row down without selection
587 // - with <Shift> modifier key pressed: move cursor one row down with selection
588 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
590 if ( ctrlPressed && shftPressed ) {
591 int value = verticalScrollBar()->value();
592 int maxval = verticalScrollBar()->maximum();
593 int spacing = fontMetrics().lineSpacing();
594 verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval );
596 else if ( shftPressed || ctrlPressed) {
597 if ( curLine < endLine )
598 moveCursor( QTextCursor::Down,
599 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
602 if ( myCmdInHistory >= 0 ) {
603 // get next command in the history
606 if ( myCmdInHistory < myHistory.count() ) {
607 // next command in history
608 nextCommand = myHistory.at( myCmdInHistory );
611 // end of history is reached
612 // last printed command
613 nextCommand = myCurrentCommand;
614 // unset history browsing mode
617 // print next or current command
618 moveCursor( QTextCursor::End );
619 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
620 textCursor().removeSelectedText();
621 addText( myPrompt + nextCommand );
622 // move cursor to the end
623 moveCursor( QTextCursor::End );
629 // <Left> arrow key: process as follows:
630 // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
631 // - with <Ctrl> modifier key pressed: move one word left (taking into account prompt)
632 // - with <Shift> modifier key pressed: move one symbol left with selection
633 // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
635 QString txt = textCursor().block().text();
636 if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) {
637 moveCursor( QTextCursor::Up );
638 moveCursor( QTextCursor::EndOfBlock );
641 QTextEdit::keyPressEvent( event );
646 // <Right> arrow key: process as follows:
647 // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
648 // - with <Ctrl> modifier key pressed: move one word right (taking into account prompt)
649 // - with <Shift> modifier key pressed: move one symbol right with selection
650 // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
652 QString txt = textCursor().block().text();
653 if ( !shftPressed ) {
654 if ( curCol < txt.length() ) {
655 if ( isCommand( txt ) && curCol < promptSize() ) {
656 cur.setPosition( cur.block().position() + promptSize() );
657 setTextCursor( cur );
662 if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) {
663 cur.setPosition( cur.position() + promptSize()+1 );
664 setTextCursor( cur );
665 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
670 QTextEdit::keyPressEvent( event );
674 // <PageUp> key: process as follows:
675 // - without <Ctrl>, <Shift> modifiers: first command in history
676 // - with <Ctrl> modifier key pressed: move cursor one page up without selection
677 // - with <Shift> modifier key pressed: move cursor one page up with selection
678 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
680 if ( ctrlPressed && shftPressed ) {
681 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
683 else if ( shftPressed || ctrlPressed ) {
685 qreal lastY = cursorRect( cur ).top();
687 // move using movePosition to keep the cursor's x
689 qreal y = cursorRect( cur ).top();
690 distance += qAbs( y - lastY );
692 moved = cur.movePosition( QTextCursor::Up,
693 shftPressed ? QTextCursor::KeepAnchor :
694 QTextCursor::MoveAnchor );
695 } while ( moved && distance < viewport()->height() );
697 cur.movePosition( QTextCursor::Down,
698 shftPressed ? QTextCursor::KeepAnchor :
699 QTextCursor::MoveAnchor );
700 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
702 setTextCursor( cur );
705 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
706 // set history browsing mode
707 myCmdInHistory = myHistory.count();
708 // remember current command
709 QTextBlock par = document()->end().previous();
710 myCurrentCommand = par.text().remove( 0, promptSize() );
712 if ( myCmdInHistory > 0 ) {
714 // get very first command in the history
715 QString firstCommand = myHistory.at( myCmdInHistory );
716 // print first command
717 moveCursor( QTextCursor::End );
718 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
719 textCursor().removeSelectedText();
720 addText( myPrompt + firstCommand );
721 // move cursor to the end
722 moveCursor( QTextCursor::End );
727 case Qt::Key_PageDown:
728 // <PageDown> key: process as follows:
729 // - without <Ctrl>, <Shift> modifiers: last command in history
730 // - with <Ctrl> modifier key pressed: move cursor one page down without selection
731 // - with <Shift> modifier key pressed: move cursor one page down with selection
732 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
734 if ( ctrlPressed && shftPressed ) {
735 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
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::Down,
747 shftPressed ? QTextCursor::KeepAnchor :
748 QTextCursor::MoveAnchor );
749 } while ( moved && distance < viewport()->height() );
751 cur.movePosition( QTextCursor::Up,
752 shftPressed ? QTextCursor::KeepAnchor :
753 QTextCursor::MoveAnchor );
754 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
756 setTextCursor( cur );
759 if ( myCmdInHistory >= 0 ) {
760 // unset history browsing mode
762 // print current command
763 moveCursor( QTextCursor::End );
764 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
765 textCursor().removeSelectedText();
766 addText( myPrompt + myCurrentCommand );
767 // move cursor to the end
768 moveCursor( QTextCursor::End );
774 // <Home> key: process as follows:
775 // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
776 // - with <Ctrl> modifier key pressed: move cursor to the very first symbol without selection
777 // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
778 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
781 moveCursor( QTextCursor::Start,
782 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
785 QString txt = textCursor().block().text();
786 if ( isCommand( txt ) ) {
788 if ( curCol > promptSize() ) {
789 cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor );
790 cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() );
794 cur.movePosition( QTextCursor::StartOfLine );
795 cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() );
797 setTextCursor( cur );
800 moveCursor( QTextCursor::StartOfBlock,
801 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
803 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
808 // <End> key: process as follows:
809 // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
810 // - with <Ctrl> modifier key pressed: move cursor to the very last symbol without selection
811 // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
812 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
814 moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock,
815 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
818 case Qt::Key_Backspace :
819 // <Backspace> key: process as follows
820 // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
821 // - with <Shift> modifier key pressed: delete previous word
822 // - with <Ctrl> modifier key pressed: delete text from the cursor to the line beginning
823 // works only for last (command) line
825 if ( cur.hasSelection() ) {
828 else if ( cur.position() > document()->end().previous().position() + promptSize() ) {
830 moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor );
831 textCursor().removeSelectedText();
833 else if ( ctrlPressed ) {
834 cur.setPosition( document()->end().previous().position() + promptSize(),
835 QTextCursor::KeepAnchor );
836 setTextCursor( cur );
837 textCursor().removeSelectedText();
840 QTextEdit::keyPressEvent( event );
844 cur.setPosition( document()->end().previous().position() + promptSize() );
845 setTextCursor( cur );
846 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
850 case Qt::Key_Delete :
851 // <Delete> key: process as follows
852 // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
853 // - with <Shift> modifier key pressed: delete next word
854 // - with <Ctrl> modifier key pressed: delete text from the cursor to the end of line
855 // works only for last (command) line
857 if ( cur.hasSelection() ) {
860 else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) {
862 moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor );
863 textCursor().removeSelectedText();
865 else if ( ctrlPressed ) {
866 moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
867 textCursor().removeSelectedText();
870 QTextEdit::keyPressEvent( event );
874 cur.setPosition( document()->end().previous().position() + promptSize() );
875 setTextCursor( cur );
876 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
880 case Qt::Key_Insert :
881 // <Insert> key: process as follows
882 // - with <Ctrl> modifier key pressed: copy()
883 // - with <Shift> modifier key pressed: paste() to the command line
888 else if ( shftPressed ) {
892 QTextEdit::keyPressEvent( event );
899 \brief Handle notification event coming from Python dispatcher.
900 \param event notification event
902 void PyConsole_Editor::customEvent( QEvent* event )
904 switch( event->type() )
906 case PrintEvent::EVENT_ID:
908 PrintEvent* pe=(PrintEvent*)event;
909 addText( pe->text(), false, pe->isError());
912 case PyInterp_Event::ES_OK:
913 case PyInterp_Event::ES_ERROR:
915 // clear command buffer
916 myCommandBuffer.truncate( 0 );
917 // add caret return line if necessary
918 QTextBlock par = document()->end().previous();
919 QString txt = par.text();
920 txt.truncate( txt.length() - 1 );
921 // IPAL19397 : addText moved to handleReturn() method
922 //if ( !txt.isEmpty() )
923 // addText( "", true );
924 // set "ready" prompt
925 myPrompt = READY_PROMPT;
929 // stop event loop (if running)
934 case PyInterp_Event::ES_INCOMPLETE:
936 // extend command buffer (multi-line command)
937 myCommandBuffer.append( "\n" );
938 // add caret return line if necessary
939 QTextBlock par = document()->end().previous();
940 QString txt = par.text();
941 txt.truncate( txt.length() - 1 );
942 // IPAL19397 : addText moved to handleReturn() method
943 //if ( !txt.isEmpty() )
944 // addText( "", true );
946 myPrompt = DOTS_PROMPT;
947 addText( myPrompt/*, true*/ ); // IPAL19397
950 // stop event loop (if running)
956 QTextEdit::customEvent( event );
959 // unset read-only state
960 setReadOnly( false );
961 // unset history browsing mode
964 if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 )
966 // process the next sheduled command from the queue (if there is any)
967 QString nextcmd = myQueue[0];
974 \brief Handle Python interpreter change.
976 Perform initialization actions if the interpreter is changed.
977 \param interp python interpreter is being set
979 void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp )
981 if ( myInterp != interp
982 // Force read-only state and wait cursor when myInterp is NULL
987 myBanner = myInterp->getbanner().c_str();
988 if ( isShowBanner() )
990 // clear command buffer
991 myCommandBuffer.truncate(0);
992 // unset read-only state
993 setReadOnly( false );
994 // unset history browsing mode
999 viewport()->unsetCursor();
1000 // stop event loop (if running)
1002 myEventLoop->exit();
1007 // set read-only state
1008 setReadOnly( true );
1010 setCursor( Qt::WaitCursor );
1016 \brief "Copy" operation.
1018 Reimplemented from Qt.
1019 Warning! In Qt4 this method is not virtual.
1021 void PyConsole_Editor::cut()
1023 QTextCursor cur = textCursor();
1024 if ( cur.hasSelection() ) {
1025 QApplication::clipboard()->setText( cur.selectedText() );
1026 int startSelection = cur.selectionStart();
1027 if ( startSelection < document()->end().previous().position() + promptSize() )
1028 startSelection = document()->end().previous().position() + promptSize();
1029 int endSelection = cur.selectionEnd();
1030 if ( endSelection < document()->end().previous().position() + promptSize() )
1031 endSelection = document()->end().previous().position() + promptSize();
1032 cur.setPosition( startSelection );
1033 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1034 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1035 setTextCursor( cur );
1036 textCursor().removeSelectedText();
1041 \brief "Paste" operation.
1043 Reimplemented from Qt.
1044 Warning! In Qt4 this method is not virtual.
1046 void PyConsole_Editor::paste()
1048 QTextCursor cur = textCursor();
1049 if ( cur.hasSelection() ) {
1050 int startSelection = cur.selectionStart();
1051 if ( startSelection < document()->end().previous().position() + promptSize() )
1052 startSelection = document()->end().previous().position() + promptSize();
1053 int endSelection = cur.selectionEnd();
1054 if ( endSelection < document()->end().previous().position() + promptSize() )
1055 endSelection = document()->end().previous().position() + promptSize();
1056 cur.setPosition( startSelection );
1057 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1058 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1059 setTextCursor( cur );
1060 textCursor().removeSelectedText();
1062 if ( textCursor().position() < document()->end().previous().position() + promptSize() )
1063 moveCursor( QTextCursor::End );
1068 \brief "Clear" operation.
1070 Reimplemented from Qt.
1071 Warning! In Qt4 this method is not virtual.
1073 void PyConsole_Editor::clear()
1076 if ( isShowBanner() )
1077 addText( myBanner );
1078 myPrompt = READY_PROMPT;
1079 addText( myPrompt );
1083 \brief "Dump commands" operation.
1085 /*void PyConsole_Editor::dump()
1087 QStringList aFilters;
1088 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
1090 QString fileName = SUIT_FileDlg::getFileName( this, QString(),
1091 aFilters, tr( "TOT_DUMP_PYCOMMANDS" ),
1092 false, true, new DumpCommandsFileValidator( this ) );
1093 if ( fileName != "" ) {
1094 QFile file( fileName );
1095 if ( !file.open( QFile::WriteOnly ) )
1098 QTextStream out (&file);
1100 for( int i=0; i<myHistory.count(); i++ ) {
1101 out<<myHistory[i]<<endl;