watchhandler.cpp 67 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22
** 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.
23
**
hjk's avatar
hjk committed
24 25
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
#include "watchhandler.h"
31 32

#include "breakhandler.h"
33
#include "debuggeractions.h"
34
#include "debuggercore.h"
35
#include "debuggerdialogs.h"
36 37 38
#include "debuggerengine.h"
#include "debuggerinternalconstants.h"
#include "debuggerprotocol.h"
39
#include "simplifytype.h"
40
#include "imageviewer.h"
41
#include "watchutils.h"
con's avatar
con committed
42

hjk's avatar
hjk committed
43
#include <utils/qtcassert.h>
44
#include <utils/savedaction.h>
con's avatar
con committed
45

46
#include <QDebug>
47
#include <QFile>
48 49
#include <QProcess>
#include <QtAlgorithms>
50
#include <QTabWidget>
51
#include <QTextEdit>
con's avatar
con committed
52 53

#include <ctype.h>
hjk's avatar
hjk committed
54

hjk's avatar
hjk committed
55 56 57 58
//#define USE_WATCH_MODEL_TEST 0
//#define USE_EXPENSIVE_CHECKS 0

#if USE_WATCH_MODEL_TEST
59
#include <modeltest.h>
hjk's avatar
hjk committed
60
#endif
con's avatar
con committed
61

hjk's avatar
hjk committed
62 63
namespace Debugger {
namespace Internal {
con's avatar
con committed
64

65 66 67 68
// Creates debug output for accesses to the model.
enum { debugModel = 0 };

#define MODEL_DEBUG(s) do { if (debugModel) qDebug() << s; } while (0)
hjk's avatar
hjk committed
69 70 71 72 73 74 75

#if USE_EXPENSIVE_CHECKS
#define CHECK(s) s
#else
#define CHECK(s)
#endif

76

77 78 79 80 81
static QHash<QByteArray, int> theWatcherNames;
static QHash<QByteArray, int> theTypeFormats;
static QHash<QByteArray, int> theIndividualFormats;
static int theUnprintableBase = -1;

82 83 84
const char INameProperty[] = "INameProperty";
const char KeyProperty[] = "KeyProperty";

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
static QByteArray stripForFormat(const QByteArray &ba)
{
    QByteArray res;
    res.reserve(ba.size());
    int inArray = 0;
    for (int i = 0; i != ba.size(); ++i) {
        const char c = ba.at(i);
        if (c == '<')
            break;
        if (c == '[')
            ++inArray;
        if (c == ']')
            --inArray;
        if (c == ' ')
            continue;
100 101
        if (c == '&') // Treat references like the referenced type.
            continue;
102 103 104 105 106
        if (inArray && c >= '0' && c <= '9')
            continue;
        res.append(c);
    }
    return res;
107 108
}

con's avatar
con committed
109 110
////////////////////////////////////////////////////////////////////
//
111
// WatchItem
con's avatar
con committed
112 113
//
////////////////////////////////////////////////////////////////////
114

hjk's avatar
hjk committed
115 116 117 118
// Used to make sure the item cache is notified of construction and
// destruction of items.

class WatchItem;
119 120
typedef QList<WatchItem *> WatchItems;

hjk's avatar
hjk committed
121 122 123
WatchItem *itemConstructor(WatchModel *model, const QByteArray &iname);
void itemDestructor(WatchModel *model, WatchItem *item);

124 125 126
class WatchItem : public WatchData
{
public:
127 128
    WatchItem *parent; // Not owned.
    WatchItems children; // Not owned. Handled via itemDestructor().
129

hjk's avatar
hjk committed
130 131 132
private:
    friend WatchItem *itemConstructor(WatchModel *model, const QByteArray &iname);
    friend void itemDestructor(WatchModel *model, WatchItem *item);
133

hjk's avatar
hjk committed
134
    WatchItem() { parent = 0; }
hjk's avatar
hjk committed
135 136
    ~WatchItem() {}
    WatchItem(const WatchItem &); // Not implemented.
137 138 139
};

///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
140
//
141
// WatchModel
hjk's avatar
hjk committed
142
//
143
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
144

145
class SeparatedView : public QTabWidget
146 147 148 149
{
    Q_OBJECT

public:
150
    SeparatedView() : QTabWidget(debuggerCore()->mainWindow())
151 152 153 154 155
    {
        setTabsClosable(true);
        connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeTab(int)));
        setWindowFlags(windowFlags() | Qt::Window);
        setWindowTitle(WatchHandler::tr("Debugger - Qt Creator"));
156 157 158 159 160 161 162 163 164

        QVariant geometry = DebuggerCore::sessionValue("DebuggerSeparateWidgetGeometry");
        if (geometry.isValid())
            setGeometry(geometry.toRect());
    }

    ~SeparatedView()
    {
        DebuggerCore::setSessionValue("DebuggerSeparateWidgetGeometry", geometry());
165 166
    }

167
    void removeObject(const QByteArray &key)
168
    {
169 170 171 172 173 174 175 176 177 178 179 180
        if (QWidget *w = findWidget(key)) {
            removeTab(indexOf(w));
            sanitize();
        }
    }

    Q_SLOT void closeTab(int index)
    {
        if (QObject *o = widget(index)) {
            QByteArray iname = o->property(INameProperty).toByteArray();
            theIndividualFormats.remove(iname);
        }
181
        removeTab(index);
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
        sanitize();
    }

    void sanitize()
    {
        if (count() == 0)
            hide();
    }

    QWidget *findWidget(const QByteArray &needle)
    {
        for (int i = count(); --i >= 0; ) {
            QWidget *w = widget(i);
            QByteArray key = w->property(KeyProperty).toByteArray();
            if (key == needle)
                return w;
        }
        return 0;
    }

    template <class T> T *prepareObject(const QByteArray &key, const QString &title)
    {
        T *t = 0;
        if (QWidget *w = findWidget(key)) {
            t = qobject_cast<T *>(w);
            if (!t)
                removeTab(indexOf(w));
        }
        if (!t) {
            t = new T;
            t->setProperty(KeyProperty, key);
            addTab(t, title);
        }

        setCurrentWidget(t);
        show();
        raise();
        return t;
220 221 222
    }
};

223

hjk's avatar
hjk committed
224
class WatchModel : public QAbstractItemModel
hjk's avatar
hjk committed
225
{
hjk's avatar
hjk committed
226
    Q_OBJECT
hjk's avatar
hjk committed
227

hjk's avatar
hjk committed
228 229 230 231 232 233 234 235 236 237 238
private:
    explicit WatchModel(WatchHandler *handler);
    ~WatchModel();

    friend WatchItem *itemConstructor(WatchModel *model, const QByteArray &iname);
    friend void itemDestructor(WatchModel *model, WatchItem *item);

public:
    int rowCount(const QModelIndex &idx = QModelIndex()) const;
    int columnCount(const QModelIndex &idx) const;

hjk's avatar
hjk committed
239 240 241
    static QString nameForFormat(int format);
    TypeFormatList typeFormatList(const WatchData &value) const;

hjk's avatar
hjk committed
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
signals:
    void currentIndexRequested(const QModelIndex &idx);
    void itemIsExpanded(const QModelIndex &idx);

private:
    QVariant data(const QModelIndex &idx, int role) const;
    bool setData(const QModelIndex &idx, const QVariant &value, int role);
    QModelIndex index(int, int, const QModelIndex &idx) const;
    QModelIndex parent(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);

    void invalidateAll(const QModelIndex &parentIndex = QModelIndex());
259
    void resetValueCacheRecursively(WatchItem *item);
260

hjk's avatar
hjk committed
261 262 263 264 265 266 267 268 269
    WatchItem *createItem(const QByteArray &iname, const QString &name, WatchItem *parent);

    friend class WatchHandler;

    WatchItem *watchItem(const QModelIndex &) const;
    QModelIndex watchIndex(const WatchItem *needle) const;
    QModelIndex watchIndexHelper(const WatchItem *needle,
        const WatchItem *parentItem, const QModelIndex &parentIndex) const;

270
    void insertDataItem(const WatchData &data, bool destructive);
hjk's avatar
hjk committed
271 272
    Q_SLOT void reinsertAllData();
    void reinsertAllDataHelper(WatchItem *item, QList<WatchData> *data);
hjk's avatar
hjk committed
273
    bool ancestorChanged(const QSet<QByteArray> &parentINames, WatchItem *item) const;
hjk's avatar
hjk committed
274 275
    void insertBulkData(const QList<WatchData> &data);
    QString displayForAutoTest(const QByteArray &iname) const;
276
    void reinitialize(bool includeInspectData = false);
hjk's avatar
hjk committed
277 278
    void destroyItem(WatchItem *item); // With model notification.
    void destroyChildren(WatchItem *item); // With model notification.
279
    void destroyHelper(const WatchItems &items); // Without model notification.
hjk's avatar
hjk committed
280 281 282 283 284 285 286 287 288 289 290 291 292
    void emitDataChanged(int column,
        const QModelIndex &parentIndex = QModelIndex());

    friend QDebug operator<<(QDebug d, const WatchModel &m);

    void dump();
    void dumpHelper(WatchItem *item);
    Q_SLOT void emitAllChanged();

    void showInEditorHelper(QString *contents, WatchItem *item, int level);
    void setCurrentItem(const QByteArray &iname);

    QString displayType(const WatchData &typeIn) const;
293 294
    QString displayName(const WatchItem *item) const;
    QString displayValue(const WatchData &data) const;
hjk's avatar
hjk committed
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
    QString formattedValue(const WatchData &data) const;
    QString removeNamespaces(QString str) const;
    void formatRequests(QByteArray *out, const WatchItem *item) const;
    DebuggerEngine *engine() const;
    int itemFormat(const WatchData &data) const;
    bool contentIsValid() const;

    WatchHandler *m_handler; // Not owned.

    WatchItem *m_root; // Owned.
    WatchItem *m_localsRoot; // Not owned.
    WatchItem *m_inspectorRoot; // Not owned.
    WatchItem *m_watchRoot; // Not owned.
    WatchItem *m_returnRoot; // Not owned.
    WatchItem *m_tooltipRoot; // Not owned.

    QSet<QByteArray> m_expandedINames;
    QSet<QByteArray> m_fetchTriggered;

hjk's avatar
hjk committed
314 315 316
    TypeFormatList builtinTypeFormatList(const WatchData &data) const;
    QStringList dumperTypeFormatList(const WatchData &data) const;
    DumperTypeFormats m_reportedTypeFormats;
hjk's avatar
hjk committed
317 318 319 320 321 322

    WatchItem *createItem(const QByteArray &iname);
    WatchItem *createItem(const WatchData &data);
    void assignData(WatchItem *item, const WatchData &data);
    WatchItem *findItem(const QByteArray &iname) const;
    friend class WatchItem;
hjk's avatar
hjk committed
323 324
    typedef QHash<QByteArray, WatchItem *> Cache;
    Cache m_cache;
325 326
    typedef QHash<QByteArray, QString> ValueCache;
    ValueCache m_valueCache;
hjk's avatar
hjk committed
327 328 329 330 331 332 333 334 335 336 337 338

    #if USE_EXPENSIVE_CHECKS
    QHash<const WatchItem *, QByteArray> m_cache2;
    void checkTree();
    void checkItem(const WatchItem *item) const;
    void checkTree(WatchItem *item, QSet<QByteArray> *inames);
    #endif
};

WatchModel::WatchModel(WatchHandler *handler)
    : m_handler(handler)
{
339
    setObjectName(QLatin1String("WatchModel"));
hjk's avatar
hjk committed
340 341 342 343 344 345 346 347 348 349 350 351 352 353
    m_root = createItem(QByteArray(), tr("Root"), 0);
    // Note: Needs to stay
    m_localsRoot = createItem("local", tr("Locals"), m_root);
    m_inspectorRoot = createItem("inspect", tr("Inspector"), m_root);
    m_watchRoot = createItem("watch", tr("Expressions"), m_root);
    m_returnRoot = createItem("return", tr("Return Value"), m_root);
    m_tooltipRoot = createItem("tooltip", tr("Tooltip"), m_root);

    connect(debuggerCore()->action(SortStructMembers), SIGNAL(valueChanged(QVariant)),
        SLOT(reinsertAllData()));
    connect(debuggerCore()->action(ShowStdNamespace), SIGNAL(valueChanged(QVariant)),
        SLOT(reinsertAllData()));
    connect(debuggerCore()->action(ShowQtNamespace), SIGNAL(valueChanged(QVariant)),
        SLOT(reinsertAllData()));
hjk's avatar
hjk committed
354 355
}

356 357
WatchModel::~WatchModel()
{
hjk's avatar
hjk committed
358 359 360 361
    CHECK(checkItem(m_root));
    destroyChildren(m_root);
    itemDestructor(this, m_root);
    QTC_CHECK(m_cache.isEmpty());
362 363
}

hjk's avatar
hjk committed
364
WatchItem *itemConstructor(WatchModel *model, const QByteArray &iname)
hjk's avatar
hjk committed
365
{
hjk's avatar
hjk committed
366 367 368 369 370 371 372
    QTC_CHECK(!model->m_cache.contains(iname));
    WatchItem *item = new WatchItem();
    item->iname = iname;
    model->m_cache[iname] = item;
    CHECK(model->m_cache2[item] = iname);
    CHECK(model->checkItem(item));
    return item;
hjk's avatar
hjk committed
373 374
}

hjk's avatar
hjk committed
375
void itemDestructor(WatchModel *model, WatchItem *item)
hjk's avatar
hjk committed
376
{
hjk's avatar
hjk committed
377 378 379 380 381
    QTC_ASSERT(model->m_cache.value(item->iname) == item, return);
    CHECK(model->checkItem(item));
    CHECK(model->m_cache2.remove(item));
    model->m_cache.remove(item->iname);
    delete item;
hjk's avatar
hjk committed
382 383
}

hjk's avatar
hjk committed
384
WatchItem *WatchModel::createItem(const QByteArray &iname, const QString &name, WatchItem *parent)
385
{
hjk's avatar
hjk committed
386 387 388 389 390 391 392 393
    WatchItem *item = itemConstructor(this, iname);
    item->name = name;
    item->hasChildren = true; // parent == 0;
    item->state = 0;
    item->parent = parent;
    if (parent)
        parent->children.append(item);
    return item;
394 395
}

396
void WatchModel::reinitialize(bool includeInspectData)
397
{
hjk's avatar
hjk committed
398 399 400 401 402 403 404
    CHECK(checkTree());
    //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << m_root->iname);
    QTC_CHECK(m_root->children.size() == 5);
    destroyChildren(m_localsRoot);
    destroyChildren(m_watchRoot);
    destroyChildren(m_returnRoot);
    destroyChildren(m_tooltipRoot);
405 406 407 408
    if (includeInspectData) {
        destroyChildren(m_inspectorRoot);
        QTC_CHECK(m_cache.size() == 6);
    }
hjk's avatar
hjk committed
409
    CHECK(checkTree());
410 411
}

hjk's avatar
hjk committed
412
void WatchModel::emitAllChanged()
413
{
hjk's avatar
hjk committed
414
    emit layoutChanged();
415
}
416

417 418 419 420
DebuggerEngine *WatchModel::engine() const
{
    return m_handler->m_engine;
}
421 422 423 424 425 426 427 428 429 430 431

void WatchModel::dump()
{
    qDebug() << "\n";
    foreach (WatchItem *child, m_root->children)
        dumpHelper(child);
}

void WatchModel::dumpHelper(WatchItem *item)
{
    qDebug() << "ITEM: " << item->iname
hjk's avatar
hjk committed
432
        << (item->parent ? item->parent->iname : "<none>");
433 434 435 436
    foreach (WatchItem *child, item->children)
        dumpHelper(child);
}

437
void WatchModel::destroyHelper(const WatchItems &items)
hjk's avatar
hjk committed
438
{
hjk's avatar
hjk committed
439 440 441 442
    for (int i = items.size(); --i >= 0; ) {
        WatchItem *item = items.at(i);
        destroyHelper(item->children);
        itemDestructor(this, item);
443
    }
hjk's avatar
hjk committed
444 445
}

446
void WatchModel::destroyItem(WatchItem *item)
con's avatar
con committed
447
{
hjk's avatar
hjk committed
448 449 450 451 452 453
    const QByteArray iname = item->iname;
    CHECK(checkTree());
    QTC_ASSERT(m_cache.contains(iname), return);

    // Deregister from model and parent.
    // It's sufficient to do this non-recursively.
454
    WatchItem *parent = item->parent;
hjk's avatar
hjk committed
455 456 457
    QTC_ASSERT(parent, return);
    QModelIndex parentIndex = watchIndex(parent);
    const int i = parent->children.indexOf(item);
458
    //MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n);
hjk's avatar
hjk committed
459 460
    beginRemoveRows(parentIndex, i, i);
    parent->children.removeAt(i);
461
    endRemoveRows();
hjk's avatar
hjk committed
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476

    // Destroy contents.
    destroyHelper(item->children);
    itemDestructor(this, item);
    QTC_ASSERT(!m_cache.contains(iname), return);
    CHECK(checkTree());
}

void WatchModel::destroyChildren(WatchItem *item)
{
    CHECK(checkTree());
    QTC_ASSERT(m_cache.contains(item->iname), return);
    if (item->children.isEmpty())
        return;

477
    WatchItems items = item->children;
hjk's avatar
hjk committed
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510

    // Deregister from model and parent.
    // It's sufficient to do this non-recursively.
    QModelIndex idx = watchIndex(item);
    beginRemoveRows(idx, 0, items.size() - 1);
    item->children.clear();
    endRemoveRows();

    // Destroy contents.
    destroyHelper(items);
    CHECK(checkTree());
}

WatchItem *WatchModel::findItem(const QByteArray &iname) const
{
    return m_cache.value(iname, 0);
}

WatchItem *WatchModel::createItem(const WatchData &data)
{
    WatchItem *item = itemConstructor(this, data.iname);
    static_cast<WatchData &>(*item) = data;
    return item;
}

void WatchModel::assignData(WatchItem *item, const WatchData &data)
{
    CHECK(checkItem(item));
    QTC_ASSERT(data.iname == item->iname,
        m_cache.remove(item->iname);
        m_cache[data.iname] = item);
    static_cast<WatchData &>(*item) = data;
    CHECK(checkItem(item));
511 512
}

513 514 515 516
void WatchModel::reinsertAllData()
{
    QList<WatchData> list;
    reinsertAllDataHelper(m_root, &list);
517
    reinitialize(true);
hjk's avatar
hjk committed
518
    insertBulkData(list);
519 520 521 522 523
}

void WatchModel::reinsertAllDataHelper(WatchItem *item, QList<WatchData> *data)
{
    data->append(*item);
hjk's avatar
hjk committed
524
    data->back().setAllUnneeded();
525 526 527 528
    foreach (WatchItem *child, item->children)
        reinsertAllDataHelper(child, data);
}

529
static QByteArray parentName(const QByteArray &iname)
530
{
hjk's avatar
hjk committed
531 532
    const int pos = iname.lastIndexOf('.');
    return pos == -1 ? QByteArray() : iname.left(pos);
con's avatar
con committed
533 534
}

535
static QString niceTypeHelper(const QByteArray &typeIn)
536
{
537 538 539
    typedef QMap<QByteArray, QString> Cache;
    static Cache cache;
    const Cache::const_iterator it = cache.constFind(typeIn);
540
    if (it != cache.constEnd())
541
        return it.value();
542
    const QString simplified = simplifyType(QLatin1String(typeIn));
543 544
    cache.insert(typeIn, simplified); // For simplicity, also cache unmodified types
    return simplified;
con's avatar
con committed
545 546
}

547
QString WatchModel::removeNamespaces(QString str) const
548
{
hjk's avatar
hjk committed
549
    if (!debuggerCore()->boolSetting(ShowStdNamespace))
550
        str.remove(QLatin1String("std::"));
hjk's avatar
hjk committed
551
    if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
552
        const QString qtNamespace = QString::fromLatin1(engine()->qtNamespace());
553
        if (!qtNamespace.isEmpty())
554 555 556 557 558
            str.remove(qtNamespace);
    }
    return str;
}

hjk's avatar
hjk committed
559
static int formatToIntegerBase(int format)
560 561
{
    switch (format) {
hjk's avatar
hjk committed
562
        case HexadecimalIntegerFormat:
hjk's avatar
hjk committed
563
            return 16;
hjk's avatar
hjk committed
564
        case BinaryIntegerFormat:
hjk's avatar
hjk committed
565
            return 2;
hjk's avatar
hjk committed
566
        case OctalIntegerFormat:
hjk's avatar
hjk committed
567
            return 8;
568 569 570 571
    }
    return 10;
}

572 573 574
template <class IntType> QString reformatInteger(IntType value, int format)
{
    switch (format) {
hjk's avatar
hjk committed
575
        case HexadecimalIntegerFormat:
576
            return QLatin1String("(hex) ") + QString::number(value, 16);
hjk's avatar
hjk committed
577
        case BinaryIntegerFormat:
578
            return QLatin1String("(bin) ") + QString::number(value, 2);
hjk's avatar
hjk committed
579
        case OctalIntegerFormat:
580
            return QLatin1String("(oct) ") + QString::number(value, 8);
581
    }
582 583 584 585 586 587
    return QString::number(value, 10); // not reached
}

static QString reformatInteger(quint64 value, int format, int size, bool isSigned)
{
    // Follow convention and don't show negative non-decimal numbers.
588
    if (format != AutomaticFormat && format != DecimalIntegerFormat)
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
        isSigned = false;

    switch (size) {
        case 1:
            value = value & 0xff;
            return isSigned
                ? reformatInteger<qint8>(value, format)
                : reformatInteger<quint8>(value, format);
        case 2:
            value = value & 0xffff;
            return isSigned
                ? reformatInteger<qint16>(value, format)
                : reformatInteger<quint16>(value, format);
        case 4:
            value = value & 0xffffffff;
            return isSigned
                ? reformatInteger<qint32>(value, format)
                : reformatInteger<quint32>(value, format);
        default:
        case 8: return isSigned
                ? reformatInteger<qint64>(value, format)
                : reformatInteger<quint64>(value, format);
    }
612 613
}

614
// Format printable (char-type) characters
615
static QString reformatCharacter(int code, int format)
616
{
617
    const QString codeS = reformatInteger(code, format, 1, true);
618
    if (code < 0) // Append unsigned value.
619
        return codeS + QLatin1String(" / ") + reformatInteger(256 + code, format, 1, false);
620
    const QChar c = QChar(uint(code));
621 622
    if (c.isPrint())
        return codeS + QLatin1String(" '") + c + QLatin1Char('\'');
623 624 625 626 627 628 629 630 631 632 633 634 635
    switch (code) {
    case 0:
        return codeS + QLatin1String(" '\\0'");
    case '\r':
        return codeS + QLatin1String(" '\\r'");
    case '\t':
        return codeS + QLatin1String(" '\\t'");
    case '\n':
        return codeS + QLatin1String(" '\\n'");
    }
    return codeS;
}

636 637 638 639
static QString quoteUnprintable(const QString &str)
{
    if (WatchHandler::unprintableBase() == 0)
        return str;
640

641
    QString encoded;
642 643 644
    if (WatchHandler::unprintableBase() == -1) {
        foreach (const QChar c, str) {
            int u = c.unicode();
645
            if (c.isPrint())
646 647
                encoded += c;
            else if (u == '\r')
648
                encoded += QLatin1String("\\r");
649
            else if (u == '\t')
650
                encoded += QLatin1String("\\t");
651
            else if (u == '\n')
652
                encoded += QLatin1String("\\n");
653
            else
654
                encoded += QString::fromLatin1("\\%1")
655 656 657 658 659
                    .arg(c.unicode(), 3, 8, QLatin1Char('0'));
        }
        return encoded;
    }

660 661 662 663
    foreach (const QChar c, str) {
        if (c.isPrint()) {
            encoded += c;
        } else if (WatchHandler::unprintableBase() == 8) {
664
            encoded += QString::fromLatin1("\\%1")
665 666
                .arg(c.unicode(), 3, 8, QLatin1Char('0'));
        } else {
667
            encoded += QString::fromLatin1("\\u%1")
668 669 670 671 672 673
                .arg(c.unicode(), 4, 16, QLatin1Char('0'));
        }
    }
    return encoded;
}

674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
static QString translate(const QString &str)
{
    if (str.startsWith(QLatin1Char('<'))) {
        if (str == QLatin1String("<Edit>"))
            return WatchHandler::tr("<Edit>");
        if (str == QLatin1String("<empty>"))
            return WatchHandler::tr("<empty>");
        if (str == QLatin1String("<uninitialized>"))
            return WatchHandler::tr("<uninitialized>");
        if (str == QLatin1String("<invalid>"))
            return WatchHandler::tr("<invalid>");
        if (str == QLatin1String("<not accessible>"))
            return WatchHandler::tr("<not accessible>");
        if (str.endsWith(QLatin1String(" items>"))) {
            // '<10 items>' or '<>10 items>' (more than)
            bool ok;
            const bool moreThan = str.at(1) == QLatin1Char('>');
            const int numberPos = moreThan ? 2 : 1;
            const int len = str.indexOf(QLatin1Char(' ')) - numberPos;
            const int size = str.mid(numberPos, len).toInt(&ok);
            QTC_ASSERT(ok, qWarning("WatchHandler: Invalid item count '%s'",
695
                qPrintable(str)));
696 697 698 699 700 701 702 703
            return moreThan ?
                     WatchHandler::tr("<more than %n items>", 0, size) :
                     WatchHandler::tr("<%n items>", 0, size);
        }
    }
    return quoteUnprintable(str);
}

704
QString WatchModel::formattedValue(const WatchData &data) const
705
{
706 707
    const QString &value = data.value;

708 709 710 711 712 713 714 715
    if (data.type == "bool") {
        if (value == QLatin1String("0"))
            return QLatin1String("false");
        if (value == QLatin1String("1"))
            return QLatin1String("true");
        return value;
    }

hjk's avatar
hjk committed
716
    const int format = itemFormat(data);
717

hjk's avatar
hjk committed
718 719 720 721 722 723 724 725 726 727 728
    // Append quoted, printable character also for decimal.
    if (data.type.endsWith("char") || data.type.endsWith("QChar")) {
        bool ok;
        const int code = value.toInt(&ok);
        return ok ? reformatCharacter(code, format) : value;
    }

    if (format == HexadecimalIntegerFormat
            || format == DecimalIntegerFormat
            || format == OctalIntegerFormat
            || format == BinaryIntegerFormat) {
729 730 731
        bool isSigned = value.startsWith(QLatin1Char('-'));
        quint64 raw = isSigned ? quint64(value.toLongLong()): value.toULongLong();
        return reformatInteger(raw, format, data.size, isSigned);
732
    }
733

hjk's avatar
hjk committed
734 735 736 737 738 739 740 741 742 743
    if (format == ScientificFloatFormat) {
        double d = value.toDouble();
        return QString::number(d, 'e');
    }

    if (format == CompactFloatFormat) {
        double d = value.toDouble();
        return QString::number(d, 'g');
    }

hjk's avatar
hjk committed
744
    if (data.type == "va_list")
745
        return value;
hjk's avatar
hjk committed
746

747
    if (!isPointerType(data.type) && !data.isVTablePointer()) {
748
        bool ok = false;
749
        qulonglong integer = value.toULongLong(&ok, 0);
750 751
        if (ok) {
            const int format = itemFormat(data);
752
            return reformatInteger(integer, format, data.size, false);
753
        }
754
    }
755

756 757 758 759 760 761 762 763
    if (data.elided) {
        QString v = value;
        v.chop(1);
        QString len = data.elided > 0 ? QString::number(data.elided)
                                      : QLatin1String("unknown length");
        return v + QLatin1String("\"... (") + len  + QLatin1Char(')');
    }

764
    return translate(value);
765 766
}

767 768 769 770 771 772 773 774 775
// Get a pointer address from pointer values reported by the debugger.
// Fix CDB formatting of pointers "0x00000000`000003fd class foo *", or
// "0x00000000`000003fd "Hallo"", or check gdb formatting of characters.
static inline quint64 pointerValue(QString data)
{
    const int blankPos = data.indexOf(QLatin1Char(' '));
    if (blankPos != -1)
        data.truncate(blankPos);
    data.remove(QLatin1Char('`'));
776
    return data.toULongLong(0, 0);
777 778
}

779 780 781 782 783 784 785 786 787
// Return the type used for editing
static inline int editType(const WatchData &d)
{
    if (d.type == "bool")
        return QVariant::Bool;
    if (isIntType(d.type))
        return d.type.contains('u') ? QVariant::ULongLong : QVariant::LongLong;
    if (isFloatType(d.type))
        return QVariant::Double;
788 789 790
    // Check for pointers using hex values (0xAD00 "Hallo")
    if (isPointerType(d.type) && d.value.startsWith(QLatin1String("0x")))
        return QVariant::ULongLong;
791 792 793 794 795 796 797 798 799 800
   return QVariant::String;
}

// Convert to editable (see above)
static inline QVariant editValue(const WatchData &d)
{
    switch (editType(d)) {
    case QVariant::Bool:
        return d.value != QLatin1String("0") && d.value != QLatin1String("false");
    case QVariant::ULongLong:
801 802
        if (isPointerType(d.type)) // Fix pointer values (0xAD00 "Hallo" -> 0xAD00)
            return QVariant(pointerValue(d.value));
803 804 805 806 807 808 809 810
        return QVariant(d.value.toULongLong());
    case QVariant::LongLong:
        return QVariant(d.value.toLongLong());
    case QVariant::Double:
        return QVariant(d.value.toDouble());
    default:
        break;
    }
811 812
    // Some string value: '0x434 "Hallo"':
    // Remove quotes and replace newlines, which will cause line edit troubles.
hjk's avatar
hjk committed
813
    QString stringValue = d.value;
814 815 816 817 818 819 820 821
    if (stringValue.endsWith(QLatin1Char('"'))) {
        const int leadingDoubleQuote = stringValue.indexOf(QLatin1Char('"'));
        if (leadingDoubleQuote != stringValue.size() - 1) {
            stringValue.truncate(stringValue.size() - 1);
            stringValue.remove(0, leadingDoubleQuote + 1);
            stringValue.replace(QLatin1String("\n"), QLatin1String("\\n"));
        }
    }
822
    return QVariant(translate(stringValue));
823 824
}

hjk's avatar
hjk committed
825
bool WatchModel::canFetchMore(const QModelIndex &idx) const
hjk's avatar
hjk committed
826
{
hjk's avatar
hjk committed
827 828 829
    if (!idx.isValid())
        return false;
    WatchItem *item = watchItem(idx);
830
    QTC_ASSERT(item, return false);
831 832
    if (!contentIsValid() && !item->isInspect())
        return false;
hjk's avatar
hjk committed
833 834 835
    if (!item->iname.contains('.'))
        return false;
    return !m_fetchTriggered.contains(item->iname);
836 837
}

hjk's avatar
hjk committed
838
void WatchModel::fetchMore(const QModelIndex &idx)
839
{
hjk's avatar
hjk committed
840 841 842
    if (!idx.isValid())
        return; // Triggered by ModelTester.
    WatchItem *item = watchItem(idx);
843 844
    QTC_ASSERT(item, return);
    QTC_ASSERT(!m_fetchTriggered.contains(item->iname), return);
hjk's avatar
hjk committed
845
    m_expandedINames.insert(item->iname);
846 847 848 849
    m_fetchTriggered.insert(item->iname);
    if (item->children.isEmpty()) {
        WatchData data = *item;
        data.setChildrenNeeded();
850 851 852
        WatchUpdateFlags flags;
        flags.tryIncremental = true;
        engine()->updateWatchData(data, flags);
853 854 855 856 857 858 859 860 861 862
    }
}

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());
863 864
    if (row >= item->children.size())
        return QModelIndex();
865 866 867 868 869 870 871 872 873
    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);
hjk's avatar
hjk committed
874 875
    const WatchItem *parent = item->parent;
    if (!parent || parent == m_root)
876 877
        return QModelIndex();

hjk's avatar
hjk committed
878
    const WatchItem *grandparent = parent->parent;
879 880 881
    if (!grandparent)
        return QModelIndex();

882 883
    const WatchItems &uncles = grandparent->children;
    for (int i = 0, n = uncles.size(); i < n; ++i)
hjk's avatar
hjk committed
884 885
        if (uncles.at(i) == parent)
            return createIndex(i, 0, (void*) parent);
886 887

    return QModelIndex();
hjk's avatar
hjk committed
888 889 890 891
}

int WatchModel::rowCount(const QModelIndex &idx) const
{
hjk's avatar
hjk committed
892 893
    if (!idx.isValid())
        return m_root->children.size();
hjk's avatar
hjk committed
894 895 896 897 898
    if (idx.column() > 0)
        return 0;
    return watchItem(idx)->children.size();
}

899
int WatchModel::columnCount(const QModelIndex &idx) const
hjk's avatar
hjk committed
900
{
901
    Q_UNUSED(idx)
902 903 904 905 906 907 908 909 910 911 912
    return 3;
}

bool WatchModel::hasChildren(const QModelIndex &parent) const
{
    WatchItem *item = watchItem(parent);
    return !item || item->hasChildren;
}

WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
{
hjk's avatar
hjk committed
913
    WatchItem *item = idx.isValid()
914
        ? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
hjk's avatar
hjk committed
915 916
    CHECK(checkItem(item));
    return item;
917 918 919 920
}

QModelIndex WatchModel::watchIndex(const WatchItem *item) const
{
hjk's avatar
hjk committed
921
    CHECK(checkItem(item));
922 923 924
    return watchIndexHelper(item, m_root, QModelIndex());
}

925
QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle,
926 927 928 929 930 931 932 933 934 935
    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;
hjk's avatar
hjk committed
936
    }
937
    return QModelIndex();
hjk's avatar
hjk committed
938 939
}

940
void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
941 942 943 944 945 946 947 948 949 950 951
{
    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));
}

952 953 954 955 956 957 958 959
void WatchModel::invalidateAll(const QModelIndex &parentIndex)
{
    QModelIndex idx1 = index(0, 0, parentIndex);
    QModelIndex idx2 = index(rowCount(parentIndex) - 1, columnCount(parentIndex) - 1, parentIndex);
    if (idx1.isValid() && idx2.isValid())
        emit dataChanged(idx1, idx2);
}

960
void WatchModel::resetValueCacheRecursively(WatchItem *item)
961
{
962
    m_valueCache[item->iname] = item->value;
963 964
    const WatchItems &items = item->children;
    for (int i = items.size(); --i >= 0; )
965
        resetValueCacheRecursively(items.at(i));
966 967
}

968 969
// Truncate value for item view, maintaining quotes.
static QString truncateValue(QString v)
970 971 972 973 974 975 976 977 978 979
{
    enum { maxLength = 512 };
    if (v.size() < maxLength)
        return v;
    const bool isQuoted = v.endsWith(QLatin1Char('"')); // check for 'char* "Hallo"'
    v.truncate(maxLength);
    v += isQuoted ? QLatin1String("...\"") : QLatin1String("...");
    return v;
}

980 981
int WatchModel::itemFormat(const WatchData &data) const
{
hjk's avatar
hjk committed
982 983
    const int individualFormat = theIndividualFormats.value(data.iname, AutomaticFormat);
    if (individualFormat != AutomaticFormat)
984
        return individualFormat;
hjk's avatar
hjk committed
985
    return theTypeFormats.value(stripForFormat(data.type), AutomaticFormat);
986 987
}

988 989
bool WatchModel::contentIsValid() const
{
hjk's avatar
hjk committed
990
    // FIXME:
991
    // inspector doesn't follow normal beginCycle()/endCycle()
hjk's avatar
hjk committed
992 993
    //if (m_type == InspectWatch)
    //    return true;
994 995 996
    return m_handler->m_contentsValid;
}

hjk's avatar
hjk committed
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
#if USE_EXPENSIVE_CHECKS
void WatchModel::checkTree()
{
    QSet<QByteArray> inames;
    checkTree(m_root, &inames);
    QSet<QByteArray> current = m_cache.keys().toSet();
    Q_ASSERT(inames == current);
}

void WatchModel::checkTree(WatchItem *item, QSet<QByteArray> *inames)
{
    checkItem(item);
    inames->insert(item->iname);
    for (int i = 0, n = item->children.size(); i != n; ++i)
        checkTree(item->children.at(i), inames);
}

void WatchModel::checkItem(const WatchItem *item) const
{
    Q_ASSERT(item->children.size() < 1000 * 1000);
    Q_ASSERT(m_cache2.contains(item));
    Q_ASSERT(m_cache2.value(item) == item->iname);
    Q_ASSERT(m_cache.value(item->iname) == item);
}
#endif

static QString expression(const WatchItem *item)
1024 1025
{
    if (!item->exp.isEmpty())
1026
         return QString::fromLatin1(item->exp);
hjk's avatar
hjk committed
1027
    if (item->address && !item->type.isEmpty()) {
1028
        return QString::fromLatin1("*(%1*)%2").
hjk's avatar
hjk committed
1029 1030 1031 1032
                arg(QLatin1String(item->type), QLatin1String(item->hexAddress()));
    }
    if (const WatchItem *parent = item->parent) {
        if (!parent->exp.isEmpty())
1033
           return QString::fromLatin1("(%1).%2")
hjk's avatar
hjk committed
1034 1035 1036
            .arg(QString::fromLatin1(parent->exp), item->name);
    }
    return QString();
1037 1038
}

1039
QString WatchModel::displayName(const WatchItem *item) const
1040 1041
{
    QString result;
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
    if (item->parent == m_watchRoot && item->name.isEmpty())
        result = tr("<Edit>");
    else if (item->parent == m_returnRoot)
        result = tr("returned value");
    else if (item->name == QLatin1String("*"))
        result = QLatin1Char('*') + item->parent->name;
    else
        result = removeNamespaces(item->name);
    return result;
}

QString WatchModel::displayValue(const WatchData &data) const
{
    QString result = removeNamespaces(truncateValue(formattedValue(data)));
    if (result.isEmpty() && data.address)
        result += QString::fromLatin1("@0x" + QByteArray::number(data.address, 16));
//    if (data.origaddr)
//        result += QString::fromLatin1(" (0x" + QByteArray::number(data.origaddr, 16) + ')');
    return result;
}

QString WatchModel::displayType(const WatchData &data) const
{
    QString result = data.displayedType.isEmpty()
        ? niceTypeHelper(data.type)
        : data.displayedType;
    if (data.bitsize)
        result += QString::fromLatin1(":%1").arg(data.bitsize);
    result.remove(QLatin1Char('\''));
    result = removeNamespaces(result);
1072 1073 1074
    return result;
}

hjk's avatar
hjk committed
1075 1076
QVariant WatchModel::data(const QModelIndex &idx, int role) const
{
hjk's avatar
hjk committed
1077 1078 1079
    if (!idx.isValid())
        return QVariant(); // Triggered by ModelTester.

1080 1081
    const WatchItem *item = watchItem(idx);
    const WatchItem &data = *item;
con's avatar
con committed
1082 1083

    switch (role) {
hjk's avatar
hjk committed
1084
        case LocalsEditTypeRole