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: 123992wilder-5.14
parent
fdff218d1c
commit
3d0a9cd462
10 changed files with 1068 additions and 2 deletions
@ -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…
Reference in new issue