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