File indexing completed on 2024-04-28 15:32:13

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1999 Reginald Stadlbauer <reggie@kde.org>
0004     SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
0005     SPDX-FileCopyrightText: 2000 Nicolas Hadacek <haadcek@kde.org>
0006     SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
0007     SPDX-FileCopyrightText: 2000 Michael Koch <koch@kde.org>
0008     SPDX-FileCopyrightText: 2001 Holger Freyther <freyther@kde.org>
0009     SPDX-FileCopyrightText: 2002 Ellis Whitehead <ellis@kde.org>
0010     SPDX-FileCopyrightText: 2002 Joseph Wenninger <jowenn@kde.org>
0011     SPDX-FileCopyrightText: 2003 Andras Mantia <amantia@kde.org>
0012     SPDX-FileCopyrightText: 2005-2006 Hamish Rodda <rodda@kde.org>
0013     SPDX-FileCopyrightText: 2006 Albert Astals Cid <aacid@kde.org>
0014     SPDX-FileCopyrightText: 2006 Clarence Dang <dang@kde.org>
0015     SPDX-FileCopyrightText: 2006 Michel Hermier <michel.hermier@gmail.com>
0016     SPDX-FileCopyrightText: 2007 Nick Shaforostoff <shafff@ukr.net>
0017 
0018     SPDX-License-Identifier: LGPL-2.0-only
0019 */
0020 
0021 #include "kselectaction.h"
0022 #include "kselectaction_p.h"
0023 
0024 #include "loggingcategory.h"
0025 
0026 #include <QActionEvent>
0027 #include <QEvent>
0028 #include <QMenu>
0029 #include <QStandardItem>
0030 #include <QToolBar>
0031 
0032 // QAction::setText("Hi") and then KPopupAccelManager exec'ing, causes
0033 // QAction::text() to return "&Hi" :(  Comboboxes don't have accels and
0034 // display ampersands literally.
0035 static QString DropAmpersands(const QString &text)
0036 {
0037     QString label = text;
0038 
0039     int p = label.indexOf(QLatin1Char('&'));
0040     while (p >= 0 && p < label.length() - 1) {
0041         if (label[p + 1].isLetterOrNumber() // Valid accelerator.
0042             || label[p + 1] == QLatin1Char('&')) { // Escaped accelerator marker.
0043             label.remove(p, 1);
0044         }
0045 
0046         p = label.indexOf(QLatin1Char('&'), p + 1);
0047     }
0048 
0049     return label;
0050 }
0051 
0052 KSelectAction::KSelectAction(QObject *parent)
0053     : KSelectAction(*new KSelectActionPrivate(this), parent)
0054 {
0055 }
0056 
0057 KSelectAction::KSelectAction(const QString &text, QObject *parent)
0058     : KSelectAction(*new KSelectActionPrivate(this), parent)
0059 {
0060     setText(text);
0061 }
0062 
0063 KSelectAction::KSelectAction(const QIcon &icon, const QString &text, QObject *parent)
0064     : KSelectAction(*new KSelectActionPrivate(this), parent)
0065 {
0066     setIcon(icon);
0067     setText(text);
0068 }
0069 
0070 KSelectAction::KSelectAction(KSelectActionPrivate &dd, QObject *parent)
0071     : QWidgetAction(parent)
0072     , d_ptr(&dd)
0073 {
0074     Q_D(KSelectAction);
0075     d->init();
0076 }
0077 
0078 KSelectAction::~KSelectAction()
0079 {
0080     menu()->deleteLater();
0081 }
0082 
0083 void KSelectActionPrivate::init()
0084 {
0085     QObject::connect(q_ptr->selectableActionGroup(), &QActionGroup::triggered, q_ptr, &KSelectAction::actionTriggered);
0086     QObject::connect(q_ptr, &QAction::toggled, q_ptr, &KSelectAction::slotToggled);
0087     q_ptr->setMenu(new QMenu());
0088     q_ptr->setEnabled(false);
0089 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 78)
0090     // forward deprecated signals to undeprecated, to be backward-compatible to unported subclasses
0091     QObject::connect(q_ptr, qOverload<int>(&KSelectAction::triggered), q_ptr, &KSelectAction::indexTriggered);
0092     QObject::connect(q_ptr, qOverload<const QString &>(&KSelectAction::triggered), q_ptr, &KSelectAction::textTriggered);
0093 #endif
0094 }
0095 
0096 QActionGroup *KSelectAction::selectableActionGroup() const
0097 {
0098     Q_D(const KSelectAction);
0099     return d->m_actionGroup;
0100 }
0101 
0102 QList<QAction *> KSelectAction::actions() const
0103 {
0104     return selectableActionGroup()->actions();
0105 }
0106 
0107 QAction *KSelectAction::currentAction() const
0108 {
0109     return selectableActionGroup()->checkedAction();
0110 }
0111 
0112 int KSelectAction::currentItem() const
0113 {
0114     return selectableActionGroup()->actions().indexOf(currentAction());
0115 }
0116 
0117 QString KSelectAction::currentText() const
0118 {
0119     if (QAction *a = currentAction()) {
0120         return ::DropAmpersands(a->text());
0121     }
0122 
0123     return QString();
0124 }
0125 
0126 bool KSelectAction::setCurrentAction(QAction *action)
0127 {
0128     // qCDebug(KWidgetsAddonsLog) << "KSelectAction::setCurrentAction(" << action << ")";
0129     if (action) {
0130         if (actions().contains(action)) {
0131             if (action->isVisible() && action->isEnabled() && action->isCheckable()) {
0132                 action->setChecked(true);
0133                 if (isCheckable()) {
0134                     setChecked(true);
0135                 }
0136                 return true;
0137             } else {
0138                 qCWarning(KWidgetsAddonsLog) << "Action does not have the correct properties to be current:" << action->text();
0139             }
0140         } else {
0141             qCWarning(KWidgetsAddonsLog) << "Action does not belong to group:" << action->text();
0142         }
0143         return false;
0144     }
0145 
0146     if (currentAction()) {
0147         currentAction()->setChecked(false);
0148     }
0149 
0150     return false;
0151 }
0152 
0153 bool KSelectAction::setCurrentItem(int index)
0154 {
0155     // qCDebug(KWidgetsAddonsLog) << "KSelectAction::setCurrentIndex(" << index << ")";
0156     return setCurrentAction(action(index));
0157 }
0158 
0159 QAction *KSelectAction::action(int index) const
0160 {
0161     if (index >= 0 && index < selectableActionGroup()->actions().count()) {
0162         return selectableActionGroup()->actions().at(index);
0163     }
0164 
0165     return nullptr;
0166 }
0167 
0168 QAction *KSelectAction::action(const QString &text, Qt::CaseSensitivity cs) const
0169 {
0170     QString compare;
0171     if (cs == Qt::CaseSensitive) {
0172         compare = text;
0173     } else {
0174         compare = text.toLower();
0175     }
0176 
0177     const auto selectableActions = selectableActionGroup()->actions();
0178     for (QAction *action : selectableActions) {
0179         const QString text = ::DropAmpersands(action->text());
0180         if (cs == Qt::CaseSensitive) {
0181             if (text == compare) {
0182                 return action;
0183             }
0184 
0185         } else if (cs == Qt::CaseInsensitive) {
0186             if (text.toLower() == compare) {
0187                 return action;
0188             }
0189         }
0190     }
0191 
0192     return nullptr;
0193 }
0194 
0195 bool KSelectAction::setCurrentAction(const QString &text, Qt::CaseSensitivity cs)
0196 {
0197     // qCDebug(KWidgetsAddonsLog) << "KSelectAction::setCurrentAction(" << text << ",cs=" << cs << ")";
0198     return setCurrentAction(action(text, cs));
0199 }
0200 
0201 void KSelectAction::setComboWidth(int width)
0202 {
0203     Q_D(KSelectAction);
0204     if (width < 0) {
0205         return;
0206     }
0207 
0208     d->m_comboWidth = width;
0209 
0210     for (QComboBox *box : std::as_const(d->m_comboBoxes)) {
0211         box->setMaximumWidth(d->m_comboWidth);
0212     }
0213 
0214     Q_EMIT changed();
0215 }
0216 
0217 void KSelectAction::setMaxComboViewCount(int n)
0218 {
0219     Q_D(KSelectAction);
0220     d->m_maxComboViewCount = n;
0221 
0222     for (QComboBox *box : std::as_const(d->m_comboBoxes)) {
0223         if (d->m_maxComboViewCount != -1) {
0224             box->setMaxVisibleItems(d->m_maxComboViewCount);
0225         } else
0226         // hardcoded qt default
0227         {
0228             box->setMaxVisibleItems(10);
0229         }
0230     }
0231 
0232     Q_EMIT changed();
0233 }
0234 
0235 void KSelectAction::addAction(QAction *action)
0236 {
0237     insertAction(nullptr, action);
0238 }
0239 
0240 QAction *KSelectAction::addAction(const QString &text)
0241 {
0242     Q_D(KSelectAction);
0243     QAction *newAction = new QAction(parent());
0244     newAction->setText(text);
0245     newAction->setCheckable(true);
0246     newAction->setProperty("isShortcutConfigurable", false);
0247 
0248     if (!d->m_menuAccelsEnabled) {
0249         newAction->setText(text);
0250         newAction->setShortcut(QKeySequence());
0251     }
0252 
0253     addAction(newAction);
0254     return newAction;
0255 }
0256 
0257 QAction *KSelectAction::addAction(const QIcon &icon, const QString &text)
0258 {
0259     QAction *newAction = addAction(text);
0260     newAction->setIcon(icon);
0261     return newAction;
0262 }
0263 
0264 QAction *KSelectAction::removeAction(QAction *action)
0265 {
0266     Q_D(KSelectAction);
0267     // qCDebug(KWidgetsAddonsLog) << "KSelectAction::removeAction(" << action << ")";
0268     // int index = selectableActionGroup()->actions().indexOf(action);
0269     // qCDebug(KWidgetsAddonsLog) << "\tindex=" << index;
0270 
0271     // Removes the action from the group and sets its parent to null.
0272     d->m_actionGroup->removeAction(action);
0273 
0274     // Disable when no action is in the group
0275     bool hasActions = selectableActionGroup()->actions().isEmpty();
0276     setEnabled(!hasActions);
0277 
0278     for (QToolButton *button : std::as_const(d->m_buttons)) {
0279         button->setEnabled(!hasActions);
0280         button->removeAction(action);
0281     }
0282 
0283     for (QComboBox *comboBox : std::as_const(d->m_comboBoxes)) {
0284         comboBox->setEnabled(!hasActions);
0285         comboBox->removeAction(action);
0286     }
0287 
0288     menu()->removeAction(action);
0289 
0290     return action;
0291 }
0292 
0293 void KSelectAction::insertAction(QAction *before, QAction *action)
0294 {
0295     Q_D(KSelectAction);
0296     action->setActionGroup(selectableActionGroup());
0297 
0298     // Re-Enable when an action is added
0299     setEnabled(true);
0300 
0301     // Keep in sync with createToolBarWidget()
0302     for (QToolButton *button : std::as_const(d->m_buttons)) {
0303         button->setEnabled(true);
0304         button->insertAction(before, action);
0305     }
0306 
0307     for (QComboBox *comboBox : std::as_const(d->m_comboBoxes)) {
0308         comboBox->setEnabled(true);
0309         comboBox->insertAction(before, action);
0310     }
0311 
0312     menu()->insertAction(before, action);
0313 }
0314 
0315 void KSelectAction::actionTriggered(QAction *action)
0316 {
0317     // cache values so we don't need access to members in the action
0318     // after we've done an emit()
0319     const QString text = ::DropAmpersands(action->text());
0320     const int index = selectableActionGroup()->actions().indexOf(action);
0321     // qCDebug(KWidgetsAddonsLog) << "KSelectAction::actionTriggered(" << action << ") text=" << text
0322     //          << " index=" << index  << " emitting triggered()" << endl;
0323 
0324     if (isCheckable()) { // if this is subsidiary of other KSelectAction-derived class
0325         trigger(); // then imitate usual QAction behaviour so that other submenus (and their items) become unchecked
0326     }
0327 
0328     Q_EMIT triggered(action);
0329 #if KWIDGETSADDONS_BUILD_DEPRECATED_SINCE(5, 78)
0330     // will also indirectly emit indexTriggered & textTriggered, due to signal connection in init()
0331     Q_EMIT triggered(index);
0332     Q_EMIT triggered(text);
0333 #else
0334     Q_EMIT indexTriggered(index);
0335     Q_EMIT textTriggered(text);
0336 #endif
0337 }
0338 
0339 QStringList KSelectAction::items() const
0340 {
0341     Q_D(const KSelectAction);
0342     QStringList ret;
0343 
0344     const auto actions = d->m_actionGroup->actions();
0345     ret.reserve(actions.size());
0346     for (QAction *action : actions) {
0347         ret << ::DropAmpersands(action->text());
0348     }
0349 
0350     return ret;
0351 }
0352 
0353 void KSelectAction::changeItem(int index, const QString &text)
0354 {
0355     Q_D(KSelectAction);
0356     if (index < 0 || index >= actions().count()) {
0357         qCWarning(KWidgetsAddonsLog) << "KSelectAction::changeItem Index out of scope";
0358         return;
0359     }
0360 
0361     actions()[index]->setText(d->makeMenuText(text));
0362 }
0363 
0364 void KSelectAction::setItems(const QStringList &lst)
0365 {
0366     Q_D(KSelectAction);
0367     // qCDebug(KWidgetsAddonsLog) << "KSelectAction::setItems(" << lst << ")";
0368 
0369     clear();
0370 
0371     for (const QString &string : lst) {
0372         if (!string.isEmpty()) {
0373             addAction(string);
0374         } else {
0375             QAction *action = new QAction(this);
0376             action->setSeparator(true);
0377             addAction(action);
0378         }
0379     }
0380 
0381     // Disable if empty and not editable
0382     setEnabled(lst.count() > 0 || d->m_edit);
0383 }
0384 
0385 int KSelectAction::comboWidth() const
0386 {
0387     Q_D(const KSelectAction);
0388     return d->m_comboWidth;
0389 }
0390 
0391 void KSelectAction::clear()
0392 {
0393     Q_D(KSelectAction);
0394     // qCDebug(KWidgetsAddonsLog) << "KSelectAction::clear()";
0395 
0396     // we need to delete the actions later since we may get a call to clear()
0397     // from a method called due to a triggered(...) signal
0398     const QList<QAction *> actions = d->m_actionGroup->actions();
0399     for (int i = 0; i < actions.count(); ++i) {
0400         // deleteLater() only removes us from the actions() list (among
0401         // other things) on the next entry into the event loop.  Until then,
0402         // e.g. action() and setCurrentItem() will be working on items
0403         // that are supposed to have been deleted.  So detach the action to
0404         // prevent this from happening.
0405         removeAction(actions[i]);
0406 
0407         actions[i]->deleteLater();
0408     }
0409 }
0410 
0411 void KSelectAction::removeAllActions()
0412 {
0413     Q_D(KSelectAction);
0414     while (d->m_actionGroup->actions().count()) {
0415         removeAction(d->m_actionGroup->actions().first());
0416     }
0417 }
0418 
0419 void KSelectAction::setEditable(bool edit)
0420 {
0421     Q_D(KSelectAction);
0422     d->m_edit = edit;
0423 
0424     for (QComboBox *comboBox : std::as_const(d->m_comboBoxes)) {
0425         comboBox->setEditable(edit);
0426     }
0427 
0428     Q_EMIT changed();
0429 }
0430 
0431 bool KSelectAction::isEditable() const
0432 {
0433     Q_D(const KSelectAction);
0434     return d->m_edit;
0435 }
0436 
0437 void KSelectAction::slotToggled(bool checked)
0438 {
0439     // if (checked && selectableActionGroup()->checkedAction())
0440     if (!checked && currentAction()) { // other's submenu item has been selected
0441         currentAction()->setChecked(false);
0442     }
0443 }
0444 
0445 KSelectAction::ToolBarMode KSelectAction::toolBarMode() const
0446 {
0447     Q_D(const KSelectAction);
0448     return d->m_toolBarMode;
0449 }
0450 
0451 void KSelectAction::setToolBarMode(ToolBarMode mode)
0452 {
0453     Q_D(KSelectAction);
0454     d->m_toolBarMode = mode;
0455 }
0456 
0457 QToolButton::ToolButtonPopupMode KSelectAction::toolButtonPopupMode() const
0458 {
0459     Q_D(const KSelectAction);
0460     return d->m_toolButtonPopupMode;
0461 }
0462 
0463 void KSelectAction::setToolButtonPopupMode(QToolButton::ToolButtonPopupMode mode)
0464 {
0465     Q_D(KSelectAction);
0466     d->m_toolButtonPopupMode = mode;
0467 }
0468 
0469 void KSelectActionPrivate::comboBoxDeleted(QComboBox *combo)
0470 {
0471     m_comboBoxes.removeAll(combo);
0472 }
0473 
0474 void KSelectActionPrivate::comboBoxCurrentIndexChanged(int index)
0475 {
0476     Q_Q(KSelectAction);
0477     // qCDebug(KWidgetsAddonsLog) << "KSelectActionPrivate::comboBoxCurrentIndexChanged(" << index << ")";
0478 
0479     QComboBox *triggeringCombo = qobject_cast<QComboBox *>(q->sender());
0480 
0481     QAction *a = q->action(index);
0482     // qCDebug(KWidgetsAddonsLog) << "\ta=" << a;
0483     if (a) {
0484         // qCDebug(KWidgetsAddonsLog) << "\t\tsetting as current action";
0485         a->trigger();
0486 
0487     } else if (q->isEditable() && triggeringCombo && triggeringCombo->count() > 0 && index == triggeringCombo->count() - 1) {
0488         // User must have added a new item by typing and pressing enter.
0489         const QString newItemText = triggeringCombo->currentText();
0490         // qCDebug(KWidgetsAddonsLog) << "\t\tuser typed new item '" << newItemText << "'";
0491 
0492         // Only 1 combobox contains this and it's not a proper action.
0493         bool blocked = triggeringCombo->blockSignals(true);
0494         triggeringCombo->removeItem(index);
0495         triggeringCombo->blockSignals(blocked);
0496 
0497         QAction *newAction = q->addAction(newItemText);
0498 
0499         newAction->trigger();
0500     } else {
0501         if (q->selectableActionGroup()->checkedAction()) {
0502             q->selectableActionGroup()->checkedAction()->setChecked(false);
0503         }
0504     }
0505 }
0506 
0507 // TODO: DropAmpersands() certainly makes sure this doesn't work.  But I don't
0508 // think it did anyway esp. in the presence KCheckAccelerator - Clarence.
0509 void KSelectAction::setMenuAccelsEnabled(bool b)
0510 {
0511     Q_D(KSelectAction);
0512     d->m_menuAccelsEnabled = b;
0513 }
0514 
0515 bool KSelectAction::menuAccelsEnabled() const
0516 {
0517     Q_D(const KSelectAction);
0518     return d->m_menuAccelsEnabled;
0519 }
0520 
0521 QWidget *KSelectAction::createWidget(QWidget *parent)
0522 {
0523     Q_D(KSelectAction);
0524     QMenu *menu = qobject_cast<QMenu *>(parent);
0525     if (menu) { // If used in a menu want to return 0 and use only the text, not a widget
0526         return nullptr;
0527     }
0528     ToolBarMode mode = toolBarMode();
0529     QToolBar *toolBar = qobject_cast<QToolBar *>(parent);
0530     if (!toolBar && mode != ComboBoxMode) { // we can return a combobox just fine.
0531         return nullptr;
0532     }
0533     switch (mode) {
0534     case MenuMode: {
0535         QToolButton *button = new QToolButton(toolBar);
0536         button->setToolTip(toolTip());
0537         button->setWhatsThis(whatsThis());
0538         button->setStatusTip(statusTip());
0539         button->setAutoRaise(true);
0540         button->setFocusPolicy(Qt::NoFocus);
0541         button->setIconSize(toolBar->iconSize());
0542         button->setToolButtonStyle(toolBar->toolButtonStyle());
0543         QObject::connect(toolBar, &QToolBar::iconSizeChanged, button, &QAbstractButton::setIconSize);
0544         QObject::connect(toolBar, &QToolBar::toolButtonStyleChanged, button, &QToolButton::setToolButtonStyle);
0545         button->setDefaultAction(this);
0546         QObject::connect(button, &QToolButton::triggered, toolBar, &QToolBar::actionTriggered);
0547 
0548         button->setPopupMode(toolButtonPopupMode());
0549 
0550         button->addActions(selectableActionGroup()->actions());
0551 
0552         d->m_buttons.append(button);
0553         return button;
0554     }
0555 
0556     case ComboBoxMode: {
0557         QComboBox *comboBox = new QComboBox(parent);
0558         comboBox->installEventFilter(this);
0559 
0560         if (d->m_maxComboViewCount != -1) {
0561             comboBox->setMaxVisibleItems(d->m_maxComboViewCount);
0562         }
0563 
0564         if (d->m_comboWidth > 0) {
0565             comboBox->setMaximumWidth(d->m_comboWidth);
0566         }
0567 
0568         comboBox->setEditable(isEditable());
0569         comboBox->setToolTip(toolTip());
0570         comboBox->setWhatsThis(whatsThis());
0571         comboBox->setStatusTip(statusTip());
0572 
0573         const auto selectableActions = selectableActionGroup()->actions();
0574         for (QAction *action : selectableActions) {
0575             comboBox->addAction(action);
0576         }
0577 
0578         if (selectableActions.isEmpty()) {
0579             comboBox->setEnabled(false);
0580         }
0581 
0582         connect(comboBox, &QComboBox::destroyed, this, [d, comboBox]() {
0583             d->comboBoxDeleted(comboBox);
0584         });
0585 
0586         connect(comboBox, qOverload<int>(&QComboBox::currentIndexChanged), this, [d](int value) {
0587             d->comboBoxCurrentIndexChanged(value);
0588         });
0589 
0590         d->m_comboBoxes.append(comboBox);
0591 
0592         return comboBox;
0593     }
0594     }
0595 
0596     return nullptr;
0597 }
0598 
0599 void KSelectAction::deleteWidget(QWidget *widget)
0600 {
0601     Q_D(KSelectAction);
0602     if (QToolButton *toolButton = qobject_cast<QToolButton *>(widget)) {
0603         d->m_buttons.removeAll(toolButton);
0604     } else if (QComboBox *comboBox = qobject_cast<QComboBox *>(widget)) {
0605         d->m_comboBoxes.removeAll(comboBox);
0606     }
0607     QWidgetAction::deleteWidget(widget);
0608 }
0609 
0610 bool KSelectAction::event(QEvent *event)
0611 {
0612     Q_D(KSelectAction);
0613     if (event->type() == QEvent::ActionChanged) {
0614         for (QComboBox *comboBox : std::as_const(d->m_comboBoxes)) {
0615             comboBox->setToolTip(toolTip());
0616             comboBox->setWhatsThis(whatsThis());
0617             comboBox->setStatusTip(statusTip());
0618         }
0619         for (QToolButton *toolButton : std::as_const(d->m_buttons)) {
0620             toolButton->setToolTip(toolTip());
0621             toolButton->setWhatsThis(whatsThis());
0622             toolButton->setStatusTip(statusTip());
0623         }
0624     }
0625     return QWidgetAction::event(event);
0626 }
0627 
0628 // KSelectAction::eventFilter() is called before action->setChecked()
0629 // invokes the signal to update QActionGroup so KSelectAction::currentItem()
0630 // returns an old value.  There are 3 possibilities, where n actions will
0631 // report QAction::isChecked() where n is:
0632 //
0633 // 0: the checked action was unchecked
0634 // 1: the checked action did not change
0635 // 2: another action was checked but QActionGroup has not been invoked yet
0636 //    to uncheck the one that was checked before
0637 //
0638 // TODO: we might want to cache this since QEvent::ActionChanged is fired
0639 //       often.
0640 static int TrueCurrentItem(KSelectAction *sa)
0641 {
0642     QAction *curAction = sa->currentAction();
0643     // qCDebug(KWidgetsAddonsLog) << "\tTrueCurrentItem(" << sa << ") curAction=" << curAction;
0644 
0645     const auto actions = sa->actions();
0646     int i = 0;
0647     for (QAction *action : actions) {
0648         if (action->isChecked()) {
0649             // qCDebug(KWidgetsAddonsLog) << "\t\taction " << action << " (text=" << action->text () << ") isChecked";
0650 
0651             // 2 actions checked case?
0652             if (action != curAction) {
0653                 // qCDebug(KWidgetsAddonsLog) << "\t\t\tmust be newly selected one";
0654                 return i;
0655             }
0656         }
0657         ++i;
0658     }
0659 
0660     // qCDebug(KWidgetsAddonsLog) << "\t\tcurrent action still selected? " << (curAction && curAction->isChecked ());
0661     // 1 or 0 actions checked case (in that order)?
0662     return (curAction && curAction->isChecked()) ? sa->actions().indexOf(curAction) : -1;
0663 }
0664 
0665 bool KSelectAction::eventFilter(QObject *watched, QEvent *event)
0666 {
0667     QComboBox *comboBox = qobject_cast<QComboBox *>(watched);
0668     if (!comboBox) {
0669         return false /*propagate event*/;
0670     }
0671 
0672     // If focus is lost, replace any edited text with the currently selected
0673     // item.
0674     if (event->type() == QEvent::FocusOut) {
0675         QFocusEvent *const e = static_cast<QFocusEvent *>(event);
0676         // qCDebug(KWidgetsAddonsLog) << "KSelectAction::eventFilter(FocusOut)"
0677         //  << "    comboBox: ptr=" << comboBox
0678         //  << " reason=" << e->reason ()
0679         //  << endl;
0680 
0681         if (e->reason() != Qt::ActiveWindowFocusReason // switch window
0682             && e->reason() != Qt::PopupFocusReason // menu
0683             && e->reason() != Qt::OtherFocusReason // inconsistently reproduceable actions...
0684         ) {
0685             // qCDebug(KWidgetsAddonsLog) << "\tkilling text";
0686             comboBox->setEditText(comboBox->itemText(comboBox->currentIndex()));
0687         }
0688 
0689         return false /*propagate event*/;
0690     }
0691 
0692     bool blocked = comboBox->blockSignals(true);
0693 
0694     if (event->type() == QEvent::ActionAdded) {
0695         QActionEvent *const e = static_cast<QActionEvent *>(event);
0696 
0697         const int index = e->before() ? comboBox->findData(QVariant::fromValue(e->before())) : comboBox->count();
0698         const int newItem = ::TrueCurrentItem(this);
0699         // qCDebug(KWidgetsAddonsLog) << "KSelectAction::eventFilter(ActionAdded)"
0700         //          << "    comboBox: ptr=" << comboBox
0701         //          << " currentItem=" << comboBox->currentIndex ()
0702         //          << "    add index=" << index
0703         //          << "    action new: e->before=" << e->before ()
0704         //          << " ptr=" << e->action ()
0705         //          << " icon=" << e->action ()->icon ()
0706         //          << " text=" << e->action ()->text ()
0707         //          << " currentItem=" << newItem
0708         //          << endl;
0709         comboBox->insertItem(index, e->action()->icon(), ::DropAmpersands(e->action()->text()), QVariant::fromValue(e->action()));
0710         if (QStandardItemModel *model = qobject_cast<QStandardItemModel *>(comboBox->model())) {
0711             QStandardItem *item = model->item(index);
0712             item->setEnabled(e->action()->isEnabled());
0713         }
0714 
0715         // Inserting an item into a combobox can change the current item so
0716         // make sure the item corresponding to the checked action is selected.
0717         comboBox->setCurrentIndex(newItem);
0718     } else if (event->type() == QEvent::ActionChanged) {
0719         QActionEvent *const e = static_cast<QActionEvent *>(event);
0720 
0721         const int index = comboBox->findData(QVariant::fromValue(e->action()));
0722         const int newItem = ::TrueCurrentItem(this);
0723         // qCDebug(KWidgetsAddonsLog) << "KSelectAction::eventFilter(ActionChanged)"
0724         //          << "    comboBox: ptr=" << comboBox
0725         //          << " currentItem=" << comboBox->currentIndex ()
0726         //          << "    changed action's index=" << index
0727         //          << "    action new: ptr=" << e->action ()
0728         //          << " icon=" << e->action ()->icon ()
0729         //          << " text=" << e->action ()->text ()
0730         //          << " currentItem=" << newItem
0731         //          << endl;
0732         comboBox->setItemIcon(index, e->action()->icon());
0733         comboBox->setItemText(index, ::DropAmpersands(e->action()->text()));
0734         if (QStandardItemModel *model = qobject_cast<QStandardItemModel *>(comboBox->model())) {
0735             QStandardItem *item = model->item(index);
0736             item->setEnabled(e->action()->isEnabled());
0737         }
0738 
0739         // The checked action may have become unchecked so
0740         // make sure the item corresponding to the checked action is selected.
0741         comboBox->setCurrentIndex(newItem);
0742     } else if (event->type() == QEvent::ActionRemoved) {
0743         QActionEvent *const e = static_cast<QActionEvent *>(event);
0744 
0745         const int index = comboBox->findData(QVariant::fromValue(e->action()));
0746         const int newItem = ::TrueCurrentItem(this);
0747         // qCDebug(KWidgetsAddonsLog) << "KSelectAction::eventFilter(ActionRemoved)"
0748         //          << "    comboBox: ptr=" << comboBox
0749         //          << " currentItem=" << comboBox->currentIndex ()
0750         //          << "    delete action index=" << index
0751         //          << "    new: currentItem=" << newItem
0752         //          << endl;
0753         comboBox->removeItem(index);
0754 
0755         // Removing an item from a combobox can change the current item so
0756         // make sure the item corresponding to the checked action is selected.
0757         comboBox->setCurrentIndex(newItem);
0758     }
0759 
0760     comboBox->blockSignals(blocked);
0761 
0762     return false /*propagate event*/;
0763 }
0764 
0765 // END
0766 
0767 #include "moc_kselectaction.cpp"