On Sunday 04 April 2010 22:17:23 Aaron J. Seigo wrote:
> On April 4, 2010, Ivan Ruchkin wrote:
> > Why can't we just take the dbus client-server mechanism from Bespin and
> > XBar? Menubar containment will serve as server and every application as a
> > client.
> 
> that's certainly a possibility. always nice to avoid reinventing the wheel.
> i haven't looked at how Bespin does it exactly, yet, so have to reserve
> some judgement.

Since this topic is currently discussed I think it makes sense to share a  
patch that extracts the client side of bespin and applies to the oxygen 
kstyle. As I'm a user of the global menubar but like oxygen better than bespin 
I have made this patch some time ago and I'm using it without major problems. 
Maybe it will save you some time looking at how the bespin model works. (Note: 
this is just the client side. You still need the X-Bar plasmoid from bespin)

I might add that there is another problem to think of. Some applications like 
KDevelop or rekonq modify the menu bar. Perhaps it would make sense to think 
also at those cases while making a decision how to design the global menu bar.

Cheers,
Michael
Index: kstyles/oxygen/oxygen.cpp
===================================================================
--- kstyles/oxygen/oxygen.cpp	(revision 1072273)
+++ kstyles/oxygen/oxygen.cpp	(working copy)
@@ -30,7 +30,10 @@
  */
 
 #include "oxygen.h"
+#include "macmenu.h"
 #include "oxygen.moc"
+#include "macmenu.moc"
+#include "macmenu-dbus.moc"
 
 #include <QtGui/QAbstractItemView>
 #include <QtGui/QApplication>
@@ -3235,6 +3238,11 @@
 
     if (qobject_cast<QMenuBar*>(widget))
     {
+        kDebug() << "********************************************** Checking App:" << QCoreApplication::applicationName();
+        if(!(("Designer"==QCoreApplication::applicationName() || "kdevelop"==QCoreApplication::applicationName()) && widget->inherits("QDesignerMenuBar"))) {
+            kDebug() << "applying MacMenu";
+            Bespin::MacMenu::manage((QMenuBar *)widget);
+        }
 
         widget->setBackgroundRole(QPalette::NoRole);
 
@@ -3340,8 +3348,11 @@
         || qobject_cast<QLineEdit*>(widget)
     ) { widget->setAttribute(Qt::WA_Hover, false); }
 
-    if (qobject_cast<QMenuBar*>(widget)
-        || (widget && widget->inherits("Q3ToolBar"))
+    if (qobject_cast<QMenuBar*>(widget)) {
+        Bespin::MacMenu::release((QMenuBar *)widget);        
+    }
+    
+    if (widget && widget->inherits("Q3ToolBar")
         || qobject_cast<QToolBar*>(widget)
         || (widget && qobject_cast<QToolBar *>(widget->parent()))
         || qobject_cast<QToolBox*>(widget))
@@ -4977,6 +4988,19 @@
 
         }
 
+        case SH_MainWindow_SpaceBelowMenuBar:
+        {
+//            if(opts.xbar)
+                if (const QMenuBar *menubar = qobject_cast<const QMenuBar*>(widget))
+                    if (0==menubar->height() && !menubar->actions().isEmpty())
+                    {   // we trick menubars if we use macmenus - hehehe...
+                        // NOTICE the final result NEEDS to be > "0" (i.e. "1") to avoid side effects...
+                        return -menubar->actionGeometry(menubar->actions().first()).height() + 1;
+                    }
+
+            return 0;
+        }
+        
         case SH_Menu_Mask:
         {
 
Index: kstyles/oxygen/macmenu.cpp
===================================================================
--- kstyles/oxygen/macmenu.cpp	(revision 0)
+++ kstyles/oxygen/macmenu.cpp	(revision 0)
@@ -0,0 +1,487 @@
+/* Bespin mac-a-like XBar KDE4
+Copyright (C) 2007 Thomas Luebking <thomas.luebk...@web.de>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License version 2 as published by the Free Software Foundation.
+
+This library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+ */
+
+#include <QActionEvent>
+#include <QApplication>
+#include <QtDBus/QDBusInterface>
+#include <QtDBus/QDBusConnectionInterface>
+#include <QLayout>
+#include <QMenuBar>
+#include <QWindowStateChangeEvent>
+
+#include "macmenu.h"
+#include "macmenu-dbus.h"
+
+#include <QtDebug>
+
+using namespace Bespin;
+
+static MacMenu *instance = 0;
+static QDBusInterface *xbar = 0;
+
+bool
+FullscreenWatcher::eventFilter(QObject *o, QEvent *ev)
+{
+    QWidget *window = qobject_cast<QWidget*>(o);
+    if (!(window && ev->type() == QEvent::WindowStateChange))
+        return false;
+    if (window->windowState() & Qt::WindowFullScreen)
+        instance->deactivate(window);
+    else
+        instance->activate(window);
+    return false;
+}
+
+static FullscreenWatcher *fullscreenWatcher = 0;
+
+MacMenu::MacMenu() : QObject()
+{
+    QDBusConnectionInterface *session = QDBusConnection::sessionBus().interface();
+
+    usingMacMenu = session->isServiceRegistered("org.kde.XBar");
+    service = QString("org.kde.XBar-%1").arg(QCoreApplication::applicationPid());
+    // register me
+    QDBusConnection::sessionBus().registerService(service);
+    QDBusConnection::sessionBus().registerObject("/XBarClient", this);
+
+    connect (qApp, SIGNAL(aboutToQuit()), this, SLOT(deactivate()));
+}
+
+
+void
+MacMenu::manage(QMenuBar *menu)
+{
+    if (!menu) // ...
+        return;
+    
+    // we only accept menus that are placed on a QMainWindow - for the moment, and probably ever
+    QWidget *dad = menu->parentWidget();
+    if (!(dad && dad->inherits("QMainWindow") && dad->layout() && dad->layout()->menuBar() == menu))
+        return;
+
+    if (!instance)
+    {
+        instance = new MacMenu;
+        xbar = new QDBusInterface( "org.kde.XBar", "/XBar", "org.kde.XBar" );
+        /*MacMenuAdaptor *adapt = */new MacMenuAdaptor(instance);
+        fullscreenWatcher = new FullscreenWatcher;
+    }
+    else if (instance->items.contains(menu))
+        return; // no double adds please!
+
+    if (instance->usingMacMenu)
+        instance->activate(menu);
+
+    connect (menu, SIGNAL(destroyed(QObject *)), instance, SLOT(_release(QObject *)));
+
+    instance->items.append(menu);
+}
+
+void
+MacMenu::release(QMenuBar *menu)
+{
+    if (!instance)
+        return;
+    instance->_release(menu);
+}
+
+void
+MacMenu::_release(QObject *o)
+{
+    xbar->call(QDBus::NoBlock, "unregisterMenu", (qlonglong)o);
+
+    QMenuBar *menu = qobject_cast<QMenuBar*>(o);
+    if (!menu) return;
+
+    items.removeAll(menu);
+    menu->removeEventFilter(this);
+    QWidget *dad = menu->parentWidget();
+    if (dad && dad->layout())
+        dad->layout()->setMenuBar(menu);
+    menu->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+    menu->adjustSize();
+//    menu->updateGeometry();
+}
+
+void
+MacMenu::activate()
+{
+    MenuList::iterator menu = items.begin();
+    while (menu != items.end())
+    {
+        if (*menu)
+            { activate(*menu); ++menu; }
+        else
+            { actions.remove(*menu); menu = items.erase(menu); }
+    }
+    usingMacMenu = true;
+}
+
+void
+MacMenu::activate(QMenuBar *menu)
+{
+    menu->removeEventFilter(this);
+    
+    // and WOWWWW - no more per window menubars...
+    menu->setFixedSize(0,0);
+    //NOTICE i used to set the menu's parent->layout()->setMenuBar(0) to get rid of the free space
+    // but this leeds to side effects (e.g. kcalc won't come up anymore...)
+    // so now the stylehint for the free space below checks the menubar height and returns
+    // a negative value so that final result will be 1 px heigh...
+    menu->updateGeometry();
+    
+    // we need to hold a copy of this list to handle action removes
+    // (as we get the event after the action has been removed from the widget...)
+    actions[menu] = menu->actions();
+    
+    // find a nice header
+    QString title = menu->window()->windowTitle();
+    const QStringList appArgs = QCoreApplication::arguments();
+    QString name = appArgs.isEmpty() ? "" : appArgs.at(0).section('/', -1);
+    if (title.isEmpty())
+        title = name;
+    else
+    {
+        int i = title.indexOf(name, 0, Qt::CaseInsensitive);
+        if (i > -1)
+            title = title.mid(i, name.length());
+    }
+    title = title.section(" - ", -1);
+    if (title.isEmpty())
+    {
+        if (!menu->actions().isEmpty())
+            title = menu->actions().at(0)->text();
+        if (title.isEmpty())    
+            title = "QApplication";
+    }
+    
+    // register the menu via dbus
+    QStringList entries;
+    foreach (QAction* action, menu->actions())
+        if (action->isSeparator())
+            entries << "<XBAR_SEPARATOR/>";
+        else
+            entries << action->text();
+    xbar->call(QDBus::NoBlock, "registerMenu", service, (qlonglong)menu, title, entries);
+    // TODO cause of now async call, the following should - maybe - attached to the above?!!
+    if (menu->isActiveWindow())
+        xbar->call(QDBus::NoBlock, "requestFocus", (qlonglong)menu);
+    
+    // take care of several widget events!
+    menu->installEventFilter(this);
+    if (menu->window())
+    {
+        menu->window()->removeEventFilter(fullscreenWatcher);
+        menu->window()->installEventFilter(fullscreenWatcher);
+    }
+}
+
+void
+MacMenu::activate(QWidget *window)
+{
+    MenuList::iterator menu = items.begin();
+    while (menu != items.end())
+    {
+        if (*menu)
+        {
+            if ((*menu)->window() == window)
+                { activate(*menu); return; }
+            ++menu;
+        }
+        else
+            { actions.remove(*menu); menu = items.erase(menu); }
+    }
+}
+
+void
+MacMenu::deactivate()
+{
+    usingMacMenu = false;
+
+    MenuList::iterator i = items.begin();
+    QMenuBar *menu = 0;
+    while (i != items.end())
+    {
+        actions.remove(*i);
+        if ((menu = *i))
+        {
+            deactivate(menu);
+            ++i;
+        }
+        else
+            i = items.erase(i);
+    }
+}
+
+void
+MacMenu::deactivate(QMenuBar *menu)
+{
+    menu->removeEventFilter(this);
+    QWidget *dad = menu->parentWidget();
+    if (dad && dad->layout())
+        dad->layout()->setMenuBar(menu);
+    menu->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
+    menu->adjustSize();
+    //             menu->updateGeometry();
+}
+
+void
+MacMenu::deactivate(QWidget *window)
+{
+    MenuList::iterator menu = items.begin();
+    while (menu != items.end())
+    {
+        if (*menu)
+        {
+            if ((*menu)->window() == window)
+                { deactivate(*menu); return; }
+            ++menu;
+        }
+        else
+        { actions.remove(*menu); menu = items.erase(menu); }
+    }
+}
+
+QMenuBar *
+MacMenu::menuBar(qlonglong key)
+{
+    MenuList::iterator i = items.begin();
+    QMenuBar *menu;
+    while (i != items.end())
+    {
+        if (!(menu = *i))
+        {
+            actions.remove(menu);
+            i = items.erase(i);
+        }
+        else
+        {
+            if ((qlonglong)menu == key)
+                return menu;
+            else
+                ++i;
+        }
+    }
+    return NULL;
+}
+
+void
+MacMenu::popup(qlonglong key, int idx, int x, int y)
+{
+    QMenuBar *menu = menuBar(key);
+    if (!menu) return;
+
+    QMenu *pop;
+    for (int i = 0; i < menu->actions().count(); ++i)
+    {
+        if (!(pop = menu->actions().at(i)->menu()))
+            continue;
+
+        if (i == idx) {
+            if (!pop->isVisible())
+            {
+                connect (pop, SIGNAL(aboutToHide()), this, SLOT(menuClosed()));
+                xbar->call(QDBus::NoBlock, "setOpenPopup", idx);
+                pop->popup(QPoint(x,y));
+            }
+            else
+            {
+                xbar->call(QDBus::NoBlock, "setOpenPopup", -1000);
+                pop->hide();
+            }
+        }
+        else
+            pop->hide();
+    }
+}
+
+void
+MacMenu::popDown(qlonglong key)
+{
+    QMenuBar *menu = menuBar(key);
+    if (!menu) return;
+
+    QWidget *pop;
+    for (int i = 0; i < menu->actions().count(); ++i)
+    {
+        if (!(pop = menu->actions().at(i)->menu()))
+            continue;
+        disconnect (pop, SIGNAL(aboutToHide()), this, SLOT(menuClosed()));
+        pop->hide();
+//         menu->activateWindow();
+        break;
+    }
+}
+
+static bool inHover = false;
+
+void
+MacMenu::hover(qlonglong key, int idx,  int x, int y)
+{
+    QMenuBar *menu = menuBar(key);
+    if (!menu) return;
+
+    QWidget *pop;
+    for (int i = 0; i < menu->actions().count(); ++i)
+    {
+        if ((i == idx) || !(pop = menu->actions().at(i)->menu()))
+            continue;
+        if (pop->isVisible())
+        {
+            inHover = true;
+            popup(key, idx, x, y); // TODO: this means a useless second pass above...
+            inHover = false;
+            break;
+        }
+    }
+}
+
+static QMenuBar *bar4menu(QMenu *menu)
+{
+    if (!menu->menuAction())
+        return 0;
+    if (menu->menuAction()->associatedWidgets().isEmpty())
+        return 0;
+    foreach (QWidget *w, menu->menuAction()->associatedWidgets())
+        if (qobject_cast<QMenuBar*>(w))
+            return static_cast<QMenuBar *>(w);
+    return 0;
+}
+
+void
+MacMenu::menuClosed()
+{
+    QObject * _sender = sender();
+    
+    if (!_sender)
+        return;
+
+    disconnect (sender(), SIGNAL(aboutToHide()), this, SLOT(menuClosed()));
+    if (!inHover)
+    {
+        xbar->call(QDBus::NoBlock, "setOpenPopup", -500);
+
+        if (QMenu *menu = qobject_cast<QMenu*>(_sender))
+        if (QMenuBar *bar = bar4menu(menu))
+            bar->activateWindow(); // CDP: QtCurve fix 'setActiveWindow' is in Qt3Support! ORIG: bar->setActiveWindow();
+    }
+}
+
+void
+MacMenu::changeAction(QMenuBar *menu, QActionEvent *ev)
+{
+    int idx;
+    const QString title = ev->action()->isSeparator() ? "<XBAR_SEPARATOR/>" : ev->action()->text();
+    if (ev->type() == QEvent::ActionAdded)
+    {
+        idx = ev->before() ? menu->actions().indexOf(ev->before())-1 : -1;
+        xbar->call(QDBus::NoBlock, "addEntry", (qlonglong)menu, idx, title);
+        actions[menu].insert(idx, ev->action());
+        return;
+    }
+    if (ev->type() == QEvent::ActionChanged)
+    {
+        idx = menu->actions().indexOf(ev->action());
+        xbar->call(QDBus::NoBlock, "changeEntry", (qlonglong)menu, idx, title);
+    }
+    else
+    { // remove
+        idx = actions[menu].indexOf(ev->action());
+        actions[menu].removeAt(idx);
+        xbar->call(QDBus::NoBlock, "removeEntry", (qlonglong)menu, idx);
+    }
+}
+
+void
+MacMenu::raise(qlonglong key)
+{
+    if (QMenuBar *menu = menuBar(key))
+    {
+        if (QWidget *win = menu->window())
+        {
+            win->showNormal();
+            win->activateWindow();
+            win->raise();
+        }
+    }
+}
+
+bool
+MacMenu::eventFilter(QObject *o, QEvent *ev)
+{
+    QMenuBar *menu = qobject_cast<QMenuBar*>(o);
+    if (!menu)
+        return false;
+
+    if (!usingMacMenu)
+        return false;
+
+    QString func;
+    switch (ev->type())
+    {
+    case QEvent::Resize:
+//         menu->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
+        if (menu->size() != QSize(0,0))
+        {
+            menu->setFixedSize(0,0);
+            menu->updateGeometry();
+        }
+        break;
+    case QEvent::ActionAdded:
+    case QEvent::ActionChanged:
+    case QEvent::ActionRemoved:
+        changeAction(menu, static_cast<QActionEvent*>(ev));
+        break;
+//     case QEvent::ParentChange:
+//         qDebug() << o << ev;
+//         return false;
+    case QEvent::EnabledChange:
+        if (static_cast<QWidget*>(o)->isEnabled())
+            xbar->call(QDBus::NoBlock, "requestFocus", (qlonglong)menu);
+        else
+            xbar->call(QDBus::NoBlock, "releaseFocus", (qlonglong)menu);
+        break;
+
+//     case QEvent::ApplicationActivate:
+    // TODO: test whether this is the only one and show it? (e.g. what about dialogs...?!)
+    case QEvent::WindowActivate:
+        xbar->call(QDBus::NoBlock, "requestFocus", (qlonglong)menu);
+        break;
+
+    case QEvent::WindowBlocked:
+    case QEvent::WindowDeactivate:
+    case QEvent::ApplicationDeactivate:
+        xbar->call(QDBus::NoBlock, "releaseFocus", (qlonglong)menu);
+        break;
+    default:
+        return false;
+
+// maybe these need to be passed through...?!
+//       QEvent::GrabKeyboard
+//       QEvent::GrabMouse
+//       QEvent::KeyPress
+//       QEvent::KeyRelease
+//       QEvent::UngrabKeyboard
+//       QEvent::UngrabMouse
+// --- and what about these ---
+//       QEvent::MenubarUpdated
+//       QEvent::ParentChange
+// -------------------
+    }
+    return false;
+}
Index: kstyles/oxygen/macmenu-dbus.h
===================================================================
--- kstyles/oxygen/macmenu-dbus.h	(revision 0)
+++ kstyles/oxygen/macmenu-dbus.h	(revision 0)
@@ -0,0 +1,51 @@
+/* Bespin mac-a-like XBar KDE4
+Copyright (C) 2007 Thomas Luebking <thomas.luebk...@web.de>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License version 2 as published by the Free Software Foundation.
+
+This library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+ */
+
+#ifndef MAC_MENU_ADAPTOR_H
+#define MAC_MENU_ADAPTOR_H
+
+#include <QtDBus/QDBusAbstractAdaptor>
+#include "macmenu.h"
+
+namespace Bespin
+{
+
+class MacMenuAdaptor : public QDBusAbstractAdaptor
+{
+   Q_OBJECT
+   Q_CLASSINFO("D-Bus Interface", "org.kde.XBarClient")
+
+private:
+   MacMenu *mm;
+
+public:
+   MacMenuAdaptor(MacMenu *macMenu) : QDBusAbstractAdaptor(macMenu), mm(macMenu) { }
+
+public slots:
+   Q_NOREPLY void activate() { mm->activate(); }
+   Q_NOREPLY void deactivate() { mm->deactivate(); }
+   Q_NOREPLY void popup(qlonglong key, int idx, int x, int y)
+   { mm->popup(key, idx, x, y); }
+   Q_NOREPLY void hover(qlonglong key, int idx, int x, int y)
+   { mm->hover(key, idx, x, y); }
+   Q_NOREPLY void popDown(qlonglong key) { mm->popDown(key); }
+   Q_NOREPLY void raise(qlonglong key) { mm->raise(key); }
+};
+} // namespace
+
+#endif //MAC_MENU_ADAPTOR_H
Index: kstyles/oxygen/macmenu.h
===================================================================
--- kstyles/oxygen/macmenu.h	(revision 0)
+++ kstyles/oxygen/macmenu.h	(revision 0)
@@ -0,0 +1,81 @@
+/* Bespin mac-a-like XBar KDE4
+Copyright (C) 2007 Thomas Luebking <thomas.luebk...@web.de>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License version 2 as published by the Free Software Foundation.
+
+This library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this library; see the file COPYING.LIB.  If not, write to
+   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.
+ */
+
+#ifndef MAC_MENU_H
+#define MAC_MENU_H
+
+#include <QMap>
+#include <QObject>
+#include <QPointer>
+
+class QMenuBar;
+class QAction;
+class QActionEvent;
+
+
+namespace Bespin
+{
+
+class FullscreenWatcher : public QObject
+{
+public:
+    FullscreenWatcher() : QObject() {};
+protected:
+    bool eventFilter(QObject *o, QEvent *ev);
+};
+    
+class MacMenu : public QObject
+{
+   Q_OBJECT
+public:
+    static void manage(QMenuBar *menu);
+    static void release(QMenuBar *menu);
+    void popup(qlonglong key, int idx, int x, int y);
+    void hover(qlonglong key, int idx,  int x, int y);
+    void popDown(qlonglong key);
+    void raise(qlonglong key);
+public slots:
+    void activate();
+    void deactivate();
+protected:
+    bool eventFilter(QObject *o, QEvent *ev);
+protected:
+    friend class FullscreenWatcher;
+    void deactivate(QWidget *window);
+    void activate(QWidget *window);
+private:
+    Q_DISABLE_COPY(MacMenu)
+    MacMenu();
+    void activate(QMenuBar *menu);
+    void changeAction(QMenuBar *menu, QActionEvent *ev);
+    void deactivate(QMenuBar *menu);
+    typedef QPointer<QMenuBar> QMenuBar_p;
+    typedef QList<QMenuBar_p> MenuList;
+    MenuList items;
+    QMenuBar *menuBar(qlonglong key);
+    QMap< QMenuBar_p, QList<QAction*> > actions;
+    bool usingMacMenu;
+    QString service;
+private slots:
+    void menuClosed();
+    void _release(QObject *);
+};
+
+} // namespace
+
+#endif //MAC_MENU_H
Index: kstyles/oxygen/CMakeLists.txt
===================================================================
--- kstyles/oxygen/CMakeLists.txt	(revision 1072273)
+++ kstyles/oxygen/CMakeLists.txt	(working copy)
@@ -45,13 +45,14 @@
     transitions/oxygentransitions.cpp
     transitions/oxygentransitionwidget.cpp
     oxygen.cpp
+    macmenu.cpp
 )
 
 kde4_add_kcfg_files(oxygen_PART_SRCS oxygenstyleconfigdata.kcfgc)
 
 kde4_add_plugin(oxygen ${oxygen_PART_SRCS})
 
-target_link_libraries(oxygen  ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS})
+target_link_libraries(oxygen  ${QT_QTDBUS_LIBRARY} ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS})
 
 install(TARGETS oxygen  DESTINATION ${PLUGIN_INSTALL_DIR}/plugins/styles/ )
 

Attachment: signature.asc
Description: This is a digitally signed message part.

_______________________________________________
Plasma-devel mailing list
Plasma-devel@kde.org
https://mail.kde.org/mailman/listinfo/plasma-devel

Reply via email to