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"