From 72cd124bc6a71d7be3bf9e6602c9de55156508ee Mon Sep 17 00:00:00 2001 From: Nick Dedekind Date: Wed, 21 Aug 2013 21:39:47 +0100 Subject: Added UnityMenuAction for out-of-line actions. Action muxer copied to submenus. --- libqmenumodel/QMenuModel/plugin.cpp | 2 + libqmenumodel/src/CMakeLists.txt | 4 + libqmenumodel/src/gtk/gtkactionobserveritem.c | 152 ++++++++++++++++++++++++++ libqmenumodel/src/gtk/gtkactionobserveritem.h | 67 ++++++++++++ libqmenumodel/src/unitymenuaction.cpp | 136 +++++++++++++++++++++++ libqmenumodel/src/unitymenuaction.h | 73 +++++++++++++ libqmenumodel/src/unitymenumodel.cpp | 142 +++++++++++++++++++++++- libqmenumodel/src/unitymenumodel.h | 10 ++ 8 files changed, 585 insertions(+), 1 deletion(-) create mode 100644 libqmenumodel/src/gtk/gtkactionobserveritem.c create mode 100644 libqmenumodel/src/gtk/gtkactionobserveritem.h create mode 100644 libqmenumodel/src/unitymenuaction.cpp create mode 100644 libqmenumodel/src/unitymenuaction.h (limited to 'libqmenumodel') diff --git a/libqmenumodel/QMenuModel/plugin.cpp b/libqmenumodel/QMenuModel/plugin.cpp index 0629099..117a37a 100644 --- a/libqmenumodel/QMenuModel/plugin.cpp +++ b/libqmenumodel/QMenuModel/plugin.cpp @@ -22,6 +22,7 @@ #include "qdbusmenumodel.h" #include "qdbusactiongroup.h" #include "qstateaction.h" +#include "unitymenuaction.h" #include "unitymenumodel.h" #include "unitythemediconprovider.h" @@ -44,4 +45,5 @@ void QMenuModelQmlPlugin::registerTypes(const char *uri) qmlRegisterType(uri, 0, 1, "QDBusMenuModel"); qmlRegisterType(uri, 0, 1, "QDBusActionGroup"); qmlRegisterType(uri, 0, 1, "UnityMenuModel"); + qmlRegisterType(uri, 0, 1, "UnityMenuAction"); } diff --git a/libqmenumodel/src/CMakeLists.txt b/libqmenumodel/src/CMakeLists.txt index ad7c6ab..be278dd 100644 --- a/libqmenumodel/src/CMakeLists.txt +++ b/libqmenumodel/src/CMakeLists.txt @@ -10,6 +10,7 @@ set(QMENUMODEL_SRC qdbusmenumodel.cpp qdbusactiongroup.cpp qstateaction.cpp + unitymenuaction.cpp unitymenumodel.cpp unitymenumodelevents.cpp unitythemediconprovider.cpp @@ -19,6 +20,8 @@ set(QMENUMODEL_SRC gtk/gtkactionobservable.h gtk/gtkactionobserver.c gtk/gtkactionobserver.h + gtk/gtkactionobserveritem.c + gtk/gtkactionobserveritem.h gtk/gtkmenutracker.c gtk/gtkmenutracker.h gtk/gtkmenutrackeritem.c @@ -58,6 +61,7 @@ set(QMENUMODEL_HEADERS qdbusobject.h qmenumodel.h qstateaction.h + unitymenuaction.h unitymenumodel.h unitythemediconprovider.h ) diff --git a/libqmenumodel/src/gtk/gtkactionobserveritem.c b/libqmenumodel/src/gtk/gtkactionobserveritem.c new file mode 100644 index 0000000..aebbcc3 --- /dev/null +++ b/libqmenumodel/src/gtk/gtkactionobserveritem.c @@ -0,0 +1,152 @@ + +#include "gtkactionobserveritem.h" + +typedef GObjectClass GtkActionObserverItemClass; + +struct _GtkActionObserverItem +{ + GObject parent_instance; + GtkActionObservable *observable; + gchar* action_name; + + GtkActionAddedFunc action_added; + GtkActionEnabledChangedFunc action_enabled_changed; + GtkActionStateChangedFunc action_state_changed; + GtkActionRemovedFunc action_removed; +}; + +static void gtk_action_observer_item_init_observer_iface (GtkActionObserverInterface *iface); +G_DEFINE_TYPE_WITH_CODE (GtkActionObserverItem, gtk_action_observer_item, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTION_OBSERVER, gtk_action_observer_item_init_observer_iface)) + +static void +gtk_action_observer_item_finalize (GObject *object) +{ + GtkActionObserverItem *self = GTK_ACTION_OBSERVER_ITEM (object); + + if (self->observable) + g_object_unref (self->observable); + + if (self->action_name) + g_free(self->action_name); + + G_OBJECT_CLASS (gtk_action_observer_item_parent_class)->finalize (object); +} + +static void +gtk_action_observer_item_init (GtkActionObserverItem * self) +{ +} + +static void +gtk_action_observer_item_class_init (GtkActionObserverItemClass *class) +{ + class->finalize = gtk_action_observer_item_finalize; +} + +static void +gtk_action_observer_item_action_added (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name, + const GVariantType *parameter_type, + gboolean enabled, + GVariant *state) +{ + g_return_if_fail (GTK_IS_ACTION_OBSERVER_ITEM (observer)); + + GtkActionObserverItem* self; + self = GTK_ACTION_OBSERVER_ITEM (observer); + self->action_added(self, action_name, enabled, state); +} + +static void +gtk_action_observer_item_action_enabled_changed (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name, + gboolean enabled) +{ + g_return_if_fail (GTK_IS_ACTION_OBSERVER_ITEM (observer)); + + GtkActionObserverItem* self; + self = GTK_ACTION_OBSERVER_ITEM (observer); + self->action_enabled_changed(self, action_name, enabled); +} + +static void +gtk_action_observer_item_action_state_changed (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name, + GVariant *state) +{ + g_return_if_fail (GTK_IS_ACTION_OBSERVER_ITEM (observer)); + + GtkActionObserverItem* self; + self = GTK_ACTION_OBSERVER_ITEM (observer); + self->action_state_changed(self, action_name, state); +} + +static void +gtk_action_observer_item_action_removed (GtkActionObserver *observer, + GtkActionObservable *observable, + const gchar *action_name) +{ + g_return_if_fail (GTK_IS_ACTION_OBSERVER_ITEM (observer)); + + GtkActionObserverItem* self; + self = GTK_ACTION_OBSERVER_ITEM (observer); + self->action_removed(self, action_name); +} + +static void +gtk_action_observer_item_init_observer_iface (GtkActionObserverInterface *iface) +{ + iface->action_added = gtk_action_observer_item_action_added; + iface->action_enabled_changed = gtk_action_observer_item_action_enabled_changed; + iface->action_state_changed = gtk_action_observer_item_action_state_changed; + iface->action_removed = gtk_action_observer_item_action_removed; +} + +GtkActionObserverItem* +gtk_action_observer_item_new (GtkActionObservable *observable, + GtkActionAddedFunc action_added, + GtkActionEnabledChangedFunc action_enabled_changed, + GtkActionStateChangedFunc action_state_changed, + GtkActionRemovedFunc action_removed) +{ + GtkActionObserverItem* self; + self = g_object_new (GTK_TYPE_ACTION_OBSERVER_ITEM, NULL); + self->observable = g_object_ref (observable); + self->action_name = NULL; + + self->action_added = action_added; + self->action_enabled_changed = action_enabled_changed; + self->action_state_changed = action_state_changed; + self->action_removed = action_removed; + + return self; +} + +void +gtk_action_observer_item_register_action (GtkActionObserverItem *self, + const gchar* action_name) +{ + gtk_action_observer_item_unregister_action(self); + + if (action_name && g_strcmp0(action_name, "") != 0) { + self->action_name = g_strdup (action_name); + + if (g_strcmp0(self->action_name, "") != 0) { + gtk_action_observable_register_observer (self->observable, self->action_name, GTK_ACTION_OBSERVER (self)); + } + } +} + +void +gtk_action_observer_item_unregister_action (GtkActionObserverItem *self) +{ + if (self->action_name) { + gtk_action_observable_unregister_observer(self->observable, self->action_name, GTK_ACTION_OBSERVER (self)); + g_free(self->action_name); + self->action_name = NULL; + } +} diff --git a/libqmenumodel/src/gtk/gtkactionobserveritem.h b/libqmenumodel/src/gtk/gtkactionobserveritem.h new file mode 100644 index 0000000..ed36974 --- /dev/null +++ b/libqmenumodel/src/gtk/gtkactionobserveritem.h @@ -0,0 +1,67 @@ +/* + * Copyright © 2011 Canonical Limited + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * licence 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Nick Dedekind . + * + * Authors: Nick Dedekind + */ + +#include "unitymenuaction.h" +#include "unitymenumodel.h" + +#include + +UnityMenuAction::UnityMenuAction(QObject* parent) + : QObject(parent), + m_valid(false), + m_enabled(false), + m_model(NULL) +{ +} + +UnityMenuAction::~UnityMenuAction() +{ + if (m_model) { + m_model->unregisterAction(this); + } +} + +QString UnityMenuAction::name() const +{ + return m_name; +} + +void UnityMenuAction::setName(const QString& name) +{ + if (m_name != name) { + m_name = name; + Q_EMIT nameChanged(m_name); + } +} + +UnityMenuModel* UnityMenuAction::model() const +{ + return m_model; +} + +void UnityMenuAction::setModel(UnityMenuModel* model) +{ + if (m_model != model) { + if (!model) { + unregisterAction(); + } + m_model = model; + registerAction(); + Q_EMIT modelChanged(model); + } +} + +QVariant UnityMenuAction::state() const +{ + return m_state; +} + +bool UnityMenuAction::isEnabled() const +{ + return m_enabled; +} + +bool UnityMenuAction::isValid() const +{ + return m_valid; +} + +void UnityMenuAction::onAdded(bool enabled, const QVariant &state) +{ + if (m_enabled != enabled) { + m_enabled = enabled; + Q_EMIT enabledChanged(m_enabled); + } + if (m_state != state) { + m_state = state; + Q_EMIT stateChanged(m_state); + } + if (m_valid != true) { + m_valid = true; + Q_EMIT validChanged(m_valid); + } +} + +void UnityMenuAction::onRemoved() +{ + if (m_valid != false) { + m_valid = false; + Q_EMIT validChanged(m_valid); + } +} + +void UnityMenuAction::onEnabledChanged(bool enabled) +{ + if (m_enabled != enabled) { + m_enabled = enabled; + Q_EMIT enabledChanged(m_enabled); + } +} + +void UnityMenuAction::onStateChanged(const QVariant &state) +{ + if (m_state != state) { + m_state = state; + Q_EMIT stateChanged(m_state); + } +} + +void UnityMenuAction::registerAction() +{ + if (m_model) { + m_model->registerAction(this); + } +} + +void UnityMenuAction::unregisterAction() +{ + if (m_model) { + m_model->unregisterAction(this); + } +} diff --git a/libqmenumodel/src/unitymenuaction.h b/libqmenumodel/src/unitymenuaction.h new file mode 100644 index 0000000..e6cc4d9 --- /dev/null +++ b/libqmenumodel/src/unitymenuaction.h @@ -0,0 +1,73 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * Authors: Nick Dedekind + */ + +#ifndef UNITYMENUACTION_H +#define UNITYMENUACTION_H + +#include +#include +class UnityMenuModel; + +class UnityMenuAction: public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QVariant state READ state NOTIFY stateChanged) + Q_PROPERTY(bool enabled READ isEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool valid READ isValid NOTIFY validChanged) + Q_PROPERTY(UnityMenuModel* model READ model WRITE setModel NOTIFY modelChanged) +public: + UnityMenuAction(QObject* parent = 0); + ~UnityMenuAction(); + + QString name() const; + void setName(const QString& str); + + UnityMenuModel* model() const; + void setModel(UnityMenuModel* model); + + QVariant state() const; + bool isEnabled() const; + bool isValid() const; + +public Q_SLOTS: + void onAdded(bool enabled, const QVariant &state); + void onRemoved(); + void onEnabledChanged(bool enabled); + void onStateChanged(const QVariant &state); + +Q_SIGNALS: + void nameChanged(const QString& name); + void modelChanged(UnityMenuModel* model); + void stateChanged(const QVariant& name); + void enabledChanged(bool enabled); + void validChanged(bool valid); + +private: + void unregisterAction(); + void registerAction(); + + QString m_name; + QVariant m_state; + bool m_valid; + bool m_enabled; + UnityMenuModel* m_model; +}; + +#endif // UNITYMENUACTIONGROUP_H diff --git a/libqmenumodel/src/unitymenumodel.cpp b/libqmenumodel/src/unitymenumodel.cpp index e5407ab..920761c 100644 --- a/libqmenumodel/src/unitymenumodel.cpp +++ b/libqmenumodel/src/unitymenumodel.cpp @@ -20,6 +20,7 @@ #include "converter.h" #include "actionstateparser.h" #include "unitymenumodelevents.h" +#include "unitymenuaction.h" #include #include @@ -28,11 +29,14 @@ extern "C" { #include "gtk/gtkactionmuxer.h" #include "gtk/gtkmenutracker.h" + #include "gtk/gtkactionobserveritem.h" } G_DEFINE_QUARK (UNITY_MENU_MODEL, unity_menu_model) G_DEFINE_QUARK (UNITY_SUBMENU_MODEL, unity_submenu_model) G_DEFINE_QUARK (UNITY_MENU_ITEM_EXTENDED_ATTRIBUTES, unity_menu_item_extended_attributes) +G_DEFINE_QUARK (UNITY_MENU_ACTION, unity_menu_action) + enum MenuRoles { LabelRole = Qt::DisplayRole + 1, @@ -52,6 +56,7 @@ class UnityMenuModelPrivate { public: UnityMenuModelPrivate(UnityMenuModel *model); + UnityMenuModelPrivate(const UnityMenuModelPrivate& other, UnityMenuModel *model); ~UnityMenuModelPrivate(); void clearItems(bool resetModel=true); @@ -72,12 +77,21 @@ public: QByteArray menuObjectPath; QHash roles; ActionStateParser* actionStateParser; + QHash registeredActions; static void nameAppeared(GDBusConnection *connection, const gchar *name, const gchar *owner, gpointer user_data); static void nameVanished(GDBusConnection *connection, const gchar *name, gpointer user_data); static void menuItemInserted(GtkMenuTrackerItem *item, gint position, gpointer user_data); static void menuItemRemoved(gint position, gpointer user_data); static void menuItemChanged(GObject *object, GParamSpec *pspec, gpointer user_data); + + static void registeredActionAdded(GtkActionObserverItem *observer_item, + const gchar *action_name, + gboolean enabled, + GVariant *state); + static void registeredActionEnabledChanged(GtkActionObserverItem *observer_item, const gchar *action_name, gboolean enabled); + static void registeredActionStateChanged(GtkActionObserverItem *observer_item, const gchar *action_name, GVariant *state); + static void registeredActionRemoved(GtkActionObserverItem *observer_item, const gchar *action_name); }; void menu_item_free (gpointer data) @@ -101,6 +115,19 @@ UnityMenuModelPrivate::UnityMenuModelPrivate(UnityMenuModel *model) this->items = g_sequence_new (menu_item_free); } +UnityMenuModelPrivate::UnityMenuModelPrivate(const UnityMenuModelPrivate& other, UnityMenuModel *model) +{ + this->model = model; + this->menutracker = NULL; + this->connection = NULL; + this->nameWatchId = 0; + this->actionStateParser = new ActionStateParser(model); + + this->muxer = GTK_ACTION_MUXER( g_object_ref(other.muxer)); + + this->items = g_sequence_new (menu_item_free); +} + UnityMenuModelPrivate::~UnityMenuModelPrivate() { this->clearItems(false); @@ -109,6 +136,12 @@ UnityMenuModelPrivate::~UnityMenuModelPrivate() g_clear_object (&this->muxer); g_clear_object (&this->connection); + QHash::const_iterator it = this->registeredActions.constBegin(); + for (; it != this->registeredActions.constEnd(); ++it) { + g_object_unref(it.value()); + } + this->registeredActions.clear(); + if (this->nameWatchId) g_bus_unwatch_name (this->nameWatchId); } @@ -236,6 +269,12 @@ UnityMenuModel::UnityMenuModel(QObject *parent): priv = new UnityMenuModelPrivate(this); } +UnityMenuModel::UnityMenuModel(const UnityMenuModelPrivate& other, QObject *parent): + QAbstractListModel(parent) +{ + priv = new UnityMenuModelPrivate(other, this); +} + UnityMenuModel::~UnityMenuModel() { delete priv; @@ -461,7 +500,7 @@ QObject * UnityMenuModel::submenu(int position, QQmlComponent* actionStateParser model = (UnityMenuModel *) g_object_get_qdata (G_OBJECT (item), unity_submenu_model_quark ()); if (model == NULL) { - model = new UnityMenuModel(this); + model = new UnityMenuModel(*priv, this); if (actionStateParser) { ActionStateParser* parser = qobject_cast(actionStateParser->create()); @@ -672,3 +711,104 @@ bool UnityMenuModel::event(QEvent* e) } return QAbstractListModel::event(e); } + +void UnityMenuModel::registerAction(UnityMenuAction* action) +{ + if (!priv->registeredActions.contains(action)) { + GtkActionObserverItem* observer_item; + observer_item = gtk_action_observer_item_new(GTK_ACTION_OBSERVABLE (priv->muxer), + UnityMenuModelPrivate::registeredActionAdded, + UnityMenuModelPrivate::registeredActionEnabledChanged, + UnityMenuModelPrivate::registeredActionStateChanged, + UnityMenuModelPrivate::registeredActionRemoved); + + g_object_set_qdata (G_OBJECT (observer_item), unity_menu_action_quark (), action); + + priv->registeredActions[action] = observer_item; + + connect(action, SIGNAL(nameChanged(const QString&)), SLOT(onRegisteredActionNameChanged(const QString&))); + } +} + +void UnityMenuModel::unregisterAction(UnityMenuAction* action) +{ + if (priv->registeredActions.contains(action)) { + GtkActionObserverItem* observer_item; + observer_item = priv->registeredActions[action]; + g_object_unref(observer_item); + priv->registeredActions.remove(action); + + disconnect(action); + } +} + +void UnityMenuModel::onRegisteredActionNameChanged(const QString& name) +{ + UnityMenuAction* action = qobject_cast(sender()); + if (!action || !priv->registeredActions.contains(action)) + return; + + GtkActionObserverItem* observer_item; + observer_item = priv->registeredActions[action]; + + QByteArray nameArray = name.toUtf8(); + const gchar* action_name = nameArray.constData(); + + gtk_action_observer_item_register_action (observer_item, action_name); + + const GVariantType *parameter_type; + gboolean enabled; + GVariant *state; + + if (g_action_group_query_action (G_ACTION_GROUP (priv->muxer), action_name, + &enabled, ¶meter_type, NULL, NULL, &state)) + { + action->onAdded(enabled, Converter::toQVariant(state)); + if (state) { + g_variant_unref (state); + } + } +} + +void UnityMenuModelPrivate::registeredActionAdded(GtkActionObserverItem *observer_item, + const gchar *action_name, + gboolean enabled, + GVariant *state) +{ + UnityMenuAction *action; + action = (UnityMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), unity_menu_action_quark ()); + // FIXME - needs to go through event loop + if (action) { + action->onAdded(enabled, Converter::toQVariant(state)); + } +} + +void UnityMenuModelPrivate::registeredActionEnabledChanged(GtkActionObserverItem *observer_item, const gchar *action_name, gboolean enabled) +{ + UnityMenuAction *action; + action = (UnityMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), unity_menu_action_quark ()); + // FIXME - needs to go through event loop + if (action) { + action->onEnabledChanged(enabled); + } +} + +void UnityMenuModelPrivate::registeredActionStateChanged(GtkActionObserverItem *observer_item, const gchar *action_name, GVariant *state) +{ + UnityMenuAction *action; + action = (UnityMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), unity_menu_action_quark ()); + // FIXME - needs to go through event loop + if (action) { + action->onStateChanged(Converter::toQVariant(state)); + } +} + +void UnityMenuModelPrivate::registeredActionRemoved(GtkActionObserverItem *observer_item, const gchar *action_name) +{ + UnityMenuAction *action; + action = (UnityMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), unity_menu_action_quark ()); + // FIXME - needs to go through event loop + if (action) { + action->onRemoved(); + } +} diff --git a/libqmenumodel/src/unitymenumodel.h b/libqmenumodel/src/unitymenumodel.h index df35f08..21a4c3c 100644 --- a/libqmenumodel/src/unitymenumodel.h +++ b/libqmenumodel/src/unitymenumodel.h @@ -22,6 +22,7 @@ #include class ActionStateParser; class QQmlComponent; +class UnityMenuAction; class UnityMenuModel: public QAbstractListModel { @@ -61,18 +62,27 @@ public: Q_INVOKABLE void activate(int index, const QVariant& parameter = QVariant()); Q_INVOKABLE void changeState(int index, const QVariant& parameter); + void registerAction(UnityMenuAction* action); + void unregisterAction(UnityMenuAction* action); + Q_SIGNALS: void busNameChanged(const QByteArray &name); void actionsChanged(const QByteArray &path); void menuObjectPathChanged(const QByteArray &path); void actionStateParserChanged(ActionStateParser* parser); +protected Q_SLOTS: + void onRegisteredActionNameChanged(const QString& name); + protected: + virtual bool event(QEvent* e); private: class UnityMenuModelPrivate *priv; friend class UnityMenuModelPrivate; + + UnityMenuModel(const UnityMenuModelPrivate& other, QObject *parent); }; #endif -- cgit v1.2.3