File indexing completed on 2025-01-05 04:55:47
0001 /* -*- c++ -*- 0002 dnattributeorderconfigwidget.cpp 0003 0004 This file is part of libkleopatra, the KDE keymanagement library 0005 SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include <config-libkleo.h> 0011 0012 #include "dnattributeorderconfigwidget.h" 0013 0014 #include <libkleo/dn.h> 0015 0016 #include <libkleo_debug.h> 0017 0018 #include <KLazyLocalizedString> 0019 #include <KLocalizedString> 0020 0021 #include <QGridLayout> 0022 #include <QHeaderView> 0023 #include <QIcon> 0024 #include <QLabel> 0025 #include <QToolButton> 0026 #include <QTreeWidget> 0027 0028 namespace 0029 { 0030 class TreeWidget : public QTreeWidget 0031 { 0032 Q_OBJECT 0033 public: 0034 using QTreeWidget::QTreeWidget; 0035 0036 protected: 0037 void focusInEvent(QFocusEvent *event) override 0038 { 0039 QTreeWidget::focusInEvent(event); 0040 // queue the invokation, so that it happens after the widget itself got focus 0041 QMetaObject::invokeMethod(this, &TreeWidget::forceAccessibleFocusEventForCurrentItem, Qt::QueuedConnection); 0042 } 0043 0044 private: 0045 void forceAccessibleFocusEventForCurrentItem() 0046 { 0047 // force Qt to send a focus event for the current item to accessibility 0048 // tools; otherwise, the user has no idea which item is selected when the 0049 // list gets keyboard input focus 0050 const auto current = currentItem(); 0051 setCurrentItem(nullptr); 0052 setCurrentItem(current); 0053 } 0054 }; 0055 } 0056 0057 class Kleo::DNAttributeOrderConfigWidget::DNAttributeOrderConfigWidgetPrivate 0058 { 0059 public: 0060 enum { Right = 0, Left = 1, UUp = 2, Up = 3, Down = 4, DDown = 5 }; 0061 0062 TreeWidget *availableLV = nullptr; 0063 TreeWidget *currentLV = nullptr; 0064 std::vector<QToolButton *> navTB; 0065 0066 QTreeWidgetItem *placeHolderItem = nullptr; 0067 }; 0068 0069 static void prepare(QTreeWidget *lv) 0070 { 0071 lv->setAllColumnsShowFocus(true); 0072 lv->header()->setStretchLastSection(true); 0073 lv->setHeaderLabels(QStringList() << QString() << i18n("Description")); 0074 } 0075 0076 Kleo::DNAttributeOrderConfigWidget::DNAttributeOrderConfigWidget(QWidget *parent, Qt::WindowFlags f) 0077 : QWidget(parent, f) 0078 , d(new DNAttributeOrderConfigWidgetPrivate) 0079 { 0080 auto glay = new QGridLayout(this); 0081 glay->setContentsMargins(0, 0, 0, 0); 0082 glay->setColumnStretch(0, 1); 0083 glay->setColumnStretch(2, 1); 0084 0085 int row = -1; 0086 0087 ++row; 0088 auto availableAttributesLabel = new QLabel(i18n("Available attributes:"), this); 0089 glay->addWidget(availableAttributesLabel, row, 0); 0090 auto currentAttributesLabel = new QLabel(i18n("Current attribute order:"), this); 0091 glay->addWidget(currentAttributesLabel, row, 2); 0092 0093 ++row; 0094 glay->setRowStretch(row, 1); 0095 0096 d->availableLV = new TreeWidget(this); 0097 availableAttributesLabel->setBuddy(d->availableLV); 0098 d->availableLV->setAccessibleName(i18n("available attributes")); 0099 prepare(d->availableLV); 0100 d->availableLV->sortItems(0, Qt::AscendingOrder); 0101 glay->addWidget(d->availableLV, row, 0); 0102 0103 d->placeHolderItem = new QTreeWidgetItem(d->availableLV); 0104 d->placeHolderItem->setText(0, QStringLiteral("_X_")); 0105 d->placeHolderItem->setText(1, i18n("All others")); 0106 d->placeHolderItem->setData(0, Qt::AccessibleTextRole, i18n("All others")); 0107 0108 struct NavButtonInfo { 0109 const char *icon; 0110 const KLazyLocalizedString accessibleName; 0111 const KLazyLocalizedString tooltip; 0112 void (DNAttributeOrderConfigWidget::*slot)(); 0113 bool autorepeat; 0114 }; 0115 static const std::vector<NavButtonInfo> navButtons = { 0116 { 0117 "go-next", 0118 kli18nc("@action:button", "Add"), 0119 kli18n("Add to current attribute order"), 0120 &DNAttributeOrderConfigWidget::slotRightButtonClicked, 0121 false, 0122 }, 0123 { 0124 "go-previous", 0125 kli18nc("@action:button", "Remove"), 0126 kli18n("Remove from current attribute order"), 0127 &DNAttributeOrderConfigWidget::slotLeftButtonClicked, 0128 false, 0129 }, 0130 { 0131 "go-top", 0132 kli18nc("@action:button", "Move to Top"), 0133 kli18n("Move to top"), 0134 &DNAttributeOrderConfigWidget::slotDoubleUpButtonClicked, 0135 false, 0136 }, 0137 { 0138 "go-up", 0139 kli18nc("@action:button", "Move Up"), 0140 kli18n("Move one up"), 0141 &DNAttributeOrderConfigWidget::slotUpButtonClicked, 0142 true, 0143 }, 0144 { 0145 "go-down", 0146 kli18nc("@action:button", "Move Down"), 0147 kli18n("Move one down"), 0148 &DNAttributeOrderConfigWidget::slotDownButtonClicked, 0149 true, 0150 }, 0151 { 0152 "go-bottom", 0153 kli18nc("@action:button", "Move to Bottom"), 0154 kli18n("Move to bottom"), 0155 &DNAttributeOrderConfigWidget::slotDoubleDownButtonClicked, 0156 false, 0157 }, 0158 }; 0159 0160 const auto createToolButton = [this](const NavButtonInfo &navButton) { 0161 auto tb = new QToolButton{this}; 0162 tb->setIcon(QIcon::fromTheme(QLatin1StringView(navButton.icon))); 0163 tb->setEnabled(false); 0164 tb->setAccessibleName(KLocalizedString{navButton.accessibleName}.toString()); 0165 tb->setToolTip(KLocalizedString(navButton.tooltip).toString()); 0166 tb->setAutoRepeat(navButton.autorepeat); 0167 connect(tb, &QToolButton::clicked, this, navButton.slot); 0168 d->navTB.push_back(tb); 0169 return tb; 0170 }; 0171 0172 { 0173 auto buttonCol = new QVBoxLayout; 0174 buttonCol->addStretch(); 0175 buttonCol->addWidget(createToolButton(navButtons[DNAttributeOrderConfigWidgetPrivate::Right])); 0176 buttonCol->addWidget(createToolButton(navButtons[DNAttributeOrderConfigWidgetPrivate::Left])); 0177 buttonCol->addStretch(); 0178 0179 glay->addLayout(buttonCol, row, 1); 0180 } 0181 0182 d->currentLV = new TreeWidget(this); 0183 currentAttributesLabel->setBuddy(d->currentLV); 0184 d->currentLV->setAccessibleName(i18n("current attribute order")); 0185 prepare(d->currentLV); 0186 glay->addWidget(d->currentLV, row, 2); 0187 0188 { 0189 auto buttonCol = new QVBoxLayout; 0190 buttonCol->addStretch(); 0191 buttonCol->addWidget(createToolButton(navButtons[DNAttributeOrderConfigWidgetPrivate::UUp])); 0192 buttonCol->addWidget(createToolButton(navButtons[DNAttributeOrderConfigWidgetPrivate::Up])); 0193 buttonCol->addWidget(createToolButton(navButtons[DNAttributeOrderConfigWidgetPrivate::Down])); 0194 buttonCol->addWidget(createToolButton(navButtons[DNAttributeOrderConfigWidgetPrivate::DDown])); 0195 buttonCol->addStretch(); 0196 0197 glay->addLayout(buttonCol, row, 3); 0198 } 0199 0200 #ifndef NDEBUG 0201 Q_ASSERT(d->navTB.size() == navButtons.size()); 0202 for (uint i = 0; i < navButtons.size(); ++i) { 0203 Q_ASSERT(d->navTB[i]->accessibleName() == KLocalizedString{navButtons[i].accessibleName}.toString()); 0204 } 0205 #endif 0206 0207 connect(d->availableLV, &QTreeWidget::itemSelectionChanged, this, &DNAttributeOrderConfigWidget::slotAvailableSelectionChanged); 0208 connect(d->currentLV, &QTreeWidget::itemSelectionChanged, this, &DNAttributeOrderConfigWidget::slotCurrentOrderSelectionChanged); 0209 } 0210 0211 Kleo::DNAttributeOrderConfigWidget::~DNAttributeOrderConfigWidget() = default; 0212 0213 void Kleo::DNAttributeOrderConfigWidget::setAttributeOrder(const QStringList &order) 0214 { 0215 // save the _X_ item: 0216 takePlaceHolderItem(); 0217 // clear the rest: 0218 d->availableLV->clear(); 0219 d->currentLV->clear(); 0220 0221 // fill the RHS listview: 0222 QTreeWidgetItem *last = nullptr; 0223 for (const auto &entry : order) { 0224 const QString attr = entry.toUpper(); 0225 if (attr == QLatin1StringView("_X_")) { 0226 takePlaceHolderItem(); 0227 d->currentLV->insertTopLevelItem(d->currentLV->topLevelItemCount(), d->placeHolderItem); 0228 last = d->placeHolderItem; 0229 } else { 0230 last = new QTreeWidgetItem(d->currentLV, last); 0231 last->setText(0, attr); 0232 const auto label = DN::attributeNameToLabel(attr); 0233 last->setText(1, label); 0234 const QString accessibleName = label + QLatin1StringView(", ") + attr; 0235 last->setData(0, Qt::AccessibleTextRole, accessibleName); 0236 } 0237 } 0238 d->currentLV->setCurrentItem(d->currentLV->topLevelItem(0)); 0239 0240 // fill the LHS listview with what's left: 0241 0242 const QStringList all = DN::attributeNames(); 0243 for (const auto &attr : all) { 0244 if (!order.contains(attr, Qt::CaseInsensitive)) { 0245 auto item = new QTreeWidgetItem(d->availableLV); 0246 item->setText(0, attr); 0247 const auto label = DN::attributeNameToLabel(attr); 0248 item->setText(1, label); 0249 const QString accessibleName = label + QLatin1StringView(", ") + attr; 0250 item->setData(0, Qt::AccessibleTextRole, accessibleName); 0251 } 0252 } 0253 0254 if (!d->placeHolderItem->treeWidget()) { 0255 d->availableLV->addTopLevelItem(d->placeHolderItem); 0256 } 0257 d->availableLV->setCurrentItem(d->availableLV->topLevelItem(0)); 0258 } 0259 0260 void Kleo::DNAttributeOrderConfigWidget::takePlaceHolderItem() 0261 { 0262 if (QTreeWidget *lv = d->placeHolderItem->treeWidget()) { 0263 lv->takeTopLevelItem(lv->indexOfTopLevelItem(d->placeHolderItem)); 0264 } 0265 } 0266 0267 QStringList Kleo::DNAttributeOrderConfigWidget::attributeOrder() const 0268 { 0269 QStringList order; 0270 for (QTreeWidgetItemIterator it(d->currentLV); (*it); ++it) { 0271 order.push_back((*it)->text(0)); 0272 } 0273 return order; 0274 } 0275 0276 void Kleo::DNAttributeOrderConfigWidget::slotAvailableSelectionChanged() 0277 { 0278 d->navTB[DNAttributeOrderConfigWidgetPrivate::Right]->setEnabled(!d->availableLV->selectedItems().empty()); 0279 } 0280 0281 void Kleo::DNAttributeOrderConfigWidget::slotCurrentOrderSelectionChanged() 0282 { 0283 const auto selectedItems = d->currentLV->selectedItems(); 0284 auto selectedItem = selectedItems.empty() ? nullptr : selectedItems.front(); 0285 enableDisableButtons(selectedItem); 0286 } 0287 0288 void Kleo::DNAttributeOrderConfigWidget::enableDisableButtons(QTreeWidgetItem *item) 0289 { 0290 d->navTB[DNAttributeOrderConfigWidgetPrivate::UUp]->setEnabled(item && d->currentLV->itemAbove(item)); 0291 d->navTB[DNAttributeOrderConfigWidgetPrivate::Up]->setEnabled(item && d->currentLV->itemAbove(item)); 0292 d->navTB[DNAttributeOrderConfigWidgetPrivate::Left]->setEnabled(item); 0293 d->navTB[DNAttributeOrderConfigWidgetPrivate::Down]->setEnabled(item && d->currentLV->itemBelow(item)); 0294 d->navTB[DNAttributeOrderConfigWidgetPrivate::DDown]->setEnabled(item && d->currentLV->itemBelow(item)); 0295 } 0296 0297 void Kleo::DNAttributeOrderConfigWidget::slotUpButtonClicked() 0298 { 0299 if (d->currentLV->selectedItems().isEmpty()) { 0300 return; 0301 } 0302 QTreeWidgetItem *item = d->currentLV->selectedItems().first(); 0303 int itemIndex = d->currentLV->indexOfTopLevelItem(item); 0304 if (itemIndex <= 0) { 0305 return; 0306 } 0307 d->currentLV->takeTopLevelItem(itemIndex); 0308 d->currentLV->insertTopLevelItem(itemIndex - 1, item); 0309 d->currentLV->setCurrentItem(item); 0310 enableDisableButtons(item); 0311 Q_EMIT changed(); 0312 } 0313 0314 void Kleo::DNAttributeOrderConfigWidget::slotDoubleUpButtonClicked() 0315 { 0316 if (d->currentLV->selectedItems().isEmpty()) { 0317 return; 0318 } 0319 QTreeWidgetItem *item = d->currentLV->selectedItems().first(); 0320 int itemIndex = d->currentLV->indexOfTopLevelItem(item); 0321 if (itemIndex == 0) { 0322 return; 0323 } 0324 d->currentLV->takeTopLevelItem(itemIndex); 0325 d->currentLV->insertTopLevelItem(0, item); 0326 d->currentLV->setCurrentItem(item); 0327 enableDisableButtons(item); 0328 Q_EMIT changed(); 0329 } 0330 0331 void Kleo::DNAttributeOrderConfigWidget::slotDownButtonClicked() 0332 { 0333 if (d->currentLV->selectedItems().isEmpty()) { 0334 return; 0335 } 0336 QTreeWidgetItem *item = d->currentLV->selectedItems().first(); 0337 int itemIndex = d->currentLV->indexOfTopLevelItem(item); 0338 if (itemIndex + 1 >= d->currentLV->topLevelItemCount()) { 0339 return; 0340 } 0341 d->currentLV->takeTopLevelItem(itemIndex); 0342 d->currentLV->insertTopLevelItem(itemIndex + 1, item); 0343 d->currentLV->setCurrentItem(item); 0344 enableDisableButtons(item); 0345 Q_EMIT changed(); 0346 } 0347 0348 void Kleo::DNAttributeOrderConfigWidget::slotDoubleDownButtonClicked() 0349 { 0350 if (d->currentLV->selectedItems().isEmpty()) { 0351 return; 0352 } 0353 QTreeWidgetItem *item = d->currentLV->selectedItems().first(); 0354 const int itemIndex = d->currentLV->indexOfTopLevelItem(item); 0355 if (itemIndex + 1 >= d->currentLV->topLevelItemCount()) { 0356 return; 0357 } 0358 d->currentLV->takeTopLevelItem(itemIndex); 0359 d->currentLV->addTopLevelItem(item); 0360 d->currentLV->setCurrentItem(item); 0361 enableDisableButtons(item); 0362 Q_EMIT changed(); 0363 } 0364 0365 void Kleo::DNAttributeOrderConfigWidget::slotLeftButtonClicked() 0366 { 0367 if (d->currentLV->selectedItems().isEmpty()) { 0368 return; 0369 } 0370 QTreeWidgetItem *right = d->currentLV->selectedItems().first(); 0371 QTreeWidgetItem *next = d->currentLV->itemBelow(right); 0372 if (!next) { 0373 next = d->currentLV->itemAbove(right); 0374 } 0375 d->currentLV->takeTopLevelItem(d->currentLV->indexOfTopLevelItem(right)); 0376 d->availableLV->addTopLevelItem(right); 0377 d->availableLV->sortItems(0, Qt::AscendingOrder); 0378 d->availableLV->setCurrentItem(right); 0379 if (next) { 0380 d->currentLV->setCurrentItem(next); 0381 } 0382 enableDisableButtons(next); 0383 Q_EMIT changed(); 0384 } 0385 0386 void Kleo::DNAttributeOrderConfigWidget::slotRightButtonClicked() 0387 { 0388 if (d->availableLV->selectedItems().isEmpty()) { 0389 return; 0390 } 0391 QTreeWidgetItem *left = d->availableLV->selectedItems().first(); 0392 QTreeWidgetItem *next = d->availableLV->itemBelow(left); 0393 if (!next) { 0394 next = d->availableLV->itemAbove(left); 0395 } 0396 d->availableLV->takeTopLevelItem(d->availableLV->indexOfTopLevelItem(left)); 0397 int newRightIndex = d->currentLV->topLevelItemCount(); 0398 if (!d->currentLV->selectedItems().isEmpty()) { 0399 QTreeWidgetItem *right = d->currentLV->selectedItems().first(); 0400 newRightIndex = d->currentLV->indexOfTopLevelItem(right); 0401 } 0402 d->currentLV->insertTopLevelItem(newRightIndex, left); 0403 d->currentLV->setCurrentItem(left); 0404 enableDisableButtons(left); 0405 d->navTB[DNAttributeOrderConfigWidgetPrivate::Right]->setEnabled(next); 0406 if (next) { 0407 d->availableLV->setCurrentItem(next); 0408 } 0409 Q_EMIT changed(); 0410 } 0411 0412 void Kleo::DNAttributeOrderConfigWidget::virtual_hook(int, void *) 0413 { 0414 } 0415 0416 #include "dnattributeorderconfigwidget.moc" 0417 0418 #include "moc_dnattributeorderconfigwidget.cpp"