File notification-daemon-svn3017.patch of Package notification-daemon

Index: configure.ac
===================================================================
--- configure.ac	(révision 2976)
+++ configure.ac	(copie de travail)
@@ -67,13 +67,13 @@ AC_EXEEXT
 AM_PROG_LIBTOOL
 AC_PROG_INTLTOOL
 
-ALL_LINGUAS="de nl sv"
+ALL_LINGUAS="ar de it nl pl sv"
 GETTEXT_PACKAGE=notification-daemon
 AC_SUBST(GETTEXT_PACKAGE)
 AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [Gettext package])
 AM_GLIB_GNU_GETTEXT
 
-REQ_GTK_VERSION=2.4.0
+REQ_GTK_VERSION=2.10.0
 REQ_GLIB_VERSION=$REQ_GTK_VERSION
 REQ_SEXY_VERSION=0.1.3
 REQ_DBUS_VERSION=0.36
@@ -162,6 +162,34 @@ fi
 AC_SUBST(CFLAGS)
 
 dnl ################################################################
+dnl # Sound Support 
+dnl ################################################################
+
+AC_ARG_ENABLE(sound,
+  [[  --enable-sound=[auto,gstreamer,no]  Sound support (default=auto)]] )
+
+if test x$enable_sound != xno -a x$enable_sound != xgstreamer; then
+	enable_sound=auto
+fi
+
+if test x$enable_sound != xno; then
+	gstreampkg="gstreamer-0.10"
+
+	if test x$enable_sound = xauto; then
+		PKG_CHECK_MODULES(GSTREAMER, $gstreampkg,
+		                  [enable_sound=gstreamer], [enable_sound=no])
+	else
+		PKG_CHECK_MODULES(GSTREAMER, $gstreampkg, [enable_sound=gstreamer])
+	fi
+
+	if test x$enable_sound = xgstreamer; then
+		AC_DEFINE([HAVE_GSTREAMER], 1, [Defined if Gstreamer is detected])
+		AC_SUBST(GSTREAMER_CFLAGS)
+		AC_SUBST(GSTREAMER_LIBS)
+	fi
+fi
+
+dnl ################################################################
 dnl # Output the Makefiles
 dnl ################################################################
 AC_CONFIG_FILES([
@@ -184,6 +212,7 @@ echo
 echo prefix............... : $prefix
 echo dbus-1 system.d dir.. : $DBUS_SYS_DIR
 echo dbus-1 services dir.. : $DBUS_SERVICES_DIR
+echo sound support........ : $enable_sound
 echo
 echo "Now type make to compile"
 echo "Then su to root and type: make install"
Index: AUTHORS
===================================================================
--- AUTHORS	(révision 2976)
+++ AUTHORS	(copie de travail)
@@ -3,13 +3,20 @@ Authors:
 	John (J5) Palmieri  <johnp@redhat.com>
 
 Contributors:
+	driehuis@playbeing.org
 	Ed Catmur <ed@catmur.co.uk>
 	felix@hsgheli.de
+	Jim Ramsay <i.am@jimramsay.com>
+	Luca Cavalli <luca.cavelli@gmail.com>
 	Matt Walton <matthew@matthew-walton.co.uk>
 	Pawel Worach <pawel.worach@gmail.com>
 	Rodney Dawes <dobey@novell.com>
+	Jonh Wendell <wendell@bani.com.br>
 
 Translators:
+	Arabic - Djihed Afifi <djihed@gmail.com>
 	Dutch - Wouter Bolsterlee <uws+gnome@xs4all.nl>
 	German - Florian Steinel <steinel@pootle.wordforge.org.flonet.net>
+	Italian - Luca Ferretti <elle.uca@libero.it>
+	Polish - Raven <piotrdrag@gmail.com>
 	Swedish - Daniel Nylander <po@danielnylander.se>
Index: ChangeLog
===================================================================
--- ChangeLog	(révision 2976)
+++ ChangeLog	(copie de travail)
@@ -1,3 +1,133 @@
+Thu Sep 25 18:53:21 PDT 2008  Christian Hammond <chipx86@chipx86.com>
+
+	* src/capplet/notification-properties.glade:
+	  - Prevent duplicate mnemonics between "Position" and "Preview."
+
+Thu Sep 25 18:52:05 PDT 2008  Christian Hammond <chipx86@chipx86.com>
+
+	* src/capplet/notification-properties.c:
+	  - Some bullet-proofing to prevent crashes with multiple preview
+	    notifications.
+
+Thu Sep 25 18:25:36 PDT 2008  Christian Hammond <chipx86@chipx86.com>
+
+	* src/daemon/daemon.c:
+	* NEWS:
+	  - Added better support for attaching context notifications to an icon on
+	    the system tray, even when it moves. Patch by Colin Walters.
+
+Tue Mar 18 21:39:53 PDT 2008  Christian Hammond <chipx86@chipx86.com>
+
+	* src/daemon/daemon.c:
+	  - Patch by parasti to prevent our notification blocking when the
+	    fullscreen window is not on the current workspace. Fixes bug #142.
+
+Tue Mar 18 20:55:27 PDT 2008  Christian Hammond <chipx86@chipx86.com>
+
+	* src/themes/bubble/eggnotificationbubblewidget.c:
+	* src/themes/standard/theme.c:
+	  - Patch by maniac to set the NOTIFICATION type hint for the notification
+		windows. Fixes bug #161.
+
+Sat Aug 25 21:42:50 PDT 2007  Christian Hammond <chipx86@chipx86.com>
+
+	* src/capplet/notification-properties.c:
+	A po/notification-daemon.it.po:
+	* AUTHORS:
+	* NEWS:
+	* configure.ac:
+	  - Added an Italian translation by Luca Ferretti.
+
+Sat Aug 25 19:42:05 PDT 2007  Christian Hammond <chipx86@chipx86.com>
+
+	* src/themes/standard/theme.c:
+	  - Escape the text in the summary so that & and < don't cause problems.
+	    Fixes bug #132.
+
+Sat Aug 25 19:31:16 PDT 2007  Christian Hammond <chipx86@chipx86.com>
+
+	* src/daemon/daemon.c:
+	  - Show notifications if a fullscreen window isn't the active window.
+	    This fixes problems if a fullscreen window is hidden or minimized.
+	    This fixes bug #142.
+
+Sat Aug 25 19:10:18 PDT 2007  Christian Hammond <chipx86@chipx86.com>
+
+	* src/themes/standard/theme.c:
+	  - Only set the urgency in the standard theme if the value is actually a
+	    uchar. We were assuming it would be, but that made it easy to crash
+	    things. Now we make sure. Fixes bug #135.
+
+Sat Aug 25 18:55:34 PDT 2007  Christian Hammond <chipx86@chipx86.com>
+
+	* src/daemon/daemon.c:
+	* src/daemon/daemon.h:
+	  - Send the reason the notification closed when emitting
+	    the NotificationClosed signal, as per the spec. Fixes bug #137.
+	  - Bump spec compliance to 1.0.
+
+Sat Aug 25 18:15:02 PDT 2007  Christian Hammond <chipx86@chipx86.com>
+
+	A po/ar.po:
+	* AUTHORS:
+	* configure.ac:
+	  - Added an Arabic translation courtesy of Djihed Afifi. Closes
+	    ticket #131.
+
+Sat Aug 25 17:58:35 PDT 2007  Christian Hammond <chipx86@chipx86.com>
+
+	* src/daemon/daemon.c:
+	* src/daemon/daemon.h:
+	  - Return an error when attempting to close a notification of ID 0.
+
+Wed Jun 13 03:12:31 PDT 2007  Christian Hammond <chipx86@chipx86.com>
+
+	* src/themes/standard/theme.c:
+	  - Patch by M.S. to fix a bug where notifications with arrows were
+	    crossing the monitor on multihead setups instead of staying on their
+	    head. (Bug #5)
+
+Wed Jun 13 02:58:07 PDT 2007  Christian Hammond <chipx86@chipx86.com>
+
+	* po/POTFILES.in:
+	* src/capplet/Makefile.am:
+	* src/capplet/notification-properties.c:
+	A src/capplet/notification-properties.glade:
+	* src/Makefile.am:
+	* AUTHORS:
+	* configure.ac:
+	  - Patch by Jonh Wendell (and some code changes and dialog tweaks by me)
+	    to add a control panel applet for specifying the theme and
+	    notification position. This is not complete. We need to support actual
+	    querying of theme engine names. Works for now, though.
+	    Closes ticket #126.
+
+Sun Apr 29 03:43:13 PDT 2007  Christian Hammond <chipx86@chipx86.com>
+
+	* data/notification-daemon.schemas.in:
+	* src/daemon/daemon.c:
+	* src/daemon/daemon.h:
+	* src/daemon/Makefile.am:
+	* AUTHORS:
+	* NEWS:
+	* configure.ac:
+	  - Added support for playing sounds when the "sound-file" hint is set or
+	    when the default_sound GConf key is set, as well as support for the
+	    "suppress-sound" hint. Patch by Jim Ramsay. (Ticket #111)
+
+Sun Apr 29 02:38:12 PDT 2007  Christian Hammond <chipx86@chipx86.com>
+
+	* src/themes/standard/theme.c:
+	  - Fix the close button size so that it's not stretched. This fixes (at
+	    least partially) bug #127. Patch by Luca Cavelli.
+
+Sun Apr 29 01:02:12 PDT 2007  Christian Hammond <chipx86@chipx86.com>
+
+	* src/daemon/engines.c:
+	  - Patch by driehuis to prevent quitting on theme engine failure. We now
+	    spit out an error and then fall back to the default theme. Closes
+	    ticket #128.
+
 ==================== 0.3.7 ====================
 
 Tue Feb 27 23:19:00 PST 2007  Christian Hammond <chipx86@chipx86.com>
Index: src/daemon/sound.c
===================================================================
--- src/daemon/sound.c	(révision 0)
+++ src/daemon/sound.c	(révision 3018)
@@ -0,0 +1,80 @@
+/*
+ * sound.c - Sound support portion of the destop notification spec
+ *
+ * Copyright (C) 2007 Jim Ramsay <i.am@jimramsay.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#include "config.h"
+
+#include "sound.h"
+
+#ifdef HAVE_GSTREAMER
+#include <gst/gst.h>
+
+static GstElement *player;
+
+static void
+sound_play_uri(const gchar* uri)
+{  
+	/*
+	 * TODO: Fade out the current sound and then start the new sound?
+	 *       Right now we just cut off the existing sound, which is kind of
+	 *       abrupt
+	 */
+
+	/* Stop the pipeline */
+	gst_element_set_state(player, GST_STATE_NULL);
+
+	/* Set the input to a local file uri */
+	g_object_set(G_OBJECT(player), "uri", uri, NULL);
+
+	/* Start the pipeline again */
+	gst_element_set_state(player, GST_STATE_PLAYING);
+}
+#endif /* HAVE_GSTREAMER */
+
+void
+sound_init(void)
+{  
+#ifdef HAVE_GSTREAMER
+	gst_init(NULL, NULL);
+
+	player = gst_element_factory_make("playbin", "Notification Player");
+
+	/*
+	 * Instead of using the default audiosink, use the gconfaudiosink,
+	 * which will respect the defaults in gstreamer-properties
+	 */
+	g_object_set(G_OBJECT(player), "audio-sink",
+		gst_element_factory_make("gconfaudiosink", "GconfAudioSink"),
+		NULL);
+
+#endif /* HAVE_GSTREAMER */
+}
+
+void
+sound_play(const gchar* filename)
+{
+	/* We are guaranteed here that the file exists */
+#ifdef HAVE_GSTREAMER
+	/* gstreamer's playbin takes uris, so make a file:// uri */
+	gchar* uri = g_strdup_printf("file://%s", filename);
+	sound_play_uri(uri);
+	g_free(uri);
+#endif /* HAVE_GSTREAMER */
+}
+
Index: src/daemon/sound.h
===================================================================
--- src/daemon/sound.h	(révision 0)
+++ src/daemon/sound.h	(révision 3018)
@@ -0,0 +1,30 @@
+/*
+ * sound.h - Sound support portion of the destop notification spec
+ *
+ * Copyright (C) 2007 Jim Ramsay <i.am@jimramsay.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#ifndef _SOUND_H
+#define _SOUND_H
+
+#include <glib.h>
+
+void sound_init(void);
+
+void sound_play(const gchar *filename);
+
+#endif /* _SOUND_H */
Index: src/daemon/daemon.c
===================================================================
--- src/daemon/daemon.c	(révision 2976)
+++ src/daemon/daemon.c	(copie de travail)
@@ -45,6 +45,7 @@
 #include "daemon.h"
 #include "engines.h"
 #include "stack.h"
+#include "sound.h"
 #include "notificationdaemon-dbus-glue.h"
 
 #define IMAGE_SIZE 48
@@ -82,13 +83,15 @@ typedef struct
 	gboolean paused;
 	guint id;
 	GtkWindow *nw;
-
+	Window src_window_xid;
 } NotifyTimeout;
 
 struct _NotifyDaemonPrivate
 {
 	guint next_id;
 	guint timeout_source;
+	GHashTable *idle_reposition_notify_ids;
+	GHashTable *monitored_window_hash;
 	GHashTable *notification_hash;
 	gboolean url_clicked_lock;
 	NotifyStack **stacks;
@@ -115,10 +118,19 @@ struct _DBusGMethodInvocation
 
 static void notify_daemon_finalize(GObject *object);
 static void _close_notification(NotifyDaemon *daemon, guint id,
-								gboolean hide_notification);
-static void _emit_closed_signal(GtkWindow *nw);
+								gboolean hide_notification,
+								NotifydClosedReason reason);
+static GdkFilterReturn _notify_x11_filter(GdkXEvent *xevent,
+										  GdkEvent *event,
+										  gpointer user_data);
+static void _emit_closed_signal(GtkWindow *nw, NotifydClosedReason reason);
 static void _action_invoked_cb(GtkWindow *nw, const char *key);
 static NotifyStackLocation get_stack_location_from_string(const char *slocation);
+static void sync_notification_position(NotifyDaemon *daemon, GtkWindow *nw,
+									   Window source);
+static void monitor_notification_source_windows(NotifyDaemon *daemon,
+												NotifyTimeout *nt,
+												Window source);
 
 G_DEFINE_TYPE(NotifyDaemon, notify_daemon, G_TYPE_OBJECT);
 
@@ -165,6 +177,10 @@ notify_daemon_init(NotifyDaemon *daemon)
 	daemon->priv->stacks_size = gdk_screen_get_n_monitors(screen);
 	daemon->priv->stacks = g_new0(NotifyStack *, daemon->priv->stacks_size);
 
+	daemon->priv->idle_reposition_notify_ids = g_hash_table_new(NULL, NULL);
+	daemon->priv->monitored_window_hash = g_hash_table_new(NULL, NULL);
+	gdk_window_add_filter(NULL, _notify_x11_filter, daemon);
+
 	for (i = 0; i < daemon->priv->stacks_size; i++)
 	{
 		daemon->priv->stacks[i] = notify_stack_new(daemon, screen,
@@ -182,6 +198,8 @@ notify_daemon_finalize(GObject *object)
 	NotifyDaemon *daemon       = NOTIFY_DAEMON(object);
 	GObjectClass *parent_class = G_OBJECT_CLASS(notify_daemon_parent_class);
 
+	g_hash_table_destroy(daemon->priv->monitored_window_hash);
+	g_hash_table_destroy(daemon->priv->idle_reposition_notify_ids);
 	g_hash_table_destroy(daemon->priv->notification_hash);
 	g_free(daemon->priv);
 
@@ -248,19 +266,23 @@ _action_invoked_cb(GtkWindow *nw, const
 	dbus_connection_send(dbus_conn, message, NULL);
 	dbus_message_unref(message);
 
-	_close_notification(daemon, id, TRUE);
+	_close_notification(daemon, id, TRUE, NOTIFYD_CLOSED_USER);
 }
 
 static void
-_emit_closed_signal(GtkWindow *nw)
+_emit_closed_signal(GtkWindow *nw, NotifydClosedReason reason)
 {
 	DBusMessage *message = create_signal(nw, "NotificationClosed");
+	dbus_message_append_args(message,
+							 DBUS_TYPE_UINT32, &reason,
+							 DBUS_TYPE_INVALID);
 	dbus_connection_send(dbus_conn, message, NULL);
 	dbus_message_unref(message);
 }
 
 static void
-_close_notification(NotifyDaemon *daemon, guint id, gboolean hide_notification)
+_close_notification(NotifyDaemon *daemon, guint id,
+					gboolean hide_notification, NotifydClosedReason reason)
 {
 	NotifyDaemonPrivate *priv = daemon->priv;
 	NotifyTimeout *nt;
@@ -269,7 +291,7 @@ _close_notification(NotifyDaemon *daemon
 
 	if (nt != NULL)
 	{
-		_emit_closed_signal(nt->nw);
+		_emit_closed_signal(nt->nw, reason);
 
 		if (hide_notification)
 			theme_hide_notification(nt->nw);
@@ -281,7 +303,118 @@ _close_notification(NotifyDaemon *daemon
 static void
 _notification_destroyed_cb(GtkWindow *nw, NotifyDaemon *daemon)
 {
-	_close_notification(daemon, NW_GET_NOTIFY_ID(nw), FALSE);
+	/*
+	 * This usually won't happen, but can if notification-daemon dies before
+	 * all notifications are closed. Mark them as expired.
+	 */
+	_close_notification(daemon, NW_GET_NOTIFY_ID(nw), FALSE,
+						NOTIFYD_CLOSED_EXPIRED);
+}
+
+typedef struct
+{
+	NotifyDaemon *daemon;
+	gint id;
+} IdleRepositionData;
+
+static gboolean
+idle_reposition_notification(gpointer datap)
+{
+	IdleRepositionData *data = (IdleRepositionData *)datap;
+	NotifyDaemon *daemon = data->daemon;
+	NotifyTimeout *nt;
+	gint notify_id;
+
+	notify_id = data->id;
+
+	/* Look up the timeout, if it's completed we don't need to do anything */
+	nt = (NotifyTimeout *)g_hash_table_lookup(daemon->priv->notification_hash,
+											  &notify_id);
+	if (nt != NULL) {
+		sync_notification_position(daemon, nt->nw, nt->src_window_xid);
+	}
+
+	g_hash_table_remove(daemon->priv->idle_reposition_notify_ids,
+						GINT_TO_POINTER(notify_id));
+	g_object_unref(daemon);
+	g_free(data);
+
+	return FALSE;
+}
+
+static void
+_queue_idle_reposition_notification(NotifyDaemon *daemon, gint notify_id)
+{
+	IdleRepositionData *data;
+	gpointer orig_key;
+	gpointer value;
+	guint idle_id;
+
+	/* Do we already have an idle update pending? */
+	if (g_hash_table_lookup_extended(daemon->priv->idle_reposition_notify_ids,
+									 GINT_TO_POINTER(notify_id),
+									 &orig_key, &value))
+	{
+		return;
+	}
+
+	data = g_new0(IdleRepositionData, 1);
+	data->daemon = g_object_ref(daemon);
+	data->id = notify_id;
+
+	/* We do this as a short timeout to avoid repositioning spam */
+	idle_id = g_timeout_add_full(G_PRIORITY_LOW, 50,
+								 idle_reposition_notification, data, NULL);
+	g_hash_table_insert(daemon->priv->idle_reposition_notify_ids,
+						GINT_TO_POINTER(notify_id), GUINT_TO_POINTER(idle_id));
+}
+
+static GdkFilterReturn
+_notify_x11_filter(GdkXEvent *xevent,
+				   GdkEvent *event,
+				   gpointer user_data)
+{
+	NotifyDaemon *daemon = NOTIFY_DAEMON(user_data);
+	XEvent *xev = (XEvent *)xevent;
+	gpointer orig_key;
+	gpointer value;
+	gint notify_id;
+	NotifyTimeout *nt;
+
+	if (xev->xany.type == DestroyNotify)
+	{
+		g_hash_table_remove(daemon->priv->monitored_window_hash,
+							GUINT_TO_POINTER(xev->xany.window));
+		return GDK_FILTER_CONTINUE;
+	}
+
+	if (!g_hash_table_lookup_extended(daemon->priv->monitored_window_hash,
+					 GUINT_TO_POINTER(xev->xany.window), &orig_key, &value))
+		return GDK_FILTER_CONTINUE;
+
+	notify_id = GPOINTER_TO_INT(value);
+
+	if (xev->xany.type == ConfigureNotify || xev->xany.type == MapNotify)
+	{
+		_queue_idle_reposition_notification(daemon, notify_id);
+	}
+	else if (xev->xany.type == ReparentNotify)
+	{
+		nt = (NotifyTimeout *)g_hash_table_lookup(
+			daemon->priv->notification_hash, &notify_id);
+
+		if (nt == NULL)
+			return GDK_FILTER_CONTINUE;
+
+		/*
+		 * If the window got reparented, we need to start monitoring the
+		 * new parents.
+		 */
+		monitor_notification_source_windows(daemon, nt, nt->src_window_xid);
+		sync_notification_position(daemon, nt->nw, nt->src_window_xid);
+	}
+
+	return GDK_FILTER_CONTINUE;
 }
 
 static void
@@ -349,7 +482,7 @@ _is_expired(gpointer key, gpointer value
 	if (now_time > expiration_time)
 	{
 		theme_notification_tick(nt->nw, 0);
-		_emit_closed_signal(nt->nw);
+		_emit_closed_signal(nt->nw, NOTIFYD_CLOSED_EXPIRED);
 		return TRUE;
 	}
 	else if (nt->paused)
@@ -425,7 +558,7 @@ _calculate_timeout(NotifyDaemon *daemon,
 	}
 }
 
-static guint
+static NotifyTimeout *
 _store_notification(NotifyDaemon *daemon, GtkWindow *nw, int timeout)
 {
 	NotifyDaemonPrivate *priv = daemon->priv;
@@ -455,7 +588,7 @@ _store_notification(NotifyDaemon *daemon
 	g_hash_table_insert(priv->notification_hash,
 						g_memdup(&id, sizeof(guint)), nt);
 
-	return id;
+	return nt;
 }
 
 static gboolean
@@ -623,7 +756,8 @@ _notify_daemon_process_icon_data(NotifyD
 	if (expected_len != tmp_array->len)
 	{
 		g_warning("_notify_daemon_process_icon_data expected image "
-				  "data to be of length %i but got a length of %i",
+				  "data to be of length %" G_GSIZE_FORMAT " but got a "
+				  "length of %u",
 				  expected_len, tmp_array->len);
 		return FALSE;
 	}
@@ -650,7 +784,8 @@ window_clicked_cb(GtkWindow *nw, GdkEven
 	}
 
 	_action_invoked_cb(nw, "default");
-	_close_notification(daemon, NW_GET_NOTIFY_ID(nw), TRUE);
+	_close_notification(daemon, NW_GET_NOTIFY_ID(nw), TRUE,
+						NOTIFYD_CLOSED_USER);
 }
 
 static void
@@ -772,19 +907,24 @@ static gboolean
 fullscreen_window_exists(GtkWidget *nw)
 {
 	WnckScreen *wnck_screen;
+	WnckWorkspace *wnck_workspace;
 	GList *l;
 
 	wnck_screen = wnck_screen_get(GDK_SCREEN_XNUMBER(
 		gdk_drawable_get_screen(GDK_DRAWABLE(GTK_WIDGET(nw)->window))));
 	wnck_screen_force_update(wnck_screen);
 
+	wnck_workspace = wnck_screen_get_active_workspace(wnck_screen);
+
 	for (l = wnck_screen_get_windows_stacked(wnck_screen);
 		 l != NULL;
 		 l = l->next)
 	{
 		WnckWindow *wnck_win = (WnckWindow *)l->data;
 
-		if (wnck_window_is_fullscreen(wnck_win))
+		if (wnck_window_is_on_workspace(wnck_win, wnck_workspace) &&
+			wnck_window_is_fullscreen(wnck_win) &&
+			wnck_window_is_active(wnck_win))
 		{
 			/*
 			 * Sanity check if the window is _really_ fullscreen to
@@ -805,6 +945,113 @@ fullscreen_window_exists(GtkWidget *nw)
 	return FALSE;
 }
 
+static Window
+get_window_parent(Display *display,
+				  Window window,
+				  Window *root)
+{
+	Window parent;
+	Window *children = NULL;
+	guint nchildren;
+	gboolean result;
+
+	gdk_error_trap_push();
+	result = XQueryTree(display, window, root, &parent, &children, &nchildren);
+	if (gdk_error_trap_pop() || !result)
+		return None;
+
+	if (children)
+		XFree(children);
+
+	return parent;
+}
+
+/*
+ * Recurse over X Window and parents, up to root, and start watching them
+ * for position changes.
+ */
+static void
+monitor_notification_source_windows(NotifyDaemon *daemon,
+									NotifyTimeout *nt,
+									Window source)
+{
+	Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+	Window root = None;
+	Window parent;
+
+	/* Store the window in the timeout */
+	g_assert(nt != NULL);
+	nt->src_window_xid = source;
+
+	for (parent = get_window_parent(display, source, &root);
+		 parent != None && root != parent;
+		 parent = get_window_parent(display, parent, &root)) {
+
+		XSelectInput(display, parent, StructureNotifyMask);
+		g_hash_table_insert(daemon->priv->monitored_window_hash,
+							GUINT_TO_POINTER(parent), GINT_TO_POINTER(nt->id));
+	}
+}
+
+/* Use a source X Window ID to reposition a notification. */
+static void
+sync_notification_position(NotifyDaemon *daemon,
+						   GtkWindow *nw,
+						   Window source)
+{
+	Display *display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+	Status result;
+	Window root;
+	Window child;
+	int x, y;
+	unsigned int width, height;
+	unsigned int border_width, depth;
+
+	gdk_error_trap_push();
+
+	/* Get the root for this window */
+	result = XGetGeometry(display, source, &root, &x, &y,
+						  &width, &height, &border_width, &depth);
+	if (gdk_error_trap_pop() || !result)
+		return;
+
+	/*
+	 * Now calculate the offset coordinates for the source window from
+	 * the root.
+	 */
+	gdk_error_trap_push ();
+	result = XTranslateCoordinates(display, source, root, 0, 0,
+								   &x, &y, &child);
+	if (gdk_error_trap_pop() || !result)
+		return;
+
+	x += width  / 2;
+	y += height / 2;
+
+	theme_set_notification_arrow(nw, TRUE, x, y);
+	theme_move_notification(nw, x, y);
+	theme_show_notification(nw);
+
+	/*
+	 * We need to manually queue a draw here as the default theme recalculates
+	 * its position in the draw handler and moves the window (which seems
+	 * fairly broken), so just calling move/show above isn't enough to cause
+	 * its position to be calculated.
+	 */
+	gtk_widget_queue_draw(GTK_WIDGET(nw));
+}
+
+GQuark
+notify_daemon_error_quark(void)
+{
+	static GQuark q = 0;
+
+	if (q == 0)
+		q = g_quark_from_static_string("notification-daemon-error-quark");
+
+	return q;
+}
+
 gboolean
 notify_daemon_notify_handler(NotifyDaemon *daemon,
 							 const gchar *app_name,
@@ -824,8 +1071,11 @@ notify_daemon_notify_handler(NotifyDaemo
 	gboolean new_notification = FALSE;
 	gint x = 0;
 	gint y = 0;
+	Window window_xid = None;
 	guint return_id;
 	gchar *sender;
+	gchar *sound_file = NULL;
+	gboolean sound_enabled;
 	gint i;
 
 	if (id > 0)
@@ -867,8 +1117,13 @@ notify_daemon_notify_handler(NotifyDaemo
 	 *XXX This needs to handle file URIs and all that.
 	 */
 
+
+	if ((data = (GValue *)g_hash_table_lookup(hints, "window-xid")) != NULL)
+	{
+		window_xid = (Window)g_value_get_uint(data);
+	}
 	/* deal with x, and y hints */
-	if ((data = (GValue *)g_hash_table_lookup(hints, "x")) != NULL)
+	else if ((data = (GValue *)g_hash_table_lookup(hints, "x")) != NULL)
 	{
 		x = g_value_get_int(data);
 
@@ -879,6 +1134,63 @@ notify_daemon_notify_handler(NotifyDaemo
 		}
 	}
 
+	/* Deal with sound hints */
+	sound_enabled = gconf_client_get_bool(gconf_client,
+										  GCONF_KEY_SOUND_ENABLED, NULL);
+	data = (GValue *)g_hash_table_lookup(hints, "suppress-sound");
+
+	if (data != NULL)
+	{
+		if (G_VALUE_HOLDS_BOOLEAN(data))
+			sound_enabled = !g_value_get_boolean(data);
+		else if (G_VALUE_HOLDS_INT(data))
+			sound_enabled = (g_value_get_int(data) != 0);
+		else
+		{
+			g_warning("suppress-sound is of type %s (expected bool or int)\n",
+					  g_type_name(G_VALUE_TYPE(data)));
+		}
+	}
+
+	if (sound_enabled)
+	{
+		data = (GValue *)g_hash_table_lookup(hints, "sound-file");
+
+		if (data != NULL)
+		{
+			sound_file = g_value_dup_string(data);
+
+			if (*sound_file == '\0' ||
+				!g_file_test(sound_file, G_FILE_TEST_EXISTS))
+			{
+				g_free(sound_file);
+				sound_file = NULL;
+			}
+		}
+
+		/*
+		 * TODO: If we don't have a sound_file yet, get the urgency hint, then
+		 *       get the corresponding system event sound
+		 *
+		 *       We will need to parse /etc/sound/events/gnome-2.soundlist
+		 *       and ~/.gnome2/sound/events/gnome-2.soundlist.
+		 */
+
+		/* If we don't have a sound file yet, use our gconf default */
+		if (sound_file == NULL)
+		{
+			sound_file = gconf_client_get_string(gconf_client,
+												 GCONF_KEY_DEFAULT_SOUND, NULL);
+			if (sound_file != NULL &&
+				(*sound_file == '\0' ||
+				 !g_file_test(sound_file, G_FILE_TEST_EXISTS)))
+			{
+				g_free(sound_file);
+				sound_file = NULL;
+			}
+		}
+	}
+
 	/* set up action buttons */
 	for (i = 0; actions[i] != NULL; i += 2)
 	{
@@ -957,7 +1269,15 @@ notify_daemon_notify_handler(NotifyDaemo
 		}
 	}
 
-	if (use_pos_data)
+
+	if (window_xid != None)
+	{
+		/*
+		 * Do nothing here if we were passed an XID; we'll call
+		 * sync_notification_position later.
+		 */
+	}
+	else if (use_pos_data)
 	{
 		/*
 		 * Typically, the theme engine will set its own position based on
@@ -983,13 +1303,34 @@ notify_daemon_notify_handler(NotifyDaemo
 		notify_stack_add_window(priv->stacks[monitor], nw, new_notification);
 	}
 
+	if (id == 0)
+	{
+		nt = _store_notification(daemon, nw, timeout);
+		return_id = nt->id;
+	}
+	else
+		return_id = id;
+
+	/*
+	 * If we have a source Window XID, start monitoring the tree
+	 * for changes, and reposition the window based on the source
+	 * window.  We need to do this after return_id is calculated.
+	 */
+	if (window_xid != None)
+	{
+		monitor_notification_source_windows(daemon, nt, window_xid);
+		sync_notification_position(daemon, nw, window_xid);
+	}
+
 	if (!screensaver_active(GTK_WIDGET(nw)) &&
 		!fullscreen_window_exists(GTK_WIDGET(nw)))
 	{
 		theme_show_notification(nw);
+		if (sound_file != NULL)
+			sound_play(sound_file);
 	}
 
-	return_id = (id == 0 ? _store_notification(daemon, nw, timeout) : id);
+	g_free(sound_file);
 
 #if CHECK_DBUS_VERSION(0, 60)
 	sender = dbus_g_method_get_sender(context);
@@ -1015,9 +1356,15 @@ gboolean
 notify_daemon_close_notification_handler(NotifyDaemon *daemon,
 										 guint id, GError **error)
 {
-	_close_notification(daemon, id, TRUE);
-
-	return TRUE;
+	if (id == 0)
+	{
+		g_set_error(error, notify_daemon_error_quark(), 100,
+					_("%u is not a valid notification ID"), id);
+		return FALSE;
+	} else {
+		_close_notification(daemon, id, TRUE, NOTIFYD_CLOSED_API);
+		return TRUE;
+	}
 }
 
 gboolean
@@ -1045,7 +1392,7 @@ notify_daemon_get_server_information(Not
 	*out_name     = g_strdup("Notification Daemon");
 	*out_vendor   = g_strdup("Galago Project");
 	*out_version  = g_strdup(VERSION);
-	*out_spec_ver = g_strdup("0.9");
+	*out_spec_ver = g_strdup("1.0");
 
 	return TRUE;
 }
@@ -1067,6 +1414,8 @@ main(int argc, char **argv)
 
 	g_log_set_always_fatal(G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
 
+	sound_init();
+
 	gtk_init(&argc, &argv);
 	gconf_init(argc, argv, NULL);
 
Index: src/daemon/Makefile.am
===================================================================
--- src/daemon/Makefile.am	(révision 2976)
+++ src/daemon/Makefile.am	(copie de travail)
@@ -6,9 +6,11 @@ notification_daemon_SOURCES = \
 	engines.c \
 	engines.h \
 	stack.c \
-	stack.h
+	stack.h \
+	sound.c \
+	sound.h
 
-notification_daemon_LDADD = $(NOTIFICATION_DAEMON_LIBS)
+notification_daemon_LDADD = $(NOTIFICATION_DAEMON_LIBS) $(GSTREAMER_LIBS)
 
 BUILT_SOURCES = notificationdaemon-dbus-glue.h
 
@@ -19,6 +21,7 @@ notificationdaemon-dbus-glue.h: notifica
 INCLUDES = \
 	-I$(top_srcdir) \
 	$(NOTIFICATION_DAEMON_CFLAGS) \
+	$(GSTREAMER_CFLAGS) \
 	-DENGINES_DIR=\"$(libdir)/notification-daemon-1.0/engines\"
 
 EXTRA_DIST = notificationdaemon.xml
Index: src/daemon/daemon.h
===================================================================
--- src/daemon/daemon.h	(révision 2976)
+++ src/daemon/daemon.h	(copie de travail)
@@ -32,6 +32,8 @@
 #define GCONF_KEY_DAEMON         "/apps/notification-daemon"
 #define GCONF_KEY_THEME          GCONF_KEY_DAEMON "/theme"
 #define GCONF_KEY_POPUP_LOCATION GCONF_KEY_DAEMON "/popup_location"
+#define GCONF_KEY_SOUND_ENABLED  GCONF_KEY_DAEMON "/sound_enabled"
+#define GCONF_KEY_DEFAULT_SOUND  GCONF_KEY_DAEMON "/default_sound"
 
 #define NOTIFY_TYPE_DAEMON (notify_daemon_get_type())
 #define NOTIFY_DAEMON(obj) \
@@ -47,6 +49,22 @@
 
 #define NOTIFY_DAEMON_DEFAULT_TIMEOUT 7000
 
+enum
+{
+	URGENCY_LOW,
+	URGENCY_NORMAL,
+	URGENCY_CRITICAL
+};
+
+typedef enum
+{
+	NOTIFYD_CLOSED_EXPIRED = 1,
+	NOTIFYD_CLOSED_USER = 2,
+	NOTIFYD_CLOSED_API = 3,
+	NOTIFYD_CLOSED_RESERVED = 4
+
+} NotifydClosedReason;
+
 typedef struct _NotifyDaemon        NotifyDaemon;
 typedef struct _NotifyDaemonClass   NotifyDaemonClass;
 typedef struct _NotifyDaemonPrivate NotifyDaemonPrivate;
@@ -64,17 +82,11 @@ struct _NotifyDaemonClass
 	GObjectClass parent_class;
 };
 
-enum _NotifyDaemonError
-{
-	NOTIFY_DAEMON_ERROR_GENERIC = 0,
-};
-
 G_BEGIN_DECLS
 
 GType notify_daemon_get_type(void);
 
-NotifyDaemon *notify_daemon_new(void)
-	G_GNUC_MALLOC;
+GQuark notify_daemon_error_quark(void);
 
 gboolean notify_daemon_notify_handler(NotifyDaemon *daemon,
 									  const gchar *app_name,
Index: src/daemon/engines.c
===================================================================
--- src/daemon/engines.c	(révision 2976)
+++ src/daemon/engines.c	(copie de travail)
@@ -60,7 +60,7 @@ load_theme_engine(const char *name)
 	if (!g_module_symbol(engine->module, #name, (gpointer *)&engine->name)) \
 	{ \
 		/* Too harsh! Fall back to default. */ \
-		g_error("Theme doesn't provide the required function '%s'", #name); \
+		g_warning("Theme doesn't provide the required function '%s'", #name); \
 		goto error; \
 	}
 
Index: src/themes/standard/theme.c
===================================================================
--- src/themes/standard/theme.c	(révision 2976)
+++ src/themes/standard/theme.c	(copie de travail)
@@ -80,16 +80,11 @@ enum
 #define BACKGROUND_OPACITY    0.92
 #define BOTTOM_GRADIENT_HEIGHT 30
 
-#if GTK_CHECK_VERSION(2, 8, 0)
-# define USE_CAIRO
-#endif
-
 #if GTK_CHECK_VERSION(2, 10, 0)
 # define USE_COMPOSITE
 #endif
 
 
-#ifdef USE_CAIRO
 static void
 fill_background(GtkWidget *widget, WindowData *windata, cairo_t *cr)
 {
@@ -131,21 +126,7 @@ fill_background(GtkWidget *widget, Windo
 	cairo_pattern_destroy(gradient);
 #endif
 }
-#else /* !USE_CAIRO */
-static void
-fill_background(GtkWidget *widget, WindowData *windata)
-{
-	GtkStyle *style = gtk_widget_get_style(windata->win);
-
-	gdk_draw_rectangle(GDK_DRAWABLE(widget->window),
-					   style->base_gc[GTK_STATE_NORMAL], TRUE,
-					   0, 0,
-					   widget->allocation.width,
-					   widget->allocation.height);
-}
-#endif
 
-#ifdef USE_CAIRO
 static void
 draw_stripe(GtkWidget *widget, WindowData *windata, cairo_t *cr)
 {
@@ -193,57 +174,22 @@ draw_stripe(GtkWidget *widget, WindowDat
 	cairo_fill(cr);
 #endif
 }
-#else /* !USE_CAIRO */
-static void
-draw_stripe(GtkWidget *widget, WindowData *windata)
-{
-	GtkStyle *style = gtk_widget_get_style(widget);
-	gboolean custom_gc = FALSE;
-	GdkColor color;
-	GdkGC *gc;
-
-	switch (windata->urgency)
-	{
-		case URGENCY_LOW: // LOW
-			gc = style->bg_gc[GTK_STATE_NORMAL];
-			break;
-
-		case URGENCY_CRITICAL: // CRITICAL
-			custom_gc = TRUE;
-			gc = gdk_gc_new(GDK_DRAWABLE(widget->window));
-			gdk_color_parse("#CC0000", &color);
-			gdk_gc_set_rgb_fg_color(gc, &color);
-			break;
-
-		case URGENCY_NORMAL: // NORMAL
-		default:
-			gc = style->bg_gc[GTK_STATE_SELECTED];
-			break;
-	}
-
-
-	gdk_draw_rectangle(widget->window, gc, TRUE,
-					   windata->main_hbox->allocation.x + 1,
-					   windata->main_hbox->allocation.y + 1,
-					   STRIPE_WIDTH,
-					   windata->main_hbox->allocation.height - 2);
-
-	if (custom_gc)
-		g_object_unref(G_OBJECT(gc));
-}
-#endif
 
 static GtkArrowType
 get_notification_arrow_type(GtkWidget *nw)
 {
 	WindowData *windata = g_object_get_data(G_OBJECT(nw), "windata");
-	int screen_height;
+	GdkScreen *screen;
+	GdkRectangle monitor_geometry;
+	int monitor;
 
-	screen_height = gdk_screen_get_height(
-		gdk_drawable_get_screen(GDK_DRAWABLE(nw->window)));
+	screen = gdk_drawable_get_screen(GDK_DRAWABLE(nw->window));
+	monitor = gdk_screen_get_monitor_at_point(screen, windata->point_x,
+											  windata->point_y);
+	gdk_screen_get_monitor_geometry(screen, monitor, &monitor_geometry);
 
-	if (windata->point_y + windata->height + DEFAULT_ARROW_HEIGHT >
-		screen_height)
+	if (windata->point_y - monitor_geometry.y + windata->height +
+		DEFAULT_ARROW_HEIGHT > monitor_geometry.height)
 	{
 		return GTK_ARROW_DOWN;
 	}
@@ -268,63 +214,71 @@ create_border_with_arrow(GtkWidget *nw,
 	int width;
 	int height;
 	int y;
+	int norm_point_x;
+	int norm_point_y;
 	GtkArrowType arrow_type;
 	GdkScreen *screen;
-	int screen_width;
-	int screen_height;
 	int arrow_side1_width = DEFAULT_ARROW_WIDTH / 2;
 	int arrow_side2_width = DEFAULT_ARROW_WIDTH / 2;
 	int arrow_offset = DEFAULT_ARROW_OFFSET;
 	GdkPoint *shape_points = NULL;
 	int i = 0;
+	int monitor;
+	GdkRectangle monitor_geometry;
 
 	width  = windata->width;
 	height = windata->height;
 
-	screen        = gdk_drawable_get_screen(GDK_DRAWABLE(nw->window));
-	screen_width  = gdk_screen_get_width(screen);
-	screen_height = gdk_screen_get_height(screen);
+	screen  = gdk_drawable_get_screen(GDK_DRAWABLE(nw->window));
+	monitor = gdk_screen_get_monitor_at_point(screen,
+											  windata->point_x,
+											  windata->point_y);
+	gdk_screen_get_monitor_geometry(screen, monitor, &monitor_geometry);
 
 	windata->num_border_points = 5;
 
 	arrow_type = get_notification_arrow_type(windata->win);
 
+	norm_point_x = windata->point_x - monitor_geometry.x;
+	norm_point_y = windata->point_y - monitor_geometry.y;
+
 	/* Handle the offset and such */
 	switch (arrow_type)
 	{
 		case GTK_ARROW_UP:
 		case GTK_ARROW_DOWN:
-			if (windata->point_x < arrow_side1_width)
+			if (norm_point_x < arrow_side1_width)
 			{
 				arrow_side1_width = 0;
 				arrow_offset = 0;
 			}
-			else if (windata->point_x > screen_width - arrow_side2_width)
+			else if (norm_point_x > monitor_geometry.width - arrow_side2_width)
 			{
 				arrow_side2_width = 0;
 				arrow_offset = width - arrow_side1_width;
 			}
 			else
 			{
-				if (windata->point_x - arrow_side2_width + width >=
-					screen_width)
+				if (norm_point_x - arrow_side2_width + width >=
+					monitor_geometry.width)
 				{
 					arrow_offset =
 						width - arrow_side1_width - arrow_side2_width -
-						(screen_width - MAX(windata->point_x +
-											arrow_side1_width,
-											screen_width -
-											DEFAULT_ARROW_OFFSET));
+						monitor_geometry.width -
+						MAX(norm_point_x + arrow_side1_width,
+							monitor_geometry.width - DEFAULT_ARROW_OFFSET);
 				}
 				else
 				{
-					arrow_offset = MIN(windata->point_x - arrow_side1_width,
+					arrow_offset = MIN(norm_point_x - arrow_side1_width,
 									   DEFAULT_ARROW_OFFSET);
 				}
 
 				if (arrow_offset == 0 ||
 					arrow_offset == width - arrow_side1_width)
+				{
 					windata->num_border_points++;
+				}
 				else
 					windata->num_border_points += 2;
 			}
@@ -439,15 +393,15 @@ create_border_with_arrow(GtkWidget *nw,
 
 		case GTK_ARROW_LEFT:
 		case GTK_ARROW_RIGHT:
-			if (windata->point_y < arrow_side1_width)
+			if (norm_point_y < arrow_side1_width)
 			{
 				arrow_side1_width = 0;
-				arrow_offset = windata->point_y;
+				arrow_offset = norm_point_y;
 			}
-			else if (windata->point_y > screen_height - arrow_side2_width)
+			else if (norm_point_y > monitor_geometry.height - arrow_side2_width)
 			{
 				arrow_side2_width = 0;
-				arrow_offset = windata->point_y - arrow_side1_width;
+				arrow_offset = norm_point_y - arrow_side1_width;
 			}
 			break;
 
@@ -463,7 +417,6 @@ create_border_with_arrow(GtkWidget *nw,
 	g_free(shape_points);
 }
 
-#ifdef USE_CAIRO
 static void
 draw_border(GtkWidget *widget, WindowData *windata, cairo_t *cr)
 {
@@ -501,56 +454,21 @@ draw_border(GtkWidget *widget, WindowDat
 
 	cairo_stroke(cr);
 }
-#else /* !USE_CAIRO */
-static void
-draw_border(GtkWidget *widget,
-			WindowData *windata)
-{
-	if (windata->gc == NULL)
-	{
-		GdkColor color;
-
-		windata->gc = gdk_gc_new(widget->window);
-		gdk_color_parse("black", &color);
-		gdk_gc_set_rgb_fg_color(windata->gc, &color);
-	}
-
-	if (windata->has_arrow)
-	{
-		create_border_with_arrow(windata->win, windata);
-
-		gdk_draw_polygon(widget->window, windata->gc, FALSE,
-						 windata->border_points, windata->num_border_points);
-		gdk_window_shape_combine_region(windata->win->window,
-										windata->window_region,
-										0, 0);
-		g_free(windata->border_points);
-		windata->border_points = NULL;
-	}
-	else
-	{
-		gdk_draw_rectangle(widget->window, windata->gc, FALSE,
-						   0, 0, windata->width - 1, windata->height - 1);
-	}
-
-}
-#endif
 
 static gboolean
 paint_window(GtkWidget *widget,
 			 GdkEventExpose *event,
 			 WindowData *windata)
 {
+	cairo_t *context;
+	cairo_surface_t *surface;
+	cairo_t *cr;
+
 	if (windata->width == 0) {
 		windata->width = windata->win->allocation.width;
 		windata->height = windata->win->allocation.height;
 	}
 
-#ifdef USE_CAIRO
-	cairo_t *context;
-	cairo_surface_t *surface;
-	cairo_t *cr;
-
 	context = gdk_cairo_create(widget->window);
 
 	cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
@@ -569,11 +487,6 @@ paint_window(GtkWidget *widget,
 	cairo_paint(context);
 	cairo_surface_destroy(surface);
 	cairo_destroy(context);
-#else /* !USE_CAIRO */
-	fill_background(widget, windata);
-	draw_border(widget, windata);
-	draw_stripe(widget, windata);
-#endif
 
 	return FALSE;
 }
@@ -666,6 +579,7 @@ create_notification(UrlClickedCb url_cli
 	GtkWidget *image;
 	GtkWidget *alignment;
 	AtkObject *atkobj;
+	GtkRcStyle *rcstyle;
 	WindowData *windata;
 #ifdef USE_COMPOSITE
 	GdkColormap *colormap;
@@ -692,6 +606,8 @@ create_notification(UrlClickedCb url_cli
 #endif
 
 	gtk_window_set_title(GTK_WINDOW(win), "Notification");
+	gtk_window_set_type_hint(GTK_WINDOW(win),
+							 GDK_WINDOW_TYPE_HINT_NOTIFICATION);
 	gtk_widget_add_events(win, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
 	gtk_widget_realize(win);
 	gtk_widget_set_size_request(win, WIDTH, -1);
@@ -762,15 +678,24 @@ create_notification(UrlClickedCb url_cli
 	atk_object_set_description(atkobj, "Notification summary text.");
 
 	/* Add the close button */
+	alignment = gtk_alignment_new(1, 0, 0, 0);
+	gtk_widget_show(alignment);
+	gtk_box_pack_start(GTK_BOX(hbox), alignment, FALSE, FALSE, 0);
+
 	close_button = gtk_button_new();
 	gtk_widget_show(close_button);
-	gtk_box_pack_start(GTK_BOX(hbox), close_button, FALSE, FALSE, 0);
+	gtk_container_add(GTK_CONTAINER(alignment), close_button);
 	gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE);
 	gtk_container_set_border_width(GTK_CONTAINER(close_button), 0);
-	gtk_widget_set_size_request(close_button, 20, 20);
+	//gtk_widget_set_size_request(close_button, 20, 20);
 	g_signal_connect_swapped(G_OBJECT(close_button), "clicked",
 							 G_CALLBACK(gtk_widget_destroy), win);
 
+	rcstyle = gtk_rc_style_new();
+	rcstyle->xthickness = rcstyle->ythickness = 0;
+	gtk_widget_modify_style(close_button, rcstyle);
+	gtk_rc_style_unref(rcstyle);
+
 	atkobj = gtk_widget_get_accessible(close_button);
 	atk_action_set_description(ATK_ACTION(atkobj), 0,
 							   "Closes the notification.");
@@ -829,7 +754,7 @@ set_notification_hints(GtkWindow *nw, GH
 
 	value = (GValue *)g_hash_table_lookup(hints, "urgency");
 
-	if (value != NULL)
+	if (value != NULL && G_VALUE_HOLDS_UCHAR(value))
 	{
 		windata->urgency = g_value_get_uchar(value);
 
@@ -866,11 +791,14 @@ notification_tick(GtkWindow *nw, glong r
 void
 set_notification_text(GtkWindow *nw, const char *summary, const char *body)
 {
-	char *str;
+	char *str, *quoted;
 	WindowData *windata = g_object_get_data(G_OBJECT(nw), "windata");
 	g_assert(windata != NULL);
 
-	str = g_strdup_printf("<b><big>%s</big></b>", summary);
+	quoted = g_markup_escape_text(summary, -1);
+	str = g_strdup_printf("<b><big>%s</big></b>", quoted);
+	g_free(quoted);
+
 	gtk_label_set_markup(GTK_LABEL(windata->summary_label), str);
 	g_free(str);
 
@@ -933,11 +861,10 @@ countdown_expose_cb(GtkWidget *pie, GdkE
 					WindowData *windata)
 {
 	GtkStyle *style = gtk_widget_get_style(windata->win);
-
-#ifdef USE_CAIRO
 	cairo_t *context;
 	cairo_surface_t *surface;
 	cairo_t *cr;
+
 	context = gdk_cairo_create(GDK_DRAWABLE(windata->pie_countdown->window));
 	cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
 	surface = cairo_surface_create_similar(
@@ -948,15 +875,11 @@ countdown_expose_cb(GtkWidget *pie, GdkE
 	cr = cairo_create(surface);
 
 	fill_background(pie, windata, cr);
-#else /* !USE_CAIRO */
-	fill_background(pie, windata);
-#endif
 
 	if (windata->timeout > 0)
 	{
 		gdouble pct = (gdouble)windata->remaining / (gdouble)windata->timeout;
 
-#ifdef USE_CAIRO
 		gdk_cairo_set_source_color(cr, &style->bg[GTK_STATE_ACTIVE]);
 
 		cairo_move_to(cr, PIE_RADIUS, PIE_RADIUS);
@@ -964,21 +887,13 @@ countdown_expose_cb(GtkWidget *pie, GdkE
 						   -G_PI_2, -(pct * G_PI * 2) - G_PI_2);
 		cairo_line_to(cr, PIE_RADIUS, PIE_RADIUS);
 		cairo_fill(cr);
-#else /* !USE_CAIRO */
-		gdk_draw_arc(GDK_DRAWABLE(windata->pie_countdown->window),
-					 style->bg_gc[GTK_STATE_ACTIVE], TRUE,
-					 0, 0, PIE_WIDTH, PIE_HEIGHT,
-					 90 * 64, pct * 360.0 * 64.0);
-#endif
 	}
 
-#ifdef USE_CAIRO
 	cairo_destroy(cr);
 	cairo_set_source_surface(context, surface, 0, 0);
 	cairo_paint(context);
 	cairo_surface_destroy(surface);
 	cairo_destroy(context);
-#endif
 
 	return TRUE;
 }
Index: src/themes/bubble/eggnotificationbubblewidget.c
===================================================================
--- src/themes/bubble/eggnotificationbubblewidget.c	(révision 2976)
+++ src/themes/bubble/eggnotificationbubblewidget.c	(copie de travail)
@@ -1125,6 +1125,7 @@ egg_notification_bubble_widget_new (void
 {
   return g_object_new (EGG_TYPE_NOTIFICATION_BUBBLE_WIDGET,
                        "type", GTK_WINDOW_POPUP,
+                       "type-hint", GDK_WINDOW_TYPE_HINT_NOTIFICATION,
                        NULL);
 }
 
Index: data/notification-daemon.schemas.in
===================================================================
--- data/notification-daemon.schemas.in	(révision 2976)
+++ data/notification-daemon.schemas.in	(copie de travail)
@@ -25,5 +25,29 @@
    </locale>
   </schema>
 
+  <schema>
+   <key>/schemas/apps/notification-daemon/sound_enabled</key>
+   <applyto>/apps/notification-daemon/sound_enabled</applyto>
+   <owner>notification-daemon</owner>
+   <type>bool</type>
+   <default>1</default>
+   <locale name="C">
+    <short>Sound Enabled</short>
+    <long>Turns on and off sound support for notifications.</long>
+   </locale>
+  </schema>
+
+  <schema>
+   <key>/schemas/apps/notification-daemon/default_sound</key>
+   <applyto>/apps/notification-daemon/default_sound</applyto>
+   <owner>notification-daemon</owner>
+   <type>string</type>
+   <default></default>
+   <locale name="C">
+    <short>Default Sound</short>
+	 <long>The default sound file used unless a notification supplies the 'sound-file' or 'suppress-sound' hint.  Leave empty for no default sound.</long>
+   </locale>
+  </schema>
+
  </schemalist>
 </gconfschemafile>
Index: NEWS
===================================================================
--- NEWS	(révision 2976)
+++ NEWS	(copie de travail)
@@ -1,3 +1,37 @@
+version 0.3.8:
+	* Bumped up the required minimum version of GTK+ to 2.10.0.
+	* Bump the notification spec version we're compliant with to 1.0.
+	* Send the reason code along with the NotificationClosed signal in order
+	  to indicate why the notification was closed. (Bug #137)
+	* Send an error if the user attempts to close an already closed
+	  notification.
+	* Text is now escaped in the summary in the Standard theme so that
+	  ampersands and other special characters show up instead of disappearing.
+	  (Bug #132)
+	* Set the type hint for notifications to TYPE_NOTIFICATION. (Bug #146)
+	* Added support for playing sounds when the "sound-file" hint is set or
+	  when the default_sound GConf key is set, as well as support for the
+	  "suppress-sound" hint. Patch by Jim Ramsay. (Ticket #111)
+	* Added a control panel applet for controlling such things as the
+	  notification theme and popup positions. Patch by John Wendell.
+	  (Ticket #126)
+	* Added better support for attaching context notifications to an icon on
+	  the system tray, even when it moves. Patch by Colin Walters.
+	* Added an Arabic translation. Patch by Djihed Afifi. (Ticket #131)
+	* Added an Italian translation. Patch by Luca Ferretti.
+	* Fixed a bug where notifications weren't displayed if a fullscreen
+	  window was minimized. (Bug #142)
+	* Fixed a bug where we were quitting on theme engine failure, instead of
+	  falling back to the default theme engine. Patch by
+	  driehuis-at-playbeing.org. (Ticket #128)
+	* Fixed a bug where notifications with arrows were crossing the monitor
+	  on multihead setups instead of staying on their head. Patch by M.S.
+	  (Bug #5)
+	* Fixed the close button size on the notifications so that they don't
+	  stretch. Patch by Luca Cavelli. (Bug #127)
+	* Fixed a crash when an unsupported value type was passed in for the
+	  urgency when using the standard theme. (Bug #135)
+
 version 0.3.7 (27-February-2007):
 	* Fixed a compatibility issue with dbus-glib 0.72. Patch by Pawel Worach.
 	  (Bug #95)
Index: po/ar.po
===================================================================
--- po/ar.po	(révision 0)
+++ po/ar.po	(révision 3018)
@@ -0,0 +1,42 @@
+# Arabic translations for notification-daemon package.
+# Copyright (C) 2007 THE notification-daemon'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the notification-daemon package.
+# Djihed Afifi <djihed@gmail.com>, 2007.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: notification-daemon 0.3.7\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-02-15 03:02-0800\n"
+"PO-Revision-Date: 2007-04-21 04:33+0100\n"
+"Last-Translator: Djihed Afifi <djihed@gmail.com>\n"
+"Language-Team: Arabeyes <doc@arabeyes.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Arabic\n"
+
+#: ../data/notification-daemon.schemas.in.h:1
+msgid "Current theme"
+msgstr "النسق الحالي"
+
+#: ../data/notification-daemon.schemas.in.h:2
+msgid "Default popup location on the workspace for stack notifications. Allowed values: \"top_left\",\"top_right\",\"bottom_left\" and \"bottom_right\""
+msgstr "الموضع الافتراضي للتنبيهات على مساحة العمل لصف التنبيهات. القيم الممكنة هي: \"top_left\",\"top_right\",\"bottom_left\" و \"bottom_right\""
+
+#: ../data/notification-daemon.schemas.in.h:3
+msgid "Popup location"
+msgstr "موقع التنبيه"
+
+#: ../data/notification-daemon.schemas.in.h:4
+msgid "The theme used when displaying notifications."
+msgstr "النسق المستخدم عند عرض التنبيهات"
+
+#: ../src/capplet/notification-properties.desktop.in.h:1
+msgid "Pop-Up Notifications"
+msgstr "تنبيهات منبثقة"
+
+#: ../src/capplet/notification-properties.desktop.in.h:2
+msgid "Set your pop-up notification preferences"
+msgstr "حدد تفضيلات التنبيهات"
+
Index: po/pl.po
===================================================================
--- po/pl.po	(révision 0)
+++ po/pl.po	(révision 3018)
@@ -0,0 +1,43 @@
+# translation of pl.po to Polish
+# Piotr Drąg <piotrdrag@gmail.com>, 2007.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: pl\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-02-15 03:02-0800\n"
+"PO-Revision-Date: 2008-03-19 16:03-0700\n"
+"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
+"Language-Team: Polish <pl@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../data/notification-daemon.schemas.in.h:1
+msgid "Current theme"
+msgstr "Obecny motyw"
+
+#: ../data/notification-daemon.schemas.in.h:2
+msgid ""
+"Default popup location on the workspace for stack notifications. Allowed "
+"values: \"top_left\",\"top_right\",\"bottom_left\" and \"bottom_right\""
+msgstr ""
+"Domyślne położenie wyskakującego okna na obszarze roboczym dla stosu "
+"powiadomień. Dozwolone wartości: \"top_left\",\"top_right\",\"bottom_left\" "
+"i \"bottom_right\""
+
+#: ../data/notification-daemon.schemas.in.h:3
+msgid "Popup location"
+msgstr "Położenie wyskakującego okna"
+
+#: ../data/notification-daemon.schemas.in.h:4
+msgid "The theme used when displaying notifications."
+msgstr "Motyw używany podczas wyświetlania powiadomień."
+
+#: ../src/capplet/notification-properties.desktop.in.h:1
+msgid "Pop-Up Notifications"
+msgstr "Wyskakujące powiadomienia"
+
+#: ../src/capplet/notification-properties.desktop.in.h:2
+msgid "Set your pop-up notification preferences"
+msgstr "Preferencje wyskakujących powiadomień"
Index: po/it.po
===================================================================
--- po/it.po	(révision 0)
+++ po/it.po	(révision 3018)
@@ -0,0 +1,136 @@
+# Italian translation for notification-daemon package.
+# Copyright (C) 2007 Free Software Foundation, Inc.
+# This file is distributed under the same license as the notification-daemon package.
+# Luca Ferretti <elle.uca@libero.it>, 2007.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: notification-daemon\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-07-17 10:07+0200\n"
+"PO-Revision-Date: 2007-07-17 10:10+0200\n"
+"Last-Translator: Luca Ferretti <elle.uca@libero.it>\n"
+"Language-Team: Italian <tp@lists.linux.it>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: data/notification-daemon.schemas.in.h:1
+msgid "Current theme"
+msgstr "Tema attuale"
+
+#: data/notification-daemon.schemas.in.h:2
+msgid "Default Sound"
+msgstr "Suono predefinito"
+
+#: data/notification-daemon.schemas.in.h:3
+msgid ""
+"Default popup location on the workspace for stack notifications. Allowed "
+"values: \"top_left\",\"top_right\",\"bottom_left\" and \"bottom_right\""
+msgstr ""
+"Posizione predefinita di comparsa sull'area di lavoro per impilare le "
+"notifiche. Valori ammessi: \"top_left\", \"top_right\", \"bottom_left\" e "
+"\"bottom_right\""
+
+#: data/notification-daemon.schemas.in.h:4
+msgid "Popup location"
+msgstr "Posizione popup"
+
+#: data/notification-daemon.schemas.in.h:5
+msgid "Sound Enabled"
+msgstr "Suono abilitato"
+
+#: data/notification-daemon.schemas.in.h:6
+msgid ""
+"The default sound file used unless a notification supplies the 'sound-file' "
+"or 'suppress-sound' hint. Leave empty for no default sound."
+msgstr ""
+"Il file audio predefinito usato, a meno che una notifica non fornisca l'hint "
+"'sound-file' o 'suppress-sound'. Lasciare in bianco per non usare alcun "
+"suono predefinito."
+
+#: data/notification-daemon.schemas.in.h:7
+msgid "The theme used when displaying notifications."
+msgstr "Il tema usato nel mostrare le notifiche."
+
+#: data/notification-daemon.schemas.in.h:8
+msgid "Turns on and off sound support for notifications."
+msgstr "Attiva e disattiva il supporto audio alle notifiche."
+
+#: src/capplet/notification-properties.c:79
+msgid "Top Left"
+msgstr "Alto - sinistra"
+
+#: src/capplet/notification-properties.c:80
+msgid "Top Right"
+msgstr "Alto - destra"
+
+#: src/capplet/notification-properties.c:81
+msgid "Bottom Left"
+msgstr "Basso - sinistra"
+
+#: src/capplet/notification-properties.c:82
+msgid "Bottom Right"
+msgstr "Basso - destra"
+
+#: src/capplet/notification-properties.c:327
+msgid "Ubuntu theme"
+msgstr "Tema di Ubuntu"
+
+#: src/capplet/notification-properties.c:329
+msgid "Standard theme"
+msgstr "Tema predefinito"
+
+#: src/capplet/notification-properties.c:422
+msgid "Error initializing libnotify"
+msgstr "Errore nell'inizializzare libnotify"
+
+#: src/capplet/notification-properties.c:436
+msgid "Notification Test"
+msgstr "Controllo notifica"
+
+#: src/capplet/notification-properties.c:437
+msgid "Just a test"
+msgstr "Solo un controllo"
+
+#: src/capplet/notification-properties.c:444
+#, c-format
+msgid "Error while displaying notification: %s"
+msgstr "Errore durante la visualizzazione della notifica: %s"
+
+#: src/capplet/notification-properties.c:501
+#, c-format
+msgid "Unable to locate glade file '%s'"
+msgstr "Impossibile localizzare il file glade «%s»"
+
+#: src/capplet/notification-properties.desktop.in.h:1
+msgid "Pop-Up Notifications"
+msgstr "Notifiche a comparsa"
+
+#: src/capplet/notification-properties.desktop.in.h:2
+msgid "Set your pop-up notification preferences"
+msgstr "Imposta le preferenze delle notifiche a comparsa (o popup)"
+
+#: src/capplet/notification-properties.glade.h:1
+msgid "      "
+msgstr "      "
+
+#: src/capplet/notification-properties.glade.h:2
+msgid "<b>General Options</b>"
+msgstr "<b>Opzioni generali</b>"
+
+#: src/capplet/notification-properties.glade.h:3
+msgid "Notification Settings"
+msgstr "Impostazioni di notifiche"
+
+#: src/capplet/notification-properties.glade.h:4
+msgid "_Position:"
+msgstr "_Posizione:"
+
+#: src/capplet/notification-properties.glade.h:5
+msgid "_Preview"
+msgstr "_Anteprima"
+
+#: src/capplet/notification-properties.glade.h:6
+msgid "_Theme:"
+msgstr "_Tema:"
Index: po/ChangeLog
===================================================================
--- po/ChangeLog	(révision 2976)
+++ po/ChangeLog	(copie de travail)
@@ -1,3 +1,8 @@
+Tue Mar 18 20:50:08 PDT 2008  Christian Hammond <chipx86@chipx86.com>
+
+	A pl.po:
+	  - Added a Polish translation by Raven.
+
 Thu Feb 15 03:03:07 PST 2007  Christian Hammond <chipx86@chipx86.com>
 
 	* POTFILES.in:
openSUSE Build Service is sponsored by