1 // Copyright (C) 2007-2014 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_Editor.h"
93 #include "PyConsole_Event.h"
94 #include "PyInterp_Event.h"
95 #include "PyInterp_Dispatcher.h"
96 #include "PyConsole_Request.h"
98 #include <SUIT_Tools.h>
99 #include <SUIT_FileDlg.h>
100 #include <SUIT_MessageBox.h>
101 #include <SUIT_FileValidator.h>
103 #include <QApplication>
104 #include <QClipboard>
105 #include <QDropEvent>
108 #include <QMouseEvent>
109 #include <QScrollBar>
110 #include <QTextBlock>
111 #include <QTextCursor>
112 #include <QTextDocument>
113 #include <QTextStream>
116 static QString READY_PROMPT = ">>> ";
117 static QString DOTS_PROMPT = "... ";
119 class DumpCommandsFileValidator : public SUIT_FileValidator
122 DumpCommandsFileValidator( QWidget* parent = 0 ) : SUIT_FileValidator ( parent ) {};
123 virtual ~DumpCommandsFileValidator() {};
124 virtual bool canSave( const QString& file, bool permissions );
127 bool DumpCommandsFileValidator::canSave(const QString& file, bool permissions)
129 QFileInfo fi( file );
130 if ( !QRegExp( "[A-Za-z_][A-Za-z0-9_]*" ).exactMatch( fi.completeBaseName() ) ) {
131 SUIT_MessageBox::critical( parent(),
132 QObject::tr("WRN_WARNING"),
133 QObject::tr("WRN_FILE_NAME_BAD") );
136 return SUIT_FileValidator::canSave( file, permissions);
139 void staticCallbackStdout( void* data, char* c )
141 if(!((PyConsole_Editor*)data)->isSuppressOutput())
142 QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c, false ) );
145 void staticCallbackStderr( void* data, char* c )
147 if(!((PyConsole_Editor*)data)->isSuppressOutput())
148 QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( c, true ) );
155 Creates python editor window.
156 \param theInterp python interper
157 \param theParent parent widget
159 PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp,
161 : QTextEdit( theParent ),
163 myCmdInHistory( -1 ),
165 myShowBanner( true ),
167 myIsSuppressOutput( false )
169 QString fntSet( "" );
170 QFont aFont = SUIT_Tools::stringToFont( fntSet );
172 setUndoRedoEnabled( false );
174 myPrompt = READY_PROMPT;
175 setLineWrapMode( QTextEdit::WidgetWidth );
176 setWordWrapMode( QTextOption::WrapAnywhere );
177 setAcceptRichText( false );
179 theInterp->setvoutcb( staticCallbackStdout, this );
180 theInterp->setverrcb( staticCallbackStderr, this );
182 // san - This is necessary for troubleless initialization
183 onPyInterpChanged( theInterp );
189 Does nothing for the moment.
191 PyConsole_Editor::~PyConsole_Editor()
196 \brief Get synchronous mode flag value.
199 \return True if python console works in synchronous mode
201 bool PyConsole_Editor::isSync() const
207 \brief Set synchronous mode flag value.
209 In synhronous mode the Python commands are executed in the GUI thread
210 and the GUI is blocked until the command is finished. In the asynchronous
211 mode each Python command is executed in the separate thread that does not
212 block the main GUI loop.
214 \param on synhronous mode flag
216 void PyConsole_Editor::setIsSync( const bool on )
222 \brief Get suppress output flag value.
224 \sa setIsSuppressOutput()
225 \return \c true if python console output is suppressed.
227 bool PyConsole_Editor::isSuppressOutput() const
229 return myIsSuppressOutput;
233 \brief Set suppress output flag value.
235 In case if suppress output flag is true, the python
236 console output suppressed.
238 \param on suppress output flag
240 void PyConsole_Editor::setIsSuppressOutput( const bool on )
242 myIsSuppressOutput = on;
246 \brief Get 'show banner' flag value.
248 \sa setIsShowBanner()
249 \return \c true if python console shows banner
251 bool PyConsole_Editor::isShowBanner() const
257 \brief Set 'show banner' flag value.
259 The banner is shown in the top of the python console window.
262 \param on 'show banner' flag
264 void PyConsole_Editor::setIsShowBanner( const bool on )
266 if ( myShowBanner != on ) {
273 \brief Get size hint for the Python console window
274 \return size hint value
276 QSize PyConsole_Editor::sizeHint() const
278 QFontMetrics fm( font() );
279 int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1;
280 QSize s(100, fm.lineSpacing()*nbLines);
285 \brief Put the string \a str to the python editor.
286 \param str string to be put in the command line of the editor
287 \param newBlock if True, then the string is printed on a new line
288 \param isError if true, the text is printed in dark red
290 void PyConsole_Editor::addText( const QString& str,
294 QTextCursor theCursor(textCursor());
297 moveCursor( QTextCursor::End );
299 theCursor.insertBlock();
301 cf.setForeground(QBrush(Qt::red));
303 cf.setForeground(QBrush(Qt::black));
304 theCursor.insertText( str, cf);
305 moveCursor( QTextCursor::End );
306 ensureCursorVisible();
310 \brief Convenient method for executing a Python command,
311 as if the user typed it manually.
312 \param command python command to be executed
314 !!! WARNING: doesn't work properly with multi-line commands. !!!
316 void PyConsole_Editor::exec( const QString& command )
318 if ( isReadOnly() ) {
319 // some interactive command is being executed in this editor...
320 // shedule the command to the queue
321 myQueue.push_back( command );
325 moveCursor( QTextCursor::End );
326 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
327 textCursor().removeSelectedText();
328 // set "ready" prompt
329 myPrompt = READY_PROMPT;
330 // clear command buffer
331 myCommandBuffer.truncate( 0 );
332 // unset history browsing mode
334 // print command line by line
335 QString cmd = command;
336 if ( !cmd.endsWith( "\n" ) ) cmd += "\n";
337 QStringList lines = command.split( "\n" );
338 for ( int i = 0; i < lines.size(); i++ ) {
339 if ( !lines[i].trimmed().isEmpty() )
340 myHistory.push_back( lines[i] );
341 addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 );
345 // set read-only mode
348 setCursor( Qt::BusyCursor );
350 // post a request to execute Python command;
351 // editor will be informed via a custom event that execution has been completed
352 PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) );
356 \brief Create request to the python dispatcher for the command execution.
358 \param command python command to be executed
360 PyInterp_Request* PyConsole_Editor::createRequest( const QString& command )
362 return new ExecCommand( myInterp, command, this, isSync() );
366 \brief Execute command in the python interpreter
367 and wait until it is finished.
369 \param command python command to be executed
371 void PyConsole_Editor::execAndWait( const QString& command )
377 // create new event loop
378 myEventLoop = new QEventLoop( this );
383 // delete event loop after command is processed
389 \brief Process "Enter" key press event.
391 Execute the command entered by the user.
393 void PyConsole_Editor::handleReturn()
395 // Position cursor at the end
396 QTextCursor curs(textCursor());
397 curs.movePosition(QTextCursor::End);
401 QTextBlock par = document()->end().previous();
402 if ( !par.isValid() ) return;
405 QString cmd = par.text().remove( 0, promptSize() );
406 // extend the command buffer with the current command
407 myCommandBuffer.append( cmd );
408 // add command to the history
409 if ( !cmd.trimmed().isEmpty() )
410 myHistory.push_back( cmd );
415 // set read-only mode
418 setCursor( Qt::BusyCursor );
420 // post a request to execute Python command;
421 // editor will be informed via a custom event that execution has been completed
422 PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) );
426 \brief Process drop event.
429 \param event drop event
431 void PyConsole_Editor::dropEvent( QDropEvent* event )
433 // get the initial drop position
434 QPoint pos = event->pos();
435 QTextCursor cur = cursorForPosition( event->pos() );
436 // if the position is not in the last line move it to the end of the command line
437 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
438 moveCursor( QTextCursor::End );
439 pos = cursorRect().center();
441 // create new drop event and use it instead of the original
443 event->possibleActions(),
445 event->mouseButtons(),
446 event->keyboardModifiers(),
448 QTextEdit::dropEvent( &de );
449 // accept the original event
450 event->acceptProposedAction();
454 \brief Process mouse button release event.
456 Left mouse button: copy selection to the clipboard.
457 Middle mouse button: paste clipboard's contents.
458 \param event mouse event
460 void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event )
462 if ( event->button() == Qt::LeftButton ) {
463 QTextEdit::mouseReleaseEvent( event );
466 else if ( event->button() == Qt::MidButton ) {
467 QTextCursor cur = cursorForPosition( event->pos() );
468 // if the position is not in the last line move it to the end of the command line
469 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
470 moveCursor( QTextCursor::End );
473 setTextCursor( cur );
475 const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ?
476 QClipboard::Selection : QClipboard::Clipboard );
478 insertFromMimeData( md );
481 QTextEdit::mouseReleaseEvent( event );
486 \brief Check if the string is command.
488 Return True if the string \a str is likely to be the command
489 (i.e. it is started from the '>>>' or '...').
490 \param str string to be checked
492 bool PyConsole_Editor::isCommand( const QString& str ) const
494 return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT );
498 \brief Handle keyboard event.
500 Implement navigation, history browsing, copy/paste and other common
502 \param event keyboard event
504 void PyConsole_Editor::keyPressEvent( QKeyEvent* event )
506 // get cursor position
507 QTextCursor cur = textCursor();
508 int curLine = cur.blockNumber();
509 int curCol = cur.columnNumber();
511 // get last edited line
512 int endLine = document()->blockCount()-1;
514 // get pressed key code
515 int aKey = event->key();
517 // check if <Ctrl> is pressed
518 bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
519 // check if <Shift> is pressed
520 bool shftPressed = event->modifiers() & Qt::ShiftModifier;
522 if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) {
523 // process <Ctrl>+<Break> key-binding and <Escape> key: clear current command
524 myCommandBuffer.truncate( 0 );
525 myPrompt = READY_PROMPT;
526 addText( myPrompt, true );
527 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
530 else if ( ctrlPressed && aKey == Qt::Key_C ) {
531 // process <Ctrl>+<C> key-binding : copy
535 else if ( ctrlPressed && aKey == Qt::Key_X ) {
536 // process <Ctrl>+<X> key-binding : cut
540 else if ( ctrlPressed && aKey == Qt::Key_V ) {
541 // process <Ctrl>+<V> key-binding : paste
546 // check for printed key
547 // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0;
549 aKey = !(QChar(aKey).isPrint()) ? aKey : 0;
553 // any printed key: just print it
555 if ( curLine < endLine || curCol < promptSize() ) {
556 moveCursor( QTextCursor::End );
558 QTextEdit::keyPressEvent( event );
563 // <Enter> key: process the current command
569 // <Up> arrow key: process as follows:
570 // - without <Ctrl>, <Shift> modifiers: previous command in history
571 // - with <Ctrl> modifier key pressed: move cursor one row up without selection
572 // - with <Shift> modifier key pressed: move cursor one row up with selection
573 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
575 if ( ctrlPressed && shftPressed ) {
576 int value = verticalScrollBar()->value();
577 int spacing = fontMetrics().lineSpacing();
578 verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 );
580 else if ( shftPressed || ctrlPressed ) {
582 moveCursor( QTextCursor::Up,
583 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
586 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
587 // set history browsing mode
588 myCmdInHistory = myHistory.count();
589 // remember current command
590 QTextBlock par = document()->end().previous();
591 myCurrentCommand = par.text().remove( 0, promptSize() );
593 if ( myCmdInHistory > 0 ) {
595 // get previous command in the history
596 QString previousCommand = myHistory.at( myCmdInHistory );
597 // print previous command
598 moveCursor( QTextCursor::End );
599 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
600 textCursor().removeSelectedText();
601 addText( myPrompt + previousCommand );
602 // move cursor to the end
603 moveCursor( QTextCursor::End );
609 // <Down> arrow key: process as follows:
610 // - without <Ctrl>, <Shift> modifiers: next command in history
611 // - with <Ctrl> modifier key pressed: move cursor one row down without selection
612 // - with <Shift> modifier key pressed: move cursor one row down with selection
613 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
615 if ( ctrlPressed && shftPressed ) {
616 int value = verticalScrollBar()->value();
617 int maxval = verticalScrollBar()->maximum();
618 int spacing = fontMetrics().lineSpacing();
619 verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval );
621 else if ( shftPressed || ctrlPressed) {
622 if ( curLine < endLine )
623 moveCursor( QTextCursor::Down,
624 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
627 if ( myCmdInHistory >= 0 ) {
628 // get next command in the history
631 if ( myCmdInHistory < myHistory.count() ) {
632 // next command in history
633 nextCommand = myHistory.at( myCmdInHistory );
636 // end of history is reached
637 // last printed command
638 nextCommand = myCurrentCommand;
639 // unset history browsing mode
642 // print next or current command
643 moveCursor( QTextCursor::End );
644 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
645 textCursor().removeSelectedText();
646 addText( myPrompt + nextCommand );
647 // move cursor to the end
648 moveCursor( QTextCursor::End );
654 // <Left> arrow key: process as follows:
655 // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
656 // - with <Ctrl> modifier key pressed: move one word left (taking into account prompt)
657 // - with <Shift> modifier key pressed: move one symbol left with selection
658 // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
660 QString txt = textCursor().block().text();
661 if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) {
662 moveCursor( QTextCursor::Up );
663 moveCursor( QTextCursor::EndOfBlock );
666 QTextEdit::keyPressEvent( event );
671 // <Right> arrow key: process as follows:
672 // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
673 // - with <Ctrl> modifier key pressed: move one word right (taking into account prompt)
674 // - with <Shift> modifier key pressed: move one symbol right with selection
675 // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
677 QString txt = textCursor().block().text();
678 if ( !shftPressed ) {
679 if ( curCol < txt.length() ) {
680 if ( isCommand( txt ) && curCol < promptSize() ) {
681 cur.setPosition( cur.block().position() + promptSize() );
682 setTextCursor( cur );
687 if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) {
688 cur.setPosition( cur.position() + promptSize()+1 );
689 setTextCursor( cur );
690 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
695 QTextEdit::keyPressEvent( event );
699 // <PageUp> key: process as follows:
700 // - without <Ctrl>, <Shift> modifiers: first command in history
701 // - with <Ctrl> modifier key pressed: move cursor one page up without selection
702 // - with <Shift> modifier key pressed: move cursor one page up with selection
703 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
705 if ( ctrlPressed && shftPressed ) {
706 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
708 else if ( shftPressed || ctrlPressed ) {
710 qreal lastY = cursorRect( cur ).top();
712 // move using movePosition to keep the cursor's x
714 qreal y = cursorRect( cur ).top();
715 distance += qAbs( y - lastY );
717 moved = cur.movePosition( QTextCursor::Up,
718 shftPressed ? QTextCursor::KeepAnchor :
719 QTextCursor::MoveAnchor );
720 } while ( moved && distance < viewport()->height() );
722 cur.movePosition( QTextCursor::Down,
723 shftPressed ? QTextCursor::KeepAnchor :
724 QTextCursor::MoveAnchor );
725 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
727 setTextCursor( cur );
730 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
731 // set history browsing mode
732 myCmdInHistory = myHistory.count();
733 // remember current command
734 QTextBlock par = document()->end().previous();
735 myCurrentCommand = par.text().remove( 0, promptSize() );
737 if ( myCmdInHistory > 0 ) {
739 // get very first command in the history
740 QString firstCommand = myHistory.at( myCmdInHistory );
741 // print first command
742 moveCursor( QTextCursor::End );
743 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
744 textCursor().removeSelectedText();
745 addText( myPrompt + firstCommand );
746 // move cursor to the end
747 moveCursor( QTextCursor::End );
752 case Qt::Key_PageDown:
753 // <PageDown> key: process as follows:
754 // - without <Ctrl>, <Shift> modifiers: last command in history
755 // - with <Ctrl> modifier key pressed: move cursor one page down without selection
756 // - with <Shift> modifier key pressed: move cursor one page down with selection
757 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
759 if ( ctrlPressed && shftPressed ) {
760 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
762 else if ( shftPressed || ctrlPressed ) {
764 qreal lastY = cursorRect( cur ).top();
766 // move using movePosition to keep the cursor's x
768 qreal y = cursorRect( cur ).top();
769 distance += qAbs( y - lastY );
771 moved = cur.movePosition( QTextCursor::Down,
772 shftPressed ? QTextCursor::KeepAnchor :
773 QTextCursor::MoveAnchor );
774 } while ( moved && distance < viewport()->height() );
776 cur.movePosition( QTextCursor::Up,
777 shftPressed ? QTextCursor::KeepAnchor :
778 QTextCursor::MoveAnchor );
779 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
781 setTextCursor( cur );
784 if ( myCmdInHistory >= 0 ) {
785 // unset history browsing mode
787 // print current command
788 moveCursor( QTextCursor::End );
789 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
790 textCursor().removeSelectedText();
791 addText( myPrompt + myCurrentCommand );
792 // move cursor to the end
793 moveCursor( QTextCursor::End );
799 // <Home> key: process as follows:
800 // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
801 // - with <Ctrl> modifier key pressed: move cursor to the very first symbol without selection
802 // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
803 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
806 moveCursor( QTextCursor::Start,
807 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
810 QString txt = textCursor().block().text();
811 if ( isCommand( txt ) ) {
813 if ( curCol > promptSize() ) {
814 cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor );
815 cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() );
819 cur.movePosition( QTextCursor::StartOfLine );
820 cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() );
822 setTextCursor( cur );
825 moveCursor( QTextCursor::StartOfBlock,
826 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
828 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
833 // <End> key: process as follows:
834 // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
835 // - with <Ctrl> modifier key pressed: move cursor to the very last symbol without selection
836 // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
837 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
839 moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock,
840 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
843 case Qt::Key_Backspace :
844 // <Backspace> key: process as follows
845 // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
846 // - with <Shift> modifier key pressed: delete previous word
847 // - with <Ctrl> modifier key pressed: delete text from the cursor to the line beginning
848 // works only for last (command) line
850 if ( cur.hasSelection() ) {
853 else if ( cur.position() > document()->end().previous().position() + promptSize() ) {
855 moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor );
856 textCursor().removeSelectedText();
858 else if ( ctrlPressed ) {
859 cur.setPosition( document()->end().previous().position() + promptSize(),
860 QTextCursor::KeepAnchor );
861 setTextCursor( cur );
862 textCursor().removeSelectedText();
865 QTextEdit::keyPressEvent( event );
869 cur.setPosition( document()->end().previous().position() + promptSize() );
870 setTextCursor( cur );
871 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
875 case Qt::Key_Delete :
876 // <Delete> key: process as follows
877 // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
878 // - with <Shift> modifier key pressed: delete next word
879 // - with <Ctrl> modifier key pressed: delete text from the cursor to the end of line
880 // works only for last (command) line
882 if ( cur.hasSelection() ) {
885 else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) {
887 moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor );
888 textCursor().removeSelectedText();
890 else if ( ctrlPressed ) {
891 moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
892 textCursor().removeSelectedText();
895 QTextEdit::keyPressEvent( event );
899 cur.setPosition( document()->end().previous().position() + promptSize() );
900 setTextCursor( cur );
901 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
905 case Qt::Key_Insert :
906 // <Insert> key: process as follows
907 // - with <Ctrl> modifier key pressed: copy()
908 // - with <Shift> modifier key pressed: paste() to the command line
913 else if ( shftPressed ) {
917 QTextEdit::keyPressEvent( event );
924 \brief Handle notification event coming from Python dispatcher.
925 \param event notification event
927 void PyConsole_Editor::customEvent( QEvent* event )
929 switch( event->type() )
931 case PrintEvent::EVENT_ID:
933 PrintEvent* pe=(PrintEvent*)event;
934 addText( pe->text(), false, pe->isError());
937 case PyInterp_Event::ES_OK:
938 case PyInterp_Event::ES_ERROR:
940 // clear command buffer
941 myCommandBuffer.truncate( 0 );
942 // add caret return line if necessary
943 QTextBlock par = document()->end().previous();
944 QString txt = par.text();
945 txt.truncate( txt.length() - 1 );
946 // IPAL19397 : addText moved to handleReturn() method
947 //if ( !txt.isEmpty() )
948 // addText( "", true );
949 // set "ready" prompt
950 myPrompt = READY_PROMPT;
954 // stop event loop (if running)
959 case PyInterp_Event::ES_INCOMPLETE:
961 // extend command buffer (multi-line command)
962 myCommandBuffer.append( "\n" );
963 // add caret return line if necessary
964 QTextBlock par = document()->end().previous();
965 QString txt = par.text();
966 txt.truncate( txt.length() - 1 );
967 // IPAL19397 : addText moved to handleReturn() method
968 //if ( !txt.isEmpty() )
969 // addText( "", true );
971 myPrompt = DOTS_PROMPT;
972 addText( myPrompt/*, true*/ ); // IPAL19397
975 // stop event loop (if running)
981 QTextEdit::customEvent( event );
984 // unset read-only state
985 setReadOnly( false );
986 // unset history browsing mode
989 if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 )
991 // process the next sheduled command from the queue (if there is any)
992 QString nextcmd = myQueue[0];
999 \brief Handle Python interpreter change.
1001 Perform initialization actions if the interpreter is changed.
1002 \param interp python interpreter is being set
1004 void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp )
1006 if ( myInterp != interp
1007 // Force read-only state and wait cursor when myInterp is NULL
1012 myBanner = myInterp->getbanner().c_str();
1013 if ( isShowBanner() )
1014 addText( myBanner );
1015 // clear command buffer
1016 myCommandBuffer.truncate(0);
1017 // unset read-only state
1018 setReadOnly( false );
1019 // unset history browsing mode
1020 myCmdInHistory = -1;
1022 addText( myPrompt );
1023 // unset busy cursor
1024 viewport()->unsetCursor();
1025 // stop event loop (if running)
1027 myEventLoop->exit();
1032 // set read-only state
1033 setReadOnly( true );
1035 setCursor( Qt::WaitCursor );
1041 \brief "Copy" operation.
1043 Reimplemented from Qt.
1044 Warning! In Qt4 this method is not virtual.
1046 void PyConsole_Editor::cut()
1048 QTextCursor cur = textCursor();
1049 if ( cur.hasSelection() ) {
1050 QApplication::clipboard()->setText( cur.selectedText() );
1051 int startSelection = cur.selectionStart();
1052 if ( startSelection < document()->end().previous().position() + promptSize() )
1053 startSelection = document()->end().previous().position() + promptSize();
1054 int endSelection = cur.selectionEnd();
1055 if ( endSelection < document()->end().previous().position() + promptSize() )
1056 endSelection = document()->end().previous().position() + promptSize();
1057 cur.setPosition( startSelection );
1058 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1059 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1060 setTextCursor( cur );
1061 textCursor().removeSelectedText();
1066 \brief "Paste" operation.
1068 Reimplemented from Qt.
1069 Warning! In Qt4 this method is not virtual.
1071 void PyConsole_Editor::paste()
1073 QTextCursor cur = textCursor();
1074 if ( cur.hasSelection() ) {
1075 int startSelection = cur.selectionStart();
1076 if ( startSelection < document()->end().previous().position() + promptSize() )
1077 startSelection = document()->end().previous().position() + promptSize();
1078 int endSelection = cur.selectionEnd();
1079 if ( endSelection < document()->end().previous().position() + promptSize() )
1080 endSelection = document()->end().previous().position() + promptSize();
1081 cur.setPosition( startSelection );
1082 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1083 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1084 setTextCursor( cur );
1085 textCursor().removeSelectedText();
1087 if ( textCursor().position() < document()->end().previous().position() + promptSize() )
1088 moveCursor( QTextCursor::End );
1093 \brief "Clear" operation.
1095 Reimplemented from Qt.
1096 Warning! In Qt4 this method is not virtual.
1098 void PyConsole_Editor::clear()
1101 if ( isShowBanner() )
1102 addText( myBanner );
1103 myPrompt = READY_PROMPT;
1104 addText( myPrompt );
1108 \brief "Dump commands" operation.
1110 void PyConsole_Editor::dump()
1112 QStringList aFilters;
1113 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
1115 QString fileName = SUIT_FileDlg::getFileName( this, QString(),
1116 aFilters, tr( "TOT_DUMP_PYCOMMANDS" ),
1117 false, true, new DumpCommandsFileValidator( this ) );
1118 if ( fileName != "" ) {
1119 QFile file( fileName );
1120 if ( !file.open( QFile::WriteOnly ) )
1123 QTextStream out (&file);
1125 for( int i=0; i<myHistory.count(); i++ ) {
1126 out<<myHistory[i]<<endl;