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 PyConsole_Editor* e = (PyConsole_Editor*)data;
143 e->putLog( QString::fromUtf8(c) );
144 QApplication::postEvent( e, new PrintEvent( QString::fromUtf8(c), false ) );
148 void staticCallbackStderr( void* data, char* c )
150 if(!((PyConsole_Editor*)data)->isSuppressOutput()) {
151 PyConsole_Editor* e = (PyConsole_Editor*)data;
152 e->putLog( QString::fromUtf8(c) );
153 QApplication::postEvent( e, new PrintEvent( QString::fromUtf8(c), true ) );
161 Creates python editor window.
162 \param theInterp python interper
163 \param theParent parent widget
165 PyConsole_Editor::PyConsole_Editor( PyConsole_Interp* theInterp,
167 : QTextEdit( theParent ),
169 myCmdInHistory( -1 ),
171 myShowBanner( true ),
173 myIsSuppressOutput( false )
175 QString fntSet( "" );
176 QFont aFont = SUIT_Tools::stringToFont( fntSet );
178 setUndoRedoEnabled( false );
180 myPrompt = READY_PROMPT;
181 setLineWrapMode( QTextEdit::WidgetWidth );
182 setWordWrapMode( QTextOption::WrapAnywhere );
183 setAcceptRichText( false );
185 theInterp->setvoutcb( staticCallbackStdout, this );
186 theInterp->setverrcb( staticCallbackStderr, this );
188 // san - This is necessary for troubleless initialization
189 onPyInterpChanged( theInterp );
195 PyConsole_Editor::~PyConsole_Editor()
201 \brief Get Python interpreter
203 PyConsole_Interp* PyConsole_Editor::getInterp() const
209 \brief Get synchronous mode flag value.
212 \return True if python console works in synchronous mode
214 bool PyConsole_Editor::isSync() const
220 \brief Set synchronous mode flag value.
222 In synhronous mode the Python commands are executed in the GUI thread
223 and the GUI is blocked until the command is finished. In the asynchronous
224 mode each Python command is executed in the separate thread that does not
225 block the main GUI loop.
227 \param on synhronous mode flag
229 void PyConsole_Editor::setIsSync( const bool on )
235 \brief Get suppress output flag value.
237 \sa setIsSuppressOutput()
238 \return \c true if python console output is suppressed.
240 bool PyConsole_Editor::isSuppressOutput() const
242 return myIsSuppressOutput;
246 \brief Set suppress output flag value.
248 In case if suppress output flag is true, the python
249 console output suppressed.
251 \param on suppress output flag
253 void PyConsole_Editor::setIsSuppressOutput( const bool on )
255 myIsSuppressOutput = on;
259 \brief Get 'show banner' flag value.
261 \sa setIsShowBanner()
262 \return \c true if python console shows banner
264 bool PyConsole_Editor::isShowBanner() const
270 \brief Set 'show banner' flag value.
272 The banner is shown in the top of the python console window.
275 \param on 'show banner' flag
277 void PyConsole_Editor::setIsShowBanner( const bool on )
279 if ( myShowBanner != on ) {
286 \brief Check if trace logging is switched on.
288 \sa startLog(), stopLog()
289 \return \c true if trace logging is switched on
291 bool PyConsole_Editor::isLogging() const
293 return !myLogFile.isEmpty();
297 \brief Get size hint for the Python console window
298 \return size hint value
300 QSize PyConsole_Editor::sizeHint() const
302 QFontMetrics fm( font() );
303 int nbLines = ( isShowBanner() ? myBanner.split("\n").count() : 0 ) + 1;
304 QSize s(100, fm.lineSpacing()*nbLines);
309 \brief Put the string \a str to the python editor.
310 \param str string to be put in the command line of the editor
311 \param newBlock if True, then the string is printed on a new line
312 \param isError if true, the text is printed in dark red
314 void PyConsole_Editor::addText( const QString& str,
318 QTextCursor theCursor(textCursor());
321 moveCursor( QTextCursor::End );
323 theCursor.insertBlock();
325 cf.setForeground(QBrush(Qt::red));
327 cf.setForeground(QBrush(Qt::black));
328 theCursor.insertText( str, cf);
329 moveCursor( QTextCursor::End );
330 ensureCursorVisible();
334 \brief Convenient method for executing a Python command,
335 as if the user typed it manually.
336 \param command python command to be executed
338 !!! WARNING: doesn't work properly with multi-line commands. !!!
340 void PyConsole_Editor::exec( const QString& command )
342 if ( isReadOnly() ) {
343 // some interactive command is being executed in this editor...
344 // shedule the command to the queue
345 myQueue.push_back( command );
349 moveCursor( QTextCursor::End );
350 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
351 textCursor().removeSelectedText();
352 // set "ready" prompt
353 myPrompt = READY_PROMPT;
354 // clear command buffer
355 myCommandBuffer.truncate( 0 );
356 // unset history browsing mode
358 // print command line by line
359 QString cmd = command;
360 if ( !cmd.endsWith( "\n" ) ) cmd += "\n";
361 QStringList lines = command.split( "\n" );
362 for ( int i = 0; i < lines.size(); i++ ) {
363 if ( !lines[i].trimmed().isEmpty() )
364 myHistory.push_back( lines[i] );
365 addText( ( i == 0 ? READY_PROMPT : DOTS_PROMPT ) + lines[i], i != 0 );
366 putLog( QString( "%1%2\n" ).arg( i == 0 ? READY_PROMPT : DOTS_PROMPT ).arg( lines[i] ) );
370 // set read-only mode
373 setCursor( Qt::BusyCursor );
375 // post a request to execute Python command;
376 // editor will be informed via a custom event that execution has been completed
377 PyInterp_Dispatcher::Get()->Exec( createRequest( cmd ) );
381 \brief Create request to the python dispatcher for the command execution.
383 \param command python command to be executed
385 PyInterp_Request* PyConsole_Editor::createRequest( const QString& command )
387 return new ExecCommand( myInterp, command, this, isSync() );
391 \brief Execute command in the python interpreter
392 and wait until it is finished.
394 \param command python command to be executed
396 void PyConsole_Editor::execAndWait( const QString& command )
402 // create new event loop
403 bool sync = isSync();
405 myEventLoop = new QEventLoop( this );
414 // delete event loop after command is processed
421 \brief Process "Enter" key press event.
423 Execute the command entered by the user.
425 void PyConsole_Editor::handleReturn()
427 // Position cursor at the end
428 QTextCursor curs(textCursor());
429 curs.movePosition(QTextCursor::End);
433 QTextBlock par = document()->end().previous();
434 if ( !par.isValid() ) return;
437 QString cmd = par.text().remove( 0, promptSize() );
438 // extend the command buffer with the current command
439 myCommandBuffer.append( cmd );
440 // add command to the history
441 if ( !cmd.trimmed().isEmpty() )
442 myHistory.push_back( cmd );
443 putLog( QString( "%1%2\n" ).arg( myPrompt ).arg( cmd ) );
448 // set read-only mode
451 setCursor( Qt::BusyCursor );
453 // post a request to execute Python command;
454 // editor will be informed via a custom event that execution has been completed
455 PyInterp_Dispatcher::Get()->Exec( createRequest( myCommandBuffer ) );
459 \brief Process drop event.
462 \param event drop event
464 void PyConsole_Editor::dropEvent( QDropEvent* event )
466 // get the initial drop position
467 QPoint pos = event->pos();
468 QTextCursor cur = cursorForPosition( event->pos() );
469 // if the position is not in the last line move it to the end of the command line
470 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
471 moveCursor( QTextCursor::End );
472 pos = cursorRect().center();
474 // create new drop event and use it instead of the original
476 event->possibleActions(),
478 event->mouseButtons(),
479 event->keyboardModifiers(),
481 QTextEdit::dropEvent( &de );
482 // accept the original event
483 event->acceptProposedAction();
487 \brief Process mouse button release event.
489 Left mouse button: copy selection to the clipboard.
490 Middle mouse button: paste clipboard's contents.
491 \param event mouse event
493 void PyConsole_Editor::mouseReleaseEvent( QMouseEvent* event )
495 if ( event->button() == Qt::LeftButton ) {
496 QTextEdit::mouseReleaseEvent( event );
499 else if ( event->button() == Qt::MidButton ) {
500 QTextCursor cur = cursorForPosition( event->pos() );
501 // if the position is not in the last line move it to the end of the command line
502 if ( cur.position() < document()->end().previous().position() + promptSize() ) {
503 moveCursor( QTextCursor::End );
506 setTextCursor( cur );
508 const QMimeData* md = QApplication::clipboard()->mimeData( QApplication::clipboard()->supportsSelection() ?
509 QClipboard::Selection : QClipboard::Clipboard );
511 insertFromMimeData( md );
514 QTextEdit::mouseReleaseEvent( event );
519 \brief Check if the string is command.
521 Return True if the string \a str is likely to be the command
522 (i.e. it is started from the '>>>' or '...').
523 \param str string to be checked
525 bool PyConsole_Editor::isCommand( const QString& str ) const
527 return str.startsWith( READY_PROMPT ) || str.startsWith( DOTS_PROMPT );
531 \brief Handle keyboard event.
533 Implement navigation, history browsing, copy/paste and other common
535 \param event keyboard event
537 void PyConsole_Editor::keyPressEvent( QKeyEvent* event )
539 // get cursor position
540 QTextCursor cur = textCursor();
541 int curLine = cur.blockNumber();
542 int curCol = cur.columnNumber();
544 // get last edited line
545 int endLine = document()->blockCount()-1;
547 // get pressed key code
548 int aKey = event->key();
550 // check if <Ctrl> is pressed
551 bool ctrlPressed = event->modifiers() & Qt::ControlModifier;
552 // check if <Shift> is pressed
553 bool shftPressed = event->modifiers() & Qt::ShiftModifier;
555 if ( aKey == Qt::Key_Escape || ( ctrlPressed && aKey == -1 ) ) {
556 // process <Ctrl>+<Break> key-binding and <Escape> key: clear current command
557 myCommandBuffer.truncate( 0 );
558 myPrompt = READY_PROMPT;
559 addText( myPrompt, true );
560 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
563 else if ( ctrlPressed && aKey == Qt::Key_C ) {
564 // process <Ctrl>+<C> key-binding : copy
568 else if ( ctrlPressed && aKey == Qt::Key_X ) {
569 // process <Ctrl>+<X> key-binding : cut
573 else if ( ctrlPressed && aKey == Qt::Key_V ) {
574 // process <Ctrl>+<V> key-binding : paste
579 // check for printed key
580 // #### aKey = ( aKey < Qt::Key_Space || aKey > Qt::Key_ydiaeresis ) ? aKey : 0;
582 aKey = !(QChar(aKey).isPrint()) ? aKey : 0;
586 // any printed key: just print it
588 if ( curLine < endLine || curCol < promptSize() ) {
589 moveCursor( QTextCursor::End );
591 QTextEdit::keyPressEvent( event );
596 // <Enter> key: process the current command
602 // <Up> arrow key: process as follows:
603 // - without <Ctrl>, <Shift> modifiers: previous command in history
604 // - with <Ctrl> modifier key pressed: move cursor one row up without selection
605 // - with <Shift> modifier key pressed: move cursor one row up with selection
606 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
608 if ( ctrlPressed && shftPressed ) {
609 int value = verticalScrollBar()->value();
610 int spacing = fontMetrics().lineSpacing();
611 verticalScrollBar()->setValue( value > spacing ? value-spacing : 0 );
613 else if ( shftPressed || ctrlPressed ) {
615 moveCursor( QTextCursor::Up,
616 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
619 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
620 // set history browsing mode
621 myCmdInHistory = myHistory.count();
622 // remember current command
623 QTextBlock par = document()->end().previous();
624 myCurrentCommand = par.text().remove( 0, promptSize() );
626 if ( myCmdInHistory > 0 ) {
628 // get previous command in the history
629 QString previousCommand = myHistory.at( myCmdInHistory );
630 // print previous command
631 moveCursor( QTextCursor::End );
632 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
633 textCursor().removeSelectedText();
634 addText( myPrompt + previousCommand );
635 // move cursor to the end
636 moveCursor( QTextCursor::End );
642 // <Down> arrow key: process as follows:
643 // - without <Ctrl>, <Shift> modifiers: next command in history
644 // - with <Ctrl> modifier key pressed: move cursor one row down without selection
645 // - with <Shift> modifier key pressed: move cursor one row down with selection
646 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
648 if ( ctrlPressed && shftPressed ) {
649 int value = verticalScrollBar()->value();
650 int maxval = verticalScrollBar()->maximum();
651 int spacing = fontMetrics().lineSpacing();
652 verticalScrollBar()->setValue( value+spacing < maxval ? value+spacing : maxval );
654 else if ( shftPressed || ctrlPressed) {
655 if ( curLine < endLine )
656 moveCursor( QTextCursor::Down,
657 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
660 if ( myCmdInHistory >= 0 ) {
661 // get next command in the history
664 if ( myCmdInHistory < myHistory.count() ) {
665 // next command in history
666 nextCommand = myHistory.at( myCmdInHistory );
669 // end of history is reached
670 // last printed command
671 nextCommand = myCurrentCommand;
672 // unset history browsing mode
675 // print next or current command
676 moveCursor( QTextCursor::End );
677 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
678 textCursor().removeSelectedText();
679 addText( myPrompt + nextCommand );
680 // move cursor to the end
681 moveCursor( QTextCursor::End );
687 // <Left> arrow key: process as follows:
688 // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
689 // - with <Ctrl> modifier key pressed: move one word left (taking into account prompt)
690 // - with <Shift> modifier key pressed: move one symbol left with selection
691 // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
693 QString txt = textCursor().block().text();
694 if ( !shftPressed && isCommand( txt ) && curCol <= promptSize() ) {
695 moveCursor( QTextCursor::Up );
696 moveCursor( QTextCursor::EndOfBlock );
699 QTextEdit::keyPressEvent( event );
704 // <Right> arrow key: process as follows:
705 // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
706 // - with <Ctrl> modifier key pressed: move one word right (taking into account prompt)
707 // - with <Shift> modifier key pressed: move one symbol right with selection
708 // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
710 QString txt = textCursor().block().text();
711 if ( !shftPressed ) {
712 if ( curCol < txt.length() ) {
713 if ( isCommand( txt ) && curCol < promptSize() ) {
714 cur.setPosition( cur.block().position() + promptSize() );
715 setTextCursor( cur );
720 if ( curLine < endLine && isCommand( textCursor().block().next().text() ) ) {
721 cur.setPosition( cur.position() + promptSize()+1 );
722 setTextCursor( cur );
723 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
728 QTextEdit::keyPressEvent( event );
732 // <PageUp> key: process as follows:
733 // - without <Ctrl>, <Shift> modifiers: first command in history
734 // - with <Ctrl> modifier key pressed: move cursor one page up without selection
735 // - with <Shift> modifier key pressed: move cursor one page up with selection
736 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
738 if ( ctrlPressed && shftPressed ) {
739 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
741 else if ( shftPressed || ctrlPressed ) {
743 qreal lastY = cursorRect( cur ).top();
745 // move using movePosition to keep the cursor's x
747 qreal y = cursorRect( cur ).top();
748 distance += qAbs( y - lastY );
750 moved = cur.movePosition( QTextCursor::Up,
751 shftPressed ? QTextCursor::KeepAnchor :
752 QTextCursor::MoveAnchor );
753 } while ( moved && distance < viewport()->height() );
755 cur.movePosition( QTextCursor::Down,
756 shftPressed ? QTextCursor::KeepAnchor :
757 QTextCursor::MoveAnchor );
758 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
760 setTextCursor( cur );
763 if ( myCmdInHistory < 0 && myHistory.count() > 0 ) {
764 // set history browsing mode
765 myCmdInHistory = myHistory.count();
766 // remember current command
767 QTextBlock par = document()->end().previous();
768 myCurrentCommand = par.text().remove( 0, promptSize() );
770 if ( myCmdInHistory > 0 ) {
772 // get very first command in the history
773 QString firstCommand = myHistory.at( myCmdInHistory );
774 // print first command
775 moveCursor( QTextCursor::End );
776 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
777 textCursor().removeSelectedText();
778 addText( myPrompt + firstCommand );
779 // move cursor to the end
780 moveCursor( QTextCursor::End );
785 case Qt::Key_PageDown:
786 // <PageDown> key: process as follows:
787 // - without <Ctrl>, <Shift> modifiers: last command in history
788 // - with <Ctrl> modifier key pressed: move cursor one page down without selection
789 // - with <Shift> modifier key pressed: move cursor one page down with selection
790 // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
792 if ( ctrlPressed && shftPressed ) {
793 verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
795 else if ( shftPressed || ctrlPressed ) {
797 qreal lastY = cursorRect( cur ).top();
799 // move using movePosition to keep the cursor's x
801 qreal y = cursorRect( cur ).top();
802 distance += qAbs( y - lastY );
804 moved = cur.movePosition( QTextCursor::Down,
805 shftPressed ? QTextCursor::KeepAnchor :
806 QTextCursor::MoveAnchor );
807 } while ( moved && distance < viewport()->height() );
809 cur.movePosition( QTextCursor::Up,
810 shftPressed ? QTextCursor::KeepAnchor :
811 QTextCursor::MoveAnchor );
812 verticalScrollBar()->triggerAction( QAbstractSlider::SliderPageStepSub );
814 setTextCursor( cur );
817 if ( myCmdInHistory >= 0 ) {
818 // unset history browsing mode
820 // print current command
821 moveCursor( QTextCursor::End );
822 moveCursor( QTextCursor::StartOfBlock, QTextCursor::KeepAnchor );
823 textCursor().removeSelectedText();
824 addText( myPrompt + myCurrentCommand );
825 // move cursor to the end
826 moveCursor( QTextCursor::End );
832 // <Home> key: process as follows:
833 // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
834 // - with <Ctrl> modifier key pressed: move cursor to the very first symbol without selection
835 // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
836 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
839 moveCursor( QTextCursor::Start,
840 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
843 QString txt = textCursor().block().text();
844 if ( isCommand( txt ) ) {
846 if ( curCol > promptSize() ) {
847 cur.movePosition( QTextCursor::StartOfLine, QTextCursor::KeepAnchor );
848 cur.movePosition( QTextCursor::Right, QTextCursor::KeepAnchor, promptSize() );
852 cur.movePosition( QTextCursor::StartOfLine );
853 cur.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, promptSize() );
855 setTextCursor( cur );
858 moveCursor( QTextCursor::StartOfBlock,
859 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
861 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
866 // <End> key: process as follows:
867 // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
868 // - with <Ctrl> modifier key pressed: move cursor to the very last symbol without selection
869 // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
870 // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
872 moveCursor( ctrlPressed ? QTextCursor::End : QTextCursor::EndOfBlock,
873 shftPressed ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor );
876 case Qt::Key_Backspace :
877 // <Backspace> key: process as follows
878 // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
879 // - with <Shift> modifier key pressed: delete previous word
880 // - with <Ctrl> modifier key pressed: delete text from the cursor to the line beginning
881 // works only for last (command) line
883 if ( cur.hasSelection() ) {
886 else if ( cur.position() > document()->end().previous().position() + promptSize() ) {
888 moveCursor( QTextCursor::PreviousWord, QTextCursor::KeepAnchor );
889 textCursor().removeSelectedText();
891 else if ( ctrlPressed ) {
892 cur.setPosition( document()->end().previous().position() + promptSize(),
893 QTextCursor::KeepAnchor );
894 setTextCursor( cur );
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_Delete :
909 // <Delete> key: process as follows
910 // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
911 // - with <Shift> modifier key pressed: delete next word
912 // - with <Ctrl> modifier key pressed: delete text from the cursor to the end of line
913 // works only for last (command) line
915 if ( cur.hasSelection() ) {
918 else if ( cur.position() > document()->end().previous().position() + promptSize()-1 ) {
920 moveCursor( QTextCursor::NextWord, QTextCursor::KeepAnchor );
921 textCursor().removeSelectedText();
923 else if ( ctrlPressed ) {
924 moveCursor( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor );
925 textCursor().removeSelectedText();
928 QTextEdit::keyPressEvent( event );
932 cur.setPosition( document()->end().previous().position() + promptSize() );
933 setTextCursor( cur );
934 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
938 case Qt::Key_Insert :
939 // <Insert> key: process as follows
940 // - with <Ctrl> modifier key pressed: copy()
941 // - with <Shift> modifier key pressed: paste() to the command line
946 else if ( shftPressed ) {
950 QTextEdit::keyPressEvent( event );
957 \brief Handle notification event coming from Python dispatcher.
958 \param event notification event
960 void PyConsole_Editor::customEvent( QEvent* event )
962 switch( event->type() )
964 case PrintEvent::EVENT_ID:
966 PrintEvent* pe=(PrintEvent*)event;
967 addText( pe->text(), false, pe->isError());
970 case PyInterp_Event::ES_OK:
971 case PyInterp_Event::ES_ERROR:
973 // clear command buffer
974 myCommandBuffer.truncate( 0 );
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 );
982 // set "ready" prompt
983 myPrompt = READY_PROMPT;
987 // stop event loop (if running)
992 case PyInterp_Event::ES_INCOMPLETE:
994 // extend command buffer (multi-line command)
995 myCommandBuffer.append( "\n" );
996 // add caret return line if necessary
997 QTextBlock par = document()->end().previous();
998 QString txt = par.text();
999 txt.truncate( txt.length() - 1 );
1000 // IPAL19397 : addText moved to handleReturn() method
1001 //if ( !txt.isEmpty() )
1002 // addText( "", true );
1004 myPrompt = DOTS_PROMPT;
1005 addText( myPrompt/*, true*/ ); // IPAL19397
1006 // unset busy cursor
1008 // stop event loop (if running)
1010 myEventLoop->exit();
1014 QTextEdit::customEvent( event );
1017 // unset read-only state
1018 setReadOnly( false );
1019 // unset history browsing mode
1020 myCmdInHistory = -1;
1022 if ( (int)event->type() == (int)PyInterp_Event::ES_OK && myQueue.count() > 0 )
1024 // process the next sheduled command from the queue (if there is any)
1025 QString nextcmd = myQueue[0];
1026 myQueue.pop_front();
1032 \brief Handle Python interpreter change.
1034 Perform initialization actions if the interpreter is changed.
1035 \param interp python interpreter is being set
1037 void PyConsole_Editor::onPyInterpChanged( PyConsole_Interp* interp )
1039 if ( myInterp != interp
1040 // Force read-only state and wait cursor when myInterp is NULL
1045 myBanner = myInterp->getbanner().c_str();
1046 if ( isShowBanner() )
1047 addText( myBanner );
1048 // clear command buffer
1049 myCommandBuffer.truncate(0);
1050 // unset read-only state
1051 setReadOnly( false );
1052 // unset history browsing mode
1053 myCmdInHistory = -1;
1055 addText( myPrompt );
1056 // unset busy cursor
1057 viewport()->unsetCursor();
1058 // stop event loop (if running)
1060 myEventLoop->exit();
1065 // set read-only state
1066 setReadOnly( true );
1068 setCursor( Qt::WaitCursor );
1074 \brief "Copy" operation.
1076 Reimplemented from Qt.
1077 Warning! In Qt4 this method is not virtual.
1079 void PyConsole_Editor::cut()
1081 QTextCursor cur = textCursor();
1082 if ( cur.hasSelection() ) {
1083 QApplication::clipboard()->setText( cur.selectedText() );
1084 int startSelection = cur.selectionStart();
1085 if ( startSelection < document()->end().previous().position() + promptSize() )
1086 startSelection = document()->end().previous().position() + promptSize();
1087 int endSelection = cur.selectionEnd();
1088 if ( endSelection < document()->end().previous().position() + promptSize() )
1089 endSelection = document()->end().previous().position() + promptSize();
1090 cur.setPosition( startSelection );
1091 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1092 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1093 setTextCursor( cur );
1094 textCursor().removeSelectedText();
1099 \brief "Paste" operation.
1101 Reimplemented from Qt.
1102 Warning! In Qt4 this method is not virtual.
1104 void PyConsole_Editor::paste()
1106 QTextCursor cur = textCursor();
1107 if ( cur.hasSelection() ) {
1108 int startSelection = cur.selectionStart();
1109 if ( startSelection < document()->end().previous().position() + promptSize() )
1110 startSelection = document()->end().previous().position() + promptSize();
1111 int endSelection = cur.selectionEnd();
1112 if ( endSelection < document()->end().previous().position() + promptSize() )
1113 endSelection = document()->end().previous().position() + promptSize();
1114 cur.setPosition( startSelection );
1115 cur.setPosition( endSelection, QTextCursor::KeepAnchor );
1116 horizontalScrollBar()->setValue( horizontalScrollBar()->minimum() );
1117 setTextCursor( cur );
1118 textCursor().removeSelectedText();
1120 if ( textCursor().position() < document()->end().previous().position() + promptSize() )
1121 moveCursor( QTextCursor::End );
1126 \brief "Clear" operation.
1128 Reimplemented from Qt.
1129 Warning! In Qt4 this method is not virtual.
1131 void PyConsole_Editor::clear()
1134 if ( isShowBanner() )
1135 addText( myBanner );
1136 myPrompt = READY_PROMPT;
1137 addText( myPrompt );
1141 \brief "Dump commands" operation.
1143 void PyConsole_Editor::dump()
1145 QStringList aFilters;
1146 aFilters.append( tr( "PYTHON_FILES_FILTER" ) );
1148 QString fileName = SUIT_FileDlg::getFileName( this, QString(),
1149 aFilters, tr( "TOT_DUMP_PYCOMMANDS" ),
1150 false, true, new DumpCommandsFileValidator( this ) );
1151 if ( !fileName.isEmpty() ) {
1152 QFile file( fileName );
1153 if ( !file.open( QFile::WriteOnly ) )
1156 QTextStream out (&file);
1158 for ( int i=0; i<myHistory.count(); i++ ) {
1159 out << myHistory[i] << endl;
1165 \brief "Start log" operation.
1167 void PyConsole_Editor::startLog()
1169 QStringList aFilters;
1170 aFilters.append( tr( "LOG_FILES_FILTER" ) );
1173 QString fileName = SUIT_FileDlg::getFileName( this, QString(),
1174 aFilters, tr( "TOT_SAVE_PYLOG" ),
1176 if ( !fileName.isEmpty() ) {
1177 if ( startLog( fileName ) ) {
1181 SUIT_MessageBox::critical( this,
1182 QObject::tr("ERR_ERROR"),
1183 QObject::tr("ERR_FILE_NOT_WRITABLE") );
1193 \brief Start python trace logging
1194 \param fileName the path to the log file
1197 bool PyConsole_Editor::startLog( const QString& fileName )
1200 if ( !fileName.isEmpty() ) {
1201 QFile file( fileName );
1202 if ( file.open( QFile::WriteOnly ) ) {
1204 myLogFile = fileName;
1212 \brief "Stop log" operation.
1215 void PyConsole_Editor::stopLog()
1217 myLogFile = QString();
1221 \brief Put string to the log file
1223 void PyConsole_Editor::putLog( const QString& s )
1225 if ( !myLogFile.isEmpty() ) {
1226 QFile file( myLogFile );
1227 if ( !file.open( QFile::Append ) )
1230 QTextStream out (&file);