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