src/scripttools/debugging/qscriptdebugger.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/scripttools/debugging/qscriptdebugger.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1990 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@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 "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>
+#include <QtGui/qmenu.h>
+#include <QtGui/qtoolbar.h>
+#include <QtGui/qtooltip.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 script
+
+*/
+
+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);
+
+    void showToolTip(const QPoint &pos, 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;
+}
+
+class QScriptToolTipJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+    QScriptToolTipJob(const QPoint &pos, int frameIndex,
+                      int lineNumber, const QStringList &path,
+                      QScriptDebuggerCommandSchedulerInterface *scheduler)
+        : QScriptDebuggerCommandSchedulerJob(scheduler), m_pos(pos),
+          m_frameIndex(frameIndex), m_lineNumber(lineNumber), m_path(path)
+        {}
+
+    void start()
+    {
+        QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+        frontend.scheduleGetPropertyExpressionValue(m_frameIndex, m_lineNumber, m_path);
+    }
+    void handleResponse(const QScriptDebuggerResponse &response, int /*commandId*/)
+    {
+        QString tip = response.result().toString();
+        if (tip.indexOf(QLatin1Char('\n')) != -1) {
+            QStringList lines = tip.split(QLatin1Char('\n'));
+            int lineCount = lines.size();
+            if (lineCount > 5) {
+                lines = lines.mid(0, 5);
+                lines.append(QString::fromLatin1("(... %0 more lines ...)").arg(lineCount - 5));
+            }
+            tip = lines.join(QLatin1String("\n"));
+        }
+        QToolTip::showText(m_pos, tip);
+        finish();
+    }
+
+private:
+    QPoint m_pos;
+    int m_frameIndex;
+    int m_lineNumber;
+    QStringList m_path;
+};
+
+/*!
+  \reimp
+*/
+void QScriptDebuggerPrivate::showToolTip(const QPoint &pos, int frameIndex,
+                                         int lineNumber, const QStringList &path)
+{
+    if (frameIndex == -1) {
+        if (stackWidget)
+            frameIndex = stackWidget->currentFrameIndex();
+        else
+            frameIndex = console->currentFrameIndex();
+    }
+    QScriptDebuggerJob *job = new QScriptToolTipJob(pos, frameIndex, lineNumber, path, this);
+    scheduleJob(job);
+}
+
+/*!
+  \reimp
+*/
+QScriptCompletionTaskInterface *QScriptDebuggerPrivate::createCompletionTask(
+    const QString &contents, int cursorPosition, int frameIndex, int options)
+{
+    return new QScriptCompletionTask(
+        contents, cursorPosition, frameIndex, this, this,
+        (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, QScriptDebugger::tr("Go to Line"),
+                                              QScriptDebugger::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);
+
+            // ### could be slow, might want to do this in a separate thread
+//            Q_ASSERT_X(false, Q_FUNC_INFO, "implement me");
+            QString xml; // = qt_scriptToXml(data.contents(), data.baseLineNumber());
+            QScriptXmlParser::Result extraInfo = QScriptXmlParser::parse(xml);
+            m_debugger->scriptsModel->addExtraScriptInfo(
+                scriptId, extraInfo.functionsInfo, extraInfo.executableLineNumbers);
+
+            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), m_index(-1) {}
+    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) {}
+
+    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;
+};
+
+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);
+        }
+    }
+}
+
+QAction *QScriptDebugger::action(DebuggerAction action, QObject *parent)
+{
+    switch (action) {
+    case InterruptAction:
+        return interruptAction(parent);
+    case ContinueAction:
+        return continueAction(parent);
+    case StepIntoAction:
+        return stepIntoAction(parent);
+    case StepOverAction:
+        return stepOverAction(parent);
+    case StepOutAction:
+        return stepOutAction(parent);
+    case RunToCursorAction:
+        return runToCursorAction(parent);
+    case RunToNewScriptAction:
+        return runToNewScriptAction(parent);
+    case ToggleBreakpointAction:
+        return toggleBreakpointAction(parent);
+    case ClearDebugOutputAction:
+        return clearDebugOutputAction(parent);
+    case ClearErrorLogAction:
+        return clearErrorLogAction(parent);
+    case ClearConsoleAction:
+        return clearConsoleAction(parent);
+    case FindInScriptAction:
+        return findInScriptAction(parent);
+    case FindNextInScriptAction:
+        return findNextInScriptAction(parent);
+    case FindPreviousInScriptAction:
+        return findPreviousInScriptAction(parent);
+    case GoToLineAction:
+        return goToLineAction(parent);
+    }
+    return 0;
+}
+
+QWidget *QScriptDebugger::widget(DebuggerWidget widget)
+{
+    switch (widget) {
+    case ConsoleWidget: {
+        QScriptDebuggerConsoleWidgetInterface *w = consoleWidget();
+        if (!w && widgetFactory()) {
+            w = widgetFactory()->createConsoleWidget();
+            setConsoleWidget(w);
+        }
+        return w;
+    }
+    case StackWidget: {
+        QScriptDebuggerStackWidgetInterface *w = stackWidget();
+        if (!w && widgetFactory()) {
+            w = widgetFactory()->createStackWidget();
+            setStackWidget(w);
+        }
+        return w;
+    }
+    case ScriptsWidget: {
+        QScriptDebuggerScriptsWidgetInterface *w = scriptsWidget();
+        if (!w && widgetFactory()) {
+            w = widgetFactory()->createScriptsWidget();
+            setScriptsWidget(w);
+        }
+        return w;
+    }
+    case LocalsWidget: {
+        QScriptDebuggerLocalsWidgetInterface *w = localsWidget();
+        if (!w && widgetFactory()) {
+            w = widgetFactory()->createLocalsWidget();
+            setLocalsWidget(w);
+        }
+        return w;
+    }
+    case CodeWidget: {
+        QScriptDebuggerCodeWidgetInterface *w = codeWidget();
+        if (!w && widgetFactory()) {
+            w = widgetFactory()->createCodeWidget();
+            setCodeWidget(w);
+        }
+        return w;
+    }
+    case CodeFinderWidget: {
+        QScriptDebuggerCodeFinderWidgetInterface *w = codeFinderWidget();
+        if (!w && widgetFactory()) {
+            w = widgetFactory()->createCodeFinderWidget();
+            setCodeFinderWidget(w);
+        }
+        return w;
+    }
+    case BreakpointsWidget: {
+        QScriptBreakpointsWidgetInterface *w = breakpointsWidget();
+        if (!w && widgetFactory()) {
+            w = widgetFactory()->createBreakpointsWidget();
+            setBreakpointsWidget(w);
+        }
+        return w;
+    }
+    case DebugOutputWidget: {
+        QScriptDebugOutputWidgetInterface *w = debugOutputWidget();
+        if (!w && widgetFactory()) {
+            w = widgetFactory()->createDebugOutputWidget();
+            setDebugOutputWidget(w);
+        }
+        return w;
+    }
+    case ErrorLogWidget: {
+        QScriptErrorLogWidgetInterface *w = errorLogWidget();
+        if (!w && widgetFactory()) {
+            w = widgetFactory()->createErrorLogWidget();
+            setErrorLogWidget(w);
+        }
+        return w;
+    }
+    }
+    return 0;
+}
+
+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, QScriptDebugger::tr("Interrupt"), parent);
+        d->interruptAction->setEnabled(!d->interactive);
+        d->interruptAction->setShortcut(QScriptDebugger::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, QScriptDebugger::tr("Continue"), parent);
+        d->continueAction->setEnabled(d->interactive);
+        d->continueAction->setShortcut(QScriptDebugger::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, QScriptDebugger::tr("Step Into"), parent);
+        d->stepIntoAction->setEnabled(d->interactive);
+        d->stepIntoAction->setShortcut(QScriptDebugger::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, QScriptDebugger::tr("Step Over"), parent);
+        d->stepOverAction->setEnabled(d->interactive);
+        d->stepOverAction->setShortcut(QScriptDebugger::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, QScriptDebugger::tr("Step Out"), parent);
+        d->stepOutAction->setEnabled(d->interactive);
+        d->stepOutAction->setShortcut(QScriptDebugger::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, QScriptDebugger::tr("Run to Cursor"), parent);
+        d->runToCursorAction->setEnabled(d->interactive);
+        d->runToCursorAction->setShortcut(QScriptDebugger::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,
+                                                           QScriptDebugger::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,
+                                                             QScriptDebugger::tr("Toggle Breakpoint"), parent);
+        d->toggleBreakpointAction->setShortcut(QScriptDebugger::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, QScriptDebugger::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, QScriptDebugger::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, QScriptDebugger::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, QScriptDebugger::tr("&Find in Script..."), parent);
+        d->findInScriptAction->setShortcut(QScriptDebugger::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, QScriptDebugger::tr("Find &Next"), parent);
+        d->findNextInScriptAction->setEnabled(d->codeFinderWidget && !d->codeFinderWidget->text().isEmpty());
+        d->findNextInScriptAction->setShortcut(QScriptDebugger::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, QScriptDebugger::tr("Find &Previous"), parent);
+        d->findPreviousInScriptAction->setEnabled(d->codeFinderWidget && !d->codeFinderWidget->text().isEmpty());
+        d->findPreviousInScriptAction->setShortcut(QScriptDebugger::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, QScriptDebugger::tr("Go to Line"), parent);
+        d->goToLineAction->setShortcut(QScriptDebugger::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;
+}
+
+QMenu *QScriptDebugger::createStandardMenu(QWidget *widgetParent, QObject *actionParent)
+{
+    QMenu *menu = new QMenu(widgetParent);
+    menu->setTitle(QScriptDebugger::tr("Debug"));
+    menu->addAction(action(ContinueAction, actionParent));
+    menu->addAction(action(InterruptAction, actionParent));
+    menu->addAction(action(StepIntoAction, actionParent));
+    menu->addAction(action(StepOverAction, actionParent));
+    menu->addAction(action(StepOutAction, actionParent));
+    menu->addAction(action(RunToCursorAction, actionParent));
+    menu->addAction(action(RunToNewScriptAction, actionParent));
+
+    menu->addSeparator();
+    menu->addAction(action(ToggleBreakpointAction, actionParent));
+
+    menu->addSeparator();
+    menu->addAction(action(ClearDebugOutputAction, actionParent));
+    menu->addAction(action(ClearErrorLogAction, actionParent));
+    menu->addAction(action(ClearConsoleAction, actionParent));
+
+    return menu;
+}
+
+#ifndef QT_NO_TOOLBAR
+QToolBar *QScriptDebugger::createStandardToolBar(QWidget *widgetParent, QObject *actionParent)
+{
+    QToolBar *tb = new QToolBar(widgetParent);
+    tb->setObjectName(QLatin1String("qtscriptdebugger_standardToolBar"));
+    tb->addAction(action(ContinueAction, actionParent));
+    tb->addAction(action(InterruptAction, actionParent));
+    tb->addAction(action(StepIntoAction, actionParent));
+    tb->addAction(action(StepOverAction, actionParent));
+    tb->addAction(action(StepOutAction, actionParent));
+    tb->addAction(action(RunToCursorAction, actionParent));
+    tb->addAction(action(RunToNewScriptAction, actionParent));
+    tb->addSeparator();
+    tb->addAction(action(FindInScriptAction, actionParent));
+    return tb;
+}
+#endif
+
+bool QScriptDebugger::isInteractive() const
+{
+    Q_D(const QScriptDebugger);
+    return d->interactive;
+}
+
+/*!
+  \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"