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 aCommand.prompt = i == 0 ? READY_PROMPT : DOTS_PROMPT;
346 myHistory.append( aCommand );
348 addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 );
352 // set read-only mode
355 setCursor( Qt::BusyCursor );
357 // post a request to execute Python command;
358 // editor will be informed via a custom event that execution has been completed
359 PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) );
363 \brief Create request to the python dispatcher for the command execution.
365 \param command python command to be executed
367 PyInterp_Request* PyConsole_Editor::createRequest( const QString& command )
369 return new ExecCommand( myInterp, command, this, isSync() );
373 \brief Execute command in the python interpreter
374 and wait until it is finished.
376 \param command python command to be executed
378 void PyConsole_Editor::execAndWait( const QString& command )
384 // create new event loop
385 myEventLoop = new QEventLoop( this );
390 // delete event loop after command is processed
396 \brief Process "Enter" key press event.
398 Execute the command entered by the user.
400 void PyConsole_Editor::handleReturn()
402 // Position cursor at the end
403 QTextCursor curs(textCursor());
404 curs.movePosition(QTextCursor::End);
408 QTextBlock par = document()->end().previous();
409 if ( !par.isValid() ) return;
412 QString cmd = par.text().remove( 0, promptSize() );
413 // extend the command buffer with the current command
414 myCommandBuffer.append( cmd );
415 // add command to the history
416 if ( !cmd.trimmed().isEmpty() ) {
418 aCommand.command = cmd;
419 aCommand.prompt = myPrompt;
420 myHistory.append( aCommand );
426 // set read-only mode
429 setCursor( Qt::BusyCursor );
431 // post a request to execute Python command;
432 // editor will be informed via a custom event that execution has been completed
433 PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) );
437 \brief Process drop event.
440 \param event drop event
442 void PyConsole_Editor::dropEvent( QDropEvent* event )
444 // get the initial drop position
445 QPoint pos = event->pos();
446 QTextCursor cur = cursorForPosition( event->pos() );
447 // if the position is not in the last line move it to the end of the command line
448 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
449 moveCursor( QTextCursor::End );
450 pos = cursorRect().center();
452 // create new drop event and use it instead of the original
454 event->possibleActions(),
456 event->mouseButtons(),
457 event->keyboardModifiers(),
459 QTextEdit::dropEvent( &de );
460 // accept the original event
461 event->acceptProposedAction();
465 \brief Process mouse button release event.
467 Left mouse button: copy selection to the clipboard.
468 Middle mouse button: paste clipboard's contents.
469 \param event mouse event
471 void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event )
473 if ( event->button() == Qt::LeftButton ) {
474 QTextEdit::mouseReleaseEvent( event );
477 else if ( event->button() == Qt::MidButton ) {
478 QTextCursor cur = cursorForPosition( event->pos() );
479 // if the position is not in the last line move it to the end of the command line
480 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
481 moveCursor( QTextCursor::End );
484 setTextCursor( cur );
486 const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ?
487 QClipboard::Selection : QClipboard::Clipboard );
489 insertFromMimeData( md );
492 QTextEdit::mouseReleaseEvent( event );
497 \brief Check if the string is command.
499 Return True if the string \a str is likely to be the command
500 (i.e. it is started from the '>>>' or '...').
501 \param str string to be checked
503 bool PyConsole_Editor::isCommand( const QString& str ) const
505 return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT );
509 \brief Handle keyboard event.
511 Implement navigation, history browsing, copy/paste and other common
513 \param event keyboard event
515 void PyConsole_Editor::keyPressEvent( QKeyEvent* event )
517 // get cursor position
518 QTextCursor cur = textCursor();
519 int curLine = cur.blockNumber();
520 int curCol = cur.columnNumber();
522 // get last edited line
523 int endLine = document()->blockCount()-1;
525 // get pressed key code
526 int aKey = event->key();
528 // check if <Ctrl> is pressed
529 bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
530 // check if <Shift> is pressed
531 bool shftPressed = event->modifiers() & Qt::ShiftModifier;
533 if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) {
534 // process <Ctrl>+<Break> key-binding and <Escape> key: clear current command
535 myCommandBuffer.truncate( 0 );
536 myPrompt = READY_PROMPT;
537 addText( myPrompt, true );
538 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
541 else if ( ctrlPressed && aKey == Qt::Key_C ) {
542 // process <Ctrl>+<C> key-binding : copy
546 else if ( ctrlPressed && aKey == Qt::Key_X ) {
547 // process <Ctrl>+<X> key-binding : cut
551 else if ( ctrlPressed && aKey == Qt::Key_V ) {
552 // process <Ctrl>+<V> key-binding : paste
557 // check for printed key
558 // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0;
560 aKey = !(QChar(aKey).isPrint()) ? aKey : 0;
564 // any printed key: just print it
566 if ( curLine < endLine || curCol < promptSize() ) {
567 moveCursor( QTextCursor::End );
569 QTextEdit::keyPressEvent( event );
574 // <Enter> key: process the current command
580 // <Up> arrow key: process as follows:
581 // - without <Ctrl>, <Shift> modifiers: previous command in history
582 // - with <Ctrl> modifier key pressed: move cursor one row up without selection
583 // - with <Shift> modifier key pressed: move cursor one row up with selection
584 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
586 if ( ctrlPressed && shftPressed ) {
587 int value = verticalScrollBar()->value();
588 int spacing = fontMetrics().lineSpacing();
589 verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 );
591 else if ( shftPressed || ctrlPressed ) {
593 moveCursor( QTextCursor::Up,
594 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
597 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
598 // set history browsing mode
599 myCmdInHistory = myHistory.count();
600 // remember current command
601 QTextBlock par = document()->end().previous();
602 myCurrentCommand = par.text().remove( 0, promptSize() );
604 if ( myCmdInHistory > 0 ) {
606 // get previous command in the history
607 QString previousCommand = myHistory.at( myCmdInHistory ).command;
608 // print previous command
609 moveCursor( QTextCursor::End );
610 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
611 textCursor().removeSelectedText();
612 addText( myPrompt + previousCommand );
613 // move cursor to the end
614 moveCursor( QTextCursor::End );
620 // <Down> arrow key: process as follows:
621 // - without <Ctrl>, <Shift> modifiers: next command in history
622 // - with <Ctrl> modifier key pressed: move cursor one row down without selection
623 // - with <Shift> modifier key pressed: move cursor one row down with selection
624 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
626 if ( ctrlPressed && shftPressed ) {
627 int value = verticalScrollBar()->value();
628 int maxval = verticalScrollBar()->maximum();
629 int spacing = fontMetrics().lineSpacing();
630 verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval );
632 else if ( shftPressed || ctrlPressed) {
633 if ( curLine < endLine )
634 moveCursor( QTextCursor::Down,
635 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
638 if ( myCmdInHistory >= 0 ) {
639 // get next command in the history
642 if ( myCmdInHistory < myHistory.count() ) {
643 // next command in history
644 nextCommand = myHistory.at( myCmdInHistory ).command;
647 // end of history is reached
648 // last printed command
649 nextCommand = myCurrentCommand;
650 // unset history browsing mode
653 // print next or current command
654 moveCursor( QTextCursor::End );
655 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
656 textCursor().removeSelectedText();
657 addText( myPrompt + nextCommand );
658 // move cursor to the end
659 moveCursor( QTextCursor::End );
665 // <Left> arrow key: process as follows:
666 // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
667 // - with <Ctrl> modifier key pressed: move one word left (taking into account prompt)
668 // - with <Shift> modifier key pressed: move one symbol left with selection
669 // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
671 QString txt = textCursor().block().text();
672 if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) {
673 moveCursor( QTextCursor::Up );
674 moveCursor( QTextCursor::EndOfBlock );
677 QTextEdit::keyPressEvent( event );
682 // <Right> arrow key: process as follows:
683 // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
684 // - with <Ctrl> modifier key pressed: move one word right (taking into account prompt)
685 // - with <Shift> modifier key pressed: move one symbol right with selection
686 // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
688 QString txt = textCursor().block().text();
689 if ( !shftPressed ) {
690 if ( curCol < txt.length() ) {
691 if ( isCommand( txt ) && curCol < promptSize() ) {
692 cur.setPosition( cur.block().position() + promptSize() );
693 setTextCursor( cur );
698 if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) {
699 cur.setPosition( cur.position() + promptSize()+1 );
700 setTextCursor( cur );
701 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
706 QTextEdit::keyPressEvent( event );
710 // <PageUp> key: process as follows:
711 // - without <Ctrl>, <Shift> modifiers: first command in history
712 // - with <Ctrl> modifier key pressed: move cursor one page up without selection
713 // - with <Shift> modifier key pressed: move cursor one page up with selection
714 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
716 if ( ctrlPressed && shftPressed ) {
717 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
719 else if ( shftPressed || ctrlPressed ) {
721 qreal lastY = cursorRect( cur ).top();
723 // move using movePosition to keep the cursor's x
725 qreal y = cursorRect( cur ).top();
726 distance += qAbs( y - lastY );
728 moved = cur.movePosition( QTextCursor::Up,
729 shftPressed ? QTextCursor::KeepAnchor :
730 QTextCursor::MoveAnchor );
731 } while ( moved && distance < viewport()->height() );
733 cur.movePosition( QTextCursor::Down,
734 shftPressed ? QTextCursor::KeepAnchor :
735 QTextCursor::MoveAnchor );
736 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
738 setTextCursor( cur );
741 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
742 // set history browsing mode
743 myCmdInHistory = myHistory.count();
744 // remember current command
745 QTextBlock par = document()->end().previous();
746 myCurrentCommand = par.text().remove( 0, promptSize() );
748 if ( myCmdInHistory > 0 ) {
750 // get very first command in the history
751 QString firstCommand = myHistory.at( myCmdInHistory ).command;
752 // print first command
753 moveCursor( QTextCursor::End );
754 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
755 textCursor().removeSelectedText();
756 addText( myPrompt + firstCommand );
757 // move cursor to the end
758 moveCursor( QTextCursor::End );
763 case Qt::Key_PageDown:
764 // <PageDown> key: process as follows:
765 // - without <Ctrl>, <Shift> modifiers: last command in history
766 // - with <Ctrl> modifier key pressed: move cursor one page down without selection
767 // - with <Shift> modifier key pressed: move cursor one page down with selection
768 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
770 if ( ctrlPressed && shftPressed ) {
771 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
773 else if ( shftPressed || ctrlPressed ) {
775 qreal lastY = cursorRect( cur ).top();
777 // move using movePosition to keep the cursor's x
779 qreal y = cursorRect( cur ).top();
780 distance += qAbs( y - lastY );
782 moved = cur.movePosition( QTextCursor::Down,
783 shftPressed ? QTextCursor::KeepAnchor :
784 QTextCursor::MoveAnchor );
785 } while ( moved && distance < viewport()->height() );
787 cur.movePosition( QTextCursor::Up,
788 shftPressed ? QTextCursor::KeepAnchor :
789 QTextCursor::MoveAnchor );
790 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
792 setTextCursor( cur );
795 if ( myCmdInHistory >= 0 ) {
796 // unset history browsing mode
798 // print current command
799 moveCursor( QTextCursor::End );
800 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
801 textCursor().removeSelectedText();
802 addText( myPrompt + myCurrentCommand );
803 // move cursor to the end
804 moveCursor( QTextCursor::End );
810 // <Home> key: process as follows:
811 // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
812 // - with <Ctrl> modifier key pressed: move cursor to the very first symbol without selection
813 // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
814 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
817 moveCursor( QTextCursor::Start,
818 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
821 QString txt = textCursor().block().text();
822 if ( isCommand( txt ) ) {
824 if ( curCol > promptSize() ) {
825 cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor );
826 cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() );
830 cur.movePosition( QTextCursor::StartOfLine );
831 cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() );
833 setTextCursor( cur );
836 moveCursor( QTextCursor::StartOfBlock,
837 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
839 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
844 // <End> key: process as follows:
845 // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
846 // - with <Ctrl> modifier key pressed: move cursor to the very last symbol without selection
847 // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
848 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
850 moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock,
851 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
854 case Qt::Key_Backspace :
855 // <Backspace> key: process as follows
856 // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
857 // - with <Shift> modifier key pressed: delete previous word
858 // - with <Ctrl> modifier key pressed: delete text from the cursor to the line beginning
859 // works only for last (command) line
861 if ( cur.hasSelection() ) {
864 else if ( cur.position() > document()->end().previous().position() + promptSize() ) {
866 moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor );
867 textCursor().removeSelectedText();
869 else if ( ctrlPressed ) {
870 cur.setPosition( document()->end().previous().position() + promptSize(),
871 QTextCursor::KeepAnchor );
872 setTextCursor( cur );
873 textCursor().removeSelectedText();
876 QTextEdit::keyPressEvent( event );
880 cur.setPosition( document()->end().previous().position() + promptSize() );
881 setTextCursor( cur );
882 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
886 case Qt::Key_Delete :
887 // <Delete> key: process as follows
888 // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
889 // - with <Shift> modifier key pressed: delete next word
890 // - with <Ctrl> modifier key pressed: delete text from the cursor to the end of line
891 // works only for last (command) line
893 if ( cur.hasSelection() ) {
896 else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) {
898 moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor );
899 textCursor().removeSelectedText();
901 else if ( ctrlPressed ) {
902 moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
903 textCursor().removeSelectedText();
906 QTextEdit::keyPressEvent( event );
910 cur.setPosition( document()->end().previous().position() + promptSize() );
911 setTextCursor( cur );
912 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
916 case Qt::Key_Insert :
917 // <Insert> key: process as follows
918 // - with <Ctrl> modifier key pressed: copy()
919 // - with <Shift> modifier key pressed: paste() to the command line
924 else if ( shftPressed ) {
928 QTextEdit::keyPressEvent( event );
935 \brief Handle notification event coming from Python dispatcher.
936 \param event notification event
938 void PyConsole_Editor::customEvent( QEvent* event )
940 switch( event->type() )
942 case PrintEvent::EVENT_ID:
944 PrintEvent* pe=(PrintEvent*)event;
945 addText( pe->text(), false, pe->isError());
946 myHistory.last().output = myHistory.last().output + pe->text();
949 case PyInterp_Event::ES_OK:
950 case PyInterp_Event::ES_ERROR:
952 // clear command buffer
953 myCommandBuffer.truncate( 0 );
954 // add caret return line if necessary
955 QTextBlock par = document()->end().previous();
956 QString txt = par.text();
957 txt.truncate( txt.length() - 1 );
958 // IPAL19397 : addText moved to handleReturn() method
959 //if ( !txt.isEmpty() )
960 // addText( "", true );
961 // set "ready" prompt
962 myPrompt = READY_PROMPT;
966 // stop event loop (if running)
971 case PyInterp_Event::ES_INCOMPLETE:
973 // extend command buffer (multi-line command)
974 myCommandBuffer.append( "\n" );
975 // add caret return line if necessary
976 QTextBlock par = document()->end().previous();
977 QString txt = par.text();
978 txt.truncate( txt.length() - 1 );
979 // IPAL19397 : addText moved to handleReturn() method
980 //if ( !txt.isEmpty() )
981 // addText( "", true );
983 myPrompt = DOTS_PROMPT;
984 addText( myPrompt/*, true*/ ); // IPAL19397
987 // stop event loop (if running)
993 QTextEdit::customEvent( event );
996 // unset read-only state
997 setReadOnly( false );
998 // unset history browsing mode
1001 if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 )
1003 // process the next sheduled command from the queue (if there is any)
1004 QString nextcmd = myQueue[0];
1005 myQueue.pop_front();
1011 \brief Handle Python interpreter change.
1013 Perform initialization actions if the interpreter is changed.
1014 \param interp python interpreter is being set
1016 void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp )
1018 if ( myInterp != interp
1019 // Force read-only state and wait cursor when myInterp is NULL
1024 myBanner = myInterp->getbanner().c_str();
1025 if ( isShowBanner() )
1026 addText( myBanner );
1027 // clear command buffer
1028 myCommandBuffer.truncate(0);
1029 // unset read-only state
1030 setReadOnly( false );
1031 // unset history browsing mode
1032 myCmdInHistory = -1;
1034 addText( myPrompt );
1035 // unset busy cursor
1036 viewport()->unsetCursor();
1037 // stop event loop (if running)
1039 myEventLoop->exit();
1044 // set read-only state
1045 setReadOnly( true );
1047 setCursor( Qt::WaitCursor );
1053 \brief "Copy" operation.
1055 Reimplemented from Qt.
1056 Warning! In Qt4 this method is not virtual.
1058 void PyConsole_Editor::cut()
1060 QTextCursor cur = textCursor();
1061 if ( cur.hasSelection() ) {
1062 QApplication::clipboard()->setText( cur.selectedText() );
1063 int startSelection = cur.selectionStart();
1064 if ( startSelection < document()->end().previous().position() + promptSize() )
1065 startSelection = document()->end().previous().position() + promptSize();
1066 int endSelection = cur.selectionEnd();
1067 if ( endSelection < document()->end().previous().position() + promptSize() )
1068 endSelection = document()->end().previous().position() + promptSize();
1069 cur.setPosition( startSelection );
1070 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1071 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1072 setTextCursor( cur );
1073 textCursor().removeSelectedText();
1078 \brief "Paste" operation.
1080 Reimplemented from Qt.
1081 Warning! In Qt4 this method is not virtual.
1083 void PyConsole_Editor::paste()
1085 QTextCursor cur = textCursor();
1086 if ( cur.hasSelection() ) {
1087 int startSelection = cur.selectionStart();
1088 if ( startSelection < document()->end().previous().position() + promptSize() )
1089 startSelection = document()->end().previous().position() + promptSize();
1090 int endSelection = cur.selectionEnd();
1091 if ( endSelection < document()->end().previous().position() + promptSize() )
1092 endSelection = document()->end().previous().position() + promptSize();
1093 cur.setPosition( startSelection );
1094 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1095 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1096 setTextCursor( cur );
1097 textCursor().removeSelectedText();
1099 if ( textCursor().position() < document()->end().previous().position() + promptSize() )
1100 moveCursor( QTextCursor::End );
1105 \brief "Clear" operation.
1107 Reimplemented from Qt.
1108 Warning! In Qt4 this method is not virtual.
1110 void PyConsole_Editor::clear()
1113 if ( isShowBanner() )
1114 addText( myBanner );
1115 myPrompt = READY_PROMPT;
1116 addText( myPrompt );
1120 \brief "Dump commands" operation.
1122 void PyConsole_Editor::dump()
1124 QStringList aFilters;
1125 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
1127 QString fileName = SUIT_FileDlg::getFileName( this, QString(),
1128 aFilters, tr( "TOT_DUMP_PYCOMMANDS" ),
1129 false, true, new DumpCommandsFileValidator( this ) );
1130 if ( !fileName.isEmpty() ) {
1131 QFile file( fileName );
1132 if ( !file.open( QFile::WriteOnly ) )
1135 QTextStream out (&file);
1137 for ( int i=0; i<myHistory.count(); i++ ) {
1138 out << myHistory.at(i).command << endl;
1144 \brief "Save log" operation.
1146 void PyConsole_Editor::saveLog()
1148 QStringList aFilters;
1149 aFilters.append( tr( "LOG_FILES_FILTER" ) );
1151 QString fileName = SUIT_FileDlg::getFileName( this, QString(),
1152 aFilters, tr( "TOT_SAVE_PYLOG" ),
1154 if ( !fileName.isEmpty() ) {
1155 QFile file( fileName );
1156 if ( !file.open( QFile::WriteOnly ) )
1159 QTextStream out (&file);
1161 for( int i = 0; i < myHistory.count(); i++ ) {
1162 out << myHistory.at(i).prompt << myHistory.at(i).command << endl;
1163 out << myHistory.at(i).output;