summaryrefslogtreecommitdiff
path: root/src/scripttools/debugging/qscriptdebuggerlocalsmodel.cpp
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commitc4af45c2914381172e1bd7ee528481edaa2fff1a (patch)
tree01265e109316fda93845e1c96c5e566d870f51d0 /src/scripttools/debugging/qscriptdebuggerlocalsmodel.cpp
downloadqtscript-c4af45c2914381172e1bd7ee528481edaa2fff1a.tar.gz
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/scripttools/debugging/qscriptdebuggerlocalsmodel.cpp')
-rw-r--r--src/scripttools/debugging/qscriptdebuggerlocalsmodel.cpp959
1 files changed, 959 insertions, 0 deletions
diff --git a/src/scripttools/debugging/qscriptdebuggerlocalsmodel.cpp b/src/scripttools/debugging/qscriptdebuggerlocalsmodel.cpp
new file mode 100644
index 0000000..efecd73
--- /dev/null
+++ b/src/scripttools/debugging/qscriptdebuggerlocalsmodel.cpp
@@ -0,0 +1,959 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 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 "qscriptdebuggerlocalsmodel_p.h"
+#include "qscriptdebuggercommandschedulerjob_p.h"
+#include "qscriptdebuggervalue_p.h"
+#include "qscriptdebuggerresponse_p.h"
+#include "qscriptdebuggerevent_p.h"
+#include "qscriptdebuggervalueproperty_p.h"
+#include "qscriptdebuggercommandschedulerinterface_p.h"
+#include "qscriptdebuggercommandschedulerfrontend_p.h"
+#include "qscriptdebuggerjobschedulerinterface_p.h"
+#include "qscriptdebuggerobjectsnapshotdelta_p.h"
+
+#include "private/qabstractitemmodel_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qpointer.h>
+#include <QtGui/qbrush.h>
+#include <QtGui/qfont.h>
+
+Q_DECLARE_METATYPE(QScriptDebuggerObjectSnapshotDelta)
+
+QT_BEGIN_NAMESPACE
+
+struct QScriptDebuggerLocalsModelNode
+{
+ enum PopulationState {
+ NotPopulated,
+ Populating,
+ Populated
+ };
+
+ QScriptDebuggerLocalsModelNode()
+ : parent(0), populationState(NotPopulated), snapshotId(-1), changed(false) {}
+
+ QScriptDebuggerLocalsModelNode(
+ const QScriptDebuggerValueProperty &prop,
+ QScriptDebuggerLocalsModelNode *par)
+ : property(prop), parent(par),
+ populationState(NotPopulated), snapshotId(-1), changed(false)
+ {
+ parent->children.append(this);
+ }
+
+ ~QScriptDebuggerLocalsModelNode() { qDeleteAll(children); }
+
+ QScriptDebuggerLocalsModelNode *findChild(const QString &name)
+ {
+ for (int i = 0; i < children.size(); ++i) {
+ QScriptDebuggerLocalsModelNode *child = children.at(i);
+ if (child->property.name() == name)
+ return child;
+ }
+ return 0;
+ }
+
+ QScriptDebuggerValueProperty property;
+ QScriptDebuggerLocalsModelNode *parent;
+ QList<QScriptDebuggerLocalsModelNode*> children;
+ PopulationState populationState;
+ int snapshotId;
+ bool changed;
+};
+
+class QScriptDebuggerLocalsModelPrivate
+ : public QAbstractItemModelPrivate
+{
+ Q_DECLARE_PUBLIC(QScriptDebuggerLocalsModel)
+public:
+ QScriptDebuggerLocalsModelPrivate();
+ ~QScriptDebuggerLocalsModelPrivate();
+
+ static QScriptDebuggerLocalsModelPrivate *get(QScriptDebuggerLocalsModel *q);
+
+ QModelIndex addTopLevelObject(const QString &name, const QScriptDebuggerValue &object);
+
+ QScriptDebuggerLocalsModelNode *nodeFromIndex(const QModelIndex &index) const;
+ QModelIndex indexFromNode(QScriptDebuggerLocalsModelNode *node) const;
+ bool isTopLevelNode(QScriptDebuggerLocalsModelNode *node) const;
+
+ void populateIndex(const QModelIndex &index);
+ void reallyPopulateIndex(const QModelIndex &index,
+ const QScriptDebuggerValuePropertyList &props);
+ void syncIndex(const QModelIndex &index);
+ void reallySyncIndex(const QModelIndex &index,
+ const QScriptDebuggerObjectSnapshotDelta &delta);
+ void syncTopLevelNodes();
+ void removeTopLevelNodes();
+ void emitScopeObjectAvailable(const QModelIndex &index);
+
+ void emitDataChanged(const QModelIndex &tl, const QModelIndex &br);
+ void removeChild(const QModelIndex &parentIndex,
+ QScriptDebuggerLocalsModelNode *parentNode, int row);
+ void depopulate(QScriptDebuggerLocalsModelNode *node);
+ void repopulate(QScriptDebuggerLocalsModelNode *node);
+ void addChildren(const QModelIndex &parentIndex,
+ QScriptDebuggerLocalsModelNode *parentNode,
+ const QScriptDebuggerValuePropertyList &props);
+
+ void deleteObjectSnapshots(const QList<qint64> &snapshotIds);
+ void deleteAllObjectSnapshots();
+
+ QScriptDebuggerJobSchedulerInterface *jobScheduler;
+ QScriptDebuggerCommandSchedulerInterface *commandScheduler;
+ QScriptDebuggerLocalsModelNode *invisibleRootNode;
+ int frameIndex;
+};
+
+QScriptDebuggerLocalsModelPrivate::QScriptDebuggerLocalsModelPrivate()
+{
+ invisibleRootNode = new QScriptDebuggerLocalsModelNode();
+ frameIndex = -1;
+}
+
+QScriptDebuggerLocalsModelPrivate::~QScriptDebuggerLocalsModelPrivate()
+{
+ delete invisibleRootNode;
+}
+
+void QScriptDebuggerLocalsModelPrivate::emitDataChanged(const QModelIndex &tl, const QModelIndex &br)
+{
+ q_func()->dataChanged(tl, br);
+}
+
+static QList<qint64> findSnapshotIdsRecursively(QScriptDebuggerLocalsModelNode *root)
+{
+ QList<qint64> result;
+ if (root->snapshotId == -1) {
+ Q_ASSERT(root->children.isEmpty());
+ return result;
+ }
+ QList<QScriptDebuggerLocalsModelNode*> nodeStack;
+ nodeStack.append(root);
+ while (!nodeStack.isEmpty()) {
+ QScriptDebuggerLocalsModelNode *node = nodeStack.takeFirst();
+ result.append(node->snapshotId);
+ for (int i = 0; i < node->children.count(); ++i) {
+ QScriptDebuggerLocalsModelNode *child = node->children.at(i);
+ if (child->snapshotId != -1)
+ nodeStack.prepend(child);
+ }
+ }
+ return result;
+}
+
+void QScriptDebuggerLocalsModelPrivate::removeChild(const QModelIndex &parentIndex,
+ QScriptDebuggerLocalsModelNode *parentNode,
+ int row)
+{
+ Q_Q(QScriptDebuggerLocalsModel);
+ q->beginRemoveRows(parentIndex, row, row);
+ QScriptDebuggerLocalsModelNode *child = parentNode->children.takeAt(row);
+ QList<qint64> snapshotIds = findSnapshotIdsRecursively(child);
+ delete child;
+ q->endRemoveRows();
+ deleteObjectSnapshots(snapshotIds);
+}
+
+void QScriptDebuggerLocalsModelPrivate::depopulate(QScriptDebuggerLocalsModelNode *node)
+{
+ Q_Q(QScriptDebuggerLocalsModel);
+ bool hasChildren = !node->children.isEmpty();
+ if (hasChildren)
+ q->beginRemoveRows(indexFromNode(node), 0, node->children.count() - 1);
+ QList<qint64> snapshotIds = findSnapshotIdsRecursively(node);
+ qDeleteAll(node->children);
+ node->children.clear();
+ node->snapshotId = -1;
+ node->populationState = QScriptDebuggerLocalsModelNode::NotPopulated;
+ if (hasChildren)
+ q->endRemoveRows();
+ deleteObjectSnapshots(snapshotIds);
+}
+
+void QScriptDebuggerLocalsModelPrivate::repopulate(QScriptDebuggerLocalsModelNode *node)
+{
+ if (node->populationState != QScriptDebuggerLocalsModelNode::Populated)
+ return;
+ depopulate(node);
+ if (node->property.value().type() == QScriptDebuggerValue::ObjectValue)
+ populateIndex(indexFromNode(node));
+}
+
+void QScriptDebuggerLocalsModelPrivate::addChildren(const QModelIndex &parentIndex,
+ QScriptDebuggerLocalsModelNode *parentNode,
+ const QScriptDebuggerValuePropertyList &props)
+{
+ Q_Q(QScriptDebuggerLocalsModel);
+ if (props.isEmpty())
+ return;
+ int first = parentNode->children.size();
+ int last = first + props.size() - 1;
+ q->beginInsertRows(parentIndex, first, last);
+ for (int i = 0; i < props.size(); ++i)
+ new QScriptDebuggerLocalsModelNode(props.at(i), parentNode);
+ q->endInsertRows();
+}
+
+void QScriptDebuggerLocalsModelPrivate::deleteObjectSnapshots(const QList<qint64> &snapshotIds)
+{
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler, 0);
+ for (int i = 0; i < snapshotIds.size(); ++i)
+ frontend.scheduleDeleteScriptObjectSnapshot(snapshotIds.at(i));
+}
+
+void QScriptDebuggerLocalsModelPrivate::deleteAllObjectSnapshots()
+{
+ QList<qint64> snapshotIds;
+ for (int i = 0; i < invisibleRootNode->children.count(); ++i)
+ snapshotIds += findSnapshotIdsRecursively(invisibleRootNode->children.at(i));
+ deleteObjectSnapshots(snapshotIds);
+}
+
+QScriptDebuggerLocalsModelPrivate *QScriptDebuggerLocalsModelPrivate::get(QScriptDebuggerLocalsModel *q)
+{
+ return q->d_func();
+}
+
+namespace {
+
+class SetPropertyJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+ SetPropertyJob(const QPersistentModelIndex &index,
+ const QString &expression,
+ QScriptDebuggerCommandSchedulerInterface *scheduler)
+ : QScriptDebuggerCommandSchedulerJob(scheduler),
+ m_index(index), m_expression(expression), m_state(0) {}
+
+ QScriptDebuggerLocalsModelPrivate *model() const
+ {
+ if (!m_index.isValid())
+ return 0;
+ QAbstractItemModel *m = const_cast<QAbstractItemModel*>(m_index.model());
+ QScriptDebuggerLocalsModel *lm = qobject_cast<QScriptDebuggerLocalsModel*>(m);
+ return QScriptDebuggerLocalsModelPrivate::get(lm);
+ }
+
+ void start()
+ {
+ if (!m_index.isValid()) {
+ // nothing to do, the node has been removed
+ return;
+ }
+ QScriptDebuggerLocalsModelNode *node = model()->nodeFromIndex(m_index);
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleEvaluate(model()->frameIndex, m_expression,
+ QString::fromLatin1("set property '%0' (%1)")
+ .arg(node->property.name())
+ .arg(QDateTime::currentDateTime().toString()));
+ }
+
+ void handleResponse(const QScriptDebuggerResponse &, int)
+ {
+ switch (m_state) {
+ case 0:
+ hibernateUntilEvaluateFinished();
+ ++m_state;
+ break;
+ case 1:
+ finish();
+ break;
+ }
+ }
+
+ void evaluateFinished(const QScriptDebuggerValue &result)
+ {
+ if (!m_index.isValid()) {
+ // nothing to do, the node has been removed
+ return;
+ }
+ QScriptDebuggerLocalsModelNode *node = model()->nodeFromIndex(m_index);
+ Q_ASSERT(node->parent != 0);
+ QScriptDebuggerValue object = node->parent->property.value();
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleSetScriptValueProperty(object, node->property.name(), result);
+ }
+
+private:
+ QPersistentModelIndex m_index;
+ QString m_expression;
+ int m_state;
+};
+
+} // namespace
+
+QScriptDebuggerLocalsModelNode *QScriptDebuggerLocalsModelPrivate::nodeFromIndex(
+ const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return invisibleRootNode;
+ return static_cast<QScriptDebuggerLocalsModelNode*>(index.internalPointer());
+}
+
+QModelIndex QScriptDebuggerLocalsModelPrivate::indexFromNode(
+ QScriptDebuggerLocalsModelNode *node) const
+{
+ if (!node || (node == invisibleRootNode))
+ return QModelIndex();
+ QScriptDebuggerLocalsModelNode *par = node->parent;
+ int row = par ? par->children.indexOf(node) : 0;
+ return createIndex(row, 0, node);
+}
+
+bool QScriptDebuggerLocalsModelPrivate::isTopLevelNode(QScriptDebuggerLocalsModelNode *node) const
+{
+ return node && (node->parent == invisibleRootNode);
+}
+
+namespace {
+
+class PopulateModelIndexJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+ PopulateModelIndexJob(const QPersistentModelIndex &index,
+ QScriptDebuggerCommandSchedulerInterface *scheduler)
+ : QScriptDebuggerCommandSchedulerJob(scheduler),
+ m_index(index), m_state(0)
+ { }
+
+ QScriptDebuggerLocalsModelPrivate *model() const
+ {
+ if (!m_index.isValid())
+ return 0;
+ QAbstractItemModel *m = const_cast<QAbstractItemModel*>(m_index.model());
+ QScriptDebuggerLocalsModel *lm = qobject_cast<QScriptDebuggerLocalsModel*>(m);
+ return QScriptDebuggerLocalsModelPrivate::get(lm);
+ }
+
+ void start()
+ {
+ if (!m_index.isValid()) {
+ // nothing to do, the node has been removed
+ finish();
+ return;
+ }
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleNewScriptObjectSnapshot();
+ }
+
+ void handleResponse(const QScriptDebuggerResponse &response,
+ int)
+ {
+ if (!m_index.isValid()) {
+ // the node has been removed
+ finish();
+ return;
+ }
+ switch (m_state) {
+ case 0: {
+ QScriptDebuggerLocalsModelNode *node = model()->nodeFromIndex(m_index);
+ Q_ASSERT(node->populationState == QScriptDebuggerLocalsModelNode::Populating);
+ node->snapshotId = response.resultAsInt();
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleScriptObjectSnapshotCapture(node->snapshotId, node->property.value());
+ ++m_state;
+ } break;
+ case 1: {
+ QScriptDebuggerObjectSnapshotDelta delta;
+ delta = qvariant_cast<QScriptDebuggerObjectSnapshotDelta>(response.result());
+ Q_ASSERT(delta.removedProperties.isEmpty());
+ Q_ASSERT(delta.changedProperties.isEmpty());
+ QScriptDebuggerValuePropertyList props = delta.addedProperties;
+ model()->reallyPopulateIndex(m_index, props);
+ finish();
+ } break;
+ }
+ }
+
+private:
+ QPersistentModelIndex m_index;
+ int m_state;
+};
+
+} // namespace
+
+void QScriptDebuggerLocalsModelPrivate::populateIndex(
+ const QModelIndex &index)
+{
+ if (!index.isValid())
+ return;
+ QScriptDebuggerLocalsModelNode *node = nodeFromIndex(index);
+ if (node->populationState != QScriptDebuggerLocalsModelNode::NotPopulated)
+ return;
+ if (node->property.value().type() != QScriptDebuggerValue::ObjectValue)
+ return;
+ node->populationState = QScriptDebuggerLocalsModelNode::Populating;
+ QScriptDebuggerJob *job = new PopulateModelIndexJob(index, commandScheduler);
+ jobScheduler->scheduleJob(job);
+}
+
+void QScriptDebuggerLocalsModelPrivate::reallyPopulateIndex(
+ const QModelIndex &index,
+ const QScriptDebuggerValuePropertyList &props)
+{
+ if (!index.isValid())
+ return;
+ QScriptDebuggerLocalsModelNode *node = nodeFromIndex(index);
+ Q_ASSERT(node->populationState == QScriptDebuggerLocalsModelNode::Populating);
+ node->populationState = QScriptDebuggerLocalsModelNode::Populated;
+ addChildren(index, node, props);
+}
+
+QScriptDebuggerLocalsModel::QScriptDebuggerLocalsModel(
+ QScriptDebuggerJobSchedulerInterface *jobScheduler,
+ QScriptDebuggerCommandSchedulerInterface *commandScheduler,
+ QObject *parent)
+ : QAbstractItemModel(*new QScriptDebuggerLocalsModelPrivate, parent)
+{
+ Q_D(QScriptDebuggerLocalsModel);
+ d->jobScheduler = jobScheduler;
+ d->commandScheduler = commandScheduler;
+}
+
+QScriptDebuggerLocalsModel::~QScriptDebuggerLocalsModel()
+{
+}
+
+QModelIndex QScriptDebuggerLocalsModelPrivate::addTopLevelObject(const QString &name, const QScriptDebuggerValue &object)
+{
+ Q_Q(QScriptDebuggerLocalsModel);
+ QScriptDebuggerLocalsModelNode *node = invisibleRootNode->findChild(name);
+ if (node)
+ return indexFromNode(node);
+ QScriptDebuggerValueProperty prop(name, object,
+ QString::fromLatin1(""), // ### string representation of object
+ /*flags=*/0);
+ int rowIndex = invisibleRootNode->children.size();
+ q->beginInsertRows(QModelIndex(), rowIndex, rowIndex);
+ node = new QScriptDebuggerLocalsModelNode(prop, invisibleRootNode);
+ q->endInsertRows();
+ return indexFromNode(node);
+}
+
+namespace {
+
+class InitModelJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+ InitModelJob(QScriptDebuggerLocalsModel *model,
+ int frameIndex,
+ QScriptDebuggerCommandSchedulerInterface *scheduler)
+ : QScriptDebuggerCommandSchedulerJob(scheduler),
+ m_model(model), m_frameIndex(frameIndex), m_state(0)
+ { }
+
+ void start()
+ {
+ if (!m_model) {
+ // Model has been deleted.
+ finish();
+ return;
+ }
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleGetScopeChain(m_frameIndex);
+ }
+
+ void handleResponse(const QScriptDebuggerResponse &response,
+ int)
+ {
+ if (!m_model) {
+ // Model has been deleted.
+ finish();
+ return;
+ }
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ QScriptDebuggerLocalsModelPrivate *model_d = QScriptDebuggerLocalsModelPrivate::get(m_model);
+ switch (m_state) {
+ case 0: {
+ QScriptDebuggerValueList scopeChain = response.resultAsScriptValueList();
+ for (int i = 0; i < scopeChain.size(); ++i) {
+ const QScriptDebuggerValue &scopeObject = scopeChain.at(i);
+ QString name = QString::fromLatin1("Scope");
+ if (i > 0)
+ name.append(QString::fromLatin1(" (%0)").arg(i));
+ QModelIndex index = model_d->addTopLevelObject(name, scopeObject);
+ if (i == 0)
+ model_d->emitScopeObjectAvailable(index);
+ }
+ frontend.scheduleGetThisObject(m_frameIndex);
+ ++m_state;
+ } break;
+ case 1: {
+ QScriptDebuggerValue thisObject = response.resultAsScriptValue();
+ model_d->addTopLevelObject(QLatin1String("this"), thisObject);
+ finish();
+ } break;
+ }
+ }
+
+private:
+ QPointer<QScriptDebuggerLocalsModel> m_model;
+ int m_frameIndex;
+ int m_state;
+};
+
+} // namespace
+
+void QScriptDebuggerLocalsModel::init(int frameIndex)
+{
+ Q_D(QScriptDebuggerLocalsModel);
+ d->frameIndex = frameIndex;
+ QScriptDebuggerJob *job = new InitModelJob(this, frameIndex, d->commandScheduler);
+ d->jobScheduler->scheduleJob(job);
+}
+
+namespace {
+
+class SyncModelJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+ SyncModelJob(QScriptDebuggerLocalsModel *model,
+ int frameIndex,
+ QScriptDebuggerCommandSchedulerInterface *scheduler)
+ : QScriptDebuggerCommandSchedulerJob(scheduler),
+ m_model(model), m_frameIndex(frameIndex), m_state(0)
+ { }
+
+ void start()
+ {
+ if (!m_model) {
+ // Model has been deleted.
+ finish();
+ return;
+ }
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ frontend.scheduleGetScopeChain(m_frameIndex);
+ }
+
+ void handleResponse(const QScriptDebuggerResponse &response,
+ int)
+ {
+ if (!m_model) {
+ // Model has been deleted.
+ finish();
+ return;
+ }
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ switch (m_state) {
+ case 0: {
+ QScriptDebuggerValueList scopeChain = response.resultAsScriptValueList();
+ m_topLevelObjects << scopeChain;
+ frontend.scheduleGetThisObject(m_frameIndex);
+ ++m_state;
+ } break;
+ case 1: {
+ QScriptDebuggerLocalsModelPrivate *model_d = QScriptDebuggerLocalsModelPrivate::get(m_model);
+ QScriptDebuggerValue thisObject = response.resultAsScriptValue();
+ m_topLevelObjects.append(thisObject);
+ bool equal = (m_topLevelObjects.size() == model_d->invisibleRootNode->children.size());
+ for (int i = 0; equal && (i < m_topLevelObjects.size()); ++i) {
+ const QScriptDebuggerValue &object = m_topLevelObjects.at(i);
+ equal = (object == model_d->invisibleRootNode->children.at(i)->property.value());
+ }
+ if (!equal) {
+ // the scope chain and/or this-object changed, so invalidate the model.
+ // we could try to be more clever, i.e. figure out
+ // exactly which objects were popped/pushed
+ model_d->removeTopLevelNodes();
+ for (int j = 0; j < m_topLevelObjects.size(); ++j) {
+ const QScriptDebuggerValue &object = m_topLevelObjects.at(j);
+ QString name;
+ if (j == m_topLevelObjects.size()-1) {
+ name = QString::fromLatin1("this");
+ } else {
+ name = QString::fromLatin1("Scope");
+ if (j > 0)
+ name.append(QString::fromLatin1(" (%0)").arg(j));
+ }
+ QModelIndex index = model_d->addTopLevelObject(name, object);
+ if (j == 0)
+ model_d->emitScopeObjectAvailable(index);
+ }
+ } else {
+ model_d->syncTopLevelNodes();
+ }
+ finish();
+ } break;
+ }
+ }
+
+private:
+ QPointer<QScriptDebuggerLocalsModel> m_model;
+ int m_frameIndex;
+ int m_state;
+ QScriptDebuggerValueList m_topLevelObjects;
+};
+
+} // namespace
+
+void QScriptDebuggerLocalsModel::sync(int frameIndex)
+{
+ Q_D(QScriptDebuggerLocalsModel);
+ d->frameIndex = frameIndex;
+ QScriptDebuggerJob *job = new SyncModelJob(this, frameIndex, d->commandScheduler);
+ d->jobScheduler->scheduleJob(job);
+}
+
+namespace {
+
+class SyncModelIndexJob : public QScriptDebuggerCommandSchedulerJob
+{
+public:
+ SyncModelIndexJob(const QPersistentModelIndex &index,
+ QScriptDebuggerCommandSchedulerInterface *scheduler)
+ : QScriptDebuggerCommandSchedulerJob(scheduler),
+ m_index(index)
+ { }
+
+ QScriptDebuggerLocalsModelPrivate *model() const
+ {
+ if (!m_index.isValid())
+ return 0;
+ QAbstractItemModel *m = const_cast<QAbstractItemModel*>(m_index.model());
+ QScriptDebuggerLocalsModel *lm = qobject_cast<QScriptDebuggerLocalsModel*>(m);
+ return QScriptDebuggerLocalsModelPrivate::get(lm);
+ }
+
+ void start()
+ {
+ if (!m_index.isValid()) {
+ // nothing to do, the node has been removed
+ finish();
+ return;
+ }
+ QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
+ QScriptDebuggerLocalsModelNode *node = model()->nodeFromIndex(m_index);
+ frontend.scheduleScriptObjectSnapshotCapture(node->snapshotId, node->property.value());
+ }
+
+ void handleResponse(const QScriptDebuggerResponse &response,
+ int)
+ {
+ QScriptDebuggerObjectSnapshotDelta delta;
+ delta = qvariant_cast<QScriptDebuggerObjectSnapshotDelta>(response.result());
+ model()->reallySyncIndex(m_index, delta);
+ finish();
+ }
+
+private:
+ QPersistentModelIndex m_index;
+};
+
+} // namespace
+
+void QScriptDebuggerLocalsModelPrivate::syncIndex(const QModelIndex &index)
+{
+ if (!index.isValid())
+ return;
+ QScriptDebuggerLocalsModelNode *node = nodeFromIndex(index);
+ if (node->populationState != QScriptDebuggerLocalsModelNode::Populated)
+ return;
+ QScriptDebuggerJob *job = new SyncModelIndexJob(index, commandScheduler);
+ jobScheduler->scheduleJob(job);
+}
+
+void QScriptDebuggerLocalsModelPrivate::reallySyncIndex(const QModelIndex &index,
+ const QScriptDebuggerObjectSnapshotDelta &delta)
+{
+ if (!index.isValid())
+ return;
+ QScriptDebuggerLocalsModelNode *node = nodeFromIndex(index);
+ // update or remove existing children
+ for (int i = 0; i < node->children.count(); ++i) {
+ QScriptDebuggerLocalsModelNode *child = node->children.at(i);
+ int j;
+ for (j = 0; j < delta.changedProperties.count(); ++j) {
+ if (child->property.name() == delta.changedProperties.at(j).name()) {
+ child->property = delta.changedProperties.at(j);
+ child->changed = true;
+ emitDataChanged(index, index.sibling(0, 1));
+ repopulate(child);
+ break;
+ }
+ }
+ if (j != delta.changedProperties.count())
+ continue; // was changed
+ for (j = 0; j < delta.removedProperties.count(); ++j) {
+ if (child->property.name() == delta.removedProperties.at(j)) {
+ removeChild(index, node, i);
+ --i;
+ break;
+ }
+ }
+ if (j != delta.removedProperties.count())
+ continue; // was removed
+ // neither changed nor removed, but its children might be
+ if (child->populationState == QScriptDebuggerLocalsModelNode::Populated) {
+ QScriptDebuggerJob *job = new SyncModelIndexJob(indexFromNode(child), commandScheduler);
+ jobScheduler->scheduleJob(job);
+ }
+ }
+ addChildren(index, node, delta.addedProperties);
+}
+
+void QScriptDebuggerLocalsModelPrivate::syncTopLevelNodes()
+{
+ Q_Q(QScriptDebuggerLocalsModel);
+ for (int i = 0; i < invisibleRootNode->children.count(); ++i) {
+ QModelIndex index = q->index(i, 0, QModelIndex());
+ syncIndex(index);
+ if (i == 0)
+ emit q->scopeObjectAvailable(index);
+ }
+}
+
+void QScriptDebuggerLocalsModelPrivate::removeTopLevelNodes()
+{
+ while (!invisibleRootNode->children.isEmpty())
+ removeChild(QModelIndex(), invisibleRootNode, 0);
+}
+
+void QScriptDebuggerLocalsModelPrivate::emitScopeObjectAvailable(const QModelIndex &index)
+{
+ emit q_func()->scopeObjectAvailable(index);
+}
+
+int QScriptDebuggerLocalsModel::frameIndex() const
+{
+ Q_D(const QScriptDebuggerLocalsModel);
+ return d->frameIndex;
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QScriptDebuggerLocalsModel::index(int row, int column, const QModelIndex &parent) const
+{
+ Q_D(const QScriptDebuggerLocalsModel);
+ QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(parent);
+ if ((row < 0) || (row >= node->children.count()))
+ return QModelIndex();
+ return createIndex(row, column, node->children.at(row));
+}
+
+/*!
+ \reimp
+*/
+QModelIndex QScriptDebuggerLocalsModel::parent(const QModelIndex &index) const
+{
+ Q_D(const QScriptDebuggerLocalsModel);
+ if (!index.isValid())
+ return QModelIndex();
+ QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(index);
+ return d->indexFromNode(node->parent);
+}
+
+/*!
+ \reimp
+*/
+int QScriptDebuggerLocalsModel::columnCount(const QModelIndex &) const
+{
+ return 2;
+}
+
+/*!
+ \reimp
+*/
+int QScriptDebuggerLocalsModel::rowCount(const QModelIndex &parent) const
+{
+ Q_D(const QScriptDebuggerLocalsModel);
+ // ### need this to make it work with a sortfilterproxymodel (QSFPM is too eager)
+ const_cast<QScriptDebuggerLocalsModel*>(this)->fetchMore(parent);
+ QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(parent);
+ return node ? node->children.count() : 0;
+}
+
+/*!
+ \reimp
+*/
+QVariant QScriptDebuggerLocalsModel::data(const QModelIndex &index, int role) const
+{
+ Q_D(const QScriptDebuggerLocalsModel);
+ if (!index.isValid())
+ return QVariant();
+ QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(index);
+ if (role == Qt::DisplayRole) {
+ if (index.column() == 0)
+ return node->property.name();
+ else if (index.column() == 1) {
+ QString str = node->property.valueAsString();
+ if (str.indexOf(QLatin1Char('\n')) != -1) {
+ QStringList lines = str.split(QLatin1Char('\n'));
+ int lineCount = lines.size();
+ if (lineCount > 1) {
+ lines = lines.mid(0, 1);
+ lines.append(QString::fromLatin1("(... %0 more lines ...)").arg(lineCount - 1));
+ }
+ str = lines.join(QLatin1String("\n"));
+ }
+ return str;
+ }
+ } else if (role == Qt::EditRole) {
+ if ((index.column() == 1) && !d->isTopLevelNode(node)) {
+ QString str = node->property.valueAsString();
+ if (node->property.value().type() == QScriptDebuggerValue::StringValue) {
+ // escape
+ str.replace(QLatin1Char('\"'), QLatin1String("\\\""));
+ str.prepend(QLatin1Char('\"'));
+ str.append(QLatin1Char('\"'));
+ }
+ return str;
+ }
+ } else if (role == Qt::ToolTipRole) {
+ if (index.column() == 1) {
+ QString str = node->property.valueAsString();
+ if (str.indexOf(QLatin1Char('\n')) != -1)
+ return str;
+ }
+ }
+ // ### do this in the delegate
+ else if (role == Qt::BackgroundRole) {
+ if (d->isTopLevelNode(node))
+ return QBrush(Qt::darkGray);
+ } else if (role == Qt::TextColorRole) {
+ if (d->isTopLevelNode(node))
+ return QColor(Qt::white);
+ } else if (role == Qt::FontRole) {
+ if (d->isTopLevelNode(node) || node->changed) {
+ QFont fnt;
+ fnt.setBold(true);
+ return fnt;
+ }
+ }
+ return QVariant();
+}
+
+/*!
+ \reimp
+*/
+bool QScriptDebuggerLocalsModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ Q_D(QScriptDebuggerLocalsModel);
+ if (!index.isValid())
+ return false;
+ if (role != Qt::EditRole)
+ return false;
+ QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(index);
+ if (!node)
+ return false;
+ QString expr = value.toString().trimmed();
+ if (expr.isEmpty())
+ return false;
+ QScriptDebuggerJob *job = new SetPropertyJob(index, expr, d->commandScheduler);
+ d->jobScheduler->scheduleJob(job);
+ return true;
+}
+
+/*!
+ \reimp
+*/
+QVariant QScriptDebuggerLocalsModel::headerData(int section, Qt::Orientation orient, int role) const
+{
+ if (orient == Qt::Horizontal) {
+ if (role == Qt::DisplayRole) {
+ if (section == 0)
+ return QCoreApplication::translate("QScriptDebuggerLocalsModel", "Name");
+ else if (section == 1)
+ return QCoreApplication::translate("QScriptDebuggerLocalsModel", "Value");
+ }
+ }
+ return QVariant();
+}
+
+/*!
+ \reimp
+*/
+Qt::ItemFlags QScriptDebuggerLocalsModel::flags(const QModelIndex &index) const
+{
+ Q_D(const QScriptDebuggerLocalsModel);
+ if (!index.isValid())
+ return 0;
+ Qt::ItemFlags ret = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+ if ((index.column() == 1) && index.parent().isValid()) {
+ QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(index);
+ if (!(node->property.flags() & QScriptValue::ReadOnly))
+ ret |= Qt::ItemIsEditable;
+ }
+ return ret;
+}
+
+/*!
+ \reimp
+*/
+bool QScriptDebuggerLocalsModel::hasChildren(const QModelIndex &parent) const
+{
+ Q_D(const QScriptDebuggerLocalsModel);
+ QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(parent);
+ if (!node)
+ return false;
+ return !node->children.isEmpty()
+ || ((node->property.value().type() == QScriptDebuggerValue::ObjectValue)
+ && (node->populationState == QScriptDebuggerLocalsModelNode::NotPopulated));
+}
+
+/*!
+ \reimp
+*/
+bool QScriptDebuggerLocalsModel::canFetchMore(const QModelIndex &parent) const
+{
+ Q_D(const QScriptDebuggerLocalsModel);
+ if (!parent.isValid())
+ return false;
+ QScriptDebuggerLocalsModelNode *node = d->nodeFromIndex(parent);
+ return node
+ && (node->property.value().type() == QScriptDebuggerValue::ObjectValue)
+ && (node->populationState == QScriptDebuggerLocalsModelNode::NotPopulated);
+}
+
+/*!
+ \reimp
+*/
+void QScriptDebuggerLocalsModel::fetchMore(const QModelIndex &parent)
+{
+ Q_D(QScriptDebuggerLocalsModel);
+ d->populateIndex(parent);
+}
+
+QT_END_NAMESPACE