aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorRenato Araujo Oliveira Filho <renato.filho@canonical.com>2012-09-11 17:57:08 -0300
committerRenato Araujo Oliveira Filho <renato.filho@canonical.com>2012-09-11 17:57:08 -0300
commit6bacc0d5db885a72202cb7f80e505642a36052d4 (patch)
tree608aa95f959b9e362d7813476e59d37c6c07ddf6 /src/common
parent31f8103187d2e27b1bb76eaed10dc9c9be226c90 (diff)
downloadqmenumodel-6bacc0d5db885a72202cb7f80e505642a36052d4.tar.gz
qmenumodel-6bacc0d5db885a72202cb7f80e505642a36052d4.tar.bz2
qmenumodel-6bacc0d5db885a72202cb7f80e505642a36052d4.zip
Splitted qmenumodel plugin in two libraries to make it testable.
Create unit test for qmenumodel library.
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt42
-rw-r--r--src/common/qdbusactiongroup.cpp193
-rw-r--r--src/common/qdbusactiongroup.h63
-rw-r--r--src/common/qdbusmenumodel.cpp66
-rw-r--r--src/common/qdbusmenumodel.h47
-rw-r--r--src/common/qdbusobject.cpp127
-rw-r--r--src/common/qdbusobject.h65
-rw-r--r--src/common/qmenumodel.cpp241
-rw-r--r--src/common/qmenumodel.h45
9 files changed, 889 insertions, 0 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
new file mode 100644
index 0000000..1428000
--- /dev/null
+++ b/src/common/CMakeLists.txt
@@ -0,0 +1,42 @@
+project(qmenumodelcommon)
+
+set(QMENUMODELCOMMON_SRC
+ qmenumodel.cpp
+ qdbusobject.cpp
+ qdbusmenumodel.cpp
+ qdbusactiongroup.cpp
+)
+
+set(QMENUMODELCOMMON_HEADERS
+ qmenumodel.h
+ qdbusobject.h
+ qdbusmenumodel.h
+ qdbusactiongroup.h
+)
+
+qt4_wrap_cpp(QMENUMODELCOMMON_MOC
+ ${QMENUMODELCOMMON_HEADERS}
+)
+
+add_library(qmenumodelcommon STATIC
+ ${QMENUMODELCOMMON_SRC}
+ ${QMENUMODELCOMMON_MOC}
+)
+
+set_target_properties(qmenumodelcommon PROPERTIES COMPILE_FLAGS -fPIC)
+
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${QT_INCLUDE_DIR}
+ ${QT_QTCORE_INCLUDE_DIR}
+ ${QT_QTGUI_INCLUDE_DIR}
+ ${GLIB_INCLUDE_DIRS}
+ ${GIO_INCLUDE_DIRS}
+)
+
+target_link_libraries(qmenumodelcommon
+ ${QT_QTCORE_LIBRARY}
+ ${QT_QTGUI_LIBRARY}
+ ${GLIB_LDFLAGS}
+ ${GIO_LDFLAGS}
+)
diff --git a/src/common/qdbusactiongroup.cpp b/src/common/qdbusactiongroup.cpp
new file mode 100644
index 0000000..8475c29
--- /dev/null
+++ b/src/common/qdbusactiongroup.cpp
@@ -0,0 +1,193 @@
+#include "qdbusactiongroup.h"
+
+#include <QDebug>
+
+#include <gio/gio.h>
+
+QDBusActionGroup::QDBusActionGroup(QObject *parent)
+ :QObject(parent),
+ m_actionGroup(NULL)
+{
+}
+
+QDBusActionGroup::~QDBusActionGroup()
+{
+ clear();
+}
+
+QAction *QDBusActionGroup::getAction(const QString &actionName)
+{
+ Q_FOREACH(QAction *act, m_actions) {
+ if (act->text() == actionName) {
+ return act;
+ }
+ }
+
+ return NULL;
+}
+
+void QDBusActionGroup::serviceVanish(GDBusConnection *)
+{
+ clear();
+}
+
+void QDBusActionGroup::serviceAppear(GDBusConnection *connection)
+{
+ GDBusActionGroup *ag = g_dbus_action_group_get(connection,
+ busName().toLatin1(),
+ objectPath().toLatin1());
+ setActionGroup(ag);
+ if (ag == NULL) {
+ stop();
+ }
+}
+
+void QDBusActionGroup::start()
+{
+ QDBusObject::connect();
+}
+
+void QDBusActionGroup::stop()
+{
+ QDBusObject::disconnect();
+}
+
+void QDBusActionGroup::busTypeChanged(BusType)
+{
+ busTypeChanged();
+}
+
+void QDBusActionGroup::busNameChanged(const QString &)
+{
+ busNameChanged();
+}
+
+void QDBusActionGroup::objectPathChanged(const QString &objectPath)
+{
+ objectPathChanged();
+}
+
+void QDBusActionGroup::statusChanged(ConnectionStatus status)
+{
+ statusChanged();
+}
+
+void QDBusActionGroup::setIntBusType(int busType)
+{
+ if ((busType > None) && (busType < LastBusType)) {
+ setBusType(static_cast<BusType>(busType));
+ }
+}
+
+void QDBusActionGroup::setActionGroup(GDBusActionGroup *ag)
+{
+ if (m_actionGroup == reinterpret_cast<GActionGroup*>(ag)) {
+ return;
+ }
+
+ if (m_actionGroup) {
+ g_signal_handler_disconnect(m_actionGroup, m_signalActionAddId);
+ g_signal_handler_disconnect(m_actionGroup, m_signalActionRemovedId);
+ m_signalActionAddId = m_signalActionRemovedId = 0;
+ g_object_unref(m_actionGroup);
+ }
+
+ m_actionGroup = reinterpret_cast<GActionGroup*>(ag);
+
+ if (m_actionGroup) {
+
+ m_signalActionAddId = g_signal_connect(m_actionGroup,
+ "action-add",
+ G_CALLBACK(QDBusActionGroup::onActionAdded),
+ this);
+
+ m_signalActionRemovedId = g_signal_connect(m_actionGroup,
+ "action-removed",
+ G_CALLBACK(QDBusActionGroup::onActionRemoved),
+ this);
+
+ gchar **actionNames = g_action_group_list_actions(m_actionGroup);
+ for(int i=0; actionNames[i] != NULL; i++) {
+ addAction(actionNames[i]);
+ }
+ g_strfreev(actionNames);
+ }
+}
+
+void QDBusActionGroup::addAction(const char *actionName)
+{
+ QAction *act = new QAction(actionName, this);
+
+ act->setEnabled(g_action_group_get_action_enabled(m_actionGroup, actionName));
+
+ const GVariantType *stateType = g_action_group_get_action_state_type(m_actionGroup, actionName);
+ if (stateType == G_VARIANT_TYPE_BOOLEAN) {
+ act->setCheckable(true);
+
+ GVariant *actState = g_action_group_get_action_state(m_actionGroup, actionName);
+ if (actState != NULL) {
+ act->setChecked(g_variant_get_boolean(actState));
+ g_variant_unref(actState);
+ }
+ }
+
+ // remove any older action with the same name
+ removeAction(actionName);
+
+ m_actions.insert(act);
+}
+
+void QDBusActionGroup::removeAction(const char *actionName)
+{
+ Q_FOREACH(QAction *act, m_actions) {
+ if (act->text() == actionName) {
+ m_actions.remove(act);
+ delete act;
+ break;
+ }
+ }
+}
+
+void QDBusActionGroup::updateAction(const char *actionName, GVariant *state)
+{
+ QAction *action = getAction(actionName);
+ if ((action != NULL) && (state != NULL)) {
+
+ const GVariantType *stateType = g_variant_get_type(state);
+ if (stateType == G_VARIANT_TYPE_BOOLEAN) {
+ action->setChecked(g_variant_get_boolean(state));
+ }
+ }
+}
+
+void QDBusActionGroup::clear()
+{
+ Q_FOREACH(QAction *act, m_actions) {
+ delete act;
+ }
+ m_actions.clear();
+
+ if (m_actionGroup != NULL) {
+ g_object_unref(m_actionGroup);
+ m_actionGroup = NULL;
+ }
+}
+
+void QDBusActionGroup::onActionAdded(GDBusActionGroup *, gchar *actionName, gpointer data)
+{
+ QDBusActionGroup *self = reinterpret_cast<QDBusActionGroup*>(data);
+ self->addAction(actionName);
+}
+
+void QDBusActionGroup::onActionRemoved(GDBusActionGroup *, gchar *actionName, gpointer data)
+{
+ QDBusActionGroup *self = reinterpret_cast<QDBusActionGroup*>(data);
+ self->removeAction(actionName);
+
+}
+
+void QDBusActionGroup::onActionStateChanged(GDBusActionGroup *ag, gchar *actionName, GVariant *value, gpointer data)
+{
+ QDBusActionGroup *self = reinterpret_cast<QDBusActionGroup*>(data);
+ self->updateAction(actionName, value);
+}
diff --git a/src/common/qdbusactiongroup.h b/src/common/qdbusactiongroup.h
new file mode 100644
index 0000000..fe743b6
--- /dev/null
+++ b/src/common/qdbusactiongroup.h
@@ -0,0 +1,63 @@
+#ifndef QDBUSACTIONGROUP_H
+#define QDBUSACTIONGROUP_H
+
+#include "qdbusobject.h"
+
+#include <QObject>
+#include <QAction>
+#include <QSet>
+
+class QDBusActionGroup : public QObject, public QDBusObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int busType READ busType WRITE setIntBusType NOTIFY busTypeChanged)
+ Q_PROPERTY(QString busName READ busName WRITE setBusName NOTIFY busNameChanged)
+ Q_PROPERTY(QString objectPath READ objectPath WRITE setObjectPath NOTIFY objectPathChanged)
+ Q_PROPERTY(int status READ status NOTIFY statusChanged)
+
+public:
+ QDBusActionGroup(QObject *parent=0);
+ ~QDBusActionGroup();
+
+Q_SIGNALS:
+ void busTypeChanged();
+ void busNameChanged();
+ void objectPathChanged();
+ void statusChanged();
+
+public Q_SLOTS:
+ void start();
+ void stop();
+ QAction *getAction(const QString &actionName);
+
+protected:
+ virtual void serviceAppear(GDBusConnection *connection);
+ virtual void serviceVanish(GDBusConnection *connection);
+
+ virtual void busTypeChanged(BusType type);
+ virtual void busNameChanged(const QString &busNameChanged);
+ virtual void objectPathChanged(const QString &objectPath);
+ virtual void statusChanged(ConnectionStatus status);
+
+private:
+ GActionGroup *m_actionGroup;
+ QSet<QAction*> m_actions;
+ int m_signalActionAddId;
+ int m_signalActionRemovedId;
+
+ // workaround to support int as bustType
+ void setIntBusType(int busType);
+
+ void setActionGroup(GDBusActionGroup *ag);
+ void addAction(const char *actionName);
+ void removeAction(const char *actionName);
+ void updateAction(const char *actionName, GVariant *state);
+ void clear();
+
+ // glib slots
+ static void onActionAdded(GDBusActionGroup *ag, gchar *actionName, gpointer data);
+ static void onActionRemoved(GDBusActionGroup *ag, gchar *actionName, gpointer data);
+ static void onActionStateChanged(GDBusActionGroup *ag, gchar *actionName, GVariant *value, gpointer data);
+};
+
+#endif
diff --git a/src/common/qdbusmenumodel.cpp b/src/common/qdbusmenumodel.cpp
new file mode 100644
index 0000000..6e35c85
--- /dev/null
+++ b/src/common/qdbusmenumodel.cpp
@@ -0,0 +1,66 @@
+#include "qdbusmenumodel.h"
+#include <QDebug>
+
+QDBusMenuModel::QDBusMenuModel(QObject *parent)
+ :QMenuModel(0, parent)
+{
+}
+
+QDBusMenuModel::~QDBusMenuModel()
+{
+}
+
+void QDBusMenuModel::start()
+{
+ QDBusObject::connect();
+}
+
+void QDBusMenuModel::stop()
+{
+ QDBusObject::disconnect();
+}
+
+void QDBusMenuModel::serviceVanish(GDBusConnection *)
+{
+ setMenuModel(NULL);
+}
+
+void QDBusMenuModel::serviceAppear(GDBusConnection *connection)
+{
+ GMenuModel *model = reinterpret_cast<GMenuModel*>(g_dbus_menu_model_get(connection,
+ busName().toLatin1(),
+ objectPath().toLatin1()));
+ setMenuModel(model);
+ if (model == NULL) {
+ stop();
+ }
+}
+
+/*
+void QDBusMenuModel::busTypeChanged(BusType)
+{
+ busTypeChanged();
+}
+
+void QDBusMenuModel::busNameChanged(const QString &)
+{
+ busNameChanged();
+}
+
+void QDBusMenuModel::objectPathChanged(const QString &objectPath)
+{
+ objectPathChanged();
+}
+
+void QDBusMenuModel::statusChanged(ConnectionStatus status)
+{
+ statusChanged();
+}
+*/
+
+void QDBusMenuModel::setIntBusType(int busType)
+{
+ if ((busType > None) && (busType < LastBusType)) {
+ setBusType(static_cast<BusType>(busType));
+ }
+}
diff --git a/src/common/qdbusmenumodel.h b/src/common/qdbusmenumodel.h
new file mode 100644
index 0000000..4fc439d
--- /dev/null
+++ b/src/common/qdbusmenumodel.h
@@ -0,0 +1,47 @@
+#ifndef QDBUSMENUMODEL_H
+#define QDBUSMENUMODEL_H
+
+#include "qdbusobject.h"
+#include "qmenumodel.h"
+
+#include <gio/gio.h>
+
+class QDBusMenuModel : public QMenuModel, public QDBusObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int busType READ busType WRITE setIntBusType NOTIFY busTypeChanged)
+ Q_PROPERTY(QString busName READ busName WRITE setBusName NOTIFY busNameChanged)
+ Q_PROPERTY(QString objectPath READ objectPath WRITE setObjectPath NOTIFY objectPathChanged)
+ Q_PROPERTY(int status READ status NOTIFY statusChanged)
+
+public:
+ QDBusMenuModel(QObject *parent=0);
+ ~QDBusMenuModel();
+
+Q_SIGNALS:
+ void busTypeChanged(BusType type);
+ void busNameChanged(const QString &busNameChanged);
+ void objectPathChanged(const QString &objectPath);
+ void statusChanged(QDBusObject::ConnectionStatus status);
+
+public Q_SLOTS:
+ void start();
+ void stop();
+
+protected:
+ virtual void serviceAppear(GDBusConnection *connection);
+ virtual void serviceVanish(GDBusConnection *connection);
+
+ /*
+ virtual void busTypeChanged(BusType type);
+ virtual void busNameChanged(const QString &busNameChanged);
+ virtual void objectPathChanged(const QString &objectPath);
+ virtual void statusChanged(ConnectionStatus status);
+ */
+
+private:
+ // workaround to support int as bustType
+ void setIntBusType(int busType);
+};
+
+#endif
diff --git a/src/common/qdbusobject.cpp b/src/common/qdbusobject.cpp
new file mode 100644
index 0000000..6dec6d9
--- /dev/null
+++ b/src/common/qdbusobject.cpp
@@ -0,0 +1,127 @@
+#include "qdbusobject.h"
+
+#include <QDebug>
+
+
+
+QDBusObject::QDBusObject()
+ :m_watchId(0),
+ m_busType(None),
+ m_status(QDBusObject::Disconnected)
+{
+ qDebug() << "DBUS CREATED";
+ qRegisterMetaType<QDBusObject::ConnectionStatus>("QDBusObject::ConnectionStatus");
+}
+
+QDBusObject::~QDBusObject()
+{
+ if (m_watchId != 0) {
+ g_bus_unwatch_name (m_watchId);
+ m_watchId = 0;
+ }
+}
+
+QDBusObject::BusType QDBusObject::busType() const
+{
+ return m_busType;
+}
+
+void QDBusObject::setBusType(QDBusObject::BusType type)
+{
+ if (m_busType != type) {
+ if (m_status != QDBusObject::Disconnected)
+ disconnect();
+ m_busType = type;
+ busTypeChanged(m_busType);
+ }
+}
+
+QString QDBusObject::busName() const
+{
+ return m_busName;
+}
+
+void QDBusObject::setBusName(const QString &busName)
+{
+ if (m_busName != busName) {
+ if (m_status != QDBusObject::Disconnected)
+ disconnect();
+ m_busName = busName;
+ busNameChanged(m_busName);
+ }
+}
+
+QString QDBusObject::objectPath() const
+{
+ return m_objectPath;
+}
+
+void QDBusObject::setObjectPath(const QString &objectPath)
+{
+ if (m_objectPath != objectPath) {
+ if (m_status != QDBusObject::Disconnected)
+ disconnect();
+ m_objectPath = objectPath;
+ objectPathChanged(m_objectPath);
+ }
+}
+
+void QDBusObject::setStatus(QDBusObject::ConnectionStatus status)
+{
+ if (m_status != status) {
+ m_status = status;
+ statusChanged(m_status);
+ }
+}
+
+QDBusObject::ConnectionStatus QDBusObject::status() const
+{
+ return m_status;
+}
+
+void QDBusObject::connect()
+{
+ if (m_status != QDBusObject::Disconnected) {
+ return;
+ } else if ((m_busType > None) && !m_objectPath.isEmpty() && !m_busName.isEmpty()) {
+ GBusType type = m_busType == SessionBus ? G_BUS_TYPE_SESSION : G_BUS_TYPE_SYSTEM;
+ m_watchId = g_bus_watch_name (type,
+ m_busName.toLatin1(),
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ QDBusObject::onServiceAppeared,
+ QDBusObject::onServiceFanished,
+ this,
+ NULL);
+
+ setStatus(QDBusObject::Connecting);
+ } else {
+ qWarning() << "Invalid dbus connection args";
+ }
+}
+
+void QDBusObject::disconnect()
+{
+ if (m_status != QDBusObject::Disconnected) {
+ g_bus_unwatch_name (m_watchId);
+ m_watchId = 0;
+ setStatus(QDBusObject::Disconnected);
+ }
+}
+
+void QDBusObject::onServiceAppeared(GDBusConnection *connection, const gchar *, const gchar *, gpointer data)
+{
+ QDBusObject *self = reinterpret_cast<QDBusObject*>(data);
+ qDebug() << "service appear";
+
+ self->setStatus(QDBusObject::Connected);
+ self->serviceAppear(connection);
+}
+
+void QDBusObject::onServiceFanished(GDBusConnection *connection, const gchar *, gpointer data)
+{
+ QDBusObject *self = reinterpret_cast<QDBusObject*>(data);
+ qDebug() << "service disappear";
+
+ self->setStatus(QDBusObject::Connecting);
+ self->serviceVanish(connection);
+}
diff --git a/src/common/qdbusobject.h b/src/common/qdbusobject.h
new file mode 100644
index 0000000..a2dc91c
--- /dev/null
+++ b/src/common/qdbusobject.h
@@ -0,0 +1,65 @@
+#ifndef QDBUSOBJECT_H
+#define QDBUSOBJECT_H
+
+#include <QObject>
+
+#include <gio/gio.h>
+
+class QDBusObject
+{
+public:
+ enum BusType {
+ None = 0,
+ SessionBus,
+ SystemBus,
+ LastBusType
+ };
+
+ enum ConnectionStatus {
+ Disconnected = 0,
+ Connecting,
+ Connected
+ };
+
+ QDBusObject();
+ ~QDBusObject();
+
+ BusType busType() const;
+ void setBusType(BusType type);
+
+ QString busName() const;
+ void setBusName(const QString &busName);
+
+ QString objectPath() const;
+ void setObjectPath(const QString &busName);
+
+ ConnectionStatus status() const;
+
+ void connect();
+ void disconnect();
+
+protected:
+ virtual void serviceAppear(GDBusConnection *connection) = 0;
+ virtual void serviceVanish(GDBusConnection *connection) = 0;
+
+ // notify functions
+ virtual void busTypeChanged(BusType type) = 0;
+ virtual void busNameChanged(const QString &busNameChanged) = 0;
+ virtual void objectPathChanged(const QString &objectPath) = 0;
+ virtual void statusChanged(ConnectionStatus status) = 0;
+
+private:
+ guint m_watchId;
+ BusType m_busType;
+ QString m_busName;
+ QString m_objectPath;
+ ConnectionStatus m_status;
+
+ void setStatus(ConnectionStatus status);
+
+ // glib slots
+ static void onServiceAppeared(GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer data);
+ static void onServiceFanished(GDBusConnection *connection, const gchar *name, gpointer data);
+};
+
+#endif
diff --git a/src/common/qmenumodel.cpp b/src/common/qmenumodel.cpp
new file mode 100644
index 0000000..6e7483c
--- /dev/null
+++ b/src/common/qmenumodel.cpp
@@ -0,0 +1,241 @@
+#include "qmenumodel.h"
+
+#include <QDebug>
+
+QMenuModel::QMenuModel(GMenuModel *other, QObject *parent)
+ : QAbstractListModel(parent),
+ m_menuModel(0),
+ m_signalChangedId(0)
+{
+ static QHash<int, QByteArray> rolesNames;
+ if (rolesNames.empty()) {
+ rolesNames[Action] = "action";
+ rolesNames[Label] = "label";
+ rolesNames[LinkSection] = "linkSection";
+ rolesNames[LinkSubMenu] = "linkSubMenu";
+ rolesNames[Extra] = "extra";
+ }
+ setRoleNames(rolesNames);
+ setMenuModel(other);
+}
+
+QMenuModel::~QMenuModel()
+{
+ setMenuModel(NULL);
+}
+
+void QMenuModel::setMenuModel(GMenuModel *other)
+{
+ if (m_menuModel == other) {
+ return;
+ }
+
+ beginResetModel();
+
+ if (m_menuModel) {
+ g_signal_handler_disconnect(m_menuModel, m_signalChangedId);
+ m_signalChangedId = 0;
+ g_object_unref(m_menuModel);
+ }
+
+ m_menuModel = other;
+
+ if (m_menuModel) {
+ // this will trigger the menu load
+ (void) g_menu_model_get_n_items(m_menuModel);
+ //qDebug() << "Menu size:" << g_menu_model_get_n_items(m_menuModel);
+ m_signalChangedId = g_signal_connect(m_menuModel,
+ "items-changed",
+ G_CALLBACK(QMenuModel::onItemsChanged),
+ this);
+ }
+
+ endResetModel();
+}
+
+GMenuModel *QMenuModel::menuModel() const
+{
+ return m_menuModel;
+}
+
+/* QAbstractItemModel */
+int QMenuModel::columnCount(const QModelIndex &) const
+{
+ return 1;
+}
+
+QVariant QMenuModel::data(const QModelIndex &index, int role) const
+{
+ QVariant attribute;
+ int rowCountValue = rowCount();
+
+ if ((rowCountValue > 0) && (index.row() >= 0) && (index.row() < rowCountValue)) {
+ if (m_menuModel) {
+ switch (role) {
+ case Action:
+ attribute = getStringAttribute(index, G_MENU_ATTRIBUTE_ACTION);
+ break;
+ case Label:
+ attribute = getStringAttribute(index, G_MENU_ATTRIBUTE_LABEL);
+ break;
+ case LinkSection:
+ attribute = getLink(index, G_MENU_LINK_SECTION);
+ break;
+ case LinkSubMenu:
+ attribute = getLink(index, G_MENU_LINK_SUBMENU);
+ break;
+ case Extra:
+ attribute = getExtraProperties(index);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return attribute;
+}
+
+QModelIndex QMenuModel::parent(const QModelIndex &index) const
+{
+ return QModelIndex();
+}
+
+int QMenuModel::rowCount(const QModelIndex &) const
+{
+ if (m_menuModel) {
+ return g_menu_model_get_n_items(m_menuModel);
+ }
+ return 0;
+}
+
+QVariant QMenuModel::getStringAttribute(const QModelIndex &index,
+ const QString &attribute) const
+{
+ QVariant result;
+ gchar* value = NULL;
+ g_menu_model_get_item_attribute(m_menuModel,
+ index.row(),
+ attribute.toLatin1(),
+ "s", &value);
+ if (value) {
+ result = QVariant(QString::fromLatin1(value));
+ g_free(value);
+ }
+ return result;
+}
+
+QVariant QMenuModel::getLink(const QModelIndex &index,
+ const QString &linkName) const
+{
+ GMenuModel *link;
+
+ link = g_menu_model_get_item_link(m_menuModel,
+ index.row(),
+ linkName.toLatin1());
+
+ if (link) {
+ QMenuModel *other = new QMenuModel(link, const_cast<QMenuModel*>(this));
+ return QVariant::fromValue<QObject*>(other);
+ }
+
+ return QVariant();
+}
+
+QVariant QMenuModel::getExtraProperties(const QModelIndex &index) const
+{
+ GMenuAttributeIter *iter = g_menu_model_iterate_item_attributes(m_menuModel, index.row());
+ if (iter == NULL) {
+ return QVariant();
+ }
+
+ QObject *extra = new QObject(const_cast<QMenuModel*>(this));
+ const gchar *attrName = NULL;
+ GVariant *value = NULL;
+ while (g_menu_attribute_iter_get_next (iter, &attrName, &value)) {
+ qDebug() << "Set property:" << attrName;
+ if (strncmp("x-", attrName, 2) == 0) {
+ extra->setProperty(attrName, parseGVariant(value));
+ }
+ }
+
+ return QVariant::fromValue<QObject*>(extra);
+}
+
+
+void QMenuModel::onItemsChanged(GMenuModel *,
+ gint position,
+ gint removed,
+ gint added,
+ gpointer data)
+{
+ QMenuModel *self = reinterpret_cast<QMenuModel*>(data);
+ //qDebug() << "Item Changed" << position << removed << added;
+
+ if (removed > 0) {
+ self->beginRemoveRows(QModelIndex(), position, position + removed - 1);
+ self->endRemoveRows();
+ }
+
+ if (added > 0) {
+ self->beginInsertRows(QModelIndex(), position, position + added - 1);
+ self->endInsertRows();
+ }
+}
+
+QVariant QMenuModel::parseGVariant(GVariant *value)
+{
+ QVariant result;
+ if (value == NULL) {
+ return result;
+ }
+
+ const GVariantType *type = g_variant_get_type(value);
+ if (g_variant_type_equal(type, G_VARIANT_TYPE_BOOLEAN)) {
+ result.setValue((bool)g_variant_get_boolean(value));
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTE)) {
+ result.setValue(g_variant_get_byte(value));
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT16)) {
+ result.setValue(g_variant_get_int16(value));
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT16)) {
+ result.setValue(g_variant_get_uint16(value));
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT32)) {
+ result.setValue(g_variant_get_int32(value));
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT32)) {
+ result.setValue(g_variant_get_uint32(value));
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT64)) {
+ result.setValue(g_variant_get_int64(value));
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT64)) {
+ result.setValue(g_variant_get_uint64(value));
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_DOUBLE)) {
+ result.setValue(g_variant_get_double(value));
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_STRING)) {
+ gsize size = 0;
+ const gchar *v = g_variant_get_string(value, &size);
+ result.setValue(QString::fromLatin1(v, size));
+ } else {
+ qWarning() << "Unsupported GVariant value";
+ }
+
+ /* TODO: implement convertions to others types
+ * G_VARIANT_TYPE_HANDLE
+ * G_VARIANT_TYPE_OBJECT_PATH
+ * G_VARIANT_TYPE_SIGNATURE
+ * G_VARIANT_TYPE_VARIANT
+ * G_VARIANT_TYPE_ANY
+ * G_VARIANT_TYPE_BASIC
+ * G_VARIANT_TYPE_MAYBE
+ * G_VARIANT_TYPE_ARRAY
+ * G_VARIANT_TYPE_TUPLE
+ * G_VARIANT_TYPE_UNIT
+ * G_VARIANT_TYPE_DICT_ENTRY
+ * G_VARIANT_TYPE_DICTIONARY
+ * G_VARIANT_TYPE_STRING_ARRAY
+ * G_VARIANT_TYPE_BYTESTRING
+ * G_VARIANT_TYPE_OBJECT_PATH_ARRAY
+ * G_VARIANT_TYPE_BYTESTRING_ARRAY
+ * G_VARIANT_TYPE_VARDICT
+ */
+
+ return result;
+}
+
diff --git a/src/common/qmenumodel.h b/src/common/qmenumodel.h
new file mode 100644
index 0000000..f1e0b61
--- /dev/null
+++ b/src/common/qmenumodel.h
@@ -0,0 +1,45 @@
+#ifndef QMENUMODEL_H
+#define QMENUMODEL_H
+
+#include <QAbstractListModel>
+#include <gio/gio.h>
+
+class QMenuModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ enum MenuRoles {
+ Action = 0,
+ Label,
+ LinkSection,
+ LinkSubMenu,
+ Extra
+ };
+
+ ~QMenuModel();
+
+ /* QAbstractItemModel */
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ QModelIndex parent (const QModelIndex &index) const;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+
+protected:
+ QMenuModel(GMenuModel *other=0, QObject *parent=0);
+ void setMenuModel(GMenuModel *model);
+ GMenuModel *menuModel() const;
+
+private:
+ GMenuModel *m_menuModel;
+ guint m_signalChangedId;
+
+ QVariant getStringAttribute(const QModelIndex &index, const QString &attribute) const;
+ QVariant getLink(const QModelIndex &index, const QString &linkName) const;
+ QVariant getExtraProperties(const QModelIndex &index) const;
+
+ static void onItemsChanged(GMenuModel *model, gint position, gint removed, gint added, gpointer data);
+ static QVariant parseGVariant(GVariant *value);
+};
+
+#endif