Commit ca808cc7 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Debugger: Add special widgets for editing watch values.

Create delegate widgets with validation for bool/int/float
types, using a QVariant-type modelData property.
Also fix register editing to be validated for quint64 values
and editing longer values as hex bigints. Anything else is not
editable.
parent 596e6030
......@@ -52,7 +52,8 @@ HEADERS += breakhandler.h \
watchhandler.h \
watchutils.h \
watchwindow.h \
threadshandler.h
threadshandler.h \
watchdelegatewidgets.h
SOURCES += breakhandler.cpp \
breakpoint.cpp \
......@@ -88,7 +89,8 @@ SOURCES += breakhandler.cpp \
watchhandler.cpp \
watchutils.cpp \
watchwindow.cpp \
stackframe.cpp
stackframe.cpp \
watchdelegatewidgets.cpp
FORMS += attachexternaldialog.ui \
attachcoredialog.ui \
......
......@@ -223,6 +223,8 @@ enum ModelRoles
// Locals and Watchers
LocalsINameRole,
LocalsEditTypeRole, // A QVariant::type describing the item
LocalsIntegerBaseRole, // Number base 16, 10, 8, 2
LocalsExpressionRole,
LocalsExpandedRole, // The preferred expanded state to the view
LocalsTypeFormatListRole,
......@@ -265,6 +267,7 @@ enum ModelRoles
RegisterNumberBaseRole, // Currently used number base
RegisterAddressRole, // Start value for opening memory view
RegisterChangedRole, // Used for painting changed values
RegisterBigNumberRole, // Register is a big integer that cannot be handled as quint64.
RequestSetRegisterRole,
RequestReloadRegistersRole,
......
......@@ -33,6 +33,7 @@
#include "debuggeragents.h"
#include "debuggerconstants.h"
#include "debuggerengine.h"
#include "watchdelegatewidgets.h"
#include <utils/qtcassert.h>
......@@ -68,17 +69,27 @@ int RegisterHandler::columnCount(const QModelIndex &parent) const
return parent.isValid() ? 0 : 2;
}
inline QString RegisterHandler::value(const Register &reg, bool padded) const
// Editor value: Preferably number, else string.
QVariant RegisterHandler::editValue(const Register &reg)
{
bool ok = true;
// Try to convert to number?
const qulonglong value = reg.value.toULongLong(&ok, 0); // Autodetect format
if (ok)
return QString::fromAscii("%1").arg(value, (padded ? m_strlen : 0), m_base);
// Cannot convert, return raw string.
if (padded && reg.value.size() < m_strlen)
return QString(m_strlen - reg.value.size(), QLatin1Char(' ')) + reg.value;
return reg.value;
return QVariant(value);
return QVariant(reg.value);
}
// Editor value: Preferably padded number, else padded string.
inline QString RegisterHandler::displayValue(const Register &reg) const
{
const QVariant editV = RegisterHandler::editValue(reg);
if (editV.type() == QVariant::ULongLong)
return QString::fromAscii("%1").arg(editV.toULongLong(), m_strlen, m_base);
const QString stringValue = editV.toString();
if (stringValue.size() < m_strlen)
return QString(m_strlen - stringValue.size(), QLatin1Char(' ')) + reg.value;
return stringValue;
}
QVariant RegisterHandler::data(const QModelIndex &index, int role) const
......@@ -92,9 +103,6 @@ QVariant RegisterHandler::data(const QModelIndex &index, int role) const
case EngineActionsEnabledRole:
return m_engine->debuggerActionsEnabled();
case RegisterNumberBaseRole:
return m_base;
}
if (!index.isValid() || index.row() >= m_registers.size())
......@@ -105,11 +113,9 @@ QVariant RegisterHandler::data(const QModelIndex &index, int role) const
switch (role) {
case RegisterAddressRole: {
// Return some address associated with the register. Autodetect format
bool ok = true;
qulonglong value = reg.value.toULongLong(&ok, 0);
return ok ? QVariant(QString::fromLatin1("0x") + QString::number(value, 16)) : QVariant();
const QVariant editV = RegisterHandler::editValue(reg);
return editV.type() == QVariant::ULongLong ? editV : QVariant();
}
break;
case Qt::DisplayRole:
switch (index.column()) {
......@@ -118,14 +124,18 @@ QVariant RegisterHandler::data(const QModelIndex &index, int role) const
return QVariant(padding + reg.name + padding);
}
case 1: // Display: Pad value for alignment
return value(reg, true);
return displayValue(reg);
} // switch column
case Qt::EditRole: // Edit: Unpadded for editing
return value(reg, false);
return RegisterHandler::editValue(reg);
case Qt::TextAlignmentRole:
return index.column() == 1 ? QVariant(Qt::AlignRight) : QVariant();
case RegisterChangedRole:
return QVariant(reg.changed);
case RegisterBigNumberRole: // Editor: Can it be handled as quint64?
return editValue(reg).type() != QVariant::ULongLong;
case RegisterNumberBaseRole: // Big integers are assumed to be hexadecimal
return editValue(reg).type() == QVariant::ULongLong ? m_base : 16;
default:
break;
}
......@@ -146,22 +156,14 @@ QVariant RegisterHandler::headerData(int section, Qt::Orientation orientation,
Qt::ItemFlags RegisterHandler::flags(const QModelIndex &idx) const
{
using namespace Qt;
if (!idx.isValid())
return ItemFlags();
static const ItemFlags notEditable =
ItemIsSelectable
// | ItemIsDragEnabled
// | ItemIsDropEnabled
| ItemIsEnabled;
static const ItemFlags editable = notEditable | ItemIsEditable;
return Qt::ItemFlags();
if (idx.column() == 1)
return editable; // locals and watcher values are editable
return notEditable;
const Qt::ItemFlags notEditable = Qt::ItemIsSelectable|Qt::ItemIsEnabled;
// Can edit registers if they are hex numbers and not arrays.
if (idx.column() == 1 && IntegerWatchLineEdit::isUnsignedHexNumber(m_registers.at(idx.row()).value))
return notEditable | Qt::ItemIsEditable;
return notEditable;
}
bool RegisterHandler::setData(const QModelIndex &index, const QVariant &value, int role)
......
......@@ -73,7 +73,8 @@ public:
private:
void calculateWidth();
inline QString value(const Register &reg, bool padded) const;
inline QString displayValue(const Register &reg) const;
static QVariant editValue(const Register &reg);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
......
......@@ -32,6 +32,8 @@
#include "debuggeractions.h"
#include "debuggerconstants.h"
#include "watchdelegatewidgets.h"
#include <utils/qtcassert.h>
#include <utils/savedaction.h>
......@@ -66,31 +68,31 @@ public:
{}
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
const QModelIndex &) const
const QModelIndex &index) const
{
QLineEdit *lineEdit = new QLineEdit(parent);
IntegerWatchLineEdit *lineEdit = new IntegerWatchLineEdit(parent);
lineEdit->setBase(index.data(RegisterNumberBaseRole).toInt());
lineEdit->setBigInt(index.data(RegisterBigNumberRole).toBool());
lineEdit->setSigned(false);
lineEdit->setAlignment(Qt::AlignRight);
return lineEdit;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const
{
QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor);
IntegerWatchLineEdit *lineEdit = qobject_cast<IntegerWatchLineEdit *>(editor);
QTC_ASSERT(lineEdit, return);
lineEdit->setText(index.data(Qt::EditRole).toString());
lineEdit->setModelData(index.data(Qt::EditRole));
}
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
void setModelData(QWidget *editor, QAbstractItemModel *, const QModelIndex &index) const
{
Q_UNUSED(model);
//qDebug() << "SET MODEL DATA";
QLineEdit *lineEdit = qobject_cast<QLineEdit*>(editor);
if (index.column() != 1)
return;
IntegerWatchLineEdit *lineEdit = qobject_cast<IntegerWatchLineEdit*>(editor);
QTC_ASSERT(lineEdit, return);
QString value = lineEdit->text();
//model->setData(index, value, Qt::EditRole);
if (index.column() == 1)
m_owner->model()->setData(index, value, RequestSetRegisterRole);
m_owner->model()->setData(index, lineEdit->modelData(), RequestSetRegisterRole);
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
......
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** 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.
**
** GNU Lesser General Public License Usage
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "watchdelegatewidgets.h"
#include <QtGui/QDoubleValidator>
#include <QtCore/QDebug>
#include <utils/qtcassert.h>
enum { debug = 0 };
namespace Debugger {
namespace Internal {
// Basic watch line edit.
WatchLineEdit::WatchLineEdit(QWidget *parent) : QLineEdit(parent)
{
}
QVariant WatchLineEdit::modelData() const
{
return QVariant(text());
}
void WatchLineEdit::setModelData(const QVariant &v)
{
if (debug)
qDebug("WatchLineEdit::setModelData(%s, '%s')", v.typeName(), qPrintable(v.toString()));
setText(v.toString());
}
/* ------ IntegerWatchLineEdit helpers:
* Integer validator using different number bases. */
class IntegerValidator : public QValidator {
public:
explicit IntegerValidator(QObject *parent);
virtual State validate(QString &, int &) const;
int base() const { return m_base; }
void setBase(int b) { m_base = b; }
bool isSigned() const { return m_signed; }
void setSigned(bool s) { m_signed = s; }
bool isBigInt() const { return m_bigInt; }
void setBigInt(bool b) { m_bigInt = b; }
static State validateEntry(const QString &s, int base, bool signedV, bool bigInt);
private:
static inline bool isCharAcceptable(const QChar &c, int base);
int m_base;
bool m_signed;
bool m_bigInt;
};
IntegerValidator::IntegerValidator(QObject *parent) :
QValidator(parent), m_base(10), m_signed(true), m_bigInt(false)
{
}
// Check valid digits depending on base.
bool IntegerValidator::isCharAcceptable(const QChar &c, int base)
{
if (c.isLetter())
return base == 16 && c.toLower().toAscii() <= 'f';
if (!c.isDigit())
return false;
const int digit = c.toAscii() - '0';
if (base == 8 && digit > 7)
return false;
if (base == 2 && digit > 1)
return false;
return true;
}
QValidator::State IntegerValidator::validate(QString &s, int &) const
{
return IntegerValidator::validateEntry(s, m_base, m_signed, m_bigInt);
}
QValidator::State IntegerValidator::validateEntry(const QString &s, int base, bool signedV, bool bigInt)
{
const int size = s.size();
if (!size)
return QValidator::Intermediate;
int pos = 0;
// Skip sign.
if (signedV && s.at(pos) == '-') {
pos++;
if (pos == size)
return QValidator::Intermediate;
}
// Hexadecimal: '0x'?
if (base == 16 && pos + 2 <= size
&& s.at(pos) == QLatin1Char('0') && s.at(pos + 1) == QLatin1Char('x')) {
pos+= 2;
if (pos == size)
return QValidator::Intermediate;
}
// Check characters past sign.
for (; pos < size; pos++)
if (!isCharAcceptable(s.at(pos), base))
return QValidator::Invalid;
// Check conversion unless big integer
if (bigInt)
return QValidator::Acceptable;
bool ok;
if (signedV) {
s.toLongLong(&ok, base);
} else {
s.toULongLong(&ok, base);
}
return ok ? QValidator::Acceptable : QValidator::Intermediate;
}
IntegerWatchLineEdit::IntegerWatchLineEdit(QWidget *parent) :
WatchLineEdit(parent),
m_validator(new IntegerValidator(this))
{
setValidator(m_validator);
}
bool IntegerWatchLineEdit::isUnsignedHexNumber(const QString &v)
{
return IntegerValidator::validateEntry(v, 16, false, true) == QValidator::Acceptable;
}
int IntegerWatchLineEdit::base() const
{
return m_validator->base();
}
void IntegerWatchLineEdit::setBase(int b)
{
QTC_ASSERT(b, return; )
m_validator->setBase(b);
}
bool IntegerWatchLineEdit::isSigned() const
{
return m_validator->isSigned();
}
void IntegerWatchLineEdit::setSigned(bool s)
{
m_validator->setSigned(s);
}
bool IntegerWatchLineEdit::isBigInt() const
{
return m_validator->isBigInt();
}
void IntegerWatchLineEdit::setBigInt(bool b)
{
m_validator->setBigInt(b);
}
QVariant IntegerWatchLineEdit::modelDataI() const
{
if (isBigInt()) // Big integer: Plain text
return QVariant(text());
bool ok;
if (isSigned()) {
const qint64 value = text().toLongLong(&ok, base());
if (ok)
return QVariant(value);
} else {
const quint64 value = text().toULongLong(&ok, base());
if (ok)
return QVariant(value);
}
return QVariant();
}
QVariant IntegerWatchLineEdit::modelData() const
{
const QVariant data = modelDataI();
if (debug)
qDebug("IntegerLineEdit::modelData(): base=%d, signed=%d, bigint=%d returns %s '%s'",
base(), isSigned(), isBigInt(), data.typeName(), qPrintable(data.toString()));
return data;
}
void IntegerWatchLineEdit::setModelData(const QVariant &v)
{
if (debug)
qDebug(">IntegerLineEdit::setModelData(%s, '%s'): base=%d, signed=%d, bigint=%d",
v.typeName(), qPrintable(v.toString()),
base(), isSigned(), isBigInt());
switch (v.type()) {
case QVariant::Int:
case QVariant::LongLong: {
const qint64 iv = v.toLongLong();
setSigned(true);
setText(QString::number(iv, base()));
}
break;
case QVariant::UInt:
case QVariant::ULongLong: {
const quint64 iv = v.toULongLong();
setSigned(false);
setText(QString::number(iv, base()));
}
break;
case QVariant::ByteArray:
setNumberText(QString::fromAscii(v.toByteArray()));
break;
case QVariant::String:
setNumberText(v.toString());
break;
default:
qWarning("Invalid value (%s) passed to IntegerLineEdit::setModelData",
v.typeName());
setText(QString(QLatin1Char('0')));
break;
}
if (debug)
qDebug("<IntegerLineEdit::setModelData(): base=%d, signed=%d, bigint=%d",
base(), isSigned(), isBigInt());
}
void IntegerWatchLineEdit::setNumberText(const QString &t)
{
setText(t);
}
// ------------- FloatWatchLineEdit
FloatWatchLineEdit::FloatWatchLineEdit(QWidget *parent) :
WatchLineEdit(parent)
{
setValidator(new QDoubleValidator(this));
}
QVariant FloatWatchLineEdit::modelData() const
{
return QVariant(text().toDouble());
}
void FloatWatchLineEdit::setModelData(const QVariant &v)
{
if (debug)
qDebug("FloatWatchLineEdit::setModelData(%s, '%s')",
v.typeName(), qPrintable(v.toString()));
switch (v.type()) {
break;
case QVariant::Double:
case QVariant::String:
setText(v.toString());
break;
case QVariant::ByteArray:
setText(QString::fromAscii(v.toByteArray()));
break;
default:
qWarning("Invalid value (%s) passed to FloatWatchLineEdit::setModelData",
v.typeName());
setText(QString::number(0.0));
break;
}
}
WatchLineEdit *WatchLineEdit::create(QVariant::Type t, QWidget *parent)
{
switch (t) {
case QVariant::Bool:
case QVariant::Int:
case QVariant::UInt:
case QVariant::LongLong:
case QVariant::ULongLong:
return new IntegerWatchLineEdit(parent);
break;
case QVariant::Double:
return new FloatWatchLineEdit(parent);
default:
break;
}
return new WatchLineEdit(parent);
}
BooleanComboBox::BooleanComboBox(QWidget *parent) : QComboBox(parent)
{
QStringList items;
items << QLatin1String("false") << QLatin1String("true");
addItems(items);
}
QVariant BooleanComboBox::modelData() const
{
// As not to confuse debuggers with 'true', 'false', we return integers 1,0.
const int rc = currentIndex() == 1 ? 1 : 0;
return QVariant(rc);
}
void BooleanComboBox::setModelData(const QVariant &v)
{
if (debug)
qDebug("BooleanComboBox::setModelData(%s, '%s')", v.typeName(), qPrintable(v.toString()));
bool value = false;
switch (v.type()) {
case QVariant::Bool:
value = v.toBool();
break;
case QVariant::Int:
case QVariant::UInt:
case QVariant::LongLong:
case QVariant::ULongLong:
value = v.toInt() != 0;
break;
default:
qWarning("Invalid value (%s) passed to BooleanComboBox::setModelData", v.typeName());
break;
}
setCurrentIndex(value ? 1 : 0);
}
} // namespace Internal
} // namespace Debugger
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** 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.
**
** GNU Lesser General Public License Usage
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef WATCHDELEGATEEDITS_H
#define WATCHDELEGATEEDITS_H
#include <QtGui/QLineEdit>
#include <QtGui/QComboBox>
#include <QtCore/QVariant>
namespace Debugger {
namespace Internal {