watchhandler.cpp 67.8 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
85
86
87
88
89
90
91
92
93
94
95
96
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;
97
98
        if (c == '&') // Treat references like the referenced type.
            continue;
99
100
101
102
103
        if (inArray && c >= '0' && c <= '9')
            continue;
        res.append(c);
    }
    return res;
104
105
}

con's avatar
con committed
106
107
////////////////////////////////////////////////////////////////////
//
108
// WatchItem
con's avatar
con committed
109
110
//
////////////////////////////////////////////////////////////////////
111

hjk's avatar
hjk committed
112
113
114
115
// Used to make sure the item cache is notified of construction and
// destruction of items.

class WatchItem;
116
117
typedef QList<WatchItem *> WatchItems;

hjk's avatar
hjk committed
118
119
120
WatchItem *itemConstructor(WatchModel *model, const QByteArray &iname);
void itemDestructor(WatchModel *model, WatchItem *item);

121
122
123
class WatchItem : public WatchData
{
public:
124
125
    WatchItem *parent; // Not owned.
    WatchItems children; // Not owned. Handled via itemDestructor().
126

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

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

///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
137
//
138
// WatchModel
hjk's avatar
hjk committed
139
//
140
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
141

142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
class SeparateViewWidget : public QTabWidget
{
    Q_OBJECT

public:
    SeparateViewWidget(QWidget *parent) : QTabWidget(parent)
    {
        setTabsClosable(true);
        connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeTab(int)));
        setWindowFlags(windowFlags() | Qt::Window);
        setWindowTitle(WatchHandler::tr("Debugger - Qt Creator"));
    }

public slots:
    void closeTab(int index)
    {
        removeTab(index);
    }
};

hjk's avatar
hjk committed
162
class WatchModel : public QAbstractItemModel
hjk's avatar
hjk committed
163
{
hjk's avatar
hjk committed
164
    Q_OBJECT
hjk's avatar
hjk committed
165

hjk's avatar
hjk committed
166
167
168
169
170
171
172
173
174
175
176
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
177
178
179
    static QString nameForFormat(int format);
    TypeFormatList typeFormatList(const WatchData &value) const;

hjk's avatar
hjk committed
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
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());
197
    void resetValueCacheRecursively(WatchItem *item);
198

hjk's avatar
hjk committed
199
200
201
202
203
204
205
206
207
    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;

208
    void insertDataItem(const WatchData &data, bool destructive);
hjk's avatar
hjk committed
209
210
    Q_SLOT void reinsertAllData();
    void reinsertAllDataHelper(WatchItem *item, QList<WatchData> *data);
hjk's avatar
hjk committed
211
    bool ancestorChanged(const QSet<QByteArray> &parentINames, WatchItem *item) const;
hjk's avatar
hjk committed
212
213
    void insertBulkData(const QList<WatchData> &data);
    QString displayForAutoTest(const QByteArray &iname) const;
214
    void reinitialize(bool includeInspectData = false);
hjk's avatar
hjk committed
215
216
    void destroyItem(WatchItem *item); // With model notification.
    void destroyChildren(WatchItem *item); // With model notification.
217
    void destroyHelper(const WatchItems &items); // Without model notification.
hjk's avatar
hjk committed
218
219
220
221
222
223
224
225
226
227
228
229
230
    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;
231
232
    QString displayName(const WatchItem *item) const;
    QString displayValue(const WatchData &data) const;
hjk's avatar
hjk committed
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
    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
252
253
254
    TypeFormatList builtinTypeFormatList(const WatchData &data) const;
    QStringList dumperTypeFormatList(const WatchData &data) const;
    DumperTypeFormats m_reportedTypeFormats;
hjk's avatar
hjk committed
255
256
257
258
259
260
261
262
263
264

    // QWidgets and QProcesses taking care of special displays.
    typedef QMap<QByteArray, QPointer<QObject> > EditHandlers;
    EditHandlers m_editHandlers;

    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
265
266
    typedef QHash<QByteArray, WatchItem *> Cache;
    Cache m_cache;
267
268
    typedef QHash<QByteArray, QString> ValueCache;
    ValueCache m_valueCache;
hjk's avatar
hjk committed
269
270
271
272
273
274
275
276
277
278
279
280

    #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)
{
281
    setObjectName(QLatin1String("WatchModel"));
hjk's avatar
hjk committed
282
283
284
285
286
287
288
289
290
291
292
293
294
295
    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
296
297
}

298
299
WatchModel::~WatchModel()
{
hjk's avatar
hjk committed
300
301
302
303
    CHECK(checkItem(m_root));
    destroyChildren(m_root);
    itemDestructor(this, m_root);
    QTC_CHECK(m_cache.isEmpty());
304
305
}

hjk's avatar
hjk committed
306
WatchItem *itemConstructor(WatchModel *model, const QByteArray &iname)
hjk's avatar
hjk committed
307
{
hjk's avatar
hjk committed
308
309
310
311
312
313
314
    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
315
316
}

hjk's avatar
hjk committed
317
void itemDestructor(WatchModel *model, WatchItem *item)
hjk's avatar
hjk committed
318
{
hjk's avatar
hjk committed
319
320
321
322
323
    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
324
325
}

hjk's avatar
hjk committed
326
WatchItem *WatchModel::createItem(const QByteArray &iname, const QString &name, WatchItem *parent)
327
{
hjk's avatar
hjk committed
328
329
330
331
332
333
334
335
    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;
336
337
}

338
void WatchModel::reinitialize(bool includeInspectData)
339
{
hjk's avatar
hjk committed
340
341
342
343
344
345
346
    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);
347
348
349
350
    if (includeInspectData) {
        destroyChildren(m_inspectorRoot);
        QTC_CHECK(m_cache.size() == 6);
    }
hjk's avatar
hjk committed
351
    CHECK(checkTree());
352
353
}

hjk's avatar
hjk committed
354
void WatchModel::emitAllChanged()
355
{
hjk's avatar
hjk committed
356
    emit layoutChanged();
357
}
358

359
360
361
362
DebuggerEngine *WatchModel::engine() const
{
    return m_handler->m_engine;
}
363
364
365
366
367
368
369
370
371
372
373

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
374
        << (item->parent ? item->parent->iname : "<none>");
375
376
377
378
    foreach (WatchItem *child, item->children)
        dumpHelper(child);
}

379
void WatchModel::destroyHelper(const WatchItems &items)
hjk's avatar
hjk committed
380
{
hjk's avatar
hjk committed
381
382
383
384
    for (int i = items.size(); --i >= 0; ) {
        WatchItem *item = items.at(i);
        destroyHelper(item->children);
        itemDestructor(this, item);
385
    }
hjk's avatar
hjk committed
386
387
}

388
void WatchModel::destroyItem(WatchItem *item)
con's avatar
con committed
389
{
hjk's avatar
hjk committed
390
391
392
393
394
395
    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.
396
    WatchItem *parent = item->parent;
hjk's avatar
hjk committed
397
398
399
    QTC_ASSERT(parent, return);
    QModelIndex parentIndex = watchIndex(parent);
    const int i = parent->children.indexOf(item);
400
    //MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n);
hjk's avatar
hjk committed
401
402
    beginRemoveRows(parentIndex, i, i);
    parent->children.removeAt(i);
403
    endRemoveRows();
hjk's avatar
hjk committed
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418

    // 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;

419
    WatchItems items = item->children;
hjk's avatar
hjk committed
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452

    // 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));
453
454
}

455
456
457
458
void WatchModel::reinsertAllData()
{
    QList<WatchData> list;
    reinsertAllDataHelper(m_root, &list);
459
    reinitialize(true);
hjk's avatar
hjk committed
460
    insertBulkData(list);
461
462
463
464
465
}

void WatchModel::reinsertAllDataHelper(WatchItem *item, QList<WatchData> *data)
{
    data->append(*item);
hjk's avatar
hjk committed
466
    data->back().setAllUnneeded();
467
468
469
470
    foreach (WatchItem *child, item->children)
        reinsertAllDataHelper(child, data);
}

471
static QByteArray parentName(const QByteArray &iname)
472
{
hjk's avatar
hjk committed
473
474
    const int pos = iname.lastIndexOf('.');
    return pos == -1 ? QByteArray() : iname.left(pos);
con's avatar
con committed
475
476
}

477
static QString niceTypeHelper(const QByteArray &typeIn)
478
{
479
480
481
    typedef QMap<QByteArray, QString> Cache;
    static Cache cache;
    const Cache::const_iterator it = cache.constFind(typeIn);
482
    if (it != cache.constEnd())
483
        return it.value();
484
    const QString simplified = simplifyType(QLatin1String(typeIn));
485
486
    cache.insert(typeIn, simplified); // For simplicity, also cache unmodified types
    return simplified;
con's avatar
con committed
487
488
}

489
QString WatchModel::removeNamespaces(QString str) const
490
{
hjk's avatar
hjk committed
491
    if (!debuggerCore()->boolSetting(ShowStdNamespace))
492
        str.remove(QLatin1String("std::"));
hjk's avatar
hjk committed
493
    if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
494
        const QString qtNamespace = QString::fromLatin1(engine()->qtNamespace());
495
        if (!qtNamespace.isEmpty())
496
497
498
499
500
            str.remove(qtNamespace);
    }
    return str;
}

hjk's avatar
hjk committed
501
static int formatToIntegerBase(int format)
502
503
{
    switch (format) {
hjk's avatar
hjk committed
504
        case HexadecimalIntegerFormat:
hjk's avatar
hjk committed
505
            return 16;
hjk's avatar
hjk committed
506
        case BinaryIntegerFormat:
hjk's avatar
hjk committed
507
            return 2;
hjk's avatar
hjk committed
508
        case OctalIntegerFormat:
hjk's avatar
hjk committed
509
            return 8;
510
511
512
513
    }
    return 10;
}

514
515
516
template <class IntType> QString reformatInteger(IntType value, int format)
{
    switch (format) {
hjk's avatar
hjk committed
517
        case HexadecimalIntegerFormat:
518
            return QLatin1String("(hex) ") + QString::number(value, 16);
hjk's avatar
hjk committed
519
        case BinaryIntegerFormat:
520
            return QLatin1String("(bin) ") + QString::number(value, 2);
hjk's avatar
hjk committed
521
        case OctalIntegerFormat:
522
            return QLatin1String("(oct) ") + QString::number(value, 8);
523
    }
524
525
526
527
528
529
    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.
530
    if (format != AutomaticFormat && format != DecimalIntegerFormat)
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
        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);
    }
554
555
}

556
// Format printable (char-type) characters
557
static QString reformatCharacter(int code, int format)
558
{
559
    const QString codeS = reformatInteger(code, format, 1, true);
560
    if (code < 0) // Append unsigned value.
561
        return codeS + QLatin1String(" / ") + reformatInteger(256 + code, format, 1, false);
562
    const QChar c = QChar(uint(code));
563
564
    if (c.isPrint())
        return codeS + QLatin1String(" '") + c + QLatin1Char('\'');
565
566
567
568
569
570
571
572
573
574
575
576
577
    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;
}

578
579
580
581
static QString quoteUnprintable(const QString &str)
{
    if (WatchHandler::unprintableBase() == 0)
        return str;
582

583
    QString encoded;
584
585
586
    if (WatchHandler::unprintableBase() == -1) {
        foreach (const QChar c, str) {
            int u = c.unicode();
587
            if (c.isPrint())
588
589
                encoded += c;
            else if (u == '\r')
590
                encoded += QLatin1String("\\r");
591
            else if (u == '\t')
592
                encoded += QLatin1String("\\t");
593
            else if (u == '\n')
594
                encoded += QLatin1String("\\n");
595
            else
596
                encoded += QString::fromLatin1("\\%1")
597
598
599
600
601
                    .arg(c.unicode(), 3, 8, QLatin1Char('0'));
        }
        return encoded;
    }

602
603
604
605
    foreach (const QChar c, str) {
        if (c.isPrint()) {
            encoded += c;
        } else if (WatchHandler::unprintableBase() == 8) {
606
            encoded += QString::fromLatin1("\\%1")
607
608
                .arg(c.unicode(), 3, 8, QLatin1Char('0'));
        } else {
609
            encoded += QString::fromLatin1("\\u%1")
610
611
612
613
614
615
                .arg(c.unicode(), 4, 16, QLatin1Char('0'));
        }
    }
    return encoded;
}

616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
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'",
637
                qPrintable(str)));
638
639
640
641
642
643
644
645
            return moreThan ?
                     WatchHandler::tr("<more than %n items>", 0, size) :
                     WatchHandler::tr("<%n items>", 0, size);
        }
    }
    return quoteUnprintable(str);
}

646
QString WatchModel::formattedValue(const WatchData &data) const
647
{
648
649
    const QString &value = data.value;

650
651
652
653
654
655
656
657
    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
658
    const int format = itemFormat(data);
659

hjk's avatar
hjk committed
660
661
662
663
664
665
666
667
668
669
670
    // 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) {
671
672
673
        bool isSigned = value.startsWith(QLatin1Char('-'));
        quint64 raw = isSigned ? quint64(value.toLongLong()): value.toULongLong();
        return reformatInteger(raw, format, data.size, isSigned);
674
    }
675

hjk's avatar
hjk committed
676
677
678
679
680
681
682
683
684
685
    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
686
    if (data.type == "va_list")
687
        return value;
hjk's avatar
hjk committed
688

689
    if (!isPointerType(data.type) && !data.isVTablePointer()) {
690
        bool ok = false;
691
        qulonglong integer = value.toULongLong(&ok, 0);
692
693
        if (ok) {
            const int format = itemFormat(data);
694
            return reformatInteger(integer, format, data.size, false);
695
        }
696
    }
697

698
699
700
701
702
703
704
705
    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(')');
    }

706
    return translate(value);
707
708
}

709
710
711
712
713
714
715
716
717
// 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('`'));
718
    return data.toULongLong(0, 0);
719
720
}

721
722
723
724
725
726
727
728
729
// 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;
730
731
732
    // Check for pointers using hex values (0xAD00 "Hallo")
    if (isPointerType(d.type) && d.value.startsWith(QLatin1String("0x")))
        return QVariant::ULongLong;
733
734
735
736
737
738
739
740
741
742
   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:
743
744
        if (isPointerType(d.type)) // Fix pointer values (0xAD00 "Hallo" -> 0xAD00)
            return QVariant(pointerValue(d.value));
745
746
747
748
749
750
751
752
        return QVariant(d.value.toULongLong());
    case QVariant::LongLong:
        return QVariant(d.value.toLongLong());
    case QVariant::Double:
        return QVariant(d.value.toDouble());
    default:
        break;
    }
753
754
    // Some string value: '0x434 "Hallo"':
    // Remove quotes and replace newlines, which will cause line edit troubles.
hjk's avatar
hjk committed
755
    QString stringValue = d.value;
756
757
758
759
760
761
762
763
    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"));
        }
    }
764
    return QVariant(translate(stringValue));
765
766
}

hjk's avatar
hjk committed
767
bool WatchModel::canFetchMore(const QModelIndex &idx) const
hjk's avatar
hjk committed
768
{
hjk's avatar
hjk committed
769
770
771
    if (!idx.isValid())
        return false;
    WatchItem *item = watchItem(idx);
772
    QTC_ASSERT(item, return false);
773
774
    if (!contentIsValid() && !item->isInspect())
        return false;
hjk's avatar
hjk committed
775
776
777
    if (!item->iname.contains('.'))
        return false;
    return !m_fetchTriggered.contains(item->iname);
778
779
}

hjk's avatar
hjk committed
780
void WatchModel::fetchMore(const QModelIndex &idx)
781
{
hjk's avatar
hjk committed
782
783
784
    if (!idx.isValid())
        return; // Triggered by ModelTester.
    WatchItem *item = watchItem(idx);
785
786
    QTC_ASSERT(item, return);
    QTC_ASSERT(!m_fetchTriggered.contains(item->iname), return);
hjk's avatar
hjk committed
787
    m_expandedINames.insert(item->iname);
788
789
790
791
    m_fetchTriggered.insert(item->iname);
    if (item->children.isEmpty()) {
        WatchData data = *item;
        data.setChildrenNeeded();
792
793
794
        WatchUpdateFlags flags;
        flags.tryIncremental = true;
        engine()->updateWatchData(data, flags);
795
796
797
798
799
800
801
802
803
804
    }
}

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());
805
806
    if (row >= item->children.size())
        return QModelIndex();
807
808
809
810
811
812
813
814
815
    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
816
817
    const WatchItem *parent = item->parent;
    if (!parent || parent == m_root)
818
819
        return QModelIndex();

hjk's avatar
hjk committed
820
    const WatchItem *grandparent = parent->parent;
821
822
823
    if (!grandparent)
        return QModelIndex();

824
825
    const WatchItems &uncles = grandparent->children;
    for (int i = 0, n = uncles.size(); i < n; ++i)
hjk's avatar
hjk committed
826
827
        if (uncles.at(i) == parent)
            return createIndex(i, 0, (void*) parent);
828
829

    return QModelIndex();
hjk's avatar
hjk committed
830
831
832
833
}

int WatchModel::rowCount(const QModelIndex &idx) const
{
hjk's avatar
hjk committed
834
835
    if (!idx.isValid())
        return m_root->children.size();
hjk's avatar
hjk committed
836
837
838
839
840
    if (idx.column() > 0)
        return 0;
    return watchItem(idx)->children.size();
}

841
int WatchModel::columnCount(const QModelIndex &idx) const
hjk's avatar
hjk committed
842
{
843
    Q_UNUSED(idx)
844
845
846
847
848
849
850
851
852
853
854
    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
855
    WatchItem *item = idx.isValid()
856
        ? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
hjk's avatar
hjk committed
857
858
    CHECK(checkItem(item));
    return item;
859
860
861
862
}

QModelIndex WatchModel::watchIndex(const WatchItem *item) const
{
hjk's avatar
hjk committed
863
    CHECK(checkItem(item));
864
865
866
    return watchIndexHelper(item, m_root, QModelIndex());
}

867
QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle,
868
869
870
871
872
873
874
875
876
877
    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
878
    }
879
    return QModelIndex();
hjk's avatar
hjk committed
880
881
}

882
void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
883
884
885
886
887
888
889
890
891
892
893
{
    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));
}

894
895
896
897
898
899
900
901
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);
}

902
void WatchModel::resetValueCacheRecursively(WatchItem *item)
903
{
904
    m_valueCache[item->iname] = item->value;
905
906
    const WatchItems &items = item->children;
    for (int i = items.size(); --i >= 0; )
907
        resetValueCacheRecursively(items.at(i));
908
909
}

910
911
// Truncate value for item view, maintaining quotes.
static QString truncateValue(QString v)
912
913
914
915
916
917
918
919
920
921
{
    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;
}

922
923
int WatchModel::itemFormat(const WatchData &data) const
{
hjk's avatar
hjk committed
924
925
    const int individualFormat = theIndividualFormats.value(data.iname, AutomaticFormat);
    if (individualFormat != AutomaticFormat)
926
        return individualFormat;
hjk's avatar
hjk committed
927
    return theTypeFormats.value(stripForFormat(data.type), AutomaticFormat);
928
929
}

930
931
bool WatchModel::contentIsValid() const
{
hjk's avatar
hjk committed
932
    // FIXME:
933
    // inspector doesn't follow normal beginCycle()/endCycle()
hjk's avatar
hjk committed
934
935
    //if (m_type == InspectWatch)
    //    return true;
936
937
938
    return m_handler->m_contentsValid;
}

hjk's avatar
hjk committed
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
#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)
966
967
{
    if (!item->exp.isEmpty())
968
         return QString::fromLatin1(item->exp);
hjk's avatar
hjk committed
969
    if (item->address && !item->type.isEmpty()) {
970
        return QString::fromLatin1("*(%1*)%2").
hjk's avatar
hjk committed
971
972
973
974
                arg(QLatin1String(item->type), QLatin1String(item->hexAddress()));
    }
    if (const WatchItem *parent = item->parent) {
        if (!parent->exp.isEmpty())
975
           return QString::fromLatin1("(%1).%2")
hjk's avatar
hjk committed
976
977
978
            .arg(QString::fromLatin1(parent->exp), item->name);
    }
    return QString();
979
980
}

981
QString WatchModel::displayName(const WatchItem *item) const
982
983
{
    QString result;
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
    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);
1014
1015
1016
    return result;
}

hjk's avatar
hjk committed
1017
1018
QVariant WatchModel::data(const QModelIndex &idx, int role) const
{
hjk's avatar
hjk committed
1019
1020
1021
    if (!idx.isValid())
        return QVariant(); // Triggered by ModelTester.

1022
1023
    const WatchItem *item = watchItem(idx);
    const WatchItem &data = *item;
con's avatar
con committed
1024
1025

    switch (role) {
hjk's avatar
hjk committed
1026
        case LocalsEditTypeRole:
1027
            return QVariant(editType(data));
hjk's avatar
hjk committed
1028

1029
1030
1031
        case LocalsNameRole:
            return QVariant(data.name);

hjk's avatar
hjk committed
1032
        case LocalsIntegerBaseRole:
1033
1034
            if (isPointerType(data.type)) // Pointers using 0x-convention
                return QVariant(16);
1035
            return QVariant(formatToIntegerBase(itemFormat(data)));
hjk's avatar
hjk committed
1036
1037

        case Qt::EditRole: {
1038
            switch (idx.column()) {
hjk's avatar
hjk committed
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
                case 0:
                    return QVariant(expression(item));
                case 1:
                    return editValue(data);
                case 2:
                    // FIXME:: To be tested: Can debuggers handle those?
                    if (!data.displayedType.isEmpty())
                        return data.displayedType;
                    return QString::fromUtf8(data.type);
            }
        }

1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
        case Qt::DisplayRole: {
            switch (idx.column()) {
                case 0:
                    return displayName(item);
                case 1:
                    return displayValue(data);
                case 2:
                    return displayType(data);
            }
        }
hjk's avatar
hjk committed
1061

1062
        case Qt::ToolTipRole:
hjk's avatar
hjk committed
1063
            return debuggerCore()->boolSetting(UseToolTipsInLocalsView)
1064
                ? data.toToolTip() : QVariant();
con's avatar
con committed
1065
1066
1067
1068

        case Qt::ForegroundRole: {
            static const QVariant red(QColor(200, 0, 0));
            static const QVariant gray(QColor(140, 140, 140));
1069
            if (idx.column() == 1) {
1070
1071
1072
1073
1074
                if (!data.valueEnabled)
                    return gray;
                if (!contentIsValid() && !data.isInspect())
                    return gray;
                if (data.value.isEmpty()) // This might still show 0x...
1075
1076
1077
                    return gray;
                if (data.value != m_valueCache.value(data.iname))
                    return red;
con's avatar
con committed
1078
1079
1080
1081
            }
            break;
        }

1082
1083
        case LocalsExpressionRole:
            return QVariant(expression(item));
hjk's avatar
hjk committed
1084

1085
1086
1087
        case LocalsRawExpressionRole:
            return data.exp;

1088
        case LocalsINameRole:
con's avatar
con committed
1089
1090
            return data.iname;

1091
        case LocalsExpandedRole:
hjk's avatar
hjk committed
1092
            return m_expandedINames.contains(data.iname);
1093

1094
        case LocalsTypeFormatListRole:
hjk's avatar
hjk committed
1095
            return QVariant::fromValue(typeFormatList(data));
1096

1097
        case LocalsTypeRole:
1098
1099
            return removeNamespaces(displayType(data));

1100
        case LocalsRawTypeRole:
1101
1102
            return QString::fromLatin1(data.type);

1103
        case LocalsTypeFormatRole:
hjk's avatar
hjk committed
1104
            return theTypeFormats.value(stripForFormat(data.type), AutomaticFormat);
1105

1106
        case LocalsIndividualFormatRole:
hjk's avatar
hjk committed
1107
            return theIndividualFormats.value(data.iname, AutomaticFormat);
1108

1109
        case LocalsRawValueRole:
1110
            return data.value;
1111

1112
1113
1114