Salome HOME
PR: merge from tag mergeto_trunk_17Jan05
[modules/kernel.git] / src / SALOMEGUI / QAD_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   : QAD_PyEditor.cxx
25 //  Author : Nicolas REJNERI
26 //  Module : SALOME
27 //  $Header$
28
29 #include "QAD_PyEditor.h"
30 #include "QAD_PyInterp.h"
31 #include "QAD_Application.h"
32 #include "QAD_Desktop.h"
33 #include "QAD_Config.h"
34 #include "QAD_Tools.h"
35 #include "QAD_MessageBox.h"
36
37 #include <qapplication.h>
38 #include <qmap.h>
39 #include <qclipboard.h>
40 #include <qthread.h>
41 #include <qdragobject.h>
42
43 // NRI : Temporary added
44 // IDL Headers
45 #include <SALOMEconfig.h>
46 #include CORBA_SERVER_HEADER(SALOMEDS)
47 #include CORBA_SERVER_HEADER(SALOMEDS_Attributes)
48 //NRI
49
50 #include "utilities.h"
51 using namespace std;
52
53
54 #ifdef _DEBUG_
55 static int MYDEBUG = 0;
56 #else
57 static int MYDEBUG = 0;
58 #endif
59
60
61 enum { IdCopy, IdPaste, IdClear, IdSelectAll };
62
63
64 static QString READY_PROMPT = ">>> ";
65 static QString DOTS_PROMPT  = "... ";
66 #define PROMPT_SIZE _currentPrompt.length()
67
68 class TInitEditorThread : public QThread
69 {
70 public:
71   TInitEditorThread(QAD_PyInterp*& theInterp, 
72                     QMutex* theStudyMutex, QMutex* theMutex,
73                     QAD_PyEditor* theListener):
74     myInterp(theInterp), 
75     myMutex(theMutex),
76     myStudyMutex(theStudyMutex),
77     myListener(theListener)
78   {
79     // san - commented as inefficient: sometimes event is processed significant period of time after this moment
80     //QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::SET_WAIT_CURSOR));
81   }
82
83   virtual ~TInitEditorThread(){}
84
85 protected:
86   virtual void run(){
87     ThreadLock anEditorLock(myMutex,"TInitEditorThread::anEditorLock");
88     ThreadLock aStudyLock(myStudyMutex,"TInitEditorThread::aStudyLock");
89     ThreadLock aPyLock = GetPyThreadLock("TInitEditorThread::aPyLock");
90     if(MYDEBUG) MESSAGE("TInitEditorThread::run() - myInterp = "<<myInterp<<"; myMutex = "<<myMutex);
91     myListener->myBanner = myInterp->getbanner().c_str();
92     QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::INITIALIZE));
93     QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::PYTHON_OK));
94     QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::UNSET_CURSOR));
95   }
96
97 private:
98   QMutex* myMutex;
99   QMutex* myStudyMutex;
100   QAD_PyInterp*& myInterp;
101   QAD_PyEditor* myListener;
102 };
103
104
105 class TExecCommandThread : public QThread
106 {
107 public:
108   TExecCommandThread(QAD_PyInterp*& theInterp, 
109                      QMutex* theStudyMutex, QMutex* theMutex,
110                      QAD_PyEditor* theListener): 
111     myInterp(theInterp), 
112     myMutex(theMutex),
113     myStudyMutex(theStudyMutex),
114     myListener(theListener), 
115     myCommand("")
116   {
117     //QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::SET_WAIT_CURSOR));
118   }
119
120   virtual ~TExecCommandThread() {}
121
122   void exec(const char* theCommand){
123     myCommand = theCommand;
124     start();
125   }
126
127 protected:
128   virtual void run(){
129     //QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::SET_WAIT_CURSOR));
130     int anId = QAD_PyEditor::PYTHON_OK;
131     if(myCommand != ""){
132       ThreadLock anEditorLock(myMutex,"TExecCommandThread::anEditorLock");
133       //ThreadLock aStudyLock(myStudyMutex,"TExecCommandThread::aStudyLock");
134       ThreadLock aPyLock = GetPyThreadLock("TExecCommandThread::aPyLock");
135       int ret = myInterp->run( myCommand.latin1() );
136       if(MYDEBUG) MESSAGE("TExecCommand::run() - myInterp = "<<myInterp<<"; myCommand = '"<<myCommand.latin1()<<"' - "<<ret);
137       if(ret < 0)
138         anId = QAD_PyEditor::PYTHON_ERROR;
139       else if(ret > 0)
140         anId = QAD_PyEditor::PYTHON_INCOMPLETE;
141       myListener->myError = myInterp->getverr().c_str();
142       myListener->myOutput = myInterp->getvout().c_str();
143     }else{
144       myListener->myError = "";
145       myListener->myOutput = "";
146     }
147     QThread::postEvent(myListener, new QCustomEvent(anId));
148     QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::UNSET_CURSOR));
149   }
150
151 private:
152   QMutex* myMutex;
153   QMutex* myStudyMutex;
154   QAD_PyInterp*& myInterp;
155   QAD_PyEditor* myListener;
156   QString myCommand;
157 };
158
159
160 /*!
161     Constructor
162 */
163 QAD_PyEditor::QAD_PyEditor(QAD_PyInterp*& theInterp, QMutex* theMutex,
164                            QWidget *theParent, const char* theName): 
165   QTextEdit(theParent,theName),
166   myStudyMutex(theMutex),
167   myInitEditorMutex(new QMutex),
168   myExecCommandMutex(new QMutex),
169   myInterp(theInterp),
170   myInitEditorThread(0),
171   myExecCommandThread(0)
172 {
173   QString fntSet = QAD_CONFIG->getSetting("Viewer:ConsoleFont");
174   QFont myFont = QAD_Tools::stringToFont( fntSet );
175 //  QFont myFont("Courier",11);
176   setFont(myFont);
177   setTextFormat(QTextEdit::PlainText);
178
179   // san - This is necessary for troubleless initialization
180   setReadOnly( true );
181   viewport()->setCursor( waitCursor );
182
183   myInitEditorThread = new TInitEditorThread(myInterp,myStudyMutex,myInitEditorMutex,this);
184   myExecCommandThread = new TExecCommandThread(myInterp,myStudyMutex,myExecCommandMutex,this);
185
186   _currentPrompt = READY_PROMPT;
187   setPalette( QAD_Application::getPalette(true) );
188   setWordWrap(NoWrap);
189
190   connect(this,SIGNAL(returnPressed()),this,SLOT(handleReturn()) );
191 }
192
193
194 void QAD_PyEditor::Init()
195 {
196   myInitEditorThread->start();
197 }
198
199
200 /*!
201     Destructor
202 */
203 QAD_PyEditor::~QAD_PyEditor()
204 {
205   if(MYDEBUG) MESSAGE("QAD_PyEditor::~QAD_PyEditor()");
206   {
207     {
208       ThreadLock aLock(myInitEditorMutex,"myInitEditorMutex");
209       delete myInitEditorThread;
210     }
211     delete myInitEditorMutex;
212   }
213   {
214     {
215       ThreadLock aLock(myExecCommandMutex,"myExecCommandMutex");
216       delete myExecCommandThread;
217     }
218     delete myExecCommandMutex;
219   }
220 }
221
222 /*!
223     Called to insert a string s 
224 */
225 void QAD_PyEditor::setText(QString s)
226 {
227   int para=paragraphs()-1;
228   int col=paragraphLength(para);
229   insertAt(s,para,col);
230   int n = paragraphs()-1;  
231   setCursorPosition( n, paragraphLength(n)); 
232 }
233
234 /*!
235     Called when an handleReturn
236 */
237 void QAD_PyEditor::handleReturn()
238 {
239   int para=paragraphs()-2;
240
241   // NRI : Temporary added
242   SALOMEDS::Study_var aStudy = QAD_Application::getDesktop()->getActiveStudy()->getStudyDocument();
243   
244   if ( aStudy->GetProperties()->IsLocked() ) {
245     QApplication::restoreOverrideCursor();
246     QAD_MessageBox::warn1 ( (QWidget*)QAD_Application::getDesktop(),
247                             QObject::tr("WARNING"), 
248                             QObject::tr("WRN_STUDY_LOCKED"),
249                             QObject::tr("BUT_OK") );
250
251     _currentPrompt = READY_PROMPT;
252     setText(_currentPrompt);
253     
254     return;
255   }  
256   // NRI
257
258   _buf.append(text(para).remove(0,PROMPT_SIZE));
259   _buf.truncate( _buf.length() - 1 );
260   setReadOnly( true );
261   viewport()->setCursor( waitCursor );
262   myExecCommandThread->exec(_buf.latin1());
263 }
264
265 /*
266    Processes drop event: paste dragged text
267 */
268 void QAD_PyEditor::contentsDropEvent( QDropEvent* event )
269 {
270   event->acceptAction();
271   QString text;
272   if ( QTextDrag::decode( event, text ) ) {
273     int par, col;
274     int endLine = paragraphs() -1;
275     col = charAt( event->pos(), &par );
276     
277     if ( col >= 0 && par >= 0 ) {
278       if ( par != endLine || col < PROMPT_SIZE ) {
279         par = endLine;
280         col = paragraphLength( endLine );
281       }
282       setCursorPosition( par, col );
283       insertAt( text, par, col );
284       removeSelection();
285     }
286   }
287 }
288
289 /*
290    Processes middle button release event - paste clipboard's contents
291 */
292 void QAD_PyEditor::contentsMouseReleaseEvent( QMouseEvent* event )
293 {
294   if ( event->button() == LeftButton ) {
295     QTextEdit::contentsMouseReleaseEvent(event);
296     copy();
297   }
298   if ( event->button() == MidButton ) {
299     if (QApplication::clipboard()->supportsSelection()) {
300       int par, col;
301       int endLine = paragraphs() -1;
302       col = charAt( event->pos(), &par );
303       if ( col >= 0 && par >= 0 ) {
304         if ( par != endLine || col < PROMPT_SIZE )
305           setCursorPosition( endLine, paragraphLength( endLine ) );
306         else
307           setCursorPosition( par, col );
308         QApplication::clipboard()->setSelectionMode(TRUE);
309         paste();
310         QApplication::clipboard()->setSelectionMode(FALSE);
311       }
312     }
313   }
314   else {
315     QTextEdit::contentsMouseReleaseEvent(event);
316   }
317 }
318
319 /*
320    Processes own popup menu
321 */
322 void QAD_PyEditor::mousePressEvent (QMouseEvent* event)
323 {
324   if ( event->button() == RightButton ) {
325     QPopupMenu *popup = new QPopupMenu( this );
326     QMap<int, int> idMap;
327
328     int para1, col1, para2, col2;
329     getSelection(&para1, &col1, &para2, &col2);
330     bool allSelected = hasSelectedText() &&
331       para1 == 0 && para2 == paragraphs()-1 && col1 == 0 && para2 == paragraphLength(para2);
332     int id;
333     id = popup->insertItem( tr( "EDIT_COPY_CMD" ) );
334     idMap.insert(IdCopy, id);
335     id = popup->insertItem( tr( "EDIT_PASTE_CMD" ) );
336     idMap.insert(IdPaste, id);
337     id = popup->insertItem( tr( "EDIT_CLEAR_CMD" ) );
338     idMap.insert(IdClear, id);
339     popup->insertSeparator();
340     id = popup->insertItem( tr( "EDIT_SELECTALL_CMD" ) );
341     idMap.insert(IdSelectAll, id);
342     popup->setItemEnabled( idMap[ IdCopy ],  hasSelectedText() );
343     popup->setItemEnabled( idMap[ IdPaste ],
344                           !isReadOnly() && (bool)QApplication::clipboard()->text().length() );
345     popup->setItemEnabled( idMap[ IdSelectAll ],
346                           (bool)text().length() && !allSelected );
347     
348     int r = popup->exec( event->globalPos() );
349     delete popup;
350     
351     if ( r == idMap[ IdCopy ] ) {
352       copy();
353     }
354     else if ( r == idMap[ IdPaste ] ) {
355       paste();
356     }
357     else if ( r == idMap[ IdClear ] ) {
358       clear();
359       setText(myBanner);
360       _currentPrompt = READY_PROMPT;
361       setText(_currentPrompt);
362     }
363     else if ( r == idMap[ IdSelectAll ] ) {
364       selectAll();
365     }
366   }
367   else {
368     QTextEdit::mousePressEvent(event);
369   }
370 }
371
372 /*!
373    Checks, is the string a command line or not.
374 */
375
376 bool QAD_PyEditor::isCommand( const QString& str) const
377 {
378   // prompt may be '>>> ' or for '... '
379   return ( str.find( READY_PROMPT ) == 0 || str.find( DOTS_PROMPT ) == 0 );
380 }
381
382
383 /*!
384     Called when a keyPress event
385 */
386 void QAD_PyEditor::keyPressEvent( QKeyEvent* e )
387 {
388   // get cursor position
389   int curLine, curCol;
390   getCursorPosition(&curLine, &curCol);
391
392   // get last edited line
393   int endLine = paragraphs() -1;
394
395   // get pressed key code
396   int aKey = e->key();
397
398   // check if <Ctrl> is pressed
399   bool ctrlPressed = e->state() & ControlButton;
400   // check if <Shift> is pressed
401   bool shftPressed = e->state() & ShiftButton;
402   // check if <Alt> is pressed
403   bool altPressed = e->state() & AltButton;
404
405   // process <Ctrl>+<C> key-bindings
406   if ( aKey == Key_C && ctrlPressed ) {
407     _buf.truncate(0);
408     setText("\n");
409     _currentPrompt = READY_PROMPT;
410     setText(_currentPrompt);
411     return;
412   }
413
414   // check for printed key
415   aKey = ( aKey < Key_Space || aKey > Key_ydiaeresis ) ? aKey : 0;
416
417   switch ( aKey ) {
418   case 0 :
419     // any printed key
420     {
421       if ( curLine < endLine || curCol < PROMPT_SIZE )
422         moveCursor( QTextEdit::MoveEnd, false );
423       QTextEdit::keyPressEvent( e );
424       break;
425     }
426   case Key_Return:
427   case Key_Enter:
428     // <Enter> key
429     {
430       moveCursor( QTextEdit::MoveEnd, false );
431       QTextEdit::keyPressEvent( e );
432       break;
433     }
434   case Key_Up:
435     // <Up> arrow key: process as follows:
436     // - without <Ctrl>, <Shift> modifiers: previous command in history
437     // - with <Ctrl> modifier key pressed:  move cursor one row up without selection
438     // - with <Shift> modifier key pressed: move cursor one row up with selection
439     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row up
440     {
441       if ( ctrlPressed && shftPressed ) {
442         scrollBy( 0, -QFontMetrics( font() ).lineSpacing() );
443       }
444       else if ( shftPressed ) {
445         if ( curLine > 0 )
446           moveCursor( QTextEdit::MoveUp, true );
447       }
448       else if ( ctrlPressed ) {
449         moveCursor( QTextEdit::MoveUp, false );
450       }
451       else { 
452         QString histLine = _currentPrompt;
453         if ( ! _isInHistory ) {
454           _isInHistory = true;
455           _currentCommand = text( endLine ).remove( 0, PROMPT_SIZE );
456           _currentCommand.truncate( _currentCommand.length() - 1 );
457         }
458         QString previousCommand = myInterp->getPrevious();
459         if ( previousCommand.compare( BEGIN_HISTORY_PY ) != 0 ) {
460           removeParagraph( endLine );
461           histLine.append( previousCommand );
462           insertParagraph( histLine, -1 );
463         }
464         moveCursor( QTextEdit::MoveEnd, false );
465       }
466       break;
467     }
468   case Key_Down:
469     // <Down> arrow key: process as follows:
470     // - without <Ctrl>, <Shift> modifiers: next command in history
471     // - with <Ctrl> modifier key pressed:  move cursor one row down without selection
472     // - with <Shift> modifier key pressed: move cursor one row down with selection
473     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one row down
474     {
475       if ( ctrlPressed && shftPressed ) {
476         scrollBy( 0, QFontMetrics( font() ).lineSpacing() );
477       }
478       else if ( shftPressed ) {
479         if ( curLine < endLine )
480           moveCursor( QTextEdit::MoveDown, true );
481       }
482       else if ( ctrlPressed ) {
483         moveCursor( QTextEdit::MoveDown, false );
484       }
485       else { 
486         QString histLine = _currentPrompt;
487         QString nextCommand = myInterp->getNext();
488         if ( nextCommand.compare( TOP_HISTORY_PY ) != 0 ) {
489           removeParagraph( endLine );
490           histLine.append( nextCommand );
491           insertParagraph( histLine, -1 );
492         }
493         else {
494           if (_isInHistory) {
495             _isInHistory = false;
496             removeParagraph( endLine );
497             histLine.append( _currentCommand );
498             insertParagraph( histLine, -1 );
499           }
500         }
501         moveCursor( QTextEdit::MoveEnd, false );
502       }
503       break;
504     }
505   case Key_Left:
506     // <Left> arrow key: process as follows:
507     // - without <Ctrl>, <Shift> modifiers: move one symbol left (taking into account prompt)
508     // - with <Ctrl> modifier key pressed:  move one word left (taking into account prompt)
509     // - with <Shift> modifier key pressed: move one symbol left with selection
510     // - with <Ctrl>+<Shift> modifier keys pressed: move one word left with selection
511     {
512       if ( !shftPressed && isCommand( text( curLine ) ) && curCol <= PROMPT_SIZE ) {
513         setCursorPosition( curLine-1, 0 );
514         moveCursor( QTextEdit::MoveLineEnd, false );
515       }
516       else {
517         QTextEdit::keyPressEvent( e );
518       }
519       break;
520     }
521   case Key_Right:
522     // <Right> arrow key: process as follows:
523     // - without <Ctrl>, <Shift> modifiers: move one symbol right (taking into account prompt)
524     // - with <Ctrl> modifier key pressed:  move one word right (taking into account prompt)
525     // - with <Shift> modifier key pressed: move one symbol right with selection
526     // - with <Ctrl>+<Shift> modifier keys pressed: move one word right with selection
527     {
528       if ( !shftPressed ) {
529         if ( curCol < paragraphLength( curLine ) ) {
530           if ( isCommand( text( curLine ) ) && curCol < PROMPT_SIZE ) {
531             setCursorPosition( curLine, PROMPT_SIZE );
532             break;
533           }
534         }
535         else {
536           if ( curLine < endLine && isCommand( text( curLine+1 ) ) ) {
537             setCursorPosition( curLine+1, PROMPT_SIZE );
538             break;
539           }
540         }
541       }
542       QTextEdit::keyPressEvent( e );
543       break;
544     }
545   case Key_PageUp:
546     // <PageUp> key: process as follows:
547     // - without <Ctrl>, <Shift> modifiers: first command in history
548     // - with <Ctrl> modifier key pressed:  move cursor one page up without selection
549     // - with <Shift> modifier key pressed: move cursor one page up with selection
550     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page up
551     {
552       if ( ctrlPressed && shftPressed ) {
553         scrollBy( 0, -visibleHeight() );
554       }
555       else if ( shftPressed ) {
556         if ( curLine > 0 )
557           moveCursor( QTextEdit::MovePgUp, true );
558       }
559       else if ( ctrlPressed ) {
560         moveCursor( QTextEdit::MovePgUp, false );
561       }
562       else { 
563         QString histLine = _currentPrompt;
564         if ( ! _isInHistory ) {
565           _isInHistory = true;
566           _currentCommand = text( endLine ).remove( 0, PROMPT_SIZE );
567           _currentCommand.truncate( _currentCommand.length() - 1 );
568         }
569         QString firstCommand = myInterp->getPrevious();
570         QString pcmd;
571         while ( ( pcmd = QString( myInterp->getPrevious() ) ).compare( BEGIN_HISTORY_PY ) != 0 )
572           firstCommand = pcmd;
573         if ( firstCommand.compare( BEGIN_HISTORY_PY ) != 0 ) {
574           removeParagraph( endLine );
575           histLine.append( firstCommand );
576           insertParagraph( histLine, -1 );
577         }
578         moveCursor( QTextEdit::MoveEnd, false );
579       }
580       break;
581     }
582   case Key_PageDown:
583     // <PageDown> key: process as follows:
584     // - without <Ctrl>, <Shift> modifiers: last command in history
585     // - with <Ctrl> modifier key pressed:  move cursor one page down without selection
586     // - with <Shift> modifier key pressed: move cursor one page down with selection
587     // - with <Ctrl>+<Shift> modifier keys pressed: scroll one page down
588     {
589       if ( ctrlPressed && shftPressed ) {
590         scrollBy( 0, visibleHeight() );
591       }
592       else if ( shftPressed ) {
593         if ( curLine < endLine )
594           moveCursor( QTextEdit::MovePgDown, true );
595       }
596       else if ( ctrlPressed ) {
597         moveCursor( QTextEdit::MovePgDown, false );
598       }
599       else { 
600         if ( _isInHistory ) {
601           QString histLine = _currentPrompt;
602           while ( QString( myInterp->getNext() ).compare( TOP_HISTORY_PY ) != 0 );
603           _isInHistory = false;
604           removeParagraph( endLine );
605           histLine.append( _currentCommand );
606           insertParagraph( histLine, -1 );
607         }
608         moveCursor( QTextEdit::MoveEnd, false );
609       }
610       break;
611     }
612   case Key_Home: 
613     // <Home> key: process as follows:
614     // - without <Ctrl>, <Shift> modifiers: move cursor to the beginning of the current line without selection
615     // - with <Ctrl> modifier key pressed:  move cursor to the very first symbol without selection
616     // - with <Shift> modifier key pressed: move cursor to the beginning of the current line with selection
617     // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very first symbol with selection
618     {
619       if ( ctrlPressed ) { 
620         moveCursor( QTextEdit::MoveHome, shftPressed );
621       }
622       else {
623         if ( isCommand( text( curLine ) ) ) {
624           int ps1, ps2, cs1, cs2;
625           bool hasSelection = hasSelectedText();
626           if ( hasSelection )
627             getSelection( &ps1, &cs1, &ps2, &cs2 );
628           removeSelection();
629           horizontalScrollBar()->setValue( horizontalScrollBar()->minValue() );
630           if ( curCol > PROMPT_SIZE && shftPressed ) 
631             setSelection( curLine, PROMPT_SIZE, curLine, ( hasSelection && ps1 == ps2 && ps1 == curLine && cs2 > PROMPT_SIZE ) ? cs2 : curCol );
632           setCursorPosition( curLine, PROMPT_SIZE );
633         }
634         else {
635           moveCursor( QTextEdit::MoveLineStart, shftPressed );
636         }
637       }
638       break;
639     }
640   case Key_End:
641     // <End> key: process as follows:
642     // - without <Ctrl>, <Shift> modifiers: move cursor to the end of the current line without selection
643     // - with <Ctrl> modifier key pressed:  move cursor to the very last symbol without selection
644     // - with <Shift> modifier key pressed: move cursor to the end of the current line with selection
645     // - with <Ctrl>+<Shift> modifier keys pressed: move cursor to the very last symbol with selection
646     {
647       if ( ctrlPressed ) { 
648         moveCursor( QTextEdit::MoveEnd, shftPressed );
649       }
650       else {
651         moveCursor( QTextEdit::MoveLineEnd, shftPressed );
652       }
653       break;
654     }  
655   case Key_Backspace :
656     // <Backspace> key: process as follows
657     // - without any modifiers : delete symbol before the cursor / selection (taking into account prompt)
658     // - with <Ctrl> modifier key pressed: delete previous word
659     // works only for last (command) line
660     {
661       if ( curLine == endLine && ( curCol > PROMPT_SIZE || curCol >= PROMPT_SIZE && hasSelectedText() ) ) {
662         if ( ctrlPressed && !hasSelectedText() ) {
663           QString txt = text( curLine );
664           int ind = curCol-1;
665           while ( ind > 0 && txt[ ind ] == ' ' ) ind--;
666           ind = txt.findRev( ' ', ind ) + 1;
667           if ( ind > PROMPT_SIZE-1 ) {
668             setSelection( curLine, ind, curLine, curCol );
669             removeSelectedText();
670           }
671           else {
672             QTextEdit::keyPressEvent( e );
673           }
674         }
675         else {
676           QTextEdit::keyPressEvent( e );
677         }
678       }
679       break;
680     }
681   case Key_Delete :
682     // <Delete> key: process as follows
683     // - without any modifiers : delete symbol after the cursor / selection (taking into account prompt)
684     // - with <Ctrl> modifier key pressed: delete next word
685     // works only for last (command) line
686     {
687       if ( curLine == endLine && curCol > PROMPT_SIZE-1 ) {
688         if ( ctrlPressed && !hasSelectedText() ) {
689           QString txt = text( curLine );
690           int ind = curCol;
691           while ( ind < txt.length()-1 && txt[ ind ] == ' ' ) ind++;
692           ind = txt.find( ' ', ind );
693           while ( ind < txt.length()-1 && txt[ ind ] == ' ' ) ind++;
694           if ( ind > PROMPT_SIZE-1 ) {
695             setSelection( curLine, curCol, curLine, ind );
696             removeSelectedText();
697           }
698           else {
699             QTextEdit::keyPressEvent( e );
700           }
701         }
702         else {
703           QTextEdit::keyPressEvent( e );
704         }
705       }
706       break;
707     }
708   case Key_Insert :
709     // <Insert> key: process as follows
710     // - with <Ctrl> modifier key pressed:  copy()
711     // - with <Shift> modifier key pressed: paste() to the command line
712     {
713       if ( ctrlPressed ) {
714         copy();
715       }
716       else if ( shftPressed ) {
717         if ( curLine != endLine || curCol < PROMPT_SIZE )
718           moveCursor( QTextEdit::MoveEnd, false );
719         paste();
720       }
721       else
722         QTextEdit::keyPressEvent( e );
723       break;
724     }
725   }
726   // NRI : DEBUG PAS TERRIBLE //
727   if (( e->key() == Key_F3) || 
728       ( e->key() == Key_F4) ||
729       ( e->key() == Key_Return) ||
730       ( e->key() == Key_Escape))
731     QAD_Application::getDesktop()->onKeyPress( e );
732   // NRI //
733 }
734
735 void QAD_PyEditor::customEvent(QCustomEvent* e)
736 {
737   switch( e->type() ) {
738   case PYTHON_OK:
739   case PYTHON_ERROR:
740     {
741       _buf.truncate(0);
742       setText(myOutput);
743       setText(myError);
744       _currentPrompt = READY_PROMPT;
745       setText(_currentPrompt);
746       break;
747     }
748   case PYTHON_INCOMPLETE:
749     {
750       _buf.append("\n");
751       _currentPrompt = DOTS_PROMPT;
752       setText(_currentPrompt);
753       break;
754     }
755   case INITIALIZE:
756     {
757       setText(myInterp->getbanner().c_str());
758       _buf.truncate(0);
759       QApplication::restoreOverrideCursor();
760       break;
761     }  
762   case SET_WAIT_CURSOR:
763     {
764       viewport()->setCursor( waitCursor );
765       break;
766     }  
767   case UNSET_CURSOR:
768     {
769       viewport()->unsetCursor();
770       break;
771     }  
772   default:
773     QTextEdit::customEvent( e );
774   }
775
776   setReadOnly( false );
777   _isInHistory = false;
778 }