Libkleo

treewidget.cpp
1/*
2 ui/treewidget.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 "treewidget.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 TreeWidget::Private
26{
27 TreeWidget *q;
28
29public:
30 QMenu *mHeaderPopup = nullptr;
31 QList<QAction *> mColumnActions;
32 QString mStateGroupName;
33
34 Private(TreeWidget *qq)
35 : q(qq)
36 {
37 }
38
39 ~Private()
40 {
41 saveColumnLayout();
42 }
43 void saveColumnLayout();
44};
45
46TreeWidget::TreeWidget(QWidget *parent)
47 : QTreeWidget::QTreeWidget(parent)
48 , d{new Private(this)}
49{
50 header()->installEventFilter(this);
51}
52
53TreeWidget::~TreeWidget() = default;
54
55void TreeWidget::Private::saveColumnLayout()
56{
57 if (mStateGroupName.isEmpty()) {
58 return;
59 }
60 auto config = KConfigGroup(KSharedConfig::openStateConfig(), mStateGroupName);
61 auto header = q->header();
62
63 QVariantList columnVisibility;
64 QVariantList columnOrder;
65 QVariantList columnWidths;
66 const int headerCount = header->count();
67 columnVisibility.reserve(headerCount);
68 columnWidths.reserve(headerCount);
69 columnOrder.reserve(headerCount);
70 for (int i = 0; i < headerCount; ++i) {
71 columnVisibility << QVariant(!q->isColumnHidden(i));
72 columnWidths << QVariant(header->sectionSize(i));
73 columnOrder << QVariant(header->visualIndex(i));
74 }
75
76 config.writeEntry("ColumnVisibility", columnVisibility);
77 config.writeEntry("ColumnOrder", columnOrder);
78 config.writeEntry("ColumnWidths", columnWidths);
79
80 config.writeEntry("SortAscending", (int)header->sortIndicatorOrder());
81 if (header->isSortIndicatorShown()) {
82 config.writeEntry("SortColumn", header->sortIndicatorSection());
83 } else {
84 config.writeEntry("SortColumn", -1);
85 }
86 config.sync();
87}
88
89bool TreeWidget::restoreColumnLayout(const QString &stateGroupName)
90{
91 if (stateGroupName.isEmpty()) {
92 return false;
93 }
94 d->mStateGroupName = stateGroupName;
95 auto config = KConfigGroup(KSharedConfig::openStateConfig(), d->mStateGroupName);
96 auto header = this->header();
97
98 QVariantList columnVisibility = config.readEntry("ColumnVisibility", QVariantList());
99 QVariantList columnOrder = config.readEntry("ColumnOrder", QVariantList());
100 QVariantList columnWidths = config.readEntry("ColumnWidths", QVariantList());
101
102 if (!columnVisibility.isEmpty() && !columnOrder.isEmpty() && !columnWidths.isEmpty()) {
103 for (int i = 0; i < header->count(); ++i) {
104 if (i >= columnOrder.size() || i >= columnWidths.size() || i >= columnVisibility.size()) {
105 // An additional column that was not around last time we saved.
106 // We default to hidden.
107 hideColumn(i);
108 continue;
109 }
110 bool visible = columnVisibility[i].toBool();
111 int width = columnWidths[i].toInt();
112 int order = columnOrder[i].toInt();
113
114 header->resizeSection(i, width ? width : 100);
115 header->moveSection(header->visualIndex(i), order);
116
117 if (!visible) {
118 hideColumn(i);
119 }
120 }
121 }
122
123 int sortOrder = config.readEntry("SortAscending", (int)Qt::AscendingOrder);
124 int sortColumn = config.readEntry("SortColumn", 0);
125 if (sortColumn >= 0) {
126 sortByColumn(sortColumn, (Qt::SortOrder)sortOrder);
127 }
128 connect(header, &QHeaderView::sectionResized, this, [this]() {
129 d->saveColumnLayout();
130 });
131 connect(header, &QHeaderView::sectionMoved, this, [this]() {
132 d->saveColumnLayout();
133 });
134 connect(header, &QHeaderView::sortIndicatorChanged, this, [this]() {
135 d->saveColumnLayout();
136 });
137 return !columnVisibility.isEmpty() && !columnOrder.isEmpty() && !columnWidths.isEmpty();
138}
139
140bool TreeWidget::eventFilter(QObject *watched, QEvent *event)
141{
142 Q_UNUSED(watched)
143 if (event->type() == QEvent::ContextMenu) {
144 auto e = static_cast<QContextMenuEvent *>(event);
145
146 if (!d->mHeaderPopup) {
147 d->mHeaderPopup = new QMenu(this);
148 d->mHeaderPopup->setTitle(i18nc("@title:menu", "View Columns"));
149 for (int i = 0; i < model()->columnCount(); ++i) {
150 QAction *tmp = d->mHeaderPopup->addAction(model()->headerData(i, Qt::Horizontal).toString());
151 tmp->setData(QVariant(i));
152 tmp->setCheckable(true);
153 d->mColumnActions << tmp;
154 }
155
156 connect(d->mHeaderPopup, &QMenu::triggered, this, [this](QAction *action) {
157 const int col = action->data().toInt();
158 if (action->isChecked()) {
159 showColumn(col);
160 } else {
161 hideColumn(col);
162 }
163
164 if (action->isChecked()) {
165 Q_EMIT columnEnabled(col);
166 } else {
167 Q_EMIT columnDisabled(col);
168 }
169 d->saveColumnLayout();
170 });
171 }
172
173 for (QAction *action : std::as_const(d->mColumnActions)) {
174 const int column = action->data().toInt();
175 action->setChecked(!isColumnHidden(column));
176 }
177
178 d->mHeaderPopup->popup(mapToGlobal(e->pos()));
179 return true;
180 }
181
182 return false;
183}
184
185QModelIndex TreeWidget::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
186{
187 // make column by column keyboard navigation with Left/Right possible by switching
188 // the selection behavior to SelectItems before calling the parent class's moveCursor,
189 // because it ignores MoveLeft/MoveRight if the selection behavior is SelectRows;
190 // moreover, temporarily disable exanding of items to prevent expanding/collapsing
191 // on MoveLeft/MoveRight
192 if ((cursorAction != MoveLeft) && (cursorAction != MoveRight)) {
193 return QTreeWidget::moveCursor(cursorAction, modifiers);
194 }
195
196 const auto savedSelectionBehavior = selectionBehavior();
197 setSelectionBehavior(SelectItems);
198 const auto savedItemsExpandable = itemsExpandable();
199 setItemsExpandable(false);
200
201 const auto result = QTreeWidget::moveCursor(cursorAction, modifiers);
202
203 setItemsExpandable(savedItemsExpandable);
204 setSelectionBehavior(savedSelectionBehavior);
205
206 return result;
207}
208
209#include "moc_treewidget.cpp"
static KSharedConfig::Ptr openStateConfig(const QString &fileName=QString())
QString i18nc(const char *context, const char *text, const TYPE &arg...)
virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)=0
void setCheckable(bool)
bool isChecked() const const
void setData(const QVariant &data)
void sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
void sectionResized(int logicalIndex, int oldSize, int newSize)
void sortIndicatorChanged(int logicalIndex, Qt::SortOrder order)
void triggered(QAction *action)
void installEventFilter(QObject *filterObj)
bool isEmpty() const const
typedef KeyboardModifiers
Horizontal
AscendingOrder
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.