watchhandler.cpp 54.5 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
hjk's avatar
hjk committed
30

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

#include "breakhandler.h"
34
#include "debuggeractions.h"
35
#include "debuggercore.h"
36
#include "debuggerdialogs.h"
37 38 39
#include "debuggerengine.h"
#include "debuggerinternalconstants.h"
#include "debuggerprotocol.h"
40
#include "simplifytype.h"
41
#include "imageviewer.h"
42
#include "watchutils.h"
43
#include "cdb/cdbengine.h" // Remove after string freeze
con's avatar
con committed
44

45 46
#include <coreplugin/icore.h>

47
#include <utils/algorithm.h>
48
#include <utils/basetreeview.h>
hjk's avatar
hjk committed
49
#include <utils/qtcassert.h>
50
#include <utils/savedaction.h>
51
#include <utils/checkablemessagebox.h>
Thorben Kroeger's avatar
Thorben Kroeger committed
52
#include <utils/theme/theme.h>
con's avatar
con committed
53

54
#include <QDebug>
55
#include <QFile>
56
#include <QPainter>
57
#include <QProcess>
58
#include <QTabWidget>
59
#include <QTextEdit>
con's avatar
con committed
60

61
#include <QTimer>
62
#include <algorithm>
63
#include <cstring>
con's avatar
con committed
64
#include <ctype.h>
hjk's avatar
hjk committed
65

66 67
using namespace Utils;

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

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

#define MODEL_DEBUG(s) do { if (debugModel) qDebug() << s; } while (0)
hjk's avatar
hjk committed
75

76
static QHash<QByteArray, int> theWatcherNames;
77
static int theWatcherCount = 0;
78 79 80 81
static QHash<QByteArray, int> theTypeFormats;
static QHash<QByteArray, int> theIndividualFormats;
static int theUnprintableBase = -1;

82 83 84
const char INameProperty[] = "INameProperty";
const char KeyProperty[] = "KeyProperty";

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
static QByteArray stripForFormat(const QByteArray &ba)
{
    QByteArray res;
    res.reserve(ba.size());
    int inArray = 0;
    for (int i = 0; i != ba.size(); ++i) {
        const char c = ba.at(i);
        if (c == '<')
            break;
        if (c == '[')
            ++inArray;
        if (c == ']')
            --inArray;
        if (c == ' ')
            continue;
100 101
        if (c == '&') // Treat references like the referenced type.
            continue;
102 103 104 105 106
        if (inArray && c >= '0' && c <= '9')
            continue;
        res.append(c);
    }
    return res;
107 108
}

hjk's avatar
hjk committed
109 110 111 112 113
static void saveWatchers()
{
    setSessionValue("Watchers", WatchHandler::watchedExpressions());
}

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
static void loadFormats()
{
    QVariant value = sessionValue("DefaultFormats");
    QMapIterator<QString, QVariant> it(value.toMap());
    while (it.hasNext()) {
        it.next();
        if (!it.key().isEmpty())
            theTypeFormats.insert(it.key().toUtf8(), it.value().toInt());
    }

    value = sessionValue("IndividualFormats");
    it = QMapIterator<QString, QVariant>(value.toMap());
    while (it.hasNext()) {
        it.next();
        if (!it.key().isEmpty())
            theIndividualFormats.insert(it.key().toUtf8(), it.value().toInt());
    }
}

static void saveFormats()
{
    QMap<QString, QVariant> formats;
    QHashIterator<QByteArray, int> it(theTypeFormats);
    while (it.hasNext()) {
        it.next();
        const int format = it.value();
        if (format != AutomaticFormat) {
            const QByteArray key = it.key().trimmed();
            if (!key.isEmpty())
                formats.insert(QString::fromLatin1(key), format);
        }
    }
    setSessionValue("DefaultFormats", formats);

    formats.clear();
    it = QHashIterator<QByteArray, int>(theIndividualFormats);
    while (it.hasNext()) {
        it.next();
        const int format = it.value();
        const QByteArray key = it.key().trimmed();
        if (!key.isEmpty())
            formats.insert(QString::fromLatin1(key), format);
    }
    setSessionValue("IndividualFormats", formats);
}

160
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
161
//
hjk's avatar
hjk committed
162
// SeparatedView
hjk's avatar
hjk committed
163
//
164
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
165

166
class SeparatedView : public QTabWidget
167 168
{
public:
169
    SeparatedView() : QTabWidget(Internal::mainWindow())
170 171
    {
        setTabsClosable(true);
hjk's avatar
hjk committed
172
        connect(this, &QTabWidget::tabCloseRequested, this, &SeparatedView::closeTab);
173 174
        setWindowFlags(windowFlags() | Qt::Window);
        setWindowTitle(WatchHandler::tr("Debugger - Qt Creator"));
175

hjk's avatar
hjk committed
176
        QVariant geometry = sessionValue("DebuggerSeparateWidgetGeometry");
177 178 179 180 181 182 183 184
        if (geometry.isValid()) {
            QRect rc = geometry.toRect();
            if (rc.width() < 200)
                rc.setWidth(200);
            if (rc.height() < 200)
                rc.setHeight(200);
            setGeometry(rc);
        }
185 186
    }

187
    void saveGeometry()
188
    {
hjk's avatar
hjk committed
189
        setSessionValue("DebuggerSeparateWidgetGeometry", geometry());
190 191
    }

192 193 194 195 196
    ~SeparatedView()
    {
        saveGeometry();
    }

197
    void removeObject(const QByteArray &key)
198
    {
199
        saveGeometry();
200 201 202 203 204 205
        if (QWidget *w = findWidget(key)) {
            removeTab(indexOf(w));
            sanitize();
        }
    }

hjk's avatar
hjk committed
206
    void closeTab(int index)
207
    {
208
        saveGeometry();
209 210 211
        if (QObject *o = widget(index)) {
            QByteArray iname = o->property(INameProperty).toByteArray();
            theIndividualFormats.remove(iname);
212
            saveFormats();
213
        }
214
        removeTab(index);
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
        sanitize();
    }

    void sanitize()
    {
        if (count() == 0)
            hide();
    }

    QWidget *findWidget(const QByteArray &needle)
    {
        for (int i = count(); --i >= 0; ) {
            QWidget *w = widget(i);
            QByteArray key = w->property(KeyProperty).toByteArray();
            if (key == needle)
                return w;
        }
        return 0;
    }

235
    template <class T> T *prepareObject(const QByteArray &key, const QString &tabName)
236 237 238 239 240 241 242 243 244 245
    {
        T *t = 0;
        if (QWidget *w = findWidget(key)) {
            t = qobject_cast<T *>(w);
            if (!t)
                removeTab(indexOf(w));
        }
        if (!t) {
            t = new T;
            t->setProperty(KeyProperty, key);
246
            addTab(t, tabName);
247 248 249 250 251 252
        }

        setCurrentWidget(t);
        show();
        raise();
        return t;
253 254 255
    }
};

256

hjk's avatar
hjk committed
257 258 259 260 261
///////////////////////////////////////////////////////////////////////
//
// WatchModel
//
///////////////////////////////////////////////////////////////////////
hjk's avatar
hjk committed
262

hjk's avatar
hjk committed
263 264
class WatchModel : public WatchModelBase
{
265
    Q_DECLARE_TR_FUNCTIONS(Debugger::Internal::WatchModel)
hjk's avatar
hjk committed
266
public:
hjk's avatar
hjk committed
267
    WatchModel(WatchHandler *handler, DebuggerEngine *engine);
hjk's avatar
hjk committed
268

hjk's avatar
hjk committed
269 270
    static QString nameForFormat(int format);

271
    QVariant data(const QModelIndex &idx, int role) const;
hjk's avatar
hjk committed
272 273 274
    bool setData(const QModelIndex &idx, const QVariant &value, int role);

    QString displayForAutoTest(const QByteArray &iname) const;
275
    void reinitialize(bool includeInspectData = false);
hjk's avatar
hjk committed
276

277 278 279
    WatchItem *findItem(const QByteArray &iname) const;
    void insertItem(WatchItem *item);
    void reexpandItems();
hjk's avatar
hjk committed
280

281 282 283
    void showEditValue(const WatchItem *item);
    void setTypeFormat(const QByteArray &type, int format);
    void setIndividualFormat(const QByteArray &iname, int format);
hjk's avatar
hjk committed
284 285 286

    QString removeNamespaces(QString str) const;

287
public:
hjk's avatar
hjk committed
288
    WatchHandler *m_handler; // Not owned.
hjk's avatar
hjk committed
289 290 291 292
    DebuggerEngine *m_engine; // Not owned.

    bool m_contentsValid;
    bool m_resetLocationScheduled;
hjk's avatar
hjk committed
293

294
    WatchItem *root() const { return static_cast<WatchItem *>(rootItem()); }
hjk's avatar
hjk committed
295 296 297 298 299 300
    WatchItem *m_localsRoot; // Not owned.
    WatchItem *m_inspectorRoot; // Not owned.
    WatchItem *m_watchRoot; // Not owned.
    WatchItem *m_returnRoot; // Not owned.
    WatchItem *m_tooltipRoot; // Not owned.

hjk's avatar
hjk committed
301
    SeparatedView *m_separatedView; // Not owned.
302

hjk's avatar
hjk committed
303
    QSet<QByteArray> m_expandedINames;
304
    QSet<QByteArray> m_fetchTriggered;
305
    QTimer m_requestUpdateTimer;
hjk's avatar
hjk committed
306

307
    QHash<QString, DisplayFormats> m_reportedTypeFormats; // Type name -> Dumper Formats
308
    QHash<QByteArray, QString> m_valueCache;
hjk's avatar
hjk committed
309 310
};

hjk's avatar
hjk committed
311 312
WatchModel::WatchModel(WatchHandler *handler, DebuggerEngine *engine)
    : m_handler(handler), m_engine(engine), m_separatedView(new SeparatedView)
hjk's avatar
hjk committed
313
{
314
    setObjectName(QLatin1String("WatchModel"));
315

hjk's avatar
hjk committed
316 317 318 319
    m_contentsValid = false;
    m_contentsValid = true; // FIXME
    m_resetLocationScheduled = false;

320 321 322 323 324 325 326 327
    setHeader(QStringList() << tr("Name") << tr("Value") << tr("Type"));
    auto root = new WatchItem;
    root->appendChild(m_localsRoot = new WatchItem("local", tr("Locals")));
    root->appendChild(m_inspectorRoot = new WatchItem("inspect", tr("Inspector")));
    root->appendChild(m_watchRoot = new WatchItem("watch", tr("Expressions")));
    root->appendChild(m_returnRoot = new WatchItem("return", tr("Return Value")));
    root->appendChild(m_tooltipRoot = new WatchItem("tooltip", tr("Tooltip")));
    setRootItem(root);
hjk's avatar
hjk committed
328

329 330 331 332
    m_requestUpdateTimer.setSingleShot(true);
    connect(&m_requestUpdateTimer, &QTimer::timeout,
        this, &WatchModel::updateStarted);

333
    connect(action(SortStructMembers), &SavedAction::valueChanged,
334
        m_engine, &DebuggerEngine::updateAll);
335
    connect(action(ShowStdNamespace), &SavedAction::valueChanged,
336
        m_engine, &DebuggerEngine::updateAll);
337
    connect(action(ShowQtNamespace), &SavedAction::valueChanged,
338
        m_engine, &DebuggerEngine::updateAll);
hjk's avatar
hjk committed
339 340
}

341
void WatchModel::reinitialize(bool includeInspectData)
342
{
343 344 345 346 347 348
    m_localsRoot->removeChildren();
    m_watchRoot->removeChildren();
    m_returnRoot->removeChildren();
    m_tooltipRoot->removeChildren();
    if (includeInspectData)
        m_inspectorRoot->removeChildren();
349
}
350

351
WatchItem *WatchModel::findItem(const QByteArray &iname) const
352
{
353
    return root()->findItem(iname);
354 355
}

356
WatchItem *WatchItem::findItem(const QByteArray &iname)
hjk's avatar
hjk committed
357
{
358
    if (this->iname == iname)
359 360 361
        return this;
    foreach (TreeItem *child, children()) {
        auto witem = static_cast<WatchItem *>(child);
362
        if (witem->iname == iname)
363
            return witem;
364
        if (witem->isAncestorOf(iname))
365
            return witem->findItem(iname);
366
    }
367
    return 0;
hjk's avatar
hjk committed
368 369
}

370
static QByteArray parentName(const QByteArray &iname)
371
{
hjk's avatar
hjk committed
372 373
    const int pos = iname.lastIndexOf('.');
    return pos == -1 ? QByteArray() : iname.left(pos);
con's avatar
con committed
374 375
}

376
static QString niceTypeHelper(const QByteArray &typeIn)
377
{
378 379 380
    typedef QMap<QByteArray, QString> Cache;
    static Cache cache;
    const Cache::const_iterator it = cache.constFind(typeIn);
381
    if (it != cache.constEnd())
382
        return it.value();
383
    const QString simplified = simplifyType(QLatin1String(typeIn));
384 385
    cache.insert(typeIn, simplified); // For simplicity, also cache unmodified types
    return simplified;
con's avatar
con committed
386 387
}

388
QString WatchModel::removeNamespaces(QString str) const
389
{
hjk's avatar
hjk committed
390
    if (!boolSetting(ShowStdNamespace))
391
        str.remove(QLatin1String("std::"));
hjk's avatar
hjk committed
392
    if (!boolSetting(ShowQtNamespace)) {
hjk's avatar
hjk committed
393
        const QString qtNamespace = QString::fromLatin1(m_engine->qtNamespace());
394
        if (!qtNamespace.isEmpty())
395 396 397 398 399
            str.remove(qtNamespace);
    }
    return str;
}

hjk's avatar
hjk committed
400
static int formatToIntegerBase(int format)
401 402
{
    switch (format) {
hjk's avatar
hjk committed
403
        case HexadecimalIntegerFormat:
hjk's avatar
hjk committed
404
            return 16;
hjk's avatar
hjk committed
405
        case BinaryIntegerFormat:
hjk's avatar
hjk committed
406
            return 2;
hjk's avatar
hjk committed
407
        case OctalIntegerFormat:
hjk's avatar
hjk committed
408
            return 8;
409 410 411 412
    }
    return 10;
}

413 414 415
template <class IntType> QString reformatInteger(IntType value, int format)
{
    switch (format) {
hjk's avatar
hjk committed
416
        case HexadecimalIntegerFormat:
417
            return QLatin1String("(hex) ") + QString::number(value, 16);
hjk's avatar
hjk committed
418
        case BinaryIntegerFormat:
419
            return QLatin1String("(bin) ") + QString::number(value, 2);
hjk's avatar
hjk committed
420
        case OctalIntegerFormat:
421
            return QLatin1String("(oct) ") + QString::number(value, 8);
422
    }
423 424 425 426 427 428
    return QString::number(value, 10); // not reached
}

static QString reformatInteger(quint64 value, int format, int size, bool isSigned)
{
    // Follow convention and don't show negative non-decimal numbers.
429
    if (format != AutomaticFormat && format != DecimalIntegerFormat)
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
        isSigned = false;

    switch (size) {
        case 1:
            value = value & 0xff;
            return isSigned
                ? reformatInteger<qint8>(value, format)
                : reformatInteger<quint8>(value, format);
        case 2:
            value = value & 0xffff;
            return isSigned
                ? reformatInteger<qint16>(value, format)
                : reformatInteger<quint16>(value, format);
        case 4:
            value = value & 0xffffffff;
            return isSigned
                ? reformatInteger<qint32>(value, format)
                : reformatInteger<quint32>(value, format);
        default:
        case 8: return isSigned
                ? reformatInteger<qint64>(value, format)
                : reformatInteger<quint64>(value, format);
    }
453 454
}

455
// Format printable (char-type) characters
456
static QString reformatCharacter(int code, int format, bool isSigned)
457
{
458
    const QString codeS = reformatInteger(code, format, 1, isSigned);
459
    if (code < 0) // Append unsigned value.
460
        return codeS + QLatin1String(" / ") + reformatInteger(256 + code, format, 1, false);
461
    const QChar c = QChar(uint(code));
462 463
    if (c.isPrint())
        return codeS + QLatin1String(" '") + c + QLatin1Char('\'');
464 465 466 467 468 469 470 471 472 473 474 475 476
    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;
}

477 478 479 480
static QString quoteUnprintable(const QString &str)
{
    if (WatchHandler::unprintableBase() == 0)
        return str;
481

482
    QString encoded;
483 484 485
    if (WatchHandler::unprintableBase() == -1) {
        foreach (const QChar c, str) {
            int u = c.unicode();
486
            if (c.isPrint())
487
                encoded += c;
488
            else if (u == '\r')
489
                encoded += QLatin1String("\\r");
490
            else if (u == '\t')
491
                encoded += QLatin1String("\\t");
492
            else if (u == '\n')
493
                encoded += QLatin1String("\\n");
494
            else
495
                encoded += QString::fromLatin1("\\%1")
496 497 498 499 500
                    .arg(c.unicode(), 3, 8, QLatin1Char('0'));
        }
        return encoded;
    }

501 502 503 504
    foreach (const QChar c, str) {
        if (c.isPrint()) {
            encoded += c;
        } else if (WatchHandler::unprintableBase() == 8) {
505
            encoded += QString::fromLatin1("\\%1")
506 507
                .arg(c.unicode(), 3, 8, QLatin1Char('0'));
        } else {
508
            encoded += QString::fromLatin1("\\u%1")
509 510 511 512 513 514
                .arg(c.unicode(), 4, 16, QLatin1Char('0'));
        }
    }
    return encoded;
}

515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
static QString translate(const QString &str)
{
    if (str.startsWith(QLatin1Char('<'))) {
        if (str == QLatin1String("<empty>"))
            return WatchHandler::tr("<empty>");
        if (str == QLatin1String("<uninitialized>"))
            return WatchHandler::tr("<uninitialized>");
        if (str == QLatin1String("<invalid>"))
            return WatchHandler::tr("<invalid>");
        if (str == QLatin1String("<not accessible>"))
            return WatchHandler::tr("<not accessible>");
        if (str.endsWith(QLatin1String(" items>"))) {
            // '<10 items>' or '<>10 items>' (more than)
            bool ok;
            const bool moreThan = str.at(1) == QLatin1Char('>');
            const int numberPos = moreThan ? 2 : 1;
            const int len = str.indexOf(QLatin1Char(' ')) - numberPos;
            const int size = str.mid(numberPos, len).toInt(&ok);
            QTC_ASSERT(ok, qWarning("WatchHandler: Invalid item count '%s'",
534
                qPrintable(str)));
535 536 537 538 539 540 541 542
            return moreThan ?
                     WatchHandler::tr("<more than %n items>", 0, size) :
                     WatchHandler::tr("<%n items>", 0, size);
        }
    }
    return quoteUnprintable(str);
}

543
QString WatchItem::formattedValue() const
544
{
545 546
    if (type == "bool") {
        if (value == QLatin1String("0"))
547
            return QLatin1String("false");
548
        if (value == QLatin1String("1"))
549
            return QLatin1String("true");
550
        return value;
551 552
    }

553
    const int format = itemFormat();
554

hjk's avatar
hjk committed
555
    // Append quoted, printable character also for decimal.
556
    // FIXME: This is unreliable.
557
    if (type.endsWith("char") || type.endsWith("QChar")) {
hjk's avatar
hjk committed
558
        bool ok;
559
        const int code = value.toInt(&ok);
560 561
        bool isUnsigned = type == "unsigned char" || type == "uchar";
        return ok ? reformatCharacter(code, format, !isUnsigned) : value;
hjk's avatar
hjk committed
562 563 564 565 566 567
    }

    if (format == HexadecimalIntegerFormat
            || format == DecimalIntegerFormat
            || format == OctalIntegerFormat
            || format == BinaryIntegerFormat) {
568 569 570
        bool isSigned = value.startsWith(QLatin1Char('-'));
        quint64 raw = isSigned ? quint64(value.toLongLong()) : value.toULongLong();
        return reformatInteger(raw, format, size, isSigned);
571
    }
572

hjk's avatar
hjk committed
573
    if (format == ScientificFloatFormat) {
574
        double dd = value.toDouble();
575
        return QString::number(dd, 'e');
hjk's avatar
hjk committed
576 577 578
    }

    if (format == CompactFloatFormat) {
579
        double dd = value.toDouble();
580
        return QString::number(dd, 'g');
hjk's avatar
hjk committed
581 582
    }

583 584
    if (type == "va_list")
        return value;
hjk's avatar
hjk committed
585

586
    if (!isPointerType(type) && !isVTablePointer()) {
587
        bool ok = false;
588
        qulonglong integer = value.toULongLong(&ok, 0);
589
        if (ok) {
590
            const int format = itemFormat();
591
            return reformatInteger(integer, format, size, false);
592
        }
593
    }
594

595 596
    if (elided) {
        QString v = value;
597
        v.chop(1);
598
        v = translate(v);
599
        QString len = elided > 0 ? QString::number(elided) : QLatin1String("unknown length");
600 601 602
        return v + QLatin1String("\"... (") + len  + QLatin1Char(')');
    }

603
    return translate(value);
604 605
}

606 607 608 609 610 611 612 613 614
// Get a pointer address from pointer values reported by the debugger.
// Fix CDB formatting of pointers "0x00000000`000003fd class foo *", or
// "0x00000000`000003fd "Hallo"", or check gdb formatting of characters.
static inline quint64 pointerValue(QString data)
{
    const int blankPos = data.indexOf(QLatin1Char(' '));
    if (blankPos != -1)
        data.truncate(blankPos);
    data.remove(QLatin1Char('`'));
615
    return data.toULongLong(0, 0);
616 617
}

618
// Return the type used for editing
619
int WatchItem::editType() const
620
{
621
    if (type == "bool")
622
        return QVariant::Bool;
623 624 625
    if (isIntType(type))
        return type.contains('u') ? QVariant::ULongLong : QVariant::LongLong;
    if (isFloatType(type))
626
        return QVariant::Double;
627
    // Check for pointers using hex values (0xAD00 "Hallo")
628
    if (isPointerType(type) && value.startsWith(QLatin1String("0x")))
629
        return QVariant::ULongLong;
630 631 632 633
   return QVariant::String;
}

// Convert to editable (see above)
634
QVariant WatchItem::editValue() const
635
{
636
    switch (editType()) {
637
    case QVariant::Bool:
638
        return value != QLatin1String("0") && value != QLatin1String("false");
639
    case QVariant::ULongLong:
640 641 642
        if (isPointerType(type)) // Fix pointer values (0xAD00 "Hallo" -> 0xAD00)
            return QVariant(pointerValue(value));
        return QVariant(value.toULongLong());
643
    case QVariant::LongLong:
644
        return QVariant(value.toLongLong());
645
    case QVariant::Double:
646
        return QVariant(value.toDouble());
647 648 649
    default:
        break;
    }
650 651
    // Some string value: '0x434 "Hallo"':
    // Remove quotes and replace newlines, which will cause line edit troubles.
652
    QString stringValue = value;
653 654 655 656 657 658 659 660
    if (stringValue.endsWith(QLatin1Char('"'))) {
        const int leadingDoubleQuote = stringValue.indexOf(QLatin1Char('"'));
        if (leadingDoubleQuote != stringValue.size() - 1) {
            stringValue.truncate(stringValue.size() - 1);
            stringValue.remove(0, leadingDoubleQuote + 1);
            stringValue.replace(QLatin1String("\n"), QLatin1String("\\n"));
        }
    }
661
    return QVariant(translate(stringValue));
662 663
}

664
bool WatchItem::canFetchMore() const
hjk's avatar
hjk committed
665
{
666
    if (!wantsChildren)
667
        return false;
668 669
    const WatchModel *model = watchModel();
    if (!model)
670
        return false;
671
    if (!model->m_contentsValid && !isInspect())
hjk's avatar
hjk committed
672
        return false;
673
    return !model->m_fetchTriggered.contains(iname);
674 675
}

676
void WatchItem::fetchMore()
677
{
678 679
    WatchModel *model = watchModel();
    if (model->m_fetchTriggered.contains(iname))
hjk's avatar
hjk committed
680 681
        return;

682 683
    model->m_expandedINames.insert(iname);
    model->m_fetchTriggered.insert(iname);
684
    if (children().isEmpty()) {
685
        setChildrenNeeded();
hjk's avatar
hjk committed
686
        model->m_engine->expandItem(iname);
hjk's avatar
hjk committed
687
    }
688 689
}

690 691
// Truncate value for item view, maintaining quotes.
static QString truncateValue(QString v)
692 693 694 695 696 697 698 699 700 701
{
    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;
}

702
int WatchItem::itemFormat() const
703
{
704
    const int individualFormat = theIndividualFormats.value(iname, AutomaticFormat);
hjk's avatar
hjk committed
705
    if (individualFormat != AutomaticFormat)
706
        return individualFormat;
707
    return theTypeFormats.value(stripForFormat(type), AutomaticFormat);
708 709
}

710
QString WatchItem::expression() const
711
{
712 713 714
    if (!exp.isEmpty())
         return QString::fromLatin1(exp);
    if (address && !type.isEmpty()) {
715
        return QString::fromLatin1("*(%1*)%2").
716
                arg(QLatin1String(type), QLatin1String(hexAddress()));
hjk's avatar
hjk committed
717
    }
718
    if (const WatchItem *p = parentItem()) {
719 720
        if (!p->exp.isEmpty())
           return QString::fromLatin1("(%1).%2").arg(QString::fromLatin1(p->exp), name);
hjk's avatar
hjk committed
721
    }
722
    return name;
723 724
}

725
QString WatchItem::displayName() const
726 727
{
    QString result;
hjk's avatar
hjk committed
728
    if (!parentItem())
729
        return result;
730
    if (iname.startsWith("return") && name.startsWith(QLatin1Char('$')))
731
        result = WatchModel::tr("returned value");
732 733
    else if (name == QLatin1String("*"))
        result = QLatin1Char('*') + parentItem()->name;
734
    else
735
        result = watchModel()->removeNamespaces(name);
736 737 738 739 740

    // Simplyfy names that refer to base classes.
    if (result.startsWith(QLatin1Char('['))) {
        result = simplifyType(result);
        if (result.size() > 30)
Fawzi Mohamed's avatar
Fawzi Mohamed committed
741
            result = result.left(27) + QLatin1String("...]");
742 743
    }

744 745 746
    return result;
}

747
QString WatchItem::displayValue() const
748
{
749
    QString result = watchModel()->removeNamespaces(truncateValue(formattedValue()));
750 751 752 753
    if (result.isEmpty() && address)
        result += QString::fromLatin1("@0x" + QByteArray::number(address, 16));
//    if (origaddr)
//        result += QString::fromLatin1(" (0x" + QByteArray::number(origaddr, 16) + ')');
754 755 756
    return result;
}

757
QString WatchItem::displayType() const
758
{
759 760 761 762 763
    QString result = displayedType.isEmpty()
        ? niceTypeHelper(type)
        : displayedType;
    if (bitsize)
        result += QString::fromLatin1(":%1").arg(bitsize);
764
    result.remove(QLatin1Char('\''));
765
    result = watchModel()->removeNamespaces(result);
766 767 768
    return result;
}

hjk's avatar
hjk committed
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
QColor WatchItem::valueColor(int column) const
{
    Theme::Color color = Theme::Debugger_WatchItem_ValueNormal;
    if (const WatchModel *model = watchModel()) {
        if (!model->m_contentsValid && !isInspect()) {
            color = Theme::Debugger_WatchItem_ValueInvalid;
        } else if (column == 1) {
            if (!valueEnabled)
                color = Theme::Debugger_WatchItem_ValueInvalid;
            else if (!model->m_contentsValid && !isInspect())
                color = Theme::Debugger_WatchItem_ValueInvalid;
            else if (column == 1 && value.isEmpty()) // This might still show 0x...
                color = Theme::Debugger_WatchItem_ValueInvalid;
            else if (column == 1 && value != model->m_valueCache.value(iname))
                color = Theme::Debugger_WatchItem_ValueChanged;
        }
    }
    return creatorTheme()->color(color);
hjk's avatar
hjk committed
787 788
}

789
QVariant WatchItem::data(int column, int role) const
hjk's avatar
hjk committed
790
{
con's avatar
con committed
791
    switch (role) {
hjk's avatar
hjk committed
792
        case LocalsEditTypeRole:
793
            return QVariant(editType());
hjk's avatar
hjk committed
794

795
        case LocalsNameRole:
796
            return QVariant(name);
797

hjk's avatar
hjk committed
798
        case LocalsIntegerBaseRole:
799
            if (isPointerType(type)) // Pointers using 0x-convention
800
                return QVariant(16);
801
            return QVariant(formatToIntegerBase(itemFormat()));
hjk's avatar
hjk committed
802 803

        case Qt::EditRole: {
804
            switch (column) {
hjk's avatar
hjk committed
805
                case 0:
806
                    return expression();
hjk's avatar
hjk committed
807
                case 1:
808
                    return editValue();
hjk's avatar
hjk committed
809 810
                case 2:
                    // FIXME:: To be tested: Can debuggers handle those?
811 812 813
                    if (!displayedType.isEmpty())
                        return displayedType;
                    return QString::fromUtf8(type);
hjk's avatar
hjk committed
814 815 816
            }
        }

817
        case Qt::DisplayRole: {
818
            switch (column) {
819
                case 0:
820
                    return displayName();
821
                case 1:
hjk's avatar