diff options
Diffstat (limited to 'libayatana-indicator/indicator-ng.c')
-rw-r--r-- | libayatana-indicator/indicator-ng.c | 1031 |
1 files changed, 0 insertions, 1031 deletions
diff --git a/libayatana-indicator/indicator-ng.c b/libayatana-indicator/indicator-ng.c deleted file mode 100644 index f057600..0000000 --- a/libayatana-indicator/indicator-ng.c +++ /dev/null @@ -1,1031 +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 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 <http://www.gnu.org/licenses/>. - * - * Authors: - * Lars Uebernickel <lars.uebernickel@canonical.com> - */ - -#include "indicator-ng.h" -#include "indicator-image-helper.h" -#include <libayatana-ido/ayatanamenuitemfactory.h> -#include <string.h> - -#define MENU_SECTIONS 20 - -struct _IndicatorNg -{ - IndicatorObject parent; - - gchar *service_file; - gchar *name; - gchar *object_path; - gchar *menu_object_path; - gchar *bus_name; - gchar *profile; - gchar *header_action; - gchar *scroll_action; - gchar *secondary_action; - gchar *submenu_action; - gint position; - - guint name_watch_id; - - GDBusConnection *session_bus; - GActionGroup *actions; - GMenuModel *menu; - - IndicatorObjectEntry entry; - gchar *accessible_desc; - - gint64 last_service_restart; - GMenuModel *lMenuSections[MENU_SECTIONS]; -}; - -static void indicator_ng_initable_iface_init (GInitableIface *initable); -G_DEFINE_TYPE_WITH_CODE (IndicatorNg, indicator_ng, INDICATOR_OBJECT_TYPE, - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, indicator_ng_initable_iface_init)) - -enum -{ - PROP_0, - PROP_SERVICE_FILE, - PROP_PROFILE, - N_PROPERTIES -}; - -static GQuark m_pActionMuxer = 0; -static GParamSpec *properties[N_PROPERTIES]; - -static void -indicator_ng_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - IndicatorNg *self = INDICATOR_NG (object); - - switch (property_id) - { - case PROP_SERVICE_FILE: - g_value_set_string (value, self->service_file); - break; - - case PROP_PROFILE: - g_value_set_string (value, self->profile); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -} - -static void -indicator_ng_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - IndicatorNg *self = INDICATOR_NG (object); - - switch (property_id) - { - case PROP_SERVICE_FILE: /* construct-only */ - self->service_file = g_strdup (g_value_get_string (value)); - break; - - case PROP_PROFILE: /* construct-only */ - self->profile = g_strdup (g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - } -} - -static void -indicator_ng_free_actions_and_menu (IndicatorNg *self) -{ - if (self->actions) - { - gtk_widget_insert_action_group (GTK_WIDGET (self->entry.menu), "indicator", NULL); - g_signal_handlers_disconnect_by_data (self->actions, self); - g_clear_object (&self->actions); - } - - 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); - } -} - -static void -indicator_ng_dispose (GObject *object) -{ - IndicatorNg *self = INDICATOR_NG (object); - - if (self->name_watch_id) - { - g_bus_unwatch_name (self->name_watch_id); - self->name_watch_id = 0; - } - - g_clear_object (&self->session_bus); - - indicator_ng_free_actions_and_menu (self); - - g_clear_object (&self->entry.label); - g_clear_object (&self->entry.image); - g_clear_object (&self->entry.menu); - - G_OBJECT_CLASS (indicator_ng_parent_class)->dispose (object); -} - -static void -indicator_ng_finalize (GObject *object) -{ - IndicatorNg *self = INDICATOR_NG (object); - - g_free (self->service_file); - g_free (self->name); - g_free (self->object_path); - g_free (self->menu_object_path); - g_free (self->bus_name); - g_free (self->accessible_desc); - g_free (self->header_action); - g_free (self->scroll_action); - g_free (self->secondary_action); - g_free (self->submenu_action); - - G_OBJECT_CLASS (indicator_ng_parent_class)->finalize (object); -} - -static GList * -indicator_ng_get_entries (IndicatorObject *io) -{ - IndicatorNg *self = INDICATOR_NG (io); - - return g_list_append (NULL, &self->entry); -} - -static gint -indicator_ng_get_position (IndicatorObject *io) -{ - IndicatorNg *self = INDICATOR_NG (io); - - return self->position; -} - -static void -indicator_ng_entry_scrolled (IndicatorObject *io, - __attribute__((unused)) IndicatorObjectEntry *entry, - gint delta, - IndicatorScrollDirection direction) -{ - IndicatorNg *self = INDICATOR_NG (io); - - if (self->actions && self->scroll_action) - { - if (direction == INDICATOR_OBJECT_SCROLL_DOWN || - direction == INDICATOR_OBJECT_SCROLL_LEFT) - { - delta *= -1; - } - - g_action_group_activate_action (self->actions, self->scroll_action, - g_variant_new_int32 (delta)); - } -} - -void -indicator_ng_secondary_activate (IndicatorObject *io, - __attribute__((unused)) IndicatorObjectEntry *entry, - __attribute__((unused)) guint timestamp, - __attribute__((unused)) gpointer user_data) -{ - IndicatorNg *self = INDICATOR_NG (io); - - if (self->actions && self->secondary_action) - { - g_action_group_activate_action (self->actions, self->secondary_action, NULL); - } -} - -static gboolean indicator_ng_menu_insert_idos(IndicatorNg *self, GMenuModel *pSection, guint nModelItem, guint nMenuItem, gboolean bNamespace, gchar *sNamespace) -{ - gboolean bChanged = FALSE; - gchar *sType; - gboolean bHasType = g_menu_model_get_item_attribute(pSection, nModelItem, "x-ayatana-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, nMenuItem)); - 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(pSection, nModelItem); - GtkMenuItem* pMenuItemNew = NULL; - gchar *sAction; - - if (bNamespace && g_menu_item_get_attribute(pMenuModelItem, G_MENU_ATTRIBUTE_ACTION, "s", &sAction)) - { - gchar *sNamespacedAction = g_strconcat(sNamespace, ".", sAction, NULL); - g_menu_item_set_attribute(pMenuModelItem, G_MENU_ATTRIBUTE_ACTION, "s", sNamespacedAction); - g_free (sNamespacedAction); - g_free (sAction); - } - - 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); - bChanged = TRUE; - } - - if (pMenuItemNew == NULL) - { - pMenuItemNew = GTK_MENU_ITEM(gtk_menu_item_new_with_label("Failed to create IDO object")); - } - - 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), nMenuItem); - g_object_unref(pMenuModelItem); - } - - g_list_free(lMenuItems); - g_free(sType); - } - - return bChanged; -} - -static void indicator_ng_menu_size_allocate(__attribute__((unused)) GtkWidget *pWidget, __attribute__((unused)) GtkAllocation *pAllocation, gpointer pUserData) -{ - IndicatorNg *self = pUserData; - GList *pMenuItem = gtk_container_get_children(GTK_CONTAINER(self->entry.menu)); - guint nWidth = 0; - guint nHeight = 0; - GdkWindow *pWindowBin = NULL; - - while (pMenuItem) - { - if (!pWindowBin) - { - pWindowBin = gtk_widget_get_parent_window(pMenuItem->data); - } - - gint nWidthNat; - gint nHeightNat; - gtk_widget_get_preferred_width(pMenuItem->data, NULL, &nWidthNat); - gtk_widget_get_preferred_height(pMenuItem->data, NULL, &nHeightNat); - nWidth = MAX((gint)nWidth, nWidthNat); - nHeight += nHeightNat; - GtkBorder cPadding; - GtkStyleContext *pContext = gtk_widget_get_style_context(GTK_WIDGET(pMenuItem->data)); - gtk_style_context_get_padding(pContext, gtk_style_context_get_state(pContext), &cPadding); - nWidth += cPadding.left + cPadding.right; - pMenuItem = g_list_next(pMenuItem); - } - - g_list_free(pMenuItem); - GtkBorder cPadding; - GtkStyleContext *pContext = gtk_widget_get_style_context(GTK_WIDGET(self->entry.menu)); - gtk_style_context_get_padding(pContext, gtk_style_context_get_state(pContext), &cPadding); - gint nBorderWidth = gtk_container_get_border_width(GTK_CONTAINER(self->entry.menu)); - gint nIconWidth; - gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &nIconWidth, NULL); - nWidth += (2 * nBorderWidth) + cPadding.left + cPadding.right + (nIconWidth * 3) / 2; - nHeight += (2 * nBorderWidth) + cPadding.top + cPadding.bottom + (nIconWidth * 3) / 4; - - GdkRectangle cRectangle = {0}; - GdkDisplay *pDisplay = gdk_display_get_default(); - GdkMonitor *pMonitor = gdk_display_get_primary_monitor(pDisplay); - gdk_monitor_get_workarea(pMonitor, &cRectangle); - - if ((gint)nHeight <= cRectangle.height) - { - gdk_window_move_resize(pWindowBin, 0, 0, nWidth, nHeight); - } - - nHeight = MIN((gint)nHeight, cRectangle.height); - - GdkWindow *pWindow = gtk_widget_get_parent_window(GTK_WIDGET(self->entry.menu)); - gdk_window_resize(pWindow, nWidth, nHeight); - - gtk_menu_reposition(self->entry.menu); -} - -static void indicator_ng_menu_section_changed(__attribute__((unused)) GMenuModel *pMenuSection, __attribute__((unused)) gint nPosition, __attribute__((unused)) gint nRemoved, __attribute__((unused)) gint nAdded, gpointer pUserData) -{ - IndicatorNg *self = pUserData; - GMenuModel *pModel = g_menu_model_get_item_link(self->menu, 0, G_MENU_LINK_SUBMENU); - guint nMenuItem = 0; - gboolean bChanged = FALSE; - - if (pModel) - { - guint nSections = g_menu_model_get_n_items(pModel); - - for (guint nSection = 0; nSection < nSections; nSection++) - { - GMenuModel *pSection = g_menu_model_get_item_link(pModel, nSection, G_MENU_LINK_SECTION); - guint nSubsections = 0; - - if (pSection) - { - gchar *sNamespace; - gboolean bNamespace = g_menu_model_get_item_attribute(pModel, nSection, G_MENU_ATTRIBUTE_ACTION_NAMESPACE, "s", &sNamespace); - nSubsections = g_menu_model_get_n_items(pSection); - - for (guint nSubsection = 0; nSubsection < nSubsections; nSubsection++) - { - GMenuModel *pSubsection = g_menu_model_get_item_link(pSection, nSubsection, G_MENU_LINK_SECTION); - - if (pSubsection) - { - guint nItems = g_menu_model_get_n_items(pSubsection); - - // Skip the subsection separator (if there is one) - GList *lMenuItems = gtk_container_get_children(GTK_CONTAINER(self->entry.menu)); - GtkWidget *pMenuItem = GTK_WIDGET(g_list_nth_data(lMenuItems, nMenuItem)); - - if (GTK_IS_SEPARATOR_MENU_ITEM(pMenuItem)) - { - nMenuItem++; - } - - g_list_free(lMenuItems); - - for (guint nItem = 0; nItem < nItems; nItem++) - { - bChanged = indicator_ng_menu_insert_idos(self, pSubsection, nItem, nMenuItem, bNamespace, sNamespace) || bChanged; - nMenuItem++; - } - } - - g_object_unref(pSubsection); - - bChanged = indicator_ng_menu_insert_idos(self, pSection, nSubsection, nMenuItem, bNamespace, sNamespace) || bChanged; - - if (!g_str_equal(self->name, "ayatana-indicator-messages")) - { - nMenuItem++; - } - } - - if (bNamespace) - { - g_free(sNamespace); - } - - g_object_unref(pSection); - } - - if (pSection && nSubsections) - { - nMenuItem++; - } - } - - g_object_unref(pModel); - } - - if (bChanged) - { - indicator_ng_menu_size_allocate(NULL, NULL, self); - } -} - -static void indicator_ng_menu_shown(__attribute__((unused)) GtkWidget *pWidget, gpointer pUserData) -{ - IndicatorNg *self = pUserData; - guint nSectionCount = 0; - - if (!self->lMenuSections[0]) - { - self->lMenuSections[0] = g_menu_model_get_item_link(self->menu, 0, G_MENU_LINK_SUBMENU); - - if (self->lMenuSections[0]) - { - guint nSections = g_menu_model_get_n_items(self->lMenuSections[0]); - - for (guint nSection = 0; nSection < nSections; nSection++) - { - self->lMenuSections[++nSectionCount] = g_menu_model_get_item_link(self->lMenuSections[0], nSection, G_MENU_LINK_SECTION); - - if (self->lMenuSections[nSectionCount]) - { - g_signal_connect(self->lMenuSections[nSectionCount], "items-changed", G_CALLBACK(indicator_ng_menu_section_changed), self); - guint nSubsections = g_menu_model_get_n_items(self->lMenuSections[nSectionCount]); - guint nParent = nSectionCount; - - for (guint nSubsection = 0; nSubsection < nSubsections; nSubsection++) - { - self->lMenuSections[++nSectionCount] = g_menu_model_get_item_link(self->lMenuSections[nParent], nSubsection, G_MENU_LINK_SECTION); - - if (self->lMenuSections[nSectionCount]) - { - g_signal_connect(self->lMenuSections[nSectionCount], "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)); - } -} - -static void -indicator_ng_menu_hidden (__attribute__((unused)) GtkWidget *widget, - gpointer user_data) -{ - IndicatorNg *self = user_data; - - if (self->submenu_action) - g_action_group_change_action_state (self->actions, self->submenu_action, - g_variant_new_boolean (FALSE)); -} - -static void -indicator_ng_set_accessible_desc (IndicatorNg *self, - const gchar *accessible_desc) -{ - g_free (self->accessible_desc); - self->accessible_desc = g_strdup (accessible_desc); - - self->entry.accessible_desc = self->accessible_desc; - g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, &self->entry); -} - -static void -indicator_ng_set_icon_from_variant (IndicatorNg *self, - GVariant *variant) -{ - GIcon *icon; - - if (variant == NULL) - { - if (self->entry.image) - { - gtk_image_clear (self->entry.image); - gtk_widget_hide (GTK_WIDGET (self->entry.image)); - } - return; - } - - gtk_widget_show (GTK_WIDGET (self->entry.image)); - - icon = g_icon_deserialize (variant); - if (icon) - { - indicator_image_helper_update_from_gicon (self->entry.image, icon); - g_object_unref (icon); - } - else - { - gchar *text = g_variant_print (variant, TRUE); - g_warning ("invalid icon variant '%s'", text); - gtk_image_set_from_icon_name (self->entry.image, "image-missing", GTK_ICON_SIZE_LARGE_TOOLBAR); - g_free (text); - } -} - -static void indicator_ng_set_label(IndicatorNg *self, const gchar *label) -{ - if (!self->entry.label) - { - return; - } - - const gchar *sLabel = label; - guint nSpacing = 3; - guint nPadding = 6; - - if (label == NULL || *label == '\0' || !self->entry.image || !gtk_widget_get_visible(GTK_WIDGET(self->entry.image))) - { - nSpacing = 0; - nPadding = 0; - } - - GtkWidget *pParent = gtk_widget_get_parent(GTK_WIDGET(self->entry.label)); - GtkCssProvider *pCssProvider = gtk_css_provider_new(); - GtkStyleContext *pStyleContext = gtk_widget_get_style_context(GTK_WIDGET(self->entry.label)); - gtk_style_context_add_provider(pStyleContext, GTK_STYLE_PROVIDER(pCssProvider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - gchar *sCss = g_strdup_printf("label{padding-left: %ipx;}", nPadding); - gtk_css_provider_load_from_data(pCssProvider, sCss, -1, NULL); - g_free(sCss); - g_object_unref(pCssProvider); - if (GTK_IS_BOX(pParent)) - { - gtk_box_set_spacing(GTK_BOX(pParent), nSpacing); - } - gtk_label_set_label(GTK_LABEL (self->entry.label), sLabel); - - if (label) - { - gtk_widget_show(GTK_WIDGET (self->entry.label)); - } -} - -static void -indicator_ng_update_entry (IndicatorNg *self) -{ - GVariant *state; - const gchar *label = NULL; - GVariant *icon = NULL; - const gchar *accessible_desc = NULL; - gboolean visible = TRUE; - - g_return_if_fail (self->menu != NULL); - g_return_if_fail (self->actions != NULL); - - if (!self->header_action || - !g_action_group_has_action (self->actions, self->header_action)) - { - indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); - return; - } - - state = g_action_group_get_action_state (self->actions, self->header_action); - if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("(sssb)"))) - { - const gchar *iconstr = NULL; - - g_variant_get (state, "(&s&s&sb)", &label, &iconstr, &accessible_desc, &visible); - - if (iconstr) - icon = g_variant_ref_sink (g_variant_new_string (iconstr)); - } - else if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("a{sv}"))) - { - g_variant_lookup (state, "label", "&s", &label); - g_variant_lookup (state, "icon", "*", &icon); - g_variant_lookup (state, "accessible-desc", "&s", &accessible_desc); - g_variant_lookup (state, "visible", "b", &visible); - } - else - g_warning ("the action of the indicator menu item must have state with type (sssb) or a{sv}"); - - indicator_ng_set_label (self, label); - indicator_ng_set_icon_from_variant (self, icon); - indicator_ng_set_accessible_desc (self, accessible_desc); - indicator_object_set_visible (INDICATOR_OBJECT (self), visible); - - if (icon) - g_variant_unref (icon); - if (state) - g_variant_unref (state); -} - -static gboolean -indicator_ng_menu_item_is_of_type (GMenuModel *menu, - gint index, - const gchar *expected_type) -{ - gchar *type; - gboolean has_type = FALSE; - - if (g_menu_model_get_item_attribute (menu, index, "x-ayatana-type", "s", &type)) - { - has_type = g_str_equal (type, expected_type); - g_free (type); - } - - return has_type; -} - -static void -indicator_ng_menu_changed (__attribute__((unused)) GMenuModel *menu, - gint position, - gint removed, - gint added, - gpointer user_data) -{ - IndicatorNg *self = user_data; - - /* The menu may only contain one item (the indicator title menu). - * Thus, the position is always 0, and there is either exactly one - * item added or exactly one item removed. - */ - g_return_if_fail (position == 0); - g_return_if_fail (added < 2 && removed < 2 && added ^ removed); - - if (removed) - indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); - - if (added) - { - g_clear_pointer (&self->header_action, g_free); - g_clear_pointer (&self->scroll_action, g_free); - g_clear_pointer (&self->secondary_action, g_free); - - if (indicator_ng_menu_item_is_of_type (self->menu, 0, "org.ayatana.indicator.root")) - { - GMenuModel *popup; - gchar *action; - - if (g_menu_model_get_item_attribute (self->menu, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action)) - { - if (g_str_has_prefix (action, "indicator.")) - self->header_action = g_strdup (action + strlen ("indicator.")); - g_free (action); - } - - if (g_menu_model_get_item_attribute (self->menu, 0, "x-ayatana-scroll-action", "s", &action)) - { - if (g_str_has_prefix (action, "indicator.")) - self->scroll_action = g_strdup (action + strlen ("indicator.")); - g_free (action); - } - - if (g_menu_model_get_item_attribute (self->menu, 0, "x-ayatana-secondary-action", "s", &action)) - { - if (g_str_has_prefix (action, "indicator.")) - self->secondary_action = g_strdup (action + strlen ("indicator.")); - g_free (action); - } - - if (g_menu_model_get_item_attribute (self->menu, 0, "submenu-action", "s", &action)) - { - if (g_str_has_prefix (action, "indicator.")) - self->submenu_action = g_strdup (action + strlen ("indicator.")); - 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) - { - gtk_menu_shell_bind_model (GTK_MENU_SHELL (self->entry.menu), popup, NULL, TRUE); - g_object_unref (popup); - } - - indicator_ng_update_entry (self); - } - else - g_warning ("indicator menu item must be of type 'org.ayatana.indicator.root'"); - } -} - -static void -indicator_ng_service_appeared (GDBusConnection *connection, - __attribute__((unused)) const gchar *name, - const gchar *name_owner, - gpointer user_data) -{ - IndicatorNg *self = user_data; - - g_assert (!self->actions); - g_assert (!self->menu); - - /* watch is not established when menu_object_path == NULL */ - g_assert (self->menu_object_path); - - self->session_bus = g_object_ref (connection); - - self->actions = G_ACTION_GROUP (g_dbus_action_group_get (connection, name_owner, self->object_path)); - gtk_widget_insert_action_group (GTK_WIDGET (self->entry.menu), "indicator", self->actions); - g_signal_connect_swapped (self->actions, "action-added", G_CALLBACK (indicator_ng_update_entry), self); - g_signal_connect_swapped (self->actions, "action-removed", G_CALLBACK (indicator_ng_update_entry), self); - g_signal_connect_swapped (self->actions, "action-state-changed", G_CALLBACK (indicator_ng_update_entry), self); - - self->menu = G_MENU_MODEL (g_dbus_menu_model_get (connection, name_owner, self->menu_object_path)); - g_signal_connect (self->menu, "items-changed", G_CALLBACK (indicator_ng_menu_changed), self); - if (g_menu_model_get_n_items (self->menu)) - indicator_ng_menu_changed (self->menu, 0, 0, 1, self); - - indicator_ng_update_entry (self); -} - -static void -indicator_ng_service_started (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - IndicatorNg *self = user_data; - GError *error = NULL; - GVariant *result; - guint32 start_service_reply; - - result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, &error); - if (!result) - { - g_warning ("Could not activate service '%s': %s", self->name, error->message); - indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); - g_error_free (error); - return; - } - - start_service_reply = 0; - g_variant_get (result, "(u)", &start_service_reply); - - switch (start_service_reply) - { - case 1: /* DBUS_START_REPLY_SUCCESS */ - break; - - case 2: /* DBUS_START_REPLY_ALREADY_RUNNING */ - g_warning ("could not start service '%s': it is already running", self->name); - break; - - default: - g_assert_not_reached (); - } - - g_variant_unref (result); -} - -static void -indicator_ng_service_vanished (__attribute__((unused)) GDBusConnection *connection, - __attribute__((unused)) const gchar *name, - gpointer user_data) -{ - IndicatorNg *self = user_data; - - indicator_ng_free_actions_and_menu (self); - - /* Names may vanish because the service decided it doesn't need to - * show its indicator anymore, or because it crashed. Let's assume it - * crashes and restart it unless it explicitly hid its indicator. */ - - if (indicator_object_entry_is_visible (INDICATOR_OBJECT (self), &self->entry)) - { - gint64 now; - - /* take care not to start it if it repeatedly crashes */ - now = g_get_monotonic_time (); - if (now - self->last_service_restart < 1 * G_USEC_PER_SEC) - { - g_warning ("The indicator '%s' vanished too quickly after appearing. It won't " - "be respawned anymore, as it could be crashing repeatedly.", self->name); - return; - } - - self->last_service_restart = now; - - g_dbus_connection_call (self->session_bus, - "org.freedesktop.DBus", - "/", - "org.freedesktop.DBus", - "StartServiceByName", - g_variant_new ("(su)", self->bus_name, 0), - G_VARIANT_TYPE ("(u)"), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - indicator_ng_service_started, - self); - } -} - -/* Get an integer from a keyfile. Returns @default_value if the key - * doesn't exist exists or is not an integer */ -static gint -g_key_file_maybe_get_integer (GKeyFile *keyfile, - const gchar *group, - const gchar *key, - gint default_value) -{ - GError *error = NULL; - gint i; - - i = g_key_file_get_integer (keyfile, group, key, &error); - if (error) - { - g_error_free (error); - return default_value; - } - - return i; -} - -static gboolean -indicator_ng_load_from_keyfile (IndicatorNg *self, - GKeyFile *keyfile, - GError **error) -{ - g_assert (self->name == NULL); - g_assert (self->object_path == NULL); - g_assert (self->menu_object_path == NULL); - - self->name = g_key_file_get_string (keyfile, "Indicator Service", "Name", error); - if (self->name == NULL) - return FALSE; - - self->object_path = g_key_file_get_string (keyfile, "Indicator Service", "ObjectPath", error); - if (self->object_path == NULL) - return FALSE; - - self->position = g_key_file_maybe_get_integer (keyfile, "Indicator Service", "Position", -1); - - /* - * Don't throw an error when the profile doesn't exist. Non-existant - * profiles are silently ignored by not showing an indicator at all. - */ - if (g_key_file_has_group (keyfile, self->profile)) - { - /* however, if the profile exists, it must have "ObjectPath" */ - self->menu_object_path = g_key_file_get_string (keyfile, self->profile, "ObjectPath", error); - if (self->menu_object_path == NULL) - return FALSE; - - /* a position in the profile overrides the global one */ - self->position = g_key_file_maybe_get_integer (keyfile, self->profile, "Position", self->position); - } - - return TRUE; -} - -static gboolean -indicator_ng_initable_init (GInitable *initable, - __attribute__((unused)) GCancellable *cancellable, - GError **error) -{ - IndicatorNg *self = INDICATOR_NG (initable); - GKeyFile *keyfile; - gboolean success = FALSE; - - self->bus_name = g_path_get_basename (self->service_file); - - keyfile = g_key_file_new (); - if (g_key_file_load_from_file (keyfile, self->service_file, G_KEY_FILE_NONE, error) && - indicator_ng_load_from_keyfile (self, keyfile, error)) - { - self->entry.name_hint = self->name; - - /* only watch the service when it supports the proile we're interested in */ - if (self->menu_object_path) - { - self->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION, - self->bus_name, - G_BUS_NAME_WATCHER_FLAGS_AUTO_START, - indicator_ng_service_appeared, - indicator_ng_service_vanished, - self, NULL); - } - - success = TRUE; - } - - g_key_file_free (keyfile); - return success; -} - -static void -indicator_ng_class_init (IndicatorNgClass *class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (class); - IndicatorObjectClass *io_class = INDICATOR_OBJECT_CLASS (class); - - object_class->get_property = indicator_ng_get_property; - object_class->set_property = indicator_ng_set_property; - object_class->dispose = indicator_ng_dispose; - object_class->finalize = indicator_ng_finalize; - - io_class->get_entries = indicator_ng_get_entries; - io_class->get_position = indicator_ng_get_position; - io_class->entry_scrolled = indicator_ng_entry_scrolled; - io_class->secondary_activate = indicator_ng_secondary_activate; - - properties[PROP_SERVICE_FILE] = g_param_spec_string ("service-file", - "Service file", - "Path of the service file", - NULL, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - properties[PROP_PROFILE] = g_param_spec_string ("profile", - "Profile", - "Indicator profile", - "desktop", - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties(object_class, N_PROPERTIES, properties); -} - -static void -indicator_ng_initable_iface_init (GInitableIface *initable) -{ - initable->init = indicator_ng_initable_init; -} - -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 ()); - - self->entry.menu = (GtkMenu*)g_object_ref_sink (gtk_menu_new ()); - - g_signal_connect (self->entry.menu, "show", G_CALLBACK (indicator_ng_menu_shown), self); - g_signal_connect (self->entry.menu, "hide", G_CALLBACK (indicator_ng_menu_hidden), self); - g_signal_connect (self->entry.menu, "size-allocate", G_CALLBACK (indicator_ng_menu_size_allocate), self); - - GtkCssProvider *pCssProvider = gtk_css_provider_new(); - GtkStyleContext *pStyleContext = gtk_widget_get_style_context(GTK_WIDGET(self->entry.menu)); - gtk_style_context_add_provider(pStyleContext, GTK_STYLE_PROVIDER(pCssProvider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - gtk_css_provider_load_from_data(pCssProvider, "menu > arrow{min-height: 0; padding: 0; margin: 0;}", -1, NULL); - - GtkWidget *pWindow = gtk_widget_get_parent(GTK_WIDGET(self->entry.menu)); - pStyleContext = gtk_widget_get_style_context(pWindow); - gtk_style_context_add_provider(pStyleContext, GTK_STYLE_PROVIDER(pCssProvider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); - gtk_css_provider_load_from_data(pCssProvider, "window > decoration {box-shadow: 0 1px 2px rgba(0,0,0,0.2), 0 0 0 1px rgba(0,0,0,0.13);}", -1, NULL); - - g_object_unref(pCssProvider); - - /* work around IndicatorObject's warning that the accessible - * description is missing. We never set it on construction, but when - * the menu model has arrived on the bus. - */ - self->accessible_desc = g_strdup (""); - self->entry.accessible_desc = self->accessible_desc; - - self->position = -1; - - indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE); -} - -IndicatorNg * -indicator_ng_new (const gchar *service_file, - GError **error) -{ - return g_initable_new (INDICATOR_TYPE_NG, NULL, error, - "service-file", service_file, - NULL); -} - -IndicatorNg * -indicator_ng_new_for_profile (const gchar *service_file, - const gchar *profile, - GError **error) -{ - return g_initable_new (INDICATOR_TYPE_NG, NULL, error, - "service-file", service_file, - "profile", profile, - NULL); -} - -const gchar * -indicator_ng_get_service_file (IndicatorNg *self) -{ - g_return_val_if_fail (INDICATOR_IS_NG (self), NULL); - - return self->service_file; -} - -const gchar * -indicator_ng_get_profile (IndicatorNg *self) -{ - g_return_val_if_fail (INDICATOR_IS_NG (self), NULL); - - return self->profile; -} |