diff options
author | Ted Gould <ted@gould.cx> | 2010-08-04 14:55:07 -0500 |
---|---|---|
committer | Ted Gould <ted@gould.cx> | 2010-08-04 14:55:07 -0500 |
commit | 6eeaa491a8749fc13b7fc5a63fc8e62134d5c803 (patch) | |
tree | 0b3f492704dfbab0341608aad877d288de72f982 | |
parent | 3f326bdebf4052e5ae620caf2cf1e17909c2f8f8 (diff) | |
parent | f2b808303f336aad4ee2663efb80a096dd61dcbd (diff) | |
download | libayatana-appindicator-6eeaa491a8749fc13b7fc5a63fc8e62134d5c803.tar.gz libayatana-appindicator-6eeaa491a8749fc13b7fc5a63fc8e62134d5c803.tar.bz2 libayatana-appindicator-6eeaa491a8749fc13b7fc5a63fc8e62134d5c803.zip |
* Upstream Merge
* Adding label support into the library
-rw-r--r-- | debian/changelog | 7 | ||||
-rw-r--r-- | docs/reference/libappindicator-sections.txt | 4 | ||||
-rw-r--r-- | example/simple-client.c | 15 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/app-indicator.c | 218 | ||||
-rw-r--r-- | src/app-indicator.h | 20 | ||||
-rw-r--r-- | src/application-service-marshal.list | 1 | ||||
-rw-r--r-- | src/notification-item.xml | 6 | ||||
-rw-r--r-- | tests/test-libappindicator.c | 133 |
9 files changed, 403 insertions, 4 deletions
diff --git a/debian/changelog b/debian/changelog index b9586bb..39f3545 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +indicator-application (0.2.3-0ubuntu2~ppa1~label1) UNRELEASED; urgency=low + + * Upstream Merge + * Adding label support into the library + + -- Ted Gould <ted@ubuntu.com> Wed, 04 Aug 2010 14:54:30 -0500 + indicator-application (0.2.3-0ubuntu1) maverick; urgency=low * New upstream release. diff --git a/docs/reference/libappindicator-sections.txt b/docs/reference/libappindicator-sections.txt index 70df0b8..49a470d 100644 --- a/docs/reference/libappindicator-sections.txt +++ b/docs/reference/libappindicator-sections.txt @@ -9,6 +9,7 @@ APP_INDICATOR_GET_CLASS APP_INDICATOR_SIGNAL_NEW_ICON APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON APP_INDICATOR_SIGNAL_NEW_STATUS +APP_INDICATOR_SIGNAL_NEW_LABEL APP_INDICATOR_SIGNAL_CONNECTION_CHANGED AppIndicatorCategory AppIndicatorStatus @@ -23,11 +24,14 @@ app_indicator_set_status app_indicator_set_attention_icon app_indicator_set_menu app_indicator_set_icon +app_indicator_set_label app_indicator_get_id app_indicator_get_category app_indicator_get_status app_indicator_get_icon app_indicator_get_attention_icon app_indicator_get_menu +app_indicator_get_label +app_indicator_get_label_guide </SECTION> diff --git a/example/simple-client.c b/example/simple-client.c index fbcaaaa..d756988 100644 --- a/example/simple-client.c +++ b/example/simple-client.c @@ -97,6 +97,18 @@ append_submenu (GtkWidget *item) gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu); } +guint percentage = 0; + +static gboolean +percent_change (gpointer user_data) +{ + percentage = (percentage + 1) % 100; + gchar * percentstr = g_strdup_printf("%d%%", percentage); + app_indicator_set_label (APP_INDICATOR(user_data), percentstr, "100%"); + g_free(percentstr); + return TRUE; +} + int main (int argc, char ** argv) { @@ -114,6 +126,9 @@ main (int argc, char ** argv) app_indicator_set_status (ci, APP_INDICATOR_STATUS_ACTIVE); app_indicator_set_attention_icon(ci, "indicator-messages-new"); + app_indicator_set_label (ci, "1%", "100%"); + + g_timeout_add_seconds(1, percent_change, ci); menu = gtk_menu_new (); GtkWidget *item = gtk_check_menu_item_new_with_label ("1"); diff --git a/src/Makefile.am b/src/Makefile.am index 68be1c0..b704b5e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -94,7 +94,8 @@ BUILT_SOURCES += \ libappindicator_la_SOURCES = \ $(libappindicator_headers) \ app-indicator-enum-types.c \ - app-indicator.c + app-indicator.c \ + application-service-marshal.c libappindicator_la_LDFLAGS = \ -version-info 0:0:0 \ diff --git a/src/app-indicator.c b/src/app-indicator.c index 32f512f..b3fa3e5 100644 --- a/src/app-indicator.c +++ b/src/app-indicator.c @@ -37,6 +37,7 @@ License version 3 and version 2.1 along with this program. If not, see #include "app-indicator.h" #include "app-indicator-enum-types.h" +#include "application-service-marshal.h" #include "notification-item-server.h" #include "notification-watcher-client.h" @@ -72,6 +73,9 @@ struct _AppIndicatorPrivate { gchar * icon_path; DbusmenuServer *menuservice; GtkWidget *menu; + gchar * label; + gchar * label_guide; + guint label_change_idle; GtkStatusIcon * status_icon; gint fallback_timer; @@ -87,6 +91,7 @@ enum { NEW_ICON, NEW_ATTENTION_ICON, NEW_STATUS, + NEW_LABEL, CONNECTION_CHANGED, LAST_SIGNAL }; @@ -104,7 +109,9 @@ enum { PROP_ATTENTION_ICON_NAME, PROP_ICON_THEME_PATH, PROP_MENU, - PROP_CONNECTED + PROP_CONNECTED, + PROP_LABEL, + PROP_LABEL_GUIDE }; /* The strings so that they can be slowly looked up. */ @@ -116,6 +123,8 @@ enum { #define PROP_ICON_THEME_PATH_S "icon-theme-path" #define PROP_MENU_S "menu" #define PROP_CONNECTED_S "connected" +#define PROP_LABEL_S "label" +#define PROP_LABEL_GUIDE_S "label-guide" /* Private macro, shhhh! */ #define APP_INDICATOR_GET_PRIVATE(o) \ @@ -136,6 +145,7 @@ static void app_indicator_finalize (GObject *object); static void app_indicator_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); /* Other stuff */ +static void signal_label_change (AppIndicator * self); static void check_connect (AppIndicator * self); static void register_service_cb (DBusGProxy * proxy, GError * error, gpointer data); static void start_fallback_timer (AppIndicator * self, gboolean disable_timeout); @@ -284,6 +294,41 @@ app_indicator_class_init (AppIndicatorClass *klass) "Pretty simple, true if we have a reasonable expectation of being displayed through this object. You should hide your TrayIcon if so.", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + AppIndicator:label: + + A label that can be shown next to the string in the application + indicator. The label will not be shown unless there is an icon + as well. The label is useful for numerical and other frequently + updated information. In general, it shouldn't be shown unless a + user requests it as it can take up a significant amount of space + on the user's panel. This may not be shown in all visualizations. + */ + g_object_class_install_property(object_class, + PROP_LABEL, + g_param_spec_string (PROP_LABEL_S, + "A label next to the icon", + "A label to provide dynamic information.", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + AppIndicator:label-guide: + + An optional string to provide guidance to the panel on how big + the #AppIndicator:label string could get. If this is set correctly + then the panel should never 'jiggle' as the string adjusts through + out the range of options. For instance, if you were providing a + percentage like "54% thrust" in #AppIndicator:label you'd want to + set this string to "100% thrust" to ensure space when Scotty can + get you enough power. + */ + g_object_class_install_property(object_class, + PROP_LABEL_GUIDE, + g_param_spec_string (PROP_LABEL_GUIDE_S, + "A string to size the space available for the label.", + "To ensure that the label does not cause the panel to 'jiggle' this string should provide information on how much space it could take.", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /* Signals */ @@ -333,6 +378,23 @@ app_indicator_class_init (AppIndicatorClass *klass) G_TYPE_STRING); /** + AppIndicator::new-label: + @arg0: The #AppIndicator object + @arg1: The string for the label + @arg1: The string for the guide + + Emitted when either #AppIndicator:label or #AppIndicator:label-guide are + changed. + */ + signals[NEW_LABEL] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_LABEL, + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (AppIndicatorClass, new_label), + NULL, NULL, + _application_service_marshal_VOID__STRING_STRING, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); + + /** AppIndicator::connection-changed: @arg0: The #AppIndicator object @arg1: Whether we're connected or not @@ -368,6 +430,9 @@ app_indicator_init (AppIndicator *self) priv->icon_path = NULL; priv->menu = NULL; priv->menuservice = NULL; + priv->label = NULL; + priv->label_guide = NULL; + priv->label_change_idle = 0; priv->watcher_proxy = NULL; priv->connection = NULL; @@ -419,6 +484,11 @@ app_indicator_dispose (GObject *object) priv->fallback_timer = 0; } + if (priv->label_change_idle != 0) { + g_source_remove(priv->label_change_idle); + priv->label_change_idle = 0; + } + if (priv->menu != NULL) { g_signal_handlers_disconnect_by_func (G_OBJECT (priv->menu), client_menu_changed, @@ -491,6 +561,16 @@ app_indicator_finalize (GObject *object) g_free(priv->icon_path); priv->icon_path = NULL; } + + if (priv->label != NULL) { + g_free(priv->label); + priv->label = NULL; + } + + if (priv->label_guide != NULL) { + g_free(priv->label_guide); + priv->label_guide = NULL; + } G_OBJECT_CLASS (app_indicator_parent_class)->finalize (object); return; @@ -563,6 +643,43 @@ app_indicator_set_property (GObject * object, guint prop_id, const GValue * valu priv->icon_path = g_value_dup_string(value); break; + case PROP_LABEL: { + gchar * oldlabel = priv->label; + priv->label = g_value_dup_string(value); + + if (g_strcmp0(oldlabel, priv->label) != 0) { + signal_label_change(APP_INDICATOR(object)); + } + + if (priv->label != NULL && priv->label[0] == '\0') { + g_free(priv->label); + priv->label = NULL; + } + + if (oldlabel != NULL) { + g_free(oldlabel); + } + break; + } + case PROP_LABEL_GUIDE: { + gchar * oldguide = priv->label_guide; + priv->label_guide = g_value_dup_string(value); + + if (g_strcmp0(oldguide, priv->label_guide) != 0) { + signal_label_change(APP_INDICATOR(object)); + } + + if (priv->label_guide != NULL && priv->label_guide[0] == '\0') { + g_free(priv->label_guide); + priv->label_guide = NULL; + } + + if (oldguide != NULL) { + g_free(oldguide); + } + break; + } + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -620,6 +737,14 @@ app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GPa g_value_set_boolean (value, priv->watcher_proxy != NULL ? TRUE : FALSE); break; + case PROP_LABEL: + g_value_set_string (value, priv->label); + break; + + case PROP_LABEL_GUIDE: + g_value_set_string (value, priv->label_guide); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -628,6 +753,39 @@ app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GPa return; } +/* Sends the label changed signal and resets the source ID */ +static gboolean +signal_label_change_idle (gpointer user_data) +{ + AppIndicator * self = (AppIndicator *)user_data; + AppIndicatorPrivate *priv = self->priv; + + g_signal_emit(G_OBJECT(self), signals[NEW_LABEL], 0, + priv->label != NULL ? priv->label : "", + priv->label_guide != NULL ? priv->label_guide : "", + TRUE); + + priv->label_change_idle = 0; + + return FALSE; +} + +/* Sets up an idle function to send the label changed signal + so that we don't send it too many times. */ +static void +signal_label_change (AppIndicator * self) +{ + AppIndicatorPrivate *priv = self->priv; + + /* don't set it twice */ + if (priv->label_change_idle != 0) { + return; + } + + priv->label_change_idle = g_idle_add(signal_label_change_idle, self); + return; +} + /* This function is used to see if we have enough information to connect to things. If we do, and we're not connected, it connects for us. */ @@ -1134,6 +1292,31 @@ app_indicator_set_icon (AppIndicator *self, const gchar *icon_name) return; } +/** + app_indicator_set_label: + @self: The #AppIndicator object to use + @label: The label to show next to the icon. + @guide: A guide to size the label correctly. + + This is a wrapper function for the #AppIndicator:label and + #AppIndicator:guide properties. This function can take #NULL + as either @label or @guide and will clear the entries. +*/ +void +app_indicator_set_label (AppIndicator *self, const gchar * label, const gchar * guide) +{ + g_return_if_fail (IS_APP_INDICATOR (self)); + /* Note: The label can be NULL, it's okay */ + /* Note: The guide can be NULL, it's okay */ + + g_object_set(G_OBJECT(self), + PROP_LABEL_S, label == NULL ? "" : label, + PROP_LABEL_GUIDE_S, guide == NULL ? "" : guide, + NULL); + + return; +} + static void activate_menuitem (DbusmenuMenuitem *mi, guint timestamp, gpointer user_data) { @@ -1661,3 +1844,36 @@ app_indicator_get_menu (AppIndicator *self) return GTK_MENU(priv->menu); } + +/** + app_indicator_get_label: + @self: The #AppIndicator object to use + + Wrapper function for property #AppIndicator:label. + + Return value: The current label. +*/ +const gchar * +app_indicator_get_label (AppIndicator *self) +{ + g_return_val_if_fail (IS_APP_INDICATOR (self), NULL); + + return self->priv->label; +} + +/** + app_indicator_get_label_guide: + @self: The #AppIndicator object to use + + Wrapper function for property #AppIndicator:label-guide. + + Return value: The current label guide. +*/ +const gchar * +app_indicator_get_label_guide (AppIndicator *self) +{ + g_return_val_if_fail (IS_APP_INDICATOR (self), NULL); + + return self->priv->label_guide; +} + diff --git a/src/app-indicator.h b/src/app-indicator.h index e37abd4..586d2dc 100644 --- a/src/app-indicator.h +++ b/src/app-indicator.h @@ -92,6 +92,11 @@ G_BEGIN_DECLS String identifier for the #AppIndicator::new-status signal. */ /** + APP_INDICATOR_SIGNAL_NEW_LABEL: + + String identifier for the #AppIndicator::new-label signal. +*/ +/** APP_INDICATOR_SIGNAL_CONNECTION_CHANGED: String identifier for the #AppIndicator::connection-changed signal. @@ -99,6 +104,7 @@ G_BEGIN_DECLS #define APP_INDICATOR_SIGNAL_NEW_ICON "new-icon" #define APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON "new-attention-icon" #define APP_INDICATOR_SIGNAL_NEW_STATUS "new-status" +#define APP_INDICATOR_SIGNAL_NEW_LABEL "new-label" #define APP_INDICATOR_SIGNAL_CONNECTION_CHANGED "connection-changed" /** @@ -152,8 +158,8 @@ typedef struct _AppIndicatorPrivate AppIndicatorPrivate; there is no Application Indicator area available. @unfallback: The function that gets called if an Application Indicator area appears after the fallback has been created. + @new_label: Slot for #AppIndicator::new-label. @app_indicator_reserved_1: Reserved for future use. - @app_indicator_reserved_2: Reserved for future use. The signals and external functions that make up the #AppIndicator class object. @@ -181,9 +187,14 @@ struct _AppIndicatorClass { void (*unfallback) (AppIndicator * indicator, GtkStatusIcon * status_icon); + /* Another DBus Signal */ + void (* new_label) (AppIndicator *indicator, + const gchar *label, + const gchar *guide, + gpointer user_data); + /* Reserved */ void (*app_indicator_reserved_1)(void); - void (*app_indicator_reserved_2)(void); }; /** @@ -226,6 +237,9 @@ void app_indicator_set_menu (AppIndicator GtkMenu *menu); void app_indicator_set_icon (AppIndicator *self, const gchar *icon_name); +void app_indicator_set_label (AppIndicator *self, + const gchar *label, + const gchar *guide); /* Get properties */ const gchar * app_indicator_get_id (AppIndicator *self); @@ -234,6 +248,8 @@ AppIndicatorStatus app_indicator_get_status (AppIndicator * const gchar * app_indicator_get_icon (AppIndicator *self); const gchar * app_indicator_get_attention_icon (AppIndicator *self); GtkMenu * app_indicator_get_menu (AppIndicator *self); +const gchar * app_indicator_get_label (AppIndicator *self); +const gchar * app_indicator_get_label_guide (AppIndicator *self); G_END_DECLS diff --git a/src/application-service-marshal.list b/src/application-service-marshal.list index 4ac8398..b49ff41 100644 --- a/src/application-service-marshal.list +++ b/src/application-service-marshal.list @@ -18,3 +18,4 @@ # with this program. If not, see <http://www.gnu.org/licenses/>. VOID: STRING, INT, STRING, STRING, STRING VOID: INT, STRING +VOID: STRING, STRING diff --git a/src/notification-item.xml b/src/notification-item.xml index a0141c3..b339e7b 100644 --- a/src/notification-item.xml +++ b/src/notification-item.xml @@ -12,6 +12,8 @@ to find the icons specified above. --> <property name="IconThemePath" type="s" access="read" /> <property name="Menu" type="o" access="read" /> + <property name="Label" type="s" access="read" /> + <property name="LabelGuide" type="s" access="read" /> <!-- Methods --> <!-- None currently --> @@ -24,6 +26,10 @@ <signal name="NewStatus"> <arg type="s" name="status" direction="out" /> </signal> + <signal name="NewLabel"> + <arg type="s" name="label" direction="out" /> + <arg type="s" name="guide" direction="out" /> + </signal> </interface> </node> diff --git a/tests/test-libappindicator.c b/tests/test-libappindicator.c index 86879b3..8d12ac5 100644 --- a/tests/test-libappindicator.c +++ b/tests/test-libappindicator.c @@ -163,12 +163,145 @@ test_libappindicator_init (void) } void +test_libappindicator_set_label (void) +{ + AppIndicator * ci = app_indicator_new ("my-id", + "my-name", + APP_INDICATOR_CATEGORY_APPLICATION_STATUS); + + g_assert(ci != NULL); + g_assert(app_indicator_get_label(ci) == NULL); + g_assert(app_indicator_get_label_guide(ci) == NULL); + + /* First check all the clearing modes, this is important as + we're going to use them later, we need them to work. */ + app_indicator_set_label(ci, NULL, NULL); + + g_assert(app_indicator_get_label(ci) == NULL); + g_assert(app_indicator_get_label_guide(ci) == NULL); + + app_indicator_set_label(ci, "", NULL); + + g_assert(app_indicator_get_label(ci) == NULL); + g_assert(app_indicator_get_label_guide(ci) == NULL); + + app_indicator_set_label(ci, NULL, ""); + + g_assert(app_indicator_get_label(ci) == NULL); + g_assert(app_indicator_get_label_guide(ci) == NULL); + + app_indicator_set_label(ci, "", ""); + + g_assert(app_indicator_get_label(ci) == NULL); + g_assert(app_indicator_get_label_guide(ci) == NULL); + + app_indicator_set_label(ci, "label", ""); + + g_assert(g_strcmp0(app_indicator_get_label(ci), "label") == 0); + g_assert(app_indicator_get_label_guide(ci) == NULL); + + app_indicator_set_label(ci, NULL, NULL); + + g_assert(app_indicator_get_label(ci) == NULL); + g_assert(app_indicator_get_label_guide(ci) == NULL); + + app_indicator_set_label(ci, "label", "guide"); + + g_assert(g_strcmp0(app_indicator_get_label(ci), "label") == 0); + g_assert(g_strcmp0(app_indicator_get_label_guide(ci), "guide") == 0); + + app_indicator_set_label(ci, "label2", "guide"); + + g_assert(g_strcmp0(app_indicator_get_label(ci), "label2") == 0); + g_assert(g_strcmp0(app_indicator_get_label_guide(ci), "guide") == 0); + + app_indicator_set_label(ci, "trick-label", "trick-guide"); + + g_assert(g_strcmp0(app_indicator_get_label(ci), "trick-label") == 0); + g_assert(g_strcmp0(app_indicator_get_label_guide(ci), "trick-guide") == 0); + + g_object_unref(G_OBJECT(ci)); + return; +} + +void +label_signals_cb (AppIndicator * appindicator, gchar * label, gchar * guide, gpointer user_data) +{ + gint * label_signals_count = (gint *)user_data; + (*label_signals_count)++; + return; +} + +void +label_signals_check (void) +{ + while (g_main_context_pending(NULL)) { + g_main_context_iteration(NULL, TRUE); + } + + return; +} + +void +test_libappindicator_label_signals (void) +{ + gint label_signals_count = 0; + AppIndicator * ci = app_indicator_new ("my-id", + "my-name", + APP_INDICATOR_CATEGORY_APPLICATION_STATUS); + + g_assert(ci != NULL); + g_assert(app_indicator_get_label(ci) == NULL); + g_assert(app_indicator_get_label_guide(ci) == NULL); + + g_signal_connect(G_OBJECT(ci), APP_INDICATOR_SIGNAL_NEW_LABEL, G_CALLBACK(label_signals_cb), &label_signals_count); + + /* Shouldn't be a signal as it should be stuck in idle */ + app_indicator_set_label(ci, "label", "guide"); + g_assert(label_signals_count == 0); + + /* Should show up after idle processing */ + label_signals_check(); + g_assert(label_signals_count == 1); + + /* Shouldn't signal with no change */ + label_signals_count = 0; + app_indicator_set_label(ci, "label", "guide"); + label_signals_check(); + g_assert(label_signals_count == 0); + + /* Change one, we should get one signal */ + app_indicator_set_label(ci, "label2", "guide"); + label_signals_check(); + g_assert(label_signals_count == 1); + + /* Change several times, one signal */ + label_signals_count = 0; + app_indicator_set_label(ci, "label1", "guide0"); + app_indicator_set_label(ci, "label1", "guide1"); + app_indicator_set_label(ci, "label2", "guide2"); + app_indicator_set_label(ci, "label3", "guide3"); + label_signals_check(); + g_assert(label_signals_count == 1); + + /* Clear should signal too */ + label_signals_count = 0; + app_indicator_set_label(ci, NULL, NULL); + label_signals_check(); + g_assert(label_signals_count == 1); + + return; +} + +void test_libappindicator_props_suite (void) { g_test_add_func ("/indicator-application/libappindicator/init", test_libappindicator_init); g_test_add_func ("/indicator-application/libappindicator/init_props", test_libappindicator_init_with_props); g_test_add_func ("/indicator-application/libappindicator/init_set_props", test_libappindicator_init_set_props); g_test_add_func ("/indicator-application/libappindicator/prop_signals", test_libappindicator_prop_signals); + g_test_add_func ("/indicator-application/libappindicator/set_label", test_libappindicator_set_label); + g_test_add_func ("/indicator-application/libappindicator/label_signals", test_libappindicator_label_signals); return; } |