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( QString::fromUtf8(c), false ) );
145 void staticCallbackStderr( void* data, char* c )
147 if(!((PyConsole_Editor*)data)->isSuppressOutput())
148 QApplication::postEvent( (PyConsole_Editor*)data, new PrintEvent( QString::fromUtf8(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() ) {
344 aCommand.command = lines[i];
345 myHistory.append( aCommand );
347 addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 );
351 // set read-only mode
354 setCursor( Qt::BusyCursor );
356 // post a request to execute Python command;
357 // editor will be informed via a custom event that execution has been completed
358 PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) );
362 \brief Create request to the python dispatcher for the command execution.
364 \param command python command to be executed
366 PyInterp_Request* PyConsole_Editor::createRequest( const QString& command )
368 return new ExecCommand( myInterp, command, this, isSync() );
372 \brief Execute command in the python interpreter
373 and wait until it is finished.
375 \param command python command to be executed
377 void PyConsole_Editor::execAndWait( const QString& command )
383 // create new event loop
384 myEventLoop = new QEventLoop( this );
389 // delete event loop after command is processed
395 \brief Process "Enter" key press event.
397 Execute the command entered by the user.
399 void PyConsole_Editor::handleReturn()
401 // Position cursor at the end
402 QTextCursor curs(textCursor());
403 curs.movePosition(QTextCursor::End);
407 QTextBlock par = document()->end().previous();
408 if ( !par.isValid() ) return;
411 QString cmd = par.text().remove( 0, promptSize() );
412 // extend the command buffer with the current command
413 myCommandBuffer.append( cmd );
414 // add command to the history
415 if ( !cmd.trimmed().isEmpty() ) {
417 aCommand.command = cmd;
418 myHistory.append( aCommand );
424 // set read-only mode
427 setCursor( Qt::BusyCursor );
429 // post a request to execute Python command;
430 // editor will be informed via a custom event that execution has been completed
431 PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) );
435 \brief Process drop event.
438 \param event drop event
440 void PyConsole_Editor::dropEvent( QDropEvent* event )
442 // get the initial drop position
443 QPoint pos = event->pos();
444 QTextCursor cur = cursorForPosition( event->pos() );
445 // if the position is not in the last line move it to the end of the command line
446 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
447 moveCursor( QTextCursor::End );
448 pos = cursorRect().center();
450 // create new drop event and use it instead of the original
452 event->possibleActions(),
454 event->mouseButtons(),
455 event->keyboardModifiers(),
457 QTextEdit::dropEvent( &de );
458 // accept the original event
459 event->acceptProposedAction();
463 \brief Process mouse button release event.
465 Left mouse button: copy selection to the clipboard.
466 Middle mouse button: paste clipboard's contents.
467 \param event mouse event
469 void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event )
471 if ( event->button() == Qt::LeftButton ) {
472 QTextEdit::mouseReleaseEvent( event );
475 else if ( event->button() == Qt::MidButton ) {
476 QTextCursor cur = cursorForPosition( event->pos() );
477 // if the position is not in the last line move it to the end of the command line
478 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
479 moveCursor( QTextCursor::End );
482 setTextCursor( cur );
484 const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ?
485 QClipboard::Selection : QClipboard::Clipboard );
487 insertFromMimeData( md );
490 QTextEdit::mouseReleaseEvent( event );
495 \brief Check if the string is command.
497 Return True if the string \a str is likely to be the command
498 (i.e. it is started from the '>>>' or '...').
499 \param str string to be checked
501 bool PyConsole_Editor::isCommand( const QString& str ) const
503 return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT );
507 \brief Handle keyboard event.
509 Implement navigation, history browsing, copy/paste and other common
511 \param event keyboard event
513 void PyConsole_Editor::keyPressEvent( QKeyEvent* event )
515 // get cursor position
516 QTextCursor cur = textCursor();
517 int curLine = cur.blockNumber();
518 int curCol = cur.columnNumber();
520 // get last edited line
521 int endLine = document()->blockCount()-1;
523 // get pressed key code
524 int aKey = event->key();
526 // check if <Ctrl> is pressed
527 bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
528 // check if <Shift> is pressed
529 bool shftPressed = event->modifiers() & Qt::ShiftModifier;
531 if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) {
532 // process <Ctrl>+<Break> key-binding and <Escape> key: clear current command
533 myCommandBuffer.truncate( 0 );
534 myPrompt = READY_PROMPT;
535 addText( myPrompt, true );
536 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
539 else if ( ctrlPressed && aKey == Qt::Key_C ) {
540 // process <Ctrl>+<C> key-binding : copy
544 else if ( ctrlPressed && aKey == Qt::Key_X ) {
545 // process <Ctrl>+<X> key-binding : cut
549 else if ( ctrlPressed && aKey == Qt::Key_V ) {
550 // process <Ctrl>+<V> key-binding : paste
555 // check for printed key
556 // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0;
558 aKey = !(QChar(aKey).isPrint()) ? aKey : 0;
562 // any printed key: just print it
564 if ( curLine < endLine || curCol < promptSize() ) {
565 moveCursor( QTextCursor::End );
567 QTextEdit::keyPressEvent( event );
572 // <Enter> key: process the current command
578 // <Up> arrow key: process as follows:
579 // - without <Ctrl>, <Shift> modifiers: previous command in history
580 // - with <Ctrl> modifier key pressed: move cursor one row up without selection
581 // - with <Shift> modifier key pressed: move cursor one row up with selection
582 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
584 if ( ctrlPressed && shftPressed ) {
585 int value = verticalScrollBar()->value();
586 int spacing = fontMetrics().lineSpacing();
587 verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 );
589 else if ( shftPressed || ctrlPressed ) {
591 moveCursor( QTextCursor::Up,
592 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
595 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
596 // set history browsing mode
597 myCmdInHistory = myHistory.count();
598 // remember current command
599 QTextBlock par = document()->end().previous();
600 myCurrentCommand = par.text().remove( 0, promptSize() );
602 if ( myCmdInHistory > 0 ) {
604 // get previous command in the history
605 QString previousCommand = myHistory.at( myCmdInHistory ).command;
606 // print previous command
607 moveCursor( QTextCursor::End );
608 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
609 textCursor().removeSelectedText();
610 addText( myPrompt + previousCommand );
611 // move cursor to the end
612 moveCursor( QTextCursor::End );
618 // <Down> arrow key: process as follows:
619 // - without <Ctrl>, <Shift> modifiers: next command in history
620 // - with <Ctrl> modifier key pressed: move cursor one row down without selection
621 // - with <Shift> modifier key pressed: move cursor one row down with selection
622 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
624 if ( ctrlPressed && shftPressed ) {
625 int value = verticalScrollBar()->value();
626 int maxval = verticalScrollBar()->maximum();
627 int spacing = fontMetrics().lineSpacing();
628 verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval );
630 else if ( shftPressed || ctrlPressed) {
631 if ( curLine < endLine )
632 moveCursor( QTextCursor::Down,
633 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
636 if ( myCmdInHistory >= 0 ) {
637 // get next command in the history
640 if ( myCmdInHistory < myHistory.count() ) {
641 // next command in history
642 nextCommand = myHistory.at( myCmdInHistory ).command;
645 // end of history is reached
646 // last printed command
647 nextCommand = myCurrentCommand;
648 // unset history browsing mode
651 // print next or current command
652 moveCursor( QTextCursor::End );
653 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
654 textCursor().removeSelectedText();
655 addText( myPrompt + nextCommand );
656 // move cursor to the end
657 moveCursor( QTextCursor::End );
663 // <Left> arrow key: process as follows:
664 // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
665 // - with <Ctrl> modifier key pressed: move one word left (taking into account prompt)
666 // - with <Shift> modifier key pressed: move one symbol left with selection
667 // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
669 QString txt = textCursor().block().text();
670 if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) {
671 moveCursor( QTextCursor::Up );
672 moveCursor( QTextCursor::EndOfBlock );
675 QTextEdit::keyPressEvent( event );
680 // <Right> arrow key: process as follows:
681 // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
682 // - with <Ctrl> modifier key pressed: move one word right (taking into account prompt)
683 // - with <Shift> modifier key pressed: move one symbol right with selection
684 // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
686 QString txt = textCursor().block().text();
687 if ( !shftPressed ) {
688 if ( curCol < txt.length() ) {
689 if ( isCommand( txt ) && curCol < promptSize() ) {
690 cur.setPosition( cur.block().position() + promptSize() );
691 setTextCursor( cur );
696 if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) {
697 cur.setPosition( cur.position() + promptSize()+1 );
698 setTextCursor( cur );
699 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
704 QTextEdit::keyPressEvent( event );
708 // <PageUp> key: process as follows:
709 // - without <Ctrl>, <Shift> modifiers: first command in history
710 // - with <Ctrl> modifier key pressed: move cursor one page up without selection
711 // - with <Shift> modifier key pressed: move cursor one page up with selection
712 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
714 if ( ctrlPressed && shftPressed ) {
715 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
717 else if ( shftPressed || ctrlPressed ) {
719 qreal lastY = cursorRect( cur ).top();
721 // move using movePosition to keep the cursor's x
723 qreal y = cursorRect( cur ).top();
724 distance += qAbs( y - lastY );
726 moved = cur.movePosition( QTextCursor::Up,
727 shftPressed ? QTextCursor::KeepAnchor :
728 QTextCursor::MoveAnchor );
729 } while ( moved && distance < viewport()->height() );
731 cur.movePosition( QTextCursor::Down,
732 shftPressed ? QTextCursor::KeepAnchor :
733 QTextCursor::MoveAnchor );
734 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
736 setTextCursor( cur );
739 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
740 // set history browsing mode
741 myCmdInHistory = myHistory.count();
742 // remember current command
743 QTextBlock par = document()->end().previous();
744 myCurrentCommand = par.text().remove( 0, promptSize() );
746 if ( myCmdInHistory > 0 ) {
748 // get very first command in the history
749 QString firstCommand = myHistory.at( myCmdInHistory ).command;
750 // print first command
751 moveCursor( QTextCursor::End );
752 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
753 textCursor().removeSelectedText();
754 addText( myPrompt + firstCommand );
755 // move cursor to the end
756 moveCursor( QTextCursor::End );
761 case Qt::Key_PageDown:
762 // <PageDown> key: process as follows:
763 // - without <Ctrl>, <Shift> modifiers: last command in history
764 // - with <Ctrl> modifier key pressed: move cursor one page down without selection
765 // - with <Shift> modifier key pressed: move cursor one page down with selection
766 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
768 if ( ctrlPressed && shftPressed ) {
769 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
771 else if ( shftPressed || ctrlPressed ) {
773 qreal lastY = cursorRect( cur ).top();
775 // move using movePosition to keep the cursor's x
777 qreal y = cursorRect( cur ).top();
778 distance += qAbs( y - lastY );
780 moved = cur.movePosition( QTextCursor::Down,
781 shftPressed ? QTextCursor::KeepAnchor :
782 QTextCursor::MoveAnchor );
783 } while ( moved && distance < viewport()->height() );
785 cur.movePosition( QTextCursor::Up,
786 shftPressed ? QTextCursor::KeepAnchor :
787 QTextCursor::MoveAnchor );
788 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
790 setTextCursor( cur );
793 if ( myCmdInHistory >= 0 ) {
794 // unset history browsing mode
796 // print current command
797 moveCursor( QTextCursor::End );
798 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
799 textCursor().removeSelectedText();
800 addText( myPrompt + myCurrentCommand );
801 // move cursor to the end
802 moveCursor( QTextCursor::End );
808 // <Home> key: process as follows:
809 // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
810 // - with <Ctrl> modifier key pressed: move cursor to the very first symbol without selection
811 // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
812 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
815 moveCursor( QTextCursor::Start,
816 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
819 QString txt = textCursor().block().text();
820 if ( isCommand( txt ) ) {
822 if ( curCol > promptSize() ) {
823 cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor );
824 cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() );
828 cur.movePosition( QTextCursor::StartOfLine );
829 cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() );
831 setTextCursor( cur );
834 moveCursor( QTextCursor::StartOfBlock,
835 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
837 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
842 // <End> key: process as follows:
843 // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
844 // - with <Ctrl> modifier key pressed: move cursor to the very last symbol without selection
845 // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
846 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
848 moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock,
849 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
852 case Qt::Key_Backspace :
853 // <Backspace> key: process as follows
854 // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
855 // - with <Shift> modifier key pressed: delete previous word
856 // - with <Ctrl> modifier key pressed: delete text from the cursor to the line beginning
857 // works only for last (command) line
859 if ( cur.hasSelection() ) {
862 else if ( cur.position() > document()->end().previous().position() + promptSize() ) {
864 moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor );
865 textCursor().removeSelectedText();
867 else if ( ctrlPressed ) {
868 cur.setPosition( document()->end().previous().position() + promptSize(),
869 QTextCursor::KeepAnchor );
870 setTextCursor( cur );
871 textCursor().removeSelectedText();
874 QTextEdit::keyPressEvent( event );
878 cur.setPosition( document()->end().previous().position() + promptSize() );
879 setTextCursor( cur );
880 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
884 case Qt::Key_Delete :
885 // <Delete> key: process as follows
886 // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
887 // - with <Shift> modifier key pressed: delete next word
888 // - with <Ctrl> modifier key pressed: delete text from the cursor to the end of line
889 // works only for last (command) line
891 if ( cur.hasSelection() ) {
894 else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) {
896 moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor );
897 textCursor().removeSelectedText();
899 else if ( ctrlPressed ) {
900 moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
901 textCursor().removeSelectedText();
904 QTextEdit::keyPressEvent( event );
908 cur.setPosition( document()->end().previous().position() + promptSize() );
909 setTextCursor( cur );
910 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
914 case Qt::Key_Insert :
915 // <Insert> key: process as follows
916 // - with <Ctrl> modifier key pressed: copy()
917 // - with <Shift> modifier key pressed: paste() to the command line
922 else if ( shftPressed ) {
926 QTextEdit::keyPressEvent( event );
933 \brief Handle notification event coming from Python dispatcher.
934 \param event notification event
936 void PyConsole_Editor::customEvent( QEvent* event )
938 switch( event->type() )
940 case PrintEvent::EVENT_ID:
942 PrintEvent* pe=(PrintEvent*)event;
943 addText( pe->text(), false, pe->isError());
944 myHistory.last().output = myHistory.last().output + pe->text();
947 case PyInterp_Event::ES_OK:
948 case PyInterp_Event::ES_ERROR:
950 // clear command buffer
951 myCommandBuffer.truncate( 0 );
952 // add caret return line if necessary
953 QTextBlock par = document()->end().previous();
954 QString txt = par.text();
955 txt.truncate( txt.length() - 1 );
956 // IPAL19397 : addText moved to handleReturn() method
957 //if ( !txt.isEmpty() )
958 // addText( "", true );
959 // set "ready" prompt
960 myPrompt = READY_PROMPT;
964 // stop event loop (if running)
969 case PyInterp_Event::ES_INCOMPLETE:
971 // extend command buffer (multi-line command)
972 myCommandBuffer.append( "\n" );
973 // add caret return line if necessary
974 QTextBlock par = document()->end().previous();
975 QString txt = par.text();
976 txt.truncate( txt.length() - 1 );
977 // IPAL19397 : addText moved to handleReturn() method
978 //if ( !txt.isEmpty() )
979 // addText( "", true );
981 myPrompt = DOTS_PROMPT;
982 addText( myPrompt/*, true*/ ); // IPAL19397
985 // stop event loop (if running)
991 QTextEdit::customEvent( event );
994 // unset read-only state
995 setReadOnly( false );
996 // unset history browsing mode
999 if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 )
1001 // process the next sheduled command from the queue (if there is any)
1002 QString nextcmd = myQueue[0];
1003 myQueue.pop_front();
1009 \brief Handle Python interpreter change.
1011 Perform initialization actions if the interpreter is changed.
1012 \param interp python interpreter is being set
1014 void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp )
1016 if ( myInterp != interp
1017 // Force read-only state and wait cursor when myInterp is NULL
1022 myBanner = myInterp->getbanner().c_str();
1023 if ( isShowBanner() )
1024 addText( myBanner );
1025 // clear command buffer
1026 myCommandBuffer.truncate(0);
1027 // unset read-only state
1028 setReadOnly( false );
1029 // unset history browsing mode
1030 myCmdInHistory = -1;
1032 addText( myPrompt );
1033 // unset busy cursor
1034 viewport()->unsetCursor();
1035 // stop event loop (if running)
1037 myEventLoop->exit();
1042 // set read-only state
1043 setReadOnly( true );
1045 setCursor( Qt::WaitCursor );
1051 \brief "Copy" operation.
1053 Reimplemented from Qt.
1054 Warning! In Qt4 this method is not virtual.
1056 void PyConsole_Editor::cut()
1058 QTextCursor cur = textCursor();
1059 if ( cur.hasSelection() ) {
1060 QApplication::clipboard()->setText( cur.selectedText() );
1061 int startSelection = cur.selectionStart();
1062 if ( startSelection < document()->end().previous().position() + promptSize() )
1063 startSelection = document()->end().previous().position() + promptSize();
1064 int endSelection = cur.selectionEnd();
1065 if ( endSelection < document()->end().previous().position() + promptSize() )
1066 endSelection = document()->end().previous().position() + promptSize();
1067 cur.setPosition( startSelection );
1068 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1069 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1070 setTextCursor( cur );
1071 textCursor().removeSelectedText();
1076 \brief "Paste" operation.
1078 Reimplemented from Qt.
1079 Warning! In Qt4 this method is not virtual.
1081 void PyConsole_Editor::paste()
1083 QTextCursor cur = textCursor();
1084 if ( cur.hasSelection() ) {
1085 int startSelection = cur.selectionStart();
1086 if ( startSelection < document()->end().previous().position() + promptSize() )
1087 startSelection = document()->end().previous().position() + promptSize();
1088 int endSelection = cur.selectionEnd();
1089 if ( endSelection < document()->end().previous().position() + promptSize() )
1090 endSelection = document()->end().previous().position() + promptSize();
1091 cur.setPosition( startSelection );
1092 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1093 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1094 setTextCursor( cur );
1095 textCursor().removeSelectedText();
1097 if ( textCursor().position() < document()->end().previous().position() + promptSize() )
1098 moveCursor( QTextCursor::End );
1103 \brief "Clear" operation.
1105 Reimplemented from Qt.
1106 Warning! In Qt4 this method is not virtual.
1108 void PyConsole_Editor::clear()
1111 if ( isShowBanner() )
1112 addText( myBanner );
1113 myPrompt = READY_PROMPT;
1114 addText( myPrompt );
1118 \brief "Dump commands" operation.
1120 void PyConsole_Editor::dump()
1122 QStringList aFilters;
1123 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
1125 QString fileName = SUIT_FileDlg::getFileName( this, QString(),
1126 aFilters, tr( "TOT_DUMP_PYCOMMANDS" ),
1127 false, true, new DumpCommandsFileValidator( this ) );
1128 if ( fileName != "" ) {
1129 QFile file( fileName );
1130 if ( !file.open( QFile::WriteOnly ) )
1133 QTextStream out (&file);
1135 for( int i=0; i<myHistory.count(); i++ ) {
1136 out<<myHistory.at(i).command<<endl;
1142 \brief "Save log" operation.
1144 void PyConsole_Editor::saveLog()
1146 QStringList aFilters;
1147 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
1149 QString fileName = SUIT_FileDlg::getFileName( this, QString(),
1150 aFilters, tr( "TOT_SAVE_PYLOG" ),
1151 false, true, new DumpCommandsFileValidator( this ) );
1152 if ( fileName != "" ) {
1153 QFile file( fileName );
1154 if ( !file.open( QFile::WriteOnly ) )
1157 QTextStream out (&file);
1159 for( int i = 0; i < myHistory.count(); i++ ) {
1160 out << myHistory.at(i).command << endl;
1161 out << myHistory.at(i).output;