File indexing completed on 2025-02-16 13:11:36
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"