File indexing completed on 2024-04-28 03:58:59

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2002 Anders Lund <anders.lund@lund.tdcadsl.dk>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "kactionselector.h"
0009 
0010 #include <QApplication>
0011 #include <QHBoxLayout>
0012 #include <QKeyEvent>
0013 #include <QLabel>
0014 #include <QListWidget>
0015 #include <QToolButton>
0016 #include <QVBoxLayout>
0017 
0018 class KActionSelectorPrivate
0019 {
0020 public:
0021     KActionSelectorPrivate(KActionSelector *qq)
0022         : q(qq)
0023     {
0024     }
0025 
0026     KActionSelector *const q = nullptr;
0027     QListWidget *availableListWidget = nullptr;
0028     QListWidget *selectedListWidget = nullptr;
0029     QToolButton *btnAdd = nullptr;
0030     QToolButton *btnRemove = nullptr;
0031     QToolButton *btnUp = nullptr;
0032     QToolButton *btnDown = nullptr;
0033     QLabel *lAvailable = nullptr;
0034     QLabel *lSelected = nullptr;
0035     bool moveOnDoubleClick : 1;
0036     bool keyboardEnabled : 1;
0037     bool showUpDownButtons : 1;
0038     QString addIcon, removeIcon, upIcon, downIcon;
0039     KActionSelector::InsertionPolicy availableInsertionPolicy, selectedInsertionPolicy;
0040 
0041     /**
0042       Move item @p item to the other listbox
0043      */
0044     void moveItem(QListWidgetItem *item);
0045 
0046     /**
0047       loads the icons for the move buttons.
0048      */
0049     void loadIcons();
0050 
0051     /**
0052       @return the index to insert an item into listbox @p lb,
0053      given InsertionPolicy @p policy.
0054 
0055      Note that if policy is Sorted, this will return -1.
0056      Sort the listbox after inserting the item in that case.
0057      */
0058     int insertionIndex(QListWidget *lb, KActionSelector::InsertionPolicy policy);
0059 
0060     /**
0061      @return the index of the first selected item in listbox @p lb.
0062      If no item is selected, it will return -1.
0063      */
0064     int selectedRowIndex(QListWidget *lb);
0065 
0066     void buttonAddClicked();
0067     void buttonRemoveClicked();
0068     void buttonUpClicked();
0069     void buttonDownClicked();
0070     void itemDoubleClicked(QListWidgetItem *item);
0071     void slotCurrentChanged(QListWidgetItem *)
0072     {
0073         q->setButtonsEnabled();
0074     }
0075 };
0076 
0077 // BEGIN Constructor/destructor
0078 
0079 KActionSelector::KActionSelector(QWidget *parent)
0080     : QWidget(parent)
0081     , d(new KActionSelectorPrivate(this))
0082 {
0083     d->moveOnDoubleClick = true;
0084     d->keyboardEnabled = true;
0085     d->addIcon = QLatin1String(QApplication::isRightToLeft() ? "go-previous" : "go-next");
0086     d->removeIcon = QLatin1String(QApplication::isRightToLeft() ? "go-next" : "go-previous");
0087     d->upIcon = QStringLiteral("go-up");
0088     d->downIcon = QStringLiteral("go-down");
0089     d->availableInsertionPolicy = Sorted;
0090     d->selectedInsertionPolicy = BelowCurrent;
0091     d->showUpDownButtons = true;
0092 
0093     QHBoxLayout *lo = new QHBoxLayout(this);
0094     lo->setContentsMargins(0, 0, 0, 0);
0095 
0096     QVBoxLayout *loAv = new QVBoxLayout();
0097     lo->addLayout(loAv);
0098     d->lAvailable = new QLabel(tr("&Available:", "@label:listbox"), this);
0099     loAv->addWidget(d->lAvailable);
0100     d->availableListWidget = new QListWidget(this);
0101     loAv->addWidget(d->availableListWidget);
0102     d->lAvailable->setBuddy(d->availableListWidget);
0103 
0104     QVBoxLayout *loHBtns = new QVBoxLayout();
0105     lo->addLayout(loHBtns);
0106     loHBtns->addStretch(1);
0107     d->btnAdd = new QToolButton(this);
0108     loHBtns->addWidget(d->btnAdd);
0109     d->btnRemove = new QToolButton(this);
0110     loHBtns->addWidget(d->btnRemove);
0111     loHBtns->addStretch(1);
0112 
0113     QVBoxLayout *loS = new QVBoxLayout();
0114     lo->addLayout(loS);
0115     d->lSelected = new QLabel(tr("&Selected:", "@label:listbox"), this);
0116     loS->addWidget(d->lSelected);
0117     d->selectedListWidget = new QListWidget(this);
0118     loS->addWidget(d->selectedListWidget);
0119     d->lSelected->setBuddy(d->selectedListWidget);
0120 
0121     QVBoxLayout *loVBtns = new QVBoxLayout();
0122     lo->addLayout(loVBtns);
0123     loVBtns->addStretch(1);
0124     d->btnUp = new QToolButton(this);
0125     d->btnUp->setAutoRepeat(true);
0126     loVBtns->addWidget(d->btnUp);
0127     d->btnDown = new QToolButton(this);
0128     d->btnDown->setAutoRepeat(true);
0129     loVBtns->addWidget(d->btnDown);
0130     loVBtns->addStretch(1);
0131 
0132     d->loadIcons();
0133 
0134     connect(d->btnAdd, &QToolButton::clicked, this, [this]() {
0135         d->buttonAddClicked();
0136     });
0137     connect(d->btnRemove, &QToolButton::clicked, this, [this]() {
0138         d->buttonRemoveClicked();
0139     });
0140     connect(d->btnUp, &QToolButton::clicked, this, [this]() {
0141         d->buttonUpClicked();
0142     });
0143     connect(d->btnDown, &QToolButton::clicked, this, [this]() {
0144         d->buttonDownClicked();
0145     });
0146     connect(d->availableListWidget, &QListWidget::itemDoubleClicked, this, [this] (QListWidgetItem *item) { d->itemDoubleClicked(item); });
0147     connect(d->selectedListWidget, &QListWidget::itemDoubleClicked, this, [this] (QListWidgetItem *item) { d->itemDoubleClicked(item); });
0148     connect(d->availableListWidget, &QListWidget::itemSelectionChanged, this, &KActionSelector::setButtonsEnabled);
0149     connect(d->selectedListWidget, &QListWidget::itemSelectionChanged, this, &KActionSelector::setButtonsEnabled);
0150 
0151     d->availableListWidget->installEventFilter(this);
0152     d->selectedListWidget->installEventFilter(this);
0153     setButtonsEnabled();
0154 }
0155 
0156 KActionSelector::~KActionSelector() = default;
0157 
0158 // END Constructor/destroctor
0159 
0160 // BEGIN Public Methods
0161 
0162 QListWidget *KActionSelector::availableListWidget() const
0163 {
0164     return d->availableListWidget;
0165 }
0166 
0167 QListWidget *KActionSelector::selectedListWidget() const
0168 {
0169     return d->selectedListWidget;
0170 }
0171 
0172 void KActionSelector::setButtonIcon(const QString &icon, MoveButton button)
0173 {
0174     switch (button) {
0175     case ButtonAdd:
0176         d->addIcon = icon;
0177         d->btnAdd->setIcon(QIcon::fromTheme(icon));
0178         break;
0179     case ButtonRemove:
0180         d->removeIcon = icon;
0181         d->btnRemove->setIcon(QIcon::fromTheme(icon));
0182         break;
0183     case ButtonUp:
0184         d->upIcon = icon;
0185         d->btnUp->setIcon(QIcon::fromTheme(icon));
0186         break;
0187     case ButtonDown:
0188         d->downIcon = icon;
0189         d->btnDown->setIcon(QIcon::fromTheme(icon));
0190         break;
0191     default:
0192         //     qCDebug(KWidgetsAddonsLog)<<"KActionSelector::setButtonIcon: DAINBREAD!";
0193         break;
0194     }
0195 }
0196 
0197 void KActionSelector::setButtonIconSet(const QIcon &iconset, MoveButton button)
0198 {
0199     switch (button) {
0200     case ButtonAdd:
0201         d->btnAdd->setIcon(iconset);
0202         break;
0203     case ButtonRemove:
0204         d->btnRemove->setIcon(iconset);
0205         break;
0206     case ButtonUp:
0207         d->btnUp->setIcon(iconset);
0208         break;
0209     case ButtonDown:
0210         d->btnDown->setIcon(iconset);
0211         break;
0212     default:
0213         //     qCDebug(KWidgetsAddonsLog)<<"KActionSelector::setButtonIconSet: DAINBREAD!";
0214         break;
0215     }
0216 }
0217 
0218 void KActionSelector::setButtonTooltip(const QString &tip, MoveButton button)
0219 {
0220     switch (button) {
0221     case ButtonAdd:
0222         d->btnAdd->setText(tip);
0223         d->btnAdd->setToolTip(tip);
0224         break;
0225     case ButtonRemove:
0226         d->btnRemove->setText(tip);
0227         d->btnRemove->setToolTip(tip);
0228         break;
0229     case ButtonUp:
0230         d->btnUp->setText(tip);
0231         d->btnUp->setToolTip(tip);
0232         break;
0233     case ButtonDown:
0234         d->btnDown->setText(tip);
0235         d->btnDown->setToolTip(tip);
0236         break;
0237     default:
0238         //     qCDebug(KWidgetsAddonsLog)<<"KActionSelector::setButtonToolTip: DAINBREAD!";
0239         break;
0240     }
0241 }
0242 
0243 void KActionSelector::setButtonWhatsThis(const QString &text, MoveButton button)
0244 {
0245     switch (button) {
0246     case ButtonAdd:
0247         d->btnAdd->setWhatsThis(text);
0248         break;
0249     case ButtonRemove:
0250         d->btnRemove->setWhatsThis(text);
0251         break;
0252     case ButtonUp:
0253         d->btnUp->setWhatsThis(text);
0254         break;
0255     case ButtonDown:
0256         d->btnDown->setWhatsThis(text);
0257         break;
0258     default:
0259         //     qCDebug(KWidgetsAddonsLog)<<"KActionSelector::setButtonWhatsThis: DAINBREAD!";
0260         break;
0261     }
0262 }
0263 
0264 // END Public Methods
0265 
0266 // BEGIN Properties
0267 
0268 bool KActionSelector::moveOnDoubleClick() const
0269 {
0270     return d->moveOnDoubleClick;
0271 }
0272 
0273 void KActionSelector::setMoveOnDoubleClick(bool b)
0274 {
0275     d->moveOnDoubleClick = b;
0276 }
0277 
0278 bool KActionSelector::keyboardEnabled() const
0279 {
0280     return d->keyboardEnabled;
0281 }
0282 
0283 void KActionSelector::setKeyboardEnabled(bool b)
0284 {
0285     d->keyboardEnabled = b;
0286 }
0287 
0288 QString KActionSelector::availableLabel() const
0289 {
0290     return d->lAvailable->text();
0291 }
0292 
0293 void KActionSelector::setAvailableLabel(const QString &text)
0294 {
0295     d->lAvailable->setText(text);
0296 }
0297 
0298 QString KActionSelector::selectedLabel() const
0299 {
0300     return d->lSelected->text();
0301 }
0302 
0303 void KActionSelector::setSelectedLabel(const QString &text)
0304 {
0305     d->lSelected->setText(text);
0306 }
0307 
0308 KActionSelector::InsertionPolicy KActionSelector::availableInsertionPolicy() const
0309 {
0310     return d->availableInsertionPolicy;
0311 }
0312 
0313 void KActionSelector::setAvailableInsertionPolicy(InsertionPolicy p)
0314 {
0315     d->availableInsertionPolicy = p;
0316 }
0317 
0318 KActionSelector::InsertionPolicy KActionSelector::selectedInsertionPolicy() const
0319 {
0320     return d->selectedInsertionPolicy;
0321 }
0322 
0323 void KActionSelector::setSelectedInsertionPolicy(InsertionPolicy p)
0324 {
0325     d->selectedInsertionPolicy = p;
0326 }
0327 
0328 bool KActionSelector::showUpDownButtons() const
0329 {
0330     return d->showUpDownButtons;
0331 }
0332 
0333 void KActionSelector::setShowUpDownButtons(bool show)
0334 {
0335     d->showUpDownButtons = show;
0336     if (show) {
0337         d->btnUp->show();
0338         d->btnDown->show();
0339     } else {
0340         d->btnUp->hide();
0341         d->btnDown->hide();
0342     }
0343 }
0344 
0345 // END Properties
0346 
0347 // BEGIN Public Slots
0348 
0349 void KActionSelector::setButtonsEnabled()
0350 {
0351     d->btnAdd->setEnabled(d->selectedRowIndex(d->availableListWidget) > -1);
0352     d->btnRemove->setEnabled(d->selectedRowIndex(d->selectedListWidget) > -1);
0353     d->btnUp->setEnabled(d->selectedRowIndex(d->selectedListWidget) > 0);
0354     d->btnDown->setEnabled(d->selectedRowIndex(d->selectedListWidget) > -1 //
0355                            && d->selectedRowIndex(d->selectedListWidget) < d->selectedListWidget->count() - 1);
0356 }
0357 
0358 // END Public Slots
0359 
0360 // BEGIN Protected
0361 void KActionSelector::keyPressEvent(QKeyEvent *e)
0362 {
0363     if (!d->keyboardEnabled) {
0364         return;
0365     }
0366     if ((e->modifiers() & Qt::ControlModifier)) {
0367         switch (e->key()) {
0368         case Qt::Key_Right:
0369             d->buttonAddClicked();
0370             break;
0371         case Qt::Key_Left:
0372             d->buttonRemoveClicked();
0373             break;
0374         case Qt::Key_Up:
0375             d->buttonUpClicked();
0376             break;
0377         case Qt::Key_Down:
0378             d->buttonDownClicked();
0379             break;
0380         default:
0381             e->ignore();
0382             return;
0383         }
0384     }
0385 }
0386 
0387 bool KActionSelector::eventFilter(QObject *o, QEvent *e)
0388 {
0389     if (d->keyboardEnabled && e->type() == QEvent::KeyPress) {
0390         if ((((QKeyEvent *)e)->modifiers() & Qt::ControlModifier)) {
0391             switch (((QKeyEvent *)e)->key()) {
0392             case Qt::Key_Right:
0393                 d->buttonAddClicked();
0394                 break;
0395             case Qt::Key_Left:
0396                 d->buttonRemoveClicked();
0397                 break;
0398             case Qt::Key_Up:
0399                 d->buttonUpClicked();
0400                 break;
0401             case Qt::Key_Down:
0402                 d->buttonDownClicked();
0403                 break;
0404             default:
0405                 return QWidget::eventFilter(o, e);
0406             }
0407             return true;
0408         } else if (QListWidget *lb = qobject_cast<QListWidget *>(o)) {
0409             switch (((QKeyEvent *)e)->key()) {
0410             case Qt::Key_Return:
0411             case Qt::Key_Enter:
0412                 int index = lb->currentRow();
0413                 if (index < 0) {
0414                     break;
0415                 }
0416                 d->moveItem(lb->item(index));
0417                 return true;
0418             }
0419         }
0420     }
0421     return QWidget::eventFilter(o, e);
0422 }
0423 
0424 // END Protected
0425 
0426 // BEGIN Private Slots
0427 
0428 void KActionSelectorPrivate::buttonAddClicked()
0429 {
0430     // move all selected items from available to selected listbox
0431     const QList<QListWidgetItem *> list = availableListWidget->selectedItems();
0432     for (QListWidgetItem *item : list) {
0433         availableListWidget->takeItem(availableListWidget->row(item));
0434         selectedListWidget->insertItem(insertionIndex(selectedListWidget, selectedInsertionPolicy), item);
0435         selectedListWidget->setCurrentItem(item);
0436         Q_EMIT q->added(item);
0437     }
0438     if (selectedInsertionPolicy == KActionSelector::Sorted) {
0439         selectedListWidget->sortItems();
0440     }
0441     selectedListWidget->setFocus();
0442 }
0443 
0444 void KActionSelectorPrivate::buttonRemoveClicked()
0445 {
0446     // move all selected items from selected to available listbox
0447     const QList<QListWidgetItem *> list = selectedListWidget->selectedItems();
0448     for (QListWidgetItem *item : list) {
0449         selectedListWidget->takeItem(selectedListWidget->row(item));
0450         availableListWidget->insertItem(insertionIndex(availableListWidget, availableInsertionPolicy), item);
0451         availableListWidget->setCurrentItem(item);
0452         Q_EMIT q->removed(item);
0453     }
0454     if (availableInsertionPolicy == KActionSelector::Sorted) {
0455         availableListWidget->sortItems();
0456     }
0457     availableListWidget->setFocus();
0458 }
0459 
0460 void KActionSelectorPrivate::buttonUpClicked()
0461 {
0462     int c = selectedRowIndex(selectedListWidget);
0463     if (c < 1) {
0464         return;
0465     }
0466     QListWidgetItem *item = selectedListWidget->item(c);
0467     selectedListWidget->takeItem(c);
0468     selectedListWidget->insertItem(c - 1, item);
0469     selectedListWidget->setCurrentItem(item);
0470     Q_EMIT q->movedUp(item);
0471 }
0472 
0473 void KActionSelectorPrivate::buttonDownClicked()
0474 {
0475     int c = selectedRowIndex(selectedListWidget);
0476     if (c < 0 || c == selectedListWidget->count() - 1) {
0477         return;
0478     }
0479     QListWidgetItem *item = selectedListWidget->item(c);
0480     selectedListWidget->takeItem(c);
0481     selectedListWidget->insertItem(c + 1, item);
0482     selectedListWidget->setCurrentItem(item);
0483     Q_EMIT q->movedDown(item);
0484 }
0485 
0486 void KActionSelectorPrivate::itemDoubleClicked(QListWidgetItem *item)
0487 {
0488     if (moveOnDoubleClick) {
0489         moveItem(item);
0490     }
0491 }
0492 
0493 // END Private Slots
0494 
0495 // BEGIN Private Methods
0496 
0497 void KActionSelectorPrivate::loadIcons()
0498 {
0499     btnAdd->setIcon(QIcon::fromTheme(addIcon));
0500     btnRemove->setIcon(QIcon::fromTheme(removeIcon));
0501     btnUp->setIcon(QIcon::fromTheme(upIcon));
0502     btnDown->setIcon(QIcon::fromTheme(downIcon));
0503 }
0504 
0505 void KActionSelectorPrivate::moveItem(QListWidgetItem *item)
0506 {
0507     QListWidget *lbFrom = item->listWidget();
0508     QListWidget *lbTo;
0509     if (lbFrom == availableListWidget) {
0510         lbTo = selectedListWidget;
0511     } else if (lbFrom == selectedListWidget) {
0512         lbTo = availableListWidget;
0513     } else { //?! somewhat unlikely...
0514         return;
0515     }
0516 
0517     KActionSelector::InsertionPolicy p = (lbTo == availableListWidget) ? availableInsertionPolicy : selectedInsertionPolicy;
0518 
0519     lbFrom->takeItem(lbFrom->row(item));
0520     lbTo->insertItem(insertionIndex(lbTo, p), item);
0521     lbTo->setFocus();
0522     lbTo->setCurrentItem(item);
0523 
0524     if (p == KActionSelector::Sorted) {
0525         lbTo->sortItems();
0526     }
0527     if (lbTo == selectedListWidget) {
0528         Q_EMIT q->added(item);
0529     } else {
0530         Q_EMIT q->removed(item);
0531     }
0532 }
0533 
0534 int KActionSelectorPrivate::insertionIndex(QListWidget *lb, KActionSelector::InsertionPolicy policy)
0535 {
0536     int index;
0537     switch (policy) {
0538     case KActionSelector::BelowCurrent:
0539         index = lb->currentRow();
0540         if (index > -1) {
0541             index += 1;
0542         }
0543         break;
0544     case KActionSelector::AtTop:
0545         index = 0;
0546         break;
0547     case KActionSelector::AtBottom:
0548         index = lb->count();
0549         break;
0550     default:
0551         index = -1;
0552     }
0553     return index;
0554 }
0555 
0556 int KActionSelectorPrivate::selectedRowIndex(QListWidget *lb)
0557 {
0558     QList<QListWidgetItem *> list = lb->selectedItems();
0559     if (list.isEmpty()) {
0560         return -1;
0561     }
0562     return lb->row(list.at(0));
0563 }
0564 
0565 // END Private Methods
0566 #include "moc_kactionselector.cpp"