blob: 5549f9235976ddd73c9a669dea0a90e2a92bc9b5 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtSCriptTools module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $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 <QtWidgets/qaction.h>
#include <QtGui/qevent.h>
#include <QtGui/qicon.h>
#include <QtWidgets/qinputdialog.h>
#include <QtWidgets/qmenu.h>
#include <QtWidgets/qtoolbar.h>
#include <QtWidgets/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"));
}
#if QT_CONFIG(tooltip)
QToolTip::showText(m_pos, tip);
#endif
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;
#ifndef QT_NO_INPUTDIALOG
bool ok = false;
int lineNumber = QInputDialog::getInt(0, QScriptDebugger::tr("Go to Line"),
QScriptDebugger::tr("Line:"),
view->cursorLineNumber(),
1, INT_MAX, 1, &ok);
if (ok)
view->gotoLine(lineNumber);
#endif
}
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) {}
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;
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);
#ifndef QT_NO_SHORTCUT
d->interruptAction->setShortcut(QScriptDebugger::tr("Shift+F5"));
#endif
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);
#ifndef QT_NO_SHORTCUT
d->continueAction->setShortcut(QScriptDebugger::tr("F5"));
#endif
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);
#ifndef QT_NO_SHORTCUT
d->stepIntoAction->setShortcut(QScriptDebugger::tr("F11"));
#endif
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);
#ifndef QT_NO_SHORTCUT
d->stepOverAction->setShortcut(QScriptDebugger::tr("F10"));
#endif
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);
#ifndef QT_NO_SHORTCUT
d->stepOutAction->setShortcut(QScriptDebugger::tr("Shift+F11"));
#endif
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);
#ifndef QT_NO_SHORTCUT
d->runToCursorAction->setShortcut(QScriptDebugger::tr("Ctrl+F10"));
#endif
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);
#ifndef QT_NO_SHORTCUT
d->toggleBreakpointAction->setShortcut(QScriptDebugger::tr("F9"));
#endif
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);
#ifndef QT_NO_SHORTCUT
d->findInScriptAction->setShortcut(QScriptDebugger::tr("Ctrl+F"));
#endif
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());
#ifndef QT_NO_SHORTCUT
d->findNextInScriptAction->setShortcut(QScriptDebugger::tr("F3"));
#endif
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());
#ifndef QT_NO_SHORTCUT
d->findPreviousInScriptAction->setShortcut(QScriptDebugger::tr("Shift+F3"));
#endif
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);
#ifndef QT_NO_SHORTCUT
d->goToLineAction->setShortcut(QScriptDebugger::tr("Ctrl+G"));
#endif
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"