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

1068 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/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; fetched = 0; }
WatchItem(const WatchData &data) : WatchData(data)
{ parent = 0; fetched = 0; }
void setData(const WatchData &data)
{ static_cast<WatchData &>(*this) = data; }
WatchItem *parent;
bool fetched; // children fetch has been triggered
QList<WatchItem *> children; // fetched children
};
2008-12-02 12:01:29 +01:00
////////////////////////////////////////////////////////////////////
//
// WatchData
//
////////////////////////////////////////////////////////////////////
WatchData::WatchData()
2008-12-02 12:01:29 +01:00
{
childCount = -1;
valuedisabled = false;
source = 0;
state = InitialState;
changed = false;
generation = -1;
2008-12-02 12:01:29 +01:00
}
void WatchData::setError(const QString &msg)
{
setAllUnneeded();
value = msg;
setChildCount(0);
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();
childCount = 1; // at least one...
}
// 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);
setChildCount(0);
}
// "numchild" is sometimes lying
//MODEL_DEBUG("\n\n\nPOINTER: " << type << value);
if (isPointerType(type))
setChildCount(value != "0x0" && value != "<null>");
// 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))
setChildCount(0);
}
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);
2009-04-01 15:10:20 +02:00
if (childCount)
str << "childCount=\"" << childCount << doubleQuoteComma;
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 (isChildCountNeeded())
str << "numchild=<needed>,";
2008-12-02 12:01:29 +01:00
if (isChildCountKnown() && childCount == -1)
str << "numchild=\"" << childCount << 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>
inline void formatToolTipRow(QTextStream &str,
const QString &category,
const Streamable &value)
{
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->childCount = 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->childCount = 1;
item->state = 0;
item->parent = m_root;
item->fetched = 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 bool iNameSorter(const WatchData &d1, const WatchData &d2)
{
if (d1.level != d2.level)
return d1.level < d2.level;
for (int level = 0; level != d1.level; ++level) {
QString name1 = d1.iname.section('.', level, level);
QString name2 = d2.iname.section('.', level, level);
//MODEL_DEBUG(" SORT: " << name1 << name2 << (name1 < name2));
if (name1 != name2 && !name1.isEmpty() && !name2.isEmpty()) {
if (name1.at(0).isDigit() && name2.at(0).isDigit())
return name1.toInt() < name2.toInt();
2008-12-02 12:01:29 +01:00
return name1 < name2;
}
}
return false;
}
*/
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;
}
2008-12-02 12:01:29 +01:00
static QString niceType(QString type)
{
if (type.contains(QLatin1String("std::"))) {
2008-12-10 14:37:15 +01:00
// std::string
type.replace(QLatin1String("basic_string<char, std::char_traits<char>, "
"std::allocator<char> >"), QLatin1String("string"));
2008-12-10 14:37:15 +01:00
// std::wstring
type.replace(QLatin1String("basic_string<wchar_t, std::char_traits<wchar_t>, "
"std::allocator<wchar_t> >"), QLatin1String("wstring"));
2008-12-02 12:01:29 +01:00
2008-12-10 14:37:15 +01:00
// std::vector
static QRegExp re1(QLatin1String("vector<(.*), std::allocator<(.*)>\\s*>"));
2008-12-10 14:37:15 +01:00
re1.setMinimal(true);
for (int i = 0; i != 10; ++i) {
if (re1.indexIn(type) == -1 || re1.cap(1) != re1.cap(2))
break;
type.replace(re1.cap(0), QLatin1String("vector<") + re1.cap(1) + QLatin1Char('>'));
}
// std::deque
static QRegExp re5(QLatin1String("deque<(.*), std::allocator<(.*)>\\s*>"));
re5.setMinimal(true);
for (int i = 0; i != 10; ++i) {
if (re5.indexIn(type) == -1 || re5.cap(1) != re5.cap(2))
break;
type.replace(re5.cap(0), QLatin1String("deque<") + re5.cap(1) + QLatin1Char('>'));
}
// std::stack
static QRegExp re6(QLatin1String("stack<(.*), std::deque<(.*), std::allocator<(.*)>\\s*> >"));
re6.setMinimal(true);
for (int i = 0; i != 10; ++i) {
if (re6.indexIn(type) == -1 || re6.cap(1) != re6.cap(2)
|| re6.cap(1) != re6.cap(2))
break;
type.replace(re6.cap(0), QLatin1String("stack<") + re6.cap(1) + QLatin1Char('>'));
2008-12-10 14:37:15 +01:00
}
// std::list
static QRegExp re2(QLatin1String("list<(.*), std::allocator<(.*)>\\s*>"));
2008-12-10 14:37:15 +01:00
re2.setMinimal(true);
2008-12-02 12:01:29 +01:00
for (int i = 0; i != 10; ++i) {
2008-12-10 14:37:15 +01:00
if (re2.indexIn(type) == -1 || re2.cap(1) != re2.cap(2))
2008-12-02 12:01:29 +01:00
break;
type.replace(re2.cap(0), QLatin1String("list<") + re2.cap(1) + QLatin1Char('>'));
2008-12-02 12:01:29 +01:00
}
// std::map
{
static QRegExp re(QLatin1String("map<(.*), (.*), std::less<(.*)>, "
"std::allocator<std::pair<(.*), (.*)> > >"));
re.setMinimal(true);
for (int i = 0; i != 10; ++i) {
if (re.indexIn(type) == -1)
break;
QString key = chopConst(re.cap(1));
QString value = chopConst(re.cap(2));
QString key1 = chopConst(re.cap(3));
QString key2 = chopConst(re.cap(4));
QString value2 = chopConst(re.cap(5));
if (value != value2 || key != key1 || key != key2) {
qDebug() << key << key1 << key2 << value << value2
<< (key == key1) << (key == key2) << (value == value2);
break;
}
type.replace(re.cap(0), QString("map<%1, %2>").arg(key).arg(value));
}
}
// std::set
static QRegExp re4(QLatin1String("set<(.*), std::less<(.*)>, std::allocator<(.*)>\\s*>"));
re1.setMinimal(true);
for (int i = 0; i != 10; ++i) {
if (re4.indexIn(type) == -1 || re4.cap(1) != re4.cap(2)
|| re4.cap(1) != re4.cap(3))
break;
type.replace(re4.cap(0), QLatin1String("set<") + re4.cap(1) + QLatin1Char('>'));
}
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)->fetched;
}
void WatchModel::fetchMore(const QModelIndex &index)
{
QTC_ASSERT(index.isValid(), return);
QTC_ASSERT(!watchItem(index)->fetched, return);
if (WatchItem *item = watchItem(index)) {
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->childCount > 0;
}
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();
}
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 = parent->children.size();
beginInsertRows(index, n, n);
parent->children.append(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;
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);
}
} // namespace Internal
} // namespace Debugger