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)
49 #include "utilities.h"
54 static int MYDEBUG = 0;
56 static int MYDEBUG = 0;
61 enum { IdCopy, IdPaste, IdClear, IdSelectAll };
64 static QString PROMPT = ">>> ";
67 class TInitEditorThread : public QThread
70 TInitEditorThread(QAD_PyInterp*& theInterp,
71 QMutex* theStudyMutex, QMutex* theMutex,
72 QAD_PyEditor* theListener):
75 myStudyMutex(theStudyMutex),
76 myListener(theListener)
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));
82 virtual ~TInitEditorThread(){}
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));
99 QAD_PyInterp*& myInterp;
100 QAD_PyEditor* myListener;
104 class TExecCommandThread : public QThread
107 TExecCommandThread(QAD_PyInterp*& theInterp,
108 QMutex* theStudyMutex, QMutex* theMutex,
109 QAD_PyEditor* theListener):
112 myStudyMutex(theStudyMutex),
113 myListener(theListener),
116 //QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::SET_WAIT_CURSOR));
119 virtual ~TExecCommandThread() {}
121 void exec(const char* theCommand){
122 myCommand = theCommand;
128 //QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::SET_WAIT_CURSOR));
129 int anId = QAD_PyEditor::PYTHON_OK;
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);
137 anId = QAD_PyEditor::PYTHON_ERROR;
139 anId = QAD_PyEditor::PYTHON_INCOMPLETE;
140 myListener->myError = myInterp->getverr().c_str();
141 myListener->myOutput = myInterp->getvout().c_str();
143 myListener->myError = "";
144 myListener->myOutput = "";
146 QThread::postEvent(myListener, new QCustomEvent(anId));
147 QThread::postEvent(myListener, new QCustomEvent(QAD_PyEditor::UNSET_CURSOR));
152 QMutex* myStudyMutex;
153 QAD_PyInterp*& myInterp;
154 QAD_PyEditor* myListener;
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),
169 myInitEditorThread(0),
170 myExecCommandThread(0)
172 QString fntSet = QAD_CONFIG->getSetting("Viewer:ConsoleFont");
173 QFont myFont = QAD_Tools::stringToFont( fntSet );
174 // QFont myFont("Courier",11);
176 setTextFormat(QTextEdit::PlainText);
178 // san - This is necessary for troubleless initialization
180 viewport()->setCursor( waitCursor );
182 myInitEditorThread = new TInitEditorThread(myInterp,myStudyMutex,myInitEditorMutex,this);
183 myExecCommandThread = new TExecCommandThread(myInterp,myStudyMutex,myExecCommandMutex,this);
185 _currentPrompt = PROMPT;
186 setPalette( QAD_Application::getPalette(true) );
189 connect(this,SIGNAL(returnPressed()),this,SLOT(handleReturn()) );
193 void QAD_PyEditor::Init()
195 myInitEditorThread->start();
202 QAD_PyEditor::~QAD_PyEditor()
204 if(MYDEBUG) MESSAGE("QAD_PyEditor::~QAD_PyEditor()");
207 ThreadLock aLock(myInitEditorMutex,"myInitEditorMutex");
208 delete myInitEditorThread;
210 delete myInitEditorMutex;
214 ThreadLock aLock(myExecCommandMutex,"myExecCommandMutex");
215 delete myExecCommandThread;
217 delete myExecCommandMutex;
222 Called to insert a string s
224 void QAD_PyEditor::setText(QString s)
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));
234 Called when an handleReturn
236 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 = ">>> ";
252 setText(_currentPrompt);
258 _buf.append(text(para).remove(0,SIZEPR));
259 _buf.truncate( _buf.length() - 1 );
261 viewport()->setCursor( waitCursor );
262 myExecCommandThread->exec(_buf.latin1());
266 Processes own popup menu
268 void QAD_PyEditor::mousePressEvent (QMouseEvent * event)
270 if ( event->button() == RightButton ) {
271 QPopupMenu *popup = new QPopupMenu( this );
272 QMap<int, int> idMap;
274 int para1, col1, para2, col2;
275 getSelection(¶1, &col1, ¶2, &col2);
276 bool allSelected = hasSelectedText() &&
277 para1 == 0 && para2 == paragraphs()-1 && col1 == 0 && para2 == paragraphLength(para2);
279 id = popup->insertItem( tr( "EDIT_COPY_CMD" ) );
280 idMap.insert(IdCopy, id);
281 id = popup->insertItem( tr( "EDIT_PASTE_CMD" ) );
282 idMap.insert(IdPaste, id);
283 id = popup->insertItem( tr( "EDIT_CLEAR_CMD" ) );
284 idMap.insert(IdClear, id);
285 popup->insertSeparator();
286 id = popup->insertItem( tr( "EDIT_SELECTALL_CMD" ) );
287 idMap.insert(IdSelectAll, id);
288 popup->setItemEnabled( idMap[ IdCopy ], hasSelectedText() );
289 popup->setItemEnabled( idMap[ IdPaste ],
290 !isReadOnly() && (bool)QApplication::clipboard()->text().length() );
291 popup->setItemEnabled( idMap[ IdSelectAll ],
292 (bool)text().length() && !allSelected );
294 int r = popup->exec( event->globalPos() );
297 if ( r == idMap[ IdCopy ] ) {
300 else if ( r == idMap[ IdPaste ] ) {
303 else if ( r == idMap[ IdClear ] ) {
306 setText(_currentPrompt);
308 else if ( r == idMap[ IdSelectAll ] ) {
314 QTextEdit::mousePressEvent(event);
319 Called when a Mouse release event
321 void QAD_PyEditor::mouseReleaseEvent ( QMouseEvent * e )
323 // MESSAGE("mouseReleaseEvent");
324 int curPara, curCol; // for cursor position
325 int endPara, endCol; // for last edited line
326 getCursorPosition(&curPara, &curCol);
327 endPara = paragraphs() -1;
328 if (e->button() != MidButton)
329 QTextEdit::mouseReleaseEvent(e);
330 else if ((curPara == endPara) && (curCol >= SIZEPR))
331 QTextEdit::mouseReleaseEvent(e);
335 Called when a drop event (Drag & Drop)
337 void QAD_PyEditor::dropEvent (QDropEvent *e)
339 MESSAGE("dropEvent : not handled");
343 Checks, is the string a command line or not.
346 bool QAD_PyEditor::isCommand( const QString& str) const
348 if (str.find(_currentPrompt)==0)
355 Called when a keyPress event
357 void QAD_PyEditor::keyPressEvent( QKeyEvent *e )
359 int curLine, curCol; // for cursor position
360 int endLine, endCol; // for last edited line
361 getCursorPosition(&curLine, &curCol);
362 endLine = paragraphs() -1;
363 //MESSAGE("current position " << curLine << ", " << curCol);
364 //MESSAGE("last line " << endLine);
368 if ((aKey >= Key_Space) && (aKey <= Key_ydiaeresis))
373 bool ctrlPressed = ( (e->state() & ControlButton) == ControlButton );
374 bool shftPressed = ( (e->state() & ShiftButton) == ShiftButton );
380 if (curLine <endLine || curCol < SIZEPR )
381 moveCursor(QTextEdit::MoveEnd, false);
382 QTextEdit::keyPressEvent( e );
388 if (curLine <endLine)
389 moveCursor(QTextEdit::MoveEnd, false);
391 moveCursor(QTextEdit::MoveLineEnd, false);
392 QTextEdit::keyPressEvent( e );
397 // if Cntr+Key_Up event then move cursor up
399 moveCursor(QTextEdit::MoveUp, false);
401 // if Shift+Key_Up event then move cursor up and select the text
402 else if ( shftPressed && curLine > 0 ){
403 moveCursor(QTextEdit::MoveUp, true);
405 // scroll the commands stack up
407 QString histLine = _currentPrompt;
411 _currentCommand = text(endLine).remove(0,SIZEPR);
412 _currentCommand.truncate( _currentCommand.length() - 1 );
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;