File indexing completed on 2024-05-19 16:34:55

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2009 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "tabboxhandler.h"
0010 
0011 #include <config-kwin.h>
0012 
0013 // own
0014 #include "clientmodel.h"
0015 #include "desktopmodel.h"
0016 #include "scripting/scripting.h"
0017 #include "switcheritem.h"
0018 #include "tabbox_logging.h"
0019 #include "utils/xcbutils.h"
0020 #include <kwinglobals.h>
0021 // Qt
0022 #include <QKeyEvent>
0023 #include <QQmlComponent>
0024 #include <QQmlContext>
0025 #include <QQmlEngine>
0026 #include <QQuickItem>
0027 #include <QQuickWindow>
0028 #include <QStandardPaths>
0029 #include <QTimer>
0030 #include <qpa/qwindowsysteminterface.h>
0031 // KDE
0032 #include <KLocalizedString>
0033 #include <KPackage/Package>
0034 #include <KPackage/PackageLoader>
0035 #include <KProcess>
0036 
0037 namespace KWin
0038 {
0039 namespace TabBox
0040 {
0041 
0042 class TabBoxHandlerPrivate
0043 {
0044 public:
0045     TabBoxHandlerPrivate(TabBoxHandler *q);
0046 
0047     ~TabBoxHandlerPrivate();
0048 
0049     /**
0050      * Updates the current highlight window state
0051      */
0052     void updateHighlightWindows();
0053     /**
0054      * Ends window highlighting
0055      */
0056     void endHighlightWindows(bool abort = false);
0057 
0058     void show();
0059     QQuickWindow *window() const;
0060     SwitcherItem *switcherItem() const;
0061 
0062     ClientModel *clientModel() const;
0063     DesktopModel *desktopModel() const;
0064 
0065     bool isHighlightWindows() const;
0066 
0067     TabBoxHandler *q; // public pointer
0068     // members
0069     TabBoxConfig config;
0070     std::unique_ptr<QQmlContext> m_qmlContext;
0071     std::unique_ptr<QQmlComponent> m_qmlComponent;
0072     QObject *m_mainItem;
0073     QMap<QString, QObject *> m_clientTabBoxes;
0074     QMap<QString, QObject *> m_desktopTabBoxes;
0075     ClientModel *m_clientModel;
0076     DesktopModel *m_desktopModel;
0077     QModelIndex index;
0078     /**
0079      * Indicates if the tabbox is shown.
0080      */
0081     bool isShown;
0082     TabBoxClient *lastRaisedClient, *lastRaisedClientSucc;
0083     int wheelAngleDelta = 0;
0084 
0085 private:
0086     QObject *createSwitcherItem(bool desktopMode);
0087 };
0088 
0089 TabBoxHandlerPrivate::TabBoxHandlerPrivate(TabBoxHandler *q)
0090     : m_qmlContext()
0091     , m_qmlComponent(nullptr)
0092     , m_mainItem(nullptr)
0093 {
0094     this->q = q;
0095     isShown = false;
0096     lastRaisedClient = nullptr;
0097     lastRaisedClientSucc = nullptr;
0098     config = TabBoxConfig();
0099     m_clientModel = new ClientModel(q);
0100     m_desktopModel = new DesktopModel(q);
0101 }
0102 
0103 TabBoxHandlerPrivate::~TabBoxHandlerPrivate()
0104 {
0105     for (auto it = m_clientTabBoxes.constBegin(); it != m_clientTabBoxes.constEnd(); ++it) {
0106         delete it.value();
0107     }
0108     for (auto it = m_desktopTabBoxes.constBegin(); it != m_desktopTabBoxes.constEnd(); ++it) {
0109         delete it.value();
0110     }
0111 }
0112 
0113 QQuickWindow *TabBoxHandlerPrivate::window() const
0114 {
0115     if (!m_mainItem) {
0116         return nullptr;
0117     }
0118     if (QQuickWindow *w = qobject_cast<QQuickWindow *>(m_mainItem)) {
0119         return w;
0120     }
0121     return m_mainItem->findChild<QQuickWindow *>();
0122 }
0123 
0124 #ifndef KWIN_UNIT_TEST
0125 SwitcherItem *TabBoxHandlerPrivate::switcherItem() const
0126 {
0127     if (!m_mainItem) {
0128         return nullptr;
0129     }
0130     if (SwitcherItem *i = qobject_cast<SwitcherItem *>(m_mainItem)) {
0131         return i;
0132     } else if (QQuickWindow *w = qobject_cast<QQuickWindow *>(m_mainItem)) {
0133         return w->contentItem()->findChild<SwitcherItem *>();
0134     }
0135     return m_mainItem->findChild<SwitcherItem *>();
0136 }
0137 #endif
0138 
0139 ClientModel *TabBoxHandlerPrivate::clientModel() const
0140 {
0141     return m_clientModel;
0142 }
0143 
0144 DesktopModel *TabBoxHandlerPrivate::desktopModel() const
0145 {
0146     return m_desktopModel;
0147 }
0148 
0149 bool TabBoxHandlerPrivate::isHighlightWindows() const
0150 {
0151     const QQuickWindow *w = window();
0152     if (w && w->visibility() == QWindow::FullScreen) {
0153         return false;
0154     }
0155     return config.isHighlightWindows();
0156 }
0157 
0158 void TabBoxHandlerPrivate::updateHighlightWindows()
0159 {
0160     if (!isShown || config.tabBoxMode() != TabBoxConfig::ClientTabBox) {
0161         return;
0162     }
0163 
0164     TabBoxClient *currentClient = q->client(index);
0165     QWindow *w = window();
0166 
0167     if (q->isKWinCompositing()) {
0168         if (lastRaisedClient) {
0169             q->elevateClient(lastRaisedClient, w, false);
0170         }
0171         lastRaisedClient = currentClient;
0172         // don't elevate desktop
0173         const auto desktop = q->desktopClient().toStrongRef();
0174         if (currentClient && (!desktop || currentClient->internalId() != desktop->internalId())) {
0175             q->elevateClient(currentClient, w, true);
0176         }
0177     } else {
0178         if (lastRaisedClient) {
0179             q->shadeClient(lastRaisedClient, true);
0180             if (lastRaisedClientSucc) {
0181                 q->restack(lastRaisedClient, lastRaisedClientSucc);
0182             }
0183             // TODO lastRaisedClient->setMinimized( lastRaisedClientWasMinimized );
0184         }
0185 
0186         lastRaisedClient = currentClient;
0187         if (lastRaisedClient) {
0188             q->shadeClient(lastRaisedClient, false);
0189             // TODO if ( (lastRaisedClientWasMinimized = lastRaisedClient->isMinimized()) )
0190             //         lastRaisedClient->setMinimized( false );
0191             TabBoxClientList order = q->stackingOrder();
0192             int succIdx = order.count() + 1;
0193             for (int i = 0; i < order.count(); ++i) {
0194                 if (order.at(i).toStrongRef() == lastRaisedClient) {
0195                     succIdx = i + 1;
0196                     break;
0197                 }
0198             }
0199             lastRaisedClientSucc = (succIdx < order.count()) ? order.at(succIdx).toStrongRef().data() : nullptr;
0200             q->raiseClient(lastRaisedClient);
0201         }
0202     }
0203 
0204     if (config.isShowTabBox() && w) {
0205         q->highlightWindows(currentClient, w);
0206     } else {
0207         q->highlightWindows(currentClient);
0208     }
0209 }
0210 
0211 void TabBoxHandlerPrivate::endHighlightWindows(bool abort)
0212 {
0213     TabBoxClient *currentClient = q->client(index);
0214     if (isHighlightWindows() && q->isKWinCompositing()) {
0215         const auto stackingOrder = q->stackingOrder();
0216         for (const QWeakPointer<TabBoxClient> &clientPointer : stackingOrder) {
0217             if (QSharedPointer<TabBoxClient> client = clientPointer.toStrongRef()) {
0218                 if (client != currentClient) { // to not mess up with wanted ShadeActive/ShadeHover state
0219                     q->shadeClient(client.data(), true);
0220                 }
0221             }
0222         }
0223     }
0224     QWindow *w = window();
0225     if (currentClient) {
0226         q->elevateClient(currentClient, w, false);
0227     }
0228     if (abort && lastRaisedClient && lastRaisedClientSucc) {
0229         q->restack(lastRaisedClient, lastRaisedClientSucc);
0230     }
0231     lastRaisedClient = nullptr;
0232     lastRaisedClientSucc = nullptr;
0233     // highlight windows
0234     q->highlightWindows();
0235 }
0236 
0237 #ifndef KWIN_UNIT_TEST
0238 QObject *TabBoxHandlerPrivate::createSwitcherItem(bool desktopMode)
0239 {
0240     // first try look'n'feel package
0241     QString file = QStandardPaths::locate(
0242         QStandardPaths::GenericDataLocation,
0243         QStringLiteral("plasma/look-and-feel/%1/contents/%2")
0244             .arg(config.layoutName(),
0245                  desktopMode ? QStringLiteral("desktopswitcher/DesktopSwitcher.qml") : QStringLiteral("windowswitcher/WindowSwitcher.qml")));
0246     if (file.isNull()) {
0247         const QString folderName = desktopMode ? QLatin1String("kwin/desktoptabbox/") : QLatin1String("kwin/tabbox/");
0248         auto findSwitcher = [this, desktopMode, folderName] {
0249             const QString type = desktopMode ? QStringLiteral("KWin/DesktopSwitcher") : QStringLiteral("KWin/WindowSwitcher");
0250             auto offers = KPackage::PackageLoader::self()->findPackages(type, folderName,
0251                                                                         [this](const KPluginMetaData &data) {
0252                                                                             return data.pluginId().compare(config.layoutName(), Qt::CaseInsensitive) == 0;
0253                                                                         });
0254             if (offers.isEmpty()) {
0255                 // load default
0256                 offers = KPackage::PackageLoader::self()->findPackages(type, folderName,
0257                                                                        [](const KPluginMetaData &data) {
0258                                                                            return data.pluginId().compare(QStringLiteral("informative"), Qt::CaseInsensitive) == 0;
0259                                                                        });
0260                 if (offers.isEmpty()) {
0261                     qCDebug(KWIN_TABBOX) << "could not find default window switcher layout";
0262                     return KPluginMetaData();
0263                 }
0264             }
0265             return offers.first();
0266         };
0267         auto service = findSwitcher();
0268         if (!service.isValid()) {
0269             return nullptr;
0270         }
0271         if (service.value(QStringLiteral("X-Plasma-API")) != QLatin1String("declarativeappletscript")) {
0272             qCDebug(KWIN_TABBOX) << "Window Switcher Layout is no declarativeappletscript";
0273             return nullptr;
0274         }
0275         auto findScriptFile = [service, folderName] {
0276             const QString pluginName = service.pluginId();
0277             const QString scriptName = service.value(QStringLiteral("X-Plasma-MainScript"));
0278             return QStandardPaths::locate(QStandardPaths::GenericDataLocation, folderName + pluginName + QLatin1String("/contents/") + scriptName);
0279         };
0280         file = findScriptFile();
0281     }
0282     if (file.isNull()) {
0283         qCDebug(KWIN_TABBOX) << "Could not find QML file for window switcher";
0284         return nullptr;
0285     }
0286     m_qmlComponent->loadUrl(QUrl::fromLocalFile(file));
0287     if (m_qmlComponent->isError()) {
0288         qCDebug(KWIN_TABBOX) << "Component failed to load: " << m_qmlComponent->errors();
0289         QStringList args;
0290         args << QStringLiteral("--passivepopup") << i18n("The Window Switcher installation is broken, resources are missing.\n"
0291                                                          "Contact your distribution about this.")
0292              << QStringLiteral("20");
0293         KProcess::startDetached(QStringLiteral("kdialog"), args);
0294         m_qmlComponent.reset(nullptr);
0295     } else {
0296         QObject *object = m_qmlComponent->create(m_qmlContext.get());
0297         if (desktopMode) {
0298             m_desktopTabBoxes.insert(config.layoutName(), object);
0299         } else {
0300             m_clientTabBoxes.insert(config.layoutName(), object);
0301         }
0302         return object;
0303     }
0304     return nullptr;
0305 }
0306 #endif
0307 
0308 void TabBoxHandlerPrivate::show()
0309 {
0310 #ifndef KWIN_UNIT_TEST
0311     if (!m_qmlContext) {
0312         qmlRegisterType<SwitcherItem>("org.kde.kwin", 2, 0, "Switcher");
0313         qmlRegisterType<SwitcherItem>("org.kde.kwin", 3, 0, "TabBoxSwitcher");
0314         m_qmlContext.reset(new QQmlContext(Scripting::self()->qmlEngine()));
0315     }
0316     if (!m_qmlComponent) {
0317         m_qmlComponent.reset(new QQmlComponent(Scripting::self()->qmlEngine()));
0318     }
0319     const bool desktopMode = (config.tabBoxMode() == TabBoxConfig::DesktopTabBox);
0320     auto findMainItem = [this](const QMap<QString, QObject *> &tabBoxes) -> QObject * {
0321         auto it = tabBoxes.constFind(config.layoutName());
0322         if (it != tabBoxes.constEnd()) {
0323             return it.value();
0324         }
0325         return nullptr;
0326     };
0327     m_mainItem = nullptr;
0328     m_mainItem = desktopMode ? findMainItem(m_desktopTabBoxes) : findMainItem(m_clientTabBoxes);
0329     if (!m_mainItem) {
0330         m_mainItem = createSwitcherItem(desktopMode);
0331         if (!m_mainItem) {
0332             return;
0333         }
0334     }
0335     if (SwitcherItem *item = switcherItem()) {
0336         // In case the model isn't yet set (see below), index will be reset and therefore we
0337         // need to save the current index row (https://bugs.kde.org/show_bug.cgi?id=333511).
0338         int indexRow = index.row();
0339         if (!item->model()) {
0340             QAbstractItemModel *model = nullptr;
0341             if (desktopMode) {
0342                 model = desktopModel();
0343             } else {
0344                 model = clientModel();
0345             }
0346             item->setModel(model);
0347         }
0348         item->setAllDesktops(config.clientDesktopMode() == TabBoxConfig::AllDesktopsClients);
0349         item->setCurrentIndex(indexRow);
0350         item->setNoModifierGrab(q->noModifierGrab());
0351         // everything is prepared, so let's make the whole thing visible
0352         item->setVisible(true);
0353     }
0354     if (QWindow *w = window()) {
0355         wheelAngleDelta = 0;
0356         w->installEventFilter(q);
0357         // pretend to activate the window to enable accessibility notifications
0358         QWindowSystemInterface::handleWindowActivated(w, Qt::TabFocusReason);
0359     }
0360 #endif
0361 }
0362 
0363 /***********************************************
0364  * TabBoxHandler
0365  ***********************************************/
0366 
0367 TabBoxHandler::TabBoxHandler(QObject *parent)
0368     : QObject(parent)
0369 {
0370     KWin::TabBox::tabBox = this;
0371     d = new TabBoxHandlerPrivate(this);
0372 }
0373 
0374 TabBoxHandler::~TabBoxHandler()
0375 {
0376     delete d;
0377 }
0378 
0379 const KWin::TabBox::TabBoxConfig &TabBoxHandler::config() const
0380 {
0381     return d->config;
0382 }
0383 
0384 void TabBoxHandler::setConfig(const TabBoxConfig &config)
0385 {
0386     d->config = config;
0387     Q_EMIT configChanged();
0388 }
0389 
0390 void TabBoxHandler::show()
0391 {
0392     d->isShown = true;
0393     d->lastRaisedClient = nullptr;
0394     d->lastRaisedClientSucc = nullptr;
0395     if (d->config.isShowTabBox()) {
0396         d->show();
0397     }
0398     if (d->isHighlightWindows()) {
0399         // TODO this should be
0400         // QMetaObject::invokeMethod(this, "initHighlightWindows", Qt::QueuedConnection);
0401         // but we somehow need to cross > 1 event cycle (likely because of queued invocation in the effects)
0402         // to ensure the EffectWindow is present when updateHighlightWindows, thus elevating the window/tabbox
0403         QTimer::singleShot(1, this, &TabBoxHandler::initHighlightWindows);
0404     }
0405 }
0406 
0407 void TabBoxHandler::initHighlightWindows()
0408 {
0409     if (isKWinCompositing()) {
0410         const auto stack = stackingOrder();
0411         for (const QWeakPointer<TabBoxClient> &clientPointer : stack) {
0412             if (QSharedPointer<TabBoxClient> client = clientPointer.toStrongRef()) {
0413                 shadeClient(client.data(), false);
0414             }
0415         }
0416     }
0417     d->updateHighlightWindows();
0418 }
0419 
0420 void TabBoxHandler::hide(bool abort)
0421 {
0422     d->isShown = false;
0423     if (d->isHighlightWindows()) {
0424         d->endHighlightWindows(abort);
0425     }
0426 #ifndef KWIN_UNIT_TEST
0427     if (SwitcherItem *item = d->switcherItem()) {
0428         item->setVisible(false);
0429     }
0430 #endif
0431     if (QQuickWindow *w = d->window()) {
0432         w->hide();
0433         w->destroy();
0434     }
0435     d->m_mainItem = nullptr;
0436 }
0437 
0438 QModelIndex TabBoxHandler::nextPrev(bool forward) const
0439 {
0440     QModelIndex ret;
0441     QAbstractItemModel *model;
0442     switch (d->config.tabBoxMode()) {
0443     case TabBoxConfig::ClientTabBox:
0444         model = d->clientModel();
0445         break;
0446     case TabBoxConfig::DesktopTabBox:
0447         model = d->desktopModel();
0448         break;
0449     default:
0450         Q_UNREACHABLE();
0451     }
0452     if (forward) {
0453         int column = d->index.column() + 1;
0454         int row = d->index.row();
0455         if (column == model->columnCount()) {
0456             column = 0;
0457             row++;
0458             if (row == model->rowCount()) {
0459                 row = 0;
0460             }
0461         }
0462         ret = model->index(row, column);
0463         if (!ret.isValid()) {
0464             ret = model->index(0, 0);
0465         }
0466     } else {
0467         int column = d->index.column() - 1;
0468         int row = d->index.row();
0469         if (column < 0) {
0470             column = model->columnCount() - 1;
0471             row--;
0472             if (row < 0) {
0473                 row = model->rowCount() - 1;
0474             }
0475         }
0476         ret = model->index(row, column);
0477         if (!ret.isValid()) {
0478             row = model->rowCount() - 1;
0479             for (int i = model->columnCount() - 1; i >= 0; i--) {
0480                 ret = model->index(row, i);
0481                 if (ret.isValid()) {
0482                     break;
0483                 }
0484             }
0485         }
0486     }
0487     if (ret.isValid()) {
0488         return ret;
0489     } else {
0490         return d->index;
0491     }
0492 }
0493 
0494 QModelIndex TabBoxHandler::desktopIndex(int desktop) const
0495 {
0496     if (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox) {
0497         return QModelIndex();
0498     }
0499     return d->desktopModel()->desktopIndex(desktop);
0500 }
0501 
0502 QList<int> TabBoxHandler::desktopList() const
0503 {
0504     if (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox) {
0505         return QList<int>();
0506     }
0507     return d->desktopModel()->desktopList();
0508 }
0509 
0510 int TabBoxHandler::desktop(const QModelIndex &index) const
0511 {
0512     if (!index.isValid() || (d->config.tabBoxMode() != TabBoxConfig::DesktopTabBox)) {
0513         return -1;
0514     }
0515     QVariant ret = d->desktopModel()->data(index, DesktopModel::DesktopRole);
0516     if (ret.isValid()) {
0517         return ret.toInt();
0518     } else {
0519         return -1;
0520     }
0521 }
0522 
0523 void TabBoxHandler::setCurrentIndex(const QModelIndex &index)
0524 {
0525     if (d->index == index) {
0526         return;
0527     }
0528     if (!index.isValid()) {
0529         return;
0530     }
0531     d->index = index;
0532     if (d->config.tabBoxMode() == TabBoxConfig::ClientTabBox) {
0533         if (d->isHighlightWindows()) {
0534             d->updateHighlightWindows();
0535         }
0536     }
0537     Q_EMIT selectedIndexChanged();
0538 }
0539 
0540 const QModelIndex &TabBoxHandler::currentIndex() const
0541 {
0542     return d->index;
0543 }
0544 
0545 void TabBoxHandler::grabbedKeyEvent(QKeyEvent *event) const
0546 {
0547     if (!d->m_mainItem || !d->window()) {
0548         return;
0549     }
0550     QCoreApplication::sendEvent(d->window(), event);
0551 }
0552 
0553 bool TabBoxHandler::containsPos(const QPoint &pos) const
0554 {
0555     if (!d->m_mainItem) {
0556         return false;
0557     }
0558     QWindow *w = d->window();
0559     if (w) {
0560         return w->geometry().contains(pos);
0561     }
0562     return false;
0563 }
0564 
0565 QModelIndex TabBoxHandler::index(QWeakPointer<KWin::TabBox::TabBoxClient> client) const
0566 {
0567     return d->clientModel()->index(client);
0568 }
0569 
0570 TabBoxClientList TabBoxHandler::clientList() const
0571 {
0572     if (d->config.tabBoxMode() != TabBoxConfig::ClientTabBox) {
0573         return TabBoxClientList();
0574     }
0575     return d->clientModel()->clientList();
0576 }
0577 
0578 TabBoxClient *TabBoxHandler::client(const QModelIndex &index) const
0579 {
0580     if ((!index.isValid()) || (d->config.tabBoxMode() != TabBoxConfig::ClientTabBox)) {
0581         return nullptr;
0582     }
0583     TabBoxClient *c = static_cast<TabBoxClient *>(
0584         d->clientModel()->data(index, ClientModel::ClientRole).value<void *>());
0585     return c;
0586 }
0587 
0588 void TabBoxHandler::createModel(bool partialReset)
0589 {
0590     switch (d->config.tabBoxMode()) {
0591     case TabBoxConfig::ClientTabBox: {
0592         d->clientModel()->createClientList(partialReset);
0593         // TODO: C++11 use lambda function
0594         bool lastRaised = false;
0595         bool lastRaisedSucc = false;
0596         const auto clients = stackingOrder();
0597         for (const auto &clientPointer : clients) {
0598             QSharedPointer<TabBoxClient> client = clientPointer.toStrongRef();
0599             if (!client) {
0600                 continue;
0601             }
0602             if (client.data() == d->lastRaisedClient) {
0603                 lastRaised = true;
0604             }
0605             if (client.data() == d->lastRaisedClientSucc) {
0606                 lastRaisedSucc = true;
0607             }
0608         }
0609         if (d->lastRaisedClient && !lastRaised) {
0610             d->lastRaisedClient = nullptr;
0611         }
0612         if (d->lastRaisedClientSucc && !lastRaisedSucc) {
0613             d->lastRaisedClientSucc = nullptr;
0614         }
0615         break;
0616     }
0617     case TabBoxConfig::DesktopTabBox:
0618         d->desktopModel()->createDesktopList();
0619         break;
0620     }
0621 }
0622 
0623 QModelIndex TabBoxHandler::first() const
0624 {
0625     QAbstractItemModel *model;
0626     switch (d->config.tabBoxMode()) {
0627     case TabBoxConfig::ClientTabBox:
0628         model = d->clientModel();
0629         break;
0630     case TabBoxConfig::DesktopTabBox:
0631         model = d->desktopModel();
0632         break;
0633     default:
0634         Q_UNREACHABLE();
0635     }
0636     return model->index(0, 0);
0637 }
0638 
0639 bool TabBoxHandler::eventFilter(QObject *watched, QEvent *e)
0640 {
0641     if (e->type() == QEvent::Wheel && watched == d->window()) {
0642         QWheelEvent *event = static_cast<QWheelEvent *>(e);
0643         // On x11 the delta for vertical scrolling might also be on X for whatever reason
0644         const int delta = std::abs(event->angleDelta().x()) > std::abs(event->angleDelta().y()) ? event->angleDelta().x() : event->angleDelta().y();
0645         d->wheelAngleDelta += delta;
0646         while (d->wheelAngleDelta <= -120) {
0647             d->wheelAngleDelta += 120;
0648             const QModelIndex index = nextPrev(true);
0649             if (index.isValid()) {
0650                 setCurrentIndex(index);
0651             }
0652         }
0653         while (d->wheelAngleDelta >= 120) {
0654             d->wheelAngleDelta -= 120;
0655             const QModelIndex index = nextPrev(false);
0656             if (index.isValid()) {
0657                 setCurrentIndex(index);
0658             }
0659         }
0660         return true;
0661     }
0662     // pass on
0663     return QObject::eventFilter(watched, e);
0664 }
0665 
0666 TabBoxHandler *tabBox = nullptr;
0667 
0668 TabBoxClient::TabBoxClient()
0669 {
0670 }
0671 
0672 TabBoxClient::~TabBoxClient()
0673 {
0674 }
0675 
0676 } // namespace TabBox
0677 } // namespace KWin