Libkleo

dnattributeorderconfigwidget.cpp
1/* -*- c++ -*-
2 dnattributeorderconfigwidget.cpp
3
4 This file is part of libkleopatra, the KDE keymanagement library
5 SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include <config-libkleo.h>
11
12#include "dnattributeorderconfigwidget.h"
13
14#include <libkleo/dn.h>
15
16#include <libkleo_debug.h>
17
18#include <KLazyLocalizedString>
19#include <KLocalizedString>
20
21#include <QGridLayout>
22#include <QHeaderView>
23#include <QIcon>
24#include <QLabel>
25#include <QToolButton>
26#include <QTreeWidget>
27
28namespace
29{
30class TreeWidget : public QTreeWidget
31{
32 Q_OBJECT
33public:
35
36protected:
37 void focusInEvent(QFocusEvent *event) override
38 {
40 // workaround for wrong order of accessible focus events emitted by Qt for QTreeWidget;
41 // on first focusing of QTreeWidget, Qt sends focus event for current item before focus event for tree
42 // so that orca doesn't announce the current item;
43 // on re-focusing of QTreeWidget, Qt only sends focus event for tree
44 auto forceAccessibleFocusEventForCurrentItem = [this]() {
45 // force Qt to send a focus event for the current item to accessibility
46 // tools; otherwise, the user has no idea which item is selected when the
47 // list gets keyboard input focus
48 const QModelIndex index = currentIndex();
49 if (index.isValid()) {
50 currentChanged(index, QModelIndex{});
51 }
52 };
53 // queue the invocation, so that it happens after the widget itself got focus
54 QMetaObject::invokeMethod(this, forceAccessibleFocusEventForCurrentItem, Qt::QueuedConnection);
55 }
56};
57}
58
59class Kleo::DNAttributeOrderConfigWidget::DNAttributeOrderConfigWidgetPrivate
60{
61public:
62 enum { Right = 0, Left = 1, UUp = 2, Up = 3, Down = 4, DDown = 5 };
63
64 TreeWidget *availableLV = nullptr;
65 TreeWidget *currentLV = nullptr;
66 std::vector<QToolButton *> navTB;
67
68 QTreeWidgetItem *placeHolderItem = nullptr;
69};
70
71static void prepare(QTreeWidget *lv)
72{
73 lv->setAllColumnsShowFocus(true);
74 lv->header()->setStretchLastSection(true);
75 lv->setHeaderLabels(QStringList() << QString() << i18n("Description"));
76}
77
78Kleo::DNAttributeOrderConfigWidget::DNAttributeOrderConfigWidget(QWidget *parent, Qt::WindowFlags f)
79 : QWidget(parent, f)
80 , d(new DNAttributeOrderConfigWidgetPrivate)
81{
82 auto glay = new QGridLayout(this);
83 glay->setContentsMargins(0, 0, 0, 0);
84 glay->setColumnStretch(0, 1);
85 glay->setColumnStretch(2, 1);
86
87 int row = -1;
88
89 ++row;
90 auto availableAttributesLabel = new QLabel(i18nc("@label:textbox", "Available attributes:"), this);
91 glay->addWidget(availableAttributesLabel, row, 0);
92 auto currentAttributesLabel = new QLabel(i18nc("@label:textbox", "Current attribute order:"), this);
93 glay->addWidget(currentAttributesLabel, row, 2);
94
95 ++row;
96 glay->setRowStretch(row, 1);
97
98 d->availableLV = new TreeWidget(this);
99 availableAttributesLabel->setBuddy(d->availableLV);
100 d->availableLV->setAccessibleName(i18n("available attributes"));
101 prepare(d->availableLV);
102 d->availableLV->sortItems(0, Qt::AscendingOrder);
103 glay->addWidget(d->availableLV, row, 0);
104
105 d->placeHolderItem = new QTreeWidgetItem(d->availableLV);
106 d->placeHolderItem->setText(0, QStringLiteral("_X_"));
107 d->placeHolderItem->setText(1, i18n("All others"));
108 d->placeHolderItem->setData(0, Qt::AccessibleTextRole, i18n("All others"));
109
110 struct NavButtonInfo {
111 const char *icon;
112 const KLazyLocalizedString accessibleName;
113 const KLazyLocalizedString tooltip;
114 void (DNAttributeOrderConfigWidget::*slot)();
115 bool autorepeat;
116 };
117 static const std::vector<NavButtonInfo> navButtons = {
118 {
119 "go-next",
120 kli18nc("@action:button", "Add"),
121 kli18n("Add to current attribute order"),
122 &DNAttributeOrderConfigWidget::slotRightButtonClicked,
123 false,
124 },
125 {
126 "go-previous",
127 kli18nc("@action:button", "Remove"),
128 kli18n("Remove from current attribute order"),
129 &DNAttributeOrderConfigWidget::slotLeftButtonClicked,
130 false,
131 },
132 {
133 "go-top",
134 kli18nc("@action:button", "Move to Top"),
135 kli18n("Move to top"),
136 &DNAttributeOrderConfigWidget::slotDoubleUpButtonClicked,
137 false,
138 },
139 {
140 "go-up",
141 kli18nc("@action:button", "Move Up"),
142 kli18n("Move one up"),
143 &DNAttributeOrderConfigWidget::slotUpButtonClicked,
144 true,
145 },
146 {
147 "go-down",
148 kli18nc("@action:button", "Move Down"),
149 kli18n("Move one down"),
150 &DNAttributeOrderConfigWidget::slotDownButtonClicked,
151 true,
152 },
153 {
154 "go-bottom",
155 kli18nc("@action:button", "Move to Bottom"),
156 kli18n("Move to bottom"),
157 &DNAttributeOrderConfigWidget::slotDoubleDownButtonClicked,
158 false,
159 },
160 };
161
162 const auto createToolButton = [this](const NavButtonInfo &navButton) {
163 auto tb = new QToolButton{this};
164 tb->setIcon(QIcon::fromTheme(QLatin1StringView(navButton.icon)));
165 tb->setEnabled(false);
166 tb->setAccessibleName(KLocalizedString{navButton.accessibleName}.toString());
167 tb->setToolTip(KLocalizedString(navButton.tooltip).toString());
168 tb->setAutoRepeat(navButton.autorepeat);
169 connect(tb, &QToolButton::clicked, this, navButton.slot);
170 d->navTB.push_back(tb);
171 return tb;
172 };
173
174 {
175 auto buttonCol = new QVBoxLayout;
176 buttonCol->addStretch();
177 buttonCol->addWidget(createToolButton(navButtons[DNAttributeOrderConfigWidgetPrivate::Right]));
178 buttonCol->addWidget(createToolButton(navButtons[DNAttributeOrderConfigWidgetPrivate::Left]));
179 buttonCol->addStretch();
180
181 glay->addLayout(buttonCol, row, 1);
182 }
183
184 d->currentLV = new TreeWidget(this);
185 currentAttributesLabel->setBuddy(d->currentLV);
186 d->currentLV->setAccessibleName(i18n("current attribute order"));
187 prepare(d->currentLV);
188 glay->addWidget(d->currentLV, row, 2);
189
190 {
191 auto buttonCol = new QVBoxLayout;
192 buttonCol->addStretch();
193 buttonCol->addWidget(createToolButton(navButtons[DNAttributeOrderConfigWidgetPrivate::UUp]));
194 buttonCol->addWidget(createToolButton(navButtons[DNAttributeOrderConfigWidgetPrivate::Up]));
195 buttonCol->addWidget(createToolButton(navButtons[DNAttributeOrderConfigWidgetPrivate::Down]));
196 buttonCol->addWidget(createToolButton(navButtons[DNAttributeOrderConfigWidgetPrivate::DDown]));
197 buttonCol->addStretch();
198
199 glay->addLayout(buttonCol, row, 3);
200 }
201
202#ifndef NDEBUG
203 Q_ASSERT(d->navTB.size() == navButtons.size());
204 for (uint i = 0; i < navButtons.size(); ++i) {
205 Q_ASSERT(d->navTB[i]->accessibleName() == KLocalizedString{navButtons[i].accessibleName}.toString());
206 }
207#endif
208
209 connect(d->availableLV, &QTreeWidget::itemSelectionChanged, this, &DNAttributeOrderConfigWidget::slotAvailableSelectionChanged);
210 connect(d->currentLV, &QTreeWidget::itemSelectionChanged, this, &DNAttributeOrderConfigWidget::slotCurrentOrderSelectionChanged);
211}
212
213Kleo::DNAttributeOrderConfigWidget::~DNAttributeOrderConfigWidget() = default;
214
215void Kleo::DNAttributeOrderConfigWidget::setAttributeOrder(const QStringList &order)
216{
217 // save the _X_ item:
218 takePlaceHolderItem();
219 // clear the rest:
220 d->availableLV->clear();
221 d->currentLV->clear();
222
223 // fill the RHS listview:
224 QTreeWidgetItem *last = nullptr;
225 for (const auto &entry : order) {
226 const QString attr = entry.toUpper();
227 if (attr == QLatin1StringView("_X_")) {
228 takePlaceHolderItem();
229 d->currentLV->insertTopLevelItem(d->currentLV->topLevelItemCount(), d->placeHolderItem);
230 last = d->placeHolderItem;
231 } else {
232 last = new QTreeWidgetItem(d->currentLV, last);
233 last->setText(0, attr);
234 const auto label = DN::attributeNameToLabel(attr);
235 last->setText(1, label);
236 const QString accessibleName = label + QLatin1StringView(", ") + attr;
238 }
239 }
240 d->currentLV->setCurrentItem(d->currentLV->topLevelItem(0));
241
242 // fill the LHS listview with what's left:
243
244 const QStringList all = DN::attributeNames();
245 for (const auto &attr : all) {
246 if (!order.contains(attr, Qt::CaseInsensitive)) {
247 auto item = new QTreeWidgetItem(d->availableLV);
248 item->setText(0, attr);
249 const auto label = DN::attributeNameToLabel(attr);
250 item->setText(1, label);
251 const QString accessibleName = label + QLatin1StringView(", ") + attr;
252 item->setData(0, Qt::AccessibleTextRole, accessibleName);
253 }
254 }
255
256 if (!d->placeHolderItem->treeWidget()) {
257 d->availableLV->addTopLevelItem(d->placeHolderItem);
258 }
259 d->availableLV->setCurrentItem(d->availableLV->topLevelItem(0));
260}
261
262void Kleo::DNAttributeOrderConfigWidget::takePlaceHolderItem()
263{
264 if (QTreeWidget *lv = d->placeHolderItem->treeWidget()) {
265 lv->takeTopLevelItem(lv->indexOfTopLevelItem(d->placeHolderItem));
266 }
267}
268
269QStringList Kleo::DNAttributeOrderConfigWidget::attributeOrder() const
270{
271 QStringList order;
272 for (QTreeWidgetItemIterator it(d->currentLV); (*it); ++it) {
273 order.push_back((*it)->text(0));
274 }
275 return order;
276}
277
278void Kleo::DNAttributeOrderConfigWidget::slotAvailableSelectionChanged()
279{
280 d->navTB[DNAttributeOrderConfigWidgetPrivate::Right]->setEnabled(!d->availableLV->selectedItems().empty());
281}
282
283void Kleo::DNAttributeOrderConfigWidget::slotCurrentOrderSelectionChanged()
284{
285 const auto selectedItems = d->currentLV->selectedItems();
286 auto selectedItem = selectedItems.empty() ? nullptr : selectedItems.front();
287 enableDisableButtons(selectedItem);
288}
289
290void Kleo::DNAttributeOrderConfigWidget::enableDisableButtons(QTreeWidgetItem *item)
291{
292 d->navTB[DNAttributeOrderConfigWidgetPrivate::UUp]->setEnabled(item && d->currentLV->itemAbove(item));
293 d->navTB[DNAttributeOrderConfigWidgetPrivate::Up]->setEnabled(item && d->currentLV->itemAbove(item));
294 d->navTB[DNAttributeOrderConfigWidgetPrivate::Left]->setEnabled(item);
295 d->navTB[DNAttributeOrderConfigWidgetPrivate::Down]->setEnabled(item && d->currentLV->itemBelow(item));
296 d->navTB[DNAttributeOrderConfigWidgetPrivate::DDown]->setEnabled(item && d->currentLV->itemBelow(item));
297}
298
299void Kleo::DNAttributeOrderConfigWidget::slotUpButtonClicked()
300{
301 if (d->currentLV->selectedItems().isEmpty()) {
302 return;
303 }
304 QTreeWidgetItem *item = d->currentLV->selectedItems().first();
305 int itemIndex = d->currentLV->indexOfTopLevelItem(item);
306 if (itemIndex <= 0) {
307 return;
308 }
309 d->currentLV->takeTopLevelItem(itemIndex);
310 d->currentLV->insertTopLevelItem(itemIndex - 1, item);
311 d->currentLV->setCurrentItem(item);
312 enableDisableButtons(item);
313 Q_EMIT changed();
314}
315
316void Kleo::DNAttributeOrderConfigWidget::slotDoubleUpButtonClicked()
317{
318 if (d->currentLV->selectedItems().isEmpty()) {
319 return;
320 }
321 QTreeWidgetItem *item = d->currentLV->selectedItems().first();
322 int itemIndex = d->currentLV->indexOfTopLevelItem(item);
323 if (itemIndex == 0) {
324 return;
325 }
326 d->currentLV->takeTopLevelItem(itemIndex);
327 d->currentLV->insertTopLevelItem(0, item);
328 d->currentLV->setCurrentItem(item);
329 enableDisableButtons(item);
330 Q_EMIT changed();
331}
332
333void Kleo::DNAttributeOrderConfigWidget::slotDownButtonClicked()
334{
335 if (d->currentLV->selectedItems().isEmpty()) {
336 return;
337 }
338 QTreeWidgetItem *item = d->currentLV->selectedItems().first();
339 int itemIndex = d->currentLV->indexOfTopLevelItem(item);
340 if (itemIndex + 1 >= d->currentLV->topLevelItemCount()) {
341 return;
342 }
343 d->currentLV->takeTopLevelItem(itemIndex);
344 d->currentLV->insertTopLevelItem(itemIndex + 1, item);
345 d->currentLV->setCurrentItem(item);
346 enableDisableButtons(item);
347 Q_EMIT changed();
348}
349
350void Kleo::DNAttributeOrderConfigWidget::slotDoubleDownButtonClicked()
351{
352 if (d->currentLV->selectedItems().isEmpty()) {
353 return;
354 }
355 QTreeWidgetItem *item = d->currentLV->selectedItems().first();
356 const int itemIndex = d->currentLV->indexOfTopLevelItem(item);
357 if (itemIndex + 1 >= d->currentLV->topLevelItemCount()) {
358 return;
359 }
360 d->currentLV->takeTopLevelItem(itemIndex);
361 d->currentLV->addTopLevelItem(item);
362 d->currentLV->setCurrentItem(item);
363 enableDisableButtons(item);
364 Q_EMIT changed();
365}
366
367void Kleo::DNAttributeOrderConfigWidget::slotLeftButtonClicked()
368{
369 if (d->currentLV->selectedItems().isEmpty()) {
370 return;
371 }
372 QTreeWidgetItem *right = d->currentLV->selectedItems().first();
373 QTreeWidgetItem *next = d->currentLV->itemBelow(right);
374 if (!next) {
375 next = d->currentLV->itemAbove(right);
376 }
377 d->currentLV->takeTopLevelItem(d->currentLV->indexOfTopLevelItem(right));
378 d->availableLV->addTopLevelItem(right);
379 d->availableLV->sortItems(0, Qt::AscendingOrder);
380 d->availableLV->setCurrentItem(right);
381 if (next) {
382 d->currentLV->setCurrentItem(next);
383 }
384 enableDisableButtons(next);
385 Q_EMIT changed();
386}
387
388void Kleo::DNAttributeOrderConfigWidget::slotRightButtonClicked()
389{
390 if (d->availableLV->selectedItems().isEmpty()) {
391 return;
392 }
393 QTreeWidgetItem *left = d->availableLV->selectedItems().first();
394 QTreeWidgetItem *next = d->availableLV->itemBelow(left);
395 if (!next) {
396 next = d->availableLV->itemAbove(left);
397 }
398 d->availableLV->takeTopLevelItem(d->availableLV->indexOfTopLevelItem(left));
399 int newRightIndex = d->currentLV->topLevelItemCount();
400 if (!d->currentLV->selectedItems().isEmpty()) {
401 QTreeWidgetItem *right = d->currentLV->selectedItems().first();
402 newRightIndex = d->currentLV->indexOfTopLevelItem(right);
403 }
404 d->currentLV->insertTopLevelItem(newRightIndex, left);
405 d->currentLV->setCurrentItem(left);
406 enableDisableButtons(left);
407 d->navTB[DNAttributeOrderConfigWidgetPrivate::Right]->setEnabled(next);
408 if (next) {
409 d->availableLV->setCurrentItem(next);
410 }
411 Q_EMIT changed();
412}
413
414void Kleo::DNAttributeOrderConfigWidget::virtual_hook(int, void *)
415{
416}
417
418#include "dnattributeorderconfigwidget.moc"
419
420#include "moc_dnattributeorderconfigwidget.cpp"
QString toString() const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QAction * next(const QObject *recvr, const char *slot, QObject *parent)
void clicked(bool checked)
void setIcon(const QIcon &icon)
virtual void focusInEvent(QFocusEvent *event) override
void addStretch(int stretch)
void setStretchLastSection(bool stretch)
QIcon fromTheme(const QString &name)
void push_back(parameter_type value)
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
bool isValid() const const
QString toUpper() const const
CaseInsensitive
QueuedConnection
AccessibleTextRole
AscendingOrder
typedef WindowFlags
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setAllColumnsShowFocus(bool enable)
QHeaderView * header() const const
QTreeWidget(QWidget *parent)
int indexOfTopLevelItem(QTreeWidgetItem *item) const const
void itemSelectionChanged()
void setHeaderLabels(const QStringList &labels)
QTreeWidgetItem * takeTopLevelItem(int index)
virtual void setData(int column, int role, const QVariant &value)
void setText(int column, const QString &text)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:11:57 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.