1 // Copyright (C) 2007-2008 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.
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
22 // SALOME SALOMEGUI : implementation of desktop and GUI kernel
23 // File : PyConsole_Editor.cxx
24 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
27 \class PyConsole_Editor
28 \brief Python command line interpreter front-end GUI widget.
30 This class provides simple GUI interface to the Python interpreter, including basic
31 navigation operations, executing commands (both interactively and programmatically),
32 copy-paste operations, history of the commands and so on.
34 Here below is the shortcut keyboard boundings used for navigation and other operations:
35 - <Enter> : execute current command
36 - <Ctrl><Break> : clear current command
37 - <Escape> : clear current command
38 - <Up> : previous command in the history
39 - <Shift><Up> : move cursor one row up with selection
40 - <Ctrl><Up> : move cursor one row up without selection
41 - <Ctrl><Shift><Up> : move cursor one row up with selection
42 - <Down> : next command in the history
43 - <Shift><Down> : move cursor one row down with selection
44 - <Ctrl><Down> : move cursor one row down without selection
45 - <Ctrl><Shift><Down> : move cursor one row down with selection
46 - <Left> : move one symbol left without selection
47 - <Shift><Left> : move one symbol left with selection
48 - <Ctrl><Left> : move one word left without selection
49 - <Ctrl><Shift><Left> : move one word left with selection
50 - <Right> : move one symbol right without selection
51 - <Shift><Right> : move one symbol right with selection
52 - <Ctrl><Right> : move one word right without selection
53 - <Ctrl><Shift><Right> : move one word right with selection
54 - <PgUp> : first command in the history
55 - <Shift><PgUp> : move one page up with selection
56 - <Ctrl><PgUp> : move one page up without selection
57 - <Ctrl><Shift><PgUp> : scroll one page up
58 - <PgDn> : last command in the history
59 - <Shift><PgDn> : move one page down with selection
60 - <Ctrl><PgDn> : move one page down without selection
61 - <Ctrl><Shift><PgDn> : scroll one page down
62 - <Home> : move to the beginning of the line without selection
63 - <Shift><Home> : move to the beginning of the line with selection
64 - <Ctrl><Home> : move to the very first symbol without selection
65 - <Ctrl><Shift><Home> : move to the very first symbol with selection
66 - <End> : move to the end of the line without selection
67 - <Shift><End> : move to the end of the line with selection
68 - <Ctrl><End> : move to the very last symbol without selection
69 - <Ctrl><Shift><End> : move to the very last symbol with selection
70 - <Backspace> : delete symbol before the cursor
71 / remove selected text and put it to the clipboard (cut)
72 - <Shift><Backspace> : delete previous word
73 / remove selected text and put it to the clipboard (cut)
74 - <Ctrl><Backspace> : delete text from the cursor to the beginning of the line
75 / remove selected text and put it to the clipboard (cut)
76 - <Delete> : delete symbol after the cursor
77 / remove selected text and put it to the clipboard (cut)
78 - <Shift><Delete> : delete next word
79 / remove selected text and put it to the clipboard (cut)
80 - <Ctrl><Delete> : delete text from the cursor to the end of the line
81 / remove selected text and put it to the clipboard (cut)
82 - <Ctrl><Insert> : copy
83 - <Shift><Insert> : paste
90 - paste multiline text: process each line as separate command
91 (including mouse middle-button click pasting)
92 - the same for drag-n-drop of multiline text
95 #include "PyConsole_Interp.h" // !!! WARNING !!! THIS INCLUDE MUST BE THE VERY FIRST !!!
96 #include "PyConsole_Editor.h"
97 #include <PyInterp_Dispatcher.h>
99 #include <SUIT_Tools.h>
101 #include <QApplication>
102 #include <QClipboard>
103 #include <QDropEvent>
106 #include <QMouseEvent>
107 #include <QScrollBar>
108 #include <QTextBlock>
109 #include <QTextCursor>
110 #include <QTextDocument>
112 static QString READY_PROMPT = ">>> ";
113 static QString DOTS_PROMPT = "... ";
114 #define PROMPT_SIZE myPrompt.length()
116 #define PRINT_EVENT 65432
120 \brief Python command execution request.
124 class ExecCommand : public PyInterp_LockRequest
130 Creates new python command execution request.
131 \param theInterp python interpreter
132 \param theCommand python command
133 \param theListener widget to get the notification messages
134 \param sync if True the request is processed synchronously
136 ExecCommand( PyInterp_Interp* theInterp,
137 const QString& theCommand,
138 PyConsole_Editor* theListener,
140 : PyInterp_LockRequest( theInterp, theListener, sync ),
141 myCommand( theCommand ), myState( PyInterp_Event::ES_OK )
146 \brief Execute the python command in the interpreter and
147 get its execution status.
149 virtual void execute()
151 if ( myCommand != "" )
153 int ret = getInterp()->run( myCommand.toLatin1() );
155 myState = PyInterp_Event::ES_ERROR;
157 myState = PyInterp_Event::ES_INCOMPLETE;
162 \brief Create and return a notification event.
163 \return new notification event
165 virtual QEvent* createEvent() const
168 QCoreApplication::sendPostedEvents( listener(), PRINT_EVENT );
169 return new PyInterp_Event( myState, (PyInterp_Request*)this );
173 QString myCommand; //!< Python command
174 int myState; //!< Python command execution status
179 \brief Python command output backend event.
183 class PrintEvent : public QEvent
188 \param c message text (python trace)
190 PrintEvent( const char* c ) : QEvent( (QEvent::Type)PRINT_EVENT ), myText( c ) {}
193 \return message text (python trace)
195 QString text() const { return myText; }
198 QString myText; //!< Event message (python trace)
201 void staticCallback( void* data, char* c )
203 QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c ) );
209 Creates python editor window.
210 \param theInterp python interper
211 \param theParent parent widget
213 PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp,
215 : QTextEdit( theParent ),
217 myCmdInHistory( -1 ),
221 QString fntSet( "" );
222 QFont aFont = SUIT_Tools::stringToFont( fntSet );
224 setUndoRedoEnabled( false );
226 myPrompt = READY_PROMPT;
227 setLineWrapMode( QTextEdit::WidgetWidth );
228 setWordWrapMode( QTextOption::WrapAnywhere );
229 setAcceptRichText( false );
231 theInterp->setvoutcb( staticCallback, this );
232 theInterp->setverrcb( staticCallback, this );
234 // san - This is necessary for troubleless initialization
235 onPyInterpChanged( theInterp );
241 Does nothing for the moment.
243 PyConsole_Editor::~PyConsole_Editor()
248 \brief Get synchronous mode flag value.
251 \return True if python console works in synchronous mode
253 bool PyConsole_Editor::isSync() const
259 \brief Set synchronous mode flag value.
261 In synhronous mode the Python commands are executed in the GUI thread
262 and the GUI is blocked until the command is finished. In the asynchronous
263 mode each Python command is executed in the separate thread that does not
264 block the main GUI loop.
266 \param on synhronous mode flag
268 void PyConsole_Editor::setIsSync( const bool on )
274 \brief Put the string \a str to the python editor.
275 \param str string to be put in the command line of the editor
276 \param newBlock if True, then the string is printed on a new line
278 void PyConsole_Editor::addText( const QString& str,
279 const bool newBlock )
281 moveCursor( QTextCursor::End );
283 textCursor().insertBlock();
284 textCursor().insertText( str );
285 moveCursor( QTextCursor::End );
286 ensureCursorVisible();
290 \brief Convenient method for executing a Python command,
291 as if the user typed it manually.
292 \param command python command to be executed
294 void PyConsole_Editor::exec( const QString& command )
296 if ( isReadOnly() ) {
297 // some interactive command is being executed in this editor...
298 // shedule the command to the queue
299 myQueue.push_back( command );
303 moveCursor( QTextCursor::End );
304 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
305 textCursor().removeSelectedText();
306 // set "ready" prompt
307 myPrompt = READY_PROMPT;
308 // clear command buffer
309 myCommandBuffer.truncate( 0 );
310 // unset history browsing mode
312 // print command line by line
313 QString cmd = command;
314 if ( !cmd.endsWith( "\n" ) ) cmd += "\n";
315 QStringList lines = command.split( "\n" );
316 for ( int i = 0; i < lines.size(); i++ ) {
317 if ( !lines[i].trimmed().isEmpty() )
318 myHistory.push_back( lines[i] );
319 addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 );
323 // set read-only mode
326 setCursor( Qt::BusyCursor );
328 // post a request to execute Python command;
329 // editor will be informed via a custom event that execution has been completed
330 PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) );
334 \brief Create request to the python dispatcher for the command execution.
336 \param command python command to be executed
338 PyInterp_Request* PyConsole_Editor::createRequest( const QString& command )
340 return new ExecCommand( myInterp, command, this, isSync() );
344 \brief Execute command in the python interpreter
345 and wait until it is finished.
347 \param command python command to be executed
349 void PyConsole_Editor::execAndWait( const QString& command )
355 // create new event loop
356 myEventLoop = new QEventLoop( this );
361 // delete event loop after command is processed
367 \brief Process "Enter" key press event.
369 Execute the command entered by the user.
371 void PyConsole_Editor::handleReturn()
374 QTextBlock par = document()->end().previous();
375 if ( !par.isValid() ) return;
378 QString cmd = par.text().remove( 0, PROMPT_SIZE );
379 // extend the command buffer with the current command
380 myCommandBuffer.append( cmd );
381 // add command to the history
382 if ( !cmd.trimmed().isEmpty() )
383 myHistory.push_back( cmd );
388 // set read-only mode
391 setCursor( Qt::BusyCursor );
393 // post a request to execute Python command;
394 // editor will be informed via a custom event that execution has been completed
395 PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) );
399 \brief Process drop event.
402 \param event drop event
404 void PyConsole_Editor::dropEvent( QDropEvent* event )
406 // get the initial drop position
407 QPoint pos = event->pos();
408 QTextCursor cur = cursorForPosition( event->pos() );
409 // if the position is not in the last line move it to the end of the command line
410 if ( cur.position() < document()->end().previous().position() + PROMPT_SIZE ) {
411 moveCursor( QTextCursor::End );
412 pos = cursorRect().center();
414 // create new drop event and use it instead of the original
416 event->possibleActions(),
418 event->mouseButtons(),
419 event->keyboardModifiers(),
421 QTextEdit::dropEvent( &de );
422 // accept the original event
423 event->acceptProposedAction();
427 \brief Process mouse button release event.
429 Left mouse button: copy selection to the clipboard.
430 Middle mouse button: paste clipboard's contents.
431 \param event mouse event
433 void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event )
435 if ( event->button() == Qt::LeftButton ) {
436 QTextEdit::mouseReleaseEvent( event );
439 else if ( event->button() == Qt::MidButton ) {
441 if ( QApplication::clipboard()->supportsSelection() )
442 text = QApplication::clipboard()->text( QClipboard::Selection );
443 if ( text.isEmpty() )
444 text = QApplication::clipboard()->text( QClipboard::Clipboard );
445 QTextCursor cur = cursorForPosition( event->pos() );
446 // if the position is not in the last line move it to the end of the command line
447 if ( cur.position() < document()->end().previous().position() + PROMPT_SIZE ) {
448 moveCursor( QTextCursor::End );
451 setTextCursor( cur );
453 textCursor().clearSelection();
454 textCursor().insertText( text );
457 QTextEdit::mouseReleaseEvent( event );
462 \brief Check if the string is command.
464 Return True if the string \a str is likely to be the command
465 (i.e. it is started from the '>>>' or '...').
466 \param str string to be checked
468 bool PyConsole_Editor::isCommand( const QString& str ) const
470 return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT );
474 \brief Handle keyboard event.
476 Implement navigation, history browsing, copy/paste and other common
478 \param event keyboard event
480 void PyConsole_Editor::keyPressEvent( QKeyEvent* event )
482 // get cursor position
483 QTextCursor cur = textCursor();
484 int curLine = cur.blockNumber();
485 int curCol = cur.columnNumber();
487 // get last edited line
488 int endLine = document()->blockCount()-1;
490 // get pressed key code
491 int aKey = event->key();
493 // check if <Ctrl> is pressed
494 bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
495 // check if <Shift> is pressed
496 bool shftPressed = event->modifiers() & Qt::ShiftModifier;
498 if ( aKey == Qt::Key_Escape || ctrlPressed && aKey == -1 ) {
499 // process <Ctrl>+<Break> key-binding and <Escape> key: clear current command
500 myCommandBuffer.truncate( 0 );
501 myPrompt = READY_PROMPT;
502 addText( myPrompt, true );
503 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
506 else if ( ctrlPressed && aKey == Qt::Key_C ) {
507 // process <Ctrl>+<C> key-binding : copy
511 else if ( ctrlPressed && aKey == Qt::Key_X ) {
512 // process <Ctrl>+<X> key-binding : cut
516 else if ( ctrlPressed && aKey == Qt::Key_V ) {
517 // process <Ctrl>+<V> key-binding : paste
522 // check for printed key
523 aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0;
527 // any printed key: just print it
529 if ( curLine < endLine || curCol < PROMPT_SIZE ) {
530 moveCursor( QTextCursor::End );
532 QTextEdit::keyPressEvent( event );
537 // <Enter> key: process the current command
543 // <Up> arrow key: process as follows:
544 // - without <Ctrl>, <Shift> modifiers: previous command in history
545 // - with <Ctrl> modifier key pressed: move cursor one row up without selection
546 // - with <Shift> modifier key pressed: move cursor one row up with selection
547 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
549 if ( ctrlPressed && shftPressed ) {
550 int value = verticalScrollBar()->value();
551 int spacing = fontMetrics().lineSpacing();
552 verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 );
554 else if ( shftPressed || ctrlPressed ) {
556 moveCursor( QTextCursor::Up,
557 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
560 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
561 // set history browsing mode
562 myCmdInHistory = myHistory.count();
563 // remember current command
564 QTextBlock par = document()->end().previous();
565 myCurrentCommand = par.text().remove( 0, PROMPT_SIZE );
567 if ( myCmdInHistory > 0 ) {
569 // get previous command in the history
570 QString previousCommand = myHistory.at( myCmdInHistory );
571 // print previous command
572 moveCursor( QTextCursor::End );
573 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
574 textCursor().removeSelectedText();
575 addText( myPrompt + previousCommand );
576 // move cursor to the end
577 moveCursor( QTextCursor::End );
583 // <Down> arrow key: process as follows:
584 // - without <Ctrl>, <Shift> modifiers: next command in history
585 // - with <Ctrl> modifier key pressed: move cursor one row down without selection
586 // - with <Shift> modifier key pressed: move cursor one row down with selection
587 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
589 if ( ctrlPressed && shftPressed ) {
590 int value = verticalScrollBar()->value();
591 int maxval = verticalScrollBar()->maximum();
592 int spacing = fontMetrics().lineSpacing();
593 verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval );
595 else if ( shftPressed || ctrlPressed) {
596 if ( curLine < endLine )
597 moveCursor( QTextCursor::Down,
598 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
601 if ( myCmdInHistory >= 0 ) {
602 // get next command in the history
605 if ( myCmdInHistory < myHistory.count() ) {
606 // next command in history
607 nextCommand = myHistory.at( myCmdInHistory );
610 // end of history is reached
611 // last printed command
612 nextCommand = myCurrentCommand;
613 // unset history browsing mode
616 // print next or current command
617 moveCursor( QTextCursor::End );
618 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
619 textCursor().removeSelectedText();
620 addText( myPrompt + nextCommand );
621 // move cursor to the end
622 moveCursor( QTextCursor::End );
628 // <Left> arrow key: process as follows:
629 // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
630 // - with <Ctrl> modifier key pressed: move one word left (taking into account prompt)
631 // - with <Shift> modifier key pressed: move one symbol left with selection
632 // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
634 QString txt = textCursor().block().text();
635 if ( !shftPressed && isCommand( txt ) && curCol <= PROMPT_SIZE ) {
636 moveCursor( QTextCursor::Up );
637 moveCursor( QTextCursor::EndOfBlock );
640 QTextEdit::keyPressEvent( event );
645 // <Right> arrow key: process as follows:
646 // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
647 // - with <Ctrl> modifier key pressed: move one word right (taking into account prompt)
648 // - with <Shift> modifier key pressed: move one symbol right with selection
649 // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
651 QString txt = textCursor().block().text();
652 if ( !shftPressed ) {
653 if ( curCol < txt.length() ) {
654 if ( isCommand( txt ) && curCol < PROMPT_SIZE ) {
655 cur.setPosition( cur.block().position() + PROMPT_SIZE );
656 setTextCursor( cur );
661 if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) {
662 cur.setPosition( cur.position() + PROMPT_SIZE+1 );
663 setTextCursor( cur );
664 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
669 QTextEdit::keyPressEvent( event );
673 // <PageUp> key: process as follows:
674 // - without <Ctrl>, <Shift> modifiers: first command in history
675 // - with <Ctrl> modifier key pressed: move cursor one page up without selection
676 // - with <Shift> modifier key pressed: move cursor one page up with selection
677 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
679 if ( ctrlPressed && shftPressed ) {
680 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
682 else if ( shftPressed || ctrlPressed ) {
684 qreal lastY = cursorRect( cur ).top();
686 // move using movePosition to keep the cursor's x
688 qreal y = cursorRect( cur ).top();
689 distance += qAbs( y - lastY );
691 moved = cur.movePosition( QTextCursor::Up,
692 shftPressed ? QTextCursor::KeepAnchor :
693 QTextCursor::MoveAnchor );
694 } while ( moved && distance < viewport()->height() );
696 cur.movePosition( QTextCursor::Down,
697 shftPressed ? QTextCursor::KeepAnchor :
698 QTextCursor::MoveAnchor );
699 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
701 setTextCursor( cur );
704 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
705 // set history browsing mode
706 myCmdInHistory = myHistory.count();
707 // remember current command
708 QTextBlock par = document()->end().previous();
709 myCurrentCommand = par.text().remove( 0, PROMPT_SIZE );
711 if ( myCmdInHistory > 0 ) {
713 // get very first command in the history
714 QString firstCommand = myHistory.at( myCmdInHistory );
715 // print first command
716 moveCursor( QTextCursor::End );
717 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
718 textCursor().removeSelectedText();
719 addText( myPrompt + firstCommand );
720 // move cursor to the end
721 moveCursor( QTextCursor::End );
726 case Qt::Key_PageDown:
727 // <PageDown> key: process as follows:
728 // - without <Ctrl>, <Shift> modifiers: last command in history
729 // - with <Ctrl> modifier key pressed: move cursor one page down without selection
730 // - with <Shift> modifier key pressed: move cursor one page down with selection
731 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
733 if ( ctrlPressed && shftPressed ) {
734 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
736 else if ( shftPressed || ctrlPressed ) {
738 qreal lastY = cursorRect( cur ).top();
740 // move using movePosition to keep the cursor's x
742 qreal y = cursorRect( cur ).top();
743 distance += qAbs( y - lastY );
745 moved = cur.movePosition( QTextCursor::Down,
746 shftPressed ? QTextCursor::KeepAnchor :
747 QTextCursor::MoveAnchor );
748 } while ( moved && distance < viewport()->height() );
750 cur.movePosition( QTextCursor::Up,
751 shftPressed ? QTextCursor::KeepAnchor :
752 QTextCursor::MoveAnchor );
753 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
755 setTextCursor( cur );
758 if ( myCmdInHistory >= 0 ) {
759 // unset history browsing mode
761 // print current command
762 moveCursor( QTextCursor::End );
763 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
764 textCursor().removeSelectedText();
765 addText( myPrompt + myCurrentCommand );
766 // move cursor to the end
767 moveCursor( QTextCursor::End );
773 // <Home> key: process as follows:
774 // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
775 // - with <Ctrl> modifier key pressed: move cursor to the very first symbol without selection
776 // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
777 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
780 moveCursor( QTextCursor::Start,
781 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
784 QString txt = textCursor().block().text();
785 if ( isCommand( txt ) ) {
787 if ( curCol > PROMPT_SIZE ) {
788 cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor );
789 cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, PROMPT_SIZE );
793 cur.movePosition( QTextCursor::StartOfLine );
794 cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, PROMPT_SIZE );
796 setTextCursor( cur );
799 moveCursor( QTextCursor::StartOfBlock,
800 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
802 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
807 // <End> key: process as follows:
808 // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
809 // - with <Ctrl> modifier key pressed: move cursor to the very last symbol without selection
810 // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
811 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
813 moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock,
814 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
817 case Qt::Key_Backspace :
818 // <Backspace> key: process as follows
819 // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
820 // - with <Shift> modifier key pressed: delete previous word
821 // - with <Ctrl> modifier key pressed: delete text from the cursor to the line beginning
822 // works only for last (command) line
824 if ( cur.hasSelection() ) {
827 else if ( cur.position() > document()->end().previous().position() + PROMPT_SIZE ) {
829 moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor );
830 textCursor().removeSelectedText();
832 else if ( ctrlPressed ) {
833 cur.setPosition( document()->end().previous().position() + PROMPT_SIZE,
834 QTextCursor::KeepAnchor );
835 setTextCursor( cur );
836 textCursor().removeSelectedText();
839 QTextEdit::keyPressEvent( event );
843 cur.setPosition( document()->end().previous().position() + PROMPT_SIZE );
844 setTextCursor( cur );
845 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
849 case Qt::Key_Delete :
850 // <Delete> key: process as follows
851 // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
852 // - with <Shift> modifier key pressed: delete next word
853 // - with <Ctrl> modifier key pressed: delete text from the cursor to the end of line
854 // works only for last (command) line
856 if ( cur.hasSelection() ) {
859 else if ( cur.position() > document()->end().previous().position() + PROMPT_SIZE-1 ) {
861 moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor );
862 textCursor().removeSelectedText();
864 else if ( ctrlPressed ) {
865 moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
866 textCursor().removeSelectedText();
869 QTextEdit::keyPressEvent( event );
873 cur.setPosition( document()->end().previous().position() + PROMPT_SIZE );
874 setTextCursor( cur );
875 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
879 case Qt::Key_Insert :
880 // <Insert> key: process as follows
881 // - with <Ctrl> modifier key pressed: copy()
882 // - with <Shift> modifier key pressed: paste() to the command line
887 else if ( shftPressed ) {
891 QTextEdit::keyPressEvent( event );
898 \brief Handle notification event coming from Python dispatcher.
899 \param event notification event
901 void PyConsole_Editor::customEvent( QEvent* event )
903 switch( event->type() )
907 PrintEvent* pe=(PrintEvent*)event;
908 addText( pe->text() );
911 case PyInterp_Event::ES_OK:
912 case PyInterp_Event::ES_ERROR:
914 // clear command buffer
915 myCommandBuffer.truncate( 0 );
916 // add caret return line if necessary
917 QTextBlock par = document()->end().previous();
918 QString txt = par.text();
919 txt.truncate( txt.length() - 1 );
920 // IPAL19397 : addText moved to handleReturn() method
921 //if ( !txt.isEmpty() )
922 // addText( "", true );
923 // set "ready" prompt
924 myPrompt = READY_PROMPT;
928 // stop event loop (if running)
933 case PyInterp_Event::ES_INCOMPLETE:
935 // extend command buffer (multi-line command)
936 myCommandBuffer.append( "\n" );
937 // add caret return line if necessary
938 QTextBlock par = document()->end().previous();
939 QString txt = par.text();
940 txt.truncate( txt.length() - 1 );
941 // IPAL19397 : addText moved to handleReturn() method
942 //if ( !txt.isEmpty() )
943 // addText( "", true );
945 myPrompt = DOTS_PROMPT;
946 addText( myPrompt/*, true*/ ); // IPAL19397
949 // stop event loop (if running)
955 QTextEdit::customEvent( event );
958 // unset read-only state
959 setReadOnly( false );
960 // unset history browsing mode
963 if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 )
965 // process the next sheduled command from the queue (if there is any)
966 QString nextcmd = myQueue[0];
973 \brief Handle Python interpreter change.
975 Perform initialization actions if the interpreter is changed.
976 \param interp python interpreter is being set
978 void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp )
980 if ( myInterp != interp
981 // Force read-only state and wait cursor when myInterp is NULL
986 myBanner = myInterp->getbanner().c_str();
988 // clear command buffer
989 myCommandBuffer.truncate(0);
990 // unset read-only state
991 setReadOnly( false );
992 // unset history browsing mode
997 viewport()->unsetCursor();
998 // stop event loop (if running)
1000 myEventLoop->exit();
1005 // set read-only state
1006 setReadOnly( true );
1008 setCursor( Qt::WaitCursor );
1014 \brief "Copy" operation.
1016 Reimplemented from Qt.
1017 Warning! In Qt4 this method is not virtual.
1019 void PyConsole_Editor::cut()
1021 QTextCursor cur = textCursor();
1022 if ( cur.hasSelection() ) {
1023 QApplication::clipboard()->setText( cur.selectedText() );
1024 int startSelection = cur.selectionStart();
1025 if ( startSelection < document()->end().previous().position() + PROMPT_SIZE )
1026 startSelection = document()->end().previous().position() + PROMPT_SIZE;
1027 int endSelection = cur.selectionEnd();
1028 if ( endSelection < document()->end().previous().position() + PROMPT_SIZE )
1029 endSelection = document()->end().previous().position() + PROMPT_SIZE;
1030 cur.setPosition( startSelection );
1031 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1032 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1033 setTextCursor( cur );
1034 textCursor().removeSelectedText();
1039 \brief "Paste" operation.
1041 Reimplemented from Qt.
1042 Warning! In Qt4 this method is not virtual.
1044 void PyConsole_Editor::paste()
1046 QTextCursor cur = textCursor();
1047 if ( cur.hasSelection() ) {
1048 int startSelection = cur.selectionStart();
1049 if ( startSelection < document()->end().previous().position() + PROMPT_SIZE )
1050 startSelection = document()->end().previous().position() + PROMPT_SIZE;
1051 int endSelection = cur.selectionEnd();
1052 if ( endSelection < document()->end().previous().position() + PROMPT_SIZE )
1053 endSelection = document()->end().previous().position() + PROMPT_SIZE;
1054 cur.setPosition( startSelection );
1055 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1056 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1057 setTextCursor( cur );
1058 textCursor().removeSelectedText();
1060 if ( textCursor().position() < document()->end().previous().position() + PROMPT_SIZE )
1061 moveCursor( QTextCursor::End );
1066 \brief "Clear" operation.
1068 Reimplemented from Qt.
1069 Warning! In Qt4 this method is not virtual.
1071 void PyConsole_Editor::clear()
1074 addText( myBanner );
1075 myPrompt = READY_PROMPT;
1076 addText( myPrompt );