File plasma-512.patch of Package plasmoid-active-window-control

From 41cf4067b90ef1e47e9a7f744009a6dd9516910f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Kostoln=C3=BD?= <clearmartin@zoho.com>
Date: Wed, 10 Jan 2018 22:50:29 +0100
Subject: [PATCH] C++ plugin along with copied dbusmenuqt and appmenumodel
 updated

added INSTALL file with install instructions
---
 INSTALL                                            |   7 ++
 lib/CMakeLists.txt                                 |   6 +-
 lib/TODO                                           |   0
 ...vewindowcontrolapplet.cpp => appmenuapplet.cpp} | 137 +++++++++++++--------
 ...activewindowcontrolapplet.h => appmenuapplet.h} |  16 +--
 plugin/CMakeLists.txt                              |  32 ++---
 plugin/appmenumodel.cpp                            |  76 ++++++++++--
 plugin/appmenumodel.h                              |  19 ++-
 ...vewindowcontrolplugin.cpp => appmenuplugin.cpp} |   4 +-
 ...activewindowcontrolplugin.h => appmenuplugin.h} |   6 +-
 plugin/libdbusmenuqt/CMakeLists.txt                |   2 -
 plugin/libdbusmenuqt/dbusmenuimporter.cpp          |  18 ++-
 plugin/libdbusmenuqt/dbusmenuimporter.h            |   3 +
 plugin/libdbusmenuqt/test/CMakeLists.txt           |   3 -
 plugin/libdbusmenuqt/test/README                   |   2 -
 plugin/libdbusmenuqt/test/main.cpp                 |  84 -------------
 plugin/qmldir                                      |   2 +-
 17 files changed, 218 insertions(+), 199 deletions(-)
 create mode 100644 INSTALL
 delete mode 100644 lib/TODO
 rename lib/{activewindowcontrolapplet.cpp => appmenuapplet.cpp} (58%)
 rename lib/{activewindowcontrolapplet.h => appmenuapplet.h} (83%)
 rename plugin/{activewindowcontrolplugin.cpp => appmenuplugin.cpp} (92%)
 rename plugin/{activewindowcontrolplugin.h => appmenuplugin.h} (90%)
 delete mode 100644 plugin/libdbusmenuqt/test/CMakeLists.txt
 delete mode 100644 plugin/libdbusmenuqt/test/README
 delete mode 100644 plugin/libdbusmenuqt/test/main.cpp

diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..f991263
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,7 @@
+INSTALATION INSTRUCTIONS
+
+$ mkdir build
+$ cd build
+$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr
+$ make
+$ make install
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index fd02426..48e9f27 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -1,8 +1,8 @@
-set(activewindowcontrolapplet_SRCS
-    activewindowcontrolapplet.cpp
+set(appmenuapplet_SRCS
+    appmenuapplet.cpp
 )
 
-add_library(plasma_applet_activewindowcontrol MODULE ${activewindowcontrolapplet_SRCS})
+add_library(plasma_applet_activewindowcontrol MODULE ${appmenuapplet_SRCS})
 
 kcoreaddons_desktop_to_json(plasma_applet_activewindowcontrol ../package/metadata.desktop)
 
diff --git a/lib/TODO b/lib/TODO
deleted file mode 100644
index e69de29..0000000
diff --git a/lib/activewindowcontrolapplet.cpp b/lib/appmenuapplet.cpp
similarity index 58%
rename from lib/activewindowcontrolapplet.cpp
rename to lib/appmenuapplet.cpp
index 005a385..082ead3 100644
--- a/lib/activewindowcontrolapplet.cpp
+++ b/lib/appmenuapplet.cpp
@@ -19,7 +19,7 @@
  *
  */
 
-#include "activewindowcontrolapplet.h"
+#include "appmenuapplet.h"
 #include "../plugin/appmenumodel.h"
 
 #include <QAction>
@@ -30,31 +30,61 @@
 #include <QQuickWindow>
 #include <QScreen>
 #include <QDBusConnection>
+#include <QDBusMessage>
+#include <QDBusPendingCall>
+#include <QDBusConnectionInterface>
+#include <QTimer>
 
-ActiveWindowControlApplet::ActiveWindowControlApplet(QObject *parent, const QVariantList &data)
+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);
+            }
+        }
+    });
 }
 
-ActiveWindowControlApplet::~ActiveWindowControlApplet() = default;
+AppMenuApplet::~AppMenuApplet() = default;
 
-void ActiveWindowControlApplet::init()
+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 *ActiveWindowControlApplet::model() const
+AppMenuModel *AppMenuApplet::model() const
 {
     return m_model;
 }
 
-void ActiveWindowControlApplet::setModel(AppMenuModel *model)
+void AppMenuApplet::setModel(AppMenuModel *model)
 {
     if (m_model != model) {
         m_model = model;
@@ -62,12 +92,12 @@ void ActiveWindowControlApplet::setModel(AppMenuModel *model)
     }
 }
 
-int ActiveWindowControlApplet::view() const
+int AppMenuApplet::view() const
 {
     return m_viewType;
 }
 
-void ActiveWindowControlApplet::setView(int type)
+void AppMenuApplet::setView(int type)
 {
     if (m_viewType != type) {
         m_viewType = type;
@@ -75,12 +105,12 @@ void ActiveWindowControlApplet::setView(int type)
     }
 }
 
-int ActiveWindowControlApplet::currentIndex() const
+int AppMenuApplet::currentIndex() const
 {
     return m_currentIndex;
 }
 
-void ActiveWindowControlApplet::setCurrentIndex(int currentIndex)
+void AppMenuApplet::setCurrentIndex(int currentIndex)
 {
     if (m_currentIndex != currentIndex) {
         m_currentIndex = currentIndex;
@@ -88,12 +118,12 @@ void ActiveWindowControlApplet::setCurrentIndex(int currentIndex)
    }
 }
 
-QQuickItem *ActiveWindowControlApplet::buttonGrid() const
+QQuickItem *AppMenuApplet::buttonGrid() const
 {
     return m_buttonGrid;
 }
 
-void ActiveWindowControlApplet::setButtonGrid(QQuickItem *buttonGrid)
+void AppMenuApplet::setButtonGrid(QQuickItem *buttonGrid)
 {
     if (m_buttonGrid != buttonGrid) {
         m_buttonGrid = buttonGrid;
@@ -101,34 +131,11 @@ void ActiveWindowControlApplet::setButtonGrid(QQuickItem *buttonGrid)
     }
 }
 
-bool ActiveWindowControlApplet::appletEnabled() const
-{
-    return m_appletEnabled;
-}
-
-void ActiveWindowControlApplet::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 *ActiveWindowControlApplet::createMenu(int idx) const
+QMenu *AppMenuApplet::createMenu(int idx) const
 {
     QMenu *menu = nullptr;
     QAction *action = nullptr;
 
-    if (!m_model) {
-        qDebug() << "model not available";
-        return menu;
-    }
-
     if (view() == CompactView) {
        menu = new QMenu();
        for (int i=0; i<m_model->rowCount(); i++) {
@@ -150,24 +157,40 @@ QMenu *ActiveWindowControlApplet::createMenu(int idx) const
     return menu;
 }
 
-void ActiveWindowControlApplet::onMenuAboutToHide()
+void AppMenuApplet::onMenuAboutToHide()
 {
     setCurrentIndex(-1);
 }
 
-void ActiveWindowControlApplet::trigger(QQuickItem *ctx, int idx)
+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) {
 
-        if (ctx && ctx->window() && ctx->window()->mouseGrabberItem()) {
-            // FIXME event forge thing enters press and hold move mode :/
-            ctx->window()->mouseGrabberItem()->ungrabMouse();
-        }
+        //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();
 
@@ -185,8 +208,16 @@ void ActiveWindowControlApplet::trigger(QQuickItem *ctx, int idx)
             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
@@ -200,13 +231,13 @@ void ActiveWindowControlApplet::trigger(QQuickItem *ctx, int idx)
         setCurrentIndex(idx);
 
         // FIXME TODO connect only once
-        connect(actionMenu, &QMenu::aboutToHide, this, &ActiveWindowControlApplet::onMenuAboutToHide, Qt::UniqueConnection);
+        connect(actionMenu, &QMenu::aboutToHide, this, &AppMenuApplet::onMenuAboutToHide, Qt::UniqueConnection);
         return;
     }
 }
 
 // FIXME TODO doesn't work on submenu
-bool ActiveWindowControlApplet::eventFilter(QObject *watched, QEvent *event)
+bool AppMenuApplet::eventFilter(QObject *watched, QEvent *event)
 {
     auto *menu = qobject_cast<QMenu *>(watched);
     if (!menu) {
@@ -258,6 +289,6 @@ bool ActiveWindowControlApplet::eventFilter(QObject *watched, QEvent *event)
     return false;
 }
 
-K_EXPORT_PLASMA_APPLET_WITH_JSON(activewindowcontrol, ActiveWindowControlApplet, "metadata.json")
+K_EXPORT_PLASMA_APPLET_WITH_JSON(appmenu, AppMenuApplet, "metadata.json")
 
-#include "activewindowcontrolapplet.moc"
+#include "appmenuapplet.moc"
diff --git a/lib/activewindowcontrolapplet.h b/lib/appmenuapplet.h
similarity index 83%
rename from lib/activewindowcontrolapplet.h
rename to lib/appmenuapplet.h
index 8f8c2df..0633c32 100644
--- a/lib/activewindowcontrolapplet.h
+++ b/lib/appmenuapplet.h
@@ -29,12 +29,10 @@ class QQuickItem;
 class QMenu;
 class AppMenuModel;
 
-class ActiveWindowControlApplet : public Plasma::Applet
+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)
@@ -49,8 +47,8 @@ class ActiveWindowControlApplet : public Plasma::Applet
         CompactView
     };
 
-    explicit ActiveWindowControlApplet(QObject *parent, const QVariantList &data);
-    ~ActiveWindowControlApplet() override;
+    explicit AppMenuApplet(QObject *parent, const QVariantList &data);
+    ~AppMenuApplet() override;
 
     void init() override;
 
@@ -65,22 +63,18 @@ class ActiveWindowControlApplet : public Plasma::Applet
     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);
+    bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE;
 
 private:
     QMenu *createMenu(int idx) const;
@@ -90,8 +84,8 @@ public slots:
 
     int m_currentIndex = -1;
     int m_viewType = FullView;
-    bool m_appletEnabled = true;
     QPointer<QMenu> m_currentMenu;
     QPointer<QQuickItem> m_buttonGrid;
     QPointer<AppMenuModel> m_model;
+    static int s_refs;
 };
diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt
index 8f855ac..1a4efb9 100644
--- a/plugin/CMakeLists.txt
+++ b/plugin/CMakeLists.txt
@@ -1,27 +1,24 @@
-set(activewindowcontrolplugin_SRCS
+set(appmenuapplet_SRCS
     appmenumodel.cpp
-    activewindowcontrolplugin.cpp
-    )
+    appmenuplugin.cpp
+)
 
-add_library(activewindowcontrolplugin SHARED ${activewindowcontrolplugin_SRCS})
-
-cmake_policy(SET CMP0053 NEW)
-cmake_policy(SET CMP0063 NEW)
+add_library(appmenuplugin SHARED ${appmenuapplet_SRCS})
 
+# load dbusmenuqt
 find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Widgets DBus)
-
+cmake_policy(SET CMP0063 NEW)
 add_subdirectory(libdbusmenuqt)
 
-target_link_libraries(activewindowcontrolplugin
+target_link_libraries(appmenuplugin
                       Qt5::Core
                       Qt5::Widgets
                       Qt5::Quick
-                      Qt5::DBus
                       KF5::Plasma
                       KF5::WindowSystem
-                      dbusmenuqt
-                      )
+                      dbusmenuqt)
 
+# load X11 libraries
 find_package(X11)
 set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries"
                         URL "http://www.x.org"
@@ -45,15 +42,12 @@ endif()
 configure_file(config-X11.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-X11.h)
 
 if(HAVE_X11)
-    find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED X11Extras)
-    find_package(XCB MODULE REQUIRED COMPONENTS XCB RANDR)
+    find_package(XCB MODULE REQUIRED COMPONENTS XCB)
     set_package_properties(XCB PROPERTIES TYPE REQUIRED)
-    target_link_libraries(activewindowcontrolplugin
-                          Qt5::X11Extras
-                          XCB::XCB
-                          )
+    target_link_libraries(appmenuplugin Qt5::X11Extras XCB::XCB)
 endif()
 
-install(TARGETS activewindowcontrolplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/private/activeWindowControl)
+# install plugin
+install(TARGETS appmenuplugin DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/private/activeWindowControl)
 
 install(FILES qmldir DESTINATION ${KDE_INSTALL_QMLDIR}/org/kde/private/activeWindowControl)
diff --git a/plugin/appmenumodel.cpp b/plugin/appmenumodel.cpp
index 9d7d5e8..3b41ec6 100644
--- a/plugin/appmenumodel.cpp
+++ b/plugin/appmenumodel.cpp
@@ -25,22 +25,28 @@
 
 #include <config-X11.h>
 
-#ifdef HAVE_X11
+#if HAVE_X11
 #include <QX11Info>
 #include <xcb/xcb.h>
 #endif
 
 #include <QAction>
+#include <QGuiApplication>
 #include <QMenu>
 #include <QDebug>
 #include <QDBusConnection>
 #include <QDBusConnectionInterface>
+#include <QDBusServiceWatcher>
 
 #include <dbusmenuimporter.h>
 
 static const QByteArray s_x11AppMenuServiceNamePropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME");
 static const QByteArray s_x11AppMenuObjectPathPropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH");
 
+#if HAVE_X11
+static QHash<QByteArray, xcb_atom_t> s_atoms;
+#endif
+
 class KDBusMenuImporter : public DBusMenuImporter
 {
 
@@ -60,17 +66,19 @@ class KDBusMenuImporter : public DBusMenuImporter
 };
 
 AppMenuModel::AppMenuModel(QObject *parent)
-            : QAbstractListModel(parent)
+            : QAbstractListModel(parent),
+              m_serviceWatcher(new QDBusServiceWatcher(this))
 {
     connect(KWindowSystem::self(), &KWindowSystem::activeWindowChanged, this, &AppMenuModel::onActiveWindowChanged);
     connect(this, &AppMenuModel::modelNeedsUpdate, this, &AppMenuModel::update, Qt::UniqueConnection);
     onActiveWindowChanged(KWindowSystem::activeWindow());
 
+    m_serviceWatcher->setConnection(QDBusConnection::sessionBus());
     //if our current DBus connection gets lost, close the menu
     //we'll select the new menu when the focus changes
-    connect(QDBusConnection::sessionBus().interface(), &QDBusConnectionInterface::serviceOwnerChanged, this, [this](const QString &serviceName, const QString &oldOwner, const QString &newOwner)
+    connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this](const QString &serviceName)
     {
-        if (serviceName == m_serviceName && newOwner.isEmpty()) {
+        if (serviceName == m_serviceName) {
             setMenuAvailable(false);
             emit modelNeedsUpdate();
         }
@@ -120,12 +128,18 @@ void AppMenuModel::update()
 
 void AppMenuModel::onActiveWindowChanged(WId id)
 {
-#ifdef HAVE_X11
+    qApp->removeNativeEventFilter(this);
+
+    if (!id) {
+        setMenuAvailable(false);
+        emit modelNeedsUpdate();
+        return;
+    }
+
+#if HAVE_X11
     if (KWindowSystem::isPlatformX11()) {
         auto *c = QX11Info::connection();
 
-        static QHash<QByteArray, xcb_atom_t> s_atoms;
-
         auto getWindowPropertyString = [c, this](WId id, const QByteArray &name) -> QByteArray {
             QByteArray value;
             if (!s_atoms.contains(name)) {
@@ -190,6 +204,11 @@ void AppMenuModel::onActiveWindowChanged(WId id)
             return;
         }
 
+        // monitor whether an app menu becomes available later
+        // this can happen when an app starts, shows its window, and only later announces global menu (e.g. Firefox)
+        qApp->installNativeEventFilter(this);
+        m_currentWindowId = id;
+
         //no menu found, set it to unavailable
         setMenuAvailable(false);
         emit modelNeedsUpdate();
@@ -234,6 +253,8 @@ void AppMenuModel::updateApplicationMenu(const QString &serviceName, const QStri
     }
 
     m_serviceName = serviceName;
+    m_serviceWatcher->setWatchedServices(QStringList({m_serviceName}));
+
     m_menuObjectPath = menuObjectPath;
 
     if (m_importer) {
@@ -259,5 +280,46 @@ void AppMenuModel::updateApplicationMenu(const QString &serviceName, const QStri
         setMenuAvailable(true);
         emit modelNeedsUpdate();
     });
+
+    connect(m_importer.data(), &DBusMenuImporter::actionActivationRequested, this, [this](QAction *action) {
+        // TODO submenus
+        auto it = std::find(m_activeActions.constBegin(), m_activeActions.constEnd(), action);
+        if (it != m_activeActions.constEnd()) {
+            requestActivateIndex(it - m_activeActions.constBegin());
+        }
+    });
+}
+
+bool AppMenuModel::nativeEventFilter(const QByteArray &eventType, void *message, long *result)
+{
+    Q_UNUSED(result);
+
+    if (!KWindowSystem::isPlatformX11() || eventType != "xcb_generic_event_t") {
+        return false;
+    }
+
+#if HAVE_X11
+    auto e = static_cast<xcb_generic_event_t *>(message);
+    const uint8_t type = e->response_type & ~0x80;
+    if (type == XCB_PROPERTY_NOTIFY) {
+        auto *event = reinterpret_cast<xcb_property_notify_event_t *>(e);
+        if (event->window == m_currentWindowId) {
+
+            auto serviceNameAtom = s_atoms.value(s_x11AppMenuServiceNamePropertyName);
+            auto objectPathAtom = s_atoms.value(s_x11AppMenuObjectPathPropertyName);
+
+            if (serviceNameAtom != XCB_ATOM_NONE && objectPathAtom != XCB_ATOM_NONE) { // shouldn't happen
+                if (event->atom == serviceNameAtom || event->atom == objectPathAtom) {
+                    // see if we now have a menu
+                    onActiveWindowChanged(KWindowSystem::activeWindow());
+                }
+            }
+        }
+    }
+#else
+    Q_UNUSED(message);
+#endif
+
+    return false;
 }
 
diff --git a/plugin/appmenumodel.h b/plugin/appmenumodel.h
index 77ad5e9..8d7ef92 100644
--- a/plugin/appmenumodel.h
+++ b/plugin/appmenumodel.h
@@ -20,6 +20,7 @@
  ******************************************************************/
 
 #include <QAbstractListModel>
+#include <QAbstractNativeEventFilter>
 #include <QStringList>
 #include <KWindowSystem>
 #include <QPointer>
@@ -27,9 +28,10 @@
 class QMenu;
 class QAction;
 class QModelIndex;
+class QDBusServiceWatcher;
 class KDBusMenuImporter;
 
-class AppMenuModel : public QAbstractListModel
+class AppMenuModel : public QAbstractListModel, public QAbstractNativeEventFilter
 {
     Q_OBJECT
 
@@ -44,15 +46,21 @@ class AppMenuModel : public QAbstractListModel
         ActionRole
     };
 
-    QVariant data(const QModelIndex &index, int role) const;
-    int rowCount(const QModelIndex &parent = QModelIndex()) const;
-    QHash<int, QByteArray> roleNames() const;
+    QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
+    int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
+    QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
 
     void updateApplicationMenu(const QString &serviceName, const QString &menuObjectPath);
 
     bool menuAvailable() const;
     void setMenuAvailable(bool set);
 
+signals:
+    void requestActivateIndex(int index);
+
+protected:
+    bool nativeEventFilter(const QByteArray &eventType, void *message, long int *result) override;
+
 private Q_SLOTS:
     void onActiveWindowChanged(WId id);
     void update();
@@ -64,10 +72,13 @@ private Q_SLOTS:
 private:
     bool m_menuAvailable;
 
+    WId m_currentWindowId = 0;
+
     QPointer<QMenu> m_menu;
     QStringList m_activeMenu;
     QList<QAction *> m_activeActions;
 
+    QDBusServiceWatcher *m_serviceWatcher;
     QString m_serviceName;
     QString m_menuObjectPath;
 
diff --git a/plugin/activewindowcontrolplugin.cpp b/plugin/appmenuplugin.cpp
similarity index 92%
rename from plugin/activewindowcontrolplugin.cpp
rename to plugin/appmenuplugin.cpp
index a5fe7de..76535cb 100644
--- a/plugin/activewindowcontrolplugin.cpp
+++ b/plugin/appmenuplugin.cpp
@@ -19,13 +19,13 @@
  *
  ******************************************************************/
 
-#include "activewindowcontrolplugin.h"
+#include "appmenuplugin.h"
 #include "appmenumodel.h"
 
 #include <QtQml>
 #include <QQmlEngine>
 
-void ActiveWindowControlPlugin::registerTypes(const char *uri)
+void AppmenuPlugin::registerTypes(const char *uri)
 {
     Q_ASSERT(uri == QLatin1String("org.kde.private.activeWindowControl"));
     qmlRegisterType<AppMenuModel>(uri, 1, 0, "AppMenuModel");
diff --git a/plugin/activewindowcontrolplugin.h b/plugin/appmenuplugin.h
similarity index 90%
rename from plugin/activewindowcontrolplugin.h
rename to plugin/appmenuplugin.h
index 6c587e0..583cec8 100644
--- a/plugin/activewindowcontrolplugin.h
+++ b/plugin/appmenuplugin.h
@@ -19,12 +19,12 @@
  *
  ******************************************************************/
 
-#ifndef ACTIVEWINDOWCONTROLPLUGIN_H
-#define ACTIVEWINDOWCONTROLPLUGIN_H
+#ifndef APPMENUPLUGIN_H
+#define APPMENUPLUGIN_H
 
 #include <QQmlExtensionPlugin>
 
-class ActiveWindowControlPlugin : public QQmlExtensionPlugin
+class AppmenuPlugin : public QQmlExtensionPlugin
 {
     Q_OBJECT
     Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
diff --git a/plugin/libdbusmenuqt/CMakeLists.txt b/plugin/libdbusmenuqt/CMakeLists.txt
index 49d3420..b92e5d6 100644
--- a/plugin/libdbusmenuqt/CMakeLists.txt
+++ b/plugin/libdbusmenuqt/CMakeLists.txt
@@ -18,5 +18,3 @@ target_link_libraries(dbusmenuqt
     Qt5::DBus
     Qt5::Widgets
 )
-
-add_subdirectory(test)
diff --git a/plugin/libdbusmenuqt/dbusmenuimporter.cpp b/plugin/libdbusmenuqt/dbusmenuimporter.cpp
index 2aac5cd..707764f 100644
--- a/plugin/libdbusmenuqt/dbusmenuimporter.cpp
+++ b/plugin/libdbusmenuqt/dbusmenuimporter.cpp
@@ -362,6 +362,11 @@ void DBusMenuImporterPrivate::slotItemsPropertiesUpdated(const DBusMenuItemList
     }
 }
 
+QAction *DBusMenuImporter::actionForId(int id) const
+{
+    return d->m_actionForId.value(id);
+}
+
 void DBusMenuImporter::slotItemActivationRequested(int id, uint /*timestamp*/)
 {
     QAction *action = d->m_actionForId.value(id);
@@ -426,12 +431,10 @@ void DBusMenuImporter::slotGetLayoutFinished(QDBusPendingCallWatcher *watcher)
                 sendClickedEvent(id);
             });
 
-            if (action->menu()) {
-                auto menu = action->menu();
-                connect(menu, &QMenu::aboutToShow, this, [menu, this]() {
-                   updateMenu(menu);
-                });
+            if (QMenu *menuAction = action->menu()) {
+                connect(menuAction, &QMenu::aboutToShow, this, &DBusMenuImporter::slotMenuAboutToShow, Qt::UniqueConnection);
             }
+            connect(menu, &QMenu::aboutToHide, this, &DBusMenuImporter::slotMenuAboutToHide, Qt::UniqueConnection);
 
             menu->addAction(action);
         } else {
@@ -441,6 +444,9 @@ void DBusMenuImporter::slotGetLayoutFinished(QDBusPendingCallWatcher *watcher)
             filteredKeys.removeOne("toggle-type");
             filteredKeys.removeOne("children-display");
             d->updateAction(*it, dbusMenuItem.properties, filteredKeys);
+            // Move the action to the tail so we can keep the order same as the dbus request.
+            menu->removeAction(action);
+            menu->addAction(action);
         }
     }
 
@@ -518,6 +524,8 @@ void DBusMenuImporter::slotMenuAboutToShow()
     QMenu *menu = qobject_cast<QMenu*>(sender());
     Q_ASSERT(menu);
 
+    updateMenu(menu);
+
     QAction *action = menu->menuAction();
     Q_ASSERT(action);
 
diff --git a/plugin/libdbusmenuqt/dbusmenuimporter.h b/plugin/libdbusmenuqt/dbusmenuimporter.h
index 6d3fd4f..6f91ee9 100644
--- a/plugin/libdbusmenuqt/dbusmenuimporter.h
+++ b/plugin/libdbusmenuqt/dbusmenuimporter.h
@@ -47,6 +47,9 @@ class DBusMenuImporter : public QObject
 
     ~DBusMenuImporter() override;
 
+
+    QAction *actionForId(int id) const;
+
     /**
      * The menu created from listening to the DBusMenuExporter over DBus
      */
diff --git a/plugin/libdbusmenuqt/test/CMakeLists.txt b/plugin/libdbusmenuqt/test/CMakeLists.txt
deleted file mode 100644
index a950a6f..0000000
--- a/plugin/libdbusmenuqt/test/CMakeLists.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-add_executable(appmenutest main.cpp)
-target_link_libraries(appmenutest
-                        Qt5::Widgets)
diff --git a/plugin/libdbusmenuqt/test/README b/plugin/libdbusmenuqt/test/README
deleted file mode 100644
index 99dd020..0000000
--- a/plugin/libdbusmenuqt/test/README
+++ /dev/null
@@ -1,2 +0,0 @@
-App with a menu, designed for use testing appmenu QPTs/applets/kded modules
-small enough that we can attach debuggers and breakpoints without drowning in data
diff --git a/plugin/libdbusmenuqt/test/main.cpp b/plugin/libdbusmenuqt/test/main.cpp
deleted file mode 100644
index 74a0e05..0000000
--- a/plugin/libdbusmenuqt/test/main.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- *   Copyright 2017 David Edmundson <davidedmundson@kde.org>
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU Library General Public License as
- *   published by the Free Software Foundation; either version 2, 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 Library 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 <QApplication>
-
-#include <QMainWindow>
-#include <QMenuBar>
-#include <QDateTime>
-#include <QIcon>
-#include <QDebug>
-
-class MainWindow : public QMainWindow
-{
-public:
-    MainWindow();
-};
-
-MainWindow::MainWindow() :
-    QMainWindow()
-{
-    /*set an initial menu with the following
-    Menu A
-      - Item
-      - Checkable Item
-      - Item With Icon
-      - A separator
-      - Menu B
-         - Item B1
-     Menu C
-      - DynamicItem ${timestamp}
-
-      TopLevelItem
-    */
-
-    QAction *t;
-    auto menuA = new QMenu("Menu A", this);
-    menuA->addAction("Item");
-
-    t = menuA->addAction("Checkable Item");
-    t->setCheckable(true);
-
-    t = menuA->addAction(QIcon::fromTheme("document-edit"), "Item with icon");
-
-    menuA->addSeparator();
-
-    auto menuB = new QMenu("Menu B", this);
-    menuB->addAction("Item B1");
-    menuA->addMenu(menuB);
-
-    menuBar()->addMenu(menuA);
-
-    auto menuC = new QMenu("Menu C", this);
-    connect(menuC, &QMenu::aboutToShow, this, [menuC]() {
-        menuC->clear();
-        menuC->addAction("Dynamic Item " + QDateTime::currentDateTime().toString());
-    });
-
-    menuBar()->addMenu(menuC);
-
-    menuBar()->addAction("Top Level Item");
-}
-
-int main(int argc, char **argv)
-{
-    QApplication app(argc, argv);
-    MainWindow mw;
-    mw.show();
-    return app.exec();
-}
diff --git a/plugin/qmldir b/plugin/qmldir
index e09e45e..e85fbc8 100644
--- a/plugin/qmldir
+++ b/plugin/qmldir
@@ -1,3 +1,3 @@
 module org.kde.private.activeWindowControl
 
-plugin activewindowcontrolplugin
+plugin appmenuplugin
openSUSE Build Service is sponsored by