1 // SALOME SALOMEGUI : implementation of desktop and GUI kernel
3 // Copyright (C) 2003 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
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.
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.
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
20 // See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org
24 // File : QAD_PyEditor.cxx
25 // Author : Nicolas REJNERI
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"
37 #include <qapplication.h>
39 #include <qclipboard.h>
41 #include <qdragobject.h>
43 // NRI : Temporary added
45 #include <SALOMEconfig.h>
46 #include CORBA_SERVER_HEADER(SALOMEDS)
47 #include CORBA_SERVER_HEADER(SALOMEDS_Attributes)
50 #include "utilities.h"
55 static int MYDEBUG = 0;
57 static int MYDEBUG = 0;
61 enum { IdCopy, IdPaste, IdClear, IdSelectAll };
64 static QString READY_PROMPT = ">>> ";
65 static QString DOTS_PROMPT = "... ";
66 #define PROMPT_SIZE _currentPrompt.length()
68 class TInitEditorThread : public QThread
71 TInitEditorThread(QAD_PyInterp*& theInterp,
72 QMutex* theStudyMutex, QMutex* theMutex,
73 QAD_PyEditor* theListener):
76 myStudyMutex(theStudyMutex),
77 myListener(theListener)
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));
83 virtual ~TInitEditorThread(){}
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));
100 QAD_PyInterp*& myInterp;
101 QAD_PyEditor* myListener;
105 class TExecCommandThread : public QThread
108 TExecCommandThread(QAD_PyInterp*& theInterp,
109 QMutex* theStudyMutex, QMutex* theMutex,
110 QAD_PyEditor* theListener):
113 myStudyMutex(theStudyMutex),
114 myListener(theListener),
117 //QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::SET_WAIT_CURSOR));
120 virtual ~TExecCommandThread() {}
122 void exec(const char* theCommand){
123 myCommand = theCommand;
129 //QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::SET_WAIT_CURSOR));
130 int anId = QAD_PyEditor::PYTHON_OK;
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);
138 anId = QAD_PyEditor::PYTHON_ERROR;
140 anId = QAD_PyEditor::PYTHON_INCOMPLETE;
141 myListener->myError = myInterp->getverr().c_str();
142 myListener->myOutput = myInterp->getvout().c_str();
144 myListener->myError = "";
145 myListener->myOutput = "";
147 QThread::postEvent(myListener, new QCustomEvent(anId));
148 QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::UNSET_CURSOR));
153 QMutex* myStudyMutex;
154 QAD_PyInterp*& myInterp;
155 QAD_PyEditor* myListener;
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),
170 myInitEditorThread(0),
171 myExecCommandThread(0)
173 QString fntSet = QAD_CONFIG->getSetting("Viewer:ConsoleFont");
174 QFont myFont = QAD_Tools::stringToFont( fntSet );
175 // QFont myFont("Courier",11);
177 setTextFormat(QTextEdit::PlainText);
179 // san - This is necessary for troubleless initialization
181 viewport()->setCursor( waitCursor );
183 myInitEditorThread = new TInitEditorThread(myInterp,myStudyMutex,myInitEditorMutex,this);
184 myExecCommandThread = new TExecCommandThread(myInterp,myStudyMutex,myExecCommandMutex,this);
186 _currentPrompt = READY_PROMPT;
187 setPalette( QAD_Application::getPalette(true) );
190 connect(this,SIGNAL(returnPressed()),this,SLOT(handleReturn()) );
194 void QAD_PyEditor::Init()
196 myInitEditorThread->start();
203 QAD_PyEditor::~QAD_PyEditor()
205 if(MYDEBUG) MESSAGE("QAD_PyEditor::~QAD_PyEditor()");
208 ThreadLock aLock(myInitEditorMutex,"myInitEditorMutex");
209 delete myInitEditorThread;
211 delete myInitEditorMutex;
215 ThreadLock aLock(myExecCommandMutex,"myExecCommandMutex");
216 delete myExecCommandThread;
218 delete myExecCommandMutex;
223 Called to insert a string s
225 void QAD_PyEditor::setText(QString s)
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));
235 Called when an handleReturn
237 void QAD_PyEditor::handleReturn()
239 int para=paragraphs()-2;
241 // NRI : Temporary added
242 SALOMEDS::Study_var aStudy = QAD_Application::getDesktop()->getActiveStudy()->getStudyDocument();
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") );
251 _currentPrompt = READY_PROMPT;
252 setText(_currentPrompt);
258 _buf.append(text(para).remove(0,PROMPT_SIZE));
259 _buf.truncate( _buf.length() - 1 );
261 viewport()->setCursor( waitCursor );
262 myExecCommandThread->exec(_buf.latin1());
266 Processes drop event: paste dragged text
268 void QAD_PyEditor::contentsDropEvent( QDropEvent* event )
270 event->acceptAction();
272 if ( QTextDrag::decode( event, text ) ) {
274 int endLine = paragraphs() -1;
275 col = charAt( event->pos(), &par );
277 if ( col >= 0 && par >= 0 ) {
278 if ( par != endLine || col < PROMPT_SIZE ) {
280 col = paragraphLength( endLine );
282 setCursorPosition( par, col );
283 insertAt( text, par, col );
290 Processes middle button release event - paste clipboard's contents
292 void QAD_PyEditor::contentsMouseReleaseEvent( QMouseEvent* event )
294 if ( event->button() == LeftButton ) {
295 QTextEdit::contentsMouseReleaseEvent(event);
298 if ( event->button() == MidButton ) {
299 if (QApplication::clipboard()->supportsSelection()) {
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 ) );
307 setCursorPosition( par, col );
308 QApplication::clipboard()->setSelectionMode(TRUE);
310 QApplication::clipboard()->setSelectionMode(FALSE);
315 QTextEdit::contentsMouseReleaseEvent(event);
320 Processes own popup menu
322 void QAD_PyEditor::mousePressEvent (QMouseEvent* event)
324 if ( event->button() == RightButton ) {
325 QPopupMenu *popup = new QPopupMenu( this );
326 QMap<int, int> idMap;
328 int para1, col1, para2, col2;
329 getSelection(¶1, &col1, ¶2, &col2);
330 bool allSelected = hasSelectedText() &&
331 para1 == 0 && para2 == paragraphs()-1 && col1 == 0 && para2 == paragraphLength(para2);
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 );
348 int r = popup->exec( event->globalPos() );
351 if ( r == idMap[ IdCopy ] ) {
354 else if ( r == idMap[ IdPaste ] ) {
357 else if ( r == idMap[ IdClear ] ) {
360 _currentPrompt = READY_PROMPT;
361 setText(_currentPrompt);
363 else if ( r == idMap[ IdSelectAll ] ) {
368 QTextEdit::mousePressEvent(event);
373 Checks, is the string a command line or not.
376 bool QAD_PyEditor::isCommand( const QString& str) const
378 // prompt may be '>>> ' or for '... '
379 return ( str.find( READY_PROMPT ) == 0 || str.find( DOTS_PROMPT ) == 0 );
384 Called when a keyPress event
386 void QAD_PyEditor::keyPressEvent( QKeyEvent* e )
388 // get cursor position
390 getCursorPosition(&curLine, &curCol);
392 // get last edited line
393 int endLine = paragraphs() -1;
395 // get pressed key code
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;
405 // process <Ctrl>+<C> key-bindings
406 if ( aKey == Key_C && ctrlPressed ) {
409 _currentPrompt = READY_PROMPT;
410 setText(_currentPrompt);
414 // check for printed key
415 aKey = ( aKey < Key_Space || aKey > Key_ydiaeresis ) ? aKey : 0;
421 if ( curLine < endLine || curCol < PROMPT_SIZE )
422 moveCursor( QTextEdit::MoveEnd, false );
423 QTextEdit::keyPressEvent( e );
430 moveCursor( QTextEdit::MoveEnd, false );
431 QTextEdit::keyPressEvent( e );
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
441 if ( ctrlPressed && shftPressed ) {
442 scrollBy( 0, -QFontMetrics( font() ).lineSpacing() );
444 else if ( shftPressed ) {
446 moveCursor( QTextEdit::MoveUp, true );
448 else if ( ctrlPressed ) {
449 moveCursor( QTextEdit::MoveUp, false );
452 QString histLine = _currentPrompt;
453 if ( ! _isInHistory ) {
455 _currentCommand = text( endLine ).remove( 0, PROMPT_SIZE );
456 _currentCommand.truncate( _currentCommand.length() - 1 );
458 QString previousCommand = myInterp->getPrevious();
459 if ( previousCommand.compare( BEGIN_HISTORY_PY ) != 0 ) {
460 removeParagraph( endLine );
461 histLine.append( previousCommand );
462 insertParagraph( histLine, -1 );
464 moveCursor( QTextEdit::MoveEnd, false );
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
475 if ( ctrlPressed && shftPressed ) {
476 scrollBy( 0, QFontMetrics( font() ).lineSpacing() );
478 else if ( shftPressed ) {
479 if ( curLine < endLine )
480 moveCursor( QTextEdit::MoveDown, true );
482 else if ( ctrlPressed ) {
483 moveCursor( QTextEdit::MoveDown, false );
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 );
495 _isInHistory = false;
496 removeParagraph( endLine );
497 histLine.append( _currentCommand );
498 insertParagraph( histLine, -1 );
501 moveCursor( QTextEdit::MoveEnd, false );
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
512 if ( !shftPressed && isCommand( text( curLine ) ) && curCol <= PROMPT_SIZE ) {
513 setCursorPosition( curLine-1, 0 );
514 moveCursor( QTextEdit::MoveLineEnd, false );
517 QTextEdit::keyPressEvent( e );
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
528 if ( !shftPressed ) {
529 if ( curCol < paragraphLength( curLine ) ) {
530 if ( isCommand( text( curLine ) ) && curCol < PROMPT_SIZE ) {
531 setCursorPosition( curLine, PROMPT_SIZE );
536 if ( curLine < endLine && isCommand( text( curLine+1 ) ) ) {
537 setCursorPosition( curLine+1, PROMPT_SIZE );
542 QTextEdit::keyPressEvent( e );
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
552 if ( ctrlPressed && shftPressed ) {
553 scrollBy( 0, -visibleHeight() );
555 else if ( shftPressed ) {
557 moveCursor( QTextEdit::MovePgUp, true );
559 else if ( ctrlPressed ) {
560 moveCursor( QTextEdit::MovePgUp, false );
563 QString histLine = _currentPrompt;
564 if ( ! _isInHistory ) {
566 _currentCommand = text( endLine ).remove( 0, PROMPT_SIZE );
567 _currentCommand.truncate( _currentCommand.length() - 1 );
569 QString firstCommand = myInterp->getPrevious();
571 while ( ( pcmd = QString( myInterp->getPrevious() ) ).compare( BEGIN_HISTORY_PY ) != 0 )
573 if ( firstCommand.compare( BEGIN_HISTORY_PY ) != 0 ) {
574 removeParagraph( endLine );
575 histLine.append( firstCommand );
576 insertParagraph( histLine, -1 );
578 moveCursor( QTextEdit::MoveEnd, false );
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
589 if ( ctrlPressed && shftPressed ) {
590 scrollBy( 0, visibleHeight() );
592 else if ( shftPressed ) {
593 if ( curLine < endLine )
594 moveCursor( QTextEdit::MovePgDown, true );
596 else if ( ctrlPressed ) {
597 moveCursor( QTextEdit::MovePgDown, false );
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 );
608 moveCursor( QTextEdit::MoveEnd, false );
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
620 moveCursor( QTextEdit::MoveHome, shftPressed );
623 if ( isCommand( text( curLine ) ) ) {
624 int ps1, ps2, cs1, cs2;
625 bool hasSelection = hasSelectedText();
627 getSelection( &ps1, &cs1, &ps2, &cs2 );
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 );
635 moveCursor( QTextEdit::MoveLineStart, shftPressed );
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
648 moveCursor( QTextEdit::MoveEnd, shftPressed );
651 moveCursor( QTextEdit::MoveLineEnd, shftPressed );
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
661 if ( curLine == endLine && ( curCol > PROMPT_SIZE || curCol >= PROMPT_SIZE && hasSelectedText() ) ) {
662 if ( ctrlPressed && !hasSelectedText() ) {
663 QString txt = text( curLine );
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();
672 QTextEdit::keyPressEvent( e );
676 QTextEdit::keyPressEvent( e );
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
687 if ( curLine == endLine && curCol > PROMPT_SIZE-1 ) {
688 if ( ctrlPressed && !hasSelectedText() ) {
689 QString txt = text( curLine );
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();
699 QTextEdit::keyPressEvent( e );
703 QTextEdit::keyPressEvent( e );
709 // <Insert> key: process as follows
710 // - with <Ctrl> modifier key pressed: copy()
711 // - with <Shift> modifier key pressed: paste() to the command line
716 else if ( shftPressed ) {
717 if ( curLine != endLine || curCol < PROMPT_SIZE )
718 moveCursor( QTextEdit::MoveEnd, false );
722 QTextEdit::keyPressEvent( e );
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 );
735 void QAD_PyEditor::customEvent(QCustomEvent* e)
737 switch( e->type() ) {
744 _currentPrompt = READY_PROMPT;
745 setText(_currentPrompt);
748 case PYTHON_INCOMPLETE:
751 _currentPrompt = DOTS_PROMPT;
752 setText(_currentPrompt);
757 setText(myInterp->getbanner().c_str());
759 QApplication::restoreOverrideCursor();
762 case SET_WAIT_CURSOR:
764 viewport()->setCursor( waitCursor );
769 viewport()->unsetCursor();
773 QTextEdit::customEvent( e );
776 setReadOnly( false );
777 _isInHistory = false;