diff --git a/applets/appmenu/plugin/appmenumodel.cpp b/applets/appmenu/plugin/appmenumodel.cpp index cb205c57c..4130d97f0 100644 --- a/applets/appmenu/plugin/appmenumodel.cpp +++ b/applets/appmenu/plugin/appmenumodel.cpp @@ -1,238 +1,240 @@ /****************************************************************** * Copyright 2016 Kai Uwe Broulik * Copyright 2016 Chinmoy Ranjan Pradhan * * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . * ******************************************************************/ #include "appmenumodel.h" #include #if HAVE_X11 #include #include #endif #include #include #include static const QByteArray s_x11AppMenuServiceNamePropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME"); static const QByteArray s_x11AppMenuObjectPathPropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH"); class KDBusMenuImporter : public DBusMenuImporter { public: KDBusMenuImporter(const QString &service, const QString &path, QObject *parent) : DBusMenuImporter(service, path, parent) { } protected: QIcon iconForName(const QString &name) override { return QIcon::fromTheme(name); } }; AppMenuModel::AppMenuModel(QObject *parent) : QAbstractListModel(parent) { connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &AppMenuModel::onActiveWindowChanged); connect(this, &AppMenuModel::modelNeedsUpdate, this, &AppMenuModel::update, Qt::UniqueConnection); onActiveWindowChanged(KWindowSystem::activeWindow()); } AppMenuModel::~AppMenuModel() = default; bool AppMenuModel::menuAvailable() const { return m_menuAvailable; } void AppMenuModel::setMenuAvailable(bool set) { if (m_menuAvailable != set) { m_menuAvailable = set; emit menuAvailableChanged(); } } int AppMenuModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_activeMenu.count(); } void AppMenuModel::update() { beginResetModel(); if (!m_activeMenu.isEmpty() && !m_activeActions.isEmpty()) { m_activeMenu.clear(); m_activeActions.clear(); } if (m_menu && m_menuAvailable) { const auto &actions = m_menu->actions(); for (QAction *action : actions) { m_activeActions.append(action); m_activeMenu.append(action->text()); } } endResetModel(); } void AppMenuModel::onActiveWindowChanged(WId id) { #if HAVE_X11 if (KWindowSystem::isPlatformX11()) { auto *c = QX11Info::connection(); static QHash s_atoms; auto getWindowPropertyString = [c, this](WId id, const QByteArray &name) -> QByteArray { QByteArray value; if (!s_atoms.contains(name)) { const xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom(c, false, name.length(), name.constData()); QScopedPointer atomReply(xcb_intern_atom_reply(c, atomCookie, Q_NULLPTR)); if (atomReply.isNull()) { return value; } s_atoms[name] = atomReply->atom; if (s_atoms[name] == XCB_ATOM_NONE) { return value; } } static const long MAX_PROP_SIZE = 10000; auto propertyCookie = xcb_get_property(c, false, id, s_atoms[name], XCB_ATOM_STRING, 0, MAX_PROP_SIZE); QScopedPointer propertyReply(xcb_get_property_reply(c, propertyCookie, NULL)); if (propertyReply.isNull()) { return value; } if (propertyReply->type == XCB_ATOM_STRING && propertyReply->format == 8 && propertyReply->value_len > 0) { const char *data = (const char *) xcb_get_property_value(propertyReply.data()); int len = propertyReply->value_len; if (data) { value = QByteArray(data, data[len - 1] ? len : len - 1); } } return value; }; auto updateMenuFromWindowIfHasMenu = [this, &getWindowPropertyString](WId id) { const QString serviceName = QString::fromUtf8(getWindowPropertyString(id, s_x11AppMenuServiceNamePropertyName)); const QString menuObjectPath = QString::fromUtf8(getWindowPropertyString(id, s_x11AppMenuObjectPathPropertyName)); if (!serviceName.isEmpty() && !menuObjectPath.isEmpty()) { updateApplicationMenu(serviceName, menuObjectPath); return true; } setMenuAvailable(false); emit modelNeedsUpdate(); return false; }; KWindowInfo info(id, NET::WMState | NET::WMWindowType, NET::WM2TransientFor); - if (info.hasState(NET::SkipTaskbar) || info.windowType(NET::UtilityMask) == NET::Utility) { + if (info.hasState(NET::SkipTaskbar) || + info.windowType(NET::UtilityMask) == NET::Utility || + info.windowType(NET::DesktopMask) == NET::Desktop) { return; } WId transientId = info.transientFor(); // lok at transient windows first while (transientId) { if (updateMenuFromWindowIfHasMenu(transientId)) { return; } transientId = KWindowInfo(transientId, 0, NET::WM2TransientFor).transientFor(); } if (updateMenuFromWindowIfHasMenu(id)) { return; } } #endif } QHash AppMenuModel::roleNames() const { QHash roleNames; roleNames[MenuRole] = "activeMenu"; roleNames[ActionRole] = "activeActions"; return roleNames; } QVariant AppMenuModel::data(const QModelIndex &index, int role) const { int row = index.row(); if (row < 0 ) { return QVariant(); } if (role == MenuRole) { return m_activeMenu.at(row); } else if(role == ActionRole) { const QVariant data = qVariantFromValue((void *) m_activeActions.at(row)); return data; } return QVariant(); } void AppMenuModel::updateApplicationMenu(const QString &serviceName, const QString &menuObjectPath) { if (m_serviceName == serviceName && m_menuObjectPath == menuObjectPath) { if (m_importer) { QMetaObject::invokeMethod(m_importer, "updateMenu", Qt::QueuedConnection); } return; } m_serviceName = serviceName; m_menuObjectPath = menuObjectPath; if (m_importer) { m_importer->deleteLater(); } m_importer = new KDBusMenuImporter(serviceName, menuObjectPath, this); QMetaObject::invokeMethod(m_importer, "updateMenu", Qt::QueuedConnection); connect(m_importer.data(), &DBusMenuImporter::menuUpdated, this, [=] { m_menu = m_importer->menu(); if (m_menu.isNull()) { return; } setMenuAvailable(true); emit modelNeedsUpdate(); }); }