From d3392a98f7b2ceb1bb4fce4a41feed261012346d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 12 Jan 2012 14:51:30 +0100 Subject: initial revision for GSettings/GtkMenu visibility support --- libindicator/indicator-object.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'libindicator/indicator-object.h') diff --git a/libindicator/indicator-object.h b/libindicator/indicator-object.h index 3a120f5..51ca758 100644 --- a/libindicator/indicator-object.h +++ b/libindicator/indicator-object.h @@ -60,6 +60,9 @@ typedef enum #define INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE "secondary-activate" #define INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE_ID (g_signal_lookup(INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE, INDICATOR_OBJECT_TYPE)) +/* the name of the GSettings schema-id property */ +#define INDICATOR_OBJECT_GSETTINGS_SCHEMA_ID "gsettings-schema-id" + typedef struct _IndicatorObject IndicatorObject; typedef struct _IndicatorObjectClass IndicatorObjectClass; typedef struct _IndicatorObjectPrivate IndicatorObjectPrivate; -- cgit v1.2.3 From c52fbaa41bd72b45341f751ba2a19e00c42a4b6d Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Fri, 13 Jan 2012 05:54:17 +0100 Subject: Better implementation. This version handles the bootstrapping conditions better, and also refs+detaches the GtkMenu for safekeeping between removed->added events s.t. it can keep being reused. --- libindicator/indicator-object.c | 340 ++++++++++++++++++++++++++-------------- libindicator/indicator-object.h | 6 +- 2 files changed, 227 insertions(+), 119 deletions(-) (limited to 'libindicator/indicator-object.h') diff --git a/libindicator/indicator-object.c b/libindicator/indicator-object.c index f808f32..55082c7 100644 --- a/libindicator/indicator-object.c +++ b/libindicator/indicator-object.c @@ -30,6 +30,25 @@ License along with this library. If not, see #include "indicator-object-marshal.h" #include "indicator-object-enum-types.h" +/** + @ENTRY_INIT: The entry hasn't been initialized yet, so its + visibility will depend upon the inital-visibility property + and the 'visible' setting in the optional GSettings schema-id + @ENTRY_INVISIBLE: The entry has been initialized but is not visible + @ENTRY_VISIBLE: The entry is visible +*/ +typedef enum { + ENTRY_INIT, + ENTRY_INVISIBLE, + ENTRY_VISIBLE +} +EntryVisibility; + +typedef struct IndicatorObjectEntryPrivate { + EntryVisibility visibility; +} +IndicatorObjectEntryPrivate; + /** IndicatorObjectPrivate: @module: The loaded module representing the object. Note to @@ -53,11 +72,16 @@ struct _IndicatorObjectPrivate { GSettings * gsettings; gchar * gsettings_schema_id; + /* Whether or not entries are visible by default */ + gboolean default_visibility; + GHashTable * entry_privates; + GStrv environments; }; #define INDICATOR_OBJECT_GET_PRIVATE(o) (INDICATOR_OBJECT(o)->priv) + /* Signals Stuff */ enum { ENTRY_ADDED, @@ -76,7 +100,8 @@ enum { found and looked up. */ enum { PROP_0, - PROP_SETTINGS_SCHEMA_ID, + PROP_GSETTINGS_SCHEMA_ID, + PROP_DEFAULT_VISIBILITY, }; @@ -90,12 +115,16 @@ static void indicator_object_finalize (GObject *object); static void set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +/* GSettings schema handling */ static void schema_clear (IndicatorObject * object); static void schema_set (IndicatorObject * object, const char * schema_id); -static GList * get_entries_default (IndicatorObject * io); -static GList * get_all_entries (IndicatorObject * io); -static void stop_listening_for_menu_visibility_changes (IndicatorObject * io); +/* entries' visibility */ +static GList * get_entries_default (IndicatorObject*); +static GList * get_all_entries (IndicatorObject*); +static void remove_all_entries_from_limbo (IndicatorObject*); +static void on_entry_added (IndicatorObject*, IndicatorObjectEntry*, gpointer); +static void on_entry_removed (IndicatorObject*, IndicatorObjectEntry*, gpointer); G_DEFINE_TYPE (IndicatorObject, indicator_object, G_TYPE_OBJECT); @@ -266,12 +295,20 @@ indicator_object_class_init (IndicatorObjectClass *klass) G_TYPE_NONE, 1, G_TYPE_POINTER, G_TYPE_NONE); /* Properties */ + param_spec = g_param_spec_string (INDICATOR_OBJECT_GSETTINGS_SCHEMA_ID, - "gsettings-schema-id", + "schema id", "The schema-id of the GSettings (if any) to monitor.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_SETTINGS_SCHEMA_ID, param_spec); + g_object_class_install_property (object_class, PROP_GSETTINGS_SCHEMA_ID, param_spec); + + param_spec = g_param_spec_string (INDICATOR_OBJECT_DEFAULT_VISIBILITY, + "default visibility", + "The schema-id of the GSettings (if any) to monitor.", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (object_class, PROP_DEFAULT_VISIBILITY, param_spec); return; } @@ -291,13 +328,16 @@ indicator_object_init (IndicatorObject *self) self->priv->entry.name_hint = NULL; self->priv->gotten_entries = FALSE; + self->priv->default_visibility = TRUE; + self->priv->entry_privates = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); self->priv->environments = NULL; self->priv->gsettings = NULL; self->priv->gsettings_schema_id = NULL; - return; + g_signal_connect (G_OBJECT(self), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, G_CALLBACK(on_entry_removed), NULL); + g_signal_connect_after (G_OBJECT(self), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, G_CALLBACK(on_entry_added), NULL); } /* Unref the objects that we're holding on to. */ @@ -307,7 +347,10 @@ indicator_object_dispose (GObject *object) IndicatorObject * io = INDICATOR_OBJECT(object); schema_clear (io); - stop_listening_for_menu_visibility_changes (io); + remove_all_entries_from_limbo (io); + + if ((io->priv != NULL) && (io->priv->entry_privates != NULL)) + g_hash_table_destroy (io->priv->entry_privates); G_OBJECT_CLASS (indicator_object_parent_class)->dispose (object); return; @@ -501,117 +544,56 @@ get_entries_default (IndicatorObject * io) return g_list_append(NULL, &(priv->entry)); } -/* finds the IndicatorObjectEntry* which contains the specified menu */ -static IndicatorObjectEntry* -find_entry_from_menu (IndicatorObject * io, GtkMenu * menu) -{ - GList * l; - GList * all_entries; - static IndicatorObjectEntry * match = NULL; - - g_return_val_if_fail (GTK_IS_MENU(menu), NULL); - g_return_val_if_fail (INDICATOR_IS_OBJECT(io), NULL); - - all_entries = get_all_entries (io); - for (l=all_entries; l && !match; l=l->next) { - IndicatorObjectEntry * entry = l->data; - if (menu == entry->menu) - match = entry; - } - - g_list_free (all_entries); - return match; -} - -static void -on_menu_show(GtkMenu * menu, gpointer io) -{ - IndicatorObjectEntry * entry = find_entry_from_menu (io, menu); - g_return_if_fail (entry != NULL); - g_signal_emit_by_name (io, INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, entry); -} - -static void -on_menu_hide(GtkMenu * menu, gpointer io) -{ - IndicatorObjectEntry * entry = find_entry_from_menu (io, menu); - g_return_if_fail (entry != NULL); - g_signal_emit_by_name (io, INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, entry); -} - -static void -ensure_signal_handler_is_connected(GObject * o, const char * key, - const char * signal_name, - GCallback callback, gpointer user_data) -{ - if (g_object_get_data (o, key) == NULL) - { - gulong * handler_id = g_new(gulong, 1); - *handler_id = g_signal_connect (o, signal_name, callback, user_data); - g_object_set_data (o, key, handler_id); - } -} - -#define HIDE_SIGNAL_KEY "indicator-object-signal-handler-id-hide" -#define SHOW_SIGNAL_KEY "indicator-object-signal-handler-id-show" - /* returns a list of all IndicatorObjectEntires whether they're visible or not */ static GList* get_all_entries (IndicatorObject * io) { - GList * l; - GList * all_entries; - g_return_val_if_fail(INDICATOR_IS_OBJECT(io), NULL); IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io); if (class->get_entries == NULL) g_error("No get_entries function on object. It must have been deleted?!?!"); - all_entries = class->get_entries(io); + return class->get_entries(io); +} - /* N.B. probably bad form to have side-effects in a simple accessor... - We're doing it this way to add visibility support without changing - before freeze. */ - for (l=all_entries; l!=NULL; l=l->next) { - IndicatorObjectEntry * entry = l->data; - GObject * o = G_OBJECT(entry->menu); - ensure_signal_handler_is_connected (o, HIDE_SIGNAL_KEY, "hide", G_CALLBACK(on_menu_hide), io); - ensure_signal_handler_is_connected (o, SHOW_SIGNAL_KEY, "show", G_CALLBACK(on_menu_show), io); +IndicatorObjectEntryPrivate * +entry_get_private (IndicatorObject * io, IndicatorObjectEntry * entry) +{ + g_return_val_if_fail (INDICATOR_IS_OBJECT(io), NULL); + g_return_val_if_fail (io->priv != NULL, NULL); + + IndicatorObjectEntryPrivate * priv = g_hash_table_lookup (io->priv->entry_privates, entry); + if (priv == NULL) + { + priv = g_new0 (IndicatorObjectEntryPrivate, 1); + priv->visibility = ENTRY_INIT; + g_hash_table_insert (io->priv->entry_privates, entry, priv); } - return all_entries; + return priv; } -static void -stop_listening_for_menu_visibility_changes (IndicatorObject * io) +static gboolean +get_default_visibility (IndicatorObject * io) { - GList * l; - GList * entries = get_all_entries (io); - - for (l=entries; l!=NULL; l=l->next) - { - gulong * handler_id; - GObject * menu = G_OBJECT(((IndicatorObjectEntry*)l->data)->menu); + g_return_val_if_fail (INDICATOR_IS_OBJECT(io), FALSE); + IndicatorObjectPrivate * priv = INDICATOR_OBJECT_GET_PRIVATE(io); + g_return_val_if_fail (priv != NULL, FALSE); - if((handler_id = g_object_get_data(menu, SHOW_SIGNAL_KEY))) { - g_signal_handler_disconnect(menu, *handler_id); - g_free (handler_id); - } + if (priv->gsettings != NULL) + return g_settings_get_boolean (priv->gsettings, "visible"); - if((handler_id = g_object_get_data(menu, HIDE_SIGNAL_KEY))) { - g_signal_handler_disconnect(menu, *handler_id); - g_free (handler_id); - } - } + return priv->default_visibility; } /** indicator_object_get_entries: @io: #IndicatorObject to query - This function calls the object's #IndicatorObjectClass::get_entries virtual - function, filters out invisible entries, and returns a GList of visible ones. + This function returns a list of visible entries. The list is built by calling + the object's #IndicatorObjectClass::get_entries virtual function and testing + each of the results for visibility. Callers should free the GList with g_list_free(), but the entries are owned by the IndicatorObject and should not be freed. @@ -622,17 +604,27 @@ GList * indicator_object_get_entries (IndicatorObject * io) { GList * l; - GList * visible_entries = NULL; + GList * ret = NULL; GList * all_entries = get_all_entries (io); - for (l=all_entries; l!=NULL; l=l->next) { + for (l=all_entries; l!=NULL; l=l->next) + { + gboolean show_me; IndicatorObjectEntry * entry = l->data; - if(gtk_widget_get_visible(GTK_WIDGET(entry->menu))) - visible_entries = g_list_append (visible_entries, entry); + IndicatorObjectEntryPrivate * entry_priv = entry_get_private (io, entry); + + switch (entry_priv->visibility) { + case ENTRY_VISIBLE: show_me = TRUE; break; + case ENTRY_INVISIBLE: show_me = FALSE; break; + default /*ENTRY_INIT*/: show_me = get_default_visibility (io); break; + } + + if (show_me) + ret = g_list_append (ret, entry); } g_list_free (all_entries); - return visible_entries; + return ret; } /** @@ -801,6 +793,29 @@ indicator_object_check_environment (IndicatorObject * io, const gchar * env) return FALSE; } +/** + indicator_object_set_visible: + @io: #IndicatorObject to check on + @visible: whether or not the entries should be visible + + Used to set all of an indicator's entries to be visible or hidden. +*/ +void +indicator_object_set_visible (IndicatorObject * io, gboolean visible) +{ + GList * l; + GList * entries; + const char * signal_name = visible ? INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED + : INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED; + + g_return_if_fail(INDICATOR_IS_OBJECT(io)); + + entries = get_all_entries (io); + for (l=entries; l!=NULL; l=l->next) + g_signal_emit_by_name (io, signal_name, l->data); + g_list_free (entries); +} + static void get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { @@ -812,11 +827,19 @@ get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspe switch (prop_id) { /* *********************** */ - case PROP_SETTINGS_SCHEMA_ID: + case PROP_GSETTINGS_SCHEMA_ID: if (G_VALUE_HOLDS_STRING(value)) { g_value_set_string(value, priv->gsettings_schema_id); } else { - g_warning("Name property requires a string value."); + g_warning("schema-id property requires a string value."); + } + break; + /* *********************** */ + case PROP_DEFAULT_VISIBILITY: + if (G_VALUE_HOLDS_BOOLEAN(value)) { + g_value_set_boolean(value, priv->default_visibility); + } else { + g_warning("default-visibility property requires a boolean value."); } break; /* *********************** */ @@ -839,14 +862,24 @@ set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec switch (prop_id) { /* *********************** */ - case PROP_SETTINGS_SCHEMA_ID: + case PROP_GSETTINGS_SCHEMA_ID: if (G_VALUE_HOLDS_STRING(value)) { schema_set (self, g_value_get_string (value)); } else { - g_warning("Name property requires a string value."); + g_warning("schema-id property requires a string value."); + } + break; + + /* *********************** */ + case PROP_DEFAULT_VISIBILITY: + if (G_VALUE_HOLDS_BOOLEAN(value)) { + priv->default_visibility = g_value_get_boolean (value); + } else { + g_warning("default-visibility property requires a boolean value."); } break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -858,23 +891,12 @@ set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec static void on_settings_changed (GSettings * gsettings, gchar * key, gpointer user_data) { - g_message ("settings changed: %s", key); - if (!g_strcmp0 (key, "visible")) { + IndicatorObject * io = INDICATOR_OBJECT (user_data); const gboolean visible = g_settings_get_boolean (gsettings, key); - const char * signal_name = visible ? "entry-added" : "entry-removed"; - - IndicatorObject * self = INDICATOR_OBJECT (user_data); - GList * entries = indicator_object_get_entries (self); - GList * walk; - - for (walk=entries; walk!=NULL; walk=walk->next) { - g_signal_emit_by_name (self, signal_name, walk->data); - } - - g_list_free (entries); - } + indicator_object_set_visible (io, visible); + } } static void @@ -911,3 +933,85 @@ schema_clear (IndicatorObject * self) priv->gsettings_schema_id = NULL; } } + +/*** +**** +**** +***/ + +#define LIMBO_REF_KEY "indicator-object-entry-limbo-key" + +/* Limbo is an idiom private to indicator-object.c + It is for entries which have been removed and are waiting to be re-added later */ + +static void +remove_widget_from_limbo (gpointer w) +{ + if (w != NULL) { + GObject * o = G_OBJECT(w); + + if (g_object_get_data(o, LIMBO_REF_KEY)) { + g_object_steal_data (o, LIMBO_REF_KEY); + g_object_unref (o); + } + } +} + +static void +remove_entry_from_limbo (IndicatorObject * io, IndicatorObjectEntry * entry) +{ + IndicatorObjectEntryPrivate * entry_priv = entry_get_private (io, entry); + + entry_priv->visibility = ENTRY_INVISIBLE; + + remove_widget_from_limbo (entry->image); + remove_widget_from_limbo (entry->label); + remove_widget_from_limbo (entry->menu); +} + +static void +remove_all_entries_from_limbo (IndicatorObject * io) +{ + GList * l; + GList * entries = get_all_entries(io); + for (l=entries; l!=NULL; l=l->next) + remove_entry_from_limbo (io, l->data); + g_list_free (entries); +} + +static void +on_entry_added (IndicatorObject * io, IndicatorObjectEntry * entry, gpointer unused G_GNUC_UNUSED) +{ + remove_entry_from_limbo (io, entry); +} + +/* The panels like to destroy an entry's widgetry when it's removed. + Protect the image, label, and menu for safekeeping in case we ever + need to add it back again. */ +static void +put_widget_in_limbo (gpointer w) +{ + if (w != NULL) { + GtkWidget * parent; + + g_object_ref (G_OBJECT(w)); + g_object_set_data (G_OBJECT(w), LIMBO_REF_KEY, GINT_TO_POINTER(1)); + + if(GTK_IS_MENU(w)) + gtk_menu_detach (GTK_MENU(w)); + else if((parent = gtk_widget_get_parent(w))) + gtk_container_remove(GTK_CONTAINER(parent), w); + } +} + +static void +on_entry_removed (IndicatorObject * io, IndicatorObjectEntry * entry, gpointer unused G_GNUC_UNUSED) +{ + IndicatorObjectEntryPrivate * entry_priv = entry_get_private (io, entry); + + entry_priv->visibility = ENTRY_VISIBLE; + + put_widget_in_limbo (entry->image); + put_widget_in_limbo (entry->label); + put_widget_in_limbo (entry->menu); +} diff --git a/libindicator/indicator-object.h b/libindicator/indicator-object.h index 51ca758..a46600f 100644 --- a/libindicator/indicator-object.h +++ b/libindicator/indicator-object.h @@ -61,7 +61,10 @@ typedef enum #define INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE_ID (g_signal_lookup(INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE, INDICATOR_OBJECT_TYPE)) /* the name of the GSettings schema-id property */ -#define INDICATOR_OBJECT_GSETTINGS_SCHEMA_ID "gsettings-schema-id" +#define INDICATOR_OBJECT_GSETTINGS_SCHEMA_ID "indicator-object-gsettings-schema-id" + +/* the name of the property to decide whether or not entries are visible by default */ +#define INDICATOR_OBJECT_DEFAULT_VISIBILITY "indicator-object-default-visibility" typedef struct _IndicatorObject IndicatorObject; typedef struct _IndicatorObjectClass IndicatorObjectClass; @@ -186,6 +189,7 @@ IndicatorObject * indicator_object_new_from_file (const gchar * file); GList * indicator_object_get_entries (IndicatorObject * io); guint indicator_object_get_location (IndicatorObject * io, IndicatorObjectEntry * entry); guint indicator_object_get_show_now (IndicatorObject * io, IndicatorObjectEntry * entry); +void indicator_object_set_visible (IndicatorObject * io, gboolean visible); void indicator_object_entry_activate (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp); void indicator_object_entry_close (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp); -- cgit v1.2.3 From 2bb19db591c96af488d90ec184ea458230a2c471 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Fri, 13 Jan 2012 15:01:01 +0100 Subject: Adding an activate window function, that can also be virtual! --- libindicator/indicator-object.c | 8 ++++++++ libindicator/indicator-object.h | 2 ++ 2 files changed, 10 insertions(+) (limited to 'libindicator/indicator-object.h') diff --git a/libindicator/indicator-object.c b/libindicator/indicator-object.c index bd056f0..7cb75ae 100644 --- a/libindicator/indicator-object.c +++ b/libindicator/indicator-object.c @@ -538,6 +538,14 @@ indicator_object_get_show_now (IndicatorObject * io, IndicatorObjectEntry * entr return FALSE; } +void +indicator_object_entry_activate_window (IndicatorObject * io, IndicatorObjectEntry * entry, guint windowid, guint timestamp) +{ + + + return; +} + /** indicator_object_entry_activate: @io: #IndicatorObject to query diff --git a/libindicator/indicator-object.h b/libindicator/indicator-object.h index 3a120f5..bbc215a 100644 --- a/libindicator/indicator-object.h +++ b/libindicator/indicator-object.h @@ -118,6 +118,7 @@ struct _IndicatorObjectClass { gboolean (*get_show_now) (IndicatorObject * io, IndicatorObjectEntry * entry); void (*entry_activate) (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp); + void (*entry_activate_window) (IndicatorObject * io, IndicatorObjectEntry * entry, guint windowid, guint timestamp); void (*entry_close) (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp); /* Signals */ @@ -184,6 +185,7 @@ GList * indicator_object_get_entries (IndicatorObject * io); guint indicator_object_get_location (IndicatorObject * io, IndicatorObjectEntry * entry); guint indicator_object_get_show_now (IndicatorObject * io, IndicatorObjectEntry * entry); void indicator_object_entry_activate (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp); +void indicator_object_entry_activate_window (IndicatorObject * io, IndicatorObjectEntry * entry, guint windowid, guint timestamp); void indicator_object_entry_close (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp); void indicator_object_set_environment (IndicatorObject * io, const GStrv env); -- cgit v1.2.3 From 8a739e176e44cfff995711606c224caa9a1920b8 Mon Sep 17 00:00:00 2001 From: Ted Gould Date: Thu, 19 Jan 2012 11:44:37 -0600 Subject: Adding a parent object pointer to the entry --- libindicator/indicator-object.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'libindicator/indicator-object.h') diff --git a/libindicator/indicator-object.h b/libindicator/indicator-object.h index bbc215a..b8b9a8b 100644 --- a/libindicator/indicator-object.h +++ b/libindicator/indicator-object.h @@ -152,6 +152,7 @@ struct _IndicatorObject { /** IndicatorObjectEntry: + @parent_object: The #IndicatorObject that created this entry @label: The label to be shown on the panel @image: The image to be shown on the panel @menu: The menu to be added to the menubar @@ -166,6 +167,7 @@ struct _IndicatorObject { @reserved4: Reserved for future use */ struct _IndicatorObjectEntry { + IndicatorObject * parent_object; GtkLabel * label; GtkImage * image; GtkMenu * menu; -- cgit v1.2.3 From 4f4190f71f8495e5bcf6779d73157931572e42ac Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 23 Jan 2012 05:39:50 -0600 Subject: another iteration of the indicator-object visibility support patch, incorporating ideas from discussion with ted - some functions were public when they should have been private - the hide/show handler is now a virtual function & is documented in indicator-object.h - added unit tests - the GSettings monitor has been removed --- libindicator/indicator-object.c | 240 ++++++++++++--------------------------- libindicator/indicator-object.h | 12 +- tests/Makefile.am | 23 +++- tests/dummy-indicator-signaler.c | 2 + tests/dummy-indicator-visible.c | 139 +++++++++++++++++++++++ tests/test-loader.c | 90 +++++++++++++++ 6 files changed, 337 insertions(+), 169 deletions(-) create mode 100644 tests/dummy-indicator-visible.c (limited to 'libindicator/indicator-object.h') diff --git a/libindicator/indicator-object.c b/libindicator/indicator-object.c index f4a4265..8deb706 100644 --- a/libindicator/indicator-object.c +++ b/libindicator/indicator-object.c @@ -32,19 +32,18 @@ License along with this library. If not, see /** @ENTRY_INIT: The entry hasn't been initialized yet, so its - visibility will depend upon the inital-visibility property - and the 'visible' setting in the optional GSettings schema-id - @ENTRY_CLOAKED: The entry has been initialized but is not visible + visibility will depend upon the inital-visibility property. @ENTRY_VISIBLE: The entry is visible + @ENTRY_INVISIBLE: The entry is invisible */ typedef enum { ENTRY_INIT, - ENTRY_CLOAKED, - ENTRY_VISIBLE + ENTRY_VISIBLE, + ENTRY_INVISIBLE } EntryVisibility; -typedef struct IndicatorObjectEntryPrivate { +typedef struct _IndicatorObjectEntryPrivate { EntryVisibility visibility; } IndicatorObjectEntryPrivate; @@ -68,10 +67,6 @@ struct _IndicatorObjectPrivate { IndicatorObjectEntry entry; gboolean gotten_entries; - /* For indicator objects that monitor a GSettings schema-id */ - GSettings * gsettings; - gchar * gsettings_schema_id; - /* Whether or not entries are visible by default */ gboolean default_visibility; GHashTable * entry_privates; @@ -115,16 +110,14 @@ static void indicator_object_finalize (GObject *object); static void set_property (GObject*, guint prop_id, const GValue*, GParamSpec* ); static void get_property (GObject*, guint prop_id, GValue*, GParamSpec* ); -/* GSettings schema handling */ -static void schema_clear (IndicatorObject* ); -static void schema_set (IndicatorObject* , const char * schema_id); - /* entries' visibility */ -static void on_entry_added (IndicatorObject*, IndicatorObjectEntry*, gpointer); -static void on_entry_removed(IndicatorObject*, IndicatorObjectEntry*, gpointer); -static void decloak_entry (IndicatorObject*, IndicatorObjectEntry*); -static GList * get_entries_default (IndicatorObject*); -static GList * get_all_entries (IndicatorObject*); +static GList * get_entries_default (IndicatorObject*); +static GList * get_all_entries (IndicatorObject*); +static void entry_being_removed_default (IndicatorObject*, IndicatorObjectEntry*); +static void indicator_object_entry_being_removed (IndicatorObject*, IndicatorObjectEntry*); +static void entry_was_added_default (IndicatorObject*, IndicatorObjectEntry*); +static void indicator_object_entry_was_added (IndicatorObject*, IndicatorObjectEntry*); +static IndicatorObjectEntryPrivate * entry_get_private (IndicatorObject*, IndicatorObjectEntry*); G_DEFINE_TYPE (IndicatorObject, indicator_object, G_TYPE_OBJECT); @@ -148,6 +141,8 @@ indicator_object_class_init (IndicatorObjectClass *klass) klass->get_accessible_desc = NULL; klass->get_entries = get_entries_default; klass->get_location = NULL; + klass->entry_being_removed = entry_being_removed_default; + klass->entry_was_added = entry_was_added_default; /** IndicatorObject::entry-added: @@ -293,15 +288,7 @@ indicator_object_class_init (IndicatorObjectClass *klass) /* Properties */ - GParamSpec * pspec; - pspec = g_param_spec_string (INDICATOR_OBJECT_GSETTINGS_SCHEMA_ID, - "schema id", - "The schema-id of the GSettings (if any) to monitor.", - NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_GSETTINGS_SCHEMA_ID, pspec); - - pspec = g_param_spec_boolean (INDICATOR_OBJECT_DEFAULT_VISIBILITY, + GParamSpec * pspec = g_param_spec_boolean (INDICATOR_OBJECT_DEFAULT_VISIBILITY, "default visibility", "Whether or not entries should initially be visible.", TRUE, @@ -329,19 +316,15 @@ indicator_object_init (IndicatorObject *self) priv->environments = NULL; - priv->gsettings = NULL; - priv->gsettings_schema_id = NULL; - self->priv = priv; - /* Listen for entries to be added/removed so that we can manage them. - By being first in line for the "removed" signal we can cloak the - entry before the client code destroys its widgetry... */ GObject * o = G_OBJECT(self); + /* Invoke the entry-being-removed virtual function first */ g_signal_connect (o, INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, - G_CALLBACK(on_entry_removed), NULL); + G_CALLBACK(indicator_object_entry_being_removed), NULL); + /* Invoke the entry-was-added virtual function last */ g_signal_connect_after (o, INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, - G_CALLBACK(on_entry_added), NULL); + G_CALLBACK(indicator_object_entry_was_added), NULL); } /* Unref the objects that we're holding on to. */ @@ -350,21 +333,18 @@ indicator_object_dispose (GObject *object) { IndicatorObject * io = INDICATOR_OBJECT(object); - /* stop listening to schema changes */ - schema_clear (io); - - /* decloak any cloaked entries so they won't leak */ + /* Ensure that hidden entries are re-added so their widgetry will + be cleaned up properly by the client */ GList * l; GList * entries = get_all_entries (io); - for (l=entries; l!=NULL; l=l->next) - decloak_entry (io, l->data); - g_list_free (entries); - - /* destroy the EntryPrivate hashtable */ - if (io->priv && io->priv->entry_privates) { - g_hash_table_destroy (io->priv->entry_privates); - io->priv->entry_privates = NULL; + const GQuark detail = (GQuark)0; + for (l=entries; l!=NULL; l=l->next) { + IndicatorObjectEntry * entry = l->data; + if (entry_get_private(io, entry)->visibility == ENTRY_INVISIBLE) { + g_signal_emit(io, signals[ENTRY_ADDED], detail, entry); + } } + g_list_free (entries); G_OBJECT_CLASS (indicator_object_parent_class)->dispose (object); } @@ -387,6 +367,11 @@ indicator_object_finalize (GObject *object) { IndicatorObjectPrivate * priv = INDICATOR_OBJECT_GET_PRIVATE(object); + if (priv->entry_privates != NULL) { + g_hash_table_destroy (priv->entry_privates); + priv->entry_privates = NULL; + } + if (priv->environments != NULL) { g_strfreev(priv->environments); priv->environments = NULL; @@ -575,7 +560,7 @@ get_all_entries (IndicatorObject * io) } /* get the private structure that corresponds to a caller-specified entry */ -IndicatorObjectEntryPrivate * +static IndicatorObjectEntryPrivate * entry_get_private (IndicatorObject * io, IndicatorObjectEntry * entry) { g_return_val_if_fail (INDICATOR_IS_OBJECT(io), NULL); @@ -593,22 +578,6 @@ entry_get_private (IndicatorObject * io, IndicatorObjectEntry * entry) return priv; } -/* Returns whether or not entries should be shown by default on startup. - This is usually 'true', but can be changed via GSettings and/or by the - INDICATOR_OBJECT_DEFAULT_VISIBILITY property. */ -static gboolean -get_default_visibility (IndicatorObject * io) -{ - g_return_val_if_fail (INDICATOR_IS_OBJECT(io), FALSE); - IndicatorObjectPrivate * priv = INDICATOR_OBJECT_GET_PRIVATE(io); - g_return_val_if_fail (priv != NULL, FALSE); - - if (priv->gsettings != NULL) - return g_settings_get_boolean (priv->gsettings, "visible"); - - return priv->default_visibility; -} - /** indicator_object_get_entries: @io: #IndicatorObject to query @@ -628,6 +597,7 @@ indicator_object_get_entries (IndicatorObject * io) GList * l; GList * ret = NULL; GList * all_entries = get_all_entries (io); + const gboolean default_visibility = INDICATOR_OBJECT_GET_PRIVATE(io)->default_visibility; for (l=all_entries; l!=NULL; l=l->next) { @@ -635,17 +605,18 @@ indicator_object_get_entries (IndicatorObject * io) IndicatorObjectEntry * entry = l->data; switch (entry_get_private(io,entry)->visibility) { - case ENTRY_VISIBLE: show_me = TRUE; break; - case ENTRY_CLOAKED: show_me = FALSE; break; - default: show_me = get_default_visibility (io); break; + case ENTRY_VISIBLE: show_me = TRUE; break; + case ENTRY_INVISIBLE: show_me = FALSE; break; + case ENTRY_INIT: show_me = default_visibility; break; + default: show_me = TRUE; g_warn_if_reached(); break; } if (show_me) - ret = g_list_append (ret, entry); + ret = g_list_prepend (ret, entry); } g_list_free (all_entries); - return ret; + return g_list_reverse (ret); } /** @@ -743,6 +714,30 @@ indicator_object_entry_close (IndicatorObject * io, IndicatorObjectEntry * entry return; } +static void +indicator_object_entry_being_removed (IndicatorObject * io, IndicatorObjectEntry * entry) +{ + g_return_if_fail(INDICATOR_IS_OBJECT(io)); + IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io); + + entry_get_private (io, entry)->visibility = ENTRY_INVISIBLE; + + if (class->entry_being_removed != NULL) + class->entry_being_removed (io, entry); +} + +static void +indicator_object_entry_was_added (IndicatorObject * io, IndicatorObjectEntry * entry) +{ + g_return_if_fail(INDICATOR_IS_OBJECT(io)); + IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io); + + entry_get_private (io, entry)->visibility = ENTRY_VISIBLE; + + if (class->entry_was_added != NULL) + class->entry_was_added (io, entry); +} + /** indicator_object_set_environment: @io: #IndicatorObject to set on @@ -825,16 +820,14 @@ indicator_object_check_environment (IndicatorObject * io, const gchar * env) void indicator_object_set_visible (IndicatorObject * io, gboolean visible) { - GList * l; - GList * entries; - const char * name = visible ? INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED - : INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED; - g_return_if_fail(INDICATOR_IS_OBJECT(io)); - entries = get_all_entries (io); + GList * l; + GList * entries = get_all_entries (io); + const guint signal_id = signals[visible ? ENTRY_ADDED : ENTRY_REMOVED]; + const GQuark detail = (GQuark)0; for (l=entries; l!=NULL; l=l->next) - g_signal_emit_by_name (io, name, l->data); + g_signal_emit(io, signal_id, detail, l->data); g_list_free (entries); } @@ -852,14 +845,6 @@ get_property (GObject * object, switch (prop_id) { /* *********************** */ - case PROP_GSETTINGS_SCHEMA_ID: - if (G_VALUE_HOLDS_STRING(value)) { - g_value_set_string(value, priv->gsettings_schema_id); - } else { - g_warning("schema-id property requires a string value."); - } - break; - /* *********************** */ case PROP_DEFAULT_VISIBILITY: if (G_VALUE_HOLDS_BOOLEAN(value)) { g_value_set_boolean(value, priv->default_visibility); @@ -889,15 +874,6 @@ set_property (GObject * object, switch (prop_id) { - /* *********************** */ - case PROP_GSETTINGS_SCHEMA_ID: - if (G_VALUE_HOLDS_STRING(value)) { - schema_set (self, g_value_get_string (value)); - } else { - g_warning("schema-id property requires a string value."); - } - break; - /* *********************** */ case PROP_DEFAULT_VISIBILITY: if (G_VALUE_HOLDS_BOOLEAN(value)) { @@ -918,58 +894,9 @@ set_property (GObject * object, **** ***/ -static void -on_settings_changed (GSettings * gsettings, gchar * key, gpointer user_data) -{ - if (!g_strcmp0 (key, "visible")) { - IndicatorObject * io = INDICATOR_OBJECT (user_data); - gboolean visible = g_settings_get_boolean (gsettings, key); - indicator_object_set_visible (io, visible); - } -} - -static void -schema_clear (IndicatorObject * self) -{ - IndicatorObjectPrivate * priv = INDICATOR_OBJECT_GET_PRIVATE(self); - g_return_if_fail (priv != NULL); - - if (priv->gsettings != NULL) { - g_object_unref (priv->gsettings); - priv->gsettings = NULL; - } - - if (priv->gsettings_schema_id != NULL) { - g_free (priv->gsettings_schema_id); - priv->gsettings_schema_id = NULL; - } -} - -static void -schema_set (IndicatorObject * object, const char * gsettings_schema_id) -{ - schema_clear (object); - - IndicatorObjectPrivate * priv = INDICATOR_OBJECT_GET_PRIVATE(object); - g_return_if_fail (priv != NULL); - - priv->gsettings_schema_id = g_strdup (gsettings_schema_id); - if (priv->gsettings_schema_id != NULL) { - priv->gsettings = g_settings_new (priv->gsettings_schema_id); - } - if (priv->gsettings != NULL) { - g_signal_connect (G_OBJECT(priv->gsettings), "changed", - G_CALLBACK(on_settings_changed), object); - } -} - -/*** -**** -***/ - /* Cloaked entries are ones which are hidden but may be re-added later. - We cloak them by reffing them + unparenting to ensure their survival - even if their gtk parent's widgetry is destroyed */ + They are reffed + unparented so that they'll survive even if the + rest of the widgetry is destroyed */ #define CLOAKED_KEY "entry-is-cloaked" static void @@ -977,33 +904,20 @@ decloak_widget (gpointer w) { if (w != NULL) { GObject * o = G_OBJECT(w); - if (g_object_get_data(o, CLOAKED_KEY)) { - g_object_steal_data (o, CLOAKED_KEY); + if (g_object_steal_data (o, CLOAKED_KEY) != NULL) { g_object_unref (o); } } } static void -decloak_entry (IndicatorObject * io, IndicatorObjectEntry * entry) +entry_was_added_default (IndicatorObject * io, IndicatorObjectEntry * entry) { - entry_get_private (io, entry)->visibility = ENTRY_VISIBLE; - decloak_widget (entry->image); decloak_widget (entry->label); decloak_widget (entry->menu); } -static void -on_entry_added (IndicatorObject * io, - IndicatorObjectEntry * entry, - gpointer unused G_GNUC_UNUSED) -{ - decloak_entry (io, entry); -} - -/* The panels like to destroy an entry's widgetry when it's removed. - Protect the image, label, and menu for future reuse */ static void cloak_widget (gpointer w) { @@ -1028,12 +942,8 @@ cloak_widget (gpointer w) } static void -on_entry_removed (IndicatorObject * io, - IndicatorObjectEntry * entry, - gpointer unused G_GNUC_UNUSED) +entry_being_removed_default (IndicatorObject * io, IndicatorObjectEntry * entry) { - entry_get_private(io,entry)->visibility = ENTRY_CLOAKED; - cloak_widget (entry->image); cloak_widget (entry->label); cloak_widget (entry->menu); diff --git a/libindicator/indicator-object.h b/libindicator/indicator-object.h index a46600f..803f1b3 100644 --- a/libindicator/indicator-object.h +++ b/libindicator/indicator-object.h @@ -60,9 +60,6 @@ typedef enum #define INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE "secondary-activate" #define INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE_ID (g_signal_lookup(INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE, INDICATOR_OBJECT_TYPE)) -/* the name of the GSettings schema-id property */ -#define INDICATOR_OBJECT_GSETTINGS_SCHEMA_ID "indicator-object-gsettings-schema-id" - /* the name of the property to decide whether or not entries are visible by default */ #define INDICATOR_OBJECT_DEFAULT_VISIBILITY "indicator-object-default-visibility" @@ -97,6 +94,12 @@ typedef struct _IndicatorObjectEntry IndicatorObjectEntry; @get_show_now: Returns whether the entry is requesting to be shown "right now" in that it has something important to tell the user. + @entry_being_removed: Called before an entry is removed. + The default implementation is to ref and unparent the + entry's widgets so that they can be re-added later. + @entry_was_added: Called after an entry is added. + The default implementation is to unref the entry's widgets if + previously reffed by entry_being_removed's default impementation @entry_activate: Should be called when the menus for a given entry are shown to the user. @entry_close: Called when the menu is closed. @@ -123,6 +126,9 @@ struct _IndicatorObjectClass { guint (*get_location) (IndicatorObject * io, IndicatorObjectEntry * entry); gboolean (*get_show_now) (IndicatorObject * io, IndicatorObjectEntry * entry); + void (*entry_being_removed) (IndicatorObject * io, IndicatorObjectEntry * entry); + void (*entry_was_added) (IndicatorObject * io, IndicatorObjectEntry * entry); + void (*entry_activate) (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp); void (*entry_close) (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp); diff --git a/tests/Makefile.am b/tests/Makefile.am index f11a9d1..a70ee58 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -14,7 +14,8 @@ lib_LTLIBRARIES = \ libdummy-indicator-blank.la \ libdummy-indicator-null.la \ libdummy-indicator-signaler.la \ - libdummy-indicator-simple.la + libdummy-indicator-simple.la \ + libdummy-indicator-visible.la DBUS_RUNNER=dbus-test-runner --dbus-config /usr/share/dbus-test-runner/session.conf XVFB_RUN=". $(srcdir)/run-xvfb.sh" @@ -161,6 +162,26 @@ libdummy_indicator_simple_la_LDFLAGS = \ -module \ -avoid-version +############################# +# Dummy Indicator Visible +############################# + +libdummy_indicator_visible_la_SOURCES = \ + dummy-indicator-visible.c + +libdummy_indicator_visible_la_CFLAGS = \ + -Wall -Werror \ + $(LIBINDICATOR_CFLAGS) -I$(top_srcdir) + +libdummy_indicator_visible_la_LIBADD = \ + $(LIBINDICATOR_LIBS) \ + -L$(top_builddir)/libindicator/.libs \ + $(INDICATOR_LIB) + +libdummy_indicator_visible_la_LDFLAGS = \ + -module \ + -avoid-version + ############################# # Service Shutdown Timeout ############################# diff --git a/tests/dummy-indicator-signaler.c b/tests/dummy-indicator-signaler.c index 00eee3b..c7a5c1f 100644 --- a/tests/dummy-indicator-signaler.c +++ b/tests/dummy-indicator-signaler.c @@ -99,6 +99,8 @@ dummy_indicator_signaler_class_init (DummyIndicatorSignalerClass *klass) io_class->get_image = get_icon; io_class->get_menu = get_menu; io_class->get_accessible_desc = get_accessible_desc; + io_class->entry_being_removed = NULL; + io_class->entry_was_added = NULL; return; } diff --git a/tests/dummy-indicator-visible.c b/tests/dummy-indicator-visible.c new file mode 100644 index 0000000..0bb9e89 --- /dev/null +++ b/tests/dummy-indicator-visible.c @@ -0,0 +1,139 @@ +/* +Test for libindicator + +Copyright 2012 Canonical Ltd. + +Authors: + Charles Kerr + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +version 3.0 as published by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License version 3.0 for more details. + +You should have received a copy of the GNU General Public +License along with this library. If not, see +. +*/ + +#include +#include + +#include "libindicator/indicator.h" +#include "libindicator/indicator-object.h" + +#define DUMMY_INDICATOR_VISIBLE_TYPE (dummy_indicator_visible_get_type ()) +#define DUMMY_INDICATOR_VISIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DUMMY_INDICATOR_VISIBLE_TYPE, DummyIndicatorVisible)) +#define DUMMY_INDICATOR_VISIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DUMMY_INDICATOR_VISIBLE_TYPE, DummyIndicatorVisibleClass)) +#define IS_DUMMY_INDICATOR_VISIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DUMMY_INDICATOR_VISIBLE_TYPE)) +#define IS_DUMMY_INDICATOR_VISIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DUMMY_INDICATOR_VISIBLE_TYPE)) +#define DUMMY_INDICATOR_VISIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DUMMY_INDICATOR_VISIBLE_TYPE, DummyIndicatorVisibleClass)) + +typedef struct _DummyIndicatorVisible DummyIndicatorVisible; +typedef struct _DummyIndicatorVisibleClass DummyIndicatorVisibleClass; + +struct _DummyIndicatorVisibleClass { + IndicatorObjectClass parent_class; +}; + +struct _DummyIndicatorVisible { + IndicatorObject parent; +}; + +GType dummy_indicator_visible_get_type (void); + +INDICATOR_SET_VERSION +INDICATOR_SET_TYPE(DUMMY_INDICATOR_VISIBLE_TYPE) + +GtkLabel * +get_label (IndicatorObject * io) +{ + return GTK_LABEL(gtk_label_new("Visible Item")); +} + +GtkImage * +get_icon (IndicatorObject * io) +{ + return GTK_IMAGE(gtk_image_new()); +} + +GtkMenu * +get_menu (IndicatorObject * io) +{ + GtkMenu * main_menu = GTK_MENU(gtk_menu_new()); + GtkWidget * loading_item = gtk_menu_item_new_with_label("Loading..."); + gtk_menu_shell_append(GTK_MENU_SHELL(main_menu), loading_item); + gtk_widget_show(GTK_WIDGET(loading_item)); + + return main_menu; +} + +const gchar * +get_accessible_desc (IndicatorObject * io) +{ + return "Visible Item"; +} + +static void dummy_indicator_visible_class_init (DummyIndicatorVisibleClass *klass); +static void dummy_indicator_visible_init (DummyIndicatorVisible *self); +static void dummy_indicator_visible_dispose (GObject *object); +static void dummy_indicator_visible_finalize (GObject *object); + +G_DEFINE_TYPE (DummyIndicatorVisible, dummy_indicator_visible, INDICATOR_OBJECT_TYPE); + +static void +dummy_indicator_entry_being_removed (IndicatorObject * io, IndicatorObjectEntry * entry) +{ + g_object_set_data(G_OBJECT(entry->label), "is-hidden", GINT_TO_POINTER(1)); + + INDICATOR_OBJECT_CLASS(dummy_indicator_visible_parent_class)->entry_being_removed (io, entry); +} + +static void +dummy_indicator_entry_was_added (IndicatorObject * io, IndicatorObjectEntry * entry) +{ + g_object_steal_data(G_OBJECT(entry->label), "is-hidden"); + + INDICATOR_OBJECT_CLASS(dummy_indicator_visible_parent_class)->entry_was_added (io, entry); +} + +static void +dummy_indicator_visible_class_init (DummyIndicatorVisibleClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = dummy_indicator_visible_dispose; + object_class->finalize = dummy_indicator_visible_finalize; + + IndicatorObjectClass * io_class = INDICATOR_OBJECT_CLASS(klass); + + io_class->get_label = get_label; + io_class->get_image = get_icon; + io_class->get_menu = get_menu; + io_class->get_accessible_desc = get_accessible_desc; + io_class->entry_being_removed = dummy_indicator_entry_being_removed; + io_class->entry_was_added = dummy_indicator_entry_was_added; +} + +static void +dummy_indicator_visible_init (DummyIndicatorVisible *self) +{ +} + +static void +dummy_indicator_visible_dispose (GObject *object) +{ + + G_OBJECT_CLASS (dummy_indicator_visible_parent_class)->dispose (object); +} + +static void +dummy_indicator_visible_finalize (GObject *object) +{ + + G_OBJECT_CLASS (dummy_indicator_visible_parent_class)->finalize (object); +} diff --git a/tests/test-loader.c b/tests/test-loader.c index ac9d4e5..51ea6f3 100644 --- a/tests/test-loader.c +++ b/tests/test-loader.c @@ -68,6 +68,95 @@ test_loader_filename_dummy_signaler (void) return; } +/*** +**** +***/ + +static void +visible_entry_added (IndicatorObject * io, IndicatorObjectEntry * entry, gpointer box) +{ + // make a frame for the entry, and add the frame to the box + GtkWidget * frame = gtk_frame_new (NULL); + GtkWidget * child = GTK_WIDGET(entry->label); + g_assert (child != NULL); + gtk_container_add (GTK_CONTAINER(frame), child); + gtk_box_pack_start (GTK_BOX(box), frame, FALSE, FALSE, 0); + g_object_set_data (G_OBJECT(child), "frame-parent", frame); +} + +static void +visible_entry_removed (IndicatorObject * io, IndicatorObjectEntry * entry, gpointer box) +{ + // destroy this entry's frame + gpointer parent = g_object_steal_data (G_OBJECT(entry->label), "frame-parent"); + if (GTK_IS_WIDGET(parent)) + gtk_widget_destroy(GTK_WIDGET(parent)); +} + +void +test_loader_filename_dummy_visible (void) +{ + const GQuark is_hidden_quark = g_quark_from_static_string ("is-hidden"); + IndicatorObject * object = indicator_object_new_from_file(BUILD_DIR "/.libs/libdummy-indicator-visible.so"); + g_assert(object != NULL); + + // create our local parent widgetry +#if GTK_CHECK_VERSION(3,0,0) + GtkWidget * box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); +#else + GtkWidget * box = gtk_hbox_new (TRUE, 0); +#endif + g_signal_connect(object, INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, + G_CALLBACK(visible_entry_added), box); + g_signal_connect(object, INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, + G_CALLBACK(visible_entry_removed), box); + + // on startup, DummyVisible has one entry and it has a label + GList * list = indicator_object_get_entries(object); + g_assert(g_list_length(list) == 1); + IndicatorObjectEntry * entry = list->data; + g_assert(entry != NULL); + g_list_free(list); + g_assert(GTK_IS_LABEL(entry->label)); + GtkWidget * label = GTK_WIDGET(entry->label); + g_assert(g_object_get_qdata(G_OBJECT(label), is_hidden_quark) == NULL); + + // add the inital entry to our local parent widgetry + visible_entry_added (object, entry, box); + entry = NULL; + list = gtk_container_get_children (GTK_CONTAINER(box)); + g_assert(g_list_length(list) == 1); + g_list_free(list); + + // hide the entries and confirm that the label survived + indicator_object_set_visible (object, FALSE); + while (g_main_context_pending(NULL)) + g_main_context_iteration(NULL, TRUE); + g_assert(GTK_IS_LABEL(label)); + g_assert(g_object_get_qdata(G_OBJECT(label), is_hidden_quark) != NULL); + list = gtk_container_get_children (GTK_CONTAINER(box)); + g_assert(g_list_length(list) == 0); + g_list_free(list); + + // restore the entries and confirm that the label survived + indicator_object_set_visible (object, TRUE); + while (g_main_context_pending(NULL)) + g_main_context_iteration(NULL, TRUE); + g_assert(GTK_IS_LABEL(label)); + g_assert(g_object_get_qdata(G_OBJECT(label), is_hidden_quark) == NULL); + list = gtk_container_get_children (GTK_CONTAINER(box)); + g_assert(g_list_length(list) == 1); + g_list_free(list); + + // cleanup + g_object_unref(object); + gtk_widget_destroy(box); +} + +/*** +**** +***/ + void test_loader_filename_dummy_simple_location (void) { @@ -174,6 +263,7 @@ test_loader_creation_deletion_suite (void) g_test_add_func ("/libindicator/loader/dummy/simple_accessors", test_loader_filename_dummy_simple_accessors); g_test_add_func ("/libindicator/loader/dummy/simple_location", test_loader_filename_dummy_simple_location); g_test_add_func ("/libindicator/loader/dummy/signaler", test_loader_filename_dummy_signaler); + g_test_add_func ("/libindicator/loader/dummy/visible", test_loader_filename_dummy_visible); return; } -- cgit v1.2.3