From 541f2122aa1dbb0376030f4df6fd7621912408a4 Mon Sep 17 00:00:00 2001 From: Jesse van den Kieboom Date: Mon, 20 Feb 2006 22:29:19 +0000 Subject: [PATCH] Added libnotify support --- gnoemoe/widgets/eggtrayicon.c | 304 +++++++++++++++++++++++----------- gnoemoe/widgets/eggtrayicon.h | 28 +++- 2 files changed, 234 insertions(+), 98 deletions(-) diff --git a/gnoemoe/widgets/eggtrayicon.c b/gnoemoe/widgets/eggtrayicon.c index 12f615a..e0f6104 100644 --- a/gnoemoe/widgets/eggtrayicon.c +++ b/gnoemoe/widgets/eggtrayicon.c @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* eggtrayicon.c * Copyright (C) 2002 Anders Carlsson * @@ -18,17 +18,20 @@ * Boston, MA 02111-1307, USA. */ -#ifdef HAVE_CONFIG_H #include -#endif - #include #include #include "eggtrayicon.h" +#include "gm-pixbuf.h" +#include +#include #include #include +#ifdef HAVE_NOTIFY +#include +#endif #ifndef EGG_COMPILATION #ifndef _ @@ -51,7 +54,21 @@ enum { PROP_0, PROP_ORIENTATION }; - + +#ifdef HAVE_NOTIFY +struct _Notify { + NotifyNotification *handle; +}; +#endif + +/* Signals */ + +enum { + MESSAGE_CLICKED, + NUM_SIGNALS +}; + +static guint egg_tray_icon_signals[NUM_SIGNALS] = {0}; static GtkPlugClass *parent_class = NULL; static void egg_tray_icon_init (EggTrayIcon *icon); @@ -65,7 +82,9 @@ static void egg_tray_icon_get_property (GObject *object, static void egg_tray_icon_realize (GtkWidget *widget); static void egg_tray_icon_unrealize (GtkWidget *widget); -static void egg_tray_icon_update_manager_window (EggTrayIcon *icon); +static void egg_tray_icon_update_manager_window (EggTrayIcon *icon, + gboolean dock_if_realized); +static void egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon); GType egg_tray_icon_get_type (void) @@ -98,7 +117,9 @@ egg_tray_icon_init (EggTrayIcon *icon) { icon->stamp = 1; icon->orientation = GTK_ORIENTATION_HORIZONTAL; - +#ifdef HAVE_NOTIFY + icon->notify = g_new0 (Notify, 1); +#endif gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK); } @@ -123,6 +144,16 @@ egg_tray_icon_class_init (EggTrayIconClass *klass) GTK_TYPE_ORIENTATION, GTK_ORIENTATION_HORIZONTAL, G_PARAM_READABLE)); + + egg_tray_icon_signals[MESSAGE_CLICKED] = + g_signal_new("message-clicked", + G_OBJECT_CLASS_TYPE(gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(EggTrayIconClass, message_clicked), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); } static void @@ -204,9 +235,9 @@ egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_ if (xev->xany.type == ClientMessage && xev->xclient.message_type == icon->manager_atom && - (Atom)(xev->xclient.data.l[1]) == icon->selection_atom) + (unsigned)(xev->xclient.data.l[1]) == icon->selection_atom) { - egg_tray_icon_update_manager_window (icon); + egg_tray_icon_update_manager_window (icon, TRUE); } else if (xev->xany.window == icon->manager_window) { @@ -217,10 +248,9 @@ egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_ } if (xev->xany.type == DestroyNotify) { - egg_tray_icon_update_manager_window (icon); + egg_tray_icon_manager_window_destroyed (icon); } } - return GDK_FILTER_CONTINUE; } @@ -246,6 +276,14 @@ egg_tray_icon_unrealize (GtkWidget *widget) if (GTK_WIDGET_CLASS (parent_class)->unrealize) (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); + +#ifdef HAVE_NOTIFY + if (EGG_TRAY_ICON (widget)->notify->handle) { + notify_notification_close (EGG_TRAY_ICON (widget)->notify->handle, NULL); + } + + g_free (EGG_TRAY_ICON (widget)->notify); +#endif } static void @@ -289,21 +327,15 @@ egg_tray_icon_send_dock_request (EggTrayIcon *icon) } static void -egg_tray_icon_update_manager_window (EggTrayIcon *icon) +egg_tray_icon_update_manager_window (EggTrayIcon *icon, + gboolean dock_if_realized) { Display *xdisplay; - xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); - if (icon->manager_window != None) - { - GdkWindow *gdkwin; + return; - gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)), - icon->manager_window); - - gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon); - } + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); XGrabServer (xdisplay); @@ -326,13 +358,41 @@ egg_tray_icon_update_manager_window (EggTrayIcon *icon) gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon); - /* Send a request that we'd like to dock */ - egg_tray_icon_send_dock_request (icon); + if (dock_if_realized && GTK_WIDGET_REALIZED (icon)) + egg_tray_icon_send_dock_request (icon); egg_tray_icon_get_orientation_property (icon); } } +static void +egg_tray_icon_manager_window_destroyed (EggTrayIcon *icon) +{ + GdkWindow *gdkwin; + + g_return_if_fail (icon->manager_window != None); + + gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)), + icon->manager_window); + + gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon); + + icon->manager_window = None; + + egg_tray_icon_update_manager_window (icon, TRUE); +} + +gboolean +egg_tray_icon_have_manager (EggTrayIcon *icon) +{ + GtkPlug * plug = GTK_PLUG (icon); + + if (plug->socket_window) + return TRUE; + else + return FALSE; +} + static void egg_tray_icon_realize (GtkWidget *widget) { @@ -367,7 +427,8 @@ egg_tray_icon_realize (GtkWidget *widget) "_NET_SYSTEM_TRAY_ORIENTATION", False); - egg_tray_icon_update_manager_window (icon); + egg_tray_icon_update_manager_window (icon, FALSE); + egg_tray_icon_send_dock_request (icon); root_window = gdk_screen_get_root_window (screen); @@ -377,95 +438,144 @@ egg_tray_icon_realize (GtkWidget *widget) } EggTrayIcon * -egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name) -{ - g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); +egg_tray_icon_new_for_screen(GdkScreen *screen, const char *name) { + g_return_val_if_fail(GDK_IS_SCREEN(screen), NULL); - return g_object_new (EGG_TYPE_TRAY_ICON, "screen", screen, "title", name, NULL); + return g_object_new(EGG_TYPE_TRAY_ICON, "screen", screen, + "title", name, NULL); } EggTrayIcon* -egg_tray_icon_new (const gchar *name) -{ - return g_object_new (EGG_TYPE_TRAY_ICON, "title", name, NULL); +egg_tray_icon_new (const gchar *name) { + return g_object_new(EGG_TYPE_TRAY_ICON, "title", name, NULL); } guint -egg_tray_icon_send_message (EggTrayIcon *icon, - gint timeout, - const gchar *message, - gint len) -{ - guint stamp; - - g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0); - g_return_val_if_fail (timeout >= 0, 0); - g_return_val_if_fail (message != NULL, 0); +egg_tray_icon_send_message(EggTrayIcon *icon, gint timeout, + const gchar *message, gint len) { + g_return_val_if_fail(EGG_IS_TRAY_ICON(icon), 0); + g_return_val_if_fail(timeout >= 0, 0); + g_return_val_if_fail(message != NULL, 0); - if (icon->manager_window == None) - return 0; - - if (len < 0) - len = strlen (message); - - stamp = icon->stamp++; - - /* Get ready to send the message */ - egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE, - (Window)gtk_plug_get_id (GTK_PLUG (icon)), - timeout, len, stamp); - - /* Now to send the actual message */ - gdk_error_trap_push (); - while (len > 0) - { - XClientMessageEvent ev; - Display *xdisplay; - - xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); - - ev.type = ClientMessage; - ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon)); - ev.format = 8; - ev.message_type = XInternAtom (xdisplay, - "_NET_SYSTEM_TRAY_MESSAGE_DATA", False); - if (len > 20) - { - memcpy (&ev.data, message, 20); - len -= 20; - message += 20; - } - else - { - memcpy (&ev.data, message, len); - len = 0; +#ifdef HAVE_NOTIFY + egg_tray_icon_notify(icon, timeout, _("Notification"), NULL, message); +#else + if (!icon->bubble) { + icon->bubble = egg_notification_bubble_new(); + egg_notification_bubble_attach(icon->bubble, GTK_WIDGET(icon)); } - XSendEvent (xdisplay, - icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev); - XSync (xdisplay, False); - } - gdk_error_trap_pop (); + egg_notification_bubble_set(icon->bubble, _("Notification"), NULL, message); + egg_notification_bubble_show(icon->bubble, timeout); +#endif - return stamp; + return 1; } void -egg_tray_icon_cancel_message (EggTrayIcon *icon, - guint id) -{ - g_return_if_fail (EGG_IS_TRAY_ICON (icon)); - g_return_if_fail (id > 0); - - egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE, - (Window)gtk_plug_get_id (GTK_PLUG (icon)), - id, 0, 0); +egg_tray_icon_cancel_message(EggTrayIcon *icon, guint id) { + g_return_if_fail(EGG_IS_TRAY_ICON(icon)); + +#ifdef HAVE_NOTIFY + if (icon->notify->handle) { + notify_notification_close(icon->notify->handle, NULL); + } +#else + g_return_if_fail(id > 0); + g_return_if_fail(icon->bubble != NULL); + + egg_notification_bubble_hide(icon->bubble); +#endif } GtkOrientation -egg_tray_icon_get_orientation (EggTrayIcon *icon) -{ - g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL); +egg_tray_icon_get_orientation(EggTrayIcon *icon) { + g_return_val_if_fail(EGG_IS_TRAY_ICON(icon), GTK_ORIENTATION_HORIZONTAL); - return icon->orientation; + return icon->orientation; +} + +#ifndef HAVE_NOTIFY +static void +egg_tray_icon_hide_notify_cb(EggNotificationBubble *bubble, gpointer data) { + egg_tray_icon_cancel_message(EGG_TRAY_ICON(data), 1); +} +#else +static void +egg_tray_icon_notify_default_cb(NotifyNotification *notify, gchar *action, + gpointer userdata) { + g_return_if_fail(EGG_IS_TRAY_ICON(userdata)); + + g_signal_emit(EGG_TRAY_ICON(userdata), + egg_tray_icon_signals[MESSAGE_CLICKED], 0); +} + +#endif + +void +egg_tray_icon_notify(EggTrayIcon *icon, guint timeout, const char *primary, + GtkWidget *msgicon, const char *secondary) { +#ifdef HAVE_NOTIFY + GtkRequisition size; + GdkPixbuf *pixbuf; + int x; + int y; + + if (!notify_is_initted()) + if (!notify_init("gnoemoe")) + return; + + if (icon->notify->handle != NULL) { + notify_notification_close (icon->notify->handle, NULL); + } + + icon->notify->handle = notify_notification_new(primary, secondary, + NULL, NULL); + + notify_notification_set_timeout (icon->notify->handle, timeout); + + if (msgicon) { + pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(msgicon)); + } else { + GtkIconTheme *theme; + gint width, height; + + theme = gtk_icon_theme_get_default(); + gtk_icon_size_lookup(GTK_ICON_SIZE_LARGE_TOOLBAR, &width, &height); + pixbuf = gm_pixbuf_get_at_size("gnoemoe_logo.svg", width, height); + } + + if (pixbuf) { + notify_notification_set_icon_from_pixbuf(icon->notify->handle, pixbuf); + } + + gdk_window_get_origin(GTK_WIDGET(icon)->window, &x, &y); + gtk_widget_size_request(GTK_WIDGET(icon), &size); + + x += size.width / 2; + y += size.height; + + notify_notification_set_hint_int32(icon->notify->handle, "x", x); + notify_notification_set_hint_int32(icon->notify->handle, "y", y); + + notify_notification_add_action(icon->notify->handle, "default", + "default", egg_tray_icon_notify_default_cb, icon, NULL); + + if (!notify_notification_show(icon->notify->handle, NULL)) { + g_warning("failed to send notification (%s)", primary); + } +#else + gint x, y; + gdk_window_get_origin(GTK_WIDGET (icon)->window, &x, &y); + + if (!icon->bubble) { + icon->bubble = egg_notification_bubble_new(); + egg_notification_bubble_attach(icon->bubble, GTK_WIDGET(icon)); + g_signal_connect_object(icon->bubble, "clicked", + G_CALLBACK (egg_tray_icon_hide_notify_cb), icon, 0); + } + + egg_notification_bubble_set(icon->bubble, primary, msgicon, secondary); + egg_notification_bubble_show(icon->bubble, timeout); +#endif } diff --git a/gnoemoe/widgets/eggtrayicon.h b/gnoemoe/widgets/eggtrayicon.h index 007f4c1..2fd4e3a 100644 --- a/gnoemoe/widgets/eggtrayicon.h +++ b/gnoemoe/widgets/eggtrayicon.h @@ -21,8 +21,15 @@ #ifndef __EGG_TRAY_ICON_H__ #define __EGG_TRAY_ICON_H__ +#include + #include #include +#ifdef HAVE_NOTIFY +#include +#else +#include "eggnotificationbubble.h" +#endif G_BEGIN_DECLS @@ -35,6 +42,9 @@ G_BEGIN_DECLS typedef struct _EggTrayIcon EggTrayIcon; typedef struct _EggTrayIconClass EggTrayIconClass; +#ifdef HAVE_NOTIFY +typedef struct _Notify Notify; +#endif struct _EggTrayIcon { @@ -47,13 +57,20 @@ struct _EggTrayIcon Atom system_tray_opcode_atom; Atom orientation_atom; Window manager_window; - GtkOrientation orientation; +#ifdef HAVE_NOTIFY + Notify *notify; +#else + EggNotificationBubble *bubble; +#endif }; struct _EggTrayIconClass { GtkPlugClass parent_class; + + /* Signals */ + void (* message_clicked) (EggTrayIcon *icon); }; GType egg_tray_icon_get_type (void); @@ -70,6 +87,15 @@ guint egg_tray_icon_send_message (EggTrayIcon *icon, void egg_tray_icon_cancel_message (EggTrayIcon *icon, guint id); +gboolean egg_tray_icon_have_manager (EggTrayIcon *icon); + +void egg_tray_icon_notify (EggTrayIcon *icon, + guint timeout, + const char *primary, + GtkWidget *msgicon, + const char *secondary); + + GtkOrientation egg_tray_icon_get_orientation (EggTrayIcon *icon); G_END_DECLS