diff --git a/src/libs/utils/basetreeview.cpp b/src/libs/utils/basetreeview.cpp index 5ca1e1bd60f..088e5dc9aeb 100644 --- a/src/libs/utils/basetreeview.cpp +++ b/src/libs/utils/basetreeview.cpp @@ -29,16 +29,188 @@ #include "basetreeview.h" +#include + #include #include #include #include #include +#include #include #include +#include #include 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(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 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" diff --git a/src/libs/utils/basetreeview.h b/src/libs/utils/basetreeview.h index 123839ce775..d80837d9c0b 100644 --- a/src/libs/utils/basetreeview.h +++ b/src/libs/utils/basetreeview.h @@ -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 diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 76abafbbb40..284b513a458 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -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); diff --git a/src/plugins/valgrind/callgrindtool.cpp b/src/plugins/valgrind/callgrindtool.cpp index cc1124552a2..6aec7c97095 100644 --- a/src/plugins/valgrind/callgrindtool.cpp +++ b/src/plugins/valgrind/callgrindtool.cpp @@ -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);