Libkleo

treeview.cpp
1/*
2 ui/treeview.cpp
3
4 This file is part of libkleopatra
5 SPDX-FileCopyrightText: 2022 g10 Code GmbH
6 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10
11#include <config-libkleo.h>
12
13#include "treeview.h"
14
15#include <KConfigGroup>
16#include <KLocalizedString>
17#include <KSharedConfig>
18
19#include <QContextMenuEvent>
20#include <QHeaderView>
21#include <QMenu>
22
23using namespace Kleo;
24
25class TreeView::Private
26{
27 TreeView *q;
28
29public:
30 QMenu *mHeaderPopup = nullptr;
31 QList<QAction *> mColumnActions;
32 QString mStateGroupName;
33
34 Private(TreeView *qq)
35 : q(qq)
36 {
37 }
38
39 ~Private()
40 {
41 saveColumnLayout();
42 }
43 void saveColumnLayout();
44};
45
46TreeView::TreeView(QWidget *parent)
47 : QTreeView::QTreeView(parent)
48 , d{new Private(this)}
49{
50 header()->installEventFilter(this);
51}
52
53TreeView::~TreeView() = default;
54
55bool TreeView::eventFilter(QObject *watched, QEvent *event)
56{
57 Q_UNUSED(watched)
58 if (event->type() == QEvent::ContextMenu) {
59 auto e = static_cast<QContextMenuEvent *>(event);
60
61 if (!d->mHeaderPopup) {
62 d->mHeaderPopup = new QMenu(this);
63 d->mHeaderPopup->setTitle(i18nc("@title:menu", "View Columns"));
64 for (int i = 0; i < model()->columnCount(); ++i) {
65 QAction *tmp = d->mHeaderPopup->addAction(model()->headerData(i, Qt::Horizontal).toString());
66 tmp->setData(QVariant(i));
67 tmp->setCheckable(true);
68 d->mColumnActions << tmp;
69 }
70
71 connect(d->mHeaderPopup, &QMenu::triggered, this, [this](QAction *action) {
72 const int col = action->data().toInt();
73 if (action->isChecked()) {
74 showColumn(col);
75 } else {
76 hideColumn(col);
77 }
78
79 if (action->isChecked()) {
80 Q_EMIT columnEnabled(col);
81 } else {
82 Q_EMIT columnDisabled(col);
83 }
84 d->saveColumnLayout();
85 });
86 }
87
88 for (QAction *action : std::as_const(d->mColumnActions)) {
89 const int column = action->data().toInt();
90 action->setChecked(!isColumnHidden(column));
91 }
92
93 d->mHeaderPopup->popup(mapToGlobal(e->pos()));
94 return true;
95 }
96
97 return false;
98}
99
100void TreeView::Private::saveColumnLayout()
101{
102 if (mStateGroupName.isEmpty()) {
103 return;
104 }
105 auto config = KConfigGroup(KSharedConfig::openStateConfig(), mStateGroupName);
106 auto header = q->header();
107
108 QVariantList columnVisibility;
109 QVariantList columnOrder;
110 QVariantList columnWidths;
111 const int headerCount = header->count();
112 columnVisibility.reserve(headerCount);
113 columnWidths.reserve(headerCount);
114 columnOrder.reserve(headerCount);
115 for (int i = 0; i < headerCount; ++i) {
116 columnVisibility << QVariant(!q->isColumnHidden(i));
117 columnWidths << QVariant(header->sectionSize(i));
118 columnOrder << QVariant(header->visualIndex(i));
119 }
120
121 config.writeEntry("ColumnVisibility", columnVisibility);
122 config.writeEntry("ColumnOrder", columnOrder);
123 config.writeEntry("ColumnWidths", columnWidths);
124
125 config.writeEntry("SortAscending", (int)header->sortIndicatorOrder());
127 config.writeEntry("SortColumn", header->sortIndicatorSection());
128 } else {
129 config.writeEntry("SortColumn", -1);
130 }
131 config.sync();
132}
133
134bool TreeView::restoreColumnLayout(const QString &stateGroupName)
135{
136 if (stateGroupName.isEmpty()) {
137 return false;
138 }
139 d->mStateGroupName = stateGroupName;
140 auto config = KConfigGroup(KSharedConfig::openStateConfig(), d->mStateGroupName);
141 auto header = this->header();
142
143 QVariantList columnVisibility = config.readEntry("ColumnVisibility", QVariantList());
144 QVariantList columnOrder = config.readEntry("ColumnOrder", QVariantList());
145 QVariantList columnWidths = config.readEntry("ColumnWidths", QVariantList());
146
147 if (!columnVisibility.isEmpty() && !columnOrder.isEmpty() && !columnWidths.isEmpty()) {
148 for (int i = 0; i < header->count(); ++i) {
149 if (i >= columnOrder.size() || i >= columnWidths.size() || i >= columnVisibility.size()) {
150 // An additional column that was not around last time we saved.
151 // We default to hidden.
152 hideColumn(i);
153 continue;
154 }
155 bool visible = columnVisibility[i].toBool();
156 int width = columnWidths[i].toInt();
157 int order = columnOrder[i].toInt();
158
159 header->resizeSection(i, width ? width : 100);
161
162 if (!visible) {
163 hideColumn(i);
164 }
165 }
166 }
167
168 int sortOrder = config.readEntry("SortAscending", (int)Qt::AscendingOrder);
169 int sortColumn = config.readEntry("SortColumn", 0);
170 if (sortColumn >= 0) {
171 sortByColumn(sortColumn, (Qt::SortOrder)sortOrder);
172 }
173
174 connect(header, &QHeaderView::sectionResized, this, [this]() {
175 d->saveColumnLayout();
176 });
177 connect(header, &QHeaderView::sectionMoved, this, [this]() {
178 d->saveColumnLayout();
179 });
181 d->saveColumnLayout();
182 });
183 return !columnVisibility.isEmpty() && !columnOrder.isEmpty() && !columnWidths.isEmpty();
184}
185
186QModelIndex TreeView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
187{
188 // make column by column keyboard navigation with Left/Right possible by switching
189 // the selection behavior to SelectItems before calling the parent class's moveCursor,
190 // because it ignores MoveLeft/MoveRight if the selection behavior is SelectRows;
191 // moreover, temporarily disable exanding of items to prevent expanding/collapsing
192 // on MoveLeft/MoveRight
193 if ((cursorAction != MoveLeft) && (cursorAction != MoveRight)) {
194 return QTreeView::moveCursor(cursorAction, modifiers);
195 }
196
197 const auto savedSelectionBehavior = selectionBehavior();
199 const auto savedItemsExpandable = itemsExpandable();
200 setItemsExpandable(false);
201
202 const auto result = QTreeView::moveCursor(cursorAction, modifiers);
203
204 setItemsExpandable(savedItemsExpandable);
205 setSelectionBehavior(savedSelectionBehavior);
206
207 return result;
208}
209
210void TreeView::saveColumnLayout(const QString &stateGroupName)
211{
212 d->mStateGroupName = stateGroupName;
213 d->saveColumnLayout();
214}
215
216#include "moc_treeview.cpp"
static KSharedConfig::Ptr openStateConfig(const QString &fileName=QString())
A tree view that allows accessible column by column keyboard navigation and that has customizable col...
Definition treeview.h:40
void saveColumnLayout(const QString &stateGroupName)
Set the state config group name to use for saving the state.
Definition treeview.cpp:210
bool restoreColumnLayout(const QString &stateGroupName)
Restores the layout state under key stateGroupName and enables state saving when the object is destro...
Definition treeview.cpp:134
QString i18nc(const char *context, const char *text, const TYPE &arg...)
virtual int columnCount(const QModelIndex &parent) const const=0
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const const
virtual bool event(QEvent *event) override
QAbstractItemModel * model() const const
void setCheckable(bool)
bool isChecked() const const
void setData(const QVariant &data)
int count() const const
void moveSection(int from, int to)
void resizeSection(int logicalIndex, int size)
void sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
void sectionResized(int logicalIndex, int oldSize, int newSize)
int sectionSize(int logicalIndex) const const
bool isSortIndicatorShown() const const
void sortIndicatorChanged(int logicalIndex, Qt::SortOrder order)
Qt::SortOrder sortIndicatorOrder() const const
int sortIndicatorSection() const const
int visualIndex(int logicalIndex) const const
void triggered(QAction *action)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void installEventFilter(QObject *filterObj)
bool isEmpty() const const
typedef KeyboardModifiers
Horizontal
AscendingOrder
QHeaderView * header() const const
void hideColumn(int column)
bool isColumnHidden(int column) const const
virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override
void sortByColumn(int column, Qt::SortOrder order)
QString toString() const const
QPoint mapToGlobal(const QPoint &pos) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 3 2024 11:44:55 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.