Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34240953
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
17 KB
Subscribers
None
View Options
diff --git a/src/ui/treeview.cpp b/src/ui/treeview.cpp
index a12e2133..59f07879 100644
--- a/src/ui/treeview.cpp
+++ b/src/ui/treeview.cpp
@@ -1,110 +1,198 @@
/*
ui/treeview.cpp
This file is part of libkleopatra
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "treeview.h"
+#include <KConfigGroup>
#include <KLocalizedString>
+#include <KSharedConfig>
#include <QContextMenuEvent>
#include <QHeaderView>
#include <QMenu>
using namespace Kleo;
class TreeView::Private
{
+ TreeView *q;
+
public:
- Private()
- {
- }
QMenu *mHeaderPopup = nullptr;
QList<QAction *> mColumnActions;
+ QString mStateGroupName;
+
+ Private(TreeView *qq)
+ : q(qq)
+ {
+ }
+
+ ~Private()
+ {
+ saveColumnLayout();
+ }
+ void saveColumnLayout();
};
TreeView::TreeView(QWidget *parent)
: QTreeView::QTreeView(parent)
- , d{new Private}
+ , d{new Private(this)}
{
header()->installEventFilter(this);
}
TreeView::~TreeView() = default;
bool TreeView::eventFilter(QObject *watched, QEvent *event)
{
Q_UNUSED(watched)
if (event->type() == QEvent::ContextMenu) {
auto e = static_cast<QContextMenuEvent *>(event);
if (!d->mHeaderPopup) {
d->mHeaderPopup = new QMenu(this);
d->mHeaderPopup->setTitle(i18nc("@title:menu", "View Columns"));
for (int i = 0; i < model()->columnCount(); ++i) {
QAction *tmp = d->mHeaderPopup->addAction(model()->headerData(i, Qt::Horizontal).toString());
tmp->setData(QVariant(i));
tmp->setCheckable(true);
d->mColumnActions << tmp;
}
connect(d->mHeaderPopup, &QMenu::triggered, this, [this](QAction *action) {
const int col = action->data().toInt();
if (action->isChecked()) {
showColumn(col);
} else {
hideColumn(col);
}
if (action->isChecked()) {
Q_EMIT columnEnabled(col);
} else {
Q_EMIT columnDisabled(col);
}
});
}
for (QAction *action : std::as_const(d->mColumnActions)) {
const int column = action->data().toInt();
action->setChecked(!isColumnHidden(column));
}
d->mHeaderPopup->popup(mapToGlobal(e->pos()));
return true;
}
return false;
}
+void TreeView::Private::saveColumnLayout()
+{
+ if (mStateGroupName.isEmpty()) {
+ return;
+ }
+ auto config = KConfigGroup(KSharedConfig::openStateConfig(), mStateGroupName);
+ auto header = q->header();
+
+ QVariantList columnVisibility;
+ QVariantList columnOrder;
+ QVariantList columnWidths;
+ const int headerCount = header->count();
+ columnVisibility.reserve(headerCount);
+ columnWidths.reserve(headerCount);
+ columnOrder.reserve(headerCount);
+ for (int i = 0; i < headerCount; ++i) {
+ columnVisibility << QVariant(!q->isColumnHidden(i));
+ columnWidths << QVariant(header->sectionSize(i));
+ columnOrder << QVariant(header->visualIndex(i));
+ }
+
+ config.writeEntry("ColumnVisibility", columnVisibility);
+ config.writeEntry("ColumnOrder", columnOrder);
+ config.writeEntry("ColumnWidths", columnWidths);
+
+ config.writeEntry("SortAscending", (int)header->sortIndicatorOrder());
+ if (header->isSortIndicatorShown()) {
+ config.writeEntry("SortColumn", header->sortIndicatorSection());
+ } else {
+ config.writeEntry("SortColumn", -1);
+ }
+}
+
+bool TreeView::restoreColumnLayout(const QString &stateGroupName)
+{
+ if (stateGroupName.isEmpty()) {
+ return false;
+ }
+ d->mStateGroupName = stateGroupName;
+ auto config = KConfigGroup(KSharedConfig::openStateConfig(), d->mStateGroupName);
+ auto header = this->header();
+
+ QVariantList columnVisibility = config.readEntry("ColumnVisibility", QVariantList());
+ QVariantList columnOrder = config.readEntry("ColumnOrder", QVariantList());
+ QVariantList columnWidths = config.readEntry("ColumnWidths", QVariantList());
+
+ if (!columnVisibility.isEmpty() && !columnOrder.isEmpty() && !columnWidths.isEmpty()) {
+ for (int i = 0; i < header->count(); ++i) {
+ if (i >= columnOrder.size() || i >= columnWidths.size() || i >= columnVisibility.size()) {
+ // An additional column that was not around last time we saved.
+ // We default to hidden.
+ hideColumn(i);
+ continue;
+ }
+ bool visible = columnVisibility[i].toBool();
+ int width = columnWidths[i].toInt();
+ int order = columnOrder[i].toInt();
+
+ header->resizeSection(i, width ? width : 100);
+ header->moveSection(header->visualIndex(i), order);
+
+ if (!visible) {
+ hideColumn(i);
+ }
+ }
+ }
+
+ int sortOrder = config.readEntry("SortAscending", (int)Qt::AscendingOrder);
+ int sortColumn = config.readEntry("SortColumn", 0);
+ if (sortColumn >= 0) {
+ sortByColumn(sortColumn, (Qt::SortOrder)sortOrder);
+ }
+ return !columnVisibility.isEmpty() && !columnOrder.isEmpty() && !columnWidths.isEmpty();
+}
+
QModelIndex TreeView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
// make column by column keyboard navigation with Left/Right possible by switching
// the selection behavior to SelectItems before calling the parent class's moveCursor,
// because it ignores MoveLeft/MoveRight if the selection behavior is SelectRows;
// moreover, temporarily disable exanding of items to prevent expanding/collapsing
// on MoveLeft/MoveRight
if ((cursorAction != MoveLeft) && (cursorAction != MoveRight)) {
return QTreeView::moveCursor(cursorAction, modifiers);
}
const auto savedSelectionBehavior = selectionBehavior();
setSelectionBehavior(SelectItems);
const auto savedItemsExpandable = itemsExpandable();
setItemsExpandable(false);
const auto result = QTreeView::moveCursor(cursorAction, modifiers);
setItemsExpandable(savedItemsExpandable);
setSelectionBehavior(savedSelectionBehavior);
return result;
}
#include "moc_treeview.cpp"
diff --git a/src/ui/treeview.h b/src/ui/treeview.h
index 0fd42db7..4df1594d 100644
--- a/src/ui/treeview.h
+++ b/src/ui/treeview.h
@@ -1,59 +1,67 @@
/*
ui/treeview.h
This file is part of libkleopatra
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kleo_export.h"
#include <QTreeView>
namespace Kleo
{
/**
* A tree view that allows accessible column by column keyboard navigation
* and that has customizable columns through a context menu in the header.
*
* Column by column navigation is required to make a tree view accessible.
*
* The TreeView allows column by column keyboard navigation even if
* the selection behavior is set to SelectRows and users can expand/collapse
* list items. To achieve this it deactivates the standard behavior of QTreeView
* to expand/collapse items if the left/right arrow keys are used.
*
* Additionally, you may want to disable parent-child navigation in tree views
* with left/right arrow keys because this also interferes with column by column
* navigation. You can do this by setting
* "QTreeView { arrow-keys-navigate-into-children: 0; }"
* as application style sheet.
*
* \sa TreeWidget
*/
class KLEO_EXPORT TreeView : public QTreeView
{
Q_OBJECT
public:
TreeView(QWidget *parent = nullptr);
~TreeView() override;
+ /**
+ * Restores the layout state under key @p stateGroupName and enables state
+ * saving when the object is destroyed. Make sure that @p stateGroupName is
+ * unique for each place the widget occurs. Returns true if some state was
+ * restored. If false is returned, no state was restored and the caller should
+ * apply the default configuration.
+ */
+ bool restoreColumnLayout(const QString &stateGroupName);
Q_SIGNALS:
void columnEnabled(int column);
void columnDisabled(int column);
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
private:
class Private;
const std::unique_ptr<Private> d;
};
}
diff --git a/src/ui/treewidget.cpp b/src/ui/treewidget.cpp
index 7056fa18..1fb33a12 100644
--- a/src/ui/treewidget.cpp
+++ b/src/ui/treewidget.cpp
@@ -1,193 +1,198 @@
/*
ui/treewidget.cpp
This file is part of libkleopatra
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "treewidget.h"
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QContextMenuEvent>
#include <QHeaderView>
#include <QMenu>
using namespace Kleo;
class TreeWidget::Private
{
+ TreeWidget *q;
+
public:
QMenu *mHeaderPopup = nullptr;
QList<QAction *> mColumnActions;
QString mStateGroupName;
- TreeWidget *q;
+ Private(TreeWidget *qq)
+ : q(qq)
+ {
+ }
~Private()
{
- q->saveColumnLayout();
+ saveColumnLayout();
}
+ void saveColumnLayout();
};
TreeWidget::TreeWidget(QWidget *parent)
: QTreeWidget::QTreeWidget(parent)
- , d{new Private}
+ , d{new Private(this)}
{
- d->q = this;
header()->installEventFilter(this);
}
TreeWidget::~TreeWidget() = default;
-void TreeWidget::saveColumnLayout()
+void TreeWidget::Private::saveColumnLayout()
{
- if (d->mStateGroupName.isEmpty()) {
+ if (mStateGroupName.isEmpty()) {
return;
}
- auto config = KConfigGroup(KSharedConfig::openStateConfig(), d->mStateGroupName);
- auto header = this->header();
+ auto config = KConfigGroup(KSharedConfig::openStateConfig(), mStateGroupName);
+ auto header = q->header();
QVariantList columnVisibility;
QVariantList columnOrder;
QVariantList columnWidths;
const int headerCount = header->count();
columnVisibility.reserve(headerCount);
columnWidths.reserve(headerCount);
columnOrder.reserve(headerCount);
for (int i = 0; i < headerCount; ++i) {
- columnVisibility << QVariant(!isColumnHidden(i));
+ columnVisibility << QVariant(!q->isColumnHidden(i));
columnWidths << QVariant(header->sectionSize(i));
columnOrder << QVariant(header->visualIndex(i));
}
config.writeEntry("ColumnVisibility", columnVisibility);
config.writeEntry("ColumnOrder", columnOrder);
config.writeEntry("ColumnWidths", columnWidths);
config.writeEntry("SortAscending", (int)header->sortIndicatorOrder());
if (header->isSortIndicatorShown()) {
config.writeEntry("SortColumn", header->sortIndicatorSection());
} else {
config.writeEntry("SortColumn", -1);
}
}
bool TreeWidget::restoreColumnLayout(const QString &stateGroupName)
{
if (stateGroupName.isEmpty()) {
return false;
}
d->mStateGroupName = stateGroupName;
auto config = KConfigGroup(KSharedConfig::openStateConfig(), d->mStateGroupName);
auto header = this->header();
QVariantList columnVisibility = config.readEntry("ColumnVisibility", QVariantList());
QVariantList columnOrder = config.readEntry("ColumnOrder", QVariantList());
QVariantList columnWidths = config.readEntry("ColumnWidths", QVariantList());
- if (!columnVisibility.isEmpty()) {
+ if (!columnVisibility.isEmpty() && !columnOrder.isEmpty() && !columnWidths.isEmpty()) {
for (int i = 0; i < header->count(); ++i) {
if (i >= columnOrder.size() || i >= columnWidths.size() || i >= columnVisibility.size()) {
// An additional column that was not around last time we saved.
// We default to hidden.
hideColumn(i);
continue;
}
bool visible = columnVisibility[i].toBool();
int width = columnWidths[i].toInt();
int order = columnOrder[i].toInt();
header->resizeSection(i, width ? width : 100);
header->moveSection(header->visualIndex(i), order);
if (!visible) {
hideColumn(i);
}
}
}
int sortOrder = config.readEntry("SortAscending", (int)Qt::AscendingOrder);
int sortColumn = config.readEntry("SortColumn", 0);
if (sortColumn >= 0) {
sortByColumn(sortColumn, (Qt::SortOrder)sortOrder);
}
- return !columnVisibility.isEmpty();
+ return !columnVisibility.isEmpty() && !columnOrder.isEmpty() && !columnWidths.isEmpty();
}
bool TreeWidget::eventFilter(QObject *watched, QEvent *event)
{
Q_UNUSED(watched)
if (event->type() == QEvent::ContextMenu) {
auto e = static_cast<QContextMenuEvent *>(event);
if (!d->mHeaderPopup) {
d->mHeaderPopup = new QMenu(this);
d->mHeaderPopup->setTitle(i18nc("@title:menu", "View Columns"));
for (int i = 0; i < model()->columnCount(); ++i) {
QAction *tmp = d->mHeaderPopup->addAction(model()->headerData(i, Qt::Horizontal).toString());
tmp->setData(QVariant(i));
tmp->setCheckable(true);
d->mColumnActions << tmp;
}
connect(d->mHeaderPopup, &QMenu::triggered, this, [this](QAction *action) {
const int col = action->data().toInt();
if (action->isChecked()) {
showColumn(col);
} else {
hideColumn(col);
}
if (action->isChecked()) {
Q_EMIT columnEnabled(col);
} else {
Q_EMIT columnDisabled(col);
}
});
}
for (QAction *action : std::as_const(d->mColumnActions)) {
const int column = action->data().toInt();
action->setChecked(!isColumnHidden(column));
}
d->mHeaderPopup->popup(mapToGlobal(e->pos()));
return true;
}
return false;
}
QModelIndex TreeWidget::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
// make column by column keyboard navigation with Left/Right possible by switching
// the selection behavior to SelectItems before calling the parent class's moveCursor,
// because it ignores MoveLeft/MoveRight if the selection behavior is SelectRows;
// moreover, temporarily disable exanding of items to prevent expanding/collapsing
// on MoveLeft/MoveRight
if ((cursorAction != MoveLeft) && (cursorAction != MoveRight)) {
return QTreeWidget::moveCursor(cursorAction, modifiers);
}
const auto savedSelectionBehavior = selectionBehavior();
setSelectionBehavior(SelectItems);
const auto savedItemsExpandable = itemsExpandable();
setItemsExpandable(false);
const auto result = QTreeWidget::moveCursor(cursorAction, modifiers);
setItemsExpandable(savedItemsExpandable);
setSelectionBehavior(savedSelectionBehavior);
return result;
}
#include "moc_treewidget.cpp"
diff --git a/src/ui/treewidget.h b/src/ui/treewidget.h
index 51a62df1..3039214f 100644
--- a/src/ui/treewidget.h
+++ b/src/ui/treewidget.h
@@ -1,58 +1,57 @@
/*
ui/treewidget.h
This file is part of libkleopatra
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kleo_export.h"
#include <QTreeWidget>
namespace Kleo
{
/**
* A tree widget that allows accessible column by column keyboard navigation
* and that has customizable columns through a context menu in the header.
*
* This is the QTreeWidget-derived variant of TreeView.
*
* \sa TreeView
*/
class KLEO_EXPORT TreeWidget : public QTreeWidget
{
Q_OBJECT
public:
TreeWidget(QWidget *parent = nullptr);
~TreeWidget() override;
/**
- * Restores the layout state under key 'stateGroupName' and enables state
- * saving when the object is destroyed. Make sure that 'stateGroupName' is
+ * Restores the layout state under key @p stateGroupName and enables state
+ * saving when the object is destroyed. Make sure that @p stateGroupName is
* unique for each place the widget occurs. Returns true if some state was
* restored. If false is returned, no state was restored and the caller should
* apply the default configuration.
*/
bool restoreColumnLayout(const QString &stateGroupName);
Q_SIGNALS:
void columnEnabled(int column);
void columnDisabled(int column);
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
private:
class Private;
const std::unique_ptr<Private> d;
- void saveColumnLayout();
};
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Dec 23, 3:16 PM (19 h, 14 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
dd/a6/b86729164bff70d9f0767d388dd6
Attached To
rLIBKLEO Libkleo
Event Timeline
Log In to Comment