forked from qt-creator/qt-creator
Debugger: Better handling of the tabbed extra views
Closing with the [x] now resets the Display mode of the associated iname, and the view hides if there are no visible tabs left. Also, remove the long-unused DisplayProcess format. Change-Id: Ibd3308549af75e345c672c07f6714d26e7196e5a Reviewed-by: Christian Stenger <christian.stenger@digia.com>
This commit is contained in:
@@ -1515,10 +1515,9 @@ StopDisplay, \
|
|||||||
DisplayImageData, \
|
DisplayImageData, \
|
||||||
DisplayUtf16String, \
|
DisplayUtf16String, \
|
||||||
DisplayImageFile, \
|
DisplayImageFile, \
|
||||||
DisplayProcess, \
|
|
||||||
DisplayLatin1String, \
|
DisplayLatin1String, \
|
||||||
DisplayUtf8String \
|
DisplayUtf8String \
|
||||||
= range(7)
|
= range(6)
|
||||||
|
|
||||||
|
|
||||||
def mapForms():
|
def mapForms():
|
||||||
|
|||||||
@@ -2866,9 +2866,8 @@ bool dumpEditValue(const SymbolGroupNode *n, const SymbolGroupValueContext &,
|
|||||||
DisplayImageData = 1,
|
DisplayImageData = 1,
|
||||||
DisplayUtf16String = 2,
|
DisplayUtf16String = 2,
|
||||||
DisplayImageFile = 3,
|
DisplayImageFile = 3,
|
||||||
DisplayProcess = 4,
|
DisplayLatin1String = 4,
|
||||||
DisplayLatin1String = 5,
|
DisplayUtf8String = 5
|
||||||
DisplayUtf8String = 6
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Formats {
|
enum Formats {
|
||||||
|
|||||||
@@ -210,9 +210,8 @@ enum DebuggerDisplay {
|
|||||||
DisplayImageData = 1,
|
DisplayImageData = 1,
|
||||||
DisplayUtf16String = 2,
|
DisplayUtf16String = 2,
|
||||||
DisplayImageFile = 3,
|
DisplayImageFile = 3,
|
||||||
DisplayProcess = 4,
|
DisplayLatin1String = 4,
|
||||||
DisplayLatin1String = 5,
|
DisplayUtf8String = 5
|
||||||
DisplayUtf8String = 6
|
|
||||||
};
|
};
|
||||||
// Decode string data as returned by the dumper helpers.
|
// Decode string data as returned by the dumper helpers.
|
||||||
QString decodeData(const QByteArray &baIn, int encoding);
|
QString decodeData(const QByteArray &baIn, int encoding);
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ static QHash<QByteArray, int> theTypeFormats;
|
|||||||
static QHash<QByteArray, int> theIndividualFormats;
|
static QHash<QByteArray, int> theIndividualFormats;
|
||||||
static int theUnprintableBase = -1;
|
static int theUnprintableBase = -1;
|
||||||
|
|
||||||
|
const char INameProperty[] = "INameProperty";
|
||||||
|
const char KeyProperty[] = "KeyProperty";
|
||||||
|
|
||||||
static QByteArray stripForFormat(const QByteArray &ba)
|
static QByteArray stripForFormat(const QByteArray &ba)
|
||||||
{
|
{
|
||||||
QByteArray res;
|
QByteArray res;
|
||||||
@@ -139,26 +142,85 @@ private:
|
|||||||
//
|
//
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class SeparateViewWidget : public QTabWidget
|
class SeparatedView : public QTabWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SeparateViewWidget(QWidget *parent) : QTabWidget(parent)
|
SeparatedView() : QTabWidget(debuggerCore()->mainWindow())
|
||||||
{
|
{
|
||||||
setTabsClosable(true);
|
setTabsClosable(true);
|
||||||
connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeTab(int)));
|
connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeTab(int)));
|
||||||
setWindowFlags(windowFlags() | Qt::Window);
|
setWindowFlags(windowFlags() | Qt::Window);
|
||||||
setWindowTitle(WatchHandler::tr("Debugger - Qt Creator"));
|
setWindowTitle(WatchHandler::tr("Debugger - Qt Creator"));
|
||||||
|
|
||||||
|
QVariant geometry = DebuggerCore::sessionValue("DebuggerSeparateWidgetGeometry");
|
||||||
|
if (geometry.isValid())
|
||||||
|
setGeometry(geometry.toRect());
|
||||||
}
|
}
|
||||||
|
|
||||||
public slots:
|
~SeparatedView()
|
||||||
void closeTab(int index)
|
|
||||||
{
|
{
|
||||||
|
DebuggerCore::setSessionValue("DebuggerSeparateWidgetGeometry", geometry());
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeObject(const QByteArray &key)
|
||||||
|
{
|
||||||
|
if (QWidget *w = findWidget(key)) {
|
||||||
|
removeTab(indexOf(w));
|
||||||
|
sanitize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SLOT void closeTab(int index)
|
||||||
|
{
|
||||||
|
if (QObject *o = widget(index)) {
|
||||||
|
QByteArray iname = o->property(INameProperty).toByteArray();
|
||||||
|
theIndividualFormats.remove(iname);
|
||||||
|
}
|
||||||
removeTab(index);
|
removeTab(index);
|
||||||
|
sanitize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sanitize()
|
||||||
|
{
|
||||||
|
if (count() == 0)
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget *findWidget(const QByteArray &needle)
|
||||||
|
{
|
||||||
|
for (int i = count(); --i >= 0; ) {
|
||||||
|
QWidget *w = widget(i);
|
||||||
|
QByteArray key = w->property(KeyProperty).toByteArray();
|
||||||
|
if (key == needle)
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T> T *prepareObject(const QByteArray &key, const QString &title)
|
||||||
|
{
|
||||||
|
T *t = 0;
|
||||||
|
if (QWidget *w = findWidget(key)) {
|
||||||
|
t = qobject_cast<T *>(w);
|
||||||
|
if (!t)
|
||||||
|
removeTab(indexOf(w));
|
||||||
|
}
|
||||||
|
if (!t) {
|
||||||
|
t = new T;
|
||||||
|
t->setProperty(KeyProperty, key);
|
||||||
|
addTab(t, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentWidget(t);
|
||||||
|
show();
|
||||||
|
raise();
|
||||||
|
return t;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class WatchModel : public QAbstractItemModel
|
class WatchModel : public QAbstractItemModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -253,10 +315,6 @@ private:
|
|||||||
QStringList dumperTypeFormatList(const WatchData &data) const;
|
QStringList dumperTypeFormatList(const WatchData &data) const;
|
||||||
DumperTypeFormats m_reportedTypeFormats;
|
DumperTypeFormats m_reportedTypeFormats;
|
||||||
|
|
||||||
// QWidgets and QProcesses taking care of special displays.
|
|
||||||
typedef QMap<QByteArray, QPointer<QObject> > EditHandlers;
|
|
||||||
EditHandlers m_editHandlers;
|
|
||||||
|
|
||||||
WatchItem *createItem(const QByteArray &iname);
|
WatchItem *createItem(const QByteArray &iname);
|
||||||
WatchItem *createItem(const WatchData &data);
|
WatchItem *createItem(const WatchData &data);
|
||||||
void assignData(WatchItem *item, const WatchData &data);
|
void assignData(WatchItem *item, const WatchData &data);
|
||||||
@@ -1537,23 +1595,19 @@ void WatchModel::setCurrentItem(const QByteArray &iname)
|
|||||||
|
|
||||||
WatchHandler::WatchHandler(DebuggerEngine *engine)
|
WatchHandler::WatchHandler(DebuggerEngine *engine)
|
||||||
{
|
{
|
||||||
m_separateWindow = 0;
|
|
||||||
m_engine = engine;
|
m_engine = engine;
|
||||||
m_watcherCounter = DebuggerCore::sessionValue("Watchers").toStringList().count();
|
m_watcherCounter = DebuggerCore::sessionValue("Watchers").toStringList().count();
|
||||||
m_model = new WatchModel(this);
|
m_model = new WatchModel(this);
|
||||||
m_contentsValid = false;
|
m_contentsValid = false;
|
||||||
m_contentsValid = true; // FIXME
|
m_contentsValid = true; // FIXME
|
||||||
m_resetLocationScheduled = false;
|
m_resetLocationScheduled = false;
|
||||||
|
m_separatedView = new SeparatedView;
|
||||||
}
|
}
|
||||||
|
|
||||||
WatchHandler::~WatchHandler()
|
WatchHandler::~WatchHandler()
|
||||||
{
|
{
|
||||||
if (m_separateWindow) {
|
delete m_separatedView;
|
||||||
DebuggerCore::setSessionValue("DebuggerSeparateWidgetGeometry",
|
m_separatedView = 0;
|
||||||
m_separateWindow->geometry());
|
|
||||||
delete m_separateWindow;
|
|
||||||
m_separateWindow = 0;
|
|
||||||
}
|
|
||||||
// Do it manually to prevent calling back in model destructors
|
// Do it manually to prevent calling back in model destructors
|
||||||
// after m_cache is destroyed.
|
// after m_cache is destroyed.
|
||||||
delete m_model;
|
delete m_model;
|
||||||
@@ -1566,14 +1620,7 @@ void WatchHandler::cleanup()
|
|||||||
theWatcherNames.remove(QByteArray());
|
theWatcherNames.remove(QByteArray());
|
||||||
m_model->reinitialize();
|
m_model->reinitialize();
|
||||||
m_model->m_fetchTriggered.clear();
|
m_model->m_fetchTriggered.clear();
|
||||||
#if 1
|
m_separatedView->hide();
|
||||||
for (WatchModel::EditHandlers::ConstIterator it = m_model->m_editHandlers.begin();
|
|
||||||
it != m_model->m_editHandlers.end(); ++it) {
|
|
||||||
if (!it.value().isNull())
|
|
||||||
delete it.value();
|
|
||||||
}
|
|
||||||
m_model->m_editHandlers.clear();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WatchHandler::insertIncompleteData(const WatchData &data)
|
void WatchHandler::insertIncompleteData(const WatchData &data)
|
||||||
@@ -1717,68 +1764,15 @@ static void swapEndian(char *d, int nchar)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int indexOf(const QTabWidget *tw, const QWidget *w)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < tw->count(); ++i)
|
|
||||||
if (tw->widget(i) == w)
|
|
||||||
return i;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WatchHandler::removeSeparateWidget(QObject *o)
|
|
||||||
{
|
|
||||||
const int index = o && o->isWidgetType() && !m_separateWindow.isNull() ?
|
|
||||||
indexOf(m_separateWindow, static_cast<QWidget *>(o)) : -1;
|
|
||||||
if (index != -1) {
|
|
||||||
m_separateWindow->removeTab(index);
|
|
||||||
if (!m_separateWindow->count())
|
|
||||||
m_separateWindow->hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WatchHandler::showSeparateWidget(QWidget *w)
|
|
||||||
{
|
|
||||||
if (m_separateWindow.isNull()) {
|
|
||||||
m_separateWindow = new SeparateViewWidget(debuggerCore()->mainWindow());
|
|
||||||
QVariant geometry = DebuggerCore::sessionValue("DebuggerSeparateWidgetGeometry");
|
|
||||||
if (geometry.isValid())
|
|
||||||
m_separateWindow->setGeometry(geometry.toRect());
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = indexOf(m_separateWindow, w);
|
|
||||||
if (index != -1)
|
|
||||||
m_separateWindow->setTabText(index, w->windowTitle());
|
|
||||||
else
|
|
||||||
index = m_separateWindow->addTab(w, w->windowTitle());
|
|
||||||
m_separateWindow->setCurrentIndex(index);
|
|
||||||
m_separateWindow->show();
|
|
||||||
m_separateWindow->raise();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WatchHandler::showEditValue(const WatchData &data)
|
void WatchHandler::showEditValue(const WatchData &data)
|
||||||
{
|
{
|
||||||
const QByteArray key = data.address ? data.hexAddress() : data.iname;
|
const QByteArray key = data.address ? data.hexAddress() : data.iname;
|
||||||
QObject *w = m_model->m_editHandlers.value(key);
|
|
||||||
switch (data.editformat) {
|
switch (data.editformat) {
|
||||||
case StopDisplay:
|
case StopDisplay:
|
||||||
m_model->m_editHandlers.remove(data.iname);
|
m_separatedView->removeObject(data.iname);
|
||||||
delete w;
|
|
||||||
break;
|
break;
|
||||||
case DisplayImageData:
|
case DisplayImageData:
|
||||||
case DisplayImageFile: { // QImage
|
case DisplayImageFile: { // QImage
|
||||||
ImageViewer *l = qobject_cast<ImageViewer *>(w);
|
|
||||||
if (!l) {
|
|
||||||
removeSeparateWidget(w);
|
|
||||||
delete w;
|
|
||||||
l = new ImageViewer;
|
|
||||||
const QString title = data.address ?
|
|
||||||
tr("%1 Object at %2").arg(QLatin1String(data.type),
|
|
||||||
QLatin1String(data.hexAddress())) :
|
|
||||||
tr("%1 Object at Unknown Address").arg(QLatin1String(data.type));
|
|
||||||
l->setWindowTitle(title);
|
|
||||||
showSeparateWidget(l);
|
|
||||||
m_model->m_editHandlers[key] = l;
|
|
||||||
}
|
|
||||||
int width = 0, height = 0, nbytes = 0, format = 0;
|
int width = 0, height = 0, nbytes = 0, format = 0;
|
||||||
QByteArray ba;
|
QByteArray ba;
|
||||||
uchar *bits = 0;
|
uchar *bits = 0;
|
||||||
@@ -1809,20 +1803,18 @@ void WatchHandler::showEditValue(const WatchData &data)
|
|||||||
QTC_ASSERT(0 < format && format < 32, return);
|
QTC_ASSERT(0 < format && format < 32, return);
|
||||||
QImage im(width, height, QImage::Format(format));
|
QImage im(width, height, QImage::Format(format));
|
||||||
qMemCopy(im.bits(), bits, nbytes);
|
qMemCopy(im.bits(), bits, nbytes);
|
||||||
l->setImage(im);
|
const QString title = data.address ?
|
||||||
showSeparateWidget(l);
|
tr("%1 Object at %2").arg(QLatin1String(data.type),
|
||||||
}
|
QLatin1String(data.hexAddress())) :
|
||||||
|
tr("%1 Object at Unknown Address").arg(QLatin1String(data.type));
|
||||||
|
ImageViewer *v = m_separatedView->prepareObject<ImageViewer>(key, title);
|
||||||
|
v->setProperty(INameProperty, data.iname);
|
||||||
|
v->setImage(im);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case DisplayUtf16String:
|
case DisplayUtf16String:
|
||||||
case DisplayLatin1String:
|
case DisplayLatin1String:
|
||||||
case DisplayUtf8String: { // String data.
|
case DisplayUtf8String: { // String data.
|
||||||
QTextEdit *t = qobject_cast<QTextEdit *>(w);
|
|
||||||
if (!t) {
|
|
||||||
removeSeparateWidget(w);
|
|
||||||
delete w;
|
|
||||||
t = new QTextEdit;
|
|
||||||
m_model->m_editHandlers[key] = t;
|
|
||||||
}
|
|
||||||
QByteArray ba = QByteArray::fromHex(data.editvalue);
|
QByteArray ba = QByteArray::fromHex(data.editvalue);
|
||||||
QString str;
|
QString str;
|
||||||
if (data.editformat == DisplayUtf16String)
|
if (data.editformat == DisplayUtf16String)
|
||||||
@@ -1831,26 +1823,11 @@ void WatchHandler::showEditValue(const WatchData &data)
|
|||||||
str = QString::fromLatin1(ba.constData(), ba.size());
|
str = QString::fromLatin1(ba.constData(), ba.size());
|
||||||
else if (data.editformat == DisplayUtf8String)
|
else if (data.editformat == DisplayUtf8String)
|
||||||
str = QString::fromUtf8(ba.constData(), ba.size());
|
str = QString::fromUtf8(ba.constData(), ba.size());
|
||||||
t->setWindowTitle(data.name);
|
QTextEdit *t = m_separatedView->prepareObject<QTextEdit>(key, data.name);
|
||||||
|
t->setProperty(INameProperty, data.iname);
|
||||||
t->setText(str);
|
t->setText(str);
|
||||||
showSeparateWidget(t);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case DisplayProcess: {
|
|
||||||
// Generic Process.
|
|
||||||
int pos = data.editvalue.indexOf('|');
|
|
||||||
QByteArray cmd = data.editvalue.left(pos);
|
|
||||||
QByteArray input = data.editvalue.mid(pos + 1);
|
|
||||||
QProcess *p = qobject_cast<QProcess *>(w);
|
|
||||||
if (!p) {
|
|
||||||
p = new QProcess;
|
|
||||||
p->start(QLatin1String(cmd));
|
|
||||||
p->waitForStarted();
|
|
||||||
m_model->m_editHandlers[key] = p;
|
|
||||||
}
|
|
||||||
p->write(input + '\n');
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
QTC_ASSERT(false, qDebug() << "Display format: " << data.editformat);
|
QTC_ASSERT(false, qDebug() << "Display format: " << data.editformat);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -36,11 +36,11 @@
|
|||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
QT_FORWARD_DECLARE_CLASS(QTabWidget)
|
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class SeparatedView;
|
||||||
|
|
||||||
// Special formats. Keep in sync with dumper.py.
|
// Special formats. Keep in sync with dumper.py.
|
||||||
enum DisplayFormat
|
enum DisplayFormat
|
||||||
{
|
{
|
||||||
@@ -190,9 +190,6 @@ public:
|
|||||||
void resetValueCache();
|
void resetValueCache();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void removeSeparateWidget(QObject *o);
|
|
||||||
void showSeparateWidget(QWidget *w);
|
|
||||||
|
|
||||||
friend class WatchModel;
|
friend class WatchModel;
|
||||||
|
|
||||||
void saveWatchers();
|
void saveWatchers();
|
||||||
@@ -203,7 +200,7 @@ private:
|
|||||||
|
|
||||||
WatchModel *m_model; // Owned.
|
WatchModel *m_model; // Owned.
|
||||||
DebuggerEngine *m_engine; // Not owned.
|
DebuggerEngine *m_engine; // Not owned.
|
||||||
QPointer<QTabWidget> m_separateWindow; // Owned.
|
SeparatedView *m_separatedView; // Owned.
|
||||||
|
|
||||||
int m_watcherCounter;
|
int m_watcherCounter;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user