Salome HOME
SMH: Add again in binary mode
[modules/gui.git] / src / PythonConsole / PythonConsole_PyEditor.cxx
1 //  SALOME SALOMEGUI : implementation of desktop and GUI kernel
2 //
3 //  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 //  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
5 // 
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. 
10 // 
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. 
15 // 
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 
19 // 
20 //  See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org 
21 //
22 //
23 //
24 //  File   : PythonConsole_PyEditor.cxx
25 //  Author : Nicolas REJNERI
26 //  Module : SALOME
27 //  $Header$
28
29 #include <PythonConsole_PyEditor.h> // this include must be first (see PyInterp_base.h)!
30 #include <PyInterp_Dispatcher.h>
31 #include <SUIT_Tools.h>
32
33 #include <qapplication.h>
34 #include <qmap.h>
35 #include <qclipboard.h>
36 #include <qdragobject.h>
37
38 //#include "utilities.h"
39 using namespace std;
40
41
42 //#ifdef _DEBUG_
43 //static int MYDEBUG = 1;
44 //#else
45 //static int MYDEBUG = 0;
46 //#endif
47
48
49 enum { IdCopy, IdPaste, IdClear, IdSelectAll };
50
51
52 static QString READY_PROMPT = ">>> ";
53 static QString DOTS_PROMPT  = "... ";
54 #define PROMPT_SIZE _currentPrompt.length()
55
56 class ExecCommand : public PyInterp_LockRequest
57 {
58 public:
59   ExecCommand(PyInterp_base* theInterp, const char* theCommand, PythonConsole_PyEditor* theListener)
60 : PyInterp_LockRequest( theInterp, theListener ), myCommand(theCommand), myState( PyInterp_Event::OK )
61   {}
62
63 protected:
64   virtual void execute(){
65     if(myCommand != ""){
66 //      if(MYDEBUG) MESSAGE("*** ExecCommand::execute() started");
67       int ret = getInterp()->run( myCommand.latin1() );
68 //      if(MYDEBUG) MESSAGE("ExecCommand::execute() - myInterp = "<<getInterp()<<"; myCommand = '"<<myCommand.latin1()<<"' - "<<ret);
69       if(ret < 0)
70         myState = PyInterp_Event::ERROR;
71       else if(ret > 0)
72         myState = PyInterp_Event::INCOMPLETE;
73       myError  = getInterp()->getverr().c_str();
74       myOutput = getInterp()->getvout().c_str();
75 //      if(MYDEBUG) MESSAGE("*** ExecCommand::execute() finished");
76     }else{
77       myError = "";
78       myOutput = "";
79     }
80   }
81
82   virtual QEvent* createEvent() const
83   {
84     return new PyInterp_Event( myState, (PyInterp_Request*)this );    
85   }
86
87 public:
88   QString myError;
89   QString myOutput;
90
91 private:
92   QString myCommand;
93   int myState;
94 };
95
96
97 /*!
98     Constructor
99 */
100 PythonConsole_PyEditor::PythonConsole_PyEditor(PyInterp_base* theInterp, QWidget *theParent, const char* theName): 
101   QTextEdit(theParent,theName),
102   myInterp( 0 )
103 {
104   QString fntSet( "" );
105   QFont aFont = SUIT_Tools::stringToFont( fntSet );
106   setFont(aFont);
107   setTextFormat(QTextEdit::PlainText);
108
109   _currentPrompt = READY_PROMPT;
110   setWordWrap(NoWrap);
111
112   connect(this,SIGNAL(returnPressed()),this,SLOT(handleReturn()) );
113
114   // san - This is necessary for troubleless initialization
115   onPyInterpChanged( theInterp );
116 }
117
118 /*!
119     Destructor
120 */
121 PythonConsole_PyEditor::~PythonConsole_PyEditor()
122 {
123 //  if(MYDEBUG) MESSAGE("PythonConsole_PyEditor::~PythonConsole_PyEditor()");
124 }
125
126 /*!
127     Called to insert a string s 
128 */
129 void PythonConsole_PyEditor::setText(QString s)
130 {
131   int para=paragraphs()-1;
132   int col=paragraphLength(para);
133   insertAt(s,para,col);
134   int n = paragraphs()-1;  
135   setCursorPosition( n, paragraphLength(n)); 
136 }
137
138 /*!
139     Convenient method for executing a Python command,
140     as if the user typed it manually
141 */
142 void PythonConsole_PyEditor::exec( const QString& command )
143 {
144   // Some interactive command is being executed in this editor -> do nothing
145   if ( isReadOnly() )
146     return;
147   int para=paragraphs()-1;
148   removeParagraph( para );
149   _currentPrompt = READY_PROMPT;
150   _buf.truncate(0);
151   _isInHistory = false;
152   setText(_currentPrompt); 
153   setText( command + "\n" ); 
154   handleReturn();
155 }
156
157 /*!
158     Called when an handleReturn
159 */
160 void PythonConsole_PyEditor::handleReturn()
161 {
162   int para=paragraphs()-2;
163   _buf.append(text(para).remove(0,PROMPT_SIZE));
164   _buf.truncate( _buf.length() - 1 );
165   setReadOnly( true );
166   viewport()->setCursor( waitCursor );
167   
168   // Post a request to execute Python command
169   // Editor will be informed via a custom event that execution has been completed
170   PyInterp_Dispatcher::Get()->Exec( new ExecCommand( myInterp, _buf.latin1(), this ) );
171 }
172
173 /*
174    Processes drop event: paste dragged text
175 */
176 void PythonConsole_PyEditor::contentsDropEvent( QDropEvent* event )
177 {
178   event->acceptAction();
179   QString text;
180   if ( QTextDrag::decode( event, text ) ) {
181     int par, col;
182     int endLine = paragraphs() -1;
183     col = charAt( event->pos(), &par );
184     
185     if ( col >= 0 && par >= 0 ) {
186       if ( par != endLine || col < PROMPT_SIZE ) {
187         par = endLine;
188         col = paragraphLength( endLine );
189       }
190       setCursorPosition( par, col );
191       insertAt( text, par, col );
192       removeSelection();
193     }
194   }
195 }
196
197 /*
198    Processes middle button release event - paste clipboard's contents
199 */
200 void PythonConsole_PyEditor::contentsMouseReleaseEvent( QMouseEvent* event )
201 {
202   if ( event->button() == LeftButton ) {
203     QTextEdit::contentsMouseReleaseEvent(event);
204     copy();
205   }
206   if ( event->button() == MidButton ) {
207     if (QApplication::clipboard()->supportsSelection()) {
208       int par, col;
209       int endLine = paragraphs() -1;
210       col = charAt( event->pos(), &par );
211       if ( col >= 0 && par >= 0 ) {
212         if ( par != endLine || col < PROMPT_SIZE )
213           setCursorPosition( endLine, paragraphLength( endLine ) );
214         else
215           setCursorPosition( par, col );
216         QApplication::clipboard()->setSelectionMode(TRUE);
217         paste();
218         QApplication::clipboard()->setSelectionMode(FALSE);
219       }
220     }
221   }
222   else {
223     QTextEdit::contentsMouseReleaseEvent(event);
224   }
225 }
226
227 /*
228    Processes own popup menu
229 */
230 void PythonConsole_PyEditor::mousePressEvent (QMouseEvent* event)
231 {
232   if ( event->button() == RightButton ) {
233     QPopupMenu *popup = new QPopupMenu( this );
234     QMap<int, int> idMap;
235
236     int para1, col1, para2, col2;
237     getSelection(&para1, &col1, &para2, &col2);
238     bool allSelected = hasSelectedText() &&
239       para1 == 0 && para2 == paragraphs()-1 && col1 == 0 && para2 == paragraphLength(para2);
240     int id;
241     id = popup->insertItem( tr( "EDIT_COPY_CMD" ) );
242     idMap.insert(IdCopy, id);
243     id = popup->insertItem( tr( "EDIT_PASTE_CMD" ) );
244     idMap.insert(IdPaste, id);
245     id = popup->insertItem( tr( "EDIT_CLEAR_CMD" ) );
246     idMap.insert(IdClear, id);
247     popup->insertSeparator();
248     id = popup->insertItem( tr( "EDIT_SELECTALL_CMD" ) );
249     idMap.insert(IdSelectAll, id);
250     popup->setItemEnabled( idMap[ IdCopy ],  hasSelectedText() );
251     popup->setItemEnabled( idMap[ IdPaste ],
252                           !isReadOnly() && (bool)QApplication::clipboard()->text().length() );
253     popup->setItemEnabled( idMap[ IdSelectAll ],
254                           (bool)text().length() && !allSelected );
255     
256     int r = popup->exec( event->globalPos() );
257     delete popup;
258     
259     if ( r == idMap[ IdCopy ] ) {
260       copy();
261     }
262     else if ( r == idMap[ IdPaste ] ) {
263       paste();
264     }
265     else if ( r == idMap[ IdClear ] ) {
266       clear();
267       setText(myBanner);
268       _currentPrompt = READY_PROMPT;
269       setText(_currentPrompt);
270     }
271     else if ( r == idMap[ IdSelectAll ] ) {
272       selectAll();
273     }
274   }
275   else {
276     QTextEdit::mousePressEvent(event);
277   }
278 }
279
280 /*!
281    Checks, is the string a command line or not.
282 */
283
284 bool PythonConsole_PyEditor::isCommand( const QString& str) const
285 {
286   // prompt may be '>>> ' or for '... '
287   return ( str.find( READY_PROMPT ) == 0 || str.find( DOTS_PROMPT ) == 0 );
288 }
289
290
291 /*!
292     Called when a keyPress event
293 */
294 void PythonConsole_PyEditor::keyPressEvent( QKeyEvent* e )
295 {
296   // get cursor position
297   int curLine, curCol;
298   getCursorPosition(&curLine, &curCol);
299
300   // get last edited line
301   int endLine = paragraphs() -1;
302
303   // get pressed key code
304   int aKey = e->key();
305
306   // check if <Ctrl> is pressed
307   bool ctrlPressed = e->state() & ControlButton;
308   // check if <Shift> is pressed
309   bool shftPressed = e->state() & ShiftButton;
310   // check if <Alt> is pressed
311   bool altPressed = e->state() & AltButton;
312
313   // process <Ctrl>+<C> key-bindings
314   if ( aKey == Key_C && ctrlPressed ) {
315     _buf.truncate(0);
316     setText("\n");
317     _currentPrompt = READY_PROMPT;
318     setText(_currentPrompt);
319     return;
320   }
321
322   // check for printed key
323   aKey = ( aKey < Key_Space || aKey > Key_ydiaeresis ) ? aKey : 0;
324
325   switch ( aKey ) {
326   case 0 :
327     // any printed key
328     {
329       if ( curLine < endLine || curCol < PROMPT_SIZE )
330         moveCursor( QTextEdit::MoveEnd, false );
331       QTextEdit::keyPressEvent( e );
332       break;
333     }
334   case Key_Return:
335   case Key_Enter:
336     // <Enter> key
337     {
338       moveCursor( QTextEdit::MoveEnd, false );
339       QTextEdit::keyPressEvent( e );
340       break;
341     }
342   case Key_Up:
343     // <Up> arrow key: process as follows:
344     // - without <Ctrl>, <Shift> modifiers: previous command in history
345     // - with <Ctrl> modifier key pressed:  move cursor one row up without selection
346     // - with <Shift> modifier key pressed: move cursor one row up with selection
347     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
348     {
349       if ( ctrlPressed && shftPressed ) {
350         scrollBy( 0, -QFontMetrics( font() ).lineSpacing() );
351       }
352       else if ( shftPressed ) {
353         if ( curLine > 0 )
354           moveCursor( QTextEdit::MoveUp, true );
355       }
356       else if ( ctrlPressed ) {
357         moveCursor( QTextEdit::MoveUp, false );
358       }
359       else { 
360         QString histLine = _currentPrompt;
361         if ( ! _isInHistory ) {
362           _isInHistory = true;
363           _currentCommand = text( endLine ).remove( 0, PROMPT_SIZE );
364           _currentCommand.truncate( _currentCommand.length() - 1 );
365         }
366         QString previousCommand = myInterp->getPrevious();
367         if ( previousCommand.compare( BEGIN_HISTORY_PY ) != 0 )
368   {
369     removeParagraph( endLine );
370           histLine.append( previousCommand );
371     append( histLine );
372         }
373         moveCursor( QTextEdit::MoveEnd, false );
374       }
375       break;
376     }
377   case Key_Down:
378     // <Down> arrow key: process as follows:
379     // - without <Ctrl>, <Shift> modifiers: next command in history
380     // - with <Ctrl> modifier key pressed:  move cursor one row down without selection
381     // - with <Shift> modifier key pressed: move cursor one row down with selection
382     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
383     {
384       if ( ctrlPressed && shftPressed ) {
385         scrollBy( 0, QFontMetrics( font() ).lineSpacing() );
386       }
387       else if ( shftPressed ) {
388         if ( curLine < endLine )
389           moveCursor( QTextEdit::MoveDown, true );
390       }
391       else if ( ctrlPressed ) {
392         moveCursor( QTextEdit::MoveDown, false );
393       }
394       else { 
395         QString histLine = _currentPrompt;
396         QString nextCommand = myInterp->getNext();
397         if ( nextCommand.compare( TOP_HISTORY_PY ) != 0 ) {
398           removeParagraph( endLine );
399           histLine.append( nextCommand );
400           append( histLine );
401         }
402         else {
403           if (_isInHistory) {
404             _isInHistory = false;
405             removeParagraph( endLine );
406             histLine.append( _currentCommand );
407             append( histLine );
408           }
409         }
410         moveCursor( QTextEdit::MoveEnd, false );
411       }
412       break;
413     }
414   case Key_Left:
415     // <Left> arrow key: process as follows:
416     // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
417     // - with <Ctrl> modifier key pressed:  move one word left (taking into account prompt)
418     // - with <Shift> modifier key pressed: move one symbol left with selection
419     // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
420     {
421       if ( !shftPressed && isCommand( text( curLine ) ) && curCol <= PROMPT_SIZE ) {
422         setCursorPosition( curLine-1, 0 );
423         moveCursor( QTextEdit::MoveLineEnd, false );
424       }
425       else {
426         QTextEdit::keyPressEvent( e );
427       }
428       break;
429     }
430   case Key_Right:
431     // <Right> arrow key: process as follows:
432     // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
433     // - with <Ctrl> modifier key pressed:  move one word right (taking into account prompt)
434     // - with <Shift> modifier key pressed: move one symbol right with selection
435     // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
436     {
437       if ( !shftPressed ) {
438         if ( curCol < paragraphLength( curLine ) ) {
439           if ( isCommand( text( curLine ) ) && curCol < PROMPT_SIZE ) {
440             setCursorPosition( curLine, PROMPT_SIZE );
441             break;
442           }
443         }
444         else {
445           if ( curLine < endLine && isCommand( text( curLine+1 ) ) ) {
446             setCursorPosition( curLine+1, PROMPT_SIZE );
447             break;
448           }
449         }
450       }
451       QTextEdit::keyPressEvent( e );
452       break;
453     }
454   case Key_PageUp:
455     // <PageUp> key: process as follows:
456     // - without <Ctrl>, <Shift> modifiers: first command in history
457     // - with <Ctrl> modifier key pressed:  move cursor one page up without selection
458     // - with <Shift> modifier key pressed: move cursor one page up with selection
459     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
460     {
461       if ( ctrlPressed && shftPressed ) {
462         scrollBy( 0, -visibleHeight() );
463       }
464       else if ( shftPressed ) {
465         if ( curLine > 0 )
466           moveCursor( QTextEdit::MovePgUp, true );
467       }
468       else if ( ctrlPressed ) {
469         moveCursor( QTextEdit::MovePgUp, false );
470       }
471       else { 
472         QString histLine = _currentPrompt;
473         if ( ! _isInHistory ) {
474           _isInHistory = true;
475           _currentCommand = text( endLine ).remove( 0, PROMPT_SIZE );
476           _currentCommand.truncate( _currentCommand.length() - 1 );
477         }
478         QString firstCommand = myInterp->getPrevious();
479         QString pcmd;
480         while ( ( pcmd = QString( myInterp->getPrevious() ) ).compare( BEGIN_HISTORY_PY ) != 0 )
481           firstCommand = pcmd;
482         if ( firstCommand.compare( BEGIN_HISTORY_PY ) != 0 ) {
483           removeParagraph( endLine );
484           histLine.append( firstCommand );
485           insertParagraph( histLine, -1 );
486         }
487         moveCursor( QTextEdit::MoveEnd, false );
488       }
489       break;
490     }
491   case Key_PageDown:
492     // <PageDown> key: process as follows:
493     // - without <Ctrl>, <Shift> modifiers: last command in history
494     // - with <Ctrl> modifier key pressed:  move cursor one page down without selection
495     // - with <Shift> modifier key pressed: move cursor one page down with selection
496     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
497     {
498       if ( ctrlPressed && shftPressed ) {
499         scrollBy( 0, visibleHeight() );
500       }
501       else if ( shftPressed ) {
502         if ( curLine < endLine )
503           moveCursor( QTextEdit::MovePgDown, true );
504       }
505       else if ( ctrlPressed ) {
506         moveCursor( QTextEdit::MovePgDown, false );
507       }
508       else { 
509         if ( _isInHistory ) {
510           QString histLine = _currentPrompt;
511           while ( QString( myInterp->getNext() ).compare( TOP_HISTORY_PY ) != 0 );
512           _isInHistory = false;
513           removeParagraph( endLine );
514           histLine.append( _currentCommand );
515           insertParagraph( histLine, -1 );
516         }
517         moveCursor( QTextEdit::MoveEnd, false );
518       }
519       break;
520     }
521   case Key_Home: 
522     // <Home> key: process as follows:
523     // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
524     // - with <Ctrl> modifier key pressed:  move cursor to the very first symbol without selection
525     // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
526     // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
527     {
528       if ( ctrlPressed ) { 
529         moveCursor( QTextEdit::MoveHome, shftPressed );
530       }
531       else {
532         if ( isCommand( text( curLine ) ) ) {
533           int ps1, ps2, cs1, cs2;
534           bool hasSelection = hasSelectedText();
535           if ( hasSelection )
536             getSelection( &ps1, &cs1, &ps2, &cs2 );
537           removeSelection();
538           horizontalScrollBar()->setValue( horizontalScrollBar()->minValue() );
539           if ( curCol > PROMPT_SIZE && shftPressed ) 
540             setSelection( curLine, PROMPT_SIZE, curLine, ( hasSelection && ps1 == ps2 && ps1 == curLine && cs2 > PROMPT_SIZE ) ? cs2 : curCol );
541           setCursorPosition( curLine, PROMPT_SIZE );
542         }
543         else {
544           moveCursor( QTextEdit::MoveLineStart, shftPressed );
545         }
546       }
547       break;
548     }
549   case Key_End:
550     // <End> key: process as follows:
551     // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
552     // - with <Ctrl> modifier key pressed:  move cursor to the very last symbol without selection
553     // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
554     // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
555     {
556       if ( ctrlPressed ) { 
557         moveCursor( QTextEdit::MoveEnd, shftPressed );
558       }
559       else {
560         moveCursor( QTextEdit::MoveLineEnd, shftPressed );
561       }
562       break;
563     }  
564   case Key_Backspace :
565     // <Backspace> key: process as follows
566     // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
567     // - with <Ctrl> modifier key pressed: delete previous word
568     // works only for last (command) line
569     {
570       if ( curLine == endLine && ( curCol > PROMPT_SIZE || curCol >= PROMPT_SIZE && hasSelectedText() ) ) {
571         if ( ctrlPressed && !hasSelectedText() ) {
572           QString txt = text( curLine );
573           int ind = curCol-1;
574           while ( ind > 0 && txt[ ind ] == ' ' ) ind--;
575           ind = txt.findRev( ' ', ind ) + 1;
576           if ( ind > PROMPT_SIZE-1 ) {
577             setSelection( curLine, ind, curLine, curCol );
578             removeSelectedText();
579           }
580           else {
581             QTextEdit::keyPressEvent( e );
582           }
583         }
584         else {
585           QTextEdit::keyPressEvent( e );
586         }
587       }
588       break;
589     }
590   case Key_Delete :
591     // <Delete> key: process as follows
592     // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
593     // - with <Ctrl> modifier key pressed: delete next word
594     // works only for last (command) line
595     {
596       if ( curLine == endLine && curCol > PROMPT_SIZE-1 ) {
597         if ( ctrlPressed && !hasSelectedText() ) {
598           QString txt = text( curLine );
599           int ind = curCol;
600           while ( ind < txt.length()-1 && txt[ ind ] == ' ' ) ind++;
601           ind = txt.find( ' ', ind );
602           while ( ind < txt.length()-1 && txt[ ind ] == ' ' ) ind++;
603           if ( ind > PROMPT_SIZE-1 ) {
604             setSelection( curLine, curCol, curLine, ind );
605             removeSelectedText();
606           }
607           else {
608             QTextEdit::keyPressEvent( e );
609           }
610         }
611         else {
612           QTextEdit::keyPressEvent( e );
613         }
614       }
615       break;
616     }
617   case Key_Insert :
618     // <Insert> key: process as follows
619     // - with <Ctrl> modifier key pressed:  copy()
620     // - with <Shift> modifier key pressed: paste() to the command line
621     {
622       if ( ctrlPressed ) {
623         copy();
624       }
625       else if ( shftPressed ) {
626         if ( curLine != endLine || curCol < PROMPT_SIZE )
627           moveCursor( QTextEdit::MoveEnd, false );
628         paste();
629       }
630       else
631         QTextEdit::keyPressEvent( e );
632       break;
633     }
634   }
635 }
636
637 /*!
638     Handles notifications coming from Python dispatcher
639 */
640 void PythonConsole_PyEditor::customEvent(QCustomEvent* e)
641 {
642   switch( e->type() ) {
643   case PyInterp_Event::OK:
644   case PyInterp_Event::ERROR:
645     {
646       PyInterp_Event* pe = dynamic_cast<PyInterp_Event*>( e );
647       if ( pe ){
648         ExecCommand* ec = dynamic_cast<ExecCommand*>( pe->GetRequest() );
649         if ( ec ){
650           // The next line has appeared dangerous in case if
651           // Python command execution has produced very large output.
652           // A more clever approach is needed...
653           setText(ec->myOutput);
654           setText(ec->myError);
655         }
656       }
657       _buf.truncate(0);
658       _currentPrompt = READY_PROMPT;
659       setText(_currentPrompt);
660       viewport()->unsetCursor();
661       break;
662     }
663   case PyInterp_Event::INCOMPLETE:
664     {
665       _buf.append("\n");
666       _currentPrompt = DOTS_PROMPT;
667       setText(_currentPrompt);
668       viewport()->unsetCursor();
669       break;
670     }
671   default:
672     QTextEdit::customEvent( e );
673   }
674
675   setReadOnly( false );
676   _isInHistory = false;
677 }
678
679 /*!
680    Handles Python interpreter change
681 */
682 void PythonConsole_PyEditor::onPyInterpChanged( PyInterp_base* interp )
683 {
684   if ( myInterp != interp 
685        // Force read-only state and wait cursor when myInterp is NULL
686       || !myInterp ){
687     myInterp = interp;
688     if ( myInterp ){
689       myBanner = myInterp->getbanner().c_str();
690       setText(myBanner);
691       _buf.truncate(0);
692       setReadOnly( false );
693       _isInHistory = false;
694       setText(_currentPrompt);
695       viewport()->unsetCursor();
696     }
697     else {
698       clear();
699       setReadOnly( true );
700       viewport()->setCursor( waitCursor );
701     }
702   }
703 }