From 3f0d02ad7f4f3b9873bd16b98cb0d57a904af1ea Mon Sep 17 00:00:00 2001 From: hjk <hjk121@nokiamail.com> Date: Tue, 15 Apr 2014 18:13:03 +0200 Subject: [PATCH] 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> --- share/qtcreator/debugger/dumper.py | 28 +- src/libs/qtcreatorcdbext/symbolgroupnode.cpp | 12 +- src/plugins/debugger/debuggerdialogs.cpp | 4 +- src/plugins/debugger/debuggerdialogs.h | 4 +- src/plugins/debugger/watchhandler.cpp | 261 ++++++++++++------- src/plugins/debugger/watchhandler.h | 81 +++++- src/plugins/debugger/watchwindow.cpp | 250 ++++++++++-------- src/plugins/debugger/watchwindow.h | 18 +- 8 files changed, 424 insertions(+), 234 deletions(-) diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index 0b19cbaeb0..b7725fbe83 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -42,6 +42,19 @@ else: verbosity = 0 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(): fileName = "/usr/bin/gnuplot" return os.path.isfile(fileName) and os.access(fileName, os.X_OK) @@ -805,44 +818,45 @@ class DumperBase: self.putItem(value.dereference()) return - if format == 1: + if format == Latin1StringFormat: # Explicitly requested Latin1 formatting. self.putType(typeName) self.putValue(self.encodeCharArray(value), Hex2EncodedLatin1) self.putNumChild(0) return - if format == 2: + if format == Utf8StringFormat: # Explicitly requested UTF-8 formatting. self.putType(typeName) self.putValue(self.encodeCharArray(value), Hex2EncodedUtf8) self.putNumChild(0) return - if format == 3: + if format == Local8BitStringFormat: # Explicitly requested local 8 bit formatting. self.putType(typeName) self.putValue(self.encodeCharArray(value), Hex2EncodedLocal8Bit) self.putNumChild(0) return - if format == 4: + if format == Utf16StringFormat: # Explicitly requested UTF-16 formatting. self.putType(typeName) self.putValue(self.encodeChar2Array(value), Hex4EncodedLittleEndian) self.putNumChild(0) return - if format == 5: + if format == Ucs4StringFormat: # Explicitly requested UCS-4 formatting. self.putType(typeName) self.putValue(self.encodeChar4Array(value), Hex8EncodedLittleEndian) self.putNumChild(0) 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. - n = (10, 100, 1000, 10000)[format - 6] + n = (10, 100, 1000, 10000)[format - Array10Format] self.putType(typeName) self.putItemCount(n) self.putNumChild(n) diff --git a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp index ce67deb698..ac6b4b6c67 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp @@ -352,13 +352,15 @@ int DumpParameters::format(const std::string &type, const std::string &iname) co 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, - FormatLatin1String = 1, - FormatUtf8String = 2, - FormatUtf16String = 3, - FormatUcs4String = 4 + FormatLatin1String = 101, + FormatUtf8String = 102, + FormatUtf16String = 104, + FormatUcs4String = 105 }; enum DumpEncoding // WatchData encoding of GDBMI values diff --git a/src/plugins/debugger/debuggerdialogs.cpp b/src/plugins/debugger/debuggerdialogs.cpp index afcea28a58..c8e4e175c5 100644 --- a/src/plugins/debugger/debuggerdialogs.cpp +++ b/src/plugins/debugger/debuggerdialogs.cpp @@ -889,9 +889,9 @@ void TypeFormatsDialog::addTypeFormats(const QString &type0, m_ui->pages[pos]->addTypeFormats(type, typeFormats, current); } -TypeFormats TypeFormatsDialog::typeFormats() const +DumperTypeFormats TypeFormatsDialog::typeFormats() const { - return TypeFormats(); + return DumperTypeFormats(); } } // namespace Internal diff --git a/src/plugins/debugger/debuggerdialogs.h b/src/plugins/debugger/debuggerdialogs.h index cc1a9bffb8..3fe1cc1b51 100644 --- a/src/plugins/debugger/debuggerdialogs.h +++ b/src/plugins/debugger/debuggerdialogs.h @@ -159,7 +159,7 @@ private: QDialogButtonBox *m_box; }; -typedef QHash<QString, QStringList> TypeFormats; +typedef QHash<QString, QStringList> DumperTypeFormats; class StartRemoteEngineDialog : public QDialog { @@ -190,7 +190,7 @@ public: void addTypeFormats(const QString &type, const QStringList &formats, int currentFormat); - TypeFormats typeFormats() const; + DumperTypeFormats typeFormats() const; private: TypeFormatsDialogUi *m_ui; diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp index dcfd28f5ee..55a0630976 100644 --- a/src/plugins/debugger/watchhandler.cpp +++ b/src/plugins/debugger/watchhandler.cpp @@ -79,7 +79,6 @@ static QHash<QByteArray, int> theTypeFormats; static QHash<QByteArray, int> theIndividualFormats; static int theUnprintableBase = -1; - static QByteArray stripForFormat(const QByteArray &ba) { QByteArray res; @@ -175,6 +174,9 @@ public: int rowCount(const QModelIndex &idx = QModelIndex()) const; int columnCount(const QModelIndex &idx) const; + static QString nameForFormat(int format); + TypeFormatList typeFormatList(const WatchData &value) const; + signals: void currentIndexRequested(const QModelIndex &idx); void itemIsExpanded(const QModelIndex &idx); @@ -247,8 +249,9 @@ private: QSet<QByteArray> m_expandedINames; QSet<QByteArray> m_fetchTriggered; - QStringList typeFormatList(const WatchData &data) const; - TypeFormats m_reportedTypeFormats; + TypeFormatList builtinTypeFormatList(const WatchData &data) const; + QStringList dumperTypeFormatList(const WatchData &data) const; + DumperTypeFormats m_reportedTypeFormats; // QWidgets and QProcesses taking care of special displays. typedef QMap<QByteArray, QPointer<QObject> > EditHandlers; @@ -498,43 +501,24 @@ QString WatchModel::removeNamespaces(QString str) const static int formatToIntegerBase(int format) { switch (format) { - case HexadecimalFormat: + case HexadecimalIntegerFormat: return 16; - case BinaryFormat: + case BinaryIntegerFormat: return 2; - case OctalFormat: + case OctalIntegerFormat: return 8; } 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) { switch (format) { - case HexadecimalFormat: + case HexadecimalIntegerFormat: return QLatin1String("(hex) ") + QString::number(value, 16); - case BinaryFormat: + case BinaryIntegerFormat: return QLatin1String("(bin) ") + QString::number(value, 2); - case OctalFormat: + case OctalIntegerFormat: return QLatin1String("(oct) ") + QString::number(value, 8); } 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) { // Follow convention and don't show negative non-decimal numbers. - if (format != DecimalFormat) + if (format != AutomaticFormat) isSigned = false; switch (size) { @@ -671,23 +655,34 @@ QString WatchModel::formattedValue(const WatchData &data) const return value; } - if (isIntegralValue(value)) { - // Append quoted, printable character also for decimal. - const int format = itemFormat(data); - if (data.type.endsWith("char") || data.type.endsWith("QChar")) { - bool ok; - const int code = value.toInt(&ok); - return ok ? reformatCharacter(code, format) : value; - } - // Rest: Leave decimal as is - if (format <= 0) - return value; + const int format = itemFormat(data); + // Append quoted, printable character also for decimal. + if (data.type.endsWith("char") || data.type.endsWith("QChar")) { + bool ok; + const int code = value.toInt(&ok); + return ok ? reformatCharacter(code, format) : value; + } + + if (format == HexadecimalIntegerFormat + || format == DecimalIntegerFormat + || format == OctalIntegerFormat + || format == BinaryIntegerFormat) { bool isSigned = value.startsWith(QLatin1Char('-')); quint64 raw = isSigned ? quint64(value.toLongLong()): value.toULongLong(); 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") return value; @@ -918,10 +913,10 @@ static QString truncateValue(QString v) int WatchModel::itemFormat(const WatchData &data) const { - const int individualFormat = theIndividualFormats.value(data.iname, -1); - if (individualFormat != -1) + const int individualFormat = theIndividualFormats.value(data.iname, AutomaticFormat); + if (individualFormat != AutomaticFormat) return individualFormat; - return theTypeFormats.value(stripForFormat(data.type), -1); + return theTypeFormats.value(stripForFormat(data.type), AutomaticFormat); } bool WatchModel::contentIsValid() const @@ -1089,7 +1084,7 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const return m_expandedINames.contains(data.iname); case LocalsTypeFormatListRole: - return typeFormatList(data); + return QVariant::fromValue(typeFormatList(data)); case LocalsTypeRole: return removeNamespaces(displayType(data)); @@ -1098,10 +1093,10 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const return QString::fromLatin1(data.type); case LocalsTypeFormatRole: - return theTypeFormats.value(stripForFormat(data.type), -1); + return theTypeFormats.value(stripForFormat(data.type), AutomaticFormat); case LocalsIndividualFormatRole: - return theIndividualFormats.value(data.iname, -1); + return theIndividualFormats.value(data.iname, AutomaticFormat); case LocalsRawValueRole: return data.value; @@ -1171,7 +1166,7 @@ bool WatchModel::setData(const QModelIndex &idx, const QVariant &value, int role case LocalsIndividualFormatRole: { const int format = value.toInt(); - if (format == -1) + if (format == AutomaticFormat) theIndividualFormats.remove(data.iname); else theIndividualFormats[data.iname] = format; @@ -1246,31 +1241,36 @@ static inline QString msgArrayFormat(int n) return WatchModel::tr("Array of %n items", 0, n); } -QStringList WatchModel::typeFormatList(const WatchData &data) const -{ - if (data.origaddr || isPointerType(data.type)) - return QStringList() - << tr("Raw pointer") - << tr("Latin1 string") - << tr("UTF8 string") - << tr("Local 8bit string") - << tr("UTF16 string") - << tr("UCS4 string") - << msgArrayFormat(10) - << msgArrayFormat(100) - << msgArrayFormat(1000) - << msgArrayFormat(10000); - if (data.type.contains("char[") || data.type.contains("char [")) - return QStringList() - << tr("Latin1 string") - << tr("UTF8 string") - << tr("Local 8bit string"); - if (isIntegralValue(data.value)) - return QStringList() - << tr("Decimal") - << tr("Hexadecimal") - << tr("Binary") - << tr("Octal"); +QString WatchModel::nameForFormat(int format) +{ + switch (format) { + case RawFormat: return tr("Raw Data"); + case Latin1StringFormat: return tr("Latin1 String"); + case Utf8StringFormat: return tr("UTF-8 String"); + case Local8BitStringFormat: return tr("Local 8-Bit String"); + case Utf16StringFormat: return tr("UTF-16 String"); + case Ucs4StringFormat: return tr("UCS-4 String"); + case Array10Format: return msgArrayFormat(10); + case Array100Format: return msgArrayFormat(100); + case Array1000Format: return msgArrayFormat(1000); + case Array10000Format: return msgArrayFormat(10000); + case DecimalIntegerFormat: return tr("Decimal Integer"); + case HexadecimalIntegerFormat: return tr("Hexadecimal Integer"); + case BinaryIntegerFormat: return tr("Binary Integer"); + case OctalIntegerFormat: return tr("Octal Integer"); + case CompactFloatFormat: return tr("Compact Float"); + case ScientificFloatFormat: return tr("Scientific Float"); + } + + QTC_CHECK(false); + return QString(); +} + +TypeFormatList WatchModel::typeFormatList(const WatchData &data) const +{ + TypeFormatList formats; + + // Types supported by dumpers: // Hack: Compensate for namespaces. QString type = QLatin1String(stripForFormat(data.type)); int pos = type.indexOf(QLatin1String("::Q")); @@ -1280,7 +1280,50 @@ QStringList WatchModel::typeFormatList(const WatchData &data) const if (pos >= 0) type.truncate(pos); 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 @@ -1439,10 +1482,10 @@ QDebug operator<<(QDebug d, const WatchModel &m) void WatchModel::formatRequests(QByteArray *out, const WatchItem *item) const { - int format = theIndividualFormats.value(item->iname, -1); - if (format == -1) - format = theTypeFormats.value(stripForFormat(item->type), -1); - if (format != -1) + int format = theIndividualFormats.value(item->iname, AutomaticFormat); + if (format == AutomaticFormat) + format = theTypeFormats.value(stripForFormat(item->type), AutomaticFormat); + if (format != AutomaticFormat) *out += item->iname + ":format=" + QByteArray::number(format) + ','; foreach (const WatchItem *child, item->children) formatRequests(out, child); @@ -1869,7 +1912,7 @@ void WatchHandler::saveFormats() while (it.hasNext()) { it.next(); const int format = it.value(); - if (format != DecimalFormat) { + if (format != AutomaticFormat) { const QByteArray key = it.key().trimmed(); if (!key.isEmpty()) 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) { const QByteArray type = stripForFormat(type0); - if (format == -1) + if (format == AutomaticFormat) theTypeFormats.remove(type); else theTypeFormats[type] = format; @@ -1958,11 +2001,11 @@ void WatchHandler::setFormat(const QByteArray &type0, int format) int WatchHandler::format(const QByteArray &iname) const { - int result = -1; + int result = AutomaticFormat; if (const WatchData *item = m_model->findItem(iname)) { - int result = theIndividualFormats.value(item->iname, -1); - if (result == -1) - result = theTypeFormats.value(stripForFormat(item->type), -1); + int result = theIndividualFormats.value(item->iname, AutomaticFormat); + if (result == AutomaticFormat) + result = theTypeFormats.value(stripForFormat(item->type), AutomaticFormat); } return result; } @@ -1990,10 +2033,13 @@ QByteArray WatchHandler::typeFormatRequests() const QHashIterator<QByteArray, int> it(theTypeFormats); while (it.hasNext()) { it.next(); - ba.append(it.key().toHex()); - ba.append('='); - ba.append(QByteArray::number(it.value())); - ba.append(','); + const int format = it.value(); + if (format >= RawFormat && format < ArtificialFormatBase) { + ba.append(it.key().toHex()); + ba.append('='); + ba.append(QByteArray::number(format)); + ba.append(','); + } } ba.chop(1); } @@ -2007,10 +2053,13 @@ QByteArray WatchHandler::individualFormatRequests() const QHashIterator<QByteArray, int> it(theIndividualFormats); while (it.hasNext()) { it.next(); - ba.append(it.key()); - ba.append('='); - ba.append(QByteArray::number(it.value())); - ba.append(','); + const int format = it.value(); + if (format >= RawFormat && format < ArtificialFormatBase) { + ba.append(it.key()); + ba.append('='); + ba.append(QByteArray::number(it.value())); + ba.append(','); + } } ba.chop(1); } @@ -2029,12 +2078,12 @@ QString WatchHandler::editorContents() return contents; } -void WatchHandler::setTypeFormats(const TypeFormats &typeFormats) +void WatchHandler::setTypeFormats(const DumperTypeFormats &typeFormats) { m_model->m_reportedTypeFormats = typeFormats; } -TypeFormats WatchHandler::typeFormats() const +DumperTypeFormats WatchHandler::typeFormats() const { 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(); qSort(l.begin(), l.end()); 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); } if (dlg.exec()) @@ -2107,6 +2156,30 @@ QSet<QByteArray> WatchHandler::expandedINames() const 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 Debugger diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h index 0e610c40f3..3f446102ef 100644 --- a/src/plugins/debugger/watchhandler.h +++ b/src/plugins/debugger/watchhandler.h @@ -32,11 +32,76 @@ #include "watchdata.h" -#include <QPointer> #include <QAbstractItemModel> +#include <QPointer> +#include <QVector> 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 { class DebuggerEngine; @@ -55,15 +120,7 @@ public: QByteArray varList; }; -typedef QHash<QString, QStringList> TypeFormats; - -enum IntegerFormat -{ - DecimalFormat = 0, // Keep that at 0 as default. - HexadecimalFormat, - BinaryFormat, - OctalFormat -}; +typedef QHash<QString, QStringList> DumperTypeFormats; // Type name -> Dumper Formats class WatchHandler : public QObject { @@ -104,8 +161,8 @@ public: int format(const QByteArray &iname) const; void addTypeFormats(const QByteArray &type, const QStringList &formats); - void setTypeFormats(const TypeFormats &typeFormats); - TypeFormats typeFormats() const; + void setTypeFormats(const DumperTypeFormats &typeFormats); + DumperTypeFormats typeFormats() const; void setUnprintableBase(int base); static int unprintableBase(); diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp index b3d033b4d3..7b62198211 100644 --- a/src/plugins/debugger/watchwindow.cpp +++ b/src/plugins/debugger/watchwindow.cpp @@ -59,6 +59,8 @@ #include <QInputDialog> #include <QMessageBox> +Q_DECLARE_METATYPE(QModelIndex) + ///////////////////////////////////////////////////////////////////// // // WatchDelegate @@ -68,6 +70,8 @@ namespace Debugger { namespace Internal { +const char CurrentIndex[] = "CurrentIndex"; + static DebuggerEngine *currentEngine() { return debuggerCore()->currentEngine(); @@ -584,109 +588,164 @@ static void copyToClipboard(const QString &clipboardText) clipboard->setText(clipboardText, QClipboard::Clipboard); } -void WatchTreeView::contextMenuEvent(QContextMenuEvent *ev) +void WatchTreeView::fillFormatMenu(QMenu *formatMenu, const QModelIndex &mi) { + QTC_CHECK(mi.isValid()); + DebuggerEngine *engine = currentEngine(); WatchHandler *handler = engine->watchHandler(); - const QModelIndexList active = activeRows(); - const QModelIndex idx = indexAt(ev->pos()); - const QModelIndex mi0 = idx.sibling(idx.row(), 0); - const QModelIndex mi1 = idx.sibling(idx.row(), 1); - const QModelIndex mi2 = idx.sibling(idx.row(), 2); - const quint64 address = addressOf(mi0); - const uint size = sizeOf(mi0); - const quint64 pointerAddress = pointerAddressOf(mi0); - const QString exp = mi0.data(LocalsExpressionRole).toString(); - const QString name = mi0.data(LocalsNameRole).toString(); + const QModelIndex mi2 = mi.sibling(mi.row(), 2); const QString type = mi2.data().toString(); - // Offer to open address pointed to or variable address. - const bool createPointerActions = pointerAddress && pointerAddress != address; - - const QStringList alternativeFormats = - mi0.data(LocalsTypeFormatListRole).toStringList(); + const TypeFormatList alternativeFormats = + mi.data(LocalsTypeFormatListRole).value<TypeFormatList>(); int typeFormat = - mi0.data(LocalsTypeFormatRole).toInt(); - if (typeFormat >= alternativeFormats.size()) - typeFormat = -1; + mi.data(LocalsTypeFormatRole).toInt(); const int individualFormat = - mi0.data(LocalsIndividualFormatRole).toInt(); + mi.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...")); + formatMenu->setTitle(tr("Change Local Display Format...")); showUnprintableUnicode = - formatMenu.addAction(tr("Treat All Characters as Printable")); + 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")); + 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")); + 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")); + 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); + 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) +{ + DebuggerEngine *engine = currentEngine(); + WatchHandler *handler = engine->watchHandler(); + + const QModelIndex idx = indexAt(ev->pos()); + const QModelIndex mi0 = idx.sibling(idx.row(), 0); + const QModelIndex mi1 = idx.sibling(idx.row(), 1); + const QModelIndex mi2 = idx.sibling(idx.row(), 2); + const quint64 address = addressOf(mi0); + const uint size = sizeOf(mi0); + const quint64 pointerAddress = pointerAddressOf(mi0); + const QString exp = mi0.data(LocalsExpressionRole).toString(); + const QString name = mi0.data(LocalsNameRole).toString(); + const QString type = mi2.data().toString(); + + // Offer to open address pointed to or variable address. + const bool createPointerActions = pointerAddress && pointerAddress != address; + const bool actionsEnabled = engine->debuggerActionsEnabled(); const bool canHandleWatches = engine->hasCapability(AddWatcherCapability); const DebuggerState state = engine->state(); @@ -782,6 +841,14 @@ void WatchTreeView::contextMenuEvent(QContextMenuEvent *ev) 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; memoryMenu.setTitle(tr("Open Memory Editor...")); QAction *actOpenMemoryEditAtObjectAddress = new QAction(&memoryMenu); @@ -839,7 +906,6 @@ void WatchTreeView::contextMenuEvent(QContextMenuEvent *ev) QAction *actCopyValue = new QAction(tr("Copy Value to Clipboard"), &menu); actCopyValue->setEnabled(idx.isValid()); - menu.addAction(actInsertNewWatchItem); menu.addAction(actSelectWidgetToWatch); menu.addMenu(&formatMenu); @@ -914,43 +980,11 @@ void WatchTreeView::contextMenuEvent(QContextMenuEvent *ev) copyToClipboard(mi1.data().toString()); } else if (act == actRemoveWatches) { 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) { QString contents = handler->editorContents(); 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) { 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); - } } } diff --git a/src/plugins/debugger/watchwindow.h b/src/plugins/debugger/watchwindow.h index f443fb481b..86decee94f 100644 --- a/src/plugins/debugger/watchwindow.h +++ b/src/plugins/debugger/watchwindow.h @@ -54,6 +54,8 @@ public: void rowActivated(const QModelIndex &index); void reset(); + void fillFormatMenu(QMenu *, const QModelIndex &mi); + public slots: void watchExpression(const QString &exp); void watchExpression(const QString &exp, const QString &name); @@ -62,11 +64,19 @@ public slots: signals: void currentIndexChanged(const QModelIndex ¤tIndex); -private: - Q_SLOT void resetHelper(); - Q_SLOT void expandNode(const QModelIndex &idx); - Q_SLOT void collapseNode(const QModelIndex &idx); +private slots: + void resetHelper(); + void expandNode(const QModelIndex &idx); + void collapseNode(const QModelIndex &idx); + + void onClearIndividualFormat(); + void onClearTypeFormat(); + void onShowUnprintable(); + void onTypeFormatChange(); + void onIndividualFormatChange(); + +private: void keyPressEvent(QKeyEvent *ev); void contextMenuEvent(QContextMenuEvent *ev); void dragEnterEvent(QDragEnterEvent *ev); -- GitLab