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