summaryrefslogtreecommitdiffstats
path: root/src/scripttools/debugging/qscriptdebugger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/scripttools/debugging/qscriptdebugger.cpp')
-rw-r--r--src/scripttools/debugging/qscriptdebugger.cpp1828
1 files changed, 1828 insertions, 0 deletions
diff --git a/src/scripttools/debugging/qscriptdebugger.cpp b/src/scripttools/debugging/qscriptdebugger.cpp
new file mode 100644
index 0000000..5a356ae
--- /dev/null
+++ b/src/scripttools/debugging/qscriptdebugger.cpp
@@ -0,0 +1,1828 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtSCriptTools module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscriptdebugger_p.h"
+#include "qscriptdebuggerconsole_p.h"
+#include "qscriptdebuggerconsolecommandmanager_p.h"
+#include "qscriptdebuggerconsolecommandjob_p.h"
+#include "qscriptstdmessagehandler_p.h"
+#include "qscriptdebuggerfrontend_p.h"
+#include "qscriptdebuggereventhandlerinterface_p.h"
+#include "qscriptdebuggerresponsehandlerinterface_p.h"
+#include "qscriptdebuggerjobschedulerinterface_p.h"
+#include "qscriptdebuggerconsolewidgetinterface_p.h"
+#include "qscriptcompletionproviderinterface_p.h"
+#include "qscriptcompletiontask_p.h"
+#include "qscripttooltipproviderinterface_p.h"
+#include "qscriptdebuggerstackwidgetinterface_p.h"
+#include "qscriptdebuggerstackmodel_p.h"
+#include "qscriptdebuggerscriptswidgetinterface_p.h"
+#include "qscriptdebuggerscriptsmodel_p.h"
+#include "qscriptdebuggerlocalswidgetinterface_p.h"
+#include "qscriptdebuggerlocalsmodel_p.h"
+#include "qscriptdebuggercodewidgetinterface_p.h"
+#include "qscriptdebuggercodeviewinterface_p.h"
+#include "qscriptdebuggercodefinderwidgetinterface_p.h"
+#include "qscriptbreakpointswidgetinterface_p.h"
+#include "qscriptbreakpointsmodel_p.h"
+#include "qscriptdebugoutputwidgetinterface_p.h"
+#include "qscripterrorlogwidgetinterface_p.h"
+#include "qscriptdebuggerwidgetfactoryinterface_p.h"
+#include "qscriptdebuggerevent_p.h"
+#include "qscriptdebuggervalue_p.h"
+#include "qscriptdebuggerresponse_p.h"
+#include "qscriptdebuggercommand_p.h"
+#include "qscriptdebuggercommandschedulerfrontend_p.h"
+#include "qscriptdebuggercommandschedulerjob_p.h"
+#include "qscriptdebuggerjob_p_p.h"
+#include "qscriptxmlparser_p.h"
+
+#include "qscriptenginedebuggerfrontend_p.h"
+#include "qscriptdebuggerbackend_p.h"
+#include <QtScript/qscriptengine.h>
+
+#include "private/qobject_p.h"
+
+#include <QtScript/qscriptcontext.h>
+#include <QtScript/qscriptcontextinfo.h>
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qdebug.h>
+
+#include <QtGui/qaction.h>
+#include <QtGui/qevent.h>
+#include <QtGui/qicon.h>
+#include <QtGui/qinputdialog.h>
+
+QT_BEGIN_NAMESPACE
+typedef QPair<QList<qint64>, QList<qint64> > QScriptScriptsDelta;
+typedef QPair<QList<qint64>, QList<qint64> > QScriptContextsDelta;
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QScriptScriptsDelta)
+
+QT_BEGIN_NAMESPACE
+
+Q_SCRIPT_EXPORT QString qt_scriptToXml(const QString &program, int lineNumber = 1);
+
+namespace {
+
+static int scriptDebuggerCount = 0;
+static bool eventCallbackRegistered = false;
+static bool widgetInPaintEvent = false;
+
+static bool scriptDebuggerEventCallback(void **data)
+{
+ QEvent *event = reinterpret_cast<QEvent*>(data[1]);
+ if (event->type() == QEvent::Paint) {
+ QObject *receiver = reinterpret_cast<QObject*>(data[0]);
+ bool was = widgetInPaintEvent;
+ widgetInPaintEvent = true;
+ QCoreApplication::instance()->notify(receiver, event);
+ widgetInPaintEvent = was;
+ bool *result = reinterpret_cast<bool*>(data[2]);
+ *result = true;
+ return true;
+ }
+ return false;
+}
+
+}
+
+/*!
+ \since 4.5
+ \class QScriptDebugger
+ \internal
+
+ \brief The QScriptDebugger class provides a Qt Script debugger.
+
+ \ingroup scripttools
+ \mainclass
+*/
+
+class QScriptDebuggerPrivate
+ : public QObjectPrivate,
+ public QScriptDebuggerCommandSchedulerInterface,
+ public QScriptDebuggerJobSchedulerInterface,
+ public QScriptDebuggerEventHandlerInterface,
+ public QScriptDebuggerResponseHandlerInterface,
+ public QScriptCompletionProviderInterface,
+ public QScriptToolTipProviderInterface
+{
+ Q_DECLARE_PUBLIC(QScriptDebugger)
+public:
+ QScriptDebuggerPrivate();
+ ~QScriptDebuggerPrivate();
+
+ int scheduleJob(QScriptDebuggerJob *job);
+ void finishJob(QScriptDebuggerJob *job);
+ void hibernateUntilEvaluateFinished(QScriptDebuggerJob *job);
+
+ void maybeStartNewJob();
+
+ int scheduleCommand(const QScriptDebuggerCommand &command,
+ QScriptDebuggerResponseHandlerInterface *responseHandler);
+
+ void handleResponse(
+ const QScriptDebuggerResponse &response, int commandId);
+ bool debuggerEvent(const QScriptDebuggerEvent &event);
+
+ QScriptCompletionTaskInterface *createCompletionTask(
+ const QString &contents, int cursorPosition, int frameIndex, int options);
+
+ QString toolTip(int frameIndex, int lineNumber,
+ const QStringList &path);
+
+ static QPixmap pixmap(const QString &path);
+
+ void startInteraction(QScriptDebuggerEvent::Type type,
+ qint64 scriptId, int lineNumber);
+ void sync();
+ void loadLocals(int frameIndex);
+ QScriptDebuggerLocalsModel *createLocalsModel();
+ void selectScriptForFrame(int frameIndex);
+ void emitStoppedSignal();
+
+ void maybeDelete(QWidget *widget);
+
+ // private slots
+ void _q_onLineEntered(const QString &contents);
+ void _q_onCurrentFrameChanged(int frameIndex);
+ void _q_onCurrentScriptChanged(qint64 scriptId);
+ void _q_onScriptLocationSelected(int lineNumber);
+ void _q_interrupt();
+ void _q_continue();
+ void _q_stepInto();
+ void _q_stepOver();
+ void _q_stepOut();
+ void _q_runToCursor();
+ void _q_runToNewScript();
+ void _q_toggleBreakpoint();
+ void _q_clearDebugOutput();
+ void _q_clearErrorLog();
+ void _q_clearConsole();
+ void _q_findInScript();
+ void _q_findNextInScript();
+ void _q_findPreviousInScript();
+ void _q_onFindCodeRequest(const QString &, int);
+ void _q_goToLine();
+
+ void executeConsoleCommand(const QString &command);
+ void findCode(const QString &exp, int options);
+
+ QScriptDebuggerFrontend *frontend;
+
+ bool interactive;
+ QScriptDebuggerConsole *console;
+
+ int nextJobId;
+ QList<QScriptDebuggerJob*> pendingJobs;
+ QList<int> pendingJobIds;
+ QScriptDebuggerJob *activeJob;
+ bool activeJobHibernating;
+ QHash<int, QScriptDebuggerCommand> watchedCommands;
+ QHash<int, QScriptDebuggerResponseHandlerInterface*> responseHandlers;
+
+ QScriptDebuggerConsoleWidgetInterface *consoleWidget;
+ QScriptDebuggerStackWidgetInterface *stackWidget;
+ QScriptDebuggerStackModel *stackModel;
+ QScriptDebuggerScriptsWidgetInterface *scriptsWidget;
+ QScriptDebuggerScriptsModel *scriptsModel;
+ QScriptDebuggerLocalsWidgetInterface *localsWidget;
+ QHash<int, QScriptDebuggerLocalsModel*> localsModels;
+ QScriptDebuggerCodeWidgetInterface *codeWidget;
+ QScriptDebuggerCodeFinderWidgetInterface *codeFinderWidget;
+ QScriptBreakpointsWidgetInterface *breakpointsWidget;
+ QScriptBreakpointsModel *breakpointsModel;
+ QScriptDebugOutputWidgetInterface *debugOutputWidget;
+ QScriptErrorLogWidgetInterface *errorLogWidget;
+ QScriptDebuggerWidgetFactoryInterface *widgetFactory;
+
+ QAction *interruptAction;
+ QAction *continueAction;
+ QAction *stepIntoAction;
+ QAction *stepOverAction;
+ QAction *stepOutAction;
+ QAction *runToCursorAction;
+ QAction *runToNewScriptAction;
+
+ QAction *toggleBreakpointAction;
+
+ QAction *clearDebugOutputAction;
+ QAction *clearErrorLogAction;
+ QAction *clearConsoleAction;
+
+ QAction *findInScriptAction;
+ QAction *findNextInScriptAction;
+ QAction *findPreviousInScriptAction;
+ QAction *goToLineAction;
+
+ int updatesEnabledTimerId;
+};
+
+QScriptDebuggerPrivate::QScriptDebuggerPrivate()
+{
+ frontend = 0;
+ activeJob = 0;
+ activeJobHibernating = false;
+ nextJobId = 0;
+ interactive = false;
+
+ console = new QScriptDebuggerConsole();
+ QString scriptsPath = QLatin1String(":/qt/scripttools/debugging/scripts/commands");
+ QScriptStdMessageHandler tmp;
+ console->loadScriptedCommands(scriptsPath, &tmp);
+
+ consoleWidget = 0;
+ stackWidget = 0;
+ stackModel = 0;
+ scriptsWidget = 0;
+ scriptsModel = 0;
+ localsWidget = 0;
+ codeWidget = 0;
+ codeFinderWidget = 0;
+ breakpointsWidget = 0;
+ breakpointsModel = 0;
+ debugOutputWidget = 0;
+ errorLogWidget = 0;
+ widgetFactory = 0;
+
+ interruptAction = 0;
+ continueAction = 0;
+ stepIntoAction = 0;
+ stepOverAction = 0;
+ stepOutAction = 0;
+ runToCursorAction = 0;
+ runToNewScriptAction = 0;
+
+ toggleBreakpointAction = 0;
+
+ clearErrorLogAction = 0;
+ clearDebugOutputAction = 0;
+ clearConsoleAction = 0;
+
+ findInScriptAction = 0;
+ findNextInScriptAction = 0;
+ findPreviousInScriptAction = 0;
+ goToLineAction = 0;
+
+ updatesEnabledTimerId = -1;
+}
+
+QScriptDebuggerPrivate::~QScriptDebuggerPrivate()
+{
+ delete console;
+ qDeleteAll(pendingJobs);
+ delete activeJob;
+ maybeDelete(consoleWidget);
+ maybeDelete(stackWidget);
+ maybeDelete(scriptsWidget);
+ maybeDelete(localsWidget);
+ maybeDelete(codeWidget);
+ maybeDelete(codeFinderWidget);
+ maybeDelete(breakpointsWidget);
+ maybeDelete(debugOutputWidget);
+ maybeDelete(errorLogWidget);
+}
+
+void QScriptDebuggerPrivate::maybeDelete(QWidget *widget)
+{
+ if (widget && !widget->parent())
+ delete widget;
+}
+
+QPixmap QScriptDebuggerPrivate::pixmap(const QString &path)
+{
+ static QString prefix = QString::fromLatin1(":/qt/scripttools/debugging/images/");
+ return QPixmap(prefix + path);
+}
+
+/*!
+ \reimp
+*/
+int QScriptDebuggerPrivate::scheduleJob(QScriptDebuggerJob *job)
+{
+ QScriptDebuggerJobPrivate *priv = QScriptDebuggerJobPrivate::get(job);
+ Q_ASSERT(priv->jobScheduler == 0);
+ priv->jobScheduler = this;
+ int id = nextJobId;
+ pendingJobs.append(job);
+ pendingJobIds.append(id);
+ maybeStartNewJob();
+ return id;
+}
+
+/*!
+ \reimp
+*/
+void QScriptDebuggerPrivate::finishJob(QScriptDebuggerJob *job)
+{
+ Q_UNUSED(job);
+ Q_ASSERT(activeJob == job);
+ delete activeJob;
+ activeJob = 0;
+ activeJobHibernating = false;
+ maybeStartNewJob();
+}
+
+/*!
+ \reimp
+*/
+void QScriptDebuggerPrivate::hibernateUntilEvaluateFinished(QScriptDebuggerJob *job)
+{
+ Q_UNUSED(job);
+ Q_ASSERT(activeJob == job);
+ activeJobHibernating = true;
+}
+
+/*!
+ Starts a new job if appropriate.
+*/
+void QScriptDebuggerPrivate::maybeStartNewJob()
+{
+ if (activeJob || pendingJobs.isEmpty())
+ return;
+ activeJob = pendingJobs.takeFirst();
+ activeJob->start();
+}
+
+/*!
+ \reimp
+*/
+int QScriptDebuggerPrivate::scheduleCommand(
+ const QScriptDebuggerCommand &command,
+ QScriptDebuggerResponseHandlerInterface *responseHandler)
+{
+ if (!frontend)
+ return -1;
+ int id = frontend->scheduleCommand(command, this);
+ if (responseHandler && (responseHandler != this))
+ responseHandlers.insert(id, responseHandler);
+ if ((command.type() == QScriptDebuggerCommand::SetBreakpoint)
+ || (command.type() == QScriptDebuggerCommand::SetBreakpointData)
+ || (command.type() == QScriptDebuggerCommand::DeleteBreakpoint)) {
+ // need to watch this command and update the breakpoints model afterwards
+ watchedCommands.insert(id, command);
+ }
+ return id;
+}
+
+/*!
+ \reimp
+*/
+void QScriptDebuggerPrivate::handleResponse(
+ const QScriptDebuggerResponse &response, int commandId)
+{
+ Q_Q(QScriptDebugger);
+ if (watchedCommands.contains(commandId)) {
+ QScriptDebuggerCommand command = watchedCommands.take(commandId);
+ if (response.error() == QScriptDebuggerResponse::NoError) {
+ if (!breakpointsModel)
+ breakpointsModel = new QScriptBreakpointsModel(this, this, q);
+ switch (command.type()) {
+ case QScriptDebuggerCommand::SetBreakpoint: {
+ int breakpointId = response.resultAsInt();
+ QScriptBreakpointData data = command.breakpointData();
+ breakpointsModel->addBreakpoint(breakpointId, data);
+ } break;
+ case QScriptDebuggerCommand::SetBreakpointData: {
+ int breakpointId = command.breakpointId();
+ QScriptBreakpointData data = command.breakpointData();
+ breakpointsModel->modifyBreakpoint(breakpointId, data);
+ } break;
+ case QScriptDebuggerCommand::DeleteBreakpoint: {
+ int breakpointId = command.breakpointId();
+ breakpointsModel->removeBreakpoint(breakpointId);
+ } break;
+ default:
+ Q_ASSERT(false);
+ }
+ }
+ } else if (response.async()) {
+ interactive = false;
+ // disable/invalidate/enable stuff
+ if (continueAction)
+ continueAction->setEnabled(false);
+ if (stepIntoAction)
+ stepIntoAction->setEnabled(false);
+ if (stepOverAction)
+ stepOverAction->setEnabled(false);
+ if (stepOutAction)
+ stepOutAction->setEnabled(false);
+ if (runToCursorAction)
+ runToCursorAction->setEnabled(false);
+ if (runToNewScriptAction)
+ runToNewScriptAction->setEnabled(false);
+ if (interruptAction)
+ interruptAction->setEnabled(true);
+
+ // the timer is to avoid flicker when stepping
+ if (stackWidget) {
+ stackWidget->setUpdatesEnabled(false);
+ stackWidget->setEnabled(false);
+ if (updatesEnabledTimerId == -1)
+ updatesEnabledTimerId = q->startTimer(75);
+ }
+ if (localsWidget) {
+ localsWidget->setUpdatesEnabled(false);
+ localsWidget->setEnabled(false);
+ if (updatesEnabledTimerId == -1)
+ updatesEnabledTimerId = q->startTimer(75);
+ }
+ if (codeWidget)
+ codeWidget->invalidateExecutionLineNumbers();
+
+ emit q->started();
+ }
+
+ QScriptDebuggerResponseHandlerInterface *realHandler = responseHandlers.take(commandId);
+ if (realHandler)
+ realHandler->handleResponse(response, commandId);
+}
+
+/*!
+ \reimp
+
+ Handles a debugger event from the frontend.
+*/
+bool QScriptDebuggerPrivate::debuggerEvent(const QScriptDebuggerEvent &event)
+{
+ Q_Q(QScriptDebugger);
+ switch (event.type()) {
+ case QScriptDebuggerEvent::None:
+ case QScriptDebuggerEvent::UserEvent:
+ case QScriptDebuggerEvent::MaxUserEvent:
+ Q_ASSERT(false);
+ break;
+
+ case QScriptDebuggerEvent::Trace:
+ if (!debugOutputWidget && widgetFactory)
+ q->setDebugOutputWidget(widgetFactory->createDebugOutputWidget());
+ if (debugOutputWidget)
+ debugOutputWidget->message(QtDebugMsg, event.message());
+ return true; // trace doesn't stall execution
+
+ case QScriptDebuggerEvent::SteppingFinished: {
+ if (!consoleWidget && widgetFactory)
+ q->setConsoleWidget(widgetFactory->createConsoleWidget());
+ if (consoleWidget) {
+ QString msg = event.message();
+ if (!msg.isEmpty())
+ consoleWidget->message(QtDebugMsg, msg);
+ }
+ } break;
+
+ case QScriptDebuggerEvent::Interrupted:
+ case QScriptDebuggerEvent::LocationReached:
+ break;
+
+ case QScriptDebuggerEvent::Breakpoint: {
+ int bpId = event.breakpointId();
+ if (!consoleWidget && widgetFactory)
+ q->setConsoleWidget(widgetFactory->createConsoleWidget());
+ if (consoleWidget) {
+ consoleWidget->message(QtDebugMsg,
+ QString::fromLatin1("Breakpoint %0 at %1, line %2.")
+ .arg(bpId).arg(event.fileName())
+ .arg(event.lineNumber()));
+ }
+ if (breakpointsModel->breakpointData(bpId).isSingleShot())
+ breakpointsModel->removeBreakpoint(bpId);
+ } break;
+
+ case QScriptDebuggerEvent::Exception: {
+ if (event.hasExceptionHandler()) {
+ // Let the exception be handled like normal.
+ // We may want to add a "Break on all exceptions" option
+ // to be able to customize this behavior.
+ return true;
+ }
+ if (!consoleWidget && widgetFactory)
+ q->setConsoleWidget(widgetFactory->createConsoleWidget());
+ if (!errorLogWidget && widgetFactory)
+ q->setErrorLogWidget(widgetFactory->createErrorLogWidget());
+ if (consoleWidget || errorLogWidget) {
+ QString fn = event.fileName();
+ if (fn.isEmpty()) {
+ if (event.scriptId() != -1)
+ fn = QString::fromLatin1("<anonymous script, id=%0>").arg(event.scriptId());
+ else
+ fn = QString::fromLatin1("<native>");
+ }
+ QString msg = QString::fromLatin1("Uncaught exception at %0:%1: %2").arg(fn)
+ .arg(event.lineNumber()).arg(event.message());
+ if (consoleWidget)
+ consoleWidget->message(QtCriticalMsg, msg);
+ if (errorLogWidget)
+ errorLogWidget->message(QtCriticalMsg, msg);
+ }
+ } break;
+
+ case QScriptDebuggerEvent::InlineEvalFinished: {
+ QScriptDebuggerValue result = event.scriptValue();
+ Q_ASSERT(console != 0);
+ int action = console->evaluateAction();
+ console->setEvaluateAction(0);
+ switch (action) {
+ case 0: { // eval command
+ if (activeJob) {
+ if (activeJobHibernating) {
+ activeJobHibernating = false;
+ activeJob->evaluateFinished(result);
+ }
+ } else if (consoleWidget) {
+ // ### if the result is an object, need to do a tostring job on it
+// messageHandler->message(QtDebugMsg, result.toString());
+ if (result.type() != QScriptDebuggerValue::UndefinedValue)
+ consoleWidget->message(QtDebugMsg, event.message());
+ }
+ } break;
+ case 1: { // return command
+ QScriptDebuggerCommandSchedulerFrontend frontend(this, this);
+ frontend.scheduleForceReturn(console->currentFrameIndex(), result);
+ } return false;
+ }
+ if (!event.isNestedEvaluate()) {
+ // in the case when evaluate() was called while the
+ // engine was not running, we don't want to enter interactive mode
+ return true;
+ }
+ } break;
+
+ case QScriptDebuggerEvent::DebuggerInvocationRequest: {
+ if (!consoleWidget && widgetFactory)
+ q->setConsoleWidget(widgetFactory->createConsoleWidget());
+ if (consoleWidget) {
+ QString fn = event.fileName();
+ if (fn.isEmpty())
+ fn = QString::fromLatin1("<anonymous script, id=%0>").arg(event.scriptId());
+ consoleWidget->message(QtDebugMsg,
+ QString::fromLatin1("Debugger invoked from %1, line %2.")
+ .arg(fn).arg(event.lineNumber()));
+ }
+ } break;
+
+ case QScriptDebuggerEvent::ForcedReturn: {
+ } break;
+
+ }
+
+ if (widgetInPaintEvent) {
+ QString msg = QString::fromLatin1("Suspending evaluation in paintEvent() is not supported; resuming.");
+ if (!consoleWidget && widgetFactory)
+ q->setConsoleWidget(widgetFactory->createConsoleWidget());
+ if (!errorLogWidget && widgetFactory)
+ q->setErrorLogWidget(widgetFactory->createErrorLogWidget());
+ if (consoleWidget)
+ consoleWidget->message(QtWarningMsg, msg);
+ if (errorLogWidget)
+ errorLogWidget->message(QtCriticalMsg, msg);
+ return true;
+ }
+
+ if (activeJobHibernating) {
+ // evaluate() did not finish normally (e.g. due to a breakpoint),
+ // so cancel the job that's waiting for it
+ delete activeJob;
+ activeJob = 0;
+ activeJobHibernating = false;
+ }
+
+ startInteraction(event.type(), event.scriptId(), event.lineNumber());
+ return !interactive;
+}
+
+/*!
+ \reimp
+*/
+QString QScriptDebuggerPrivate::toolTip(int frameIndex, int lineNumber,
+ const QStringList &path)
+{
+ if (frameIndex == -1) {
+ if (stackWidget)
+ frameIndex = stackWidget->currentFrameIndex();
+ else
+ frameIndex = console->currentFrameIndex();
+ }
+ // ### cheating for now, need to use async API
+ QScriptEngineDebuggerFrontend *edf = static_cast<QScriptEngineDebuggerFrontend*>(frontend);
+ QScriptDebuggerBackend *backend = edf->backend();
+ QScriptContext *ctx = backend->context(frameIndex);
+ if (!ctx || path.isEmpty())
+ return QString();
+ QScriptContextInfo ctxInfo(ctx);
+ if (ctx->callee().isValid()
+ && ((lineNumber < ctxInfo.functionStartLineNumber())
+ || (lineNumber > ctxInfo.functionEndLineNumber()))) {
+ return QString();
+ }
+ QScriptValueList objects;
+ int pathIndex = 0;
+ if (path.at(0) == QLatin1String("this")) {
+ objects.append(ctx->thisObject());
+ ++pathIndex;
+ } else {
+#if QT_VERSION >= 0x040500
+ objects << ctx->scopeChain();
+#else
+ objects.append(ctx->activationObject());
+#endif
+ }
+ for (int i = 0; i < objects.size(); ++i) {
+ QScriptValue val = objects.at(i);
+ for (int j = pathIndex; val.isValid() && (j < path.size()); ++j) {
+ val = val.property(path.at(j));
+ }
+ if (val.isValid()) {
+ bool hadException = (ctx->state() == QScriptContext::ExceptionState);
+ QString str = val.toString();
+ if (!hadException && backend->engine()->hasUncaughtException())
+ backend->engine()->clearExceptions();
+ return str;
+ }
+ }
+ return QString();
+}
+
+/*!
+ \reimp
+*/
+QScriptCompletionTaskInterface *QScriptDebuggerPrivate::createCompletionTask(
+ const QString &contents, int cursorPosition, int frameIndex, int options)
+{
+ return new QScriptCompletionTask(
+ contents, cursorPosition, frameIndex, frontend,
+ (options & QScriptCompletionProviderInterface::ConsoleCommandCompletion) ? console : 0);
+}
+
+/*!
+ Slot called when a line has been entered in the console widget.
+*/
+void QScriptDebuggerPrivate::_q_onLineEntered(const QString &contents)
+{
+ QScriptDebuggerConsoleCommandJob *commandJob;
+ commandJob = console->consumeInput(contents, consoleWidget, this);
+ if (commandJob != 0) {
+ scheduleJob(commandJob);
+ consoleWidget->setLineContinuationMode(false);
+ } else if (console->hasIncompleteInput()) {
+ consoleWidget->setLineContinuationMode(true);
+ }
+}
+
+/*!
+ Slot called when the current index has changed in the stack widget.
+*/
+void QScriptDebuggerPrivate::_q_onCurrentFrameChanged(int frameIndex)
+{
+ loadLocals(frameIndex);
+ selectScriptForFrame(frameIndex);
+}
+
+/*!
+ Slot called when the current script has changed in the scripts widget.
+*/
+void QScriptDebuggerPrivate::_q_onCurrentScriptChanged(qint64 scriptId)
+{
+ if (codeWidget && (codeWidget->currentScriptId() != scriptId)) {
+ codeWidget->setCurrentScript(scriptId);
+ QScriptDebuggerCodeViewInterface *view = codeWidget->currentView();
+ if (view)
+ view->setExecutionLineNumber(-1, /*error=*/false);
+ }
+}
+
+void QScriptDebuggerPrivate::_q_onScriptLocationSelected(int lineNumber)
+{
+ QScriptDebuggerCodeViewInterface *view = codeWidget->currentView();
+ if (!view)
+ return;
+ view->gotoLine(lineNumber);
+}
+
+void QScriptDebuggerPrivate::_q_interrupt()
+{
+ executeConsoleCommand(QString::fromLatin1("interrupt"));
+}
+
+void QScriptDebuggerPrivate::_q_continue()
+{
+ executeConsoleCommand(QString::fromLatin1("continue"));
+}
+
+void QScriptDebuggerPrivate::_q_stepInto()
+{
+ executeConsoleCommand(QString::fromLatin1("step"));
+}
+
+void QScriptDebuggerPrivate::_q_stepOver()
+{
+ executeConsoleCommand(QString::fromLatin1("next"));
+}
+
+void QScriptDebuggerPrivate::_q_stepOut()
+{
+ executeConsoleCommand(QString::fromLatin1("finish"));
+}
+
+void QScriptDebuggerPrivate::_q_runToCursor()
+{
+ qint64 scriptId = codeWidget->currentScriptId();
+ int lineNumber = codeWidget->currentView()->cursorLineNumber();
+ QScriptDebuggerCommandSchedulerFrontend frontend(this, this);
+ frontend.scheduleRunToLocation(scriptId, lineNumber);
+}
+
+void QScriptDebuggerPrivate::_q_runToNewScript()
+{
+ QScriptDebuggerCommandSchedulerFrontend frontend(this, this);
+ frontend.scheduleRunToLocation(QString(), -1);
+}
+
+void QScriptDebuggerPrivate::_q_toggleBreakpoint()
+{
+ Q_ASSERT(codeWidget != 0);
+ QScriptDebuggerCodeViewInterface *view = codeWidget->currentView();
+ if (!view)
+ return;
+ qint64 scriptId = codeWidget->currentScriptId();
+ int lineNumber = view->cursorLineNumber();
+ Q_ASSERT(breakpointsModel != 0);
+ int bpId = breakpointsModel->resolveBreakpoint(scriptId, lineNumber);
+ if (bpId != -1) {
+ breakpointsModel->deleteBreakpoint(bpId);
+ } else {
+ QScriptBreakpointData data(scriptId, lineNumber);
+ if (scriptsModel)
+ data.setFileName(scriptsModel->scriptData(scriptId).fileName());
+ breakpointsModel->setBreakpoint(data);
+ }
+}
+
+void QScriptDebuggerPrivate::_q_clearDebugOutput()
+{
+ if (debugOutputWidget)
+ debugOutputWidget->clear();
+}
+
+void QScriptDebuggerPrivate::_q_clearErrorLog()
+{
+ if (errorLogWidget)
+ errorLogWidget->clear();
+}
+
+void QScriptDebuggerPrivate::_q_clearConsole()
+{
+ if (consoleWidget)
+ consoleWidget->clear();
+}
+
+void QScriptDebuggerPrivate::executeConsoleCommand(const QString &command)
+{
+ QString tmp = console->incompleteInput();
+ console->setIncompleteInput(QString());
+ QScriptDebuggerJob *job = console->consumeInput(console->commandPrefix() + command, debugOutputWidget, this);
+ console->setIncompleteInput(tmp);
+ if (job != 0) {
+ scheduleJob(job);
+ // once to send the command...
+ QCoreApplication::processEvents();
+ // ... and once to receive the response
+ QCoreApplication::processEvents();
+ }
+}
+
+void QScriptDebuggerPrivate::_q_findInScript()
+{
+ if (!codeFinderWidget && widgetFactory)
+ q_func()->setCodeFinderWidget(widgetFactory->createCodeFinderWidget());
+ if (codeFinderWidget) {
+ codeFinderWidget->show();
+ codeFinderWidget->setFocus(Qt::OtherFocusReason);
+ }
+}
+
+void QScriptDebuggerPrivate::_q_findNextInScript()
+{
+ findCode(codeFinderWidget->text(), codeFinderWidget->findOptions());
+}
+
+void QScriptDebuggerPrivate::_q_findPreviousInScript()
+{
+ int options = codeFinderWidget->findOptions();
+ options |= QTextDocument::FindBackward;
+ findCode(codeFinderWidget->text(), options);
+}
+
+void QScriptDebuggerPrivate::_q_onFindCodeRequest(
+ const QString &exp, int options)
+{
+ findCode(exp, options);
+ if (findNextInScriptAction)
+ findNextInScriptAction->setEnabled(!exp.isEmpty());
+ if (findPreviousInScriptAction)
+ findPreviousInScriptAction->setEnabled(!exp.isEmpty());
+}
+
+void QScriptDebuggerPrivate::findCode(const QString &exp, int options)
+{
+ QScriptDebuggerCodeViewInterface *view = codeWidget->currentView();
+ if (!view)
+ return;
+ int result = view->find(exp, options);
+ codeFinderWidget->setOK(((result & 0x1) != 0) || exp.isEmpty());
+ codeFinderWidget->setWrapped((result & 0x2) != 0);
+}
+
+void QScriptDebuggerPrivate::_q_goToLine()
+{
+ QScriptDebuggerCodeViewInterface *view = codeWidget->currentView();
+ if (!view)
+ return;
+ bool ok = false;
+ int lineNumber = QInputDialog::getInteger(0, QObject::tr("Go to Line"),
+ QObject::tr("Line:"),
+ view->cursorLineNumber(),
+ 1, INT_MAX, 1, &ok);
+ if (ok)
+ view->gotoLine(lineNumber);
+}
+
+class QScriptDebuggerShowLineJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+ QScriptDebuggerShowLineJob(qint64 scriptId, int lineNumber,
+ QScriptMessageHandlerInterface *messageHandler,
+ QScriptDebuggerCommandSchedulerInterface *scheduler)
+ : QScriptDebuggerCommandSchedulerJob(scheduler),
+ m_scriptId(scriptId), m_lineNumber(lineNumber),
+ m_messageHandler(messageHandler) {}
+
+ void start()
+ {
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleGetScriptData(m_scriptId);
+ }
+ void handleResponse(const QScriptDebuggerResponse &response, int /*commandId*/)
+ {
+ QScriptScriptData data = response.resultAsScriptData();
+ QString line = data.lines(m_lineNumber, 1).value(0);
+ m_messageHandler->message(QtDebugMsg, QString::fromLatin1("%0\t%1")
+ .arg(m_lineNumber).arg(line));
+ finish();
+ }
+
+private:
+ qint64 m_scriptId;
+ int m_lineNumber;
+ QScriptMessageHandlerInterface *m_messageHandler;
+};
+
+namespace {
+
+class SyncStackJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+ SyncStackJob(QScriptDebuggerPrivate *debugger)
+ : QScriptDebuggerCommandSchedulerJob(debugger),
+ m_debugger(debugger), m_index(0) {}
+ void start()
+ {
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleGetContextInfo(m_index); // ### getContextInfos()
+ }
+ void handleResponse(const QScriptDebuggerResponse &response,
+ int)
+ {
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ if (response.error() != QScriptDebuggerResponse::InvalidContextIndex) {
+ m_infos.append(response.resultAsContextInfo());
+ frontend.scheduleGetContextInfo(++m_index);
+ } else {
+ m_debugger->stackModel->setContextInfos(m_infos);
+ if (m_debugger->stackWidget->currentFrameIndex() == -1)
+ m_debugger->stackWidget->setCurrentFrameIndex(0);
+ m_debugger->stackWidget->setUpdatesEnabled(true);
+ m_debugger->stackWidget->setEnabled(true);
+ finish();
+ }
+ }
+
+private:
+ QScriptDebuggerPrivate *m_debugger;
+ int m_index;
+ QList<QScriptContextInfo> m_infos;
+};
+
+class SyncScriptsJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+ SyncScriptsJob(QScriptDebuggerPrivate *debugger)
+ : QScriptDebuggerCommandSchedulerJob(debugger),
+ m_debugger(debugger), m_index(-1) {}
+
+ void start()
+ {
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleScriptsCheckpoint();
+ }
+ void handleResponse(const QScriptDebuggerResponse &response,
+ int)
+ {
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ if (m_index == -1) {
+ QScriptScriptsDelta delta;
+ delta = qvariant_cast<QScriptScriptsDelta>(response.result());
+
+ const QList<qint64> &removed = delta.second;
+ for (int i = 0; i < removed.size(); ++i)
+ m_debugger->scriptsModel->removeScript(removed.at(i));
+
+ m_added = delta.first;
+ if (!m_added.isEmpty()) {
+ frontend.scheduleGetScriptData(m_added.at(++m_index));
+ } else {
+ m_debugger->scriptsModel->commit();
+ finish();
+ }
+ } else {
+ QScriptScriptData data = response.resultAsScriptData();
+ qint64 scriptId = m_added.at(m_index);
+ m_debugger->scriptsModel->addScript(scriptId, data);
+
+#if QT_VERSION >= 0x040500
+ // ### could be slow, might want to do this in a separate thread
+ QString xml = qt_scriptToXml(data.contents(), data.baseLineNumber());
+ QScriptXmlParser::Result extraInfo = QScriptXmlParser::parse(xml);
+ m_debugger->scriptsModel->addExtraScriptInfo(
+ scriptId, extraInfo.functionsInfo, extraInfo.executableLineNumbers);
+#endif
+
+ if (++m_index < m_added.size())
+ frontend.scheduleGetScriptData(m_added.at(m_index));
+ else {
+ m_debugger->scriptsModel->commit();
+ finish();
+ }
+ }
+ }
+
+private:
+ QScriptDebuggerPrivate *m_debugger;
+ int m_index;
+ QList<qint64> m_added;
+};
+
+class SyncBreakpointsJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+ SyncBreakpointsJob(QScriptDebuggerPrivate *debugger)
+ : QScriptDebuggerCommandSchedulerJob(debugger),
+ m_debugger(debugger) {}
+ void start()
+ {
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleGetBreakpoints();
+ }
+ void handleResponse(const QScriptDebuggerResponse &response,
+ int)
+ {
+ QScriptBreakpointMap breakpoints = response.resultAsBreakpoints();
+ QScriptBreakpointMap::const_iterator it;
+ for (it = breakpoints.constBegin(); it != breakpoints.constEnd(); ++it) {
+ int id = it.key();
+ QScriptBreakpointData newData = it.value();
+ QScriptBreakpointData existingData = m_debugger->breakpointsModel->breakpointData(id);
+ if (existingData.isValid() && (existingData != newData))
+ m_debugger->breakpointsModel->modifyBreakpoint(id, newData);
+ }
+ finish();
+ }
+
+private:
+ QScriptDebuggerPrivate *m_debugger;
+ int m_index;
+ QList<QScriptContextInfo> m_infos;
+};
+
+class SyncLocalsJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+ SyncLocalsJob(QScriptDebuggerPrivate *debugger)
+ : QScriptDebuggerCommandSchedulerJob(debugger),
+ m_debugger(debugger) {}
+
+ void start()
+ {
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleContextsCheckpoint();
+ }
+ void handleResponse(const QScriptDebuggerResponse &response,
+ int)
+ {
+ QScriptContextsDelta delta = qvariant_cast<QScriptContextsDelta>(response.result());
+ for (int i = 0; i < delta.first.size(); ++i) {
+ QScriptDebuggerLocalsModel *model = m_debugger->localsModels.take(delta.first.at(i));
+ delete model;
+ }
+ finish();
+ }
+
+private:
+ QScriptDebuggerPrivate *m_debugger;
+};
+
+class LoadLocalsJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+ LoadLocalsJob(QScriptDebuggerPrivate *debugger, int frameIndex)
+ : QScriptDebuggerCommandSchedulerJob(debugger),
+ m_debugger(debugger), m_frameIndex(frameIndex), m_state(0) {}
+
+ void start()
+ {
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleGetContextId(m_frameIndex);
+ }
+ void handleResponse(const QScriptDebuggerResponse &response,
+ int)
+ {
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ qint64 contextId = response.resultAsLongLong();
+ QScriptDebuggerLocalsModel *model = m_debugger->localsModels.value(contextId);
+ if (model) {
+ model->sync(m_frameIndex);
+ } else {
+ model = m_debugger->createLocalsModel();
+ m_debugger->localsModels.insert(contextId, model);
+ model->init(m_frameIndex);
+ }
+ if (m_debugger->localsWidget) {
+ if (m_debugger->localsWidget->localsModel() != model) // ### bug in qtreeview
+ m_debugger->localsWidget->setLocalsModel(model);
+ m_debugger->localsWidget->setUpdatesEnabled(true);
+ m_debugger->localsWidget->setEnabled(true);
+ }
+ finish();
+ }
+
+private:
+ QScriptDebuggerPrivate *m_debugger;
+ int m_frameIndex;
+ int m_state;
+};
+
+class EmitStoppedSignalJob : public QScriptDebuggerJob
+{
+public:
+ EmitStoppedSignalJob(QScriptDebuggerPrivate *debugger)
+ : m_debugger(debugger) {}
+
+ void start()
+ {
+ m_debugger->emitStoppedSignal();
+ finish();
+ }
+
+private:
+ QScriptDebuggerPrivate *m_debugger;
+};
+
+} // namespace
+
+void QScriptDebuggerPrivate::startInteraction(QScriptDebuggerEvent::Type type,
+ qint64 scriptId, int lineNumber)
+{
+ Q_Q(QScriptDebugger);
+ if (type != QScriptDebuggerEvent::InlineEvalFinished) {
+ if (stackWidget)
+ stackWidget->setCurrentFrameIndex(0);
+ console->setCurrentFrameIndex(0);
+ console->setCurrentScriptId(scriptId);
+ console->setCurrentLineNumber(lineNumber);
+ }
+
+ if ((scriptId != -1) && consoleWidget) {
+ QScriptDebuggerJob *job = new QScriptDebuggerShowLineJob(scriptId, lineNumber, consoleWidget, this);
+ scheduleJob(job);
+ }
+
+ sync();
+
+ if (!interactive) {
+ interactive = true;
+ if (updatesEnabledTimerId != -1) {
+ q->killTimer(updatesEnabledTimerId);
+ updatesEnabledTimerId = -1;
+ }
+ console->bumpSessionId();
+ scheduleJob(new EmitStoppedSignalJob(this));
+ }
+
+ if (consoleWidget)
+ consoleWidget->activateWindow();
+ else if (codeWidget)
+ codeWidget->activateWindow();
+
+ if (continueAction)
+ continueAction->setEnabled(true);
+ if (stepIntoAction)
+ stepIntoAction->setEnabled(true);
+ if (stepOverAction)
+ stepOverAction->setEnabled(true);
+ if (stepOutAction)
+ stepOutAction->setEnabled(true);
+ if (runToCursorAction)
+ runToCursorAction->setEnabled(true);
+ if (runToNewScriptAction)
+ runToNewScriptAction->setEnabled(true);
+ if (interruptAction)
+ interruptAction->setEnabled(false);
+
+ bool hasScript = (codeWidget != 0);
+ if (findInScriptAction)
+ findInScriptAction->setEnabled(hasScript);
+ if (toggleBreakpointAction)
+ toggleBreakpointAction->setEnabled(hasScript);
+ if (goToLineAction)
+ goToLineAction->setEnabled(hasScript);
+}
+
+void QScriptDebuggerPrivate::sync()
+{
+ if (localsWidget) {
+ QScriptDebuggerJob *job = new SyncLocalsJob(this);
+ scheduleJob(job);
+ }
+ if (scriptsModel) {
+ QScriptDebuggerJob *job = new SyncScriptsJob(this);
+ scheduleJob(job);
+ }
+ if (stackModel) {
+ QScriptDebuggerJob *job = new SyncStackJob(this);
+ scheduleJob(job);
+ }
+ if (breakpointsModel) {
+ // need to sync because the ignore-count could have changed
+ QScriptDebuggerJob *job = new SyncBreakpointsJob(this);
+ scheduleJob(job);
+ }
+
+ if (stackWidget && (stackWidget->currentFrameIndex() != -1)) {
+ int index = stackWidget->currentFrameIndex();
+ loadLocals(index);
+ selectScriptForFrame(index);
+ } else if (codeWidget && (console->currentFrameIndex() != -1)) {
+ selectScriptForFrame(console->currentFrameIndex());
+ }
+}
+
+void QScriptDebuggerPrivate::loadLocals(int frameIndex)
+{
+ LoadLocalsJob *job = new LoadLocalsJob(this, frameIndex);
+ scheduleJob(job);
+}
+
+QScriptDebuggerLocalsModel *QScriptDebuggerPrivate::createLocalsModel()
+{
+ return new QScriptDebuggerLocalsModel(this, this, q_func());
+}
+
+namespace {
+
+class ShowFrameCodeJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+ ShowFrameCodeJob(QScriptDebuggerPrivate *debugger, int frameIndex)
+ : QScriptDebuggerCommandSchedulerJob(debugger),
+ m_debugger(debugger), m_frameIndex(frameIndex) {}
+
+ void start()
+ {
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleGetContextInfo(m_frameIndex);
+ }
+ void handleResponse(const QScriptDebuggerResponse &response,
+ int)
+ {
+ if (m_info.isNull()) {
+ m_info = response.resultAsContextInfo();
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleGetContextState(m_frameIndex);
+ } else {
+ int contextState = response.resultAsInt();
+ bool error = (contextState == QScriptContext::ExceptionState);
+ if (m_debugger->scriptsWidget) {
+ m_debugger->scriptsWidget->setCurrentScript(m_info.scriptId());
+ }
+ if (m_debugger->codeWidget) {
+ m_debugger->codeWidget->setCurrentScript(m_info.scriptId());
+ QScriptDebuggerCodeViewInterface *view = m_debugger->codeWidget->currentView();
+ if (view)
+ view->setExecutionLineNumber(m_info.lineNumber(), error);
+ }
+ finish();
+ }
+ }
+
+private:
+ QScriptDebuggerPrivate *m_debugger;
+ int m_frameIndex;
+ QScriptContextInfo m_info;
+};
+
+} // namespace
+
+void QScriptDebuggerPrivate::selectScriptForFrame(int frameIndex)
+{
+ QScriptDebuggerJob *job = new ShowFrameCodeJob(this, frameIndex);
+ scheduleJob(job);
+}
+
+void QScriptDebuggerPrivate::emitStoppedSignal()
+{
+ emit q_func()->stopped();
+}
+
+/*!
+ Constructs a new QScriptDebugger object.
+*/
+QScriptDebugger::QScriptDebugger(QObject *parent)
+ : QObject(*new QScriptDebuggerPrivate, parent)
+{
+ ++scriptDebuggerCount;
+}
+
+/*!
+ Destroys this QScriptDebugger.
+*/
+QScriptDebugger::~QScriptDebugger()
+{
+ --scriptDebuggerCount;
+ if ((scriptDebuggerCount == 0) && eventCallbackRegistered) {
+ eventCallbackRegistered = false;
+ QInternal::unregisterCallback(QInternal::EventNotifyCallback,
+ scriptDebuggerEventCallback);
+ }
+}
+
+/*!
+ \internal
+*/
+QScriptDebugger::QScriptDebugger(QScriptDebuggerPrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+}
+
+QScriptDebuggerFrontend *QScriptDebugger::frontend() const
+{
+ Q_D(const QScriptDebugger);
+ return d->frontend;
+}
+
+void QScriptDebugger::setFrontend(QScriptDebuggerFrontend *frontend)
+{
+ Q_D(QScriptDebugger);
+ if (d->frontend)
+ d->frontend->setEventHandler(0);
+ d->frontend = frontend;
+ if (frontend) {
+ frontend->setEventHandler(d);
+ if (!eventCallbackRegistered) {
+ eventCallbackRegistered = true;
+ QInternal::registerCallback(QInternal::EventNotifyCallback,
+ scriptDebuggerEventCallback);
+ }
+ }
+}
+
+QScriptDebuggerConsoleWidgetInterface *QScriptDebugger::consoleWidget() const
+{
+ Q_D(const QScriptDebugger);
+ return d->consoleWidget;
+}
+
+void QScriptDebugger::setConsoleWidget(QScriptDebuggerConsoleWidgetInterface *consoleWidget)
+{
+ Q_D(QScriptDebugger);
+ if (d->consoleWidget) {
+ QObject::disconnect(d->consoleWidget, 0, this, 0);
+ }
+ d->consoleWidget = consoleWidget;
+ if (consoleWidget) {
+ consoleWidget->setCommandHistorian(d->console);
+ consoleWidget->setCompletionProvider(d);
+ QObject::connect(consoleWidget, SIGNAL(lineEntered(QString)),
+ this, SLOT(_q_onLineEntered(QString)));
+
+ d->console->showDebuggerInfoMessage(consoleWidget);
+ }
+}
+
+QScriptDebuggerStackWidgetInterface *QScriptDebugger::stackWidget() const
+{
+ Q_D(const QScriptDebugger);
+ return d->stackWidget;
+}
+
+void QScriptDebugger::setStackWidget(QScriptDebuggerStackWidgetInterface *stackWidget)
+{
+ Q_D(QScriptDebugger);
+ if (d->stackWidget) {
+ QObject::disconnect(d->stackWidget, 0, this, 0);
+ }
+ d->stackWidget = stackWidget;
+ if (stackWidget) {
+ if (!d->stackModel) {
+ d->stackModel = new QScriptDebuggerStackModel(this);
+ if (d->interactive)
+ d->scheduleJob(new SyncStackJob(d));
+ }
+ stackWidget->setStackModel(d->stackModel);
+ QObject::connect(stackWidget, SIGNAL(currentFrameChanged(int)),
+ this, SLOT(_q_onCurrentFrameChanged(int)));
+ }
+}
+
+QScriptDebuggerScriptsWidgetInterface *QScriptDebugger::scriptsWidget() const
+{
+ Q_D(const QScriptDebugger);
+ return d->scriptsWidget;
+}
+
+void QScriptDebugger::setScriptsWidget(QScriptDebuggerScriptsWidgetInterface *scriptsWidget)
+{
+ Q_D(QScriptDebugger);
+ if (d->scriptsWidget) {
+ QObject::disconnect(d->scriptsWidget, 0, this, 0);
+ }
+ d->scriptsWidget = scriptsWidget;
+ if (scriptsWidget) {
+ if (!d->scriptsModel) {
+ d->scriptsModel = new QScriptDebuggerScriptsModel(this);
+ if (d->interactive)
+ d->scheduleJob(new SyncScriptsJob(d));
+ }
+ scriptsWidget->setScriptsModel(d->scriptsModel);
+ QObject::connect(scriptsWidget, SIGNAL(currentScriptChanged(qint64)),
+ this, SLOT(_q_onCurrentScriptChanged(qint64)));
+ QObject::connect(d->scriptsWidget, SIGNAL(scriptLocationSelected(int)),
+ this, SLOT(_q_onScriptLocationSelected(int)));
+ }
+}
+
+QScriptDebuggerLocalsWidgetInterface *QScriptDebugger::localsWidget() const
+{
+ Q_D(const QScriptDebugger);
+ return d->localsWidget;
+}
+
+void QScriptDebugger::setLocalsWidget(QScriptDebuggerLocalsWidgetInterface *localsWidget)
+{
+ Q_D(QScriptDebugger);
+ if (d->localsWidget) {
+ // ### d->localsWidget->setLocalsModel(0);
+ }
+ localsWidget->setCompletionProvider(d);
+ d->localsWidget = localsWidget;
+}
+
+QScriptDebuggerCodeWidgetInterface *QScriptDebugger::codeWidget() const
+{
+ Q_D(const QScriptDebugger);
+ return d->codeWidget;
+}
+
+void QScriptDebugger::setCodeWidget(QScriptDebuggerCodeWidgetInterface *codeWidget)
+{
+ Q_D(QScriptDebugger);
+ if (d->codeWidget) {
+ d->codeWidget->removeEventFilter(this);
+ }
+ d->codeWidget = codeWidget;
+ if (codeWidget) {
+ if (!d->scriptsModel) {
+ d->scriptsModel = new QScriptDebuggerScriptsModel(this);
+ if (d->interactive)
+ d->scheduleJob(new SyncScriptsJob(d));
+ }
+ codeWidget->setScriptsModel(d->scriptsModel);
+ if (!d->breakpointsModel) {
+ d->breakpointsModel = new QScriptBreakpointsModel(d, d, this);
+ if (d->interactive)
+ d->scheduleJob(new SyncBreakpointsJob(d));
+ }
+ codeWidget->setBreakpointsModel(d->breakpointsModel);
+ codeWidget->setToolTipProvider(d);
+ codeWidget->installEventFilter(this);
+ }
+ bool hasScript = (codeWidget != 0) && (codeWidget->currentView() != 0);
+ if (d->findInScriptAction)
+ d->findInScriptAction->setEnabled(hasScript && (d->codeFinderWidget != 0));
+ if (d->goToLineAction)
+ d->goToLineAction->setEnabled(hasScript);
+ if (d->toggleBreakpointAction)
+ d->toggleBreakpointAction->setEnabled(hasScript);
+}
+
+QScriptDebuggerCodeFinderWidgetInterface *QScriptDebugger::codeFinderWidget() const
+{
+ Q_D(const QScriptDebugger);
+ return d->codeFinderWidget;
+}
+
+void QScriptDebugger::setCodeFinderWidget(QScriptDebuggerCodeFinderWidgetInterface *codeFinderWidget)
+{
+ Q_D(QScriptDebugger);
+ if (d->codeFinderWidget) {
+ QObject::disconnect(d->codeFinderWidget, 0, this, 0);
+ }
+ d->codeFinderWidget = codeFinderWidget;
+ if (codeFinderWidget) {
+ QObject::connect(codeFinderWidget, SIGNAL(findRequest(QString,int)),
+ this, SLOT(_q_onFindCodeRequest(QString,int)));
+ }
+ if (d->findInScriptAction) {
+ d->findInScriptAction->setEnabled(
+ (codeFinderWidget != 0)
+ && (d->codeWidget != 0)
+ && (d->codeWidget->currentView() != 0));
+ }
+}
+
+QScriptDebugOutputWidgetInterface *QScriptDebugger::debugOutputWidget() const
+{
+ Q_D(const QScriptDebugger);
+ return d->debugOutputWidget;
+}
+
+void QScriptDebugger::setDebugOutputWidget(QScriptDebugOutputWidgetInterface *debugOutputWidget)
+{
+ Q_D(QScriptDebugger);
+ d->debugOutputWidget = debugOutputWidget;
+}
+
+QScriptBreakpointsWidgetInterface *QScriptDebugger::breakpointsWidget() const
+{
+ Q_D(const QScriptDebugger);
+ return d->breakpointsWidget;
+}
+
+void QScriptDebugger::setBreakpointsWidget(QScriptBreakpointsWidgetInterface *breakpointsWidget)
+{
+ Q_D(QScriptDebugger);
+ if (d->breakpointsWidget) {
+ // ### invalidate
+ }
+ d->breakpointsWidget = breakpointsWidget;
+ if (breakpointsWidget) {
+ if (!d->breakpointsModel) {
+ d->breakpointsModel = new QScriptBreakpointsModel(d, d, this);
+ if (d->interactive)
+ d->scheduleJob(new SyncBreakpointsJob(d));
+ }
+ d->breakpointsWidget->setBreakpointsModel(d->breakpointsModel);
+ d->breakpointsWidget->setScriptsModel(d->scriptsModel);
+ }
+}
+
+QScriptErrorLogWidgetInterface *QScriptDebugger::errorLogWidget() const
+{
+ Q_D(const QScriptDebugger);
+ return d->errorLogWidget;
+}
+
+void QScriptDebugger::setErrorLogWidget(QScriptErrorLogWidgetInterface *errorLogWidget)
+{
+ Q_D(QScriptDebugger);
+ d->errorLogWidget = errorLogWidget;
+}
+
+QScriptDebuggerWidgetFactoryInterface *QScriptDebugger::widgetFactory() const
+{
+ Q_D(const QScriptDebugger);
+ return d->widgetFactory;
+}
+
+void QScriptDebugger::setWidgetFactory(QScriptDebuggerWidgetFactoryInterface *factory)
+{
+ Q_D(QScriptDebugger);
+ d->widgetFactory = factory;
+}
+
+QAction *QScriptDebugger::interruptAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->interruptAction) {
+ QIcon interruptIcon;
+ interruptIcon.addPixmap(d->pixmap(QString::fromLatin1("interrupt.png")), QIcon::Normal);
+ interruptIcon.addPixmap(d->pixmap(QString::fromLatin1("d_interrupt.png")), QIcon::Disabled);
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->interruptAction = new QAction(interruptIcon, QObject::tr("Interrupt"), parent);
+ d->interruptAction->setEnabled(!d->interactive);
+ d->interruptAction->setShortcut(QObject::tr("Shift+F5"));
+ QObject::connect(d->interruptAction, SIGNAL(triggered()),
+ that, SLOT(_q_interrupt()));
+ }
+ return d->interruptAction;
+}
+
+QAction *QScriptDebugger::continueAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->continueAction) {
+ QIcon continueIcon;
+ continueIcon.addPixmap(d->pixmap(QString::fromLatin1("play.png")), QIcon::Normal);
+ continueIcon.addPixmap(d->pixmap(QString::fromLatin1("d_play.png")), QIcon::Disabled);
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->continueAction = new QAction(continueIcon, QObject::tr("Continue"), parent);
+ d->continueAction->setEnabled(d->interactive);
+ d->continueAction->setShortcut(QObject::tr("F5"));
+ QObject::connect(d->continueAction, SIGNAL(triggered()),
+ that, SLOT(_q_continue()));
+ }
+ return d->continueAction;
+}
+
+QAction *QScriptDebugger::stepIntoAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->stepIntoAction) {
+ QIcon stepIntoIcon;
+ stepIntoIcon.addPixmap(d->pixmap(QString::fromLatin1("stepinto.png")), QIcon::Normal);
+ stepIntoIcon.addPixmap(d->pixmap(QString::fromLatin1("d_stepinto.png")), QIcon::Disabled);
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->stepIntoAction = new QAction(stepIntoIcon, QObject::tr("Step Into"), parent);
+ d->stepIntoAction->setEnabled(d->interactive);
+ d->stepIntoAction->setShortcut(QObject::tr("F11"));
+ QObject::connect(d->stepIntoAction, SIGNAL(triggered()),
+ that, SLOT(_q_stepInto()));
+ }
+ return d->stepIntoAction;
+}
+
+QAction *QScriptDebugger::stepOverAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->stepOverAction) {
+ QIcon stepOverIcon;
+ stepOverIcon.addPixmap(d->pixmap(QString::fromLatin1("stepover.png")), QIcon::Normal);
+ stepOverIcon.addPixmap(d->pixmap(QString::fromLatin1("d_stepover.png")), QIcon::Disabled);
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->stepOverAction = new QAction(stepOverIcon, QObject::tr("Step Over"), parent);
+ d->stepOverAction->setEnabled(d->interactive);
+ d->stepOverAction->setShortcut(QObject::tr("F10"));
+ QObject::connect(d->stepOverAction, SIGNAL(triggered()),
+ that, SLOT(_q_stepOver()));
+ }
+ return d->stepOverAction;
+}
+
+QAction *QScriptDebugger::stepOutAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->stepOutAction) {
+ QIcon stepOutIcon;
+ stepOutIcon.addPixmap(d->pixmap(QString::fromLatin1("stepout.png")), QIcon::Normal);
+ stepOutIcon.addPixmap(d->pixmap(QString::fromLatin1("d_stepout.png")), QIcon::Disabled);
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->stepOutAction = new QAction(stepOutIcon, QObject::tr("Step Out"), parent);
+ d->stepOutAction->setEnabled(d->interactive);
+ d->stepOutAction->setShortcut(QObject::tr("Shift+F11"));
+ QObject::connect(d->stepOutAction, SIGNAL(triggered()),
+ that, SLOT(_q_stepOut()));
+ }
+ return d->stepOutAction;
+}
+
+QAction *QScriptDebugger::runToCursorAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->runToCursorAction) {
+ QIcon runToCursorIcon;
+ runToCursorIcon.addPixmap(d->pixmap(QString::fromLatin1("runtocursor.png")), QIcon::Normal);
+ runToCursorIcon.addPixmap(d->pixmap(QString::fromLatin1("d_runtocursor.png")), QIcon::Disabled);
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->runToCursorAction = new QAction(runToCursorIcon, QObject::tr("Run to Cursor"), parent);
+ d->runToCursorAction->setEnabled(d->interactive);
+ d->runToCursorAction->setShortcut(QObject::tr("Ctrl+F10"));
+ QObject::connect(d->runToCursorAction, SIGNAL(triggered()),
+ that, SLOT(_q_runToCursor()));
+ }
+ return d->runToCursorAction;
+}
+
+QAction *QScriptDebugger::runToNewScriptAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->runToNewScriptAction) {
+ QIcon runToNewScriptIcon;
+ runToNewScriptIcon.addPixmap(d->pixmap(QString::fromLatin1("runtonewscript.png")), QIcon::Normal);
+ runToNewScriptIcon.addPixmap(d->pixmap(QString::fromLatin1("d_breakonscriptload.png")), QIcon::Disabled);
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->runToNewScriptAction = new QAction(runToNewScriptIcon,
+ QObject::tr("Run to New Script"), parent);
+ d->runToNewScriptAction->setEnabled(d->interactive);
+ QObject::connect(d->runToNewScriptAction, SIGNAL(triggered()),
+ that, SLOT(_q_runToNewScript()));
+ }
+ return d->runToNewScriptAction;
+}
+
+QAction *QScriptDebugger::toggleBreakpointAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->toggleBreakpointAction) {
+ QIcon toggleBreakpointIcon;
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->toggleBreakpointAction = new QAction(toggleBreakpointIcon,
+ QObject::tr("Toggle Breakpoint"), parent);
+ d->toggleBreakpointAction->setShortcut(QObject::tr("F9"));
+ d->toggleBreakpointAction->setEnabled((d->codeWidget != 0) && (d->codeWidget->currentView() != 0));
+ QObject::connect(d->toggleBreakpointAction, SIGNAL(triggered()),
+ that, SLOT(_q_toggleBreakpoint()));
+ }
+ return d->toggleBreakpointAction;
+}
+
+QAction *QScriptDebugger::clearDebugOutputAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->clearDebugOutputAction) {
+ QIcon clearDebugOutputIcon;
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->clearDebugOutputAction = new QAction(clearDebugOutputIcon, QObject::tr("Clear Debug Output"), parent);
+ QObject::connect(d->clearDebugOutputAction, SIGNAL(triggered()),
+ that, SLOT(_q_clearDebugOutput()));
+ }
+ return d->clearDebugOutputAction;
+}
+
+QAction *QScriptDebugger::clearErrorLogAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->clearErrorLogAction) {
+ QIcon clearErrorLogIcon;
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->clearErrorLogAction = new QAction(clearErrorLogIcon, QObject::tr("Clear Error Log"), parent);
+ QObject::connect(d->clearErrorLogAction, SIGNAL(triggered()),
+ that, SLOT(_q_clearErrorLog()));
+ }
+ return d->clearErrorLogAction;
+}
+
+QAction *QScriptDebugger::clearConsoleAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->clearConsoleAction) {
+ QIcon clearConsoleIcon;
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->clearConsoleAction = new QAction(clearConsoleIcon, QObject::tr("Clear Console"), parent);
+ QObject::connect(d->clearConsoleAction, SIGNAL(triggered()),
+ that, SLOT(_q_clearConsole()));
+ }
+ return d->clearConsoleAction;
+}
+
+QAction *QScriptDebugger::findInScriptAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->findInScriptAction) {
+ QIcon findInScriptIcon;
+ findInScriptIcon.addPixmap(d->pixmap(QString::fromLatin1("find.png")), QIcon::Normal);
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->findInScriptAction = new QAction(findInScriptIcon, QObject::tr("&Find in Script..."), parent);
+ d->findInScriptAction->setShortcut(QObject::tr("Ctrl+F"));
+ d->findInScriptAction->setEnabled(
+ (d->codeFinderWidget != 0)
+ && (d->codeWidget != 0)
+ && (d->codeWidget->currentView() != 0));
+ QObject::connect(d->findInScriptAction, SIGNAL(triggered()),
+ that, SLOT(_q_findInScript()));
+ }
+ return d->findInScriptAction;
+}
+
+QAction *QScriptDebugger::findNextInScriptAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->findNextInScriptAction) {
+ QIcon findNextInScriptIcon;
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->findNextInScriptAction = new QAction(findNextInScriptIcon, QObject::tr("Find &Next"), parent);
+ d->findNextInScriptAction->setEnabled(d->codeFinderWidget && !d->codeFinderWidget->text().isEmpty());
+ d->findNextInScriptAction->setShortcut(QObject::tr("F3"));
+ QObject::connect(d->findNextInScriptAction, SIGNAL(triggered()),
+ that, SLOT(_q_findNextInScript()));
+ }
+ return d->findNextInScriptAction;
+}
+
+QAction *QScriptDebugger::findPreviousInScriptAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->findPreviousInScriptAction) {
+ QIcon findPreviousInScriptIcon;
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->findPreviousInScriptAction = new QAction(findPreviousInScriptIcon, QObject::tr("Find &Previous"), parent);
+ d->findPreviousInScriptAction->setEnabled(d->codeFinderWidget && !d->codeFinderWidget->text().isEmpty());
+ d->findPreviousInScriptAction->setShortcut(QObject::tr("Shift+F3"));
+ QObject::connect(d->findPreviousInScriptAction, SIGNAL(triggered()),
+ that, SLOT(_q_findPreviousInScript()));
+ }
+ return d->findPreviousInScriptAction;
+}
+
+QAction *QScriptDebugger::goToLineAction(QObject *parent) const
+{
+ Q_D(const QScriptDebugger);
+ if (!d->goToLineAction) {
+ QIcon goToLineIcon;
+ QScriptDebugger *that = const_cast<QScriptDebugger*>(this);
+ that->d_func()->goToLineAction = new QAction(goToLineIcon, QObject::tr("Go to Line"), parent);
+ d->goToLineAction->setShortcut(QObject::tr("Ctrl+G"));
+ d->goToLineAction->setEnabled((d->codeWidget != 0) && (d->codeWidget->currentView() != 0));
+ QObject::connect(d->goToLineAction, SIGNAL(triggered()),
+ that, SLOT(_q_goToLine()));
+ }
+ return d->goToLineAction;
+}
+
+/*!
+ \reimp
+*/
+bool QScriptDebugger::eventFilter(QObject *watched, QEvent *e)
+{
+ Q_D(QScriptDebugger);
+ if (watched == d->codeWidget) {
+ if (e->type() == QEvent::KeyPress) {
+ d->_q_findInScript();
+ d->codeFinderWidget->setText(static_cast<QKeyEvent*>(e)->text());
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+void QScriptDebugger::timerEvent(QTimerEvent *e)
+{
+ Q_D(QScriptDebugger);
+ if (e->timerId() == d->updatesEnabledTimerId) {
+ killTimer(d->updatesEnabledTimerId);
+ d->updatesEnabledTimerId = -1;
+ if (d->stackWidget)
+ d->stackWidget->setUpdatesEnabled(true);
+ if (d->localsWidget)
+ d->localsWidget->setUpdatesEnabled(true);
+ } else {
+ QObject::timerEvent(e);
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qscriptdebugger_p.cpp"