forked from qt-creator/qt-creator
Debugger: Save width of manually resized tree view columns
Task-number: QTCREATORBUG-12670 Change-Id: I5c31ffd6d3bb3060e851df56e9d9a80101df9347 Reviewed-by: Alessandro Portale <alessandro.portale@digia.com>
This commit is contained in:
@@ -29,16 +29,188 @@
|
||||
|
||||
#include "basetreeview.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFontMetrics>
|
||||
#include <QHeaderView>
|
||||
#include <QItemDelegate>
|
||||
#include <QLabel>
|
||||
#include <QMap>
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
#include <QSettings>
|
||||
#include <QTimer>
|
||||
|
||||
namespace Utils {
|
||||
namespace Internal {
|
||||
|
||||
const char ColumnKey[] = "Columns";
|
||||
|
||||
class BaseTreeViewPrivate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit BaseTreeViewPrivate(BaseTreeView *parent)
|
||||
: q(parent), m_settings(0), m_expectUserChanges(false)
|
||||
{}
|
||||
|
||||
bool eventFilter(QObject *, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::MouseMove) {
|
||||
// At this time we don't know which section will get which size.
|
||||
// But we know that a resizedSection() will be emitted later.
|
||||
QMouseEvent *me = static_cast<QMouseEvent *>(event);
|
||||
if (me->buttons() & Qt::LeftButton)
|
||||
m_expectUserChanges = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void readSettings()
|
||||
{
|
||||
// This only reads setting, does not restore state.
|
||||
// Storage format is a flat list of column numbers and width.
|
||||
// Columns not mentioned are resized to content////
|
||||
m_userHandled.clear();
|
||||
if (m_settings && !m_settingsKey.isEmpty()) {
|
||||
m_settings->beginGroup(m_settingsKey);
|
||||
QVariantList l = m_settings->value(QLatin1String(ColumnKey)).toList();
|
||||
QTC_ASSERT(l.size() % 2 == 0, qDebug() << m_settingsKey; l.append(0));
|
||||
for (int i = 0; i < l.size(); i += 2) {
|
||||
int column = l.at(i).toInt();
|
||||
int width = l.at(i + 1).toInt();
|
||||
QTC_ASSERT(column >= 0 && column < 20, qDebug() << m_settingsKey << column; continue);
|
||||
QTC_ASSERT(width > 0 && width < 10000, qDebug() << m_settingsKey << width; continue);
|
||||
m_userHandled[column] = width;
|
||||
}
|
||||
m_settings->endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void restoreState()
|
||||
{
|
||||
if (m_settings && !m_settingsKey.isEmpty()) {
|
||||
QHeaderView *h = q->header();
|
||||
for (auto it = m_userHandled.constBegin(), et = m_userHandled.constEnd(); it != et; ++it) {
|
||||
const int column = it.key();
|
||||
const int targetSize = it.value();
|
||||
const int currentSize = h->sectionSize(column);
|
||||
if (targetSize > 0 && targetSize != currentSize)
|
||||
h->resizeSection(column, targetSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void saveState()
|
||||
{
|
||||
if (m_settings && !m_settingsKey.isEmpty()) {
|
||||
m_settings->beginGroup(m_settingsKey);
|
||||
QVariantList l;
|
||||
for (auto it = m_userHandled.constBegin(), et = m_userHandled.constEnd(); it != et; ++it) {
|
||||
const int column = it.key();
|
||||
const int width = it.value();
|
||||
QTC_ASSERT(column >= 0 && column < q->model()->columnCount(), continue);
|
||||
QTC_ASSERT(width > 0 && width < 10000, continue);
|
||||
l.append(column);
|
||||
l.append(width);
|
||||
}
|
||||
m_settings->setValue(QLatin1String(ColumnKey), l);
|
||||
m_settings->endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
Q_SLOT void handleSectionResized(int logicalIndex, int /*oldSize*/, int newSize)
|
||||
{
|
||||
if (m_expectUserChanges) {
|
||||
m_userHandled[logicalIndex] = newSize;
|
||||
saveState();
|
||||
m_expectUserChanges = false;
|
||||
}
|
||||
}
|
||||
|
||||
int suggestedColumnSize(int column) const
|
||||
{
|
||||
QHeaderView *h = q->header();
|
||||
QTC_ASSERT(h, return -1);
|
||||
QAbstractItemModel *m = q->model();
|
||||
QTC_ASSERT(m, return -1);
|
||||
|
||||
QModelIndex a = q->indexAt(QPoint(1, 1));
|
||||
a = a.sibling(a.row(), column);
|
||||
QFontMetrics fm = q->fontMetrics();
|
||||
int minimum = fm.width(m->headerData(column, Qt::Horizontal).toString());
|
||||
const int ind = q->indentation();
|
||||
for (int i = 0; i < 100 && a.isValid(); ++i) {
|
||||
const QString s = m->data(a).toString();
|
||||
int w = fm.width(s) + 10;
|
||||
if (column == 0) {
|
||||
for (QModelIndex b = a.parent(); b.isValid(); b = b.parent())
|
||||
w += ind;
|
||||
}
|
||||
if (w > minimum)
|
||||
minimum = w;
|
||||
a = q->indexBelow(a);
|
||||
}
|
||||
return minimum;
|
||||
}
|
||||
|
||||
Q_SLOT void resizeColumns()
|
||||
{
|
||||
QHeaderView *h = q->header();
|
||||
QTC_ASSERT(h, return);
|
||||
|
||||
if (m_settings && !m_settingsKey.isEmpty()) {
|
||||
for (int i = 0, n = h->count(); i != n; ++i) {
|
||||
int targetSize;
|
||||
if (m_userHandled.contains(i))
|
||||
targetSize = m_userHandled.value(i);
|
||||
else
|
||||
targetSize = suggestedColumnSize(i);
|
||||
const int currentSize = h->sectionSize(i);
|
||||
if (targetSize > 0 && targetSize != currentSize)
|
||||
h->resizeSection(i, targetSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Q_SLOT void rowActivatedHelper(const QModelIndex &index)
|
||||
{
|
||||
q->rowActivated(index);
|
||||
}
|
||||
|
||||
Q_SLOT void rowClickedHelper(const QModelIndex &index)
|
||||
{
|
||||
q->rowClicked(index);
|
||||
}
|
||||
|
||||
Q_SLOT void toggleColumnWidth(int logicalIndex)
|
||||
{
|
||||
QHeaderView *h = q->header();
|
||||
const int currentSize = h->sectionSize(logicalIndex);
|
||||
const int suggestedSize = suggestedColumnSize(logicalIndex);
|
||||
int targetSize = suggestedSize;
|
||||
// We switch to the size suggested by the contents, except
|
||||
// when we have that size already, in that case minimize.
|
||||
if (currentSize == suggestedSize) {
|
||||
QFontMetrics fm = q->fontMetrics();
|
||||
int headerSize = fm.width(q->model()->headerData(logicalIndex, Qt::Horizontal).toString());
|
||||
int minSize = 10 * fm.width(QLatin1Char('x'));
|
||||
targetSize = qMax(minSize, headerSize);
|
||||
}
|
||||
h->resizeSection(logicalIndex, targetSize);
|
||||
m_userHandled.remove(logicalIndex); // Reset.
|
||||
saveState();
|
||||
}
|
||||
|
||||
public:
|
||||
BaseTreeView *q;
|
||||
QMap<int, int> m_userHandled; // column -> width, "not present" means "automatic"
|
||||
QSettings *m_settings;
|
||||
QString m_settingsKey;
|
||||
bool m_expectUserChanges;
|
||||
};
|
||||
|
||||
class BaseTreeViewDelegate : public QItemDelegate
|
||||
{
|
||||
@@ -58,8 +230,12 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
using namespace Internal;
|
||||
|
||||
BaseTreeView::BaseTreeView(QWidget *parent)
|
||||
: TreeView(parent)
|
||||
: TreeView(parent), d(new BaseTreeViewPrivate(this))
|
||||
{
|
||||
setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
@@ -68,30 +244,42 @@ BaseTreeView::BaseTreeView(QWidget *parent)
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
setUniformRowHeights(true);
|
||||
setItemDelegate(new BaseTreeViewDelegate(this));
|
||||
header()->setDefaultAlignment(Qt::AlignLeft);
|
||||
header()->setClickable(true);
|
||||
|
||||
QHeaderView *h = header();
|
||||
h->setDefaultAlignment(Qt::AlignLeft);
|
||||
h->setClickable(true);
|
||||
h->viewport()->installEventFilter(d);
|
||||
|
||||
connect(this, SIGNAL(activated(QModelIndex)),
|
||||
SLOT(rowActivatedHelper(QModelIndex)));
|
||||
d, SLOT(rowActivatedHelper(QModelIndex)));
|
||||
connect(this, SIGNAL(clicked(QModelIndex)),
|
||||
SLOT(rowClickedHelper(QModelIndex)));
|
||||
connect(header(), SIGNAL(sectionClicked(int)),
|
||||
SLOT(toggleColumnWidth(int)));
|
||||
d, SLOT(rowClickedHelper(QModelIndex)));
|
||||
connect(h, SIGNAL(sectionClicked(int)),
|
||||
d, SLOT(toggleColumnWidth(int)));
|
||||
connect(h, SIGNAL(sectionResized(int,int,int)),
|
||||
d, SLOT(handleSectionResized(int,int,int)));
|
||||
}
|
||||
|
||||
BaseTreeView::~BaseTreeView()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void BaseTreeView::setModel(QAbstractItemModel *m)
|
||||
{
|
||||
QAbstractItemModel *oldModel = model();
|
||||
const char *sig = "columnAdjustmentRequested()";
|
||||
if (model()) {
|
||||
if (oldModel) {
|
||||
int index = model()->metaObject()->indexOfSignal(sig);
|
||||
if (index != -1)
|
||||
disconnect(model(), SIGNAL(columnAdjustmentRequested()), this, SLOT(resizeColumns()));
|
||||
disconnect(model(), SIGNAL(columnAdjustmentRequested()), d, SLOT(resizeColumns()));
|
||||
}
|
||||
TreeView::setModel(m);
|
||||
if (m) {
|
||||
int index = m->metaObject()->indexOfSignal(sig);
|
||||
if (index != -1)
|
||||
connect(m, SIGNAL(columnAdjustmentRequested()), this, SLOT(resizeColumns()));
|
||||
connect(m, SIGNAL(columnAdjustmentRequested()), d, SLOT(resizeColumns()));
|
||||
d->restoreState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,60 +288,15 @@ void BaseTreeView::mousePressEvent(QMouseEvent *ev)
|
||||
TreeView::mousePressEvent(ev);
|
||||
const QModelIndex mi = indexAt(ev->pos());
|
||||
if (!mi.isValid())
|
||||
toggleColumnWidth(columnAt(ev->x()));
|
||||
d->toggleColumnWidth(columnAt(ev->x()));
|
||||
}
|
||||
|
||||
void BaseTreeView::resizeColumns()
|
||||
void BaseTreeView::setSettings(QSettings *settings, const QByteArray &key)
|
||||
{
|
||||
QHeaderView *h = header();
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
for (int i = 0, n = h->count(); i != n; ++i) {
|
||||
int targetSize = suggestedColumnSize(i);
|
||||
if (targetSize > 0)
|
||||
h->resizeSection(i, targetSize);
|
||||
}
|
||||
}
|
||||
|
||||
int BaseTreeView::suggestedColumnSize(int column) const
|
||||
{
|
||||
QHeaderView *h = header();
|
||||
if (!h)
|
||||
return -1;
|
||||
|
||||
QModelIndex a = indexAt(QPoint(1, 1));
|
||||
a = a.sibling(a.row(), column);
|
||||
QFontMetrics fm(font());
|
||||
int m = fm.width(model()->headerData(column, Qt::Horizontal).toString());
|
||||
const int ind = indentation();
|
||||
for (int i = 0; i < 100 && a.isValid(); ++i) {
|
||||
const QString s = model()->data(a).toString();
|
||||
int w = fm.width(s) + 10;
|
||||
if (column == 0) {
|
||||
for (QModelIndex b = a.parent(); b.isValid(); b = b.parent())
|
||||
w += ind;
|
||||
}
|
||||
if (w > m)
|
||||
m = w;
|
||||
a = indexBelow(a);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
void BaseTreeView::toggleColumnWidth(int logicalIndex)
|
||||
{
|
||||
QHeaderView *h = header();
|
||||
const int currentSize = h->sectionSize(logicalIndex);
|
||||
const int suggestedSize = suggestedColumnSize(logicalIndex);
|
||||
if (currentSize == suggestedSize) {
|
||||
QFontMetrics fm(font());
|
||||
int headerSize = fm.width(model()->headerData(logicalIndex, Qt::Horizontal).toString());
|
||||
int minSize = 10 * fm.width(QLatin1Char('x'));
|
||||
h->resizeSection(logicalIndex, qMax(minSize, headerSize));
|
||||
} else {
|
||||
h->resizeSection(logicalIndex, suggestedSize);
|
||||
}
|
||||
QTC_ASSERT(!d->m_settings, qDebug() << "DUPLICATED setSettings" << key);
|
||||
d->m_settings = settings;
|
||||
d->m_settingsKey = QString::fromLatin1(key);
|
||||
d->readSettings();
|
||||
}
|
||||
|
||||
QModelIndexList BaseTreeView::activeRows() const
|
||||
@@ -169,3 +312,5 @@ QModelIndexList BaseTreeView::activeRows() const
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
#include "basetreeview.moc"
|
||||
|
@@ -34,15 +34,23 @@
|
||||
|
||||
#include "itemviews.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSettings;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Utils {
|
||||
|
||||
namespace Internal { class BaseTreeViewPrivate; }
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT BaseTreeView : public TreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BaseTreeView(QWidget *parent = 0);
|
||||
~BaseTreeView();
|
||||
|
||||
void setSettings(QSettings *settings, const QByteArray &key);
|
||||
QModelIndexList activeRows() const;
|
||||
|
||||
void setModel(QAbstractItemModel *model);
|
||||
@@ -51,16 +59,10 @@ public:
|
||||
void mousePressEvent(QMouseEvent *ev);
|
||||
|
||||
public slots:
|
||||
void resizeColumns();
|
||||
void setAlternatingRowColorsHelper(bool on) { setAlternatingRowColors(on); }
|
||||
|
||||
private slots:
|
||||
void rowActivatedHelper(const QModelIndex &index) { rowActivated(index); }
|
||||
void rowClickedHelper(const QModelIndex &index) { rowClicked(index); }
|
||||
void toggleColumnWidth(int logicalIndex);
|
||||
|
||||
private:
|
||||
int suggestedColumnSize(int column) const;
|
||||
Internal::BaseTreeViewPrivate *d;
|
||||
};
|
||||
|
||||
} // namespace Utils
|
||||
|
@@ -834,7 +834,6 @@ public slots:
|
||||
m_returnView->header()->resizeSection(section, newSize);
|
||||
}
|
||||
|
||||
|
||||
void sourceFilesDockToggled(bool on)
|
||||
{
|
||||
if (on && m_currentEngine->state() == InferiorStopOk)
|
||||
@@ -2762,6 +2761,8 @@ void DebuggerPluginPrivate::extensionsInitialized()
|
||||
{
|
||||
const QKeySequence debugKey = QKeySequence(UseMacShortcuts ? tr("Ctrl+Y") : tr("F5"));
|
||||
|
||||
QSettings *settings = Core::ICore::settings();
|
||||
|
||||
m_debuggerSettings = new DebuggerSettings;
|
||||
m_debuggerSettings->readSettings();
|
||||
|
||||
@@ -2794,39 +2795,48 @@ void DebuggerPluginPrivate::extensionsInitialized()
|
||||
|
||||
m_breakHandler = new BreakHandler;
|
||||
m_breakView = new BreakTreeView;
|
||||
m_breakView->setSettings(settings, "Debugger.BreakWindow");
|
||||
m_breakView->setModel(m_breakHandler->model());
|
||||
m_breakWindow = addSearch(m_breakView, tr("Breakpoints"), DOCKWIDGET_BREAK);
|
||||
|
||||
m_modulesView = new ModulesTreeView;
|
||||
m_modulesView->setSettings(settings, "Debugger.ModulesView");
|
||||
m_modulesWindow = addSearch(m_modulesView, tr("Modules"), DOCKWIDGET_MODULES);
|
||||
|
||||
m_registerView = new RegisterTreeView;
|
||||
m_registerView->setSettings(settings, "Debugger.RegisterView");
|
||||
m_registerWindow = addSearch(m_registerView, tr("Registers"), DOCKWIDGET_REGISTER);
|
||||
|
||||
m_stackView = new StackTreeView;
|
||||
m_stackView->setSettings(settings, "Debugger.StackView");
|
||||
m_stackWindow = addSearch(m_stackView, tr("Stack"), DOCKWIDGET_STACK);
|
||||
|
||||
m_sourceFilesView = new SourceFilesTreeView;
|
||||
m_sourceFilesView->setSettings(settings, "Debugger.SourceFilesView");
|
||||
m_sourceFilesWindow = addSearch(m_sourceFilesView, tr("Source Files"), DOCKWIDGET_SOURCE_FILES);
|
||||
|
||||
m_threadsView = new ThreadsTreeView;
|
||||
m_threadsView->setSettings(settings, "Debugger.ThreadsView");
|
||||
m_threadsWindow = addSearch(m_threadsView, tr("Threads"), DOCKWIDGET_THREADS);
|
||||
|
||||
m_returnView = new WatchTreeView(ReturnType);
|
||||
m_returnView = new WatchTreeView(ReturnType); // No settings.
|
||||
m_returnWindow = addSearch(m_returnView, tr("Locals and Expressions"), "CppDebugReturn");
|
||||
|
||||
m_localsView = new WatchTreeView(LocalsType);
|
||||
m_localsView->setSettings(settings, "Debugger.LocalsView");
|
||||
m_localsWindow = addSearch(m_localsView, tr("Locals and Expressions"), "CppDebugLocals");
|
||||
|
||||
m_watchersView = new WatchTreeView(WatchersType);
|
||||
m_watchersView = new WatchTreeView(WatchersType); // No settings.
|
||||
m_watchersWindow = addSearch(m_watchersView, tr("Locals and Expressions"), "CppDebugWatchers");
|
||||
|
||||
m_inspectorView = new WatchTreeView(InspectType);
|
||||
m_inspectorView->setSettings(settings, "Debugger.LocalsView"); // sic! same as locals view.
|
||||
m_inspectorWindow = addSearch(m_inspectorView, tr("Locals and Expressions"), "Inspector");
|
||||
|
||||
// Snapshot
|
||||
m_snapshotHandler = new SnapshotHandler;
|
||||
m_snapshotView = new SnapshotTreeView(m_snapshotHandler);
|
||||
m_snapshotView->setSettings(settings, "Debugger.SnapshotView");
|
||||
m_snapshotView->setModel(m_snapshotHandler->model());
|
||||
m_snapshotWindow = addSearch(m_snapshotView, tr("Snapshots"), DOCKWIDGET_SNAPSHOTS);
|
||||
|
||||
|
@@ -587,6 +587,8 @@ QWidget *CallgrindToolPrivate::createWidgets()
|
||||
{
|
||||
QTC_ASSERT(!m_visualisation, return 0);
|
||||
|
||||
QSettings *coreSettings = ICore::settings();
|
||||
|
||||
//
|
||||
// DockWidgets
|
||||
//
|
||||
@@ -600,6 +602,7 @@ QWidget *CallgrindToolPrivate::createWidgets()
|
||||
|
||||
m_callersView = new CostView(mw);
|
||||
m_callersView->setObjectName(QLatin1String("Valgrind.CallgrindTool.CallersView"));
|
||||
m_callersView->setSettings(coreSettings, "Valgrind.CallgrindTool.CallersView");
|
||||
m_callersView->sortByColumn(CallModel::CostColumn);
|
||||
m_callersView->setFrameStyle(QFrame::NoFrame);
|
||||
// enable sorting
|
||||
@@ -612,6 +615,7 @@ QWidget *CallgrindToolPrivate::createWidgets()
|
||||
|
||||
m_calleesView = new CostView(mw);
|
||||
m_calleesView->setObjectName(QLatin1String("Valgrind.CallgrindTool.CalleesView"));
|
||||
m_calleesView->setSettings(coreSettings, "Valgrind.CallgrindTool.CalleesView");
|
||||
m_calleesView->sortByColumn(CallModel::CostColumn);
|
||||
m_calleesView->setFrameStyle(QFrame::NoFrame);
|
||||
// enable sorting
|
||||
@@ -624,6 +628,7 @@ QWidget *CallgrindToolPrivate::createWidgets()
|
||||
|
||||
m_flatView = new CostView(mw);
|
||||
m_flatView->setObjectName(QLatin1String("Valgrind.CallgrindTool.FlatView"));
|
||||
m_flatView->setSettings(coreSettings, "Valgrind.CallgrindTool.FlatView");
|
||||
m_flatView->sortByColumn(DataModel::SelfCostColumn);
|
||||
m_flatView->setFrameStyle(QFrame::NoFrame);
|
||||
m_flatView->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
|
Reference in New Issue
Block a user