watchhandler.cpp 49 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) 2010 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
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** 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.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

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

#include "breakhandler.h"
#include "breakpoint.h"
34
#include "debuggeractions.h"
35
36
37
38
#include "debuggeragents.h"
#include "debuggerengine.h"
#include "debuggerplugin.h"
#include "watchutils.h"
con's avatar
con committed
39
40
41
42
43

#if USE_MODEL_TEST
#include "modeltest.h"
#endif

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

#include <QtCore/QDebug>
#include <QtCore/QEvent>
hjk's avatar
hjk committed
49
#include <QtCore/QFile>
50
#include <QtCore/QProcess>
51
#include <QtCore/QTextStream>
hjk's avatar
hjk committed
52
#include <QtCore/QTimer>
hjk's avatar
hjk committed
53
#include <QtCore/QtAlgorithms>
con's avatar
con committed
54

55
#include <QtGui/QAction>
con's avatar
con committed
56
57
58
59
60
61
62
#include <QtGui/QApplication>
#include <QtGui/QLabel>
#include <QtGui/QToolTip>
#include <QtGui/QTextEdit>

#include <ctype.h>

hjk's avatar
hjk committed
63

64
65
// creates debug output for accesses to the model
//#define DEBUG_MODEL 1
con's avatar
con committed
66
67
68
69

#if DEBUG_MODEL
#   define MODEL_DEBUG(s) qDebug() << s
#else
70
#   define MODEL_DEBUG(s)
con's avatar
con committed
71
72
73
#endif
#define MODEL_DEBUGX(s) qDebug() << s

hjk's avatar
hjk committed
74
75
namespace Debugger {
namespace Internal {
con's avatar
con committed
76

77
static const QString strNotInScope =
78
    QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
79

80
static int watcherCounter = 0;
81
82
static int generationCounter = 0;

83
84
static DebuggerPlugin *plugin() { return DebuggerPlugin::instance(); }

con's avatar
con committed
85
86
////////////////////////////////////////////////////////////////////
//
87
// WatchItem
con's avatar
con committed
88
89
//
////////////////////////////////////////////////////////////////////
90

91
92
93
class WatchItem : public WatchData
{
public:
94
    WatchItem() { parent = 0; }
95

hjk's avatar
hjk committed
96
97
98
99
100
101
    ~WatchItem() {
        if (parent != 0)
            parent->children.removeOne(this);
        qDeleteAll(children);
    }

102
    WatchItem(const WatchData &data) : WatchData(data)
103
        { parent = 0; }
104
105

    void setData(const WatchData &data)
hjk's avatar
hjk committed
106
        { static_cast<WatchData &>(*this) = data; }
107
108
109
110
111

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

112

113
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
114
//
115
// WatchModel
hjk's avatar
hjk committed
116
//
117
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
118

119
120
WatchModel::WatchModel(WatchHandler *handler, WatchType type)
    : QAbstractItemModel(handler), m_handler(handler), m_type(type)
hjk's avatar
hjk committed
121
{
122
123
124
125
    m_root = new WatchItem;
    m_root->hasChildren = 1;
    m_root->state = 0;
    m_root->name = WatchHandler::tr("Root");
126
    m_root->parent = 0;
hjk's avatar
hjk committed
127

128
    switch (m_type) {
129
130
131
132
        case ReturnWatch:
            m_root->iname = "return";
            m_root->name = WatchHandler::tr("Return Value");
            break;
133
        case LocalsWatch:
134
            m_root->iname = "local";
135
            m_root->name = WatchHandler::tr("Locals");
136
137
            break;
        case WatchersWatch:
138
            m_root->iname = "watch";
139
            m_root->name = WatchHandler::tr("Watchers");
140
141
            break;
        case TooltipsWatch:
142
            m_root->iname = "tooltip";
143
            m_root->name = WatchHandler::tr("Tooltip");
144
145
            break;
    }
hjk's avatar
hjk committed
146
147
}

148
149
150
151
152
WatchModel::~WatchModel()
{
    delete m_root;
}

153
WatchItem *WatchModel::rootItem() const
hjk's avatar
hjk committed
154
{
155
    return m_root;
hjk's avatar
hjk committed
156
157
}

158
void WatchModel::reinitialize()
hjk's avatar
hjk committed
159
{
160
    int n = m_root->children.size();
161
162
    if (n == 0)
        return;
163
164
    //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << m_root->iname);
    QModelIndex index = watchIndex(m_root);
165
    beginRemoveRows(index, 0, n - 1);
166
167
    qDeleteAll(m_root->children);
    m_root->children.clear();
168
    endRemoveRows();
hjk's avatar
hjk committed
169
170
}

171
172
173
174
175
void WatchModel::emitAllChanged()
{
    emit layoutChanged();
}

176
void WatchModel::beginCycle()
177
178
179
180
181
182
183
{
    emit enableUpdates(false);
}

void WatchModel::endCycle()
{
    removeOutdated();
184
    m_fetchTriggered.clear();
185
186
    emit enableUpdates(true);
}
187

188
189
190
191
DebuggerEngine *WatchModel::engine() const
{
    return m_handler->m_engine;
}
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

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

209
void WatchModel::removeOutdated()
hjk's avatar
hjk committed
210
{
211
    foreach (WatchItem *child, m_root->children)
212
213
214
215
216
217
        removeOutdatedHelper(child);
#if DEBUG_MODEL
#if USE_MODEL_TEST
    //(void) new ModelTest(this, this);
#endif
#endif
hjk's avatar
hjk committed
218
219
}

220
void WatchModel::removeOutdatedHelper(WatchItem *item)
hjk's avatar
hjk committed
221
{
222
    if (item->generation < generationCounter) {
223
        destroyItem(item);
224
    } else {
225
226
        foreach (WatchItem *child, item->children)
            removeOutdatedHelper(child);
227
    }
hjk's avatar
hjk committed
228
229
}

230
void WatchModel::destroyItem(WatchItem *item)
con's avatar
con committed
231
{
232
233
234
235
236
237
238
    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();
239
    delete item;
240
241
}

242
static QByteArray parentName(const QByteArray &iname)
243
{
244
    int pos = iname.lastIndexOf('.');
con's avatar
con committed
245
    if (pos == -1)
246
        return QByteArray();
con's avatar
con committed
247
248
249
    return iname.left(pos);
}

250

251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
static QString chopConst(QString type)
{
   while (1) {
        if (type.startsWith("const"))
            type = type.mid(5);
        else if (type.startsWith(' '))
            type = type.mid(1);
        else if (type.endsWith("const"))
            type.chop(5);
        else if (type.endsWith(' '))
            type.chop(1);
        else
            break;
    }
    return type;
}

268
static inline QRegExp stdStringRegExp(const QString &charType)
con's avatar
con committed
269
{
270
271
272
273
274
275
276
277
278
279
280
281
    QString rc = QLatin1String("basic_string<");
    rc += charType;
    rc += QLatin1String(",[ ]?std::char_traits<");
    rc += charType;
    rc += QLatin1String(">,[ ]?std::allocator<");
    rc += charType;
    rc += QLatin1String("> >");
    const QRegExp re(rc);
    Q_ASSERT(re.isValid());
    return re;
}

282
static QString niceTypeHelper(const QString typeIn)
283
284
285
{
    static QMap<QString, QString> cache;
    const QMap<QString, QString>::const_iterator it = cache.constFind(typeIn);
286
    if (it != cache.constEnd())
287
        return it.value();
288

289
290
    QString type = typeIn;
    type.replace(QLatin1Char('*'), QLatin1Char('@'));
291
292
293
294

    for (int i = 0; i < 10; ++i) {
        int start = type.indexOf("std::allocator<");
        if (start == -1)
295
            break;
296
297
298
299
300
301
302
303
304
305
306
307
        // search for matching '>'
        int pos;
        int level = 0;
        for (pos = start + 12; pos < type.size(); ++pos) {
            int c = type.at(pos).unicode();
            if (c == '<') {
                ++level;
            } else if (c == '>') {
                --level;
                if (level == 0)
                    break;
            }
308
        }
309
310
311
        QString alloc = type.mid(start, pos + 1 - start).trimmed();
        QString inner = alloc.mid(15, alloc.size() - 16).trimmed();

312
        if (inner == QLatin1String("char")) { // std::string
313
            const QRegExp stringRegexp = stdStringRegExp(inner);
314
315
            type.replace(stringRegexp, QLatin1String("string"));
        } else if (inner == QLatin1String("wchar_t")) { // std::wstring
316
            const QRegExp wchartStringRegexp = stdStringRegExp(inner);
317
318
            type.replace(wchartStringRegexp, QLatin1String("wstring"));
        } else if (inner == QLatin1String("unsigned short")) { // std::wstring/MSVC
319
            const QRegExp usStringRegexp = stdStringRegExp(inner);
320
321
            type.replace(usStringRegexp, QLatin1String("wstring"));
        }
322
        // std::vector, std::deque, std::list
323
        const QRegExp re1(QString::fromLatin1("(vector|list|deque)<%1, ?%2\\s*>").arg(inner, alloc));
324
        Q_ASSERT(re1.isValid());
325
        if (re1.indexIn(type) != -1)
326
            type.replace(re1.cap(0), QString::fromLatin1("%1<%2>").arg(re1.cap(1), inner));
327
328

        // std::stack
329
        QRegExp re6(QString::fromLatin1("stack<%1, ?std::deque<%2> >").arg(inner, inner));
330
331
332
        if (!re6.isMinimal())
            re6.setMinimal(true);
        Q_ASSERT(re6.isValid());
333
        if (re6.indexIn(type) != -1)
334
            type.replace(re6.cap(0), QString::fromLatin1("stack<%1>").arg(inner));
335
336

        // std::set
337
        QRegExp re4(QString::fromLatin1("set<%1, ?std::less<%2>, ?%3\\s*>").arg(inner, inner, alloc));
338
339
340
        if (!re4.isMinimal())
            re4.setMinimal(true);
        Q_ASSERT(re4.isValid());
341
        if (re4.indexIn(type) != -1)
342
            type.replace(re4.cap(0), QString::fromLatin1("set<%1>").arg(inner));
con's avatar
con committed
343

344
        // std::map
345
346
347
348
349
350
351
352
353
354
355
        if (inner.startsWith("std::pair<")) {
            // search for outermost ','
            int pos;
            int level = 0;
            for (pos = 10; pos < inner.size(); ++pos) {
                int c = inner.at(pos).unicode();
                if (c == '<')
                    ++level;
                else if (c == '>')
                    --level;
                else if (c == ',' && level == 0)
356
                    break;
357
358
359
            }
            QString ckey = inner.mid(10, pos - 10);
            QString key = chopConst(ckey);
360
361
            QString value = inner.mid(pos + 2, inner.size() - 3 - pos).trimmed();
            QRegExp re5(QString("map<%1, ?%2, ?std::less<%3 ?>, ?%4\\s*>")
362
                .arg(key, value, key, alloc));
363
364
365
            if (!re5.isMinimal())
                re5.setMinimal(true);
            Q_ASSERT(re5.isValid());
366
            if (re5.indexIn(type) != -1) {
367
                type.replace(re5.cap(0), QString("map<%1, %2>").arg(key, value));
368
369
            } else {
                QRegExp re7(QString("map<const %1, ?%2, ?std::less<const %3>, ?%4\\s*>")
370
                    .arg(key, value, key, alloc));
371
372
                if (!re7.isMinimal())
                    re7.setMinimal(true);
373
374
                if (re7.indexIn(type) != -1)
                    type.replace(re7.cap(0), QString("map<const %1, %2>").arg(key, value));
375
            }
376
        }
con's avatar
con committed
377
    }
378
    type.replace(QLatin1Char('@'), QLatin1Char('*'));
379
    type.replace(QLatin1String(" >"), QString(QLatin1Char('>')));
380
    cache.insert(typeIn, type); // For simplicity, also cache unmodified types
con's avatar
con committed
381
382
383
    return type;
}

384
385
386
QString WatchModel::niceType(const QString &typeIn) const
{
    QString type = niceTypeHelper(typeIn);
387
    if (!theDebuggerBoolSetting(ShowStdNamespace))
388
        type = type.remove("std::");
389
390
    if (!theDebuggerBoolSetting(ShowQtNamespace))
        type = type.remove(engine()->qtNamespace());
391
392
393
    return type;
}

394
395
396
397
398
399
400
401
402
403
404
405
406
template <class IntType> QString reformatInteger(IntType value, int format)
{
    switch (format) {
    case HexadecimalFormat:
        return ("(hex) ") + QString::number(value, 16);
    case BinaryFormat:
        return ("(bin) ") + QString::number(value, 2);
    case OctalFormat:
        return ("(oct) ") + QString::number(value, 8);
    }
    return QString::number(value); // not reached
}

407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
// Format printable (char-type) characters
static inline QString reformatCharacter(int code, int format)
{
    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;
}

static inline QString formattedValue(const WatchData &data, int format)
429
{
430
    if (isIntType(data.type)) {
431
432
433
434
435
436
437
        if (data.value.isEmpty())
            return data.value;
        // Do not reformat booleans (reported as 'true, false').
        const QChar firstChar = data.value.at(0);
        if (!firstChar.isDigit() && firstChar != QLatin1Char('-'))
            return data.value;
        // Append quoted, printable character also for decimal.
438
        if (data.type.endsWith("char")) {
439
440
441
442
443
            bool ok;
            const int code = data.value.toInt(&ok);
            return ok ? reformatCharacter(code, format) : data.value;
        }
        // Rest: Leave decimal as is
444
445
        if (format <= 0)
            return data.value;
446
        // Evil hack, covers 'unsigned' as well as quint64.
447
        if (data.type.contains('u'))
448
            return reformatInteger(data.value.toULongLong(), format);
449
450
        return reformatInteger(data.value.toLongLong(), format);
    }
451
452
453
    QString result = data.value;
    result.replace(QLatin1Char('\n'), QLatin1String("\\n"));
    return result;
454
455
}

456
bool WatchModel::canFetchMore(const QModelIndex &index) const
hjk's avatar
hjk committed
457
{
458
459
    WatchItem *item = watchItem(index);
    QTC_ASSERT(item, return false);
460
    return index.isValid() && !m_fetchTriggered.contains(item->iname);
461
462
463
464
465
}

void WatchModel::fetchMore(const QModelIndex &index)
{
    QTC_ASSERT(index.isValid(), return);
466
467
468
469
470
471
472
473
    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();
474
        engine()->updateWatchData(data);
475
476
477
478
479
480
481
482
483
484
    }
}

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());
485
486
    if (row >= item->children.size())
        return QModelIndex();
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
    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
508
509
510
511
512
513
514
515
516
}

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

517
int WatchModel::columnCount(const QModelIndex &idx) const
hjk's avatar
hjk committed
518
{
519
    Q_UNUSED(idx)
520
521
522
523
524
525
526
527
528
529
530
    return 3;
}

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

WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
{
531
    return idx.isValid()
532
533
534
535
536
537
538
539
        ? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
}

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

540
QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle,
541
542
543
544
545
546
547
548
549
550
    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
551
    }
552
    return QModelIndex();
hjk's avatar
hjk committed
553
554
}

555
void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
556
557
558
559
560
561
562
563
564
565
566
{
    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));
}

567
568
569
570
571
572
573
574
575
576
577
578
// Truncate value for item view, maintaining quotes
static inline QString truncateValue(QString v)
{
    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;
}

579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
// Get a pointer address from pointer values reported by the debugger.
// Fix CDB formatting of pointers "0x00000000`000003fd class foo *",
// check gdb formatting of characters.
static inline quint64 pointerValue(QString data)
{
    if (data.isEmpty() || !data.startsWith(QLatin1String("0x")))
        return 0;
    data.remove(0, 2);
    const int blankPos = data.indexOf(QLatin1Char(' '));
    if (blankPos != -1)
        data.truncate(blankPos);
    data.remove(QLatin1Char('`'));
    bool ok;
    const quint64 address = data.toULongLong(&ok, 16);
    return ok ? address : quint64(0);
}

hjk's avatar
hjk committed
596
597
QVariant WatchModel::data(const QModelIndex &idx, int role) const
{
598
599
600
601
602
603
604
605
    switch (role) {
        case EngineCapabilitiesRole:
            return engine()->debuggerCapabilities();

        case EngineActionsEnabledRole:
            return engine()->debuggerActionsEnabled();
    }

606
607
    const WatchItem *item = watchItem(idx);
    const WatchItem &data = *item;
con's avatar
con committed
608
609

    switch (role) {
hjk's avatar
hjk committed
610
        case Qt::EditRole:
con's avatar
con committed
611
        case Qt::DisplayRole: {
612
            switch (idx.column()) {
613
                case 0:
614
615
                    if (data.name.isEmpty() && role == Qt::DisplayRole)
                        return tr("<Edit>");
616
                    if (data.name == QLatin1String("*") && item->parent)
617
                        return QVariant(QLatin1Char('*') + item->parent->name);
618
619
                    return data.name;
                case 1: {
620
621
                    int format =
                        m_handler->m_individualFormats.value(data.iname, -1);
622
623
                    if (format == -1)
                        format = m_handler->m_typeFormats.value(data.type, -1);
con's avatar
con committed
624
                    return truncateValue(formattedValue(data, format));
625
626
                }
                case 2: {
627
628
629
                    if (!data.displayedType.isEmpty())
                        return data.displayedType;
                    return niceType(data.type);
630
                }
con's avatar
con committed
631
632
633
634
635
                default: break;
            }
            break;
        }

636
        case Qt::ToolTipRole:
637
638
            return theDebuggerBoolSetting(UseToolTipsInLocalsView)
                ? data.toToolTip() : QVariant();
con's avatar
con committed
639
640
641
642

        case Qt::ForegroundRole: {
            static const QVariant red(QColor(200, 0, 0));
            static const QVariant gray(QColor(140, 140, 140));
643
            switch (idx.column()) {
644
                case 1: return !data.valueEnabled ? gray : data.changed ? red : QVariant();
con's avatar
con committed
645
646
647
648
            }
            break;
        }

649
650
651
        case LocalsExpressionRole: {
            if (!data.exp.isEmpty())
                return data.exp;
652
653
654
655
            if (!data.addr.isEmpty() && !data.type.isEmpty()) {
                bool ok;
                const quint64 addr = data.addr.toULongLong(&ok, 16);
                if (ok && addr)
656
                    return QString("*(%1*)%2").arg(QLatin1String(data.type)).arg(addr);
657
            }
658
659
660
661
662
663
            WatchItem *parent = item->parent;
            if (parent && !parent->exp.isEmpty())
                return QString("(%1).%2")
                    .arg(QString::fromLatin1(parent->exp)).arg(data.name);
            return QVariant();
        }
664

665
        case LocalsINameRole:
con's avatar
con committed
666
667
            return data.iname;

668
        case LocalsExpandedRole:
hjk's avatar
hjk committed
669
            return m_handler->m_expandedINames.contains(data.iname);
670

671
        case LocalsTypeFormatListRole: {
672
            if (isIntType(data.type) && data.type != "bool")
673
674
                return QStringList() << tr("decimal") << tr("hexadecimal")
                    << tr("binary") << tr("octal");
675
            if (data.type.endsWith('*'))
676
                return QStringList()
677
                    << tr("Raw pointer")
678
679
680
681
                    << tr("Latin1 string")
                    << tr("UTF8 string")
                    << tr("UTF16 string")
                    << tr("UCS4 string");
682
683
684
685
686
            // Hack: Compensate for namespaces.
            QString type = data.type;
            int pos = type.indexOf("::Q");
            if (pos >= 0 && type.count(':') == 2)
                type = type.mid(pos + 2);
687
688
689
            pos = type.indexOf('<');
            if (pos >= 0)
                type = type.left(pos);
690
691
            return m_handler->m_reportedTypeFormats.value(type);
        }
692

693
        case LocalsTypeFormatRole:
694
            return m_handler->m_typeFormats.value(data.type, -1);
695

696
        case LocalsIndividualFormatRole:
697
            return m_handler->m_individualFormats.value(data.iname, -1);
698

699
        case LocalsRawValueRole:
700
            return data.value;
701

702
        case LocalsPointerValueRole:
703
704
705
            if (isPointerType(data.type))
                return pointerValue(data.value);
            return QVariant(quint64(0));
706
707
708
709
710
711
712
713
714
715
716
717
718
719

        case LocalsIsWatchpointAtAddressRole:
            return engine()->breakHandler()
                ->watchPointAt(data.coreAddress());

        case LocalsAddressRole:
            return data.coreAddress();

        case LocalsIsWatchpointAtPointerValueRole:
            if (isPointerType(data.type))
                return engine()->breakHandler()
                    ->watchPointAt(pointerValue(data.addr));
            return false;

con's avatar
con committed
720
        default:
721
            break;
con's avatar
con committed
722
723
724
725
    }
    return QVariant();
}

hjk's avatar
hjk committed
726
bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
727
{
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
    switch (role) {
        case RequestAssignValueRole: {
            QString str = value.toString();
            int i = str.indexOf('=');
            if (i != -1)
                engine()->assignValueInDebugger(str.left(i), str.mid(i + 1));
            return true;
        }

        case RequestAssignTypeRole: {
            QString str = value.toString();
            int i = str.indexOf('=');
            if (i != -1)
                engine()->assignValueInDebugger(str.left(i), str.mid(i + 1));
            return true;
        }

        case RequestToggleWatchRole: {
            BreakHandler *handler = engine()->breakHandler();
            const quint64 address = value.toULongLong();
            const QByteArray addressBA =
                QByteArray("0x") + QByteArray::number(address, 16);
            const int index = handler->findWatchPointIndexByAddress(addressBA);
            if (index == -1) {
                BreakpointData *data = new BreakpointData;
                data->type = BreakpointData::WatchpointType;
                data->address = addressBA;
                handler->appendBreakpoint(data);
            } else {
                handler->removeBreakpoint(index);
            }
            engine()->attemptBreakpointSynchronization();
            return true;
        }

        case RequestShowMemoryRole: {
            (void) new MemoryViewAgent(engine(), value.toULongLong());
            return true;
        }

        case RequestClearCppCodeModelSnapshotRole: {
            plugin()->clearCppCodeModelSnapshot();
            return true;
        }
772
773
774
775
776

        case RequestWatchPointRole: {
            engine()->watchPoint(value.toPoint());
            return true;
        }
777
778
    }

779
    WatchItem &data = *watchItem(index);
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799

    switch (role) {
        case LocalsExpandedRole:
            if (value.toBool()) {
                // Should already have been triggered by fetchMore()
                //QTC_ASSERT(m_handler->m_expandedINames.contains(data.iname), /**/);
                m_handler->m_expandedINames.insert(data.iname);
            } 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) {
800
                m_handler->m_individualFormats.remove(data.iname);
801
            } else {
802
                m_handler->m_individualFormats[data.iname] = format;
803
804
805
            }
            engine()->updateWatchData(data);
            break;
806
        }
807
808
809
810
811
812
813
814

        case RequestRemoveWatchExpressionRole:
            m_handler->removeWatchExpression(value.toString());
            break;

        case RequestWatchExpressionRole:
            m_handler->watchExpression(value.toString());
            break;
hjk's avatar
hjk committed
815
    }
816
817
818
819
    emit dataChanged(index, index);
    return true;
}

hjk's avatar
hjk committed
820
Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
con's avatar
con committed
821
822
823
824
825
826
827
828
829
{
    using namespace Qt;

    if (!idx.isValid())
        return ItemFlags();

    // enabled, editable, selectable, checkable, and can be used both as the
    // source of a drag and drop operation and as a drop target.

830
    static const ItemFlags notEditable =
con's avatar
con committed
831
          ItemIsSelectable
832
833
        // | ItemIsDragEnabled
        // | ItemIsDropEnabled
con's avatar
con committed
834
835
836
837
        // | ItemIsUserCheckable
        // | ItemIsTristate
        | ItemIsEnabled;

838
    static const ItemFlags editable = notEditable | ItemIsEditable;
con's avatar
con committed
839

hjk's avatar
hjk committed
840
    const WatchData &data = *watchItem(idx);
841

842
843
844
845
    if (idx.column() == 0)
        return editable; // Watcher names are editable.

    if (data.isWatcher()) {
846
        if (!data.name.isEmpty()) {
847
848
849
850
851
852
853
854
855
856
            // 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.
    }
857
    return notEditable;
con's avatar
con committed
858
859
}

hjk's avatar
hjk committed
860
QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
con's avatar
con committed
861
862
863
864
865
{
    if (orientation == Qt::Vertical)
        return QVariant();
    if (role == Qt::DisplayRole) {
        switch (section) {
hjk's avatar
hjk committed
866
867
868
            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
869
870
        }
    }
871
    return QVariant();
con's avatar
con committed
872
873
}

874
struct IName : public QByteArray
875
{
876
    IName(const QByteArray &iname) : QByteArray(iname) {}
877
878
};

879
bool iNameLess(const QString &iname1, const QString &iname2)
880
881
882
{
    QString name1 = iname1.section('.', -1);
    QString name2 = iname2.section('.', -1);
883
    if (!name1.isEmpty() && !name2.isEmpty()) {
hjk's avatar
hjk committed
884
885
886
887
888
889
        if (name1.at(0).isDigit() && name2.at(0).isDigit()) {
            bool ok1 = false, ok2 = false;
            int i1 = name1.toInt(&ok1), i2 = name2.toInt(&ok2);
            if (ok1 && ok2)
                return i1 < i2;
        }
890
    }
891
    return name1 < name2;
892
893
}

894
895
896
897
bool operator<(const IName &iname1, const IName &iname2)
{
    return iNameLess(iname1, iname2);
}
898
899
900

static bool iNameSorter(const WatchItem *item1, const WatchItem *item2)
{
901
    return iNameLess(item1->iname, item2->iname);
902
903
}

904
static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
905
{
906
907
    QList<WatchItem *>::const_iterator it =
        qLowerBound(list.begin(), list.end(), item, iNameSorter);
908
    return it - list.begin();
909
910
}

911
void WatchModel::insertData(const WatchData &data)
con's avatar
con committed
912
{
hjk's avatar
hjk committed
913
    //qDebug() << "WMI:" << data.toString();
914
    //static int bulk = 0;
hjk's avatar
hjk committed
915
    //qDebug() << "SINGLE: " << ++bulk << data.toString();
hjk's avatar
hjk committed
916
917
918
919
920
    if (data.iname.isEmpty()) {
        int x;
        x = 1;
    }
    QTC_ASSERT(!data.iname.isEmpty(), qDebug() << data.toString(); return);
921
922
923
924
    WatchItem *parent = findItem(parentName(data.iname), m_root);
    if (!parent) {
        WatchData parent;
        parent.iname = parentName(data.iname);
hjk's avatar
hjk committed
925
926
927
        MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname);
        if (!parent.iname.isEmpty())
            insertData(parent);
hjk's avatar
hjk committed
928
929
        return;
    }
930
931
    QModelIndex index = watchIndex(parent);
    if (WatchItem *oldItem = findItem(data.iname, parent)) {
932
933
        bool hadChildren = oldItem->hasChildren;
        // Overwrite old entry.
934
935
936
937
938
939
940
941
        bool changed = !data.value.isEmpty()
            && data.value != oldItem->value
            && data.value != strNotInScope;
        oldItem->setData(data);
        oldItem->changed = changed;
        oldItem->generation = generationCounter;
        QModelIndex idx = watchIndex(oldItem);
        emit dataChanged(idx, idx.sibling(idx.row(), 2));
942
943
944
945
946
947
948
949
950
951
952

        // This works around http://bugreports.qt.nokia.com/browse/QTBUG-7115
        // by creating and destroying a dummy child item.
        if (!hadChildren && oldItem->hasChildren) {
            WatchData dummy = data;
            dummy.iname = data.iname + ".x";
            dummy.hasChildren = false;
            dummy.setAllUnneeded();
            insertData(dummy);
            destroyItem(findItem(dummy.iname, m_root));
        }
953
    } else {
954
        // Add new entry.
955
956
957
958
959
960
961
962
963
        WatchItem *item = new WatchItem(data);
        item->parent = parent;
        item->generation = generationCounter;
        item->changed = true;
        int n = findInsertPosition(parent->children, item);
        beginInsertRows(index, n, n);
        parent->children.insert(n, item);
        endInsertRows();
    }
con's avatar
con committed
964
965
}

966
967
void WatchModel::insertBulkData(const QList<WatchData> &list)
{
968
#if 0
969
    for (int i = 0; i != list.size(); ++i)
970
971
972
973
974
975
        insertData(list.at(i));
    return;
#endif
    // This method does not properly insert items in proper "iname sort
    // order", leading to random removal of items in removeOutDated();

976
    //qDebug() << "WMI:" << list.toString();
977
    //static int bulk = 0;
978
    //foreach (const WatchItem &data, list)
hjk's avatar
hjk committed
979
    //    qDebug() << "BULK: " << ++bulk << data.toString();
980
    QTC_ASSERT(!list.isEmpty(), return);
981
    QByteArray parentIName = parentName(list.at(0).iname);
982
983
984
985
986
987
988
989
990
991
992
993
994
995
    WatchItem *parent = findItem(parentIName, m_root);
    if (!parent) {
        WatchData parent;
        parent.iname = parentIName;
        insertData(parent);
        MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << list.at(0).iname);
        return;
    }
    QModelIndex index = watchIndex(parent);

    QMap<IName, WatchData> newList;
    typedef QMap<IName, WatchData>::iterator Iterator;
    foreach (const WatchItem &data, list)
        newList[data.iname] = data;
996
997
998
999
1000
    if (newList.size() != list.size()) {
        qDebug() << "LIST: ";
        foreach (const WatchItem &data, list)
            qDebug() << data.toString();
        qDebug() << "NEW LIST: ";