watchhandler.cpp 53.1 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2 3 4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.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
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.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 40
#include "debuggerengine.h"
#include "watchutils.h"
con's avatar
con committed
41

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

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

49 50
#include <cplusplus/CppRewriter.h>

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

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

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

con's avatar
con committed
64

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

68 69 70 71 72 73
// 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

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

77
static int watcherCounter = 0;
78 79
static int generationCounter = 0;

hjk's avatar
hjk committed
80 81
QHash<QByteArray, int> WatchHandler::m_watcherNames;
QHash<QByteArray, int> WatchHandler::m_typeFormats;
82
int WatchHandler::m_unprintableBase = 0;
hjk's avatar
hjk committed
83

84 85 86 87 88 89
static QByteArray stripTemplate(const QByteArray &ba)
{
    int pos = ba.indexOf('<');
    return pos == -1 ? ba : ba.left(pos);
}

con's avatar
con committed
90 91
////////////////////////////////////////////////////////////////////
//
92
// WatchItem
con's avatar
con committed
93 94
//
////////////////////////////////////////////////////////////////////
95

96 97 98
class WatchItem : public WatchData
{
public:
99
    WatchItem() { parent = 0; }
100

hjk's avatar
hjk committed
101 102 103 104 105 106
    ~WatchItem() {
        if (parent != 0)
            parent->children.removeOne(this);
        qDeleteAll(children);
    }

107
    WatchItem(const WatchData &data) : WatchData(data)
108
        { parent = 0; }
109 110

    void setData(const WatchData &data)
hjk's avatar
hjk committed
111
        { static_cast<WatchData &>(*this) = data; }
112 113 114 115 116

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

117

118
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
119
//
120
// WatchModel
hjk's avatar
hjk committed
121
//
122
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
123

124 125
WatchModel::WatchModel(WatchHandler *handler, WatchType type)
    : QAbstractItemModel(handler), m_handler(handler), m_type(type)
hjk's avatar
hjk committed
126
{
127 128 129 130
    m_root = new WatchItem;
    m_root->hasChildren = 1;
    m_root->state = 0;
    m_root->name = WatchHandler::tr("Root");
131
    m_root->parent = 0;
hjk's avatar
hjk committed
132

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

153 154 155 156 157
WatchModel::~WatchModel()
{
    delete m_root;
}

158
WatchItem *WatchModel::rootItem() const
hjk's avatar
hjk committed
159
{
160
    return m_root;
hjk's avatar
hjk committed
161 162
}

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

176 177 178 179 180
void WatchModel::emitAllChanged()
{
    emit layoutChanged();
}

181
void WatchModel::beginCycle()
182 183 184 185 186 187 188
{
    emit enableUpdates(false);
}

void WatchModel::endCycle()
{
    removeOutdated();
189
    m_fetchTriggered.clear();
190 191
    emit enableUpdates(true);
}
192

193 194 195 196
DebuggerEngine *WatchModel::engine() const
{
    return m_handler->m_engine;
}
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213

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

214
void WatchModel::removeOutdated()
hjk's avatar
hjk committed
215
{
216
    foreach (WatchItem *child, m_root->children)
217 218
        removeOutdatedHelper(child);
#if DEBUG_MODEL
hjk's avatar
hjk committed
219 220
#if USE_WATCH_MODEL_TEST
    (void) new ModelTest(this, this);
221 222
#endif
#endif
hjk's avatar
hjk committed
223 224
}

225
void WatchModel::removeOutdatedHelper(WatchItem *item)
hjk's avatar
hjk committed
226
{
227
    if (item->generation < generationCounter) {
228
        destroyItem(item);
229
    } else {
230 231
        foreach (WatchItem *child, item->children)
            removeOutdatedHelper(child);
232
    }
hjk's avatar
hjk committed
233 234
}

235
void WatchModel::destroyItem(WatchItem *item)
con's avatar
con committed
236
{
237 238 239 240 241 242 243
    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();
244
    delete item;
245 246
}

247
static QByteArray parentName(const QByteArray &iname)
248
{
249
    int pos = iname.lastIndexOf('.');
con's avatar
con committed
250
    if (pos == -1)
251
        return QByteArray();
con's avatar
con committed
252 253 254
    return iname.left(pos);
}

255
static QString niceTypeHelper(const QByteArray &typeIn)
256
{
257 258 259
    typedef QMap<QByteArray, QString> Cache;
    static Cache cache;
    const Cache::const_iterator it = cache.constFind(typeIn);
260
    if (it != cache.constEnd())
261
        return it.value();
262 263 264
    const QString simplified = CPlusPlus::simplifySTLType(typeIn);
    cache.insert(typeIn, simplified); // For simplicity, also cache unmodified types
    return simplified;
con's avatar
con committed
265 266
}

267
static QString removeNamespaces(QString str, const QByteArray &ns)
268
{
hjk's avatar
hjk committed
269
    if (!debuggerCore()->boolSetting(ShowStdNamespace))
270
        str.remove(QLatin1String("std::"));
hjk's avatar
hjk committed
271
    if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
272
        const QString qtNamespace = QString::fromLatin1(ns);
273
        if (!qtNamespace.isEmpty())
274 275 276 277 278 279 280 281 282 283 284 285 286 287
            str.remove(qtNamespace);
    }
    return str;
}

static QString removeInitialNamespace(QString str, const QByteArray &ns)
{
    if (str.startsWith(QLatin1String("std::"))
            && debuggerCore()->boolSetting(ShowStdNamespace))
        str = str.mid(5);
    if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
        const QString qtNamespace = QString::fromLatin1(ns);
        if (!qtNamespace.isEmpty() && str.startsWith(qtNamespace))
            str = str.mid(qtNamespace.size());
288
    }
289 290 291 292 293
    return str;
}

QString WatchModel::displayType(const WatchData &data) const
{
294
    QString base = data.displayedType.isEmpty()
295 296
        ? niceTypeHelper(data.type)
        : data.displayedType;
297 298 299
    if (data.bitsize)
        base += QString(":%1").arg(data.bitsize);
    return base;
300 301
}

hjk's avatar
hjk committed
302
static int formatToIntegerBase(int format)
303 304
{
    switch (format) {
hjk's avatar
hjk committed
305 306 307 308 309 310
        case HexadecimalFormat:
            return 16;
        case BinaryFormat:
            return 2;
        case OctalFormat:
            return 8;
311 312 313 314
    }
    return 10;
}

315 316 317
template <class IntType> QString reformatInteger(IntType value, int format)
{
    switch (format) {
hjk's avatar
hjk committed
318 319 320 321 322 323
        case HexadecimalFormat:
            return ("(hex) ") + QString::number(value, 16);
        case BinaryFormat:
            return ("(bin) ") + QString::number(value, 2);
        case OctalFormat:
            return ("(oct) ") + QString::number(value, 8);
324 325 326 327
    }
    return QString::number(value); // not reached
}

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
// 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)
350
{
351
    if (isIntType(data.type)) {
352 353 354 355 356 357 358
        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.
359
        if (data.type.endsWith("char")) {
360 361 362 363 364
            bool ok;
            const int code = data.value.toInt(&ok);
            return ok ? reformatCharacter(code, format) : data.value;
        }
        // Rest: Leave decimal as is
365 366
        if (format <= 0)
            return data.value;
367
        // Evil hack, covers 'unsigned' as well as quint64.
368
        if (data.type.contains('u'))
369
            return reformatInteger(data.value.toULongLong(), format);
370 371
        return reformatInteger(data.value.toLongLong(), format);
    }
372

373 374 375 376 377 378
    if (!isPointerType(data.type)) {
        bool ok = false;
        qulonglong integer = data.value.toULongLong(&ok, 0);
        if (ok)
           return reformatInteger(integer, format);
    }
379

380 381
    QString result = data.value;
    result.replace(QLatin1Char('\n'), QLatin1String("\\n"));
382 383 384 385 386 387 388 389 390 391 392
    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
393
        else if (result.endsWith(" items>")) {
394 395 396 397 398
            // '<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);
hjk's avatar
hjk committed
399 400
            QTC_ASSERT(ok, qWarning("WatchHandler: Invalid item count '%s'",
                qPrintable(result)))
401 402 403
            result = moreThan ?
                     WatchHandler::tr("<more than %n items>", 0, size) :
                     WatchHandler::tr("<%n items>", 0, size);
Friedemann Kleint's avatar
Friedemann Kleint committed
404
        }
405
    }
406

407
    return result;
408 409
}

410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
// 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);
}

427 428 429 430 431 432 433 434 435
// 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;
436 437 438
    // Check for pointers using hex values (0xAD00 "Hallo")
    if (isPointerType(d.type) && d.value.startsWith(QLatin1String("0x")))
        return QVariant::ULongLong;
439 440 441 442 443 444 445 446 447 448
   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:
449 450
        if (isPointerType(d.type)) // Fix pointer values (0xAD00 "Hallo" -> 0xAD00)
            return QVariant(pointerValue(d.value));
451 452 453 454 455 456 457 458 459
        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.
hjk's avatar
hjk committed
460
    QString stringValue = d.value;
461 462 463 464
    stringValue.replace(QLatin1String("\n"), QLatin1String("\\n"));
    return QVariant(stringValue);
}

465
bool WatchModel::canFetchMore(const QModelIndex &index) const
hjk's avatar
hjk committed
466
{
467 468
    WatchItem *item = watchItem(index);
    QTC_ASSERT(item, return false);
469
    return index.isValid() && !m_fetchTriggered.contains(item->iname);
470 471 472 473 474
}

void WatchModel::fetchMore(const QModelIndex &index)
{
    QTC_ASSERT(index.isValid(), return);
475 476 477 478 479 480 481 482
    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();
483 484 485
        WatchUpdateFlags flags;
        flags.tryIncremental = true;
        engine()->updateWatchData(data, flags);
486 487 488 489 490 491 492 493 494 495
    }
}

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());
496 497
    if (row >= item->children.size())
        return QModelIndex();
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
    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
519 520 521 522 523 524 525 526 527
}

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

528
int WatchModel::columnCount(const QModelIndex &idx) const
hjk's avatar
hjk committed
529
{
530
    Q_UNUSED(idx)
531 532 533 534 535 536 537 538 539 540 541
    return 3;
}

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

WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
{
542
    return idx.isValid()
543 544 545 546 547 548 549 550
        ? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
}

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

551
QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle,
552 553 554 555 556 557 558 559 560 561
    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
562
    }
563
    return QModelIndex();
hjk's avatar
hjk committed
564 565
}

566
void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
567 568 569 570 571 572 573 574 575 576 577
{
    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));
}

578 579 580 581 582 583 584 585 586 587 588 589
// 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;
}

590 591 592 593 594
int WatchModel::itemFormat(const WatchData &data) const
{
    const int individualFormat = m_handler->m_individualFormats.value(data.iname, -1);
    if (individualFormat != -1)
        return individualFormat;
595
    return m_handler->m_typeFormats.value(stripTemplate(data.type), -1);
596 597
}

598 599 600 601
static inline QString expression(const WatchItem *item)
{
    if (!item->exp.isEmpty())
         return QString::fromAscii(item->exp);
hjk's avatar
hjk committed
602 603 604 605 606 607 608 609 610 611
    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();
612 613
}

hjk's avatar
hjk committed
614 615
QVariant WatchModel::data(const QModelIndex &idx, int role) const
{
616 617
    const WatchItem *item = watchItem(idx);
    const WatchItem &data = *item;
con's avatar
con committed
618 619

    switch (role) {
hjk's avatar
hjk committed
620
        case LocalsEditTypeRole:
621
            return QVariant(editType(data));
hjk's avatar
hjk committed
622

623 624 625
        case LocalsNameRole:
            return QVariant(data.name);

hjk's avatar
hjk committed
626
        case LocalsIntegerBaseRole:
627 628
            if (isPointerType(data.type)) // Pointers using 0x-convention
                return QVariant(16);
629
            return QVariant(formatToIntegerBase(itemFormat(data)));
hjk's avatar
hjk committed
630 631

        case Qt::EditRole: {
632
            switch (idx.column()) {
hjk's avatar
hjk committed
633 634 635 636 637 638 639 640 641 642 643 644 645 646
                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;
            }
        }

647
        case Qt::DisplayRole: {
648
            const QByteArray qtNameSpace = engine()->qtNamespace();
649
            QString result;
650
            switch (idx.column()) {
651
                case 0:
652
                    if (data.name.isEmpty())
653 654 655 656
                        result = tr("<Edit>");
                    else if (data.name == QLatin1String("*") && item->parent)
                        result = QLatin1Char('*') + item->parent->name;
                    else
657
                        result = removeInitialNamespace(data.name, qtNameSpace);
658
                    break;
659
                case 1:
660
                    result = removeInitialNamespace(truncateValue(
661
                            formattedValue(data, itemFormat(data))), qtNameSpace);
662
                    if (data.referencingAddress) {
663
                        result += QLatin1String(" @");
664
                        result += QString::fromLatin1(data.hexAddress());
665
                    }
666
                    break;
667
                case 2:
668
                    result = removeNamespaces(displayType(data), qtNameSpace);
669
                    break;
670 671
                default:
                    break;
hjk's avatar
hjk committed
672
            }
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687
            if (WatchHandler::m_unprintableBase == 0)
                return result;
            QString encoded;
            foreach (const QChar c, result) {
                if (c.isPrint()) {
                    encoded += c;
                } else if (WatchHandler::m_unprintableBase == 8) {
                    encoded += QString("\\%1")
                        .arg(c.unicode(), 3, 8, QLatin1Char('0'));
                } else {
                    encoded += QString("\\u%1")
                        .arg(c.unicode(), 4, 16, QLatin1Char('0'));
                }
            }
            return encoded;
688
        }
hjk's avatar
hjk committed
689

690
        case Qt::ToolTipRole:
hjk's avatar
hjk committed
691
            return debuggerCore()->boolSetting(UseToolTipsInLocalsView)
692
                ? data.toToolTip() : QVariant();
con's avatar
con committed
693 694 695 696

        case Qt::ForegroundRole: {
            static const QVariant red(QColor(200, 0, 0));
            static const QVariant gray(QColor(140, 140, 140));
697
            switch (idx.column()) {
hjk's avatar
hjk committed
698 699
                case 1: return !data.valueEnabled ? gray
                            : data.changed ? red : QVariant();
con's avatar
con committed
700 701 702 703
            }
            break;
        }

704 705
        case LocalsExpressionRole:
            return QVariant(expression(item));
hjk's avatar
hjk committed
706

707 708 709
        case LocalsRawExpressionRole:
            return data.exp;

710
        case LocalsINameRole:
con's avatar
con committed
711 712
            return data.iname;

713
        case LocalsExpandedRole:
hjk's avatar
hjk committed
714
            return m_handler->m_expandedINames.contains(data.iname);
715

716
        case LocalsTypeFormatListRole: {
717
            if (data.referencingAddress || isPointerType(data.type))
718
                return QStringList()
719
                    << tr("Raw pointer")
720 721 722 723
                    << tr("Latin1 string")
                    << tr("UTF8 string")
                    << tr("UTF16 string")
                    << tr("UCS4 string");
724 725 726 727
            if (data.type.contains("char[") || data.type.contains("char ["))
                return QStringList()
                    << tr("Latin1 string")
                    << tr("UTF8 string");
728 729 730
            bool ok = false;
            (void)data.value.toULongLong(&ok, 0);
            if ((isIntType(data.type) && data.type != "bool") || ok)
731
                return QStringList()
732 733 734 735
                    << tr("Decimal")
                    << tr("Hexadecimal")
                    << tr("Binary")
                    << tr("Octal");
736
            // Hack: Compensate for namespaces.
737
            QString type = stripTemplate(data.type);
738 739 740
            int pos = type.indexOf("::Q");
            if (pos >= 0 && type.count(':') == 2)
                type = type.mid(pos + 2);
741 742 743
            pos = type.indexOf('<');
            if (pos >= 0)
                type = type.left(pos);
744
            type.replace(':', '_');
745 746
            return m_handler->m_reportedTypeFormats.value(type);
        }
747 748 749 750
        case LocalsTypeRole:
           return removeNamespaces(displayType(data), engine()->qtNamespace());
        case LocalsRawTypeRole:
           return QString::fromLatin1(data.type);
751
        case LocalsTypeFormatRole:
752
            return m_handler->m_typeFormats.value(stripTemplate(data.type), -1);
753

754
        case LocalsIndividualFormatRole:
755
            return m_handler->m_individualFormats.value(data.iname, -1);
756

757
        case LocalsRawValueRole:
758
            return data.value;
759

760
        case LocalsPointerValueRole:
761 762 763
            if (isPointerType(data.type))
                return pointerValue(data.value);
            return QVariant(quint64(0));
764

hjk's avatar
hjk committed
765
        case LocalsIsWatchpointAtAddressRole: {
766
            BreakpointParameters bp(WatchpointAtAddress);
hjk's avatar
hjk committed
767 768 769
            bp.address = data.coreAddress();
            return engine()->breakHandler()->findWatchpoint(bp) != 0;
        }
770 771 772

        case LocalsAddressRole:
            return data.coreAddress();
773 774
        case LocalsReferencingAddressRole:
            return QVariant(data.referencingAddress);
775 776
        case LocalsSizeRole:
            return QVariant(data.size);
777 778

        case LocalsIsWatchpointAtPointerValueRole:
hjk's avatar
hjk committed
779
            if (isPointerType(data.type)) {
780
                BreakpointParameters bp(WatchpointAtAddress);
hjk's avatar
hjk committed
781 782 783
                bp.address = pointerValue(data.value);
                return engine()->breakHandler()->findWatchpoint(bp) != 0;
            }
784 785
            return false;

con's avatar
con committed
786
        default:
787
            break;
con's avatar
con committed
788 789 790 791
    }
    return QVariant();
}

hjk's avatar
hjk committed
792
bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
793
{
794
    WatchItem &data = *watchItem(index);
795 796

    switch (role) {
797 798 799 800 801 802 803 804 805 806 807
        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;
            }
808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825
        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) {
826
                m_handler->m_individualFormats.remove(data.iname);
827
            } else {
828
                m_handler->m_individualFormats[data.iname] = format;
829 830 831
            }
            engine()->updateWatchData(data);
            break;
832
        }
hjk's avatar
hjk committed
833
    }
834

835 836 837 838
    emit dataChanged(index, index);
    return true;
}

hjk's avatar
hjk committed
839
Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
con's avatar
con committed
840 841
{
    if (!idx.isValid())
842
        return Qt::ItemFlags();
con's avatar
con committed
843

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

847
    static const Qt::ItemFlags notEditable
848
        = /* Qt::ItemIsSelectable | */ Qt::ItemIsEnabled;
849
    static const Qt::ItemFlags editable = notEditable | Qt::ItemIsEditable;
con's avatar
con committed
850

851 852
    // Disable editing if debuggee is positively running.
    const bool isRunning = engine() && engine()->state() == InferiorRunOk;
853
    if (isRunning && engine() && !engine()->acceptsWatchesWhileRunning())
854
        return notEditable;
con's avatar
con committed
855

hjk's avatar
hjk committed
856
    const WatchData &data = *watchItem(idx);
857
    if (data.isWatcher()) {
858 859 860
        if (idx.column() == 0 && data.iname.count('.') == 1)
            return editable; // Watcher names are editable.

861
        if (!data.name.isEmpty()) {
862 863 864 865 866 867 868 869 870 871
            // 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.
    }
872
    return notEditable;
con's avatar
con committed
873 874
}

hjk's avatar
hjk committed
875
QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
con's avatar
con committed
876 877 878 879 880
{
    if (orientation == Qt::Vertical)
        return QVariant();
    if (role == Qt::DisplayRole) {
        switch (section) {
hjk's avatar
hjk committed
881 882 883
            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
884 885
        }
    }
886
    return QVariant();
con's avatar
con committed
887 888
}

889 890 891 892 893 894
// 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;
895

896 897
static bool watchDataLessThan(const QByteArray &iname1, int sortId1,
    const QByteArray &iname2, int sortId2)
898
{
899 900 901 902 903 904 905 906
    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++;
907
    }
908 909 910 911 912 913 914 915 916 917 918 919 920
    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;
921 922
}

923
// Sort key for watch data consisting of iname and numerical sort id.
924 925 926 927
struct WatchDataSortKey
{
    explicit WatchDataSortKey(const WatchData &wd)
        : iname(wd.iname), sortId(wd.sortId) {}
928 929 930 931 932
    QByteArray iname;
    int sortId;
};

inline bool operator<(const WatchDataSortKey &k1, const WatchDataSortKey &k2)
933
{
934
    return watchDataLessThan(k1.iname, k1.sortId, k2.iname, k2.sortId);
935
}
936

937
bool watchItemSorter(const WatchItem *item1, const WatchItem *item2)
938
{
939
    return watchDataLessThan(item1->iname, item1->sortId, item2->iname, item2->sortId);
940 941
}

942
static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
943
{
hjk's avatar
hjk committed
944
    sortWatchDataAlphabetically = debuggerCore()->boolSetting(SortStructMembers);
945 946
    const QList<WatchItem *>::const_iterator it =
        qLowerBound(list.begin(), list.end(), item, watchItemSorter);
947
    return it - list.begin();
948 949
}

950
void WatchModel::insertData(const WatchData &data)
con's avatar
con committed
951
{
hjk's avatar
hjk committed
952
    QTC_ASSERT(!data.iname.isEmpty(), qDebug() << data.toString(); return);
953 954 955 956
    WatchItem *parent = findItem(parentName(data.iname), m_root);
    if (!parent) {
        WatchData parent;
        parent.iname = parentName(data.iname);
hjk's avatar