From f8036895f95acc49f484af564d2b56f14c158dd9 Mon Sep 17 00:00:00 2001 From: Robert Tari Date: Tue, 16 Nov 2021 08:10:18 +0100 Subject: Rename unity* source files --- examples/ayatanaqmlmenumodel.qml | 118 +++ examples/unityqmlmenumodel.qml | 118 --- libqmenumodel/QMenuModel/plugin.cpp | 4 +- libqmenumodel/src/CMakeLists.txt | 12 +- libqmenumodel/src/ayatanamenuaction.cpp | 157 ++++ libqmenumodel/src/ayatanamenuaction.h | 84 ++ libqmenumodel/src/ayatanamenuactionevents.cpp | 46 ++ libqmenumodel/src/ayatanamenuactionevents.h | 65 ++ libqmenumodel/src/ayatanamenumodel.cpp | 1064 +++++++++++++++++++++++++ libqmenumodel/src/ayatanamenumodel.h | 97 +++ libqmenumodel/src/ayatanamenumodelevents.cpp | 65 ++ libqmenumodel/src/ayatanamenumodelevents.h | 71 ++ libqmenumodel/src/unitymenuaction.cpp | 157 ---- libqmenumodel/src/unitymenuaction.h | 84 -- libqmenumodel/src/unitymenuactionevents.cpp | 46 -- libqmenumodel/src/unitymenuactionevents.h | 65 -- libqmenumodel/src/unitymenumodel.cpp | 1064 ------------------------- libqmenumodel/src/unitymenumodel.h | 97 --- libqmenumodel/src/unitymenumodelevents.cpp | 65 -- libqmenumodel/src/unitymenumodelevents.h | 71 -- tests/client/ayatanamenuactiontest.cpp | 62 ++ tests/client/unitymenuactiontest.cpp | 62 -- 22 files changed, 1837 insertions(+), 1837 deletions(-) create mode 100644 examples/ayatanaqmlmenumodel.qml delete mode 100644 examples/unityqmlmenumodel.qml create mode 100644 libqmenumodel/src/ayatanamenuaction.cpp create mode 100644 libqmenumodel/src/ayatanamenuaction.h create mode 100644 libqmenumodel/src/ayatanamenuactionevents.cpp create mode 100644 libqmenumodel/src/ayatanamenuactionevents.h create mode 100644 libqmenumodel/src/ayatanamenumodel.cpp create mode 100644 libqmenumodel/src/ayatanamenumodel.h create mode 100644 libqmenumodel/src/ayatanamenumodelevents.cpp create mode 100644 libqmenumodel/src/ayatanamenumodelevents.h delete mode 100644 libqmenumodel/src/unitymenuaction.cpp delete mode 100644 libqmenumodel/src/unitymenuaction.h delete mode 100644 libqmenumodel/src/unitymenuactionevents.cpp delete mode 100644 libqmenumodel/src/unitymenuactionevents.h delete mode 100644 libqmenumodel/src/unitymenumodel.cpp delete mode 100644 libqmenumodel/src/unitymenumodel.h delete mode 100644 libqmenumodel/src/unitymenumodelevents.cpp delete mode 100644 libqmenumodel/src/unitymenumodelevents.h create mode 100644 tests/client/ayatanamenuactiontest.cpp delete mode 100644 tests/client/unitymenuactiontest.cpp diff --git a/examples/ayatanaqmlmenumodel.qml b/examples/ayatanaqmlmenumodel.qml new file mode 100644 index 0000000..e7360e0 --- /dev/null +++ b/examples/ayatanaqmlmenumodel.qml @@ -0,0 +1,118 @@ +/* + * 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: Lars Uebernickel + */ + +import QtQuick 2.0 +import QMenuModel 0.1 + +Item { + width: 400; + height: 500; + + UnityMenuModel { + id: menu + busName: "org.ayatana.indicator.sound" + actions: { "indicator": "/org/ayatana/indicator/sound" } + menuObjectPath: "/org/ayatana/indicator/sound/desktop" + } + + ListView { + id: listview + anchors.fill: parent + anchors.margins: 10 + spacing: 3 + model: menu + + delegate: Loader { + sourceComponent: { + if (isSeparator) { + return separator; + } + else if (type == "org.ayatana.indicator.slider") { + listview.model.loadExtendedAttributes(index, {'min-icon': 'icon', + 'max-icon': 'icon'}); + return slider; + } + else { + return menuitem; + } + } + + Component { + id: separator + Rectangle { + width: listview.width + height: 4 + color: "blue" + } + } + + Component { + id: slider + Rectangle { + width: listview.width + color: "#ddd" + height: 40 + Row { + anchors.fill: parent + Image { + source: ext.minIcon + } + Text { + text: model.actionState + } + Image { + source: ext.maxIcon + } + } + } + } + + Component { + id: menuitem + Rectangle { + width: listview.width + height: 40 + color: "#ddd" + Row { + anchors.fill: parent + anchors.margins: 5 + Image { + source: icon + } + Text { + height: parent.height + verticalAlignment: Text.AlignVCenter + color: sensitive ? "black" : "#aaa"; + text: label + } + } + MouseArea { + anchors.fill: parent + onClicked: { + var submenu = listview.model.submenu(index); + if (submenu) + listview.model = submenu; + else + action.activate(); + } + } + } + } + } + } +} diff --git a/examples/unityqmlmenumodel.qml b/examples/unityqmlmenumodel.qml deleted file mode 100644 index e7360e0..0000000 --- a/examples/unityqmlmenumodel.qml +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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: Lars Uebernickel - */ - -import QtQuick 2.0 -import QMenuModel 0.1 - -Item { - width: 400; - height: 500; - - UnityMenuModel { - id: menu - busName: "org.ayatana.indicator.sound" - actions: { "indicator": "/org/ayatana/indicator/sound" } - menuObjectPath: "/org/ayatana/indicator/sound/desktop" - } - - ListView { - id: listview - anchors.fill: parent - anchors.margins: 10 - spacing: 3 - model: menu - - delegate: Loader { - sourceComponent: { - if (isSeparator) { - return separator; - } - else if (type == "org.ayatana.indicator.slider") { - listview.model.loadExtendedAttributes(index, {'min-icon': 'icon', - 'max-icon': 'icon'}); - return slider; - } - else { - return menuitem; - } - } - - Component { - id: separator - Rectangle { - width: listview.width - height: 4 - color: "blue" - } - } - - Component { - id: slider - Rectangle { - width: listview.width - color: "#ddd" - height: 40 - Row { - anchors.fill: parent - Image { - source: ext.minIcon - } - Text { - text: model.actionState - } - Image { - source: ext.maxIcon - } - } - } - } - - Component { - id: menuitem - Rectangle { - width: listview.width - height: 40 - color: "#ddd" - Row { - anchors.fill: parent - anchors.margins: 5 - Image { - source: icon - } - Text { - height: parent.height - verticalAlignment: Text.AlignVCenter - color: sensitive ? "black" : "#aaa"; - text: label - } - } - MouseArea { - anchors.fill: parent - onClicked: { - var submenu = listview.model.submenu(index); - if (submenu) - listview.model = submenu; - else - action.activate(); - } - } - } - } - } - } -} diff --git a/libqmenumodel/QMenuModel/plugin.cpp b/libqmenumodel/QMenuModel/plugin.cpp index b7a4400..5ea750f 100644 --- a/libqmenumodel/QMenuModel/plugin.cpp +++ b/libqmenumodel/QMenuModel/plugin.cpp @@ -22,8 +22,8 @@ #include "qdbusmenumodel.h" #include "qdbusactiongroup.h" #include "qstateaction.h" -#include "unitymenuaction.h" -#include "unitymenumodel.h" +#include "ayatanamenuaction.h" +#include "ayatanamenumodel.h" #include diff --git a/libqmenumodel/src/CMakeLists.txt b/libqmenumodel/src/CMakeLists.txt index 9a49840..02c4b4c 100644 --- a/libqmenumodel/src/CMakeLists.txt +++ b/libqmenumodel/src/CMakeLists.txt @@ -11,10 +11,10 @@ set(QMENUMODEL_SRC qdbusactiongroup.cpp qmenumodelevents.cpp qstateaction.cpp - unitymenuaction.cpp - unitymenuactionevents.cpp - unitymenumodel.cpp - unitymenumodelevents.cpp + ayatanamenuaction.cpp + ayatanamenuactionevents.cpp + ayatanamenumodel.cpp + ayatanamenumodelevents.cpp gtk/gtkactionmuxer.c gtk/gtkactionmuxer.h gtk/gtkactionobservable.c @@ -62,8 +62,8 @@ set(QMENUMODEL_HEADERS qdbusobject.h qmenumodel.h qstateaction.h - unitymenuaction.h - unitymenumodel.h + ayatanamenuaction.h + ayatanamenumodel.h ) set(INCLUDEDIR qmenumodel) diff --git a/libqmenumodel/src/ayatanamenuaction.cpp b/libqmenumodel/src/ayatanamenuaction.cpp new file mode 100644 index 0000000..bbbbf77 --- /dev/null +++ b/libqmenumodel/src/ayatanamenuaction.cpp @@ -0,0 +1,157 @@ +/* + * 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 + */ + +#include "ayatanamenuaction.h" +#include "ayatanamenumodel.h" +#include "ayatanamenuactionevents.h" + +UnityMenuAction::UnityMenuAction(QObject* parent) + : QObject(parent), + m_valid(false), + m_enabled(false), + m_model(NULL), + m_index(-1) +{ +} + +UnityMenuAction::~UnityMenuAction() +{ + unregisterAction(); +} + +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) { + unregisterAction(); + m_model = model; + registerAction(); + Q_EMIT modelChanged(model); + } +} + +QVariant UnityMenuAction::state() const +{ + return m_state; +} + +void UnityMenuAction::setState(const QVariant& state) +{ + if (m_state != state) { + m_state = state; + Q_EMIT stateChanged(m_state); + } +} + +bool UnityMenuAction::isEnabled() const +{ + return m_enabled; +} + +void UnityMenuAction::setEnabled(bool enabled) +{ + if (m_enabled != enabled) { + m_enabled = enabled; + Q_EMIT enabledChanged(m_enabled); + } +} + +bool UnityMenuAction::isValid() const +{ + return m_valid; +} + +void UnityMenuAction::setValid(bool valid) +{ + if (m_valid != valid) { + m_valid = valid; + Q_EMIT validChanged(m_valid); + } +} + +int UnityMenuAction::index() const +{ + return m_index; +} + +void UnityMenuAction::setIndex(int i) +{ + if (i != m_index) { + m_index = i; + Q_EMIT indexChanged(m_index); + } +} + +void UnityMenuAction::registerAction() +{ + if (m_model) { + m_model->registerAction(this); + } +} + +void UnityMenuAction::unregisterAction() +{ + if (m_model) { + m_model->unregisterAction(this); + } +} + +bool UnityMenuAction::event(QEvent* e) +{ + if (e->type() == UnityMenuActionAddEvent::eventType) { + UnityMenuActionAddEvent *umaae = static_cast(e); + + setEnabled(umaae->enabled); + setState(umaae->state); + setValid(true); + return true; + } else if (e->type() == UnityMenuActionEnabledChangedEvent::eventType) { + UnityMenuActionEnabledChangedEvent *umaece = static_cast(e); + + setEnabled(umaece->enabled); + return true; + } else if (e->type() == UnityMenuActionStateChangeEvent::eventType) { + UnityMenuActionStateChangeEvent *umasce = static_cast(e); + + setState(umasce->state); + return true; + } else if (e->type() == UnityMenuActionRemoveEvent::eventType) { + UnityMenuActionRemoveEvent *umare = static_cast(e); + + setValid(false); + return true; + } + return QObject::event(e); +} diff --git a/libqmenumodel/src/ayatanamenuaction.h b/libqmenumodel/src/ayatanamenuaction.h new file mode 100644 index 0000000..3a7cbb8 --- /dev/null +++ b/libqmenumodel/src/ayatanamenuaction.h @@ -0,0 +1,84 @@ +/* + * 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 AYATANAMENUACTION_H +#define AYATANAMENUACTION_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) + Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged) + +public: + UnityMenuAction(QObject* parent = 0); + ~UnityMenuAction(); + + QString name() const; + void setName(const QString& str); + + UnityMenuModel* model() const; + void setModel(UnityMenuModel* model); + + int index() const; + void setIndex(int i); + + QVariant state() const; + bool isEnabled() const; + bool isValid() const; + +Q_SIGNALS: + Q_INVOKABLE void activate(const QVariant& parameter = QVariant()); + Q_INVOKABLE void changeState(const QVariant& parameter); + + void nameChanged(const QString& name); + void modelChanged(UnityMenuModel* model); + void stateChanged(const QVariant& name); + void enabledChanged(bool enabled); + void validChanged(bool valid); + void indexChanged(int index); + +protected: + virtual bool event(QEvent* e); + + void setState(const QVariant& state); + void setEnabled(bool enabled); + void setValid(bool valid); + +private: + void unregisterAction(); + void registerAction(); + + QString m_name; + QVariant m_state; + bool m_valid; + bool m_enabled; + UnityMenuModel* m_model; + int m_index; +}; + +#endif // AYATANAMENUACTIONGROUP_H diff --git a/libqmenumodel/src/ayatanamenuactionevents.cpp b/libqmenumodel/src/ayatanamenuactionevents.cpp new file mode 100644 index 0000000..f83d35b --- /dev/null +++ b/libqmenumodel/src/ayatanamenuactionevents.cpp @@ -0,0 +1,46 @@ +/* + * 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: + * Nicholas Dedekind (QEvent::registerEventType()); +const QEvent::Type UnityMenuActionRemoveEvent::eventType = static_cast(QEvent::registerEventType()); +const QEvent::Type UnityMenuActionEnabledChangedEvent::eventType = static_cast(QEvent::registerEventType()); +const QEvent::Type UnityMenuActionStateChangeEvent::eventType = static_cast(QEvent::registerEventType()); + +UnityMenuActionAddEvent::UnityMenuActionAddEvent(bool _enabled, const QVariant& _state) + : QEvent(UnityMenuActionAddEvent::eventType), + enabled(_enabled), + state(_state) +{} + +UnityMenuActionRemoveEvent::UnityMenuActionRemoveEvent() + : QEvent(UnityMenuActionRemoveEvent::eventType) +{ +} + +UnityMenuActionEnabledChangedEvent::UnityMenuActionEnabledChangedEvent(bool _enabled) + : QEvent(UnityMenuActionEnabledChangedEvent::eventType), + enabled(_enabled) +{} + +UnityMenuActionStateChangeEvent::UnityMenuActionStateChangeEvent(const QVariant& _state) + : QEvent(UnityMenuActionStateChangeEvent::eventType), + state(_state) +{} diff --git a/libqmenumodel/src/ayatanamenuactionevents.h b/libqmenumodel/src/ayatanamenuactionevents.h new file mode 100644 index 0000000..2365da0 --- /dev/null +++ b/libqmenumodel/src/ayatanamenuactionevents.h @@ -0,0 +1,65 @@ +/* + * 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: + * Nicholas Dedekind +#include + +/* Event for a unitymenuaction add */ +class UnityMenuActionAddEvent : public QEvent +{ +public: + static const QEvent::Type eventType; + UnityMenuActionAddEvent(bool enabled, const QVariant& state); + + bool enabled; + QVariant state; +}; + +/* Event for a unitymenuaction remove */ +class UnityMenuActionRemoveEvent : public QEvent +{ +public: + static const QEvent::Type eventType; + UnityMenuActionRemoveEvent(); +}; + +/* Event for change in enabled value of a unitymenuaction */ +class UnityMenuActionEnabledChangedEvent : public QEvent +{ +public: + static const QEvent::Type eventType; + UnityMenuActionEnabledChangedEvent(bool enabled); + + int enabled; +}; + +/* Event for change in state value of a unitymenuaction */ +class UnityMenuActionStateChangeEvent : public QEvent +{ +public: + static const QEvent::Type eventType; + UnityMenuActionStateChangeEvent(const QVariant& state); + + QVariant state; +}; + +#endif //AYATANAMENUACTIONEVENTS_H diff --git a/libqmenumodel/src/ayatanamenumodel.cpp b/libqmenumodel/src/ayatanamenumodel.cpp new file mode 100644 index 0000000..24bfa40 --- /dev/null +++ b/libqmenumodel/src/ayatanamenumodel.cpp @@ -0,0 +1,1064 @@ +/* + * 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: Lars Uebernickel + */ + +#include "ayatanamenumodel.h" +#include "converter.h" +#include "actionstateparser.h" +#include "ayatanamenumodelevents.h" +#include "ayatanamenuaction.h" +#include "ayatanamenuactionevents.h" +#include "logging.h" + +#include +#include +#include +#include + +extern "C" { + #include "gtk/gtkactionmuxer.h" + #include "gtk/gtkmenutracker.h" + #include "gtk/gtksimpleactionobserver.h" +} + +Q_LOGGING_CATEGORY(unitymenumodel, "qmenumodel.unitymenumodel", QtCriticalMsg) + +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, + SensitiveRole, + IsSeparatorRole, + IconRole, + TypeRole, + ExtendedAttributesRole, + ActionRole, + ActionStateRole, + IsCheckRole, + IsRadioRole, + IsToggledRole, + ShortcutRole, + HasSubmenuRole +}; + +class UnityMenuModelPrivate +{ +public: + UnityMenuModelPrivate(UnityMenuModel *model); + UnityMenuModelPrivate(const UnityMenuModelPrivate& other, UnityMenuModel *model); + ~UnityMenuModelPrivate(); + + void clearItems(bool resetModel=true); + void clearName(); + void updateActions(); + void updateMenuModel(); + QVariant itemState(GtkMenuTrackerItem *item); + + UnityMenuModel *model; + GtkActionMuxer *muxer; + GtkMenuTracker *menutracker; + GSequence *items; + GDBusConnection *connection; + QByteArray busName; + QByteArray nameOwner; + guint nameWatchId; + QVariantMap actions; + QByteArray menuObjectPath; + QHash roles; + ActionStateParser* actionStateParser; + QHash registeredActions; + bool destructorGuard; + + 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(GPtrArray *items, gint position, gpointer user_data); + static void menuItemRemoved(gint position, gint n_items, gpointer user_data); + static void menuItemChanged(GObject *object, GParamSpec *pspec, gpointer user_data); + + static void registeredActionAdded(GtkSimpleActionObserver *observer_item, + const gchar *action_name, + gboolean enabled, + GVariant *state); + static void registeredActionEnabledChanged(GtkSimpleActionObserver *observer_item, const gchar *action_name, gboolean enabled); + static void registeredActionStateChanged(GtkSimpleActionObserver *observer_item, const gchar *action_name, GVariant *state); + static void registeredActionRemoved(GtkSimpleActionObserver *observer_item, const gchar *action_name); + + gchar * fullActionName(UnityMenuAction *action); + void updateRegisteredAction(UnityMenuAction *action); +}; + +void menu_item_free (gpointer data) +{ + GtkMenuTrackerItem *item = (GtkMenuTrackerItem *) data; + + g_signal_handlers_disconnect_by_func (item, (gpointer) UnityMenuModelPrivate::menuItemChanged, NULL); + g_object_unref (item); +} + +UnityMenuModelPrivate::UnityMenuModelPrivate(UnityMenuModel *model) +{ + this->model = model; + this->menutracker = NULL; + this->connection = NULL; + this->nameWatchId = 0; + this->actionStateParser = new ActionStateParser(model); + this->destructorGuard = false; + + this->muxer = gtk_action_muxer_new (); + + 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->destructorGuard = false; + + this->muxer = GTK_ACTION_MUXER( g_object_ref(other.muxer)); + + this->items = g_sequence_new (menu_item_free); +} + +UnityMenuModelPrivate::~UnityMenuModelPrivate() +{ + this->destructorGuard = true; + this->clearItems(false); + + g_sequence_free(this->items); + g_clear_pointer (&this->menutracker, gtk_menu_tracker_free); + 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()); + it.key()->setModel(NULL); + } + this->registeredActions.clear(); + + if (this->nameWatchId) + g_bus_unwatch_name (this->nameWatchId); +} + +void UnityMenuModelPrivate::clearItems(bool resetModel) +{ + UnityMenuModelClearEvent ummce(resetModel); + QCoreApplication::sendEvent(model, &ummce); +} + +void UnityMenuModelPrivate::clearName() +{ + this->clearItems(); + + this->nameOwner = QByteArray(); + + this->updateActions(); + this->updateMenuModel(); + + Q_EMIT model->nameOwnerChanged (this->nameOwner); +} + +void UnityMenuModelPrivate::updateActions() +{ + Q_FOREACH (QString prefix, this->actions.keys()) + gtk_action_muxer_remove (this->muxer, prefix.toUtf8()); + + if (this->nameOwner.isEmpty()) + return; + + for (QVariantMap::const_iterator it = this->actions.constBegin(); it != this->actions.constEnd(); ++it) { + GDBusActionGroup *actions; + + actions = g_dbus_action_group_get (this->connection, this->nameOwner, it.value().toByteArray()); + gtk_action_muxer_insert (this->muxer, it.key().toUtf8(), G_ACTION_GROUP (actions)); + + g_object_unref (actions); + } +} + +void UnityMenuModelPrivate::updateMenuModel() +{ + this->clearItems(); + g_clear_pointer (&this->menutracker, gtk_menu_tracker_free); + + if (!this->nameOwner.isEmpty()) { + GDBusMenuModel *menu; + + menu = g_dbus_menu_model_get (this->connection, this->nameOwner, this->menuObjectPath.constData()); + this->menutracker = gtk_menu_tracker_new (GTK_ACTION_OBSERVABLE (this->muxer), + G_MENU_MODEL (menu), TRUE, NULL, + menuItemInserted, menuItemRemoved, this); + + g_object_unref (menu); + } +} + +QVariant UnityMenuModelPrivate::itemState(GtkMenuTrackerItem *item) +{ + QVariant result; + + GVariant *state = gtk_menu_tracker_item_get_action_state (item); + if (state != NULL) { + if (actionStateParser != NULL) { + result = actionStateParser->toQVariant(state); + } + g_variant_unref (state); + } + + return result; +} + +void UnityMenuModelPrivate::nameAppeared(GDBusConnection *connection, const gchar *name, const gchar *owner, gpointer user_data) +{ + UnityMenuModelPrivate *priv = (UnityMenuModelPrivate *)user_data; + + priv->connection = (GDBusConnection *) g_object_ref (connection); + priv->nameOwner = owner; + + priv->updateActions(); + priv->updateMenuModel(); + + Q_EMIT priv->model->nameOwnerChanged (priv->nameOwner); +} + +void UnityMenuModelPrivate::nameVanished(GDBusConnection *connection, const gchar *name, gpointer user_data) +{ + UnityMenuModelPrivate *priv = (UnityMenuModelPrivate *)user_data; + + priv->clearName(); +} + +void UnityMenuModelPrivate::menuItemInserted(GPtrArray *items, gint position, gpointer user_data) +{ + UnityMenuModelPrivate *priv = (UnityMenuModelPrivate *)user_data; + + UnityMenuModelAddRowEvent ummare(items, position); + QCoreApplication::sendEvent(priv->model, &ummare); +} + +void UnityMenuModelPrivate::menuItemRemoved(gint position, gint n_items, gpointer user_data) +{ + UnityMenuModelPrivate *priv = (UnityMenuModelPrivate *)user_data; + + UnityMenuModelRemoveRowEvent ummrre(position, n_items); + QCoreApplication::sendEvent(priv->model, &ummrre); +} + +void UnityMenuModelPrivate::menuItemChanged(GObject *object, GParamSpec *pspec, gpointer user_data) +{ + GSequenceIter *it = (GSequenceIter *) user_data; + GtkMenuTrackerItem *item; + GtkActionObservable *muxer; + UnityMenuModel *model; + gint position; + + item = (GtkMenuTrackerItem *) g_sequence_get (it); + muxer = _gtk_menu_tracker_item_get_observable (item); + model = (UnityMenuModel *) g_object_get_qdata (G_OBJECT (item), unity_menu_model_quark ()); + position = g_sequence_iter_get_position (it); + + UnityMenuModelDataChangeEvent ummdce(position); + QCoreApplication::sendEvent(model, &ummdce); +} + +UnityMenuModel::UnityMenuModel(QObject *parent): + QAbstractListModel(parent) +{ + priv = new UnityMenuModelPrivate(this); +} + +UnityMenuModel::UnityMenuModel(const UnityMenuModelPrivate& other, UnityMenuModel *parent): + QAbstractListModel(parent) +{ + priv = new UnityMenuModelPrivate(other, this); +} + +UnityMenuModel::~UnityMenuModel() +{ + delete priv; +} + +QByteArray UnityMenuModel::busName() const +{ + return priv->busName; +} + +QByteArray UnityMenuModel::nameOwner() const +{ + return priv->nameOwner; +} + +void UnityMenuModel::setBusName(const QByteArray &name) +{ + if (name == priv->busName) + return; + + priv->clearName(); + + if (priv->nameWatchId) + g_bus_unwatch_name (priv->nameWatchId); + + priv->nameWatchId = g_bus_watch_name (G_BUS_TYPE_SESSION, name.constData(), G_BUS_NAME_WATCHER_FLAGS_AUTO_START, + UnityMenuModelPrivate::nameAppeared, UnityMenuModelPrivate::nameVanished, + priv, NULL); + priv->busName = name; + Q_EMIT busNameChanged (priv->busName); +} + +QVariantMap UnityMenuModel::actions() const +{ + return priv->actions; +} + +void UnityMenuModel::setActions(const QVariantMap &actions) +{ + priv->actions = actions; + priv->updateActions(); +} + +QByteArray UnityMenuModel::menuObjectPath() const +{ + return priv->menuObjectPath; +} + +void UnityMenuModel::setMenuObjectPath(const QByteArray &path) +{ + priv->menuObjectPath = path; + priv->updateMenuModel(); +} + +ActionStateParser* UnityMenuModel::actionStateParser() const +{ + return priv->actionStateParser; +} + +void UnityMenuModel::setActionStateParser(ActionStateParser* actionStateParser) +{ + if (priv->actionStateParser != actionStateParser) { + priv->actionStateParser = actionStateParser; + Q_EMIT actionStateParserChanged(actionStateParser); + } +} + +int UnityMenuModel::rowCount(const QModelIndex &parent) const +{ + return !parent.isValid() ? g_sequence_get_length (priv->items) : 0; +} + +int UnityMenuModel::columnCount(const QModelIndex &parent) const +{ + return 1; +} + +static QString iconUri(GIcon *icon) +{ + QString uri; + + if (G_IS_THEMED_ICON (icon)) { + const gchar* const* iconNames = g_themed_icon_get_names (G_THEMED_ICON (icon)); + guint index = 0; + while(iconNames[index] != NULL) { + if (QIcon::hasThemeIcon(iconNames[index])) { + uri = QString("image://theme/") + iconNames[index]; + break; + } + index++; + } + } + else if (G_IS_FILE_ICON (icon)) { + GFile *file; + + file = g_file_icon_get_file (G_FILE_ICON (icon)); + if (file) { + gchar *fileuri; + + fileuri = g_file_get_uri (file); + uri = QString(fileuri); + + g_free (fileuri); + } + } + else if (G_IS_BYTES_ICON (icon)) { + gsize size; + gconstpointer data; + gchar *base64; + + data = g_bytes_get_data (g_bytes_icon_get_bytes (G_BYTES_ICON (icon)), &size); + base64 = g_base64_encode ((const guchar *) data, size); + + uri = QString("data://"); + uri.append (base64); + + g_free (base64); + } + + return uri; +} + +QVariant UnityMenuModel::data(const QModelIndex &index, int role) const +{ + GSequenceIter *it; + GtkMenuTrackerItem *item; + + it = g_sequence_get_iter_at_pos (priv->items, index.row()); + if (g_sequence_iter_is_end (it)) { + return QVariant(); + } + + item = (GtkMenuTrackerItem *) g_sequence_get (it); + if (!item) { + return QVariant(); + } + + switch (role) { + case LabelRole: + return gtk_menu_tracker_item_get_label (item); + + case SensitiveRole: + return gtk_menu_tracker_item_get_sensitive (item) == TRUE ? true : false; + + case IsSeparatorRole: + return gtk_menu_tracker_item_get_is_separator (item) == TRUE ? true : false; + + case IconRole: { + GIcon *icon = gtk_menu_tracker_item_get_icon (item); + if (icon) { + QString uri = iconUri(icon); + g_object_unref (icon); + return uri; + } + else + return QString(); + } + + case TypeRole: { + gchar *type; + auto ret = gtk_menu_tracker_item_get_attribute (item, "x-ayatana-type", "s", &type); + + // If we can't get x-ayatana-type, try legacy x-canonical-type type + if (!ret) + ret = gtk_menu_tracker_item_get_attribute (item, "x-canonical-type", "s", &type); + + if (ret) { + QVariant v(type); + g_free (type); + return v; + } + else + return QVariant(); + } + + case ExtendedAttributesRole: { + QVariantMap *map = (QVariantMap *) g_object_get_qdata (G_OBJECT (item), unity_menu_item_extended_attributes_quark ()); + return map ? *map : QVariant(); + } + + case ActionRole: { + gchar *action_name = gtk_menu_tracker_item_get_action_name (item); + QString v(action_name); + g_free(action_name); + return v; + } + + case ActionStateRole: + return priv->itemState(item); + + case IsCheckRole: + return gtk_menu_tracker_item_get_role (item) == GTK_MENU_TRACKER_ITEM_ROLE_CHECK; + + case IsRadioRole: + return gtk_menu_tracker_item_get_role (item) == GTK_MENU_TRACKER_ITEM_ROLE_RADIO; + + case IsToggledRole: + return gtk_menu_tracker_item_get_toggled (item) != FALSE; + + case ShortcutRole: + return QKeySequence(gtk_menu_tracker_item_get_accel (item), QKeySequence::NativeText); + + case HasSubmenuRole: + return gtk_menu_tracker_item_get_has_submenu (item) != FALSE; + + default: + return QVariant(); + } +} + +QModelIndex UnityMenuModel::index(int row, int column, const QModelIndex &parent) const +{ + return createIndex(row, column); +} + +QModelIndex UnityMenuModel::parent(const QModelIndex &index) const +{ + return QModelIndex(); +} + +QHash UnityMenuModel::roleNames() const +{ + QHash names; + + names[LabelRole] = "label"; + names[SensitiveRole] = "sensitive"; + names[IsSeparatorRole] = "isSeparator"; + names[IconRole] = "icon"; + names[TypeRole] = "type"; + names[ExtendedAttributesRole] = "ext"; + names[ActionRole] = "action"; + names[ActionStateRole] = "actionState"; + names[IsCheckRole] = "isCheck"; + names[IsRadioRole] = "isRadio"; + names[IsToggledRole] = "isToggled"; + names[ShortcutRole] = "shortcut"; + names[HasSubmenuRole] = "hasSubmenu"; + + return names; +} + +QObject * UnityMenuModel::submenu(int position, QQmlComponent* actionStateParser) +{ + GSequenceIter *it; + GtkMenuTrackerItem *item; + UnityMenuModel *model; + + it = g_sequence_get_iter_at_pos (priv->items, position); + if (g_sequence_iter_is_end (it)) { + return NULL; + } + + item = (GtkMenuTrackerItem *) g_sequence_get (it); + if (!item || !gtk_menu_tracker_item_get_has_submenu (item)) { + return NULL; + } + + model = (UnityMenuModel *) g_object_get_qdata (G_OBJECT (item), unity_submenu_model_quark ()); + if (model == NULL) { + model = new UnityMenuModel(*priv, this); + + if (actionStateParser) { + ActionStateParser* parser = qobject_cast(actionStateParser->create()); + if (parser) { + model->setActionStateParser(parser); + } + } + + model->priv->menutracker = gtk_menu_tracker_new_for_item_submenu (item, + UnityMenuModelPrivate::menuItemInserted, + UnityMenuModelPrivate::menuItemRemoved, + model->priv); + g_object_set_qdata (G_OBJECT (item), unity_submenu_model_quark (), model); + } + + return model; +} + +static void freeExtendedAttrs(gpointer data) +{ + QVariantMap *extendedAttrs = (QVariantMap *) data; + delete extendedAttrs; +} + +static QVariant attributeToQVariant(GVariant *value, const QString &type) +{ + QVariant result; + + if (type == "int") { + if (g_variant_is_of_type (value, G_VARIANT_TYPE_INT32)) { + result = QVariant(g_variant_get_int32(value)); + } + } + else if (type == "int64") { + if (g_variant_is_of_type (value, G_VARIANT_TYPE_INT64)) { + result = QVariant((qlonglong)g_variant_get_int64(value)); + } + } + else if (type == "bool") { + if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)) { + result = QVariant(g_variant_get_boolean(value)); + } + } + else if (type == "string") { + if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) { + result = QVariant(g_variant_get_string(value, NULL)); + } + } + else if (type == "double") { + if (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE)) { + result = QVariant(g_variant_get_double(value)); + } + } + else if (type == "variant") { + if (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT)) { + result = Converter::toQVariant(value); + } + } + else if (type == "icon") { + GIcon *icon = g_icon_deserialize (value); + if (icon) { + result = iconUri(icon); + g_object_unref (icon); + } + else { + result = QVariant(""); + } + } + + return result; +} + +/* convert 'some-key' to 'someKey' or 'SomeKey'. (from dconf-qt) */ +static QString qtify_name(const char *name) +{ + bool next_cap = false; + QString result; + + while (*name) { + if (*name == '-') { + next_cap = true; + } else if (next_cap) { + result.append(toupper(*name)); + next_cap = false; + } else { + result.append(*name); + } + + name++; + } + + return result; +} + +bool UnityMenuModel::loadExtendedAttributes(int position, const QVariantMap &schema) +{ + GSequenceIter *it; + GtkMenuTrackerItem *item; + QVariantMap *extendedAttrs; + + it = g_sequence_get_iter_at_pos (priv->items, position); + if (g_sequence_iter_is_end (it)) { + return false; + } + + item = (GtkMenuTrackerItem *) g_sequence_get (it); + if (!item) { + return false; + } + + extendedAttrs = new QVariantMap; + + for (QVariantMap::const_iterator it = schema.constBegin(); it != schema.constEnd(); ++it) { + QString name = it.key(); + QString type = it.value().toString(); + + GVariant *value = gtk_menu_tracker_item_get_attribute_value (item, name.toUtf8(), NULL); + if (value == NULL) { + qCWarning(unitymenumodel, "loadExtendedAttributes: menu item does not contain '%s'", it.key().toUtf8().constData()); + continue; + } + + const QVariant &qvalue = attributeToQVariant(value, type); + if (qvalue.isValid()) + extendedAttrs->insert(qtify_name (name.toUtf8()), qvalue); + else + qCWarning(unitymenumodel, "loadExtendedAttributes: key '%s' is of type '%s' (expected '%s')", + name.toUtf8().constData(), g_variant_get_type_string(value), type.constData()); + + g_variant_unref (value); + } + + g_object_set_qdata_full (G_OBJECT (item), unity_menu_item_extended_attributes_quark (), + extendedAttrs, freeExtendedAttrs); + + Q_EMIT dataChanged(index(position, 0), index(position, 0), QVector() << ExtendedAttributesRole); + return true; +} + +QVariant UnityMenuModel::get(int row, const QByteArray &role) +{ + if (priv->roles.isEmpty()) { + QHash names = roleNames(); + Q_FOREACH (int role, names.keys()) + priv->roles.insert(names[role], role); + } + + return this->data(this->index(row, 0), priv->roles[role]); +} + +void UnityMenuModel::activate(int index, const QVariant& parameter) +{ + GSequenceIter *it; + GtkMenuTrackerItem *item; + GVariant *value; + const GVariantType *parameter_type; + + it = g_sequence_get_iter_at_pos (priv->items, index); + if (g_sequence_iter_is_end (it)) { + return; + } + + item = (GtkMenuTrackerItem *) g_sequence_get (it); + if (!item) { + return; + } + + if (parameter.isValid()) { + gchar *action; + + action = gtk_menu_tracker_item_get_action_name (item); + parameter_type = g_action_group_get_action_parameter_type (G_ACTION_GROUP (priv->muxer), action); + value = Converter::toGVariantWithSchema(parameter, g_variant_type_peek_string (parameter_type)); + g_action_group_activate_action (G_ACTION_GROUP (priv->muxer), action, value); + + g_free (action); + } else { + gtk_menu_tracker_item_activated (item); + } +} + +void UnityMenuModel::aboutToShow(int index) +{ + GSequenceIter *it = g_sequence_get_iter_at_pos (priv->items, index); + if (g_sequence_iter_is_end (it)) { + return; + } + + auto item = static_cast(g_sequence_get(it)); + if (!item) { + return; + } + + quint64 actionTag; + if (gtk_menu_tracker_item_get_attribute (item, "qtubuntu-tag", "t", &actionTag)) { + // Child UnityMenuModel have priv->connection null, so climb to the parent until we find a non null one + UnityMenuModelPrivate *privToUse = priv; + while (privToUse && !privToUse->connection) { + auto pModel = dynamic_cast(privToUse->model->QObject::parent()); + if (pModel) { + privToUse = pModel->priv; + } else { + privToUse = nullptr; + } + } + if (privToUse) { + g_dbus_connection_call (privToUse->connection, + privToUse->busName, + privToUse->menuObjectPath, + "qtubuntu.actions.extra", + "aboutToShow", + g_variant_new("(t)", actionTag), + nullptr, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + G_MAXINT, + nullptr, + nullptr, + nullptr); + } + } +} + +void UnityMenuModel::activateByVariantString(int index, const QString& parameter) +{ + activate(index, Converter::toQVariantFromVariantString(parameter)); +} + +void UnityMenuModel::changeStateByVariantString(int index, const QString& parameter) +{ + changeState(index, Converter::toQVariantFromVariantString(parameter)); +} + +void UnityMenuModel::changeState(int index, const QVariant& parameter) +{ + GSequenceIter *it; + GtkMenuTrackerItem* item; + GVariant* data; + GVariant* current_state; + + it = g_sequence_get_iter_at_pos (priv->items, index); + if (g_sequence_iter_is_end (it)) { + return; + } + + item = (GtkMenuTrackerItem *) g_sequence_get (it); + if (!item) { + return; + } + + current_state = gtk_menu_tracker_item_get_action_state (item); + if (current_state) { + // Attempt to convert the parameter to the expected type + data = Converter::toGVariantWithSchema(parameter, g_variant_get_type_string(current_state)); + g_variant_unref (current_state); + } else { + data = Converter::toGVariant(parameter); + } + + gtk_menu_tracker_item_change_state (item, data); + if (data) { + g_variant_unref(data); + } +} + + +bool UnityMenuModel::event(QEvent* e) +{ + if (e->type() == UnityMenuModelClearEvent::eventType) { + UnityMenuModelClearEvent *emmce = static_cast(e); + + GSequenceIter *begin; + GSequenceIter *end; + + if (emmce->reset) + beginResetModel(); + + begin = g_sequence_get_begin_iter (priv->items); + end = g_sequence_get_end_iter (priv->items); + g_sequence_remove_range (begin, end); + + if (emmce->reset) + endResetModel(); + + return true; + } else if (e->type() == UnityMenuModelAddRowEvent::eventType) { + UnityMenuModelAddRowEvent *ummrce = static_cast(e); + + GSequenceIter *it; + it = g_sequence_get_iter_at_pos (priv->items, ummrce->position); + + beginInsertRows(QModelIndex(), ummrce->position, ummrce->position + ummrce->items->len - 1); + + for (gint i = ummrce->items->len - 1; i >= 0; --i) { + GtkMenuTrackerItem *item = (GtkMenuTrackerItem*)g_ptr_array_index(ummrce->items, i); + it = g_sequence_insert_before (it, g_object_ref (item)); + g_object_set_qdata (G_OBJECT (item), unity_menu_model_quark (), this); + g_signal_connect (item, "notify", G_CALLBACK (UnityMenuModelPrivate::menuItemChanged), it); + } + + endInsertRows(); + return true; + } else if (e->type() == UnityMenuModelRemoveRowEvent::eventType) { + UnityMenuModelRemoveRowEvent *ummrre = static_cast(e); + + beginRemoveRows(QModelIndex(), ummrre->position, ummrre->position + ummrre->nItems - 1); + for (int i = 0; i < ummrre->nItems; ++i) { + GSequenceIter *it = g_sequence_get_iter_at_pos (priv->items, ummrre->position); + if (!g_sequence_iter_is_end (it)) { + g_sequence_remove (it); + } + } + endRemoveRows(); + + return true; + } else if (e->type() == UnityMenuModelDataChangeEvent::eventType) { + UnityMenuModelDataChangeEvent *ummdce = static_cast(e); + + Q_EMIT dataChanged(index(ummdce->position, 0), index(ummdce->position, 0)); + return true; + } + return QAbstractListModel::event(e); +} + +void UnityMenuModel::registerAction(UnityMenuAction* action) +{ + if (priv->destructorGuard) + return; + + if (!priv->registeredActions.contains(action)) { + GtkSimpleActionObserver* observer_item; + observer_item = gtk_simple_action_observer_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&))); + connect(action, SIGNAL(indexChanged(int)), SLOT(onRegisteredActionIndexChanged(int))); + connect(action, SIGNAL(activate(const QVariant&)), SLOT(onRegisteredActionActivated(const QVariant&))); + connect(action, SIGNAL(changeState(const QVariant&)), SLOT(onRegisteredActionStateChanged(const QVariant&))); + } +} + +void UnityMenuModel::unregisterAction(UnityMenuAction* action) +{ + if (priv->destructorGuard) + return; + + if (priv->registeredActions.contains(action)) { + GtkSimpleActionObserver* observer_item; + observer_item = priv->registeredActions[action]; + g_object_unref(observer_item); + priv->registeredActions.remove(action); + + disconnect(action); + } +} + +/* Returns the full name for @action + * + * If @action is associated with a menu item that is inside of a + * section or submenu with "action-namespace" set, this namespace + * is prepended to @action->name() + */ +char * UnityMenuModelPrivate::fullActionName(UnityMenuAction *action) +{ + GSequenceIter *iter; + QByteArray bytes; + const gchar *name; + gchar *full_name = NULL; + + bytes = action->name().toUtf8(); + name = bytes.constData(); + + iter = g_sequence_get_iter_at_pos (this->items, action->index()); + if (!g_sequence_iter_is_end (iter)) { + GtkMenuTrackerItem *item; + const gchar *action_namespace; + + item = (GtkMenuTrackerItem *) g_sequence_get (iter); + if (!item) { + return g_strdup (name); + } + + action_namespace = gtk_menu_tracker_item_get_action_namespace (item); + if (action_namespace != NULL) + return g_strjoin (".", action_namespace, name, NULL); + } + + return g_strdup (name); +} + +void UnityMenuModelPrivate::updateRegisteredAction(UnityMenuAction *action) +{ + GtkSimpleActionObserver *observer_item; + gchar *action_name; + gboolean enabled; + GVariant *state; + + if (!action || !this->registeredActions.contains(action)) + return; + + action_name = fullActionName(action); + + observer_item = this->registeredActions[action]; + gtk_simple_action_observer_register_action (observer_item, action_name); + + if (g_action_group_query_action (G_ACTION_GROUP (this->muxer), action_name, + &enabled, NULL, NULL, NULL, &state)) + { + UnityMenuActionAddEvent umaae(enabled, Converter::toQVariant(state)); + QCoreApplication::sendEvent(action, &umaae); + + if (state) { + g_variant_unref (state); + } + } + + g_free(action_name); +} + +void UnityMenuModel::onRegisteredActionNameChanged(const QString& name) +{ + priv->updateRegisteredAction(qobject_cast(sender())); +} + +void UnityMenuModel::onRegisteredActionIndexChanged(int index) +{ + priv->updateRegisteredAction(qobject_cast(sender())); +} + +void UnityMenuModel::onRegisteredActionActivated(const QVariant& parameter) +{ + UnityMenuAction* action = qobject_cast(sender()); + if (!action || action->name().isEmpty()) + return; + + gchar* action_name = priv->fullActionName(action); + + g_action_group_activate_action (G_ACTION_GROUP (priv->muxer), action_name, Converter::toGVariant(parameter)); + + g_free(action_name); +} + +void UnityMenuModel::onRegisteredActionStateChanged(const QVariant& parameter) +{ + UnityMenuAction* action = qobject_cast(sender()); + if (!action || action->name().isEmpty()) + return; + + gchar* action_name = priv->fullActionName(action); + + g_action_group_change_action_state (G_ACTION_GROUP (priv->muxer), action_name, Converter::toGVariant(parameter)); + + g_free(action_name); +} + +void UnityMenuModelPrivate::registeredActionAdded(GtkSimpleActionObserver *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 ()); + + if (action) { + UnityMenuActionAddEvent umaae(enabled, Converter::toQVariant(state)); + QCoreApplication::sendEvent(action, &umaae); + } +} + +void UnityMenuModelPrivate::registeredActionEnabledChanged(GtkSimpleActionObserver *observer_item, const gchar *action_name, gboolean enabled) +{ + UnityMenuAction *action; + action = (UnityMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), unity_menu_action_quark ()); + + if (action) { + UnityMenuActionEnabledChangedEvent umaece(enabled); + QCoreApplication::sendEvent(action, &umaece); + } +} + +void UnityMenuModelPrivate::registeredActionStateChanged(GtkSimpleActionObserver *observer_item, const gchar *action_name, GVariant *state) +{ + UnityMenuAction *action; + action = (UnityMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), unity_menu_action_quark ()); + + if (action) { + UnityMenuActionStateChangeEvent umasce(Converter::toQVariant(state)); + QCoreApplication::sendEvent(action, &umasce); + } +} + +void UnityMenuModelPrivate::registeredActionRemoved(GtkSimpleActionObserver *observer_item, const gchar *action_name) +{ + UnityMenuAction *action; + action = (UnityMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), unity_menu_action_quark ()); + + if (action) { + UnityMenuActionRemoveEvent umare; + QCoreApplication::sendEvent(action, &umare); + } +} diff --git a/libqmenumodel/src/ayatanamenumodel.h b/libqmenumodel/src/ayatanamenumodel.h new file mode 100644 index 0000000..7d11a67 --- /dev/null +++ b/libqmenumodel/src/ayatanamenumodel.h @@ -0,0 +1,97 @@ +/* + * 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: Lars Uebernickel + */ + +#ifndef AYATANAMENUMODEL_H +#define AYATANAMENUMODEL_H + +#include +class ActionStateParser; +class QQmlComponent; +class UnityMenuAction; + +class UnityMenuModel: public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(QByteArray busName READ busName WRITE setBusName NOTIFY busNameChanged) + Q_PROPERTY(QByteArray nameOwner READ nameOwner NOTIFY nameOwnerChanged) + Q_PROPERTY(QVariantMap actions READ actions WRITE setActions NOTIFY actionsChanged) + Q_PROPERTY(QByteArray menuObjectPath READ menuObjectPath WRITE setMenuObjectPath NOTIFY menuObjectPathChanged) + Q_PROPERTY(ActionStateParser* actionStateParser READ actionStateParser WRITE setActionStateParser NOTIFY actionStateParserChanged) + +public: + UnityMenuModel(QObject *parent = NULL); + virtual ~UnityMenuModel(); + + QByteArray busName() const; + void setBusName(const QByteArray &name); + + QByteArray nameOwner() const; + + QVariantMap actions() const; + void setActions(const QVariantMap &actions); + + QByteArray menuObjectPath() const; + void setMenuObjectPath(const QByteArray &path); + + ActionStateParser* actionStateParser() const; + void setActionStateParser(ActionStateParser* actionStateParser); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index) const; + QHash roleNames() const; + + Q_INVOKABLE QObject * submenu(int position, QQmlComponent* actionStateParser = NULL); + Q_INVOKABLE bool loadExtendedAttributes(int position, const QVariantMap &schema); + Q_INVOKABLE QVariant get(int row, const QByteArray &role); + + Q_INVOKABLE void activate(int index, const QVariant& parameter = QVariant()); + Q_INVOKABLE void aboutToShow(int index); + Q_INVOKABLE void activateByVariantString(int index, const QString& parameter = QString()); + Q_INVOKABLE void changeState(int index, const QVariant& parameter); + Q_INVOKABLE void changeStateByVariantString(int index, const QString& parameter); + + void registerAction(UnityMenuAction* action); + void unregisterAction(UnityMenuAction* action); + +Q_SIGNALS: + void busNameChanged(const QByteArray &name); + void nameOwnerChanged(const QByteArray &owner); + void actionsChanged(const QByteArray &path); + void menuObjectPathChanged(const QByteArray &path); + void actionStateParserChanged(ActionStateParser* parser); + +protected Q_SLOTS: + void onRegisteredActionNameChanged(const QString& name); + void onRegisteredActionIndexChanged(int); + void onRegisteredActionActivated(const QVariant& parameter); + void onRegisteredActionStateChanged(const QVariant& parameter); + +protected: + virtual bool event(QEvent* e); + +private: + class UnityMenuModelPrivate *priv; + friend class UnityMenuModelPrivate; + + UnityMenuModel(const UnityMenuModelPrivate& other, UnityMenuModel *parent); +}; + +#endif diff --git a/libqmenumodel/src/ayatanamenumodelevents.cpp b/libqmenumodel/src/ayatanamenumodelevents.cpp new file mode 100644 index 0000000..57b2fa0 --- /dev/null +++ b/libqmenumodel/src/ayatanamenumodelevents.cpp @@ -0,0 +1,65 @@ +/* + * 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: + * Nicholas Dedekind +#include + +#include "ayatanamenumodelevents.h" +#include "ayatanamenumodel.h" + +const QEvent::Type UnityMenuModelClearEvent::eventType = static_cast(QEvent::registerEventType()); +const QEvent::Type UnityMenuModelAddRowEvent::eventType = static_cast(QEvent::registerEventType()); +const QEvent::Type UnityMenuModelRemoveRowEvent::eventType = static_cast(QEvent::registerEventType()); +const QEvent::Type UnityMenuModelDataChangeEvent::eventType = static_cast(QEvent::registerEventType()); + +UnityMenuModelClearEvent::UnityMenuModelClearEvent(bool _reset) + : QEvent(UnityMenuModelClearEvent::eventType), + reset(_reset) +{} + +UnityMenuModelAddRowEvent::UnityMenuModelAddRowEvent(GPtrArray *_items, int _position) + : QEvent(UnityMenuModelAddRowEvent::eventType), + items(_items), + position(_position) +{ + if (items) { + for (gint i = 0; i < items->len; ++i) + g_object_ref(g_ptr_array_index(items, i)); + g_ptr_array_ref(items); + } +} + +UnityMenuModelAddRowEvent::~UnityMenuModelAddRowEvent() +{ + if (items) { + for (gint i = 0; i < items->len; ++i) + g_object_unref(g_ptr_array_index(items, i)); + g_ptr_array_unref(items); + } +} + +UnityMenuModelRemoveRowEvent::UnityMenuModelRemoveRowEvent(int _position, int _nItems) + : QEvent(UnityMenuModelRemoveRowEvent::eventType), + position(_position), nItems(_nItems) +{} + +UnityMenuModelDataChangeEvent::UnityMenuModelDataChangeEvent(int _position) + : QEvent(UnityMenuModelDataChangeEvent::eventType), + position(_position) +{} diff --git a/libqmenumodel/src/ayatanamenumodelevents.h b/libqmenumodel/src/ayatanamenumodelevents.h new file mode 100644 index 0000000..1fa067a --- /dev/null +++ b/libqmenumodel/src/ayatanamenumodelevents.h @@ -0,0 +1,71 @@ +/* + * 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: + * Nicholas Dedekind +#include + +typedef struct _GtkMenuTrackerItem GtkMenuTrackerItem; + +/* Event for a unitymenumodel clear */ +class UnityMenuModelClearEvent : public QEvent +{ +public: + static const QEvent::Type eventType; + UnityMenuModelClearEvent(bool reset); + + bool reset; +}; + +/* Event for a row add for unitymenumodel */ +class UnityMenuModelAddRowEvent : public QEvent +{ +public: + static const QEvent::Type eventType; + UnityMenuModelAddRowEvent(GPtrArray *_items, int position); + ~UnityMenuModelAddRowEvent(); + + GPtrArray *items; + int position; +}; + +/* Event for a row remove for unitymenumodel */ +class UnityMenuModelRemoveRowEvent : public QEvent +{ +public: + static const QEvent::Type eventType; + UnityMenuModelRemoveRowEvent(int position, int nItems); + + int position; + int nItems; +}; + +/* Event for a row data change for unitymenumodel */ +class UnityMenuModelDataChangeEvent : public QEvent +{ +public: + static const QEvent::Type eventType; + UnityMenuModelDataChangeEvent(int position); + + int position; +}; + +#endif //AYATANAMENUMODELEVENTS_H diff --git a/libqmenumodel/src/unitymenuaction.cpp b/libqmenumodel/src/unitymenuaction.cpp deleted file mode 100644 index 1f0372d..0000000 --- a/libqmenumodel/src/unitymenuaction.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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 - */ - -#include "unitymenuaction.h" -#include "unitymenumodel.h" -#include "unitymenuactionevents.h" - -UnityMenuAction::UnityMenuAction(QObject* parent) - : QObject(parent), - m_valid(false), - m_enabled(false), - m_model(NULL), - m_index(-1) -{ -} - -UnityMenuAction::~UnityMenuAction() -{ - unregisterAction(); -} - -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) { - unregisterAction(); - m_model = model; - registerAction(); - Q_EMIT modelChanged(model); - } -} - -QVariant UnityMenuAction::state() const -{ - return m_state; -} - -void UnityMenuAction::setState(const QVariant& state) -{ - if (m_state != state) { - m_state = state; - Q_EMIT stateChanged(m_state); - } -} - -bool UnityMenuAction::isEnabled() const -{ - return m_enabled; -} - -void UnityMenuAction::setEnabled(bool enabled) -{ - if (m_enabled != enabled) { - m_enabled = enabled; - Q_EMIT enabledChanged(m_enabled); - } -} - -bool UnityMenuAction::isValid() const -{ - return m_valid; -} - -void UnityMenuAction::setValid(bool valid) -{ - if (m_valid != valid) { - m_valid = valid; - Q_EMIT validChanged(m_valid); - } -} - -int UnityMenuAction::index() const -{ - return m_index; -} - -void UnityMenuAction::setIndex(int i) -{ - if (i != m_index) { - m_index = i; - Q_EMIT indexChanged(m_index); - } -} - -void UnityMenuAction::registerAction() -{ - if (m_model) { - m_model->registerAction(this); - } -} - -void UnityMenuAction::unregisterAction() -{ - if (m_model) { - m_model->unregisterAction(this); - } -} - -bool UnityMenuAction::event(QEvent* e) -{ - if (e->type() == UnityMenuActionAddEvent::eventType) { - UnityMenuActionAddEvent *umaae = static_cast(e); - - setEnabled(umaae->enabled); - setState(umaae->state); - setValid(true); - return true; - } else if (e->type() == UnityMenuActionEnabledChangedEvent::eventType) { - UnityMenuActionEnabledChangedEvent *umaece = static_cast(e); - - setEnabled(umaece->enabled); - return true; - } else if (e->type() == UnityMenuActionStateChangeEvent::eventType) { - UnityMenuActionStateChangeEvent *umasce = static_cast(e); - - setState(umasce->state); - return true; - } else if (e->type() == UnityMenuActionRemoveEvent::eventType) { - UnityMenuActionRemoveEvent *umare = static_cast(e); - - setValid(false); - return true; - } - return QObject::event(e); -} diff --git a/libqmenumodel/src/unitymenuaction.h b/libqmenumodel/src/unitymenuaction.h deleted file mode 100644 index 7479d2c..0000000 --- a/libqmenumodel/src/unitymenuaction.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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) - Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged) - -public: - UnityMenuAction(QObject* parent = 0); - ~UnityMenuAction(); - - QString name() const; - void setName(const QString& str); - - UnityMenuModel* model() const; - void setModel(UnityMenuModel* model); - - int index() const; - void setIndex(int i); - - QVariant state() const; - bool isEnabled() const; - bool isValid() const; - -Q_SIGNALS: - Q_INVOKABLE void activate(const QVariant& parameter = QVariant()); - Q_INVOKABLE void changeState(const QVariant& parameter); - - void nameChanged(const QString& name); - void modelChanged(UnityMenuModel* model); - void stateChanged(const QVariant& name); - void enabledChanged(bool enabled); - void validChanged(bool valid); - void indexChanged(int index); - -protected: - virtual bool event(QEvent* e); - - void setState(const QVariant& state); - void setEnabled(bool enabled); - void setValid(bool valid); - -private: - void unregisterAction(); - void registerAction(); - - QString m_name; - QVariant m_state; - bool m_valid; - bool m_enabled; - UnityMenuModel* m_model; - int m_index; -}; - -#endif // UNITYMENUACTIONGROUP_H diff --git a/libqmenumodel/src/unitymenuactionevents.cpp b/libqmenumodel/src/unitymenuactionevents.cpp deleted file mode 100644 index f05e536..0000000 --- a/libqmenumodel/src/unitymenuactionevents.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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: - * Nicholas Dedekind (QEvent::registerEventType()); -const QEvent::Type UnityMenuActionRemoveEvent::eventType = static_cast(QEvent::registerEventType()); -const QEvent::Type UnityMenuActionEnabledChangedEvent::eventType = static_cast(QEvent::registerEventType()); -const QEvent::Type UnityMenuActionStateChangeEvent::eventType = static_cast(QEvent::registerEventType()); - -UnityMenuActionAddEvent::UnityMenuActionAddEvent(bool _enabled, const QVariant& _state) - : QEvent(UnityMenuActionAddEvent::eventType), - enabled(_enabled), - state(_state) -{} - -UnityMenuActionRemoveEvent::UnityMenuActionRemoveEvent() - : QEvent(UnityMenuActionRemoveEvent::eventType) -{ -} - -UnityMenuActionEnabledChangedEvent::UnityMenuActionEnabledChangedEvent(bool _enabled) - : QEvent(UnityMenuActionEnabledChangedEvent::eventType), - enabled(_enabled) -{} - -UnityMenuActionStateChangeEvent::UnityMenuActionStateChangeEvent(const QVariant& _state) - : QEvent(UnityMenuActionStateChangeEvent::eventType), - state(_state) -{} diff --git a/libqmenumodel/src/unitymenuactionevents.h b/libqmenumodel/src/unitymenuactionevents.h deleted file mode 100644 index 44cb5de..0000000 --- a/libqmenumodel/src/unitymenuactionevents.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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: - * Nicholas Dedekind -#include - -/* Event for a unitymenuaction add */ -class UnityMenuActionAddEvent : public QEvent -{ -public: - static const QEvent::Type eventType; - UnityMenuActionAddEvent(bool enabled, const QVariant& state); - - bool enabled; - QVariant state; -}; - -/* Event for a unitymenuaction remove */ -class UnityMenuActionRemoveEvent : public QEvent -{ -public: - static const QEvent::Type eventType; - UnityMenuActionRemoveEvent(); -}; - -/* Event for change in enabled value of a unitymenuaction */ -class UnityMenuActionEnabledChangedEvent : public QEvent -{ -public: - static const QEvent::Type eventType; - UnityMenuActionEnabledChangedEvent(bool enabled); - - int enabled; -}; - -/* Event for change in state value of a unitymenuaction */ -class UnityMenuActionStateChangeEvent : public QEvent -{ -public: - static const QEvent::Type eventType; - UnityMenuActionStateChangeEvent(const QVariant& state); - - QVariant state; -}; - -#endif //UNITYMENUACTIONEVENTS_H diff --git a/libqmenumodel/src/unitymenumodel.cpp b/libqmenumodel/src/unitymenumodel.cpp deleted file mode 100644 index fafed4c..0000000 --- a/libqmenumodel/src/unitymenumodel.cpp +++ /dev/null @@ -1,1064 +0,0 @@ -/* - * 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: Lars Uebernickel - */ - -#include "unitymenumodel.h" -#include "converter.h" -#include "actionstateparser.h" -#include "unitymenumodelevents.h" -#include "unitymenuaction.h" -#include "unitymenuactionevents.h" -#include "logging.h" - -#include -#include -#include -#include - -extern "C" { - #include "gtk/gtkactionmuxer.h" - #include "gtk/gtkmenutracker.h" - #include "gtk/gtksimpleactionobserver.h" -} - -Q_LOGGING_CATEGORY(unitymenumodel, "qmenumodel.unitymenumodel", QtCriticalMsg) - -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, - SensitiveRole, - IsSeparatorRole, - IconRole, - TypeRole, - ExtendedAttributesRole, - ActionRole, - ActionStateRole, - IsCheckRole, - IsRadioRole, - IsToggledRole, - ShortcutRole, - HasSubmenuRole -}; - -class UnityMenuModelPrivate -{ -public: - UnityMenuModelPrivate(UnityMenuModel *model); - UnityMenuModelPrivate(const UnityMenuModelPrivate& other, UnityMenuModel *model); - ~UnityMenuModelPrivate(); - - void clearItems(bool resetModel=true); - void clearName(); - void updateActions(); - void updateMenuModel(); - QVariant itemState(GtkMenuTrackerItem *item); - - UnityMenuModel *model; - GtkActionMuxer *muxer; - GtkMenuTracker *menutracker; - GSequence *items; - GDBusConnection *connection; - QByteArray busName; - QByteArray nameOwner; - guint nameWatchId; - QVariantMap actions; - QByteArray menuObjectPath; - QHash roles; - ActionStateParser* actionStateParser; - QHash registeredActions; - bool destructorGuard; - - 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(GPtrArray *items, gint position, gpointer user_data); - static void menuItemRemoved(gint position, gint n_items, gpointer user_data); - static void menuItemChanged(GObject *object, GParamSpec *pspec, gpointer user_data); - - static void registeredActionAdded(GtkSimpleActionObserver *observer_item, - const gchar *action_name, - gboolean enabled, - GVariant *state); - static void registeredActionEnabledChanged(GtkSimpleActionObserver *observer_item, const gchar *action_name, gboolean enabled); - static void registeredActionStateChanged(GtkSimpleActionObserver *observer_item, const gchar *action_name, GVariant *state); - static void registeredActionRemoved(GtkSimpleActionObserver *observer_item, const gchar *action_name); - - gchar * fullActionName(UnityMenuAction *action); - void updateRegisteredAction(UnityMenuAction *action); -}; - -void menu_item_free (gpointer data) -{ - GtkMenuTrackerItem *item = (GtkMenuTrackerItem *) data; - - g_signal_handlers_disconnect_by_func (item, (gpointer) UnityMenuModelPrivate::menuItemChanged, NULL); - g_object_unref (item); -} - -UnityMenuModelPrivate::UnityMenuModelPrivate(UnityMenuModel *model) -{ - this->model = model; - this->menutracker = NULL; - this->connection = NULL; - this->nameWatchId = 0; - this->actionStateParser = new ActionStateParser(model); - this->destructorGuard = false; - - this->muxer = gtk_action_muxer_new (); - - 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->destructorGuard = false; - - this->muxer = GTK_ACTION_MUXER( g_object_ref(other.muxer)); - - this->items = g_sequence_new (menu_item_free); -} - -UnityMenuModelPrivate::~UnityMenuModelPrivate() -{ - this->destructorGuard = true; - this->clearItems(false); - - g_sequence_free(this->items); - g_clear_pointer (&this->menutracker, gtk_menu_tracker_free); - 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()); - it.key()->setModel(NULL); - } - this->registeredActions.clear(); - - if (this->nameWatchId) - g_bus_unwatch_name (this->nameWatchId); -} - -void UnityMenuModelPrivate::clearItems(bool resetModel) -{ - UnityMenuModelClearEvent ummce(resetModel); - QCoreApplication::sendEvent(model, &ummce); -} - -void UnityMenuModelPrivate::clearName() -{ - this->clearItems(); - - this->nameOwner = QByteArray(); - - this->updateActions(); - this->updateMenuModel(); - - Q_EMIT model->nameOwnerChanged (this->nameOwner); -} - -void UnityMenuModelPrivate::updateActions() -{ - Q_FOREACH (QString prefix, this->actions.keys()) - gtk_action_muxer_remove (this->muxer, prefix.toUtf8()); - - if (this->nameOwner.isEmpty()) - return; - - for (QVariantMap::const_iterator it = this->actions.constBegin(); it != this->actions.constEnd(); ++it) { - GDBusActionGroup *actions; - - actions = g_dbus_action_group_get (this->connection, this->nameOwner, it.value().toByteArray()); - gtk_action_muxer_insert (this->muxer, it.key().toUtf8(), G_ACTION_GROUP (actions)); - - g_object_unref (actions); - } -} - -void UnityMenuModelPrivate::updateMenuModel() -{ - this->clearItems(); - g_clear_pointer (&this->menutracker, gtk_menu_tracker_free); - - if (!this->nameOwner.isEmpty()) { - GDBusMenuModel *menu; - - menu = g_dbus_menu_model_get (this->connection, this->nameOwner, this->menuObjectPath.constData()); - this->menutracker = gtk_menu_tracker_new (GTK_ACTION_OBSERVABLE (this->muxer), - G_MENU_MODEL (menu), TRUE, NULL, - menuItemInserted, menuItemRemoved, this); - - g_object_unref (menu); - } -} - -QVariant UnityMenuModelPrivate::itemState(GtkMenuTrackerItem *item) -{ - QVariant result; - - GVariant *state = gtk_menu_tracker_item_get_action_state (item); - if (state != NULL) { - if (actionStateParser != NULL) { - result = actionStateParser->toQVariant(state); - } - g_variant_unref (state); - } - - return result; -} - -void UnityMenuModelPrivate::nameAppeared(GDBusConnection *connection, const gchar *name, const gchar *owner, gpointer user_data) -{ - UnityMenuModelPrivate *priv = (UnityMenuModelPrivate *)user_data; - - priv->connection = (GDBusConnection *) g_object_ref (connection); - priv->nameOwner = owner; - - priv->updateActions(); - priv->updateMenuModel(); - - Q_EMIT priv->model->nameOwnerChanged (priv->nameOwner); -} - -void UnityMenuModelPrivate::nameVanished(GDBusConnection *connection, const gchar *name, gpointer user_data) -{ - UnityMenuModelPrivate *priv = (UnityMenuModelPrivate *)user_data; - - priv->clearName(); -} - -void UnityMenuModelPrivate::menuItemInserted(GPtrArray *items, gint position, gpointer user_data) -{ - UnityMenuModelPrivate *priv = (UnityMenuModelPrivate *)user_data; - - UnityMenuModelAddRowEvent ummare(items, position); - QCoreApplication::sendEvent(priv->model, &ummare); -} - -void UnityMenuModelPrivate::menuItemRemoved(gint position, gint n_items, gpointer user_data) -{ - UnityMenuModelPrivate *priv = (UnityMenuModelPrivate *)user_data; - - UnityMenuModelRemoveRowEvent ummrre(position, n_items); - QCoreApplication::sendEvent(priv->model, &ummrre); -} - -void UnityMenuModelPrivate::menuItemChanged(GObject *object, GParamSpec *pspec, gpointer user_data) -{ - GSequenceIter *it = (GSequenceIter *) user_data; - GtkMenuTrackerItem *item; - GtkActionObservable *muxer; - UnityMenuModel *model; - gint position; - - item = (GtkMenuTrackerItem *) g_sequence_get (it); - muxer = _gtk_menu_tracker_item_get_observable (item); - model = (UnityMenuModel *) g_object_get_qdata (G_OBJECT (item), unity_menu_model_quark ()); - position = g_sequence_iter_get_position (it); - - UnityMenuModelDataChangeEvent ummdce(position); - QCoreApplication::sendEvent(model, &ummdce); -} - -UnityMenuModel::UnityMenuModel(QObject *parent): - QAbstractListModel(parent) -{ - priv = new UnityMenuModelPrivate(this); -} - -UnityMenuModel::UnityMenuModel(const UnityMenuModelPrivate& other, UnityMenuModel *parent): - QAbstractListModel(parent) -{ - priv = new UnityMenuModelPrivate(other, this); -} - -UnityMenuModel::~UnityMenuModel() -{ - delete priv; -} - -QByteArray UnityMenuModel::busName() const -{ - return priv->busName; -} - -QByteArray UnityMenuModel::nameOwner() const -{ - return priv->nameOwner; -} - -void UnityMenuModel::setBusName(const QByteArray &name) -{ - if (name == priv->busName) - return; - - priv->clearName(); - - if (priv->nameWatchId) - g_bus_unwatch_name (priv->nameWatchId); - - priv->nameWatchId = g_bus_watch_name (G_BUS_TYPE_SESSION, name.constData(), G_BUS_NAME_WATCHER_FLAGS_AUTO_START, - UnityMenuModelPrivate::nameAppeared, UnityMenuModelPrivate::nameVanished, - priv, NULL); - priv->busName = name; - Q_EMIT busNameChanged (priv->busName); -} - -QVariantMap UnityMenuModel::actions() const -{ - return priv->actions; -} - -void UnityMenuModel::setActions(const QVariantMap &actions) -{ - priv->actions = actions; - priv->updateActions(); -} - -QByteArray UnityMenuModel::menuObjectPath() const -{ - return priv->menuObjectPath; -} - -void UnityMenuModel::setMenuObjectPath(const QByteArray &path) -{ - priv->menuObjectPath = path; - priv->updateMenuModel(); -} - -ActionStateParser* UnityMenuModel::actionStateParser() const -{ - return priv->actionStateParser; -} - -void UnityMenuModel::setActionStateParser(ActionStateParser* actionStateParser) -{ - if (priv->actionStateParser != actionStateParser) { - priv->actionStateParser = actionStateParser; - Q_EMIT actionStateParserChanged(actionStateParser); - } -} - -int UnityMenuModel::rowCount(const QModelIndex &parent) const -{ - return !parent.isValid() ? g_sequence_get_length (priv->items) : 0; -} - -int UnityMenuModel::columnCount(const QModelIndex &parent) const -{ - return 1; -} - -static QString iconUri(GIcon *icon) -{ - QString uri; - - if (G_IS_THEMED_ICON (icon)) { - const gchar* const* iconNames = g_themed_icon_get_names (G_THEMED_ICON (icon)); - guint index = 0; - while(iconNames[index] != NULL) { - if (QIcon::hasThemeIcon(iconNames[index])) { - uri = QString("image://theme/") + iconNames[index]; - break; - } - index++; - } - } - else if (G_IS_FILE_ICON (icon)) { - GFile *file; - - file = g_file_icon_get_file (G_FILE_ICON (icon)); - if (file) { - gchar *fileuri; - - fileuri = g_file_get_uri (file); - uri = QString(fileuri); - - g_free (fileuri); - } - } - else if (G_IS_BYTES_ICON (icon)) { - gsize size; - gconstpointer data; - gchar *base64; - - data = g_bytes_get_data (g_bytes_icon_get_bytes (G_BYTES_ICON (icon)), &size); - base64 = g_base64_encode ((const guchar *) data, size); - - uri = QString("data://"); - uri.append (base64); - - g_free (base64); - } - - return uri; -} - -QVariant UnityMenuModel::data(const QModelIndex &index, int role) const -{ - GSequenceIter *it; - GtkMenuTrackerItem *item; - - it = g_sequence_get_iter_at_pos (priv->items, index.row()); - if (g_sequence_iter_is_end (it)) { - return QVariant(); - } - - item = (GtkMenuTrackerItem *) g_sequence_get (it); - if (!item) { - return QVariant(); - } - - switch (role) { - case LabelRole: - return gtk_menu_tracker_item_get_label (item); - - case SensitiveRole: - return gtk_menu_tracker_item_get_sensitive (item) == TRUE ? true : false; - - case IsSeparatorRole: - return gtk_menu_tracker_item_get_is_separator (item) == TRUE ? true : false; - - case IconRole: { - GIcon *icon = gtk_menu_tracker_item_get_icon (item); - if (icon) { - QString uri = iconUri(icon); - g_object_unref (icon); - return uri; - } - else - return QString(); - } - - case TypeRole: { - gchar *type; - auto ret = gtk_menu_tracker_item_get_attribute (item, "x-ayatana-type", "s", &type); - - // If we can't get x-ayatana-type, try legacy x-canonical-type type - if (!ret) - ret = gtk_menu_tracker_item_get_attribute (item, "x-canonical-type", "s", &type); - - if (ret) { - QVariant v(type); - g_free (type); - return v; - } - else - return QVariant(); - } - - case ExtendedAttributesRole: { - QVariantMap *map = (QVariantMap *) g_object_get_qdata (G_OBJECT (item), unity_menu_item_extended_attributes_quark ()); - return map ? *map : QVariant(); - } - - case ActionRole: { - gchar *action_name = gtk_menu_tracker_item_get_action_name (item); - QString v(action_name); - g_free(action_name); - return v; - } - - case ActionStateRole: - return priv->itemState(item); - - case IsCheckRole: - return gtk_menu_tracker_item_get_role (item) == GTK_MENU_TRACKER_ITEM_ROLE_CHECK; - - case IsRadioRole: - return gtk_menu_tracker_item_get_role (item) == GTK_MENU_TRACKER_ITEM_ROLE_RADIO; - - case IsToggledRole: - return gtk_menu_tracker_item_get_toggled (item) != FALSE; - - case ShortcutRole: - return QKeySequence(gtk_menu_tracker_item_get_accel (item), QKeySequence::NativeText); - - case HasSubmenuRole: - return gtk_menu_tracker_item_get_has_submenu (item) != FALSE; - - default: - return QVariant(); - } -} - -QModelIndex UnityMenuModel::index(int row, int column, const QModelIndex &parent) const -{ - return createIndex(row, column); -} - -QModelIndex UnityMenuModel::parent(const QModelIndex &index) const -{ - return QModelIndex(); -} - -QHash UnityMenuModel::roleNames() const -{ - QHash names; - - names[LabelRole] = "label"; - names[SensitiveRole] = "sensitive"; - names[IsSeparatorRole] = "isSeparator"; - names[IconRole] = "icon"; - names[TypeRole] = "type"; - names[ExtendedAttributesRole] = "ext"; - names[ActionRole] = "action"; - names[ActionStateRole] = "actionState"; - names[IsCheckRole] = "isCheck"; - names[IsRadioRole] = "isRadio"; - names[IsToggledRole] = "isToggled"; - names[ShortcutRole] = "shortcut"; - names[HasSubmenuRole] = "hasSubmenu"; - - return names; -} - -QObject * UnityMenuModel::submenu(int position, QQmlComponent* actionStateParser) -{ - GSequenceIter *it; - GtkMenuTrackerItem *item; - UnityMenuModel *model; - - it = g_sequence_get_iter_at_pos (priv->items, position); - if (g_sequence_iter_is_end (it)) { - return NULL; - } - - item = (GtkMenuTrackerItem *) g_sequence_get (it); - if (!item || !gtk_menu_tracker_item_get_has_submenu (item)) { - return NULL; - } - - model = (UnityMenuModel *) g_object_get_qdata (G_OBJECT (item), unity_submenu_model_quark ()); - if (model == NULL) { - model = new UnityMenuModel(*priv, this); - - if (actionStateParser) { - ActionStateParser* parser = qobject_cast(actionStateParser->create()); - if (parser) { - model->setActionStateParser(parser); - } - } - - model->priv->menutracker = gtk_menu_tracker_new_for_item_submenu (item, - UnityMenuModelPrivate::menuItemInserted, - UnityMenuModelPrivate::menuItemRemoved, - model->priv); - g_object_set_qdata (G_OBJECT (item), unity_submenu_model_quark (), model); - } - - return model; -} - -static void freeExtendedAttrs(gpointer data) -{ - QVariantMap *extendedAttrs = (QVariantMap *) data; - delete extendedAttrs; -} - -static QVariant attributeToQVariant(GVariant *value, const QString &type) -{ - QVariant result; - - if (type == "int") { - if (g_variant_is_of_type (value, G_VARIANT_TYPE_INT32)) { - result = QVariant(g_variant_get_int32(value)); - } - } - else if (type == "int64") { - if (g_variant_is_of_type (value, G_VARIANT_TYPE_INT64)) { - result = QVariant((qlonglong)g_variant_get_int64(value)); - } - } - else if (type == "bool") { - if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)) { - result = QVariant(g_variant_get_boolean(value)); - } - } - else if (type == "string") { - if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) { - result = QVariant(g_variant_get_string(value, NULL)); - } - } - else if (type == "double") { - if (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE)) { - result = QVariant(g_variant_get_double(value)); - } - } - else if (type == "variant") { - if (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT)) { - result = Converter::toQVariant(value); - } - } - else if (type == "icon") { - GIcon *icon = g_icon_deserialize (value); - if (icon) { - result = iconUri(icon); - g_object_unref (icon); - } - else { - result = QVariant(""); - } - } - - return result; -} - -/* convert 'some-key' to 'someKey' or 'SomeKey'. (from dconf-qt) */ -static QString qtify_name(const char *name) -{ - bool next_cap = false; - QString result; - - while (*name) { - if (*name == '-') { - next_cap = true; - } else if (next_cap) { - result.append(toupper(*name)); - next_cap = false; - } else { - result.append(*name); - } - - name++; - } - - return result; -} - -bool UnityMenuModel::loadExtendedAttributes(int position, const QVariantMap &schema) -{ - GSequenceIter *it; - GtkMenuTrackerItem *item; - QVariantMap *extendedAttrs; - - it = g_sequence_get_iter_at_pos (priv->items, position); - if (g_sequence_iter_is_end (it)) { - return false; - } - - item = (GtkMenuTrackerItem *) g_sequence_get (it); - if (!item) { - return false; - } - - extendedAttrs = new QVariantMap; - - for (QVariantMap::const_iterator it = schema.constBegin(); it != schema.constEnd(); ++it) { - QString name = it.key(); - QString type = it.value().toString(); - - GVariant *value = gtk_menu_tracker_item_get_attribute_value (item, name.toUtf8(), NULL); - if (value == NULL) { - qCWarning(unitymenumodel, "loadExtendedAttributes: menu item does not contain '%s'", it.key().toUtf8().constData()); - continue; - } - - const QVariant &qvalue = attributeToQVariant(value, type); - if (qvalue.isValid()) - extendedAttrs->insert(qtify_name (name.toUtf8()), qvalue); - else - qCWarning(unitymenumodel, "loadExtendedAttributes: key '%s' is of type '%s' (expected '%s')", - name.toUtf8().constData(), g_variant_get_type_string(value), type.constData()); - - g_variant_unref (value); - } - - g_object_set_qdata_full (G_OBJECT (item), unity_menu_item_extended_attributes_quark (), - extendedAttrs, freeExtendedAttrs); - - Q_EMIT dataChanged(index(position, 0), index(position, 0), QVector() << ExtendedAttributesRole); - return true; -} - -QVariant UnityMenuModel::get(int row, const QByteArray &role) -{ - if (priv->roles.isEmpty()) { - QHash names = roleNames(); - Q_FOREACH (int role, names.keys()) - priv->roles.insert(names[role], role); - } - - return this->data(this->index(row, 0), priv->roles[role]); -} - -void UnityMenuModel::activate(int index, const QVariant& parameter) -{ - GSequenceIter *it; - GtkMenuTrackerItem *item; - GVariant *value; - const GVariantType *parameter_type; - - it = g_sequence_get_iter_at_pos (priv->items, index); - if (g_sequence_iter_is_end (it)) { - return; - } - - item = (GtkMenuTrackerItem *) g_sequence_get (it); - if (!item) { - return; - } - - if (parameter.isValid()) { - gchar *action; - - action = gtk_menu_tracker_item_get_action_name (item); - parameter_type = g_action_group_get_action_parameter_type (G_ACTION_GROUP (priv->muxer), action); - value = Converter::toGVariantWithSchema(parameter, g_variant_type_peek_string (parameter_type)); - g_action_group_activate_action (G_ACTION_GROUP (priv->muxer), action, value); - - g_free (action); - } else { - gtk_menu_tracker_item_activated (item); - } -} - -void UnityMenuModel::aboutToShow(int index) -{ - GSequenceIter *it = g_sequence_get_iter_at_pos (priv->items, index); - if (g_sequence_iter_is_end (it)) { - return; - } - - auto item = static_cast(g_sequence_get(it)); - if (!item) { - return; - } - - quint64 actionTag; - if (gtk_menu_tracker_item_get_attribute (item, "qtubuntu-tag", "t", &actionTag)) { - // Child UnityMenuModel have priv->connection null, so climb to the parent until we find a non null one - UnityMenuModelPrivate *privToUse = priv; - while (privToUse && !privToUse->connection) { - auto pModel = dynamic_cast(privToUse->model->QObject::parent()); - if (pModel) { - privToUse = pModel->priv; - } else { - privToUse = nullptr; - } - } - if (privToUse) { - g_dbus_connection_call (privToUse->connection, - privToUse->busName, - privToUse->menuObjectPath, - "qtubuntu.actions.extra", - "aboutToShow", - g_variant_new("(t)", actionTag), - nullptr, - G_DBUS_CALL_FLAGS_NO_AUTO_START, - G_MAXINT, - nullptr, - nullptr, - nullptr); - } - } -} - -void UnityMenuModel::activateByVariantString(int index, const QString& parameter) -{ - activate(index, Converter::toQVariantFromVariantString(parameter)); -} - -void UnityMenuModel::changeStateByVariantString(int index, const QString& parameter) -{ - changeState(index, Converter::toQVariantFromVariantString(parameter)); -} - -void UnityMenuModel::changeState(int index, const QVariant& parameter) -{ - GSequenceIter *it; - GtkMenuTrackerItem* item; - GVariant* data; - GVariant* current_state; - - it = g_sequence_get_iter_at_pos (priv->items, index); - if (g_sequence_iter_is_end (it)) { - return; - } - - item = (GtkMenuTrackerItem *) g_sequence_get (it); - if (!item) { - return; - } - - current_state = gtk_menu_tracker_item_get_action_state (item); - if (current_state) { - // Attempt to convert the parameter to the expected type - data = Converter::toGVariantWithSchema(parameter, g_variant_get_type_string(current_state)); - g_variant_unref (current_state); - } else { - data = Converter::toGVariant(parameter); - } - - gtk_menu_tracker_item_change_state (item, data); - if (data) { - g_variant_unref(data); - } -} - - -bool UnityMenuModel::event(QEvent* e) -{ - if (e->type() == UnityMenuModelClearEvent::eventType) { - UnityMenuModelClearEvent *emmce = static_cast(e); - - GSequenceIter *begin; - GSequenceIter *end; - - if (emmce->reset) - beginResetModel(); - - begin = g_sequence_get_begin_iter (priv->items); - end = g_sequence_get_end_iter (priv->items); - g_sequence_remove_range (begin, end); - - if (emmce->reset) - endResetModel(); - - return true; - } else if (e->type() == UnityMenuModelAddRowEvent::eventType) { - UnityMenuModelAddRowEvent *ummrce = static_cast(e); - - GSequenceIter *it; - it = g_sequence_get_iter_at_pos (priv->items, ummrce->position); - - beginInsertRows(QModelIndex(), ummrce->position, ummrce->position + ummrce->items->len - 1); - - for (gint i = ummrce->items->len - 1; i >= 0; --i) { - GtkMenuTrackerItem *item = (GtkMenuTrackerItem*)g_ptr_array_index(ummrce->items, i); - it = g_sequence_insert_before (it, g_object_ref (item)); - g_object_set_qdata (G_OBJECT (item), unity_menu_model_quark (), this); - g_signal_connect (item, "notify", G_CALLBACK (UnityMenuModelPrivate::menuItemChanged), it); - } - - endInsertRows(); - return true; - } else if (e->type() == UnityMenuModelRemoveRowEvent::eventType) { - UnityMenuModelRemoveRowEvent *ummrre = static_cast(e); - - beginRemoveRows(QModelIndex(), ummrre->position, ummrre->position + ummrre->nItems - 1); - for (int i = 0; i < ummrre->nItems; ++i) { - GSequenceIter *it = g_sequence_get_iter_at_pos (priv->items, ummrre->position); - if (!g_sequence_iter_is_end (it)) { - g_sequence_remove (it); - } - } - endRemoveRows(); - - return true; - } else if (e->type() == UnityMenuModelDataChangeEvent::eventType) { - UnityMenuModelDataChangeEvent *ummdce = static_cast(e); - - Q_EMIT dataChanged(index(ummdce->position, 0), index(ummdce->position, 0)); - return true; - } - return QAbstractListModel::event(e); -} - -void UnityMenuModel::registerAction(UnityMenuAction* action) -{ - if (priv->destructorGuard) - return; - - if (!priv->registeredActions.contains(action)) { - GtkSimpleActionObserver* observer_item; - observer_item = gtk_simple_action_observer_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&))); - connect(action, SIGNAL(indexChanged(int)), SLOT(onRegisteredActionIndexChanged(int))); - connect(action, SIGNAL(activate(const QVariant&)), SLOT(onRegisteredActionActivated(const QVariant&))); - connect(action, SIGNAL(changeState(const QVariant&)), SLOT(onRegisteredActionStateChanged(const QVariant&))); - } -} - -void UnityMenuModel::unregisterAction(UnityMenuAction* action) -{ - if (priv->destructorGuard) - return; - - if (priv->registeredActions.contains(action)) { - GtkSimpleActionObserver* observer_item; - observer_item = priv->registeredActions[action]; - g_object_unref(observer_item); - priv->registeredActions.remove(action); - - disconnect(action); - } -} - -/* Returns the full name for @action - * - * If @action is associated with a menu item that is inside of a - * section or submenu with "action-namespace" set, this namespace - * is prepended to @action->name() - */ -char * UnityMenuModelPrivate::fullActionName(UnityMenuAction *action) -{ - GSequenceIter *iter; - QByteArray bytes; - const gchar *name; - gchar *full_name = NULL; - - bytes = action->name().toUtf8(); - name = bytes.constData(); - - iter = g_sequence_get_iter_at_pos (this->items, action->index()); - if (!g_sequence_iter_is_end (iter)) { - GtkMenuTrackerItem *item; - const gchar *action_namespace; - - item = (GtkMenuTrackerItem *) g_sequence_get (iter); - if (!item) { - return g_strdup (name); - } - - action_namespace = gtk_menu_tracker_item_get_action_namespace (item); - if (action_namespace != NULL) - return g_strjoin (".", action_namespace, name, NULL); - } - - return g_strdup (name); -} - -void UnityMenuModelPrivate::updateRegisteredAction(UnityMenuAction *action) -{ - GtkSimpleActionObserver *observer_item; - gchar *action_name; - gboolean enabled; - GVariant *state; - - if (!action || !this->registeredActions.contains(action)) - return; - - action_name = fullActionName(action); - - observer_item = this->registeredActions[action]; - gtk_simple_action_observer_register_action (observer_item, action_name); - - if (g_action_group_query_action (G_ACTION_GROUP (this->muxer), action_name, - &enabled, NULL, NULL, NULL, &state)) - { - UnityMenuActionAddEvent umaae(enabled, Converter::toQVariant(state)); - QCoreApplication::sendEvent(action, &umaae); - - if (state) { - g_variant_unref (state); - } - } - - g_free(action_name); -} - -void UnityMenuModel::onRegisteredActionNameChanged(const QString& name) -{ - priv->updateRegisteredAction(qobject_cast(sender())); -} - -void UnityMenuModel::onRegisteredActionIndexChanged(int index) -{ - priv->updateRegisteredAction(qobject_cast(sender())); -} - -void UnityMenuModel::onRegisteredActionActivated(const QVariant& parameter) -{ - UnityMenuAction* action = qobject_cast(sender()); - if (!action || action->name().isEmpty()) - return; - - gchar* action_name = priv->fullActionName(action); - - g_action_group_activate_action (G_ACTION_GROUP (priv->muxer), action_name, Converter::toGVariant(parameter)); - - g_free(action_name); -} - -void UnityMenuModel::onRegisteredActionStateChanged(const QVariant& parameter) -{ - UnityMenuAction* action = qobject_cast(sender()); - if (!action || action->name().isEmpty()) - return; - - gchar* action_name = priv->fullActionName(action); - - g_action_group_change_action_state (G_ACTION_GROUP (priv->muxer), action_name, Converter::toGVariant(parameter)); - - g_free(action_name); -} - -void UnityMenuModelPrivate::registeredActionAdded(GtkSimpleActionObserver *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 ()); - - if (action) { - UnityMenuActionAddEvent umaae(enabled, Converter::toQVariant(state)); - QCoreApplication::sendEvent(action, &umaae); - } -} - -void UnityMenuModelPrivate::registeredActionEnabledChanged(GtkSimpleActionObserver *observer_item, const gchar *action_name, gboolean enabled) -{ - UnityMenuAction *action; - action = (UnityMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), unity_menu_action_quark ()); - - if (action) { - UnityMenuActionEnabledChangedEvent umaece(enabled); - QCoreApplication::sendEvent(action, &umaece); - } -} - -void UnityMenuModelPrivate::registeredActionStateChanged(GtkSimpleActionObserver *observer_item, const gchar *action_name, GVariant *state) -{ - UnityMenuAction *action; - action = (UnityMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), unity_menu_action_quark ()); - - if (action) { - UnityMenuActionStateChangeEvent umasce(Converter::toQVariant(state)); - QCoreApplication::sendEvent(action, &umasce); - } -} - -void UnityMenuModelPrivate::registeredActionRemoved(GtkSimpleActionObserver *observer_item, const gchar *action_name) -{ - UnityMenuAction *action; - action = (UnityMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), unity_menu_action_quark ()); - - if (action) { - UnityMenuActionRemoveEvent umare; - QCoreApplication::sendEvent(action, &umare); - } -} diff --git a/libqmenumodel/src/unitymenumodel.h b/libqmenumodel/src/unitymenumodel.h deleted file mode 100644 index 1a2a190..0000000 --- a/libqmenumodel/src/unitymenumodel.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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: Lars Uebernickel - */ - -#ifndef UNITYMENUMODEL_H -#define UNITYMENUMODEL_H - -#include -class ActionStateParser; -class QQmlComponent; -class UnityMenuAction; - -class UnityMenuModel: public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(QByteArray busName READ busName WRITE setBusName NOTIFY busNameChanged) - Q_PROPERTY(QByteArray nameOwner READ nameOwner NOTIFY nameOwnerChanged) - Q_PROPERTY(QVariantMap actions READ actions WRITE setActions NOTIFY actionsChanged) - Q_PROPERTY(QByteArray menuObjectPath READ menuObjectPath WRITE setMenuObjectPath NOTIFY menuObjectPathChanged) - Q_PROPERTY(ActionStateParser* actionStateParser READ actionStateParser WRITE setActionStateParser NOTIFY actionStateParserChanged) - -public: - UnityMenuModel(QObject *parent = NULL); - virtual ~UnityMenuModel(); - - QByteArray busName() const; - void setBusName(const QByteArray &name); - - QByteArray nameOwner() const; - - QVariantMap actions() const; - void setActions(const QVariantMap &actions); - - QByteArray menuObjectPath() const; - void setMenuObjectPath(const QByteArray &path); - - ActionStateParser* actionStateParser() const; - void setActionStateParser(ActionStateParser* actionStateParser); - - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; - QModelIndex parent(const QModelIndex &index) const; - QHash roleNames() const; - - Q_INVOKABLE QObject * submenu(int position, QQmlComponent* actionStateParser = NULL); - Q_INVOKABLE bool loadExtendedAttributes(int position, const QVariantMap &schema); - Q_INVOKABLE QVariant get(int row, const QByteArray &role); - - Q_INVOKABLE void activate(int index, const QVariant& parameter = QVariant()); - Q_INVOKABLE void aboutToShow(int index); - Q_INVOKABLE void activateByVariantString(int index, const QString& parameter = QString()); - Q_INVOKABLE void changeState(int index, const QVariant& parameter); - Q_INVOKABLE void changeStateByVariantString(int index, const QString& parameter); - - void registerAction(UnityMenuAction* action); - void unregisterAction(UnityMenuAction* action); - -Q_SIGNALS: - void busNameChanged(const QByteArray &name); - void nameOwnerChanged(const QByteArray &owner); - void actionsChanged(const QByteArray &path); - void menuObjectPathChanged(const QByteArray &path); - void actionStateParserChanged(ActionStateParser* parser); - -protected Q_SLOTS: - void onRegisteredActionNameChanged(const QString& name); - void onRegisteredActionIndexChanged(int); - void onRegisteredActionActivated(const QVariant& parameter); - void onRegisteredActionStateChanged(const QVariant& parameter); - -protected: - virtual bool event(QEvent* e); - -private: - class UnityMenuModelPrivate *priv; - friend class UnityMenuModelPrivate; - - UnityMenuModel(const UnityMenuModelPrivate& other, UnityMenuModel *parent); -}; - -#endif diff --git a/libqmenumodel/src/unitymenumodelevents.cpp b/libqmenumodel/src/unitymenumodelevents.cpp deleted file mode 100644 index 46a9517..0000000 --- a/libqmenumodel/src/unitymenumodelevents.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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: - * Nicholas Dedekind -#include - -#include "unitymenumodelevents.h" -#include "unitymenumodel.h" - -const QEvent::Type UnityMenuModelClearEvent::eventType = static_cast(QEvent::registerEventType()); -const QEvent::Type UnityMenuModelAddRowEvent::eventType = static_cast(QEvent::registerEventType()); -const QEvent::Type UnityMenuModelRemoveRowEvent::eventType = static_cast(QEvent::registerEventType()); -const QEvent::Type UnityMenuModelDataChangeEvent::eventType = static_cast(QEvent::registerEventType()); - -UnityMenuModelClearEvent::UnityMenuModelClearEvent(bool _reset) - : QEvent(UnityMenuModelClearEvent::eventType), - reset(_reset) -{} - -UnityMenuModelAddRowEvent::UnityMenuModelAddRowEvent(GPtrArray *_items, int _position) - : QEvent(UnityMenuModelAddRowEvent::eventType), - items(_items), - position(_position) -{ - if (items) { - for (gint i = 0; i < items->len; ++i) - g_object_ref(g_ptr_array_index(items, i)); - g_ptr_array_ref(items); - } -} - -UnityMenuModelAddRowEvent::~UnityMenuModelAddRowEvent() -{ - if (items) { - for (gint i = 0; i < items->len; ++i) - g_object_unref(g_ptr_array_index(items, i)); - g_ptr_array_unref(items); - } -} - -UnityMenuModelRemoveRowEvent::UnityMenuModelRemoveRowEvent(int _position, int _nItems) - : QEvent(UnityMenuModelRemoveRowEvent::eventType), - position(_position), nItems(_nItems) -{} - -UnityMenuModelDataChangeEvent::UnityMenuModelDataChangeEvent(int _position) - : QEvent(UnityMenuModelDataChangeEvent::eventType), - position(_position) -{} diff --git a/libqmenumodel/src/unitymenumodelevents.h b/libqmenumodel/src/unitymenumodelevents.h deleted file mode 100644 index d860f0e..0000000 --- a/libqmenumodel/src/unitymenumodelevents.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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: - * Nicholas Dedekind -#include - -typedef struct _GtkMenuTrackerItem GtkMenuTrackerItem; - -/* Event for a unitymenumodel clear */ -class UnityMenuModelClearEvent : public QEvent -{ -public: - static const QEvent::Type eventType; - UnityMenuModelClearEvent(bool reset); - - bool reset; -}; - -/* Event for a row add for unitymenumodel */ -class UnityMenuModelAddRowEvent : public QEvent -{ -public: - static const QEvent::Type eventType; - UnityMenuModelAddRowEvent(GPtrArray *_items, int position); - ~UnityMenuModelAddRowEvent(); - - GPtrArray *items; - int position; -}; - -/* Event for a row remove for unitymenumodel */ -class UnityMenuModelRemoveRowEvent : public QEvent -{ -public: - static const QEvent::Type eventType; - UnityMenuModelRemoveRowEvent(int position, int nItems); - - int position; - int nItems; -}; - -/* Event for a row data change for unitymenumodel */ -class UnityMenuModelDataChangeEvent : public QEvent -{ -public: - static const QEvent::Type eventType; - UnityMenuModelDataChangeEvent(int position); - - int position; -}; - -#endif //UNITYMENUMODELEVENTS_H diff --git a/tests/client/ayatanamenuactiontest.cpp b/tests/client/ayatanamenuactiontest.cpp new file mode 100644 index 0000000..3dcec95 --- /dev/null +++ b/tests/client/ayatanamenuactiontest.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2014 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 + */ + +#include "ayatanamenumodel.h" +#include "ayatanamenuaction.h" + +#include +#include + +class UnityMenuActionTest : public QObject +{ + Q_OBJECT +private: + +private Q_SLOTS: + + /* + * Test if the propety busType handle correct integer values + */ + void testDestroyAfterModel() + { + UnityMenuModel* model = new UnityMenuModel; + UnityMenuAction* action = new UnityMenuAction; + action->setModel(model); + + delete model; + delete action; + } + + /* + * Test if the propety busType handle correct integer values + */ + void testDestroyBeforeModel() + { + UnityMenuModel* model = new UnityMenuModel; + UnityMenuAction* action = new UnityMenuAction; + action->setModel(model); + + delete action; + delete model; + } +}; + +QTEST_MAIN(UnityMenuActionTest) + +#include "unitymenuactiontest.moc" diff --git a/tests/client/unitymenuactiontest.cpp b/tests/client/unitymenuactiontest.cpp deleted file mode 100644 index d33ab2e..0000000 --- a/tests/client/unitymenuactiontest.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2014 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 - */ - -#include "unitymenumodel.h" -#include "unitymenuaction.h" - -#include -#include - -class UnityMenuActionTest : public QObject -{ - Q_OBJECT -private: - -private Q_SLOTS: - - /* - * Test if the propety busType handle correct integer values - */ - void testDestroyAfterModel() - { - UnityMenuModel* model = new UnityMenuModel; - UnityMenuAction* action = new UnityMenuAction; - action->setModel(model); - - delete model; - delete action; - } - - /* - * Test if the propety busType handle correct integer values - */ - void testDestroyBeforeModel() - { - UnityMenuModel* model = new UnityMenuModel; - UnityMenuAction* action = new UnityMenuAction; - action->setModel(model); - - delete action; - delete model; - } -}; - -QTEST_MAIN(UnityMenuActionTest) - -#include "unitymenuactiontest.moc" -- cgit v1.2.3