diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 215 | ||||
-rw-r--r-- | src/app-menu-item.c | 47 | ||||
-rw-r--r-- | src/default-applications.c | 43 | ||||
-rw-r--r-- | src/default-applications.h | 4 | ||||
-rw-r--r-- | src/indicator-messages-status-provider-0.5.pc.in.in | 15 | ||||
-rw-r--r-- | src/indicator-messages.c | 157 | ||||
-rw-r--r-- | src/launcher-menu-item.c | 38 | ||||
-rw-r--r-- | src/messages-service-dbus.c | 9 | ||||
-rw-r--r-- | src/messages-service.c | 82 | ||||
-rw-r--r-- | src/messages-service.xml | 4 | ||||
-rw-r--r-- | src/status-items.c | 314 | ||||
-rw-r--r-- | src/status-items.h | 38 | ||||
-rw-r--r-- | src/status-provider-emesene.c | 348 | ||||
-rw-r--r-- | src/status-provider-emesene.h | 56 | ||||
-rw-r--r-- | src/status-provider-mc5.c | 308 | ||||
-rw-r--r-- | src/status-provider-mc5.h | 56 | ||||
-rw-r--r-- | src/status-provider-mc5.list | 1 | ||||
-rw-r--r-- | src/status-provider-pidgin.c | 433 | ||||
-rw-r--r-- | src/status-provider-pidgin.h | 56 | ||||
-rw-r--r-- | src/status-provider-pidgin.list | 1 | ||||
-rw-r--r-- | src/status-provider-telepathy.c | 385 | ||||
-rw-r--r-- | src/status-provider-telepathy.h | 56 | ||||
-rw-r--r-- | src/status-provider-telepathy.list | 1 | ||||
-rw-r--r-- | src/status-provider.c | 101 | ||||
-rw-r--r-- | src/status-provider.h | 81 |
25 files changed, 2783 insertions, 66 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 8597d77..c631436 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,12 @@ +BUILT_SOURCES = +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = + libexec_PROGRAMS = indicator-messages-service + ###################################### # Building the messages indicator ###################################### @@ -44,7 +50,11 @@ indicator_messages_service_SOURCES = \ seen-db.c \ seen-db.h \ dirs.h \ - dbus-data.h + dbus-data.h \ + \ + status-items.c \ + status-items.h + indicator_messages_service_CFLAGS = \ $(APPLET_CFLAGS) \ -Wall \ @@ -52,8 +62,12 @@ indicator_messages_service_CFLAGS = \ -Wl,-z,defs \ -Wl,--as-needed \ -Werror \ - -DG_LOG_DOMAIN=\"Indicator-Messages\" -indicator_messages_service_LDADD = $(APPLET_LIBS) + -DG_LOG_DOMAIN=\"Indicator-Messages\" \ + -DSTATUS_PROVIDER_DIR=\"$(STATUS_PROVIDER_DIR)\" + +indicator_messages_service_LDADD = \ + $(APPLET_LIBS) \ + libindicator-messages-status-provider.la gen-%.xml.h: %.xml @echo "Building $@ from $<" @@ -65,13 +79,198 @@ gen-%.xml.c: %.xml @sed -e "s:\":\\\\\":g" -e s:^:\": -e s:\$$:\\\\n\": $< >> $@ @echo ";" >> $@ -BUILT_SOURCES = \ +BUILT_SOURCES += \ gen-messages-service.xml.h \ gen-messages-service.xml.c -CLEANFILES = \ - $(BUILT_SOURCES) - -EXTRA_DIST = \ +EXTRA_DIST += \ messages-service.xml +###################################### +# Status Provider Library +###################################### + +STATUS_PROVIDER_API_VERSION = 1 +STATUS_PROVIDER_DIR = $(libexecdir)/status-providers/$(STATUS_PROVIDER_API_VERSION) +statusprovidersdir = $(STATUS_PROVIDER_DIR) +statusproviders_LTLIBRARIES = + +EXTRA_DIST += \ + indicator-messages-status-provider-0.5.pc.in.in +CLEANFILES += \ + indicator-messages-status-provider-0.5.pc + +pkgconfig_DATA = indicator-messages-status-provider-0.5.pc +pkgconfigdir = $(libdir)/pkgconfig + +%.pc: %.pc.in + sed \ + -e "s|\@status_provider_dir\@|$(STATUS_PROVIDER_DIR)|" \ + -e "s|\@status_provider_api_version\@|$(STATUS_PROVIDER_API_VERSION)|" \ + $< > $@ + +lib_LTLIBRARIES = \ + libindicator-messages-status-provider.la + +libindicator_messages_status_provider_la_HEADERS = \ + status-provider.h + +libindicator_messages_status_provider_la_SOURCES = \ + $(libindicator_messages_status_provider_HEADERS) \ + status-provider.c + +libindicator_messages_status_provider_ladir = \ + $(includedir)/libindicator-messages-status-provider-$(STATUS_PROVIDER_API_VERSION)/ + +libindicator_messages_status_provider_la_LDFLAGS = \ + -version-info $(STATUS_PROVIDER_API_VERSION):0:0 \ + -no-undefined \ + -export-symbols-regex "^[^_].*" + +libindicator_messages_status_provider_la_LIBADD = \ + $(APPLET_LIBS) + +libindicator_messages_status_provider_la_CFLAGS = \ + $(APPLET_CFLAGS) \ + -Wall -Werror + +###################################### +# Status provider: Pidgin +###################################### + +statusproviders_LTLIBRARIES += libpidgin.la +libpidgin_la_SOURCES = \ + status-provider-pidgin.h \ + status-provider-pidgin.c \ + status-provider-pidgin-marshal.h \ + status-provider-pidgin-marshal.c +libpidgin_la_CFLAGS = \ + $(APPLET_CFLAGS) \ + $(STATUS_PROVIDER_PIDGIN_CFLAGS) \ + -Wall -Werror \ + -DG_LOG_DOMAIN=\"Status-Provider-Pidgin\" +libpidgin_la_LIBADD = \ + libindicator-messages-status-provider.la \ + $(APPLET_LIBS) \ + $(STATUS_PROVIDER_PIDGIN_LIBS) +libpidgin_la_LDFLAGS = -module -avoid-version + +status-provider-pidgin-marshal.h: $(srcdir)/status-provider-pidgin.list + glib-genmarshal --header \ + --prefix=_status_provider_pidgin_marshal $(srcdir)/status-provider-pidgin.list \ + > status-provider-pidgin-marshal.h + +status-provider-pidgin-marshal.c: $(srcdir)/status-provider-pidgin.list + glib-genmarshal --body \ + --prefix=_status_provider_pidgin_marshal $(srcdir)/status-provider-pidgin.list \ + > status-provider-pidgin-marshal.c + +BUILT_SOURCES += \ + status-provider-pidgin-marshal.h \ + status-provider-pidgin-marshal.c + +EXTRA_DIST += \ + status-provider-pidgin.list + +###################################### +# Status provider: Mission Control 4 +###################################### + +statusproviders_LTLIBRARIES += libtelepathy.la +libtelepathy_la_SOURCES = \ + status-provider-telepathy.h \ + status-provider-telepathy.c \ + status-provider-telepathy-marshal.h \ + status-provider-telepathy-marshal.c +libtelepathy_la_CFLAGS = \ + $(APPLET_CFLAGS) \ + $(STATUS_PROVIDER_TELEPATHY_CFLAGS) \ + -Wall -Werror \ + -DG_LOG_DOMAIN=\"Status-Provider-Telepathy\" +libtelepathy_la_LIBADD = \ + libindicator-messages-status-provider.la \ + $(APPLET_LIBS) \ + $(STATUS_PROVIDER_TELEPATHY_LIBS) +libtelepathy_la_LDFLAGS = -module -avoid-version + +status-provider-telepathy-marshal.h: $(srcdir)/status-provider-telepathy.list + glib-genmarshal --header \ + --prefix=_status_provider_telepathy_marshal $(srcdir)/status-provider-telepathy.list \ + > status-provider-telepathy-marshal.h + +status-provider-telepathy-marshal.c: $(srcdir)/status-provider-telepathy.list + glib-genmarshal --body \ + --prefix=_status_provider_telepathy_marshal $(srcdir)/status-provider-telepathy.list \ + > status-provider-telepathy-marshal.c + +BUILT_SOURCES += \ + status-provider-telepathy-marshal.h \ + status-provider-telepathy-marshal.c + +EXTRA_DIST += \ + status-provider-telepathy.list + +###################################### +# Status provider: Mission Control 5 +###################################### + +statusproviders_LTLIBRARIES += libmc5.la +libmc5_la_SOURCES = \ + status-provider-mc5.h \ + status-provider-mc5.c \ + status-provider-mc5-marshal.h \ + status-provider-mc5-marshal.c +libmc5_la_CFLAGS = \ + $(APPLET_CFLAGS) \ + $(STATUS_PROVIDER_MC5_CFLAGS) \ + -Wall -Werror \ + -DG_LOG_DOMAIN=\"Status-Provider-MC5\" +libmc5_la_LIBADD = \ + libindicator-messages-status-provider.la \ + $(APPLET_LIBS) \ + $(STATUS_PROVIDER_MC5_LIBS) +libmc5_la_LDFLAGS = -module -avoid-version + +status-provider-mc5-marshal.h: $(srcdir)/status-provider-mc5.list + glib-genmarshal --header \ + --prefix=_status_provider_mc5_marshal $(srcdir)/status-provider-mc5.list \ + > status-provider-mc5-marshal.h + +status-provider-mc5-marshal.c: $(srcdir)/status-provider-mc5.list + glib-genmarshal --body \ + --prefix=_status_provider_mc5_marshal $(srcdir)/status-provider-mc5.list \ + > status-provider-mc5-marshal.c + +BUILT_SOURCES += \ + status-provider-mc5-marshal.h \ + status-provider-mc5-marshal.c + +EXTRA_DIST += \ + status-provider-mc5.list + +###################################### +# Status provider: Emesene +###################################### + +statusproviders_LTLIBRARIES += libemesene.la +libemesene_la_SOURCES = \ + status-provider-emesene.h \ + status-provider-emesene.c +libemesene_la_CFLAGS = \ + $(APPLET_CFLAGS) \ + $(STATUS_PROVIDER_EMESENE_CFLAGS) \ + -Wall -Werror \ + -DG_LOG_DOMAIN=\"Status-Provider-Emesene\" +libemesene_la_LIBADD = \ + libindicator-messages-status-provider.la \ + $(APPLET_LIBS) \ + $(STATUS_PROVIDER_EMESENE_LIBS) +libemesene_la_LDFLAGS = -module -avoid-version + +###################################### +# Extras +###################################### + +CLEANFILES += \ + $(BUILT_SOURCES) + diff --git a/src/app-menu-item.c b/src/app-menu-item.c index e846914..ef3fbc0 100644 --- a/src/app-menu-item.c +++ b/src/app-menu-item.c @@ -52,6 +52,7 @@ struct _AppMenuItemPrivate gchar * type; GAppInfo * appinfo; + GKeyFile * keyfile; gchar * desktop; guint unreadcount; @@ -129,6 +130,7 @@ app_menu_item_init (AppMenuItem *self) priv->server = NULL; priv->type = NULL; priv->appinfo = NULL; + priv->keyfile = NULL; priv->desktop = NULL; priv->unreadcount = 0; @@ -179,6 +181,16 @@ app_menu_item_dispose (GObject *object) priv->client = NULL; } + if (priv->appinfo != NULL) { + g_object_unref(priv->appinfo); + priv->appinfo = NULL; + } + + if (priv->keyfile != NULL) { + g_object_unref(priv->keyfile); + priv->keyfile = NULL; + } + G_OBJECT_CLASS (app_menu_item_parent_class)->dispose (object); } @@ -192,14 +204,12 @@ app_menu_item_finalize (GObject *object) if (priv->type != NULL) { g_free(priv->type); + priv->type = NULL; } if (priv->desktop != NULL) { g_free(priv->desktop); - } - - if (priv->appinfo != NULL) { - g_object_unref(priv->appinfo); + priv->desktop = NULL; } G_OBJECT_CLASS (app_menu_item_parent_class)->finalize (object); @@ -298,7 +308,7 @@ count_cb (IndicateListener * listener, IndicateListenerServer * server, guint va /* Callback for when we ask the server for the path to it's desktop file. We then turn it into an app structure and start sucking data out of it. - Mostly the name. */ + Mostly the name. And the icon. */ static void desktop_cb (IndicateListener * listener, IndicateListenerServer * server, const gchar * value, gpointer data) { @@ -325,6 +335,9 @@ desktop_cb (IndicateListener * listener, IndicateListenerServer * server, const priv->appinfo = G_APP_INFO(g_desktop_app_info_new_from_filename(value)); g_return_if_fail(priv->appinfo != NULL); + priv->keyfile = g_key_file_new(); + g_key_file_load_from_file(priv->keyfile, value, G_KEY_FILE_NONE, NULL); + priv->desktop = g_strdup(value); dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); @@ -334,8 +347,28 @@ desktop_cb (IndicateListener * listener, IndicateListenerServer * server, const const gchar * def_icon = get_default_icon(priv->desktop); if (def_icon == NULL) { - GIcon * icon = g_app_info_get_icon(priv->appinfo); - gchar * iconstr = g_icon_to_string(icon); + gchar * iconstr = NULL; + + /* Check for the over ride key and see if we should be using that + icon. If we can't get it, then go back to the app info */ + if (g_key_file_has_key(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, ICON_KEY, NULL) && iconstr == NULL) { + GError * error = NULL; + + iconstr = g_key_file_get_string(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, ICON_KEY, &error); + + if (error != NULL) { + /* Can't figure out why this would happen, but sure, let's print something */ + g_warning("Error getting '" ICON_KEY "' from desktop file: %s", error->message); + g_error_free(error); + } + } + + /* For some reason that didn't work, let's try the app info */ + if (iconstr == NULL) { + GIcon * icon = g_app_info_get_icon(priv->appinfo); + iconstr = g_icon_to_string(icon); + } + dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), APPLICATION_MENUITEM_PROP_ICON, iconstr); g_free(iconstr); } else { diff --git a/src/default-applications.c b/src/default-applications.c index afb5025..0382e4d 100644 --- a/src/default-applications.c +++ b/src/default-applications.c @@ -21,20 +21,22 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <glib.h> #include <glib/gi18n.h> +#include <gio/gio.h> +#include <gio/gdesktopappinfo.h> #include "default-applications.h" struct default_db_t { const gchar * desktop_file; + const gchar * uri_scheme; const gchar * name; const gchar * setupname; const gchar * icon; }; struct default_db_t default_db[] = { - {"evolution.desktop", N_("Mail"), N_("Set Up Mail..."), "applications-email-panel"}, - {"empathy.desktop", N_("Chat"), N_("Set Up Chat..."), "applications-chat-panel"}, - {"gwibber.desktop", N_("Broadcast"), N_("Set Up Broadcast Account..."), "applications-microblogging-panel"}, - {NULL, NULL} + {NULL, "mailto", N_("Mail"), N_("Set Up Mail..."), "applications-email-panel"}, + {"empathy.desktop", NULL, N_("Chat"), N_("Set Up Chat..."), "applications-chat-panel"}, + {"gwibber.desktop", NULL, N_("Broadcast"), N_("Set Up Broadcast Account..."), "applications-microblogging-panel"}, }; static struct default_db_t * @@ -44,17 +46,40 @@ get_default_helper (const gchar * desktop_path) gchar * basename = g_path_get_basename(desktop_path); g_return_val_if_fail(basename != NULL, NULL); + gboolean found = FALSE; gint i; - for (i = 0; default_db[i].desktop_file != NULL; i++) { - if (g_strcmp0(default_db[i].desktop_file, basename) == 0) { - break; + gint length = G_N_ELEMENTS(default_db); + for (i = 0; i < length && !found; i++) { + if (default_db[i].desktop_file) { + if (g_strcmp0(default_db[i].desktop_file, basename) == 0) { + found = TRUE; + } + } else if (default_db[i].uri_scheme) { + GAppInfo *info = g_app_info_get_default_for_uri_scheme(default_db[i].uri_scheme); + if (!info) { + continue; + } + + const gchar * filename = g_desktop_app_info_get_filename(G_DESKTOP_APP_INFO(info)); + if (!filename) { + g_object_unref(info); + continue; + } + + gchar * default_basename = g_path_get_basename(filename); + g_object_unref(info); + if (g_strcmp0(default_basename, basename) == 0) { + found = TRUE; + } + + g_free(default_basename); } } g_free(basename); - if (default_db[i].desktop_file != NULL) { - return &default_db[i]; + if (found) { + return &default_db[i - 1]; } return NULL; diff --git a/src/default-applications.h b/src/default-applications.h index 0a32e7c..df52b38 100644 --- a/src/default-applications.h +++ b/src/default-applications.h @@ -22,6 +22,10 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #ifndef DEFAULT_APPLICATIONS_H__ #define DEFAULT_APPLICATIONS_H__ 1 +/* Used for override icons in the normal case, but didn't + have a better place to put it. */ +#define ICON_KEY "X-Ayatana-Messaging-Menu-Icon" + const gchar * get_default_name (const gchar * desktop_path); const gchar * get_default_setup (const gchar * desktop_path); const gchar * get_default_icon (const gchar * desktop_path); diff --git a/src/indicator-messages-status-provider-0.5.pc.in.in b/src/indicator-messages-status-provider-0.5.pc.in.in new file mode 100644 index 0000000..3fe4cf2 --- /dev/null +++ b/src/indicator-messages-status-provider-0.5.pc.in.in @@ -0,0 +1,15 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +bindir=@bindir@ +includedir=@includedir@ + +statusproviderdir=@status_provider_dir@ + +Cflags: -I${includedir}/indicator-messages-status-provider-0.@status_provider_api_version@ +Requires: gobject-2.0 +Libs: -L${libdir} -lindicator-messages-status-provider + +Name: indicator-messages-status-provider +Description: Status providers for the indicator-messages menu. +Version: @VERSION@ diff --git a/src/indicator-messages.c b/src/indicator-messages.c index 0f5fa31..1b96464 100644 --- a/src/indicator-messages.c +++ b/src/indicator-messages.c @@ -23,9 +23,16 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <string.h> #include <glib.h> #include <glib-object.h> +#include <glib/gi18n.h> #include <gtk/gtk.h> + +#if GTK_CHECK_VERSION(3, 0, 0) +#include <libdbusmenu-gtk3/menu.h> +#include <libdbusmenu-gtk3/menuitem.h> +#else #include <libdbusmenu-gtk/menu.h> #include <libdbusmenu-gtk/menuitem.h> +#endif #include <libindicator/indicator.h> #include <libindicator/indicator-object.h> @@ -52,6 +59,7 @@ typedef struct _IndicatorMessagesClass IndicatorMessagesClass; struct _IndicatorMessagesClass { IndicatorObjectClass parent_class; + void (*update_a11y_desc) (IndicatorServiceManager * service, gpointer * user_data); }; struct _IndicatorMessages { @@ -71,6 +79,8 @@ static GDBusProxy * icon_proxy = NULL; static GtkSizeGroup * indicator_right_group = NULL; static GDBusNodeInfo * bus_node_info = NULL; static GDBusInterfaceInfo * bus_interface_info = NULL; +static const gchar * accessible_desc = NULL; +static IndicatorObject * indicator = NULL; /* Prototypes */ static void indicator_messages_class_init (IndicatorMessagesClass *klass); @@ -79,12 +89,37 @@ static void indicator_messages_dispose (GObject *object); static void indicator_messages_finalize (GObject *object); static GtkImage * get_icon (IndicatorObject * io); static GtkMenu * get_menu (IndicatorObject * io); +static void indicator_messages_middle_click (IndicatorObject * io, + IndicatorObjectEntry * entry, + guint time, gpointer data); +static const gchar * get_accessible_desc (IndicatorObject * io); static void connection_change (IndicatorServiceManager * sm, gboolean connected, gpointer user_data); G_DEFINE_TYPE (IndicatorMessages, indicator_messages, INDICATOR_OBJECT_TYPE); +static void +update_a11y_desc (void) +{ + g_return_if_fail(IS_INDICATOR_MESSAGES(indicator)); + + GList *entries = indicator_object_get_entries(indicator); + IndicatorObjectEntry * entry = (IndicatorObjectEntry *)entries->data; + + entry->accessible_desc = get_accessible_desc(indicator); + + g_signal_emit(G_OBJECT(indicator), + INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE_ID, + 0, + entry, + TRUE); + + g_list_free(entries); + + return; +} + /* Initialize the one-timers */ static void indicator_messages_class_init (IndicatorMessagesClass *klass) @@ -98,6 +133,8 @@ indicator_messages_class_init (IndicatorMessagesClass *klass) io_class->get_image = get_icon; io_class->get_menu = get_menu; + io_class->get_accessible_desc = get_accessible_desc; + io_class->secondary_activate = indicator_messages_middle_click; if (bus_node_info == NULL) { GError * error = NULL; @@ -131,6 +168,8 @@ indicator_messages_init (IndicatorMessages *self) self->service = indicator_service_manager_new_version(INDICATOR_MESSAGES_DBUS_NAME, 1); g_signal_connect(self->service, INDICATOR_SERVICE_MANAGER_SIGNAL_CONNECTION_CHANGE, G_CALLBACK(connection_change), self); + indicator = INDICATOR_OBJECT(self); + return; } @@ -172,8 +211,10 @@ proxy_signal (GDBusProxy * proxy, const gchar * sender, const gchar * signal, GV if (g_strcmp0("AttentionChanged", signal) == 0) { if (prop) { indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages-new"); + accessible_desc = _("New Messages"); } else { indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages"); + accessible_desc = _("Messages"); } } else if (g_strcmp0("IconChanged", signal) == 0) { if (prop) { @@ -185,6 +226,8 @@ proxy_signal (GDBusProxy * proxy, const gchar * sender, const gchar * signal, GV g_warning("Unknown signal %s", signal); } + update_a11y_desc(); + return; } @@ -205,10 +248,14 @@ attention_cb (GObject * object, GAsyncResult * ares, gpointer user_data) if (prop) { indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages-new"); + accessible_desc = _("New Messages"); } else { indicator_image_helper_update(GTK_IMAGE(main_image), "indicator-messages"); + accessible_desc = _("Messages"); } + update_a11y_desc(); + return; } @@ -369,12 +416,18 @@ application_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * valu } /* Draws a triangle on the left, using fg[STATE_TYPE] color. */ +#if GTK_CHECK_VERSION(3, 0, 0) +static gboolean +application_triangle_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data) +{ +#else static gboolean application_triangle_draw_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data) { + cairo_t *cr; +#endif GtkAllocation allocation; GtkStyle *style; - cairo_t *cr; int x, y, arrow_width, arrow_height; if (!GTK_IS_WIDGET (widget)) return FALSE; @@ -391,13 +444,22 @@ application_triangle_draw_cb (GtkWidget *widget, GdkEventExpose *event, gpointer arrow_width = 5; /* the pixel-based reference triangle is 5x9 */ arrow_height = 9; gtk_widget_get_allocation (widget, &allocation); +#if GTK_CHECK_VERSION(3, 0, 0) + x = 0; + y = allocation.height/2.0 - (double)arrow_height/2.0; +#else x = allocation.x; y = allocation.y + allocation.height/2.0 - (double)arrow_height/2.0; +#endif +#if GTK_CHECK_VERSION(3, 0, 0) + cairo_save (cr); +#else /* initialize cairo drawing area */ cr = (cairo_t*) gdk_cairo_create (gtk_widget_get_window (widget)); +#endif - /* set line width */ + /* set line width */ cairo_set_line_width (cr, 1.0); /* cairo drawing code */ @@ -410,8 +472,12 @@ application_triangle_draw_cb (GtkWidget *widget, GdkEventExpose *event, gpointer style->fg[gtk_widget_get_state(widget)].blue/65535.0); cairo_fill (cr); +#if GTK_CHECK_VERSION(3, 0, 0) + cairo_restore (cr); +#else /* remember to destroy cairo context to avoid leaks */ cairo_destroy (cr); +#endif return FALSE; } @@ -431,12 +497,18 @@ custom_cairo_rounded_rectangle (cairo_t *cr, } /* Draws a rounded rectangle with text inside. */ +#if GTK_CHECK_VERSION(3, 0, 0) +static gboolean +numbers_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data) +{ +#else static gboolean numbers_draw_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data) { + cairo_t *cr; +#endif GtkAllocation allocation; GtkStyle *style; - cairo_t *cr; double x, y, w, h; PangoLayout * layout; gint font_size = RIGHT_LABEL_FONT_SIZE; @@ -448,10 +520,15 @@ numbers_draw_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data) /* set arrow position / dimensions */ gtk_widget_get_allocation (widget, &allocation); - w = allocation.width; - h = allocation.height; +#if GTK_CHECK_VERSION(3, 0, 0) + x = 0; + y = 0; +#else x = allocation.x; y = allocation.y; +#endif + w = allocation.width; + h = allocation.height; layout = gtk_label_get_layout (GTK_LABEL(widget)); @@ -461,10 +538,14 @@ numbers_draw_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data) /* const PangoFontDescription * font_description = pango_layout_get_font_description (layout); font_size = pango_font_description_get_size (font_description); */ +#if GTK_CHECK_VERSION(3, 0, 0) + cairo_save (cr); +#else /* initialize cairo drawing area */ cr = (cairo_t*) gdk_cairo_create (gtk_widget_get_window (widget)); +#endif - /* set line width */ + /* set line width */ cairo_set_line_width (cr, 1.0); cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); @@ -480,8 +561,12 @@ numbers_draw_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data) pango_cairo_layout_path (cr, layout); cairo_fill (cr); +#if GTK_CHECK_VERSION(3, 0, 0) + cairo_restore (cr); +#else /* remember to destroy cairo context to avoid leaks */ - cairo_destroy (cr); + cairo_destroy (cr); +#endif return TRUE; } @@ -489,18 +574,13 @@ numbers_draw_cb (GtkWidget *widget, GdkEventExpose *event, gpointer data) /* Builds a menu item representing a running application in the messaging menu */ static gboolean -new_application_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client) +new_application_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data) { g_debug ("%s (\"%s\")", __func__, dbusmenu_menuitem_property_get(newitem, APPLICATION_MENUITEM_PROP_NAME)); GtkMenuItem * gmi = GTK_MENU_ITEM(gtk_image_menu_item_new()); gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(gmi), TRUE); - gint padding = 4; - gtk_widget_style_get(GTK_WIDGET(gmi), "horizontal-padding", &padding, NULL); - - GtkWidget * hbox = gtk_hbox_new(FALSE, 0); - /* Set the minimum size, we always want it to take space */ gint width, height; gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height); @@ -517,12 +597,10 @@ new_application_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbu /* Application name in a label */ GtkWidget * label = gtk_label_new(dbusmenu_menuitem_property_get(newitem, APPLICATION_MENUITEM_PROP_NAME)); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, padding); gtk_widget_show(label); /* Insert the hbox */ - gtk_container_add(GTK_CONTAINER(gmi), hbox); - gtk_widget_show(hbox); + gtk_container_add(GTK_CONTAINER(gmi), label); /* Attach some of the standard GTK stuff */ dbusmenu_gtkclient_newitem_base(DBUSMENU_GTKCLIENT(client), newitem, gmi, parent); @@ -530,7 +608,11 @@ new_application_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbu /* Make sure we can handle the label changing */ g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(application_prop_change_cb), label); g_signal_connect(G_OBJECT(newitem), DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED, G_CALLBACK(application_icon_change_cb), icon); +#if GTK_CHECK_VERSION(3, 0, 0) + g_signal_connect_after(G_OBJECT (gmi), "draw", G_CALLBACK (application_triangle_draw_cb), newitem); +#else g_signal_connect_after(G_OBJECT (gmi), "expose_event", G_CALLBACK (application_triangle_draw_cb), newitem); +#endif return TRUE; } @@ -595,7 +677,7 @@ indicator_prop_change_cb (DbusmenuMenuitem * mi, gchar * prop, GVariant * value, shifting over and putting the icon in with some right side text that'll be determined by the service. */ static gboolean -new_indicator_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client) +new_indicator_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, DbusmenuClient * client, gpointer user_data) { g_return_val_if_fail(DBUSMENU_IS_MENUITEM(newitem), FALSE); g_return_val_if_fail(DBUSMENU_IS_GTKCLIENT(client), FALSE); @@ -607,9 +689,9 @@ new_indicator_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusm gint padding = 4; gint font_size = RIGHT_LABEL_FONT_SIZE; - gtk_widget_style_get(GTK_WIDGET(gmi), "horizontal-padding", &padding, NULL); + gtk_widget_style_get(GTK_WIDGET(gmi), "toggle-spacing", &padding, NULL); - GtkWidget * hbox = gtk_hbox_new(FALSE, 0); + GtkWidget * hbox = gtk_hbox_new(FALSE, padding); /* Icon, probably someone's face or avatar on an IM */ mi_data->icon = gtk_image_new(); @@ -642,7 +724,7 @@ new_indicator_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusm g_object_unref(resized_pixbuf); } gtk_misc_set_alignment(GTK_MISC(mi_data->icon), 0.0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), mi_data->icon, FALSE, FALSE, padding); + gtk_box_pack_start(GTK_BOX(hbox), mi_data->icon, FALSE, FALSE, 0); if (pixbuf != NULL) { gtk_widget_show(mi_data->icon); @@ -651,7 +733,7 @@ new_indicator_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusm /* Label, probably a username, chat room or mailbox name */ mi_data->label = gtk_label_new(dbusmenu_menuitem_property_get(newitem, INDICATOR_MENUITEM_PROP_LABEL)); gtk_misc_set_alignment(GTK_MISC(mi_data->label), 0.0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), mi_data->label, TRUE, TRUE, padding); + gtk_box_pack_start(GTK_BOX(hbox), mi_data->label, TRUE, TRUE, 0); gtk_widget_show(mi_data->label); /* Usually either the time or the count on the individual @@ -659,8 +741,13 @@ new_indicator_item (DbusmenuMenuitem * newitem, DbusmenuMenuitem * parent, Dbusm mi_data->right = gtk_label_new(dbusmenu_menuitem_property_get(newitem, INDICATOR_MENUITEM_PROP_RIGHT)); gtk_size_group_add_widget(indicator_right_group, mi_data->right); /* install extra decoration overlay */ +#if GTK_CHECK_VERSION(3, 0, 0) + g_signal_connect (G_OBJECT (mi_data->right), "draw", + G_CALLBACK (numbers_draw_cb), NULL); +#else g_signal_connect (G_OBJECT (mi_data->right), "expose_event", G_CALLBACK (numbers_draw_cb), NULL); +#endif gtk_misc_set_alignment(GTK_MISC(mi_data->right), 1.0, 0.5); gtk_box_pack_start(GTK_BOX(hbox), mi_data->right, FALSE, FALSE, padding + font_size/2.0); @@ -701,3 +788,31 @@ get_menu (IndicatorObject * io) return GTK_MENU(menu); } + +/* Returns the accessible description of the indicator */ +static const gchar * +get_accessible_desc (IndicatorObject * io) +{ + return accessible_desc; +} + +/* Hide the notifications on middle-click over the indicator-messages */ +static void +indicator_messages_middle_click (IndicatorObject * io, IndicatorObjectEntry * entry, + guint time, gpointer data) +{ + if (icon_proxy == NULL) { + return; + } + + g_dbus_proxy_call(icon_proxy, + "ClearAttention", + NULL, /* params */ + G_DBUS_CALL_FLAGS_NONE, + -1, /* timeout */ + NULL, /* cancel */ + NULL, + NULL); + + return; +} diff --git a/src/launcher-menu-item.c b/src/launcher-menu-item.c index a47af03..e01806e 100644 --- a/src/launcher-menu-item.c +++ b/src/launcher-menu-item.c @@ -44,6 +44,7 @@ typedef struct _LauncherMenuItemPrivate LauncherMenuItemPrivate; struct _LauncherMenuItemPrivate { GAppInfo * appinfo; + GKeyFile * keyfile; gchar * desktop; IndicatorDesktopShortcuts * ids; GList * shortcuts; @@ -93,10 +94,13 @@ launcher_menu_item_init (LauncherMenuItem *self) priv->appinfo = NULL; priv->desktop = NULL; + priv->keyfile = NULL; priv->ids = NULL; priv->shortcuts = NULL; + dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(self), DBUSMENU_MENUITEM_PROP_TYPE, APPLICATION_MENUITEM_TYPE); + return; } @@ -118,6 +122,11 @@ launcher_menu_item_dispose (GObject *object) priv->appinfo = NULL; } + if (priv->keyfile != NULL) { + g_object_unref(priv->keyfile); + priv->keyfile = NULL; + } + if (priv->ids != NULL) { g_object_unref(priv->ids); priv->ids = NULL; @@ -158,6 +167,8 @@ launcher_menu_item_new (const gchar * desktop_file) /* Parse the desktop file we've been given. */ priv->appinfo = G_APP_INFO(g_desktop_app_info_new_from_filename(desktop_file)); + priv->keyfile = g_key_file_new(); + g_key_file_load_from_file(priv->keyfile, desktop_file, G_KEY_FILE_NONE, NULL); priv->desktop = g_strdup(desktop_file); /* Set the appropriate values on this menu item based on the @@ -248,18 +259,35 @@ nick_activate_cb (LauncherMenuItem * self, guint timestamp, gpointer data) return; } +/* Figure out the appropriate icon for this launcher */ gchar * launcher_menu_item_get_icon (LauncherMenuItem * appitem) { LauncherMenuItemPrivate * priv = LAUNCHER_MENU_ITEM_GET_PRIVATE(appitem); + gchar * retval = NULL; - if (priv->appinfo == NULL) { - return NULL; - } else { + /* Check to see if there is a specific icon for the messaging + menu first. */ + if (g_key_file_has_key(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, ICON_KEY, NULL) && retval == NULL) { + GError * error = NULL; + + retval = g_key_file_get_string(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, ICON_KEY, &error); + + if (error != NULL) { + /* Can't figure out why this would happen, but sure, let's print something */ + g_warning("Error getting '" ICON_KEY "' from desktop file: %s", error->message); + g_error_free(error); + } + } + + /* If there's not, or there is an error, we'll use the one + from the application info */ + if (priv->appinfo != NULL && retval == NULL) { GIcon * icon = g_app_info_get_icon(priv->appinfo); - gchar * iconstr = g_icon_to_string(icon); - return iconstr; + retval = g_icon_to_string(icon); } + + return retval; } /* When the menu item is clicked on it tries to launch diff --git a/src/messages-service-dbus.c b/src/messages-service-dbus.c index 6cc33e0..1585ac0 100644 --- a/src/messages-service-dbus.c +++ b/src/messages-service-dbus.c @@ -200,7 +200,10 @@ message_service_dbus_new (void) static void bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data) { - MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(user_data); + MessageServiceDbus * ms = MESSAGE_SERVICE_DBUS(user_data); + if (ms == NULL) { return; } + + MessageServiceDbusPrivate * priv = MESSAGE_SERVICE_DBUS_GET_PRIVATE(ms); if (g_strcmp0("AttentionRequested", method) == 0) { g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", priv->dot)); @@ -208,6 +211,10 @@ bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar } else if (g_strcmp0("IconShown", method) == 0) { g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", priv->hidden)); return; + } else if (g_strcmp0("ClearAttention", method) == 0) { + message_service_dbus_set_attention(ms, FALSE); + g_dbus_method_invocation_return_value(invocation, NULL); + return; } else { g_warning("Unknown function call '%s'", method); } diff --git a/src/messages-service.c b/src/messages-service.c index e3352e8..e8fe576 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -28,10 +28,10 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <libindicate/listener.h> #include <libindicator/indicator-service.h> #include <gio/gio.h> +#include <glib/gi18n.h> #include <libdbusmenu-glib/client.h> #include <libdbusmenu-glib/server.h> -#include <libdbusmenu-glib/menuitem-proxy.h> #include "im-menu-item.h" #include "app-menu-item.h" @@ -40,6 +40,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include "dirs.h" #include "messages-service-dbus.h" #include "seen-db.h" +#include "status-items.h" static IndicatorService * service = NULL; static IndicateListener * listener = NULL; @@ -47,6 +48,8 @@ static GList * serverList = NULL; static GList * launcherList = NULL; static DbusmenuMenuitem * root_menuitem = NULL; +static DbusmenuMenuitem * status_separator = NULL; +static DbusmenuMenuitem * clear_attention = NULL; static GMainLoop * mainloop = NULL; static MessageServiceDbus * dbus_interface = NULL; @@ -797,6 +800,8 @@ menushell_foreach_cb (DbusmenuMenuitem * data_mi, gpointer data_ms) { AppMenuItem * appmenu = APP_MENU_ITEM(data_mi); if (!g_strcmp0(INDICATE_LISTENER_SERVER_DBUS_NAME((IndicateListenerServer*)msl->server), INDICATE_LISTENER_SERVER_DBUS_NAME(app_menu_item_get_server(appmenu)))) { msl->found = TRUE; + /* Return a position at the end of our shortcuts */ + msl->position += g_list_length(app_menu_item_get_items(appmenu)); } else { msl->position++; } @@ -833,9 +838,13 @@ resort_menu (DbusmenuMenuitem * menushell) guint position = 0; GList * serverentry; GList * launcherentry = launcherList; - DbusmenuMenuitem * last_separator = NULL; g_debug("Reordering Menu:"); + + if (DBUSMENU_IS_MENUITEM(status_separator)) { + position = dbusmenu_menuitem_get_position(status_separator, root_menuitem) + 1; + g_debug("\tPriming with location of status separator: %d", position); + } for (serverentry = serverList; serverentry != NULL; serverentry = serverentry->next) { serverList_t * si = (serverList_t *)serverentry->data; @@ -865,7 +874,6 @@ resort_menu (DbusmenuMenuitem * menushell) if (!launcher_menu_item_get_eclipsed(li->menuitem)) { /* Only clear the visiblity if we're not eclipsed */ dbusmenu_menuitem_property_set_bool(li->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - last_separator = li->separator; } position++; @@ -921,7 +929,6 @@ resort_menu (DbusmenuMenuitem * menushell) /* Note, this isn't the last if we can't see it */ } else { dbusmenu_menuitem_property_set_bool(si->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - last_separator = si->separator; } dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(si->separator), position); @@ -953,17 +960,15 @@ resort_menu (DbusmenuMenuitem * menushell) if (!launcher_menu_item_get_eclipsed(li->menuitem)) { /* Only clear the visiblity if we're not eclipsed */ dbusmenu_menuitem_property_set_bool(li->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - last_separator = li->separator; } position++; launcherentry = launcherentry->next; } - if (last_separator != NULL) { - dbusmenu_menuitem_property_set_bool(last_separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - } else { - g_warning("No last separator on resort"); + if (clear_attention != NULL) { + dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), clear_attention, position); + position++; /* Not needed, but reduce bugs on code tacked on here, compiler will remove */ } return; @@ -1432,13 +1437,40 @@ service_shutdown (IndicatorService * service, gpointer user_data) return; } +/* Respond to changing status by updating the icon that + is on the panel */ +static void +status_update_callback (void) +{ + return; +} + +/* The clear attention item has been clicked on, what to do? */ +static void +clear_attention_activate (DbusmenuMenuitem * mi, guint timestamp, MessageServiceDbus * dbus) +{ + message_service_dbus_set_attention(dbus, FALSE); + return; +} + +/* Handle an update of the active state to ensure that we're + only enabled when we could do something. */ +static void +clear_attention_handler (MessageServiceDbus * msd, gboolean attention, DbusmenuMenuitem * clearitem) +{ + dbusmenu_menuitem_property_set_bool(clearitem, DBUSMENU_MENUITEM_PROP_ENABLED, attention); + return; +} + /* Oh, if you don't know what main() is for we really shouldn't be talking. */ int main (int argc, char ** argv) { + /* Glib init */ g_type_init(); + /* Create the Indicator Service interface */ service = indicator_service_new_version(INDICATOR_MESSAGES_DBUS_NAME, 1); g_signal_connect(service, INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_shutdown), NULL); @@ -1448,31 +1480,57 @@ main (int argc, char ** argv) bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); textdomain (GETTEXT_PACKAGE); + /* Create the Seen DB */ seen_db_init(); + /* Bring up the service DBus interface */ dbus_interface = message_service_dbus_new(); - listener = indicate_listener_ref_default(); - serverList = NULL; - + /* Build the base menu */ root_menuitem = dbusmenu_menuitem_new(); DbusmenuServer * server = dbusmenu_server_new(INDICATOR_MESSAGES_DBUS_OBJECT); dbusmenu_server_set_root(server, root_menuitem); + /* Add status items */ + GList * statusitems = status_items_build(&status_update_callback); + while (statusitems != NULL) { + dbusmenu_menuitem_child_append(root_menuitem, DBUSMENU_MENUITEM(statusitems->data)); + statusitems = g_list_next(statusitems); + } + status_separator = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(status_separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); + dbusmenu_menuitem_child_append(root_menuitem, status_separator); + + /* Add in the clear attention item */ + clear_attention = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(clear_attention, DBUSMENU_MENUITEM_PROP_LABEL, _("Clear")); + dbusmenu_menuitem_child_append(root_menuitem, clear_attention); + g_signal_connect(G_OBJECT(dbus_interface), MESSAGE_SERVICE_DBUS_SIGNAL_ATTENTION_CHANGED, G_CALLBACK(clear_attention_handler), clear_attention); + g_signal_connect(G_OBJECT(clear_attention), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(clear_attention_activate), dbus_interface); + + /* Start up the libindicate listener */ + listener = indicate_listener_ref_default(); + serverList = NULL; + g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_INDICATOR_ADDED, G_CALLBACK(indicator_added), root_menuitem); g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_INDICATOR_REMOVED, G_CALLBACK(indicator_removed), root_menuitem); g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_SERVER_ADDED, G_CALLBACK(server_added), root_menuitem); g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_SERVER_REMOVED, G_CALLBACK(server_removed), root_menuitem); + /* Find launchers by looking through the config directories + in the idle loop */ g_idle_add(blacklist_init, NULL); g_idle_add(build_launchers, SYSTEM_APPS_DIR); g_idle_add(build_launchers, SYSTEM_APPS_DIR_OLD); gchar * userdir = g_build_filename(g_get_user_config_dir(), USER_APPS_DIR, NULL); g_idle_add(build_launchers, userdir); + /* Let's run a mainloop */ mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); + /* Clean up */ + status_items_cleanup(); g_free(userdir); return 0; diff --git a/src/messages-service.xml b/src/messages-service.xml index 8a592db..d79049e 100644 --- a/src/messages-service.xml +++ b/src/messages-service.xml @@ -3,15 +3,13 @@ <interface name="com.canonical.indicator.messages.service"> <!-- Methods --> - <method name="Watch"> - <annotation name="org.freedesktop.DBus.GLib.Async" value="true" /> - </method> <method name="AttentionRequested"> <arg type="b" name="dot" direction="out" /> </method> <method name="IconShown"> <arg type="b" name="hidden" direction="out" /> </method> + <method name="ClearAttention" /> <!-- Signals --> <signal name="AttentionChanged"> diff --git a/src/status-items.c b/src/status-items.c new file mode 100644 index 0000000..25a7aa2 --- /dev/null +++ b/src/status-items.c @@ -0,0 +1,314 @@ +/* +Code to build and maintain the status adjustment menuitems. + +Copyright 2011 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +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/>. +*/ + +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <libdbusmenu-glib/dbusmenu-glib.h> + +#include "status-items.h" +#include "status-provider.h" + +static const gchar * status_strings [STATUS_PROVIDER_STATUS_LAST] = { + /* STATUS_PROVIDER_STATUS_ONLINE, */ N_("Available"), + /* STATUS_PROVIDER_STATUS_AWAY, */ N_("Away"), + /* STATUS_PROVIDER_STATUS_DND */ N_("Busy"), + /* STATUS_PROVIDER_STATUS_INVISIBLE */ N_("Invisible"), + /* STATUS_PROVIDER_STATUS_OFFLINE, */ N_("Offline"), + /* STATUS_PROVIDER_STATUS_DISCONNECTED*/ N_("Offline") +}; + +static const gchar * status_icons[STATUS_PROVIDER_STATUS_LAST] = { + /* STATUS_PROVIDER_STATUS_ONLINE, */ "user-available", + /* STATUS_PROVIDER_STATUS_AWAY, */ "user-away", + /* STATUS_PROVIDER_STATUS_DND, */ "user-busy", + /* STATUS_PROVIDER_STATUS_INVISIBLE, */ "user-invisible", + /* STATUS_PROVIDER_STATUS_OFFLINE */ "user-offline", + /* STATUS_PROVIDER_STATUS_DISCONNECTED */ "user-offline-panel" +}; + +static const gchar * panel_icons[STATUS_PROVIDER_STATUS_LAST] = { + /* STATUS_PROVIDER_STATUS_ONLINE, */ "indicator-messages-user-available", + /* STATUS_PROVIDER_STATUS_AWAY, */ "indicator-messages-user-away", + /* STATUS_PROVIDER_STATUS_DND, */ "indicator-messages-user-busy", + /* STATUS_PROVIDER_STATUS_INVISIBLE, */ "indicator-messages-user-invisible", + /* STATUS_PROVIDER_STATUS_OFFLINE */ "indicator-messages-user-offline", + /* STATUS_PROVIDER_STATUS_DISCONNECTED */ "indicator-messages-user-disconnected" +}; + +static const gchar * panel_active_icons[STATUS_PROVIDER_STATUS_LAST] = { + /* STATUS_PROVIDER_STATUS_ONLINE, */ "indicator-messages-new-user-available", + /* STATUS_PROVIDER_STATUS_AWAY, */ "indicator-messages-new-user-away", + /* STATUS_PROVIDER_STATUS_DND, */ "indicator-messages-new-user-busy", + /* STATUS_PROVIDER_STATUS_INVISIBLE, */ "indicator-messages-new-user-invisible", + /* STATUS_PROVIDER_STATUS_OFFLINE */ "indicator-messages-new-user-offline", + /* STATUS_PROVIDER_STATUS_DISCONNECTED */ "indicator-messages-new-user-disconnected" +}; + +/* Prototypes */ +static gboolean provider_directory_parse (gpointer dir); +static gboolean load_status_provider (gpointer dir); +static void user_status_change (DbusmenuMenuitem * item, guint timestamp, gpointer pstatus); + +/* Globals */ +static StatusProviderStatus current_status = STATUS_PROVIDER_STATUS_DISCONNECTED; +static GList * menuitems = NULL; +static GList * status_providers = NULL; +static StatusUpdateFunc update_func = NULL; + +/* Build the inital status items and start kicking off the async code + for handling all the statuses */ +GList * +status_items_build (StatusUpdateFunc status_update_func) +{ + int i; + for (i = STATUS_PROVIDER_STATUS_ONLINE; i < STATUS_PROVIDER_STATUS_DISCONNECTED; i++) { + DbusmenuMenuitem * item = dbusmenu_menuitem_new(); + + dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, _(status_strings[i])); + dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_ICON_NAME, status_icons[i]); + + dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); + dbusmenu_menuitem_property_set_bool(item, DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); + + dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, DBUSMENU_MENUITEM_TOGGLE_RADIO); + dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); + + g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(user_status_change), GINT_TO_POINTER(i)); + + menuitems = g_list_append(menuitems, item); + } + + update_func = status_update_func; + + const gchar * status_providers_env = g_getenv("INDICATOR_MESSAGES_STATUS_PROVIDER_DIR"); + if (status_providers_env == NULL) { + g_idle_add(provider_directory_parse, STATUS_PROVIDER_DIR); + } else { + g_idle_add(provider_directory_parse, (gpointer)status_providers_env); + } + + return menuitems; +} + +/* Clean up our globals and stop with all this allocation + of memory */ +void +status_items_cleanup (void) +{ + while (status_providers != NULL) { + StatusProvider * sprovider = STATUS_PROVIDER(status_providers->data); + g_object_unref(sprovider); + status_providers = g_list_remove(status_providers, sprovider); + } + + return; +} + +/* Get the icon that should be shown on the panel */ +const gchar * +status_current_panel_icon (gboolean alert) +{ + if (alert) { + return panel_active_icons[current_status]; + } else { + return panel_icons[current_status]; + } +} + +/* Update status from all the providers */ +static void +update_status (void) +{ + StatusProviderStatus status = STATUS_PROVIDER_STATUS_DISCONNECTED; + GList * provider; + + for (provider = status_providers; provider != NULL; provider = g_list_next(provider)) { + StatusProviderStatus localstatus = status_provider_get_status(STATUS_PROVIDER(provider->data)); + + if (localstatus < status) { + status = localstatus; + } + } + + if (status == current_status) { + return; + } + + current_status = status; + + if (update_func != NULL) { + update_func(); + } + + GList * menu; + int i; + for (menu = menuitems, i = 0; menu != NULL && i < STATUS_PROVIDER_STATUS_DISCONNECTED; menu = g_list_next(menu), i++) { + /* If we're the seleced status or if we're disconnected + show the user that we're offline */ + if (i == current_status || (current_status == STATUS_PROVIDER_STATUS_DISCONNECTED && i == STATUS_PROVIDER_STATUS_OFFLINE)) { + dbusmenu_menuitem_property_set_int(DBUSMENU_MENUITEM(menu->data), DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED); + } else { + dbusmenu_menuitem_property_set_int(DBUSMENU_MENUITEM(menu->data), DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); + } + + if (current_status == STATUS_PROVIDER_STATUS_DISCONNECTED) { + dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(menu->data), DBUSMENU_MENUITEM_PROP_ENABLED, FALSE); + } else { + dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(menu->data), DBUSMENU_MENUITEM_PROP_ENABLED, TRUE); + } + } + + return; +} + +/* Handle the user requesting a status change */ +static void +user_status_change (DbusmenuMenuitem * item, guint timestamp, gpointer pstatus) +{ + StatusProviderStatus status = GPOINTER_TO_INT(pstatus); + GList * provider; + + /* Set each provider to this status */ + for (provider = status_providers; provider != NULL; provider = g_list_next(provider)) { + status_provider_set_status(STATUS_PROVIDER(provider->data), status); + } + + /* See what we really are now */ + update_status(); + return; +} + +/* Start parsing a directory and setting up the entires in the idle loop */ +static gboolean +provider_directory_parse (gpointer directory) +{ + const gchar * dirname = (const gchar *)directory; + g_debug("Looking for status providers in: %s", dirname); + + if (!g_file_test(dirname, G_FILE_TEST_EXISTS)) { + return FALSE; + } + + GDir * dir = g_dir_open(dirname, 0, NULL); + if (dir == NULL) { + return FALSE; + } + + const gchar * name; + while ((name = g_dir_read_name(dir)) != NULL) { + if (!g_str_has_suffix(name, G_MODULE_SUFFIX)) { + continue; + } + + gchar * fullname = g_build_filename(dirname, name, NULL); + g_idle_add(load_status_provider, fullname); + } + + g_dir_close(dir); + + return FALSE; +} + +/* Close the module as an idle function so that we know + it's all cleaned up */ +static gboolean +module_destroy_in_idle_helper (gpointer data) +{ + GModule * module = (GModule *)data; + if (module != NULL) { + g_debug("Unloading module: %s", g_module_name(module)); + g_module_close(module); + } + return FALSE; +} + +/* Set up an idle function to close the module */ +static void +module_destroy_in_idle (gpointer data) +{ + g_idle_add_full(G_PRIORITY_LOW, module_destroy_in_idle_helper, data, NULL); + return; +} + +/* Load a particular status provider */ +static gboolean +load_status_provider (gpointer dir) +{ + gchar * provider = (gchar *)dir; + + if (!g_file_test(provider, G_FILE_TEST_EXISTS)) { + goto exit_final; + } + + g_debug("Loading status provider: %s", provider); + + GModule * module; + + module = g_module_open(provider, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + if (module == NULL) { + g_warning("Unable to module for: %s", provider); + goto exit_module_fail; + } + + /* Got it */ + GType (*type_func) (void); + if (!g_module_symbol(module, STATUS_PROVIDER_EXPORT_S, (gpointer *)&type_func)) { + g_warning("Unable to find type symbol in: %s", provider); + goto exit_module_fail; + } + + GType provider_type = type_func(); + if (provider_type == 0) { + g_warning("Unable to create type from: %s", provider); + goto exit_module_fail; + } + + StatusProvider * sprovider = STATUS_PROVIDER(g_object_new(provider_type, NULL)); + if (sprovider == NULL) { + g_warning("Unable to build provider from: %s", provider); + goto exit_module_fail; + } + + /* On update let's talk to all of them and create the aggregate + value to export */ + g_signal_connect(G_OBJECT(sprovider), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED, G_CALLBACK(update_status), NULL); + + /* Attach the module object to the status provider so + that when the status provider is free'd the module + is close automatically. */ + g_object_set_data_full(G_OBJECT(sprovider), "status-provider-module", module, module_destroy_in_idle); + + status_providers = g_list_prepend(status_providers, sprovider); + + /* Force and update every time just so we know we're + in a consistent state*/ + update_status(); + + goto exit_final; + +exit_module_fail: + g_module_close(module); + +exit_final: + g_free(provider); + return FALSE; +} diff --git a/src/status-items.h b/src/status-items.h new file mode 100644 index 0000000..fe7900c --- /dev/null +++ b/src/status-items.h @@ -0,0 +1,38 @@ +/* +Code to build and maintain the status adjustment menuitems. + +Copyright 2011 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +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/>. +*/ + +#ifndef __STATUS_ITEMS_H__ +#define __STATUS_ITEMS_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +typedef void (*StatusUpdateFunc) (void); + +GList * status_items_build (StatusUpdateFunc update_func); +const gchar * status_current_panel_icon (gboolean alert); +void status_items_cleanup (void); + +G_END_DECLS + +#endif /* __STATUS_ITEMS_H__ */ + diff --git a/src/status-provider-emesene.c b/src/status-provider-emesene.c new file mode 100644 index 0000000..5aa6698 --- /dev/null +++ b/src/status-provider-emesene.c @@ -0,0 +1,348 @@ +/* +A small wrapper utility to load indicators and put them as menu items +into the gnome-panel using it's applet interface. + +Copyright 2011 Canonical Ltd. + +Authors: + Stefano Candori <stefano.candori@gmail.com> + +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/>. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "status-provider.h" +#include "status-provider-emesene.h" + +#include <dbus/dbus-glib.h> + +typedef enum { + EM_STATUS_ONLINE, + EM_STATUS_INVISIBLE, + EM_STATUS_BUSY, + EM_STATUS_AWAY, + EM_STATUS_IDLE, + EM_STATUS_OFFLINE +} em_status_t; + +static const StatusProviderStatus em_to_sp_map[] = { + /* EM_STATUS_ONLINE, */ STATUS_PROVIDER_STATUS_ONLINE, + /* EM_STATUS_INVISIBLE, */ STATUS_PROVIDER_STATUS_INVISIBLE, + /* EM_STATUS_BUSY, */ STATUS_PROVIDER_STATUS_DND, + /* EM_STATUS_AWAY, */ STATUS_PROVIDER_STATUS_AWAY, + /* EM_STATUS_IDLE, */ STATUS_PROVIDER_STATUS_AWAY, + /* EM_STATUS_OFFLINE, */ STATUS_PROVIDER_STATUS_OFFLINE +}; + +static const em_status_t sp_to_em_map[STATUS_PROVIDER_STATUS_LAST] = { + /* STATUS_PROVIDER_STATUS_ONLINE, */ EM_STATUS_ONLINE, + /* STATUS_PROVIDER_STATUS_AWAY, */ EM_STATUS_AWAY, + /* STATUS_PROVIDER_STATUS_DND */ EM_STATUS_BUSY, + /* STATUS_PROVIDER_STATUS_INVISIBLE*/ EM_STATUS_INVISIBLE, + /* STATUS_PROVIDER_STATUS_OFFLINE */ EM_STATUS_OFFLINE, + /* STATUS_PROVIDER_STATUS_DISCONNECTED*/ EM_STATUS_OFFLINE +}; + +typedef struct _StatusProviderEmesenePrivate StatusProviderEmesenePrivate; +struct _StatusProviderEmesenePrivate { + DBusGProxy * proxy; + DBusGProxy * dbus_proxy; + em_status_t em_status; +}; + +#define STATUS_PROVIDER_EMESENE_GET_PRIVATE(o) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((o), STATUS_PROVIDER_EMESENE_TYPE, StatusProviderEmesenePrivate)) + +/* Prototypes */ +/* GObject stuff */ +static void status_provider_emesene_class_init (StatusProviderEmeseneClass *klass); +static void status_provider_emesene_init (StatusProviderEmesene *self); +static void status_provider_emesene_dispose (GObject *object); +static void status_provider_emesene_finalize (GObject *object); +/* Internal Funcs */ +static void set_status (StatusProvider * sp, StatusProviderStatus status); +static StatusProviderStatus get_status (StatusProvider * sp); +static void setup_emesene_proxy (StatusProviderEmesene * self); +static void dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderEmesene * self); + +G_DEFINE_TYPE (StatusProviderEmesene, status_provider_emesene, STATUS_PROVIDER_TYPE); + +STATUS_PROVIDER_EXPORT_TYPE(STATUS_PROVIDER_EMESENE_TYPE) + +static void +status_provider_emesene_class_init (StatusProviderEmeseneClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (StatusProviderEmesenePrivate)); + + object_class->dispose = status_provider_emesene_dispose; + object_class->finalize = status_provider_emesene_finalize; + + StatusProviderClass * spclass = STATUS_PROVIDER_CLASS(klass); + + spclass->set_status = set_status; + spclass->get_status = get_status; + + return; +} + +static void +status_cb (DBusGProxy * proxy, DBusGProxyCall * call, gpointer userdata) +{ + StatusProviderEmesene * spe = STATUS_PROVIDER_EMESENE(userdata); + StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(spe); + + GError * error = NULL; + gint status = 0; + if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_INT, &status, G_TYPE_INVALID)) { + g_warning("Unable to get status from Emesene: %s", error->message); + g_error_free(error); + return; + } + + priv->em_status = status; + g_signal_emit(G_OBJECT(spe), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, em_to_sp_map[priv->em_status], TRUE); + return; +} + +static void +changed_status (DBusGProxy * proxy, gint status, StatusProviderEmesene * spe) +{ + StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(spe); + g_debug("Emesene changed status to %d", status); + priv->em_status = status; + g_signal_emit(G_OBJECT(spe), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, em_to_sp_map[priv->em_status], TRUE); + return; +} + +static void +proxy_destroy (DBusGProxy * proxy, StatusProviderEmesene * spe) +{ + StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(spe); + + priv->proxy = NULL; + priv->em_status = EM_STATUS_OFFLINE; + + g_signal_emit(G_OBJECT(spe), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, em_to_sp_map[priv->em_status], TRUE); + return; +} + +static void +status_provider_emesene_init (StatusProviderEmesene *self) +{ + StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(self); + + priv->proxy = NULL; + priv->em_status = EM_STATUS_OFFLINE; + + DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + g_return_if_fail(bus != NULL); /* Can't do anymore DBus stuff without this, + all non-DBus stuff should be done */ + + GError * error = NULL; + + /* Set up the dbus Proxy */ + priv->dbus_proxy = dbus_g_proxy_new_for_name_owner (bus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + &error); + if (error != NULL) { + g_warning("Unable to connect to DBus events: %s", error->message); + g_error_free(error); + return; + } + + /* Configure the name owner changing */ + dbus_g_proxy_add_signal(priv->dbus_proxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal(priv->dbus_proxy, "NameOwnerChanged", + G_CALLBACK(dbus_namechange), + self, NULL); + + setup_emesene_proxy(self); + + return; +} + +/* Watch to see if the Emesene comes up on Dbus */ +static void +dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderEmesene * self) +{ + g_return_if_fail(name != NULL); + g_return_if_fail(new != NULL); + + if (g_strcmp0(name, "org.emesene.Service") == 0) { + setup_emesene_proxy(self); + } + return; +} + +/* Setup the Emesene proxy so that we can talk to it + and get signals from it. */ +static void +setup_emesene_proxy (StatusProviderEmesene * self) +{ + StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(self); + + if (priv->proxy != NULL) { + g_debug("Doh!We were asked to set up a Emesene proxy when we already had one."); + return; + } + + DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + g_return_if_fail(bus != NULL); /* Can't do anymore DBus stuff without this, + all non-DBus stuff should be done */ + + GError * error = NULL; + + /* Set up the Emesene Proxy */ + priv->proxy = dbus_g_proxy_new_for_name_owner (bus, + "org.emesene.Service", + "/org/emesene/Service", + "org.emesene.Service", + &error); + /* Report any errors */ + if (error != NULL) { + g_debug("Unable to get Emesene proxy: %s", error->message); + g_error_free(error); + } + + /* If we have a proxy, let's start using it */ + if (priv->proxy != NULL) { + /* Set the proxy to NULL if it's destroyed */ + g_object_add_weak_pointer (G_OBJECT(priv->proxy), (gpointer *)&priv->proxy); + /* If it's destroyed, let's clean up as well */ + g_signal_connect(G_OBJECT(priv->proxy), "destroy", + G_CALLBACK(proxy_destroy), self); + + /* Watching for the status change coming from the + Emesene side of things. */ + g_debug("Adding Emesene Signals"); + dbus_g_proxy_add_signal (priv->proxy, + "status_changed", + G_TYPE_INT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal(priv->proxy, + "status_changed", + G_CALLBACK(changed_status), + (void *)self, + NULL); + + /* Get the current status to update our cached + value of the status. */ + dbus_g_proxy_begin_call(priv->proxy, + "get_status", + status_cb, + self, + NULL, + G_TYPE_INVALID); + } + + return; +} + +static void +status_provider_emesene_dispose (GObject *object) +{ + StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(object); + + if (priv->proxy != NULL) { + g_object_unref(priv->proxy); + priv->proxy = NULL; + } + + G_OBJECT_CLASS (status_provider_emesene_parent_class)->dispose (object); + return; +} + +static void +status_provider_emesene_finalize (GObject *object) +{ + + G_OBJECT_CLASS (status_provider_emesene_parent_class)->finalize (object); + return; +} + +/** + status_provider_emesene_new: + + Creates a new #StatusProviderEmesene object. No parameters or anything + like that. Just a convience function. + + Return value: A new instance of #StatusProviderEmesene +*/ +StatusProvider * +status_provider_emesene_new (void) +{ + return STATUS_PROVIDER(g_object_new(STATUS_PROVIDER_EMESENE_TYPE, NULL)); +} + +/* Takes the status provided generically for Status providers + and turns it into a Emesene status and sends it to Emesene. */ +static void +set_status (StatusProvider * sp, StatusProviderStatus status) +{ + g_return_if_fail(IS_STATUS_PROVIDER_EMESENE(sp)); + StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(sp); + + g_debug("Emesene set status to %d", status); + if (priv->proxy == NULL) { + return; + } + + priv->em_status = sp_to_em_map[status]; + + gboolean ret = FALSE; + GError * error = NULL; + + ret = dbus_g_proxy_call(priv->proxy, + "set_status", &error, + G_TYPE_INT, priv->em_status, + G_TYPE_INVALID, + G_TYPE_INVALID); + + if (!ret) { + if (error != NULL) { + g_warning("Emesene unable to change to status: %s", error->message); + g_error_free(error); + } else { + g_warning("Emesene unable to change to status"); + } + error = NULL; + } + + g_signal_emit(G_OBJECT(sp), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, em_to_sp_map[priv->em_status], TRUE); + return; +} + +/* Takes the cached Emesene status and makes it into the generic + Status provider status. If there is no Emesene proxy then it + returns the disconnected state. */ +static StatusProviderStatus +get_status (StatusProvider * sp) +{ + g_return_val_if_fail(IS_STATUS_PROVIDER_EMESENE(sp), STATUS_PROVIDER_STATUS_DISCONNECTED); + StatusProviderEmesenePrivate * priv = STATUS_PROVIDER_EMESENE_GET_PRIVATE(sp); + + if (priv->proxy == NULL) { + return STATUS_PROVIDER_STATUS_DISCONNECTED; + } + + return em_to_sp_map[priv->em_status]; +} diff --git a/src/status-provider-emesene.h b/src/status-provider-emesene.h new file mode 100644 index 0000000..2309684 --- /dev/null +++ b/src/status-provider-emesene.h @@ -0,0 +1,56 @@ +/* +A small wrapper utility to load indicators and put them as menu items +into the gnome-panel using it's applet interface. + +Copyright 20011 Canonical Ltd. + +Authors: + Stefano Candori <stefano.candori@gmail.com> + +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/>. +*/ + +#ifndef __STATUS_PROVIDER_EMESENE_H__ +#define __STATUS_PROVIDER_EMESENE_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "status-provider.h" + +G_BEGIN_DECLS + +#define STATUS_PROVIDER_EMESENE_TYPE (status_provider_emesene_get_type ()) +#define STATUS_PROVIDER_EMESENE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_PROVIDER_EMESENE_TYPE, StatusProviderEmesene)) +#define STATUS_PROVIDER_EMESENE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_PROVIDER_EMESENE_TYPE, StatusProviderEmeseneClass)) +#define IS_STATUS_PROVIDER_EMESENE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_PROVIDER_EMESENE_TYPE)) +#define IS_STATUS_PROVIDER_EMESENE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_PROVIDER_EMESENE_TYPE)) +#define STATUS_PROVIDER_EMESENE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_PROVIDER_EMESENE_TYPE, StatusProviderEmeseneClass)) + + +typedef struct _StatusProviderEmeseneClass StatusProviderEmeseneClass; +struct _StatusProviderEmeseneClass { + StatusProviderClass parent_class; +}; + +typedef struct _StatusProviderEmesene StatusProviderEmesene; +struct _StatusProviderEmesene { + StatusProvider parent; +}; + +GType status_provider_emesene_get_type (void); +StatusProvider * status_provider_emesene_new (void); + +G_END_DECLS + +#endif diff --git a/src/status-provider-mc5.c b/src/status-provider-mc5.c new file mode 100644 index 0000000..e70496c --- /dev/null +++ b/src/status-provider-mc5.c @@ -0,0 +1,308 @@ +/* +A small wrapper utility to load indicators and put them as menu items +into the gnome-panel using it's applet interface. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +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/>. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <telepathy-glib/account-manager.h> + +#include "status-provider.h" +#include "status-provider-mc5.h" +#include "status-provider-mc5-marshal.h" + +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-bindings.h> + +static gchar * sp_to_mc_map[STATUS_PROVIDER_STATUS_LAST] = { + /* STATUS_PROVIDER_STATUS_ONLINE, */ "available", + /* STATUS_PROVIDER_STATUS_AWAY, */ "away", + /* STATUS_PROVIDER_STATUS_DND */ "busy", + /* STATUS_PROVIDER_STATUS_INVISIBLE*/ "hidden", + /* STATUS_PROVIDER_STATUS_OFFLINE */ "offline", + /* STATUS_PROVIDER_STATUS_DISCONNECTED*/NULL +}; + +static TpConnectionPresenceType sp_to_tp_map[STATUS_PROVIDER_STATUS_LAST] = { + /* STATUS_PROVIDER_STATUS_ONLINE, */ TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, + /* STATUS_PROVIDER_STATUS_AWAY, */ TP_CONNECTION_PRESENCE_TYPE_AWAY, + /* STATUS_PROVIDER_STATUS_DND */ TP_CONNECTION_PRESENCE_TYPE_BUSY, + /* STATUS_PROVIDER_STATUS_INVISIBLE*/ TP_CONNECTION_PRESENCE_TYPE_HIDDEN, + /* STATUS_PROVIDER_STATUS_OFFLINE */ TP_CONNECTION_PRESENCE_TYPE_OFFLINE, + /* STATUS_PROVIDER_STATUS_DISCONNECTED*/ TP_CONNECTION_PRESENCE_TYPE_UNSET +}; + +static StatusProviderStatus tp_to_sp_map[TP_CONNECTION_PRESENCE_TYPE_ERROR + 1] = { + /* TP_CONNECTION_PRESENCE_TYPE_UNSET */ STATUS_PROVIDER_STATUS_DISCONNECTED, + /* TP_CONNECTION_PRESENCE_TYPE_OFFLINE */ STATUS_PROVIDER_STATUS_OFFLINE, + /* TP_CONNECTION_PRESENCE_TYPE_AVAILABLE */ STATUS_PROVIDER_STATUS_ONLINE, + /* TP_CONNECTION_PRESENCE_TYPE_AWAY */ STATUS_PROVIDER_STATUS_AWAY, + /* TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY */ STATUS_PROVIDER_STATUS_AWAY, + /* TP_CONNECTION_PRESENCE_TYPE_HIDDEN */ STATUS_PROVIDER_STATUS_INVISIBLE, + /* TP_CONNECTION_PRESENCE_TYPE_BUSY */ STATUS_PROVIDER_STATUS_DND, + /* TP_CONNECTION_PRESENCE_TYPE_UNKNOWN */ STATUS_PROVIDER_STATUS_DISCONNECTED, + /* TP_CONNECTION_PRESENCE_TYPE_ERROR */ STATUS_PROVIDER_STATUS_DISCONNECTED +}; + +typedef struct _StatusProviderMC5Private StatusProviderMC5Private; +struct _StatusProviderMC5Private { + TpAccountManager * manager; + StatusProviderStatus status; + DBusGProxy * dbus_proxy; +}; + +#define STATUS_PROVIDER_MC5_GET_PRIVATE(o) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((o), STATUS_PROVIDER_MC5_TYPE, StatusProviderMC5Private)) +#define MC5_WELL_KNOWN_NAME "org.freedesktop.Telepathy.AccountManager" + +/* Prototypes */ +/* GObject stuff */ +static void status_provider_mc5_class_init (StatusProviderMC5Class *klass); +static void status_provider_mc5_init (StatusProviderMC5 *self); +static void status_provider_mc5_dispose (GObject *object); +static void status_provider_mc5_finalize (GObject *object); +/* Internal Funcs */ +static void set_status (StatusProvider * sp, StatusProviderStatus status); +static StatusProviderStatus get_status (StatusProvider * sp); +static void presence_changed (TpAccountManager * eam, guint type, const gchar * type_str, const gchar * message, StatusProviderMC5 * sp); +static void dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderMC5 * self); +static void mc5_exists_cb (DBusGProxy * proxy, gboolean exists, GError * error, gpointer userdata); + +G_DEFINE_TYPE (StatusProviderMC5, status_provider_mc5, STATUS_PROVIDER_TYPE); + +STATUS_PROVIDER_EXPORT_TYPE(STATUS_PROVIDER_MC5_TYPE) + +/* Create the class. We over ride a few functions but nothing + really shocking. Most interesting is the set and get status. */ +static void +status_provider_mc5_class_init (StatusProviderMC5Class *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (StatusProviderMC5Private)); + + object_class->dispose = status_provider_mc5_dispose; + object_class->finalize = status_provider_mc5_finalize; + + StatusProviderClass * spclass = STATUS_PROVIDER_CLASS(klass); + + spclass->set_status = set_status; + spclass->get_status = get_status; + + return; +} + +/* Build our telepathy account manager instance if we don't + have one. */ +static void +build_eam (StatusProviderMC5 * self) +{ + StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(self); + + if (priv->manager != NULL) { + return; + } + + priv->manager = tp_account_manager_dup(); + g_signal_connect(G_OBJECT(priv->manager), "most-available-presence-changed", G_CALLBACK(presence_changed), self); + + return; +} + +/* Creating an instance of the status provider. We set the variables + and create an TpAccountManager object. It does all the hard + work in this module of tracking MissionControl and enumerating the + accounts and all that jazz. */ +static void +status_provider_mc5_init (StatusProviderMC5 *self) +{ + StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(self); + + priv->status = STATUS_PROVIDER_STATUS_DISCONNECTED; + priv->manager = NULL; + + DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + g_return_if_fail(bus != NULL); /* Can't do anymore DBus stuff without this, + all non-DBus stuff should be done */ + + GError * error = NULL; + + /* Set up the dbus Proxy */ + priv->dbus_proxy = dbus_g_proxy_new_for_name_owner (bus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + &error); + if (error != NULL) { + g_warning("Unable to connect to DBus events: %s", error->message); + g_error_free(error); + return; + } + + /* Configure the name owner changing */ + dbus_g_proxy_add_signal(priv->dbus_proxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal(priv->dbus_proxy, "NameOwnerChanged", + G_CALLBACK(dbus_namechange), + self, NULL); + + org_freedesktop_DBus_name_has_owner_async(priv->dbus_proxy, MC5_WELL_KNOWN_NAME, mc5_exists_cb, self); + + return; +} + +/* Unref the account manager and move on. Sadly, we're + leaving the show. */ +static void +status_provider_mc5_dispose (GObject *object) +{ + StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(object); + + if (priv->manager != NULL) { + g_object_unref(priv->manager); + priv->manager = NULL; + } + + if (priv->dbus_proxy != NULL) { + g_object_unref(priv->dbus_proxy); + priv->dbus_proxy = NULL; + } + + G_OBJECT_CLASS (status_provider_mc5_parent_class)->dispose (object); + return; +} + +/* Pass to superclass */ +static void +status_provider_mc5_finalize (GObject *object) +{ + + G_OBJECT_CLASS (status_provider_mc5_parent_class)->finalize (object); + return; +} + +/* Watch for MC5 Coming on and off the bus. */ +static void +dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderMC5 * self) +{ + /* g_debug("DBUS NAMECHANGE: %s %s %s", name, prev, new); */ + + if (prev[0] == '\0' && g_strcmp0(name, MC5_WELL_KNOWN_NAME) == 0) { + g_debug("MC5 Coming online"); + build_eam(self); + } + if (new[0] == '\0' && g_strcmp0(name, MC5_WELL_KNOWN_NAME) == 0) { + g_debug("MC5 going offline"); + StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(self); + if (priv->manager != NULL) { + g_object_unref(priv->manager); + priv->manager = NULL; + } + + priv->status = STATUS_PROVIDER_STATUS_DISCONNECTED; + g_signal_emit(G_OBJECT(self), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, priv->status, TRUE); + } + + return; +} + +/* Callback for the Dbus command to do HasOwner on + the MC5 service. If it exists, we want to have an + account manager. */ +static void +mc5_exists_cb (DBusGProxy * proxy, gboolean exists, GError * error, gpointer userdata) +{ + if (error) { + g_warning("Unable to check if MC5 is running: %s", error->message); + return; + } + + if (exists) { + build_eam(STATUS_PROVIDER_MC5(userdata)); + } + + return; +} + +/** + status_provider_mc5_new: + + Creates a new #StatusProviderMC5 object. No parameters or anything + like that. Just a convience function. + + Return value: A new instance of #StatusProviderMC5 +*/ +StatusProvider * +status_provider_mc5_new (void) +{ + return STATUS_PROVIDER(g_object_new(STATUS_PROVIDER_MC5_TYPE, NULL)); +} + +/* Setting the status in the empathy account manager. We're + basically requesting a global status. This may or may not + get applied to all accounts. It's really the best we can + hope to do. */ +static void +set_status (StatusProvider * sp, StatusProviderStatus status) +{ + StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(sp); + + build_eam(STATUS_PROVIDER_MC5(sp)); + tp_account_manager_set_all_requested_presences(priv->manager, sp_to_tp_map[status], sp_to_mc_map[status], ""); + + return; +} + +/* Gets the status, uses the cached value that we have. Asking + would just be painful. */ +static StatusProviderStatus +get_status (StatusProvider * sp) +{ + g_return_val_if_fail(IS_STATUS_PROVIDER_MC5(sp), STATUS_PROVIDER_STATUS_DISCONNECTED); + StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(sp); + + if (priv->manager == NULL) { + return STATUS_PROVIDER_STATUS_DISCONNECTED; + } + + return priv->status; +} + +/* A signal handler for when the TpAccountManager believes + that the global status has changed. It roughly calculates this + by finding the most available of all accounts that are active. */ +static void +presence_changed (TpAccountManager * eam, guint type, const gchar * type_str, const gchar * message, StatusProviderMC5 * sp) +{ + StatusProviderMC5Private * priv = STATUS_PROVIDER_MC5_GET_PRIVATE(sp); + + g_debug("MC5 Status changed: %d %s %s", type, type_str, message); + + if (priv->status != tp_to_sp_map[type]) { + priv->status = tp_to_sp_map[type]; + g_signal_emit(G_OBJECT(sp), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, priv->status, TRUE); + } + + return; +} + diff --git a/src/status-provider-mc5.h b/src/status-provider-mc5.h new file mode 100644 index 0000000..4d5659d --- /dev/null +++ b/src/status-provider-mc5.h @@ -0,0 +1,56 @@ +/* +A small wrapper utility to load indicators and put them as menu items +into the gnome-panel using it's applet interface. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +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/>. +*/ + +#ifndef __STATUS_PROVIDER_MC5_H__ +#define __STATUS_PROVIDER_MC5_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "status-provider.h" + +G_BEGIN_DECLS + +#define STATUS_PROVIDER_MC5_TYPE (status_provider_mc5_get_type ()) +#define STATUS_PROVIDER_MC5(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_PROVIDER_MC5_TYPE, StatusProviderMC5)) +#define STATUS_PROVIDER_MC5_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_PROVIDER_MC5_TYPE, StatusProviderMC5Class)) +#define IS_STATUS_PROVIDER_MC5(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_PROVIDER_MC5_TYPE)) +#define IS_STATUS_PROVIDER_MC5_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_PROVIDER_MC5_TYPE)) +#define STATUS_PROVIDER_MC5_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_PROVIDER_MC5_TYPE, StatusProviderMC5Class)) + + +typedef struct _StatusProviderMC5Class StatusProviderMC5Class; +struct _StatusProviderMC5Class { + StatusProviderClass parent_class; +}; + +typedef struct _StatusProviderMC5 StatusProviderMC5; +struct _StatusProviderMC5 { + StatusProvider parent; +}; + +GType status_provider_mc5_get_type (void); +StatusProvider * status_provider_mc5_new (void); + +G_END_DECLS + +#endif diff --git a/src/status-provider-mc5.list b/src/status-provider-mc5.list new file mode 100644 index 0000000..5ab45bf --- /dev/null +++ b/src/status-provider-mc5.list @@ -0,0 +1 @@ +VOID:UINT,STRING diff --git a/src/status-provider-pidgin.c b/src/status-provider-pidgin.c new file mode 100644 index 0000000..6c73d56 --- /dev/null +++ b/src/status-provider-pidgin.c @@ -0,0 +1,433 @@ +/* +A small wrapper utility to load indicators and put them as menu items +into the gnome-panel using it's applet interface. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +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/>. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "status-provider.h" +#include "status-provider-pidgin.h" +#include "status-provider-pidgin-marshal.h" + +#include <dbus/dbus-glib.h> + +typedef enum { + PG_STATUS_UNKNOWN, + PG_STATUS_OFFLINE, + PG_STATUS_AVAILABLE, + PG_STATUS_UNAVAILABLE, + PG_STATUS_INVISIBLE, + PG_STATUS_AWAY, + PG_STATUS_EXTENDEND_AWAY, + PG_STATUS_MOBILE, + PG_STATUS_TUNE +} pg_status_t; + +static const StatusProviderStatus pg_to_sp_map[] = { + /* PG_STATUS_UNKNOWN, */ STATUS_PROVIDER_STATUS_OFFLINE, + /* PG_STATUS_OFFLINE, */ STATUS_PROVIDER_STATUS_OFFLINE, + /* PG_STATUS_AVAILABLE, */ STATUS_PROVIDER_STATUS_ONLINE, + /* PG_STATUS_UNAVAILABLE, */ STATUS_PROVIDER_STATUS_DND, + /* PG_STATUS_INVISIBLE, */ STATUS_PROVIDER_STATUS_INVISIBLE, + /* PG_STATUS_AWAY, */ STATUS_PROVIDER_STATUS_AWAY, + /* PG_STATUS_EXTENDEND_AWAY, */ STATUS_PROVIDER_STATUS_AWAY, + /* PG_STATUS_MOBILE, */ STATUS_PROVIDER_STATUS_OFFLINE, + /* PG_STATUS_TUNE */ STATUS_PROVIDER_STATUS_OFFLINE +}; + +static const pg_status_t sp_to_pg_map[STATUS_PROVIDER_STATUS_LAST] = { + /* STATUS_PROVIDER_STATUS_ONLINE, */ PG_STATUS_AVAILABLE, + /* STATUS_PROVIDER_STATUS_AWAY, */ PG_STATUS_AWAY, + /* STATUS_PROVIDER_STATUS_DND */ PG_STATUS_UNAVAILABLE, + /* STATUS_PROVIDER_STATUS_INVISIBLE*/ PG_STATUS_INVISIBLE, + /* STATUS_PROVIDER_STATUS_OFFLINE */ PG_STATUS_OFFLINE, + /* STATUS_PROVIDER_STATUS_DISCONNECTED*/ PG_STATUS_OFFLINE +}; + +typedef struct _StatusProviderPidginPrivate StatusProviderPidginPrivate; +struct _StatusProviderPidginPrivate { + DBusGProxy * proxy; + DBusGProxy * dbus_proxy; + pg_status_t pg_status; +}; + +#define STATUS_PROVIDER_PIDGIN_GET_PRIVATE(o) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((o), STATUS_PROVIDER_PIDGIN_TYPE, StatusProviderPidginPrivate)) + +/* Prototypes */ +/* GObject stuff */ +static void status_provider_pidgin_class_init (StatusProviderPidginClass *klass); +static void status_provider_pidgin_init (StatusProviderPidgin *self); +static void status_provider_pidgin_dispose (GObject *object); +static void status_provider_pidgin_finalize (GObject *object); +/* Internal Funcs */ +static void set_status (StatusProvider * sp, StatusProviderStatus status); +static StatusProviderStatus get_status (StatusProvider * sp); +static void setup_pidgin_proxy (StatusProviderPidgin * self); +static void dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderPidgin * self); + +G_DEFINE_TYPE (StatusProviderPidgin, status_provider_pidgin, STATUS_PROVIDER_TYPE); + +STATUS_PROVIDER_EXPORT_TYPE(STATUS_PROVIDER_PIDGIN_TYPE) + +static void +status_provider_pidgin_class_init (StatusProviderPidginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (StatusProviderPidginPrivate)); + + object_class->dispose = status_provider_pidgin_dispose; + object_class->finalize = status_provider_pidgin_finalize; + + StatusProviderClass * spclass = STATUS_PROVIDER_CLASS(klass); + + spclass->set_status = set_status; + spclass->get_status = get_status; + + return; +} + +static void +type_cb (DBusGProxy * proxy, DBusGProxyCall * call, gpointer userdata) +{ + GError * error = NULL; + gint status = 0; + if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_INT, &status, G_TYPE_INVALID)) { + g_warning("Unable to get type from Pidgin: %s", error->message); + g_error_free(error); + return; + } + + StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(userdata); + if (status != priv->pg_status) { + priv->pg_status = status; + + g_signal_emit(G_OBJECT(userdata), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, pg_to_sp_map[priv->pg_status], TRUE); + } + + return; +} + +static void +saved_status_to_type (StatusProviderPidgin * spp, gint savedstatus) +{ + StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(spp); + + g_debug("Pidgin figuring out type for %d", savedstatus); + dbus_g_proxy_begin_call(priv->proxy, + "PurpleSavedstatusGetType", type_cb, spp, NULL, + G_TYPE_INT, savedstatus, G_TYPE_INVALID); + + return; +} + +static void +savedstatus_cb (DBusGProxy * proxy, DBusGProxyCall * call, gpointer userdata) +{ + GError * error = NULL; + gint status = 0; + if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_INT, &status, G_TYPE_INVALID)) { + g_warning("Unable to get saved status from Pidgin: %s", error->message); + g_error_free(error); + return; + } + + saved_status_to_type(STATUS_PROVIDER_PIDGIN(userdata), status); + return; +} + + +static void +changed_status (DBusGProxy * proxy, gint savedstatus, GError ** error, StatusProviderPidgin * spp) +{ + saved_status_to_type(spp, savedstatus); + return; +} + +static void +proxy_destroy (DBusGProxy * proxy, StatusProviderPidgin * spp) +{ + StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(spp); + + priv->proxy = NULL; + priv->pg_status = PG_STATUS_OFFLINE; + + g_signal_emit(G_OBJECT(spp), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, pg_to_sp_map[priv->pg_status], TRUE); + return; +} + +static void +status_provider_pidgin_init (StatusProviderPidgin *self) +{ + StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(self); + + priv->proxy = NULL; + priv->pg_status = PG_STATUS_OFFLINE; + + DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + g_return_if_fail(bus != NULL); /* Can't do anymore DBus stuff without this, + all non-DBus stuff should be done */ + + GError * error = NULL; + + /* Set up the dbus Proxy */ + priv->dbus_proxy = dbus_g_proxy_new_for_name_owner (bus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + &error); + if (error != NULL) { + g_warning("Unable to connect to DBus events: %s", error->message); + g_error_free(error); + return; + } + + /* Configure the name owner changing */ + dbus_g_proxy_add_signal(priv->dbus_proxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal(priv->dbus_proxy, "NameOwnerChanged", + G_CALLBACK(dbus_namechange), + self, NULL); + + setup_pidgin_proxy(self); + + return; +} + +/* Watch to see if the Pidgin comes up on Dbus */ +static void +dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderPidgin * self) +{ + g_return_if_fail(name != NULL); + g_return_if_fail(new != NULL); + + if (g_strcmp0(name, "im.pidgin.purple.PurpleService") == 0) { + setup_pidgin_proxy(self); + } + return; +} + +/* Setup the Pidgin proxy so that we can talk to it + and get signals from it. */ +static void +setup_pidgin_proxy (StatusProviderPidgin * self) +{ + StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(self); + + if (priv->proxy != NULL) { + g_debug("Odd, we were asked to set up a Pidgin proxy when we already had one."); + return; + } + + DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + g_return_if_fail(bus != NULL); /* Can't do anymore DBus stuff without this, + all non-DBus stuff should be done */ + + GError * error = NULL; + + /* Set up the Pidgin Proxy */ + priv->proxy = dbus_g_proxy_new_for_name_owner (bus, + "im.pidgin.purple.PurpleService", + "/im/pidgin/purple/PurpleObject", + "im.pidgin.purple.PurpleInterface", + &error); + /* Report any errors */ + if (error != NULL) { + g_debug("Unable to get Pidgin proxy: %s", error->message); + g_error_free(error); + } + + /* If we have a proxy, let's start using it */ + if (priv->proxy != NULL) { + /* Set the proxy to NULL if it's destroyed */ + g_object_add_weak_pointer (G_OBJECT(priv->proxy), (gpointer *)&priv->proxy); + /* If it's destroyed, let's clean up as well */ + g_signal_connect(G_OBJECT(priv->proxy), "destroy", + G_CALLBACK(proxy_destroy), self); + + /* Watching for the status change coming from the + Pidgin side of things. */ + g_debug("Adding Pidgin Signals"); + dbus_g_object_register_marshaller(_status_provider_pidgin_marshal_VOID__INT_INT, + G_TYPE_NONE, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->proxy, + "SavedstatusChanged", + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal(priv->proxy, + "SavedstatusChanged", + G_CALLBACK(changed_status), + (void *)self, + NULL); + + /* Get the current status to update our cached + value of the status. */ + dbus_g_proxy_begin_call(priv->proxy, + "PurpleSavedstatusGetCurrent", + savedstatus_cb, + self, + NULL, + G_TYPE_INVALID); + } + + return; +} + +static void +status_provider_pidgin_dispose (GObject *object) +{ + StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(object); + + if (priv->proxy != NULL) { + g_object_unref(priv->proxy); + priv->proxy = NULL; + } + + G_OBJECT_CLASS (status_provider_pidgin_parent_class)->dispose (object); + return; +} + +static void +status_provider_pidgin_finalize (GObject *object) +{ + + G_OBJECT_CLASS (status_provider_pidgin_parent_class)->finalize (object); + return; +} + +/** + status_provider_pidgin_new: + + Creates a new #StatusProviderPidgin object. No parameters or anything + like that. Just a convience function. + + Return value: A new instance of #StatusProviderPidgin +*/ +StatusProvider * +status_provider_pidgin_new (void) +{ + return STATUS_PROVIDER(g_object_new(STATUS_PROVIDER_PIDGIN_TYPE, NULL)); +} + +/* Takes the status provided generically for Status providers + and turns it into a Pidgin status and sends it to Pidgin. */ +static void +set_status (StatusProvider * sp, StatusProviderStatus status) +{ + gchar * message = ""; + + g_return_if_fail(IS_STATUS_PROVIDER_PIDGIN(sp)); + StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(sp); + + g_debug("\tPidgin set status to %d", status); + if (priv->proxy == NULL) { + return; + } + + priv->pg_status = sp_to_pg_map[status]; + gint status_val = 0; + gboolean ret = FALSE; + GError * error = NULL; + + ret = dbus_g_proxy_call(priv->proxy, + "PurpleSavedstatusFindTransientByTypeAndMessage", &error, + G_TYPE_INT, priv->pg_status, + G_TYPE_STRING, message, + G_TYPE_INVALID, + G_TYPE_INT, &status_val, + G_TYPE_INVALID); + + if (!ret) { + if (error != NULL) { + g_error_free(error); + } + error = NULL; + status_val = 0; + g_debug("No Pidgin saved status to apply"); + } + + if (status_val == 0) { + ret = dbus_g_proxy_call(priv->proxy, + "PurpleSavedstatusNew", &error, + G_TYPE_STRING, message, + G_TYPE_INT, priv->pg_status, + G_TYPE_INVALID, + G_TYPE_INT, &status_val, + G_TYPE_INVALID); + + if (!ret) { + status_val = 0; + if (error != NULL) { + g_warning("Unable to create Pidgin status for %d: %s", status, error->message); + g_error_free(error); + } else { + g_warning("Unable to create Pidgin status for %d", status); + } + error = NULL; + } + } + + if (status_val == 0) { + return; + } + + ret = dbus_g_proxy_call(priv->proxy, + "PurpleSavedstatusActivate", &error, + G_TYPE_INT, status_val, + G_TYPE_INVALID, + G_TYPE_INVALID); + + if (!ret) { + if (error != NULL) { + g_warning("Pidgin unable to change to status: %s", error->message); + g_error_free(error); + } else { + g_warning("Pidgin unable to change to status"); + } + error = NULL; + } + + g_signal_emit(G_OBJECT(sp), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, pg_to_sp_map[priv->pg_status], TRUE); + return; +} + +/* Takes the cached Pidgin status and makes it into the generic + Status provider status. If there is no Pidgin proxy then it + returns the disconnected state. */ +static StatusProviderStatus +get_status (StatusProvider * sp) +{ + g_return_val_if_fail(IS_STATUS_PROVIDER_PIDGIN(sp), STATUS_PROVIDER_STATUS_DISCONNECTED); + StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(sp); + + if (priv->proxy == NULL) { + return STATUS_PROVIDER_STATUS_DISCONNECTED; + } + + return pg_to_sp_map[priv->pg_status]; +} diff --git a/src/status-provider-pidgin.h b/src/status-provider-pidgin.h new file mode 100644 index 0000000..54b1718 --- /dev/null +++ b/src/status-provider-pidgin.h @@ -0,0 +1,56 @@ +/* +A small wrapper utility to load indicators and put them as menu items +into the gnome-panel using it's applet interface. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +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/>. +*/ + +#ifndef __STATUS_PROVIDER_PIDGIN_H__ +#define __STATUS_PROVIDER_PIDGIN_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "status-provider.h" + +G_BEGIN_DECLS + +#define STATUS_PROVIDER_PIDGIN_TYPE (status_provider_pidgin_get_type ()) +#define STATUS_PROVIDER_PIDGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_PROVIDER_PIDGIN_TYPE, StatusProviderPidgin)) +#define STATUS_PROVIDER_PIDGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_PROVIDER_PIDGIN_TYPE, StatusProviderPidginClass)) +#define IS_STATUS_PROVIDER_PIDGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_PROVIDER_PIDGIN_TYPE)) +#define IS_STATUS_PROVIDER_PIDGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_PROVIDER_PIDGIN_TYPE)) +#define STATUS_PROVIDER_PIDGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_PROVIDER_PIDGIN_TYPE, StatusProviderPidginClass)) + + +typedef struct _StatusProviderPidginClass StatusProviderPidginClass; +struct _StatusProviderPidginClass { + StatusProviderClass parent_class; +}; + +typedef struct _StatusProviderPidgin StatusProviderPidgin; +struct _StatusProviderPidgin { + StatusProvider parent; +}; + +GType status_provider_pidgin_get_type (void); +StatusProvider * status_provider_pidgin_new (void); + +G_END_DECLS + +#endif diff --git a/src/status-provider-pidgin.list b/src/status-provider-pidgin.list new file mode 100644 index 0000000..1f953dd --- /dev/null +++ b/src/status-provider-pidgin.list @@ -0,0 +1 @@ +VOID:INT,INT diff --git a/src/status-provider-telepathy.c b/src/status-provider-telepathy.c new file mode 100644 index 0000000..c8e89da --- /dev/null +++ b/src/status-provider-telepathy.c @@ -0,0 +1,385 @@ +/* +A small wrapper utility to load indicators and put them as menu items +into the gnome-panel using it's applet interface. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +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/>. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "status-provider.h" +#include "status-provider-telepathy.h" +#include "status-provider-telepathy-marshal.h" + +#include <dbus/dbus-glib.h> + +typedef enum { + MC_STATUS_UNSET, + MC_STATUS_OFFLINE, + MC_STATUS_AVAILABLE, + MC_STATUS_AWAY, + MC_STATUS_EXTENDED_AWAY, + MC_STATUS_HIDDEN, + MC_STATUS_DND +} mc_status_t; + +static StatusProviderStatus mc_to_sp_map[] = { + /* MC_STATUS_UNSET, */ STATUS_PROVIDER_STATUS_OFFLINE, + /* MC_STATUS_OFFLINE, */ STATUS_PROVIDER_STATUS_OFFLINE, + /* MC_STATUS_AVAILABLE, */ STATUS_PROVIDER_STATUS_ONLINE, + /* MC_STATUS_AWAY, */ STATUS_PROVIDER_STATUS_AWAY, + /* MC_STATUS_EXTENDED_AWAY, */ STATUS_PROVIDER_STATUS_AWAY, + /* MC_STATUS_HIDDEN, */ STATUS_PROVIDER_STATUS_INVISIBLE, + /* MC_STATUS_DND */ STATUS_PROVIDER_STATUS_DND +}; + +static mc_status_t sp_to_mc_map[] = { + /* STATUS_PROVIDER_STATUS_ONLINE, */ MC_STATUS_AVAILABLE, + /* STATUS_PROVIDER_STATUS_AWAY, */ MC_STATUS_AWAY, + /* STATUS_PROVIDER_STATUS_DND */ MC_STATUS_DND, + /* STATUS_PROVIDER_STATUS_INVISIBLE*/ MC_STATUS_HIDDEN, + /* STATUS_PROVIDER_STATUS_OFFLINE */ MC_STATUS_OFFLINE, + /* STATUS_PROVIDER_STATUS_DISCONNECTED*/MC_STATUS_OFFLINE +}; + +typedef struct _StatusProviderTelepathyPrivate StatusProviderTelepathyPrivate; +struct _StatusProviderTelepathyPrivate { + DBusGProxy * proxy; + DBusGProxy * dbus_proxy; + mc_status_t mc_status; +}; + +#define STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(o) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((o), STATUS_PROVIDER_TELEPATHY_TYPE, StatusProviderTelepathyPrivate)) + +/* Prototypes */ +/* GObject stuff */ +static void status_provider_telepathy_class_init (StatusProviderTelepathyClass *klass); +static void status_provider_telepathy_init (StatusProviderTelepathy *self); +static void status_provider_telepathy_dispose (GObject *object); +static void status_provider_telepathy_finalize (GObject *object); +/* Internal Funcs */ +static void build_telepathy_proxy (StatusProviderTelepathy * self); +static void dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderTelepathy * self); +static void set_status (StatusProvider * sp, StatusProviderStatus status); +static StatusProviderStatus get_status (StatusProvider * sp); +static void changed_status (DBusGProxy * proxy, guint status, gchar * message, StatusProvider * sp); +static void proxy_destroy (DBusGProxy * proxy, StatusProvider * sp); +static void get_status_async (DBusGProxy * proxy, DBusGProxyCall * call, gpointer userdata); + +G_DEFINE_TYPE (StatusProviderTelepathy, status_provider_telepathy, STATUS_PROVIDER_TYPE); + +STATUS_PROVIDER_EXPORT_TYPE(STATUS_PROVIDER_TELEPATHY_TYPE) + +static void +status_provider_telepathy_class_init (StatusProviderTelepathyClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (StatusProviderTelepathyPrivate)); + + object_class->dispose = status_provider_telepathy_dispose; + object_class->finalize = status_provider_telepathy_finalize; + + StatusProviderClass * spclass = STATUS_PROVIDER_CLASS(klass); + + spclass->set_status = set_status; + spclass->get_status = get_status; + + return; +} + + +static void +status_provider_telepathy_init (StatusProviderTelepathy *self) +{ + StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(self); + + priv->proxy = NULL; + priv->dbus_proxy = NULL; + priv->mc_status = MC_STATUS_OFFLINE; + + GError * error = NULL; + + /* Grabbing the session bus */ + DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (bus == NULL) { + g_warning("Unable to connect to Session Bus: %s", error == NULL ? "No message" : error->message); + g_error_free(error); + return; + } + + /* Set up the dbus Proxy */ + priv->dbus_proxy = dbus_g_proxy_new_for_name_owner (bus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + &error); + if (error != NULL) { + g_warning("Unable to connect to DBus events: %s", error->message); + g_error_free(error); + return; + } + + /* Configure the name owner changing */ + dbus_g_proxy_add_signal(priv->dbus_proxy, "NameOwnerChanged", + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal(priv->dbus_proxy, "NameOwnerChanged", + G_CALLBACK(dbus_namechange), + self, NULL); + + build_telepathy_proxy(self); + + return; +} + +/* Builds up the proxy to Mission Control and configures all of the + signals for getting info from the proxy. Also does a call to get + the inital value of the status. */ +static void +build_telepathy_proxy (StatusProviderTelepathy * self) +{ + g_debug("Building Telepathy Proxy"); + StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(self); + + if (priv->proxy != NULL) { + g_debug("Hmm, being asked to build a proxy we alredy have."); + return; + } + + GError * error = NULL; + + /* Grabbing the session bus */ + DBusGConnection * session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (session_bus == NULL) { + g_warning("Unable to connect to Session Bus: %s", error == NULL ? "No message" : error->message); + g_error_free(error); + return; + } + + /* Get the proxy to Mission Control */ + priv->proxy = dbus_g_proxy_new_for_name_owner(session_bus, + "org.freedesktop.Telepathy.MissionControl", + "/org/freedesktop/Telepathy/MissionControl", + "org.freedesktop.Telepathy.MissionControl", + &error); + + if (priv->proxy != NULL) { + /* If it goes, we set the proxy to NULL */ + g_object_add_weak_pointer (G_OBJECT(priv->proxy), (gpointer *)&priv->proxy); + /* And we clean up other variables associated */ + g_signal_connect(G_OBJECT(priv->proxy), "destroy", + G_CALLBACK(proxy_destroy), self); + + /* Set up the signal handler for watching when status changes. */ + dbus_g_object_register_marshaller(_status_provider_telepathy_marshal_VOID__UINT_STRING, + G_TYPE_NONE, + G_TYPE_UINT, + G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->proxy, + "PresenceChanged", + G_TYPE_UINT, + G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal(priv->proxy, + "PresenceChanged", + G_CALLBACK(changed_status), + (void *)self, + NULL); + + /* Do a get here, to init the status */ + dbus_g_proxy_begin_call(priv->proxy, + "GetStatus", + get_status_async, + self, + NULL, + G_TYPE_INVALID); + } else { + g_warning("Unable to connect to Mission Control"); + if (error != NULL) { + g_error_free(error); + } + } + + return; +} + +/* Watch to see if the Mission Control comes up on Dbus */ +static void +dbus_namechange (DBusGProxy * proxy, const gchar * name, const gchar * prev, const gchar * new, StatusProviderTelepathy * self) +{ + g_return_if_fail(name != NULL); + g_return_if_fail(new != NULL); + + if (g_strcmp0(name, "org.freedesktop.Telepathy.MissionControl") == 0) { + build_telepathy_proxy(self); + } + return; +} + +static void +status_provider_telepathy_dispose (GObject *object) +{ + StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(object); + + if (priv->proxy != NULL) { + g_object_unref(priv->proxy); + priv->proxy = NULL; + } + + G_OBJECT_CLASS (status_provider_telepathy_parent_class)->dispose (object); + return; +} + +static void +status_provider_telepathy_finalize (GObject *object) +{ + + G_OBJECT_CLASS (status_provider_telepathy_parent_class)->finalize (object); + return; +} + +/** + status_provider_telepathy_new: + + Creates a new #StatusProviderTelepathy object. No parameters or anything + like that. Just a convience function. + + Return value: A new instance of #StatusProviderTelepathy +*/ +StatusProvider * +status_provider_telepathy_new (void) +{ + return STATUS_PROVIDER(g_object_new(STATUS_PROVIDER_TELEPATHY_TYPE, NULL)); +} + +static void +set_status (StatusProvider * sp, StatusProviderStatus status) +{ + StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(sp); + if (priv->proxy == NULL) { + priv->mc_status = MC_STATUS_OFFLINE; + return; + } + + priv->mc_status = sp_to_mc_map[status]; + + guint mcstatus = MC_STATUS_UNSET; + gboolean ret = FALSE; + GError * error = NULL; + + ret = dbus_g_proxy_call(priv->proxy, + "GetPresence", &error, + G_TYPE_INVALID, + G_TYPE_UINT, &priv->mc_status, + G_TYPE_INVALID); + + /* If we can't get the get call to work, let's not set */ + if (!ret) { + if (error != NULL) { + g_error_free(error); + } + return; + } + + /* If the get call doesn't return a status, that means that there + are no clients connected. We don't want to connect them by telling + MC that we're going online -- we'd like to be more passive than that. */ + if (mcstatus == MC_STATUS_UNSET) { + return; + } + + ret = dbus_g_proxy_call(priv->proxy, + "SetPresence", &error, + G_TYPE_UINT, priv->mc_status, + G_TYPE_STRING, "", + G_TYPE_INVALID, + G_TYPE_INVALID); + + if (!ret) { + if (error != NULL) { + g_warning("Unable to set Mission Control Presence: %s", error->message); + g_error_free(error); + } else { + g_warning("Unable to set Mission Control Presence"); + } + return; + } + + return; +} + +static StatusProviderStatus +get_status (StatusProvider * sp) +{ + g_return_val_if_fail(IS_STATUS_PROVIDER_TELEPATHY(sp), STATUS_PROVIDER_STATUS_DISCONNECTED); + StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(sp); + + if (priv->proxy == NULL) { + return STATUS_PROVIDER_STATUS_DISCONNECTED; + } + + return mc_to_sp_map[priv->mc_status]; +} + +static void +changed_status (DBusGProxy * proxy, guint status, gchar * message, StatusProvider * sp) +{ + StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(sp); + priv->mc_status = status; + g_signal_emit(G_OBJECT(sp), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, mc_to_sp_map[priv->mc_status], TRUE); +} + +static void +proxy_destroy (DBusGProxy * proxy, StatusProvider * sp) +{ + g_debug("Signal: Mission Control proxy destroyed"); + g_signal_emit(G_OBJECT(sp), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, STATUS_PROVIDER_STATUS_OFFLINE, TRUE); + return; +} + +static void +get_status_async (DBusGProxy * proxy, DBusGProxyCall * call, gpointer userdata) +{ + GError * error = NULL; + guint status = 0; + if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_UINT, &status, G_TYPE_INVALID)) { + g_warning("Unable to get type from Mission Control: %s", error->message); + g_error_free(error); + return; + } + + StatusProviderTelepathyPrivate * priv = STATUS_PROVIDER_TELEPATHY_GET_PRIVATE(userdata); + + gboolean changed = FALSE; + if (status != priv->mc_status) { + changed = TRUE; + } + + priv->mc_status = status; + + if (changed) { + g_signal_emit(G_OBJECT(userdata), STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID, 0, mc_to_sp_map[priv->mc_status], TRUE); + } + + return; +} diff --git a/src/status-provider-telepathy.h b/src/status-provider-telepathy.h new file mode 100644 index 0000000..a67ee40 --- /dev/null +++ b/src/status-provider-telepathy.h @@ -0,0 +1,56 @@ +/* +A small wrapper utility to load indicators and put them as menu items +into the gnome-panel using it's applet interface. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +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/>. +*/ + +#ifndef __STATUS_PROVIDER_TELEPATHY_H__ +#define __STATUS_PROVIDER_TELEPATHY_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "status-provider.h" + +G_BEGIN_DECLS + +#define STATUS_PROVIDER_TELEPATHY_TYPE (status_provider_telepathy_get_type ()) +#define STATUS_PROVIDER_TELEPATHY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_PROVIDER_TELEPATHY_TYPE, StatusProviderTelepathy)) +#define STATUS_PROVIDER_TELEPATHY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_PROVIDER_TELEPATHY_TYPE, StatusProviderTelepathyClass)) +#define IS_STATUS_PROVIDER_TELEPATHY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_PROVIDER_TELEPATHY_TYPE)) +#define IS_STATUS_PROVIDER_TELEPATHY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_PROVIDER_TELEPATHY_TYPE)) +#define STATUS_PROVIDER_TELEPATHY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_PROVIDER_TELEPATHY_TYPE, StatusProviderTelepathyClass)) + + +typedef struct _StatusProviderTelepathyClass StatusProviderTelepathyClass; +struct _StatusProviderTelepathyClass { + StatusProviderClass parent_class; +}; + +typedef struct _StatusProviderTelepathy StatusProviderTelepathy; +struct _StatusProviderTelepathy { + StatusProvider parent; +}; + +GType status_provider_telepathy_get_type (void); +StatusProvider * status_provider_telepathy_new (void); + +G_END_DECLS + +#endif diff --git a/src/status-provider-telepathy.list b/src/status-provider-telepathy.list new file mode 100644 index 0000000..5ab45bf --- /dev/null +++ b/src/status-provider-telepathy.list @@ -0,0 +1 @@ +VOID:UINT,STRING diff --git a/src/status-provider.c b/src/status-provider.c new file mode 100644 index 0000000..1139e54 --- /dev/null +++ b/src/status-provider.c @@ -0,0 +1,101 @@ +/* +A small wrapper utility to load indicators and put them as menu items +into the gnome-panel using it's applet interface. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +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/>. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "status-provider.h" + +/* Signals */ +enum { + STATUS_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/* GObject Boilerplate */ +static void status_provider_class_init (StatusProviderClass *klass); +static void status_provider_init (StatusProvider *self); + +G_DEFINE_TYPE (StatusProvider, status_provider, G_TYPE_OBJECT); + +static void +status_provider_class_init (StatusProviderClass *klass) +{ + // GObjectClass *object_class = G_OBJECT_CLASS (klass); + + klass->status_changed = NULL; + + klass->set_status = NULL; + klass->get_status = NULL; + + /** + StatusProvider::status-changed: + @arg0: The #StatusProvider object. + @arg1: The new status #StatusProviderStatus + + Should be emitted by subclasses everytime that the status + changes externally to us. + */ + signals[STATUS_CHANGED] = g_signal_new(STATUS_PROVIDER_SIGNAL_STATUS_CHANGED, + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(StatusProviderClass, status_changed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + return; +} + +static void +status_provider_init (StatusProvider *self) +{ + + return; +} + +void +status_provider_set_status (StatusProvider * sp, StatusProviderStatus status) +{ + g_return_if_fail(IS_STATUS_PROVIDER(sp)); + + StatusProviderClass * class = STATUS_PROVIDER_GET_CLASS(sp); + g_return_if_fail(class != NULL); + g_return_if_fail(class->set_status != NULL); + + return class->set_status(sp, status); +} + +StatusProviderStatus +status_provider_get_status (StatusProvider * sp) +{ + g_return_val_if_fail(IS_STATUS_PROVIDER(sp), STATUS_PROVIDER_STATUS_OFFLINE); + + StatusProviderClass * class = STATUS_PROVIDER_GET_CLASS(sp); + g_return_val_if_fail(class->get_status != NULL, STATUS_PROVIDER_STATUS_OFFLINE); + + return class->get_status(sp); +} + diff --git a/src/status-provider.h b/src/status-provider.h new file mode 100644 index 0000000..45433fc --- /dev/null +++ b/src/status-provider.h @@ -0,0 +1,81 @@ +/* +A small wrapper utility to load indicators and put them as menu items +into the gnome-panel using it's applet interface. + +Copyright 2009 Canonical Ltd. + +Authors: + Ted Gould <ted@canonical.com> + +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/>. +*/ + +#ifndef __STATUS_PROVIDER_H__ +#define __STATUS_PROVIDER_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define STATUS_PROVIDER_EXPORT_TYPE(x) GType status_provider_export_type (void) { return (x); } +#define STATUS_PROVIDER_EXPORT_S "status_provider_export_type" + +#define STATUS_PROVIDER_TYPE (status_provider_get_type ()) +#define STATUS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_PROVIDER_TYPE, StatusProvider)) +#define STATUS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_PROVIDER_TYPE, StatusProviderClass)) +#define IS_STATUS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_PROVIDER_TYPE)) +#define IS_STATUS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_PROVIDER_TYPE)) +#define STATUS_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_PROVIDER_TYPE, StatusProviderClass)) + +typedef enum +{ + STATUS_PROVIDER_STATUS_ONLINE, + STATUS_PROVIDER_STATUS_AWAY, + STATUS_PROVIDER_STATUS_DND, + STATUS_PROVIDER_STATUS_INVISIBLE, + STATUS_PROVIDER_STATUS_OFFLINE, + STATUS_PROVIDER_STATUS_DISCONNECTED, + /* Leave as last */ + STATUS_PROVIDER_STATUS_LAST +} +StatusProviderStatus; + +#define STATUS_PROVIDER_SIGNAL_STATUS_CHANGED "status-changed" +#define STATUS_PROVIDER_SIGNAL_STATUS_CHANGED_ID (g_signal_lookup(STATUS_PROVIDER_SIGNAL_STATUS_CHANGED, STATUS_PROVIDER_TYPE)) + +typedef struct _StatusProvider StatusProvider; +struct _StatusProvider { + GObject parent; +}; + +typedef struct _StatusProviderClass StatusProviderClass; +struct _StatusProviderClass { + GObjectClass parent_class; + + /* Signals */ + void (*status_changed) (StatusProviderStatus newstatus); + + /* Virtual Functions */ + void (*set_status) (StatusProvider * sp, StatusProviderStatus newstatus); + StatusProviderStatus (*get_status) (StatusProvider * sp); +}; + +GType status_provider_get_type (void); + +void status_provider_set_status (StatusProvider * sp, StatusProviderStatus status); +StatusProviderStatus status_provider_get_status (StatusProvider * sp); + +G_END_DECLS + +#endif |