1 // Copyright (C) 2007-2013 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
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()
199 \brief Get synchronous mode flag value.
202 \return True if python console works in synchronous mode
204 bool PyConsole_Editor::isSync() const
210 \brief Set synchronous mode flag value.
212 In synhronous mode the Python commands are executed in the GUI thread
213 and the GUI is blocked until the command is finished. In the asynchronous
214 mode each Python command is executed in the separate thread that does not
215 block the main GUI loop.
217 \param on synhronous mode flag
219 void PyConsole_Editor::setIsSync( const bool on )
225 \brief Get suppress output flag value.
227 \sa setIsSuppressOutput()
228 \return \c true if python console output is suppressed.
230 bool PyConsole_Editor::isSuppressOutput() const
232 return myIsSuppressOutput;
236 \brief Set suppress output flag value.
238 In case if suppress output flag is true, the python
239 console output suppressed.
241 \param on suppress output flag
243 void PyConsole_Editor::setIsSuppressOutput( const bool on )
245 myIsSuppressOutput = on;
249 \brief Get 'show banner' flag value.
251 \sa setIsShowBanner()
252 \return \c true if python console shows banner
254 bool PyConsole_Editor::isShowBanner() const
260 \brief Set 'show banner' flag value.
262 The banner is shown in the top of the python console window.
265 \param on 'show banner' flag
267 void PyConsole_Editor::setIsShowBanner( const bool on )
269 if ( myShowBanner != on ) {
276 \brief Get size hint for the Python console window
277 \return size hint value
279 QSize PyConsole_Editor::sizeHint() const
281 QFontMetrics fm( font() );
282 int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1;
283 QSize s(100, fm.lineSpacing()*nbLines);
288 \brief Put the string \a str to the python editor.
289 \param str string to be put in the command line of the editor
290 \param newBlock if True, then the string is printed on a new line
291 \param isError if true, the text is printed in dark red
293 void PyConsole_Editor::addText( const QString& str,
297 QTextCursor theCursor(textCursor());
300 moveCursor( QTextCursor::End );
302 theCursor.insertBlock();
304 cf.setForeground(QBrush(Qt::red));
306 cf.setForeground(QBrush(Qt::black));
307 theCursor.insertText( str, cf);
308 moveCursor( QTextCursor::End );
309 ensureCursorVisible();
313 \brief Convenient method for executing a Python command,
314 as if the user typed it manually.
315 \param command python command to be executed
317 !!! WARNING: doesn't work properly with multi-line commands. !!!
319 void PyConsole_Editor::exec( const QString& command )
321 if ( isReadOnly() ) {
322 // some interactive command is being executed in this editor...
323 // shedule the command to the queue
324 myQueue.push_back( command );
328 moveCursor( QTextCursor::End );
329 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
330 textCursor().removeSelectedText();
331 // set "ready" prompt
332 myPrompt = READY_PROMPT;
333 // clear command buffer
334 myCommandBuffer.truncate( 0 );
335 // unset history browsing mode
337 // print command line by line
338 QString cmd = command;
339 if ( !cmd.endsWith( "\n" ) ) cmd += "\n";
340 QStringList lines = command.split( "\n" );
341 for ( int i = 0; i < lines.size(); i++ ) {
342 if ( !lines[i].trimmed().isEmpty() )
343 myHistory.push_back( lines[i] );
344 addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 );
348 // set read-only mode
351 setCursor( Qt::BusyCursor );
353 // post a request to execute Python command;
354 // editor will be informed via a custom event that execution has been completed
355 PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) );
359 \brief Create request to the python dispatcher for the command execution.
361 \param command python command to be executed
363 PyInterp_Request* PyConsole_Editor::createRequest( const QString& command )
365 return new ExecCommand( myInterp, command, this, isSync() );
369 \brief Execute command in the python interpreter
370 and wait until it is finished.
372 \param command python command to be executed
374 void PyConsole_Editor::execAndWait( const QString& command )
380 // create new event loop
381 myEventLoop = new QEventLoop( this );
386 // delete event loop after command is processed
392 \brief Process "Enter" key press event.
394 Execute the command entered by the user.
396 void PyConsole_Editor::handleReturn()
398 // Position cursor at the end
399 QTextCursor curs(textCursor());
400 curs.movePosition(QTextCursor::End);
404 QTextBlock par = document()->end().previous();
405 if ( !par.isValid() ) return;
408 QString cmd = par.text().remove( 0, promptSize() );
409 // extend the command buffer with the current command
410 myCommandBuffer.append( cmd );
411 // add command to the history
412 if ( !cmd.trimmed().isEmpty() )
413 myHistory.push_back( cmd );
418 // set read-only mode
421 setCursor( Qt::BusyCursor );
423 // post a request to execute Python command;
424 // editor will be informed via a custom event that execution has been completed
425 PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) );
429 \brief Process drop event.
432 \param event drop event
434 void PyConsole_Editor::dropEvent( QDropEvent* event )
436 // get the initial drop position
437 QPoint pos = event->pos();
438 QTextCursor cur = cursorForPosition( event->pos() );
439 // if the position is not in the last line move it to the end of the command line
440 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
441 moveCursor( QTextCursor::End );
442 pos = cursorRect().center();
444 // create new drop event and use it instead of the original
446 event->possibleActions(),
448 event->mouseButtons(),
449 event->keyboardModifiers(),
451 QTextEdit::dropEvent( &de );
452 // accept the original event
453 event->acceptProposedAction();
457 \brief Process mouse button release event.
459 Left mouse button: copy selection to the clipboard.
460 Middle mouse button: paste clipboard's contents.
461 \param event mouse event
463 void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event )
465 if ( event->button() == Qt::LeftButton ) {
466 QTextEdit::mouseReleaseEvent( event );
469 else if ( event->button() == Qt::MidButton ) {
470 QTextCursor cur = cursorForPosition( event->pos() );
471 // if the position is not in the last line move it to the end of the command line
472 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
473 moveCursor( QTextCursor::End );
476 setTextCursor( cur );
478 const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ?
479 QClipboard::Selection : QClipboard::Clipboard );
481 insertFromMimeData( md );
484 QTextEdit::mouseReleaseEvent( event );
489 \brief Check if the string is command.
491 Return True if the string \a str is likely to be the command
492 (i.e. it is started from the '>>>' or '...').
493 \param str string to be checked
495 bool PyConsole_Editor::isCommand( const QString& str ) const
497 return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT );
501 \brief Handle keyboard event.
503 Implement navigation, history browsing, copy/paste and other common
505 \param event keyboard event
507 void PyConsole_Editor::keyPressEvent( QKeyEvent* event )
509 // get cursor position
510 QTextCursor cur = textCursor();
511 int curLine = cur.blockNumber();
512 int curCol = cur.columnNumber();
514 // get last edited line
515 int endLine = document()->blockCount()-1;
517 // get pressed key code
518 int aKey = event->key();
520 // check if <Ctrl> is pressed
521 bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
522 // check if <Shift> is pressed
523 bool shftPressed = event->modifiers() & Qt::ShiftModifier;
525 if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) {
526 // process <Ctrl>+<Break> key-binding and <Escape> key: clear current command
527 myCommandBuffer.truncate( 0 );
528 myPrompt = READY_PROMPT;
529 addText( myPrompt, true );
530 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
533 else if ( ctrlPressed && aKey == Qt::Key_C ) {
534 // process <Ctrl>+<C> key-binding : copy
538 else if ( ctrlPressed && aKey == Qt::Key_X ) {
539 // process <Ctrl>+<X> key-binding : cut
543 else if ( ctrlPressed && aKey == Qt::Key_V ) {
544 // process <Ctrl>+<V> key-binding : paste
549 // check for printed key
550 // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0;
552 aKey = !(QChar(aKey).isPrint()) ? aKey : 0;
556 // any printed key: just print it
558 if ( curLine < endLine || curCol < promptSize() ) {
559 moveCursor( QTextCursor::End );
561 QTextEdit::keyPressEvent( event );
566 // <Enter> key: process the current command
572 // <Up> arrow key: process as follows:
573 // - without <Ctrl>, <Shift> modifiers: previous command in history
574 // - with <Ctrl> modifier key pressed: move cursor one row up without selection
575 // - with <Shift> modifier key pressed: move cursor one row up with selection
576 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
578 if ( ctrlPressed && shftPressed ) {
579 int value = verticalScrollBar()->value();
580 int spacing = fontMetrics().lineSpacing();
581 verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 );
583 else if ( shftPressed || ctrlPressed ) {
585 moveCursor( QTextCursor::Up,
586 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
589 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
590 // set history browsing mode
591 myCmdInHistory = myHistory.count();
592 // remember current command
593 QTextBlock par = document()->end().previous();
594 myCurrentCommand = par.text().remove( 0, promptSize() );
596 if ( myCmdInHistory > 0 ) {
598 // get previous command in the history
599 QString previousCommand = myHistory.at( myCmdInHistory );
600 // print previous command
601 moveCursor( QTextCursor::End );
602 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
603 textCursor().removeSelectedText();
604 addText( myPrompt + previousCommand );
605 // move cursor to the end
606 moveCursor( QTextCursor::End );
612 // <Down> arrow key: process as follows:
613 // - without <Ctrl>, <Shift> modifiers: next command in history
614 // - with <Ctrl> modifier key pressed: move cursor one row down without selection
615 // - with <Shift> modifier key pressed: move cursor one row down with selection
616 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
618 if ( ctrlPressed && shftPressed ) {
619 int value = verticalScrollBar()->value();
620 int maxval = verticalScrollBar()->maximum();
621 int spacing = fontMetrics().lineSpacing();
622 verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval );
624 else if ( shftPressed || ctrlPressed) {
625 if ( curLine < endLine )
626 moveCursor( QTextCursor::Down,
627 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
630 if ( myCmdInHistory >= 0 ) {
631 // get next command in the history
634 if ( myCmdInHistory < myHistory.count() ) {
635 // next command in history
636 nextCommand = myHistory.at( myCmdInHistory );
639 // end of history is reached
640 // last printed command
641 nextCommand = myCurrentCommand;
642 // unset history browsing mode
645 // print next or current command
646 moveCursor( QTextCursor::End );
647 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
648 textCursor().removeSelectedText();
649 addText( myPrompt + nextCommand );
650 // move cursor to the end
651 moveCursor( QTextCursor::End );
657 // <Left> arrow key: process as follows:
658 // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
659 // - with <Ctrl> modifier key pressed: move one word left (taking into account prompt)
660 // - with <Shift> modifier key pressed: move one symbol left with selection
661 // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
663 QString txt = textCursor().block().text();
664 if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) {
665 moveCursor( QTextCursor::Up );
666 moveCursor( QTextCursor::EndOfBlock );
669 QTextEdit::keyPressEvent( event );
674 // <Right> arrow key: process as follows:
675 // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
676 // - with <Ctrl> modifier key pressed: move one word right (taking into account prompt)
677 // - with <Shift> modifier key pressed: move one symbol right with selection
678 // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
680 QString txt = textCursor().block().text();
681 if ( !shftPressed ) {
682 if ( curCol < txt.length() ) {
683 if ( isCommand( txt ) && curCol < promptSize() ) {
684 cur.setPosition( cur.block().position() + promptSize() );
685 setTextCursor( cur );
690 if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) {
691 cur.setPosition( cur.position() + promptSize()+1 );
692 setTextCursor( cur );
693 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
698 QTextEdit::keyPressEvent( event );
702 // <PageUp> key: process as follows:
703 // - without <Ctrl>, <Shift> modifiers: first command in history
704 // - with <Ctrl> modifier key pressed: move cursor one page up without selection
705 // - with <Shift> modifier key pressed: move cursor one page up with selection
706 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
708 if ( ctrlPressed && shftPressed ) {
709 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
711 else if ( shftPressed || ctrlPressed ) {
713 qreal lastY = cursorRect( cur ).top();
715 // move using movePosition to keep the cursor's x
717 qreal y = cursorRect( cur ).top();
718 distance += qAbs( y - lastY );
720 moved = cur.movePosition( QTextCursor::Up,
721 shftPressed ? QTextCursor::KeepAnchor :
722 QTextCursor::MoveAnchor );
723 } while ( moved && distance < viewport()->height() );
725 cur.movePosition( QTextCursor::Down,
726 shftPressed ? QTextCursor::KeepAnchor :
727 QTextCursor::MoveAnchor );
728 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
730 setTextCursor( cur );
733 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
734 // set history browsing mode
735 myCmdInHistory = myHistory.count();
736 // remember current command
737 QTextBlock par = document()->end().previous();
738 myCurrentCommand = par.text().remove( 0, promptSize() );
740 if ( myCmdInHistory > 0 ) {
742 // get very first command in the history
743 QString firstCommand = myHistory.at( myCmdInHistory );
744 // print first command
745 moveCursor( QTextCursor::End );
746 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
747 textCursor().removeSelectedText();
748 addText( myPrompt + firstCommand );
749 // move cursor to the end
750 moveCursor( QTextCursor::End );
755 case Qt::Key_PageDown:
756 // <PageDown> key: process as follows:
757 // - without <Ctrl>, <Shift> modifiers: last command in history
758 // - with <Ctrl> modifier key pressed: move cursor one page down without selection
759 // - with <Shift> modifier key pressed: move cursor one page down with selection
760 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
762 if ( ctrlPressed && shftPressed ) {
763 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
765 else if ( shftPressed || ctrlPressed ) {
767 qreal lastY = cursorRect( cur ).top();
769 // move using movePosition to keep the cursor's x
771 qreal y = cursorRect( cur ).top();
772 distance += qAbs( y - lastY );
774 moved = cur.movePosition( QTextCursor::Down,
775 shftPressed ? QTextCursor::KeepAnchor :
776 QTextCursor::MoveAnchor );
777 } while ( moved && distance < viewport()->height() );
779 cur.movePosition( QTextCursor::Up,
780 shftPressed ? QTextCursor::KeepAnchor :
781 QTextCursor::MoveAnchor );
782 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
784 setTextCursor( cur );
787 if ( myCmdInHistory >= 0 ) {
788 // unset history browsing mode
790 // print current command
791 moveCursor( QTextCursor::End );
792 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
793 textCursor().removeSelectedText();
794 addText( myPrompt + myCurrentCommand );
795 // move cursor to the end
796 moveCursor( QTextCursor::End );
802 // <Home> key: process as follows:
803 // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
804 // - with <Ctrl> modifier key pressed: move cursor to the very first symbol without selection
805 // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
806 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
809 moveCursor( QTextCursor::Start,
810 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
813 QString txt = textCursor().block().text();
814 if ( isCommand( txt ) ) {
816 if ( curCol > promptSize() ) {
817 cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor );
818 cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() );
822 cur.movePosition( QTextCursor::StartOfLine );
823 cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() );
825 setTextCursor( cur );
828 moveCursor( QTextCursor::StartOfBlock,
829 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
831 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
836 // <End> key: process as follows:
837 // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
838 // - with <Ctrl> modifier key pressed: move cursor to the very last symbol without selection
839 // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
840 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
842 moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock,
843 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
846 case Qt::Key_Backspace :
847 // <Backspace> key: process as follows
848 // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
849 // - with <Shift> modifier key pressed: delete previous word
850 // - with <Ctrl> modifier key pressed: delete text from the cursor to the line beginning
851 // works only for last (command) line
853 if ( cur.hasSelection() ) {
856 else if ( cur.position() > document()->end().previous().position() + promptSize() ) {
858 moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor );
859 textCursor().removeSelectedText();
861 else if ( ctrlPressed ) {
862 cur.setPosition( document()->end().previous().position() + promptSize(),
863 QTextCursor::KeepAnchor );
864 setTextCursor( cur );
865 textCursor().removeSelectedText();
868 QTextEdit::keyPressEvent( event );
872 cur.setPosition( document()->end().previous().position() + promptSize() );
873 setTextCursor( cur );
874 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
878 case Qt::Key_Delete :
879 // <Delete> key: process as follows
880 // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
881 // - with <Shift> modifier key pressed: delete next word
882 // - with <Ctrl> modifier key pressed: delete text from the cursor to the end of line
883 // works only for last (command) line
885 if ( cur.hasSelection() ) {
888 else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) {
890 moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor );
891 textCursor().removeSelectedText();
893 else if ( ctrlPressed ) {
894 moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
895 textCursor().removeSelectedText();
898 QTextEdit::keyPressEvent( event );
902 cur.setPosition( document()->end().previous().position() + promptSize() );
903 setTextCursor( cur );
904 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
908 case Qt::Key_Insert :
909 // <Insert> key: process as follows
910 // - with <Ctrl> modifier key pressed: copy()
911 // - with <Shift> modifier key pressed: paste() to the command line
916 else if ( shftPressed ) {
920 QTextEdit::keyPressEvent( event );
927 \brief Handle notification event coming from Python dispatcher.
928 \param event notification event
930 void PyConsole_Editor::customEvent( QEvent* event )
932 switch( event->type() )
934 case PrintEvent::EVENT_ID:
936 PrintEvent* pe=(PrintEvent*)event;
937 addText( pe->text(), false, pe->isError());
940 case PyInterp_Event::ES_OK:
941 case PyInterp_Event::ES_ERROR:
943 // clear command buffer
944 myCommandBuffer.truncate( 0 );
945 // add caret return line if necessary
946 QTextBlock par = document()->end().previous();
947 QString txt = par.text();
948 txt.truncate( txt.length() - 1 );
949 // IPAL19397 : addText moved to handleReturn() method
950 //if ( !txt.isEmpty() )
951 // addText( "", true );
952 // set "ready" prompt
953 myPrompt = READY_PROMPT;
957 // stop event loop (if running)
962 case PyInterp_Event::ES_INCOMPLETE:
964 // extend command buffer (multi-line command)
965 myCommandBuffer.append( "\n" );
966 // add caret return line if necessary
967 QTextBlock par = document()->end().previous();
968 QString txt = par.text();
969 txt.truncate( txt.length() - 1 );
970 // IPAL19397 : addText moved to handleReturn() method
971 //if ( !txt.isEmpty() )
972 // addText( "", true );
974 myPrompt = DOTS_PROMPT;
975 addText( myPrompt/*, true*/ ); // IPAL19397
978 // stop event loop (if running)
984 QTextEdit::customEvent( event );
987 // unset read-only state
988 setReadOnly( false );
989 // unset history browsing mode
992 if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 )
994 // process the next sheduled command from the queue (if there is any)
995 QString nextcmd = myQueue[0];
1002 \brief Handle Python interpreter change.
1004 Perform initialization actions if the interpreter is changed.
1005 \param interp python interpreter is being set
1007 void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp )
1009 if ( myInterp != interp
1010 // Force read-only state and wait cursor when myInterp is NULL
1015 myBanner = myInterp->getbanner().c_str();
1016 if ( isShowBanner() )
1017 addText( myBanner );
1018 // clear command buffer
1019 myCommandBuffer.truncate(0);
1020 // unset read-only state
1021 setReadOnly( false );
1022 // unset history browsing mode
1023 myCmdInHistory = -1;
1025 addText( myPrompt );
1026 // unset busy cursor
1027 viewport()->unsetCursor();
1028 // stop event loop (if running)
1030 myEventLoop->exit();
1035 // set read-only state
1036 setReadOnly( true );
1038 setCursor( Qt::WaitCursor );
1044 \brief "Copy" operation.
1046 Reimplemented from Qt.
1047 Warning! In Qt4 this method is not virtual.
1049 void PyConsole_Editor::cut()
1051 QTextCursor cur = textCursor();
1052 if ( cur.hasSelection() ) {
1053 QApplication::clipboard()->setText( cur.selectedText() );
1054 int startSelection = cur.selectionStart();
1055 if ( startSelection < document()->end().previous().position() + promptSize() )
1056 startSelection = document()->end().previous().position() + promptSize();
1057 int endSelection = cur.selectionEnd();
1058 if ( endSelection < document()->end().previous().position() + promptSize() )
1059 endSelection = document()->end().previous().position() + promptSize();
1060 cur.setPosition( startSelection );
1061 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1062 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1063 setTextCursor( cur );
1064 textCursor().removeSelectedText();
1069 \brief "Paste" operation.
1071 Reimplemented from Qt.
1072 Warning! In Qt4 this method is not virtual.
1074 void PyConsole_Editor::paste()
1076 QTextCursor cur = textCursor();
1077 if ( cur.hasSelection() ) {
1078 int startSelection = cur.selectionStart();
1079 if ( startSelection < document()->end().previous().position() + promptSize() )
1080 startSelection = document()->end().previous().position() + promptSize();
1081 int endSelection = cur.selectionEnd();
1082 if ( endSelection < document()->end().previous().position() + promptSize() )
1083 endSelection = document()->end().previous().position() + promptSize();
1084 cur.setPosition( startSelection );
1085 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1086 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1087 setTextCursor( cur );
1088 textCursor().removeSelectedText();
1090 if ( textCursor().position() < document()->end().previous().position() + promptSize() )
1091 moveCursor( QTextCursor::End );
1096 \brief "Clear" operation.
1098 Reimplemented from Qt.
1099 Warning! In Qt4 this method is not virtual.
1101 void PyConsole_Editor::clear()
1104 if ( isShowBanner() )
1105 addText( myBanner );
1106 myPrompt = READY_PROMPT;
1107 addText( myPrompt );
1111 \brief "Dump commands" operation.
1113 void PyConsole_Editor::dump()
1115 QStringList aFilters;
1116 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
1118 QString fileName = SUIT_FileDlg::getFileName( this, QString(),
1119 aFilters, tr( "TOT_DUMP_PYCOMMANDS" ),
1120 false, true, new DumpCommandsFileValidator( this ) );
1121 if ( fileName != "" ) {
1122 QFile file( fileName );
1123 if ( !file.open( QFile::WriteOnly ) )
1126 QTextStream out (&file);
1128 for( int i=0; i<myHistory.count(); i++ ) {
1129 out<<myHistory[i]<<endl;