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

KDE's Doxygen guidelines are available online.