2009-02-25 09:15:00 +01:00
|
|
|
/**************************************************************************
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-06-17 00:01:27 +10:00
|
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Commercial Usage
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
|
|
|
|
** accordance with the Qt Commercial License Agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
|
** a written agreement between you and Nokia.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** GNU Lesser General Public License Usage
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
** If you are unsure which license is appropriate for your use, please
|
2009-08-14 09:30:56 +02:00
|
|
|
** contact the sales department at http://qt.nokia.com/contact.
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2009-02-25 09:15:00 +01:00
|
|
|
**************************************************************************/
|
2008-12-02 15:08:31 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "watchhandler.h"
|
2009-03-25 17:33:49 +01:00
|
|
|
#include "watchutils.h"
|
2009-03-19 09:32:09 +01:00
|
|
|
#include "debuggeractions.h"
|
2009-10-01 18:01:23 +02:00
|
|
|
#include "debuggermanager.h"
|
2009-10-28 11:50:58 +01:00
|
|
|
#include "idebuggerengine.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
#if USE_MODEL_TEST
|
|
|
|
|
#include "modeltest.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-12-09 16:18:28 +01:00
|
|
|
#include <utils/qtcassert.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
#include <QtCore/QDebug>
|
|
|
|
|
#include <QtCore/QEvent>
|
2009-11-04 17:11:35 +01:00
|
|
|
|
2009-06-19 15:53:46 +02:00
|
|
|
#include <QtCore/QtAlgorithms>
|
2009-03-25 17:33:49 +01:00
|
|
|
#include <QtCore/QTextStream>
|
2009-06-17 16:00:03 +02:00
|
|
|
#include <QtCore/QTimer>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-03-19 09:32:09 +01:00
|
|
|
#include <QtGui/QAction>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <QtGui/QApplication>
|
|
|
|
|
#include <QtGui/QLabel>
|
|
|
|
|
#include <QtGui/QToolTip>
|
|
|
|
|
#include <QtGui/QTextEdit>
|
|
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
2009-04-02 11:41:36 +02:00
|
|
|
|
2009-06-24 12:31:09 +02:00
|
|
|
// creates debug output for accesses to the model
|
|
|
|
|
//#define DEBUG_MODEL 1
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
#if DEBUG_MODEL
|
|
|
|
|
# define MODEL_DEBUG(s) qDebug() << s
|
|
|
|
|
#else
|
2010-01-29 21:33:57 +01:00
|
|
|
# define MODEL_DEBUG(s)
|
2008-12-02 12:01:29 +01:00
|
|
|
#endif
|
|
|
|
|
#define MODEL_DEBUGX(s) qDebug() << s
|
|
|
|
|
|
2009-06-17 16:00:03 +02:00
|
|
|
namespace Debugger {
|
|
|
|
|
namespace Internal {
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
static const QString strNotInScope =
|
2009-09-15 18:21:40 +02:00
|
|
|
QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
|
2009-07-13 09:11:07 +02:00
|
|
|
|
2009-04-15 10:06:31 +02:00
|
|
|
static int watcherCounter = 0;
|
2009-07-13 09:11:07 +02:00
|
|
|
static int generationCounter = 0;
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
2009-07-13 09:11:07 +02:00
|
|
|
// WatchItem
|
2008-12-02 12:01:29 +01:00
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
2009-07-10 14:36:28 +02:00
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
class WatchItem : public WatchData
|
|
|
|
|
{
|
|
|
|
|
public:
|
2009-11-26 14:11:57 +01:00
|
|
|
WatchItem() { parent = 0; }
|
2009-07-13 09:11:07 +02:00
|
|
|
|
2010-02-17 13:54:16 +01:00
|
|
|
~WatchItem() {
|
|
|
|
|
if (parent != 0)
|
|
|
|
|
parent->children.removeOne(this);
|
|
|
|
|
qDeleteAll(children);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
WatchItem(const WatchData &data) : WatchData(data)
|
2009-11-26 14:11:57 +01:00
|
|
|
{ parent = 0; }
|
2009-07-13 09:11:07 +02:00
|
|
|
|
|
|
|
|
void setData(const WatchData &data)
|
2010-01-06 18:00:59 +01:00
|
|
|
{ static_cast<WatchData &>(*this) = data; }
|
2009-07-13 09:11:07 +02:00
|
|
|
|
|
|
|
|
WatchItem *parent;
|
|
|
|
|
QList<WatchItem *> children; // fetched children
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// WatchData
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
2010-01-29 21:33:57 +01:00
|
|
|
|
2009-06-22 11:35:08 +02:00
|
|
|
WatchData::WatchData() :
|
|
|
|
|
hasChildren(false),
|
|
|
|
|
generation(-1),
|
2009-08-31 09:14:04 +02:00
|
|
|
valueEnabled(true),
|
|
|
|
|
valueEditable(true),
|
2009-10-16 16:26:28 +02:00
|
|
|
error(false),
|
2009-06-22 11:35:08 +02:00
|
|
|
source(0),
|
|
|
|
|
state(InitialState),
|
|
|
|
|
changed(false)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-31 09:14:04 +02:00
|
|
|
bool WatchData::isEqual(const WatchData &other) const
|
|
|
|
|
{
|
|
|
|
|
return iname == other.iname
|
|
|
|
|
&& exp == other.exp
|
|
|
|
|
&& name == other.name
|
|
|
|
|
&& value == other.value
|
|
|
|
|
&& editvalue == other.editvalue
|
|
|
|
|
&& valuetooltip == other.valuetooltip
|
|
|
|
|
&& type == other.type
|
|
|
|
|
&& displayedType == other.displayedType
|
|
|
|
|
&& variable == other.variable
|
|
|
|
|
&& addr == other.addr
|
|
|
|
|
&& saddr == other.saddr
|
|
|
|
|
&& framekey == other.framekey
|
|
|
|
|
&& hasChildren == other.hasChildren
|
|
|
|
|
&& valueEnabled == other.valueEnabled
|
2009-10-16 16:26:28 +02:00
|
|
|
&& valueEditable == other.valueEditable
|
|
|
|
|
&& error == other.error;
|
2009-08-31 09:14:04 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void WatchData::setError(const QString &msg)
|
|
|
|
|
{
|
|
|
|
|
setAllUnneeded();
|
|
|
|
|
value = msg;
|
2009-06-22 11:35:08 +02:00
|
|
|
setHasChildren(false);
|
2009-08-31 09:14:04 +02:00
|
|
|
valueEnabled = false;
|
|
|
|
|
valueEditable = false;
|
2009-10-16 16:26:28 +02:00
|
|
|
error = true;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-04-02 10:54:22 +02:00
|
|
|
void WatchData::setValue(const QString &value0)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-04-02 10:54:22 +02:00
|
|
|
value = value0;
|
2008-12-02 12:01:29 +01:00
|
|
|
if (value == "{...}") {
|
|
|
|
|
value.clear();
|
2009-06-22 11:35:08 +02:00
|
|
|
hasChildren = true; // at least one...
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// avoid duplicated information
|
2010-02-01 12:43:56 +01:00
|
|
|
if (value.startsWith(QLatin1Char('(')) && value.contains(") 0x"))
|
2008-12-02 12:01:29 +01:00
|
|
|
value = value.mid(value.lastIndexOf(") 0x") + 2);
|
|
|
|
|
|
|
|
|
|
// doubles are sometimes displayed as "@0x6141378: 1.2".
|
|
|
|
|
// I don't want that.
|
|
|
|
|
if (/*isIntOrFloatType(type) && */ value.startsWith("@0x")
|
|
|
|
|
&& value.contains(':')) {
|
|
|
|
|
value = value.mid(value.indexOf(':') + 2);
|
2009-06-22 11:35:08 +02:00
|
|
|
setHasChildren(false);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// "numchild" is sometimes lying
|
|
|
|
|
//MODEL_DEBUG("\n\n\nPOINTER: " << type << value);
|
|
|
|
|
if (isPointerType(type))
|
2009-12-07 12:06:01 +01:00
|
|
|
setHasChildren(value != "0x0" && value != "<null>"
|
|
|
|
|
&& !isCharPointerType(type));
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
// pointer type information is available in the 'type'
|
|
|
|
|
// column. No need to duplicate it here.
|
2010-02-01 12:43:56 +01:00
|
|
|
if (value.startsWith(QLatin1Char('(') + type + ") 0x"))
|
|
|
|
|
value = value.section(QLatin1Char(' '), -1, -1);
|
2010-01-29 21:33:57 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
setValueUnneeded();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchData::setValueToolTip(const QString &tooltip)
|
|
|
|
|
{
|
|
|
|
|
valuetooltip = tooltip;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-13 14:16:50 +02:00
|
|
|
void WatchData::setType(const QString &str, bool guessChildrenFromType)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
type = str.trimmed();
|
|
|
|
|
bool changed = true;
|
|
|
|
|
while (changed) {
|
2009-03-25 17:33:49 +01:00
|
|
|
if (type.endsWith(QLatin1String("const")))
|
2008-12-02 12:01:29 +01:00
|
|
|
type.chop(5);
|
2009-03-25 17:33:49 +01:00
|
|
|
else if (type.endsWith(QLatin1Char(' ')))
|
2008-12-02 12:01:29 +01:00
|
|
|
type.chop(1);
|
2009-03-25 17:33:49 +01:00
|
|
|
else if (type.endsWith(QLatin1Char('&')))
|
2008-12-02 12:01:29 +01:00
|
|
|
type.chop(1);
|
2009-03-25 17:33:49 +01:00
|
|
|
else if (type.startsWith(QLatin1String("const ")))
|
2008-12-02 12:01:29 +01:00
|
|
|
type = type.mid(6);
|
2009-03-25 17:33:49 +01:00
|
|
|
else if (type.startsWith(QLatin1String("volatile ")))
|
2008-12-02 12:01:29 +01:00
|
|
|
type = type.mid(9);
|
2009-03-25 17:33:49 +01:00
|
|
|
else if (type.startsWith(QLatin1String("class ")))
|
2008-12-02 12:01:29 +01:00
|
|
|
type = type.mid(6);
|
2009-03-25 17:33:49 +01:00
|
|
|
else if (type.startsWith(QLatin1String("struct ")))
|
2008-12-02 12:01:29 +01:00
|
|
|
type = type.mid(6);
|
2009-03-25 17:33:49 +01:00
|
|
|
else if (type.startsWith(QLatin1Char(' ')))
|
2008-12-02 12:01:29 +01:00
|
|
|
type = type.mid(1);
|
|
|
|
|
else
|
|
|
|
|
changed = false;
|
|
|
|
|
}
|
|
|
|
|
setTypeUnneeded();
|
2009-10-13 14:16:50 +02:00
|
|
|
if (guessChildrenFromType) {
|
|
|
|
|
switch (guessChildren(type)) {
|
2009-07-14 11:21:52 +02:00
|
|
|
case HasChildren:
|
|
|
|
|
setHasChildren(true);
|
|
|
|
|
break;
|
|
|
|
|
case HasNoChildren:
|
|
|
|
|
setHasChildren(false);
|
|
|
|
|
break;
|
|
|
|
|
case HasPossiblyChildren:
|
|
|
|
|
setHasChildren(true); // FIXME: bold assumption
|
|
|
|
|
break;
|
2009-10-13 14:16:50 +02:00
|
|
|
}
|
2009-07-14 11:21:52 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-06-25 15:00:57 +02:00
|
|
|
void WatchData::setAddress(const QString &str)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-01-05 16:51:55 +01:00
|
|
|
addr = str.toLatin1();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString WatchData::toString() const
|
|
|
|
|
{
|
2009-03-25 17:33:49 +01:00
|
|
|
const char *doubleQuoteComma = "\",";
|
|
|
|
|
QString res;
|
|
|
|
|
QTextStream str(&res);
|
2009-08-26 17:29:22 +02:00
|
|
|
str << QLatin1Char('{');
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!iname.isEmpty())
|
2009-03-25 17:33:49 +01:00
|
|
|
str << "iname=\"" << iname << doubleQuoteComma;
|
2009-09-21 14:55:39 +02:00
|
|
|
if (!name.isEmpty() && name != iname)
|
|
|
|
|
str << "name=\"" << name << doubleQuoteComma;
|
2009-10-16 16:26:28 +02:00
|
|
|
if (error)
|
|
|
|
|
str << "error,";
|
2009-07-06 17:36:50 +02:00
|
|
|
if (!addr.isEmpty())
|
|
|
|
|
str << "addr=\"" << addr << doubleQuoteComma;
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!exp.isEmpty())
|
2009-03-25 17:33:49 +01:00
|
|
|
str << "exp=\"" << exp << doubleQuoteComma;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
if (!variable.isEmpty())
|
2009-03-25 17:33:49 +01:00
|
|
|
str << "variable=\"" << variable << doubleQuoteComma;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
if (isValueNeeded())
|
2009-03-25 17:33:49 +01:00
|
|
|
str << "value=<needed>,";
|
2008-12-02 12:01:29 +01:00
|
|
|
if (isValueKnown() && !value.isEmpty())
|
2009-03-25 17:33:49 +01:00
|
|
|
str << "value=\"" << value << doubleQuoteComma;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
if (!editvalue.isEmpty())
|
2009-03-25 17:33:49 +01:00
|
|
|
str << "editvalue=\"" << editvalue << doubleQuoteComma;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
if (isTypeNeeded())
|
2009-03-25 17:33:49 +01:00
|
|
|
str << "type=<needed>,";
|
2008-12-02 12:01:29 +01:00
|
|
|
if (isTypeKnown() && !type.isEmpty())
|
2009-03-25 17:33:49 +01:00
|
|
|
str << "type=\"" << type << doubleQuoteComma;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-06-22 11:35:08 +02:00
|
|
|
if (isHasChildrenNeeded())
|
|
|
|
|
str << "hasChildren=<needed>,";
|
|
|
|
|
if (isHasChildrenKnown())
|
|
|
|
|
str << "hasChildren=\"" << (hasChildren ? "true" : "false") << doubleQuoteComma;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
if (isChildrenNeeded())
|
2009-03-25 17:33:49 +01:00
|
|
|
str << "children=<needed>,";
|
2009-10-01 14:41:30 +02:00
|
|
|
if (source)
|
|
|
|
|
str << "source=" << source;
|
2009-03-25 17:33:49 +01:00
|
|
|
str.flush();
|
|
|
|
|
if (res.endsWith(QLatin1Char(',')))
|
|
|
|
|
res.truncate(res.size() - 1);
|
|
|
|
|
return res + QLatin1Char('}');
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-05-13 14:39:55 +02:00
|
|
|
// Format a tooltip fow with aligned colon
|
2009-07-06 10:48:15 +02:00
|
|
|
static void formatToolTipRow(QTextStream &str, const QString &category, const QString &value)
|
2009-05-13 14:39:55 +02:00
|
|
|
{
|
2009-07-06 10:48:15 +02:00
|
|
|
str << "<tr><td>" << category << "</td><td> : </td><td>"
|
|
|
|
|
<< Qt::escape(value) << "</td></tr>";
|
2009-05-13 14:39:55 +02:00
|
|
|
}
|
|
|
|
|
|
2009-08-12 11:21:44 +02:00
|
|
|
static inline QString typeToolTip(const WatchData &wd)
|
|
|
|
|
{
|
|
|
|
|
if (wd.displayedType.isEmpty())
|
|
|
|
|
return wd.type;
|
|
|
|
|
QString rc = wd.displayedType;
|
|
|
|
|
rc += QLatin1String(" (");
|
|
|
|
|
rc += wd.type;
|
|
|
|
|
rc += QLatin1Char(')');
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-13 14:39:55 +02:00
|
|
|
QString WatchData::toToolTip() const
|
|
|
|
|
{
|
2009-05-29 16:24:46 +02:00
|
|
|
if (!valuetooltip.isEmpty())
|
|
|
|
|
return QString::number(valuetooltip.size());
|
2009-05-13 14:39:55 +02:00
|
|
|
QString res;
|
|
|
|
|
QTextStream str(&res);
|
|
|
|
|
str << "<html><body><table>";
|
2010-01-06 16:38:10 +01:00
|
|
|
formatToolTipRow(str, WatchHandler::tr("Name"), name);
|
2009-07-06 10:48:15 +02:00
|
|
|
formatToolTipRow(str, WatchHandler::tr("Expression"), exp);
|
2009-08-12 11:21:44 +02:00
|
|
|
formatToolTipRow(str, WatchHandler::tr("Type"), typeToolTip(*this));
|
2009-05-13 14:39:55 +02:00
|
|
|
QString val = value;
|
|
|
|
|
if (value.size() > 1000) {
|
|
|
|
|
val.truncate(1000);
|
|
|
|
|
val += WatchHandler::tr(" ... <cut off>");
|
|
|
|
|
}
|
2009-07-06 10:48:15 +02:00
|
|
|
formatToolTipRow(str, WatchHandler::tr("Value"), val);
|
|
|
|
|
formatToolTipRow(str, WatchHandler::tr("Object Address"), addr);
|
|
|
|
|
formatToolTipRow(str, WatchHandler::tr("Stored Address"), saddr);
|
|
|
|
|
formatToolTipRow(str, WatchHandler::tr("Internal ID"), iname);
|
2009-08-31 09:14:04 +02:00
|
|
|
formatToolTipRow(str, WatchHandler::tr("Generation"),
|
|
|
|
|
QString::number(generation));
|
2009-05-13 14:39:55 +02:00
|
|
|
str << "</table></body></html>";
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-16 16:26:28 +02:00
|
|
|
QString WatchData::msgNotInScope()
|
|
|
|
|
{
|
|
|
|
|
static const QString rc = QCoreApplication::translate("Debugger::Internal::WatchData", "<not in scope>");
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-04 13:19:49 +01:00
|
|
|
const QString &WatchData::shadowedNameFormat()
|
|
|
|
|
{
|
|
|
|
|
static const QString format = QCoreApplication::translate("Debugger::Internal::WatchData", "%1 <shadowed %2>");
|
|
|
|
|
return format;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-16 16:26:28 +02:00
|
|
|
QString WatchData::shadowedName(const QString &name, int seen)
|
|
|
|
|
{
|
|
|
|
|
if (seen <= 0)
|
|
|
|
|
return name;
|
2010-02-04 13:19:49 +01:00
|
|
|
return shadowedNameFormat().arg(name, seen);
|
2009-10-16 16:26:28 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
2009-06-17 16:00:03 +02:00
|
|
|
//
|
2009-07-13 09:11:07 +02:00
|
|
|
// WatchModel
|
2009-06-17 16:00:03 +02:00
|
|
|
//
|
2009-07-13 09:11:07 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
2009-06-17 16:00:03 +02:00
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
WatchModel::WatchModel(WatchHandler *handler, WatchType type)
|
|
|
|
|
: QAbstractItemModel(handler), m_handler(handler), m_type(type)
|
2009-06-17 16:00:03 +02:00
|
|
|
{
|
2009-11-26 14:11:57 +01:00
|
|
|
m_inExtraLayoutChanged = false;
|
2009-07-13 09:11:07 +02:00
|
|
|
m_root = new WatchItem;
|
|
|
|
|
m_root->hasChildren = 1;
|
|
|
|
|
m_root->state = 0;
|
|
|
|
|
m_root->name = WatchHandler::tr("Root");
|
2009-07-13 11:25:05 +02:00
|
|
|
m_root->parent = 0;
|
2009-06-17 16:00:03 +02:00
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
switch (m_type) {
|
|
|
|
|
case LocalsWatch:
|
2010-01-05 16:51:55 +01:00
|
|
|
m_root->iname = "local";
|
2009-07-13 11:25:05 +02:00
|
|
|
m_root->name = WatchHandler::tr("Locals");
|
2009-07-13 09:11:07 +02:00
|
|
|
break;
|
|
|
|
|
case WatchersWatch:
|
2010-01-05 16:51:55 +01:00
|
|
|
m_root->iname = "watch";
|
2009-07-13 11:25:05 +02:00
|
|
|
m_root->name = WatchHandler::tr("Watchers");
|
2009-07-13 09:11:07 +02:00
|
|
|
break;
|
|
|
|
|
case TooltipsWatch:
|
2010-01-05 16:51:55 +01:00
|
|
|
m_root->iname = "tooltip";
|
2009-07-13 11:25:05 +02:00
|
|
|
m_root->name = WatchHandler::tr("Tooltip");
|
2009-07-13 09:11:07 +02:00
|
|
|
break;
|
|
|
|
|
}
|
2009-06-17 16:00:03 +02:00
|
|
|
}
|
|
|
|
|
|
2009-11-04 17:11:35 +01:00
|
|
|
WatchModel::~WatchModel()
|
|
|
|
|
{
|
|
|
|
|
delete m_root;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-13 11:25:05 +02:00
|
|
|
WatchItem *WatchModel::rootItem() const
|
2009-06-17 16:00:03 +02:00
|
|
|
{
|
2009-07-13 11:25:05 +02:00
|
|
|
return m_root;
|
2009-06-17 16:00:03 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
void WatchModel::reinitialize()
|
2009-06-17 16:00:03 +02:00
|
|
|
{
|
2009-07-13 11:25:05 +02:00
|
|
|
int n = m_root->children.size();
|
2009-07-13 09:11:07 +02:00
|
|
|
if (n == 0)
|
|
|
|
|
return;
|
2009-07-13 11:25:05 +02:00
|
|
|
//MODEL_DEBUG("REMOVING " << n << " CHILDREN OF " << m_root->iname);
|
|
|
|
|
QModelIndex index = watchIndex(m_root);
|
2009-07-13 09:11:07 +02:00
|
|
|
beginRemoveRows(index, 0, n - 1);
|
2009-07-13 11:25:05 +02:00
|
|
|
qDeleteAll(m_root->children);
|
|
|
|
|
m_root->children.clear();
|
2009-07-13 09:11:07 +02:00
|
|
|
endRemoveRows();
|
2009-06-17 16:00:03 +02:00
|
|
|
}
|
|
|
|
|
|
2009-11-25 08:35:02 +01:00
|
|
|
void WatchModel::emitAllChanged()
|
|
|
|
|
{
|
|
|
|
|
emit layoutChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-04 16:23:51 +01:00
|
|
|
void WatchModel::beginCycle(bool clearFetchTriggered)
|
2009-08-31 09:14:04 +02:00
|
|
|
{
|
|
|
|
|
emit enableUpdates(false);
|
2010-02-04 16:23:51 +01:00
|
|
|
if (clearFetchTriggered)
|
|
|
|
|
m_fetchTriggered.clear();
|
2009-08-31 09:14:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchModel::endCycle()
|
|
|
|
|
{
|
|
|
|
|
removeOutdated();
|
|
|
|
|
emit enableUpdates(true);
|
2010-02-04 13:20:11 +01:00
|
|
|
// Prevent 'fetchMore()' from being triggered.
|
2009-11-26 14:11:57 +01:00
|
|
|
m_inExtraLayoutChanged = true;
|
|
|
|
|
emit layoutChanged();
|
|
|
|
|
QTimer::singleShot(0, this, SLOT(resetExtraLayoutChanged()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchModel::resetExtraLayoutChanged()
|
|
|
|
|
{
|
|
|
|
|
m_inExtraLayoutChanged = false;
|
2009-08-31 09:14:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchModel::dump()
|
|
|
|
|
{
|
|
|
|
|
qDebug() << "\n";
|
|
|
|
|
foreach (WatchItem *child, m_root->children)
|
|
|
|
|
dumpHelper(child);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchModel::dumpHelper(WatchItem *item)
|
|
|
|
|
{
|
|
|
|
|
qDebug() << "ITEM: " << item->iname
|
|
|
|
|
<< (item->parent ? item->parent->iname : "<none>")
|
|
|
|
|
<< item->generation;
|
|
|
|
|
foreach (WatchItem *child, item->children)
|
|
|
|
|
dumpHelper(child);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
void WatchModel::removeOutdated()
|
2009-06-17 16:00:03 +02:00
|
|
|
{
|
2009-07-13 11:25:05 +02:00
|
|
|
foreach (WatchItem *child, m_root->children)
|
2009-07-13 09:11:07 +02:00
|
|
|
removeOutdatedHelper(child);
|
|
|
|
|
#if DEBUG_MODEL
|
|
|
|
|
#if USE_MODEL_TEST
|
|
|
|
|
//(void) new ModelTest(this, this);
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
2009-06-17 16:00:03 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
void WatchModel::removeOutdatedHelper(WatchItem *item)
|
2009-06-17 16:00:03 +02:00
|
|
|
{
|
2009-08-31 09:14:04 +02:00
|
|
|
if (item->generation < generationCounter) {
|
2009-11-04 17:11:35 +01:00
|
|
|
destroyItem(item);
|
2009-08-31 09:14:04 +02:00
|
|
|
} else {
|
2009-07-13 09:11:07 +02:00
|
|
|
foreach (WatchItem *child, item->children)
|
|
|
|
|
removeOutdatedHelper(child);
|
2009-06-30 13:57:00 +02:00
|
|
|
}
|
2009-06-17 16:00:03 +02:00
|
|
|
}
|
|
|
|
|
|
2009-11-04 17:11:35 +01:00
|
|
|
void WatchModel::destroyItem(WatchItem *item)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-07-13 09:11:07 +02:00
|
|
|
WatchItem *parent = item->parent;
|
|
|
|
|
QModelIndex index = watchIndex(parent);
|
|
|
|
|
int n = parent->children.indexOf(item);
|
|
|
|
|
//MODEL_DEBUG("NEED TO REMOVE: " << item->iname << "AT" << n);
|
|
|
|
|
beginRemoveRows(index, n, n);
|
|
|
|
|
parent->children.removeAt(n);
|
|
|
|
|
endRemoveRows();
|
2009-11-04 17:11:35 +01:00
|
|
|
delete item;
|
2009-07-10 14:36:28 +02:00
|
|
|
}
|
|
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
static QByteArray parentName(const QByteArray &iname)
|
2009-07-10 14:36:28 +02:00
|
|
|
{
|
2010-01-05 16:51:55 +01:00
|
|
|
int pos = iname.lastIndexOf('.');
|
2008-12-02 12:01:29 +01:00
|
|
|
if (pos == -1)
|
2010-01-05 16:51:55 +01:00
|
|
|
return QByteArray();
|
2008-12-02 12:01:29 +01:00
|
|
|
return iname.left(pos);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
|
2009-04-09 13:47:54 +02:00
|
|
|
static QString chopConst(QString type)
|
|
|
|
|
{
|
|
|
|
|
while (1) {
|
|
|
|
|
if (type.startsWith("const"))
|
|
|
|
|
type = type.mid(5);
|
|
|
|
|
else if (type.startsWith(' '))
|
|
|
|
|
type = type.mid(1);
|
|
|
|
|
else if (type.endsWith("const"))
|
|
|
|
|
type.chop(5);
|
|
|
|
|
else if (type.endsWith(' '))
|
|
|
|
|
type.chop(1);
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-03 13:56:27 +02:00
|
|
|
static inline QRegExp stdStringRegExp(const QString &charType)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-07-03 13:56:27 +02:00
|
|
|
QString rc = QLatin1String("basic_string<");
|
|
|
|
|
rc += charType;
|
|
|
|
|
rc += QLatin1String(",[ ]?std::char_traits<");
|
|
|
|
|
rc += charType;
|
|
|
|
|
rc += QLatin1String(">,[ ]?std::allocator<");
|
|
|
|
|
rc += charType;
|
|
|
|
|
rc += QLatin1String("> >");
|
|
|
|
|
const QRegExp re(rc);
|
|
|
|
|
Q_ASSERT(re.isValid());
|
|
|
|
|
return re;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-25 08:35:02 +01:00
|
|
|
static QString niceTypeHelper(const QString typeIn)
|
2009-07-03 13:56:27 +02:00
|
|
|
{
|
|
|
|
|
static QMap<QString, QString> cache;
|
|
|
|
|
const QMap<QString, QString>::const_iterator it = cache.constFind(typeIn);
|
2009-08-31 09:14:04 +02:00
|
|
|
if (it != cache.constEnd())
|
2009-07-03 13:56:27 +02:00
|
|
|
return it.value();
|
2009-08-31 09:14:04 +02:00
|
|
|
|
2009-07-03 13:56:27 +02:00
|
|
|
QString type = typeIn;
|
|
|
|
|
type.replace(QLatin1Char('*'), QLatin1Char('@'));
|
2009-06-23 10:24:25 +02:00
|
|
|
|
|
|
|
|
for (int i = 0; i < 10; ++i) {
|
|
|
|
|
int start = type.indexOf("std::allocator<");
|
|
|
|
|
if (start == -1)
|
2010-01-29 21:33:57 +01:00
|
|
|
break;
|
2009-06-23 10:24:25 +02:00
|
|
|
// search for matching '>'
|
|
|
|
|
int pos;
|
|
|
|
|
int level = 0;
|
|
|
|
|
for (pos = start + 12; pos < type.size(); ++pos) {
|
|
|
|
|
int c = type.at(pos).unicode();
|
|
|
|
|
if (c == '<') {
|
|
|
|
|
++level;
|
|
|
|
|
} else if (c == '>') {
|
|
|
|
|
--level;
|
|
|
|
|
if (level == 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2009-04-09 13:47:54 +02:00
|
|
|
}
|
2009-06-23 10:24:25 +02:00
|
|
|
QString alloc = type.mid(start, pos + 1 - start).trimmed();
|
|
|
|
|
QString inner = alloc.mid(15, alloc.size() - 16).trimmed();
|
|
|
|
|
|
2009-07-03 13:56:27 +02:00
|
|
|
if (inner == QLatin1String("char")) { // std::string
|
2009-08-31 09:14:04 +02:00
|
|
|
const QRegExp stringRegexp = stdStringRegExp(inner);
|
2009-07-03 13:56:27 +02:00
|
|
|
type.replace(stringRegexp, QLatin1String("string"));
|
|
|
|
|
} else if (inner == QLatin1String("wchar_t")) { // std::wstring
|
2009-08-31 09:14:04 +02:00
|
|
|
const QRegExp wchartStringRegexp = stdStringRegExp(inner);
|
2009-07-03 13:56:27 +02:00
|
|
|
type.replace(wchartStringRegexp, QLatin1String("wstring"));
|
|
|
|
|
} else if (inner == QLatin1String("unsigned short")) { // std::wstring/MSVC
|
2009-08-31 09:14:04 +02:00
|
|
|
const QRegExp usStringRegexp = stdStringRegExp(inner);
|
2009-07-03 13:56:27 +02:00
|
|
|
type.replace(usStringRegexp, QLatin1String("wstring"));
|
|
|
|
|
}
|
2009-06-23 10:24:25 +02:00
|
|
|
// std::vector, std::deque, std::list
|
2009-08-31 09:14:04 +02:00
|
|
|
const QRegExp re1(QString::fromLatin1("(vector|list|deque)<%1, ?%2\\s*>").arg(inner, alloc));
|
2009-07-03 13:56:27 +02:00
|
|
|
Q_ASSERT(re1.isValid());
|
2009-06-23 10:24:25 +02:00
|
|
|
if (re1.indexIn(type) != -1)
|
2009-07-03 13:56:27 +02:00
|
|
|
type.replace(re1.cap(0), QString::fromLatin1("%1<%2>").arg(re1.cap(1), inner));
|
2009-04-09 13:47:54 +02:00
|
|
|
|
|
|
|
|
// std::stack
|
2009-08-31 09:14:04 +02:00
|
|
|
QRegExp re6(QString::fromLatin1("stack<%1, ?std::deque<%2> >").arg(inner, inner));
|
2009-07-03 13:56:27 +02:00
|
|
|
if (!re6.isMinimal())
|
|
|
|
|
re6.setMinimal(true);
|
|
|
|
|
Q_ASSERT(re6.isValid());
|
2009-06-23 10:24:25 +02:00
|
|
|
if (re6.indexIn(type) != -1)
|
2009-07-03 13:56:27 +02:00
|
|
|
type.replace(re6.cap(0), QString::fromLatin1("stack<%1>").arg(inner));
|
2009-06-23 10:24:25 +02:00
|
|
|
|
|
|
|
|
// std::set
|
2009-08-31 09:14:04 +02:00
|
|
|
QRegExp re4(QString::fromLatin1("set<%1, ?std::less<%2>, ?%3\\s*>").arg(inner, inner, alloc));
|
2009-07-03 13:56:27 +02:00
|
|
|
if (!re4.isMinimal())
|
|
|
|
|
re4.setMinimal(true);
|
|
|
|
|
Q_ASSERT(re4.isValid());
|
2009-06-23 10:24:25 +02:00
|
|
|
if (re4.indexIn(type) != -1)
|
2009-07-03 13:56:27 +02:00
|
|
|
type.replace(re4.cap(0), QString::fromLatin1("set<%1>").arg(inner));
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2008-12-11 16:10:47 +01:00
|
|
|
// std::map
|
2009-06-23 10:24:25 +02:00
|
|
|
if (inner.startsWith("std::pair<")) {
|
|
|
|
|
// search for outermost ','
|
|
|
|
|
int pos;
|
|
|
|
|
int level = 0;
|
|
|
|
|
for (pos = 10; pos < inner.size(); ++pos) {
|
|
|
|
|
int c = inner.at(pos).unicode();
|
|
|
|
|
if (c == '<')
|
|
|
|
|
++level;
|
|
|
|
|
else if (c == '>')
|
|
|
|
|
--level;
|
|
|
|
|
else if (c == ',' && level == 0)
|
2009-04-09 13:47:54 +02:00
|
|
|
break;
|
2009-06-23 10:24:25 +02:00
|
|
|
}
|
|
|
|
|
QString ckey = inner.mid(10, pos - 10);
|
|
|
|
|
QString key = chopConst(ckey);
|
2009-08-31 09:14:04 +02:00
|
|
|
QString value = inner.mid(pos + 2, inner.size() - 3 - pos).trimmed();
|
|
|
|
|
QRegExp re5(QString("map<%1, ?%2, ?std::less<%3 ?>, ?%4\\s*>")
|
2009-06-23 10:24:25 +02:00
|
|
|
.arg(key, value, key, alloc));
|
2009-07-03 13:56:27 +02:00
|
|
|
if (!re5.isMinimal())
|
|
|
|
|
re5.setMinimal(true);
|
|
|
|
|
Q_ASSERT(re5.isValid());
|
2009-08-31 09:14:04 +02:00
|
|
|
if (re5.indexIn(type) != -1) {
|
2009-06-23 10:24:25 +02:00
|
|
|
type.replace(re5.cap(0), QString("map<%1, %2>").arg(key, value));
|
2009-08-31 09:14:04 +02:00
|
|
|
} else {
|
|
|
|
|
QRegExp re7(QString("map<const %1, ?%2, ?std::less<const %3>, ?%4\\s*>")
|
2009-06-23 10:24:25 +02:00
|
|
|
.arg(key, value, key, alloc));
|
2009-07-03 13:56:27 +02:00
|
|
|
if (!re7.isMinimal())
|
|
|
|
|
re7.setMinimal(true);
|
2009-06-23 10:24:25 +02:00
|
|
|
if (re7.indexIn(type) != -1)
|
|
|
|
|
type.replace(re7.cap(0), QString("map<const %1, %2>").arg(key, value));
|
2009-04-09 13:47:54 +02:00
|
|
|
}
|
2008-12-11 16:10:47 +01:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-07-03 13:56:27 +02:00
|
|
|
type.replace(QLatin1Char('@'), QLatin1Char('*'));
|
2009-06-23 10:24:25 +02:00
|
|
|
type.replace(QLatin1String(" >"), QString(QLatin1Char('>')));
|
2009-07-03 13:56:27 +02:00
|
|
|
cache.insert(typeIn, type); // For simplicity, also cache unmodified types
|
2008-12-02 12:01:29 +01:00
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-25 08:35:02 +01:00
|
|
|
QString WatchModel::niceType(const QString &typeIn) const
|
|
|
|
|
{
|
|
|
|
|
QString type = niceTypeHelper(typeIn);
|
2009-12-02 14:10:14 +01:00
|
|
|
if (!theDebuggerBoolSetting(ShowStdNamespace))
|
2009-11-25 08:35:02 +01:00
|
|
|
type = type.remove("std::");
|
2009-11-26 14:58:07 +01:00
|
|
|
IDebuggerEngine *engine = m_handler->m_manager->currentEngine();
|
2009-12-02 14:10:14 +01:00
|
|
|
if (engine && !theDebuggerBoolSetting(ShowQtNamespace))
|
2009-11-26 14:58:07 +01:00
|
|
|
type = type.remove(engine->qtNamespace());
|
2009-11-25 08:35:02 +01:00
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-25 16:32:44 +01:00
|
|
|
template <class IntType> QString reformatInteger(IntType value, int format)
|
|
|
|
|
{
|
|
|
|
|
switch (format) {
|
|
|
|
|
case HexadecimalFormat:
|
|
|
|
|
return ("(hex) ") + QString::number(value, 16);
|
|
|
|
|
case BinaryFormat:
|
|
|
|
|
return ("(bin) ") + QString::number(value, 2);
|
|
|
|
|
case OctalFormat:
|
|
|
|
|
return ("(oct) ") + QString::number(value, 8);
|
|
|
|
|
}
|
|
|
|
|
return QString::number(value); // not reached
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-08 16:00:06 +01:00
|
|
|
static QString formattedValue(const WatchData &data, int format)
|
2009-07-03 11:20:47 +02:00
|
|
|
{
|
2009-07-03 13:25:51 +02:00
|
|
|
if (isIntType(data.type)) {
|
2009-11-25 16:32:44 +01:00
|
|
|
if (format <= 0)
|
|
|
|
|
return data.value;
|
2009-12-08 17:46:20 +01:00
|
|
|
// Evil hack, covers 'unsigned' as well as quint64.
|
|
|
|
|
if (data.type.contains(QLatin1Char('u')))
|
2009-11-25 16:32:44 +01:00
|
|
|
return reformatInteger(data.value.toULongLong(), format);
|
2009-12-08 16:00:06 +01:00
|
|
|
return reformatInteger(data.value.toLongLong(), format);
|
|
|
|
|
}
|
|
|
|
|
if (0 && !data.addr.isEmpty()) {
|
|
|
|
|
if (format == BaldPointerFormat)
|
|
|
|
|
return data.value;
|
|
|
|
|
bool ok = false;
|
|
|
|
|
const void *addr =
|
|
|
|
|
reinterpret_cast<void *>(data.value.toULongLong(&ok, 0));
|
|
|
|
|
if (!ok || !addr)
|
|
|
|
|
return data.value;
|
2010-01-11 10:22:55 +01:00
|
|
|
// FIXME: add a round trip through the debugger to prevent crashs?
|
2009-12-08 16:00:06 +01:00
|
|
|
if (format == Latin1StringFormat)
|
|
|
|
|
return QString::fromLatin1(static_cast<const char *>(addr));
|
|
|
|
|
if (format == Local8BitStringFormat)
|
|
|
|
|
return QString::fromLocal8Bit(static_cast<const char *>(addr));
|
|
|
|
|
if (format == Utf8StringFormat)
|
|
|
|
|
return QString::fromUtf8(static_cast<const char *>(addr));
|
|
|
|
|
if (format == Utf16StringFormat)
|
|
|
|
|
return QString::fromUtf16(static_cast<const ushort *>(addr));
|
|
|
|
|
if (format == Ucs4StringFormat)
|
|
|
|
|
return QString::fromUcs4(static_cast<const uint *>(addr));
|
|
|
|
|
return data.value;
|
2009-07-03 11:20:47 +02:00
|
|
|
}
|
|
|
|
|
return data.value;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
bool WatchModel::canFetchMore(const QModelIndex &index) const
|
2009-06-17 16:00:03 +02:00
|
|
|
{
|
2010-02-04 13:20:11 +01:00
|
|
|
WatchItem *item = watchItem(index);
|
|
|
|
|
QTC_ASSERT(item, return false);
|
2009-12-04 13:22:39 +01:00
|
|
|
return !m_inExtraLayoutChanged && index.isValid()
|
2010-02-04 13:20:11 +01:00
|
|
|
&& !m_fetchTriggered.contains(item->iname);
|
2009-07-13 09:11:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchModel::fetchMore(const QModelIndex &index)
|
|
|
|
|
{
|
2009-11-26 14:11:57 +01:00
|
|
|
if (m_inExtraLayoutChanged)
|
|
|
|
|
return;
|
2009-07-13 09:11:07 +02:00
|
|
|
QTC_ASSERT(index.isValid(), return);
|
2009-11-26 14:11:57 +01:00
|
|
|
WatchItem *item = watchItem(index);
|
|
|
|
|
QTC_ASSERT(item, return);
|
|
|
|
|
QTC_ASSERT(!m_fetchTriggered.contains(item->iname), return);
|
|
|
|
|
m_handler->m_expandedINames.insert(item->iname);
|
|
|
|
|
m_fetchTriggered.insert(item->iname);
|
|
|
|
|
if (item->children.isEmpty()) {
|
|
|
|
|
WatchData data = *item;
|
|
|
|
|
data.setChildrenNeeded();
|
|
|
|
|
m_handler->m_manager->updateWatchData(data);
|
2009-07-13 09:11:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QModelIndex WatchModel::index(int row, int column, const QModelIndex &parent) const
|
|
|
|
|
{
|
|
|
|
|
if (!hasIndex(row, column, parent))
|
|
|
|
|
return QModelIndex();
|
|
|
|
|
|
|
|
|
|
const WatchItem *item = watchItem(parent);
|
|
|
|
|
QTC_ASSERT(item, return QModelIndex());
|
2009-07-13 11:25:05 +02:00
|
|
|
if (row >= item->children.size())
|
|
|
|
|
return QModelIndex();
|
2009-07-13 09:11:07 +02:00
|
|
|
return createIndex(row, column, (void*)(item->children.at(row)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QModelIndex WatchModel::parent(const QModelIndex &idx) const
|
|
|
|
|
{
|
|
|
|
|
if (!idx.isValid())
|
|
|
|
|
return QModelIndex();
|
|
|
|
|
|
|
|
|
|
const WatchItem *item = watchItem(idx);
|
|
|
|
|
if (!item->parent || item->parent == m_root)
|
|
|
|
|
return QModelIndex();
|
|
|
|
|
|
|
|
|
|
const WatchItem *grandparent = item->parent->parent;
|
|
|
|
|
if (!grandparent)
|
|
|
|
|
return QModelIndex();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < grandparent->children.size(); ++i)
|
|
|
|
|
if (grandparent->children.at(i) == item->parent)
|
|
|
|
|
return createIndex(i, 0, (void*) item->parent);
|
|
|
|
|
|
|
|
|
|
return QModelIndex();
|
2009-06-17 16:00:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int WatchModel::rowCount(const QModelIndex &idx) const
|
|
|
|
|
{
|
|
|
|
|
if (idx.column() > 0)
|
|
|
|
|
return 0;
|
|
|
|
|
return watchItem(idx)->children.size();
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
int WatchModel::columnCount(const QModelIndex &idx) const
|
2009-06-17 16:00:03 +02:00
|
|
|
{
|
2009-07-13 17:35:17 +02:00
|
|
|
Q_UNUSED(idx)
|
2009-07-13 09:11:07 +02:00
|
|
|
return 3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool WatchModel::hasChildren(const QModelIndex &parent) const
|
|
|
|
|
{
|
|
|
|
|
WatchItem *item = watchItem(parent);
|
|
|
|
|
return !item || item->hasChildren;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WatchItem *WatchModel::watchItem(const QModelIndex &idx) const
|
|
|
|
|
{
|
2010-01-29 21:33:57 +01:00
|
|
|
return idx.isValid()
|
2009-07-13 09:11:07 +02:00
|
|
|
? static_cast<WatchItem*>(idx.internalPointer()) : m_root;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QModelIndex WatchModel::watchIndex(const WatchItem *item) const
|
|
|
|
|
{
|
|
|
|
|
return watchIndexHelper(item, m_root, QModelIndex());
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-29 21:33:57 +01:00
|
|
|
QModelIndex WatchModel::watchIndexHelper(const WatchItem *needle,
|
2009-07-13 09:11:07 +02:00
|
|
|
const WatchItem *parentItem, const QModelIndex &parentIndex) const
|
|
|
|
|
{
|
|
|
|
|
if (needle == parentItem)
|
|
|
|
|
return parentIndex;
|
|
|
|
|
for (int i = parentItem->children.size(); --i >= 0; ) {
|
|
|
|
|
const WatchItem *childItem = parentItem->children.at(i);
|
|
|
|
|
QModelIndex childIndex = index(i, 0, parentIndex);
|
|
|
|
|
QModelIndex idx = watchIndexHelper(needle, childItem, childIndex);
|
|
|
|
|
if (idx.isValid())
|
|
|
|
|
return idx;
|
2009-06-17 16:00:03 +02:00
|
|
|
}
|
2009-07-13 09:11:07 +02:00
|
|
|
return QModelIndex();
|
2009-06-17 16:00:03 +02:00
|
|
|
}
|
|
|
|
|
|
2010-01-29 21:33:57 +01:00
|
|
|
void WatchModel::emitDataChanged(int column, const QModelIndex &parentIndex)
|
2009-07-03 11:20:47 +02:00
|
|
|
{
|
|
|
|
|
QModelIndex idx1 = index(0, column, parentIndex);
|
|
|
|
|
QModelIndex idx2 = index(rowCount(parentIndex) - 1, column, parentIndex);
|
|
|
|
|
if (idx1.isValid() && idx2.isValid())
|
|
|
|
|
emit dataChanged(idx1, idx2);
|
|
|
|
|
//qDebug() << "CHANGING:\n" << idx1 << "\n" << idx2 << "\n"
|
|
|
|
|
// << data(parentIndex, INameRole).toString();
|
|
|
|
|
for (int i = rowCount(parentIndex); --i >= 0; )
|
|
|
|
|
emitDataChanged(column, index(i, 0, parentIndex));
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-13 10:36:37 +01:00
|
|
|
// Truncate value for item view, maintaining quotes
|
|
|
|
|
static inline QString truncateValue(QString v)
|
|
|
|
|
{
|
|
|
|
|
enum { maxLength = 512 };
|
|
|
|
|
if (v.size() < maxLength)
|
|
|
|
|
return v;
|
|
|
|
|
const bool isQuoted = v.endsWith(QLatin1Char('"')); // check for 'char* "Hallo"'
|
|
|
|
|
v.truncate(maxLength);
|
|
|
|
|
v += isQuoted ? QLatin1String("...\"") : QLatin1String("...");
|
|
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-17 16:00:03 +02:00
|
|
|
QVariant WatchModel::data(const QModelIndex &idx, int role) const
|
|
|
|
|
{
|
2010-01-06 13:30:11 +01:00
|
|
|
const WatchItem *item = watchItem(idx);
|
|
|
|
|
const WatchItem &data = *item;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
switch (role) {
|
|
|
|
|
case Qt::DisplayRole: {
|
2009-07-13 09:11:07 +02:00
|
|
|
switch (idx.column()) {
|
2009-12-08 16:00:06 +01:00
|
|
|
case 0:
|
2010-01-07 13:12:33 +01:00
|
|
|
if (data.name == QLatin1String("*") && item->parent)
|
2010-02-08 11:07:18 +01:00
|
|
|
return QVariant(QLatin1Char('*') + item->parent->name);
|
2009-12-08 16:00:06 +01:00
|
|
|
return data.name;
|
|
|
|
|
case 1: {
|
|
|
|
|
int format = m_handler->m_individualFormats.value(data.iname, -1);
|
|
|
|
|
if (format == -1)
|
|
|
|
|
format = m_handler->m_typeFormats.value(data.type, -1);
|
|
|
|
|
//qDebug() << "FORMATTED: " << format << formattedValue(data, format);
|
2010-01-14 18:45:35 +01:00
|
|
|
return truncateValue(formattedValue(data, format));
|
2009-12-08 16:00:06 +01:00
|
|
|
}
|
|
|
|
|
case 2: {
|
2009-08-12 11:21:44 +02:00
|
|
|
if (!data.displayedType.isEmpty())
|
|
|
|
|
return data.displayedType;
|
|
|
|
|
return niceType(data.type);
|
2009-12-08 16:00:06 +01:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-13 14:39:55 +02:00
|
|
|
case Qt::ToolTipRole:
|
2009-09-23 16:11:25 +02:00
|
|
|
return theDebuggerBoolSetting(UseToolTipsInLocalsView)
|
|
|
|
|
? data.toToolTip() : QVariant();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
case Qt::ForegroundRole: {
|
|
|
|
|
static const QVariant red(QColor(200, 0, 0));
|
|
|
|
|
static const QVariant gray(QColor(140, 140, 140));
|
2009-07-13 09:11:07 +02:00
|
|
|
switch (idx.column()) {
|
2009-08-31 09:14:04 +02:00
|
|
|
case 1: return !data.valueEnabled ? gray : data.changed ? red : QVariant();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-19 15:33:11 +01:00
|
|
|
case ExpressionRole:
|
|
|
|
|
return data.exp;
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
case INameRole:
|
|
|
|
|
return data.iname;
|
|
|
|
|
|
2008-12-08 11:00:46 +01:00
|
|
|
case ExpandedRole:
|
2009-06-17 16:00:03 +02:00
|
|
|
return m_handler->m_expandedINames.contains(data.iname);
|
|
|
|
|
//FIXME return node < 4 || m_expandedINames.contains(data.iname);
|
2009-06-24 12:31:09 +02:00
|
|
|
|
|
|
|
|
case ActiveDataRole:
|
|
|
|
|
qDebug() << "ASK FOR" << data.iname;
|
|
|
|
|
return true;
|
2010-01-29 21:33:57 +01:00
|
|
|
|
2009-07-03 11:20:47 +02:00
|
|
|
case TypeFormatListRole:
|
2009-07-03 13:25:51 +02:00
|
|
|
if (isIntType(data.type))
|
2009-07-03 11:20:47 +02:00
|
|
|
return QStringList() << tr("decimal") << tr("hexadecimal")
|
|
|
|
|
<< tr("binary") << tr("octal");
|
2009-12-08 16:00:06 +01:00
|
|
|
if (!data.addr.isEmpty())
|
|
|
|
|
return QStringList()
|
|
|
|
|
<< tr("Bald pointer")
|
|
|
|
|
<< tr("Latin1 string")
|
|
|
|
|
<< tr("UTF8 string")
|
|
|
|
|
<< tr("UTF16 string")
|
|
|
|
|
<< tr("UCS4 string");
|
2009-07-03 11:20:47 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TypeFormatRole:
|
2009-11-25 16:32:44 +01:00
|
|
|
return m_handler->m_typeFormats.value(data.type, -1);
|
2009-12-08 16:00:06 +01:00
|
|
|
|
2009-11-25 16:32:44 +01:00
|
|
|
case IndividualFormatRole:
|
|
|
|
|
return m_handler->m_individualFormats.value(data.iname, -1);
|
2009-12-08 16:00:06 +01:00
|
|
|
|
2009-08-12 10:51:25 +02:00
|
|
|
case AddressRole: {
|
|
|
|
|
if (!data.addr.isEmpty())
|
|
|
|
|
return data.addr;
|
|
|
|
|
bool ok;
|
|
|
|
|
(void) data.value.toULongLong(&ok, 0);
|
|
|
|
|
if (ok)
|
|
|
|
|
return data.value;
|
|
|
|
|
return QVariant();
|
|
|
|
|
}
|
2009-07-03 11:20:47 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
default:
|
2010-01-29 21:33:57 +01:00
|
|
|
break;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
return QVariant();
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-17 16:00:03 +02:00
|
|
|
bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
2009-04-07 13:38:19 +02:00
|
|
|
{
|
2009-07-13 09:11:07 +02:00
|
|
|
WatchItem &data = *watchItem(index);
|
2009-06-17 16:00:03 +02:00
|
|
|
if (role == ExpandedRole) {
|
2009-10-20 16:02:35 +02:00
|
|
|
if (value.toBool()) {
|
|
|
|
|
// Should already have been triggered by fetchMore()
|
2009-10-27 17:19:47 +01:00
|
|
|
//QTC_ASSERT(m_handler->m_expandedINames.contains(data.iname), /**/);
|
2009-07-03 11:20:47 +02:00
|
|
|
m_handler->m_expandedINames.insert(data.iname);
|
2009-10-20 16:02:35 +02:00
|
|
|
} else {
|
2009-07-03 11:20:47 +02:00
|
|
|
m_handler->m_expandedINames.remove(data.iname);
|
2009-10-20 16:02:35 +02:00
|
|
|
}
|
2009-07-03 11:20:47 +02:00
|
|
|
} else if (role == TypeFormatRole) {
|
|
|
|
|
m_handler->setFormat(data.type, value.toInt());
|
2010-01-29 21:33:57 +01:00
|
|
|
} else if (role == IndividualFormatRole) {
|
2009-11-25 16:32:44 +01:00
|
|
|
const int format = value.toInt();
|
|
|
|
|
if (format == -1) {
|
|
|
|
|
m_handler->m_individualFormats.remove(data.iname);
|
|
|
|
|
} else {
|
|
|
|
|
m_handler->m_individualFormats[data.iname] = format;
|
|
|
|
|
}
|
2009-06-17 16:00:03 +02:00
|
|
|
}
|
2009-04-07 13:38:19 +02:00
|
|
|
emit dataChanged(index, index);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-17 16:00:03 +02:00
|
|
|
Qt::ItemFlags WatchModel::flags(const QModelIndex &idx) const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
using namespace Qt;
|
|
|
|
|
|
|
|
|
|
if (!idx.isValid())
|
|
|
|
|
return ItemFlags();
|
|
|
|
|
|
|
|
|
|
// enabled, editable, selectable, checkable, and can be used both as the
|
|
|
|
|
// source of a drag and drop operation and as a drop target.
|
|
|
|
|
|
2009-03-19 15:24:18 +01:00
|
|
|
static const ItemFlags notEditable =
|
2008-12-02 12:01:29 +01:00
|
|
|
ItemIsSelectable
|
|
|
|
|
| ItemIsDragEnabled
|
|
|
|
|
| ItemIsDropEnabled
|
|
|
|
|
// | ItemIsUserCheckable
|
|
|
|
|
// | ItemIsTristate
|
|
|
|
|
| ItemIsEnabled;
|
|
|
|
|
|
2009-03-19 15:24:18 +01:00
|
|
|
static const ItemFlags editable = notEditable | ItemIsEditable;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-06-17 16:00:03 +02:00
|
|
|
const WatchData &data = *watchItem(idx);
|
2009-03-19 15:24:18 +01:00
|
|
|
|
|
|
|
|
if (data.isWatcher() && idx.column() == 0)
|
2009-04-03 13:39:14 +02:00
|
|
|
return editable; // watcher names are editable
|
|
|
|
|
if (data.isWatcher() && idx.column() == 2)
|
|
|
|
|
return editable; // watcher types are
|
2009-08-31 09:14:04 +02:00
|
|
|
if (idx.column() == 1 && data.valueEditable)
|
|
|
|
|
return editable; // locals and watcher values are sometimes editable
|
|
|
|
|
return notEditable;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-06-17 16:00:03 +02:00
|
|
|
QVariant WatchModel::headerData(int section, Qt::Orientation orientation, int role) const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
if (orientation == Qt::Vertical)
|
|
|
|
|
return QVariant();
|
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
|
|
|
switch (section) {
|
2009-05-29 10:35:04 +02:00
|
|
|
case 0: return QString(tr("Name") + QLatin1String(" "));
|
|
|
|
|
case 1: return QString(tr("Value") + QLatin1String(" "));
|
|
|
|
|
case 2: return QString(tr("Type") + QLatin1String(" "));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-01-29 21:33:57 +01:00
|
|
|
return QVariant();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
struct IName : public QByteArray
|
2009-06-19 15:53:46 +02:00
|
|
|
{
|
2010-01-05 16:51:55 +01:00
|
|
|
IName(const QByteArray &iname) : QByteArray(iname) {}
|
2009-07-13 12:28:47 +02:00
|
|
|
};
|
|
|
|
|
|
2009-08-31 09:14:04 +02:00
|
|
|
bool iNameLess(const QString &iname1, const QString &iname2)
|
2009-07-13 12:28:47 +02:00
|
|
|
{
|
|
|
|
|
QString name1 = iname1.section('.', -1);
|
|
|
|
|
QString name2 = iname2.section('.', -1);
|
2009-07-13 09:11:07 +02:00
|
|
|
if (!name1.isEmpty() && !name2.isEmpty()) {
|
2009-08-27 16:07:35 +02:00
|
|
|
if (name1.at(0).isDigit() && name2.at(0).isDigit()) {
|
|
|
|
|
bool ok1 = false, ok2 = false;
|
|
|
|
|
int i1 = name1.toInt(&ok1), i2 = name2.toInt(&ok2);
|
|
|
|
|
if (ok1 && ok2)
|
|
|
|
|
return i1 < i2;
|
|
|
|
|
}
|
2009-06-19 15:53:46 +02:00
|
|
|
}
|
2010-01-29 21:33:57 +01:00
|
|
|
return name1 < name2;
|
2009-06-19 15:53:46 +02:00
|
|
|
}
|
|
|
|
|
|
2009-08-31 09:14:04 +02:00
|
|
|
bool operator<(const IName &iname1, const IName &iname2)
|
|
|
|
|
{
|
|
|
|
|
return iNameLess(iname1, iname2);
|
|
|
|
|
}
|
2009-07-13 12:28:47 +02:00
|
|
|
|
|
|
|
|
static bool iNameSorter(const WatchItem *item1, const WatchItem *item2)
|
|
|
|
|
{
|
2009-08-31 09:14:04 +02:00
|
|
|
return iNameLess(item1->iname, item2->iname);
|
2009-07-13 12:28:47 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
static int findInsertPosition(const QList<WatchItem *> &list, const WatchItem *item)
|
2009-06-19 15:53:46 +02:00
|
|
|
{
|
2009-07-13 09:11:07 +02:00
|
|
|
QList<WatchItem *>::const_iterator it =
|
|
|
|
|
qLowerBound(list.begin(), list.end(), item, iNameSorter);
|
2010-01-29 21:33:57 +01:00
|
|
|
return it - list.begin();
|
2009-06-19 15:53:46 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
void WatchModel::insertData(const WatchData &data)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-10-14 09:41:14 +02:00
|
|
|
//qDebug() << "WMI:" << data.toString();
|
2009-08-31 09:14:04 +02:00
|
|
|
//static int bulk = 0;
|
2009-08-28 17:31:45 +02:00
|
|
|
//qDebug() << "SINGLE: " << ++bulk << data.toString();
|
2009-10-14 09:41:14 +02:00
|
|
|
if (data.iname.isEmpty()) {
|
|
|
|
|
int x;
|
|
|
|
|
x = 1;
|
|
|
|
|
}
|
|
|
|
|
QTC_ASSERT(!data.iname.isEmpty(), qDebug() << data.toString(); return);
|
2009-07-13 09:11:07 +02:00
|
|
|
WatchItem *parent = findItem(parentName(data.iname), m_root);
|
|
|
|
|
if (!parent) {
|
|
|
|
|
WatchData parent;
|
|
|
|
|
parent.iname = parentName(data.iname);
|
2009-10-14 09:41:14 +02:00
|
|
|
MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << data.iname);
|
|
|
|
|
if (!parent.iname.isEmpty())
|
|
|
|
|
insertData(parent);
|
2009-06-17 16:00:03 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2009-07-13 09:11:07 +02:00
|
|
|
QModelIndex index = watchIndex(parent);
|
|
|
|
|
if (WatchItem *oldItem = findItem(data.iname, parent)) {
|
|
|
|
|
// overwrite old entry
|
|
|
|
|
//MODEL_DEBUG("OVERWRITE : " << data.iname << data.value);
|
|
|
|
|
bool changed = !data.value.isEmpty()
|
|
|
|
|
&& data.value != oldItem->value
|
|
|
|
|
&& data.value != strNotInScope;
|
|
|
|
|
oldItem->setData(data);
|
|
|
|
|
oldItem->changed = changed;
|
|
|
|
|
oldItem->generation = generationCounter;
|
|
|
|
|
QModelIndex idx = watchIndex(oldItem);
|
|
|
|
|
emit dataChanged(idx, idx.sibling(idx.row(), 2));
|
|
|
|
|
} else {
|
|
|
|
|
// add new entry
|
2009-07-13 11:25:05 +02:00
|
|
|
//MODEL_DEBUG("ADD : " << data.iname << data.value);
|
2009-07-13 09:11:07 +02:00
|
|
|
WatchItem *item = new WatchItem(data);
|
|
|
|
|
item->parent = parent;
|
|
|
|
|
item->generation = generationCounter;
|
|
|
|
|
item->changed = true;
|
|
|
|
|
int n = findInsertPosition(parent->children, item);
|
|
|
|
|
beginInsertRows(index, n, n);
|
|
|
|
|
parent->children.insert(n, item);
|
|
|
|
|
endInsertRows();
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-07-13 12:28:47 +02:00
|
|
|
void WatchModel::insertBulkData(const QList<WatchData> &list)
|
|
|
|
|
{
|
2009-10-01 18:01:23 +02:00
|
|
|
#if 0
|
2010-01-29 21:33:57 +01:00
|
|
|
for (int i = 0; i != list.size(); ++i)
|
2009-08-31 09:14:04 +02:00
|
|
|
insertData(list.at(i));
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
|
|
|
|
// This method does not properly insert items in proper "iname sort
|
|
|
|
|
// order", leading to random removal of items in removeOutDated();
|
|
|
|
|
|
2009-08-28 14:34:08 +02:00
|
|
|
//qDebug() << "WMI:" << list.toString();
|
2009-08-31 09:14:04 +02:00
|
|
|
//static int bulk = 0;
|
2009-08-28 14:34:08 +02:00
|
|
|
//foreach (const WatchItem &data, list)
|
2009-08-28 17:31:45 +02:00
|
|
|
// qDebug() << "BULK: " << ++bulk << data.toString();
|
2009-07-13 12:28:47 +02:00
|
|
|
QTC_ASSERT(!list.isEmpty(), return);
|
2010-01-05 16:51:55 +01:00
|
|
|
QByteArray parentIName = parentName(list.at(0).iname);
|
2009-07-13 12:28:47 +02:00
|
|
|
WatchItem *parent = findItem(parentIName, m_root);
|
|
|
|
|
if (!parent) {
|
|
|
|
|
WatchData parent;
|
|
|
|
|
parent.iname = parentIName;
|
|
|
|
|
insertData(parent);
|
|
|
|
|
MODEL_DEBUG("\nFIXING MISSING PARENT FOR\n" << list.at(0).iname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QModelIndex index = watchIndex(parent);
|
|
|
|
|
|
|
|
|
|
QMap<IName, WatchData> newList;
|
|
|
|
|
typedef QMap<IName, WatchData>::iterator Iterator;
|
|
|
|
|
foreach (const WatchItem &data, list)
|
|
|
|
|
newList[data.iname] = data;
|
2009-08-26 17:29:22 +02:00
|
|
|
if (newList.size() != list.size()) {
|
|
|
|
|
qDebug() << "LIST: ";
|
|
|
|
|
foreach (const WatchItem &data, list)
|
|
|
|
|
qDebug() << data.toString();
|
|
|
|
|
qDebug() << "NEW LIST: ";
|
2009-12-09 14:07:04 +01:00
|
|
|
foreach (const WatchItem &data, newList)
|
2009-08-26 17:29:22 +02:00
|
|
|
qDebug() << data.toString();
|
|
|
|
|
qDebug() << "P->CHILDREN: ";
|
|
|
|
|
foreach (const WatchItem *item, parent->children)
|
|
|
|
|
qDebug() << item->toString();
|
|
|
|
|
qDebug()
|
|
|
|
|
<< "P->CHILDREN.SIZE: " << parent->children.size()
|
|
|
|
|
<< "NEWLIST SIZE: " << newList.size()
|
|
|
|
|
<< "LIST SIZE: " << list.size();
|
|
|
|
|
}
|
|
|
|
|
QTC_ASSERT(newList.size() == list.size(), return);
|
2009-07-13 12:28:47 +02:00
|
|
|
|
|
|
|
|
foreach (WatchItem *oldItem, parent->children) {
|
|
|
|
|
Iterator it = newList.find(oldItem->iname);
|
|
|
|
|
if (it == newList.end()) {
|
2009-08-31 09:14:04 +02:00
|
|
|
WatchData data = *oldItem;
|
|
|
|
|
data.generation = generationCounter;
|
|
|
|
|
newList[oldItem->iname] = data;
|
2009-07-13 12:28:47 +02:00
|
|
|
} else {
|
|
|
|
|
bool changed = !it->value.isEmpty()
|
|
|
|
|
&& it->value != oldItem->value
|
|
|
|
|
&& it->value != strNotInScope;
|
|
|
|
|
it->changed = changed;
|
2009-08-31 09:14:04 +02:00
|
|
|
if (it->generation == -1)
|
|
|
|
|
it->generation = generationCounter;
|
2009-07-13 12:28:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-31 09:14:04 +02:00
|
|
|
for (Iterator it = newList.begin(); it != newList.end(); ++it) {
|
|
|
|
|
qDebug() << " NEW: " << it->iname;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-13 12:28:47 +02:00
|
|
|
// overwrite existing items
|
|
|
|
|
Iterator it = newList.begin();
|
|
|
|
|
QModelIndex idx = watchIndex(parent);
|
2009-08-31 09:14:04 +02:00
|
|
|
const int oldCount = parent->children.size();
|
|
|
|
|
for (int i = 0; i < oldCount; ++i, ++it) {
|
|
|
|
|
if (!parent->children[i]->isEqual(*it)) {
|
|
|
|
|
qDebug() << "REPLACING" << parent->children.at(i)->iname
|
|
|
|
|
<< " WITH " << it->iname << it->generation;
|
|
|
|
|
parent->children[i]->setData(*it);
|
|
|
|
|
if (parent->children[i]->generation == -1)
|
|
|
|
|
parent->children[i]->generation = generationCounter;
|
|
|
|
|
//emit dataChanged(idx.sibling(i, 0), idx.sibling(i, 2));
|
|
|
|
|
} else {
|
|
|
|
|
//qDebug() << "SKIPPING REPLACEMENT" << parent->children.at(i)->iname;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-13 12:28:47 +02:00
|
|
|
emit dataChanged(idx.sibling(0, 0), idx.sibling(oldCount - 1, 2));
|
|
|
|
|
|
|
|
|
|
// add new items
|
|
|
|
|
if (oldCount < newList.size()) {
|
|
|
|
|
beginInsertRows(index, oldCount, newList.size() - 1);
|
|
|
|
|
//MODEL_DEBUG("INSERT : " << data.iname << data.value);
|
|
|
|
|
for (int i = oldCount; i < newList.size(); ++i, ++it) {
|
|
|
|
|
WatchItem *item = new WatchItem(*it);
|
2009-08-31 09:14:04 +02:00
|
|
|
qDebug() << "ADDING" << it->iname;
|
2009-07-13 12:28:47 +02:00
|
|
|
item->parent = parent;
|
2009-08-31 09:14:04 +02:00
|
|
|
if (item->generation == -1)
|
|
|
|
|
item->generation = generationCounter;
|
2009-07-13 12:28:47 +02:00
|
|
|
item->changed = true;
|
|
|
|
|
parent->children.append(item);
|
|
|
|
|
}
|
|
|
|
|
endInsertRows();
|
|
|
|
|
}
|
2009-08-31 09:14:04 +02:00
|
|
|
//qDebug() << "ITEMS: " << parent->children.size();
|
|
|
|
|
dump();
|
2009-07-13 12:28:47 +02:00
|
|
|
}
|
|
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
WatchItem *WatchModel::findItem(const QByteArray &iname, WatchItem *root) const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-07-13 09:11:07 +02:00
|
|
|
if (root->iname == iname)
|
2009-06-17 16:00:03 +02:00
|
|
|
return root;
|
|
|
|
|
for (int i = root->children.size(); --i >= 0; )
|
2009-07-13 09:11:07 +02:00
|
|
|
if (WatchItem *item = findItem(iname, root->children.at(i)))
|
2009-06-17 16:00:03 +02:00
|
|
|
return item;
|
2008-12-02 12:01:29 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-12 11:21:44 +02:00
|
|
|
static void debugRecursion(QDebug &d, const WatchItem *item, int depth)
|
|
|
|
|
{
|
|
|
|
|
d << QString(2 * depth, QLatin1Char(' ')) << item->toString() << '\n';
|
|
|
|
|
foreach(const WatchItem *i, item->children)
|
|
|
|
|
debugRecursion(d, i, depth + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDebug operator<<(QDebug d, const WatchModel &m)
|
|
|
|
|
{
|
|
|
|
|
QDebug nospace = d.nospace();
|
|
|
|
|
if (m.m_root)
|
|
|
|
|
debugRecursion(nospace, m.m_root, 0);
|
|
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-06-17 16:00:03 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// WatchHandler
|
|
|
|
|
//
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2009-10-01 18:01:23 +02:00
|
|
|
WatchHandler::WatchHandler(DebuggerManager *manager)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-10-01 18:01:23 +02:00
|
|
|
m_manager = manager;
|
2009-07-13 09:11:07 +02:00
|
|
|
m_expandPointers = true;
|
|
|
|
|
m_inChange = false;
|
|
|
|
|
|
|
|
|
|
m_locals = new WatchModel(this, LocalsWatch);
|
|
|
|
|
m_watchers = new WatchModel(this, WatchersWatch);
|
|
|
|
|
m_tooltips = new WatchModel(this, TooltipsWatch);
|
|
|
|
|
|
2009-06-17 16:00:03 +02:00
|
|
|
connect(theDebuggerAction(WatchExpression),
|
|
|
|
|
SIGNAL(triggered()), this, SLOT(watchExpression()));
|
|
|
|
|
connect(theDebuggerAction(RemoveWatchExpression),
|
|
|
|
|
SIGNAL(triggered()), this, SLOT(removeWatchExpression()));
|
2009-11-25 08:35:02 +01:00
|
|
|
connect(theDebuggerAction(ShowStdNamespace),
|
|
|
|
|
SIGNAL(triggered()), this, SLOT(emitAllChanged()));
|
|
|
|
|
connect(theDebuggerAction(ShowQtNamespace),
|
|
|
|
|
SIGNAL(triggered()), this, SLOT(emitAllChanged()));
|
2009-07-13 09:11:07 +02:00
|
|
|
}
|
|
|
|
|
|
2010-02-04 16:23:51 +01:00
|
|
|
void WatchHandler::beginCycle(bool clearFetchTriggered)
|
2009-08-31 09:14:04 +02:00
|
|
|
{
|
|
|
|
|
++generationCounter;
|
2010-02-04 16:23:51 +01:00
|
|
|
m_locals->beginCycle(clearFetchTriggered);
|
|
|
|
|
m_watchers->beginCycle(clearFetchTriggered);
|
|
|
|
|
m_tooltips->beginCycle(clearFetchTriggered);
|
2009-08-31 09:14:04 +02:00
|
|
|
}
|
|
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
void WatchHandler::endCycle()
|
|
|
|
|
{
|
2009-08-31 09:14:04 +02:00
|
|
|
m_locals->endCycle();
|
|
|
|
|
m_watchers->endCycle();
|
|
|
|
|
m_tooltips->endCycle();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchHandler::cleanup()
|
|
|
|
|
{
|
|
|
|
|
m_expandedINames.clear();
|
|
|
|
|
m_displayedINames.clear();
|
2009-07-13 09:11:07 +02:00
|
|
|
m_locals->reinitialize();
|
|
|
|
|
m_tooltips->reinitialize();
|
2010-02-25 17:56:00 +01:00
|
|
|
m_locals->m_fetchTriggered.clear();
|
|
|
|
|
m_watchers->m_fetchTriggered.clear();
|
|
|
|
|
m_tooltips->m_fetchTriggered.clear();
|
2008-12-02 12:01:29 +01:00
|
|
|
#if 0
|
|
|
|
|
for (EditWindows::ConstIterator it = m_editWindows.begin();
|
|
|
|
|
it != m_editWindows.end(); ++it) {
|
|
|
|
|
if (!it.value().isNull())
|
|
|
|
|
delete it.value();
|
|
|
|
|
}
|
|
|
|
|
m_editWindows.clear();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-25 08:35:02 +01:00
|
|
|
void WatchHandler::emitAllChanged()
|
|
|
|
|
{
|
|
|
|
|
m_locals->emitAllChanged();
|
|
|
|
|
m_watchers->emitAllChanged();
|
|
|
|
|
m_tooltips->emitAllChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-13 09:11:07 +02:00
|
|
|
void WatchHandler::insertData(const WatchData &data)
|
|
|
|
|
{
|
|
|
|
|
MODEL_DEBUG("INSERTDATA: " << data.toString());
|
2009-10-13 14:16:50 +02:00
|
|
|
if (!data.isValid()) {
|
2010-02-11 13:21:38 +01:00
|
|
|
qWarning("%s:%d: Attempt to insert invalid watch item: %s",
|
|
|
|
|
__FILE__, __LINE__, qPrintable(data.toString()));
|
2009-10-13 14:16:50 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2009-10-16 10:28:03 +02:00
|
|
|
if (data.isSomethingNeeded() && data.iname.contains('.')) {
|
2009-10-28 14:35:36 +01:00
|
|
|
MODEL_DEBUG("SOMETHING NEEDED: " << data.toString());
|
2009-12-04 12:07:59 +01:00
|
|
|
IDebuggerEngine *engine = m_manager->currentEngine();
|
|
|
|
|
if (engine && !engine->isSynchroneous()) {
|
2009-10-28 11:50:58 +01:00
|
|
|
m_manager->updateWatchData(data);
|
|
|
|
|
} else {
|
|
|
|
|
qDebug() << "ENDLESS LOOP: SOMETHING NEEDED: " << data.toString();
|
|
|
|
|
WatchData data1 = data;
|
|
|
|
|
data1.setAllUnneeded();
|
|
|
|
|
data1.setValue(QLatin1String("<unavailable synchroneous data>"));
|
|
|
|
|
data1.setHasChildren(false);
|
|
|
|
|
WatchModel *model = modelForIName(data.iname);
|
2009-10-28 15:39:07 +01:00
|
|
|
QTC_ASSERT(model, return);
|
2009-10-28 11:50:58 +01:00
|
|
|
model->insertData(data1);
|
|
|
|
|
}
|
2009-07-13 09:11:07 +02:00
|
|
|
} else {
|
|
|
|
|
WatchModel *model = modelForIName(data.iname);
|
|
|
|
|
QTC_ASSERT(model, return);
|
2009-10-14 09:41:14 +02:00
|
|
|
MODEL_DEBUG("NOTHING NEEDED: " << data.toString());
|
2009-07-13 09:11:07 +02:00
|
|
|
model->insertData(data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-01 18:01:23 +02:00
|
|
|
// Bulk-insertion
|
2009-07-13 12:28:47 +02:00
|
|
|
void WatchHandler::insertBulkData(const QList<WatchData> &list)
|
|
|
|
|
{
|
2009-10-01 18:01:23 +02:00
|
|
|
#if 1
|
2009-08-28 17:31:45 +02:00
|
|
|
foreach (const WatchItem &data, list)
|
|
|
|
|
insertData(data);
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-07-13 12:28:47 +02:00
|
|
|
if (list.isEmpty())
|
|
|
|
|
return;
|
2010-01-05 16:51:55 +01:00
|
|
|
QMap<QByteArray, QList<WatchData> > hash;
|
2009-07-13 12:28:47 +02:00
|
|
|
|
|
|
|
|
foreach (const WatchData &data, list) {
|
2009-08-31 09:14:04 +02:00
|
|
|
// we insert everything, including incomplete stuff
|
|
|
|
|
// to reduce the number of row add operations in the model.
|
2009-10-13 14:16:50 +02:00
|
|
|
if (data.isValid()) {
|
|
|
|
|
hash[parentName(data.iname)].append(data);
|
|
|
|
|
} else {
|
2010-02-11 13:21:38 +01:00
|
|
|
qWarning("%s:%d: Attempt to bulk-insert invalid watch item: %s",
|
|
|
|
|
__FILE__, __LINE__, qPrintable(data.toString()));
|
2009-10-13 14:16:50 +02:00
|
|
|
}
|
2009-07-13 12:28:47 +02:00
|
|
|
}
|
2010-01-05 16:51:55 +01:00
|
|
|
foreach (const QByteArray &parentIName, hash.keys()) {
|
2009-07-13 12:28:47 +02:00
|
|
|
WatchModel *model = modelForIName(parentIName);
|
|
|
|
|
QTC_ASSERT(model, return);
|
|
|
|
|
model->insertBulkData(hash[parentIName]);
|
|
|
|
|
}
|
2009-08-31 09:14:04 +02:00
|
|
|
|
|
|
|
|
foreach (const WatchData &data, list) {
|
|
|
|
|
if (data.isSomethingNeeded())
|
2009-10-01 18:01:23 +02:00
|
|
|
m_manager->updateWatchData(data);
|
2009-08-31 09:14:04 +02:00
|
|
|
}
|
2009-07-13 12:28:47 +02:00
|
|
|
}
|
|
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
void WatchHandler::removeData(const QByteArray &iname)
|
2009-06-24 12:31:09 +02:00
|
|
|
{
|
|
|
|
|
WatchModel *model = modelForIName(iname);
|
|
|
|
|
if (!model)
|
|
|
|
|
return;
|
2009-07-13 09:11:07 +02:00
|
|
|
WatchItem *item = model->findItem(iname, model->m_root);
|
|
|
|
|
if (item)
|
2009-11-04 17:11:35 +01:00
|
|
|
model->destroyItem(item);
|
2009-06-24 12:31:09 +02:00
|
|
|
}
|
|
|
|
|
|
2009-03-19 12:53:58 +01:00
|
|
|
void WatchHandler::watchExpression()
|
|
|
|
|
{
|
|
|
|
|
if (QAction *action = qobject_cast<QAction *>(sender()))
|
|
|
|
|
watchExpression(action->data().toString());
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
QByteArray WatchHandler::watcherName(const QByteArray &exp)
|
2009-04-15 10:06:31 +02:00
|
|
|
{
|
2010-01-05 16:51:55 +01:00
|
|
|
return "watch." + QByteArray::number(m_watcherNames[exp]);
|
2009-04-15 10:06:31 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
void WatchHandler::watchExpression(const QString &exp)
|
|
|
|
|
{
|
|
|
|
|
// FIXME: 'exp' can contain illegal characters
|
|
|
|
|
WatchData data;
|
2010-01-05 16:51:55 +01:00
|
|
|
data.exp = exp.toLatin1();
|
2008-12-02 12:01:29 +01:00
|
|
|
data.name = exp;
|
2010-01-05 16:51:55 +01:00
|
|
|
m_watcherNames[data.exp] = watcherCounter++;
|
2009-06-23 13:43:23 +02:00
|
|
|
if (exp.isEmpty() || exp == watcherEditPlaceHolder())
|
2009-06-18 10:34:53 +02:00
|
|
|
data.setAllUnneeded();
|
2010-01-05 16:51:55 +01:00
|
|
|
data.iname = watcherName(data.exp);
|
2009-12-04 13:36:14 +01:00
|
|
|
IDebuggerEngine *engine = m_manager->currentEngine();
|
|
|
|
|
if (engine && engine->isSynchroneous())
|
|
|
|
|
m_manager->updateWatchData(data);
|
|
|
|
|
else
|
|
|
|
|
insertData(data);
|
|
|
|
|
m_manager->updateWatchData(data);
|
2008-12-17 17:43:01 +01:00
|
|
|
saveWatchers();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchHandler::setDisplayedIName(const QString &iname, bool on)
|
|
|
|
|
{
|
2009-07-13 17:35:17 +02:00
|
|
|
Q_UNUSED(iname)
|
|
|
|
|
Q_UNUSED(on)
|
2009-06-17 16:00:03 +02:00
|
|
|
/*
|
2008-12-02 12:01:29 +01:00
|
|
|
WatchData *d = findData(iname);
|
|
|
|
|
if (!on || !d) {
|
|
|
|
|
delete m_editWindows.take(iname);
|
|
|
|
|
m_displayedINames.remove(iname);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (d->exp.isEmpty()) {
|
|
|
|
|
//emit statusMessageRequested(tr("Sorry. Cannot visualize objects without known address."), 5000);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
d->setValueNeeded();
|
|
|
|
|
m_displayedINames.insert(iname);
|
|
|
|
|
insertData(*d);
|
2009-06-17 16:00:03 +02:00
|
|
|
*/
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchHandler::showEditValue(const WatchData &data)
|
|
|
|
|
{
|
|
|
|
|
// editvalue is always base64 encoded
|
|
|
|
|
QByteArray ba = QByteArray::fromBase64(data.editvalue);
|
|
|
|
|
//QByteArray ba = data.editvalue;
|
|
|
|
|
QWidget *w = m_editWindows.value(data.iname);
|
|
|
|
|
qDebug() << "SHOW_EDIT_VALUE " << data.toString() << data.type
|
|
|
|
|
<< data.iname << w;
|
2009-03-25 17:33:49 +01:00
|
|
|
if (data.type == QLatin1String("QImage")) {
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!w) {
|
|
|
|
|
w = new QLabel;
|
|
|
|
|
m_editWindows[data.iname] = w;
|
|
|
|
|
}
|
|
|
|
|
QDataStream ds(&ba, QIODevice::ReadOnly);
|
|
|
|
|
QVariant v;
|
|
|
|
|
ds >> v;
|
|
|
|
|
QString type = QString::fromAscii(v.typeName());
|
|
|
|
|
QImage im = v.value<QImage>();
|
|
|
|
|
if (QLabel *l = qobject_cast<QLabel *>(w))
|
|
|
|
|
l->setPixmap(QPixmap::fromImage(im));
|
2009-03-25 17:33:49 +01:00
|
|
|
} else if (data.type == QLatin1String("QPixmap")) {
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!w) {
|
|
|
|
|
w = new QLabel;
|
|
|
|
|
m_editWindows[data.iname] = w;
|
|
|
|
|
}
|
|
|
|
|
QDataStream ds(&ba, QIODevice::ReadOnly);
|
|
|
|
|
QVariant v;
|
|
|
|
|
ds >> v;
|
|
|
|
|
QString type = QString::fromAscii(v.typeName());
|
|
|
|
|
QPixmap im = v.value<QPixmap>();
|
|
|
|
|
if (QLabel *l = qobject_cast<QLabel *>(w))
|
|
|
|
|
l->setPixmap(im);
|
2009-03-25 17:33:49 +01:00
|
|
|
} else if (data.type == QLatin1String("QString")) {
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!w) {
|
|
|
|
|
w = new QTextEdit;
|
|
|
|
|
m_editWindows[data.iname] = w;
|
|
|
|
|
}
|
|
|
|
|
#if 0
|
|
|
|
|
QDataStream ds(&ba, QIODevice::ReadOnly);
|
|
|
|
|
QVariant v;
|
|
|
|
|
ds >> v;
|
|
|
|
|
QString type = QString::fromAscii(v.typeName());
|
|
|
|
|
QString str = v.value<QString>();
|
|
|
|
|
#else
|
|
|
|
|
MODEL_DEBUG("DATA: " << ba);
|
|
|
|
|
QString str = QString::fromUtf16((ushort *)ba.constData(), ba.size()/2);
|
|
|
|
|
#endif
|
|
|
|
|
if (QTextEdit *t = qobject_cast<QTextEdit *>(w))
|
|
|
|
|
t->setText(str);
|
|
|
|
|
}
|
|
|
|
|
if (w)
|
|
|
|
|
w->show();
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-19 12:53:58 +01:00
|
|
|
void WatchHandler::removeWatchExpression()
|
|
|
|
|
{
|
|
|
|
|
if (QAction *action = qobject_cast<QAction *>(sender()))
|
|
|
|
|
removeWatchExpression(action->data().toString());
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
void WatchHandler::removeWatchExpression(const QString &exp0)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-01-05 16:51:55 +01:00
|
|
|
QByteArray exp = exp0.toLatin1();
|
2009-03-04 10:11:42 +01:00
|
|
|
MODEL_DEBUG("REMOVE WATCH: " << exp);
|
2009-06-17 16:00:03 +02:00
|
|
|
m_watcherNames.remove(exp);
|
2009-07-13 11:25:05 +02:00
|
|
|
foreach (WatchItem *item, m_watchers->rootItem()->children) {
|
2009-07-13 09:11:07 +02:00
|
|
|
if (item->exp == exp) {
|
2009-11-04 17:11:35 +01:00
|
|
|
m_watchers->destroyItem(item);
|
2009-06-17 16:00:03 +02:00
|
|
|
saveWatchers();
|
2009-07-13 09:11:07 +02:00
|
|
|
break;
|
2008-12-17 18:02:08 +01:00
|
|
|
}
|
2009-07-13 09:11:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchHandler::updateWatchers()
|
|
|
|
|
{
|
|
|
|
|
//qDebug() << "UPDATE WATCHERS";
|
|
|
|
|
// copy over all watchers and mark all watchers as incomplete
|
2010-01-05 16:51:55 +01:00
|
|
|
foreach (const QByteArray &exp, m_watcherNames.keys()) {
|
2009-07-13 09:11:07 +02:00
|
|
|
WatchData data;
|
|
|
|
|
data.iname = watcherName(exp);
|
|
|
|
|
data.setAllNeeded();
|
|
|
|
|
data.name = exp;
|
|
|
|
|
data.exp = exp;
|
|
|
|
|
insertData(data);
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2008-12-17 17:43:01 +01:00
|
|
|
|
|
|
|
|
void WatchHandler::loadWatchers()
|
|
|
|
|
{
|
2009-10-01 18:01:23 +02:00
|
|
|
QVariant value = m_manager->sessionValue("Watchers");
|
2009-04-15 10:06:31 +02:00
|
|
|
foreach (const QString &exp, value.toStringList())
|
2010-01-05 16:51:55 +01:00
|
|
|
m_watcherNames[exp.toLatin1()] = watcherCounter++;
|
2009-07-03 11:20:47 +02:00
|
|
|
|
2008-12-19 15:02:09 +01:00
|
|
|
//qDebug() << "LOAD WATCHERS: " << m_watchers;
|
2009-06-17 16:00:03 +02:00
|
|
|
//reinitializeWatchersHelper();
|
2008-12-17 17:43:01 +01:00
|
|
|
}
|
|
|
|
|
|
2009-12-04 13:22:39 +01:00
|
|
|
QStringList WatchHandler::watchedExpressions() const
|
2008-12-17 17:43:01 +01:00
|
|
|
{
|
2009-12-04 13:22:39 +01:00
|
|
|
// Filter out invalid watchers.
|
2009-06-23 13:43:23 +02:00
|
|
|
QStringList watcherNames;
|
2010-01-05 16:51:55 +01:00
|
|
|
QHashIterator<QByteArray, int> it(m_watcherNames);
|
2009-07-03 11:20:47 +02:00
|
|
|
while (it.hasNext()) {
|
|
|
|
|
it.next();
|
2009-06-23 13:43:23 +02:00
|
|
|
const QString &watcherName = it.key();
|
|
|
|
|
if (!watcherName.isEmpty() && watcherName != watcherEditPlaceHolder())
|
|
|
|
|
watcherNames.push_back(watcherName);
|
|
|
|
|
}
|
2009-12-04 13:22:39 +01:00
|
|
|
return watcherNames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchHandler::saveWatchers()
|
|
|
|
|
{
|
|
|
|
|
//qDebug() << "SAVE WATCHERS: " << m_watchers;
|
|
|
|
|
m_manager->setSessionValue("Watchers", QVariant(watchedExpressions()));
|
2008-12-17 17:43:01 +01:00
|
|
|
}
|
|
|
|
|
|
2009-07-03 11:20:47 +02:00
|
|
|
void WatchHandler::loadTypeFormats()
|
|
|
|
|
{
|
2009-10-01 18:01:23 +02:00
|
|
|
QVariant value = m_manager->sessionValue("DefaultFormats");
|
2009-07-03 11:20:47 +02:00
|
|
|
QMap<QString, QVariant> typeFormats = value.toMap();
|
|
|
|
|
QMapIterator<QString, QVariant> it(typeFormats);
|
|
|
|
|
while (it.hasNext()) {
|
|
|
|
|
it.next();
|
|
|
|
|
if (!it.key().isEmpty())
|
|
|
|
|
m_typeFormats.insert(it.key(), it.value().toInt());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchHandler::saveTypeFormats()
|
|
|
|
|
{
|
|
|
|
|
QMap<QString, QVariant> typeFormats;
|
|
|
|
|
QHashIterator<QString, int> it(m_typeFormats);
|
|
|
|
|
while (it.hasNext()) {
|
|
|
|
|
it.next();
|
2009-11-25 16:32:44 +01:00
|
|
|
const int format = it.value();
|
|
|
|
|
if (format != DecimalFormat) {
|
|
|
|
|
const QString key = it.key().trimmed();
|
|
|
|
|
if (!key.isEmpty())
|
|
|
|
|
typeFormats.insert(key, format);
|
|
|
|
|
}
|
2009-07-03 11:20:47 +02:00
|
|
|
}
|
2009-10-01 18:01:23 +02:00
|
|
|
m_manager->setSessionValue("DefaultFormats", QVariant(typeFormats));
|
2009-07-03 11:20:47 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-17 17:43:01 +01:00
|
|
|
void WatchHandler::saveSessionData()
|
|
|
|
|
{
|
|
|
|
|
saveWatchers();
|
2009-07-03 11:20:47 +02:00
|
|
|
saveTypeFormats();
|
2008-12-17 17:43:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WatchHandler::loadSessionData()
|
|
|
|
|
{
|
|
|
|
|
loadWatchers();
|
2009-07-03 11:20:47 +02:00
|
|
|
loadTypeFormats();
|
2010-01-05 16:51:55 +01:00
|
|
|
foreach (const QByteArray &exp, m_watcherNames.keys()) {
|
2009-06-17 16:00:03 +02:00
|
|
|
WatchData data;
|
|
|
|
|
data.iname = watcherName(exp);
|
|
|
|
|
data.setAllUnneeded();
|
|
|
|
|
data.name = exp;
|
|
|
|
|
data.exp = exp;
|
2009-07-13 09:11:07 +02:00
|
|
|
insertData(data);
|
2009-06-17 16:00:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WatchModel *WatchHandler::model(WatchType type) const
|
|
|
|
|
{
|
2009-07-13 09:11:07 +02:00
|
|
|
switch (type) {
|
|
|
|
|
case LocalsWatch: return m_locals;
|
|
|
|
|
case WatchersWatch: return m_watchers;
|
|
|
|
|
case TooltipsWatch: return m_tooltips;
|
|
|
|
|
}
|
|
|
|
|
QTC_ASSERT(false, /**/);
|
|
|
|
|
return 0;
|
2009-07-10 14:36:28 +02:00
|
|
|
}
|
2010-01-29 21:33:57 +01:00
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
WatchModel *WatchHandler::modelForIName(const QByteArray &iname) const
|
2009-06-17 16:00:03 +02:00
|
|
|
{
|
2010-01-05 16:51:55 +01:00
|
|
|
if (iname.startsWith("local"))
|
2009-07-13 09:11:07 +02:00
|
|
|
return m_locals;
|
2010-01-05 16:51:55 +01:00
|
|
|
if (iname.startsWith("tooltip"))
|
2009-07-13 09:11:07 +02:00
|
|
|
return m_tooltips;
|
2010-01-08 18:16:16 +01:00
|
|
|
if (iname.startsWith("watch"))
|
|
|
|
|
return m_watchers;
|
2009-10-14 09:41:14 +02:00
|
|
|
QTC_ASSERT(false, qDebug() << "INAME: " << iname);
|
2009-07-13 09:11:07 +02:00
|
|
|
return 0;
|
2008-12-17 17:43:01 +01:00
|
|
|
}
|
2009-06-17 16:00:03 +02:00
|
|
|
|
2010-01-05 16:51:55 +01:00
|
|
|
WatchData *WatchHandler::findItem(const QByteArray &iname) const
|
2009-06-17 16:00:03 +02:00
|
|
|
{
|
|
|
|
|
const WatchModel *model = modelForIName(iname);
|
|
|
|
|
QTC_ASSERT(model, return 0);
|
2009-07-13 09:11:07 +02:00
|
|
|
return model->findItem(iname, model->m_root);
|
2009-06-17 16:00:03 +02:00
|
|
|
}
|
|
|
|
|
|
2009-06-18 10:34:53 +02:00
|
|
|
QString WatchHandler::watcherEditPlaceHolder()
|
|
|
|
|
{
|
|
|
|
|
static const QString rc = tr("<Edit>");
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
2009-06-24 12:31:09 +02:00
|
|
|
|
2009-07-03 11:20:47 +02:00
|
|
|
void WatchHandler::setFormat(const QString &type, int format)
|
|
|
|
|
{
|
|
|
|
|
m_typeFormats[type] = format;
|
|
|
|
|
saveTypeFormats();
|
2009-07-13 09:11:07 +02:00
|
|
|
m_locals->emitDataChanged(1);
|
|
|
|
|
m_watchers->emitDataChanged(1);
|
|
|
|
|
m_tooltips->emitDataChanged(1);
|
2009-07-03 11:20:47 +02:00
|
|
|
}
|
|
|
|
|
|
2009-06-17 16:00:03 +02:00
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Debugger
|