diff --git a/applets/appmenu/lib/appmenuapplet.cpp b/applets/appmenu/lib/appmenuapplet.cpp index 376cd1bb5..082ead3d4 100644 --- a/applets/appmenu/lib/appmenuapplet.cpp +++ b/applets/appmenu/lib/appmenuapplet.cpp @@ -1,283 +1,294 @@ /* * Copyright 2016 Kai Uwe Broulik * * 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 "appmenuapplet.h" #include "../plugin/appmenumodel.h" #include #include #include #include #include #include #include #include +#include +#include +#include #include +int AppMenuApplet::s_refs = 0; + +static const QString s_viewService(QStringLiteral("org.kde.kappmenuview")); + AppMenuApplet::AppMenuApplet(QObject *parent, const QVariantList &data) : Plasma::Applet(parent, data) { + ++s_refs; + //if we're the first, regster the service + if (s_refs == 1) { + QDBusConnection::sessionBus().interface()->registerService(s_viewService, + QDBusConnectionInterface::QueueService, + QDBusConnectionInterface::DontAllowReplacement); + } + /*it registers or unregisters the service when the destroyed value of the applet change, + and not in the dtor, because: + when we "delete" an applet, it just hides it for about a minute setting its status + to destroyed, in order to be able to do a clean undo: if we undo, there will be + another destroyedchanged and destroyed will be false. + When this happens, if we are the only appmenu applet existing, the dbus interface + will have to be registered again*/ + connect(this, &Applet::destroyedChanged, this, [this](bool destroyed) { + if (destroyed) { + //if we were the last, unregister + if (--s_refs == 0) { + QDBusConnection::sessionBus().interface()->unregisterService(s_viewService); + } + } else { + //if we're the first, regster the service + if (++s_refs == 1) { + QDBusConnection::sessionBus().interface()->registerService(s_viewService, + QDBusConnectionInterface::QueueService, + QDBusConnectionInterface::DontAllowReplacement); + } + } + }); } AppMenuApplet::~AppMenuApplet() = default; void AppMenuApplet::init() { - // TODO Wayland PlasmaShellSurface stuff - QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.kappmenu"), - QStringLiteral("/KAppMenu"), - QStringLiteral("org.kde.kappmenu"), - QStringLiteral("reconfigured"), - this, SLOT(updateAppletEnabled())); - updateAppletEnabled(); } AppMenuModel *AppMenuApplet::model() const { return m_model; } void AppMenuApplet::setModel(AppMenuModel *model) { if (m_model != model) { m_model = model; emit modelChanged(); } } int AppMenuApplet::view() const { return m_viewType; } void AppMenuApplet::setView(int type) { if (m_viewType != type) { m_viewType = type; emit viewChanged(); } } int AppMenuApplet::currentIndex() const { return m_currentIndex; } void AppMenuApplet::setCurrentIndex(int currentIndex) { if (m_currentIndex != currentIndex) { m_currentIndex = currentIndex; emit currentIndexChanged(); } } QQuickItem *AppMenuApplet::buttonGrid() const { return m_buttonGrid; } void AppMenuApplet::setButtonGrid(QQuickItem *buttonGrid) { if (m_buttonGrid != buttonGrid) { m_buttonGrid = buttonGrid; emit buttonGridChanged(); } } -bool AppMenuApplet::appletEnabled() const -{ - return m_appletEnabled; -} - -void AppMenuApplet::updateAppletEnabled() -{ - KConfigGroup config(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), QStringLiteral("Appmenu Style")); - const QString &menuStyle = config.readEntry(QStringLiteral("Style")); - - const bool enabled = (menuStyle == QLatin1String("Widget")); - - if (m_appletEnabled != enabled) { - m_appletEnabled = enabled; - emit appletEnabledChanged(); - } -} - QMenu *AppMenuApplet::createMenu(int idx) const { QMenu *menu = nullptr; QAction *action = nullptr; if (view() == CompactView) { menu = new QMenu(); for (int i=0; irowCount(); i++) { const QModelIndex index = m_model->index(i, 0); const QVariant data = m_model->data(index, AppMenuModel::ActionRole); action = (QAction *)data.value(); menu->addAction(action); } menu->setAttribute(Qt::WA_DeleteOnClose); } else if (view() == FullView) { const QModelIndex index = m_model->index(idx, 0); const QVariant data = m_model->data(index, AppMenuModel::ActionRole); action = (QAction *)data.value(); if (action) { menu = action->menu(); } } return menu; } void AppMenuApplet::onMenuAboutToHide() { setCurrentIndex(-1); } void AppMenuApplet::trigger(QQuickItem *ctx, int idx) { if (m_currentIndex == idx) { return; } if (!ctx || !ctx->window() || !ctx->window()->screen()) { return; } QMenu *actionMenu = createMenu(idx); if (actionMenu) { //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 = [ctx]() { if (ctx && ctx->window() && ctx->window()->mouseGrabberItem()) { // FIXME event forge thing enters press and hold move mode :/ ctx->window()->mouseGrabberItem()->ungrabMouse(); } }; QTimer::singleShot(0, ctx, ungrabMouseHack); //end workaround const auto &geo = ctx->window()->screen()->availableVirtualGeometry(); QPoint pos = ctx->window()->mapToGlobal(ctx->mapToScene(QPointF()).toPoint()); if (location() == Plasma::Types::TopEdge) { pos.setY(pos.y() + ctx->height()); } actionMenu->adjustSize(); pos = QPoint(qBound(geo.x(), pos.x(), geo.x() + geo.width() - actionMenu->width()), qBound(geo.y(), pos.y(), geo.y() + geo.height() - actionMenu->height())); if (view() == FullView) { actionMenu->installEventFilter(this); } setStatus(Plasma::Types::AcceptingInputStatus); actionMenu->winId();//create window handle actionMenu->windowHandle()->setTransientParent(ctx->window()); actionMenu->popup(pos); //we can return to passive immediately, an autohide panel will stay open whilst //any transient window is showing setStatus(Plasma::Types::PassiveStatus); if (view() == FullView) { // hide the old menu only after showing the new one to avoid brief flickering // in other windows as they briefly re-gain focus QMenu *oldMenu = m_currentMenu; m_currentMenu = actionMenu; if (oldMenu && oldMenu != actionMenu) { oldMenu->hide(); } } setCurrentIndex(idx); // FIXME TODO connect only once connect(actionMenu, &QMenu::aboutToHide, this, &AppMenuApplet::onMenuAboutToHide, Qt::UniqueConnection); return; } } // FIXME TODO doesn't work on submenu bool AppMenuApplet::eventFilter(QObject *watched, QEvent *event) { auto *menu = qobject_cast(watched); if (!menu) { return false; } if (event->type() == QEvent::KeyPress) { auto *e = static_cast(event); // TODO right to left languages if (e->key() == Qt::Key_Left) { int desiredIndex = m_currentIndex - 1; emit requestActivateIndex(desiredIndex); return true; } else if (e->key() == Qt::Key_Right) { if (menu->activeAction() && menu->activeAction()->menu()) { return false; } int desiredIndex = m_currentIndex + 1; emit requestActivateIndex(desiredIndex); return true; } } else if (event->type() == QEvent::MouseMove) { auto *e = static_cast(event); if (!m_buttonGrid || !m_buttonGrid->window()) { return false; } // FIXME the panel margin breaks Fitt's law :( const QPointF &windowLocalPos = m_buttonGrid->window()->mapFromGlobal(e->globalPos()); const QPointF &buttonGridLocalPos = m_buttonGrid->mapFromScene(windowLocalPos); auto *item = m_buttonGrid->childAt(buttonGridLocalPos.x(), buttonGridLocalPos.y()); if (!item) { return false; } bool ok; const int buttonIndex = item->property("buttonIndex").toInt(&ok); if (!ok) { return false; } emit requestActivateIndex(buttonIndex); } return false; } K_EXPORT_PLASMA_APPLET_WITH_JSON(appmenu, AppMenuApplet, "metadata.json") #include "appmenuapplet.moc" diff --git a/applets/appmenu/lib/appmenuapplet.h b/applets/appmenu/lib/appmenuapplet.h index c6a870f6c..0633c322e 100644 --- a/applets/appmenu/lib/appmenuapplet.h +++ b/applets/appmenu/lib/appmenuapplet.h @@ -1,97 +1,91 @@ /* * Copyright 2016 Kai Uwe Broulik * * 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 . * */ #pragma once #include #include class KDBusMenuImporter; class QQuickItem; class QMenu; class AppMenuModel; class AppMenuApplet : public Plasma::Applet { Q_OBJECT - Q_PROPERTY(bool appletEnabled READ appletEnabled NOTIFY appletEnabledChanged) - Q_PROPERTY(AppMenuModel* model READ model WRITE setModel NOTIFY modelChanged) Q_PROPERTY(int view READ view WRITE setView NOTIFY viewChanged) Q_PROPERTY(int currentIndex READ currentIndex NOTIFY currentIndexChanged) Q_PROPERTY(QQuickItem *buttonGrid READ buttonGrid WRITE setButtonGrid NOTIFY buttonGridChanged) public: enum ViewType { FullView, CompactView }; explicit AppMenuApplet(QObject *parent, const QVariantList &data); ~AppMenuApplet() override; void init() override; int currentIndex() const; QQuickItem *buttonGrid() const; void setButtonGrid(QQuickItem *buttonGrid); AppMenuModel *model() const; void setModel(AppMenuModel *model); int view() const; void setView(int type); - bool appletEnabled() const; - signals: void modelChanged(); void viewChanged(); void currentIndexChanged(); void buttonGridChanged(); - void appletEnabledChanged(); void requestActivateIndex(int index); public slots: - void updateAppletEnabled(); void trigger(QQuickItem *ctx, int idx); protected: bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE; private: QMenu *createMenu(int idx) const; void setCurrentIndex(int currentIndex); void onMenuAboutToHide(); int m_currentIndex = -1; int m_viewType = FullView; - bool m_appletEnabled = true; QPointer m_currentMenu; QPointer m_buttonGrid; QPointer m_model; + static int s_refs; }; diff --git a/applets/appmenu/package/contents/ui/main.qml b/applets/appmenu/package/contents/ui/main.qml index c89f833d7..83c648731 100644 --- a/applets/appmenu/package/contents/ui/main.qml +++ b/applets/appmenu/package/contents/ui/main.qml @@ -1,121 +1,110 @@ /* * Copyright 2013 Heena Mahour * Copyright 2013 Sebastian Kügler * Copyright 2016 Kai Uwe Broulik * * 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, see . */ import QtQuick 2.0 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 import org.kde.plasma.plasmoid 2.0 import org.kde.kquickcontrolsaddons 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.private.appmenu 1.0 as AppMenuPrivate Item { id: root readonly property bool vertical: plasmoid.formFactor === PlasmaCore.Types.Vertical - readonly property bool appletEnabled: plasmoid.nativeInterface.appletEnabled readonly property bool view: plasmoid.configuration.compactView readonly property bool menuAvailable: appMenuModel.menuAvailable readonly property bool kcmAuthorized: KCMShell.authorize(["style.desktop"]).length > 0 onViewChanged: { plasmoid.nativeInterface.view = view } - Plasmoid.preferredRepresentation: (plasmoid.configuration.compactView || vertical || !appletEnabled) ? Plasmoid.compactRepresentation : Plasmoid.fullRepresentation + Plasmoid.preferredRepresentation: (plasmoid.configuration.compactView || vertical) ? Plasmoid.compactRepresentation : Plasmoid.fullRepresentation Plasmoid.compactRepresentation: PlasmaComponents.ToolButton { readonly property int fakeIndex: 0 Layout.fillWidth: false Layout.fillHeight: false Layout.minimumWidth: implicitWidth Layout.maximumWidth: implicitWidth - enabled: appletEnabled ? menuAvailable : kcmAuthorized - checkable: appletEnabled && menuAvailable && plasmoid.nativeInterface.currentIndex === fakeIndex + enabled: menuAvailable + checkable: menuAvailable && plasmoid.nativeInterface.currentIndex === fakeIndex checked: checkable - iconSource: appletEnabled ? "application-menu" : "emblem-warning" - onClicked: { - if (appletEnabled) { - plasmoid.nativeInterface.trigger(this, 0); - } else { - KCMShell.open("style") - } - } + iconSource: i18n("application-menu") + onClicked: plasmoid.nativeInterface.trigger(this, 0); } Plasmoid.fullRepresentation: GridLayout { id: buttonGrid //when we're not enabled set to active to show the configure button - Plasmoid.status: !appletEnabled || buttonRepeater.count > 0 ? + Plasmoid.status: buttonRepeater.count > 0 ? PlasmaCore.Types.ActiveStatus : PlasmaCore.Types.HiddenStatus Layout.minimumWidth: implicitWidth Layout.minimumHeight: implicitHeight flow: root.vertical ? GridLayout.TopToBottom : GridLayout.LeftToRight rowSpacing: units.smallSpacing columnSpacing: units.smallSpacing Component.onCompleted: { plasmoid.nativeInterface.buttonGrid = buttonGrid // using a Connections {} doesn't work for some reason in Qt >= 5.8 plasmoid.nativeInterface.requestActivateIndex.connect(function (index) { var idx = Math.max(0, Math.min(buttonRepeater.count - 1, index)) var button = buttonRepeater.itemAt(index) if (button) { button.clicked() } }); } Repeater { id: buttonRepeater model: appMenuModel PlasmaComponents.ToolButton { readonly property int buttonIndex: index Layout.preferredWidth: minimumWidth Layout.fillWidth: root.vertical Layout.fillHeight: !root.vertical text: activeMenu // fake highlighted checkable: plasmoid.nativeInterface.currentIndex === index checked: checkable onClicked: { plasmoid.nativeInterface.trigger(this, index) } } } } - Plasmoid.toolTipMainText: appletEnabled ? "" : i18n("Application Menu Widget is disabled") - Plasmoid.toolTipSubText: appletEnabled || !root.kcmAuthorized ? "" - : i18nc("it being the 'Application Menu Widget'", "Go to System Settings > Application Style > Fine Tuning (tab) to enable it."); - AppMenuPrivate.AppMenuModel { id: appMenuModel onRequestActivateIndex: plasmoid.nativeInterface.requestActivateIndex(index) Component.onCompleted: { plasmoid.nativeInterface.model = appMenuModel } } } diff --git a/appmenu/appmenu.cpp b/appmenu/appmenu.cpp index 64f054798..fd643be8a 100644 --- a/appmenu/appmenu.cpp +++ b/appmenu/appmenu.cpp @@ -1,209 +1,211 @@ /* This file is part of the KDE project. Copyright (c) 2011 Lionel Chauvin Copyright (c) 2011,2012 Cédric Bellegarde Copyright (c) 2016 Kai Uwe Broulik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "appmenu.h" #include "kdbusimporter.h" #include "menuimporteradaptor.h" #include "appmenuadaptor.h" #include "appmenu_dbus.h" #include "verticalmenu.h" #include #include #include #include #include #include #include #include #include #if HAVE_X11 #include #include #endif static const QByteArray s_x11AppMenuServiceNamePropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME"); static const QByteArray s_x11AppMenuObjectPathPropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH"); K_PLUGIN_FACTORY_WITH_JSON(AppMenuFactory, "appmenu.json", registerPlugin();) AppMenuModule::AppMenuModule(QObject* parent, const QList&) : KDEDModule(parent), m_appmenuDBus(new AppmenuDBus(this)) { reconfigure(); m_appmenuDBus->connectToBus(); connect(m_appmenuDBus, &AppmenuDBus::appShowMenu, this, &AppMenuModule::slotShowMenu); connect(m_appmenuDBus, &AppmenuDBus::reconfigured, this, &AppMenuModule::reconfigure); // transfer our signals to dbus connect(this, &AppMenuModule::showRequest, m_appmenuDBus, &AppmenuDBus::showRequest); connect(this, &AppMenuModule::menuHidden, m_appmenuDBus, &AppmenuDBus::menuHidden); connect(this, &AppMenuModule::menuShown, m_appmenuDBus, &AppmenuDBus::menuShown); + + m_menuViewWatcher = new QDBusServiceWatcher(QStringLiteral("org.kde.kappmenuview"), QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForRegistration|QDBusServiceWatcher::WatchForUnregistration, this); + + auto setupMenuImporter = [this]() { + QDBusConnection::sessionBus().connect({}, {}, QStringLiteral("com.canonical.dbusmenu"), + QStringLiteral("ItemActivationRequested"), + this, SLOT(itemActivationRequested(int,uint))); + + // Setup a menu importer if needed + if (!m_menuImporter) { + m_menuImporter = new MenuImporter(this); + connect(m_menuImporter, &MenuImporter::WindowRegistered, this, &AppMenuModule::slotWindowRegistered); + m_menuImporter->connectToBus(); + } + }; + connect(m_menuViewWatcher, &QDBusServiceWatcher::serviceRegistered, this, setupMenuImporter); + connect(m_menuViewWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this](const QString &service) { + Q_UNUSED(service) + QDBusConnection::sessionBus().disconnect({}, {}, QStringLiteral("com.canonical.dbusmenu"), + QStringLiteral("ItemActivationRequested"), + this, SLOT(itemActivationRequested(int,uint))); + delete m_menuImporter; + m_menuImporter = nullptr; + }); + + if (QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.kappmenuview"))) { + setupMenuImporter(); + } } AppMenuModule::~AppMenuModule() = default; void AppMenuModule::slotWindowRegistered(WId id, const QString &serviceName, const QDBusObjectPath &menuObjectPath) { #ifdef HAVE_X11 if (KWindowSystem::isPlatformX11()) { auto *c = QX11Info::connection(); static xcb_atom_t s_serviceNameAtom = XCB_ATOM_NONE; static xcb_atom_t s_objectPathAtom = XCB_ATOM_NONE; auto setWindowProperty = [c](WId id, xcb_atom_t &atom, const QByteArray &name, const QByteArray &value) { if (atom == XCB_ATOM_NONE) { const xcb_intern_atom_cookie_t cookie = xcb_intern_atom(c, false, name.length(), name.constData()); QScopedPointer reply(xcb_intern_atom_reply(c, cookie, Q_NULLPTR)); if (reply.isNull()) { return; } atom = reply->atom; if (atom == XCB_ATOM_NONE) { return; } } xcb_change_property(c, XCB_PROP_MODE_REPLACE, id, atom, XCB_ATOM_STRING, 8, value.length(), value.constData()); }; // TODO only set the property if it doesn't already exist setWindowProperty(id, s_serviceNameAtom, s_x11AppMenuServiceNamePropertyName, serviceName.toUtf8()); setWindowProperty(id, s_objectPathAtom, s_x11AppMenuObjectPathPropertyName, menuObjectPath.path().toUtf8()); } #endif } void AppMenuModule::slotShowMenu(int x, int y, const QString &serviceName, const QDBusObjectPath &menuObjectPath, int actionId) { if (!m_menuImporter) { return; } // If menu visible, hide it if (m_menu && m_menu.data()->isVisible()) { m_menu.data()->hide(); return; } //dbus call by user (for khotkey shortcut) if (x == -1 || y == -1) { // We do not know kwin button position, so tell kwin to show menu emit showRequest(serviceName, menuObjectPath, actionId); return; } auto *importer = new KDBusMenuImporter(serviceName, menuObjectPath.path(), this); QMetaObject::invokeMethod(importer, "updateMenu", Qt::QueuedConnection); disconnect(importer, 0, this, 0); // ensure we don't popup multiple times in case the menu updates again later connect(importer, &KDBusMenuImporter::menuUpdated, this, [=](QMenu *m) { QMenu *menu = importer->menu(); if (!menu || menu != m) { return; } m_menu = qobject_cast(menu); m_menu.data()->setServiceName(serviceName); m_menu.data()->setMenuObjectPath(menuObjectPath); connect(m_menu.data(), &QMenu::aboutToHide, this, [this, importer] { hideMenu(); importer->deleteLater(); }); //m_menuImporter->fakeUnityAboutToShow(serviceName, menuObjectPath); m_menu.data()->popup(QPoint(x, y) / qApp->devicePixelRatio()); QAction *actiontoActivate = importer->actionForId(actionId); emit menuShown(serviceName, menuObjectPath); if (actiontoActivate) { m_menu.data()->setActiveAction(actiontoActivate); } }); } void AppMenuModule::hideMenu() { if (m_menu) { emit menuHidden(m_menu.data()->serviceName(), m_menu->menuObjectPath()); } } void AppMenuModule::itemActivationRequested(int actionId, uint timeStamp) { Q_UNUSED(timeStamp); emit showRequest(message().service(), QDBusObjectPath(message().path()), actionId); } -// reload settings +// this method is not really used anymore but has to be kept for DBus compatibility void AppMenuModule::reconfigure() { - hideMenu(); // hide window decoration menu if exists - - KConfigGroup config(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), QStringLiteral("Appmenu Style")); - const QString &menuStyle = config.readEntry("Style", "InApplication"); - // TODO enum or Kconfigxt or what not? - if (menuStyle == QLatin1String("Decoration")) { - QDBusConnection::sessionBus().connect({}, {}, QStringLiteral("com.canonical.dbusmenu"), - QStringLiteral("ItemActivationRequested"), - this, SLOT(itemActivationRequested(int,uint))); - } else { - QDBusConnection::sessionBus().disconnect({}, {}, QStringLiteral("com.canonical.dbusmenu"), - QStringLiteral("ItemActivationRequested"), - this, SLOT(itemActivationRequested(int,uint))); - } - - if (menuStyle == QLatin1String("InApplication")) { - delete m_menuImporter; - m_menuImporter = nullptr; - return; - } - - // Setup a menu importer if needed - if (!m_menuImporter) { - m_menuImporter = new MenuImporter(this); - connect(m_menuImporter, &MenuImporter::WindowRegistered, this, &AppMenuModule::slotWindowRegistered); - m_menuImporter->connectToBus(); - } } #include "appmenu.moc" diff --git a/appmenu/appmenu.h b/appmenu/appmenu.h index 3369c5120..338e515e5 100644 --- a/appmenu/appmenu.h +++ b/appmenu/appmenu.h @@ -1,97 +1,99 @@ /* This file is part of the KDE project. Copyright (c) 2011 Lionel Chauvin Copyright (c) 2011,2012 Cédric Bellegarde Copyright (c) 2016 Kai Uwe Broulik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef APPMENUMODULE_H #define APPMENUMODULE_H #include #include #include "menuimporter.h" class QDBusPendingCallWatcher; +class QDBusServiceWatcher; class KDBusMenuImporter; class AppmenuDBus; class TopMenuBar; class VerticalMenu; class AppMenuModule : public KDEDModule, protected QDBusContext { Q_OBJECT public: AppMenuModule(QObject* parent, const QList& list); ~AppMenuModule() override; Q_SIGNALS: /** * We do not know where is menu decoration button, so tell kwin to show menu */ void showRequest(const QString &serviceName, const QDBusObjectPath &menuObjectPath, int actionId); /** * This signal is emitted whenever popup menu/menubar is shown * Useful for decorations to know if menu button should look pressed */ void menuShown(const QString &service, const QDBusObjectPath &objectPath); /** * This signal is emitted whenever popup menu/menubar is hidden * Useful for decorations to know if menu button should be release */ void menuHidden(const QString &service, const QDBusObjectPath &objectPath); private Q_SLOTS: /** * A new window was registered to AppMenu * * For compatibility this will set the DBus service name and menu object path as properties * on the window so we keep working with clients that use the DBusMenu "properly". */ void slotWindowRegistered(WId id, const QString &serviceName, const QDBusObjectPath &menuObjectPath); /** * Show menu at QPoint(x,y) for DBus serviceName and menuObjectPath * if x or y == -1, show in application window */ void slotShowMenu(int x, int y, const QString &serviceName, const QDBusObjectPath &menuObjectPath, int actionId); /** * Reconfigure module */ void reconfigure(); void itemActivationRequested(int actionId, uint timeStamp); private: void hideMenu(); void fakeUnityAboutToShow(const QString &service, const QDBusObjectPath &menuObjectPath); KDBusMenuImporter *getImporter(const QString &service, const QString &path); MenuImporter *m_menuImporter = nullptr; AppmenuDBus *m_appmenuDBus; + QDBusServiceWatcher *m_menuViewWatcher; QPointer m_menu; }; #endif