OSDN Git Service

Merge branch '2.3'
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / watchhandler.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
30 **
31 **************************************************************************/
32
33 #include "watchhandler.h"
34
35 #include "breakhandler.h"
36 #include "debuggerinternalconstants.h"
37 #include "debuggeractions.h"
38 #include "debuggercore.h"
39 #include "debuggerengine.h"
40 #include "watchutils.h"
41
42 #if USE_WATCH_MODEL_TEST
43 #include "modeltest.h"
44 #endif
45
46 #include <utils/qtcassert.h>
47 #include <utils/savedaction.h>
48
49 #include <cplusplus/CppRewriter.h>
50
51 #include <QtCore/QDebug>
52 #include <QtCore/QEvent>
53 #include <QtCore/QFile>
54 #include <QtCore/QProcess>
55 #include <QtCore/QTextStream>
56 #include <QtCore/QtAlgorithms>
57
58 #include <QtGui/QLabel>
59 #include <QtGui/QTextEdit>
60
61 #include <ctype.h>
62 #include <utils/qtcassert.h>
63
64
65 namespace Debugger {
66 namespace Internal {
67
68 // Creates debug output for accesses to the model.
69 enum { debugModel = 0 };
70
71 #define MODEL_DEBUG(s) do { if (debugModel) qDebug() << s; } while (0)
72 #define MODEL_DEBUGX(s) qDebug() << s
73
74 static const QString strNotInScope =
75     QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
76
77 static int watcherCounter = 0;
78 static int generationCounter = 0;
79
80 QHash<QByteArray, int> WatchHandler::m_watcherNames;
81 QHash<QByteArray, int> WatchHandler::m_typeFormats;
82 int WatchHandler::m_unprintableBase = 0;
83
84 static QByteArray stripTemplate(const QByteArray &ba)
85 {
86     int pos = ba.indexOf('<');
87     return pos == -1 ? ba : ba.left(pos);
88 }
89
90 ////////////////////////////////////////////////////////////////////
91 //
92 // WatchItem
93 //
94 ////////////////////////////////////////////////////////////////////
95
96 class WatchItem : public WatchData
97 {
98 public:
99     WatchItem() { parent = 0; }
100
101     ~WatchItem() {
102         if (parent != 0)
103             parent->children.removeOne(this);
104         qDeleteAll(children);
105     }
106
107     WatchItem(const WatchData &data) : WatchData(data)
108         { parent = 0; }
109
110     void setData(const WatchData &data)
111         { static_cast<WatchData &>(*this) = data; }
112
113     WatchItem *parent;
114     QList<WatchItem *> children;  // fetched children
115 };
116
117
118 ///////////////////////////////////////////////////////////////////////
119 //
120 // WatchModel
121 //
122 ///////////////////////////////////////////////////////////////////////
123
124 WatchModel::WatchModel(WatchHandler *handler, WatchType type)
125     : QAbstractItemModel(handler), m_handler(handler), m_type(type)
126 {
127     m_root = new WatchItem;
128     m_root->hasChildren = 1;
129     m_root->state = 0;
130     m_root->name = WatchHandler::tr("Root");
131     m_root->parent = 0;
132
133     switch (m_type) {
134         case ReturnWatch:
135             m_root->iname = "return";
136             m_root->name = WatchHandler::tr("Return Value");
137             break;
138         case LocalsWatch:
139             m_root->iname = "local";
140             m_root->name = WatchHandler::tr("Locals");
141             break;
142         case WatchersWatch:
143             m_root->iname = "watch";
144             m_root->name = WatchHandler::tr("Watchers");
145             break;
146         case TooltipsWatch:
147             m_root->iname = "tooltip";
148             m_root->name = WatchHandler::tr("Tooltip");
149             break;
150     }
151 }
152
153 WatchModel::~WatchModel()
154 {
155     delete m_root;
156 }
157
158 WatchItem *WatchModel::rootItem() const
159 {
160     return m_root;
161 }
162
163 void WatchModel::reinitialize()
164 {
165     int n = m_root->children.size();
166     if (n == 0)
167         return;
168     //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << m_root->iname);
169     QModelIndex index = watchIndex(m_root);
170     beginRemoveRows(index, 0, n - 1);
171     qDeleteAll(m_root->children);
172     m_root->children.clear();
173     endRemoveRows();
174 }
175
176 void WatchModel::emitAllChanged()
177 {
178     emit layoutChanged();
179 }
180
181 void WatchModel::beginCycle()
182 {
183     emit enableUpdates(false);
184 }
185
186 void WatchModel::endCycle()
187 {
188     removeOutdated();
189     m_fetchTriggered.clear();
190     emit enableUpdates(true);
191 }
192
193 DebuggerEngine *WatchModel::engine() const
194 {
195     return m_handler->m_engine;
196 }
197
198 void WatchModel::dump()
199 {
200     qDebug() << "\n";
201     foreach (WatchItem *child, m_root->children)
202         dumpHelper(child);
203 }
204
205 void WatchModel::dumpHelper(WatchItem *item)
206 {
207     qDebug() << "ITEM: " << item->iname
208         << (item->parent ? item->parent->iname : "<none>")
209         << item->generation;
210     foreach (WatchItem *child, item->children)
211         dumpHelper(child);
212 }
213
214 void WatchModel::removeOutdated()
215 {
216     foreach (WatchItem *child, m_root->children)
217         removeOutdatedHelper(child);
218 #if DEBUG_MODEL
219 #if USE_WATCH_MODEL_TEST
220     (void) new ModelTest(this, this);
221 #endif
222 #endif
223 }
224
225 void WatchModel::removeOutdatedHelper(WatchItem *item)
226 {
227     if (item->generation < generationCounter) {
228         destroyItem(item);
229     } else {
230         foreach (WatchItem *child, item->children)
231             removeOutdatedHelper(child);
232     }
233 }
234
235 void WatchModel::destroyItem(WatchItem *item)
236 {
237     WatchItem *parent = item->parent;
238     QModelIndex index = watchIndex(parent);
239     int n = parent->children.indexOf(item);
240     //MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n);
241     beginRemoveRows(index, n, n);
242     parent->children.removeAt(n);
243     endRemoveRows();
244     delete item;
245 }
246
247 static QByteArray parentName(const QByteArray &iname)
248 {
249     int pos = iname.lastIndexOf('.');
250     if (pos == -1)
251         return QByteArray();
252     return iname.left(pos);
253 }
254
255 static QString niceTypeHelper(const QByteArray &typeIn)
256 {
257     typedef QMap<QByteArray, QString> Cache;
258     static Cache cache;
259     const Cache::const_iterator it = cache.constFind(typeIn);
260     if (it != cache.constEnd())
261         return it.value();
262     const QString simplified = CPlusPlus::simplifySTLType(typeIn);
263     cache.insert(typeIn, simplified); // For simplicity, also cache unmodified types
264     return simplified;
265 }
266
267 static QString removeNamespaces(QString str, const QByteArray &ns)
268 {
269     if (!debuggerCore()->boolSetting(ShowStdNamespace))
270         str.remove(QLatin1String("std::"));
271     if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
272         const QString qtNamespace = QString::fromLatin1(ns);
273         if (!qtNamespace.isEmpty())
274             str.remove(qtNamespace);
275     }
276     return str;
277 }
278
279 static QString removeInitialNamespace(QString str, const QByteArray &ns)
280 {
281     if (str.startsWith(QLatin1String("std::"))
282             && debuggerCore()->boolSetting(ShowStdNamespace))
283         str = str.mid(5);
284     if (!debuggerCore()->boolSetting(ShowQtNamespace)) {
285         const QString qtNamespace = QString::fromLatin1(ns);
286         if (!qtNamespace.isEmpty() && str.startsWith(qtNamespace))
287             str = str.mid(qtNamespace.size());
288     }
289     return str;
290 }
291
292 QString WatchModel::displayType(const WatchData &data) const
293 {
294     QString base = data.displayedType.isEmpty()
295         ? niceTypeHelper(data.type)
296         : data.displayedType;
297     if (data.bitsize)
298         base += QString(":%1").arg(data.bitsize);
299     base.remove('\'');
300     return base;
301 }
302
303 static int formatToIntegerBase(int format)
304 {
305     switch (format) {
306         case HexadecimalFormat:
307             return 16;
308         case BinaryFormat:
309             return 2;
310         case OctalFormat:
311             return 8;
312     }
313     return 10;
314 }
315
316 template <class IntType> QString reformatInteger(IntType value, int format)
317 {
318     switch (format) {
319         case HexadecimalFormat:
320             return ("(hex) ") + QString::number(value, 16);
321         case BinaryFormat:
322             return ("(bin) ") + QString::number(value, 2);
323         case OctalFormat:
324             return ("(oct) ") + QString::number(value, 8);
325     }
326     return QString::number(value); // not reached
327 }
328
329 // Format printable (char-type) characters
330 static inline QString reformatCharacter(int code, int format)
331 {
332     const QString codeS = reformatInteger(code, format);
333     if (code < 0) // Append unsigned value.
334         return codeS + QLatin1String(" / ") + reformatInteger(256 + code, format);
335     if (code >= 32 && code < 128)
336         return codeS + QLatin1String(" '") + QChar(code) + QLatin1Char('\'');
337     switch (code) {
338     case 0:
339         return codeS + QLatin1String(" '\\0'");
340     case '\r':
341         return codeS + QLatin1String(" '\\r'");
342     case '\t':
343         return codeS + QLatin1String(" '\\t'");
344     case '\n':
345         return codeS + QLatin1String(" '\\n'");
346     }
347     return codeS;
348 }
349
350 static inline QString formattedValue(const WatchData &data, int format)
351 {
352     if (isIntType(data.type)) {
353         if (data.value.isEmpty())
354             return data.value;
355         // Do not reformat booleans (reported as 'true, false').
356         const QChar firstChar = data.value.at(0);
357         if (!firstChar.isDigit() && firstChar != QLatin1Char('-'))
358             return data.value;
359         // Append quoted, printable character also for decimal.
360         if (data.type.endsWith("char")) {
361             bool ok;
362             const int code = data.value.toInt(&ok);
363             return ok ? reformatCharacter(code, format) : data.value;
364         }
365         // Rest: Leave decimal as is
366         if (format <= 0)
367             return data.value;
368         // Evil hack, covers 'unsigned' as well as quint64.
369         if (data.type.contains('u'))
370             return reformatInteger(data.value.toULongLong(0, 0), format);
371         return reformatInteger(data.value.toLongLong(), format);
372     }
373
374     if (!isPointerType(data.type) && !isVTablePointer(data.type)) {
375         bool ok = false;
376         qulonglong integer = data.value.toULongLong(&ok, 0);
377         if (ok)
378            return reformatInteger(integer, format);
379     }
380
381     QString result = data.value;
382     result.replace(QLatin1Char('\n'), QLatin1String("\\n"));
383     if (result.startsWith(QLatin1Char('<'))) {
384         if (result == QLatin1String("<Edit>"))
385             result = WatchHandler::tr("<Edit>");
386         else if (result == QLatin1String("<empty>"))
387             result = WatchHandler::tr("<empty>");
388         else if (result == QLatin1String("<uninitialized>"))
389             result = WatchHandler::tr("<uninitialized>");
390         else if (result == QLatin1String("<invalid>"))
391             result = WatchHandler::tr("<invalid>");
392         else if (result == QLatin1String("<not accessible>"))
393             result = WatchHandler::tr("<not accessible>");
394         else if (result.endsWith(" items>")) {
395             // '<10 items>' or '<>10 items>' (more than)
396             bool ok;
397             const bool moreThan = result.at(1) == QLatin1Char('>');
398             const int numberPos = moreThan ? 2 : 1;
399             const int size = result.mid(numberPos, result.indexOf(' ') - numberPos).toInt(&ok);
400             QTC_ASSERT(ok, qWarning("WatchHandler: Invalid item count '%s'",
401                 qPrintable(result)))
402             result = moreThan ?
403                      WatchHandler::tr("<more than %n items>", 0, size) :
404                      WatchHandler::tr("<%n items>", 0, size);
405         }
406     }
407
408     return result;
409 }
410
411 // Get a pointer address from pointer values reported by the debugger.
412 // Fix CDB formatting of pointers "0x00000000`000003fd class foo *", or
413 // "0x00000000`000003fd "Hallo"", or check gdb formatting of characters.
414 static inline quint64 pointerValue(QString data)
415 {
416     const int blankPos = data.indexOf(QLatin1Char(' '));
417     if (blankPos != -1)
418         data.truncate(blankPos);
419     data.remove(QLatin1Char('`'));
420     return data.toULongLong(0, 0);
421 }
422
423 // Return the type used for editing
424 static inline int editType(const WatchData &d)
425 {
426     if (d.type == "bool")
427         return QVariant::Bool;
428     if (isIntType(d.type))
429         return d.type.contains('u') ? QVariant::ULongLong : QVariant::LongLong;
430     if (isFloatType(d.type))
431         return QVariant::Double;
432     // Check for pointers using hex values (0xAD00 "Hallo")
433     if (isPointerType(d.type) && d.value.startsWith(QLatin1String("0x")))
434         return QVariant::ULongLong;
435    return QVariant::String;
436 }
437
438 // Convert to editable (see above)
439 static inline QVariant editValue(const WatchData &d)
440 {
441     switch (editType(d)) {
442     case QVariant::Bool:
443         return d.value != QLatin1String("0") && d.value != QLatin1String("false");
444     case QVariant::ULongLong:
445         if (isPointerType(d.type)) // Fix pointer values (0xAD00 "Hallo" -> 0xAD00)
446             return QVariant(pointerValue(d.value));
447         return QVariant(d.value.toULongLong());
448     case QVariant::LongLong:
449         return QVariant(d.value.toLongLong());
450     case QVariant::Double:
451         return QVariant(d.value.toDouble());
452     default:
453         break;
454     }
455     // Some string value: '0x434 "Hallo"':
456     // Remove quotes and replace newlines, which will cause line edit troubles.
457     QString stringValue = d.value;
458     if (stringValue.endsWith(QLatin1Char('"'))) {
459         const int leadingDoubleQuote = stringValue.indexOf(QLatin1Char('"'));
460         if (leadingDoubleQuote != stringValue.size() - 1) {
461             stringValue.truncate(stringValue.size() - 1);
462             stringValue.remove(0, leadingDoubleQuote + 1);
463             stringValue.replace(QLatin1String("\n"), QLatin1String("\\n"));
464         }
465     }
466     return QVariant(stringValue);
467 }
468
469 bool WatchModel::canFetchMore(const QModelIndex &index) const
470 {
471     WatchItem *item = watchItem(index);
472     QTC_ASSERT(item, return false);
473     return index.isValid() && !m_fetchTriggered.contains(item->iname);
474 }
475
476 void WatchModel::fetchMore(const QModelIndex &index)
477 {
478     QTC_ASSERT(index.isValid(), return);
479     WatchItem *item = watchItem(index);
480     QTC_ASSERT(item, return);
481     QTC_ASSERT(!m_fetchTriggered.contains(item->iname), return);
482     m_handler->m_expandedINames.insert(item->iname);
483     m_fetchTriggered.insert(item->iname);
484     if (item->children.isEmpty()) {
485         WatchData data = *item;
486         data.setChildrenNeeded();
487         WatchUpdateFlags flags;
488         flags.tryIncremental = true;
489         engine()->updateWatchData(data, flags);
490     }
491 }
492
493 QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const
494 {
495     if (!hasIndex(row, column, parent))
496         return QModelIndex();
497
498     const WatchItem *item = watchItem(parent);
499     QTC_ASSERT(item, return QModelIndex());
500     if (row >= item->children.size())
501         return QModelIndex();
502     return createIndex(row, column, (void*)(item->children.at(row)));
503 }
504
505 QModelIndex WatchModel::parent(const QModelIndex &idx) const
506 {
507     if (!idx.isValid())
508         return QModelIndex();
509
510     const WatchItem *item = watchItem(idx);
511     if (!item->parent || item->parent == m_root)
512         return QModelIndex();
513
514     const WatchItem *grandparent = item->parent->parent;
515     if (!grandparent)
516         return QModelIndex();
517
518     for (int i = 0; i < grandparent->children.size(); ++i)
519         if (grandparent->children.at(i) == item->parent)
520             return createIndex(i, 0, (void*) item->parent);
521
522     return QModelIndex();
523 }
524
525 int WatchModel::rowCount(const QModelIndex &idx) const
526 {
527     if (idx.column() > 0)
528         return 0;
529     return watchItem(idx)->children.size();
530 }
531
532 int WatchModel::columnCount(const QModelIndex &idx) const
533 {
534     Q_UNUSED(idx)
535     return 3;
536 }
537
538 bool WatchModel::hasChildren(const QModelIndex &parent) const
539 {
540     WatchItem *item = watchItem(parent);
541     return !item || item->hasChildren;
542 }
543
544 WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
545 {
546     return idx.isValid()
547         ? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
548 }
549
550 QModelIndex WatchModel::watchIndex(const WatchItem *item) const
551 {
552     return watchIndexHelper(item, m_root, QModelIndex());
553 }
554
555 QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle,
556     const WatchItem *parentItem, const QModelIndex &parentIndex) const
557 {
558     if (needle == parentItem)
559         return parentIndex;
560     for (int i = parentItem->children.size(); --i >= 0; ) {
561         const WatchItem *childItem = parentItem->children.at(i);
562         QModelIndex childIndex = index(i, 0, parentIndex);
563         QModelIndex idx = watchIndexHelper(needle, childItem, childIndex);
564         if (idx.isValid())
565             return idx;
566     }
567     return QModelIndex();
568 }
569
570 void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
571 {
572     QModelIndex idx1 = index(0, column, parentIndex);
573     QModelIndex idx2 = index(rowCount(parentIndex) - 1, column, parentIndex);
574     if (idx1.isValid() && idx2.isValid())
575         emit dataChanged(idx1, idx2);
576     //qDebug() << "CHANGING:\n" << idx1 << "\n" << idx2 << "\n"
577     //    << data(parentIndex, INameRole).toString();
578     for (int i = rowCount(parentIndex); --i >= 0; )
579         emitDataChanged(column, index(i, 0, parentIndex));
580 }
581
582 // Truncate value for item view, maintaining quotes
583 static inline QString truncateValue(QString v)
584 {
585     enum { maxLength = 512 };
586     if (v.size() < maxLength)
587         return v;
588     const bool isQuoted = v.endsWith(QLatin1Char('"')); // check for 'char* "Hallo"'
589     v.truncate(maxLength);
590     v += isQuoted ? QLatin1String("...\"") : QLatin1String("...");
591     return v;
592 }
593
594 int WatchModel::itemFormat(const WatchData &data) const
595 {
596     const int individualFormat = m_handler->m_individualFormats.value(data.iname, -1);
597     if (individualFormat != -1)
598         return individualFormat;
599     return m_handler->m_typeFormats.value(stripTemplate(data.type), -1);
600 }
601
602 static inline QString expression(const WatchItem *item)
603 {
604     if (!item->exp.isEmpty())
605          return QString::fromAscii(item->exp);
606     if (item->address && !item->type.isEmpty()) {
607         return QString::fromAscii("*(%1*)%2").
608                 arg(QLatin1String(item->type), QLatin1String(item->hexAddress()));
609     }
610     if (const WatchItem *parent = item->parent) {
611         if (!parent->exp.isEmpty())
612            return QString::fromAscii("(%1).%2")
613             .arg(QString::fromLatin1(parent->exp), item->name);
614     }
615     return QString();
616 }
617
618 QVariant WatchModel::data(const QModelIndex &idx, int role) const
619 {
620     const WatchItem *item = watchItem(idx);
621     const WatchItem &data = *item;
622
623     switch (role) {
624         case LocalsEditTypeRole:
625             return QVariant(editType(data));
626
627         case LocalsNameRole:
628             return QVariant(data.name);
629
630         case LocalsIntegerBaseRole:
631             if (isPointerType(data.type)) // Pointers using 0x-convention
632                 return QVariant(16);
633             return QVariant(formatToIntegerBase(itemFormat(data)));
634
635         case Qt::EditRole: {
636             switch (idx.column()) {
637                 case 0:
638                     return QVariant(expression(item));
639                 case 1:
640                     return editValue(data);
641                 case 2:
642                     // FIXME:: To be tested: Can debuggers handle those?
643                     if (!data.displayedType.isEmpty())
644                         return data.displayedType;
645                     return QString::fromUtf8(data.type);
646                 default:
647                     break;
648             }
649         }
650
651         case Qt::DisplayRole: {
652             const QByteArray qtNameSpace = engine()->qtNamespace();
653             QString result;
654             switch (idx.column()) {
655                 case 0:
656                     if (data.name.isEmpty())
657                         result = tr("<Edit>");
658                     else if (data.name == QLatin1String("*") && item->parent)
659                         result = QLatin1Char('*') + item->parent->name;
660                     else
661                         result = removeInitialNamespace(data.name, qtNameSpace);
662                     break;
663                 case 1:
664                     result = removeInitialNamespace(truncateValue(
665                             formattedValue(data, itemFormat(data))), qtNameSpace);
666                     if (data.referencingAddress) {
667                         result += QLatin1String(" @");
668                         result += QString::fromLatin1(data.hexAddress());
669                     }
670                     break;
671                 case 2:
672                     result = removeNamespaces(displayType(data), qtNameSpace);
673                     break;
674                 default:
675                     break;
676             }
677             if (WatchHandler::m_unprintableBase == 0)
678                 return result;
679             QString encoded;
680             foreach (const QChar c, result) {
681                 if (c.isPrint()) {
682                     encoded += c;
683                 } else if (WatchHandler::m_unprintableBase == 8) {
684                     encoded += QString("\\%1")
685                         .arg(c.unicode(), 3, 8, QLatin1Char('0'));
686                 } else {
687                     encoded += QString("\\u%1")
688                         .arg(c.unicode(), 4, 16, QLatin1Char('0'));
689                 }
690             }
691             return encoded;
692         }
693
694         case Qt::ToolTipRole:
695             return debuggerCore()->boolSetting(UseToolTipsInLocalsView)
696                 ? data.toToolTip() : QVariant();
697
698         case Qt::ForegroundRole: {
699             static const QVariant red(QColor(200, 0, 0));
700             static const QVariant gray(QColor(140, 140, 140));
701             switch (idx.column()) {
702                 case 1: return !data.valueEnabled ? gray
703                             : data.changed ? red : QVariant();
704             }
705             break;
706         }
707
708         case LocalsExpressionRole:
709             return QVariant(expression(item));
710
711         case LocalsRawExpressionRole:
712             return data.exp;
713
714         case LocalsINameRole:
715             return data.iname;
716
717         case LocalsExpandedRole:
718             return m_handler->m_expandedINames.contains(data.iname);
719
720         case LocalsTypeFormatListRole: {
721             if (data.referencingAddress || isPointerType(data.type))
722                 return QStringList()
723                     << tr("Raw pointer")
724                     << tr("Latin1 string")
725                     << tr("UTF8 string")
726                     << tr("Local 8bit string")
727                     << tr("UTF16 string")
728                     << tr("UCS4 string");
729             if (data.type.contains("char[") || data.type.contains("char ["))
730                 return QStringList()
731                     << tr("Latin1 string")
732                     << tr("UTF8 string")
733                     << tr("Local 8bit string");
734             bool ok = false;
735             (void)data.value.toULongLong(&ok, 0);
736             if ((isIntType(data.type) && data.type != "bool") || ok)
737                 return QStringList()
738                     << tr("Decimal")
739                     << tr("Hexadecimal")
740                     << tr("Binary")
741                     << tr("Octal");
742             // Hack: Compensate for namespaces.
743             QString type = stripTemplate(data.type);
744             int pos = type.indexOf("::Q");
745             if (pos >= 0 && type.count(':') == 2)
746                 type = type.mid(pos + 2);
747             pos = type.indexOf('<');
748             if (pos >= 0)
749                 type = type.left(pos);
750             type.replace(':', '_');
751             return m_handler->m_reportedTypeFormats.value(type);
752         }
753         case LocalsTypeRole:
754            return removeNamespaces(displayType(data), engine()->qtNamespace());
755         case LocalsRawTypeRole:
756            return QString::fromLatin1(data.type);
757         case LocalsTypeFormatRole:
758             return m_handler->m_typeFormats.value(stripTemplate(data.type), -1);
759
760         case LocalsIndividualFormatRole:
761             return m_handler->m_individualFormats.value(data.iname, -1);
762
763         case LocalsRawValueRole:
764             return data.value;
765
766         case LocalsPointerValueRole:
767             if (isPointerType(data.type))
768                 return pointerValue(data.value);
769             return QVariant(quint64(0));
770
771         case LocalsIsWatchpointAtAddressRole: {
772             BreakpointParameters bp(WatchpointAtAddress);
773             bp.address = data.coreAddress();
774             return engine()->breakHandler()->findWatchpoint(bp) != 0;
775         }
776
777         case LocalsAddressRole:
778             return QVariant(data.coreAddress());
779         case LocalsReferencingAddressRole:
780             return QVariant(data.referencingAddress);
781         case LocalsSizeRole:
782             return QVariant(data.size);
783
784         case LocalsIsWatchpointAtPointerValueRole:
785             if (isPointerType(data.type)) {
786                 BreakpointParameters bp(WatchpointAtAddress);
787                 bp.address = pointerValue(data.value);
788                 return engine()->breakHandler()->findWatchpoint(bp) != 0;
789             }
790             return false;
791
792         default:
793             break;
794     }
795     return QVariant();
796 }
797
798 bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
799 {
800     WatchItem &data = *watchItem(index);
801
802     switch (role) {
803         case Qt::EditRole:
804             switch (index.column()) {
805             case 0: // Watch expression: See delegate.
806                 break;
807             case 1: // Change value
808                 engine()->assignValueInDebugger(&data, expression(&data), value);
809                 break;
810             case 2: // TODO: Implement change type.
811                 engine()->assignValueInDebugger(&data, expression(&data), value);
812                 break;
813             }
814         case LocalsExpandedRole:
815             if (value.toBool()) {
816                 // Should already have been triggered by fetchMore()
817                 //QTC_ASSERT(m_handler->m_expandedINames.contains(data.iname), /**/);
818                 m_handler->m_expandedINames.insert(data.iname);
819             } else {
820                 m_handler->m_expandedINames.remove(data.iname);
821             }
822             break;
823
824         case LocalsTypeFormatRole:
825             m_handler->setFormat(data.type, value.toInt());
826             engine()->updateWatchData(data);
827             break;
828
829         case LocalsIndividualFormatRole: {
830             const int format = value.toInt();
831             if (format == -1) {
832                 m_handler->m_individualFormats.remove(data.iname);
833             } else {
834                 m_handler->m_individualFormats[data.iname] = format;
835             }
836             engine()->updateWatchData(data);
837             break;
838         }
839     }
840
841     emit dataChanged(index, index);
842     return true;
843 }
844
845 Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
846 {
847     if (!idx.isValid())
848         return Qt::ItemFlags();
849
850     // Enabled, editable, selectable, checkable, and can be used both as the
851     // source of a drag and drop operation and as a drop target.
852
853     static const Qt::ItemFlags notEditable
854         = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
855     static const Qt::ItemFlags editable = notEditable | Qt::ItemIsEditable;
856
857     // Disable editing if debuggee is positively running.
858     const bool isRunning = engine() && engine()->state() == InferiorRunOk;
859     if (isRunning && engine() && !engine()->acceptsWatchesWhileRunning())
860         return notEditable;
861
862     const WatchData &data = *watchItem(idx);
863     if (data.isWatcher()) {
864         if (idx.column() == 0 && data.iname.count('.') == 1)
865             return editable; // Watcher names are editable.
866
867         if (!data.name.isEmpty()) {
868             // FIXME: Forcing types is not implemented yet.
869             //if (idx.column() == 2)
870             //    return editable; // Watcher types can be set by force.
871             if (idx.column() == 1 && data.valueEditable)
872                 return editable; // Watcher values are sometimes editable.
873         }
874     } else {
875         if (idx.column() == 1 && data.valueEditable)
876             return editable; // Locals values are sometimes editable.
877     }
878     return notEditable;
879 }
880
881 QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
882 {
883     if (orientation == Qt::Vertical)
884         return QVariant();
885     if (role == Qt::DisplayRole) {
886         switch (section) {
887             case 0: return QString(tr("Name")  + QLatin1String("     "));
888             case 1: return QString(tr("Value") + QLatin1String("     "));
889             case 2: return QString(tr("Type")  + QLatin1String("     "));
890         }
891     }
892     return QVariant();
893 }
894
895 // Determine sort order of watch items by sort order or alphabetical inames
896 // according to setting 'SortStructMembers'. We need a map key for bulkInsert
897 // and a predicate for finding the insertion position of a single item.
898
899 // Set this before using any of the below according to action
900 static bool sortWatchDataAlphabetically = true;
901
902 static bool watchDataLessThan(const QByteArray &iname1, int sortId1,
903     const QByteArray &iname2, int sortId2)
904 {
905     if (!sortWatchDataAlphabetically)
906         return sortId1 < sortId2;
907     // Get positions of last part of iname 'local.this.i1" -> "i1"
908     int cmpPos1 = iname1.lastIndexOf('.');
909     if (cmpPos1 == -1) {
910         cmpPos1 = 0;
911     } else {
912         cmpPos1++;
913     }
914     int cmpPos2 = iname2.lastIndexOf('.');
915     if (cmpPos2 == -1) {
916         cmpPos2 = 0;
917     } else {
918         cmpPos2++;
919     }
920     // Are we looking at an array with numerical inames 'local.this.i1.0" ->
921     // Go by sort id.
922     if (cmpPos1 < iname1.size() && cmpPos2 < iname2.size()
923             && isdigit(iname1.at(cmpPos1)) && isdigit(iname2.at(cmpPos2)))
924         return sortId1 < sortId2;
925     // Alphabetically
926     return qstrcmp(iname1.constData() + cmpPos1, iname2.constData() + cmpPos2) < 0;
927 }
928
929 // Sort key for watch data consisting of iname and numerical sort id.
930 struct WatchDataSortKey
931 {
932     explicit WatchDataSortKey(const WatchData &wd)
933         : iname(wd.iname), sortId(wd.sortId) {}
934     QByteArray iname;
935     int sortId;
936 };
937
938 inline bool operator<(const WatchDataSortKey &k1, const WatchDataSortKey &k2)
939 {
940     return watchDataLessThan(k1.iname, k1.sortId, k2.iname, k2.sortId);
941 }
942
943 bool watchItemSorter(const WatchItem *item1, const WatchItem *item2)
944 {
945     return watchDataLessThan(item1->iname, item1->sortId, item2->iname, item2->sortId);
946 }
947
948 static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
949 {
950     sortWatchDataAlphabetically = debuggerCore()->boolSetting(SortStructMembers);
951     const QList<WatchItem *>::const_iterator it =
952         qLowerBound(list.begin(), list.end(), item, watchItemSorter);
953     return it - list.begin();
954 }
955
956 void WatchModel::insertData(const WatchData &data)
957 {
958     QTC_ASSERT(!data.iname.isEmpty(), qDebug() << data.toString(); return);
959     WatchItem *parent = findItem(parentName(data.iname), m_root);
960     if (!parent) {
961         WatchData parent;
962         parent.iname = parentName(data.iname);
963         MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname);
964         if (!parent.iname.isEmpty())
965             insertData(parent);
966         return;
967     }
968     QModelIndex index = watchIndex(parent);
969     if (WatchItem *oldItem = findItem(data.iname, parent)) {
970         bool hadChildren = oldItem->hasChildren;
971         // Overwrite old entry.
972         bool changed = !data.value.isEmpty()
973             && data.value != oldItem->value
974             && data.value != strNotInScope;
975         oldItem->setData(data);
976         oldItem->changed = changed;
977         oldItem->generation = generationCounter;
978         QModelIndex idx = watchIndex(oldItem);
979         emit dataChanged(idx, idx.sibling(idx.row(), 2));
980
981         // This works around http://bugreports.qt.nokia.com/browse/QTBUG-7115
982         // by creating and destroying a dummy child item.
983         if (!hadChildren && oldItem->hasChildren) {
984             WatchData dummy = data;
985             dummy.iname = data.iname + ".x";
986             dummy.hasChildren = false;
987             dummy.setAllUnneeded();
988             insertData(dummy);
989             destroyItem(findItem(dummy.iname, m_root));
990         }
991     } else {
992         // Add new entry.
993         WatchItem *item = new WatchItem(data);
994         item->parent = parent;
995         item->generation = generationCounter;
996         item->changed = true;
997         const int n = findInsertPosition(parent->children, item);
998         beginInsertRows(index, n, n);
999         parent->children.insert(n, item);
1000         endInsertRows();
1001     }
1002 }
1003
1004 void WatchModel::insertBulkData(const QList<WatchData> &list)
1005 {
1006 #if 0
1007     for (int i = 0; i != list.size(); ++i)
1008         insertData(list.at(i));
1009     return;
1010 #endif
1011     // This method does not properly insert items in proper "iname sort
1012     // order", leading to random removal of items in removeOutdated();
1013
1014     //qDebug() << "WMI:" << list.toString();
1015     //static int bulk = 0;
1016     //foreach (const WatchItem &data, list)
1017     //    qDebug() << "BULK: " << ++bulk << data.toString();
1018     QTC_ASSERT(!list.isEmpty(), return);
1019     QByteArray parentIName = parentName(list.at(0).iname);
1020     WatchItem *parent = findItem(parentIName, m_root);
1021     if (!parent) {
1022         WatchData parent;
1023         parent.iname = parentIName;
1024         insertData(parent);
1025         MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << list.at(0).iname);
1026         return;
1027     }
1028     QModelIndex index = watchIndex(parent);
1029
1030     sortWatchDataAlphabetically = debuggerCore()->boolSetting(SortStructMembers);
1031     QMap<WatchDataSortKey, WatchData> newList;
1032     typedef QMap<WatchDataSortKey, WatchData>::iterator Iterator;
1033     foreach (const WatchItem &data, list)
1034         newList.insert(WatchDataSortKey(data), data);
1035     if (newList.size() != list.size()) {
1036         qDebug() << "LIST: ";
1037         foreach (const WatchItem &data, list)
1038             qDebug() << data.toString();
1039         qDebug() << "NEW LIST: ";
1040         foreach (const WatchItem &data, newList)
1041             qDebug() << data.toString();
1042         qDebug() << "P->CHILDREN: ";
1043         foreach (const WatchItem *item, parent->children)
1044             qDebug() << item->toString();
1045         qDebug()
1046             << "P->CHILDREN.SIZE: " << parent->children.size()
1047             << "NEWLIST SIZE: " << newList.size()
1048             << "LIST SIZE: " << list.size();
1049     }
1050     QTC_ASSERT(newList.size() == list.size(), return);
1051
1052     foreach (WatchItem *oldItem, parent->children) {
1053         const WatchDataSortKey oldSortKey(*oldItem);
1054         Iterator it = newList.find(oldSortKey);
1055         if (it == newList.end()) {
1056             WatchData data = *oldItem;
1057             data.generation = generationCounter;
1058             newList.insert(oldSortKey, data);
1059         } else {
1060             bool changed = !it->value.isEmpty()
1061                 && it->value != oldItem->value
1062                 && it->value != strNotInScope;
1063             it->changed = changed;
1064             if (it->generation == -1)
1065                 it->generation = generationCounter;
1066         }
1067     }
1068
1069     for (Iterator it = newList.begin(); it != newList.end(); ++it) {
1070         qDebug() << "  NEW: " << it->iname;
1071     }
1072
1073     // overwrite existing items
1074     Iterator it = newList.begin();
1075     QModelIndex idx = watchIndex(parent);
1076     const int oldCount = parent->children.size();
1077     for (int i = 0; i < oldCount; ++i, ++it) {
1078         if (!parent->children[i]->isEqual(*it)) {
1079             qDebug() << "REPLACING" << parent->children.at(i)->iname
1080                 << " WITH " << it->iname << it->generation;
1081             parent->children[i]->setData(*it);
1082             if (parent->children[i]->generation == -1)
1083                 parent->children[i]->generation = generationCounter;
1084             //emit dataChanged(idx.sibling(i, 0), idx.sibling(i, 2));
1085         } else {
1086             //qDebug() << "SKIPPING REPLACEMENT" << parent->children.at(i)->iname;
1087         }
1088     }
1089     emit dataChanged(idx.sibling(0, 0), idx.sibling(oldCount - 1, 2));
1090
1091     // add new items
1092     if (oldCount < newList.size()) {
1093         beginInsertRows(index, oldCount, newList.size() - 1);
1094         //MODEL_DEBUG("INSERT : " << data.iname << data.value);
1095         for (int i = oldCount; i < newList.size(); ++i, ++it) {
1096             WatchItem *item = new WatchItem(*it);
1097             qDebug() << "ADDING" << it->iname;
1098             item->parent = parent;
1099             if (item->generation == -1)
1100                 item->generation = generationCounter;
1101             item->changed = true;
1102             parent->children.append(item);
1103         }
1104         endInsertRows();
1105     }
1106     //qDebug() << "ITEMS: " << parent->children.size();
1107     dump();
1108 }
1109
1110 WatchItem *WatchModel::findItem(const QByteArray &iname, WatchItem *root) const
1111 {
1112     if (root->iname == iname)
1113         return root;
1114     for (int i = root->children.size(); --i >= 0; )
1115         if (WatchItem *item = findItem(iname, root->children.at(i)))
1116             return item;
1117     return 0;
1118 }
1119
1120 static void debugRecursion(QDebug &d, const WatchItem *item, int depth)
1121 {
1122     d << QString(2 * depth, QLatin1Char(' ')) << item->toString() << '\n';
1123     foreach (const WatchItem *child, item->children)
1124         debugRecursion(d, child, depth + 1);
1125 }
1126
1127 QDebug operator<<(QDebug d, const WatchModel &m)
1128 {
1129     QDebug nospace = d.nospace();
1130     if (m.m_root)
1131         debugRecursion(nospace, m.m_root, 0);
1132     return d;
1133 }
1134
1135 void WatchModel::formatRequests(QByteArray *out, const WatchItem *item) const
1136 {
1137     int format = m_handler->m_individualFormats.value(item->iname, -1);
1138     if (format == -1)
1139         format = m_handler->m_typeFormats.value(stripTemplate(item->type), -1);
1140     if (format != -1)
1141         *out += item->iname + ":format=" + QByteArray::number(format) + ',';
1142     foreach (const WatchItem *child, item->children)
1143         formatRequests(out, child);
1144 }
1145
1146 ///////////////////////////////////////////////////////////////////////
1147 //
1148 // WatchHandler
1149 //
1150 ///////////////////////////////////////////////////////////////////////
1151
1152 WatchHandler::WatchHandler(DebuggerEngine *engine)
1153 {
1154     m_engine = engine;
1155     m_inChange = false;
1156
1157     m_return = new WatchModel(this, ReturnWatch);
1158     m_locals = new WatchModel(this, LocalsWatch);
1159     m_watchers = new WatchModel(this, WatchersWatch);
1160     m_tooltips = new WatchModel(this, TooltipsWatch);
1161
1162     connect(debuggerCore()->action(ShowStdNamespace),
1163         SIGNAL(triggered()), SLOT(emitAllChanged()));
1164     connect(debuggerCore()->action(ShowQtNamespace),
1165         SIGNAL(triggered()), SLOT(emitAllChanged()));
1166     connect(debuggerCore()->action(SortStructMembers),
1167         SIGNAL(triggered()), SLOT(emitAllChanged()));
1168 }
1169
1170 void WatchHandler::beginCycle(bool fullCycle)
1171 {
1172     if (fullCycle)
1173         ++generationCounter;
1174     m_return->beginCycle();
1175     m_locals->beginCycle();
1176     m_watchers->beginCycle();
1177     m_tooltips->beginCycle();
1178 }
1179
1180 void WatchHandler::endCycle(bool /*fullCycle*/)
1181 {
1182     m_return->endCycle();
1183     m_locals->endCycle();
1184     m_watchers->endCycle();
1185     m_tooltips->endCycle();
1186     updateWatchersWindow();
1187 }
1188
1189 void WatchHandler::cleanup()
1190 {
1191     m_expandedINames.clear();
1192     m_return->reinitialize();
1193     m_locals->reinitialize();
1194     m_tooltips->reinitialize();
1195     m_return->m_fetchTriggered.clear();
1196     m_locals->m_fetchTriggered.clear();
1197     m_watchers->m_fetchTriggered.clear();
1198     m_tooltips->m_fetchTriggered.clear();
1199 #if 1
1200     for (EditHandlers::ConstIterator it = m_editHandlers.begin();
1201             it != m_editHandlers.end(); ++it) {
1202         if (!it.value().isNull())
1203             delete it.value();
1204     }
1205     m_editHandlers.clear();
1206 #endif
1207 }
1208
1209 void WatchHandler::emitAllChanged()
1210 {
1211     m_return->emitAllChanged();
1212     m_locals->emitAllChanged();
1213     m_watchers->emitAllChanged();
1214     m_tooltips->emitAllChanged();
1215 }
1216
1217 void WatchHandler::insertData(const WatchData &data)
1218 {
1219     MODEL_DEBUG("INSERTDATA: " << data.toString());
1220     if (!data.isValid()) {
1221         qWarning("%s:%d: Attempt to insert invalid watch item: %s",
1222             __FILE__, __LINE__, qPrintable(data.toString()));
1223         return;
1224     }
1225
1226     if (data.isSomethingNeeded() && data.iname.contains('.')) {
1227         MODEL_DEBUG("SOMETHING NEEDED: " << data.toString());
1228         if (!m_engine->isSynchronous()) {
1229             WatchModel *model = modelForIName(data.iname);
1230             QTC_ASSERT(model, return);
1231             model->insertData(data);
1232
1233             m_engine->updateWatchData(data);
1234         } else {
1235             m_engine->showMessage(QLatin1String("ENDLESS LOOP: SOMETHING NEEDED: ")
1236                 + data.toString());
1237             WatchData data1 = data;
1238             data1.setAllUnneeded();
1239             data1.setValue(QLatin1String("<unavailable synchronous data>"));
1240             data1.setHasChildren(false);
1241             WatchModel *model = modelForIName(data.iname);
1242             QTC_ASSERT(model, return);
1243             model->insertData(data1);
1244         }
1245     } else {
1246         WatchModel *model = modelForIName(data.iname);
1247         QTC_ASSERT(model, return);
1248         MODEL_DEBUG("NOTHING NEEDED: " << data.toString());
1249         model->insertData(data);
1250         showEditValue(data);
1251     }
1252 }
1253
1254 // Bulk-insertion
1255 void WatchHandler::insertBulkData(const QList<WatchData> &list)
1256 {
1257 #if 1
1258     foreach (const WatchItem &data, list)
1259         insertData(data);
1260     return;
1261 #endif
1262
1263     if (list.isEmpty())
1264         return;
1265     QMap<QByteArray, QList<WatchData> > hash;
1266
1267     foreach (const WatchData &data, list) {
1268         // we insert everything, including incomplete stuff
1269         // to reduce the number of row add operations in the model.
1270         if (data.isValid()) {
1271             hash[parentName(data.iname)].append(data);
1272         } else {
1273             qWarning("%s:%d: Attempt to bulk-insert invalid watch item: %s",
1274                 __FILE__, __LINE__, qPrintable(data.toString()));
1275         }
1276     }
1277     foreach (const QByteArray &parentIName, hash.keys()) {
1278         WatchModel *model = modelForIName(parentIName);
1279         QTC_ASSERT(model, return);
1280         model->insertBulkData(hash[parentIName]);
1281     }
1282
1283     foreach (const WatchData &data, list) {
1284         if (data.isSomethingNeeded())
1285             m_engine->updateWatchData(data);
1286     }
1287 }
1288
1289 void WatchHandler::removeData(const QByteArray &iname)
1290 {
1291     WatchModel *model = modelForIName(iname);
1292     if (!model)
1293         return;
1294     WatchItem *item = model->findItem(iname, model->m_root);
1295     if (item)
1296         model->destroyItem(item);
1297 }
1298
1299 QByteArray WatchHandler::watcherName(const QByteArray &exp)
1300 {
1301     return "watch." + QByteArray::number(m_watcherNames[exp]);
1302 }
1303
1304 void WatchHandler::watchExpression(const QString &exp)
1305 {
1306     QTC_ASSERT(m_engine, return);
1307     // Do not insert the same entry more then once.
1308     if (m_watcherNames.value(exp.toLatin1()))
1309         return;
1310
1311     // FIXME: 'exp' can contain illegal characters
1312     WatchData data;
1313     data.exp = exp.toLatin1();
1314     data.name = exp;
1315     m_watcherNames[data.exp] = watcherCounter++;
1316     saveWatchers();
1317
1318     if (exp.isEmpty())
1319         data.setAllUnneeded();
1320     data.iname = watcherName(data.exp);
1321     if (m_engine->state() == DebuggerNotReady) {
1322         data.setAllUnneeded();
1323         data.setValue(" ");
1324         data.setHasChildren(false);
1325         insertData(data);
1326     } else if (m_engine->isSynchronous()) {
1327         m_engine->updateWatchData(data);
1328     } else {
1329         insertData(data);
1330     }
1331     updateWatchersWindow();
1332     emitAllChanged();
1333 }
1334
1335 static void swapEndian(char *d, int nchar)
1336 {
1337     QTC_ASSERT(nchar % 4 == 0, return);
1338     for (int i = 0; i < nchar; i += 4) {
1339         char c = d[i];
1340         d[i] = d[i + 3];
1341         d[i + 3] = c;
1342         c = d[i + 1];
1343         d[i + 1] = d[i + 2];
1344         d[i + 2] = c;
1345     }
1346 }
1347
1348 void WatchHandler::showEditValue(const WatchData &data)
1349 {
1350     const QByteArray key  = data.address ? data.hexAddress() : data.iname;
1351     QObject *w = m_editHandlers.value(key);
1352     if (data.editformat == 0x0) {
1353         m_editHandlers.remove(data.iname);
1354         delete w;
1355     } else if (data.editformat == 1 || data.editformat == 3) {
1356         // QImage
1357         QLabel *l = qobject_cast<QLabel *>(w);
1358         if (!l) {
1359             delete w;
1360             l = new QLabel;
1361             const QString title = data.address ?
1362                 tr("%1 Object at %2").arg(QLatin1String(data.type),
1363                     QLatin1String(data.hexAddress())) :
1364                 tr("%1 Object at Unknown Address").arg(QLatin1String(data.type));
1365             l->setWindowTitle(title);
1366             m_editHandlers[key] = l;
1367         }
1368         int width, height, format;
1369         QByteArray ba;
1370         uchar *bits;
1371         if (data.editformat == 1) {
1372             ba = QByteArray::fromHex(data.editvalue);
1373             const int *header = (int *)(ba.data());
1374             swapEndian(ba.data(), ba.size());
1375             bits = 12 + (uchar *)(ba.data());
1376             width = header[0];
1377             height = header[1];
1378             format = header[2];
1379         } else { // data.editformat == 3
1380             QTextStream ts(data.editvalue);
1381             QString fileName;
1382             ts >> width >> height >> format >> fileName;
1383             QFile f(fileName);
1384             f.open(QIODevice::ReadOnly);
1385             ba = f.readAll();
1386             bits = (uchar*)ba.data();
1387         }
1388         QImage im(bits, width, height, QImage::Format(format));
1389
1390 #if 1
1391         // Qt bug. Enforce copy of image data.
1392         QImage im2(im);
1393         im.detach();
1394 #endif
1395
1396         l->setPixmap(QPixmap::fromImage(im));
1397         l->resize(width, height);
1398         l->show();
1399     } else if (data.editformat == 2) {
1400         // Display QString in a separate widget.
1401         QTextEdit *t = qobject_cast<QTextEdit *>(w);
1402         if (!t) {
1403             delete w;
1404             t = new QTextEdit;
1405             m_editHandlers[key] = t;
1406         }
1407         QByteArray ba = QByteArray::fromHex(data.editvalue);
1408         QString str = QString::fromUtf16((ushort *)ba.constData(), ba.size()/2);
1409         t->setText(str);
1410         t->resize(400, 200);
1411         t->show();
1412     } else if (data.editformat == 4) {
1413         // Generic Process.
1414         int pos = data.editvalue.indexOf('|');
1415         QByteArray cmd = data.editvalue.left(pos);
1416         QByteArray input = data.editvalue.mid(pos + 1);
1417         QProcess *p = qobject_cast<QProcess *>(w);
1418         if (!p) {
1419             p = new QProcess;
1420             p->start(cmd);
1421             p->waitForStarted();
1422             m_editHandlers[key] = p;
1423         }
1424         p->write(input + '\n');
1425     } else {
1426         QTC_ASSERT(false, qDebug() << "Display format: " << data.editformat);
1427     }
1428 }
1429
1430 void WatchHandler::clearWatches()
1431 {
1432     if (m_watcherNames.isEmpty())
1433         return;
1434     const QList<WatchItem *> watches = m_watchers->rootItem()->children;
1435     for (int i = watches.size() - 1; i >= 0; i--)
1436         m_watchers->destroyItem(watches.at(i));
1437     m_watcherNames.clear();
1438     watcherCounter = 0;
1439     updateWatchersWindow();
1440     emitAllChanged();
1441     saveWatchers();
1442 }
1443
1444 void WatchHandler::removeWatchExpression(const QString &exp0)
1445 {
1446     QByteArray exp = exp0.toLatin1();
1447     MODEL_DEBUG("REMOVE WATCH: " << exp);
1448     m_watcherNames.remove(exp);
1449     foreach (WatchItem *item, m_watchers->rootItem()->children) {
1450         if (item->exp == exp) {
1451             m_watchers->destroyItem(item);
1452             saveWatchers();
1453             updateWatchersWindow();
1454             emitAllChanged();
1455             break;
1456         }
1457     }
1458 }
1459
1460 void WatchHandler::updateWatchersWindow()
1461 {
1462     // Force show/hide of watchers and return view.
1463     debuggerCore()->updateWatchersWindow();
1464 }
1465
1466 QStringList WatchHandler::watchedExpressions()
1467 {
1468     // Filter out invalid watchers.
1469     QStringList watcherNames;
1470     QHashIterator<QByteArray, int> it(m_watcherNames);
1471     while (it.hasNext()) {
1472         it.next();
1473         const QString &watcherName = it.key();
1474         if (!watcherName.isEmpty())
1475             watcherNames.push_back(watcherName);
1476     }
1477     return watcherNames;
1478 }
1479
1480 void WatchHandler::saveWatchers()
1481 {
1482     debuggerCore()->setSessionValue("Watchers", QVariant(watchedExpressions()));
1483 }
1484
1485 void WatchHandler::loadTypeFormats()
1486 {
1487     QVariant value = debuggerCore()->sessionValue("DefaultFormats");
1488     QMap<QString, QVariant> typeFormats = value.toMap();
1489     QMapIterator<QString, QVariant> it(typeFormats);
1490     while (it.hasNext()) {
1491         it.next();
1492         if (!it.key().isEmpty())
1493             m_typeFormats.insert(it.key().toUtf8(), it.value().toInt());
1494     }
1495 }
1496
1497 void WatchHandler::saveTypeFormats()
1498 {
1499     QMap<QString, QVariant> typeFormats;
1500     QHashIterator<QByteArray, int> it(m_typeFormats);
1501     while (it.hasNext()) {
1502         it.next();
1503         const int format = it.value();
1504         if (format != DecimalFormat) {
1505             const QString key = it.key().trimmed();
1506             if (!key.isEmpty())
1507                 typeFormats.insert(key, format);
1508         }
1509     }
1510     debuggerCore()->setSessionValue("DefaultFormats", QVariant(typeFormats));
1511 }
1512
1513 void WatchHandler::saveSessionData()
1514 {
1515     saveWatchers();
1516     saveTypeFormats();
1517 }
1518
1519 void WatchHandler::loadSessionData()
1520 {
1521     loadTypeFormats();
1522     m_watcherNames.clear();
1523     QVariant value = debuggerCore()->sessionValue("Watchers");
1524     foreach (WatchItem *item, m_watchers->rootItem()->children)
1525         m_watchers->destroyItem(item);
1526     foreach (const QString &exp, value.toStringList())
1527         watchExpression(exp);
1528     updateWatchersWindow();
1529     emitAllChanged();
1530 }
1531
1532 void WatchHandler::updateWatchers()
1533 {
1534     foreach (WatchItem *item, m_watchers->rootItem()->children)
1535         m_watchers->destroyItem(item);
1536     // Copy over all watchers and mark all watchers as incomplete.
1537     foreach (const QByteArray &exp, m_watcherNames.keys()) {
1538         WatchData data;
1539         data.iname = watcherName(exp);
1540         data.setAllNeeded();
1541         data.name = exp;
1542         data.exp = exp;
1543         insertData(data);
1544     }
1545 }
1546
1547 WatchModel *WatchHandler::model(WatchType type) const
1548 {
1549     switch (type) {
1550         case ReturnWatch: return m_return;
1551         case LocalsWatch: return m_locals;
1552         case WatchersWatch: return m_watchers;
1553         case TooltipsWatch: return m_tooltips;
1554     }
1555     QTC_ASSERT(false, /**/);
1556     return 0;
1557 }
1558
1559 WatchModel *WatchHandler::modelForIName(const QByteArray &iname) const
1560 {
1561     if (iname.startsWith("return"))
1562         return m_return;
1563     if (iname.startsWith("local"))
1564         return m_locals;
1565     if (iname.startsWith("tooltip"))
1566         return m_tooltips;
1567     if (iname.startsWith("watch"))
1568         return m_watchers;
1569     QTC_ASSERT(false, qDebug() << "INAME: " << iname);
1570     return 0;
1571 }
1572
1573 const WatchData *WatchHandler::watchData(WatchType type, const QModelIndex &index) const
1574 {
1575     if (index.isValid())
1576         if (const WatchModel *m = model(type))
1577             return m->watchItem(index);
1578     return 0;
1579 }
1580
1581 const WatchData *WatchHandler::findItem(const QByteArray &iname) const
1582 {
1583     const WatchModel *model = modelForIName(iname);
1584     QTC_ASSERT(model, return 0);
1585     return model->findItem(iname, model->m_root);
1586 }
1587
1588 QModelIndex WatchHandler::itemIndex(const QByteArray &iname) const
1589 {
1590     if (const WatchModel *model = modelForIName(iname))
1591         if (WatchItem *item = model->findItem(iname, model->m_root))
1592             return model->watchIndex(item);
1593     return QModelIndex();
1594 }
1595
1596 void WatchHandler::setFormat(const QByteArray &type0, int format)
1597 {
1598     const QByteArray type = stripTemplate(type0);
1599     if (format == -1)
1600         m_typeFormats.remove(type);
1601     else
1602         m_typeFormats[type] = format;
1603     saveTypeFormats();
1604     m_return->emitDataChanged(1);
1605     m_locals->emitDataChanged(1);
1606     m_watchers->emitDataChanged(1);
1607     m_tooltips->emitDataChanged(1);
1608 }
1609
1610 int WatchHandler::format(const QByteArray &iname) const
1611 {
1612     int result = -1;
1613     if (const WatchData *item = findItem(iname)) {
1614         int result = m_individualFormats.value(item->iname, -1);
1615         if (result == -1)
1616             result = m_typeFormats.value(stripTemplate(item->type), -1);
1617     }
1618     return result;
1619 }
1620
1621 QByteArray WatchHandler::expansionRequests() const
1622 {
1623     QByteArray ba;
1624     //m_locals->formatRequests(&ba, m_locals->m_root);
1625     //m_watchers->formatRequests(&ba, m_watchers->m_root);
1626     if (!m_expandedINames.isEmpty()) {
1627         QSetIterator<QByteArray> jt(m_expandedINames);
1628         while (jt.hasNext()) {
1629             QByteArray iname = jt.next();
1630             ba.append(iname);
1631             ba.append(',');
1632         }
1633         ba.chop(1);
1634     }
1635     return ba;
1636 }
1637
1638 QByteArray WatchHandler::typeFormatRequests() const
1639 {
1640     QByteArray ba;
1641     if (!m_typeFormats.isEmpty()) {
1642         QHashIterator<QByteArray, int> it(m_typeFormats);
1643         while (it.hasNext()) {
1644             it.next();
1645             ba.append(it.key().toHex());
1646             ba.append('=');
1647             ba.append(QByteArray::number(it.value()));
1648             ba.append(',');
1649         }
1650         ba.chop(1);
1651     }
1652     return ba;
1653 }
1654
1655 QByteArray WatchHandler::individualFormatRequests() const
1656 {
1657     QByteArray ba;
1658     if (!m_individualFormats.isEmpty()) {
1659         QHashIterator<QByteArray, int> it(m_individualFormats);
1660         while (it.hasNext()) {
1661             it.next();
1662             ba.append(it.key());
1663             ba.append('=');
1664             ba.append(QByteArray::number(it.value()));
1665             ba.append(',');
1666         }
1667         ba.chop(1);
1668     }
1669     return ba;
1670 }
1671
1672 void WatchHandler::addTypeFormats(const QByteArray &type, const QStringList &formats)
1673 {
1674     m_reportedTypeFormats.insert(stripTemplate(type), formats);
1675 }
1676
1677 QString WatchHandler::editorContents()
1678 {
1679     QString contents;
1680     showInEditorHelper(&contents, m_locals->m_root, 0);
1681     showInEditorHelper(&contents, m_watchers->m_root, 0);
1682     return contents;
1683 }
1684
1685 void WatchHandler::showInEditorHelper(QString *contents, WatchItem *item, int depth)
1686 {
1687     const QChar tab = QLatin1Char('\t');
1688     const QChar nl = QLatin1Char('\n');
1689     contents->append(QString(depth, tab));
1690     contents->append(item->name);
1691     contents->append(tab);
1692     contents->append(item->value);
1693     contents->append(nl);
1694     foreach (WatchItem *child, item->children)
1695        showInEditorHelper(contents, child, depth + 1);
1696 }
1697
1698 void WatchHandler::removeTooltip()
1699 {
1700     m_tooltips->reinitialize();
1701     m_tooltips->emitAllChanged();
1702 }
1703
1704 void WatchHandler::rebuildModel()
1705 {
1706     beginCycle();
1707
1708     const QList<WatchItem *> watches = m_watchers->rootItem()->children;
1709     for (int i = watches.size() - 1; i >= 0; i--)
1710         m_watchers->destroyItem(watches.at(i));
1711
1712     foreach (const QString &exp, watchedExpressions()) {
1713         WatchData data;
1714         data.exp = exp.toLatin1();
1715         data.name = exp;
1716         data.iname = watcherName(data.exp);
1717         data.setAllUnneeded();
1718
1719         insertData(data);
1720     }
1721
1722     endCycle();
1723 }
1724
1725 } // namespace Internal
1726 } // namespace Debugger