watchhandler.cpp 57.3 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33
#include "watchhandler.h"
34
35

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

hjk's avatar
hjk committed
43
#if USE_WATCH_MODEL_TEST
con's avatar
con committed
44
45
46
#include "modeltest.h"
#endif

hjk's avatar
hjk committed
47
#include <utils/qtcassert.h>
48
#include <utils/savedaction.h>
con's avatar
con committed
49

50
51
#include <cplusplus/CppRewriter.h>

con's avatar
con committed
52
53
#include <QtCore/QDebug>
#include <QtCore/QEvent>
hjk's avatar
hjk committed
54
#include <QtCore/QFile>
55
#include <QtCore/QProcess>
56
#include <QtCore/QTextStream>
hjk's avatar
hjk committed
57
#include <QtCore/QtAlgorithms>
con's avatar
con committed
58
59
60
61
62

#include <QtGui/QLabel>
#include <QtGui/QTextEdit>

#include <ctype.h>
63
#include <utils/qtcassert.h>
hjk's avatar
hjk committed
64

con's avatar
con committed
65

hjk's avatar
hjk committed
66
67
namespace Debugger {
namespace Internal {
con's avatar
con committed
68

69
70
71
72
73
74
// Creates debug output for accesses to the model.
enum { debugModel = 0 };

#define MODEL_DEBUG(s) do { if (debugModel) qDebug() << s; } while (0)
#define MODEL_DEBUGX(s) qDebug() << s

hjk's avatar
hjk committed
75
76
77
QHash<QByteArray, int> WatchHandler::m_watcherNames;
QHash<QByteArray, int> WatchHandler::m_typeFormats;

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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;
        if (inArray && c >= '0' && c <= '9')
            continue;
        res.append(c);
    }
    return res;
98
99
}

100
static int m_unprintableBase = -1;
101
102
103
104
105
106
107
108
109
110
111
112

void WatchHandler::setUnprintableBase(int base)
{
    m_unprintableBase = base;
    emitAllChanged();
}

int WatchHandler::unprintableBase()
{
    return m_unprintableBase;
}

con's avatar
con committed
113
114
////////////////////////////////////////////////////////////////////
//
115
// WatchItem
con's avatar
con committed
116
117
//
////////////////////////////////////////////////////////////////////
118

119
120
121
class WatchItem : public WatchData
{
public:
122
    WatchItem() { parent = 0; }
123

hjk's avatar
hjk committed
124
125
126
127
128
129
    ~WatchItem() {
        if (parent != 0)
            parent->children.removeOne(this);
        qDeleteAll(children);
    }

130
    WatchItem(const WatchData &data) : WatchData(data)
131
        { parent = 0; }
132
133

    void setData(const WatchData &data)
hjk's avatar
hjk committed
134
        { static_cast<WatchData &>(*this) = data; }
135
136
137
138
139

    WatchItem *parent;
    QList<WatchItem *> children;  // fetched children
};

140

141
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
142
//
143
// WatchModel
hjk's avatar
hjk committed
144
//
145
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
146

147
WatchModel::WatchModel(WatchHandler *handler, WatchType type)
Aurindam Jana's avatar
Aurindam Jana committed
148
149
    : QAbstractItemModel(handler), m_generationCounter(0),
      m_handler(handler), m_type(type)
hjk's avatar
hjk committed
150
{
151
152
153
154
    m_root = new WatchItem;
    m_root->hasChildren = 1;
    m_root->state = 0;
    m_root->name = WatchHandler::tr("Root");
155
    m_root->parent = 0;
hjk's avatar
hjk committed
156

157
    switch (m_type) {
158
159
160
161
        case ReturnWatch:
            m_root->iname = "return";
            m_root->name = WatchHandler::tr("Return Value");
            break;
162
        case LocalsWatch:
163
            m_root->iname = "local";
164
            m_root->name = WatchHandler::tr("Locals");
165
166
            break;
        case WatchersWatch:
167
            m_root->iname = "watch";
168
            m_root->name = WatchHandler::tr("Watchers");
169
170
            break;
        case TooltipsWatch:
171
            m_root->iname = "tooltip";
172
            m_root->name = WatchHandler::tr("Tooltip");
173
174
            break;
    }
hjk's avatar
hjk committed
175
176
}

177
178
179
180
181
WatchModel::~WatchModel()
{
    delete m_root;
}

182
WatchItem *WatchModel::rootItem() const
hjk's avatar
hjk committed
183
{
184
    return m_root;
hjk's avatar
hjk committed
185
186
}

187
void WatchModel::reinitialize()
hjk's avatar
hjk committed
188
{
189
    int n = m_root->children.size();
190
191
    if (n == 0)
        return;
192
193
    //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << m_root->iname);
    QModelIndex index = watchIndex(m_root);
194
    beginRemoveRows(index, 0, n - 1);
195
196
    qDeleteAll(m_root->children);
    m_root->children.clear();
197
    endRemoveRows();
hjk's avatar
hjk committed
198
199
}

200
201
202
203
204
void WatchModel::emitAllChanged()
{
    emit layoutChanged();
}

205
void WatchModel::beginCycle(bool fullCycle)
206
{
207
208
209
    if (fullCycle)
        m_generationCounter++;

210
    //emit enableUpdates(false);
211
212
213
214
215
}

void WatchModel::endCycle()
{
    removeOutdated();
216
    m_fetchTriggered.clear();
217
    //emit enableUpdates(true);
218
}
219

220
221
222
223
DebuggerEngine *WatchModel::engine() const
{
    return m_handler->m_engine;
}
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240

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);
}

241
void WatchModel::removeOutdated()
hjk's avatar
hjk committed
242
{
243
    foreach (WatchItem *child, m_root->children)
244
245
        removeOutdatedHelper(child);
#if DEBUG_MODEL
hjk's avatar
hjk committed
246
247
#if USE_WATCH_MODEL_TEST
    (void) new ModelTest(this, this);
248
249
#endif
#endif
hjk's avatar
hjk committed
250
251
}

252
void WatchModel::removeOutdatedHelper(WatchItem *item)
hjk's avatar
hjk committed
253
{
254
    if (item->generation < m_generationCounter) {
255
        destroyItem(item);
256
    } else {
257
258
        foreach (WatchItem *child, item->children)
            removeOutdatedHelper(child);
259
    }
hjk's avatar
hjk committed
260
261
}

262
void WatchModel::destroyItem(WatchItem *item)
con's avatar
con committed
263
{
264
265
266
267
268
269
270
    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();
271
    delete item;
272
273
}

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
void WatchModel::reinsertAllData()
{
    QList<WatchData> list;
    reinsertAllDataHelper(m_root, &list);
    reinitialize();
    foreach (WatchItem data, list) {
        data.setAllUnneeded();
        insertData(data);
    }
    layoutChanged();
}

void WatchModel::reinsertAllDataHelper(WatchItem *item, QList<WatchData> *data)
{
    data->append(*item);
    foreach (WatchItem *child, item->children)
        reinsertAllDataHelper(child, data);
}

293
static QByteArray parentName(const QByteArray &iname)
294
{
295
    int pos = iname.lastIndexOf('.');
con's avatar
con committed
296
    if (pos == -1)
297
        return QByteArray();
con's avatar
con committed
298
299
300
    return iname.left(pos);
}

301
static QString niceTypeHelper(const QByteArray &typeIn)
302
{
303
304
305
    typedef QMap<QByteArray, QString> Cache;
    static Cache cache;
    const Cache::const_iterator it = cache.constFind(typeIn);
306
    if (it != cache.constEnd())
307
        return it.value();
308
    const QString simplified = CPlusPlus::simplifySTLType(QLatin1String(typeIn));
309
310
    cache.insert(typeIn, simplified); // For simplicity, also cache unmodified types
    return simplified;
con's avatar
con committed
311
312
}

313
QString WatchModel::removeNamespaces(QString str) const
314
{
hjk's avatar
hjk committed
315
    if (!debuggerCore()->boolSetting(ShowStdNamespace))
316
        str.remove(QLatin1String("std::"));
hjk's avatar
hjk committed
317
    if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
318
        const QString qtNamespace = QString::fromLatin1(engine()->qtNamespace());
319
        if (!qtNamespace.isEmpty())
320
321
322
323
324
            str.remove(qtNamespace);
    }
    return str;
}

325
QString WatchModel::removeInitialNamespace(QString str) const
326
327
328
329
330
{
    if (str.startsWith(QLatin1String("std::"))
            && debuggerCore()->boolSetting(ShowStdNamespace))
        str = str.mid(5);
    if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
331
        const QByteArray qtNamespace = engine()->qtNamespace();
332
        if (!qtNamespace.isEmpty() && str.startsWith(QLatin1String(qtNamespace)))
333
            str = str.mid(qtNamespace.size());
334
    }
335
336
337
338
339
    return str;
}

QString WatchModel::displayType(const WatchData &data) const
{
340
    QString base = data.displayedType.isEmpty()
341
342
        ? niceTypeHelper(data.type)
        : data.displayedType;
343
    if (data.bitsize)
344
345
        base += QString::fromLatin1(":%1").arg(data.bitsize);
    base.remove(QLatin1Char('\''));
346
    return base;
347
348
}

hjk's avatar
hjk committed
349
static int formatToIntegerBase(int format)
350
351
{
    switch (format) {
hjk's avatar
hjk committed
352
353
354
355
356
357
        case HexadecimalFormat:
            return 16;
        case BinaryFormat:
            return 2;
        case OctalFormat:
            return 8;
358
359
360
361
    }
    return 10;
}

362
363
364
template <class IntType> QString reformatInteger(IntType value, int format)
{
    switch (format) {
hjk's avatar
hjk committed
365
        case HexadecimalFormat:
366
            return QLatin1String("(hex) ") + QString::number(value, 16);
hjk's avatar
hjk committed
367
        case BinaryFormat:
368
            return QLatin1String("(bin) ") + QString::number(value, 2);
hjk's avatar
hjk committed
369
        case OctalFormat:
370
            return QLatin1String("(oct) ") + QString::number(value, 8);
371
372
373
374
    }
    return QString::number(value); // not reached
}

375
// Format printable (char-type) characters
376
static QString reformatCharacter(int code, int format)
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
{
    const QString codeS = reformatInteger(code, format);
    if (code < 0) // Append unsigned value.
        return codeS + QLatin1String(" / ") + reformatInteger(256 + code, format);
    if (code >= 32 && code < 128)
        return codeS + QLatin1String(" '") + QChar(code) + QLatin1Char('\'');
    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;
}

396
397
398
399
static QString quoteUnprintable(const QString &str)
{
    if (WatchHandler::unprintableBase() == 0)
        return str;
400

401
    QString encoded;
402
403
404
405
406
407
    if (WatchHandler::unprintableBase() == -1) {
        foreach (const QChar c, str) {
            int u = c.unicode();
            if (u >= 32 && u < 127)
                encoded += c;
            else if (u == '\r')
408
                encoded += QLatin1String("\\r");
409
            else if (u == '\t')
410
                encoded += QLatin1String("\\t");
411
            else if (u == '\n')
412
                encoded += QLatin1String("\\n");
413
            else
414
                encoded += QString::fromLatin1("\\%1")
415
416
417
418
419
                    .arg(c.unicode(), 3, 8, QLatin1Char('0'));
        }
        return encoded;
    }

420
421
422
423
    foreach (const QChar c, str) {
        if (c.isPrint()) {
            encoded += c;
        } else if (WatchHandler::unprintableBase() == 8) {
424
            encoded += QString::fromLatin1("\\%1")
425
426
                .arg(c.unicode(), 3, 8, QLatin1Char('0'));
        } else {
427
            encoded += QString::fromLatin1("\\u%1")
428
429
430
431
432
433
                .arg(c.unicode(), 4, 16, QLatin1Char('0'));
        }
    }
    return encoded;
}

434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
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'",
                qPrintable(str)))
            return moreThan ?
                     WatchHandler::tr("<more than %n items>", 0, size) :
                     WatchHandler::tr("<%n items>", 0, size);
        }
    }
    return quoteUnprintable(str);
}

464
QString WatchModel::formattedValue(const WatchData &data) const
465
{
466
467
468
469
    const QByteArray qtNamespace = engine()->qtNamespace();
    const QString &value = data.value;

    int format = itemFormat(data);
470
    if (isIntType(data.type)) {
471
472
        if (value.isEmpty())
            return value;
473
        // Do not reformat booleans (reported as 'true, false').
474
        const QChar firstChar = value.at(0);
475
        if (!firstChar.isDigit() && firstChar != QLatin1Char('-'))
476
            return value;
477
        // Append quoted, printable character also for decimal.
478
        if (data.type.endsWith("char")) {
479
            bool ok;
480
481
            const int code = value.toInt(&ok);
            return ok ? reformatCharacter(code, format) : value;
482
483
        }
        // Rest: Leave decimal as is
484
        if (format <= 0)
485
            return value;
486
        // Evil hack, covers 'unsigned' as well as quint64.
487
        if (data.type.contains('u'))
488
489
            return reformatInteger(value.toULongLong(0, 0), format);
        return reformatInteger(value.toLongLong(), format);
490
    }
491

hjk's avatar
hjk committed
492
    if (data.type == "va_list")
493
        return value;
hjk's avatar
hjk committed
494

495
    if (!isPointerType(data.type) && !data.isVTablePointer()) {
496
        bool ok = false;
497
        qulonglong integer = value.toULongLong(&ok, 0);
498
499
500
        if (ok)
           return reformatInteger(integer, format);
    }
501

502
    return translate(value);
503
504
}

505
506
507
508
509
510
511
512
513
// 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('`'));
514
    return data.toULongLong(0, 0);
515
516
}

517
518
519
520
521
522
523
524
525
// 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;
526
527
528
    // Check for pointers using hex values (0xAD00 "Hallo")
    if (isPointerType(d.type) && d.value.startsWith(QLatin1String("0x")))
        return QVariant::ULongLong;
529
530
531
532
533
534
535
536
537
538
   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:
539
540
        if (isPointerType(d.type)) // Fix pointer values (0xAD00 "Hallo" -> 0xAD00)
            return QVariant(pointerValue(d.value));
541
542
543
544
545
546
547
548
        return QVariant(d.value.toULongLong());
    case QVariant::LongLong:
        return QVariant(d.value.toLongLong());
    case QVariant::Double:
        return QVariant(d.value.toDouble());
    default:
        break;
    }
549
550
    // Some string value: '0x434 "Hallo"':
    // Remove quotes and replace newlines, which will cause line edit troubles.
hjk's avatar
hjk committed
551
    QString stringValue = d.value;
552
553
554
555
556
557
558
559
    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"));
        }
    }
560
    return QVariant(translate(stringValue));
561
562
}

563
bool WatchModel::canFetchMore(const QModelIndex &index) const
hjk's avatar
hjk committed
564
{
565
566
    WatchItem *item = watchItem(index);
    QTC_ASSERT(item, return false);
567
    return index.isValid() && m_handler->m_contentsValid && !m_fetchTriggered.contains(item->iname);
568
569
570
571
572
}

void WatchModel::fetchMore(const QModelIndex &index)
{
    QTC_ASSERT(index.isValid(), return);
573
574
575
576
577
578
579
580
    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();
581
582
583
        WatchUpdateFlags flags;
        flags.tryIncremental = true;
        engine()->updateWatchData(data, flags);
584
585
586
587
588
589
590
591
592
593
    }
}

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());
594
595
    if (row >= item->children.size())
        return QModelIndex();
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
    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();
hjk's avatar
hjk committed
617
618
619
620
621
622
623
624
625
}

int WatchModel::rowCount(const QModelIndex &idx) const
{
    if (idx.column() > 0)
        return 0;
    return watchItem(idx)->children.size();
}

626
int WatchModel::columnCount(const QModelIndex &idx) const
hjk's avatar
hjk committed
627
{
628
    Q_UNUSED(idx)
629
630
631
632
633
634
635
636
637
638
639
    return 3;
}

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

WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
{
640
    return idx.isValid()
641
642
643
644
645
646
647
648
        ? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
}

QModelIndex WatchModel::watchIndex(const WatchItem *item) const
{
    return watchIndexHelper(item, m_root, QModelIndex());
}

649
QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle,
650
651
652
653
654
655
656
657
658
659
    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
660
    }
661
    return QModelIndex();
hjk's avatar
hjk committed
662
663
}

664
void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
665
666
667
668
669
670
671
672
673
674
675
{
    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));
}

676
677
678
679
680
681
682
683
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);
}

684
685
// Truncate value for item view, maintaining quotes.
static QString truncateValue(QString v)
686
687
688
689
690
691
692
693
694
695
{
    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;
}

696
697
698
699
700
int WatchModel::itemFormat(const WatchData &data) const
{
    const int individualFormat = m_handler->m_individualFormats.value(data.iname, -1);
    if (individualFormat != -1)
        return individualFormat;
701
    return m_handler->m_typeFormats.value(stripForFormat(data.type), -1);
702
703
}

704
705
706
707
static inline QString expression(const WatchItem *item)
{
    if (!item->exp.isEmpty())
         return QString::fromAscii(item->exp);
hjk's avatar
hjk committed
708
709
710
711
712
713
714
715
716
717
    if (item->address && !item->type.isEmpty()) {
        return QString::fromAscii("*(%1*)%2").
                arg(QLatin1String(item->type), QLatin1String(item->hexAddress()));
    }
    if (const WatchItem *parent = item->parent) {
        if (!parent->exp.isEmpty())
           return QString::fromAscii("(%1).%2")
            .arg(QString::fromLatin1(parent->exp), item->name);
    }
    return QString();
718
719
}

720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
QString WatchModel::display(const WatchItem *item, int col) const
{
    QString result;
    switch (col) {
        case 0:
            if (item->name.isEmpty())
                result = tr("<Edit>");
            else if (item->name == QLatin1String("*") && item->parent)
                result = QLatin1Char('*') + item->parent->name;
            else
                result = removeInitialNamespace(item->name);
            break;
        case 1:
            result = removeInitialNamespace(
                truncateValue(formattedValue(*item)));
            if (item->referencingAddress) {
                result += QLatin1String(" @");
737
                result += QString::fromLatin1(item->hexReferencingAddress());
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
            }
            break;
        case 2:
            result = removeNamespaces(displayType(*item));
            break;
        default:
            break;
    }
    return result;
}

QString WatchModel::displayForAutoTest(const QByteArray &iname) const
{
    const WatchItem *item = findItem(iname, m_root);
    if (item)
        return display(item, 1) + QLatin1Char(' ') + display(item, 2);
    return QString();
}

hjk's avatar
hjk committed
757
758
QVariant WatchModel::data(const QModelIndex &idx, int role) const
{
759
760
    const WatchItem *item = watchItem(idx);
    const WatchItem &data = *item;
con's avatar
con committed
761
762

    switch (role) {
hjk's avatar
hjk committed
763
        case LocalsEditTypeRole:
764
            return QVariant(editType(data));
hjk's avatar
hjk committed
765

766
767
768
        case LocalsNameRole:
            return QVariant(data.name);

hjk's avatar
hjk committed
769
        case LocalsIntegerBaseRole:
770
771
            if (isPointerType(data.type)) // Pointers using 0x-convention
                return QVariant(16);
772
            return QVariant(formatToIntegerBase(itemFormat(data)));
hjk's avatar
hjk committed
773
774

        case Qt::EditRole: {
775
            switch (idx.column()) {
hjk's avatar
hjk committed
776
777
778
779
780
781
782
783
784
785
786
787
788
789
                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);
                default:
                    break;
            }
        }

790
791
        case Qt::DisplayRole:
            return display(item, idx.column());
hjk's avatar
hjk committed
792

793
        case Qt::ToolTipRole:
hjk's avatar
hjk committed
794
            return debuggerCore()->boolSetting(UseToolTipsInLocalsView)
795
                ? data.toToolTip() : QVariant();
con's avatar
con committed
796
797
798
799

        case Qt::ForegroundRole: {
            static const QVariant red(QColor(200, 0, 0));
            static const QVariant gray(QColor(140, 140, 140));
800
            switch (idx.column()) {
801
                case 1: return (!data.valueEnabled || !m_handler->m_contentsValid) ? gray
hjk's avatar
hjk committed
802
                            : data.changed ? red : QVariant();
con's avatar
con committed
803
804
805
806
            }
            break;
        }

807
808
        case LocalsExpressionRole:
            return QVariant(expression(item));
hjk's avatar
hjk committed
809

810
811
812
        case LocalsRawExpressionRole:
            return data.exp;

813
        case LocalsINameRole:
con's avatar
con committed
814
815
            return data.iname;

816
        case LocalsExpandedRole:
hjk's avatar
hjk committed
817
            return m_handler->m_expandedINames.contains(data.iname);
818

819
820
821
        case LocalsTypeFormatListRole:
            return m_handler->typeFormatList(data);

822
        case LocalsTypeRole:
823
824
            return removeNamespaces(displayType(data));

825
        case LocalsRawTypeRole:
826
827
            return QString::fromLatin1(data.type);

828
        case LocalsTypeFormatRole:
829
            return m_handler->m_typeFormats.value(stripForFormat(data.type), -1);
830

831
        case LocalsIndividualFormatRole:
832
            return m_handler->m_individualFormats.value(data.iname, -1);
833

834
        case LocalsRawValueRole:
835
            return data.value;
836

837
        case LocalsPointerValueRole:
838
839
840
            if (isPointerType(data.type))
                return pointerValue(data.value);
            return QVariant(quint64(0));
841

hjk's avatar
hjk committed
842
        case LocalsIsWatchpointAtAddressRole: {
843
            BreakpointParameters bp(WatchpointAtAddress);
hjk's avatar
hjk committed
844
845
846
            bp.address = data.coreAddress();
            return engine()->breakHandler()->findWatchpoint(bp) != 0;
        }
847
848

        case LocalsAddressRole:
849
            return QVariant(data.coreAddress());
850
851
        case LocalsReferencingAddressRole:
            return QVariant(data.referencingAddress);
852
853
        case LocalsSizeRole:
            return QVariant(data.size);
854
855

        case LocalsIsWatchpointAtPointerValueRole:
hjk's avatar
hjk committed
856
            if (isPointerType(data.type)) {
857
                BreakpointParameters bp(WatchpointAtAddress);
hjk's avatar
hjk committed
858
859
860
                bp.address = pointerValue(data.value);
                return engine()->breakHandler()->findWatchpoint(bp) != 0;
            }
861
862
            return false;

con's avatar
con committed
863
        default:
864
            break;
con's avatar
con committed
865
866
867
868
    }
    return QVariant();
}

hjk's avatar
hjk committed
869
bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
870
{
871
    WatchItem &data = *watchItem(index);
872
873

    switch (role) {
874
875
876
877
878
879
880
881
882
883
884
        case Qt::EditRole:
            switch (index.column()) {
            case 0: // Watch expression: See delegate.
                break;
            case 1: // Change value
                engine()->assignValueInDebugger(&data, expression(&data), value);
                break;
            case 2: // TODO: Implement change type.
                engine()->assignValueInDebugger(&data, expression(&data), value);
                break;
            }
885
886
887
        case LocalsExpandedRole:
            if (value.toBool()) {
                // Should already have been triggered by fetchMore()
888
                //QTC_CHECK(m_handler->m_expandedINames.contains(data.iname));
889
890
891
892
893
894
895
896
897
898
899
900
901
902
                m_handler->m_expandedINames.insert(data.iname);
            } else {
                m_handler->m_expandedINames.remove(data.iname);
            }
            break;

        case LocalsTypeFormatRole:
            m_handler->setFormat(data.type, value.toInt());
            engine()->updateWatchData(data);
            break;

        case LocalsIndividualFormatRole: {
            const int format = value.toInt();
            if (format == -1) {
903
                m_handler->m_individualFormats.remove(data.iname);
904
            } else {
905
                m_handler->m_individualFormats[data.iname] = format;
906
907
908
            }
            engine()->updateWatchData(data);
            break;
909
        }
hjk's avatar
hjk committed
910
    }
911

912
913
914
915
    emit dataChanged(index, index);
    return true;
}

hjk's avatar
hjk committed
916
Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
con's avatar
con committed
917
{
918
919
920
    if (!m_handler->m_contentsValid)
        return Qt::ItemFlags();

con's avatar
con committed
921
    if (!idx.isValid())
922
        return Qt::ItemFlags();
con's avatar
con committed
923

924
    // Enabled, editable, selectable, checkable, and can be used both as the
con's avatar
con committed
925
926
    // source of a drag and drop operation and as a drop target.

927
    static const Qt::ItemFlags notEditable
928
        = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
929
    static const Qt::ItemFlags editable = notEditable | Qt::ItemIsEditable;
con's avatar
con committed
930

931
932
    // Disable editing if debuggee is positively running.
    const bool isRunning = engine() && engine()->state() == InferiorRunOk;
933
    if (isRunning && engine() && !engine()->hasCapability(AddWatcherWhileRunningCapability))
934
        return notEditable;
con's avatar
con committed
935

hjk's avatar
hjk committed
936
    const WatchData &data = *watchItem(idx);
937
    if (data.isWatcher()) {
938
939
940
        if (idx.column() == 0 && data.iname.count('.') == 1)
            return editable; // Watcher names are editable.

941
        if (!data.name.isEmpty()) {
942
943
944
945
946
947
948
949
950
951
            // FIXME: Forcing types is not implemented yet.
            //if (idx.column() == 2)
            //    return editable; // Watcher types can be set by force.
            if (idx.column() == 1 && data.valueEditable)
                return editable; // Watcher values are sometimes editable.
        }
    } else {
        if (idx.column() == 1 && data.valueEditable)
            return editable; // Locals values are sometimes editable.
    }
952
    return notEditable;
con's avatar
con committed
953
954
}

hjk's avatar
hjk committed
955
QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
con's avatar
con committed
956
957
958
959
960
{
    if (orientation == Qt::Vertical)
        return QVariant();
    if (role == Qt::DisplayRole) {
        switch (section) {
hjk's avatar
hjk committed
961
962
963
            case 0: return QString(tr("Name")  + QLatin1String("     "));
            case 1: return QString(tr("Value") + QLatin1String("     "));
            case 2: return QString(tr("Type")  + QLatin1String("     "));
con's avatar
con committed
964
965
        }
    }
966
    return QVariant();
con's avatar
con committed
967
968
}

969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
QStringList WatchHandler::typeFormatList(const WatchData &data) const
{
    if (data.referencingAddress || isPointerType(data.type))
        return QStringList()
            << tr("Raw pointer")
            << tr("Latin1 string")
            << tr("UTF8 string")
            << tr("Local 8bit string")
            << tr("UTF16 string")
            << tr("UCS4 string");
    if (data.type.contains("char[") || data.type.contains("char ["))
        return QStringList()
            << tr("Latin1 string")
            << tr("UTF8 string")
            << tr("Local 8bit string");
    bool ok = false;
    (void)data.value.toULongLong(&ok, 0);
    if ((isIntType(data.type) && data.type != "bool") || ok)
        return QStringList()
            << tr("Decimal")
            << tr("Hexadecimal")
            << tr("Binary")
            << tr("Octal");
    // Hack: Compensate for namespaces.