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