forked from qt-creator/qt-creator
Enabled the use of Debugger-specific watch/locals models.
- Modified WatchModel to handle storage of an hierarchy of WatchItems. - Factored out code for asynchronous population to AsyncWatchModel and added a mixin for convenient handling - Added base class for synchronous models. - Implement simple, synchronous models for CDB, greatly simplifying code and finally getting manual expansion right. Signed-off-by: hjk <qtc-committer@nokia.com>
This commit is contained in:
107
src/plugins/debugger/abstractsyncwatchmodel.cpp
Normal file
107
src/plugins/debugger/abstractsyncwatchmodel.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** Commercial Usage
|
||||
**
|
||||
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||
** accordance with the Qt Commercial License Agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Nokia.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** If you are unsure which license is appropriate for your use, please
|
||||
** contact the sales department at http://www.qtsoftware.com/contact.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "abstractsyncwatchmodel.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
enum { debug = 0 };
|
||||
|
||||
AbstractSyncWatchModel::AbstractSyncWatchModel(WatchHandler *handler, WatchType type, QObject *parent) :
|
||||
WatchModel(handler, type, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractSyncWatchModel::fetchMore(const QModelIndex &parent)
|
||||
{
|
||||
WatchItem *item = watchItem(parent);
|
||||
if (!item || item == root())
|
||||
return;
|
||||
if (debug)
|
||||
qDebug() << ">fetchMore" << item->toString();
|
||||
// Figure out children...
|
||||
if (item->isHasChildrenNeeded()) {
|
||||
m_errorMessage.clear();
|
||||
if (!complete(item, &m_errorMessage)) {
|
||||
item->setHasChildren(false);
|
||||
emit error(m_errorMessage);
|
||||
}
|
||||
}
|
||||
if (item->isChildrenNeeded()) {
|
||||
m_errorMessage.clear();
|
||||
if (fetchChildren(item, &m_errorMessage)) {
|
||||
item->setChildrenUnneeded();
|
||||
} else {
|
||||
item->setHasChildren(false);
|
||||
emit error(m_errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (debug)
|
||||
qDebug() << "<fetchMore" << item->iname << item->children.size();
|
||||
}
|
||||
|
||||
bool AbstractSyncWatchModel::canFetchMore(const QModelIndex &parent) const
|
||||
{
|
||||
WatchItem *item = watchItem(parent);
|
||||
if (!item || item == root())
|
||||
return false;
|
||||
const bool rc = item->isChildrenNeeded() || item->isHasChildrenNeeded();
|
||||
if (debug)
|
||||
qDebug() << "canFetchMore" << rc << item->iname;
|
||||
return rc;
|
||||
}
|
||||
|
||||
QVariant AbstractSyncWatchModel::data(const QModelIndex &idx, int role) const
|
||||
{
|
||||
if (WatchItem *wdata = watchItem(idx)) {
|
||||
const int column = idx.column();
|
||||
// Is any display data (except children) needed?
|
||||
const bool incomplete = (wdata->state & ~WatchData::ChildrenNeeded);
|
||||
if ((debug > 1) && incomplete && column == 0 && role == Qt::DisplayRole)
|
||||
qDebug() << "data()" << "incomplete=" << incomplete << wdata->toString();
|
||||
if (incomplete) {
|
||||
AbstractSyncWatchModel *nonConstThis = const_cast<AbstractSyncWatchModel *>(this);
|
||||
m_errorMessage.clear();
|
||||
if (!nonConstThis->complete(wdata, &m_errorMessage)) {
|
||||
wdata->setAllUnneeded();
|
||||
nonConstThis->emit error(m_errorMessage);
|
||||
}
|
||||
}
|
||||
return WatchModel::data(*wdata, column, role);
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
@@ -27,49 +27,49 @@
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBSTACKFRAMECONTEXT_H
|
||||
#define CDBSTACKFRAMECONTEXT_H
|
||||
#ifndef ABSTRACTSYNCWATCHMODEL_H
|
||||
#define ABSTRACTSYNCWATCHMODEL_H
|
||||
|
||||
#include "watchhandler.h"
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QDebug;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class WatchData;
|
||||
class WatchHandler;
|
||||
class CdbSymbolGroupContext;
|
||||
class CdbDumperHelper;
|
||||
/* AbstractSyncWatchModel: To be used by synchonous debuggers.
|
||||
* Implements all of WatchModel and provides new virtuals for
|
||||
* the debugger to complete items. */
|
||||
|
||||
/* CdbStackFrameContext manages a symbol group context and
|
||||
* a dumper context. It dispatches calls between the local items
|
||||
* that are handled by the symbol group and those that are handled by the dumpers. */
|
||||
|
||||
class CdbStackFrameContext
|
||||
class AbstractSyncWatchModel : public WatchModel
|
||||
{
|
||||
Q_DISABLE_COPY(CdbStackFrameContext)
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
CdbSymbolGroupContext *symbolContext);
|
||||
~CdbStackFrameContext();
|
||||
explicit AbstractSyncWatchModel(WatchHandler *handler, WatchType type, QObject *parent = 0);
|
||||
|
||||
bool assignValue(const QString &iname, const QString &value,
|
||||
QString *newValue /* = 0 */, QString *errorMessage);
|
||||
bool editorToolTip(const QString &iname, QString *value, QString *errorMessage);
|
||||
virtual QVariant data(const QModelIndex &index, int role) const;
|
||||
|
||||
bool populateModelInitially(WatchHandler *wh, QString *errorMessage);
|
||||
virtual void fetchMore(const QModelIndex &parent);
|
||||
virtual bool canFetchMore(const QModelIndex &parent) const;
|
||||
|
||||
bool completeData(const WatchData &incompleteLocal,
|
||||
WatchHandler *wh,
|
||||
QString *errorMessage);
|
||||
signals:
|
||||
// Emitted if one of fetchChildren/complete fails.
|
||||
void error(const QString &);
|
||||
|
||||
protected:
|
||||
// Overwrite these virtuals to fetch children of an item and to complete it
|
||||
virtual bool fetchChildren(WatchItem *wd, QString *errorMessage) = 0;
|
||||
virtual bool complete(WatchItem *wd, QString *errorMessage) = 0;
|
||||
|
||||
private:
|
||||
const bool m_useDumpers;
|
||||
const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||
CdbSymbolGroupContext *m_symbolContext;
|
||||
mutable QString m_errorMessage;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // CDBSTACKFRAMECONTEXT_H
|
||||
#endif // ABSTRACTSYNCWATCHMODEL_H
|
||||
232
src/plugins/debugger/asyncwatchmodel.cpp
Normal file
232
src/plugins/debugger/asyncwatchmodel.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** Commercial Usage
|
||||
**
|
||||
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||
** accordance with the Qt Commercial License Agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Nokia.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** If you are unsure which license is appropriate for your use, please
|
||||
** contact the sales department at http://www.qtsoftware.com/contact.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "asyncwatchmodel.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
int AsyncWatchModel::generationCounter = 0;
|
||||
|
||||
static const QString strNotInScope =
|
||||
QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WatchItem
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
AsyncWatchModel::AsyncWatchModel(WatchHandler *handler, WatchType type, QObject *parent) :
|
||||
WatchModel(handler, type, parent)
|
||||
{
|
||||
dummyRoot()->fetchTriggered = true;
|
||||
}
|
||||
|
||||
void AsyncWatchModel::removeOutdated()
|
||||
{
|
||||
WatchItem *item = dummyRoot();
|
||||
QTC_ASSERT(item, return);
|
||||
foreach (WatchItem *child, item->children)
|
||||
removeOutdatedHelper(child);
|
||||
#if DEBUG_MODEL
|
||||
#if USE_MODEL_TEST
|
||||
//(void) new ModelTest(this, this);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void AsyncWatchModel::removeOutdatedHelper(WatchItem *item)
|
||||
{
|
||||
if (item->generation < generationCounter)
|
||||
removeItem(item);
|
||||
else {
|
||||
foreach (WatchItem *child, item->children)
|
||||
removeOutdatedHelper(child);
|
||||
item->fetchTriggered = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AsyncWatchModel::canFetchMore(const QModelIndex &index) const
|
||||
{
|
||||
if (index.isValid())
|
||||
if (const WatchItem *item = watchItem(index))
|
||||
return !item->fetchTriggered;
|
||||
return false;
|
||||
}
|
||||
|
||||
void AsyncWatchModel::fetchMore(const QModelIndex &index)
|
||||
{
|
||||
QTC_ASSERT(index.isValid(), return);
|
||||
WatchItem *item = watchItem(index);
|
||||
QTC_ASSERT(item && !item->fetchTriggered, return);
|
||||
item->fetchTriggered = true;
|
||||
WatchData data = *item;
|
||||
data.setChildrenNeeded();
|
||||
emit watchDataUpdateNeeded(data);
|
||||
}
|
||||
|
||||
static bool iNameSorter(const WatchItem *item1, const WatchItem *item2)
|
||||
{
|
||||
QString name1 = item1->iname.section('.', -1);
|
||||
QString name2 = item2->iname.section('.', -1);
|
||||
if (!name1.isEmpty() && !name2.isEmpty()) {
|
||||
if (name1.at(0).isDigit() && name2.at(0).isDigit())
|
||||
return name1.toInt() < name2.toInt();
|
||||
}
|
||||
return name1 < name2;
|
||||
}
|
||||
|
||||
static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
|
||||
{
|
||||
QList<WatchItem *>::const_iterator it =
|
||||
qLowerBound(list.begin(), list.end(), item, iNameSorter);
|
||||
return it - list.begin();
|
||||
}
|
||||
|
||||
void AsyncWatchModel::insertData(const WatchData &data)
|
||||
{
|
||||
// qDebug() << "WMI:" << data.toString();
|
||||
QTC_ASSERT(!data.iname.isEmpty(), return);
|
||||
WatchItem *parent = findItemByIName(parentName(data.iname), root());
|
||||
if (!parent) {
|
||||
WatchData parent;
|
||||
parent.iname = parentName(data.iname);
|
||||
insertData(parent);
|
||||
//MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname);
|
||||
return;
|
||||
}
|
||||
QModelIndex index = watchIndex(parent);
|
||||
if (WatchItem *oldItem = findItemByIName(data.iname, parent)) {
|
||||
// overwrite old entry
|
||||
//MODEL_DEBUG("OVERWRITE : " << data.iname << data.value);
|
||||
bool changed = !data.value.isEmpty()
|
||||
&& data.value != oldItem->value
|
||||
&& data.value != strNotInScope;
|
||||
oldItem->setData(data);
|
||||
oldItem->changed = changed;
|
||||
oldItem->generation = generationCounter;
|
||||
QModelIndex idx = watchIndex(oldItem);
|
||||
emit dataChanged(idx, idx.sibling(idx.row(), 2));
|
||||
} else {
|
||||
// add new entry
|
||||
//MODEL_DEBUG("INSERT : " << data.iname << data.value);
|
||||
WatchItem *item = new WatchItem(data);
|
||||
item->parent = parent;
|
||||
item->generation = generationCounter;
|
||||
item->changed = true;
|
||||
int n = findInsertPosition(parent->children, item);
|
||||
beginInsertRows(index, n, n);
|
||||
parent->children.insert(n, item);
|
||||
endInsertRows();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------- AsyncWatchModelMixin
|
||||
AsyncWatchModelMixin::AsyncWatchModelMixin(WatchHandler *wh,
|
||||
QObject *parent) :
|
||||
QObject(parent),
|
||||
m_wh(wh)
|
||||
{
|
||||
}
|
||||
|
||||
AsyncWatchModelMixin::~AsyncWatchModelMixin()
|
||||
{
|
||||
}
|
||||
|
||||
AsyncWatchModel *AsyncWatchModelMixin::model(int wt) const
|
||||
{
|
||||
if (wt < 0 || wt >= WatchModelCount)
|
||||
return 0;
|
||||
if (!m_models[wt]) {
|
||||
m_models[wt] = new AsyncWatchModel(m_wh, static_cast<WatchType>(wt), const_cast<AsyncWatchModelMixin *>(this));
|
||||
connect(m_models[wt], SIGNAL(watchDataUpdateNeeded(WatchData)), this, SIGNAL(watchDataUpdateNeeded(WatchData)));
|
||||
m_models[wt]->setObjectName(QLatin1String("model") + QString::number(wt));
|
||||
}
|
||||
return m_models[wt];
|
||||
}
|
||||
|
||||
AsyncWatchModel *AsyncWatchModelMixin::modelForIName(const QString &iname) const
|
||||
{
|
||||
const WatchType wt = WatchHandler::watchTypeOfIName(iname);
|
||||
QTC_ASSERT(wt != WatchModelCount, return 0);
|
||||
return model(wt);
|
||||
}
|
||||
|
||||
void AsyncWatchModelMixin::beginCycle()
|
||||
{
|
||||
++AsyncWatchModel::generationCounter;
|
||||
}
|
||||
|
||||
void AsyncWatchModelMixin::updateWatchers()
|
||||
{
|
||||
//qDebug() << "UPDATE WATCHERS";
|
||||
// copy over all watchers and mark all watchers as incomplete
|
||||
foreach (const QString &exp, m_wh->watcherExpressions()) {
|
||||
WatchData data;
|
||||
data.iname = m_wh->watcherName(exp);
|
||||
data.setAllNeeded();
|
||||
data.name = exp;
|
||||
data.exp = exp;
|
||||
insertData(data);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWatchModelMixin::endCycle()
|
||||
{
|
||||
for (int m = 0; m < WatchModelCount; m++)
|
||||
if (m_models[m])
|
||||
m_models[m]->removeOutdated();
|
||||
}
|
||||
|
||||
void AsyncWatchModelMixin::insertWatcher(const WatchData &data)
|
||||
{
|
||||
// Is the engine active?
|
||||
if (m_models[WatchersWatch] && m_wh->model(WatchersWatch) == m_models[WatchersWatch])
|
||||
insertData(data);
|
||||
}
|
||||
|
||||
void AsyncWatchModelMixin::insertData(const WatchData &data)
|
||||
{
|
||||
QTC_ASSERT(data.isValid(), return);
|
||||
if (data.isSomethingNeeded()) {
|
||||
emit watchDataUpdateNeeded(data);
|
||||
} else {
|
||||
AsyncWatchModel *model = modelForIName(data.iname);
|
||||
QTC_ASSERT(model, return);
|
||||
model->insertData(data);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
102
src/plugins/debugger/asyncwatchmodel.h
Normal file
102
src/plugins/debugger/asyncwatchmodel.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** Commercial Usage
|
||||
**
|
||||
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||
** accordance with the Qt Commercial License Agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Nokia.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** If you are unsure which license is appropriate for your use, please
|
||||
** contact the sales department at http://www.qtsoftware.com/contact.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef ASYNCWATCHMODEL_H
|
||||
#define ASYNCWATCHMODEL_H
|
||||
|
||||
#include "watchhandler.h"
|
||||
#include <QtCore/QPointer>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
/* AsyncWatchModel: incrementally populated by asynchronous debuggers via
|
||||
* fetch. */
|
||||
|
||||
class AsyncWatchModel : public WatchModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AsyncWatchModel(WatchHandler *handler, WatchType type, QObject *parent = 0);
|
||||
|
||||
bool canFetchMore(const QModelIndex &parent) const;
|
||||
void fetchMore(const QModelIndex &parent);
|
||||
|
||||
friend class WatchHandler;
|
||||
friend class GdbEngine;
|
||||
|
||||
void insertData(const WatchData &data);
|
||||
|
||||
void removeOutdated();
|
||||
void removeOutdatedHelper(WatchItem *item);
|
||||
|
||||
void setActiveData(const QString &data) { m_activeData = data; }
|
||||
|
||||
static int generationCounter;
|
||||
|
||||
signals:
|
||||
void watchDataUpdateNeeded(const WatchData &data);
|
||||
|
||||
private:
|
||||
QString m_activeData;
|
||||
WatchItem *m_root;
|
||||
};
|
||||
|
||||
/* A Mixin to manage asynchronous models. */
|
||||
|
||||
class AsyncWatchModelMixin : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AsyncWatchModelMixin(WatchHandler *wh, QObject *parent = 0);
|
||||
virtual ~AsyncWatchModelMixin();
|
||||
|
||||
AsyncWatchModel *model(int t) const;
|
||||
AsyncWatchModel *modelForIName(const QString &iname) const;
|
||||
|
||||
void beginCycle(); // called at begin of updateLocals() cycle
|
||||
void updateWatchers(); // called after locals are fetched
|
||||
void endCycle(); // called after all results have been received
|
||||
|
||||
public slots:
|
||||
void insertData(const WatchData &data);
|
||||
// For watchers, ensures that engine is active
|
||||
void insertWatcher(const WatchData &data);
|
||||
|
||||
signals:
|
||||
void watchDataUpdateNeeded(const WatchData &data);
|
||||
|
||||
private:
|
||||
WatchHandler *m_wh;
|
||||
mutable QPointer<AsyncWatchModel> m_models[WatchModelCount];
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // ASYNCWATCHMODEL_H
|
||||
@@ -39,7 +39,6 @@ HEADERS += \
|
||||
$$PWD/cdbsymbolgroupcontext.h \
|
||||
$$PWD/cdbsymbolgroupcontext_tpl.h \
|
||||
$$PWD/cdbstacktracecontext.h \
|
||||
$$PWD/cdbstackframecontext.h \
|
||||
$$PWD/cdbbreakpoint.h \
|
||||
$$PWD/cdbmodules.h \
|
||||
$$PWD/cdbassembler.h \
|
||||
@@ -47,14 +46,14 @@ HEADERS += \
|
||||
$$PWD/cdboptionspage.h \
|
||||
$$PWD/cdbdumperhelper.h \
|
||||
$$PWD/cdbsymbolpathlisteditor.h \
|
||||
$$PWD/cdbexceptionutils.h
|
||||
$$PWD/cdbexceptionutils.h \
|
||||
$$PWD/cdbwatchmodels.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/cdbdebugengine.cpp \
|
||||
$$PWD/cdbdebugeventcallback.cpp \
|
||||
$$PWD/cdbdebugoutput.cpp \
|
||||
$$PWD/cdbsymbolgroupcontext.cpp \
|
||||
$$PWD/cdbstackframecontext.cpp \
|
||||
$$PWD/cdbstacktracecontext.cpp \
|
||||
$$PWD/cdbbreakpoint.cpp \
|
||||
$$PWD/cdbmodules.cpp \
|
||||
@@ -63,7 +62,8 @@ SOURCES += \
|
||||
$$PWD/cdboptionspage.cpp \
|
||||
$$PWD/cdbdumperhelper.cpp \
|
||||
$$PWD/cdbsymbolpathlisteditor.cpp \
|
||||
$$PWD/cdbexceptionutils.cpp
|
||||
$$PWD/cdbexceptionutils.cpp \
|
||||
$$PWD/cdbwatchmodels.cpp
|
||||
|
||||
FORMS += $$PWD/cdboptionspagewidget.ui
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
#include "cdbdebugengine.h"
|
||||
#include "cdbdebugengine_p.h"
|
||||
#include "cdbstacktracecontext.h"
|
||||
#include "cdbstackframecontext.h"
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
#include "cdbbreakpoint.h"
|
||||
#include "cdbmodules.h"
|
||||
@@ -47,6 +46,7 @@
|
||||
#include "moduleshandler.h"
|
||||
#include "disassemblerhandler.h"
|
||||
#include "watchutils.h"
|
||||
#include "cdbwatchmodels.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <utils/qtcassert.h>
|
||||
@@ -301,7 +301,9 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent,
|
||||
m_debuggerManagerAccess(parent->engineInterface()),
|
||||
m_currentStackTrace(0),
|
||||
m_firstActivatedFrame(true),
|
||||
m_mode(AttachCore)
|
||||
m_mode(AttachCore),
|
||||
m_localsModel(0),
|
||||
m_watchModel(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -390,6 +392,15 @@ CdbDebugEnginePrivate::~CdbDebugEnginePrivate()
|
||||
m_cif.debugDataSpaces->Release();
|
||||
}
|
||||
|
||||
void CdbDebugEnginePrivate::saveLocalsViewState()
|
||||
{
|
||||
if (m_localsModel && m_localsModel->symbolGroupContext()) {
|
||||
const int oldFrame = m_debuggerManagerAccess->stackHandler()->currentIndex();
|
||||
if (oldFrame != -1)
|
||||
m_debuggerManagerAccess->watchHandler()->saveLocalsViewState(oldFrame);
|
||||
}
|
||||
}
|
||||
|
||||
void CdbDebugEnginePrivate::clearForRun()
|
||||
{
|
||||
if (debugCDB)
|
||||
@@ -403,6 +414,10 @@ void CdbDebugEnginePrivate::clearForRun()
|
||||
|
||||
void CdbDebugEnginePrivate::cleanStackTrace()
|
||||
{
|
||||
if (m_localsModel) {
|
||||
saveLocalsViewState();
|
||||
m_localsModel->setSymbolGroupContext(0);
|
||||
}
|
||||
if (m_currentStackTrace) {
|
||||
delete m_currentStackTrace;
|
||||
m_currentStackTrace = 0;
|
||||
@@ -465,14 +480,19 @@ QString CdbDebugEngine::editorToolTip(const QString &exp, const QString &functio
|
||||
QString errorMessage;
|
||||
QString rc;
|
||||
// Find the frame of the function if there is any
|
||||
CdbStackFrameContext *frame = 0;
|
||||
const QString iname = QLatin1String("local.") + exp;
|
||||
if (m_d->m_currentStackTrace && !function.isEmpty()) {
|
||||
const int frameIndex = m_d->m_currentStackTrace->indexOf(function);
|
||||
if (frameIndex != -1)
|
||||
frame = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
|
||||
if (frameIndex != -1) {
|
||||
// Are we at the current frame?
|
||||
if (frameIndex == m_d->m_debuggerManagerAccess->stackHandler()->currentIndex()) {
|
||||
if (const WatchData *wd = m_d->m_localsModel->findItemByIName(iname, m_d->m_localsModel->root()))
|
||||
return wd->toToolTip(); }
|
||||
// Nope, try to retrieve via another symbol group
|
||||
if (m_d->m_currentStackTrace->editorToolTip(frameIndex, iname, &rc, &errorMessage))
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if (frame && frame->editorToolTip(QLatin1String("local.") + exp, &rc, &errorMessage))
|
||||
return rc;
|
||||
// No function/symbol context found, try to evaluate in current context.
|
||||
// Do not append type as this will mostly be 'long long' for integers, etc.
|
||||
QString type;
|
||||
@@ -770,67 +790,6 @@ void CdbDebugEngine::detachDebugger()
|
||||
m_d->endDebugging(CdbDebugEnginePrivate::EndDebuggingDetach);
|
||||
}
|
||||
|
||||
CdbStackFrameContext *CdbDebugEnginePrivate::getStackFrameContext(int frameIndex, QString *errorMessage) const
|
||||
{
|
||||
if (!m_currentStackTrace) {
|
||||
*errorMessage = QLatin1String(msgNoStackTraceC);
|
||||
return 0;
|
||||
}
|
||||
if (CdbStackFrameContext *sg = m_currentStackTrace->frameContextAt(frameIndex, errorMessage))
|
||||
return sg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CdbDebugEngine::evaluateWatcher(WatchData *wd)
|
||||
{
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << Q_FUNC_INFO << wd->exp;
|
||||
QString errorMessage;
|
||||
QString value;
|
||||
QString type;
|
||||
if (evaluateExpression(wd->exp, &value, &type, &errorMessage)) {
|
||||
wd->setValue(value);
|
||||
wd->setType(type);
|
||||
} else {
|
||||
wd->setValue(errorMessage);
|
||||
wd->setTypeUnneeded();
|
||||
}
|
||||
wd->setHasChildren(false);
|
||||
}
|
||||
|
||||
void CdbDebugEngine::updateWatchData(const WatchData &incomplete)
|
||||
{
|
||||
// Watch item was edited while running
|
||||
if (m_d->isDebuggeeRunning())
|
||||
return;
|
||||
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << Q_FUNC_INFO << "\n " << incomplete.toString();
|
||||
|
||||
WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
|
||||
if (incomplete.iname.startsWith(QLatin1String("watch."))) {
|
||||
WatchData watchData = incomplete;
|
||||
evaluateWatcher(&watchData);
|
||||
watchHandler->insertData(watchData);
|
||||
return;
|
||||
}
|
||||
|
||||
const int frameIndex = m_d->m_debuggerManagerAccess->stackHandler()->currentIndex();
|
||||
|
||||
bool success = false;
|
||||
QString errorMessage;
|
||||
do {
|
||||
CdbStackFrameContext *sg = m_d->m_currentStackTrace->frameContextAt(frameIndex, &errorMessage);
|
||||
if (!sg)
|
||||
break;
|
||||
if (!sg->completeData(incomplete, watchHandler, &errorMessage))
|
||||
break;
|
||||
success = true;
|
||||
} while (false);
|
||||
if (!success)
|
||||
warning(msgFunctionFailed(Q_FUNC_INFO, errorMessage));
|
||||
}
|
||||
|
||||
void CdbDebugEngine::stepExec()
|
||||
{
|
||||
if (debugCDB)
|
||||
@@ -1037,18 +996,14 @@ void CdbDebugEngine::assignValueInDebugger(const QString &expr, const QString &v
|
||||
bool success = false;
|
||||
do {
|
||||
QString newValue;
|
||||
CdbStackFrameContext *sg = m_d->getStackFrameContext(frameIndex, &errorMessage);
|
||||
if (!sg)
|
||||
if (frameIndex < 0 || !m_d->m_currentStackTrace) {
|
||||
errorMessage = tr("No current stack trace.");
|
||||
break;
|
||||
if (!sg->assignValue(expr, value, &newValue, &errorMessage))
|
||||
break;
|
||||
// Update view
|
||||
WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
|
||||
if (WatchData *fwd = watchHandler->findItem(expr)) {
|
||||
fwd->setValue(newValue);
|
||||
watchHandler->insertData(*fwd);
|
||||
watchHandler->updateWatchers();
|
||||
}
|
||||
// Assign in stack and update view
|
||||
if (!m_d->m_currentStackTrace->assignValue(frameIndex, expr, value, &newValue, &errorMessage))
|
||||
break;
|
||||
m_d->m_localsModel->setValueByExpression(expr, newValue);
|
||||
success = true;
|
||||
} while (false);
|
||||
if (!success) {
|
||||
@@ -1078,6 +1033,11 @@ bool CdbDebugEnginePrivate::executeDebuggerCommand(CIDebugControl *ctrl, const Q
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CdbDebugEngine::isDebuggeeHalted() const
|
||||
{
|
||||
return m_d->m_hDebuggeeProcess && !m_d->isDebuggeeRunning();
|
||||
}
|
||||
|
||||
bool CdbDebugEngine::evaluateExpression(const QString &expression,
|
||||
QString *value,
|
||||
QString *type,
|
||||
@@ -1132,21 +1092,21 @@ void CdbDebugEngine::activateFrame(int frameIndex)
|
||||
bool success = false;
|
||||
do {
|
||||
StackHandler *stackHandler = m_d->m_debuggerManagerAccess->stackHandler();
|
||||
WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
|
||||
const int oldIndex = stackHandler->currentIndex();
|
||||
if (frameIndex >= stackHandler->stackSize()) {
|
||||
errorMessage = msgStackIndexOutOfRange(frameIndex, stackHandler->stackSize());
|
||||
break;
|
||||
}
|
||||
|
||||
if (oldIndex != frameIndex)
|
||||
if (oldIndex != frameIndex) {
|
||||
m_d->saveLocalsViewState();
|
||||
stackHandler->setCurrentIndex(frameIndex);
|
||||
}
|
||||
|
||||
const StackFrame &frame = stackHandler->currentFrame();
|
||||
if (!frame.isUsable()) {
|
||||
m_d->m_localsModel->setSymbolGroupContext(0);
|
||||
// Clean out model
|
||||
watchHandler->beginCycle();
|
||||
watchHandler->endCycle();
|
||||
errorMessage = QString::fromLatin1("%1: file %2 unusable.").
|
||||
arg(QLatin1String(Q_FUNC_INFO), frame.file);
|
||||
break;
|
||||
@@ -1155,10 +1115,18 @@ void CdbDebugEngine::activateFrame(int frameIndex)
|
||||
m_d->m_debuggerManager->gotoLocation(frame.file, frame.line, true);
|
||||
|
||||
if (oldIndex != frameIndex || m_d->m_firstActivatedFrame) {
|
||||
watchHandler->beginCycle();
|
||||
if (CdbStackFrameContext *sgc = m_d->getStackFrameContext(frameIndex, &errorMessage))
|
||||
success = sgc->populateModelInitially(watchHandler, &errorMessage);
|
||||
watchHandler->endCycle();
|
||||
m_d->m_localsModel->setUseDumpers(m_d->m_dumper->isEnabled() && theDebuggerBoolSetting(UseDebuggingHelpers));
|
||||
CdbSymbolGroupContext *ctx = 0;
|
||||
if (m_d->m_currentStackTrace) {
|
||||
ctx = m_d->m_currentStackTrace->symbolGroupAt(frameIndex, &errorMessage);
|
||||
success = ctx != 0;
|
||||
} else {
|
||||
errorMessage = QLatin1String(msgNoStackTraceC);
|
||||
}
|
||||
m_d->m_localsModel->setSymbolGroupContext(ctx);
|
||||
m_d->m_debuggerManagerAccess->watchHandler()->restoreLocalsViewState(frameIndex);
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
} while (false);
|
||||
if (!success)
|
||||
@@ -1222,7 +1190,7 @@ bool CdbDebugEnginePrivate::attemptBreakpointSynchronization(QString *errorMessa
|
||||
m_cif.debugSymbols,
|
||||
m_debuggerManagerAccess->breakHandler(),
|
||||
errorMessage, &warnings);
|
||||
if (const int warningsCount = warnings.size())
|
||||
if (const int warningsCount = warnings.size())
|
||||
for (int w = 0; w < warningsCount; w++)
|
||||
m_engine->warning(warnings.at(w));
|
||||
return ok;
|
||||
@@ -1500,7 +1468,8 @@ void CdbDebugEnginePrivate::updateStackTrace()
|
||||
m_debuggerManagerAccess->stackHandler()->setCurrentIndex(current);
|
||||
m_engine->activateFrame(current);
|
||||
}
|
||||
m_debuggerManagerAccess->watchHandler()->updateWatchers();
|
||||
// Update watchers
|
||||
m_watchModel->refresh();
|
||||
}
|
||||
|
||||
void CdbDebugEnginePrivate::updateModules()
|
||||
@@ -1512,8 +1481,6 @@ void CdbDebugEnginePrivate::updateModules()
|
||||
m_debuggerManagerAccess->modulesHandler()->setModules(modules);
|
||||
}
|
||||
|
||||
static const char *dumperPrefixC = "dumper";
|
||||
|
||||
void CdbDebugEnginePrivate::handleModuleLoad(const QString &name)
|
||||
{
|
||||
if (debugCDB>2)
|
||||
@@ -1586,6 +1553,37 @@ bool CdbDebugEnginePrivate::setSymbolPaths(const QStringList &s, QString *errorM
|
||||
return true;
|
||||
}
|
||||
|
||||
void CdbDebugEngine::insertWatcher(const WatchData &wd)
|
||||
{
|
||||
// Make sure engine is active
|
||||
if (m_d->m_watchModel && m_d->m_watchModel == m_d->m_debuggerManagerAccess->watchHandler()->model(WatchersWatch))
|
||||
m_d->m_watchModel->addWatcher(wd);
|
||||
}
|
||||
|
||||
WatchModel *CdbDebugEngine::watchModel(int type) const
|
||||
{
|
||||
switch (type) {
|
||||
case LocalsWatch:
|
||||
if (!m_d->m_localsModel) {
|
||||
m_d->m_localsModel = new CdbLocalsModel(m_d->m_dumper, m_d->m_debuggerManagerAccess->watchHandler(), LocalsWatch, const_cast<CdbDebugEngine*>(this));
|
||||
connect(m_d->m_localsModel, SIGNAL(error(QString)), this, SLOT(warning(QString)));
|
||||
}
|
||||
return m_d->m_localsModel;
|
||||
case WatchersWatch:
|
||||
if (!m_d->m_watchModel) {
|
||||
CdbDebugEngine* nonConstThis = const_cast<CdbDebugEngine*>(this);
|
||||
WatchHandler *wh = m_d->m_debuggerManagerAccess->watchHandler();
|
||||
m_d->m_watchModel = new CdbWatchModel(nonConstThis, m_d->m_dumper, wh, WatchersWatch, nonConstThis);
|
||||
connect(m_d->m_watchModel, SIGNAL(error(QString)), this, SLOT(warning(QString)));
|
||||
connect(wh, SIGNAL(watcherInserted(WatchData)), this, SLOT(insertWatcher(WatchData)));
|
||||
}
|
||||
return m_d->m_watchModel;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
|
||||
@@ -64,15 +64,14 @@ public:
|
||||
virtual bool startDebugger(const QSharedPointer<DebuggerStartParameters> &startParameters);
|
||||
virtual void exitDebugger();
|
||||
virtual void detachDebugger();
|
||||
virtual void updateWatchData(const WatchData &data);
|
||||
|
||||
virtual void stepExec();
|
||||
virtual void stepOutExec();
|
||||
virtual void nextExec();
|
||||
virtual void stepIExec();
|
||||
virtual void nextIExec();
|
||||
|
||||
virtual void continueInferior();
|
||||
|
||||
virtual void continueInferior();
|
||||
virtual void interruptInferior();
|
||||
|
||||
virtual void runToLineExec(const QString &fileName, int lineNumber);
|
||||
@@ -97,6 +96,11 @@ public:
|
||||
virtual void reloadSourceFiles();
|
||||
virtual void reloadFullStack() {}
|
||||
|
||||
WatchModel *watchModel(int type) const;
|
||||
|
||||
bool isDebuggeeHalted() const;
|
||||
bool evaluateExpression(const QString &expression, QString *value, QString *type, QString *errorMessage);
|
||||
|
||||
public slots:
|
||||
void syncDebuggerPaths();
|
||||
|
||||
@@ -106,8 +110,9 @@ protected:
|
||||
private slots:
|
||||
void slotConsoleStubStarted();
|
||||
void slotConsoleStubError(const QString &msg);
|
||||
void slotConsoleStubTerminated();
|
||||
void slotConsoleStubTerminated();
|
||||
void warning(const QString &w);
|
||||
void insertWatcher(const WatchData &);
|
||||
|
||||
private:
|
||||
bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage);
|
||||
@@ -116,8 +121,7 @@ private:
|
||||
void killWatchTimer();
|
||||
void processTerminated(unsigned long exitCode);
|
||||
bool executeDebuggerCommand(const QString &command, QString *errorMessage);
|
||||
bool evaluateExpression(const QString &expression, QString *value, QString *type, QString *errorMessage);
|
||||
void evaluateWatcher(WatchData *wd);
|
||||
|
||||
QString editorToolTip(const QString &exp, const QString &function);
|
||||
|
||||
CdbDebugEnginePrivate *m_d;
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
#include <utils/consoleprocess.h>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QMap>
|
||||
|
||||
namespace Debugger {
|
||||
@@ -49,6 +50,8 @@ class IDebuggerManagerAccessForEngines;
|
||||
class WatchHandler;
|
||||
class CdbStackFrameContext;
|
||||
class CdbStackTraceContext;
|
||||
class CdbLocalsModel;
|
||||
class CdbWatchModel;
|
||||
|
||||
// Thin wrapper around the 'DBEng' debugger engine shared library
|
||||
// which is loaded at runtime.
|
||||
@@ -115,7 +118,7 @@ struct CdbDebugEnginePrivate
|
||||
|
||||
bool isDebuggeeRunning() const { return m_watchTimer != -1; }
|
||||
void handleDebugEvent();
|
||||
void updateThreadList();
|
||||
void updateThreadList();
|
||||
void updateStackTrace();
|
||||
void updateModules();
|
||||
|
||||
@@ -123,7 +126,6 @@ struct CdbDebugEnginePrivate
|
||||
void cleanStackTrace();
|
||||
void clearForRun();
|
||||
void handleModuleLoad(const QString &);
|
||||
CdbStackFrameContext *getStackFrameContext(int frameIndex, QString *errorMessage) const;
|
||||
void clearDisplay();
|
||||
|
||||
bool interruptInterferiorProcess(QString *errorMessage);
|
||||
@@ -137,6 +139,8 @@ struct CdbDebugEnginePrivate
|
||||
enum EndDebuggingMode { EndDebuggingDetach, EndDebuggingTerminate, EndDebuggingAuto };
|
||||
void endDebugging(EndDebuggingMode em = EndDebuggingAuto);
|
||||
|
||||
void saveLocalsViewState();
|
||||
|
||||
static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage);
|
||||
static bool evaluateExpression(CIDebugControl *ctrl, const QString &expression, DEBUG_VALUE *v, QString *errorMessage);
|
||||
|
||||
@@ -155,7 +159,7 @@ struct CdbDebugEnginePrivate
|
||||
int m_watchTimer;
|
||||
CdbComInterfaces m_cif;
|
||||
CdbDebugEventCallback m_debugEventCallBack;
|
||||
CdbDebugOutput m_debugOutputCallBack;
|
||||
CdbDebugOutput m_debugOutputCallBack;
|
||||
QSharedPointer<CdbDumperHelper> m_dumper;
|
||||
|
||||
CdbDebugEngine* m_engine;
|
||||
@@ -168,6 +172,9 @@ struct CdbDebugEnginePrivate
|
||||
|
||||
DebuggerStartMode m_mode;
|
||||
Core::Utils::ConsoleProcess m_consoleStubProc;
|
||||
|
||||
QPointer<CdbLocalsModel> m_localsModel;
|
||||
QPointer<CdbWatchModel> m_watchModel;
|
||||
};
|
||||
|
||||
// helper functions
|
||||
|
||||
@@ -28,11 +28,11 @@
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbstacktracecontext.h"
|
||||
#include "cdbstackframecontext.h"
|
||||
#include "cdbbreakpoint.h"
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
#include "cdbdebugengine_p.h"
|
||||
#include "cdbdumperhelper.h"
|
||||
#include "debuggeractions.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QTextStream>
|
||||
@@ -90,7 +90,7 @@ bool CdbStackTraceContext::init(unsigned long frameCount, QString * /*errorMessa
|
||||
qDebug() << Q_FUNC_INFO << frameCount;
|
||||
|
||||
m_frameContexts.resize(frameCount);
|
||||
qFill(m_frameContexts, static_cast<CdbStackFrameContext*>(0));
|
||||
qFill(m_frameContexts, static_cast<CdbSymbolGroupContext*>(0));
|
||||
|
||||
// Convert the DEBUG_STACK_FRAMEs to our StackFrame structure and populate the frames
|
||||
WCHAR wszBuf[MAX_PATH];
|
||||
@@ -145,7 +145,7 @@ static inline QString msgFrameContextFailed(int index, const StackFrame &f, cons
|
||||
arg(index).arg(f.function).arg(f.line).arg(f.file, why);
|
||||
}
|
||||
|
||||
CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *errorMessage)
|
||||
CdbSymbolGroupContext *CdbStackTraceContext::symbolGroupAt(int index, QString *errorMessage)
|
||||
{
|
||||
// Create a frame on demand
|
||||
if (debugCDB)
|
||||
@@ -158,6 +158,7 @@ CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *e
|
||||
}
|
||||
if (m_frameContexts.at(index))
|
||||
return m_frameContexts.at(index);
|
||||
// Create COM and wrap
|
||||
CIDebugSymbolGroup *sg = createSymbolGroup(index, errorMessage);
|
||||
if (!sg) {
|
||||
*errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage);
|
||||
@@ -168,9 +169,8 @@ CdbStackFrameContext *CdbStackTraceContext::frameContextAt(int index, QString *e
|
||||
*errorMessage = msgFrameContextFailed(index, m_frames.at(index), *errorMessage);
|
||||
return 0;
|
||||
}
|
||||
CdbStackFrameContext *fr = new CdbStackFrameContext(m_dumper, sc);
|
||||
m_frameContexts[index] = fr;
|
||||
return fr;
|
||||
m_frameContexts[index] = sc;
|
||||
return sc;
|
||||
}
|
||||
|
||||
CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *errorMessage)
|
||||
@@ -198,6 +198,49 @@ CIDebugSymbolGroup *CdbStackTraceContext::createSymbolGroup(int index, QString *
|
||||
return sg;
|
||||
}
|
||||
|
||||
bool CdbStackTraceContext::editorToolTip(int frameIndex,
|
||||
const QString &iname,
|
||||
QString *value,
|
||||
QString *errorMessage)
|
||||
{
|
||||
value->clear();
|
||||
// Look up iname in the frame's symbol group.
|
||||
CdbSymbolGroupContext *m_symbolContext = symbolGroupAt(frameIndex, errorMessage);
|
||||
if (!m_symbolContext)
|
||||
return false;
|
||||
unsigned long index;
|
||||
if (!m_symbolContext->lookupPrefix(iname, &index)) {
|
||||
*errorMessage = QString::fromLatin1("%1 not found.").arg(iname);
|
||||
return false;
|
||||
}
|
||||
const WatchData wd = m_symbolContext->symbolAt(index);
|
||||
// Check dumpers. Should actually be just one item.
|
||||
if (theDebuggerBoolSetting(UseDebuggingHelpers) && m_dumper->isEnabled()) {
|
||||
QList<WatchData> result;
|
||||
if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, 1, &result, errorMessage)) {
|
||||
foreach (const WatchData &dwd, result) {
|
||||
if (!value->isEmpty())
|
||||
value->append(QLatin1Char('\n'));
|
||||
value->append(dwd.toToolTip());
|
||||
}
|
||||
return true;
|
||||
} // Dumped ok
|
||||
} // has Dumpers
|
||||
*value = wd.toToolTip();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CdbStackTraceContext::assignValue(int frameIndex,
|
||||
const QString &iname,
|
||||
const QString &value,
|
||||
QString *newValue /* = 0 */, QString *errorMessage)
|
||||
{
|
||||
if (CdbSymbolGroupContext *symbolContext = symbolGroupAt(frameIndex, errorMessage))
|
||||
return symbolContext->assignValue(iname, value, newValue, errorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QString CdbStackTraceContext::toString() const
|
||||
{
|
||||
QString rc;
|
||||
|
||||
@@ -47,11 +47,10 @@ namespace Internal {
|
||||
|
||||
struct CdbComInterfaces;
|
||||
class CdbSymbolGroupContext;
|
||||
class CdbStackFrameContext;
|
||||
class CdbDumperHelper;
|
||||
|
||||
/* Context representing a break point stack consisting of several frames.
|
||||
* Maintains an on-demand constructed list of CdbStackFrameContext
|
||||
* Maintains an on-demand constructed list of CdbSymbolGroupContext
|
||||
* containining the local variables of the stack. */
|
||||
|
||||
class CdbStackTraceContext
|
||||
@@ -75,7 +74,17 @@ public:
|
||||
// Top-Level instruction offset for disassembler
|
||||
ULONG64 instructionOffset() const { return m_instructionOffset; }
|
||||
|
||||
CdbStackFrameContext *frameContextAt(int index, QString *errorMessage);
|
||||
CdbSymbolGroupContext *symbolGroupAt(int index, QString *errorMessage);
|
||||
|
||||
// Helper to retrieve an editor tooltip for a frame. Note that
|
||||
// for the current frame, the LocalsModel should be consulted first.
|
||||
bool editorToolTip(int frameIndex,const QString &iname, QString *value, QString *errorMessage);
|
||||
|
||||
// Assign value in Debugger
|
||||
bool assignValue(int frameIndex,
|
||||
const QString &iname,
|
||||
const QString &value,
|
||||
QString *newValue /* = 0 */, QString *errorMessage);
|
||||
|
||||
// Format for logging
|
||||
void format(QTextStream &str) const;
|
||||
@@ -89,7 +98,7 @@ private:
|
||||
CdbComInterfaces *m_cif;
|
||||
|
||||
DEBUG_STACK_FRAME m_cdbFrames[maxFrames];
|
||||
QVector <CdbStackFrameContext*> m_frameContexts;
|
||||
QVector <CdbSymbolGroupContext*> m_frameContexts;
|
||||
QList<StackFrame> m_frames;
|
||||
ULONG64 m_instructionOffset;
|
||||
};
|
||||
|
||||
@@ -406,7 +406,6 @@ WatchData CdbSymbolGroupContext::symbolAt(unsigned long index) const
|
||||
const QString value = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, index);
|
||||
wd.setType(type);
|
||||
wd.setValue(fixValue(value));
|
||||
wd.setChildrenNeeded(); // compensate side effects of above setters
|
||||
// Figure out children. The SubElement is only a guess unless the symbol,
|
||||
// is expanded, so, we leave this as a guess for later updates.
|
||||
// If the symbol has children (expanded or not), we leave the 'Children' flag
|
||||
|
||||
@@ -81,31 +81,6 @@ public:
|
||||
bool assignValue(const QString &iname, const QString &value,
|
||||
QString *newValue /* = 0 */, QString *errorMessage);
|
||||
|
||||
// Initially populate the locals model for a new stackframe.
|
||||
// Write a sequence of WatchData to it, recurse down if the
|
||||
// recursionPredicate agrees. The ignorePredicate can be used
|
||||
// to terminate processing after insertion of an item (if the calling
|
||||
// routine wants to insert another subtree).
|
||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||
static bool populateModelInitially(CdbSymbolGroupContext *sg,
|
||||
OutputIterator it,
|
||||
RecursionPredicate recursionPredicate,
|
||||
IgnorePredicate ignorePredicate,
|
||||
QString *errorMessage);
|
||||
|
||||
// Complete children of a WatchData item.
|
||||
// Write a sequence of WatchData to it, recurse if the
|
||||
// recursionPredicate agrees. The ignorePredicate can be used
|
||||
// to terminate processing after insertion of an item (if the calling
|
||||
// routine wants to insert another subtree).
|
||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||
static bool completeData (CdbSymbolGroupContext *sg,
|
||||
WatchData incompleteLocal,
|
||||
OutputIterator it,
|
||||
RecursionPredicate recursionPredicate,
|
||||
IgnorePredicate ignorePredicate,
|
||||
QString *errorMessage);
|
||||
|
||||
// Retrieve child symbols of prefix as a sequence of WatchData.
|
||||
template <class OutputIterator>
|
||||
bool getChildSymbols(const QString &prefix, OutputIterator it, QString *errorMessage);
|
||||
|
||||
@@ -66,105 +66,6 @@ bool CdbSymbolGroupContext::getChildSymbols(const QString &prefix, OutputIterato
|
||||
return true;
|
||||
}
|
||||
|
||||
// Insert a symbol (and its first level children depending on forceRecursion)
|
||||
// The parent symbol is inserted before the children to make dumper handling
|
||||
// simpler. In theory, it can happen that the symbol context indicates
|
||||
// children but can expand none, which would lead to invalid parent information
|
||||
// (expand icon), though (ignore for simplicity).
|
||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||
bool insertSymbolRecursion(WatchData wd,
|
||||
CdbSymbolGroupContext *sg,
|
||||
OutputIterator it,
|
||||
RecursionPredicate recursionPredicate,
|
||||
IgnorePredicate ignorePredicate,
|
||||
QString *errorMessage)
|
||||
{
|
||||
// Find out whether to recurse (has children and predicate agrees)
|
||||
const bool hasChildren = wd.hasChildren || wd.isChildrenNeeded();
|
||||
const bool recurse = hasChildren && recursionPredicate(wd);
|
||||
if (debugSgRecursion)
|
||||
qDebug() << "insertSymbolRecursion" << '\n' << wd.iname << "recurse=" << recurse;
|
||||
if (!recurse) {
|
||||
// No further recursion at this level, pretend entry is complete
|
||||
// to the watchmodel for the parent to show (only remaining watchhandler-specific
|
||||
// part here).
|
||||
if (wd.isChildrenNeeded()) {
|
||||
wd.setHasChildren(true);
|
||||
wd.setChildrenUnneeded();
|
||||
}
|
||||
if (debugSgRecursion)
|
||||
qDebug() << " INSERTING non-recursive: " << wd.toString();
|
||||
*it = wd;
|
||||
++it;
|
||||
return true;
|
||||
}
|
||||
// Recursion: Indicate children unneeded
|
||||
wd.setHasChildren(true);
|
||||
wd.setChildrenUnneeded();
|
||||
if (debugSgRecursion)
|
||||
qDebug() << " INSERTING recursive: " << wd.toString();
|
||||
*it = wd;
|
||||
++it;
|
||||
// Recurse unless the predicate disagrees
|
||||
if (ignorePredicate(wd))
|
||||
return true;
|
||||
QList<WatchData> watchList;
|
||||
// This implicitly enforces expansion
|
||||
if (!sg->getChildSymbols(wd.iname, WatchDataBackInserter(watchList), errorMessage))
|
||||
return false;
|
||||
const int childCount = watchList.size();
|
||||
for (int c = 0; c < childCount; c++) {
|
||||
const WatchData &cwd = watchList.at(c);
|
||||
if (wd.isValid()) { // We sometimes get empty names for deeply nested data
|
||||
if (!insertSymbolRecursion(cwd, sg, it, recursionPredicate, ignorePredicate, errorMessage))
|
||||
return false;
|
||||
} else {
|
||||
const QString msg = QString::fromLatin1("WARNING: Skipping invalid child symbol #%2 (type %3) of '%4'.").
|
||||
arg(QLatin1String(Q_FUNC_INFO)).arg(c).arg(cwd.type, wd.iname);
|
||||
qWarning("%s\n", qPrintable(msg));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||
bool CdbSymbolGroupContext::populateModelInitially(CdbSymbolGroupContext *sg,
|
||||
OutputIterator it,
|
||||
RecursionPredicate recursionPredicate,
|
||||
IgnorePredicate ignorePredicate,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugSgRecursion)
|
||||
qDebug() << "### CdbSymbolGroupContext::populateModelInitially";
|
||||
|
||||
// Insert root items
|
||||
QList<WatchData> watchList;
|
||||
if (!sg->getChildSymbols(sg->prefix(), WatchDataBackInserter(watchList), errorMessage))
|
||||
return false;
|
||||
// Insert data
|
||||
foreach(const WatchData &wd, watchList)
|
||||
if (!insertSymbolRecursion(wd, sg, it, recursionPredicate, ignorePredicate, errorMessage))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class OutputIterator, class RecursionPredicate, class IgnorePredicate>
|
||||
bool CdbSymbolGroupContext::completeData(CdbSymbolGroupContext *sg,
|
||||
WatchData incompleteLocal,
|
||||
OutputIterator it,
|
||||
RecursionPredicate recursionPredicate,
|
||||
IgnorePredicate ignorePredicate,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugSgRecursion)
|
||||
qDebug().nospace() << "###>CdbSymbolGroupContext::completeData" << ' ' << incompleteLocal.iname << '\n';
|
||||
// If the symbols are already expanded in the context, they will be re-inserted,
|
||||
// which is not handled for simplicity.
|
||||
if (!insertSymbolRecursion(incompleteLocal, sg, it, recursionPredicate, ignorePredicate, errorMessage))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
|
||||
477
src/plugins/debugger/cdb/cdbwatchmodels.cpp
Normal file
477
src/plugins/debugger/cdb/cdbwatchmodels.cpp
Normal file
@@ -0,0 +1,477 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** Commercial Usage
|
||||
**
|
||||
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||
** accordance with the Qt Commercial License Agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Nokia.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** If you are unsure which license is appropriate for your use, please
|
||||
** contact the sales department at http://www.qtsoftware.com/contact.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#include "cdbwatchmodels.h"
|
||||
#include "cdbdumperhelper.h"
|
||||
#include "cdbsymbolgroupcontext.h"
|
||||
#include "cdbdebugengine.h"
|
||||
#include "watchutils.h"
|
||||
#include "debuggeractions.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QRegExp>
|
||||
|
||||
enum { debugCDBWatchHandling = 0 };
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
enum { LocalsOwnerSymbolGroup, LocalsOwnerDumper };
|
||||
|
||||
static inline QString msgNoStackFrame()
|
||||
{
|
||||
return QCoreApplication::translate("CdbWatchModels", "No active stack frame present.");
|
||||
}
|
||||
|
||||
static inline QString msgUnknown()
|
||||
{
|
||||
return QCoreApplication::translate("CdbWatchModels", "<Unknown>");
|
||||
}
|
||||
|
||||
// Helper to add a sequence of WatchData from the symbol group context to an item
|
||||
// without using dumpers.
|
||||
|
||||
class SymbolGroupInserter
|
||||
{
|
||||
public:
|
||||
explicit SymbolGroupInserter(WatchItem *parent) : m_parent(parent) {}
|
||||
|
||||
inline SymbolGroupInserter& operator*() { return *this; }
|
||||
inline SymbolGroupInserter&operator=(const WatchData &wd) {
|
||||
WatchItem *item = new WatchItem(wd);
|
||||
item->source = LocalsOwnerSymbolGroup;
|
||||
m_parent->addChild(item);
|
||||
return *this;
|
||||
}
|
||||
inline SymbolGroupInserter&operator++() { return *this; }
|
||||
|
||||
private:
|
||||
WatchItem *m_parent;
|
||||
};
|
||||
|
||||
// fixDumperResult: When querying an item, the queried item is sometimes returned in
|
||||
// incomplete form. Take over values from source, set items with missing addresses to
|
||||
// "complete".
|
||||
static inline void fixDumperResult(const WatchData &source,
|
||||
QList<WatchData> *result)
|
||||
{
|
||||
if (debugCDBWatchHandling > 1) {
|
||||
qDebug() << "fixDumperResult for : " << source.toString();
|
||||
foreach (const WatchData &wd, *result)
|
||||
qDebug() << " " << wd.toString();
|
||||
}
|
||||
const int size = result->size();
|
||||
if (!size)
|
||||
return;
|
||||
WatchData &returned = result->front();
|
||||
if (returned.iname != source.iname)
|
||||
return;
|
||||
if (returned.type.isEmpty())
|
||||
returned.setType(source.type);
|
||||
if (returned.isValueNeeded()) {
|
||||
if (source.isValueKnown()) {
|
||||
returned.setValue(source.value);
|
||||
} else {
|
||||
// Should not happen
|
||||
returned.setValue(msgUnknown());
|
||||
qWarning("%s: No value for %s\n", Q_FUNC_INFO, qPrintable(returned.toString()));
|
||||
}
|
||||
}
|
||||
if (size == 1)
|
||||
return;
|
||||
// Fix the children: If the address is missing, we cannot query any further.
|
||||
const QList<WatchData>::iterator wend = result->end();
|
||||
QList<WatchData>::iterator it = result->begin();
|
||||
for (++it; it != wend; ++it) {
|
||||
WatchData &wd = *it;
|
||||
if (wd.addr.isEmpty() && wd.isSomethingNeeded()) {
|
||||
wd.setAllUnneeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dump an item. If *ptrToDumpedItem == 0, allocate a new item and set it.
|
||||
// If it is non-null, the item pointed to will receive the results
|
||||
// ("complete" functionality).
|
||||
static CdbDumperHelper::DumpResult
|
||||
dumpItem(const QSharedPointer<CdbDumperHelper> dumper,
|
||||
const WatchData &wd,
|
||||
WatchItem **ptrToDumpedItem,
|
||||
int dumperOwnerValue, QString *errorMessage)
|
||||
{
|
||||
QList<WatchData> dumperResult;
|
||||
WatchItem *dumpedItem = *ptrToDumpedItem;
|
||||
const CdbDumperHelper::DumpResult rc = dumper->dumpType(wd, true, dumperOwnerValue, &dumperResult, errorMessage);
|
||||
if (debugCDBWatchHandling > 1)
|
||||
qDebug() << "dumper for " << wd.type << " returns " << rc;
|
||||
|
||||
switch (rc) {
|
||||
case CdbDumperHelper::DumpError:
|
||||
return rc;
|
||||
case CdbDumperHelper::DumpNotHandled:
|
||||
errorMessage->clear();
|
||||
return rc;
|
||||
case CdbDumperHelper::DumpOk:
|
||||
break;
|
||||
}
|
||||
// Dumpers omit types for complicated templates
|
||||
fixDumperResult(wd, &dumperResult);
|
||||
// Discard the original item and insert the dumper results
|
||||
if (dumpedItem) {
|
||||
dumpedItem->setData(dumperResult.front());
|
||||
} else {
|
||||
dumpedItem = new WatchItem(dumperResult.front());
|
||||
*ptrToDumpedItem = dumpedItem;
|
||||
}
|
||||
dumperResult.pop_front();
|
||||
foreach(const WatchData &dwd, dumperResult)
|
||||
dumpedItem->addChild(new WatchItem(dwd));
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Is this a non-null pointer to a dumpeable item with a value
|
||||
// "0x4343 class QString *" ? - Insert a fake '*' dereferenced item
|
||||
// and run dumpers on it. If that succeeds, insert the fake items owned by dumpers,
|
||||
// Note that the symbol context does not create '*' dereferenced items for
|
||||
// classes (see note in its header documentation).
|
||||
static bool expandPointerToDumpable(const QSharedPointer<CdbDumperHelper> dumper,
|
||||
const WatchData &wd,
|
||||
WatchItem *parent,
|
||||
QString *errorMessage)
|
||||
{
|
||||
|
||||
|
||||
if (debugCDBWatchHandling > 1)
|
||||
qDebug() << ">expandPointerToDumpable" << wd.iname;
|
||||
|
||||
WatchItem *derefedWdItem = 0;
|
||||
WatchItem *ptrWd = 0;
|
||||
bool handled = false;
|
||||
do {
|
||||
if (!isPointerType(wd.type))
|
||||
break;
|
||||
const int classPos = wd.value.indexOf(" class ");
|
||||
if (classPos == -1)
|
||||
break;
|
||||
const QString hexAddrS = wd.value.mid(0, classPos);
|
||||
static const QRegExp hexNullPattern(QLatin1String("0x0+"));
|
||||
Q_ASSERT(hexNullPattern.isValid());
|
||||
if (hexNullPattern.exactMatch(hexAddrS))
|
||||
break;
|
||||
const QString type = stripPointerType(wd.value.mid(classPos + 7));
|
||||
WatchData derefedWd;
|
||||
derefedWd.setType(type);
|
||||
derefedWd.setAddress(hexAddrS);
|
||||
derefedWd.name = QString(QLatin1Char('*'));
|
||||
derefedWd.iname = wd.iname + QLatin1String(".*");
|
||||
derefedWd.source = LocalsOwnerDumper;
|
||||
QList<WatchData> dumperResult;
|
||||
const CdbDumperHelper::DumpResult dr = dumpItem(dumper, derefedWd, &derefedWdItem, LocalsOwnerDumper, errorMessage);
|
||||
if (dr != CdbDumperHelper::DumpOk)
|
||||
break;
|
||||
// Insert the pointer item with 1 additional child + its dumper results
|
||||
// Note: formal arguments might already be expanded in the symbol group.
|
||||
ptrWd = new WatchItem(wd);
|
||||
ptrWd->source = LocalsOwnerDumper;
|
||||
ptrWd->setHasChildren(true);
|
||||
ptrWd->setChildrenUnneeded();
|
||||
ptrWd->addChild(derefedWdItem);
|
||||
parent->addChild(ptrWd);
|
||||
handled = true;
|
||||
} while (false);
|
||||
if (!handled) {
|
||||
delete derefedWdItem;
|
||||
delete ptrWd;
|
||||
}
|
||||
if (debugCDBWatchHandling > 1)
|
||||
qDebug() << "<expandPointerToDumpable returns " << handled << *errorMessage;
|
||||
return handled;
|
||||
}
|
||||
|
||||
// Main routine for inserting an item from the symbol group using the dumpers
|
||||
// where applicable.
|
||||
static inline bool insertDumpedItem(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
const WatchData &wd,
|
||||
WatchItem *parent,
|
||||
QString *errorMessage)
|
||||
{
|
||||
if (debugCDBWatchHandling > 1)
|
||||
qDebug() << "insertItem=" << wd.toString();
|
||||
// Check pointer to dumpeable, dumpeable, insert accordingly.
|
||||
if (expandPointerToDumpable(dumper, wd, parent, errorMessage))
|
||||
return true;
|
||||
WatchItem *dumpedItem = 0;
|
||||
// Add item owned by Dumper or symbol group on failure.
|
||||
const CdbDumperHelper::DumpResult dr = dumpItem(dumper, wd, &dumpedItem, LocalsOwnerDumper, errorMessage);
|
||||
switch (dr) {
|
||||
case CdbDumperHelper::DumpOk:
|
||||
dumpedItem->parent = parent;
|
||||
parent->children.push_back(dumpedItem);
|
||||
break;
|
||||
case CdbDumperHelper::DumpNotHandled:
|
||||
case CdbDumperHelper::DumpError: {
|
||||
WatchItem *symbolItem = new WatchItem(wd);
|
||||
symbolItem->source = LocalsOwnerSymbolGroup;
|
||||
parent->addChild(symbolItem);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper to add a sequence of WatchData from the symbol group context to an item.
|
||||
// Checks if the item is dumpable in some way; if so, dump it and use that instead of
|
||||
// symbol group.
|
||||
class SymbolGroupDumperInserter
|
||||
{
|
||||
public:
|
||||
explicit SymbolGroupDumperInserter(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
WatchItem *parent,
|
||||
QString *errorMessage);
|
||||
|
||||
inline SymbolGroupDumperInserter& operator*() { return *this; }
|
||||
inline SymbolGroupDumperInserter&operator=(const WatchData &wd) {
|
||||
insertDumpedItem(m_dumper, wd, m_parent, m_errorMessage);
|
||||
return *this;
|
||||
}
|
||||
inline SymbolGroupDumperInserter&operator++() { return *this; }
|
||||
|
||||
private:
|
||||
const QSharedPointer<CdbDumperHelper> m_dumper;
|
||||
WatchItem *m_parent;
|
||||
QString *m_errorMessage;
|
||||
};
|
||||
|
||||
SymbolGroupDumperInserter::SymbolGroupDumperInserter(const QSharedPointer<CdbDumperHelper> &dumper,
|
||||
WatchItem *parent,
|
||||
QString *errorMessage) :
|
||||
m_dumper(dumper),
|
||||
m_parent(parent),
|
||||
m_errorMessage(errorMessage)
|
||||
{
|
||||
}
|
||||
|
||||
// -------------- CdbLocalsModel
|
||||
|
||||
CdbLocalsModel::CdbLocalsModel(const QSharedPointer<CdbDumperHelper> &dh,
|
||||
WatchHandler *handler, WatchType type, QObject *parent) :
|
||||
AbstractSyncWatchModel(handler, type, parent),
|
||||
m_dumperHelper(dh),
|
||||
m_symbolGroupContext(0),
|
||||
m_useDumpers(false)
|
||||
{
|
||||
}
|
||||
|
||||
CdbLocalsModel::~CdbLocalsModel()
|
||||
{
|
||||
}
|
||||
|
||||
bool CdbLocalsModel::fetchChildren(WatchItem *wd, QString *errorMessage)
|
||||
{
|
||||
if (!m_symbolGroupContext) {
|
||||
*errorMessage = msgNoStackFrame();
|
||||
return false;
|
||||
}
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << "fetchChildren" << wd->iname;
|
||||
|
||||
// Check the owner and call it to expand the item.
|
||||
switch (wd->source) {
|
||||
case LocalsOwnerSymbolGroup:
|
||||
if (m_useDumpers && m_dumperHelper->isEnabled()) {
|
||||
SymbolGroupDumperInserter inserter(m_dumperHelper, wd, errorMessage);
|
||||
return m_symbolGroupContext->getChildSymbols(wd->iname, inserter, errorMessage);
|
||||
} else {
|
||||
return m_symbolGroupContext->getChildSymbols(wd->iname, SymbolGroupInserter(wd), errorMessage);
|
||||
}
|
||||
break;
|
||||
case LocalsOwnerDumper:
|
||||
if (dumpItem(m_dumperHelper, *wd, &wd, LocalsOwnerDumper, errorMessage) == CdbDumperHelper::DumpOk)
|
||||
return true;
|
||||
if (wd->isValueNeeded())
|
||||
wd->setValue(msgUnknown());
|
||||
qWarning("%s: No value for %s\n", Q_FUNC_INFO, qPrintable(wd->toString()));
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CdbLocalsModel::complete(WatchItem *wd, QString *errorMessage)
|
||||
{
|
||||
if (!m_symbolGroupContext) {
|
||||
*errorMessage = msgNoStackFrame();
|
||||
return false;
|
||||
}
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << "complete" << wd->iname;
|
||||
// Might as well fetch children when completing a dumped item.
|
||||
return fetchChildren(wd, errorMessage);
|
||||
}
|
||||
|
||||
void CdbLocalsModel::setSymbolGroupContext(CdbSymbolGroupContext *s)
|
||||
{
|
||||
if (s == m_symbolGroupContext)
|
||||
return;
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << ">setSymbolGroupContext" << s;
|
||||
m_symbolGroupContext = s;
|
||||
reinitialize();
|
||||
if (!m_symbolGroupContext)
|
||||
return;
|
||||
// Populate first row
|
||||
WatchItem *item = dummyRoot();
|
||||
QString errorMessage;
|
||||
do {
|
||||
if (m_useDumpers && m_dumperHelper->isEnabled()) {
|
||||
SymbolGroupDumperInserter inserter(m_dumperHelper, item, &errorMessage);
|
||||
if (!m_symbolGroupContext->getChildSymbols(m_symbolGroupContext->prefix(), inserter, &errorMessage)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!m_symbolGroupContext->getChildSymbols(m_symbolGroupContext->prefix(), SymbolGroupInserter(item), &errorMessage))
|
||||
break;
|
||||
}
|
||||
if (item->children.empty())
|
||||
break;
|
||||
reset();
|
||||
} while (false);
|
||||
if (!errorMessage.isEmpty())
|
||||
emit error(errorMessage);
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << "<setSymbolGroupContext" << item->children.size() << errorMessage;
|
||||
if (debugCDBWatchHandling > 1)
|
||||
qDebug() << '\n' << *this;
|
||||
}
|
||||
|
||||
// ---- CdbWatchModel
|
||||
|
||||
enum { WatchOwnerNewItem, WatchOwnerExpression, WatchOwnerDumper };
|
||||
|
||||
CdbWatchModel::CdbWatchModel(CdbDebugEngine *engine,
|
||||
const QSharedPointer<CdbDumperHelper> &dh,
|
||||
WatchHandler *handler,
|
||||
WatchType type, QObject *parent) :
|
||||
AbstractSyncWatchModel(handler, type, parent),
|
||||
m_dumperHelper(dh),
|
||||
m_engine(engine)
|
||||
{
|
||||
}
|
||||
|
||||
CdbWatchModel::~CdbWatchModel()
|
||||
{
|
||||
}
|
||||
|
||||
bool CdbWatchModel::evaluateWatchExpression(WatchData *wd, QString *errorMessage)
|
||||
{
|
||||
QString value;
|
||||
QString type;
|
||||
|
||||
const bool rc = m_engine->evaluateExpression(wd->exp, &value, &type, errorMessage);
|
||||
if (!rc) {
|
||||
wd->setValue(msgUnknown());
|
||||
return false;
|
||||
}
|
||||
wd->setValue(value);
|
||||
wd->setType(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CdbWatchModel::fetchChildren(WatchItem *wd, QString *errorMessage)
|
||||
{
|
||||
if (debugCDBWatchHandling)
|
||||
qDebug() << "Watch:fetchChildren" << wd->iname << wd->source;
|
||||
// We need to be halted.
|
||||
if (!m_engine->isDebuggeeHalted()) {
|
||||
*errorMessage = QCoreApplication::translate("CdbWatchModels", "Can evaluates watches only in halted state.");
|
||||
wd->setValue(msgUnknown());
|
||||
return false;
|
||||
}
|
||||
// New item with address -> dumper.
|
||||
if (wd->source == WatchOwnerNewItem)
|
||||
wd->source = wd->addr.isEmpty() ? WatchOwnerExpression : WatchOwnerDumper;
|
||||
// Expressions
|
||||
if (wd->source == WatchOwnerExpression)
|
||||
return evaluateWatchExpression(wd, errorMessage);
|
||||
// Variables by address
|
||||
if (!m_dumperHelper->isEnabled() || !theDebuggerBoolSetting(UseDebuggingHelpers)) {
|
||||
*errorMessage = QCoreApplication::translate("CdbWatchModels", "Cannot evaluate '%1' due to dumpers being disabled.").arg(wd->name);
|
||||
return false;
|
||||
}
|
||||
return dumpItem(m_dumperHelper, *wd, &wd, WatchOwnerDumper, errorMessage) == CdbDumperHelper::DumpOk;
|
||||
}
|
||||
|
||||
bool CdbWatchModel::complete(WatchItem *wd, QString *errorMessage)
|
||||
{
|
||||
return fetchChildren(wd, errorMessage);
|
||||
}
|
||||
|
||||
void CdbWatchModel::addWatcher(const WatchData &wd)
|
||||
{
|
||||
WatchItem *root = dummyRoot();
|
||||
if (!root)
|
||||
return;
|
||||
const QModelIndex rootIndex = watchIndex(root);
|
||||
beginInsertRows(rootIndex, root->children.size(), root->children.size());
|
||||
root->addChild(new WatchItem(wd));
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void CdbWatchModel::refresh()
|
||||
{
|
||||
// Refresh data for a new break
|
||||
WatchItem *root = dummyRoot();
|
||||
if (!root)
|
||||
return;
|
||||
const int childCount = root->children.size();
|
||||
if (!childCount)
|
||||
return;
|
||||
// reset flags, if there children, trigger a reset
|
||||
bool resetRequired = false;
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
WatchItem *topLevel = root->children.at(i);
|
||||
topLevel->setAllNeeded();
|
||||
if (!topLevel->children.empty()) {
|
||||
topLevel->removeChildren();
|
||||
resetRequired = true;
|
||||
}
|
||||
}
|
||||
// notify model
|
||||
if (resetRequired) {
|
||||
reset();
|
||||
} else {
|
||||
const QModelIndex topLeft = watchIndex(root->children.front());
|
||||
const QModelIndex bottomLeft = root->children.size() == 1 ? topLeft : watchIndex(root->children.back());
|
||||
emit dataChanged(topLeft.sibling(0, 1), bottomLeft.sibling(0, columnCount() -1));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
96
src/plugins/debugger/cdb/cdbwatchmodels.h
Normal file
96
src/plugins/debugger/cdb/cdbwatchmodels.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/**************************************************************************
|
||||
**
|
||||
** This file is part of Qt Creator
|
||||
**
|
||||
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
**
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** Commercial Usage
|
||||
**
|
||||
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||
** accordance with the Qt Commercial License Agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Nokia.
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** If you are unsure which license is appropriate for your use, please
|
||||
** contact the sales department at http://www.qtsoftware.com/contact.
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef CDBWATCHMODELS_H
|
||||
#define CDBWATCHMODELS_H
|
||||
|
||||
#include "abstractsyncwatchmodel.h"
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class CdbSymbolGroupContext;
|
||||
class CdbDumperHelper;
|
||||
class CdbDebugEngine;
|
||||
|
||||
class CdbLocalsModel : public AbstractSyncWatchModel {
|
||||
Q_DISABLE_COPY(CdbLocalsModel)
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CdbLocalsModel(const QSharedPointer<CdbDumperHelper> &dh,
|
||||
WatchHandler *handler, WatchType type, QObject *parent = 0);
|
||||
~CdbLocalsModel();
|
||||
|
||||
// Set a symbolgroup context, thus activating a stack frame.
|
||||
// Set 0 to clear out.
|
||||
void setSymbolGroupContext(CdbSymbolGroupContext *sg = 0);
|
||||
CdbSymbolGroupContext *symbolGroupContext() const { return m_symbolGroupContext; }
|
||||
|
||||
void setUseDumpers(bool d) { m_useDumpers = d; }
|
||||
|
||||
protected:
|
||||
virtual bool fetchChildren(WatchItem *wd, QString *errorMessage);
|
||||
virtual bool complete(WatchItem *wd, QString *errorMessage);
|
||||
|
||||
private:
|
||||
const QSharedPointer<CdbDumperHelper> m_dumperHelper;
|
||||
CdbSymbolGroupContext *m_symbolGroupContext;
|
||||
bool m_useDumpers;
|
||||
};
|
||||
|
||||
class CdbWatchModel : public AbstractSyncWatchModel {
|
||||
Q_DISABLE_COPY(CdbWatchModel)
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CdbWatchModel(CdbDebugEngine *engine,
|
||||
const QSharedPointer<CdbDumperHelper> &dh,
|
||||
WatchHandler *handler,
|
||||
WatchType type, QObject *parent = 0);
|
||||
~CdbWatchModel();
|
||||
|
||||
public slots:
|
||||
void addWatcher(const WatchData &d);
|
||||
void refresh();
|
||||
|
||||
protected:
|
||||
virtual bool fetchChildren(WatchItem *wd, QString *errorMessage);
|
||||
virtual bool complete(WatchItem *wd, QString *errorMessage);
|
||||
|
||||
private:
|
||||
bool evaluateWatchExpression(WatchData *wd, QString *errorMessage);
|
||||
|
||||
const QSharedPointer<CdbDumperHelper> m_dumperHelper;
|
||||
CdbDebugEngine *m_engine;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Debugger
|
||||
|
||||
#endif // CDBWATCHMODELS_H
|
||||
@@ -44,6 +44,8 @@ HEADERS += \
|
||||
sourcefileswindow.h \
|
||||
threadswindow.h \
|
||||
watchhandler.h \
|
||||
asyncwatchmodel.h \
|
||||
abstractsyncwatchmodel.h \
|
||||
watchwindow.h \
|
||||
|
||||
SOURCES += \
|
||||
@@ -71,6 +73,8 @@ SOURCES += \
|
||||
sourcefileswindow.cpp \
|
||||
threadswindow.cpp \
|
||||
watchhandler.cpp \
|
||||
asyncwatchmodel.cpp \
|
||||
abstractsyncwatchmodel.cpp \
|
||||
watchwindow.cpp \
|
||||
|
||||
FORMS += attachexternaldialog.ui \
|
||||
|
||||
@@ -187,7 +187,8 @@ static IDebuggerEngine *tcfEngine = 0;
|
||||
|
||||
DebuggerManager::DebuggerManager()
|
||||
: m_startParameters(new DebuggerStartParameters),
|
||||
m_inferiorPid(0)
|
||||
m_inferiorPid(0),
|
||||
m_watchHandler(new WatchHandler)
|
||||
{
|
||||
init();
|
||||
}
|
||||
@@ -231,6 +232,8 @@ void DebuggerManager::init()
|
||||
m_sourceFilesWindow = new SourceFilesWindow;
|
||||
m_threadsWindow = new ThreadsWindow;
|
||||
m_localsWindow = new WatchWindow(WatchWindow::LocalsType);
|
||||
m_watchHandler->init(m_localsWindow);
|
||||
|
||||
m_watchersWindow = new WatchWindow(WatchWindow::WatchersType);
|
||||
//m_tooltipWindow = new WatchWindow(WatchWindow::TooltipType);
|
||||
m_statusTimer = new QTimer(this);
|
||||
@@ -321,14 +324,7 @@ void DebuggerManager::init()
|
||||
m_registerHandler = new RegisterHandler;
|
||||
registerView->setModel(m_registerHandler->model());
|
||||
|
||||
// Locals
|
||||
m_watchHandler = new WatchHandler;
|
||||
QTreeView *localsView = qobject_cast<QTreeView *>(m_localsWindow);
|
||||
localsView->setModel(m_watchHandler->model(LocalsWatch));
|
||||
|
||||
// Watchers
|
||||
QTreeView *watchersView = qobject_cast<QTreeView *>(m_watchersWindow);
|
||||
watchersView->setModel(m_watchHandler->model(WatchersWatch));
|
||||
// Locals/Watchers
|
||||
connect(m_watchHandler, SIGNAL(sessionValueRequested(QString,QVariant*)),
|
||||
this, SIGNAL(sessionValueRequested(QString,QVariant*)));
|
||||
connect(m_watchHandler, SIGNAL(setSessionValueRequested(QString,QVariant)),
|
||||
@@ -337,12 +333,6 @@ void DebuggerManager::init()
|
||||
this, SLOT(assignValueInDebugger()), Qt::QueuedConnection);
|
||||
|
||||
// Tooltip
|
||||
//QTreeView *tooltipView = qobject_cast<QTreeView *>(m_tooltipWindow);
|
||||
//tooltipView->setModel(m_watchHandler->model(TooltipsWatch));
|
||||
|
||||
connect(m_watchHandler, SIGNAL(watchDataUpdateNeeded(WatchData)),
|
||||
this, SLOT(updateWatchData(WatchData)));
|
||||
|
||||
m_continueAction = new QAction(this);
|
||||
m_continueAction->setText(tr("Continue"));
|
||||
m_continueAction->setIcon(QIcon(":/debugger/images/debugger_continue_small.png"));
|
||||
@@ -471,12 +461,34 @@ void DebuggerManager::init()
|
||||
localsAndWatchers->setStretchFactor(2, 1);
|
||||
m_watchDock = createDockForWidget(localsAndWatchers);
|
||||
|
||||
initializeWatchModels(gdbEngine);
|
||||
|
||||
setStatus(DebuggerProcessNotReady);
|
||||
}
|
||||
|
||||
void DebuggerManager::initializeWatchModels(IDebuggerEngine *engine)
|
||||
{
|
||||
if (engine == 0)
|
||||
return;
|
||||
WatchModel *localsModel = engine->watchModel(LocalsWatch);
|
||||
m_watchHandler->setModel(LocalsWatch, localsModel);
|
||||
m_localsWindow->setModel(localsModel);
|
||||
|
||||
WatchModel *watchModel = engine->watchModel(WatchersWatch);
|
||||
m_watchHandler->setModel(WatchersWatch, watchModel);
|
||||
m_watchersWindow->setModel(watchModel);
|
||||
|
||||
if (WatchModel *toolTipModel = engine->watchModel(TooltipsWatch)) {
|
||||
m_watchHandler->setModel(TooltipsWatch, toolTipModel);
|
||||
} else {
|
||||
m_watchHandler->setModel(TooltipsWatch, 0);
|
||||
}
|
||||
}
|
||||
|
||||
QList<Core::IOptionsPage*> DebuggerManager::initializeEngines(unsigned enabledTypeFlags)
|
||||
{
|
||||
QList<Core::IOptionsPage*> rc;
|
||||
// Initialize watch models so columns show up initially.
|
||||
if (enabledTypeFlags & GdbEngineType)
|
||||
gdbEngine = createGdbEngine(this, &rc);
|
||||
winEngine = createWinEngine(this, (enabledTypeFlags & CdbEngineType), &rc);
|
||||
@@ -487,6 +499,12 @@ QList<Core::IOptionsPage*> DebuggerManager::initializeEngines(unsigned enabledTy
|
||||
m_engine = 0;
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << Q_FUNC_INFO << gdbEngine << winEngine << scriptEngine << rc.size();
|
||||
if (gdbEngine) {
|
||||
initializeWatchModels(gdbEngine);
|
||||
} else {
|
||||
if (winEngine)
|
||||
initializeWatchModels(winEngine);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -802,12 +820,6 @@ void DebuggerManager::setToolTipExpression(const QPoint &mousePos, TextEditor::I
|
||||
m_engine->setToolTipExpression(mousePos, editor, cursorPos);
|
||||
}
|
||||
|
||||
void DebuggerManager::updateWatchData(const WatchData &data)
|
||||
{
|
||||
if (m_engine)
|
||||
m_engine->updateWatchData(data);
|
||||
}
|
||||
|
||||
QVariant DebuggerManager::sessionValue(const QString &name)
|
||||
{
|
||||
// this is answered by the plugin
|
||||
@@ -862,7 +874,7 @@ static IDebuggerEngine *determineDebuggerEngine(const QString &executable,
|
||||
return scriptEngine;
|
||||
}
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#ifndef Q_OS_WIN
|
||||
Q_UNUSED(settingsIdHint)
|
||||
if (!gdbEngine) {
|
||||
*errorMessage = msgEngineNotAvailable("Gdb Engine");
|
||||
@@ -943,6 +955,7 @@ void DebuggerManager::startNewDebugger(DebuggerRunControl *runControl,
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!m_engine) {
|
||||
debuggingFinished();
|
||||
// Create Message box with possibility to go to settings
|
||||
@@ -956,6 +969,7 @@ void DebuggerManager::startNewDebugger(DebuggerRunControl *runControl,
|
||||
Core::ICore::instance()->showOptionsDialog(_(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY), settingsIdHint);
|
||||
return;
|
||||
}
|
||||
initializeWatchModels(m_engine);
|
||||
|
||||
if (Debugger::Constants::Internal::debug)
|
||||
qDebug() << m_startParameters->executable << m_engine;
|
||||
|
||||
@@ -46,6 +46,7 @@ class QModelIndex;
|
||||
class QPoint;
|
||||
class QTimer;
|
||||
class QWidget;
|
||||
class QTreeView;
|
||||
class QDebug;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
@@ -307,7 +308,6 @@ public slots:
|
||||
void detachDebugger();
|
||||
|
||||
void addToWatchWindow();
|
||||
void updateWatchData(const WatchData &data);
|
||||
|
||||
void sessionLoaded();
|
||||
void sessionUnloaded();
|
||||
@@ -418,6 +418,7 @@ public:
|
||||
private:
|
||||
void init();
|
||||
void runTest(const QString &fileName);
|
||||
void initializeWatchModels(IDebuggerEngine *engine);
|
||||
QDockWidget *createDockForWidget(QWidget *widget);
|
||||
Q_SLOT void createNewDock(QWidget *widget);
|
||||
void updateDockWidget(QDockWidget *dockWidget);
|
||||
@@ -483,13 +484,12 @@ private:
|
||||
|
||||
QWidget *m_breakWindow;
|
||||
QWidget *m_disassemblerWindow;
|
||||
QWidget *m_localsWindow;
|
||||
QTreeView *m_localsWindow;
|
||||
QWidget *m_registerWindow;
|
||||
QWidget *m_modulesWindow;
|
||||
//QWidget *m_tooltipWindow;
|
||||
QWidget *m_stackWindow;
|
||||
QWidget *m_threadsWindow;
|
||||
QWidget *m_watchersWindow;
|
||||
QTreeView *m_watchersWindow;
|
||||
DebuggerOutputWindow *m_outputWindow;
|
||||
|
||||
int m_status;
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "registerhandler.h"
|
||||
#include "stackhandler.h"
|
||||
#include "watchhandler.h"
|
||||
#include "asyncwatchmodel.h"
|
||||
#include "sourcefileswindow.h"
|
||||
|
||||
#include "debuggerdialogs.h"
|
||||
@@ -152,7 +153,8 @@ GdbEngine::GdbEngine(DebuggerManager *parent) :
|
||||
m_dumperInjectionLoad(false),
|
||||
#endif
|
||||
q(parent),
|
||||
qq(parent->engineInterface())
|
||||
qq(parent->engineInterface()),
|
||||
m_models(qq->watchHandler())
|
||||
{
|
||||
m_stubProc.setMode(Core::Utils::ConsoleProcess::Debug);
|
||||
#ifdef Q_OS_UNIX
|
||||
@@ -170,6 +172,8 @@ GdbEngine::~GdbEngine()
|
||||
|
||||
void GdbEngine::initializeConnections()
|
||||
{
|
||||
connect(qq->watchHandler(), SIGNAL(watcherInserted(WatchData)), &m_models, SLOT(insertWatcher(WatchData)));
|
||||
connect(&m_models, SIGNAL(watchDataUpdateNeeded(WatchData)), this, SLOT(updateWatchData(WatchData)));
|
||||
// Gdb Process interaction
|
||||
connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
|
||||
this, SLOT(gdbProcError(QProcess::ProcessError)));
|
||||
@@ -2696,10 +2700,10 @@ static QString tooltipINameForExpression(const QString &exp)
|
||||
bool GdbEngine::showToolTip()
|
||||
{
|
||||
WatchHandler *handler = qq->watchHandler();
|
||||
WatchModel *model = handler->model(TooltipsWatch);
|
||||
AsyncWatchModel *model = static_cast<AsyncWatchModel *>(handler->model(TooltipsWatch));
|
||||
QString iname = tooltipINameForExpression(m_toolTipExpression);
|
||||
model->setActiveData(iname);
|
||||
WatchItem *item = model->findItem(iname, model->dummyRoot());
|
||||
WatchItem *item = model->findItemByIName(iname, model->dummyRoot());
|
||||
if (!item) {
|
||||
hideDebuggerToolTip();
|
||||
return false;
|
||||
@@ -2788,7 +2792,7 @@ void GdbEngine::setToolTipExpression(const QPoint &mousePos,
|
||||
toolTip.name = exp;
|
||||
toolTip.iname = tooltipINameForExpression(exp);
|
||||
qq->watchHandler()->removeData(toolTip.iname);
|
||||
qq->watchHandler()->insertData(toolTip);
|
||||
m_models.insertData(toolTip);
|
||||
}
|
||||
|
||||
|
||||
@@ -3146,7 +3150,7 @@ void GdbEngine::rebuildModel()
|
||||
PENDING_DEBUG("REBUILDING MODEL");
|
||||
emit gdbInputAvailable(LogStatus, _("<Rebuild Watchmodel>"));
|
||||
q->showStatusMessage(tr("Finished retrieving data."), 400);
|
||||
qq->watchHandler()->endCycle();
|
||||
m_models.endCycle();
|
||||
showToolTip();
|
||||
}
|
||||
|
||||
@@ -3536,7 +3540,7 @@ void GdbEngine::updateLocals()
|
||||
PENDING_DEBUG("\nRESET PENDING");
|
||||
//m_toolTipCache.clear();
|
||||
m_toolTipExpression.clear();
|
||||
qq->watchHandler()->beginCycle();
|
||||
m_models.beginCycle();
|
||||
|
||||
QString level = QString::number(currentFrame());
|
||||
// '2' is 'list with type and value'
|
||||
@@ -3590,7 +3594,7 @@ void GdbEngine::handleStackListLocals(const GdbResultRecord &record, const QVari
|
||||
locals += m_currentFunctionArgs;
|
||||
|
||||
setLocals(locals);
|
||||
qq->watchHandler()->updateWatchers();
|
||||
m_models.updateWatchers();
|
||||
}
|
||||
|
||||
void GdbEngine::setLocals(const QList<GdbMi> &locals)
|
||||
@@ -3662,7 +3666,7 @@ void GdbEngine::insertData(const WatchData &data0)
|
||||
qDebug() << "BOGUS VALUE:" << data.toString();
|
||||
return;
|
||||
}
|
||||
qq->watchHandler()->insertData(data);
|
||||
m_models.insertData(data);
|
||||
}
|
||||
|
||||
void GdbEngine::handleVarListChildrenHelper(const GdbMi &item,
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "gdbmi.h"
|
||||
#include "outputcollector.h"
|
||||
#include "watchutils.h"
|
||||
#include "asyncwatchmodel.h"
|
||||
|
||||
#include <consoleprocess.h>
|
||||
|
||||
@@ -122,6 +123,8 @@ private:
|
||||
void loadAllSymbols();
|
||||
virtual QList<Symbol> moduleSymbols(const QString &moduleName);
|
||||
|
||||
WatchModel *watchModel(int type) const { return m_models.model(type); }
|
||||
|
||||
Q_SLOT void setDebugDebuggingHelpers(const QVariant &on);
|
||||
Q_SLOT void setUseDebuggingHelpers(const QVariant &on);
|
||||
|
||||
@@ -197,6 +200,7 @@ private slots:
|
||||
void stubStarted();
|
||||
void stubError(const QString &msg);
|
||||
void uploadProcError(QProcess::ProcessError error);
|
||||
void updateWatchData(const WatchData &data);
|
||||
|
||||
private:
|
||||
int terminationIndex(const QByteArray &buffer, int &length);
|
||||
@@ -324,7 +328,6 @@ private:
|
||||
// FIXME: BaseClass. called to improve situation for a watch item
|
||||
void updateSubItem(const WatchData &data);
|
||||
|
||||
void updateWatchData(const WatchData &data);
|
||||
void rebuildModel();
|
||||
|
||||
void insertData(const WatchData &data);
|
||||
@@ -384,6 +387,7 @@ private:
|
||||
|
||||
DebuggerManager * const q;
|
||||
IDebuggerManagerAccessForEngines * const qq;
|
||||
AsyncWatchModelMixin m_models;
|
||||
// make sure to re-initialize new members in initializeVariables();
|
||||
};
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ class ITextEditor;
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class WatchModel;
|
||||
|
||||
class Symbol;
|
||||
class WatchData;
|
||||
struct DebuggerStartParameters;
|
||||
@@ -60,14 +62,13 @@ public:
|
||||
virtual bool startDebugger(const QSharedPointer<DebuggerStartParameters> &startParameters) = 0;
|
||||
virtual void exitDebugger() = 0;
|
||||
virtual void detachDebugger() {}
|
||||
virtual void updateWatchData(const WatchData &data) = 0;
|
||||
|
||||
virtual void stepExec() = 0;
|
||||
virtual void stepOutExec() = 0;
|
||||
virtual void nextExec() = 0;
|
||||
virtual void stepIExec() = 0;
|
||||
virtual void nextIExec() = 0;
|
||||
|
||||
|
||||
virtual void continueInferior() = 0;
|
||||
virtual void interruptInferior() = 0;
|
||||
|
||||
@@ -95,6 +96,8 @@ public:
|
||||
virtual void reloadFullStack() = 0;
|
||||
|
||||
virtual void watchPoint(const QPoint &) {}
|
||||
|
||||
virtual WatchModel *watchModel(int type) const = 0;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -185,10 +185,13 @@ void ScriptAgent::scriptUnload(qint64 scriptId)
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
ScriptEngine::ScriptEngine(DebuggerManager *parent)
|
||||
ScriptEngine::ScriptEngine(DebuggerManager *parent) :
|
||||
q(parent),
|
||||
qq(parent->engineInterface()),
|
||||
m_models(qq->watchHandler())
|
||||
{
|
||||
q = parent;
|
||||
qq = parent->engineInterface();
|
||||
connect(qq->watchHandler(), SIGNAL(watcherInserted(WatchData)), &m_models, SLOT(insertWatcher(WatchData)));
|
||||
connect(&m_models, SIGNAL(watchDataUpdateNeeded(WatchData)), this, SLOT(updateWatchData(WatchData)));
|
||||
m_scriptEngine = new QScriptEngine(this);
|
||||
m_scriptAgent = new ScriptAgent(this, m_scriptEngine);
|
||||
m_scriptEngine->setAgent(m_scriptAgent);
|
||||
@@ -572,7 +575,7 @@ void ScriptEngine::maybeBreakNow(bool byFunction)
|
||||
void ScriptEngine::updateLocals()
|
||||
{
|
||||
QScriptContext *context = m_scriptEngine->currentContext();
|
||||
qq->watchHandler()->beginCycle();
|
||||
m_models.beginCycle();
|
||||
//SDEBUG("UPDATE LOCALS");
|
||||
|
||||
//
|
||||
@@ -604,7 +607,7 @@ void ScriptEngine::updateLocals()
|
||||
data.iname = "local";
|
||||
data.name = "local";
|
||||
data.scriptValue = context->activationObject();
|
||||
qq->watchHandler()->insertData(data);
|
||||
m_models.insertData(data);
|
||||
|
||||
// FIXME: Use an extra thread. This here is evil
|
||||
m_stopped = true;
|
||||
@@ -679,7 +682,7 @@ void ScriptEngine::updateSubItem(const WatchData &data0)
|
||||
data.setType("<unknown>");
|
||||
data.setValue("<unknown>");
|
||||
}
|
||||
qq->watchHandler()->insertData(data);
|
||||
m_models.insertData(data);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -697,13 +700,13 @@ void ScriptEngine::updateSubItem(const WatchData &data0)
|
||||
data1.setChildrenNeeded();
|
||||
else
|
||||
data1.setChildrenUnneeded();
|
||||
qq->watchHandler()->insertData(data1);
|
||||
m_models.insertData(data1);
|
||||
++numChild;
|
||||
}
|
||||
//SDEBUG(" ... CHILDREN: " << numChild);
|
||||
data.setHasChildren(numChild > 0);
|
||||
data.setChildrenUnneeded();
|
||||
qq->watchHandler()->insertData(data);
|
||||
m_models.insertData(data);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -716,7 +719,7 @@ void ScriptEngine::updateSubItem(const WatchData &data0)
|
||||
}
|
||||
data.setHasChildren(numChild > 0);
|
||||
//SDEBUG(" ... CHILDCOUNT: " << numChild);
|
||||
qq->watchHandler()->insertData(data);
|
||||
m_models.insertData(data);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ class QScriptValue;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "idebuggerengine.h"
|
||||
#include "asyncwatchmodel.h"
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
@@ -107,14 +108,20 @@ private:
|
||||
|
||||
bool supportsThreads() const { return true; }
|
||||
void maybeBreakNow(bool byFunction);
|
||||
void updateWatchData(const WatchData &data);
|
||||
|
||||
void updateLocals();
|
||||
void updateSubItem(const WatchData &data);
|
||||
|
||||
WatchModel *watchModel(int type) const { return m_models.model(type); }
|
||||
|
||||
private slots:
|
||||
void updateWatchData(const WatchData &data);
|
||||
|
||||
private:
|
||||
friend class ScriptAgent;
|
||||
DebuggerManager *q;
|
||||
IDebuggerManagerAccessForEngines *qq;
|
||||
AsyncWatchModelMixin m_models;
|
||||
|
||||
QScriptEngine *m_scriptEngine;
|
||||
QString m_scriptContents;
|
||||
|
||||
@@ -112,11 +112,13 @@ QString TcfEngine::TcfCommand::toString() const
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
TcfEngine::TcfEngine(DebuggerManager *parent)
|
||||
TcfEngine::TcfEngine(DebuggerManager *parent) :
|
||||
q(parent),
|
||||
qq(parent->engineInterface()),
|
||||
m_models(qq->watchHandler())
|
||||
{
|
||||
q = parent;
|
||||
qq = parent->engineInterface();
|
||||
|
||||
connect(qq->watchHandler(), SIGNAL(watcherInserted(WatchData)), &m_models, SLOT(insertWatcher(WatchData)));
|
||||
connect(&m_models, SIGNAL(watchDataUpdateNeeded(WatchData)), this, SLOT(updateWatchData(WatchData)));
|
||||
m_congestion = 0;
|
||||
m_inAir = 0;
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#ifndef DEBUGGER_TCFENGINE_H
|
||||
#define DEBUGGER_TCFENGINE_H
|
||||
|
||||
#include "asyncwatchmodel.h"
|
||||
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QMap>
|
||||
@@ -112,10 +114,11 @@ private:
|
||||
|
||||
bool supportsThreads() const { return true; }
|
||||
void maybeBreakNow(bool byFunction);
|
||||
void updateWatchData(const WatchData &data);
|
||||
void updateLocals();
|
||||
void updateSubItem(const WatchData &data);
|
||||
|
||||
WatchModel *watchModel(int type) const { return m_models.model(type); }
|
||||
|
||||
Q_SLOT void socketConnected();
|
||||
Q_SLOT void socketDisconnected();
|
||||
Q_SLOT void socketError(QAbstractSocket::SocketError);
|
||||
@@ -128,6 +131,7 @@ private:
|
||||
|
||||
private:
|
||||
Q_SLOT void startDebugging();
|
||||
Q_SLOT void updateWatchData(const WatchData &data);
|
||||
|
||||
typedef void (TcfEngine::*TcfCommandCallback)
|
||||
(const JsonValue &record, const QVariant &cookie);
|
||||
@@ -153,7 +157,7 @@ private:
|
||||
QHash<int, TcfCommand> m_cookieForToken;
|
||||
|
||||
QQueue<TcfCommand> m_sendQueue;
|
||||
|
||||
|
||||
// timer based congestion control. does not seem to work well.
|
||||
void enqueueCommand(const TcfCommand &command);
|
||||
Q_SLOT void handleSendTimer();
|
||||
@@ -166,6 +170,7 @@ private:
|
||||
|
||||
DebuggerManager *q;
|
||||
IDebuggerManagerAccessForEngines *qq;
|
||||
AsyncWatchModelMixin m_models;
|
||||
QTcpSocket *m_socket;
|
||||
QByteArray m_inbuffer;
|
||||
QList<QByteArray> m_services;
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include <QtGui/QLabel>
|
||||
#include <QtGui/QToolTip>
|
||||
#include <QtGui/QTextEdit>
|
||||
#include <QtGui/QTreeView>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
@@ -67,40 +68,13 @@
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
static const QString strNotInScope =
|
||||
QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
|
||||
|
||||
static int watcherCounter = 0;
|
||||
static int generationCounter = 0;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WatchItem
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
class WatchItem : public WatchData
|
||||
{
|
||||
public:
|
||||
WatchItem() { parent = 0; fetchTriggered = false; }
|
||||
|
||||
WatchItem(const WatchData &data) : WatchData(data)
|
||||
{ parent = 0; fetchTriggered = false; }
|
||||
|
||||
void setData(const WatchData &data)
|
||||
{ static_cast<WatchData &>(*this) = data; }
|
||||
|
||||
WatchItem *parent;
|
||||
bool fetchTriggered; // children fetch has been triggered
|
||||
QList<WatchItem *> children; // fetched children
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WatchData
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
WatchData::WatchData() :
|
||||
hasChildren(false),
|
||||
generation(-1),
|
||||
@@ -148,7 +122,7 @@ void WatchData::setValue(const QString &value0)
|
||||
// column. No need to duplicate it here.
|
||||
if (value.startsWith("(" + type + ") 0x"))
|
||||
value = value.section(" ", -1, -1);
|
||||
|
||||
|
||||
setValueUnneeded();
|
||||
}
|
||||
|
||||
@@ -272,109 +246,125 @@ QString WatchData::toToolTip() const
|
||||
return res;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WatchItem
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
WatchItem::WatchItem() :
|
||||
parent(0),
|
||||
fetchTriggered(false)
|
||||
{
|
||||
}
|
||||
|
||||
WatchItem::WatchItem(const WatchData &data) :
|
||||
WatchData(data),
|
||||
parent(0),
|
||||
fetchTriggered(false)
|
||||
{
|
||||
}
|
||||
|
||||
void WatchItem::setData(const WatchData &data)
|
||||
{
|
||||
static_cast<WatchData &>(*this) = data;
|
||||
}
|
||||
|
||||
WatchItem::~WatchItem()
|
||||
{
|
||||
qDeleteAll(children);
|
||||
}
|
||||
|
||||
void WatchItem::removeChildren()
|
||||
{
|
||||
if (!children.empty()) {
|
||||
qDeleteAll(children);
|
||||
children.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void WatchItem::addChild(WatchItem *child)
|
||||
{
|
||||
children.push_back(child);
|
||||
child->parent = this;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WatchPredicate
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
WatchPredicate::WatchPredicate(Mode mode, const QString &pattern) :
|
||||
m_pattern(pattern),
|
||||
m_mode(mode)
|
||||
{
|
||||
}
|
||||
|
||||
bool WatchPredicate::operator()(const WatchData &w) const
|
||||
{
|
||||
switch (m_mode) {
|
||||
case INameMatch:
|
||||
return m_pattern == w.iname;
|
||||
case ExpressionMatch:
|
||||
return m_pattern == w.exp;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WatchModel
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
WatchModel::WatchModel(WatchHandler *handler, WatchType type)
|
||||
: QAbstractItemModel(handler), m_handler(handler), m_type(type)
|
||||
WatchModel::WatchModel(WatchHandler *handler, WatchType type, QObject *parent) :
|
||||
QAbstractItemModel(parent),
|
||||
m_root(new WatchItem),
|
||||
m_handler(handler),
|
||||
m_type(type)
|
||||
{
|
||||
m_root = new WatchItem;
|
||||
WatchItem *dummyRoot = new WatchItem;
|
||||
|
||||
switch (type) {
|
||||
case LocalsWatch:
|
||||
dummyRoot->iname = QLatin1String("local");
|
||||
dummyRoot->name = WatchHandler::tr("Locals");
|
||||
break;
|
||||
case WatchersWatch:
|
||||
dummyRoot->iname = QLatin1String("watch");
|
||||
dummyRoot->name = WatchHandler::tr("Watchers");
|
||||
break;
|
||||
case TooltipsWatch:
|
||||
dummyRoot->iname = QLatin1String("tooltip");
|
||||
dummyRoot->name = WatchHandler::tr("Tooltip");
|
||||
break;
|
||||
case WatchModelCount:
|
||||
break;
|
||||
}
|
||||
dummyRoot->hasChildren = true;
|
||||
dummyRoot->state = 0;
|
||||
|
||||
m_root->addChild(dummyRoot);
|
||||
|
||||
m_root->hasChildren = 1;
|
||||
m_root->state = 0;
|
||||
m_root->name = WatchHandler::tr("Root");
|
||||
|
||||
WatchItem *item = new WatchItem;
|
||||
|
||||
switch (m_type) {
|
||||
case LocalsWatch:
|
||||
item->iname = QLatin1String("local");
|
||||
item->name = WatchHandler::tr("Locals");
|
||||
break;
|
||||
case WatchersWatch:
|
||||
item->iname = QLatin1String("watch");
|
||||
item->name = WatchHandler::tr("Watchers");
|
||||
break;
|
||||
case TooltipsWatch:
|
||||
item->iname = QLatin1String("tooltip");
|
||||
item->name = WatchHandler::tr("Tooltip");
|
||||
break;
|
||||
}
|
||||
item->hasChildren = true;
|
||||
item->state = 0;
|
||||
item->parent = m_root;
|
||||
item->fetchTriggered = true;
|
||||
|
||||
m_root->children.append(item);
|
||||
}
|
||||
|
||||
WatchItem *WatchModel::dummyRoot() const
|
||||
WatchModel::~WatchModel()
|
||||
{
|
||||
QTC_ASSERT(!m_root->children.isEmpty(), return 0);
|
||||
return m_root->children.front();
|
||||
delete m_root;
|
||||
}
|
||||
|
||||
void WatchModel::reinitialize()
|
||||
QString WatchModel::parentName(const QString &iname)
|
||||
{
|
||||
WatchItem *item = dummyRoot();
|
||||
QTC_ASSERT(item, return);
|
||||
QModelIndex index = watchIndex(item);
|
||||
int n = item->children.size();
|
||||
if (n == 0)
|
||||
return;
|
||||
//MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << item->iname);
|
||||
beginRemoveRows(index, 0, n - 1);
|
||||
qDeleteAll(item->children);
|
||||
item->children.clear();
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
void WatchModel::removeOutdated()
|
||||
{
|
||||
WatchItem *item = dummyRoot();
|
||||
QTC_ASSERT(item, return);
|
||||
foreach (WatchItem *child, item->children)
|
||||
removeOutdatedHelper(child);
|
||||
#if DEBUG_MODEL
|
||||
#if USE_MODEL_TEST
|
||||
//(void) new ModelTest(this, this);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void WatchModel::removeOutdatedHelper(WatchItem *item)
|
||||
{
|
||||
if (item->generation < generationCounter)
|
||||
removeItem(item);
|
||||
else {
|
||||
foreach (WatchItem *child, item->children)
|
||||
removeOutdatedHelper(child);
|
||||
item->fetchTriggered = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WatchModel::removeItem(WatchItem *item)
|
||||
{
|
||||
WatchItem *parent = item->parent;
|
||||
QModelIndex index = watchIndex(parent);
|
||||
int n = parent->children.indexOf(item);
|
||||
//MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n);
|
||||
beginRemoveRows(index, n, n);
|
||||
parent->children.removeAt(n);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
static QString parentName(const QString &iname)
|
||||
{
|
||||
int pos = iname.lastIndexOf(QLatin1Char('.'));
|
||||
const int pos = iname.lastIndexOf(QLatin1Char('.'));
|
||||
if (pos == -1)
|
||||
return QString();
|
||||
return iname.left(pos);
|
||||
}
|
||||
|
||||
|
||||
static QString chopConst(QString type)
|
||||
{
|
||||
while (1) {
|
||||
@@ -419,7 +409,7 @@ QString niceType(const QString typeIn)
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
int start = type.indexOf("std::allocator<");
|
||||
if (start == -1)
|
||||
break;
|
||||
break;
|
||||
// search for matching '>'
|
||||
int pos;
|
||||
int level = 0;
|
||||
@@ -527,51 +517,10 @@ static QString formattedValue(const WatchData &data,
|
||||
return data.value;
|
||||
}
|
||||
|
||||
bool WatchModel::canFetchMore(const QModelIndex &index) const
|
||||
int WatchModel::columnCount(const QModelIndex &idx) const
|
||||
{
|
||||
return index.isValid() && !watchItem(index)->fetchTriggered;
|
||||
}
|
||||
|
||||
void WatchModel::fetchMore(const QModelIndex &index)
|
||||
{
|
||||
QTC_ASSERT(index.isValid(), return);
|
||||
QTC_ASSERT(!watchItem(index)->fetchTriggered, return);
|
||||
if (WatchItem *item = watchItem(index)) {
|
||||
item->fetchTriggered = true;
|
||||
WatchData data = *item;
|
||||
data.setChildrenNeeded();
|
||||
emit m_handler->watchDataUpdateNeeded(data);
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!hasIndex(row, column, parent))
|
||||
return QModelIndex();
|
||||
|
||||
const WatchItem *item = watchItem(parent);
|
||||
QTC_ASSERT(item, return QModelIndex());
|
||||
return createIndex(row, column, (void*)(item->children.at(row)));
|
||||
}
|
||||
|
||||
QModelIndex WatchModel::parent(const QModelIndex &idx) const
|
||||
{
|
||||
if (!idx.isValid())
|
||||
return QModelIndex();
|
||||
|
||||
const WatchItem *item = watchItem(idx);
|
||||
if (!item->parent || item->parent == m_root)
|
||||
return QModelIndex();
|
||||
|
||||
const WatchItem *grandparent = item->parent->parent;
|
||||
if (!grandparent)
|
||||
return QModelIndex();
|
||||
|
||||
for (int i = 0; i < grandparent->children.size(); ++i)
|
||||
if (grandparent->children.at(i) == item->parent)
|
||||
return createIndex(i, 0, (void*) item->parent);
|
||||
|
||||
return QModelIndex();
|
||||
Q_UNUSED(idx);
|
||||
return 3;
|
||||
}
|
||||
|
||||
int WatchModel::rowCount(const QModelIndex &idx) const
|
||||
@@ -583,45 +532,18 @@ int WatchModel::rowCount(const QModelIndex &idx) const
|
||||
return watchItem(idx)->children.size();
|
||||
}
|
||||
|
||||
int WatchModel::columnCount(const QModelIndex &idx) const
|
||||
bool WatchModel::hasChildren(const QModelIndex &idx) const
|
||||
{
|
||||
Q_UNUSED(idx);
|
||||
return 3;
|
||||
}
|
||||
|
||||
bool WatchModel::hasChildren(const QModelIndex &parent) const
|
||||
{
|
||||
WatchItem *item = watchItem(parent);
|
||||
return !item || item->hasChildren;
|
||||
}
|
||||
|
||||
WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
|
||||
{
|
||||
return idx.isValid()
|
||||
? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
|
||||
}
|
||||
|
||||
QModelIndex WatchModel::watchIndex(const WatchItem *item) const
|
||||
{
|
||||
return watchIndexHelper(item, m_root, QModelIndex());
|
||||
}
|
||||
|
||||
QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle,
|
||||
const WatchItem *parentItem, const QModelIndex &parentIndex) const
|
||||
{
|
||||
if (needle == parentItem)
|
||||
return parentIndex;
|
||||
for (int i = parentItem->children.size(); --i >= 0; ) {
|
||||
const WatchItem *childItem = parentItem->children.at(i);
|
||||
QModelIndex childIndex = index(i, 0, parentIndex);
|
||||
QModelIndex idx = watchIndexHelper(needle, childItem, childIndex);
|
||||
if (idx.isValid())
|
||||
return idx;
|
||||
if (const WatchItem *item = watchItem(idx)) {
|
||||
if (!item->children.empty())
|
||||
return true;
|
||||
QTC_ASSERT(item->isHasChildrenKnown(), return false);
|
||||
return item->hasChildren;
|
||||
}
|
||||
return QModelIndex();
|
||||
return false;
|
||||
}
|
||||
|
||||
void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
|
||||
void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
|
||||
{
|
||||
QModelIndex idx1 = index(0, column, parentIndex);
|
||||
QModelIndex idx2 = index(rowCount(parentIndex) - 1, column, parentIndex);
|
||||
@@ -635,11 +557,16 @@ void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
|
||||
|
||||
QVariant WatchModel::data(const QModelIndex &idx, int role) const
|
||||
{
|
||||
const WatchItem &data = *watchItem(idx);
|
||||
if (const WatchData *wdata = watchItem(idx))
|
||||
return data(*wdata, idx.column(), role);
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant WatchModel::data(const WatchData &data, int column, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole: {
|
||||
switch (idx.column()) {
|
||||
switch (column) {
|
||||
case 0: return data.name;
|
||||
case 1: return formattedValue(data,
|
||||
m_handler->m_individualFormats[data.iname],
|
||||
@@ -657,7 +584,7 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
|
||||
static const QVariant red(QColor(200, 0, 0));
|
||||
static const QVariant black(QColor(0, 0, 0));
|
||||
static const QVariant gray(QColor(140, 140, 140));
|
||||
switch (idx.column()) {
|
||||
switch (column) {
|
||||
case 0: return black;
|
||||
case 1: return data.valuedisabled ? gray : data.changed ? red : black;
|
||||
case 2: return black;
|
||||
@@ -678,7 +605,7 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
|
||||
case ActiveDataRole:
|
||||
qDebug() << "ASK FOR" << data.iname;
|
||||
return true;
|
||||
|
||||
|
||||
case TypeFormatListRole:
|
||||
if (isIntType(data.type))
|
||||
return QStringList() << tr("decimal") << tr("hexadecimal")
|
||||
@@ -696,14 +623,14 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
WatchItem &data = *watchItem(index);
|
||||
WatchData &data = *watchItem(index);
|
||||
if (role == ExpandedRole) {
|
||||
if (value.toBool())
|
||||
m_handler->m_expandedINames.insert(data.iname);
|
||||
@@ -760,75 +687,162 @@ QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int ro
|
||||
case 2: return QString(tr("Type") + QLatin1String(" "));
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
static bool iNameSorter(const WatchItem *item1, const WatchItem *item2)
|
||||
void WatchModel::setValueByIName(const QString &iname, const QString &value)
|
||||
{
|
||||
QString name1 = item1->iname.section('.', -1);
|
||||
QString name2 = item2->iname.section('.', -1);
|
||||
if (!name1.isEmpty() && !name2.isEmpty()) {
|
||||
if (name1.at(0).isDigit() && name2.at(0).isDigit())
|
||||
return name1.toInt() < name2.toInt();
|
||||
if (WatchItem *wd = findItemByIName(iname, dummyRoot())) {
|
||||
if (wd->value != value) {
|
||||
wd->setValue(value);
|
||||
const QModelIndex index = watchIndex(wd);
|
||||
emit dataChanged(index.sibling(0, 1), index.sibling(0, 2));
|
||||
}
|
||||
}
|
||||
return name1 < name2;
|
||||
}
|
||||
|
||||
static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
|
||||
void WatchModel::setValueByExpression(const QString &exp, const QString &value)
|
||||
{
|
||||
QList<WatchItem *>::const_iterator it =
|
||||
qLowerBound(list.begin(), list.end(), item, iNameSorter);
|
||||
return it - list.begin();
|
||||
if (WatchItem *wd = findItemByExpression(exp, dummyRoot())) {
|
||||
if (wd->value != value) {
|
||||
wd->setValue(value);
|
||||
const QModelIndex index = watchIndex(wd);
|
||||
emit dataChanged(index.sibling(0, 1), index.sibling(0, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WatchModel::insertData(const WatchData &data)
|
||||
WatchItem *WatchModel::root() const
|
||||
{
|
||||
// qDebug() << "WMI:" << data.toString();
|
||||
QTC_ASSERT(!data.iname.isEmpty(), return);
|
||||
WatchItem *parent = findItem(parentName(data.iname), m_root);
|
||||
if (!parent) {
|
||||
WatchData parent;
|
||||
parent.iname = parentName(data.iname);
|
||||
insertData(parent);
|
||||
//MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname);
|
||||
return m_root;
|
||||
}
|
||||
|
||||
WatchItem *WatchModel::dummyRoot() const
|
||||
{
|
||||
if (!m_root->children.isEmpty())
|
||||
return m_root->children.front();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WatchModel::reinitialize()
|
||||
{
|
||||
WatchItem *item = dummyRoot();
|
||||
QTC_ASSERT(item, return);
|
||||
const QModelIndex index = watchIndex(item);
|
||||
const int n = item->children.size();
|
||||
if (n == 0)
|
||||
return;
|
||||
}
|
||||
QModelIndex index = watchIndex(parent);
|
||||
if (WatchItem *oldItem = findItem(data.iname, parent)) {
|
||||
// overwrite old entry
|
||||
//MODEL_DEBUG("OVERWRITE : " << data.iname << data.value);
|
||||
bool changed = !data.value.isEmpty()
|
||||
&& data.value != oldItem->value
|
||||
&& data.value != strNotInScope;
|
||||
oldItem->setData(data);
|
||||
oldItem->changed = changed;
|
||||
oldItem->generation = generationCounter;
|
||||
QModelIndex idx = watchIndex(oldItem);
|
||||
emit dataChanged(idx, idx.sibling(idx.row(), 2));
|
||||
} else {
|
||||
// add new entry
|
||||
//MODEL_DEBUG("INSERT : " << data.iname << data.value);
|
||||
WatchItem *item = new WatchItem(data);
|
||||
item->parent = parent;
|
||||
item->generation = generationCounter;
|
||||
item->changed = true;
|
||||
int n = findInsertPosition(parent->children, item);
|
||||
beginInsertRows(index, n, n);
|
||||
parent->children.insert(n, item);
|
||||
endInsertRows();
|
||||
}
|
||||
//MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << item->iname);
|
||||
beginRemoveRows(index, 0, n - 1);
|
||||
item->removeChildren();
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
WatchItem *WatchModel::findItem(const QString &iname, WatchItem *root) const
|
||||
void WatchModel::removeItem(WatchItem *itemIn)
|
||||
{
|
||||
if (root->iname == iname)
|
||||
WatchItem *item = static_cast<WatchItem *>(itemIn);
|
||||
WatchItem *parent = item->parent;
|
||||
const QModelIndex index = watchIndex(parent);
|
||||
const int n = parent->children.indexOf(item);
|
||||
//MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n);
|
||||
beginRemoveRows(index, n, n);
|
||||
parent->children.removeAt(n);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!hasIndex(row, column, parent))
|
||||
return QModelIndex();
|
||||
|
||||
const WatchItem *item = watchItem(parent);
|
||||
QTC_ASSERT(item, return QModelIndex());
|
||||
return createIndex(row, column, (void*)(item->children.at(row)));
|
||||
}
|
||||
|
||||
QModelIndex WatchModel::parent(const QModelIndex &idx) const
|
||||
{
|
||||
if (!idx.isValid())
|
||||
return QModelIndex();
|
||||
|
||||
const WatchItem *item = static_cast<WatchItem *>(watchItem(idx));
|
||||
if (!item->parent || item->parent == m_root)
|
||||
return QModelIndex();
|
||||
|
||||
const WatchItem *grandparent = item->parent->parent;
|
||||
if (!grandparent)
|
||||
return QModelIndex();
|
||||
|
||||
for (int i = 0; i < grandparent->children.size(); ++i)
|
||||
if (grandparent->children.at(i) == item->parent)
|
||||
return createIndex(i, 0, (void*) item->parent);
|
||||
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
|
||||
|
||||
{
|
||||
return idx.isValid()
|
||||
? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
|
||||
}
|
||||
|
||||
QModelIndex WatchModel::watchIndex(const WatchItem *item) const
|
||||
{
|
||||
return watchIndexHelper(item, m_root, QModelIndex());
|
||||
}
|
||||
|
||||
QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle,
|
||||
const WatchItem *parentItem, const QModelIndex &parentIndex) const
|
||||
{
|
||||
if (needle == parentItem)
|
||||
return parentIndex;
|
||||
for (int i = parentItem->children.size(); --i >= 0; ) {
|
||||
const WatchItem *childItem = parentItem->children.at(i);
|
||||
QModelIndex childIndex = index(i, 0, parentIndex);
|
||||
QModelIndex idx = watchIndexHelper(needle, childItem, childIndex);
|
||||
if (idx.isValid())
|
||||
return idx;
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
static WatchItem *findRecursion(WatchItem *root, const WatchPredicate &p)
|
||||
{
|
||||
if (p(*root))
|
||||
return root;
|
||||
for (int i = root->children.size(); --i >= 0; )
|
||||
if (WatchItem *item = findItem(iname, root->children.at(i)))
|
||||
if (WatchItem *item = findRecursion(root->children.at(i), p))
|
||||
return item;
|
||||
return 0;
|
||||
}
|
||||
|
||||
WatchItem *WatchModel::findItemByExpression(const QString &exp, WatchItem *root) const
|
||||
{
|
||||
return findRecursion(root, WatchPredicate(WatchPredicate::ExpressionMatch, exp));
|
||||
}
|
||||
|
||||
WatchItem *WatchModel::findItemByIName(const QString &iname, WatchItem *root) const
|
||||
{
|
||||
return findRecursion(root, WatchPredicate(WatchPredicate::INameMatch, iname));
|
||||
}
|
||||
|
||||
static void debugItemRecursion(QDebug d, WatchItem *item, QString indent = QString())
|
||||
{
|
||||
qDebug() << (indent + item->toString());
|
||||
if (!item->children.isEmpty()) {
|
||||
indent += QLatin1String(" ");
|
||||
foreach(WatchItem *c, item->children)
|
||||
debugItemRecursion(d, c, indent);
|
||||
}
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug d, const WatchModel &wm)
|
||||
{
|
||||
if (WatchItem *root = wm.dummyRoot())
|
||||
debugItemRecursion(d.nospace(), root);
|
||||
return d;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -836,34 +850,25 @@ WatchItem *WatchModel::findItem(const QString &iname, WatchItem *root) const
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
WatchHandler::WatchHandler()
|
||||
WatchHandler::WatchHandler() :
|
||||
m_expandPointers(true),
|
||||
m_inChange(false)
|
||||
{
|
||||
m_expandPointers = true;
|
||||
m_inChange = false;
|
||||
|
||||
m_locals = new WatchModel(this, LocalsWatch);
|
||||
m_watchers = new WatchModel(this, WatchersWatch);
|
||||
m_tooltips = new WatchModel(this, TooltipsWatch);
|
||||
|
||||
connect(theDebuggerAction(WatchExpression),
|
||||
SIGNAL(triggered()), this, SLOT(watchExpression()));
|
||||
connect(theDebuggerAction(RemoveWatchExpression),
|
||||
SIGNAL(triggered()), this, SLOT(removeWatchExpression()));
|
||||
}
|
||||
|
||||
void WatchHandler::endCycle()
|
||||
{
|
||||
m_locals->removeOutdated();
|
||||
m_watchers->removeOutdated();
|
||||
m_tooltips->removeOutdated();
|
||||
qFill(m_models, m_models + WatchModelCount, static_cast<WatchModel*>(0));
|
||||
}
|
||||
|
||||
void WatchHandler::cleanup()
|
||||
{
|
||||
m_expandedINames.clear();
|
||||
m_displayedINames.clear();
|
||||
m_locals->reinitialize();
|
||||
m_tooltips->reinitialize();
|
||||
if (m_models[LocalsWatch])
|
||||
m_models[LocalsWatch]->reinitialize();
|
||||
if (m_models[TooltipsWatch])
|
||||
m_models[TooltipsWatch]->reinitialize();
|
||||
#if 0
|
||||
for (EditWindows::ConstIterator it = m_editWindows.begin();
|
||||
it != m_editWindows.end(); ++it) {
|
||||
@@ -874,26 +879,12 @@ void WatchHandler::cleanup()
|
||||
#endif
|
||||
}
|
||||
|
||||
void WatchHandler::insertData(const WatchData &data)
|
||||
{
|
||||
MODEL_DEBUG("INSERTDATA: " << data.toString());
|
||||
QTC_ASSERT(data.isValid(), return);
|
||||
if (data.isSomethingNeeded()) {
|
||||
emit watchDataUpdateNeeded(data);
|
||||
} else {
|
||||
WatchModel *model = modelForIName(data.iname);
|
||||
QTC_ASSERT(model, return);
|
||||
model->insertData(data);
|
||||
}
|
||||
}
|
||||
|
||||
void WatchHandler::removeData(const QString &iname)
|
||||
{
|
||||
WatchModel *model = modelForIName(iname);
|
||||
if (!model)
|
||||
return;
|
||||
WatchItem *item = model->findItem(iname, model->m_root);
|
||||
if (item)
|
||||
if (WatchItem *item = model->findItemByIName(iname, model->root()))
|
||||
model->removeItem(item);
|
||||
}
|
||||
|
||||
@@ -918,7 +909,7 @@ void WatchHandler::watchExpression(const QString &exp)
|
||||
if (exp.isEmpty() || exp == watcherEditPlaceHolder())
|
||||
data.setAllUnneeded();
|
||||
data.iname = watcherName(exp);
|
||||
insertData(data);
|
||||
emit watcherInserted(data);
|
||||
saveWatchers();
|
||||
//emit watchModelUpdateRequested();
|
||||
}
|
||||
@@ -1008,33 +999,11 @@ void WatchHandler::removeWatchExpression(const QString &exp)
|
||||
{
|
||||
MODEL_DEBUG("REMOVE WATCH: " << exp);
|
||||
m_watcherNames.remove(exp);
|
||||
foreach (WatchItem *item, m_watchers->dummyRoot()->children) {
|
||||
if (item->exp == exp) {
|
||||
m_watchers->removeItem(item);
|
||||
if (WatchModel *model = m_models[WatchersWatch])
|
||||
if (WatchItem *item = model->findItemByExpression(exp, model->root())) {
|
||||
model->removeItem(item);
|
||||
saveWatchers();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WatchHandler::beginCycle()
|
||||
{
|
||||
++generationCounter;
|
||||
//m_locals->beginCycle();
|
||||
}
|
||||
|
||||
void WatchHandler::updateWatchers()
|
||||
{
|
||||
//qDebug() << "UPDATE WATCHERS";
|
||||
// copy over all watchers and mark all watchers as incomplete
|
||||
foreach (const QString &exp, m_watcherNames.keys()) {
|
||||
WatchData data;
|
||||
data.iname = watcherName(exp);
|
||||
data.setAllNeeded();
|
||||
data.name = exp;
|
||||
data.exp = exp;
|
||||
insertData(data);
|
||||
}
|
||||
}
|
||||
|
||||
void WatchHandler::loadWatchers()
|
||||
@@ -1076,6 +1045,11 @@ void WatchHandler::loadTypeFormats()
|
||||
}
|
||||
}
|
||||
|
||||
QStringList WatchHandler::watcherExpressions() const
|
||||
{
|
||||
return m_watcherNames.keys();
|
||||
}
|
||||
|
||||
void WatchHandler::saveTypeFormats()
|
||||
{
|
||||
QMap<QString, QVariant> typeFormats;
|
||||
@@ -1099,44 +1073,61 @@ void WatchHandler::loadSessionData()
|
||||
{
|
||||
loadWatchers();
|
||||
loadTypeFormats();
|
||||
initWatchModel();
|
||||
}
|
||||
|
||||
void WatchHandler::initWatchModel()
|
||||
{
|
||||
foreach (const QString &exp, m_watcherNames.keys()) {
|
||||
WatchData data;
|
||||
data.iname = watcherName(exp);
|
||||
data.setAllUnneeded();
|
||||
data.name = exp;
|
||||
data.exp = exp;
|
||||
insertData(data);
|
||||
emit watcherInserted(data);
|
||||
}
|
||||
}
|
||||
|
||||
WatchModel *WatchHandler::model(WatchType type) const
|
||||
{
|
||||
switch (type) {
|
||||
case LocalsWatch: return m_locals;
|
||||
case WatchersWatch: return m_watchers;
|
||||
case TooltipsWatch: return m_tooltips;
|
||||
}
|
||||
QTC_ASSERT(false, /**/);
|
||||
return 0;
|
||||
return m_models[type];
|
||||
}
|
||||
|
||||
WatchModel *WatchHandler::modelForIName(const QString &iname) const
|
||||
|
||||
void WatchHandler::setModel(WatchType type, WatchModel *model)
|
||||
{
|
||||
if (m_models[type] == model)
|
||||
return;
|
||||
if (type == WatchersWatch && m_models[type])
|
||||
saveWatchers();
|
||||
m_models[type] = model;
|
||||
if (type == WatchersWatch)
|
||||
initWatchModel();
|
||||
}
|
||||
|
||||
WatchType WatchHandler::watchTypeOfIName(const QString &iname)
|
||||
{
|
||||
if (iname.startsWith(QLatin1String("local.")))
|
||||
return m_locals;
|
||||
return LocalsWatch;
|
||||
if (iname.startsWith(QLatin1String("watch.")))
|
||||
return m_watchers;
|
||||
return WatchersWatch;
|
||||
if (iname.startsWith(QLatin1String("tooltip")))
|
||||
return m_tooltips;
|
||||
QTC_ASSERT(false, /**/);
|
||||
return 0;
|
||||
return TooltipsWatch;
|
||||
return WatchModelCount;
|
||||
}
|
||||
|
||||
WatchModel *WatchHandler::modelForIName(const QString &iname) const
|
||||
{
|
||||
const WatchType t = watchTypeOfIName(iname);
|
||||
if (t == WatchModelCount)
|
||||
return 0;
|
||||
return m_models[t];
|
||||
}
|
||||
|
||||
WatchData *WatchHandler::findItem(const QString &iname) const
|
||||
{
|
||||
const WatchModel *model = modelForIName(iname);
|
||||
QTC_ASSERT(model, return 0);
|
||||
return model->findItem(iname, model->m_root);
|
||||
return model->findItemByIName(iname, model->root());
|
||||
}
|
||||
|
||||
QString WatchHandler::watcherEditPlaceHolder()
|
||||
@@ -1149,9 +1140,55 @@ void WatchHandler::setFormat(const QString &type, int format)
|
||||
{
|
||||
m_typeFormats[type] = format;
|
||||
saveTypeFormats();
|
||||
m_locals->emitDataChanged(1);
|
||||
m_watchers->emitDataChanged(1);
|
||||
m_tooltips->emitDataChanged(1);
|
||||
for (int m = 0; m < WatchModelCount; m++)
|
||||
m_models[m]->emitDataChanged(1);
|
||||
}
|
||||
|
||||
void WatchHandler::init(QTreeView *localsView)
|
||||
{
|
||||
m_localsView = localsView;
|
||||
}
|
||||
|
||||
void WatchHandler::saveLocalsViewState(int frame)
|
||||
{
|
||||
WatchModel *model = m_models[LocalsWatch];
|
||||
if (!model || !m_localsView)
|
||||
return;
|
||||
LocalsViewStateMap::iterator it = m_localsViewState.find(frame);
|
||||
if (it == m_localsViewState.end())
|
||||
it = m_localsViewState.insert(frame, LocalsViewState());
|
||||
|
||||
const QModelIndex topIndex = m_localsView->indexAt(QPoint(0, 0));
|
||||
it.value().firstVisibleRow = topIndex.isValid() ? topIndex.row() : 0;
|
||||
it.value().expandedINames = m_expandedINames;
|
||||
}
|
||||
|
||||
void WatchHandler::restoreLocalsViewState(int frame)
|
||||
{
|
||||
WatchModel *model = m_models[LocalsWatch];
|
||||
if (!model || !m_localsView)
|
||||
return;
|
||||
int firstVisibleRow = 0;
|
||||
const LocalsViewStateMap::const_iterator it = m_localsViewState.constFind(frame);
|
||||
if (it != m_localsViewState.constEnd()) {
|
||||
firstVisibleRow = it.value().firstVisibleRow;
|
||||
m_expandedINames = it.value().expandedINames;
|
||||
}
|
||||
// Loop over and expand valid inames, purge out invalid.
|
||||
WatchItem *root = model->root();
|
||||
for (QSet<QString>::iterator it = m_expandedINames.begin(); it != m_expandedINames.end(); ) {
|
||||
if (const WatchItem *wd = model->findItemByIName(*it, root)) {
|
||||
m_localsView->expand(model->watchIndex(wd));
|
||||
++it;
|
||||
} else {
|
||||
it = m_expandedINames.erase(it);
|
||||
}
|
||||
}
|
||||
if (firstVisibleRow) {
|
||||
const QModelIndex index = model->index(0, 0).child(firstVisibleRow, 0);
|
||||
if (index.isValid())
|
||||
m_localsView->scrollTo(index, QAbstractItemView::PositionAtTop);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -32,19 +32,21 @@
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QSet>
|
||||
#include <QtGui/QStandardItem>
|
||||
#include <QtGui/QStandardItemModel>
|
||||
#include <QtGui/QTreeView>
|
||||
#include <QtCore/QAbstractItemModel>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTreeView;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Debugger {
|
||||
namespace Internal {
|
||||
|
||||
class WatchItem;
|
||||
class WatchHandler;
|
||||
enum WatchType { LocalsWatch, WatchersWatch, TooltipsWatch };
|
||||
enum WatchType { LocalsWatch, WatchersWatch, TooltipsWatch, WatchModelCount };
|
||||
|
||||
class WatchData
|
||||
{
|
||||
@@ -160,51 +162,95 @@ enum DumpableFormat
|
||||
PlainFomat,
|
||||
};
|
||||
|
||||
class WatchHandler;
|
||||
|
||||
// Item used by the model.
|
||||
class WatchItem : public WatchData
|
||||
{
|
||||
Q_DISABLE_COPY(WatchItem)
|
||||
public:
|
||||
WatchItem();
|
||||
explicit WatchItem(const WatchData &data);
|
||||
|
||||
~WatchItem();
|
||||
|
||||
void setData(const WatchData &data);
|
||||
void addChild(WatchItem *child);
|
||||
void removeChildren();
|
||||
|
||||
WatchItem *parent;
|
||||
bool fetchTriggered;
|
||||
QList<WatchItem*> children;
|
||||
};
|
||||
|
||||
/* WatchModel: To be used by synchonous debuggers.
|
||||
* Implements all of WatchModel and provides new virtuals for
|
||||
* the debugger to complete items. */
|
||||
|
||||
class WatchModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit WatchModel(WatchHandler *handler, WatchType type, QObject *parent = 0);
|
||||
virtual ~WatchModel();
|
||||
|
||||
private:
|
||||
explicit WatchModel(WatchHandler *handler, WatchType type);
|
||||
virtual QModelIndex index(int, int, const QModelIndex &idx = QModelIndex()) const;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
QModelIndex index(int, int, const QModelIndex &idx) const;
|
||||
QModelIndex parent(const QModelIndex &idx) const;
|
||||
int rowCount(const QModelIndex &idx) const;
|
||||
int columnCount(const QModelIndex &idx) const;
|
||||
bool hasChildren(const QModelIndex &idx) const;
|
||||
Qt::ItemFlags flags(const QModelIndex &idx) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const;
|
||||
bool canFetchMore(const QModelIndex &parent) const;
|
||||
void fetchMore(const QModelIndex &parent);
|
||||
virtual int columnCount(const QModelIndex &idx= QModelIndex()) const;
|
||||
virtual int rowCount(const QModelIndex &idx) const;
|
||||
virtual bool hasChildren(const QModelIndex &idx) const;
|
||||
virtual QVariant data(const QModelIndex &index, int role) const;
|
||||
virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
|
||||
friend class WatchHandler;
|
||||
friend class GdbEngine;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex &idx) const;
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const;
|
||||
|
||||
virtual QModelIndex parent(const QModelIndex &idx) const;
|
||||
|
||||
WatchItem *watchItem(const QModelIndex &) const;
|
||||
QModelIndex watchIndex(const WatchItem *needle) const;
|
||||
QModelIndex watchIndexHelper(const WatchItem *needle,
|
||||
const WatchItem *parentItem, const QModelIndex &parentIndex) const;
|
||||
|
||||
void insertData(const WatchData &data);
|
||||
WatchItem *findItem(const QString &iname, WatchItem *root) const;
|
||||
WatchItem *findItemByIName(const QString &iname, WatchItem *root) const;
|
||||
WatchItem *findItemByExpression(const QString &iname, WatchItem *root) const;
|
||||
void reinitialize();
|
||||
void removeOutdated();
|
||||
void removeOutdatedHelper(WatchItem *item);
|
||||
WatchItem *dummyRoot() const;
|
||||
void removeItem(WatchItem *item);
|
||||
void setActiveData(const QString &data) { m_activeData = data; }
|
||||
|
||||
void emitDataChanged(int column,
|
||||
const QModelIndex &parentIndex = QModelIndex());
|
||||
void removeItem(WatchItem *item);
|
||||
WatchItem *root() const;
|
||||
|
||||
WatchItem *dummyRoot() const;
|
||||
|
||||
void setValueByIName(const QString &iname, const QString &value);
|
||||
void setValueByExpression(const QString &iname, const QString &value);
|
||||
|
||||
void emitDataChanged(int column, const QModelIndex &parentIndex = QModelIndex());
|
||||
|
||||
protected:
|
||||
WatchHandler *handler() const { return m_handler; }
|
||||
QVariant data(const WatchData &data, int column, int role) const;
|
||||
|
||||
static QString parentName(const QString &iname);
|
||||
|
||||
private:
|
||||
WatchHandler *m_handler;
|
||||
WatchType m_type;
|
||||
WatchItem *m_root;
|
||||
QString m_activeData;
|
||||
WatchHandler *m_handler;
|
||||
const WatchType m_type;
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug d, const WatchModel &wm);
|
||||
|
||||
/* A helper predicate for implementing model find routines */
|
||||
class WatchPredicate {
|
||||
public:
|
||||
enum Mode { INameMatch, ExpressionMatch };
|
||||
explicit WatchPredicate(Mode m, const QString &pattern);
|
||||
bool operator()(const WatchData &w) const;
|
||||
|
||||
private:
|
||||
const QString &m_pattern;
|
||||
const Mode m_mode;
|
||||
};
|
||||
|
||||
class WatchHandler : public QObject
|
||||
@@ -213,21 +259,25 @@ class WatchHandler : public QObject
|
||||
|
||||
public:
|
||||
WatchHandler();
|
||||
void init(QTreeView *localsView);
|
||||
|
||||
WatchModel *model(WatchType type) const;
|
||||
static WatchType watchTypeOfIName(const QString &iname);
|
||||
WatchModel *modelForIName(const QString &data) const;
|
||||
|
||||
QStringList watcherExpressions() const;
|
||||
QString watcherName(const QString &exp);
|
||||
|
||||
void setModel(WatchType type, WatchModel *model);
|
||||
|
||||
//public slots:
|
||||
void cleanup();
|
||||
Q_SLOT void watchExpression(); // data in action->data().toString()
|
||||
Q_SLOT void watchExpression(const QString &exp);
|
||||
Q_SLOT void removeWatchExpression();
|
||||
Q_SLOT void removeWatchExpression(const QString &exp);
|
||||
void beginCycle(); // called at begin of updateLocals() cycle
|
||||
void updateWatchers(); // called after locals are fetched
|
||||
void endCycle(); // called after all results have been received
|
||||
void showEditValue(const WatchData &data);
|
||||
|
||||
void insertData(const WatchData &data);
|
||||
void removeData(const QString &iname);
|
||||
WatchData *findItem(const QString &iname) const;
|
||||
|
||||
@@ -241,18 +291,32 @@ public:
|
||||
QSet<QString> expandedINames() const
|
||||
{ return m_expandedINames; }
|
||||
|
||||
// For debuggers that clean out the locals models between frames:
|
||||
// save/restore the expansion state.
|
||||
Q_SLOT void restoreLocalsViewState(int frame = 0);
|
||||
Q_SLOT void saveLocalsViewState(int frame = 0);
|
||||
|
||||
static QString watcherEditPlaceHolder();
|
||||
|
||||
signals:
|
||||
void watchDataUpdateNeeded(const WatchData &data);
|
||||
void watcherInserted(const WatchData &data);
|
||||
void sessionValueRequested(const QString &name, QVariant *value);
|
||||
void setSessionValueRequested(const QString &name, const QVariant &value);
|
||||
|
||||
private:
|
||||
friend class WatchModel;
|
||||
friend class WatchModel; // needs formats, expanded inames
|
||||
|
||||
// Per stack-frame locals state
|
||||
struct LocalsViewState {
|
||||
LocalsViewState() : firstVisibleRow(0) {}
|
||||
int firstVisibleRow;
|
||||
QSet<QString> expandedINames;
|
||||
};
|
||||
typedef QMap<int, LocalsViewState> LocalsViewStateMap;
|
||||
|
||||
void loadWatchers();
|
||||
void saveWatchers();
|
||||
void initWatchModel();
|
||||
|
||||
void loadTypeFormats();
|
||||
void saveTypeFormats();
|
||||
@@ -265,17 +329,16 @@ private:
|
||||
EditWindows m_editWindows;
|
||||
|
||||
QHash<QString, int> m_watcherNames;
|
||||
QString watcherName(const QString &exp);
|
||||
QHash<QString, int> m_typeFormats;
|
||||
QHash<QString, int> m_individualFormats;
|
||||
|
||||
void setDisplayedIName(const QString &iname, bool on);
|
||||
QSet<QString> m_expandedINames; // those expanded in the treeview
|
||||
QSet<QString> m_displayedINames; // those with "external" viewers
|
||||
LocalsViewStateMap m_localsViewState;
|
||||
|
||||
WatchModel *m_locals;
|
||||
WatchModel *m_watchers;
|
||||
WatchModel *m_tooltips;
|
||||
WatchModel *m_models[WatchModelCount];
|
||||
QPointer<QTreeView> m_localsView;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -554,19 +554,22 @@ QList<WatchData> QtDumperResult::toWatchData(int source) const
|
||||
}
|
||||
wchild.setType(dchild.type.isEmpty() ? childType : dchild.type);
|
||||
wchild.setAddress(dchild.address);
|
||||
// Child overrides.
|
||||
const int effectiveChildChildCount = dchild.childCount == -1 ? childChildCount : dchild.childCount;
|
||||
switch (effectiveChildChildCount) {
|
||||
// Child overrides. Note that WatchData::setType sets
|
||||
// childcount = 0 for some known types.
|
||||
if (wchild.isHasChildrenNeeded()) {
|
||||
const int effectiveChildChildCount = dchild.childCount == -1 ? childChildCount : dchild.childCount;
|
||||
switch (effectiveChildChildCount) {
|
||||
case -1:
|
||||
wchild.setChildrenNeeded();
|
||||
wchild.setHasChildrenNeeded();
|
||||
break;
|
||||
wchild.setChildrenNeeded();
|
||||
wchild.setHasChildrenNeeded();
|
||||
break;
|
||||
case 0:
|
||||
wchild.setHasChildren(false);
|
||||
break;
|
||||
wchild.setHasChildren(false);
|
||||
break;
|
||||
default:
|
||||
wchild.setHasChildren(true);
|
||||
break;
|
||||
wchild.setHasChildren(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,9 +327,11 @@ void WatchWindow::editItem(const QModelIndex &idx)
|
||||
Q_UNUSED(idx); // FIXME
|
||||
}
|
||||
|
||||
void WatchWindow::setModel(QAbstractItemModel *model)
|
||||
void WatchWindow::setModel(QAbstractItemModel *newModel)
|
||||
{
|
||||
QTreeView::setModel(model);
|
||||
if (model() == newModel)
|
||||
return;
|
||||
QTreeView::setModel(newModel);
|
||||
|
||||
setRootIsDecorated(true);
|
||||
header()->setDefaultAlignment(Qt::AlignLeft);
|
||||
@@ -337,7 +339,7 @@ void WatchWindow::setModel(QAbstractItemModel *model)
|
||||
if (m_type != LocalsType)
|
||||
header()->hide();
|
||||
|
||||
connect(model, SIGNAL(layoutChanged()), this, SLOT(resetHelper()));
|
||||
connect(newModel, SIGNAL(layoutChanged()), this, SLOT(resetHelper()));
|
||||
}
|
||||
|
||||
void WatchWindow::resetHelper()
|
||||
|
||||
Reference in New Issue
Block a user