Debugger: Rework display type selection

The previous index based way was getting too brittle, use enums instead.
Also add a switch between exponential and flat display for floating
point types.

Task-number: QTCREATORBUG-12050
Change-Id: I86addbac5a80e8b79b176c6107b251b466503fe7
Reviewed-by: David Schulz <david.schulz@digia.com>
Reviewed-by: Christian Stenger <christian.stenger@digia.com>
This commit is contained in:
hjk
2014-04-15 18:13:03 +02:00
parent 81274c0624
commit 3f0d02ad7f
8 changed files with 432 additions and 242 deletions

View File

@@ -42,6 +42,19 @@ else:
verbosity = 0 verbosity = 0
verbosity = 1 verbosity = 1
# Known special formats. Keep in sync with DisplayFormat in watchhandler.h
KnownDumperFormatBase, \
Latin1StringFormat, \
Utf8StringFormat, \
Local8BitStringFormat, \
Utf16StringFormat, \
Ucs4StringFormat, \
Array10Format, \
Array100Format, \
Array1000Format, \
Array10000Format \
= range(100, 110)
def hasPlot(): def hasPlot():
fileName = "/usr/bin/gnuplot" fileName = "/usr/bin/gnuplot"
return os.path.isfile(fileName) and os.access(fileName, os.X_OK) return os.path.isfile(fileName) and os.access(fileName, os.X_OK)
@@ -805,44 +818,45 @@ class DumperBase:
self.putItem(value.dereference()) self.putItem(value.dereference())
return return
if format == 1: if format == Latin1StringFormat:
# Explicitly requested Latin1 formatting. # Explicitly requested Latin1 formatting.
self.putType(typeName) self.putType(typeName)
self.putValue(self.encodeCharArray(value), Hex2EncodedLatin1) self.putValue(self.encodeCharArray(value), Hex2EncodedLatin1)
self.putNumChild(0) self.putNumChild(0)
return return
if format == 2: if format == Utf8StringFormat:
# Explicitly requested UTF-8 formatting. # Explicitly requested UTF-8 formatting.
self.putType(typeName) self.putType(typeName)
self.putValue(self.encodeCharArray(value), Hex2EncodedUtf8) self.putValue(self.encodeCharArray(value), Hex2EncodedUtf8)
self.putNumChild(0) self.putNumChild(0)
return return
if format == 3: if format == Local8BitStringFormat:
# Explicitly requested local 8 bit formatting. # Explicitly requested local 8 bit formatting.
self.putType(typeName) self.putType(typeName)
self.putValue(self.encodeCharArray(value), Hex2EncodedLocal8Bit) self.putValue(self.encodeCharArray(value), Hex2EncodedLocal8Bit)
self.putNumChild(0) self.putNumChild(0)
return return
if format == 4: if format == Utf16StringFormat:
# Explicitly requested UTF-16 formatting. # Explicitly requested UTF-16 formatting.
self.putType(typeName) self.putType(typeName)
self.putValue(self.encodeChar2Array(value), Hex4EncodedLittleEndian) self.putValue(self.encodeChar2Array(value), Hex4EncodedLittleEndian)
self.putNumChild(0) self.putNumChild(0)
return return
if format == 5: if format == Ucs4StringFormat:
# Explicitly requested UCS-4 formatting. # Explicitly requested UCS-4 formatting.
self.putType(typeName) self.putType(typeName)
self.putValue(self.encodeChar4Array(value), Hex8EncodedLittleEndian) self.putValue(self.encodeChar4Array(value), Hex8EncodedLittleEndian)
self.putNumChild(0) self.putNumChild(0)
return return
if not format is None and format >= 6 and format <= 9: if not format is None \
and format >= Array10Format and format <= Array1000Format:
# Explicitly requested formatting as array of n items. # Explicitly requested formatting as array of n items.
n = (10, 100, 1000, 10000)[format - 6] n = (10, 100, 1000, 10000)[format - Array10Format]
self.putType(typeName) self.putType(typeName)
self.putItemCount(n) self.putItemCount(n)
self.putNumChild(n) self.putNumChild(n)

View File

@@ -352,13 +352,15 @@ int DumpParameters::format(const std::string &type, const std::string &iname) co
return -1; return -1;
} }
enum PointerFormats // Watch data pointer format requests // Watch data pointer format requests. This should match the values
// in DisplayFormat in watchhandler.h.
enum PointerFormats
{ {
FormatAuto = 0, FormatAuto = 0,
FormatLatin1String = 1, FormatLatin1String = 101,
FormatUtf8String = 2, FormatUtf8String = 102,
FormatUtf16String = 3, FormatUtf16String = 104,
FormatUcs4String = 4 FormatUcs4String = 105
}; };
enum DumpEncoding // WatchData encoding of GDBMI values enum DumpEncoding // WatchData encoding of GDBMI values

View File

@@ -889,9 +889,9 @@ void TypeFormatsDialog::addTypeFormats(const QString &type0,
m_ui->pages[pos]->addTypeFormats(type, typeFormats, current); m_ui->pages[pos]->addTypeFormats(type, typeFormats, current);
} }
TypeFormats TypeFormatsDialog::typeFormats() const DumperTypeFormats TypeFormatsDialog::typeFormats() const
{ {
return TypeFormats(); return DumperTypeFormats();
} }
} // namespace Internal } // namespace Internal

View File

@@ -159,7 +159,7 @@ private:
QDialogButtonBox *m_box; QDialogButtonBox *m_box;
}; };
typedef QHash<QString, QStringList> TypeFormats; typedef QHash<QString, QStringList> DumperTypeFormats;
class StartRemoteEngineDialog : public QDialog class StartRemoteEngineDialog : public QDialog
{ {
@@ -190,7 +190,7 @@ public:
void addTypeFormats(const QString &type, const QStringList &formats, void addTypeFormats(const QString &type, const QStringList &formats,
int currentFormat); int currentFormat);
TypeFormats typeFormats() const; DumperTypeFormats typeFormats() const;
private: private:
TypeFormatsDialogUi *m_ui; TypeFormatsDialogUi *m_ui;

View File

@@ -79,7 +79,6 @@ static QHash<QByteArray, int> theTypeFormats;
static QHash<QByteArray, int> theIndividualFormats; static QHash<QByteArray, int> theIndividualFormats;
static int theUnprintableBase = -1; static int theUnprintableBase = -1;
static QByteArray stripForFormat(const QByteArray &ba) static QByteArray stripForFormat(const QByteArray &ba)
{ {
QByteArray res; QByteArray res;
@@ -175,6 +174,9 @@ public:
int rowCount(const QModelIndex &idx = QModelIndex()) const; int rowCount(const QModelIndex &idx = QModelIndex()) const;
int columnCount(const QModelIndex &idx) const; int columnCount(const QModelIndex &idx) const;
static QString nameForFormat(int format);
TypeFormatList typeFormatList(const WatchData &value) const;
signals: signals:
void currentIndexRequested(const QModelIndex &idx); void currentIndexRequested(const QModelIndex &idx);
void itemIsExpanded(const QModelIndex &idx); void itemIsExpanded(const QModelIndex &idx);
@@ -247,8 +249,9 @@ private:
QSet<QByteArray> m_expandedINames; QSet<QByteArray> m_expandedINames;
QSet<QByteArray> m_fetchTriggered; QSet<QByteArray> m_fetchTriggered;
QStringList typeFormatList(const WatchData &data) const; TypeFormatList builtinTypeFormatList(const WatchData &data) const;
TypeFormats m_reportedTypeFormats; QStringList dumperTypeFormatList(const WatchData &data) const;
DumperTypeFormats m_reportedTypeFormats;
// QWidgets and QProcesses taking care of special displays. // QWidgets and QProcesses taking care of special displays.
typedef QMap<QByteArray, QPointer<QObject> > EditHandlers; typedef QMap<QByteArray, QPointer<QObject> > EditHandlers;
@@ -498,43 +501,24 @@ QString WatchModel::removeNamespaces(QString str) const
static int formatToIntegerBase(int format) static int formatToIntegerBase(int format)
{ {
switch (format) { switch (format) {
case HexadecimalFormat: case HexadecimalIntegerFormat:
return 16; return 16;
case BinaryFormat: case BinaryIntegerFormat:
return 2; return 2;
case OctalFormat: case OctalIntegerFormat:
return 8; return 8;
} }
return 10; return 10;
} }
static bool isIntegralValue(const QString &value)
{
if (value.startsWith(QLatin1Char('-')))
return isIntegralValue(value.mid(1));
bool ok;
value.toULongLong(&ok, 10);
if (ok)
return true;
value.toULongLong(&ok, 16);
if (ok)
return true;
value.toULongLong(&ok, 8);
if (ok)
return true;
return false;
}
template <class IntType> QString reformatInteger(IntType value, int format) template <class IntType> QString reformatInteger(IntType value, int format)
{ {
switch (format) { switch (format) {
case HexadecimalFormat: case HexadecimalIntegerFormat:
return QLatin1String("(hex) ") + QString::number(value, 16); return QLatin1String("(hex) ") + QString::number(value, 16);
case BinaryFormat: case BinaryIntegerFormat:
return QLatin1String("(bin) ") + QString::number(value, 2); return QLatin1String("(bin) ") + QString::number(value, 2);
case OctalFormat: case OctalIntegerFormat:
return QLatin1String("(oct) ") + QString::number(value, 8); return QLatin1String("(oct) ") + QString::number(value, 8);
} }
return QString::number(value, 10); // not reached return QString::number(value, 10); // not reached
@@ -543,7 +527,7 @@ template <class IntType> QString reformatInteger(IntType value, int format)
static QString reformatInteger(quint64 value, int format, int size, bool isSigned) static QString reformatInteger(quint64 value, int format, int size, bool isSigned)
{ {
// Follow convention and don't show negative non-decimal numbers. // Follow convention and don't show negative non-decimal numbers.
if (format != DecimalFormat) if (format != AutomaticFormat)
isSigned = false; isSigned = false;
switch (size) { switch (size) {
@@ -671,23 +655,34 @@ QString WatchModel::formattedValue(const WatchData &data) const
return value; return value;
} }
if (isIntegralValue(value)) {
// Append quoted, printable character also for decimal.
const int format = itemFormat(data); const int format = itemFormat(data);
// Append quoted, printable character also for decimal.
if (data.type.endsWith("char") || data.type.endsWith("QChar")) { if (data.type.endsWith("char") || data.type.endsWith("QChar")) {
bool ok; bool ok;
const int code = value.toInt(&ok); const int code = value.toInt(&ok);
return ok ? reformatCharacter(code, format) : value; return ok ? reformatCharacter(code, format) : value;
} }
// Rest: Leave decimal as is
if (format <= 0)
return value;
if (format == HexadecimalIntegerFormat
|| format == DecimalIntegerFormat
|| format == OctalIntegerFormat
|| format == BinaryIntegerFormat) {
bool isSigned = value.startsWith(QLatin1Char('-')); bool isSigned = value.startsWith(QLatin1Char('-'));
quint64 raw = isSigned ? quint64(value.toLongLong()): value.toULongLong(); quint64 raw = isSigned ? quint64(value.toLongLong()): value.toULongLong();
return reformatInteger(raw, format, data.size, isSigned); return reformatInteger(raw, format, data.size, isSigned);
} }
if (format == ScientificFloatFormat) {
double d = value.toDouble();
return QString::number(d, 'e');
}
if (format == CompactFloatFormat) {
double d = value.toDouble();
return QString::number(d, 'g');
}
if (data.type == "va_list") if (data.type == "va_list")
return value; return value;
@@ -918,10 +913,10 @@ static QString truncateValue(QString v)
int WatchModel::itemFormat(const WatchData &data) const int WatchModel::itemFormat(const WatchData &data) const
{ {
const int individualFormat = theIndividualFormats.value(data.iname, -1); const int individualFormat = theIndividualFormats.value(data.iname, AutomaticFormat);
if (individualFormat != -1) if (individualFormat != AutomaticFormat)
return individualFormat; return individualFormat;
return theTypeFormats.value(stripForFormat(data.type), -1); return theTypeFormats.value(stripForFormat(data.type), AutomaticFormat);
} }
bool WatchModel::contentIsValid() const bool WatchModel::contentIsValid() const
@@ -1089,7 +1084,7 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
return m_expandedINames.contains(data.iname); return m_expandedINames.contains(data.iname);
case LocalsTypeFormatListRole: case LocalsTypeFormatListRole:
return typeFormatList(data); return QVariant::fromValue(typeFormatList(data));
case LocalsTypeRole: case LocalsTypeRole:
return removeNamespaces(displayType(data)); return removeNamespaces(displayType(data));
@@ -1098,10 +1093,10 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
return QString::fromLatin1(data.type); return QString::fromLatin1(data.type);
case LocalsTypeFormatRole: case LocalsTypeFormatRole:
return theTypeFormats.value(stripForFormat(data.type), -1); return theTypeFormats.value(stripForFormat(data.type), AutomaticFormat);
case LocalsIndividualFormatRole: case LocalsIndividualFormatRole:
return theIndividualFormats.value(data.iname, -1); return theIndividualFormats.value(data.iname, AutomaticFormat);
case LocalsRawValueRole: case LocalsRawValueRole:
return data.value; return data.value;
@@ -1171,7 +1166,7 @@ bool WatchModel::setData(const QModelIndex &idx, const QVariant &value, int role
case LocalsIndividualFormatRole: { case LocalsIndividualFormatRole: {
const int format = value.toInt(); const int format = value.toInt();
if (format == -1) if (format == AutomaticFormat)
theIndividualFormats.remove(data.iname); theIndividualFormats.remove(data.iname);
else else
theIndividualFormats[data.iname] = format; theIndividualFormats[data.iname] = format;
@@ -1246,31 +1241,36 @@ static inline QString msgArrayFormat(int n)
return WatchModel::tr("Array of %n items", 0, n); return WatchModel::tr("Array of %n items", 0, n);
} }
QStringList WatchModel::typeFormatList(const WatchData &data) const QString WatchModel::nameForFormat(int format)
{ {
if (data.origaddr || isPointerType(data.type)) switch (format) {
return QStringList() case RawFormat: return tr("Raw Data");
<< tr("Raw pointer") case Latin1StringFormat: return tr("Latin1 String");
<< tr("Latin1 string") case Utf8StringFormat: return tr("UTF-8 String");
<< tr("UTF8 string") case Local8BitStringFormat: return tr("Local 8-Bit String");
<< tr("Local 8bit string") case Utf16StringFormat: return tr("UTF-16 String");
<< tr("UTF16 string") case Ucs4StringFormat: return tr("UCS-4 String");
<< tr("UCS4 string") case Array10Format: return msgArrayFormat(10);
<< msgArrayFormat(10) case Array100Format: return msgArrayFormat(100);
<< msgArrayFormat(100) case Array1000Format: return msgArrayFormat(1000);
<< msgArrayFormat(1000) case Array10000Format: return msgArrayFormat(10000);
<< msgArrayFormat(10000); case DecimalIntegerFormat: return tr("Decimal Integer");
if (data.type.contains("char[") || data.type.contains("char [")) case HexadecimalIntegerFormat: return tr("Hexadecimal Integer");
return QStringList() case BinaryIntegerFormat: return tr("Binary Integer");
<< tr("Latin1 string") case OctalIntegerFormat: return tr("Octal Integer");
<< tr("UTF8 string") case CompactFloatFormat: return tr("Compact Float");
<< tr("Local 8bit string"); case ScientificFloatFormat: return tr("Scientific Float");
if (isIntegralValue(data.value)) }
return QStringList()
<< tr("Decimal") QTC_CHECK(false);
<< tr("Hexadecimal") return QString();
<< tr("Binary") }
<< tr("Octal");
TypeFormatList WatchModel::typeFormatList(const WatchData &data) const
{
TypeFormatList formats;
// Types supported by dumpers:
// Hack: Compensate for namespaces. // Hack: Compensate for namespaces.
QString type = QLatin1String(stripForFormat(data.type)); QString type = QLatin1String(stripForFormat(data.type));
int pos = type.indexOf(QLatin1String("::Q")); int pos = type.indexOf(QLatin1String("::Q"));
@@ -1280,7 +1280,50 @@ QStringList WatchModel::typeFormatList(const WatchData &data) const
if (pos >= 0) if (pos >= 0)
type.truncate(pos); type.truncate(pos);
type.replace(QLatin1Char(':'), QLatin1Char('_')); type.replace(QLatin1Char(':'), QLatin1Char('_'));
return m_reportedTypeFormats.value(type); QStringList reported = m_reportedTypeFormats.value(type);
for (int i = 0, n = reported.size(); i != n; ++i)
formats.append(TypeFormatItem(reported.at(i), i));
// Fixed artificial string and pointer types.
if (data.origaddr || isPointerType(data.type)) {
formats.append(RawFormat);
formats.append(Latin1StringFormat);
formats.append(Utf8StringFormat);
formats.append(Local8BitStringFormat);
formats.append(Utf16StringFormat);
formats.append(Ucs4StringFormat);
formats.append(Array10Format);
formats.append(Array100Format);
formats.append(Array1000Format);
formats.append(Array10000Format);
} else if (data.type.contains("char[") || data.type.contains("char [")) {
formats.append(Latin1StringFormat);
formats.append(Utf8StringFormat);
formats.append(Ucs4StringFormat);
}
// Fixed artificial floating point types.
bool ok = false;
data.value.toDouble(&ok);
if (ok) {
formats.append(CompactFloatFormat);
formats.append(ScientificFloatFormat);
}
// Fixed artificial integral types.
data.value.toULongLong(&ok, 10);
if (!ok)
data.value.toULongLong(&ok, 16);
if (!ok)
data.value.toULongLong(&ok, 8);
if (ok) {
formats.append(DecimalIntegerFormat);
formats.append(HexadecimalIntegerFormat);
formats.append(BinaryIntegerFormat);
formats.append(OctalIntegerFormat);
}
return formats;
} }
// Determine sort order of watch items by sort order or alphabetical inames // Determine sort order of watch items by sort order or alphabetical inames
@@ -1439,10 +1482,10 @@ QDebug operator<<(QDebug d, const WatchModel &m)
void WatchModel::formatRequests(QByteArray *out, const WatchItem *item) const void WatchModel::formatRequests(QByteArray *out, const WatchItem *item) const
{ {
int format = theIndividualFormats.value(item->iname, -1); int format = theIndividualFormats.value(item->iname, AutomaticFormat);
if (format == -1) if (format == AutomaticFormat)
format = theTypeFormats.value(stripForFormat(item->type), -1); format = theTypeFormats.value(stripForFormat(item->type), AutomaticFormat);
if (format != -1) if (format != AutomaticFormat)
*out += item->iname + ":format=" + QByteArray::number(format) + ','; *out += item->iname + ":format=" + QByteArray::number(format) + ',';
foreach (const WatchItem *child, item->children) foreach (const WatchItem *child, item->children)
formatRequests(out, child); formatRequests(out, child);
@@ -1869,7 +1912,7 @@ void WatchHandler::saveFormats()
while (it.hasNext()) { while (it.hasNext()) {
it.next(); it.next();
const int format = it.value(); const int format = it.value();
if (format != DecimalFormat) { if (format != AutomaticFormat) {
const QByteArray key = it.key().trimmed(); const QByteArray key = it.key().trimmed();
if (!key.isEmpty()) if (!key.isEmpty())
formats.insert(QString::fromLatin1(key), format); formats.insert(QString::fromLatin1(key), format);
@@ -1948,7 +1991,7 @@ bool WatchHandler::hasItem(const QByteArray &iname) const
void WatchHandler::setFormat(const QByteArray &type0, int format) void WatchHandler::setFormat(const QByteArray &type0, int format)
{ {
const QByteArray type = stripForFormat(type0); const QByteArray type = stripForFormat(type0);
if (format == -1) if (format == AutomaticFormat)
theTypeFormats.remove(type); theTypeFormats.remove(type);
else else
theTypeFormats[type] = format; theTypeFormats[type] = format;
@@ -1958,11 +2001,11 @@ void WatchHandler::setFormat(const QByteArray &type0, int format)
int WatchHandler::format(const QByteArray &iname) const int WatchHandler::format(const QByteArray &iname) const
{ {
int result = -1; int result = AutomaticFormat;
if (const WatchData *item = m_model->findItem(iname)) { if (const WatchData *item = m_model->findItem(iname)) {
int result = theIndividualFormats.value(item->iname, -1); int result = theIndividualFormats.value(item->iname, AutomaticFormat);
if (result == -1) if (result == AutomaticFormat)
result = theTypeFormats.value(stripForFormat(item->type), -1); result = theTypeFormats.value(stripForFormat(item->type), AutomaticFormat);
} }
return result; return result;
} }
@@ -1990,11 +2033,14 @@ QByteArray WatchHandler::typeFormatRequests() const
QHashIterator<QByteArray, int> it(theTypeFormats); QHashIterator<QByteArray, int> it(theTypeFormats);
while (it.hasNext()) { while (it.hasNext()) {
it.next(); it.next();
const int format = it.value();
if (format >= RawFormat && format < ArtificialFormatBase) {
ba.append(it.key().toHex()); ba.append(it.key().toHex());
ba.append('='); ba.append('=');
ba.append(QByteArray::number(it.value())); ba.append(QByteArray::number(format));
ba.append(','); ba.append(',');
} }
}
ba.chop(1); ba.chop(1);
} }
return ba; return ba;
@@ -2007,11 +2053,14 @@ QByteArray WatchHandler::individualFormatRequests() const
QHashIterator<QByteArray, int> it(theIndividualFormats); QHashIterator<QByteArray, int> it(theIndividualFormats);
while (it.hasNext()) { while (it.hasNext()) {
it.next(); it.next();
const int format = it.value();
if (format >= RawFormat && format < ArtificialFormatBase) {
ba.append(it.key()); ba.append(it.key());
ba.append('='); ba.append('=');
ba.append(QByteArray::number(it.value())); ba.append(QByteArray::number(it.value()));
ba.append(','); ba.append(',');
} }
}
ba.chop(1); ba.chop(1);
} }
return ba; return ba;
@@ -2029,12 +2078,12 @@ QString WatchHandler::editorContents()
return contents; return contents;
} }
void WatchHandler::setTypeFormats(const TypeFormats &typeFormats) void WatchHandler::setTypeFormats(const DumperTypeFormats &typeFormats)
{ {
m_model->m_reportedTypeFormats = typeFormats; m_model->m_reportedTypeFormats = typeFormats;
} }
TypeFormats WatchHandler::typeFormats() const DumperTypeFormats WatchHandler::typeFormats() const
{ {
return m_model->m_reportedTypeFormats; return m_model->m_reportedTypeFormats;
} }
@@ -2048,7 +2097,7 @@ void WatchHandler::editTypeFormats(bool includeLocals, const QByteArray &iname)
QList<QString> l = m_model->m_reportedTypeFormats.keys(); QList<QString> l = m_model->m_reportedTypeFormats.keys();
qSort(l.begin(), l.end()); qSort(l.begin(), l.end());
foreach (const QString &ba, l) { foreach (const QString &ba, l) {
int f = iname.isEmpty() ? -1 : format(iname); int f = iname.isEmpty() ? AutomaticFormat : format(iname);
dlg.addTypeFormats(ba, m_model->m_reportedTypeFormats.value(ba), f); dlg.addTypeFormats(ba, m_model->m_reportedTypeFormats.value(ba), f);
} }
if (dlg.exec()) if (dlg.exec())
@@ -2107,6 +2156,30 @@ QSet<QByteArray> WatchHandler::expandedINames() const
return m_model->m_expandedINames; return m_model->m_expandedINames;
} }
////////////////////////////////////////////////////////////////////
//
// TypeFormatItem/List
//
////////////////////////////////////////////////////////////////////
TypeFormatItem::TypeFormatItem(const QString &display, int format)
: display(display), format(format)
{}
void TypeFormatList::append(int format)
{
append(TypeFormatItem(WatchModel::nameForFormat(format), format));
}
TypeFormatItem TypeFormatList::find(int format) const
{
for (int i = 0; i != size(); ++i)
if (at(i).format == format)
return at(i);
return TypeFormatItem();
}
} // namespace Internal } // namespace Internal
} // namespace Debugger } // namespace Debugger

View File

@@ -32,11 +32,76 @@
#include "watchdata.h" #include "watchdata.h"
#include <QPointer>
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QPointer>
#include <QVector>
QT_FORWARD_DECLARE_CLASS(QTabWidget) QT_FORWARD_DECLARE_CLASS(QTabWidget)
namespace Debugger {
namespace Internal {
// Special formats. Keep in sync with dumper.py.
enum DisplayFormat
{
AutomaticFormat = -1, // Based on type for individuals, dumper default for types.
RawFormat = 0,
// Values between 1 and 99 refer to dumper provided custom formats.
// Values between 100 and 199 refer to well-known formats handled in dumpers.
KnownDumperFormatBase = 100,
Latin1StringFormat,
Utf8StringFormat,
Local8BitStringFormat,
Utf16StringFormat,
Ucs4StringFormat,
Array10Format,
Array100Format,
Array1000Format,
Array10000Format,
// Values above 200 refer to format solely handled in the WatchHandler code
ArtificialFormatBase = 200,
BoolTextFormat,
BoolIntegerFormat,
DecimalIntegerFormat,
HexadecimalIntegerFormat,
BinaryIntegerFormat,
OctalIntegerFormat,
CompactFloatFormat,
ScientificFloatFormat,
};
class TypeFormatItem
{
public:
TypeFormatItem() : format(-1) {}
TypeFormatItem(const QString &display, int format);
QString display;
int format;
};
class TypeFormatList : public QVector<TypeFormatItem>
{
public:
using QVector::append;
void append(int format);
TypeFormatItem find(int format) const;
};
} // namespace Internal
} // namespace Debugger
Q_DECLARE_METATYPE(Debugger::Internal::TypeFormatList)
namespace Debugger { namespace Debugger {
class DebuggerEngine; class DebuggerEngine;
@@ -55,15 +120,7 @@ public:
QByteArray varList; QByteArray varList;
}; };
typedef QHash<QString, QStringList> TypeFormats; typedef QHash<QString, QStringList> DumperTypeFormats; // Type name -> Dumper Formats
enum IntegerFormat
{
DecimalFormat = 0, // Keep that at 0 as default.
HexadecimalFormat,
BinaryFormat,
OctalFormat
};
class WatchHandler : public QObject class WatchHandler : public QObject
{ {
@@ -104,8 +161,8 @@ public:
int format(const QByteArray &iname) const; int format(const QByteArray &iname) const;
void addTypeFormats(const QByteArray &type, const QStringList &formats); void addTypeFormats(const QByteArray &type, const QStringList &formats);
void setTypeFormats(const TypeFormats &typeFormats); void setTypeFormats(const DumperTypeFormats &typeFormats);
TypeFormats typeFormats() const; DumperTypeFormats typeFormats() const;
void setUnprintableBase(int base); void setUnprintableBase(int base);
static int unprintableBase(); static int unprintableBase();

View File

@@ -59,6 +59,8 @@
#include <QInputDialog> #include <QInputDialog>
#include <QMessageBox> #include <QMessageBox>
Q_DECLARE_METATYPE(QModelIndex)
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// //
// WatchDelegate // WatchDelegate
@@ -68,6 +70,8 @@
namespace Debugger { namespace Debugger {
namespace Internal { namespace Internal {
const char CurrentIndex[] = "CurrentIndex";
static DebuggerEngine *currentEngine() static DebuggerEngine *currentEngine()
{ {
return debuggerCore()->currentEngine(); return debuggerCore()->currentEngine();
@@ -584,12 +588,150 @@ static void copyToClipboard(const QString &clipboardText)
clipboard->setText(clipboardText, QClipboard::Clipboard); clipboard->setText(clipboardText, QClipboard::Clipboard);
} }
void WatchTreeView::fillFormatMenu(QMenu *formatMenu, const QModelIndex &mi)
{
QTC_CHECK(mi.isValid());
DebuggerEngine *engine = currentEngine();
WatchHandler *handler = engine->watchHandler();
const QModelIndex mi2 = mi.sibling(mi.row(), 2);
const QString type = mi2.data().toString();
const TypeFormatList alternativeFormats =
mi.data(LocalsTypeFormatListRole).value<TypeFormatList>();
int typeFormat =
mi.data(LocalsTypeFormatRole).toInt();
const int individualFormat =
mi.data(LocalsIndividualFormatRole).toInt();
const int unprintableBase = handler->unprintableBase();
QAction *showUnprintableUnicode = 0;
QAction *showUnprintableEscape = 0;
QAction *showUnprintableOctal = 0;
QAction *showUnprintableHexadecimal = 0;
formatMenu->setTitle(tr("Change Local Display Format..."));
showUnprintableUnicode =
formatMenu->addAction(tr("Treat All Characters as Printable"));
showUnprintableUnicode->setCheckable(true);
showUnprintableUnicode->setChecked(unprintableBase == 0);
showUnprintableUnicode->setData(0);
showUnprintableEscape =
formatMenu->addAction(tr("Show Unprintable Characters as Escape Sequences"));
showUnprintableEscape->setCheckable(true);
showUnprintableEscape->setChecked(unprintableBase == -1);
showUnprintableEscape->setData(-1);
showUnprintableOctal =
formatMenu->addAction(tr("Show Unprintable Characters as Octal"));
showUnprintableOctal->setCheckable(true);
showUnprintableOctal->setChecked(unprintableBase == 8);
showUnprintableOctal->setData(8);
showUnprintableHexadecimal =
formatMenu->addAction(tr("Show Unprintable Characters as Hexadecimal"));
showUnprintableHexadecimal->setCheckable(true);
showUnprintableHexadecimal->setChecked(unprintableBase == 16);
showUnprintableHexadecimal->setData(16);
connect(showUnprintableUnicode, SIGNAL(triggered()), SLOT(onShowUnprintable()));
connect(showUnprintableEscape, SIGNAL(triggered()), SLOT(onShowUnprintable()));
connect(showUnprintableOctal, SIGNAL(triggered()), SLOT(onShowUnprintable()));
connect(showUnprintableHexadecimal, SIGNAL(triggered()), SLOT(onShowUnprintable()));
const QString spacer = QLatin1String(" ");
formatMenu->addSeparator();
QAction *dummy = formatMenu->addAction(
tr("Change Display for Object Named \"%1\":").arg(mi.data().toString()));
dummy->setEnabled(false);
QString msg = (individualFormat == AutomaticFormat && typeFormat != AutomaticFormat)
? tr("Use Format for Type (Currently %1)")
.arg(alternativeFormats.find(typeFormat).display)
: tr("Use Display Format Based on Type") + QLatin1Char(' ');
QAction *clearIndividualFormatAction = formatMenu->addAction(spacer + msg);
clearIndividualFormatAction->setCheckable(true);
clearIndividualFormatAction->setChecked(individualFormat == AutomaticFormat);
connect(clearIndividualFormatAction, SIGNAL(triggered()),
SLOT(onClearIndividualFormat()));
for (int i = 0; i != alternativeFormats.size(); ++i) {
const QString display = spacer + alternativeFormats.at(i).display;
const int format = alternativeFormats.at(i).format;
QAction *act = new QAction(display, formatMenu);
act->setData(format);
act->setCheckable(true);
act->setChecked(format == individualFormat);
act->setProperty(CurrentIndex, QVariant::fromValue(mi));
formatMenu->addAction(act);
connect(act, SIGNAL(triggered()), SLOT(onIndividualFormatChange()));
}
formatMenu->addSeparator();
dummy = formatMenu->addAction(tr("Change Display for Type \"%1\":").arg(type));
dummy->setEnabled(false);
QAction *clearTypeFormatAction = formatMenu->addAction(spacer + tr("Automatic"));
clearTypeFormatAction->setCheckable(true);
clearTypeFormatAction->setChecked(typeFormat == AutomaticFormat);
connect(clearTypeFormatAction, SIGNAL(triggered()), SLOT(onClearTypeFormat()));
for (int i = 0; i != alternativeFormats.size(); ++i) {
const QString display = spacer + alternativeFormats.at(i).display;
QAction *act = new QAction(display, formatMenu);
const int format = alternativeFormats.at(i).format;
act->setData(format);
act->setCheckable(true);
act->setChecked(format == typeFormat);
act->setProperty(CurrentIndex, QVariant::fromValue(mi));
formatMenu->addAction(act);
connect(act, SIGNAL(triggered()), SLOT(onTypeFormatChange()));
}
}
void WatchTreeView::onClearTypeFormat()
{
const QModelIndexList active = activeRows();
foreach (const QModelIndex &idx, active)
setModelData(LocalsTypeFormatRole, AutomaticFormat, idx);
}
void WatchTreeView::onClearIndividualFormat()
{
const QModelIndexList active = activeRows();
foreach (const QModelIndex &idx, active)
setModelData(LocalsIndividualFormatRole, AutomaticFormat, idx);
}
void WatchTreeView::onShowUnprintable()
{
QAction *act = qobject_cast<QAction *>(sender());
QTC_ASSERT(act, return);
DebuggerEngine *engine = currentEngine();
WatchHandler *handler = engine->watchHandler();
handler->setUnprintableBase(act->data().toInt());
}
void WatchTreeView::onTypeFormatChange()
{
QAction *act = qobject_cast<QAction *>(sender());
QTC_ASSERT(act, return);
QModelIndex idx = act->property(CurrentIndex).value<QModelIndex>();
setModelData(LocalsTypeFormatRole, act->data(), idx);
}
void WatchTreeView::onIndividualFormatChange()
{
QAction *act = qobject_cast<QAction *>(sender());
QTC_ASSERT(act, return);
QModelIndex idx = act->property(CurrentIndex).value<QModelIndex>();
setModelData(LocalsIndividualFormatRole, act->data(), idx);
}
void WatchTreeView::contextMenuEvent(QContextMenuEvent *ev) void WatchTreeView::contextMenuEvent(QContextMenuEvent *ev)
{ {
DebuggerEngine *engine = currentEngine(); DebuggerEngine *engine = currentEngine();
WatchHandler *handler = engine->watchHandler(); WatchHandler *handler = engine->watchHandler();
const QModelIndexList active = activeRows();
const QModelIndex idx = indexAt(ev->pos()); const QModelIndex idx = indexAt(ev->pos());
const QModelIndex mi0 = idx.sibling(idx.row(), 0); const QModelIndex mi0 = idx.sibling(idx.row(), 0);
const QModelIndex mi1 = idx.sibling(idx.row(), 1); const QModelIndex mi1 = idx.sibling(idx.row(), 1);
@@ -604,89 +746,6 @@ void WatchTreeView::contextMenuEvent(QContextMenuEvent *ev)
// Offer to open address pointed to or variable address. // Offer to open address pointed to or variable address.
const bool createPointerActions = pointerAddress && pointerAddress != address; const bool createPointerActions = pointerAddress && pointerAddress != address;
const QStringList alternativeFormats =
mi0.data(LocalsTypeFormatListRole).toStringList();
int typeFormat =
mi0.data(LocalsTypeFormatRole).toInt();
if (typeFormat >= alternativeFormats.size())
typeFormat = -1;
const int individualFormat =
mi0.data(LocalsIndividualFormatRole).toInt();
const int unprintableBase = handler->unprintableBase();
QMenu formatMenu;
QList<QAction *> typeFormatActions;
QList<QAction *> individualFormatActions;
QAction *clearTypeFormatAction = 0;
QAction *clearIndividualFormatAction = 0;
QAction *showUnprintableUnicode = 0;
QAction *showUnprintableEscape = 0;
QAction *showUnprintableOctal = 0;
QAction *showUnprintableHexadecimal = 0;
formatMenu.setTitle(tr("Change Local Display Format..."));
showUnprintableUnicode =
formatMenu.addAction(tr("Treat All Characters as Printable"));
showUnprintableUnicode->setCheckable(true);
showUnprintableUnicode->setChecked(unprintableBase == 0);
showUnprintableEscape =
formatMenu.addAction(tr("Show Unprintable Characters as Escape Sequences"));
showUnprintableEscape->setCheckable(true);
showUnprintableEscape->setChecked(unprintableBase == -1);
showUnprintableOctal =
formatMenu.addAction(tr("Show Unprintable Characters as Octal"));
showUnprintableOctal->setCheckable(true);
showUnprintableOctal->setChecked(unprintableBase == 8);
showUnprintableHexadecimal =
formatMenu.addAction(tr("Show Unprintable Characters as Hexadecimal"));
showUnprintableHexadecimal->setCheckable(true);
showUnprintableHexadecimal->setChecked(unprintableBase == 16);
if (idx.isValid() /*&& !alternativeFormats.isEmpty() */) {
const QString spacer = QLatin1String(" ");
formatMenu.addSeparator();
QAction *dummy = formatMenu.addAction(
tr("Change Display for Object Named \"%1\":").arg(mi0.data().toString()));
dummy->setEnabled(false);
QString msg = (individualFormat == -1 && typeFormat != -1)
? tr("Use Format for Type (Currently %1)")
.arg(alternativeFormats.at(typeFormat))
: tr("Use Display Format Based on Type") + QLatin1Char(' ');
clearIndividualFormatAction = formatMenu.addAction(spacer + msg);
clearIndividualFormatAction->setCheckable(true);
clearIndividualFormatAction->setChecked(individualFormat == -1);
for (int i = 0; i != alternativeFormats.size(); ++i) {
const QString format = spacer + alternativeFormats.at(i);
QAction *act = new QAction(format, &formatMenu);
act->setCheckable(true);
if (i == individualFormat)
act->setChecked(true);
formatMenu.addAction(act);
individualFormatActions.append(act);
}
formatMenu.addSeparator();
dummy = formatMenu.addAction(
tr("Change Display for Type \"%1\":").arg(type));
dummy->setEnabled(false);
clearTypeFormatAction = formatMenu.addAction(spacer + tr("Automatic"));
//clearTypeFormatAction->setEnabled(typeFormat != -1);
//clearTypeFormatAction->setEnabled(individualFormat != -1);
clearTypeFormatAction->setCheckable(true);
clearTypeFormatAction->setChecked(typeFormat == -1);
for (int i = 0; i != alternativeFormats.size(); ++i) {
const QString format = spacer + alternativeFormats.at(i);
QAction *act = new QAction(format, &formatMenu);
act->setCheckable(true);
//act->setEnabled(individualFormat != -1);
if (i == typeFormat)
act->setChecked(true);
formatMenu.addAction(act);
typeFormatActions.append(act);
}
} else {
QAction *dummy = formatMenu.addAction(
tr("Change Display for Type or Item..."));
dummy->setEnabled(false);
}
const bool actionsEnabled = engine->debuggerActionsEnabled(); const bool actionsEnabled = engine->debuggerActionsEnabled();
const bool canHandleWatches = engine->hasCapability(AddWatcherCapability); const bool canHandleWatches = engine->hasCapability(AddWatcherCapability);
const DebuggerState state = engine->state(); const DebuggerState state = engine->state();
@@ -782,6 +841,14 @@ void WatchTreeView::contextMenuEvent(QContextMenuEvent *ev)
menu.addAction(actRemoveWatches); menu.addAction(actRemoveWatches);
} }
QMenu formatMenu;
if (mi0.isValid()) {
fillFormatMenu(&formatMenu, mi0);
} else {
QAction *dummy = formatMenu.addAction(tr("Change Display for Type or Item..."));
dummy->setEnabled(false);
}
QMenu memoryMenu; QMenu memoryMenu;
memoryMenu.setTitle(tr("Open Memory Editor...")); memoryMenu.setTitle(tr("Open Memory Editor..."));
QAction *actOpenMemoryEditAtObjectAddress = new QAction(&memoryMenu); QAction *actOpenMemoryEditAtObjectAddress = new QAction(&memoryMenu);
@@ -839,7 +906,6 @@ void WatchTreeView::contextMenuEvent(QContextMenuEvent *ev)
QAction *actCopyValue = new QAction(tr("Copy Value to Clipboard"), &menu); QAction *actCopyValue = new QAction(tr("Copy Value to Clipboard"), &menu);
actCopyValue->setEnabled(idx.isValid()); actCopyValue->setEnabled(idx.isValid());
menu.addAction(actInsertNewWatchItem); menu.addAction(actInsertNewWatchItem);
menu.addAction(actSelectWidgetToWatch); menu.addAction(actSelectWidgetToWatch);
menu.addMenu(&formatMenu); menu.addMenu(&formatMenu);
@@ -914,43 +980,11 @@ void WatchTreeView::contextMenuEvent(QContextMenuEvent *ev)
copyToClipboard(mi1.data().toString()); copyToClipboard(mi1.data().toString());
} else if (act == actRemoveWatches) { } else if (act == actRemoveWatches) {
handler->clearWatches(); handler->clearWatches();
} else if (act == clearTypeFormatAction) {
foreach (const QModelIndex &idx, active)
setModelData(LocalsTypeFormatRole, -1, idx);
} else if (act == clearIndividualFormatAction) {
foreach (const QModelIndex &idx, active)
setModelData(LocalsIndividualFormatRole, -1, idx);
} else if (act == actShowInEditor) { } else if (act == actShowInEditor) {
QString contents = handler->editorContents(); QString contents = handler->editorContents();
debuggerCore()->openTextEditor(tr("Locals & Expressions"), contents); debuggerCore()->openTextEditor(tr("Locals & Expressions"), contents);
} else if (act == showUnprintableUnicode) {
handler->setUnprintableBase(0);
} else if (act == showUnprintableEscape) {
handler->setUnprintableBase(-1);
} else if (act == showUnprintableOctal) {
handler->setUnprintableBase(8);
} else if (act == showUnprintableHexadecimal) {
handler->setUnprintableBase(16);
} else if (act == actCloseEditorToolTips) { } else if (act == actCloseEditorToolTips) {
DebuggerToolTipManager::closeAllToolTips(); DebuggerToolTipManager::closeAllToolTips();
} else if (handleBaseContextAction(act)) {
;
} else {
// Restrict multiple changes to items of the same type
// to avoid assigning illegal formats.
const QVariant currentType = mi1.data(LocalsTypeRole);
for (int i = 0; i != typeFormatActions.size(); ++i) {
if (act == typeFormatActions.at(i))
foreach (const QModelIndex &idx, active)
if (idx.data(LocalsTypeRole) == currentType)
setModelData(LocalsTypeFormatRole, i, idx);
}
for (int i = 0; i != individualFormatActions.size(); ++i) {
if (act == individualFormatActions.at(i))
foreach (const QModelIndex &idx, active)
if (idx.data(LocalsTypeRole) == currentType)
setModelData(LocalsIndividualFormatRole, i, idx);
}
} }
} }

View File

@@ -54,6 +54,8 @@ public:
void rowActivated(const QModelIndex &index); void rowActivated(const QModelIndex &index);
void reset(); void reset();
void fillFormatMenu(QMenu *, const QModelIndex &mi);
public slots: public slots:
void watchExpression(const QString &exp); void watchExpression(const QString &exp);
void watchExpression(const QString &exp, const QString &name); void watchExpression(const QString &exp, const QString &name);
@@ -62,11 +64,19 @@ public slots:
signals: signals:
void currentIndexChanged(const QModelIndex &currentIndex); void currentIndexChanged(const QModelIndex &currentIndex);
private: private slots:
Q_SLOT void resetHelper(); void resetHelper();
Q_SLOT void expandNode(const QModelIndex &idx); void expandNode(const QModelIndex &idx);
Q_SLOT void collapseNode(const QModelIndex &idx); void collapseNode(const QModelIndex &idx);
void onClearIndividualFormat();
void onClearTypeFormat();
void onShowUnprintable();
void onTypeFormatChange();
void onIndividualFormatChange();
private:
void keyPressEvent(QKeyEvent *ev); void keyPressEvent(QKeyEvent *ev);
void contextMenuEvent(QContextMenuEvent *ev); void contextMenuEvent(QContextMenuEvent *ev);
void dragEnterEvent(QDragEnterEvent *ev); void dragEnterEvent(QDragEnterEvent *ev);