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>
42 // NRI : Temporary added
44 #include <SALOMEconfig.h>
45 #include CORBA_SERVER_HEADER(SALOMEDS)
46 #include CORBA_SERVER_HEADER(SALOMEDS_Attributes)
53 static int MYDEBUG = 0;
55 static int MYDEBUG = 0;
60 enum { IdCopy, IdPaste, IdClear, IdSelectAll };
63 static QString PROMPT = ">>> ";
66 class TInitEditorThread : public QThread
69 TInitEditorThread(QAD_PyInterp*& theInterp,
70 QMutex* theStudyMutex, QMutex* theMutex,
71 QAD_PyEditor* theListener):
74 myStudyMutex(theStudyMutex),
75 myListener(theListener)
77 // san - commented as inefficient: sometimes event is processed significant period of time after this moment
78 //QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::SET_WAIT_CURSOR));
81 virtual ~TInitEditorThread(){}
85 ThreadLock anEditorLock(myMutex,"TInitEditorThread::anEditorLock");
86 ThreadLock aStudyLock(myStudyMutex,"TInitEditorThread::aStudyLock");
87 ThreadLock aPyLock = GetPyThreadLock("TInitEditorThread::aPyLock");
88 if(MYDEBUG) MESSAGE("TInitEditorThread::run() - myInterp = "<<myInterp<<"; myMutex = "<<myMutex);
89 myListener->myBanner = myInterp->getbanner().c_str();
90 QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::INITIALIZE));
91 QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::PYTHON_OK));
92 QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::UNSET_CURSOR));
98 QAD_PyInterp*& myInterp;
99 QAD_PyEditor* myListener;
103 class TExecCommandThread : public QThread
106 TExecCommandThread(QAD_PyInterp*& theInterp,
107 QMutex* theStudyMutex, QMutex* theMutex,
108 QAD_PyEditor* theListener):
111 myStudyMutex(theStudyMutex),
112 myListener(theListener),
115 //QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::SET_WAIT_CURSOR));
118 virtual ~TExecCommandThread() {}
120 void exec(const char* theCommand){
121 myCommand = theCommand;
127 //QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::SET_WAIT_CURSOR));
128 int anId = QAD_PyEditor::PYTHON_OK;
130 ThreadLock anEditorLock(myMutex,"TExecCommandThread::anEditorLock");
131 //ThreadLock aStudyLock(myStudyMutex,"TExecCommandThread::aStudyLock");
132 ThreadLock aPyLock = GetPyThreadLock("TExecCommandThread::aPyLock");
133 int ret = myInterp->run( myCommand.latin1() );
134 if(MYDEBUG) MESSAGE("TExecCommand::run() - myInterp = "<<myInterp<<"; myCommand = '"<<myCommand.latin1()<<"' - "<<ret);
136 anId = QAD_PyEditor::PYTHON_ERROR;
138 anId = QAD_PyEditor::PYTHON_INCOMPLETE;
139 myListener->myError = myInterp->getverr().c_str();
140 myListener->myOutput = myInterp->getvout().c_str();
142 myListener->myError = "";
143 myListener->myOutput = "";
145 QThread::postEvent(myListener, new QCustomEvent(anId));
146 QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::UNSET_CURSOR));
151 QMutex* myStudyMutex;
152 QAD_PyInterp*& myInterp;
153 QAD_PyEditor* myListener;
161 QAD_PyEditor::QAD_PyEditor(QAD_PyInterp*& theInterp, QMutex* theMutex,
162 QWidget *theParent, const char* theName):
163 QTextEdit(theParent,theName),
164 myStudyMutex(theMutex),
165 myInitEditorMutex(new QMutex),
166 myExecCommandMutex(new QMutex),
168 myInitEditorThread(0),
169 myExecCommandThread(0)
171 QString fntSet = QAD_CONFIG->getSetting("Viewer:ConsoleFont");
172 QFont myFont = QAD_Tools::stringToFont( fntSet );
173 // QFont myFont("Courier",11);
175 setTextFormat(QTextEdit::PlainText);
177 // san - This is necessary for troubleless initialization
179 viewport()->setCursor( waitCursor );
181 myInitEditorThread = new TInitEditorThread(myInterp,myStudyMutex,myInitEditorMutex,this);
182 myExecCommandThread = new TExecCommandThread(myInterp,myStudyMutex,myExecCommandMutex,this);
184 _currentPrompt = PROMPT;
185 setPalette( QAD_Application::getPalette(true) );
188 connect(this,SIGNAL(returnPressed()),this,SLOT(handleReturn()) );
192 void QAD_PyEditor::Init()
194 myInitEditorThread->start();
201 QAD_PyEditor::~QAD_PyEditor()
203 if(MYDEBUG) MESSAGE("QAD_PyEditor::~QAD_PyEditor()");
206 ThreadLock aLock(myInitEditorMutex,"myInitEditorMutex");
207 delete myInitEditorThread;
209 delete myInitEditorMutex;
213 ThreadLock aLock(myExecCommandMutex,"myExecCommandMutex");
214 delete myExecCommandThread;
216 delete myExecCommandMutex;
221 Called to insert a string s
223 void QAD_PyEditor::setText(QString s)
225 int para=paragraphs()-1;
226 int col=paragraphLength(para);
227 insertAt(s,para,col);
228 int n = paragraphs()-1;
229 setCursorPosition( n, paragraphLength(n));
233 Called when an handleReturn
235 void QAD_PyEditor::handleReturn()
238 int para=paragraphs()-2;
240 // NRI : Temporary added
241 SALOMEDS::Study_var aStudy = QAD_Application::getDesktop()->getActiveStudy()->getStudyDocument();
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") );
250 _currentPrompt = ">>> ";
251 setText(_currentPrompt);
257 _buf.append(text(para).remove(0,SIZEPR));
258 _buf.truncate( _buf.length() - 1 );
260 viewport()->setCursor( waitCursor );
261 myExecCommandThread->exec(_buf.latin1());
265 Processes own popup menu
267 void QAD_PyEditor::mousePressEvent (QMouseEvent * event)
269 if ( event->button() == RightButton ) {
270 QPopupMenu *popup = new QPopupMenu( this );
271 QMap<int, int> idMap;
273 int para1, col1, para2, col2;
274 getSelection(¶1, &col1, ¶2, &col2);
275 bool allSelected = hasSelectedText() &&
276 para1 == 0 && para2 == paragraphs()-1 && col1 == 0 && para2 == paragraphLength(para2);
278 id = popup->insertItem( tr( "EDIT_COPY_CMD" ) );
279 idMap.insert(IdCopy, id);
280 id = popup->insertItem( tr( "EDIT_PASTE_CMD" ) );
281 idMap.insert(IdPaste, id);
282 id = popup->insertItem( tr( "EDIT_CLEAR_CMD" ) );
283 idMap.insert(IdClear, id);
284 popup->insertSeparator();
285 id = popup->insertItem( tr( "EDIT_SELECTALL_CMD" ) );
286 idMap.insert(IdSelectAll, id);
287 popup->setItemEnabled( idMap[ IdCopy ], hasSelectedText() );
288 popup->setItemEnabled( idMap[ IdPaste ],
289 !isReadOnly() && (bool)QApplication::clipboard()->text().length() );
290 popup->setItemEnabled( idMap[ IdSelectAll ],
291 (bool)text().length() && !allSelected );
293 int r = popup->exec( event->globalPos() );
296 if ( r == idMap[ IdCopy ] ) {
299 else if ( r == idMap[ IdPaste ] ) {
302 else if ( r == idMap[ IdClear ] ) {
305 setText(_currentPrompt);
307 else if ( r == idMap[ IdSelectAll ] ) {
313 QTextEdit::mousePressEvent(event);
318 Called when a Mouse release event
320 void QAD_PyEditor::mouseReleaseEvent ( QMouseEvent * e )
322 // MESSAGE("mouseReleaseEvent");
323 int curPara, curCol; // for cursor position
324 int endPara, endCol; // for last edited line
325 getCursorPosition(&curPara, &curCol);
326 endPara = paragraphs() -1;
327 if (e->button() != MidButton)
328 QTextEdit::mouseReleaseEvent(e);
329 else if ((curPara == endPara) && (curCol >= SIZEPR))
330 QTextEdit::mouseReleaseEvent(e);
334 Called when a drop event (Drag & Drop)
336 void QAD_PyEditor::dropEvent (QDropEvent *e)
338 MESSAGE("dropEvent : not handled");
342 Checks, is the string a command line or not.
345 bool QAD_PyEditor::isCommand( const QString& str) const
347 if (str.find(_currentPrompt)==0)
354 Called when a keyPress event
356 void QAD_PyEditor::keyPressEvent( QKeyEvent *e )
358 int curLine, curCol; // for cursor position
359 int endLine, endCol; // for last edited line
360 getCursorPosition(&curLine, &curCol);
361 endLine = paragraphs() -1;
362 //MESSAGE("current position " << curLine << ", " << curCol);
363 //MESSAGE("last line " << endLine);
367 if ((aKey >= Key_Space) && (aKey <= Key_ydiaeresis))
372 bool ctrlPressed = ( (e->state() & ControlButton) == ControlButton );
373 bool shftPressed = ( (e->state() & ShiftButton) == ShiftButton );
379 if (curLine <endLine || curCol < SIZEPR )
380 moveCursor(QTextEdit::MoveEnd, false);
381 QTextEdit::keyPressEvent( e );
387 if (curLine <endLine)
388 moveCursor(QTextEdit::MoveEnd, false);
390 moveCursor(QTextEdit::MoveLineEnd, false);
391 QTextEdit::keyPressEvent( e );
396 // if Cntr+Key_Up event then move cursor up
398 moveCursor(QTextEdit::MoveUp, false);
400 // if Shift+Key_Up event then move cursor up and select the text
401 else if ( shftPressed && curLine > 0 ){
402 moveCursor(QTextEdit::MoveUp, true);
404 // scroll the commands stack up
406 QString histLine = _currentPrompt;
410 _currentCommand = text(endLine).remove(0,SIZEPR);
411 _currentCommand.truncate( _currentCommand.length() - 1 );
412 SCRUTE(_currentCommand);
414 QString previousCommand = myInterp->getPrevious();
415 if (previousCommand.compare(BEGIN_HISTORY_PY) != 0)
417 removeParagraph(endLine);
418 histLine.append(previousCommand);
419 insertParagraph(histLine, -1);
421 moveCursor(QTextEdit::MoveEnd, false);
427 // if Cntr+Key_Down event then move cursor down
429 moveCursor(QTextEdit::MoveDown, false);
431 // if Shift+Key_Down event then move cursor down and select the text
432 else if ( shftPressed && curLine < endLine ) {
433 moveCursor(QTextEdit::MoveDown, true);
435 // scroll the commands stack down
437 QString histLine = _currentPrompt;
438 QString nextCommand = myInterp->getNext();
439 if (nextCommand.compare(TOP_HISTORY_PY) != 0)
441 removeParagraph(endLine);
442 histLine.append(nextCommand);
443 insertParagraph(histLine, -1);
448 _isInHistory = false;
449 removeParagraph(endLine);
450 histLine.append(_currentCommand);
451 insertParagraph(histLine, -1);
453 moveCursor(QTextEdit::MoveEnd, false);
459 if (!shftPressed && isCommand(text(curLine)) && curCol <= SIZEPR )
461 setCursorPosition((curLine -1), SIZEPR);
462 moveCursor(QTextEdit::MoveLineEnd, false);
464 else QTextEdit::keyPressEvent( e );
469 if (!shftPressed && isCommand(text(curLine))
470 && curCol < SIZEPR) setCursorPosition(curLine, SIZEPR);
471 QTextEdit::keyPressEvent( e );
476 horizontalScrollBar()->setValue( horizontalScrollBar()->minValue() );
477 if (isCommand(text(curLine))) {
478 setCursorPosition(curLine, SIZEPR);
479 if ( curCol > SIZEPR && shftPressed )
480 setSelection( curLine, SIZEPR, curLine, curCol );
484 else moveCursor(QTextEdit::MoveLineStart, shftPressed);
489 moveCursor(QTextEdit::MoveLineEnd, shftPressed);
494 if ((curLine == endLine) && (curCol > SIZEPR))
495 QTextEdit::keyPressEvent( e );
500 if ((curLine == endLine) && (curCol > SIZEPR-1))
501 QTextEdit::keyPressEvent( e );
508 else if ( shftPressed ) {
509 moveCursor(QTextEdit::MoveEnd, false);
513 QTextEdit::keyPressEvent( e );
517 if ( e->key() == Key_C && ( e->state() & ControlButton ) )
521 _currentPrompt = ">>> ";
522 setText(_currentPrompt);
525 // NRI : DEBUG PAS TERRIBLE //
526 if (( e->key() == Key_F3) ||
527 ( e->key() == Key_F4) ||
528 ( e->key() == Key_Return) ||
529 ( e->key() == Key_Escape))
530 QAD_Application::getDesktop()->onKeyPress( e );
534 void QAD_PyEditor::customEvent(QCustomEvent *e)
536 switch( e->type() ) {
543 _currentPrompt = ">>> ";
544 setText(_currentPrompt);
547 case PYTHON_INCOMPLETE:
550 _currentPrompt = "... ";
551 setText(_currentPrompt);
556 setText(myInterp->getbanner().c_str());
558 QApplication::restoreOverrideCursor();
561 case SET_WAIT_CURSOR:
563 viewport()->setCursor( waitCursor );
568 viewport()->unsetCursor();
572 QTextEdit::customEvent( e );
575 setReadOnly( false );
576 _isInHistory = false;