watchhandler.cpp 53.8 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

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

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

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

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

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

#include <ctype.h>
57
#include <utils/qtcassert.h>
hjk's avatar
hjk committed
58

59
60
// creates debug output for accesses to the model
//#define DEBUG_MODEL 1
con's avatar
con committed
61
62
63
64

#if DEBUG_MODEL
#   define MODEL_DEBUG(s) qDebug() << s
#else
65
#   define MODEL_DEBUG(s)
con's avatar
con committed
66
67
68
#endif
#define MODEL_DEBUGX(s) qDebug() << s

hjk's avatar
hjk committed
69
70
namespace Debugger {
namespace Internal {
con's avatar
con committed
71

72
static const QString strNotInScope =
73
    QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
74

75
static int watcherCounter = 0;
76
77
static int generationCounter = 0;

hjk's avatar
hjk committed
78
79
80
QHash<QByteArray, int> WatchHandler::m_watcherNames;
QHash<QByteArray, int> WatchHandler::m_typeFormats;

con's avatar
con committed
81
82
////////////////////////////////////////////////////////////////////
//
83
// WatchItem
con's avatar
con committed
84
85
//
////////////////////////////////////////////////////////////////////
86

87
88
89
class WatchItem : public WatchData
{
public:
90
    WatchItem() { parent = 0; }
91

hjk's avatar
hjk committed
92
93
94
95
96
97
    ~WatchItem() {
        if (parent != 0)
            parent->children.removeOne(this);
        qDeleteAll(children);
    }

98
    WatchItem(const WatchData &data) : WatchData(data)
99
        { parent = 0; }
100
101

    void setData(const WatchData &data)
hjk's avatar
hjk committed
102
        { static_cast<WatchData &>(*this) = data; }
103
104
105
106
107

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

108

109
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
110
//
111
// WatchModel
hjk's avatar
hjk committed
112
//
113
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
114

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

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

144
145
146
147
148
WatchModel::~WatchModel()
{
    delete m_root;
}

149
WatchItem *WatchModel::rootItem() const
hjk's avatar
hjk committed
150
{
151
    return m_root;
hjk's avatar
hjk committed
152
153
}

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

167
168
169
170
171
void WatchModel::emitAllChanged()
{
    emit layoutChanged();
}

172
void WatchModel::beginCycle()
173
174
175
176
177
178
179
{
    emit enableUpdates(false);
}

void WatchModel::endCycle()
{
    removeOutdated();
180
    m_fetchTriggered.clear();
181
182
    emit enableUpdates(true);
}
183

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

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

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

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

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

238
static QByteArray parentName(const QByteArray &iname)
239
{
240
    int pos = iname.lastIndexOf('.');
con's avatar
con committed
241
    if (pos == -1)
242
        return QByteArray();
con's avatar
con committed
243
244
245
    return iname.left(pos);
}

246

247
248
249
static QString chopConst(QString type)
{
   while (1) {
hjk's avatar
hjk committed
250
        if (type.startsWith(QLatin1String("const")))
251
            type = type.mid(5);
hjk's avatar
hjk committed
252
        else if (type.startsWith(QLatin1Char(' ')))
253
            type = type.mid(1);
hjk's avatar
hjk committed
254
        else if (type.endsWith(QLatin1String("const")))
255
            type.chop(5);
hjk's avatar
hjk committed
256
        else if (type.endsWith(QLatin1Char(' ')))
257
258
259
260
261
262
263
            type.chop(1);
        else
            break;
    }
    return type;
}

264
static inline QRegExp stdStringRegExp(const QString &charType)
con's avatar
con committed
265
{
266
267
268
269
270
271
272
273
274
275
276
277
    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;
}

278
static QString niceTypeHelper(const QByteArray &typeIn)
279
{
280
281
282
    typedef QMap<QByteArray, QString> Cache;
    static Cache cache;
    const Cache::const_iterator it = cache.constFind(typeIn);
283
    if (it != cache.constEnd())
284
        return it.value();
285

hjk's avatar
hjk committed
286
    QString type = QString::fromUtf8(typeIn);
287
    type.replace(QLatin1Char('*'), QLatin1Char('@'));
288
289
290
291

    for (int i = 0; i < 10; ++i) {
        int start = type.indexOf("std::allocator<");
        if (start == -1)
292
            break;
293
294
295
296
297
298
299
300
301
302
303
304
        // 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;
            }
305
        }
306
307
308
        QString alloc = type.mid(start, pos + 1 - start).trimmed();
        QString inner = alloc.mid(15, alloc.size() - 16).trimmed();

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

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

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

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

381
QString WatchModel::displayType(const WatchData &data) const
382
{
383
384
385
    if (!data.displayedType.isEmpty())
        return data.displayedType;
    QString type = niceTypeHelper(data.type);
hjk's avatar
hjk committed
386
    if (!debuggerCore()->boolSetting(ShowStdNamespace))
387
        type.remove(QLatin1String("std::"));
hjk's avatar
hjk committed
388
    if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
389
390
391
392
        const QString qtNamespace = QString::fromLatin1(engine()->qtNamespace());
        if (!qtNamespace.isEmpty())
            type.remove(qtNamespace);
    }
393
394
395
    return type;
}

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
static inline int formatToIntegerBase(int format)
{
    switch (format) {
    case Debugger::Internal::HexadecimalFormat:
        return 16;
        break;
    case Debugger::Internal::BinaryFormat:
        return 2;
        break;
    case Debugger::Internal::OctalFormat:
        return 8;
    default:
        break;
    }
    return 10;
}

413
414
415
416
417
418
419
420
421
422
423
424
425
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
}

426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
// 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)
448
{
449
    if (isIntType(data.type)) {
450
451
452
453
454
455
456
        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.
457
        if (data.type.endsWith("char")) {
458
459
460
461
462
            bool ok;
            const int code = data.value.toInt(&ok);
            return ok ? reformatCharacter(code, format) : data.value;
        }
        // Rest: Leave decimal as is
463
464
        if (format <= 0)
            return data.value;
465
        // Evil hack, covers 'unsigned' as well as quint64.
466
        if (data.type.contains('u'))
467
            return reformatInteger(data.value.toULongLong(), format);
468
469
        return reformatInteger(data.value.toLongLong(), format);
    }
470
471
    QString result = data.value;
    result.replace(QLatin1Char('\n'), QLatin1String("\\n"));
472
473
474
475
476
477
478
479
480
481
482
    if (result.startsWith(QLatin1Char('<'))) {
        if (result == QLatin1String("<Edit>"))
            result = WatchHandler::tr("<Edit>");
        else if (result == QLatin1String("<empty>"))
            result = WatchHandler::tr("<empty>");
        else if (result == QLatin1String("<uninitialized>"))
            result = WatchHandler::tr("<uninitialized>");
        else if (result == QLatin1String("<invalid>"))
            result = WatchHandler::tr("<invalid>");
        else if (result == QLatin1String("<not accessible>"))
            result = WatchHandler::tr("<not accessible>");
Friedemann Kleint's avatar
Friedemann Kleint committed
483
        else if (result.endsWith(" items>")) {
484
485
486
487
488
489
490
491
492
            // '<10 items>' or '<>10 items>' (more than)
            bool ok;
            const bool moreThan = result.at(1) == QLatin1Char('>');
            const int numberPos = moreThan ? 2 : 1;
            const int size = result.mid(numberPos, result.indexOf(' ') - numberPos).toInt(&ok);
            QTC_ASSERT(ok, qWarning("WatchHandler: Invalid item count '%s'", qPrintable(result)) ; )
            result = moreThan ?
                     WatchHandler::tr("<more than %n items>", 0, size) :
                     WatchHandler::tr("<%n items>", 0, size);
Friedemann Kleint's avatar
Friedemann Kleint committed
493
        }
494
    }
495
    return result;
496
497
}

498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
// 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)
{
    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);
}

515
516
517
518
519
520
521
522
523
// 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;
524
525
526
    // Check for pointers using hex values (0xAD00 "Hallo")
    if (isPointerType(d.type) && d.value.startsWith(QLatin1String("0x")))
        return QVariant::ULongLong;
527
528
529
530
531
532
533
534
535
536
   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:
537
538
        if (isPointerType(d.type)) // Fix pointer values (0xAD00 "Hallo" -> 0xAD00)
            return QVariant(pointerValue(d.value));
539
540
541
542
543
544
545
546
547
548
549
550
551
552
        return QVariant(d.value.toULongLong());
    case QVariant::LongLong:
        return QVariant(d.value.toLongLong());
    case QVariant::Double:
        return QVariant(d.value.toDouble());
    default:
        break;
    }
    // Replace newlines, which will cause line edit troubles.
    QString stringValue;
    stringValue.replace(QLatin1String("\n"), QLatin1String("\\n"));
    return QVariant(stringValue);
}

553
bool WatchModel::canFetchMore(const QModelIndex &index) const
hjk's avatar
hjk committed
554
{
555
556
    WatchItem *item = watchItem(index);
    QTC_ASSERT(item, return false);
557
    return index.isValid() && !m_fetchTriggered.contains(item->iname);
558
559
560
561
562
}

void WatchModel::fetchMore(const QModelIndex &index)
{
    QTC_ASSERT(index.isValid(), return);
563
564
565
566
567
568
569
570
    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();
571
572
573
        WatchUpdateFlags flags;
        flags.tryIncremental = true;
        engine()->updateWatchData(data, flags);
574
575
576
577
578
579
580
581
582
583
    }
}

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());
584
585
    if (row >= item->children.size())
        return QModelIndex();
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
    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
607
608
609
610
611
612
613
614
615
}

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

616
int WatchModel::columnCount(const QModelIndex &idx) const
hjk's avatar
hjk committed
617
{
618
    Q_UNUSED(idx)
619
620
621
622
623
624
625
626
627
628
629
    return 3;
}

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

WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
{
630
    return idx.isValid()
631
632
633
634
635
636
637
638
        ? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
}

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

639
QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle,
640
641
642
643
644
645
646
647
648
649
    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
650
    }
651
    return QModelIndex();
hjk's avatar
hjk committed
652
653
}

654
void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
655
656
657
658
659
660
661
662
663
664
665
{
    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));
}

666
667
668
669
670
671
672
673
674
675
676
677
// 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;
}

678
679
680
681
682
683
684
685
int WatchModel::itemFormat(const WatchData &data) const
{
    const int individualFormat = m_handler->m_individualFormats.value(data.iname, -1);
    if (individualFormat != -1)
        return individualFormat;
    return m_handler->m_typeFormats.value(data.type, -1);
}

686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
static inline QString expression(const WatchItem *item)
{
    if (!item->exp.isEmpty())
         return QString::fromAscii(item->exp);
     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();
}

hjk's avatar
hjk committed
702
703
QVariant WatchModel::data(const QModelIndex &idx, int role) const
{
704
705
706
707
708
709
    switch (role) {
        case EngineCapabilitiesRole:
            return engine()->debuggerCapabilities();

        case EngineActionsEnabledRole:
            return engine()->debuggerActionsEnabled();
710
711
712

       case EngineStateRole:
            return QVariant(int(engine()->state()));
713
714
    }

715
716
    const WatchItem *item = watchItem(idx);
    const WatchItem &data = *item;
con's avatar
con committed
717
718

    switch (role) {
719
720
721
        case  LocalsEditTypeRole:
            return QVariant(editType(data));
       case LocalsIntegerBaseRole:
722
723
            if (isPointerType(data.type)) // Pointers using 0x-convention
                return QVariant(16);
724
            return QVariant(formatToIntegerBase(itemFormat(data)));
hjk's avatar
hjk committed
725
        case Qt::EditRole:
726
727
728
729
730
731
732
733
734
735
736
737
            switch (idx.column()) {
            case 0:
                return QVariant(expression(item));
            case 1:
                return editValue(data);
            case 2:
                if (!data.displayedType.isEmpty()) // To be tested: Can debuggers handle those?
                    return data.displayedType;
                return QString::fromUtf8(data.type);
            default: break;
            } // switch editrole column
        case Qt::DisplayRole:
738
            switch (idx.column()) {
739
                case 0:
740
                    if (data.name.isEmpty())
741
                        return tr("<Edit>");
742
                    if (data.name == QLatin1String("*") && item->parent)
743
                        return QVariant(QLatin1Char('*') + item->parent->name);
744
                    return data.name;
745
                case 1:
746
747
748
                    return truncateValue(formattedValue(data, itemFormat(data)));
                case 2:
                    return displayType(data);
con's avatar
con committed
749
                default: break;
750
            }  // switch editrole column
751
        case Qt::ToolTipRole:
hjk's avatar
hjk committed
752
            return debuggerCore()->boolSetting(UseToolTipsInLocalsView)
753
                ? data.toToolTip() : QVariant();
con's avatar
con committed
754
755
756
757

        case Qt::ForegroundRole: {
            static const QVariant red(QColor(200, 0, 0));
            static const QVariant gray(QColor(140, 140, 140));
758
            switch (idx.column()) {
759
                case 1: return !data.valueEnabled ? gray : data.changed ? red : QVariant();
con's avatar
con committed
760
761
762
763
            }
            break;
        }

764
765
        case LocalsExpressionRole:
            return QVariant(expression(item));
766
        case LocalsINameRole:
con's avatar
con committed
767
768
            return data.iname;

769
        case LocalsExpandedRole:
hjk's avatar
hjk committed
770
            return m_handler->m_expandedINames.contains(data.iname);
771

772
        case LocalsTypeFormatListRole: {
773
            if (isIntType(data.type) && data.type != "bool")
774
775
                return QStringList() << tr("decimal") << tr("hexadecimal")
                    << tr("binary") << tr("octal");
776
            if (data.type.endsWith('*'))
777
                return QStringList()
778
                    << tr("Raw pointer")
779
780
781
782
                    << tr("Latin1 string")
                    << tr("UTF8 string")
                    << tr("UTF16 string")
                    << tr("UCS4 string");
783
784
785
786
787
            // Hack: Compensate for namespaces.
            QString type = data.type;
            int pos = type.indexOf("::Q");
            if (pos >= 0 && type.count(':') == 2)
                type = type.mid(pos + 2);
788
789
790
            pos = type.indexOf('<');
            if (pos >= 0)
                type = type.left(pos);
791
792
            return m_handler->m_reportedTypeFormats.value(type);
        }
793

794
        case LocalsTypeFormatRole:
795
            return m_handler->m_typeFormats.value(data.type, -1);
796

797
        case LocalsIndividualFormatRole:
798
            return m_handler->m_individualFormats.value(data.iname, -1);
799

800
        case LocalsRawValueRole:
801
            return data.value;
802

803
        case LocalsPointerValueRole:
804
805
806
            if (isPointerType(data.type))
                return pointerValue(data.value);
            return QVariant(quint64(0));
807
808
809

        case LocalsIsWatchpointAtAddressRole:
            return engine()->breakHandler()
hjk's avatar
hjk committed
810
                ->hasWatchpointAt(data.coreAddress());
811
812
813
814
815
816
817

        case LocalsAddressRole:
            return data.coreAddress();

        case LocalsIsWatchpointAtPointerValueRole:
            if (isPointerType(data.type))
                return engine()->breakHandler()
hjk's avatar
hjk committed
818
                    ->hasWatchpointAt(pointerValue(data.value));
819
820
            return false;

con's avatar
con committed
821
        default:
822
            break;
con's avatar
con committed
823
824
825
826
    }
    return QVariant();
}

hjk's avatar
hjk committed
827
bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
828
{
829
    WatchItem &data = *watchItem(index);
830
831

    switch (role) {
832
833
834
835
836
837
838
839
840
841
842
        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;
            }
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
        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) {
861
                m_handler->m_individualFormats.remove(data.iname);
862
            } else {
863
                m_handler->m_individualFormats[data.iname] = format;
864
865
866
            }
            engine()->updateWatchData(data);
            break;
867
        }
hjk's avatar
hjk committed
868
    }
869

870
871
872
873
    emit dataChanged(index, index);
    return true;
}

hjk's avatar
hjk committed
874
Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
con's avatar
con committed
875
876
{
    if (!idx.isValid())
877
        return Qt::ItemFlags();
con's avatar
con committed
878
879
880
881

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

882
883
    static const Qt::ItemFlags notEditable = Qt::ItemIsSelectable| Qt::ItemIsEnabled;
    static const Qt::ItemFlags editable = notEditable | Qt::ItemIsEditable;
con's avatar
con committed
884

885
886
887
888
    // Disable editing if debuggee is positively running.
    const bool isRunning = engine() && engine()->state() == InferiorRunOk;
    if (isRunning)
        return notEditable;
con's avatar
con committed
889

hjk's avatar
hjk committed
890
    const WatchData &data = *watchItem(idx);
891
    if (data.isWatcher()) {
892
893
894
        if (idx.column() == 0 && data.iname.count('.') == 1)
            return editable; // Watcher names are editable.

895
        if (!data.name.isEmpty()) {
896
897
898
899
900
901
902
903
904
905
            // 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.
    }
906
    return notEditable;
con's avatar
con committed
907
908
}

hjk's avatar
hjk committed
909
QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
con's avatar
con committed
910
911
912
913
914
{
    if (orientation == Qt::Vertical)
        return QVariant();
    if (role == Qt::DisplayRole) {
        switch (section) {
hjk's avatar
hjk committed
915
916
917
            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
918
919
        }
    }
920
    return QVariant();
con's avatar
con committed
921
922
}

923
924
925
926
927
928
// Determine sort order of watch items by sort order or alphabetical inames
// according to setting 'SortStructMembers'. We need a map key for bulkInsert
// and a predicate for finding the insertion position of a single item.

// Set this before using any of the below according to action
static bool sortWatchDataAlphabetically = true;
929

930
static bool watchDataLessThan(const QByteArray &iname1, int sortId1, const QByteArray &iname2, int sortId2)
931
{
932
933
934
935
936
937
938
939
    if (!sortWatchDataAlphabetically)
        return sortId1 < sortId2;
    // Get positions of last part of iname 'local.this.i1" -> "i1"
    int cmpPos1 = iname1.lastIndexOf('.');
    if (cmpPos1 == -1) {
        cmpPos1 = 0;
    } else {
        cmpPos1++;
940
    }
941
942
943
944
945
946
947
948
949
950
951
952
953
    int cmpPos2 = iname2.lastIndexOf('.');
    if (cmpPos2 == -1) {
        cmpPos2 = 0;
    } else {
        cmpPos2++;
    }
    // Are we looking at an array with numerical inames 'local.this.i1.0" ->
    // Go by sort id.
    if (cmpPos1 < iname1.size() && cmpPos2 < iname2.size()
            && isdigit(iname1.at(cmpPos1)) && isdigit(iname2.at(cmpPos2)))
        return sortId1 < sortId2;
    // Alphabetically
    return qstrcmp(iname1.constData() + cmpPos1, iname2.constData() + cmpPos2) < 0;
954
955
}

956
957
958
959
960
961
962
963
964
// Sort key for watch data consisting of iname and numerical sort id.
struct WatchDataSortKey {
    explicit WatchDataSortKey(const WatchData &wd) :
             iname(wd.iname), sortId(wd.sortId) {}
    QByteArray iname;
    int sortId;
};

inline bool operator<(const WatchDataSortKey &k1, const WatchDataSortKey &k2)
965
{
966
    return watchDataLessThan(k1.iname, k1.sortId, k2.iname, k2.sortId);
967
}
968

969
bool watchItemSorter(const WatchItem *item1, const WatchItem *item2)
970
{
971
    return watchDataLessThan(item1->iname, item1->sortId, item2->iname, item2->sortId);
972
973
}

974
static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
975
{
hjk's avatar
hjk committed
976
    sortWatchDataAlphabetically = debuggerCore()->boolSetting(SortStructMembers);
977
978
    const QList<WatchItem *>::const_iterator it =
        qLowerBound(list.begin(), list.end(), item, watchItemSorter);
979
    return it - list.begin();
980
981
}

982
void WatchModel::insertData(const WatchData &data)
con's avatar
con committed
983
{
hjk's avatar
hjk committed
984
    //qDebug() << "WMI:" << data.toString();
985
    //static int bulk = 0;
hjk's avatar
hjk committed
986
    //qDebug() << "SINGLE: " << ++bulk << data.toString();
hjk's avatar
hjk committed
987
988
989
990
991
    if (data.iname.isEmpty()) {
        int x;
        x = 1;
    }
    QTC_ASSERT(!data.iname.isEmpty(), qDebug() << data.toString(); return);
992
993
994
995
    WatchItem *parent = findItem(parentName(data.iname), m_root);
    if (!parent) {
        WatchData parent;
        parent.iname = parentName(data.iname);
hjk's avatar
hjk committed
996
997
998
        MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname);
        if (!parent.iname.isEmpty())
            insertData(parent);
hjk's avatar
hjk committed
999
1000
        return;
    }
1001
1002
    QModelIndex index = watchIndex(parent);
    if (WatchItem *oldItem = findItem(data.iname, parent)) {
1003
1004
        bool hadChildren = oldItem->hasChildren;
        // Overwrite old entry.
1005
1006
1007
1008
1009
1010
1011
1012
        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));
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023

        // 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));
        }
1024
    } else {
1025
        // Add new entry.
1026
1027
1028
1029
        WatchItem *item = new WatchItem(data);
        item->parent = parent;
        item->generation = generationCounter;
        item