Files
qt-creator/src/plugins/debugger/watchhandler.cpp

1073 lines
31 KiB
C++
Raw Normal View History

/**************************************************************************
2008-12-02 12:01:29 +01:00
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
2008-12-02 12:01:29 +01:00
**
** Contact: Nokia Corporation (qt-info@nokia.com)
2008-12-02 12:01:29 +01:00
**
** 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.
2008-12-02 12:01:29 +01:00
**
**************************************************************************/
2008-12-02 15:08:31 +01:00
2008-12-02 12:01:29 +01:00
#include "watchhandler.h"
#include "watchutils.h"
#include "debuggeractions.h"
2008-12-02 12:01:29 +01:00
#if USE_MODEL_TEST
#include "modeltest.h"
#endif
2008-12-09 16:18:28 +01:00
#include <utils/qtcassert.h>
2008-12-02 12:01:29 +01:00
#include <QtCore/QDebug>
#include <QtCore/QEvent>
#include <QtCore/QtAlgorithms>
#include <QtCore/QTextStream>
#include <QtCore/QTimer>
2008-12-02 12:01:29 +01:00
#include <QtGui/QAction>
2008-12-02 12:01:29 +01:00
#include <QtGui/QApplication>
#include <QtGui/QLabel>
#include <QtGui/QToolTip>
#include <QtGui/QTextEdit>
#include <ctype.h>
2008-12-02 12:01:29 +01:00
// creates debug output regarding pending watch data results
//#define DEBUG_PENDING 1
//// creates debug output for accesses to the itemmodel
#define DEBUG_MODEL 1
2008-12-02 12:01:29 +01:00
#if DEBUG_MODEL
# define MODEL_DEBUG(s) qDebug() << s
#else
# define MODEL_DEBUG(s)
#endif
#define MODEL_DEBUGX(s) qDebug() << s
namespace Debugger {
namespace Internal {
2008-12-02 12:01:29 +01:00
static const QString strNotInScope =
QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
2008-12-02 12:01:29 +01:00
static int watcherCounter = 0;
static int generationCounter = 0;
////////////////////////////////////////////////////////////////////
//
// WatchItem
//
////////////////////////////////////////////////////////////////////
class WatchItem : public WatchData
{
public:
WatchItem() { parent = 0; fetchedTriggered = 0; }
WatchItem(const WatchData &data) : WatchData(data)
{ parent = 0; fetchedTriggered = 0; }
void setData(const WatchData &data)
{ static_cast<WatchData &>(*this) = data; }
WatchItem *parent;
bool fetchedTriggered; // children fetch has been triggered
QList<WatchItem *> children; // fetched children
};
2008-12-02 12:01:29 +01:00
////////////////////////////////////////////////////////////////////
//
// WatchData
//
////////////////////////////////////////////////////////////////////
WatchData::WatchData() :
hasChildren(false),
generation(-1),
valuedisabled(false),
source(0),
state(InitialState),
changed(false)
2008-12-02 12:01:29 +01:00
{
}
void WatchData::setError(const QString &msg)
{
setAllUnneeded();
value = msg;
setHasChildren(false);
2008-12-02 12:01:29 +01:00
valuedisabled = true;
}
void WatchData::setValue(const QString &value0)
2008-12-02 12:01:29 +01:00
{
value = value0;
2008-12-02 12:01:29 +01:00
if (value == "{...}") {
value.clear();
hasChildren = true; // at least one...
2008-12-02 12:01:29 +01:00
}
// avoid duplicated information
if (value.startsWith("(") && value.contains(") 0x"))
value = value.mid(value.lastIndexOf(") 0x") + 2);
// doubles are sometimes displayed as "@0x6141378: 1.2".
// I don't want that.
if (/*isIntOrFloatType(type) && */ value.startsWith("@0x")
&& value.contains(':')) {
value = value.mid(value.indexOf(':') + 2);
setHasChildren(false);
2008-12-02 12:01:29 +01:00
}
// "numchild" is sometimes lying
//MODEL_DEBUG("\n\n\nPOINTER: " << type << value);
if (isPointerType(type))
setHasChildren(value != "0x0" && value != "<null>");
2008-12-02 12:01:29 +01:00
// pointer type information is available in the 'type'
// column. No need to duplicate it here.
if (value.startsWith("(" + type + ") 0x"))
value = value.section(" ", -1, -1);
setValueUnneeded();
}
void WatchData::setValueToolTip(const QString &tooltip)
{
valuetooltip = tooltip;
}
void WatchData::setType(const QString &str)
{
type = str.trimmed();
bool changed = true;
while (changed) {
if (type.endsWith(QLatin1String("const")))
2008-12-02 12:01:29 +01:00
type.chop(5);
else if (type.endsWith(QLatin1Char(' ')))
2008-12-02 12:01:29 +01:00
type.chop(1);
else if (type.endsWith(QLatin1Char('&')))
2008-12-02 12:01:29 +01:00
type.chop(1);
else if (type.startsWith(QLatin1String("const ")))
2008-12-02 12:01:29 +01:00
type = type.mid(6);
else if (type.startsWith(QLatin1String("volatile ")))
2008-12-02 12:01:29 +01:00
type = type.mid(9);
else if (type.startsWith(QLatin1String("class ")))
2008-12-02 12:01:29 +01:00
type = type.mid(6);
else if (type.startsWith(QLatin1String("struct ")))
2008-12-02 12:01:29 +01:00
type = type.mid(6);
else if (type.startsWith(QLatin1Char(' ')))
2008-12-02 12:01:29 +01:00
type = type.mid(1);
else
changed = false;
}
setTypeUnneeded();
if (isIntOrFloatType(type))
setHasChildren(false);
2008-12-02 12:01:29 +01:00
}
void WatchData::setAddress(const QString & str)
{
addr = str;
}
WatchData WatchData::pointerChildPlaceHolder() const
{
WatchData data1;
data1.iname = iname + QLatin1String(".*");
data1.name = QLatin1Char('*') + name;
data1
.exp = QLatin1String("(*(") + exp + QLatin1String("))");
data1.type = stripPointerType(type);
data1.setValueNeeded();
return data1;
}
2008-12-02 12:01:29 +01:00
QString WatchData::toString() const
{
const char *doubleQuoteComma = "\",";
QString res;
QTextStream str(&res);
2008-12-02 12:01:29 +01:00
if (!iname.isEmpty())
str << "iname=\"" << iname << doubleQuoteComma;
2008-12-02 12:01:29 +01:00
if (!exp.isEmpty())
str << "exp=\"" << exp << doubleQuoteComma;
2008-12-02 12:01:29 +01:00
if (!variable.isEmpty())
str << "variable=\"" << variable << doubleQuoteComma;
2008-12-02 12:01:29 +01:00
if (isValueNeeded())
str << "value=<needed>,";
2008-12-02 12:01:29 +01:00
if (isValueKnown() && !value.isEmpty())
str << "value=\"" << value << doubleQuoteComma;
2008-12-02 12:01:29 +01:00
if (!editvalue.isEmpty())
str << "editvalue=\"" << editvalue << doubleQuoteComma;
2008-12-02 12:01:29 +01:00
if (isTypeNeeded())
str << "type=<needed>,";
2008-12-02 12:01:29 +01:00
if (isTypeKnown() && !type.isEmpty())
str << "type=\"" << type << doubleQuoteComma;
2008-12-02 12:01:29 +01:00
if (isHasChildrenNeeded())
str << "hasChildren=<needed>,";
if (isHasChildrenKnown())
str << "hasChildren=\"" << (hasChildren ? "true" : "false") << doubleQuoteComma;
2008-12-02 12:01:29 +01:00
if (isChildrenNeeded())
str << "children=<needed>,";
str.flush();
if (res.endsWith(QLatin1Char(',')))
res.truncate(res.size() - 1);
return res + QLatin1Char('}');
2008-12-02 12:01:29 +01:00
}
2009-05-13 14:39:55 +02:00
// Format a tooltip fow with aligned colon
template <class Streamable>
void formatToolTipRow(QTextStream &str, const QString &category, const Streamable &value)
2009-05-13 14:39:55 +02:00
{
str << "<tr><td>" << category << "</td><td> : </td><td>" << value << "</td></tr>";
}
QString WatchData::toToolTip() const
{
if (!valuetooltip.isEmpty())
return QString::number(valuetooltip.size());
2009-05-13 14:39:55 +02:00
QString res;
QTextStream str(&res);
str << "<html><body><table>";
formatToolTipRow(str, WatchHandler::tr("Expression"), Qt::escape(exp));
formatToolTipRow(str, WatchHandler::tr("Type"), Qt::escape(type));
QString val = value;
if (value.size() > 1000) {
val.truncate(1000);
val += WatchHandler::tr(" ... <cut off>");
}
formatToolTipRow(str, WatchHandler::tr("Value"), Qt::escape(val));
formatToolTipRow(str, WatchHandler::tr("Object Address"), Qt::escape(addr));
formatToolTipRow(str, WatchHandler::tr("Stored Address"), Qt::escape(saddr));
formatToolTipRow(str, WatchHandler::tr("iname"), Qt::escape(iname));
str << "</table></body></html>";
return res;
}
///////////////////////////////////////////////////////////////////////
//
// WatchModel
//
///////////////////////////////////////////////////////////////////////
WatchModel::WatchModel(WatchHandler *handler, WatchType type)
: QAbstractItemModel(handler), m_handler(handler), m_type(type)
{
m_root = new WatchItem;
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->fetchedTriggered = true;
m_root->children.append(item);
}
WatchItem *WatchModel::dummyRoot() const
{
QTC_ASSERT(!m_root->children.isEmpty(), return 0);
return m_root->children.front();
}
void WatchModel::reinitialize()
{
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);
}
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();
}
2008-12-02 12:01:29 +01:00
static QString parentName(const QString &iname)
{
int pos = iname.lastIndexOf(QLatin1Char('.'));
2008-12-02 12:01:29 +01:00
if (pos == -1)
return QString();
return iname.left(pos);
}
static QString chopConst(QString type)
{
while (1) {
if (type.startsWith("const"))
type = type.mid(5);
else if (type.startsWith(' '))
type = type.mid(1);
else if (type.endsWith("const"))
type.chop(5);
else if (type.endsWith(' '))
type.chop(1);
else
break;
}
return type;
}
QString niceType(QString type)
2008-12-02 12:01:29 +01:00
{
type.replace('*', '@');
for (int i = 0; i < 10; ++i) {
int start = type.indexOf("std::allocator<");
if (start == -1)
break;
// search for matching '>'
int pos;
int level = 0;
for (pos = start + 12; pos < type.size(); ++pos) {
int c = type.at(pos).unicode();
if (c == '<') {
++level;
} else if (c == '>') {
--level;
if (level == 0)
break;
}
}
QString alloc = type.mid(start, pos + 1 - start).trimmed();
QString inner = alloc.mid(15, alloc.size() - 16).trimmed();
if (inner == QLatin1String("char"))
// std::string
type.replace(QLatin1String("basic_string<char, std::char_traits<char>, "
"std::allocator<char> >"), QLatin1String("string"));
else if (inner == QLatin1String("wchar_t"))
// std::wstring
type.replace(QLatin1String("basic_string<wchar_t, std::char_traits<wchar_t>, "
"std::allocator<wchar_t> >"), QLatin1String("wstring"));
// std::vector, std::deque, std::list
QRegExp re1(QString("(vector|list|deque)<%1, %2\\s*>").arg(inner, alloc));
if (re1.indexIn(type) != -1)
type.replace(re1.cap(0), QString("%1<%2>").arg(re1.cap(1), inner));
// std::stack
QRegExp re6(QString("stack<%1, std::deque<%2> >").arg(inner, inner));
re6.setMinimal(true);
if (re6.indexIn(type) != -1)
type.replace(re6.cap(0), QString("stack<%1>").arg(inner));
// std::set
QRegExp re4(QString("set<%1, std::less<%2>, %3\\s*>").arg(inner, inner, alloc));
re4.setMinimal(true);
if (re4.indexIn(type) != -1)
type.replace(re4.cap(0), QString("set<%1>").arg(inner));
2008-12-10 14:37:15 +01:00
2008-12-02 12:01:29 +01:00
// std::map
if (inner.startsWith("std::pair<")) {
// search for outermost ','
int pos;
int level = 0;
for (pos = 10; pos < inner.size(); ++pos) {
int c = inner.at(pos).unicode();
if (c == '<')
++level;
else if (c == '>')
--level;
else if (c == ',' && level == 0)
break;
}
QString ckey = inner.mid(10, pos - 10);
QString key = chopConst(ckey);
QString value = inner.mid(pos + 2, inner.size() - 3 - pos);
QRegExp re5(QString("map<%1, %2, std::less<%3>, %4\\s*>")
.arg(key, value, key, alloc));
re5.setMinimal(true);
if (re5.indexIn(type) != -1)
type.replace(re5.cap(0), QString("map<%1, %2>").arg(key, value));
else {
QRegExp re7(QString("map<const %1, %2, std::less<const %3>, %4\\s*>")
.arg(key, value, key, alloc));
re7.setMinimal(true);
if (re7.indexIn(type) != -1)
type.replace(re7.cap(0), QString("map<const %1, %2>").arg(key, value));
}
}
2008-12-02 12:01:29 +01:00
}
type.replace('@', '*');
type.replace(QLatin1String(" >"), QString(QLatin1Char('>')));
2008-12-02 12:01:29 +01:00
return type;
}
bool WatchModel::canFetchMore(const QModelIndex &index) const
2008-12-02 12:01:29 +01:00
{
return index.isValid() && !watchItem(index)->fetchedTriggered;
}
void WatchModel::fetchMore(const QModelIndex &index)
{
QTC_ASSERT(index.isValid(), return);
QTC_ASSERT(!watchItem(index)->fetchedTriggered, return);
if (WatchItem *item = watchItem(index)) {
item->fetchedTriggered = 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);
2008-12-02 12:01:29 +01:00
return QModelIndex();
}
int WatchModel::rowCount(const QModelIndex &idx) const
{
if (!idx.isValid())
return 1;
if (idx.column() > 0)
return 0;
return watchItem(idx)->children.size();
}
int WatchModel::columnCount(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;
}
return QModelIndex();
}
QVariant WatchModel::data(const QModelIndex &idx, int role) const
{
const WatchItem &data = *watchItem(idx);
2008-12-02 12:01:29 +01:00
switch (role) {
case Qt::DisplayRole: {
switch (idx.column()) {
case 0: return data.name;
case 1: return data.value;
case 2: return niceType(data.type);
default: break;
}
break;
}
2009-05-13 14:39:55 +02:00
case Qt::ToolTipRole:
return data.toToolTip();
2008-12-02 12:01:29 +01:00
case Qt::ForegroundRole: {
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()) {
case 0: return black;
case 1: return data.valuedisabled ? gray : data.changed ? red : black;
case 2: return black;
}
break;
}
case ExpressionRole:
return data.exp;
2008-12-02 12:01:29 +01:00
case INameRole:
return data.iname;
case ExpandedRole:
return m_handler->m_expandedINames.contains(data.iname);
//FIXME return node < 4 || m_expandedINames.contains(data.iname);
2008-12-02 12:01:29 +01:00
default:
break;
}
return QVariant();
}
bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role == ExpandedRole) {
QString iname = data(index, INameRole).toString();
if (value.toBool())
m_handler->m_expandedINames.insert(iname);
else
m_handler->m_expandedINames.remove(iname);
}
emit dataChanged(index, index);
return true;
}
Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
2008-12-02 12:01:29 +01:00
{
using namespace Qt;
if (!idx.isValid())
return ItemFlags();
// enabled, editable, selectable, checkable, and can be used both as the
// source of a drag and drop operation and as a drop target.
static const ItemFlags notEditable =
2008-12-02 12:01:29 +01:00
ItemIsSelectable
| ItemIsDragEnabled
| ItemIsDropEnabled
// | ItemIsUserCheckable
// | ItemIsTristate
| ItemIsEnabled;
static const ItemFlags editable = notEditable | ItemIsEditable;
2008-12-02 12:01:29 +01:00
const WatchData &data = *watchItem(idx);
if (data.isWatcher() && idx.column() == 0)
return editable; // watcher names are editable
if (data.isWatcher() && idx.column() == 2)
return editable; // watcher types are
if (idx.column() == 1)
return editable; // locals and watcher values are editable
return notEditable;
2008-12-02 12:01:29 +01:00
}
QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
2008-12-02 12:01:29 +01:00
{
if (orientation == Qt::Vertical)
return QVariant();
if (role == Qt::DisplayRole) {
switch (section) {
2009-05-29 10:35:04 +02:00
case 0: return QString(tr("Name") + QLatin1String(" "));
case 1: return QString(tr("Value") + QLatin1String(" "));
case 2: return QString(tr("Type") + QLatin1String(" "));
2008-12-02 12:01:29 +01:00
}
}
return QVariant();
}
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 WatchModel::insertData(const WatchData &data)
2008-12-02 12:01:29 +01:00
{
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;
}
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();
}
2008-12-02 12:01:29 +01:00
}
WatchItem *WatchModel::findItem(const QString &iname, WatchItem *root) const
2008-12-02 12:01:29 +01:00
{
if (root->iname == iname)
return root;
for (int i = root->children.size(); --i >= 0; )
if (WatchItem *item = findItem(iname, root->children.at(i)))
return item;
2008-12-02 12:01:29 +01:00
return 0;
}
///////////////////////////////////////////////////////////////////////
//
// WatchHandler
//
///////////////////////////////////////////////////////////////////////
WatchHandler::WatchHandler()
2008-12-02 12:01:29 +01:00
{
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()));
2008-12-02 12:01:29 +01:00
}
void WatchHandler::endCycle()
2008-12-02 12:01:29 +01:00
{
m_locals->removeOutdated();
m_watchers->removeOutdated();
m_tooltips->removeOutdated();
/*
2008-12-02 12:01:29 +01:00
if (m_inChange) {
MODEL_DEBUG("RECREATE MODEL IGNORED, CURRENT SET:\n" << toString());
return;
}
#ifdef DEBUG_PENDING
MODEL_DEBUG("RECREATE MODEL, CURRENT SET:\n" << toString());
#endif
QHash<QString, int> oldTopINames;
2008-12-02 12:01:29 +01:00
QHash<QString, QString> oldValues;
for (int i = 0, n = m_oldSet.size(); i != n; ++i) {
WatchData &data = m_oldSet[i];
oldValues[data.iname] = data.value;
if (data.level == 2)
++oldTopINames[data.iname];
2008-12-02 12:01:29 +01:00
}
#ifdef DEBUG_PENDING
MODEL_DEBUG("OLD VALUES: " << oldValues);
#endif
for (int i = m_completeSet.size(); --i >= 0; ) {
WatchData &data = m_completeSet[i];
data.level = data.iname.isEmpty() ? 0 : data.iname.count('.') + 1;
data.childIndex.clear();
}
qSort(m_completeSet.begin(), m_completeSet.end(), &iNameSorter);
// Possibly append dummy items to prevent empty views
bool ok = true;
2008-12-02 12:01:29 +01:00
if (ok) {
for (int i = 1; i <= 3; ++i) {
WatchData &data = m_displaySet[i];
if (data.childIndex.size() == 0) {
WatchData dummy;
dummy.state = 0;
dummy.row = 0;
if (i == 1) {
dummy.iname = QLatin1String("local.dummy");
dummy.name = tr("<No Locals>");
2008-12-02 12:01:29 +01:00
} else if (i == 2) {
dummy.iname = QLatin1String("tooltip.dummy");
dummy.name = tr("<No Tooltip>");
2008-12-02 12:01:29 +01:00
} else {
dummy.iname = QLatin1String("watch.dummy");
dummy.name = tr("<No Watchers>");
2008-12-02 12:01:29 +01:00
}
dummy.level = 2;
dummy.parentIndex = i;
dummy.childCount = 0;
data.childIndex.append(m_displaySet.size());
m_displaySet.append(dummy);
}
}
}
*/
2008-12-02 12:01:29 +01:00
}
void WatchHandler::cleanup()
{
m_expandedINames.clear();
m_displayedINames.clear();
m_locals->reinitialize();
m_tooltips->reinitialize();
2008-12-02 12:01:29 +01:00
#if 0
for (EditWindows::ConstIterator it = m_editWindows.begin();
it != m_editWindows.end(); ++it) {
if (!it.value().isNull())
delete it.value();
}
m_editWindows.clear();
#endif
}
void WatchHandler::insertData(const WatchData &data)
{
//MODEL_DEBUG("INSERTDATA: " << data.toString());
2008-12-09 12:08:56 +01:00
QTC_ASSERT(data.isValid(), return);
if (data.isSomethingNeeded()) {
emit watchDataUpdateNeeded(data);
} else {
WatchModel *model = modelForIName(data.iname);
QTC_ASSERT(model, return);
model->insertData(data);
}
2008-12-02 12:01:29 +01:00
}
void WatchHandler::watchExpression()
{
if (QAction *action = qobject_cast<QAction *>(sender()))
watchExpression(action->data().toString());
}
QString WatchHandler::watcherName(const QString &exp)
{
return QLatin1String("watch.") + QString::number(m_watcherNames[exp]);
}
2008-12-02 12:01:29 +01:00
void WatchHandler::watchExpression(const QString &exp)
{
// FIXME: 'exp' can contain illegal characters
m_watcherNames[exp] = watcherCounter++;
2008-12-02 12:01:29 +01:00
WatchData data;
data.exp = exp;
data.name = exp;
if (exp == watcherEditPlaceHolder())
data.setAllUnneeded();
data.iname = watcherName(exp);
2008-12-02 12:01:29 +01:00
insertData(data);
saveWatchers();
//emit watchModelUpdateRequested();
2008-12-02 12:01:29 +01:00
}
void WatchHandler::setDisplayedIName(const QString &iname, bool on)
{
Q_UNUSED(iname);
Q_UNUSED(on);
/*
2008-12-02 12:01:29 +01:00
WatchData *d = findData(iname);
if (!on || !d) {
delete m_editWindows.take(iname);
m_displayedINames.remove(iname);
return;
}
if (d->exp.isEmpty()) {
//emit statusMessageRequested(tr("Sorry. Cannot visualize objects without known address."), 5000);
return;
}
d->setValueNeeded();
m_displayedINames.insert(iname);
insertData(*d);
*/
2008-12-02 12:01:29 +01:00
}
void WatchHandler::showEditValue(const WatchData &data)
{
// editvalue is always base64 encoded
QByteArray ba = QByteArray::fromBase64(data.editvalue);
//QByteArray ba = data.editvalue;
QWidget *w = m_editWindows.value(data.iname);
qDebug() << "SHOW_EDIT_VALUE " << data.toString() << data.type
<< data.iname << w;
if (data.type == QLatin1String("QImage")) {
2008-12-02 12:01:29 +01:00
if (!w) {
w = new QLabel;
m_editWindows[data.iname] = w;
}
QDataStream ds(&ba, QIODevice::ReadOnly);
QVariant v;
ds >> v;
QString type = QString::fromAscii(v.typeName());
QImage im = v.value<QImage>();
if (QLabel *l = qobject_cast<QLabel *>(w))
l->setPixmap(QPixmap::fromImage(im));
} else if (data.type == QLatin1String("QPixmap")) {
2008-12-02 12:01:29 +01:00
if (!w) {
w = new QLabel;
m_editWindows[data.iname] = w;
}
QDataStream ds(&ba, QIODevice::ReadOnly);
QVariant v;
ds >> v;
QString type = QString::fromAscii(v.typeName());
QPixmap im = v.value<QPixmap>();
if (QLabel *l = qobject_cast<QLabel *>(w))
l->setPixmap(im);
} else if (data.type == QLatin1String("QString")) {
2008-12-02 12:01:29 +01:00
if (!w) {
w = new QTextEdit;
m_editWindows[data.iname] = w;
}
#if 0
QDataStream ds(&ba, QIODevice::ReadOnly);
QVariant v;
ds >> v;
QString type = QString::fromAscii(v.typeName());
QString str = v.value<QString>();
#else
MODEL_DEBUG("DATA: " << ba);
QString str = QString::fromUtf16((ushort *)ba.constData(), ba.size()/2);
#endif
if (QTextEdit *t = qobject_cast<QTextEdit *>(w))
t->setText(str);
}
if (w)
w->show();
}
void WatchHandler::removeWatchExpression()
{
if (QAction *action = qobject_cast<QAction *>(sender()))
removeWatchExpression(action->data().toString());
}
void WatchHandler::removeWatchExpression(const QString &exp)
2008-12-02 12:01:29 +01:00
{
MODEL_DEBUG("REMOVE WATCH: " << exp);
m_watcherNames.remove(exp);
foreach (WatchItem *item, m_watchers->dummyRoot()->children) {
if (item->exp == exp) {
m_watchers->removeItem(item);
saveWatchers();
break;
}
}
2008-12-02 12:01:29 +01:00
}
void WatchHandler::beginCycle()
2008-12-02 12:01:29 +01:00
{
++generationCounter;
//m_locals->beginCycle();
}
2008-12-02 12:01:29 +01:00
void WatchHandler::updateWatchers()
{
//qDebug() << "UPDATE WATCHERS";
2008-12-02 12:01:29 +01:00
// 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);
2008-12-02 12:01:29 +01:00
}
}
void WatchHandler::loadWatchers()
{
QVariant value;
sessionValueRequested("Watchers", &value);
foreach (const QString &exp, value.toStringList())
m_watcherNames[exp] = watcherCounter++;
//qDebug() << "LOAD WATCHERS: " << m_watchers;
//reinitializeWatchersHelper();
}
void WatchHandler::saveWatchers()
{
//qDebug() << "SAVE WATCHERS: " << m_watchers.keys();
setSessionValueRequested("Watchers", QVariant(m_watcherNames.keys()));
}
void WatchHandler::saveSessionData()
{
saveWatchers();
}
void WatchHandler::loadSessionData()
{
loadWatchers();
foreach (const QString &exp, m_watcherNames.keys()) {
WatchData data;
data.iname = watcherName(exp);
data.setAllUnneeded();
data.name = exp;
data.exp = exp;
insertData(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;
}
WatchModel *WatchHandler::modelForIName(const QString &iname) const
{
if (iname.startsWith(QLatin1String("local.")))
return m_locals;
if (iname.startsWith(QLatin1String("watch.")))
return m_watchers;
if (iname.startsWith(QLatin1String("tooltip.")))
return m_tooltips;
QTC_ASSERT(false, /**/);
return 0;
}
WatchData *WatchHandler::findItem(const QString &iname) const
{
const WatchModel *model = modelForIName(iname);
QTC_ASSERT(model, return 0);
return model->findItem(iname, model->m_root);
}
QString WatchHandler::watcherEditPlaceHolder()
{
static const QString rc = tr("<Edit>");
return rc;
}
} // namespace Internal
} // namespace Debugger