1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
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.
16 ** GNU Lesser General Public License Usage
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.
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
30 #include "watchhandler.h"
31 #include "watchutils.h"
32 #include "debuggeractions.h"
33 #include "debuggermanager.h"
34 #include "idebuggerengine.h"
37 #include "modeltest.h"
40 #include <utils/qtcassert.h>
42 #include <QtCore/QDebug>
43 #include <QtCore/QEvent>
45 #include <QtCore/QtAlgorithms>
46 #include <QtCore/QTextStream>
47 #include <QtCore/QTimer>
49 #include <QtGui/QAction>
50 #include <QtGui/QApplication>
51 #include <QtGui/QLabel>
52 #include <QtGui/QToolTip>
53 #include <QtGui/QTextEdit>
58 // creates debug output for accesses to the model
59 //#define DEBUG_MODEL 1
62 # define MODEL_DEBUG(s) qDebug() << s
64 # define MODEL_DEBUG(s)
66 #define MODEL_DEBUGX(s) qDebug() << s
71 static const QString strNotInScope =
72 QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
74 static int watcherCounter = 0;
75 static int generationCounter = 0;
77 ////////////////////////////////////////////////////////////////////
81 ////////////////////////////////////////////////////////////////////
83 class WatchItem : public WatchData
86 WatchItem() { parent = 0; fetchTriggered = false; }
88 WatchItem(const WatchData &data) : WatchData(data)
89 { parent = 0; fetchTriggered = false; }
91 void setData(const WatchData &data)
92 { static_cast<WatchData &>(*this) = data; }
95 bool fetchTriggered; // children fetch has been triggered
96 QList<WatchItem *> children; // fetched children
99 ////////////////////////////////////////////////////////////////////
103 ////////////////////////////////////////////////////////////////////
105 WatchData::WatchData() :
117 bool WatchData::isEqual(const WatchData &other) const
119 return iname == other.iname
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;
137 void WatchData::setError(const QString &msg)
141 setHasChildren(false);
142 valueEnabled = false;
143 valueEditable = false;
147 void WatchData::setValue(const QString &value0)
150 if (value == "{...}") {
152 hasChildren = true; // at least one...
155 // avoid duplicated information
156 if (value.startsWith("(") && value.contains(") 0x"))
157 value = value.mid(value.lastIndexOf(") 0x") + 2);
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);
167 // "numchild" is sometimes lying
168 //MODEL_DEBUG("\n\n\nPOINTER: " << type << value);
169 if (isPointerType(type))
170 setHasChildren(value != "0x0" && value != "<null>");
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);
180 void WatchData::setValueToolTip(const QString &tooltip)
182 valuetooltip = tooltip;
185 void WatchData::setType(const QString &str, bool guessChildrenFromType)
187 type = str.trimmed();
190 if (type.endsWith(QLatin1String("const")))
192 else if (type.endsWith(QLatin1Char(' ')))
194 else if (type.endsWith(QLatin1Char('&')))
196 else if (type.startsWith(QLatin1String("const ")))
198 else if (type.startsWith(QLatin1String("volatile ")))
200 else if (type.startsWith(QLatin1String("class ")))
202 else if (type.startsWith(QLatin1String("struct ")))
204 else if (type.startsWith(QLatin1Char(' ')))
210 if (guessChildrenFromType) {
211 switch (guessChildren(type)) {
213 setHasChildren(true);
216 setHasChildren(false);
218 case HasPossiblyChildren:
219 setHasChildren(true); // FIXME: bold assumption
225 void WatchData::setAddress(const QString &str)
230 QString WatchData::toString() const
232 const char *doubleQuoteComma = "\",";
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;
243 str << "addr=\"" << addr << doubleQuoteComma;
245 str << "exp=\"" << exp << doubleQuoteComma;
247 if (!variable.isEmpty())
248 str << "variable=\"" << variable << doubleQuoteComma;
251 str << "value=<needed>,";
252 if (isValueKnown() && !value.isEmpty())
253 str << "value=\"" << value << doubleQuoteComma;
255 if (!editvalue.isEmpty())
256 str << "editvalue=\"" << editvalue << doubleQuoteComma;
259 str << "type=<needed>,";
260 if (isTypeKnown() && !type.isEmpty())
261 str << "type=\"" << type << doubleQuoteComma;
263 if (isHasChildrenNeeded())
264 str << "hasChildren=<needed>,";
265 if (isHasChildrenKnown())
266 str << "hasChildren=\"" << (hasChildren ? "true" : "false") << doubleQuoteComma;
268 if (isChildrenNeeded())
269 str << "children=<needed>,";
271 str << "source=" << source;
273 if (res.endsWith(QLatin1Char(',')))
274 res.truncate(res.size() - 1);
275 return res + QLatin1Char('}');
278 // Format a tooltip fow with aligned colon
279 static void formatToolTipRow(QTextStream &str, const QString &category, const QString &value)
281 str << "<tr><td>" << category << "</td><td> : </td><td>"
282 << Qt::escape(value) << "</td></tr>";
285 static inline QString typeToolTip(const WatchData &wd)
287 if (wd.displayedType.isEmpty())
289 QString rc = wd.displayedType;
290 rc += QLatin1String(" (");
292 rc += QLatin1Char(')');
296 QString WatchData::toToolTip() const
298 if (!valuetooltip.isEmpty())
299 return QString::number(valuetooltip.size());
301 QTextStream str(&res);
302 str << "<html><body><table>";
303 formatToolTipRow(str, WatchHandler::tr("Expression"), exp);
304 formatToolTipRow(str, WatchHandler::tr("Type"), typeToolTip(*this));
306 if (value.size() > 1000) {
308 val += WatchHandler::tr(" ... <cut off>");
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>";
320 QString WatchData::msgNotInScope()
322 static const QString rc = QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
326 QString WatchData::shadowedName(const QString &name, int seen)
330 return QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>").arg(name).arg(seen);
333 ///////////////////////////////////////////////////////////////////////
337 ///////////////////////////////////////////////////////////////////////
339 WatchModel::WatchModel(WatchHandler *handler, WatchType type)
340 : QAbstractItemModel(handler), m_handler(handler), m_type(type)
342 m_root = new WatchItem;
343 m_root->hasChildren = 1;
345 m_root->name = WatchHandler::tr("Root");
347 m_root->fetchTriggered = true;
351 m_root->iname = QLatin1String("local");
352 m_root->name = WatchHandler::tr("Locals");
355 m_root->iname = QLatin1String("watch");
356 m_root->name = WatchHandler::tr("Watchers");
359 m_root->iname = QLatin1String("tooltip");
360 m_root->name = WatchHandler::tr("Tooltip");
365 WatchModel::~WatchModel()
370 WatchItem *WatchModel::rootItem() const
375 void WatchModel::reinitialize()
377 int n = m_root->children.size();
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();
388 void WatchModel::beginCycle()
390 emit enableUpdates(false);
393 void WatchModel::endCycle()
396 emit enableUpdates(true);
399 void WatchModel::dump()
402 foreach (WatchItem *child, m_root->children)
406 void WatchModel::dumpHelper(WatchItem *item)
408 qDebug() << "ITEM: " << item->iname
409 << (item->parent ? item->parent->iname : "<none>")
411 foreach (WatchItem *child, item->children)
415 void WatchModel::removeOutdated()
417 foreach (WatchItem *child, m_root->children)
418 removeOutdatedHelper(child);
421 //(void) new ModelTest(this, this);
426 void WatchModel::removeOutdatedHelper(WatchItem *item)
428 if (item->generation < generationCounter) {
431 foreach (WatchItem *child, item->children)
432 removeOutdatedHelper(child);
433 item->fetchTriggered = false;
437 void WatchModel::destroyItem(WatchItem *item)
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);
449 static QString parentName(const QString &iname)
451 int pos = iname.lastIndexOf(QLatin1Char('.'));
454 return iname.left(pos);
458 static QString chopConst(QString type)
461 if (type.startsWith("const"))
463 else if (type.startsWith(' '))
465 else if (type.endsWith("const"))
467 else if (type.endsWith(' '))
475 static inline QRegExp stdStringRegExp(const QString &charType)
477 QString rc = QLatin1String("basic_string<");
479 rc += QLatin1String(",[ ]?std::char_traits<");
481 rc += QLatin1String(">,[ ]?std::allocator<");
483 rc += QLatin1String("> >");
484 const QRegExp re(rc);
485 Q_ASSERT(re.isValid());
489 QString niceType(const QString typeIn)
491 static QMap<QString, QString> cache;
492 const QMap<QString, QString>::const_iterator it = cache.constFind(typeIn);
493 if (it != cache.constEnd())
496 QString type = typeIn;
497 type.replace(QLatin1Char('*'), QLatin1Char('@'));
499 for (int i = 0; i < 10; ++i) {
500 int start = type.indexOf("std::allocator<");
503 // search for matching '>'
506 for (pos = start + 12; pos < type.size(); ++pos) {
507 int c = type.at(pos).unicode();
510 } else if (c == '>') {
516 QString alloc = type.mid(start, pos + 1 - start).trimmed();
517 QString inner = alloc.mid(15, alloc.size() - 16).trimmed();
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"));
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));
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));
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));
552 if (inner.startsWith("std::pair<")) {
553 // search for outermost ','
556 for (pos = 10; pos < inner.size(); ++pos) {
557 int c = inner.at(pos).unicode();
562 else if (c == ',' && level == 0)
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));
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));
585 type.replace(QLatin1Char('@'), QLatin1Char('*'));
586 type.replace(QLatin1String(" >"), QString(QLatin1Char('>')));
587 cache.insert(typeIn, type); // For simplicity, also cache unmodified types
591 static QString formattedValue(const WatchData &data,
592 int individualFormat, int typeFormat)
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);
609 bool WatchModel::canFetchMore(const QModelIndex &index) const
611 return index.isValid() && !watchItem(index)->fetchTriggered;
614 void WatchModel::fetchMore(const QModelIndex &index)
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);
629 QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const
631 if (!hasIndex(row, column, parent))
632 return QModelIndex();
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)));
641 QModelIndex WatchModel::parent(const QModelIndex &idx) const
644 return QModelIndex();
646 const WatchItem *item = watchItem(idx);
647 if (!item->parent || item->parent == m_root)
648 return QModelIndex();
650 const WatchItem *grandparent = item->parent->parent;
652 return QModelIndex();
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);
658 return QModelIndex();
661 int WatchModel::rowCount(const QModelIndex &idx) const
663 if (idx.column() > 0)
665 return watchItem(idx)->children.size();
668 int WatchModel::columnCount(const QModelIndex &idx) const
674 bool WatchModel::hasChildren(const QModelIndex &parent) const
676 WatchItem *item = watchItem(parent);
677 return !item || item->hasChildren;
680 WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
683 ? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
686 QModelIndex WatchModel::watchIndex(const WatchItem *item) const
688 return watchIndexHelper(item, m_root, QModelIndex());
691 QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle,
692 const WatchItem *parentItem, const QModelIndex &parentIndex) const
694 if (needle == parentItem)
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);
703 return QModelIndex();
706 void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
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));
718 QVariant WatchModel::data(const QModelIndex &idx, int role) const
720 const WatchItem &data = *watchItem(idx);
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));
730 if (!data.displayedType.isEmpty())
731 return data.displayedType;
732 return niceType(data.type);
738 case Qt::ToolTipRole:
739 return theDebuggerBoolSetting(UseToolTipsInLocalsView)
740 ? data.toToolTip() : QVariant();
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();
758 return m_handler->m_expandedINames.contains(data.iname);
759 //FIXME return node < 4 || m_expandedINames.contains(data.iname);
762 qDebug() << "ASK FOR" << data.iname;
765 case TypeFormatListRole:
766 if (isIntType(data.type))
767 return QStringList() << tr("decimal") << tr("hexadecimal")
768 << tr("binary") << tr("octal");
772 return m_handler->m_typeFormats[data.type];
774 case IndividualFormatRole: {
775 int format = m_handler->m_individualFormats[data.iname];
777 return m_handler->m_typeFormats[data.type];
782 if (!data.addr.isEmpty())
785 (void) data.value.toULongLong(&ok, 0);
797 bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
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);
806 m_handler->m_expandedINames.remove(data.iname);
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();
813 emit dataChanged(index, index);
817 Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
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.
827 static const ItemFlags notEditable =
831 // | ItemIsUserCheckable
835 static const ItemFlags editable = notEditable | ItemIsEditable;
837 const WatchData &data = *watchItem(idx);
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
848 QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
850 if (orientation == Qt::Vertical)
852 if (role == Qt::DisplayRole) {
854 case 0: return QString(tr("Name") + QLatin1String(" "));
855 case 1: return QString(tr("Value") + QLatin1String(" "));
856 case 2: return QString(tr("Type") + QLatin1String(" "));
862 struct IName : public QString
864 IName(const QString &iname) : QString(iname) {}
867 bool iNameLess(const QString &iname1, const QString &iname2)
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);
879 return name1 < name2;
882 bool operator<(const IName &iname1, const IName &iname2)
884 return iNameLess(iname1, iname2);
887 static bool iNameSorter(const WatchItem *item1, const WatchItem *item2)
889 return iNameLess(item1->iname, item2->iname);
892 static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
894 QList<WatchItem *>::const_iterator it =
895 qLowerBound(list.begin(), list.end(), item, iNameSorter);
896 return it - list.begin();
899 void WatchModel::insertData(const WatchData &data)
901 //qDebug() << "WMI:" << data.toString();
902 //static int bulk = 0;
903 //qDebug() << "SINGLE: " << ++bulk << data.toString();
904 if (data.iname.isEmpty()) {
908 QTC_ASSERT(!data.iname.isEmpty(), qDebug() << data.toString(); return);
909 WatchItem *parent = findItem(parentName(data.iname), m_root);
912 parent.iname = parentName(data.iname);
913 MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname);
914 if (!parent.iname.isEmpty())
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));
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);
944 void WatchModel::insertBulkData(const QList<WatchData> &list)
947 for (int i = 0; i != list.size(); ++i)
948 insertData(list.at(i));
951 // This method does not properly insert items in proper "iname sort
952 // order", leading to random removal of items in removeOutDated();
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);
963 parent.iname = parentIName;
965 MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << list.at(0).iname);
968 QModelIndex index = watchIndex(parent);
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();
985 << "P->CHILDREN.SIZE: " << parent->children.size()
986 << "NEWLIST SIZE: " << newList.size()
987 << "LIST SIZE: " << list.size();
989 QTC_ASSERT(newList.size() == list.size(), return);
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;
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;
1007 for (Iterator it = newList.begin(); it != newList.end(); ++it) {
1008 qDebug() << " NEW: " << it->iname;
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));
1024 //qDebug() << "SKIPPING REPLACEMENT" << parent->children.at(i)->iname;
1027 emit dataChanged(idx.sibling(0, 0), idx.sibling(oldCount - 1, 2));
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);
1044 //qDebug() << "ITEMS: " << parent->children.size();
1048 WatchItem *WatchModel::findItem(const QString &iname, WatchItem *root) const
1050 if (root->iname == iname)
1052 for (int i = root->children.size(); --i >= 0; )
1053 if (WatchItem *item = findItem(iname, root->children.at(i)))
1058 static void debugRecursion(QDebug &d, const WatchItem *item, int depth)
1060 d << QString(2 * depth, QLatin1Char(' ')) << item->toString() << '\n';
1061 foreach(const WatchItem *i, item->children)
1062 debugRecursion(d, i, depth + 1);
1065 QDebug operator<<(QDebug d, const WatchModel &m)
1067 QDebug nospace = d.nospace();
1069 debugRecursion(nospace, m.m_root, 0);
1074 ///////////////////////////////////////////////////////////////////////
1078 ///////////////////////////////////////////////////////////////////////
1080 WatchHandler::WatchHandler(DebuggerManager *manager)
1082 m_manager = manager;
1083 m_expandPointers = true;
1086 m_locals = new WatchModel(this, LocalsWatch);
1087 m_watchers = new WatchModel(this, WatchersWatch);
1088 m_tooltips = new WatchModel(this, TooltipsWatch);
1090 connect(theDebuggerAction(WatchExpression),
1091 SIGNAL(triggered()), this, SLOT(watchExpression()));
1092 connect(theDebuggerAction(RemoveWatchExpression),
1093 SIGNAL(triggered()), this, SLOT(removeWatchExpression()));
1096 void WatchHandler::beginCycle()
1098 ++generationCounter;
1099 m_locals->beginCycle();
1100 m_watchers->beginCycle();
1101 m_tooltips->beginCycle();
1104 void WatchHandler::endCycle()
1106 m_locals->endCycle();
1107 m_watchers->endCycle();
1108 m_tooltips->endCycle();
1111 void WatchHandler::cleanup()
1113 m_expandedINames.clear();
1114 m_displayedINames.clear();
1115 m_locals->reinitialize();
1116 m_tooltips->reinitialize();
1118 for (EditWindows::ConstIterator it = m_editWindows.begin();
1119 it != m_editWindows.end(); ++it) {
1120 if (!it.value().isNull())
1123 m_editWindows.clear();
1127 void WatchHandler::insertData(const WatchData &data)
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()));
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);
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);
1149 WatchModel *model = modelForIName(data.iname);
1150 QTC_ASSERT(model, return);
1151 MODEL_DEBUG("NOTHING NEEDED: " << data.toString());
1152 model->insertData(data);
1157 void WatchHandler::insertBulkData(const QList<WatchData> &list)
1160 foreach (const WatchItem &data, list)
1167 QMap<QString, QList<WatchData> > hash;
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);
1175 qWarning("%s:%d: Attempt to bulk-insert invalid watch item: %s", __FILE__, __LINE__, qPrintable(data.toString()));
1178 foreach (const QString &parentIName, hash.keys()) {
1179 WatchModel *model = modelForIName(parentIName);
1180 QTC_ASSERT(model, return);
1181 model->insertBulkData(hash[parentIName]);
1184 foreach (const WatchData &data, list) {
1185 if (data.isSomethingNeeded())
1186 m_manager->updateWatchData(data);
1190 void WatchHandler::removeData(const QString &iname)
1192 WatchModel *model = modelForIName(iname);
1195 WatchItem *item = model->findItem(iname, model->m_root);
1197 model->destroyItem(item);
1200 void WatchHandler::watchExpression()
1202 if (QAction *action = qobject_cast<QAction *>(sender()))
1203 watchExpression(action->data().toString());
1206 QString WatchHandler::watcherName(const QString &exp)
1208 return QLatin1String("watch.") + QString::number(m_watcherNames[exp]);
1211 void WatchHandler::watchExpression(const QString &exp)
1213 // FIXME: 'exp' can contain illegal characters
1214 m_watcherNames[exp] = watcherCounter++;
1218 if (exp.isEmpty() || exp == watcherEditPlaceHolder())
1219 data.setAllUnneeded();
1220 data.iname = watcherName(exp);
1225 void WatchHandler::setDisplayedIName(const QString &iname, bool on)
1230 WatchData *d = findData(iname);
1232 delete m_editWindows.take(iname);
1233 m_displayedINames.remove(iname);
1236 if (d->exp.isEmpty()) {
1237 //emit statusMessageRequested(tr("Sorry. Cannot visualize objects without known address."), 5000);
1240 d->setValueNeeded();
1241 m_displayedINames.insert(iname);
1246 void WatchHandler::showEditValue(const WatchData &data)
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
1254 if (data.type == QLatin1String("QImage")) {
1257 m_editWindows[data.iname] = w;
1259 QDataStream ds(&ba, QIODevice::ReadOnly);
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")) {
1269 m_editWindows[data.iname] = w;
1271 QDataStream ds(&ba, QIODevice::ReadOnly);
1274 QString type = QString::fromAscii(v.typeName());
1275 QPixmap im = v.value<QPixmap>();
1276 if (QLabel *l = qobject_cast<QLabel *>(w))
1278 } else if (data.type == QLatin1String("QString")) {
1281 m_editWindows[data.iname] = w;
1284 QDataStream ds(&ba, QIODevice::ReadOnly);
1287 QString type = QString::fromAscii(v.typeName());
1288 QString str = v.value<QString>();
1290 MODEL_DEBUG("DATA: " << ba);
1291 QString str = QString::fromUtf16((ushort *)ba.constData(), ba.size()/2);
1293 if (QTextEdit *t = qobject_cast<QTextEdit *>(w))
1300 void WatchHandler::removeWatchExpression()
1302 if (QAction *action = qobject_cast<QAction *>(sender()))
1303 removeWatchExpression(action->data().toString());
1306 void WatchHandler::removeWatchExpression(const QString &exp)
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);
1319 void WatchHandler::updateWatchers()
1321 //qDebug() << "UPDATE WATCHERS";
1322 // copy over all watchers and mark all watchers as incomplete
1323 foreach (const QString &exp, m_watcherNames.keys()) {
1325 data.iname = watcherName(exp);
1326 data.setAllNeeded();
1333 void WatchHandler::loadWatchers()
1335 QVariant value = m_manager->sessionValue("Watchers");
1336 foreach (const QString &exp, value.toStringList())
1337 m_watcherNames[exp] = watcherCounter++;
1339 //qDebug() << "LOAD WATCHERS: " << m_watchers;
1340 //reinitializeWatchersHelper();
1343 void WatchHandler::saveWatchers()
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()) {
1351 const QString &watcherName = it.key();
1352 if (!watcherName.isEmpty() && watcherName != watcherEditPlaceHolder())
1353 watcherNames.push_back(watcherName);
1355 m_manager->setSessionValue("Watchers", QVariant(watcherNames));
1358 void WatchHandler::loadTypeFormats()
1360 QVariant value = m_manager->sessionValue("DefaultFormats");
1361 QMap<QString, QVariant> typeFormats = value.toMap();
1362 QMapIterator<QString, QVariant> it(typeFormats);
1363 while (it.hasNext()) {
1365 if (!it.key().isEmpty())
1366 m_typeFormats.insert(it.key(), it.value().toInt());
1370 void WatchHandler::saveTypeFormats()
1372 QMap<QString, QVariant> typeFormats;
1373 QHashIterator<QString, int> it(m_typeFormats);
1374 while (it.hasNext()) {
1376 QString key = it.key().trimmed();
1378 typeFormats.insert(key, it.value());
1380 m_manager->setSessionValue("DefaultFormats", QVariant(typeFormats));
1383 void WatchHandler::saveSessionData()
1389 void WatchHandler::loadSessionData()
1393 foreach (const QString &exp, m_watcherNames.keys()) {
1395 data.iname = watcherName(exp);
1396 data.setAllUnneeded();
1403 WatchModel *WatchHandler::model(WatchType type) const
1406 case LocalsWatch: return m_locals;
1407 case WatchersWatch: return m_watchers;
1408 case TooltipsWatch: return m_tooltips;
1410 QTC_ASSERT(false, /**/);
1414 WatchModel *WatchHandler::modelForIName(const QString &iname) const
1416 if (iname.startsWith(QLatin1String("local")))
1418 if (iname.startsWith(QLatin1String("watch")))
1420 if (iname.startsWith(QLatin1String("tooltip")))
1422 QTC_ASSERT(false, qDebug() << "INAME: " << iname);
1426 WatchData *WatchHandler::findItem(const QString &iname) const
1428 const WatchModel *model = modelForIName(iname);
1429 QTC_ASSERT(model, return 0);
1430 return model->findItem(iname, model->m_root);
1433 QString WatchHandler::watcherEditPlaceHolder()
1435 static const QString rc = tr("<Edit>");
1439 void WatchHandler::setFormat(const QString &type, int format)
1441 m_typeFormats[type] = format;
1443 m_locals->emitDataChanged(1);
1444 m_watchers->emitDataChanged(1);
1445 m_tooltips->emitDataChanged(1);
1448 } // namespace Internal
1449 } // namespace Debugger