watchhandler.cpp 31.6 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 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
26
** contact the sales department at http://www.qtsoftware.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
#include "watchutils.h"
32
#include "debuggeractions.h"
con's avatar
con committed
33
34
35
36
37

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

hjk's avatar
hjk committed
38
#include <utils/qtcassert.h>
con's avatar
con committed
39
40
41

#include <QtCore/QDebug>
#include <QtCore/QEvent>
42
#include <QtCore/QtAlgorithms>
43
#include <QtCore/QTextStream>
hjk's avatar
hjk committed
44
#include <QtCore/QTimer>
con's avatar
con committed
45

46
#include <QtGui/QAction>
con's avatar
con committed
47
48
49
50
51
52
53
#include <QtGui/QApplication>
#include <QtGui/QLabel>
#include <QtGui/QToolTip>
#include <QtGui/QTextEdit>

#include <ctype.h>

hjk's avatar
hjk committed
54

con's avatar
con committed
55
// creates debug output regarding pending watch data results
hjk's avatar
hjk committed
56
//#define DEBUG_PENDING 1
57
58
// creates debug output for accesses to the model
//#define DEBUG_MODEL 1
con's avatar
con committed
59
60
61
62
63
64
65
66

#if DEBUG_MODEL
#   define MODEL_DEBUG(s) qDebug() << s
#else
#   define MODEL_DEBUG(s) 
#endif
#define MODEL_DEBUGX(s) qDebug() << s

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

70
71
static const QString strNotInScope =
        QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
con's avatar
con committed
72

73
static int watcherCounter = 0;
hjk's avatar
hjk committed
74
75
76
77
78
79
80
81
82
83
84
static int generationCounter = 0;

////////////////////////////////////////////////////////////////////
//
// WatchItem
//
////////////////////////////////////////////////////////////////////

class WatchItem : public WatchData
{
public:
85
    WatchItem() { parent = 0; fetchTriggered = false; }
hjk's avatar
hjk committed
86
87

    WatchItem(const WatchData &data) : WatchData(data)
88
        { parent = 0; fetchTriggered = false; }
hjk's avatar
hjk committed
89
90
91
92
93

    void setData(const WatchData &data)
        { static_cast<WatchData &>(*this) = data; }

    WatchItem *parent;
94
    bool fetchTriggered;      // children fetch has been triggered
hjk's avatar
hjk committed
95
96
97
    QList<WatchItem *> children;  // fetched children
};

con's avatar
con committed
98
99
100
101
102
////////////////////////////////////////////////////////////////////
//
// WatchData
//
////////////////////////////////////////////////////////////////////
103
104
105
106
107
108
109
110
   
WatchData::WatchData() :
    hasChildren(false),
    generation(-1),
    valuedisabled(false),
    source(0),
    state(InitialState),
    changed(false)
con's avatar
con committed
111
112
113
114
115
116
117
{
}

void WatchData::setError(const QString &msg)
{
    setAllUnneeded();
    value = msg;
118
    setHasChildren(false);
con's avatar
con committed
119
120
121
    valuedisabled = true;
}

hjk's avatar
hjk committed
122
void WatchData::setValue(const QString &value0)
con's avatar
con committed
123
{
hjk's avatar
hjk committed
124
    value = value0;
con's avatar
con committed
125
126
    if (value == "{...}") {
        value.clear();
127
        hasChildren = true; // at least one...
con's avatar
con committed
128
129
130
131
132
133
134
135
136
137
138
    }

    // avoid duplicated information
    if (value.startsWith("(") && value.contains(") 0x"))
        value = value.mid(value.lastIndexOf(") 0x") + 2);

    // doubles are sometimes displayed as "@0x6141378: 1.2".
    // I don't want that.
    if (/*isIntOrFloatType(type) && */ value.startsWith("@0x")
         && value.contains(':')) {
        value = value.mid(value.indexOf(':') + 2);
139
        setHasChildren(false);
con's avatar
con committed
140
141
142
143
144
    }

    // "numchild" is sometimes lying
    //MODEL_DEBUG("\n\n\nPOINTER: " << type << value);
    if (isPointerType(type))
145
        setHasChildren(value != "0x0" && value != "<null>");
con's avatar
con committed
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

    // pointer type information is available in the 'type'
    // column. No need to duplicate it here.
    if (value.startsWith("(" + type + ") 0x"))
        value = value.section(" ", -1, -1);
    
    setValueUnneeded();
}

void WatchData::setValueToolTip(const QString &tooltip)
{
    valuetooltip = tooltip;
}

void WatchData::setType(const QString &str)
{
    type = str.trimmed();
    bool changed = true;
    while (changed) {
165
        if (type.endsWith(QLatin1String("const")))
con's avatar
con committed
166
            type.chop(5);
167
        else if (type.endsWith(QLatin1Char(' ')))
con's avatar
con committed
168
            type.chop(1);
169
        else if (type.endsWith(QLatin1Char('&')))
con's avatar
con committed
170
            type.chop(1);
171
        else if (type.startsWith(QLatin1String("const ")))
con's avatar
con committed
172
            type = type.mid(6);
173
        else if (type.startsWith(QLatin1String("volatile ")))
con's avatar
con committed
174
            type = type.mid(9);
175
        else if (type.startsWith(QLatin1String("class ")))
con's avatar
con committed
176
            type = type.mid(6);
177
        else if (type.startsWith(QLatin1String("struct ")))
con's avatar
con committed
178
            type = type.mid(6);
179
        else if (type.startsWith(QLatin1Char(' ')))
con's avatar
con committed
180
181
182
183
184
185
            type = type.mid(1);
        else
            changed = false;
    }
    setTypeUnneeded();
    if (isIntOrFloatType(type))
186
        setHasChildren(false);
con's avatar
con committed
187
188
}

189
void WatchData::setAddress(const QString &str)
con's avatar
con committed
190
191
192
193
{
    addr = str;
}

194
195
196
197
198
WatchData WatchData::pointerChildPlaceHolder() const
{
    WatchData data1;
    data1.iname = iname + QLatin1String(".*");
    data1.name = QLatin1Char('*') + name;
199
    data1.exp = QLatin1String("(*(") + exp + QLatin1String("))");
200
201
202
203
204
    data1.type = stripPointerType(type);
    data1.setValueNeeded();
    return data1;
}

con's avatar
con committed
205
206
QString WatchData::toString() const
{
207
208
209
    const char *doubleQuoteComma = "\",";
    QString res;
    QTextStream str(&res);
con's avatar
con committed
210
    if (!iname.isEmpty())
211
        str << "iname=\"" << iname << doubleQuoteComma;
con's avatar
con committed
212
    if (!exp.isEmpty())
213
        str << "exp=\"" << exp << doubleQuoteComma;
con's avatar
con committed
214
215

    if (!variable.isEmpty())
216
        str << "variable=\"" << variable << doubleQuoteComma;
con's avatar
con committed
217
218

    if (isValueNeeded())
219
        str << "value=<needed>,";
con's avatar
con committed
220
    if (isValueKnown() && !value.isEmpty())
221
        str << "value=\"" << value << doubleQuoteComma;
con's avatar
con committed
222
223

    if (!editvalue.isEmpty())
224
        str << "editvalue=\"" << editvalue << doubleQuoteComma;
con's avatar
con committed
225
226

    if (isTypeNeeded())
227
        str << "type=<needed>,";
con's avatar
con committed
228
    if (isTypeKnown() && !type.isEmpty())
229
        str << "type=\"" << type << doubleQuoteComma;
con's avatar
con committed
230

231
232
233
234
    if (isHasChildrenNeeded())
        str << "hasChildren=<needed>,";
    if (isHasChildrenKnown())
        str << "hasChildren=\"" << (hasChildren ? "true" : "false") << doubleQuoteComma;
con's avatar
con committed
235
236

    if (isChildrenNeeded())
237
238
239
240
241
        str << "children=<needed>,";
    str.flush();
    if (res.endsWith(QLatin1Char(',')))
        res.truncate(res.size() - 1);
    return res + QLatin1Char('}');
con's avatar
con committed
242
243
}

244
245
// Format a tooltip fow with aligned colon
template <class Streamable>
246
void formatToolTipRow(QTextStream &str, const QString &category, const Streamable &value)
247
248
249
250
251
252
{
    str << "<tr><td>" << category << "</td><td> : </td><td>" << value  << "</td></tr>";
}

QString WatchData::toToolTip() const
{
hjk's avatar
hjk committed
253
254
    if (!valuetooltip.isEmpty())
        return QString::number(valuetooltip.size());
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
    QString res;
    QTextStream str(&res);
    str << "<html><body><table>";
    formatToolTipRow(str, WatchHandler::tr("Expression"), Qt::escape(exp));
    formatToolTipRow(str, WatchHandler::tr("Type"), Qt::escape(type));
    QString val = value;
    if (value.size() > 1000) {
        val.truncate(1000);
        val +=  WatchHandler::tr(" ... <cut off>");
    }
    formatToolTipRow(str, WatchHandler::tr("Value"), Qt::escape(val));
    formatToolTipRow(str, WatchHandler::tr("Object Address"), Qt::escape(addr));
    formatToolTipRow(str, WatchHandler::tr("Stored Address"), Qt::escape(saddr));
    formatToolTipRow(str, WatchHandler::tr("iname"), Qt::escape(iname));
    str << "</table></body></html>";
    return res;
}

hjk's avatar
hjk committed
273
274
275
276
277
278
279
280
281
282
///////////////////////////////////////////////////////////////////////
//
// WatchModel
//
///////////////////////////////////////////////////////////////////////

WatchModel::WatchModel(WatchHandler *handler, WatchType type)
    : QAbstractItemModel(handler), m_handler(handler), m_type(type)
{
    m_root = new WatchItem;
283
    m_root->hasChildren = 1;
hjk's avatar
hjk committed
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
    m_root->state = 0;
    m_root->name = WatchHandler::tr("Root");

    WatchItem *item = new WatchItem;

    switch (m_type) {
        case LocalsWatch:
            item->iname = QLatin1String("local");
            item->name = WatchHandler::tr("Locals");
            break;
        case WatchersWatch:
            item->iname = QLatin1String("watch");
            item->name = WatchHandler::tr("Watchers");
            break;
        case TooltipsWatch:
            item->iname = QLatin1String("tooltip");
            item->name = WatchHandler::tr("Tooltip");
            break;
    }
303
    item->hasChildren = true;
hjk's avatar
hjk committed
304
305
    item->state = 0;
    item->parent = m_root;
306
    item->fetchTriggered = true;
hjk's avatar
hjk committed
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348

    m_root->children.append(item);
}

WatchItem *WatchModel::dummyRoot() const
{
    QTC_ASSERT(!m_root->children.isEmpty(), return 0);
    return m_root->children.front();
}

void WatchModel::reinitialize()
{
    WatchItem *item = dummyRoot();
    QTC_ASSERT(item, return);
    QModelIndex index = watchIndex(item);
    int n = item->children.size();
    if (n == 0)
        return;
    //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << item->iname);
    beginRemoveRows(index, 0, n - 1);
    qDeleteAll(item->children);
    item->children.clear();
    endRemoveRows();
}

void WatchModel::removeOutdated()
{
    WatchItem *item = dummyRoot();
    QTC_ASSERT(item, return);
    foreach (WatchItem *child, item->children)
        removeOutdatedHelper(child);
#if DEBUG_MODEL
#if USE_MODEL_TEST
    //(void) new ModelTest(this, this);
#endif
#endif
}

void WatchModel::removeOutdatedHelper(WatchItem *item)
{
    if (item->generation < generationCounter)
        removeItem(item);
349
    else {
hjk's avatar
hjk committed
350
351
        foreach (WatchItem *child, item->children)
            removeOutdatedHelper(child);
352
353
        item->fetchTriggered = false;
    }
hjk's avatar
hjk committed
354
355
356
357
358
359
360
361
362
363
364
365
366
}

void WatchModel::removeItem(WatchItem *item)
{
    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();
}

con's avatar
con committed
367
368
static QString parentName(const QString &iname)
{
369
    int pos = iname.lastIndexOf(QLatin1Char('.'));
con's avatar
con committed
370
371
372
373
374
375
    if (pos == -1)
        return QString();
    return iname.left(pos);
}


376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
static QString chopConst(QString type)
{
   while (1) {
        if (type.startsWith("const"))
            type = type.mid(5);
        else if (type.startsWith(' '))
            type = type.mid(1);
        else if (type.endsWith("const"))
            type.chop(5);
        else if (type.endsWith(' '))
            type.chop(1);
        else
            break;
    }
    return type;
}

393
QString niceType(QString type)
con's avatar
con committed
394
{
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
    type.replace('*', '@');

    for (int i = 0; i < 10; ++i) {
        int start = type.indexOf("std::allocator<");
        if (start == -1)
            break; 
        // 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;
            }
413
        }
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
        QString alloc = type.mid(start, pos + 1 - start).trimmed();
        QString inner = alloc.mid(15, alloc.size() - 16).trimmed();

        if (inner == QLatin1String("char"))
            // std::string
            type.replace(QLatin1String("basic_string<char, std::char_traits<char>, "
                "std::allocator<char> >"), QLatin1String("string"));
        else if (inner == QLatin1String("wchar_t"))
            // std::wstring
            type.replace(QLatin1String("basic_string<wchar_t, std::char_traits<wchar_t>, "
                "std::allocator<wchar_t> >"), QLatin1String("wstring"));

        // std::vector, std::deque, std::list
        QRegExp re1(QString("(vector|list|deque)<%1, %2\\s*>").arg(inner, alloc));
        if (re1.indexIn(type) != -1)
            type.replace(re1.cap(0), QString("%1<%2>").arg(re1.cap(1), inner));
430
431
432


        // std::stack
433
        QRegExp re6(QString("stack<%1, std::deque<%2> >").arg(inner, inner));
434
        re6.setMinimal(true);
435
436
437
438
439
440
441
442
        if (re6.indexIn(type) != -1)
            type.replace(re6.cap(0), QString("stack<%1>").arg(inner));

        // std::set
        QRegExp re4(QString("set<%1, std::less<%2>, %3\\s*>").arg(inner, inner, alloc));
        re4.setMinimal(true);
        if (re4.indexIn(type) != -1)
            type.replace(re4.cap(0), QString("set<%1>").arg(inner));
hjk's avatar
hjk committed
443

con's avatar
con committed
444

445
        // std::map
446
447
448
449
450
451
452
453
454
455
456
        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)
457
                    break;
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
            }
            QString ckey = inner.mid(10, pos - 10);
            QString key = chopConst(ckey);
            QString value = inner.mid(pos + 2, inner.size() - 3 - pos);

            QRegExp re5(QString("map<%1, %2, std::less<%3>, %4\\s*>")
                .arg(key, value, key, alloc));
            re5.setMinimal(true);
            if (re5.indexIn(type) != -1)
                type.replace(re5.cap(0), QString("map<%1, %2>").arg(key, value));
            else {
                QRegExp re7(QString("map<const %1, %2, std::less<const %3>, %4\\s*>")
                    .arg(key, value, key, alloc));
                re7.setMinimal(true);
                if (re7.indexIn(type) != -1)
                    type.replace(re7.cap(0), QString("map<const %1, %2>").arg(key, value));
474
            }
475
        }
con's avatar
con committed
476
    }
477
478
    type.replace('@', '*');
    type.replace(QLatin1String(" >"), QString(QLatin1Char('>')));
con's avatar
con committed
479
480
481
    return type;
}

hjk's avatar
hjk committed
482
bool WatchModel::canFetchMore(const QModelIndex &index) const
con's avatar
con committed
483
{
484
    return index.isValid() && !watchItem(index)->fetchTriggered;
hjk's avatar
hjk committed
485
486
487
488
489
}

void WatchModel::fetchMore(const QModelIndex &index)
{
    QTC_ASSERT(index.isValid(), return);
490
    QTC_ASSERT(!watchItem(index)->fetchTriggered, return);
hjk's avatar
hjk committed
491
    if (WatchItem *item = watchItem(index)) {
492
        item->fetchTriggered = true;
hjk's avatar
hjk committed
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
        WatchData data = *item;
        data.setChildrenNeeded();
        emit m_handler->watchDataUpdateNeeded(data);
    }
}

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());
    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);
con's avatar
con committed
525

hjk's avatar
hjk committed
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
    return QModelIndex();
}

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

int WatchModel::columnCount(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return 3;
}

bool WatchModel::hasChildren(const QModelIndex &parent) const
{
    WatchItem *item = watchItem(parent);
547
    return !item || item->hasChildren;
hjk's avatar
hjk committed
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
}

WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
{
    return idx.isValid() 
        ? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
}

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

QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle, 
    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;
    }
    return QModelIndex();
}

QVariant WatchModel::data(const QModelIndex &idx, int role) const
{
    const WatchItem &data = *watchItem(idx);
con's avatar
con committed
579
580
581
582
583
584
585
586
587
588
589
590

    switch (role) {
        case Qt::DisplayRole: {
            switch (idx.column()) {
                case 0: return data.name;
                case 1: return data.value;
                case 2: return niceType(data.type);
                default: break;
            }
            break;
        }

591
592
        case Qt::ToolTipRole:
            return data.toToolTip();
con's avatar
con committed
593
594
595
596
597
598
599
600
601
602
603
604
605

        case Qt::ForegroundRole: {
            static const QVariant red(QColor(200, 0, 0));
            static const QVariant black(QColor(0, 0, 0));
            static const QVariant gray(QColor(140, 140, 140));
            switch (idx.column()) {
                case 0: return black;
                case 1: return data.valuedisabled ? gray : data.changed ? red : black;
                case 2: return black;
            }
            break;
        }

606
607
608
        case ExpressionRole:
            return data.exp;

con's avatar
con committed
609
610
611
        case INameRole:
            return data.iname;

612
        case ExpandedRole:
hjk's avatar
hjk committed
613
614
            return m_handler->m_expandedINames.contains(data.iname);
            //FIXME return node < 4 || m_expandedINames.contains(data.iname);
615
616
617
618

        case ActiveDataRole:
            qDebug() << "ASK FOR" << data.iname;
            return true;
619
    
con's avatar
con committed
620
621
622
623
624
625
        default:
            break; 
    }
    return QVariant();
}

hjk's avatar
hjk committed
626
bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
627
{
hjk's avatar
hjk committed
628
629
630
631
632
633
634
    if (role == ExpandedRole) {
        QString iname = data(index, INameRole).toString();
        if (value.toBool())
            m_handler->m_expandedINames.insert(iname);
        else
            m_handler->m_expandedINames.remove(iname);
    }
635
636
637
638
    emit dataChanged(index, index);
    return true;
}

hjk's avatar
hjk committed
639
Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
con's avatar
con committed
640
641
642
643
644
645
646
647
648
{
    using namespace Qt;

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

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

649
    static const ItemFlags notEditable =
con's avatar
con committed
650
651
652
653
654
655
656
          ItemIsSelectable
        | ItemIsDragEnabled
        | ItemIsDropEnabled
        // | ItemIsUserCheckable
        // | ItemIsTristate
        | ItemIsEnabled;

657
    static const ItemFlags editable = notEditable | ItemIsEditable;
con's avatar
con committed
658

hjk's avatar
hjk committed
659
    const WatchData &data = *watchItem(idx);
660
661

    if (data.isWatcher() && idx.column() == 0)
hjk's avatar
hjk committed
662
663
664
665
666
        return editable; // watcher names are editable
    if (data.isWatcher() && idx.column() == 2)
        return editable; // watcher types are
    if (idx.column() == 1)
        return editable; // locals and watcher values are editable
667
    return  notEditable;
con's avatar
con committed
668
669
}

hjk's avatar
hjk committed
670
QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
con's avatar
con committed
671
672
673
674
675
{
    if (orientation == Qt::Vertical)
        return QVariant();
    if (role == Qt::DisplayRole) {
        switch (section) {
hjk's avatar
hjk committed
676
677
678
            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
679
680
681
682
683
        }
    }
    return QVariant(); 
}

684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
static bool iNameSorter(const WatchItem *item1, const WatchItem *item2)
{
    QString name1 = item1->iname.section('.', -1);
    QString name2 = item2->iname.section('.', -1);
    if (!name1.isEmpty() && !name2.isEmpty()) {
        if (name1.at(0).isDigit() && name2.at(0).isDigit())
            return name1.toInt() < name2.toInt();
    }
    return name1 < name2; 
}

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

hjk's avatar
hjk committed
702
void WatchModel::insertData(const WatchData &data)
con's avatar
con committed
703
{
hjk's avatar
hjk committed
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
    QTC_ASSERT(!data.iname.isEmpty(), return);
    WatchItem *parent = findItem(parentName(data.iname), m_root);
    if (!parent) {
        WatchData parent;
        parent.iname = parentName(data.iname);
        insertData(parent);
        //MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname);
        return;
    }
    QModelIndex index = watchIndex(parent);
    if (WatchItem *oldItem = findItem(data.iname, parent)) {
        // overwrite old entry
        //MODEL_DEBUG("OVERWRITE : " << data.iname << data.value);
        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));
    } else {
        // add new entry
        //MODEL_DEBUG("INSERT : " << data.iname << data.value);
        WatchItem *item = new WatchItem(data);
        item->parent = parent;
        item->generation = generationCounter;
        item->changed = true;
732
        int n = findInsertPosition(parent->children, item);
hjk's avatar
hjk committed
733
        beginInsertRows(index, n, n);
734
        parent->children.insert(n, item);
hjk's avatar
hjk committed
735
736
        endInsertRows();
    }
con's avatar
con committed
737
738
}

hjk's avatar
hjk committed
739
WatchItem *WatchModel::findItem(const QString &iname, WatchItem *root) const
con's avatar
con committed
740
{
hjk's avatar
hjk committed
741
742
743
744
745
    if (root->iname == iname)
        return root;
    for (int i = root->children.size(); --i >= 0; )
        if (WatchItem *item = findItem(iname, root->children.at(i)))
            return item;
con's avatar
con committed
746
747
748
749
    return 0;
}


hjk's avatar
hjk committed
750
751
752
753
754
755
756
///////////////////////////////////////////////////////////////////////
//
// WatchHandler
//
///////////////////////////////////////////////////////////////////////

WatchHandler::WatchHandler()
con's avatar
con committed
757
{
hjk's avatar
hjk committed
758
759
760
761
762
763
764
765
766
767
768
    m_expandPointers = true;
    m_inChange = false;

    m_locals = new WatchModel(this, LocalsWatch);
    m_watchers = new WatchModel(this, WatchersWatch);
    m_tooltips = new WatchModel(this, TooltipsWatch);

    connect(theDebuggerAction(WatchExpression),
        SIGNAL(triggered()), this, SLOT(watchExpression()));
    connect(theDebuggerAction(RemoveWatchExpression),
        SIGNAL(triggered()), this, SLOT(removeWatchExpression()));
con's avatar
con committed
769
770
}

hjk's avatar
hjk committed
771
void WatchHandler::endCycle()
con's avatar
con committed
772
{
hjk's avatar
hjk committed
773
774
775
776
777
    m_locals->removeOutdated();
    m_watchers->removeOutdated();
    m_tooltips->removeOutdated();

/*
con's avatar
con committed
778
779
780
781
782
783
784
785
786
    if (m_inChange) {
        MODEL_DEBUG("RECREATE MODEL IGNORED, CURRENT SET:\n" << toString());
        return;
    }

    #ifdef DEBUG_PENDING
    MODEL_DEBUG("RECREATE MODEL, CURRENT SET:\n" << toString());
    #endif

787
    QHash<QString, int> oldTopINames;
con's avatar
con committed
788
789
790
791
    QHash<QString, QString> oldValues;
    for (int i = 0, n = m_oldSet.size(); i != n; ++i) {
        WatchData &data = m_oldSet[i];
        oldValues[data.iname] = data.value;
792
793
        if (data.level == 2)
            ++oldTopINames[data.iname];
con's avatar
con committed
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
    }
    #ifdef DEBUG_PENDING
    MODEL_DEBUG("OLD VALUES: " << oldValues);
    #endif

    for (int i = m_completeSet.size(); --i >= 0; ) {
        WatchData &data = m_completeSet[i];
        data.level = data.iname.isEmpty() ? 0 : data.iname.count('.') + 1;
        data.childIndex.clear();
    }

    qSort(m_completeSet.begin(), m_completeSet.end(), &iNameSorter);

    // Possibly append dummy items to prevent empty views
    bool ok = true;
hjk's avatar
hjk committed
809

con's avatar
con committed
810
811
812
813
814
815
816
817
    if (ok) {
        for (int i = 1; i <= 3; ++i) {
            WatchData &data = m_displaySet[i];
            if (data.childIndex.size() == 0) {
                WatchData dummy;
                dummy.state = 0;
                dummy.row = 0;
                if (i == 1) {
818
                    dummy.iname = QLatin1String("local.dummy");
819
                    dummy.name  = tr("<No Locals>");
con's avatar
con committed
820
                } else if (i == 2) {
821
                    dummy.iname = QLatin1String("tooltip.dummy");
822
                    dummy.name  = tr("<No Tooltip>");
con's avatar
con committed
823
                } else {
824
                    dummy.iname = QLatin1String("watch.dummy");
825
                    dummy.name  = tr("<No Watchers>");
con's avatar
con committed
826
827
828
829
830
831
832
833
834
                }
                dummy.level = 2;
                dummy.parentIndex = i;
                dummy.childCount = 0;
                data.childIndex.append(m_displaySet.size());
                m_displaySet.append(dummy); 
            }
        }
    }
hjk's avatar
hjk committed
835
*/
con's avatar
con committed
836
837
838
839
840
841
}

void WatchHandler::cleanup()
{
    m_expandedINames.clear();
    m_displayedINames.clear();
hjk's avatar
hjk committed
842
843
    m_locals->reinitialize();
    m_tooltips->reinitialize();
con's avatar
con committed
844
845
846
847
848
849
850
851
852
853
854
855
#if 0
    for (EditWindows::ConstIterator it = m_editWindows.begin();
            it != m_editWindows.end(); ++it) {
        if (!it.value().isNull())
            delete it.value();
    }
    m_editWindows.clear();
#endif
}

void WatchHandler::insertData(const WatchData &data)
{
856
    MODEL_DEBUG("INSERTDATA: " << data.toString());
hjk's avatar
hjk committed
857
    QTC_ASSERT(data.isValid(), return);
hjk's avatar
hjk committed
858
859
860
861
862
863
864
    if (data.isSomethingNeeded()) {
        emit watchDataUpdateNeeded(data);
    } else {
        WatchModel *model = modelForIName(data.iname);
        QTC_ASSERT(model, return);
        model->insertData(data);
    }
con's avatar
con committed
865
866
}

867
868
869
870
871
872
873
874
875
876
void WatchHandler::removeData(const QString &iname)
{
    WatchModel *model = modelForIName(iname);
    if (!model)
        return;
    WatchItem *item = model->findItem(iname, model->m_root);
    if (item)
        model->removeItem(item);
}

877
878
879
880
881
882
void WatchHandler::watchExpression()
{
    if (QAction *action = qobject_cast<QAction *>(sender()))
        watchExpression(action->data().toString());
}

883
884
QString WatchHandler::watcherName(const QString &exp)
{
hjk's avatar
hjk committed
885
    return QLatin1String("watch.") + QString::number(m_watcherNames[exp]);
886
887
}

con's avatar
con committed
888
889
890
void WatchHandler::watchExpression(const QString &exp)
{
    // FIXME: 'exp' can contain illegal characters
hjk's avatar
hjk committed
891
    m_watcherNames[exp] = watcherCounter++;
con's avatar
con committed
892
893
894
    WatchData data;
    data.exp = exp;
    data.name = exp;
895
    if (exp.isEmpty() || exp == watcherEditPlaceHolder())
896
        data.setAllUnneeded();
897
    data.iname = watcherName(exp);
con's avatar
con committed
898
    insertData(data);
hjk's avatar
hjk committed
899
    saveWatchers();
hjk's avatar
hjk committed
900
    //emit watchModelUpdateRequested();
con's avatar
con committed
901
902
903
904
}

void WatchHandler::setDisplayedIName(const QString &iname, bool on)
{
hjk's avatar
hjk committed
905
906
907
    Q_UNUSED(iname);
    Q_UNUSED(on);
/*
con's avatar
con committed
908
909
910
911
912
913
914
915
916
917
918
919
920
    WatchData *d = findData(iname);
    if (!on || !d) {
        delete m_editWindows.take(iname);
        m_displayedINames.remove(iname);
        return;
    }
    if (d->exp.isEmpty()) {
        //emit statusMessageRequested(tr("Sorry. Cannot visualize objects without known address."), 5000);
        return;
    }
    d->setValueNeeded();
    m_displayedINames.insert(iname);
    insertData(*d);
hjk's avatar
hjk committed
921
*/
con's avatar
con committed
922
923
924
925
926
927
928
929
930
931
}

void WatchHandler::showEditValue(const WatchData &data)
{
    // editvalue is always base64 encoded
    QByteArray ba = QByteArray::fromBase64(data.editvalue);
    //QByteArray ba = data.editvalue;
    QWidget *w = m_editWindows.value(data.iname);
    qDebug() << "SHOW_EDIT_VALUE " << data.toString() << data.type
            << data.iname << w;
932
    if (data.type == QLatin1String("QImage")) {
con's avatar
con committed
933
934
935
936
937
938
939
940
941
942
943
        if (!w) {
            w = new QLabel;
            m_editWindows[data.iname] = w;
        }
        QDataStream ds(&ba, QIODevice::ReadOnly);
        QVariant v;
        ds >> v;
        QString type = QString::fromAscii(v.typeName());
        QImage im = v.value<QImage>();
        if (QLabel *l = qobject_cast<QLabel *>(w))
            l->setPixmap(QPixmap::fromImage(im));
944
    } else if (data.type == QLatin1String("QPixmap")) {
con's avatar
con committed
945
946
947
948
949
950
951
952
953
954
955
        if (!w) {
            w = new QLabel;
            m_editWindows[data.iname] = w;
        }
        QDataStream ds(&ba, QIODevice::ReadOnly);
        QVariant v;
        ds >> v;
        QString type = QString::fromAscii(v.typeName());
        QPixmap im = v.value<QPixmap>();
        if (QLabel *l = qobject_cast<QLabel *>(w))
            l->setPixmap(im);
956
    } else if (data.type == QLatin1String("QString")) {
con's avatar
con committed
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
        if (!w) {
            w = new QTextEdit;
            m_editWindows[data.iname] = w;
        }
#if 0
        QDataStream ds(&ba, QIODevice::ReadOnly);
        QVariant v;
        ds >> v;
        QString type = QString::fromAscii(v.typeName());
        QString str = v.value<QString>();
#else
        MODEL_DEBUG("DATA: " << ba);
        QString str = QString::fromUtf16((ushort *)ba.constData(), ba.size()/2);
#endif
        if (QTextEdit *t = qobject_cast<QTextEdit *>(w))
            t->setText(str);
    }
    if (w)
        w->show();
}

978
979
980
981
982
983
void WatchHandler::removeWatchExpression()
{
    if (QAction *action = qobject_cast<QAction *>(sender()))
        removeWatchExpression(action->data().toString());
}

984
void WatchHandler::removeWatchExpression(const QString &exp)
con's avatar
con committed
985
{
986
    MODEL_DEBUG("REMOVE WATCH: " << exp);
hjk's avatar
hjk committed
987
988
989
990
991
    m_watcherNames.remove(exp);
    foreach (WatchItem *item, m_watchers->dummyRoot()->children) {
        if (item->exp == exp) {
            m_watchers->removeItem(item);
            saveWatchers();
992
993
994
            break;
        }
    }
con's avatar
con committed
995
996
}

hjk's avatar
hjk committed
997
void WatchHandler::beginCycle()
con's avatar
con committed
998
{
hjk's avatar
hjk committed
999
1000
    ++generationCounter;
    //m_locals->beginCycle();
hjk's avatar
hjk committed
1001
}
con's avatar
con committed
1002

hjk's avatar
hjk committed
1003
void WatchHandler::updateWatchers()
hjk's avatar
hjk committed
1004
{
hjk's avatar
hjk committed
1005
    //qDebug() << "UPDATE WATCHERS";
con's avatar
con committed
1006
    // copy over all watchers and mark all watchers as incomplete
hjk's avatar
hjk committed
1007
    foreach (const QString &exp, m_watcherNames.keys()) {
hjk's avatar
hjk committed
1008
        WatchData data;
1009
        data.iname = watcherName(exp);
hjk's avatar
hjk committed
1010
1011
1012
1013
        data.setAllNeeded();
        data.name = exp;
        data.exp = exp;
        insertData(data);
con's avatar
con committed
1014
1015
    }
}
hjk's avatar
hjk committed
1016
1017
1018
1019
1020

void WatchHandler::loadWatchers()
{
    QVariant value;
    sessionValueRequested("Watchers", &value);
1021
    foreach (const QString &exp, value.toStringList())
hjk's avatar
hjk committed
1022
        m_watcherNames[exp] = watcherCounter++;
1023
    //qDebug() << "LOAD WATCHERS: " << m_watchers;
hjk's avatar
hjk committed
1024
    //reinitializeWatchersHelper();
hjk's avatar
hjk committed
1025
1026
1027
1028
}

void WatchHandler::saveWatchers()
{
1029
    //qDebug() << "SAVE WATCHERS: " << m_watchers.keys();
1030
1031
1032
1033
1034
1035
1036
1037
1038
    // Filter out valid watchers.
    QStringList watcherNames;
    const QHash<QString, int>::const_iterator cend = m_watcherNames.constEnd();
    for (QHash<QString, int>::const_iterator it = m_watcherNames.constBegin(); it != cend; ++it) {
        const QString &watcherName = it.key();
        if (!watcherName.isEmpty() && watcherName != watcherEditPlaceHolder())
            watcherNames.push_back(watcherName);
    }
    setSessionValueRequested("Watchers", QVariant(watcherNames));
hjk's avatar
hjk committed
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
}

void WatchHandler::saveSessionData()
{
    saveWatchers();
}

void WatchHandler::loadSessionData()
{
    loadWatchers();
hjk's avatar
hjk committed
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
    foreach (const QString &exp, m_watcherNames.keys()) {
        WatchData data;
        data.iname = watcherName(exp);
        data.setAllUnneeded();
        data.name = exp;
        data.exp = exp;
        insertData(data);
    }
}

WatchModel *WatchHandler::model(WatchType type) const
{
    switch (type) {
        case LocalsWatch: return m_locals;
        case WatchersWatch: return m_watchers;
        case TooltipsWatch: return m_tooltips;
    }
    QTC_ASSERT(false, /**/);
    return 0;
}
    
WatchModel *WatchHandler::modelForIName(const QString &iname) const
{
    if (iname.startsWith(QLatin1String("local.")))
        return m_locals;
    if (iname.startsWith(QLatin1String("watch.")))
        return m_watchers;
1076
    if (iname.startsWith(QLatin1String("tooltip")))
hjk's avatar
hjk committed
1077
1078
1079
        return m_tooltips;
    QTC_ASSERT(false, /**/);
    return 0;
hjk's avatar
hjk committed
1080
}
hjk's avatar
hjk committed
1081
1082
1083
1084
1085
1086
1087
1088

WatchData *WatchHandler::findItem(const QString &iname) const
{
    const WatchModel *model = modelForIName(iname);
    QTC_ASSERT(model, return 0);
    return model->findItem(iname, model->m_root);
}

1089
1090
1091
1092
1093
QString WatchHandler::watcherEditPlaceHolder()
{
    static const QString rc = tr("<Edit>");
    return rc;
}
1094

hjk's avatar
hjk committed
1095
1096
} // namespace Internal
} // namespace Debugger