diff --git a/applets/kicker/plugin/actionlist.cpp b/applets/kicker/plugin/actionlist.cpp index 15acfd825..27b2bf4d2 100644 --- a/applets/kicker/plugin/actionlist.cpp +++ b/applets/kicker/plugin/actionlist.cpp @@ -1,405 +1,414 @@ /*************************************************************************** * Copyright (C) 2013 by Aurélien Gâteau * * Copyright (C) 2014 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "actionlist.h" #include "menuentryeditor.h" #include #include #include #include #include #include #include #include #include #include #include #include "containmentinterface.h" #ifdef HAVE_APPSTREAMQT #include #endif namespace KAStats = KActivities::Stats; using namespace KAStats; using namespace KAStats::Terms; namespace Kicker { QVariantMap createActionItem(const QString &label, const QString &actionId, const QVariant &argument) { QVariantMap map; map["text"] = label; map["actionId"] = actionId; if (argument.isValid()) { map["actionArgument"] = argument; } return map; } QVariantMap createTitleActionItem(const QString &label) { QVariantMap map; map["text"] = label; map["type"] = "title"; return map; } QVariantMap createSeparatorActionItem() { QVariantMap map; map["type"] = "separator"; return map; } QVariantList createActionListForFileItem(const KFileItem &fileItem) { QVariantList list; KService::List services = KMimeTypeTrader::self()->query(fileItem.mimetype(), "Application"); if (!services.isEmpty()) { list << createTitleActionItem(i18n("Open with:")); foreach (const KService::Ptr service, services) { const QString text = service->name().replace('&', "&&"); QVariantMap item = createActionItem(text, "_kicker_fileItem_openWith", service->entryPath()); item["icon"] = service->icon(); list << item; } list << createSeparatorActionItem(); } list << createActionItem(i18n("Properties"), "_kicker_fileItem_properties"); return list; } bool handleFileItemAction(const KFileItem &fileItem, const QString &actionId, const QVariant &argument, bool *close) { if (actionId == "_kicker_fileItem_properties") { KPropertiesDialog *dlg = new KPropertiesDialog(fileItem, QApplication::activeWindow()); dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->show(); *close = false; return true; } if (actionId == "_kicker_fileItem_openWith") { const QString path = argument.toString(); const KService::Ptr service = KService::serviceByDesktopPath(path); if (!service) { return false; } KRun::runService(*service, QList() << fileItem.url(), QApplication::activeWindow()); *close = true; return true; } return false; } QVariantList createAddLauncherActionList(QObject *appletInterface, const KService::Ptr &service) { QVariantList actionList; if (!service) { return actionList; } if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::Desktop)) { actionList << Kicker::createActionItem(i18n("Add to Desktop"), "addToDesktop"); } if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::Panel)) { actionList << Kicker::createActionItem(i18n("Add to Panel (Widget)"), "addToPanel"); } if (service && ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::TaskManager, service->entryPath())) { actionList << Kicker::createActionItem(i18n("Pin to Task Manager"), "addToTaskManager"); } return actionList; } bool handleAddLauncherAction(const QString &actionId, QObject *appletInterface, const KService::Ptr &service) { if (!service) { return false; } if (actionId == QLatin1String("addToDesktop")) { if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::Desktop)) { ContainmentInterface::addLauncher(appletInterface, ContainmentInterface::Desktop, service->entryPath()); } return true; } else if (actionId == QLatin1String("addToPanel")) { if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::Panel)) { ContainmentInterface::addLauncher(appletInterface, ContainmentInterface::Panel, service->entryPath()); } return true; } else if (actionId == QLatin1String("addToTaskManager")) { if (ContainmentInterface::mayAddLauncher(appletInterface, ContainmentInterface::TaskManager, service->entryPath())) { ContainmentInterface::addLauncher(appletInterface, ContainmentInterface::TaskManager, service->entryPath()); } return true; } return false; } -// HACK TEMP FIXME TODO IVAN QString storageIdFromService(KService::Ptr service) { QString storageId = service->storageId(); - if (storageId.startsWith("org.kde.")) { - storageId = storageId.right(storageId.length() - 8); - } - if (storageId.endsWith(".desktop")) { storageId = storageId.left(storageId.length() - 8); } return storageId; } QVariantList jumpListActions(KService::Ptr service) { QVariantList list; if (!service) { return list; } const auto &actions = service->actions(); foreach (const KServiceAction &action, actions) { if (action.text().isEmpty() || action.exec().isEmpty()) { continue; } QVariantMap item = createActionItem(action.text(), "_kicker_jumpListAction", action.exec()); item[QStringLiteral("icon")] = action.icon(); list << item; } return list; } QVariantList recentDocumentActions(KService::Ptr service) { QVariantList list; if (!service) { return list; } const QString storageId = storageIdFromService(service); if (storageId.isEmpty()) { return list; } auto query = UsedResources | RecentlyUsedFirst | Agent(storageId) | Type::any() | Activity::current() | Url::file(); + // Due to KRecentDocument::add() bug, application name "" could + // be stored instead of its desktop entry name "org.kde.". Let's + // check for both in order to get all results for the application. + if (storageId.startsWith("org.kde.")) { + query = query | Agent(storageId.mid(8)); + } + ResultSet results(query); ResultSet::const_iterator resultIt; resultIt = results.begin(); while (list.count() < 6 && resultIt != results.end()) { const QString resource = (*resultIt).resource(); const QUrl url(resource); if (!url.isValid()) { continue; } const KFileItem fileItem(url); if (!fileItem.isFile()) { continue; } if (list.count() == 0) { list << createTitleActionItem(i18n("Recent Documents")); } QVariantMap item = createActionItem(url.fileName(), "_kicker_recentDocument", resource); item["icon"] = fileItem.iconName(); list << item; ++resultIt; } if (list.count()) { list << createActionItem(i18n("Forget Recent Documents"), "_kicker_forgetRecentDocuments"); } return list; } bool handleRecentDocumentAction(KService::Ptr service, const QString &actionId, const QVariant &_argument) { if (!service) { return false; } if (actionId == "_kicker_forgetRecentDocuments") { const QString storageId = storageIdFromService(service); if (storageId.isEmpty()) { return false; } auto query = UsedResources | Agent(storageId) | Type::any() | Activity::current() | Url::file(); + // Due to KRecentDocument::add() bug, application name "" could + // be stored instead of its desktop entry name "org.kde.". Let's + // check for both in order to get all results for the application. + if (storageId.startsWith("org.kde.")) { + query = query | Agent(storageId.mid(8)); + } + KAStats::forgetResources(query); return false; } QString argument = _argument.toString(); if (argument.isEmpty()) { return false; } return (KRun::runService(*service, QList() << QUrl(argument), QApplication::activeWindow()) != 0); } Q_GLOBAL_STATIC(MenuEntryEditor, menuEntryEditor) bool canEditApplication(const KService::Ptr &service) { return (service->isApplication() && menuEntryEditor->canEdit(service->entryPath())); } void editApplication(const QString &entryPath, const QString &menuId) { menuEntryEditor->edit(entryPath, menuId); } QVariantList editApplicationAction(const KService::Ptr &service) { QVariantList actionList; if (canEditApplication(service)) { QVariantMap editAction = Kicker::createActionItem(i18n("Edit Application..."), "editApplication"); editAction["icon"] = "kmenuedit"; // TODO: Using the KMenuEdit icon might be misleading. actionList << editAction; } return actionList; } bool handleEditApplicationAction(const QString &actionId, const KService::Ptr &service) { if (service && actionId == "editApplication" && canEditApplication(service)) { Kicker::editApplication(service->entryPath(), service->menuId()); return true; } return false; } #ifdef HAVE_APPSTREAMQT Q_GLOBAL_STATIC(AppStream::Pool, appstreamPool) #endif QVariantList appstreamActions(const KService::Ptr &service) { QVariantList ret; #ifdef HAVE_APPSTREAMQT const KService::Ptr appStreamHandler = KMimeTypeTrader::self()->preferredService(QStringLiteral("x-scheme-handler/appstream")); // Don't show action if we can't find any app to handle appstream:// URLs. if (!appStreamHandler) { if (!KProtocolInfo::isHelperProtocol(QStringLiteral("appstream")) || KProtocolInfo::exec(QStringLiteral("appstream")).isEmpty()) { return ret; } } if (!appstreamPool.exists()) { appstreamPool->load(); } const auto components = appstreamPool->componentsById(service->desktopEntryName()+QLatin1String(".desktop")); for(const auto &component: components) { const QString componentId = component.id(); QVariantMap appstreamAction = Kicker::createActionItem(i18nc("@action opens a software center with the application", "Manage '%1'...", component.name()), "manageApplication", QVariant(QStringLiteral("appstream://") + componentId)); appstreamAction[QStringLiteral("icon")] = QStringLiteral("applications-other"); ret << appstreamAction; } #else Q_UNUSED(service) #endif return ret; } bool handleAppstreamActions(const QString &actionId, const QVariant &argument) { if (actionId == "manageApplication") { return QDesktopServices::openUrl(QUrl(argument.toString())); } return false; } } diff --git a/applets/taskmanager/plugin/backend.cpp b/applets/taskmanager/plugin/backend.cpp index e1bde4019..166ccc74c 100644 --- a/applets/taskmanager/plugin/backend.cpp +++ b/applets/taskmanager/plugin/backend.cpp @@ -1,590 +1,600 @@ /*************************************************************************** * Copyright (C) 2012-2016 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ #include "backend.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KAStats = KActivities::Stats; using namespace KAStats; using namespace KAStats::Terms; Backend::Backend(QObject* parent) : QObject(parent) , m_panelWinId(0) , m_highlightWindows(false) , m_actionGroup(new QActionGroup(this)) { } Backend::~Backend() { } QQuickItem *Backend::taskManagerItem() const { return m_taskManagerItem; } void Backend::setTaskManagerItem(QQuickItem* item) { if (item != m_taskManagerItem) { m_taskManagerItem = item; emit taskManagerItemChanged(); } } QQuickItem *Backend::toolTipItem() const { return m_toolTipItem; } void Backend::setToolTipItem(QQuickItem *item) { if (item != m_toolTipItem) { m_toolTipItem = item; connect(item, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(toolTipWindowChanged(QQuickWindow*))); emit toolTipItemChanged(); } } QQuickWindow *Backend::groupDialog() const { return m_groupDialog; } void Backend::setGroupDialog(QQuickWindow *dialog) { if (dialog != m_groupDialog) { m_groupDialog = dialog; emit groupDialogChanged(); } } bool Backend::highlightWindows() const { return m_highlightWindows; } void Backend::setHighlightWindows(bool highlight) { if (highlight != m_highlightWindows) { m_highlightWindows = highlight; updateWindowHighlight(); emit highlightWindowsChanged(); } } QUrl Backend::tryDecodeApplicationsUrl(const QUrl &launcherUrl) { if (launcherUrl.isValid() && launcherUrl.scheme() == QStringLiteral("applications")) { const KService::Ptr service = KService::serviceByMenuId(launcherUrl.path()); if (service) { return QUrl::fromLocalFile(service->entryPath()); } } return launcherUrl; } QVariantList Backend::jumpListActions(const QUrl &launcherUrl, QObject *parent) { if (!parent) { return QVariantList(); } QUrl desktopEntryUrl = tryDecodeApplicationsUrl(launcherUrl); if (!desktopEntryUrl.isValid() || !desktopEntryUrl.isLocalFile() || !KDesktopFile::isDesktopFile(desktopEntryUrl.toLocalFile())) { return QVariantList(); } QVariantList actions; KDesktopFile desktopFile(desktopEntryUrl.toLocalFile()); const QStringList &jumpListActions = desktopFile.readActions(); const QLatin1String kde("KDE"); foreach (const QString &actionName, jumpListActions) { const KConfigGroup &actionGroup = desktopFile.actionGroup(actionName); if (!actionGroup.isValid() || !actionGroup.exists()) { continue; } const QStringList ¬ShowIn = actionGroup.readXdgListEntry(QStringLiteral("NotShowIn")); if (notShowIn.contains(kde)) { continue; } const QStringList &onlyShowIn = actionGroup.readXdgListEntry(QStringLiteral("OnlyShowIn")); if (!onlyShowIn.isEmpty() && !onlyShowIn.contains(kde)) { continue; } const QString &name = actionGroup.readEntry(QStringLiteral("Name")); const QString &exec = actionGroup.readEntry(QStringLiteral("Exec")); if (name.isEmpty() || exec.isEmpty()) { continue; } QAction *action = new QAction(parent); action->setText(name); action->setIcon(QIcon::fromTheme(actionGroup.readEntry("Icon"))); action->setProperty("exec", exec); // so we can show the proper application name and icon when it launches action->setProperty("applicationName", desktopFile.readName()); action->setProperty("applicationIcon", desktopFile.readIcon()); connect(action, &QAction::triggered, this, &Backend::handleJumpListAction); actions << QVariant::fromValue(action); } return actions; } QVariantList Backend::placesActions(const QUrl &launcherUrl, bool showAllPlaces, QObject *parent) { if (!parent) { return QVariantList(); } QUrl desktopEntryUrl = tryDecodeApplicationsUrl(launcherUrl); if (!desktopEntryUrl.isValid() || !desktopEntryUrl.isLocalFile() || !KDesktopFile::isDesktopFile(desktopEntryUrl.toLocalFile())) { return QVariantList(); } QVariantList actions; KDesktopFile desktopFile(desktopEntryUrl.toLocalFile()); // Since we can't have dynamic jump list actions, at least add the user's "Places" for file managers. const QStringList &categories = desktopFile.desktopGroup().readXdgListEntry(QStringLiteral("Categories")); if (!categories.contains(QLatin1String("FileManager"))) { return actions; } QString previousGroup; QMenu *subMenu = nullptr; QScopedPointer placesModel(new KFilePlacesModel()); for (int i = 0; i < placesModel->rowCount(); ++i) { QModelIndex idx = placesModel->index(i, 0); if (placesModel->isHidden(idx)) { continue; } const QString &title = idx.data(Qt::DisplayRole).toString(); const QIcon &icon = idx.data(Qt::DecorationRole).value(); const QUrl &url = idx.data(KFilePlacesModel::UrlRole).toUrl(); QAction *placeAction = new QAction(icon, title, parent); connect(placeAction, &QAction::triggered, this, [this, url, desktopEntryUrl] { KService::Ptr service = KService::serviceByDesktopPath(desktopEntryUrl.toLocalFile()); if (!service) { return; } KRun::runService(*service, {url}, QApplication::activeWindow()); }); const QString &groupName = idx.data(KFilePlacesModel::GroupRole).toString(); if (previousGroup.isEmpty()) { // Skip first group heading. previousGroup = groupName; } // Put all subsequent categories into a submenu. if (previousGroup != groupName) { QAction *subMenuAction = new QAction(groupName, parent); subMenu = new QMenu(); // Cannot parent a QMenu to a QAction, need to delete it manually. connect(parent, &QObject::destroyed, subMenu, &QObject::deleteLater); subMenuAction->setMenu(subMenu); actions << QVariant::fromValue(subMenuAction); previousGroup = groupName; } if (subMenu) { subMenu->addAction(placeAction); } else { actions << QVariant::fromValue(placeAction); } } // There is nothing more frustrating than having a "More" entry that ends up showing just one or two // additional entries. Therefore we truncate to max. 5 entries only if there are more than 7 in total. if (!showAllPlaces && actions.count() > 7) { const int totalActionCount = actions.count(); while (actions.count() > 5) { actions.removeLast(); } QAction *action = new QAction(parent); action->setText(i18ncp("Show all user Places", "%1 more Place", "%1 more Places", totalActionCount - actions.count())); connect(action, &QAction::triggered, this, &Backend::showAllPlaces); actions << QVariant::fromValue(action); } return actions; } QVariantList Backend::recentDocumentActions(const QUrl &launcherUrl, QObject *parent) { if (!parent) { return QVariantList(); } QUrl desktopEntryUrl = tryDecodeApplicationsUrl(launcherUrl); if (!desktopEntryUrl.isValid() || !desktopEntryUrl.isLocalFile() || !KDesktopFile::isDesktopFile(desktopEntryUrl.toLocalFile())) { return QVariantList(); } QVariantList actions; QString desktopName = desktopEntryUrl.fileName(); QString storageId = desktopName; - if (storageId.startsWith(QLatin1String("org.kde."))) { - storageId = storageId.right(storageId.length() - 8); - } - if (storageId.endsWith(QLatin1String(".desktop"))) { storageId = storageId.left(storageId.length() - 8); } auto query = UsedResources | RecentlyUsedFirst | Agent(storageId) | Type::any() | Activity::current() | Url::file(); + // Due to KRecentDocument::add() bug, application name "" could + // be stored instead of its desktop entry name "org.kde.". Let's + // check for both in order to get all results for the application. + if (storageId.startsWith(QLatin1String("org.kde."))) { + query = query | Agent(storageId.mid(8)); + } + ResultSet results(query); ResultSet::const_iterator resultIt = results.begin(); int actionCount = 0; while (actionCount < 5 && resultIt != results.end()) { const QString resource = (*resultIt).resource(); const QUrl url(resource); if (!url.isValid()) { continue; } const KFileItem fileItem(url); if (!fileItem.isFile()) { continue; } QAction *action = new QAction(parent); action->setText(url.fileName()); action->setIcon(QIcon::fromTheme(fileItem.iconName(), QIcon::fromTheme("unknown"))); action->setProperty("agent", storageId); action->setProperty("entryPath", desktopEntryUrl); action->setData(resource); connect(action, &QAction::triggered, this, &Backend::handleRecentDocumentAction); actions << QVariant::fromValue(action); ++resultIt; ++actionCount; } if (actionCount > 0) { QAction *action = new QAction(parent); action->setText(i18n("Forget Recent Documents")); action->setProperty("agent", storageId); connect(action, &QAction::triggered, this, &Backend::handleRecentDocumentAction); actions << QVariant::fromValue(action); } return actions; } void Backend::toolTipWindowChanged(QQuickWindow *window) { Q_UNUSED(window) updateWindowHighlight(); } void Backend::handleJumpListAction() const { const QAction *action = qobject_cast(sender()); if (!action) { return; } KRun::run(action->property("exec").toString(), {}, nullptr, action->property("applicationName").toString(), action->property("applicationIcon").toString()); } void Backend::handleRecentDocumentAction() const { const QAction *action = qobject_cast(sender()); if (!action) { return; } const QString agent = action->property("agent").toString(); if (agent.isEmpty()) { return; } const QString desktopPath = action->property("entryPath").toUrl().toLocalFile(); const QString resource = action->data().toString(); if (desktopPath.isEmpty() || resource.isEmpty()) { auto query = UsedResources | Agent(agent) | Type::any() | Activity::current() | Url::file(); + // Due to KRecentDocument::add() bug, application name "" could + // be stored instead of its desktop entry name "org.kde.". Let's + // check for both in order to get all results for the application. + if (agent.startsWith(QLatin1String("org.kde."))) { + query = query | Agent(agent.mid(8)); + } + KAStats::forgetResources(query); return; } KService::Ptr service = KService::serviceByDesktopPath(desktopPath); qDebug() << service; if (!service) { return; } KRun::runService(*service, QList() << QUrl(resource), QApplication::activeWindow()); } void Backend::setActionGroup(QAction *action) const { if (action) { action->setActionGroup(m_actionGroup); } } QRect Backend::globalRect(QQuickItem *item) const { if (!item || !item->window()) { return QRect(); } QRect iconRect(item->x(), item->y(), item->width(), item->height()); iconRect.moveTopLeft(item->parentItem()->mapToScene(iconRect.topLeft()).toPoint()); iconRect.moveTopLeft(item->window()->mapToGlobal(iconRect.topLeft())); return iconRect; } void Backend::ungrabMouse(QQuickItem *item) const { //this is a workaround where Qt will fail to realise a mouse has been released // this happens if a window which does not accept focus spawns a new window that takes focus and X grab // whilst the mouse is depressed // https://bugreports.qt.io/browse/QTBUG-59044 // this causes the next click to go missing //by releasing manually we avoid that situation auto ungrabMouseHack = [item]() { if (item && item->window() && item->window()->mouseGrabberItem()) { item->window()->mouseGrabberItem()->ungrabMouse(); } }; //pre 5.8.0 QQuickWindow code is "item->grabMouse(); sendEvent(item, mouseEvent)" //post 5.8.0 QQuickWindow code is sendEvent(item, mouseEvent); item->grabMouse() if (QVersionNumber::fromString(qVersion()) > QVersionNumber(5, 8, 0)) { QTimer::singleShot(0, item, ungrabMouseHack); } else { ungrabMouseHack(); } //end workaround } bool Backend::canPresentWindows() const { return (KWindowSystem::compositingActive() && KWindowEffects::isEffectAvailable(KWindowEffects::PresentWindowsGroup)); } void Backend::presentWindows(const QVariant &_winIds) { if (!m_taskManagerItem || !m_taskManagerItem->window()) { return; } QList winIds; const QVariantList &_winIdsList = _winIds.toList(); foreach(const QVariant &_winId, _winIdsList) { bool ok = false; qlonglong winId = _winId.toLongLong(&ok); if (ok) { winIds.append(winId); } } if (!winIds.count()) { return; } if (m_windowsToHighlight.count()) { m_windowsToHighlight.clear(); updateWindowHighlight(); } KWindowEffects::presentWindows(m_taskManagerItem->window()->winId(), winIds); } bool Backend::isApplication(const QUrl &url) const { if (!url.isValid() || !url.isLocalFile()) { return false; } const QString &localPath = url.toLocalFile(); if (!KDesktopFile::isDesktopFile(localPath)) { return false; } KDesktopFile desktopFile(localPath); return desktopFile.hasApplicationType(); } QList Backend::jsonArrayToUrlList(const QJsonArray &array) const { QList urls; urls.reserve(array.count()); for (auto it = array.constBegin(), end = array.constEnd(); it != end; ++it) { urls << QUrl(it->toString()); } return urls; } void Backend::cancelHighlightWindows() { m_windowsToHighlight.clear(); updateWindowHighlight(); } void Backend::windowsHovered(const QVariant &_winIds, bool hovered) { m_windowsToHighlight.clear(); if (hovered) { const QVariantList &winIds = _winIds.toList(); foreach(const QVariant &_winId, winIds) { bool ok = false; qlonglong winId = _winId.toLongLong(&ok); if (ok) { m_windowsToHighlight.append(winId); } } } updateWindowHighlight(); } void Backend::updateWindowHighlight() { if (!m_highlightWindows) { if (m_panelWinId) { KWindowEffects::highlightWindows(m_panelWinId, QList()); m_panelWinId = 0; } return; } if (m_taskManagerItem && m_taskManagerItem->window()) { m_panelWinId = m_taskManagerItem->window()->winId(); } else { return; } QList windows = m_windowsToHighlight; if (windows.count() && m_toolTipItem && m_toolTipItem->window()) { windows.append(m_toolTipItem->window()->winId()); } if (windows.count() && m_groupDialog) { windows.append(m_groupDialog->winId()); } KWindowEffects::highlightWindows(m_panelWinId, windows); }