Salome HOME
IPAL9137
[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
39 using namespace std;
40
41 //#ifdef _DEBUG_
42 //static int MYDEBUG = 1;
43 //#else
44 //static int MYDEBUG = 0;
45 //#endif
46
47
48 enum { IdCopy, IdPaste, IdClear, IdSelectAll };
49
50
51 static QString READY_PROMPT = ">>> ";
52 static QString DOTS_PROMPT  = "... ";
53 #define PROMPT_SIZE _currentPrompt.length()
54
55 class ExecCommand : public PyInterp_LockRequest
56 {
57 public:
58   ExecCommand(PyInterp_base* theInterp, const char* theCommand, PythonConsole_PyEditor* theListener)
59 : PyInterp_LockRequest( theInterp, theListener ), myCommand(theCommand), myState( PyInterp_Event::OK )
60   {}
61
62 protected:
63   virtual void execute(){
64     if(myCommand != ""){
65 //      if(MYDEBUG) MESSAGE("*** ExecCommand::execute() started");
66       int ret = getInterp()->run( myCommand.latin1() );
67 //      if(MYDEBUG) MESSAGE("ExecCommand::execute() - myInterp = "<<getInterp()<<"; myCommand = '"<<myCommand.latin1()<<"' - "<<ret);
68       if(ret < 0)
69         myState = PyInterp_Event::ERROR;
70       else if(ret > 0)
71         myState = PyInterp_Event::INCOMPLETE;
72       myError  = getInterp()->getverr().c_str();
73       myOutput = getInterp()->getvout().c_str();
74 //      if(MYDEBUG) MESSAGE("*** ExecCommand::execute() finished");
75     }else{
76       myError = "";
77       myOutput = "";
78     }
79   }
80
81   virtual QEvent* createEvent() const
82   {
83     return new PyInterp_Event( myState, (PyInterp_Request*)this );    
84   }
85
86 public:
87   QString myError;
88   QString myOutput;
89
90 private:
91   QString myCommand;
92   int myState;
93 };
94
95
96 /*!
97     Constructor
98 */
99 PythonConsole_PyEditor::PythonConsole_PyEditor(PyInterp_base* theInterp, QWidget *theParent, const char* theName): 
100   QTextEdit(theParent,theName),
101   myInterp( 0 )
102 {
103   QString fntSet( "" );
104   QFont aFont = SUIT_Tools::stringToFont( fntSet );
105   setFont(aFont);
106   setTextFormat(QTextEdit::PlainText);
107   setUndoRedoEnabled( false );
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( "\n" + _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
311   // process <Ctrl>+<C> key-bindings
312   if ( aKey == Key_C && ctrlPressed ) {
313     _buf.truncate(0);
314     setText("\n");
315     _currentPrompt = READY_PROMPT;
316     setText(_currentPrompt);
317     return;
318   }
319
320   // check for printed key
321   aKey = ( aKey < Key_Space || aKey > Key_ydiaeresis ) ? aKey : 0;
322
323   switch ( aKey ) {
324   case 0 :
325     // any printed key
326     {
327       if ( curLine < endLine || curCol < PROMPT_SIZE )
328         moveCursor( QTextEdit::MoveEnd, false );
329       QTextEdit::keyPressEvent( e );
330       break;
331     }
332   case Key_Return:
333   case Key_Enter:
334     // <Enter> key
335     {
336       moveCursor( QTextEdit::MoveEnd, false );
337       QTextEdit::keyPressEvent( e );
338       break;
339     }
340   case Key_Up:
341     // <Up> arrow key: process as follows:
342     // - without <Ctrl>, <Shift> modifiers: previous command in history
343     // - with <Ctrl> modifier key pressed:  move cursor one row up without selection
344     // - with <Shift> modifier key pressed: move cursor one row up with selection
345     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
346     {
347       if ( ctrlPressed && shftPressed ) {
348         scrollBy( 0, -QFontMetrics( font() ).lineSpacing() );
349       }
350       else if ( shftPressed ) {
351         if ( curLine > 0 )
352           moveCursor( QTextEdit::MoveUp, true );
353       }
354       else if ( ctrlPressed ) {
355         moveCursor( QTextEdit::MoveUp, false );
356       }
357       else { 
358         QString histLine = _currentPrompt;
359         if ( ! _isInHistory ) {
360           _isInHistory = true;
361           _currentCommand = text( endLine ).remove( 0, PROMPT_SIZE );
362           _currentCommand.truncate( _currentCommand.length() - 1 );
363         }
364         QString previousCommand = myInterp->getPrevious();
365         if ( previousCommand.compare( BEGIN_HISTORY_PY ) != 0 )
366   {
367     removeParagraph( endLine );
368           histLine.append( previousCommand );
369     append( histLine );
370         }
371         moveCursor( QTextEdit::MoveEnd, false );
372       }
373       break;
374     }
375   case Key_Down:
376     // <Down> arrow key: process as follows:
377     // - without <Ctrl>, <Shift> modifiers: next command in history
378     // - with <Ctrl> modifier key pressed:  move cursor one row down without selection
379     // - with <Shift> modifier key pressed: move cursor one row down with selection
380     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
381     {
382       if ( ctrlPressed && shftPressed ) {
383         scrollBy( 0, QFontMetrics( font() ).lineSpacing() );
384       }
385       else if ( shftPressed ) {
386         if ( curLine < endLine )
387           moveCursor( QTextEdit::MoveDown, true );
388       }
389       else if ( ctrlPressed ) {
390         moveCursor( QTextEdit::MoveDown, false );
391       }
392       else { 
393         QString histLine = _currentPrompt;
394         QString nextCommand = myInterp->getNext();
395         if ( nextCommand.compare( TOP_HISTORY_PY ) != 0 ) {
396           removeParagraph( endLine );
397           histLine.append( nextCommand );
398           append( histLine );
399         }
400         else {
401           if (_isInHistory) {
402             _isInHistory = false;
403             removeParagraph( endLine );
404             histLine.append( _currentCommand );
405             append( histLine );
406           }
407         }
408         moveCursor( QTextEdit::MoveEnd, false );
409       }
410       break;
411     }
412   case Key_Left:
413     // <Left> arrow key: process as follows:
414     // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
415     // - with <Ctrl> modifier key pressed:  move one word left (taking into account prompt)
416     // - with <Shift> modifier key pressed: move one symbol left with selection
417     // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
418     {
419       if ( !shftPressed && isCommand( text( curLine ) ) && curCol <= PROMPT_SIZE ) {
420         setCursorPosition( curLine-1, 0 );
421         moveCursor( QTextEdit::MoveLineEnd, false );
422       }
423       else {
424         QTextEdit::keyPressEvent( e );
425       }
426       break;
427     }
428   case Key_Right:
429     // <Right> arrow key: process as follows:
430     // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
431     // - with <Ctrl> modifier key pressed:  move one word right (taking into account prompt)
432     // - with <Shift> modifier key pressed: move one symbol right with selection
433     // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
434     {
435       if ( !shftPressed ) {
436         if ( curCol < paragraphLength( curLine ) ) {
437           if ( isCommand( text( curLine ) ) && curCol < PROMPT_SIZE ) {
438             setCursorPosition( curLine, PROMPT_SIZE );
439             break;
440           }
441         }
442         else {
443           if ( curLine < endLine && isCommand( text( curLine+1 ) ) ) {
444             setCursorPosition( curLine+1, PROMPT_SIZE );
445             break;
446           }
447         }
448       }
449       QTextEdit::keyPressEvent( e );
450       break;
451     }
452   case Key_PageUp:
453     // <PageUp> key: process as follows:
454     // - without <Ctrl>, <Shift> modifiers: first command in history
455     // - with <Ctrl> modifier key pressed:  move cursor one page up without selection
456     // - with <Shift> modifier key pressed: move cursor one page up with selection
457     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
458     {
459       if ( ctrlPressed && shftPressed ) {
460         scrollBy( 0, -visibleHeight() );
461       }
462       else if ( shftPressed ) {
463         if ( curLine > 0 )
464           moveCursor( QTextEdit::MovePgUp, true );
465       }
466       else if ( ctrlPressed ) {
467         moveCursor( QTextEdit::MovePgUp, false );
468       }
469       else { 
470         QString histLine = _currentPrompt;
471         if ( ! _isInHistory ) {
472           _isInHistory = true;
473           _currentCommand = text( endLine ).remove( 0, PROMPT_SIZE );
474           _currentCommand.truncate( _currentCommand.length() - 1 );
475         }
476         QString firstCommand = myInterp->getPrevious();
477         QString pcmd;
478         while ( ( pcmd = QString( myInterp->getPrevious() ) ).compare( BEGIN_HISTORY_PY ) != 0 )
479           firstCommand = pcmd;
480         if ( firstCommand.compare( BEGIN_HISTORY_PY ) != 0 ) {
481           removeParagraph( endLine );
482           histLine.append( firstCommand );
483           insertParagraph( histLine, -1 );
484         }
485         moveCursor( QTextEdit::MoveEnd, false );
486       }
487       break;
488     }
489   case Key_PageDown:
490     // <PageDown> key: process as follows:
491     // - without <Ctrl>, <Shift> modifiers: last command in history
492     // - with <Ctrl> modifier key pressed:  move cursor one page down without selection
493     // - with <Shift> modifier key pressed: move cursor one page down with selection
494     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
495     {
496       if ( ctrlPressed && shftPressed ) {
497         scrollBy( 0, visibleHeight() );
498       }
499       else if ( shftPressed ) {
500         if ( curLine < endLine )
501           moveCursor( QTextEdit::MovePgDown, true );
502       }
503       else if ( ctrlPressed ) {
504         moveCursor( QTextEdit::MovePgDown, false );
505       }
506       else { 
507         if ( _isInHistory ) {
508           QString histLine = _currentPrompt;
509           while ( QString( myInterp->getNext() ).compare( TOP_HISTORY_PY ) != 0 );
510           _isInHistory = false;
511           removeParagraph( endLine );
512           histLine.append( _currentCommand );
513           insertParagraph( histLine, -1 );
514         }
515         moveCursor( QTextEdit::MoveEnd, false );
516       }
517       break;
518     }
519   case Key_Home: 
520     // <Home> key: process as follows:
521     // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
522     // - with <Ctrl> modifier key pressed:  move cursor to the very first symbol without selection
523     // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
524     // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
525     {
526       if ( ctrlPressed ) { 
527         moveCursor( QTextEdit::MoveHome, shftPressed );
528       }
529       else {
530         if ( isCommand( text( curLine ) ) ) {
531           int ps1, ps2, cs1, cs2;
532           bool hasSelection = hasSelectedText();
533           if ( hasSelection )
534             getSelection( &ps1, &cs1, &ps2, &cs2 );
535           removeSelection();
536           horizontalScrollBar()->setValue( horizontalScrollBar()->minValue() );
537           if ( curCol > PROMPT_SIZE && shftPressed ) 
538             setSelection( curLine, PROMPT_SIZE, curLine, ( hasSelection && ps1 == ps2 && ps1 == curLine && cs2 > PROMPT_SIZE ) ? cs2 : curCol );
539           setCursorPosition( curLine, PROMPT_SIZE );
540         }
541         else {
542           moveCursor( QTextEdit::MoveLineStart, shftPressed );
543         }
544       }
545       break;
546     }
547   case Key_End:
548     // <End> key: process as follows:
549     // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
550     // - with <Ctrl> modifier key pressed:  move cursor to the very last symbol without selection
551     // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
552     // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
553     {
554       if ( ctrlPressed ) { 
555         moveCursor( QTextEdit::MoveEnd, shftPressed );
556       }
557       else {
558         moveCursor( QTextEdit::MoveLineEnd, shftPressed );
559       }
560       break;
561     }  
562   case Key_Backspace :
563     // <Backspace> key: process as follows
564     // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
565     // - with <Ctrl> modifier key pressed: delete previous word
566     // works only for last (command) line
567     {
568       if ( curLine == endLine && ( curCol > PROMPT_SIZE || curCol >= PROMPT_SIZE && hasSelectedText() ) ) {
569         if ( ctrlPressed && !hasSelectedText() ) {
570           QString txt = text( curLine );
571           int ind = curCol-1;
572           while ( ind > 0 && txt[ ind ] == ' ' ) ind--;
573           ind = txt.findRev( ' ', ind ) + 1;
574           if ( ind > PROMPT_SIZE-1 ) {
575             setSelection( curLine, ind, curLine, curCol );
576             removeSelectedText();
577           }
578           else {
579             QTextEdit::keyPressEvent( e );
580           }
581         }
582         else {
583           QTextEdit::keyPressEvent( e );
584         }
585       }
586       break;
587     }
588   case Key_Delete :
589     // <Delete> key: process as follows
590     // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
591     // - with <Ctrl> modifier key pressed: delete next word
592     // works only for last (command) line
593     {
594       if ( curLine == endLine && curCol > PROMPT_SIZE-1 ) {
595         if ( ctrlPressed && !hasSelectedText() ) {
596           QString txt = text( curLine );
597           int ind = curCol;
598           while ( ind < txt.length()-1 && txt[ ind ] == ' ' ) ind++;
599           ind = txt.find( ' ', ind );
600           while ( ind < txt.length()-1 && txt[ ind ] == ' ' ) ind++;
601           if ( ind > PROMPT_SIZE-1 ) {
602             setSelection( curLine, curCol, curLine, ind );
603             removeSelectedText();
604           }
605           else {
606             QTextEdit::keyPressEvent( e );
607           }
608         }
609         else {
610           QTextEdit::keyPressEvent( e );
611         }
612       }
613       break;
614     }
615   case Key_Insert :
616     // <Insert> key: process as follows
617     // - with <Ctrl> modifier key pressed:  copy()
618     // - with <Shift> modifier key pressed: paste() to the command line
619     {
620       if ( ctrlPressed ) {
621         copy();
622       }
623       else if ( shftPressed ) {
624         if ( curLine != endLine || curCol < PROMPT_SIZE )
625           moveCursor( QTextEdit::MoveEnd, false );
626         paste();
627       }
628       else
629         QTextEdit::keyPressEvent( e );
630       break;
631     }
632   }
633 }
634
635 /*!
636     Handles notifications coming from Python dispatcher
637 */
638 void PythonConsole_PyEditor::customEvent(QCustomEvent* e)
639 {
640   switch( e->type() ) {
641   case PyInterp_Event::OK:
642   case PyInterp_Event::ERROR:
643     {
644       PyInterp_Event* pe = dynamic_cast<PyInterp_Event*>( e );
645       if ( pe ){
646         ExecCommand* ec = dynamic_cast<ExecCommand*>( pe->GetRequest() );
647         if ( ec ){
648           // The next line has appeared dangerous in case if
649           // Python command execution has produced very large output.
650           // A more clever approach is needed...
651           setText(ec->myOutput);
652           setText(ec->myError);
653         }
654       }
655       _buf.truncate(0);
656       _currentPrompt = READY_PROMPT;
657       setText(_currentPrompt);
658       viewport()->unsetCursor();
659       break;
660     }
661   case PyInterp_Event::INCOMPLETE:
662     {
663       _buf.append("\n");
664       _currentPrompt = DOTS_PROMPT;
665       setText(_currentPrompt);
666       viewport()->unsetCursor();
667       break;
668     }
669   default:
670     QTextEdit::customEvent( e );
671   }
672
673   setReadOnly( false );
674   _isInHistory = false;
675 }
676
677 /*!
678    Handles Python interpreter change
679 */
680 void PythonConsole_PyEditor::onPyInterpChanged( PyInterp_base* interp )
681 {
682   if ( myInterp != interp 
683        // Force read-only state and wait cursor when myInterp is NULL
684       || !myInterp ){
685     myInterp = interp;
686     if ( myInterp ){
687       myBanner = myInterp->getbanner().c_str();
688       setText(myBanner);
689       _buf.truncate(0);
690       setReadOnly( false );
691       _isInHistory = false;
692       setText(_currentPrompt);
693       viewport()->unsetCursor();
694     }
695     else {
696       clear();
697       setReadOnly( true );
698       viewport()->setCursor( waitCursor );
699     }
700   }
701 }
702
703 QPopupMenu* PythonConsole_PyEditor::createPopupMenu( const QPoint& pos )
704 {
705   QPopupMenu* popup = QTextEdit::createPopupMenu( pos );
706
707   QValueList<int> ids;
708   for ( int i = 0; popup && i < popup->count(); i++ )
709   {
710     if ( !popup->isItemEnabled( popup->idAt( i ) ) )
711       ids.append( popup->idAt( i ) );
712   }
713
714   for ( QValueList<int>::const_iterator it = ids.begin(); it != ids.end(); ++it )
715     popup->removeItem( *it );
716
717   SUIT_Tools::simplifySeparators( popup );
718
719   if ( !popup->count() )
720   {
721     delete popup;
722     popup = 0;
723   }
724
725   return popup;
726 }