Newer
Older
/**************************************************************************
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Nokia Corporation (qt-info@nokia.com)
** 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
**************************************************************************/
#include "watchutils.h"
#include "debuggeractions.h"
#include "idebuggerengine.h"
#include <QtCore/QtAlgorithms>
#include <QtCore/QTextStream>
#include <QtGui/QApplication>
#include <QtGui/QLabel>
#include <QtGui/QToolTip>
#include <QtGui/QTextEdit>
#include <ctype.h>
// creates debug output for accesses to the model
//#define DEBUG_MODEL 1
#if DEBUG_MODEL
# define MODEL_DEBUG(s) qDebug() << s
#else
# define MODEL_DEBUG(s)
#endif
#define MODEL_DEBUGX(s) qDebug() << s
static const QString strNotInScope =
QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
static int watcherCounter = 0;
static int generationCounter = 0;
////////////////////////////////////////////////////////////////////
//
//
////////////////////////////////////////////////////////////////////
class WatchItem : public WatchData
{
public:
WatchItem() { parent = 0; }
WatchItem(const WatchData &data) : WatchData(data)
void setData(const WatchData &data)
WatchItem *parent;
QList<WatchItem *> children; // fetched children
};
////////////////////////////////////////////////////////////////////
//
// WatchData
//
////////////////////////////////////////////////////////////////////
WatchData::WatchData() :
hasChildren(false),
generation(-1),
valueEnabled(true),
valueEditable(true),
error(false),
source(0),
state(InitialState),
changed(false)
bool WatchData::isEqual(const WatchData &other) const
{
return iname == other.iname
&& exp == other.exp
&& name == other.name
&& value == other.value
&& editvalue == other.editvalue
&& valuetooltip == other.valuetooltip
&& type == other.type
&& displayedType == other.displayedType
&& variable == other.variable
&& addr == other.addr
&& saddr == other.saddr
&& framekey == other.framekey
&& hasChildren == other.hasChildren
&& valueEnabled == other.valueEnabled
&& valueEditable == other.valueEditable
&& error == other.error;
void WatchData::setError(const QString &msg)
{
setAllUnneeded();
value = msg;
setHasChildren(false);
valueEnabled = false;
valueEditable = false;
error = true;
void WatchData::setValue(const QString &value0)
hasChildren = true; // 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);
setHasChildren(false);
}
// "numchild" is sometimes lying
//MODEL_DEBUG("\n\n\nPOINTER: " << type << value);
if (isPointerType(type))
setHasChildren(value != "0x0" && value != "<null>"
&& !isCharPointerType(type));
// 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, bool guessChildrenFromType)
{
type = str.trimmed();
bool changed = true;
while (changed) {
if (type.endsWith(QLatin1String("const")))
else if (type.endsWith(QLatin1Char(' ')))
else if (type.endsWith(QLatin1Char('&')))
else if (type.startsWith(QLatin1String("const ")))
else if (type.startsWith(QLatin1String("volatile ")))
else if (type.startsWith(QLatin1String("class ")))
else if (type.startsWith(QLatin1String("struct ")))
else if (type.startsWith(QLatin1Char(' ')))
type = type.mid(1);
else
changed = false;
}
setTypeUnneeded();
if (guessChildrenFromType) {
switch (guessChildren(type)) {
case HasChildren:
setHasChildren(true);
break;
case HasNoChildren:
setHasChildren(false);
break;
case HasPossiblyChildren:
setHasChildren(true); // FIXME: bold assumption
break;
void WatchData::setAddress(const QString &str)
addr = str.toLatin1();
const char *doubleQuoteComma = "\",";
QString res;
QTextStream str(&res);
str << "iname=\"" << iname << doubleQuoteComma;
if (!name.isEmpty() && name != iname)
str << "name=\"" << name << doubleQuoteComma;
if (error)
str << "error,";
if (!addr.isEmpty())
str << "addr=\"" << addr << doubleQuoteComma;
str << "exp=\"" << exp << doubleQuoteComma;
str << "variable=\"" << variable << doubleQuoteComma;
str << "value=<needed>,";
str << "value=\"" << value << doubleQuoteComma;
str << "editvalue=\"" << editvalue << doubleQuoteComma;
str << "type=<needed>,";
str << "type=\"" << type << doubleQuoteComma;
if (isHasChildrenNeeded())
str << "hasChildren=<needed>,";
if (isHasChildrenKnown())
str << "hasChildren=\"" << (hasChildren ? "true" : "false") << doubleQuoteComma;
str << "children=<needed>,";
if (source)
str << "source=" << source;
str.flush();
if (res.endsWith(QLatin1Char(',')))
res.truncate(res.size() - 1);
return res + QLatin1Char('}');
// Format a tooltip fow with aligned colon
static void formatToolTipRow(QTextStream &str, const QString &category, const QString &value)
str << "<tr><td>" << category << "</td><td> : </td><td>"
<< Qt::escape(value) << "</td></tr>";
static inline QString typeToolTip(const WatchData &wd)
{
if (wd.displayedType.isEmpty())
return wd.type;
QString rc = wd.displayedType;
rc += QLatin1String(" (");
rc += wd.type;
rc += QLatin1Char(')');
return rc;
}
QString WatchData::toToolTip() const
{
if (!valuetooltip.isEmpty())
return QString::number(valuetooltip.size());
QString res;
QTextStream str(&res);
str << "<html><body><table>";
formatToolTipRow(str, WatchHandler::tr("Name"), name);
formatToolTipRow(str, WatchHandler::tr("Expression"), exp);
formatToolTipRow(str, WatchHandler::tr("Type"), typeToolTip(*this));
QString val = value;
if (value.size() > 1000) {
val.truncate(1000);
val += WatchHandler::tr(" ... <cut off>");
}
formatToolTipRow(str, WatchHandler::tr("Value"), val);
formatToolTipRow(str, WatchHandler::tr("Object Address"), addr);
formatToolTipRow(str, WatchHandler::tr("Stored Address"), saddr);
formatToolTipRow(str, WatchHandler::tr("Internal ID"), iname);
formatToolTipRow(str, WatchHandler::tr("Generation"),
QString::number(generation));
str << "</table></body></html>";
return res;
}
QString WatchData::msgNotInScope()
{
static const QString rc = QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
return rc;
}
QString WatchData::shadowedName(const QString &name, int seen)
{
if (seen <= 0)
return name;
return QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>").arg(name).arg(seen);
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
WatchModel::WatchModel(WatchHandler *handler, WatchType type)
: QAbstractItemModel(handler), m_handler(handler), m_type(type)
m_inExtraLayoutChanged = false;
m_root = new WatchItem;
m_root->hasChildren = 1;
m_root->state = 0;
m_root->name = WatchHandler::tr("Root");
switch (m_type) {
case LocalsWatch:
m_root->iname = "local";
m_root->name = WatchHandler::tr("Locals");
break;
case WatchersWatch:
m_root->iname = "watch";
m_root->name = WatchHandler::tr("Watchers");
break;
case TooltipsWatch:
m_root->iname = "tooltip";
m_root->name = WatchHandler::tr("Tooltip");
WatchModel::~WatchModel()
{
delete m_root;
}
WatchItem *WatchModel::rootItem() const
void WatchModel::reinitialize()
int n = m_root->children.size();
if (n == 0)
return;
//MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << m_root->iname);
QModelIndex index = watchIndex(m_root);
beginRemoveRows(index, 0, n - 1);
qDeleteAll(m_root->children);
m_root->children.clear();
endRemoveRows();
void WatchModel::emitAllChanged()
{
emit layoutChanged();
}
void WatchModel::beginCycle()
{
m_fetchTriggered.clear();
emit enableUpdates(false);
}
void WatchModel::endCycle()
{
removeOutdated();
emit enableUpdates(true);
// Prevent 'fetchMore()' from being triggered
m_inExtraLayoutChanged = true;
emit layoutChanged();
QTimer::singleShot(0, this, SLOT(resetExtraLayoutChanged()));
}
void WatchModel::resetExtraLayoutChanged()
{
m_inExtraLayoutChanged = false;
}
void WatchModel::dump()
{
qDebug() << "\n";
foreach (WatchItem *child, m_root->children)
dumpHelper(child);
}
void WatchModel::dumpHelper(WatchItem *item)
{
qDebug() << "ITEM: " << item->iname
<< (item->parent ? item->parent->iname : "<none>")
<< item->generation;
foreach (WatchItem *child, item->children)
dumpHelper(child);
}
void WatchModel::removeOutdated()
foreach (WatchItem *child, m_root->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) {
foreach (WatchItem *child, item->children)
removeOutdatedHelper(child);

hjk
committed
}
void WatchModel::destroyItem(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 QByteArray parentName(const QByteArray &iname)
int pos = iname.lastIndexOf('.');
return QByteArray();
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;
}
static inline QRegExp stdStringRegExp(const QString &charType)
QString rc = QLatin1String("basic_string<");
rc += charType;
rc += QLatin1String(",[ ]?std::char_traits<");
rc += charType;
rc += QLatin1String(">,[ ]?std::allocator<");
rc += charType;
rc += QLatin1String("> >");
const QRegExp re(rc);
Q_ASSERT(re.isValid());
return re;
}
static QString niceTypeHelper(const QString typeIn)
{
static QMap<QString, QString> cache;
const QMap<QString, QString>::const_iterator it = cache.constFind(typeIn);
if (it != cache.constEnd())
QString type = typeIn;
type.replace(QLatin1Char('*'), QLatin1Char('@'));
for (int i = 0; i < 10; ++i) {
int start = type.indexOf("std::allocator<");
if (start == -1)
// 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
const QRegExp stringRegexp = stdStringRegExp(inner);
type.replace(stringRegexp, QLatin1String("string"));
} else if (inner == QLatin1String("wchar_t")) { // std::wstring
const QRegExp wchartStringRegexp = stdStringRegExp(inner);
type.replace(wchartStringRegexp, QLatin1String("wstring"));
} else if (inner == QLatin1String("unsigned short")) { // std::wstring/MSVC
const QRegExp usStringRegexp = stdStringRegExp(inner);
type.replace(usStringRegexp, QLatin1String("wstring"));
}
// std::vector, std::deque, std::list
const QRegExp re1(QString::fromLatin1("(vector|list|deque)<%1, ?%2\\s*>").arg(inner, alloc));
if (re1.indexIn(type) != -1)
type.replace(re1.cap(0), QString::fromLatin1("%1<%2>").arg(re1.cap(1), inner));
// std::stack
QRegExp re6(QString::fromLatin1("stack<%1, ?std::deque<%2> >").arg(inner, inner));
if (!re6.isMinimal())
re6.setMinimal(true);
Q_ASSERT(re6.isValid());
if (re6.indexIn(type) != -1)
type.replace(re6.cap(0), QString::fromLatin1("stack<%1>").arg(inner));
QRegExp re4(QString::fromLatin1("set<%1, ?std::less<%2>, ?%3\\s*>").arg(inner, inner, alloc));
if (!re4.isMinimal())
re4.setMinimal(true);
Q_ASSERT(re4.isValid());
if (re4.indexIn(type) != -1)
type.replace(re4.cap(0), QString::fromLatin1("set<%1>").arg(inner));
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)
}
QString ckey = inner.mid(10, pos - 10);
QString key = chopConst(ckey);
QString value = inner.mid(pos + 2, inner.size() - 3 - pos).trimmed();
QRegExp re5(QString("map<%1, ?%2, ?std::less<%3 ?>, ?%4\\s*>")
.arg(key, value, key, alloc));
if (!re5.isMinimal())
re5.setMinimal(true);
Q_ASSERT(re5.isValid());
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));
if (!re7.isMinimal())
re7.setMinimal(true);
if (re7.indexIn(type) != -1)
type.replace(re7.cap(0), QString("map<const %1, %2>").arg(key, value));
type.replace(QLatin1Char('@'), QLatin1Char('*'));
type.replace(QLatin1String(" >"), QString(QLatin1Char('>')));
cache.insert(typeIn, type); // For simplicity, also cache unmodified types
QString WatchModel::niceType(const QString &typeIn) const
{
QString type = niceTypeHelper(typeIn);
if (!theDebuggerBoolSetting(ShowStdNamespace))
type = type.remove("std::");
IDebuggerEngine *engine = m_handler->m_manager->currentEngine();
if (engine && !theDebuggerBoolSetting(ShowQtNamespace))
type = type.remove(engine->qtNamespace());
return type;
}
template <class IntType> QString reformatInteger(IntType value, int format)
{
switch (format) {
case HexadecimalFormat:
return ("(hex) ") + QString::number(value, 16);
case BinaryFormat:
return ("(bin) ") + QString::number(value, 2);
case OctalFormat:
return ("(oct) ") + QString::number(value, 8);
}
return QString::number(value); // not reached
}
static QString formattedValue(const WatchData &data, int format)
if (isIntType(data.type)) {
if (format <= 0)
return data.value;
// Evil hack, covers 'unsigned' as well as quint64.
if (data.type.contains(QLatin1Char('u')))
return reformatInteger(data.value.toULongLong(), format);
return reformatInteger(data.value.toLongLong(), format);
}
if (0 && !data.addr.isEmpty()) {
if (format == BaldPointerFormat)
return data.value;
bool ok = false;
const void *addr =
reinterpret_cast<void *>(data.value.toULongLong(&ok, 0));
if (!ok || !addr)
return data.value;
// FIXME: add a round trip through the debugger to prevent crashs?
if (format == Latin1StringFormat)
return QString::fromLatin1(static_cast<const char *>(addr));
if (format == Local8BitStringFormat)
return QString::fromLocal8Bit(static_cast<const char *>(addr));
if (format == Utf8StringFormat)
return QString::fromUtf8(static_cast<const char *>(addr));
if (format == Utf16StringFormat)
return QString::fromUtf16(static_cast<const ushort *>(addr));
if (format == Ucs4StringFormat)
return QString::fromUcs4(static_cast<const uint *>(addr));
return data.value;
}
return data.value;
}
bool WatchModel::canFetchMore(const QModelIndex &index) const
return !m_inExtraLayoutChanged && index.isValid()
&& !m_fetchTriggered.contains(watchItem(index)->iname);
}
void WatchModel::fetchMore(const QModelIndex &index)
{
if (m_inExtraLayoutChanged)
return;
QTC_ASSERT(index.isValid(), return);
WatchItem *item = watchItem(index);
QTC_ASSERT(item, return);
QTC_ASSERT(!m_fetchTriggered.contains(item->iname), return);
m_handler->m_expandedINames.insert(item->iname);
m_fetchTriggered.insert(item->iname);
if (item->children.isEmpty()) {
WatchData data = *item;
data.setChildrenNeeded();
m_handler->m_manager->updateWatchData(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());
if (row >= item->children.size())
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();
}
int WatchModel::rowCount(const QModelIndex &idx) const
{
if (idx.column() > 0)
return 0;
return watchItem(idx)->children.size();
}
int WatchModel::columnCount(const QModelIndex &idx) const
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
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();
void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
{
QModelIndex idx1 = index(0, column, parentIndex);
QModelIndex idx2 = index(rowCount(parentIndex) - 1, column, parentIndex);
if (idx1.isValid() && idx2.isValid())
emit dataChanged(idx1, idx2);
//qDebug() << "CHANGING:\n" << idx1 << "\n" << idx2 << "\n"
// << data(parentIndex, INameRole).toString();
for (int i = rowCount(parentIndex); --i >= 0; )
emitDataChanged(column, index(i, 0, parentIndex));
}
QVariant WatchModel::data(const QModelIndex &idx, int role) const
{
const WatchItem *item = watchItem(idx);
const WatchItem &data = *item;
switch (idx.column()) {
case 0:
if (data.name == QLatin1String("*") && item->parent)
return QLatin1String("*") + item->parent->name;
return data.name;
case 1: {
int format = m_handler->m_individualFormats.value(data.iname, -1);
if (format == -1)
format = m_handler->m_typeFormats.value(data.type, -1);
//qDebug() << "FORMATTED: " << format << formattedValue(data, format);
return formattedValue(data, format);
}
case 2: {
if (!data.displayedType.isEmpty())
return data.displayedType;
return niceType(data.type);
}
return theDebuggerBoolSetting(UseToolTipsInLocalsView)
? data.toToolTip() : QVariant();
case Qt::ForegroundRole: {
static const QVariant red(QColor(200, 0, 0));
static const QVariant gray(QColor(140, 140, 140));
switch (idx.column()) {
case 1: return !data.valueEnabled ? gray : data.changed ? red : QVariant();
case ExpressionRole:
return data.exp;
return m_handler->m_expandedINames.contains(data.iname);
//FIXME return node < 4 || m_expandedINames.contains(data.iname);
case ActiveDataRole:
qDebug() << "ASK FOR" << data.iname;
return true;
case TypeFormatListRole:
return QStringList() << tr("decimal") << tr("hexadecimal")
<< tr("binary") << tr("octal");
if (!data.addr.isEmpty())
return QStringList()
<< tr("Bald pointer")
<< tr("Latin1 string")
<< tr("UTF8 string")
<< tr("UTF16 string")
<< tr("UCS4 string");
break;
case TypeFormatRole:
return m_handler->m_typeFormats.value(data.type, -1);
case IndividualFormatRole:
return m_handler->m_individualFormats.value(data.iname, -1);
case AddressRole: {
if (!data.addr.isEmpty())
return data.addr;
bool ok;
(void) data.value.toULongLong(&ok, 0);
if (ok)
return data.value;
return QVariant();
}
bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
WatchItem &data = *watchItem(index);
if (value.toBool()) {
// Should already have been triggered by fetchMore()
//QTC_ASSERT(m_handler->m_expandedINames.contains(data.iname), /**/);
m_handler->m_expandedINames.insert(data.iname);
m_handler->m_expandedINames.remove(data.iname);
} else if (role == TypeFormatRole) {
m_handler->setFormat(data.type, value.toInt());
} else if (role == IndividualFormatRole) {
const int format = value.toInt();
if (format == -1) {
m_handler->m_individualFormats.remove(data.iname);
} else {
m_handler->m_individualFormats[data.iname] = format;
}
emit dataChanged(index, index);
return true;
}
Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
{
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.
ItemIsSelectable
| ItemIsDragEnabled
| ItemIsDropEnabled
// | ItemIsUserCheckable
// | ItemIsTristate
| ItemIsEnabled;
static const ItemFlags editable = notEditable | ItemIsEditable;
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 && data.valueEditable)
return editable; // locals and watcher values are sometimes editable
return notEditable;
QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical)
return QVariant();
if (role == Qt::DisplayRole) {
switch (section) {
case 0: return QString(tr("Name") + QLatin1String(" "));
case 1: return QString(tr("Value") + QLatin1String(" "));
case 2: return QString(tr("Type") + QLatin1String(" "));
return QVariant();
struct IName : public QByteArray
IName(const QByteArray &iname) : QByteArray(iname) {}
bool iNameLess(const QString &iname1, const QString &iname2)
{
QString name1 = iname1.section('.', -1);
QString name2 = iname2.section('.', -1);
if (!name1.isEmpty() && !name2.isEmpty()) {
if (name1.at(0).isDigit() && name2.at(0).isDigit()) {
bool ok1 = false, ok2 = false;
int i1 = name1.toInt(&ok1), i2 = name2.toInt(&ok2);
if (ok1 && ok2)
return i1 < i2;
}
return name1 < name2;
bool operator<(const IName &iname1, const IName &iname2)
{
return iNameLess(iname1, iname2);
}
static bool iNameSorter(const WatchItem *item1, const WatchItem *item2)
{
return iNameLess(item1->iname, item2->iname);
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)
//static int bulk = 0;
//qDebug() << "SINGLE: " << ++bulk << data.toString();
if (data.iname.isEmpty()) {
int x;
x = 1;
}
QTC_ASSERT(!data.iname.isEmpty(), qDebug() << data.toString(); return);
WatchItem *parent = findItem(parentName(data.iname), m_root);
if (!parent) {
WatchData parent;
parent.iname = parentName(data.iname);
MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname);
if (!parent.iname.isEmpty())
insertData(parent);
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;