00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "PreCompiled.h"
00025 #ifndef _PreComp_
00026 # include <QClipboard>
00027 # include <QDockWidget>
00028 # include <QGridLayout>
00029 # include <QHBoxLayout>
00030 # include <QKeyEvent>
00031 # include <QMenu>
00032 # include <QMessageBox>
00033 # include <QPushButton>
00034 # include <QSpacerItem>
00035 # include <QTextCursor>
00036 # include <QTextDocumentFragment>
00037 # include <QTextStream>
00038 # include <QUrl>
00039 #endif
00040
00041 #include "PythonConsole.h"
00042 #include "PythonConsolePy.h"
00043 #include "CallTips.h"
00044 #include "Application.h"
00045 #include "Action.h"
00046 #include "Command.h"
00047 #include "DlgEditorImp.h"
00048 #include "FileDialog.h"
00049 #include "MainWindow.h"
00050
00051
00052 #include <Base/Interpreter.h>
00053 #include <Base/Exception.h>
00054 #include <CXX/Exception.hxx>
00055
00056 using namespace Gui;
00057
00058 namespace Gui {
00059 struct PythonConsoleP
00060 {
00061 enum Output {Error = 20, Message = 21};
00062 enum CopyType {Normal, History, Command};
00063 CopyType type;
00064 PyObject *_stdoutPy, *_stderrPy, *_stdinPy, *_stdin;
00065 InteractiveInterpreter* interpreter;
00066 CallTipsList* callTipsList;
00067 ConsoleHistory history;
00068 QString output, error;
00069 QStringList statements;
00070 bool interactive;
00071 QMap<QString, QColor> colormap;
00072 PythonConsoleP()
00073 {
00074 type = Normal;
00075 interpreter = 0;
00076 colormap[QLatin1String("Text")] = Qt::black;
00077 colormap[QLatin1String("Bookmark")] = Qt::cyan;
00078 colormap[QLatin1String("Breakpoint")] = Qt::red;
00079 colormap[QLatin1String("Keyword")] = Qt::blue;
00080 colormap[QLatin1String("Comment")] = QColor(0, 170, 0);
00081 colormap[QLatin1String("Block comment")] = QColor(160, 160, 164);
00082 colormap[QLatin1String("Number")] = Qt::blue;
00083 colormap[QLatin1String("String")] = Qt::red;
00084 colormap[QLatin1String("Character")] = Qt::red;
00085 colormap[QLatin1String("Class name")] = QColor(255, 170, 0);
00086 colormap[QLatin1String("Define name")] = QColor(255, 170, 0);
00087 colormap[QLatin1String("Operator")] = QColor(160, 160, 164);
00088 colormap[QLatin1String("Python output")] = QColor(170, 170, 127);
00089 colormap[QLatin1String("Python error")] = Qt::red;
00090 }
00091 };
00092 struct InteractiveInterpreterP
00093 {
00094 PyObject* interpreter;
00095 PyObject* sysmodule;
00096 QStringList buffer;
00097 };
00098 }
00099
00100 InteractiveInterpreter::InteractiveInterpreter()
00101 {
00102
00103 Base::PyGILStateLocker lock;
00104 PyObject* module = PyImport_ImportModule("code");
00105 if (!module)
00106 throw Base::PyException();
00107 PyObject* func = PyObject_GetAttrString(module, "InteractiveInterpreter");
00108 PyObject* args = Py_BuildValue("()");
00109 d = new InteractiveInterpreterP;
00110 d->interpreter = PyEval_CallObject(func,args);
00111 Py_DECREF(args);
00112 Py_DECREF(func);
00113 Py_DECREF(module);
00114
00115 setPrompt();
00116 }
00117
00118 InteractiveInterpreter::~InteractiveInterpreter()
00119 {
00120 Base::PyGILStateLocker lock;
00121 Py_XDECREF(d->interpreter);
00122 Py_XDECREF(d->sysmodule);
00123 delete d;
00124 }
00125
00129 void InteractiveInterpreter::setPrompt()
00130 {
00131
00132 Base::PyGILStateLocker lock;
00133 d->sysmodule = PyImport_ImportModule("sys");
00134 if (!PyObject_HasAttrString(d->sysmodule, "ps1"))
00135 PyObject_SetAttrString(d->sysmodule, "ps1", PyString_FromString(">>> "));
00136 if (!PyObject_HasAttrString(d->sysmodule, "ps2"))
00137 PyObject_SetAttrString(d->sysmodule, "ps2", PyString_FromString("... "));
00138 }
00139
00151 PyObject* InteractiveInterpreter::compile(const char* source) const
00152 {
00153 Base::PyGILStateLocker lock;
00154 PyObject* func = PyObject_GetAttrString(d->interpreter, "compile");
00155 PyObject* args = Py_BuildValue("(s)", source);
00156 PyObject* eval = PyEval_CallObject(func,args);
00157
00158 Py_DECREF(args);
00159 Py_DECREF(func);
00160
00161 if (eval){
00162 return eval;
00163 } else {
00164
00165 throw Base::Exception();
00166 }
00167
00168
00169 return 0;
00170 }
00171
00183 int InteractiveInterpreter::compileCommand(const char* source) const
00184 {
00185 Base::PyGILStateLocker lock;
00186 PyObject* func = PyObject_GetAttrString(d->interpreter, "compile");
00187 PyObject* args = Py_BuildValue("(s)", source);
00188 PyObject* eval = PyEval_CallObject(func,args);
00189
00190 Py_DECREF(args);
00191 Py_DECREF(func);
00192
00193 int ret = 0;
00194 if (eval){
00195 if (PyObject_TypeCheck(Py_None, eval->ob_type))
00196 ret = 1;
00197 else
00198 ret = 0;
00199 Py_DECREF(eval);
00200 } else {
00201 ret = -1;
00202 }
00203
00204 return ret;
00205 }
00206
00225 bool InteractiveInterpreter::runSource(const char* source) const
00226 {
00227 Base::PyGILStateLocker lock;
00228 PyObject* code;
00229 try {
00230 code = compile(source);
00231 } catch (const Base::Exception&) {
00232
00233
00234
00235 PyObject *errobj, *errdata, *errtraceback;
00236 PyErr_Fetch(&errobj, &errdata, &errtraceback);
00237 PyErr_Restore(errobj, errdata, 0);
00238
00239 if (PyErr_Occurred()) PyErr_Print();
00240 return false;
00241 }
00242
00243
00244 if (PyObject_TypeCheck(Py_None, code->ob_type)) {
00245 Py_DECREF(code);
00246 return true;
00247 }
00248
00249
00250 runCode((PyCodeObject*)code);
00251 return false;
00252 }
00253
00254
00255
00256
00257
00258
00259 void InteractiveInterpreter::runCode(PyCodeObject* code) const
00260 {
00261 Base::PyGILStateLocker lock;
00262 PyObject *module, *dict, *presult;
00263 module = PyImport_AddModule("__main__");
00264 if (module == NULL)
00265 throw Base::PyException();
00266 dict = PyModule_GetDict(module);
00267 if (dict == NULL)
00268 throw Base::PyException();
00269
00270
00271 presult = PyEval_EvalCode(code, dict, dict);
00272 Py_XDECREF(code);
00273 if (!presult) {
00274 if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
00275
00276 throw Base::SystemExitException();
00277 }
00278 if ( PyErr_Occurred() )
00279 PyErr_Print();
00280 } else {
00281 Py_DECREF(presult);
00282 }
00283 }
00284
00289 bool InteractiveInterpreter::push(const char* line)
00290 {
00291 d->buffer.append(QString::fromAscii(line));
00292 QString source = d->buffer.join(QLatin1String("\n"));
00293 try {
00294
00295 bool more = runSource(source.toAscii());
00296 if (!more)
00297 d->buffer.clear();
00298 return more;
00299 } catch (const Base::SystemExitException&) {
00300 d->buffer.clear();
00301 throw;
00302 } catch (...) {
00303
00304 d->buffer.clear();
00305 if (PyErr_Occurred())
00306 PyErr_Print();
00307 throw;
00308 }
00309
00310 return false;
00311 }
00312
00313 QStringList InteractiveInterpreter::getBuffer() const
00314 {
00315 return d->buffer;
00316 }
00317
00318 void InteractiveInterpreter::setBuffer(const QStringList& buf)
00319 {
00320 d->buffer = buf;
00321 }
00322
00323 void InteractiveInterpreter::clearBuffer()
00324 {
00325 d->buffer.clear();
00326 }
00327
00328
00329
00333 PythonConsole::PythonConsole(QWidget *parent)
00334 : TextEdit(parent), WindowParameter( "Editor" )
00335 {
00336 d = new PythonConsoleP();
00337 d->interactive = false;
00338
00339
00340 try {
00341 d->interpreter = new InteractiveInterpreter();
00342 } catch (const Base::Exception& e) {
00343 setPlainText(QString::fromAscii(e.what()));
00344 setEnabled(false);
00345 }
00346
00347
00348 pythonSyntax = new PythonConsoleHighlighter(this);
00349 pythonSyntax->setDocument(this->document());
00350
00351
00352 d->callTipsList = new CallTipsList(this);
00353 d->callTipsList->setFrameStyle(QFrame::Box|QFrame::Raised);
00354 d->callTipsList->setLineWidth(2);
00355 installEventFilter(d->callTipsList);
00356 viewport()->installEventFilter(d->callTipsList);
00357 d->callTipsList->setSelectionMode( QAbstractItemView::SingleSelection );
00358 d->callTipsList->hide();
00359
00360 QFont serifFont(QLatin1String("Courier"), 10, QFont::Normal);
00361 setFont(serifFont);
00362
00363
00364 ParameterGrp::handle hPrefGrp = getWindowParameter();
00365 hPrefGrp->Attach( this );
00366 hPrefGrp->NotifyAll();
00367
00368
00369 setUndoRedoEnabled( false );
00370 setAcceptDrops( true );
00371
00372
00373 Base::PyGILStateLocker lock;
00374 d->_stdoutPy = new PythonStdout(this);
00375 d->_stderrPy = new PythonStderr(this);
00376 d->_stdinPy = new PythonStdin (this);
00377 d->_stdin = PySys_GetObject("stdin");
00378 PySys_SetObject("stdin", d->_stdinPy);
00379
00380 const char* version = PyString_AsString(PySys_GetObject("version"));
00381 const char* platform = PyString_AsString(PySys_GetObject("platform"));
00382 d->output = QString::fromAscii("Python %1 on %2\n"
00383 "Type 'help', 'copyright', 'credits' or 'license' for more information.")
00384 .arg(QString::fromAscii(version)).arg(QString::fromAscii(platform));
00385 printPrompt(false);
00386 }
00387
00389 PythonConsole::~PythonConsole()
00390 {
00391 Base::PyGILStateLocker lock;
00392 getWindowParameter()->Detach( this );
00393 delete pythonSyntax;
00394 Py_XDECREF(d->_stdoutPy);
00395 Py_XDECREF(d->_stderrPy);
00396 Py_XDECREF(d->_stdinPy);
00397 delete d->interpreter;
00398 delete d;
00399 }
00400
00402 void PythonConsole::OnChange( Base::Subject<const char*> &rCaller,const char* sReason )
00403 {
00404 ParameterGrp::handle hPrefGrp = getWindowParameter();
00405
00406 if (strcmp(sReason, "FontSize") == 0 || strcmp(sReason, "Font") == 0) {
00407 int fontSize = hPrefGrp->GetInt("FontSize", 10);
00408 QString fontFamily = QString::fromAscii(hPrefGrp->GetASCII("Font", "Courier").c_str());
00409
00410 QFont font(fontFamily, fontSize);
00411 setFont(font);
00412 QFontMetrics metric(font);
00413 int width = metric.width(QLatin1String("0000"));
00414 setTabStopWidth(width);
00415 } else {
00416 QMap<QString, QColor>::ConstIterator it = d->colormap.find(QString::fromAscii(sReason));
00417 if (it != d->colormap.end()) {
00418 QColor color = it.value();
00419 unsigned long col = (color.red() << 24) | (color.green() << 16) | (color.blue() << 8);
00420 col = hPrefGrp->GetUnsigned( sReason, col);
00421 color.setRgb((col>>24)&0xff, (col>>16)&0xff, (col>>8)&0xff);
00422 pythonSyntax->setColor(QString::fromAscii(sReason), color);
00423 }
00424 }
00425 }
00426
00431 void PythonConsole::keyPressEvent(QKeyEvent * e)
00432 {
00433 if (e->modifiers() & Qt::ControlModifier) {
00434 switch( e->key() )
00435 {
00436 case Qt::Key_Up:
00437 {
00438
00439 if (!d->history.isEmpty()) {
00440 if (d->history.prev()) {
00441 QString cmd = d->history.value();
00442 overrideCursor(cmd);
00443 } return;
00444 }
00445 } break;
00446 case Qt::Key_Down:
00447 {
00448
00449 if (!d->history.isEmpty()) {
00450 if (d->history.next()) {
00451 QString cmd = d->history.value();
00452 overrideCursor(cmd);
00453 } return;
00454 }
00455 } break;
00456 default:
00457 break;
00458 }
00459 }
00460
00461 switch (e->key())
00462 {
00463
00464 case Qt::Key_Return:
00465 case Qt::Key_Enter:
00466 {
00467
00468 QTextCursor cursor = textCursor();
00469 cursor.movePosition(QTextCursor::End);
00470
00471
00472 QTextBlock block = cursor.block();
00473 QString line = block.text();
00474
00475
00476 line = line.mid(4);
00477
00478
00479 d->history.append(line);
00480
00481
00482 runSource(line);
00483 } break;
00484 case Qt::Key_Period:
00485 {
00486 QTextCursor cursor = textCursor();
00487 QTextBlock block = cursor.block();
00488 QString text = block.text();
00489 int length = cursor.position() - block.position();
00490 TextEdit::keyPressEvent(e);
00491 d->callTipsList->showTips(text.left(length));
00492 } break;
00493 case Qt::Key_Home:
00494 {
00495 if (e->modifiers() & Qt::ControlModifier) {
00496 TextEdit::keyPressEvent(e);
00497 } else {
00498 QTextCursor::MoveMode mode = e->modifiers() & Qt::ShiftModifier
00499 ? QTextCursor::KeepAnchor
00500 : QTextCursor::MoveAnchor;
00501 QTextCursor cursor = textCursor();
00502 QTextBlock block = cursor.block();
00503 QString text = block.text();
00504 int cursorPos = block.position();
00505 if (text.startsWith(QLatin1String(">>> ")) ||
00506 text.startsWith(QLatin1String("... ")))
00507 cursorPos += 4;
00508 cursor.setPosition(cursorPos, mode);
00509 setTextCursor(cursor);
00510 ensureCursorVisible();
00511 }
00512 } break;
00513 default:
00514 {
00515 TextEdit::keyPressEvent(e);
00516
00517
00518
00519 if (d->callTipsList->isVisible()) {
00520 d->callTipsList->validateCursor();
00521 }
00522 } break;
00523 }
00524 }
00525
00530 void PythonConsole::insertPythonOutput( const QString& msg )
00531 {
00532 d->output += msg;
00533 }
00534
00539 void PythonConsole::insertPythonError ( const QString& err )
00540 {
00541 d->error += err;
00542 }
00543
00547 void PythonConsole::printPrompt(bool incomplete)
00548 {
00549
00550 if (!d->output.isEmpty()) {
00551 appendOutput(d->output, (int)PythonConsoleP::Message);
00552 d->output = QString::null;
00553 }
00554
00555
00556 if (!d->error.isEmpty()) {
00557 appendOutput(d->error, (int)PythonConsoleP::Error);
00558 d->error = QString::null;
00559 }
00560
00561
00562 QTextCursor cursor = textCursor();
00563 cursor.beginEditBlock();
00564 cursor.movePosition(QTextCursor::End);
00565 QTextBlock block = cursor.block();
00566
00567
00568
00569
00570
00571 if (block.length() > 1)
00572 cursor.insertBlock(cursor.blockFormat(), cursor.charFormat());
00573 else
00574 block.setUserState(0);
00575
00576 incomplete ? cursor.insertText(QString::fromAscii("... "))
00577 : cursor.insertText(QString::fromAscii(">>> "));
00578 cursor.endEditBlock();
00579
00580
00581 cursor.movePosition(QTextCursor::End);
00582 setTextCursor(cursor);
00583 }
00584
00589 void PythonConsole::appendOutput(const QString& output, int state)
00590 {
00591 QTextCursor cursor = textCursor();
00592 cursor.movePosition(QTextCursor::End);
00593 int pos = cursor.position() + 1;
00594
00595
00596 cursor.beginEditBlock();
00597 appendPlainText(output);
00598
00599 QTextBlock block = this->document()->findBlock(pos);
00600 while (block.isValid()) {
00601 block.setUserState(state);
00602 block = block.next();
00603 }
00604 cursor.endEditBlock();
00605 }
00606
00610 void PythonConsole::runSource(const QString& line)
00611 {
00612 bool incomplete = false;
00613 Base::PyGILStateLocker lock;
00614 PyObject* default_stdout = PySys_GetObject("stdout");
00615 PyObject* default_stderr = PySys_GetObject("stderr");
00616 PySys_SetObject("stdout", d->_stdoutPy);
00617 PySys_SetObject("stderr", d->_stderrPy);
00618 d->interactive = true;
00619
00620 try {
00621
00622 incomplete = d->interpreter->push(line.toUtf8());
00623 setFocus();
00624 }
00625 catch (const Base::SystemExitException&) {
00626 ParameterGrp::handle hPrefGrp = getWindowParameter();
00627 bool check = hPrefGrp->GetBool("CheckSystemExit",true);
00628 if (!check) Base::Interpreter().systemExit();
00629 int ret = QMessageBox::question(this, tr("System exit"), tr("The application is still running.\nDo you want to exit without saving your data?"),
00630 QMessageBox::Yes, QMessageBox::No|QMessageBox::Escape|QMessageBox::Default);
00631 if (ret == QMessageBox::Yes) {
00632 Base::Interpreter().systemExit();
00633 } else {
00634 PyErr_Clear();
00635 }
00636 }
00637 catch (const Py::Exception&) {
00638 QMessageBox::critical(this, tr("Python console"), tr("Unhandled PyCXX exception."));
00639 }
00640 catch (const Base::Exception&) {
00641 QMessageBox::critical(this, tr("Python console"), tr("Unhandled FreeCAD exception."));
00642 }
00643 catch (const std::exception&) {
00644 QMessageBox::critical(this, tr("Python console"), tr("Unhandled std C++ exception."));
00645 }
00646 catch (...) {
00647 QMessageBox::critical(this, tr("Python console"), tr("Unhandled unknown C++ exception."));
00648 }
00649
00650 PySys_SetObject("stdout", default_stdout);
00651 PySys_SetObject("stderr", default_stderr);
00652 printPrompt(incomplete);
00653 d->interactive = false;
00654 for (QStringList::Iterator it = d->statements.begin(); it != d->statements.end(); ++it)
00655 printStatement(*it);
00656 d->statements.clear();
00657 }
00658
00659 bool PythonConsole::isComment(const QString& source) const
00660 {
00661 if (source.isEmpty())
00662 return false;
00663 int i=0;
00664 while (i < source.length()) {
00665 QChar ch = source.at(i++);
00666 if (ch.isSpace())
00667 continue;
00668 if (ch == QLatin1Char('#'))
00669 return true;
00670 }
00671
00672 return false;
00673 }
00674
00679 void PythonConsole::printStatement( const QString& cmd )
00680 {
00681
00682
00683 if (d->interactive) {
00684 d->statements << cmd;
00685 return;
00686 }
00687
00688 QTextCursor cursor = textCursor();
00689 QStringList statements = cmd.split(QLatin1String("\n"));
00690 for (QStringList::Iterator it = statements.begin(); it != statements.end(); ++it) {
00691
00692 cursor.movePosition(QTextCursor::End);
00693 cursor.insertText( *it );
00694 d->history.append( *it );
00695 printPrompt(false);
00696 }
00697 }
00698
00702 void PythonConsole::showEvent (QShowEvent * e)
00703 {
00704 TextEdit::showEvent(e);
00705
00706 setFocus();
00707 }
00708
00709 void PythonConsole::visibilityChanged (bool visible)
00710 {
00711 if (visible)
00712 setFocus();
00713 }
00714
00715 void PythonConsole::changeEvent(QEvent *e)
00716 {
00717 if (e->type() == QEvent::ParentChange) {
00718 QDockWidget* dw = qobject_cast<QDockWidget*>(this->parentWidget());
00719 if (dw) {
00720 connect(dw, SIGNAL(visibilityChanged(bool)),
00721 this, SLOT(visibilityChanged(bool)));
00722 }
00723 }
00724 TextEdit::changeEvent(e);
00725 }
00726
00730 void PythonConsole::dropEvent (QDropEvent * e)
00731 {
00732 const QMimeData* mimeData = e->mimeData();
00733 if (mimeData->hasFormat(QLatin1String("text/x-action-items"))) {
00734 QByteArray itemData = mimeData->data(QLatin1String("text/x-action-items"));
00735 QDataStream dataStream(&itemData, QIODevice::ReadOnly);
00736
00737 int ctActions; dataStream >> ctActions;
00738 for (int i=0; i<ctActions; i++) {
00739 QString action;
00740 dataStream >> action;
00741 printStatement(QString::fromAscii("Gui.runCommand(\"%1\")").arg(action));
00742 }
00743
00744 e->setDropAction(Qt::CopyAction);
00745 e->accept();
00746 }
00747 else
00748 QPlainTextEdit::dropEvent(e);
00749 }
00750
00752 void PythonConsole::dragMoveEvent( QDragMoveEvent *e )
00753 {
00754 const QMimeData* mimeData = e->mimeData();
00755 if (mimeData->hasFormat(QLatin1String("text/x-action-items")))
00756 e->accept();
00757 else
00758 QPlainTextEdit::dragMoveEvent(e);
00759 }
00760
00762 void PythonConsole::dragEnterEvent (QDragEnterEvent * e)
00763 {
00764 const QMimeData* mimeData = e->mimeData();
00765 if (mimeData->hasFormat(QLatin1String("text/x-action-items")))
00766 e->accept();
00767 else
00768 QPlainTextEdit::dragEnterEvent(e);
00769 }
00770
00771 bool PythonConsole::canInsertFromMimeData (const QMimeData * source) const
00772 {
00773 if (source->hasText())
00774 return true;
00775 if (source->hasUrls()) {
00776 QList<QUrl> uri = source->urls();
00777 for (QList<QUrl>::ConstIterator it = uri.begin(); it != uri.end(); ++it) {
00778 QFileInfo info((*it).toLocalFile());
00779 if (info.exists() && info.isFile()) {
00780 QString ext = info.suffix().toLower();
00781 if (ext == QLatin1String("py") || ext == QLatin1String("fcmacro"))
00782 return true;
00783 }
00784 }
00785 }
00786
00787 return false;
00788 }
00789
00793 void PythonConsole::insertFromMimeData (const QMimeData * source)
00794 {
00795 if (!source)
00796 return;
00797
00798
00799 if (source->hasUrls()) {
00800 QList<QUrl> uri = source->urls();
00801 for (QList<QUrl>::ConstIterator it = uri.begin(); it != uri.end(); ++it) {
00802
00803 QFileInfo info((*it).toLocalFile());
00804 QString ext = info.suffix().toLower();
00805 if (info.exists() && info.isFile() &&
00806 (ext == QLatin1String("py") || ext == QLatin1String("fcmacro"))) {
00807
00808 QFile file(info.absoluteFilePath());
00809 if (file.open(QIODevice::ReadOnly)) {
00810 QTextStream str(&file);
00811 runSourceFromMimeData(str.readAll());
00812 }
00813 file.close();
00814 }
00815 }
00816
00817 return;
00818 }
00819 if (source->hasText()) {
00820 runSourceFromMimeData(source->text());
00821 return;
00822 }
00823 }
00824
00825 QMimeData * PythonConsole::createMimeDataFromSelection () const
00826 {
00827 QMimeData* mime = new QMimeData();
00828
00829 switch (d->type) {
00830 case PythonConsoleP::Normal:
00831 {
00832 const QTextDocumentFragment fragment(textCursor());
00833 mime->setText(fragment.toPlainText());
00834 } break;
00835 case PythonConsoleP::Command:
00836 {
00837 QTextCursor cursor = textCursor();
00838 int s = cursor.selectionStart();
00839 int e = cursor.selectionEnd();
00840 QTextBlock b;
00841 QStringList lines;
00842 for (b = document()->begin(); b.isValid(); b = b.next()) {
00843 int pos = b.position();
00844 if ( pos >= s && pos <= e ) {
00845 if (b.userState() > -1 && b.userState() < pythonSyntax->maximumUserState()) {
00846 QString line = b.text();
00847
00848 line = line.mid(4);
00849 lines << line;
00850 }
00851 }
00852 }
00853
00854 QString text = lines.join(QLatin1String("\n"));
00855 mime->setText(text);
00856 } break;
00857 case PythonConsoleP::History:
00858 {
00859 const QStringList& hist = d->history.values();
00860 QString text = hist.join(QLatin1String("\n"));
00861 mime->setText(text);
00862 } break;
00863 }
00864
00865 return mime;
00866 }
00867
00868 void PythonConsole::runSourceFromMimeData(const QString& source)
00869 {
00870
00871
00872
00873
00874
00875
00876
00877
00878 QString text = source;
00879 if (text.isNull())
00880 return;
00881
00882 #if defined (Q_OS_LINUX)
00883
00884 text.replace(QLatin1String("\r\n"), QLatin1String("\n"));
00885 #elif defined(Q_OS_WIN32)
00886
00887 text.replace(QLatin1String("\r\n"), QLatin1String("\n"));
00888 #elif defined(Q_OS_MAC)
00889
00890 text.replace(QLatin1Char('\r'), QLatin1Char('\n'));
00891 #endif
00892
00893
00894 QStringList lines = text.split(QLatin1Char('\n'));
00895 QString last = lines.back();
00896 lines.pop_back();
00897
00898 QTextCursor cursor = textCursor();
00899 QStringList buffer = d->interpreter->getBuffer();
00900 d->interpreter->clearBuffer();
00901
00902 int countNewlines = lines.count(), i = 0;
00903 for (QStringList::Iterator it = lines.begin(); it != lines.end(); ++it, ++i) {
00904 QString line = *it;
00905
00906
00907 cursor.insertText(*it);
00908
00909
00910
00911 if (i == 0) {
00912
00913
00914 cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
00915 QString select = cursor.selectedText();
00916 cursor.removeSelectedText();
00917 last = last + select;
00918 line = cursor.block().text();
00919 line = line.mid(4);
00920 }
00921
00922
00923 d->history.append(line);
00924
00925 buffer.append(line);
00926 int ret = d->interpreter->compileCommand(buffer.join(QLatin1String("\n")).toUtf8());
00927 if (ret == 1) {
00928 printPrompt(true);
00929 }
00930 else if (ret == 0) {
00931
00932 int k=i+1;
00933 QString nextline;
00934 while ((nextline.isEmpty() || isComment(nextline)) && k < countNewlines) {
00935 nextline = lines[k];
00936 k++;
00937 }
00938
00939 int ret = d->interpreter->compileCommand(nextline.toUtf8());
00940
00941
00942
00943 if (ret == -1) {
00944
00945 printPrompt(true);
00946 }
00947 else {
00948 runSource(buffer.join(QLatin1String("\n")));
00949 buffer.clear();
00950 }
00951 }
00952 else {
00953 runSource(buffer.join(QLatin1String("\n")));
00954 ensureCursorVisible();
00955 return;
00956 }
00957 }
00958
00959
00960 d->interpreter->setBuffer(buffer);
00961 cursor.insertText(last);
00962 ensureCursorVisible();
00963 }
00964
00968 void PythonConsole::overrideCursor(const QString& txt)
00969 {
00970
00971 QTextCursor cursor = textCursor();
00972 QTextBlock block = cursor.block();
00973 cursor.movePosition(QTextCursor::End);
00974 cursor.movePosition(QTextCursor::StartOfLine);
00975 cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 4);
00976 cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, block.text().length());
00977 cursor.removeSelectedText();
00978 cursor.insertText(txt);
00979
00980 cursor.movePosition(QTextCursor::End);
00981 setTextCursor(cursor);
00982 }
00983
00984 void PythonConsole::contextMenuEvent ( QContextMenuEvent * e )
00985 {
00986 QMenu menu(this);
00987 QAction *a;
00988
00989 a = menu.addAction(tr("&Copy"), this, SLOT(copy()), Qt::CTRL+Qt::Key_C);
00990 a->setEnabled(textCursor().hasSelection());
00991
00992 a = menu.addAction(tr("&Copy command"), this, SLOT(onCopyCommand()));
00993 a->setEnabled(textCursor().hasSelection());
00994
00995 a = menu.addAction(tr("&Copy history"), this, SLOT(onCopyHistory()));
00996 a->setEnabled(!d->history.isEmpty());
00997
00998 a = menu.addAction( tr("Save history as..."), this, SLOT(onSaveHistoryAs()));
00999 a->setEnabled(!d->history.isEmpty());
01000
01001 menu.addSeparator();
01002 a = menu.addAction(tr("&Paste"), this, SLOT(paste()), Qt::CTRL+Qt::Key_V);
01003 const QMimeData *md = QApplication::clipboard()->mimeData();
01004 a->setEnabled(md && canInsertFromMimeData(md));
01005
01006 a = menu.addAction(tr("Select All"), this, SLOT(selectAll()), Qt::CTRL+Qt::Key_A);
01007 a->setEnabled(!document()->isEmpty());
01008
01009 menu.addSeparator();
01010 menu.addAction( tr("Insert file name..."), this, SLOT(onInsertFileName()));
01011 menu.addSeparator();
01012
01013 QAction* wrap = menu.addAction(tr("Word wrap"));
01014 wrap->setCheckable(true);
01015 wrap->setChecked(this->wordWrapMode() != QTextOption::NoWrap);
01016
01017 QAction* exec = menu.exec(e->globalPos());
01018 if (exec == wrap) {
01019 this->setWordWrapMode(wrap->isChecked()
01020 ? QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap);
01021 }
01022 }
01023
01024 void PythonConsole::onSaveHistoryAs()
01025 {
01026 QString cMacroPath = QString::fromUtf8(getDefaultParameter()->GetGroup( "Macro" )->
01027 GetASCII("MacroPath",App::Application::getUserAppDataDir().c_str()).c_str());
01028 QString fn = FileDialog::getSaveFileName(this, tr("Save History"), cMacroPath,
01029 tr("Macro Files (*.FCMacro *.py)"));
01030 if (!fn.isEmpty()) {
01031 int dot = fn.indexOf(QLatin1Char('.'));
01032 if (dot != -1) {
01033 QFile f(fn);
01034 if (f.open(QIODevice::WriteOnly)) {
01035 QTextStream t (&f);
01036 const QStringList& hist = d->history.values();
01037 for (QStringList::ConstIterator it = hist.begin(); it != hist.end(); ++it)
01038 t << *it << "\n";
01039 f.close();
01040 }
01041 }
01042 }
01043 }
01044
01045 void PythonConsole::onInsertFileName()
01046 {
01047 QString fn = Gui::FileDialog::getOpenFileName(Gui::getMainWindow(), tr("Insert file name"), QString::null, tr("All Files (*.*)") );
01048 if ( fn.isEmpty() )
01049 return;
01050 insertPlainText(fn);
01051 }
01052
01056 void PythonConsole::onCopyHistory()
01057 {
01058 if (d->history.isEmpty())
01059 return;
01060 d->type = PythonConsoleP::History;
01061 QMimeData *data = createMimeDataFromSelection();
01062 QApplication::clipboard()->setMimeData(data);
01063 d->type = PythonConsoleP::Normal;
01064 }
01065
01069 void PythonConsole::onCopyCommand()
01070 {
01071 d->type = PythonConsoleP::Command;
01072 copy();
01073 d->type = PythonConsoleP::Normal;
01074 }
01075
01076
01077
01078 PythonConsoleHighlighter::PythonConsoleHighlighter(QObject* parent)
01079 : PythonSyntaxHighlighter(parent)
01080 {
01081 }
01082
01083 PythonConsoleHighlighter::~PythonConsoleHighlighter()
01084 {
01085 }
01086
01087 void PythonConsoleHighlighter::highlightBlock(const QString& text)
01088 {
01089 const int ErrorOutput = (int)PythonConsoleP::Error;
01090 const int MessageOutput = (int)PythonConsoleP::Message;
01091
01092
01093 int stateOfPara = currentBlockState();
01094
01095 switch (stateOfPara)
01096 {
01097 case ErrorOutput:
01098 {
01099
01100 QTextCharFormat errorFormat;
01101 errorFormat.setForeground(color(QLatin1String("Python error")));
01102 errorFormat.setFontItalic(true);
01103 setFormat( 0, text.length(), errorFormat);
01104 } break;
01105 case MessageOutput:
01106 {
01107
01108 QTextCharFormat outputFormat;
01109 outputFormat.setForeground(color(QLatin1String("Python output")));
01110 setFormat( 0, text.length(), outputFormat);
01111 } break;
01112 default:
01113 {
01114 PythonSyntaxHighlighter::highlightBlock(text);
01115 } break;
01116 }
01117 }
01118
01119 void PythonConsoleHighlighter::colorChanged(const QString& type, const QColor& col)
01120 {
01121 }
01122
01123
01124
01125 ConsoleHistory::ConsoleHistory()
01126 {
01127 it = _history.end();
01128 }
01129
01130 ConsoleHistory::~ConsoleHistory()
01131 {
01132 }
01133
01134 void ConsoleHistory::first()
01135 {
01136 it = _history.begin();
01137 }
01138
01139 bool ConsoleHistory::more()
01140 {
01141 return (it != _history.end());
01142 }
01143
01144 bool ConsoleHistory::next()
01145 {
01146 if (it != _history.end()) {
01147 for (++it; it != _history.end(); ++it) {
01148 if (!it->isEmpty())
01149 break;
01150 }
01151 return true;
01152 }
01153
01154 return false;
01155 }
01156
01157 bool ConsoleHistory::prev()
01158 {
01159 if (it != _history.begin()) {
01160 for (--it; it != _history.begin(); --it) {
01161 if (!it->isEmpty())
01162 break;
01163 }
01164 return true;
01165 }
01166
01167 return false;
01168 }
01169
01170 bool ConsoleHistory::isEmpty() const
01171 {
01172 return _history.isEmpty();
01173 }
01174
01175 QString ConsoleHistory::value() const
01176 {
01177 if ( it != _history.end() )
01178 return *it;
01179 else
01180 return QString::null;
01181 }
01182
01183 void ConsoleHistory::append( const QString& item )
01184 {
01185 _history.append( item );
01186 it = _history.end();
01187 }
01188
01189 const QStringList& ConsoleHistory::values() const
01190 {
01191 return this->_history;
01192 }
01193
01194
01195
01196
01197
01198 PythonInputField::PythonInputField(QWidget* parent)
01199 : QWidget(parent)
01200 {
01201 QGridLayout* gridLayout = new QGridLayout(this);
01202 gridLayout->setSpacing(6);
01203 gridLayout->setMargin(9);
01204
01205 editField = new PythonEditor(this);
01206 gridLayout->addWidget(editField, 0, 0, 1, 1);
01207
01208 QHBoxLayout* hboxLayout = new QHBoxLayout();
01209 hboxLayout->setSpacing(6);
01210 hboxLayout->setMargin(0);
01211
01212 QSpacerItem* spacerItem = new QSpacerItem(131, 31, QSizePolicy::Expanding, QSizePolicy::Minimum);
01213 hboxLayout->addItem(spacerItem);
01214
01215 okButton = new QPushButton(this);
01216 hboxLayout->addWidget(okButton);
01217 clearButton = new QPushButton(this);
01218 hboxLayout->addWidget(clearButton);
01219 gridLayout->addLayout(hboxLayout, 1, 0, 1, 1);
01220
01221
01222 this->setWindowTitle(Gui::PythonConsole::tr("Python Input Dialog"));
01223 okButton->setText(tr("OK"));
01224 clearButton->setText(tr("Clear"));
01225
01226 QObject::connect(okButton, SIGNAL(clicked()), this, SIGNAL(textEntered()));
01227 QObject::connect(clearButton, SIGNAL(clicked()), editField, SLOT(clear()));
01228 }
01229
01230 PythonInputField::~PythonInputField()
01231 {
01232 }
01233
01234 QString PythonInputField::getText() const
01235 {
01236 return editField->toPlainText();
01237 }
01238
01239 void PythonInputField::clear()
01240 {
01241 return editField->clear();
01242 }
01243
01244 void PythonInputField::changeEvent(QEvent *e)
01245 {
01246 if (e->type() == QEvent::LanguageChange) {
01247 this->setWindowTitle(Gui::PythonConsole::tr("Python Input Dialog"));
01248 okButton->setText(tr("OK"));
01249 clearButton->setText(tr("Clear"));
01250 }
01251 else {
01252 QWidget::changeEvent(e);
01253 }
01254 }
01255
01256 void PythonInputField::showEvent(QShowEvent* e)
01257 {
01258 editField->setFocus();
01259 }
01260
01261 #include "moc_PythonConsole.cpp"