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 
28 namespace
29 {
30 class TreeWidget : public QTreeWidget
31 {
32  Q_OBJECT
33 public:
35 
36 protected:
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 
44 private:
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 
57 class Kleo::DNAttributeOrderConfigWidget::DNAttributeOrderConfigWidgetPrivate
58 {
59 public:
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 
69 static void prepare(QTreeWidget *lv)
70 {
71  lv->setAllColumnsShowFocus(true);
72  lv->header()->setStretchLastSection(true);
73  lv->setHeaderLabels(QStringList() << QString() << i18n("Description"));
74 }
75 
76 Kleo::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(i18n("Available attributes:"), this);
89  glay->addWidget(availableAttributesLabel, row, 0);
90  auto currentAttributesLabel = new QLabel(i18n("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 
211 Kleo::DNAttributeOrderConfigWidget::~DNAttributeOrderConfigWidget() = default;
212 
213 void 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;
235  last->setData(0, Qt::AccessibleTextRole, accessibleName);
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 
260 void Kleo::DNAttributeOrderConfigWidget::takePlaceHolderItem()
261 {
262  if (QTreeWidget *lv = d->placeHolderItem->treeWidget()) {
263  lv->takeTopLevelItem(lv->indexOfTopLevelItem(d->placeHolderItem));
264  }
265 }
266 
267 QStringList 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 
276 void Kleo::DNAttributeOrderConfigWidget::slotAvailableSelectionChanged()
277 {
278  d->navTB[DNAttributeOrderConfigWidgetPrivate::Right]->setEnabled(!d->availableLV->selectedItems().empty());
279 }
280 
281 void Kleo::DNAttributeOrderConfigWidget::slotCurrentOrderSelectionChanged()
282 {
283  const auto selectedItems = d->currentLV->selectedItems();
284  auto selectedItem = selectedItems.empty() ? nullptr : selectedItems.front();
285  enableDisableButtons(selectedItem);
286 }
287 
288 void 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 
297 void 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 
314 void 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 
331 void 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 
348 void 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 
365 void 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 
386 void 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 
412 void Kleo::DNAttributeOrderConfigWidget::virtual_hook(int, void *)
413 {
414 }
415 
416 #include "dnattributeorderconfigwidget.moc"
417 
418 #include "moc_dnattributeorderconfigwidget.cpp"
QTextStream & right(QTextStream &stream)
AccessibleTextRole
QString toUpper() const const
CaseInsensitive
void setStretchLastSection(bool stretch)
QTextStream & left(QTextStream &stream)
QTreeWidget(QWidget *parent)
void clicked(bool checked)
AscendingOrder
QIcon fromTheme(const QString &name)
int indexOfTopLevelItem(QTreeWidgetItem *item) const const
void push_back(const T &value)
QString toString() const
void addStretch(int stretch)
QHeaderView * header() const const
typedef WindowFlags
bool empty() const const
QString i18n(const char *text, const TYPE &arg...)
QueuedConnection
void setText(int column, const QString &text)
void setAllColumnsShowFocus(bool enable)
virtual void focusInEvent(QFocusEvent *event) override
void itemSelectionChanged()
void setCurrentItem(QTreeWidgetItem *item)
void setIcon(const QIcon &icon)
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
QTreeWidgetItem * takeTopLevelItem(int index)
void setHeaderLabels(const QStringList &labels)
virtual void setData(int column, int role, const QVariant &value)
QAction * next(const QObject *recvr, const char *slot, QObject *parent)
QTreeWidgetItem * currentItem() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Thu Feb 15 2024 03:56:14 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.