Use upcoming version of libdbusmenu-qt

This contains a temporary fork of the importer, with the main paths
fixed.
The old code spawned a new event loop in the signal QMenu::aboutToShow()
This refetched the menu, even though we had already fetched it, before
calling show.

Spawning new event loops from a QML function, leads to all sorts of
crashes. This
fixes that, and saves some pointless DBus traffic

BUG: 343971
BUG: 345838
BUG: 345933
REVIEW: 123992
wilder-5.14
David Edmundson 11 years ago
parent fdff218d1c
commit 3d0a9cd462
  1. 7
      dataengines/statusnotifieritem/CMakeLists.txt
  2. 2
      dataengines/statusnotifieritem/libdbusmenuqt/README
  3. 532
      dataengines/statusnotifieritem/libdbusmenuqt/dbusmenuimporter.cpp
  4. 109
      dataengines/statusnotifieritem/libdbusmenuqt/dbusmenuimporter.h
  5. 82
      dataengines/statusnotifieritem/libdbusmenuqt/dbusmenushortcut_p.cpp
  6. 39
      dataengines/statusnotifieritem/libdbusmenuqt/dbusmenushortcut_p.h
  7. 111
      dataengines/statusnotifieritem/libdbusmenuqt/dbusmenutypes_p.cpp
  8. 93
      dataengines/statusnotifieritem/libdbusmenuqt/dbusmenutypes_p.h
  9. 64
      dataengines/statusnotifieritem/libdbusmenuqt/utils.cpp
  10. 31
      dataengines/statusnotifieritem/libdbusmenuqt/utils_p.h

@ -1,7 +1,6 @@
include_directories(${plasma-workspace_SOURCE_DIR}/statusnotifierwatcher)
include_directories(${dbusmenu-qt5_INCLUDE_DIRS})
# We add our source code here
set(statusnotifieritem_engine_SRCS
statusnotifieritem_engine.cpp
@ -9,6 +8,11 @@ set(statusnotifieritem_engine_SRCS
statusnotifieritemservice.cpp
statusnotifieritemjob.cpp
systemtraytypes.cpp
libdbusmenuqt/dbusmenuimporter.cpp
libdbusmenuqt/dbusmenushortcut_p.cpp
libdbusmenuqt/dbusmenutypes_p.cpp
libdbusmenuqt/utils.cpp
)
set(statusnotifierwatcher_xml ${KNOTIFICATIONS_DBUS_INTERFACES_DIR}/kf5_org.kde.StatusNotifierWatcher.xml)
@ -29,7 +33,6 @@ target_link_libraries(plasma_engine_statusnotifieritem
KF5::Service
KF5::Plasma
KF5::IconThemes
dbusmenu-qt5
)
kcoreaddons_desktop_to_json(plasma_engine_statusnotifieritem plasma-dataengine-statusnotifieritem.desktop)

@ -0,0 +1,2 @@
Contains a patched version of the import path of libdbusmenu-qt
Remove when next version of libdbusmenu-qt is released.

@ -0,0 +1,532 @@
/* This file is part of the dbusmenu-qt library
Copyright 2009 Canonical
Author: Aurelien Gateau <aurelien.gateau@canonical.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License (LGPL) as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later
version.
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 "dbusmenuimporter.h"
// Qt
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusReply>
#include <QDBusVariant>
#include <QFont>
#include <QMenu>
#include <QPointer>
#include <QSignalMapper>
#include <QTime>
#include <QTimer>
#include <QToolButton>
#include <QWidgetAction>
#include <QSet>
#include <QDebug>
// Local
#include "dbusmenutypes_p.h"
#include "dbusmenushortcut_p.h"
#include "utils_p.h"
//#define BENCHMARK
#ifdef BENCHMARK
#include <QTime>
static QTime sChrono;
#endif
#define DMRETURN_IF_FAIL(cond) if (!(cond)) { \
qWarning() << "Condition failed: " #cond; \
return; \
}
static const char *DBUSMENU_INTERFACE = "com.canonical.dbusmenu";
static const int ABOUT_TO_SHOW_TIMEOUT = 3000;
static const int REFRESH_TIMEOUT = 4000;
static const char *DBUSMENU_PROPERTY_ID = "_dbusmenu_id";
static const char *DBUSMENU_PROPERTY_ICON_NAME = "_dbusmenu_icon_name";
static const char *DBUSMENU_PROPERTY_ICON_DATA_HASH = "_dbusmenu_icon_data_hash";
static QAction *createKdeTitle(QAction *action, QWidget *parent)
{
QToolButton *titleWidget = new QToolButton(0);
QFont font = titleWidget->font();
font.setBold(true);
titleWidget->setFont(font);
titleWidget->setIcon(action->icon());
titleWidget->setText(action->text());
titleWidget->setDown(true);
titleWidget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
QWidgetAction *titleAction = new QWidgetAction(parent);
titleAction->setDefaultWidget(titleWidget);
return titleAction;
}
class DBusMenuImporterPrivate
{
public:
DBusMenuImporter *q;
QDBusAbstractInterface *m_interface;
QMenu *m_menu;
typedef QMap<int, QPointer<QAction> > ActionForId;
ActionForId m_actionForId;
QSignalMapper m_mapper;
QTimer *m_pendingLayoutUpdateTimer;
QSet<int> m_idsRefreshedByAboutToShow;
QSet<int> m_pendingLayoutUpdates;
int m_nPendingRequests;
QDBusPendingCallWatcher *refresh(int id)
{
m_nPendingRequests++;
#ifdef BENCHMARK
DMDEBUG << "Starting refresh chrono for id" << id;
sChrono.start();
#endif
QDBusPendingCall call = m_interface->asyncCall("GetLayout", id, 1, QStringList());
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, q);
watcher->setProperty(DBUSMENU_PROPERTY_ID, id);
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
q, SLOT(slotGetLayoutFinished(QDBusPendingCallWatcher*)));
return watcher;
}
QMenu *createMenu(QWidget *parent)
{
QMenu *menu = q->createMenu(parent);
return menu;
}
/**
* Init all the immutable action properties here
* TODO: Document immutable properties?
*
* Note: we remove properties we handle from the map (using QMap::take()
* instead of QMap::value()) to avoid warnings about these properties in
* updateAction()
*/
QAction *createAction(int id, const QVariantMap &_map, QWidget *parent)
{
QVariantMap map = _map;
QAction *action = new QAction(parent);
action->setProperty(DBUSMENU_PROPERTY_ID, id);
QString type = map.take("type").toString();
if (type == "separator") {
action->setSeparator(true);
}
if (map.take("children-display").toString() == "submenu") {
QMenu *menu = createMenu(parent);
action->setMenu(menu);
}
QString toggleType = map.take("toggle-type").toString();
if (!toggleType.isEmpty()) {
action->setCheckable(true);
if (toggleType == "radio") {
QActionGroup *group = new QActionGroup(action);
group->addAction(action);
}
}
bool isKdeTitle = map.take("x-kde-title").toBool();
updateAction(action, map, map.keys());
if (isKdeTitle) {
action = createKdeTitle(action, parent);
}
return action;
}
/**
* Update mutable properties of an action. A property may be listed in
* requestedProperties but not in map, this means we should use the default value
* for this property.
*
* @param action the action to update
* @param map holds the property values
* @param requestedProperties which properties has been requested
*/
void updateAction(QAction *action, const QVariantMap &map, const QStringList &requestedProperties)
{
Q_FOREACH(const QString &key, requestedProperties) {
updateActionProperty(action, key, map.value(key));
}
}
void updateActionProperty(QAction *action, const QString &key, const QVariant &value)
{
if (key == QLatin1String("label")) {
updateActionLabel(action, value);
} else if (key == QLatin1String("enabled")) {
updateActionEnabled(action, value);
} else if (key == QLatin1String("toggle-state")) {
updateActionChecked(action, value);
} else if (key == QLatin1String("icon-name")) {
updateActionIconByName(action, value);
} else if (key == QLatin1String("icon-data")) {
updateActionIconByData(action, value);
} else if (key == QLatin1String("visible")) {
updateActionVisible(action, value);
} else if (key == QLatin1String("shortcut")) {
updateActionShortcut(action, value);
} else if (key == QLatin1String("children-display")) {
} else {
qWarning() << "Unhandled property update" << key;
}
}
void updateActionLabel(QAction *action, const QVariant &value)
{
QString text = swapMnemonicChar(value.toString(), '_', '&');
action->setText(text);
}
void updateActionEnabled(QAction *action, const QVariant &value)
{
action->setEnabled(value.isValid() ? value.toBool(): true);
}
void updateActionChecked(QAction *action, const QVariant &value)
{
if (action->isCheckable() && value.isValid()) {
action->setChecked(value.toInt() == 1);
}
}
void updateActionIconByName(QAction *action, const QVariant &value)
{
const QString iconName = value.toString();
const QString previous = action->property(DBUSMENU_PROPERTY_ICON_NAME).toString();
if (previous == iconName) {
return;
}
action->setProperty(DBUSMENU_PROPERTY_ICON_NAME, iconName);
if (iconName.isEmpty()) {
action->setIcon(QIcon());
return;
}
action->setIcon(q->iconForName(iconName));
}
void updateActionIconByData(QAction *action, const QVariant &value)
{
const QByteArray data = value.toByteArray();
uint dataHash = qHash(data);
uint previousDataHash = action->property(DBUSMENU_PROPERTY_ICON_DATA_HASH).toUInt();
if (previousDataHash == dataHash) {
return;
}
action->setProperty(DBUSMENU_PROPERTY_ICON_DATA_HASH, dataHash);
QPixmap pix;
if (!pix.loadFromData(data)) {
qWarning() << "Failed to decode icon-data property for action" << action->text();
action->setIcon(QIcon());
return;
}
action->setIcon(QIcon(pix));
}
void updateActionVisible(QAction *action, const QVariant &value)
{
action->setVisible(value.isValid() ? value.toBool() : true);
}
void updateActionShortcut(QAction *action, const QVariant &value)
{
QDBusArgument arg = value.value<QDBusArgument>();
DBusMenuShortcut dmShortcut;
arg >> dmShortcut;
QKeySequence keySequence = dmShortcut.toKeySequence();
action->setShortcut(keySequence);
}
QMenu *menuForId(int id) const
{
if (id == 0) {
return q->menu();
}
QAction *action = m_actionForId.value(id);
if (!action) {
return 0;
}
return action->menu();
}
void slotItemsPropertiesUpdated(const DBusMenuItemList &updatedList, const DBusMenuItemKeysList &removedList);
void sendEvent(int id, const QString &eventId)
{
QVariant empty = QVariant::fromValue(QDBusVariant(QString()));
m_interface->asyncCall("Event", id, eventId, empty, 0u);
}
};
DBusMenuImporter::DBusMenuImporter(const QString &service, const QString &path, QObject *parent)
: QObject(parent)
, d(new DBusMenuImporterPrivate)
{
DBusMenuTypes_register();
d->q = this;
d->m_interface = new QDBusInterface(service, path, DBUSMENU_INTERFACE, QDBusConnection::sessionBus(), this);
d->m_menu = 0;
d->m_nPendingRequests = 0;
connect(&d->m_mapper, SIGNAL(mapped(int)), SLOT(sendClickedEvent(int)));
d->m_pendingLayoutUpdateTimer = new QTimer(this);
d->m_pendingLayoutUpdateTimer->setSingleShot(true);
connect(d->m_pendingLayoutUpdateTimer, SIGNAL(timeout()), SLOT(processPendingLayoutUpdates()));
QDBusConnection::sessionBus().connect(service, path, DBUSMENU_INTERFACE, "LayoutUpdated", "ui",
this, SLOT(slotLayoutUpdated(uint, int)));
QDBusConnection::sessionBus().connect(service, path, DBUSMENU_INTERFACE, "ItemsPropertiesUpdated", "a(ia{sv})a(ias)",
this, SLOT(slotItemsPropertiesUpdated(DBusMenuItemList, DBusMenuItemKeysList)));
QDBusConnection::sessionBus().connect(service, path, DBUSMENU_INTERFACE, "ItemActivationRequested", "iu",
this, SLOT(slotItemActivationRequested(int, uint)));
d->refresh(0);
}
DBusMenuImporter::~DBusMenuImporter()
{
// Do not use "delete d->m_menu": even if we are being deleted we should
// leave enough time for the menu to finish what it was doing, for example
// if it was being displayed.
d->m_menu->deleteLater();
delete d;
}
void DBusMenuImporter::slotLayoutUpdated(uint revision, int parentId)
{
if (d->m_idsRefreshedByAboutToShow.remove(parentId)) {
return;
}
d->m_pendingLayoutUpdates << parentId;
if (!d->m_pendingLayoutUpdateTimer->isActive()) {
d->m_pendingLayoutUpdateTimer->start();
}
}
void DBusMenuImporter::processPendingLayoutUpdates()
{
QSet<int> ids = d->m_pendingLayoutUpdates;
d->m_pendingLayoutUpdates.clear();
Q_FOREACH(int id, ids) {
d->refresh(id);
}
}
QMenu *DBusMenuImporter::menu() const
{
if (!d->m_menu) {
d->m_menu = d->createMenu(0);
}
return d->m_menu;
}
void DBusMenuImporterPrivate::slotItemsPropertiesUpdated(const DBusMenuItemList &updatedList, const DBusMenuItemKeysList &removedList)
{
Q_FOREACH(const DBusMenuItem &item, updatedList) {
QAction *action = m_actionForId.value(item.id);
if (!action) {
// We don't know this action. It probably is in a menu we haven't fetched yet.
continue;
}
QVariantMap::ConstIterator
it = item.properties.constBegin(),
end = item.properties.constEnd();
for(; it != end; ++it) {
updateActionProperty(action, it.key(), it.value());
}
}
Q_FOREACH(const DBusMenuItemKeys &item, removedList) {
QAction *action = m_actionForId.value(item.id);
if (!action) {
// We don't know this action. It probably is in a menu we haven't fetched yet.
continue;
}
Q_FOREACH(const QString &key, item.properties) {
updateActionProperty(action, key, QVariant());
}
}
}
void DBusMenuImporter::slotItemActivationRequested(int id, uint /*timestamp*/)
{
QAction *action = d->m_actionForId.value(id);
DMRETURN_IF_FAIL(action);
actionActivationRequested(action);
}
void DBusMenuImporter::slotGetLayoutFinished(QDBusPendingCallWatcher *watcher)
{
int parentId = watcher->property(DBUSMENU_PROPERTY_ID).toInt();
watcher->deleteLater();
d->m_nPendingRequests--;
QDBusPendingReply<uint, DBusMenuLayoutItem> reply = *watcher;
if (!reply.isValid()) {
qWarning() << reply.error().message();
if (d->m_nPendingRequests == 0) {
emit menuUpdated();
}
return;
}
#ifdef BENCHMARK
DMDEBUG << "- items received:" << sChrono.elapsed() << "ms";
#endif
DBusMenuLayoutItem rootItem = reply.argumentAt<1>();
QMenu *menu = d->menuForId(parentId);
if (!menu) {
qWarning() << "No menu for id" << parentId;
return;
}
menu->clear();
Q_FOREACH(const DBusMenuLayoutItem &dbusMenuItem, rootItem.children) {
QAction *action = d->createAction(dbusMenuItem.id, dbusMenuItem.properties, menu);
DBusMenuImporterPrivate::ActionForId::Iterator it = d->m_actionForId.find(dbusMenuItem.id);
if (it == d->m_actionForId.end()) {
d->m_actionForId.insert(dbusMenuItem.id, action);
} else {
delete *it;
*it = action;
}
menu->addAction(action);
connect(action, SIGNAL(triggered()),
&d->m_mapper, SLOT(map()));
d->m_mapper.setMapping(action, dbusMenuItem.id);
if( action->menu() )
{
d->refresh( dbusMenuItem.id );
}
}
if (d->m_nPendingRequests == 0) {
emit menuUpdated();
}
#ifdef BENCHMARK
DMDEBUG << "- Menu filled:" << sChrono.elapsed() << "ms";
#endif
}
void DBusMenuImporter::sendClickedEvent(int id)
{
d->sendEvent(id, QString("clicked"));
}
void DBusMenuImporter::updateMenu()
{
QMenu *menu = DBusMenuImporter::menu();
Q_ASSERT(menu);
QAction *action = menu->menuAction();
Q_ASSERT(action);
int id = action->property(DBUSMENU_PROPERTY_ID).toInt();
QDBusPendingCall call = d->m_interface->asyncCall("AboutToShow", id);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
watcher->setProperty(DBUSMENU_PROPERTY_ID, id);
connect(watcher, &QDBusPendingCallWatcher::finished, this,
&DBusMenuImporter::slotAboutToShowDBusCallFinished);
}
void DBusMenuImporter::slotAboutToShowDBusCallFinished(QDBusPendingCallWatcher *watcher)
{
int id = watcher->property(DBUSMENU_PROPERTY_ID).toInt();
watcher->deleteLater();
QDBusPendingReply<bool> reply = *watcher;
if (reply.isError()) {
menuUpdated();
qWarning() << "Call to AboutToShow() failed:" << reply.error().message();
return;
}
bool needRefresh = reply.argumentAt<0>();
QMenu *menu = d->menuForId(id);
DMRETURN_IF_FAIL(menu);
if (needRefresh || menu->actions().isEmpty()) {
d->m_idsRefreshedByAboutToShow << id;
d->refresh(id);
} else {
menuUpdated();
}
}
void DBusMenuImporter::slotMenuAboutToHide()
{
QMenu *menu = qobject_cast<QMenu*>(sender());
Q_ASSERT(menu);
QAction *action = menu->menuAction();
Q_ASSERT(action);
int id = action->property(DBUSMENU_PROPERTY_ID).toInt();
d->sendEvent(id, QString("closed"));
}
void DBusMenuImporter::slotMenuAboutToShow()
{
QMenu *menu = qobject_cast<QMenu*>(sender());
Q_ASSERT(menu);
QAction *action = menu->menuAction();
Q_ASSERT(action);
int id = action->property(DBUSMENU_PROPERTY_ID).toInt();
d->sendEvent(id, QString("opened"));
}
QMenu *DBusMenuImporter::createMenu(QWidget *parent)
{
return new QMenu(parent);
}
QIcon DBusMenuImporter::iconForName(const QString &/*name*/)
{
return QIcon();
}
#include "moc_dbusmenuimporter.cpp"

@ -0,0 +1,109 @@
/* This file is part of the dbusmenu-qt library
Copyright 2009 Canonical
Author: Aurelien Gateau <aurelien.gateau@canonical.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License (LGPL) as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later
version.
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 DBUSMENUIMPORTER_H
#define DBUSMENUIMPORTER_H
// Qt
#include <QtCore/QObject>
class QAction;
class QDBusAbstractInterface;
class QDBusPendingCallWatcher;
class QDBusVariant;
class QIcon;
class QMenu;
class DBusMenuImporterPrivate;
/**
* A DBusMenuImporter instance can recreate a menu serialized over DBus by
* DBusMenuExporter
*/
class DBusMenuImporter : public QObject
{
Q_OBJECT
public:
/**
* Creates a DBusMenuImporter listening over DBus on service, path
*/
DBusMenuImporter(const QString &service, const QString &path, QObject *parent = 0);
virtual ~DBusMenuImporter();
/**
* The menu created from listening to the DBusMenuExporter over DBus
*/
QMenu *menu() const;
public Q_SLOTS:
/**
* Load the menu
*
* Will emit menuUpdated() when complete.
* This should be done before showing a menu
*/
void updateMenu();
Q_SIGNALS:
/**
* Emitted after a call to updateMenu().
* @see updateMenu()
*/
void menuUpdated();
/**
* Emitted when the exporter was asked to activate an action
*/
void actionActivationRequested(QAction *);
protected:
/**
* Must create a menu, may be customized to fit host appearance.
* Default implementation creates a simple QMenu.
*/
virtual QMenu *createMenu(QWidget *parent);
/**
* Must convert a name into an icon.
* Default implementation returns a null icon.
*/
virtual QIcon iconForName(const QString &);
private Q_SLOTS:
void sendClickedEvent(int);
void slotMenuAboutToShow();
void slotMenuAboutToHide();
void slotAboutToShowDBusCallFinished(QDBusPendingCallWatcher *);
void slotItemActivationRequested(int id, uint timestamp);
void processPendingLayoutUpdates();
void slotLayoutUpdated(uint revision, int parentId);
void slotGetLayoutFinished(QDBusPendingCallWatcher *);
private:
Q_DISABLE_COPY(DBusMenuImporter)
DBusMenuImporterPrivate *const d;
friend class DBusMenuImporterPrivate;
// Use Q_PRIVATE_SLOT to avoid exposing DBusMenuItemList
Q_PRIVATE_SLOT(d, void slotItemsPropertiesUpdated(const DBusMenuItemList &updatedList, const DBusMenuItemKeysList &removedList))
};
#endif /* DBUSMENUIMPORTER_H */

@ -0,0 +1,82 @@
/* This file is part of the dbusmenu-qt library
Copyright 2009 Canonical
Author: Aurelien Gateau <aurelien.gateau@canonical.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License (LGPL) as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later
version.
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 "dbusmenushortcut_p.h"
// Qt
#include <QtGui/QKeySequence>
static const int QT_COLUMN = 0;
static const int DM_COLUMN = 1;
static void processKeyTokens(QStringList* tokens, int srcCol, int dstCol)
{
struct Row {
const char* zero;
const char* one;
const char* operator[](int col) const { return col == 0 ? zero : one; }
};
static const Row table[] =
{ {"Meta", "Super"},
{"Ctrl", "Control"},
// Special cases for compatibility with libdbusmenu-glib which uses
// "plus" for "+" and "minus" for "-".
// cf https://bugs.launchpad.net/libdbusmenu-qt/+bug/712565
{"+", "plus"},
{"-", "minus"},
{0, 0}
};
const Row* ptr = table;
for (; ptr->zero != 0; ++ptr) {
const char* from = (*ptr)[srcCol];
const char* to = (*ptr)[dstCol];
tokens->replaceInStrings(from, to);
}
}
DBusMenuShortcut DBusMenuShortcut::fromKeySequence(const QKeySequence& sequence)
{
QString string = sequence.toString();
DBusMenuShortcut shortcut;
QStringList tokens = string.split(", ");
Q_FOREACH(QString token, tokens) {
// Hack: Qt::CTRL | Qt::Key_Plus is turned into the string "Ctrl++",
// but we don't want the call to token.split() to consider the
// second '+' as a separator so we replace it with its final value.
token.replace("++", "+plus");
QStringList keyTokens = token.split('+');
processKeyTokens(&keyTokens, QT_COLUMN, DM_COLUMN);
shortcut << keyTokens;
}
return shortcut;
}
QKeySequence DBusMenuShortcut::toKeySequence() const
{
QStringList tmp;
Q_FOREACH(const QStringList& keyTokens_, *this) {
QStringList keyTokens = keyTokens_;
processKeyTokens(&keyTokens, DM_COLUMN, QT_COLUMN);
tmp << keyTokens.join(QLatin1String("+"));
}
QString string = tmp.join(QLatin1String(", "));
return QKeySequence::fromString(string);
}

@ -0,0 +1,39 @@
/* This file is part of the dbusmenu-qt library
Copyright 2009 Canonical
Author: Aurelien Gateau <aurelien.gateau@canonical.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License (LGPL) as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later
version.
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 DBUSMENUSHORTCUT_H
#define DBUSMENUSHORTCUT_H
// Qt
#include <QtCore/QMetaType>
#include <QtCore/QStringList>
class QKeySequence;
class DBusMenuShortcut : public QList<QStringList>
{
public:
QKeySequence toKeySequence() const;
static DBusMenuShortcut fromKeySequence(const QKeySequence&);
};
Q_DECLARE_METATYPE(DBusMenuShortcut)
#endif /* DBUSMENUSHORTCUT_H */

@ -0,0 +1,111 @@
/* This file is part of the dbusmenu-qt library
Copyright 2009 Canonical
Author: Aurelien Gateau <aurelien.gateau@canonical.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License (LGPL) as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later
version.
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 "dbusmenutypes_p.h"
// Local
#include "dbusmenushortcut_p.h"
// Qt
#include <QDBusArgument>
#include <QDBusMetaType>
//// DBusMenuItem
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItem &obj)
{
argument.beginStructure();
argument << obj.id << obj.properties;
argument.endStructure();
return argument;
}
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItem &obj)
{
argument.beginStructure();
argument >> obj.id >> obj.properties;
argument.endStructure();
return argument;
}
//// DBusMenuItemKeys
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItemKeys &obj)
{
argument.beginStructure();
argument << obj.id << obj.properties;
argument.endStructure();
return argument;
}
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItemKeys &obj)
{
argument.beginStructure();
argument >> obj.id >> obj.properties;
argument.endStructure();
return argument;
}
//// DBusMenuLayoutItem
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuLayoutItem &obj)
{
argument.beginStructure();
argument << obj.id << obj.properties;
argument.beginArray(qMetaTypeId<QDBusVariant>());
Q_FOREACH(const DBusMenuLayoutItem& child, obj.children) {
argument << QDBusVariant(QVariant::fromValue<DBusMenuLayoutItem>(child));
}
argument.endArray();
argument.endStructure();
return argument;
}
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuLayoutItem &obj)
{
argument.beginStructure();
argument >> obj.id >> obj.properties;
argument.beginArray();
while (!argument.atEnd()) {
QDBusVariant dbusVariant;
argument >> dbusVariant;
QDBusArgument childArgument = dbusVariant.variant().value<QDBusArgument>();
DBusMenuLayoutItem child;
childArgument >> child;
obj.children.append(child);
}
argument.endArray();
argument.endStructure();
return argument;
}
void DBusMenuTypes_register()
{
static bool registered = false;
if (registered) {
return;
}
qDBusRegisterMetaType<DBusMenuItem>();
qDBusRegisterMetaType<DBusMenuItemList>();
qDBusRegisterMetaType<DBusMenuItemKeys>();
qDBusRegisterMetaType<DBusMenuItemKeysList>();
qDBusRegisterMetaType<DBusMenuLayoutItem>();
qDBusRegisterMetaType<DBusMenuLayoutItemList>();
qDBusRegisterMetaType<DBusMenuShortcut>();
registered = true;
}

@ -0,0 +1,93 @@
/* This file is part of the dbusmenu-qt library
Copyright 2009 Canonical
Author: Aurelien Gateau <aurelien.gateau@canonical.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License (LGPL) as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later
version.
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 DBUSMENUTYPES_P_H
#define DBUSMENUTYPES_P_H
// Qt
#include <QtCore/QList>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
class QDBusArgument;
//// DBusMenuItem
/**
* Internal struct used to communicate on DBus
*/
struct DBusMenuItem
{
int id;
QVariantMap properties;
};
Q_DECLARE_METATYPE(DBusMenuItem)
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItem &item);
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItem &item);
typedef QList<DBusMenuItem> DBusMenuItemList;
Q_DECLARE_METATYPE(DBusMenuItemList)
//// DBusMenuItemKeys
/**
* Represents a list of keys for a menu item
*/
struct DBusMenuItemKeys
{
int id;
QStringList properties;
};
Q_DECLARE_METATYPE(DBusMenuItemKeys)
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItemKeys &);
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItemKeys &);
typedef QList<DBusMenuItemKeys> DBusMenuItemKeysList;
Q_DECLARE_METATYPE(DBusMenuItemKeysList)
//// DBusMenuLayoutItem
/**
* Represents an item with its children. GetLayout() returns a
* DBusMenuLayoutItemList.
*/
struct DBusMenuLayoutItem;
struct DBusMenuLayoutItem
{
int id;
QVariantMap properties;
QList<DBusMenuLayoutItem> children;
};
Q_DECLARE_METATYPE(DBusMenuLayoutItem)
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuLayoutItem &);
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuLayoutItem &);
typedef QList<DBusMenuLayoutItem> DBusMenuLayoutItemList;
Q_DECLARE_METATYPE(DBusMenuLayoutItemList)
void DBusMenuTypes_register();
#endif /* DBUSMENUTYPES_P_H */

@ -0,0 +1,64 @@
/* This file is part of the dbusmenu-qt library
Copyright 2010 Canonical
Author: Aurelien Gateau <aurelien.gateau@canonical.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License (LGPL) as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later
version.
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 "utils_p.h"
// Qt
#include <QString>
QString swapMnemonicChar(const QString &in, const char src, const char dst)
{
QString out;
bool mnemonicFound = false;
for (int pos = 0; pos < in.length(); ) {
QChar ch = in[pos];
if (ch == src) {
if (pos == in.length() - 1) {
// 'src' at the end of string, skip it
++pos;
} else {
if (in[pos + 1] == src) {
// A real 'src'
out += src;
pos += 2;
} else if (!mnemonicFound) {
// We found the mnemonic
mnemonicFound = true;
out += dst;
++pos;
} else {
// We already have a mnemonic, just skip the char
++pos;
}
}
} else if (ch == dst) {
// Escape 'dst'
out += dst;
out += dst;
++pos;
} else {
out += ch;
++pos;
}
}
return out;
}

@ -0,0 +1,31 @@
/* This file is part of the dbusmenu-qt library
Copyright 2010 Canonical
Author: Aurelien Gateau <aurelien.gateau@canonical.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License (LGPL) as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later
version.
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 UTILS_P_H
#define UTILS_P_H
class QString;
/**
* Swap mnemonic char: Qt uses '&', while dbusmenu uses '_'
*/
QString swapMnemonicChar(const QString &in, const char src, const char dst);
#endif /* UTILS_P_H */
Loading…
Cancel
Save