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