From b619354abad7662a8ad53d4885741d4d2ea8f4a0 Mon Sep 17 00:00:00 2001 From: Robert Tari Date: Wed, 22 Jul 2020 14:17:17 +0200 Subject: Display IDO widgets/Use own action muxer/Allow IDO CSS styling --- libayatana-indicator/ayatanamenuitemfactory.c | 69 +++++++++++++++ libayatana-indicator/ayatanamenuitemfactory.h | 61 ++++++++++++++ libayatana-indicator/indicator-ng.c | 116 +++++++++++++++++++++++++- 3 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 libayatana-indicator/ayatanamenuitemfactory.c create mode 100644 libayatana-indicator/ayatanamenuitemfactory.h diff --git a/libayatana-indicator/ayatanamenuitemfactory.c b/libayatana-indicator/ayatanamenuitemfactory.c new file mode 100644 index 0000000..0e333ab --- /dev/null +++ b/libayatana-indicator/ayatanamenuitemfactory.c @@ -0,0 +1,69 @@ +/* +* Copyright 2013 Canonical Ltd. +* +* This program is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 3, as published +* by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranties of +* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +* +* Authors: +* Lars Uebernickel +*/ + +#include "ayatanamenuitemfactory.h" + +G_DEFINE_INTERFACE_WITH_CODE (AyatanaMenuItemFactory, ayatana_menu_item_factory, G_TYPE_OBJECT, + GIOExtensionPoint *ep = g_io_extension_point_register (AYATANA_MENU_ITEM_FACTORY_EXTENSION_POINT_NAME); + g_io_extension_point_set_required_type (ep, g_define_type_id);) + +/* + * ayatana_menu_item_factory_get_all: + * + * Returns a static list of all registered factories. + */ +GList * +ayatana_menu_item_factory_get_all (void) +{ + static GList *factories = NULL; + + if (factories == NULL) + { + GIOExtensionPoint *ep; + GList *it; + + g_type_ensure (AYATANA_TYPE_MENU_ITEM_FACTORY); + ep = g_io_extension_point_lookup (AYATANA_MENU_ITEM_FACTORY_EXTENSION_POINT_NAME); + for (it = g_io_extension_point_get_extensions (ep); it != NULL; it = it->next) + { + GIOExtension *ext = it->data; + AyatanaMenuItemFactory *factory; + + factory = g_object_new (g_io_extension_get_type (ext), NULL); + factories = g_list_prepend (factories, factory); + } + factories = g_list_reverse (factories); + } + + return factories; +} + +static void +ayatana_menu_item_factory_default_init (AyatanaMenuItemFactoryInterface *iface) +{ +} + +GtkMenuItem * +ayatana_menu_item_factory_create_menu_item (AyatanaMenuItemFactory *factory, + const gchar *type, + GMenuItem *menuitem, + GActionGroup *actions) +{ + return AYATANA_MENU_ITEM_FACTORY_GET_IFACE (factory)->create_menu_item (factory, type, menuitem, actions); +} diff --git a/libayatana-indicator/ayatanamenuitemfactory.h b/libayatana-indicator/ayatanamenuitemfactory.h new file mode 100644 index 0000000..4a241be --- /dev/null +++ b/libayatana-indicator/ayatanamenuitemfactory.h @@ -0,0 +1,61 @@ +/* +* Copyright 2013 Canonical Ltd. +* +* This program is free software: you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 3, as published +* by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranties of +* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +* +* Authors: +* Lars Uebernickel +*/ + +#ifndef __AYATANA_MENU_ITEM_FACTORY_H__ +#define __AYATANA_MENU_ITEM_FACTORY_H__ + +#include + +G_BEGIN_DECLS + +#define AYATANA_TYPE_MENU_ITEM_FACTORY (ayatana_menu_item_factory_get_type ()) +#define AYATANA_MENU_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), AYATANA_TYPE_MENU_ITEM_FACTORY, AyatanaMenuItemFactory)) +#define AYATANA_IS_MENU_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), AYATANA_TYPE_MENU_ITEM_FACTORY)) +#define AYATANA_MENU_ITEM_FACTORY_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), AYATANA_TYPE_MENU_ITEM_FACTORY, AyatanaMenuItemFactoryInterface)) + +#define AYATANA_MENU_ITEM_FACTORY_EXTENSION_POINT_NAME "ayatana-menu-item-factory" + +typedef struct _AyatanaMenuItemFactoryInterface AyatanaMenuItemFactoryInterface; +typedef struct _AyatanaMenuItemFactory AyatanaMenuItemFactory; + +struct _AyatanaMenuItemFactoryInterface +{ + GTypeInterface iface; + + GtkMenuItem * (*create_menu_item) (AyatanaMenuItemFactory *factory, + const gchar *type, + GMenuItem *menuitem, + GActionGroup *actions); +}; + +GDK_AVAILABLE_IN_3_10 +GList * ayatana_menu_item_factory_get_all (void); + +GDK_AVAILABLE_IN_3_10 +GType ayatana_menu_item_factory_get_type (void); + +GDK_AVAILABLE_IN_3_10 +GtkMenuItem * ayatana_menu_item_factory_create_menu_item (AyatanaMenuItemFactory *factory, + const gchar *type, + GMenuItem *menuitem, + GActionGroup *actions); + +G_END_DECLS + +#endif diff --git a/libayatana-indicator/indicator-ng.c b/libayatana-indicator/indicator-ng.c index d1040a1..9801d3c 100644 --- a/libayatana-indicator/indicator-ng.c +++ b/libayatana-indicator/indicator-ng.c @@ -19,9 +19,11 @@ #include "indicator-ng.h" #include "indicator-image-helper.h" - +#include "ayatanamenuitemfactory.h" #include +#define MENU_SECTIONS 20 + struct _IndicatorNg { IndicatorObject parent; @@ -48,6 +50,7 @@ struct _IndicatorNg gchar *accessible_desc; gint64 last_service_restart; + GMenuModel *lMenuSections[MENU_SECTIONS]; }; static void indicator_ng_initable_iface_init (GInitableIface *initable); @@ -62,6 +65,7 @@ enum N_PROPERTIES }; +static GQuark m_pActionMuxer = 0; static GParamSpec *properties[N_PROPERTIES]; static void @@ -122,6 +126,15 @@ indicator_ng_free_actions_and_menu (IndicatorNg *self) if (self->menu) { + for (guint nMenuSection = 0; nMenuSection < MENU_SECTIONS; nMenuSection++) + { + if (self->lMenuSections[nMenuSection]) + { + g_object_unref(self->lMenuSections[nMenuSection]); + self->lMenuSections[nMenuSection] = NULL; + } + } + g_signal_handlers_disconnect_by_data (self->menu, self); g_clear_object (&self->menu); } @@ -219,12 +232,98 @@ indicator_ng_secondary_activate (IndicatorObject *io, } } +static void indicator_ng_menu_section_changed(GMenuModel *pMenuSection, gint nPosition, gint nRemoved, gint nAdded, gpointer pUserData) +{ + IndicatorNg *self = pUserData; + GMenuModel *pMenuModel = g_menu_model_get_item_link(self->menu, 0, G_MENU_LINK_SUBMENU); + guint nCurrMenuItem = 0; + + if (pMenuModel) + { + gint nSections = g_menu_model_get_n_items(pMenuModel); + + for (gint nSection = 0; nSection < nSections; nSection++) + { + GMenuModel *pMenuModelSection = g_menu_model_get_item_link(pMenuModel, nSection, G_MENU_LINK_SECTION); + + if (pMenuModelSection) + { + gint nMenuItems = g_menu_model_get_n_items(pMenuModelSection); + + for (gint nMenuItem = 0; nMenuItem < nMenuItems; nMenuItem++) + { + gchar *sType; + gboolean bHasType = g_menu_model_get_item_attribute(pMenuModelSection, nMenuItem, "x-canonical-type", "s", &sType); + + if (bHasType) + { + GList *lMenuItems = gtk_container_get_children(GTK_CONTAINER(self->entry.menu)); + GtkWidget *pMenuItemOld = GTK_WIDGET(g_list_nth_data(lMenuItems, nCurrMenuItem)); + const gchar *sName = gtk_widget_get_name(pMenuItemOld); + + if (!g_str_equal(sName, sType)) + { + GActionGroup *pActionGroup = (GActionGroup*)g_object_get_qdata(G_OBJECT(self->entry.menu), m_pActionMuxer); + GMenuItem *pMenuModelItem = g_menu_item_new_from_model(pMenuModelSection, nMenuItem); + GtkMenuItem* pMenuItemNew = NULL; + + for (GList *pFactory = ayatana_menu_item_factory_get_all(); pFactory != NULL && pMenuItemNew == NULL; pFactory = pFactory->next) + { + pMenuItemNew = ayatana_menu_item_factory_create_menu_item(pFactory->data, sType, pMenuModelItem, pActionGroup); + } + + gtk_widget_set_name(GTK_WIDGET(pMenuItemNew), sType); + gtk_widget_show(GTK_WIDGET(pMenuItemNew)); + gtk_container_remove(GTK_CONTAINER(self->entry.menu), pMenuItemOld); + gtk_menu_shell_insert(GTK_MENU_SHELL(self->entry.menu), GTK_WIDGET(pMenuItemNew), nCurrMenuItem); + g_object_unref(pMenuModelItem); + } + + g_list_free(lMenuItems); + } + + nCurrMenuItem++; + } + + g_object_unref(pMenuModelSection); + } + + nCurrMenuItem++; + } + + g_object_unref(pMenuModel); + } +} + static void indicator_ng_menu_shown (GtkWidget *widget, gpointer user_data) { IndicatorNg *self = user_data; + if (!self->lMenuSections[0]) + { + self->lMenuSections[0] = g_menu_model_get_item_link(self->menu, 0, G_MENU_LINK_SUBMENU); + + if (self->lMenuSections[0]) + { + gint nSections = g_menu_model_get_n_items(self->lMenuSections[0]); + + for (gint nSection = 0; nSection < nSections; nSection++) + { + self->lMenuSections[nSection + 1] = g_menu_model_get_item_link(self->lMenuSections[0], nSection, G_MENU_LINK_SECTION); + + if (self->lMenuSections[nSection + 1]) + { + g_signal_connect(self->lMenuSections[nSection + 1], "items-changed", G_CALLBACK(indicator_ng_menu_section_changed), self); + } + } + + g_signal_connect(self->lMenuSections[0], "items-changed", G_CALLBACK(indicator_ng_menu_section_changed), self); + indicator_ng_menu_section_changed(self->lMenuSections[0], 0, 0, 1, self); + } + } + if (self->submenu_action) g_action_group_change_action_state (self->actions, self->submenu_action, g_variant_new_boolean (TRUE)); @@ -425,6 +524,14 @@ indicator_ng_menu_changed (GMenuModel *menu, g_free (action); } + for (guint nMenuSection = 0; nMenuSection < MENU_SECTIONS; nMenuSection++) + { + if (self->lMenuSections[nMenuSection]) + { + g_object_unref(self->lMenuSections[nMenuSection]); + } + } + popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU); if (popup) { @@ -687,6 +794,13 @@ indicator_ng_initable_iface_init (GInitableIface *initable) static void indicator_ng_init (IndicatorNg *self) { + m_pActionMuxer = g_quark_from_static_string ("gtk-widget-action-muxer"); + + for (guint nMenuSection = 0; nMenuSection < MENU_SECTIONS; nMenuSection++) + { + self->lMenuSections[nMenuSection] = NULL; + } + self->entry.label = (GtkLabel*)g_object_ref_sink (gtk_label_new (NULL)); self->entry.image = (GtkImage*)g_object_ref_sink (gtk_image_new ()); -- cgit v1.2.3