OSDN Git Service

82c694880bac279e7093bf8519502a17fa6a4fae
[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) 2009 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** Commercial Usage
10 **
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** 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://qt.nokia.com/contact.
27 **
28 **************************************************************************/
29
30 #include "watchhandler.h"
31 #include "watchutils.h"
32 #include "debuggeractions.h"
33 #include "debuggermanager.h"
34 #include "idebuggerengine.h"
35
36 #if USE_MODEL_TEST
37 #include "modeltest.h"
38 #endif
39
40 #include <utils/qtcassert.h>
41
42 #include <QtCore/QDebug>
43 #include <QtCore/QEvent>
44
45 #include <QtCore/QtAlgorithms>
46 #include <QtCore/QTextStream>
47 #include <QtCore/QTimer>
48
49 #include <QtGui/QAction>
50 #include <QtGui/QApplication>
51 #include <QtGui/QLabel>
52 #include <QtGui/QToolTip>
53 #include <QtGui/QTextEdit>
54
55 #include <ctype.h>
56
57
58 // creates debug output for accesses to the model
59 //#define DEBUG_MODEL 1
60
61 #if DEBUG_MODEL
62 #   define MODEL_DEBUG(s) qDebug() << s
63 #else
64 #   define MODEL_DEBUG(s) 
65 #endif
66 #define MODEL_DEBUGX(s) qDebug() << s
67
68 namespace Debugger {
69 namespace Internal {
70
71 static const QString strNotInScope =
72     QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
73
74 static int watcherCounter = 0;
75 static int generationCounter = 0;
76
77 ////////////////////////////////////////////////////////////////////
78 //
79 // WatchItem
80 //
81 ////////////////////////////////////////////////////////////////////
82
83 class WatchItem : public WatchData
84 {
85 public:
86     WatchItem() { parent = 0; fetchTriggered = false; }
87
88     WatchItem(const WatchData &data) : WatchData(data)
89         { parent = 0; fetchTriggered = false; }
90
91     void setData(const WatchData &data)
92         { static_cast<WatchData &>(*this) = data; }
93
94     WatchItem *parent;
95     bool fetchTriggered;      // children fetch has been triggered
96     QList<WatchItem *> children;  // fetched children
97 };
98
99 ////////////////////////////////////////////////////////////////////
100 //
101 // WatchData
102 //
103 ////////////////////////////////////////////////////////////////////
104    
105 WatchData::WatchData() :
106     hasChildren(false),
107     generation(-1),
108     valueEnabled(true),
109     valueEditable(true),
110     error(false),
111     source(0),
112     state(InitialState),
113     changed(false)
114 {
115 }
116
117 bool WatchData::isEqual(const WatchData &other) const
118 {
119     return iname == other.iname
120       && exp == other.exp
121       && name == other.name
122       && value == other.value
123       && editvalue == other.editvalue
124       && valuetooltip == other.valuetooltip
125       && type == other.type
126       && displayedType == other.displayedType
127       && variable == other.variable
128       && addr == other.addr
129       && saddr == other.saddr
130       && framekey == other.framekey
131       && hasChildren == other.hasChildren
132       && valueEnabled == other.valueEnabled
133       && valueEditable == other.valueEditable
134       && error == other.error;
135 }
136
137 void WatchData::setError(const QString &msg)
138 {
139     setAllUnneeded();
140     value = msg;
141     setHasChildren(false);
142     valueEnabled = false;
143     valueEditable = false;
144     error = true;
145 }
146
147 void WatchData::setValue(const QString &value0)
148 {
149     value = value0;
150     if (value == "{...}") {
151         value.clear();
152         hasChildren = true; // at least one...
153     }
154
155     // avoid duplicated information
156     if (value.startsWith("(") && value.contains(") 0x"))
157         value = value.mid(value.lastIndexOf(") 0x") + 2);
158
159     // doubles are sometimes displayed as "@0x6141378: 1.2".
160     // I don't want that.
161     if (/*isIntOrFloatType(type) && */ value.startsWith("@0x")
162          && value.contains(':')) {
163         value = value.mid(value.indexOf(':') + 2);
164         setHasChildren(false);
165     }
166
167     // "numchild" is sometimes lying
168     //MODEL_DEBUG("\n\n\nPOINTER: " << type << value);
169     if (isPointerType(type))
170         setHasChildren(value != "0x0" && value != "<null>");
171
172     // pointer type information is available in the 'type'
173     // column. No need to duplicate it here.
174     if (value.startsWith("(" + type + ") 0x"))
175         value = value.section(" ", -1, -1);
176     
177     setValueUnneeded();
178 }
179
180 void WatchData::setValueToolTip(const QString &tooltip)
181 {
182     valuetooltip = tooltip;
183 }
184
185 void WatchData::setType(const QString &str, bool guessChildrenFromType)
186 {
187     type = str.trimmed();
188     bool changed = true;
189     while (changed) {
190         if (type.endsWith(QLatin1String("const")))
191             type.chop(5);
192         else if (type.endsWith(QLatin1Char(' ')))
193             type.chop(1);
194         else if (type.endsWith(QLatin1Char('&')))
195             type.chop(1);
196         else if (type.startsWith(QLatin1String("const ")))
197             type = type.mid(6);
198         else if (type.startsWith(QLatin1String("volatile ")))
199             type = type.mid(9);
200         else if (type.startsWith(QLatin1String("class ")))
201             type = type.mid(6);
202         else if (type.startsWith(QLatin1String("struct ")))
203             type = type.mid(6);
204         else if (type.startsWith(QLatin1Char(' ')))
205             type = type.mid(1);
206         else
207             changed = false;
208     }
209     setTypeUnneeded();
210     if (guessChildrenFromType) {
211         switch (guessChildren(type)) {
212         case HasChildren:
213             setHasChildren(true);
214             break;
215         case HasNoChildren:
216             setHasChildren(false);
217             break;
218         case HasPossiblyChildren:
219             setHasChildren(true); // FIXME: bold assumption
220             break;
221         }
222     }
223 }
224
225 void WatchData::setAddress(const QString &str)
226 {
227     addr = str;
228 }
229
230 QString WatchData::toString() const
231 {
232     const char *doubleQuoteComma = "\",";
233     QString res;
234     QTextStream str(&res);
235     str << QLatin1Char('{');
236     if (!iname.isEmpty())
237         str << "iname=\"" << iname << doubleQuoteComma;
238     if (!name.isEmpty() && name != iname)
239         str << "name=\"" << name << doubleQuoteComma;
240     if (error)
241         str << "error,";
242     if (!addr.isEmpty())
243         str << "addr=\"" << addr << doubleQuoteComma;
244     if (!exp.isEmpty())
245         str << "exp=\"" << exp << doubleQuoteComma;
246
247     if (!variable.isEmpty())
248         str << "variable=\"" << variable << doubleQuoteComma;
249
250     if (isValueNeeded())
251         str << "value=<needed>,";
252     if (isValueKnown() && !value.isEmpty())
253         str << "value=\"" << value << doubleQuoteComma;
254
255     if (!editvalue.isEmpty())
256         str << "editvalue=\"" << editvalue << doubleQuoteComma;
257
258     if (isTypeNeeded())
259         str << "type=<needed>,";
260     if (isTypeKnown() && !type.isEmpty())
261         str << "type=\"" << type << doubleQuoteComma;
262
263     if (isHasChildrenNeeded())
264         str << "hasChildren=<needed>,";
265     if (isHasChildrenKnown())
266         str << "hasChildren=\"" << (hasChildren ? "true" : "false") << doubleQuoteComma;
267
268     if (isChildrenNeeded())
269         str << "children=<needed>,";
270     if (source)
271         str << "source=" << source;
272     str.flush();
273     if (res.endsWith(QLatin1Char(',')))
274         res.truncate(res.size() - 1);
275     return res + QLatin1Char('}');
276 }
277
278 // Format a tooltip fow with aligned colon
279 static void formatToolTipRow(QTextStream &str, const QString &category, const QString &value)
280 {
281     str << "<tr><td>" << category << "</td><td> : </td><td>"
282         << Qt::escape(value) << "</td></tr>";
283 }
284
285 static inline QString typeToolTip(const WatchData &wd)
286 {
287     if (wd.displayedType.isEmpty())
288         return wd.type;
289     QString rc = wd.displayedType;
290     rc += QLatin1String(" (");
291     rc += wd.type;
292     rc += QLatin1Char(')');
293     return rc;
294 }
295
296 QString WatchData::toToolTip() const
297 {
298     if (!valuetooltip.isEmpty())
299         return QString::number(valuetooltip.size());
300     QString res;
301     QTextStream str(&res);
302     str << "<html><body><table>";
303     formatToolTipRow(str, WatchHandler::tr("Expression"), exp);
304     formatToolTipRow(str, WatchHandler::tr("Type"), typeToolTip(*this));
305     QString val = value;
306     if (value.size() > 1000) {
307         val.truncate(1000);
308         val +=  WatchHandler::tr(" ... <cut off>");
309     }
310     formatToolTipRow(str, WatchHandler::tr("Value"), val);
311     formatToolTipRow(str, WatchHandler::tr("Object Address"), addr);
312     formatToolTipRow(str, WatchHandler::tr("Stored Address"), saddr);
313     formatToolTipRow(str, WatchHandler::tr("Internal ID"), iname);
314     formatToolTipRow(str, WatchHandler::tr("Generation"),
315         QString::number(generation));
316     str << "</table></body></html>";
317     return res;
318 }
319
320 QString WatchData::msgNotInScope()
321 {
322     static const QString rc = QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
323     return rc;
324 }
325
326 QString WatchData::shadowedName(const QString &name, int seen)
327 {
328     if (seen <= 0)
329         return name;
330     return QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>").arg(name).arg(seen);
331 }
332
333 ///////////////////////////////////////////////////////////////////////
334 //
335 // WatchModel
336 //
337 ///////////////////////////////////////////////////////////////////////
338
339 WatchModel::WatchModel(WatchHandler *handler, WatchType type)
340     : QAbstractItemModel(handler), m_handler(handler), m_type(type)
341 {
342     m_root = new WatchItem;
343     m_root->hasChildren = 1;
344     m_root->state = 0;
345     m_root->name = WatchHandler::tr("Root");
346     m_root->parent = 0;
347     m_root->fetchTriggered = true;
348
349     switch (m_type) {
350         case LocalsWatch:
351             m_root->iname = QLatin1String("local");
352             m_root->name = WatchHandler::tr("Locals");
353             break;
354         case WatchersWatch:
355             m_root->iname = QLatin1String("watch");
356             m_root->name = WatchHandler::tr("Watchers");
357             break;
358         case TooltipsWatch:
359             m_root->iname = QLatin1String("tooltip");
360             m_root->name = WatchHandler::tr("Tooltip");
361             break;
362     }
363 }
364
365 WatchModel::~WatchModel()
366 {
367     delete m_root;
368 }
369
370 WatchItem *WatchModel::rootItem() const
371 {
372     return m_root;
373 }
374
375 void WatchModel::reinitialize()
376 {
377     int n = m_root->children.size();
378     if (n == 0)
379         return;
380     //MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << m_root->iname);
381     QModelIndex index = watchIndex(m_root);
382     beginRemoveRows(index, 0, n - 1);
383     qDeleteAll(m_root->children);
384     m_root->children.clear();
385     endRemoveRows();
386 }
387
388 void WatchModel::beginCycle()
389 {
390     emit enableUpdates(false);
391 }
392
393 void WatchModel::endCycle()
394 {
395     removeOutdated();
396     emit enableUpdates(true);
397 }
398
399 void WatchModel::dump()
400 {
401     qDebug() << "\n";
402     foreach (WatchItem *child, m_root->children)
403         dumpHelper(child);
404 }
405
406 void WatchModel::dumpHelper(WatchItem *item)
407 {
408     qDebug() << "ITEM: " << item->iname
409         << (item->parent ? item->parent->iname : "<none>")
410         << item->generation;
411     foreach (WatchItem *child, item->children)
412         dumpHelper(child);
413 }
414
415 void WatchModel::removeOutdated()
416 {
417     foreach (WatchItem *child, m_root->children)
418         removeOutdatedHelper(child);
419 #if DEBUG_MODEL
420 #if USE_MODEL_TEST
421     //(void) new ModelTest(this, this);
422 #endif
423 #endif
424 }
425
426 void WatchModel::removeOutdatedHelper(WatchItem *item)
427 {
428     if (item->generation < generationCounter) {
429         destroyItem(item);
430     } else {
431         foreach (WatchItem *child, item->children)
432             removeOutdatedHelper(child);
433         item->fetchTriggered = false;
434     }
435 }
436
437 void WatchModel::destroyItem(WatchItem *item)
438 {
439     WatchItem *parent = item->parent;
440     QModelIndex index = watchIndex(parent);
441     int n = parent->children.indexOf(item);
442     //MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n);
443     beginRemoveRows(index, n, n);
444     parent->children.removeAt(n);
445     endRemoveRows();
446     delete item;
447 }
448
449 static QString parentName(const QString &iname)
450 {
451     int pos = iname.lastIndexOf(QLatin1Char('.'));
452     if (pos == -1)
453         return QString();
454     return iname.left(pos);
455 }
456
457
458 static QString chopConst(QString type)
459 {
460    while (1) {
461         if (type.startsWith("const"))
462             type = type.mid(5);
463         else if (type.startsWith(' '))
464             type = type.mid(1);
465         else if (type.endsWith("const"))
466             type.chop(5);
467         else if (type.endsWith(' '))
468             type.chop(1);
469         else
470             break;
471     }
472     return type;
473 }
474
475 static inline QRegExp stdStringRegExp(const QString &charType)
476 {
477     QString rc = QLatin1String("basic_string<");
478     rc += charType;
479     rc += QLatin1String(",[ ]?std::char_traits<");
480     rc += charType;
481     rc += QLatin1String(">,[ ]?std::allocator<");
482     rc += charType;
483     rc += QLatin1String("> >");
484     const QRegExp re(rc);
485     Q_ASSERT(re.isValid());
486     return re;
487 }
488
489 QString niceType(const QString typeIn)
490 {
491     static QMap<QString, QString> cache;
492     const QMap<QString, QString>::const_iterator it = cache.constFind(typeIn);
493     if (it != cache.constEnd())
494         return it.value();
495
496     QString type = typeIn;
497     type.replace(QLatin1Char('*'), QLatin1Char('@'));
498
499     for (int i = 0; i < 10; ++i) {
500         int start = type.indexOf("std::allocator<");
501         if (start == -1)
502             break; 
503         // search for matching '>'
504         int pos;
505         int level = 0;
506         for (pos = start + 12; pos < type.size(); ++pos) {
507             int c = type.at(pos).unicode();
508             if (c == '<') {
509                 ++level;
510             } else if (c == '>') {
511                 --level;
512                 if (level == 0)
513                     break;
514             }
515         }
516         QString alloc = type.mid(start, pos + 1 - start).trimmed();
517         QString inner = alloc.mid(15, alloc.size() - 16).trimmed();
518
519         if (inner == QLatin1String("char")) { // std::string
520             const QRegExp stringRegexp = stdStringRegExp(inner);
521             type.replace(stringRegexp, QLatin1String("string"));
522         } else if (inner == QLatin1String("wchar_t")) { // std::wstring
523             const QRegExp wchartStringRegexp = stdStringRegExp(inner);
524             type.replace(wchartStringRegexp, QLatin1String("wstring"));
525         } else if (inner == QLatin1String("unsigned short")) { // std::wstring/MSVC
526             const QRegExp usStringRegexp = stdStringRegExp(inner);
527             type.replace(usStringRegexp, QLatin1String("wstring"));
528         }
529         // std::vector, std::deque, std::list
530         const QRegExp re1(QString::fromLatin1("(vector|list|deque)<%1, ?%2\\s*>").arg(inner, alloc));
531         Q_ASSERT(re1.isValid());
532         if (re1.indexIn(type) != -1)
533             type.replace(re1.cap(0), QString::fromLatin1("%1<%2>").arg(re1.cap(1), inner));
534
535         // std::stack
536         QRegExp re6(QString::fromLatin1("stack<%1, ?std::deque<%2> >").arg(inner, inner));
537         if (!re6.isMinimal())
538             re6.setMinimal(true);
539         Q_ASSERT(re6.isValid());
540         if (re6.indexIn(type) != -1)
541             type.replace(re6.cap(0), QString::fromLatin1("stack<%1>").arg(inner));
542
543         // std::set
544         QRegExp re4(QString::fromLatin1("set<%1, ?std::less<%2>, ?%3\\s*>").arg(inner, inner, alloc));
545         if (!re4.isMinimal())
546             re4.setMinimal(true);
547         Q_ASSERT(re4.isValid());
548         if (re4.indexIn(type) != -1)
549             type.replace(re4.cap(0), QString::fromLatin1("set<%1>").arg(inner));
550
551         // std::map
552         if (inner.startsWith("std::pair<")) {
553             // search for outermost ','
554             int pos;
555             int level = 0;
556             for (pos = 10; pos < inner.size(); ++pos) {
557                 int c = inner.at(pos).unicode();
558                 if (c == '<')
559                     ++level;
560                 else if (c == '>')
561                     --level;
562                 else if (c == ',' && level == 0)
563                     break;
564             }
565             QString ckey = inner.mid(10, pos - 10);
566             QString key = chopConst(ckey);
567             QString value = inner.mid(pos + 2, inner.size() - 3 - pos).trimmed();
568             QRegExp re5(QString("map<%1, ?%2, ?std::less<%3 ?>, ?%4\\s*>")
569                 .arg(key, value, key, alloc));
570             if (!re5.isMinimal())
571                 re5.setMinimal(true);
572             Q_ASSERT(re5.isValid());
573             if (re5.indexIn(type) != -1) {
574                 type.replace(re5.cap(0), QString("map<%1, %2>").arg(key, value));
575             } else {
576                 QRegExp re7(QString("map<const %1, ?%2, ?std::less<const %3>, ?%4\\s*>")
577                     .arg(key, value, key, alloc));
578                 if (!re7.isMinimal())
579                     re7.setMinimal(true);
580                 if (re7.indexIn(type) != -1)
581                     type.replace(re7.cap(0), QString("map<const %1, %2>").arg(key, value));
582             }
583         }
584     }
585     type.replace(QLatin1Char('@'), QLatin1Char('*'));
586     type.replace(QLatin1String(" >"), QString(QLatin1Char('>')));
587     cache.insert(typeIn, type); // For simplicity, also cache unmodified types
588     return type;
589 }
590
591 static QString formattedValue(const WatchData &data,
592     int individualFormat, int typeFormat)
593 {
594     if (isIntType(data.type)) {
595         int format = individualFormat == -1 ? typeFormat : individualFormat;
596         int value = data.value.toInt();
597         if (format == HexadecimalFormat)
598             return ("(hex) ") + QString::number(value, 16);
599         if (format == BinaryFormat)
600             return ("(bin) ") + QString::number(value, 2);
601         if (format == OctalFormat)
602             return ("(oct) ") + QString::number(value, 8);
603         return data.value;
604     }
605
606     return data.value;
607 }
608
609 bool WatchModel::canFetchMore(const QModelIndex &index) const
610 {
611     return index.isValid() && !watchItem(index)->fetchTriggered;
612 }
613
614 void WatchModel::fetchMore(const QModelIndex &index)
615 {
616     QTC_ASSERT(index.isValid(), return);
617     QTC_ASSERT(!watchItem(index)->fetchTriggered, return);
618     if (WatchItem *item = watchItem(index)) {
619         m_handler->m_expandedINames.insert(item->iname);
620         item->fetchTriggered = true;
621         if (item->children.isEmpty()) {
622             WatchData data = *item;
623             data.setChildrenNeeded();
624             m_handler->m_manager->updateWatchData(data);
625         }
626     }
627 }
628
629 QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const
630 {
631     if (!hasIndex(row, column, parent))
632         return QModelIndex();
633
634     const WatchItem *item = watchItem(parent);
635     QTC_ASSERT(item, return QModelIndex());
636     if (row >= item->children.size())
637         return QModelIndex();
638     return createIndex(row, column, (void*)(item->children.at(row)));
639 }
640
641 QModelIndex WatchModel::parent(const QModelIndex &idx) const
642 {
643     if (!idx.isValid())
644         return QModelIndex();
645
646     const WatchItem *item = watchItem(idx);
647     if (!item->parent || item->parent == m_root)
648         return QModelIndex();
649
650     const WatchItem *grandparent = item->parent->parent;
651     if (!grandparent)
652         return QModelIndex();
653
654     for (int i = 0; i < grandparent->children.size(); ++i)
655         if (grandparent->children.at(i) == item->parent)
656             return createIndex(i, 0, (void*) item->parent);
657
658     return QModelIndex();
659 }
660
661 int WatchModel::rowCount(const QModelIndex &idx) const
662 {
663     if (idx.column() > 0)
664         return 0;
665     return watchItem(idx)->children.size();
666 }
667
668 int WatchModel::columnCount(const QModelIndex &idx) const
669 {
670     Q_UNUSED(idx)
671     return 3;
672 }
673
674 bool WatchModel::hasChildren(const QModelIndex &parent) const
675 {
676     WatchItem *item = watchItem(parent);
677     return !item || item->hasChildren;
678 }
679
680 WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
681 {
682     return idx.isValid() 
683         ? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
684 }
685
686 QModelIndex WatchModel::watchIndex(const WatchItem *item) const
687 {
688     return watchIndexHelper(item, m_root, QModelIndex());
689 }
690
691 QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle, 
692     const WatchItem *parentItem, const QModelIndex &parentIndex) const
693 {
694     if (needle == parentItem)
695         return parentIndex;
696     for (int i = parentItem->children.size(); --i >= 0; ) {
697         const WatchItem *childItem = parentItem->children.at(i);
698         QModelIndex childIndex = index(i, 0, parentIndex);
699         QModelIndex idx = watchIndexHelper(needle, childItem, childIndex);
700         if (idx.isValid())
701             return idx;
702     }
703     return QModelIndex();
704 }
705
706 void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex) 
707 {
708     QModelIndex idx1 = index(0, column, parentIndex);
709     QModelIndex idx2 = index(rowCount(parentIndex) - 1, column, parentIndex);
710     if (idx1.isValid() && idx2.isValid())
711         emit dataChanged(idx1, idx2);
712     //qDebug() << "CHANGING:\n" << idx1 << "\n" << idx2 << "\n"
713     //    << data(parentIndex, INameRole).toString();
714     for (int i = rowCount(parentIndex); --i >= 0; )
715         emitDataChanged(column, index(i, 0, parentIndex));
716 }
717
718 QVariant WatchModel::data(const QModelIndex &idx, int role) const
719 {
720     const WatchItem &data = *watchItem(idx);
721
722     switch (role) {
723         case Qt::DisplayRole: {
724             switch (idx.column()) {
725                 case 0: return data.name;
726                 case 1: return formattedValue(data,
727                     m_handler->m_individualFormats.value(data.iname, -1),
728                     m_handler->m_typeFormats.value(data.type, -1));
729                 case 2:
730                     if (!data.displayedType.isEmpty())
731                         return data.displayedType;
732                     return niceType(data.type);
733                 default: break;
734             }
735             break;
736         }
737
738         case Qt::ToolTipRole:
739             return theDebuggerBoolSetting(UseToolTipsInLocalsView)
740                 ? data.toToolTip() : QVariant();
741
742         case Qt::ForegroundRole: {
743             static const QVariant red(QColor(200, 0, 0));
744             static const QVariant gray(QColor(140, 140, 140));
745             switch (idx.column()) {
746                 case 1: return !data.valueEnabled ? gray : data.changed ? red : QVariant();
747             }
748             break;
749         }
750
751         case ExpressionRole:
752             return data.exp;
753
754         case INameRole:
755             return data.iname;
756
757         case ExpandedRole:
758             return m_handler->m_expandedINames.contains(data.iname);
759             //FIXME return node < 4 || m_expandedINames.contains(data.iname);
760
761         case ActiveDataRole:
762             qDebug() << "ASK FOR" << data.iname;
763             return true;
764    
765         case TypeFormatListRole:
766             if (isIntType(data.type))
767                 return QStringList() << tr("decimal") << tr("hexadecimal")
768                     << tr("binary") << tr("octal");
769             break;
770
771         case TypeFormatRole:
772             return m_handler->m_typeFormats[data.type];
773
774         case IndividualFormatRole: {
775             int format = m_handler->m_individualFormats[data.iname];
776             if (format == -1)
777                 return m_handler->m_typeFormats[data.type];
778             return format;
779         }
780         
781         case AddressRole: {
782             if (!data.addr.isEmpty())
783                 return data.addr;
784             bool ok;
785             (void) data.value.toULongLong(&ok, 0);
786             if (ok)
787                 return data.value;
788             return QVariant();
789         }
790
791         default:
792             break; 
793     }
794     return QVariant();
795 }
796
797 bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
798 {
799     WatchItem &data = *watchItem(index);
800     if (role == ExpandedRole) {
801         if (value.toBool()) {
802             // Should already have been triggered by fetchMore()
803             //QTC_ASSERT(m_handler->m_expandedINames.contains(data.iname), /**/);
804             m_handler->m_expandedINames.insert(data.iname);
805         } else {
806             m_handler->m_expandedINames.remove(data.iname);
807         }
808     } else if (role == TypeFormatRole) {
809         m_handler->setFormat(data.type, value.toInt());
810     } else if (role == IndividualFormatRole) {
811         m_handler->m_individualFormats[data.iname] = value.toInt();
812     }
813     emit dataChanged(index, index);
814     return true;
815 }
816
817 Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
818 {
819     using namespace Qt;
820
821     if (!idx.isValid())
822         return ItemFlags();
823
824     // enabled, editable, selectable, checkable, and can be used both as the
825     // source of a drag and drop operation and as a drop target.
826
827     static const ItemFlags notEditable =
828           ItemIsSelectable
829         | ItemIsDragEnabled
830         | ItemIsDropEnabled
831         // | ItemIsUserCheckable
832         // | ItemIsTristate
833         | ItemIsEnabled;
834
835     static const ItemFlags editable = notEditable | ItemIsEditable;
836
837     const WatchData &data = *watchItem(idx);
838
839     if (data.isWatcher() && idx.column() == 0)
840         return editable; // watcher names are editable
841     if (data.isWatcher() && idx.column() == 2)
842         return editable; // watcher types are
843     if (idx.column() == 1 && data.valueEditable)
844         return editable; // locals and watcher values are sometimes editable
845     return notEditable;
846 }
847
848 QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
849 {
850     if (orientation == Qt::Vertical)
851         return QVariant();
852     if (role == Qt::DisplayRole) {
853         switch (section) {
854             case 0: return QString(tr("Name")  + QLatin1String("     "));
855             case 1: return QString(tr("Value") + QLatin1String("     "));
856             case 2: return QString(tr("Type")  + QLatin1String("     "));
857         }
858     }
859     return QVariant(); 
860 }
861
862 struct IName : public QString
863 {
864     IName(const QString &iname) : QString(iname) {}
865 };
866
867 bool iNameLess(const QString &iname1, const QString &iname2)
868 {
869     QString name1 = iname1.section('.', -1);
870     QString name2 = iname2.section('.', -1);
871     if (!name1.isEmpty() && !name2.isEmpty()) {
872         if (name1.at(0).isDigit() && name2.at(0).isDigit()) {
873             bool ok1 = false, ok2 = false;
874             int i1 = name1.toInt(&ok1), i2 = name2.toInt(&ok2);
875             if (ok1 && ok2)
876                 return i1 < i2;
877         }
878     }
879     return name1 < name2; 
880 }
881
882 bool operator<(const IName &iname1, const IName &iname2)
883 {
884     return iNameLess(iname1, iname2);
885 }
886
887 static bool iNameSorter(const WatchItem *item1, const WatchItem *item2)
888 {
889     return iNameLess(item1->iname, item2->iname);
890 }
891
892 static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
893 {
894     QList<WatchItem *>::const_iterator it =
895         qLowerBound(list.begin(), list.end(), item, iNameSorter);
896     return it - list.begin(); 
897 }
898
899 void WatchModel::insertData(const WatchData &data)
900 {
901     //qDebug() << "WMI:" << data.toString();
902     //static int bulk = 0;
903     //qDebug() << "SINGLE: " << ++bulk << data.toString();
904     if (data.iname.isEmpty()) {
905         int x;
906         x = 1;
907     }
908     QTC_ASSERT(!data.iname.isEmpty(), qDebug() << data.toString(); return);
909     WatchItem *parent = findItem(parentName(data.iname), m_root);
910     if (!parent) {
911         WatchData parent;
912         parent.iname = parentName(data.iname);
913         MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname);
914         if (!parent.iname.isEmpty())
915             insertData(parent);
916         return;
917     }
918     QModelIndex index = watchIndex(parent);
919     if (WatchItem *oldItem = findItem(data.iname, parent)) {
920         // overwrite old entry
921         //MODEL_DEBUG("OVERWRITE : " << data.iname << data.value);
922         bool changed = !data.value.isEmpty()
923             && data.value != oldItem->value
924             && data.value != strNotInScope;
925         oldItem->setData(data);
926         oldItem->changed = changed;
927         oldItem->generation = generationCounter;
928         QModelIndex idx = watchIndex(oldItem);
929         emit dataChanged(idx, idx.sibling(idx.row(), 2));
930     } else {
931         // add new entry
932         //MODEL_DEBUG("ADD : " << data.iname << data.value);
933         WatchItem *item = new WatchItem(data);
934         item->parent = parent;
935         item->generation = generationCounter;
936         item->changed = true;
937         int n = findInsertPosition(parent->children, item);
938         beginInsertRows(index, n, n);
939         parent->children.insert(n, item);
940         endInsertRows();
941     }
942 }
943
944 void WatchModel::insertBulkData(const QList<WatchData> &list)
945 {
946 #if 0
947     for (int i = 0; i != list.size(); ++i) 
948         insertData(list.at(i));
949     return;
950 #endif
951     // This method does not properly insert items in proper "iname sort
952     // order", leading to random removal of items in removeOutDated();
953
954     //qDebug() << "WMI:" << list.toString();
955     //static int bulk = 0;
956     //foreach (const WatchItem &data, list)
957     //    qDebug() << "BULK: " << ++bulk << data.toString();
958     QTC_ASSERT(!list.isEmpty(), return);
959     QString parentIName = parentName(list.at(0).iname);
960     WatchItem *parent = findItem(parentIName, m_root);
961     if (!parent) {
962         WatchData parent;
963         parent.iname = parentIName;
964         insertData(parent);
965         MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << list.at(0).iname);
966         return;
967     }
968     QModelIndex index = watchIndex(parent);
969
970     QMap<IName, WatchData> newList;
971     typedef QMap<IName, WatchData>::iterator Iterator;
972     foreach (const WatchItem &data, list)
973         newList[data.iname] = data;
974     if (newList.size() != list.size()) {
975         qDebug() << "LIST: ";
976         foreach (const WatchItem &data, list)
977             qDebug() << data.toString();
978         qDebug() << "NEW LIST: ";
979         foreach (const WatchItem &data, newList.values())
980             qDebug() << data.toString();
981         qDebug() << "P->CHILDREN: ";
982         foreach (const WatchItem *item, parent->children)
983             qDebug() << item->toString();
984         qDebug()
985             << "P->CHILDREN.SIZE: " << parent->children.size()
986             << "NEWLIST SIZE: " << newList.size()
987             << "LIST SIZE: " << list.size();
988     }
989     QTC_ASSERT(newList.size() == list.size(), return);
990
991     foreach (WatchItem *oldItem, parent->children) {
992         Iterator it = newList.find(oldItem->iname);
993         if (it == newList.end()) {
994             WatchData data = *oldItem;
995             data.generation = generationCounter;
996             newList[oldItem->iname] = data;
997         } else {
998             bool changed = !it->value.isEmpty()
999                 && it->value != oldItem->value
1000                 && it->value != strNotInScope;
1001             it->changed = changed;
1002             if (it->generation == -1)
1003                 it->generation = generationCounter;
1004         }
1005     }
1006
1007     for (Iterator it = newList.begin(); it != newList.end(); ++it) {
1008         qDebug() << "  NEW: " << it->iname;
1009     }
1010
1011     // overwrite existing items
1012     Iterator it = newList.begin();
1013     QModelIndex idx = watchIndex(parent);
1014     const int oldCount = parent->children.size();
1015     for (int i = 0; i < oldCount; ++i, ++it) {
1016         if (!parent->children[i]->isEqual(*it)) {
1017             qDebug() << "REPLACING" << parent->children.at(i)->iname
1018                 << " WITH " << it->iname << it->generation;
1019             parent->children[i]->setData(*it);
1020             if (parent->children[i]->generation == -1)
1021                 parent->children[i]->generation = generationCounter;
1022             //emit dataChanged(idx.sibling(i, 0), idx.sibling(i, 2));
1023         } else {
1024             //qDebug() << "SKIPPING REPLACEMENT" << parent->children.at(i)->iname;
1025         }
1026     }
1027     emit dataChanged(idx.sibling(0, 0), idx.sibling(oldCount - 1, 2));
1028
1029     // add new items
1030     if (oldCount < newList.size()) {
1031         beginInsertRows(index, oldCount, newList.size() - 1);
1032         //MODEL_DEBUG("INSERT : " << data.iname << data.value);
1033         for (int i = oldCount; i < newList.size(); ++i, ++it) {
1034             WatchItem *item = new WatchItem(*it);
1035             qDebug() << "ADDING" << it->iname;
1036             item->parent = parent;
1037             if (item->generation == -1)
1038                 item->generation = generationCounter;
1039             item->changed = true;
1040             parent->children.append(item);
1041         }
1042         endInsertRows();
1043     }
1044     //qDebug() << "ITEMS: " << parent->children.size();
1045     dump();
1046 }
1047
1048 WatchItem *WatchModel::findItem(const QString &iname, WatchItem *root) const
1049 {
1050     if (root->iname == iname)
1051         return root;
1052     for (int i = root->children.size(); --i >= 0; )
1053         if (WatchItem *item = findItem(iname, root->children.at(i)))
1054             return item;
1055     return 0;
1056 }
1057
1058 static void debugRecursion(QDebug &d, const WatchItem *item, int depth)
1059 {
1060     d << QString(2 * depth, QLatin1Char(' ')) << item->toString() << '\n';
1061     foreach(const WatchItem *i, item->children)
1062         debugRecursion(d, i, depth + 1);
1063 }
1064
1065 QDebug operator<<(QDebug d, const WatchModel &m)
1066 {
1067     QDebug nospace = d.nospace();
1068     if (m.m_root)
1069         debugRecursion(nospace, m.m_root, 0);
1070     return d;
1071 }
1072
1073
1074 ///////////////////////////////////////////////////////////////////////
1075 //
1076 // WatchHandler
1077 //
1078 ///////////////////////////////////////////////////////////////////////
1079
1080 WatchHandler::WatchHandler(DebuggerManager *manager)
1081 {
1082     m_manager = manager;
1083     m_expandPointers = true;
1084     m_inChange = false;
1085
1086     m_locals = new WatchModel(this, LocalsWatch);
1087     m_watchers = new WatchModel(this, WatchersWatch);
1088     m_tooltips = new WatchModel(this, TooltipsWatch);
1089
1090     connect(theDebuggerAction(WatchExpression),
1091         SIGNAL(triggered()), this, SLOT(watchExpression()));
1092     connect(theDebuggerAction(RemoveWatchExpression),
1093         SIGNAL(triggered()), this, SLOT(removeWatchExpression()));
1094 }
1095
1096 void WatchHandler::beginCycle()
1097 {
1098     ++generationCounter;
1099     m_locals->beginCycle();
1100     m_watchers->beginCycle();
1101     m_tooltips->beginCycle();
1102 }
1103
1104 void WatchHandler::endCycle()
1105 {
1106     m_locals->endCycle();
1107     m_watchers->endCycle();
1108     m_tooltips->endCycle();
1109 }
1110
1111 void WatchHandler::cleanup()
1112 {
1113     m_expandedINames.clear();
1114     m_displayedINames.clear();
1115     m_locals->reinitialize();
1116     m_tooltips->reinitialize();
1117 #if 0
1118     for (EditWindows::ConstIterator it = m_editWindows.begin();
1119             it != m_editWindows.end(); ++it) {
1120         if (!it.value().isNull())
1121             delete it.value();
1122     }
1123     m_editWindows.clear();
1124 #endif
1125 }
1126
1127 void WatchHandler::insertData(const WatchData &data)
1128 {
1129     MODEL_DEBUG("INSERTDATA: " << data.toString());
1130     if (!data.isValid()) {
1131         qWarning("%s:%d: Attempt to insert invalid watch item: %s", __FILE__, __LINE__, qPrintable(data.toString()));
1132         return;
1133     }
1134     if (data.isSomethingNeeded() && data.iname.contains('.')) {
1135         MODEL_DEBUG("SOMETHING NEEDED: " << data.toString());
1136         if (!m_manager->currentEngine()->isSynchroneous()) {
1137             m_manager->updateWatchData(data);
1138         } else {
1139             qDebug() << "ENDLESS LOOP: SOMETHING NEEDED: " << data.toString();
1140             WatchData data1 = data;
1141             data1.setAllUnneeded();
1142             data1.setValue(QLatin1String("<unavailable synchroneous data>"));
1143             data1.setHasChildren(false);
1144             WatchModel *model = modelForIName(data.iname);
1145             QTC_ASSERT(model, return);
1146             model->insertData(data1);
1147         }
1148     } else {
1149         WatchModel *model = modelForIName(data.iname);
1150         QTC_ASSERT(model, return);
1151         MODEL_DEBUG("NOTHING NEEDED: " << data.toString());
1152         model->insertData(data);
1153     }
1154 }
1155
1156 // Bulk-insertion
1157 void WatchHandler::insertBulkData(const QList<WatchData> &list)
1158 {
1159 #if 1
1160     foreach (const WatchItem &data, list)
1161         insertData(data);
1162     return;
1163 #endif
1164
1165     if (list.isEmpty())
1166         return;
1167     QMap<QString, QList<WatchData> > hash;
1168
1169     foreach (const WatchData &data, list) {
1170         // we insert everything, including incomplete stuff
1171         // to reduce the number of row add operations in the model.
1172         if (data.isValid()) {
1173             hash[parentName(data.iname)].append(data);
1174         } else {
1175             qWarning("%s:%d: Attempt to bulk-insert invalid watch item: %s", __FILE__, __LINE__, qPrintable(data.toString()));
1176         }
1177     }
1178     foreach (const QString &parentIName, hash.keys()) {
1179         WatchModel *model = modelForIName(parentIName);
1180         QTC_ASSERT(model, return);
1181         model->insertBulkData(hash[parentIName]);
1182     }
1183
1184     foreach (const WatchData &data, list) {
1185         if (data.isSomethingNeeded())
1186             m_manager->updateWatchData(data);
1187     }
1188 }
1189
1190 void WatchHandler::removeData(const QString &iname)
1191 {
1192     WatchModel *model = modelForIName(iname);
1193     if (!model)
1194         return;
1195     WatchItem *item = model->findItem(iname, model->m_root);
1196     if (item)
1197         model->destroyItem(item);
1198 }
1199
1200 void WatchHandler::watchExpression()
1201 {
1202     if (QAction *action = qobject_cast<QAction *>(sender()))
1203         watchExpression(action->data().toString());
1204 }
1205
1206 QString WatchHandler::watcherName(const QString &exp)
1207 {
1208     return QLatin1String("watch.") + QString::number(m_watcherNames[exp]);
1209 }
1210
1211 void WatchHandler::watchExpression(const QString &exp)
1212 {
1213     // FIXME: 'exp' can contain illegal characters
1214     m_watcherNames[exp] = watcherCounter++;
1215     WatchData data;
1216     data.exp = exp;
1217     data.name = exp;
1218     if (exp.isEmpty() || exp == watcherEditPlaceHolder())
1219         data.setAllUnneeded();
1220     data.iname = watcherName(exp);
1221     insertData(data);
1222     saveWatchers();
1223 }
1224
1225 void WatchHandler::setDisplayedIName(const QString &iname, bool on)
1226 {
1227     Q_UNUSED(iname)
1228     Q_UNUSED(on)
1229 /*
1230     WatchData *d = findData(iname);
1231     if (!on || !d) {
1232         delete m_editWindows.take(iname);
1233         m_displayedINames.remove(iname);
1234         return;
1235     }
1236     if (d->exp.isEmpty()) {
1237         //emit statusMessageRequested(tr("Sorry. Cannot visualize objects without known address."), 5000);
1238         return;
1239     }
1240     d->setValueNeeded();
1241     m_displayedINames.insert(iname);
1242     insertData(*d);
1243 */
1244 }
1245
1246 void WatchHandler::showEditValue(const WatchData &data)
1247 {
1248     // editvalue is always base64 encoded
1249     QByteArray ba = QByteArray::fromBase64(data.editvalue);
1250     //QByteArray ba = data.editvalue;
1251     QWidget *w = m_editWindows.value(data.iname);
1252     qDebug() << "SHOW_EDIT_VALUE " << data.toString() << data.type
1253             << data.iname << w;
1254     if (data.type == QLatin1String("QImage")) {
1255         if (!w) {
1256             w = new QLabel;
1257             m_editWindows[data.iname] = w;
1258         }
1259         QDataStream ds(&ba, QIODevice::ReadOnly);
1260         QVariant v;
1261         ds >> v;
1262         QString type = QString::fromAscii(v.typeName());
1263         QImage im = v.value<QImage>();
1264         if (QLabel *l = qobject_cast<QLabel *>(w))
1265             l->setPixmap(QPixmap::fromImage(im));
1266     } else if (data.type == QLatin1String("QPixmap")) {
1267         if (!w) {
1268             w = new QLabel;
1269             m_editWindows[data.iname] = w;
1270         }
1271         QDataStream ds(&ba, QIODevice::ReadOnly);
1272         QVariant v;
1273         ds >> v;
1274         QString type = QString::fromAscii(v.typeName());
1275         QPixmap im = v.value<QPixmap>();
1276         if (QLabel *l = qobject_cast<QLabel *>(w))
1277             l->setPixmap(im);
1278     } else if (data.type == QLatin1String("QString")) {
1279         if (!w) {
1280             w = new QTextEdit;
1281             m_editWindows[data.iname] = w;
1282         }
1283 #if 0
1284         QDataStream ds(&ba, QIODevice::ReadOnly);
1285         QVariant v;
1286         ds >> v;
1287         QString type = QString::fromAscii(v.typeName());
1288         QString str = v.value<QString>();
1289 #else
1290         MODEL_DEBUG("DATA: " << ba);
1291         QString str = QString::fromUtf16((ushort *)ba.constData(), ba.size()/2);
1292 #endif
1293         if (QTextEdit *t = qobject_cast<QTextEdit *>(w))
1294             t->setText(str);
1295     }
1296     if (w)
1297         w->show();
1298 }
1299
1300 void WatchHandler::removeWatchExpression()
1301 {
1302     if (QAction *action = qobject_cast<QAction *>(sender()))
1303         removeWatchExpression(action->data().toString());
1304 }
1305
1306 void WatchHandler::removeWatchExpression(const QString &exp)
1307 {
1308     MODEL_DEBUG("REMOVE WATCH: " << exp);
1309     m_watcherNames.remove(exp);
1310     foreach (WatchItem *item, m_watchers->rootItem()->children) {
1311         if (item->exp == exp) {
1312             m_watchers->destroyItem(item);
1313             saveWatchers();
1314             break;
1315         }
1316     }
1317 }
1318
1319 void WatchHandler::updateWatchers()
1320 {
1321     //qDebug() << "UPDATE WATCHERS";
1322     // copy over all watchers and mark all watchers as incomplete
1323     foreach (const QString &exp, m_watcherNames.keys()) {
1324         WatchData data;
1325         data.iname = watcherName(exp);
1326         data.setAllNeeded();
1327         data.name = exp;
1328         data.exp = exp;
1329         insertData(data);
1330     }
1331 }
1332
1333 void WatchHandler::loadWatchers()
1334 {
1335     QVariant value = m_manager->sessionValue("Watchers");
1336     foreach (const QString &exp, value.toStringList())
1337         m_watcherNames[exp] = watcherCounter++;
1338
1339     //qDebug() << "LOAD WATCHERS: " << m_watchers;
1340     //reinitializeWatchersHelper();
1341 }
1342
1343 void WatchHandler::saveWatchers()
1344 {
1345     //qDebug() << "SAVE WATCHERS: " << m_watchers;
1346     // Filter out valid watchers.
1347     QStringList watcherNames;
1348     QHashIterator<QString, int> it(m_watcherNames);
1349     while (it.hasNext()) {
1350         it.next();
1351         const QString &watcherName = it.key();
1352         if (!watcherName.isEmpty() && watcherName != watcherEditPlaceHolder())
1353             watcherNames.push_back(watcherName);
1354     }
1355     m_manager->setSessionValue("Watchers", QVariant(watcherNames));
1356 }
1357
1358 void WatchHandler::loadTypeFormats()
1359 {
1360     QVariant value = m_manager->sessionValue("DefaultFormats");
1361     QMap<QString, QVariant> typeFormats = value.toMap();
1362     QMapIterator<QString, QVariant> it(typeFormats);
1363     while (it.hasNext()) {
1364         it.next();
1365         if (!it.key().isEmpty())
1366             m_typeFormats.insert(it.key(), it.value().toInt());
1367     }
1368 }
1369
1370 void WatchHandler::saveTypeFormats()
1371 {
1372     QMap<QString, QVariant> typeFormats;
1373     QHashIterator<QString, int> it(m_typeFormats);
1374     while (it.hasNext()) {
1375         it.next();
1376         QString key = it.key().trimmed();
1377         if (!key.isEmpty())
1378             typeFormats.insert(key, it.value());
1379     }
1380     m_manager->setSessionValue("DefaultFormats", QVariant(typeFormats));
1381 }
1382
1383 void WatchHandler::saveSessionData()
1384 {
1385     saveWatchers();
1386     saveTypeFormats();
1387 }
1388
1389 void WatchHandler::loadSessionData()
1390 {
1391     loadWatchers();
1392     loadTypeFormats();
1393     foreach (const QString &exp, m_watcherNames.keys()) {
1394         WatchData data;
1395         data.iname = watcherName(exp);
1396         data.setAllUnneeded();
1397         data.name = exp;
1398         data.exp = exp;
1399         insertData(data);
1400     }
1401 }
1402
1403 WatchModel *WatchHandler::model(WatchType type) const
1404 {
1405     switch (type) {
1406         case LocalsWatch: return m_locals;
1407         case WatchersWatch: return m_watchers;
1408         case TooltipsWatch: return m_tooltips;
1409     }
1410     QTC_ASSERT(false, /**/);
1411     return 0;
1412 }
1413     
1414 WatchModel *WatchHandler::modelForIName(const QString &iname) const
1415 {
1416     if (iname.startsWith(QLatin1String("local")))
1417         return m_locals;
1418     if (iname.startsWith(QLatin1String("watch")))
1419         return m_watchers;
1420     if (iname.startsWith(QLatin1String("tooltip")))
1421         return m_tooltips;
1422     QTC_ASSERT(false, qDebug() << "INAME: " << iname);
1423     return 0;
1424 }
1425
1426 WatchData *WatchHandler::findItem(const QString &iname) const
1427 {
1428     const WatchModel *model = modelForIName(iname);
1429     QTC_ASSERT(model, return 0);
1430     return model->findItem(iname, model->m_root);
1431 }
1432
1433 QString WatchHandler::watcherEditPlaceHolder()
1434 {
1435     static const QString rc = tr("<Edit>");
1436     return rc;
1437 }
1438
1439 void WatchHandler::setFormat(const QString &type, int format)
1440 {
1441     m_typeFormats[type] = format;
1442     saveTypeFormats();
1443     m_locals->emitDataChanged(1);
1444     m_watchers->emitDataChanged(1);
1445     m_tooltips->emitDataChanged(1);
1446 }
1447
1448 } // namespace Internal
1449 } // namespace Debugger