summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog26
-rw-r--r--ChangeLog.pre-2-1026
-rw-r--r--gtk/Makefile.am10
-rw-r--r--gtk/gtk.h1
-rw-r--r--gtk/gtk.symbols26
-rwxr-xr-xgtk/gtkstatusicon.c925
-rwxr-xr-xgtk/gtkstatusicon.h108
-rwxr-xr-xgtk/gtktrayicon-x11.c498
-rwxr-xr-xgtk/gtktrayicon.h75
-rw-r--r--tests/Makefile.am6
-rwxr-xr-xtests/teststatusicon.c209
11 files changed, 1906 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 95d729d0a..ff20bae7c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,32 @@
+2005-08-29 Matthias Clasen <mclasen@redhat.com>
+
+ Add a cross-platform "tray icon" API, by
+ porting EggStatusIcon/EggTrayIcon (#105101)
+
+ * gtk/gtkstatusicon.h: A GtkStatusIcon is an object which
+ displays an icon in a notification area.
+
+ * gtk/gtkstatusicon-x11.c: GtkStatusIcon implementation for
+ X11, using GtkTrayIcon.
+
+ * gtk/gtktrayicon.h:
+ * gtk/gtktrayicon.c: An implementation of the freedesktop.org
+ system tray specification, not public API.
+
+ * gtk/gtk.symbols: Add new exported functions.
+
+ * gtk/gtk.h: Include gtkstatusicon.h.
+
+ * gtk/Makefile.am: Add new files.
+
+ * tests/Makefile.am:
+ * tests/teststatusicon.c: Test for GtkStatusIcon.
+
2005-08-29 Christopher Aillon <caillon@redhat.com>
* gtk/gtkstock.c: Add builtin GtkStockItems for GTK_STOCK_CONNECT
and GTK_STOCK_DISCONNECT
-
+
2005-08-29 Matthias Clasen <mclasen@redhat.com>
* gtk/gtksocket-x11.c (_gtk_socket_windowing_embed_notify):
diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10
index 95d729d0a..ff20bae7c 100644
--- a/ChangeLog.pre-2-10
+++ b/ChangeLog.pre-2-10
@@ -1,8 +1,32 @@
+2005-08-29 Matthias Clasen <mclasen@redhat.com>
+
+ Add a cross-platform "tray icon" API, by
+ porting EggStatusIcon/EggTrayIcon (#105101)
+
+ * gtk/gtkstatusicon.h: A GtkStatusIcon is an object which
+ displays an icon in a notification area.
+
+ * gtk/gtkstatusicon-x11.c: GtkStatusIcon implementation for
+ X11, using GtkTrayIcon.
+
+ * gtk/gtktrayicon.h:
+ * gtk/gtktrayicon.c: An implementation of the freedesktop.org
+ system tray specification, not public API.
+
+ * gtk/gtk.symbols: Add new exported functions.
+
+ * gtk/gtk.h: Include gtkstatusicon.h.
+
+ * gtk/Makefile.am: Add new files.
+
+ * tests/Makefile.am:
+ * tests/teststatusicon.c: Test for GtkStatusIcon.
+
2005-08-29 Christopher Aillon <caillon@redhat.com>
* gtk/gtkstock.c: Add builtin GtkStockItems for GTK_STOCK_CONNECT
and GTK_STOCK_DISCONNECT
-
+
2005-08-29 Matthias Clasen <mclasen@redhat.com>
* gtk/gtksocket-x11.c (_gtk_socket_windowing_embed_notify):
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 5f9433081..4ea8eda68 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -239,6 +239,7 @@ gtk_public_h_sources = \
gtksocket.h \
gtkspinbutton.h \
gtkstatusbar.h \
+ gtkstatusicon.h \
gtkstock.h \
gtkstyle.h \
gtktable.h \
@@ -535,8 +536,13 @@ gtk_c_sources += gtkfilesystemwin32.c
endif
if USE_X11
-gtk_private_h_sources += gtkxembed.h
-gtk_c_sources += gtkplug-x11.c gtksocket-x11.c gtkxembed.c
+gtk_private_h_sources += gtkxembed.h gtktrayicon.h
+gtk_c_sources += \
+ gtkplug-x11.c \
+ gtksocket-x11.c \
+ gtkxembed.c \
+ gtktrayicon.c \
+ gtkstatusicon-x11.c
else
if USE_WIN32
gtk_private_h_sources += gtkwin32embed.h
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 17249f5c8..174333fc0 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -150,6 +150,7 @@
#include <gtk/gtksocket.h>
#include <gtk/gtkspinbutton.h>
#include <gtk/gtkstatusbar.h>
+#include <gtk/gtkstatusicon.h>
#include <gtk/gtkstock.h>
#include <gtk/gtkstyle.h>
#include <gtk/gtktable.h>
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 916581397..f0ea5cbdb 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -973,6 +973,32 @@ gtk_drag_unhighlight
#endif
#endif
+#if IN_HEADER(__GTK_STATUS_ICON_H__)
+#if IN_FILE(__GTK_STATUS_ICON_C__)
+gtk_status_icon_get_type G_GNUC_CONST
+gtk_status_icon_new
+gtk_status_icon_new_from_pixbuf
+gtk_status_icon_new_from_file
+gtk_status_icon_new_from_stock
+gtk_status_icon_new_from_icon_name
+gtk_status_icon_set_from_pixbuf
+gtk_status_icon_set_from_file
+gtk_status_icon_set_from_stock
+gtk_status_icon_set_from_icon_name
+gtk_status_icon_get_storage_type
+gtk_status_icon_get_pixbuf
+gtk_status_icon_get_stock
+gtk_status_icon_get_icon_name
+gtk_status_icon_get_size
+gtk_status_icon_set_tooltip
+gtk_status_icon_set_visible
+gtk_status_icon_get_visible
+gtk_status_icon_set_blinking
+gtk_status_icon_get_blinking
+gtk_status_icon_is_embedded
+#endif
+#endif
+
#if IN_HEADER(__GTK_STYLE_H__)
#if IN_FILE(__GTK_STYLE_C__)
#ifndef GTK_DISABLE_DEPRECATED
diff --git a/gtk/gtkstatusicon.c b/gtk/gtkstatusicon.c
new file mode 100755
index 000000000..490b5aa52
--- /dev/null
+++ b/gtk/gtkstatusicon.c
@@ -0,0 +1,925 @@
+/* gtkstatusicon-x11.c:
+ *
+ * Copyright (C) 2003 Sun Microsystems, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Mark McLoughlin <mark@skynet.ie>
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "gtkstatusicon.h"
+
+#include "gtkintl.h"
+#include "gtkiconfactory.h"
+#include "gtkmarshalers.h"
+#include "gtktooltips.h"
+#include "gtktrayicon.h"
+
+#include "gtkprivate.h"
+#include "gtkwidget.h"
+
+#include "gtkalias.h"
+
+#define BLINK_TIMEOUT 500
+
+enum
+{
+ PROP_0,
+ PROP_PIXBUF,
+ PROP_FILE,
+ PROP_STOCK,
+ PROP_ICON_NAME,
+ PROP_STORAGE_TYPE,
+ PROP_SIZE,
+ PROP_VISIBLE,
+ PROP_BLINKING
+};
+
+enum
+{
+ ACTIVATE_SIGNAL,
+ POPUP_MENU_SIGNAL,
+ SIZE_CHANGED_SIGNAL,
+ LAST_SIGNAL
+};
+
+struct _GtkStatusIconPrivate
+{
+ GtkWidget *tray_icon;
+ GtkWidget *image;
+ gint size;
+
+ gint image_width;
+ gint image_height;
+
+ GtkTooltips *tooltips;
+
+ GtkImageType storage_type;
+
+ union
+ {
+ GdkPixbuf *pixbuf;
+ gchar *stock_id;
+ gchar *icon_name;
+ } image_data;
+
+ GdkPixbuf *blank_icon;
+ guint blinking_timeout;
+
+ guint blinking : 1;
+ guint blink_off : 1;
+ guint visible : 1;
+};
+
+static void gtk_status_icon_finalize (GObject *object);
+static void gtk_status_icon_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_status_icon_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gtk_status_icon_size_allocate (GtkStatusIcon *status_icon,
+ GtkAllocation *allocation);
+static gboolean gtk_status_icon_button_press (GtkStatusIcon *status_icon,
+ GdkEventButton *event);
+static void gtk_status_icon_disable_blinking (GtkStatusIcon *status_icon);
+static void gtk_status_icon_reset_image_data (GtkStatusIcon *status_icon);
+
+
+static guint status_icon_signals [LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GtkStatusIcon, gtk_status_icon, G_TYPE_OBJECT);
+
+static void
+gtk_status_icon_class_init (GtkStatusIconClass *class)
+{
+ GObjectClass *gobject_class = (GObjectClass *) class;
+
+ gobject_class->finalize = gtk_status_icon_finalize;
+ gobject_class->set_property = gtk_status_icon_set_property;
+ gobject_class->get_property = gtk_status_icon_get_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_PIXBUF,
+ g_param_spec_object ("pixbuf",
+ P_("Pixbuf"),
+ P_("A GdkPixbuf to display"),
+ GDK_TYPE_PIXBUF,
+ GTK_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_FILE,
+ g_param_spec_string ("file",
+ P_("Filename"),
+ P_("Filename to load and display"),
+ NULL,
+ GTK_PARAM_WRITABLE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_STOCK,
+ g_param_spec_string ("stock",
+ P_("Stock ID"),
+ P_("Stock ID for a stock image to display"),
+ NULL,
+ GTK_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_ICON_NAME,
+ g_param_spec_string ("icon-name",
+ P_("Icon Name"),
+ P_("The name of the icon from the icon theme"),
+ NULL,
+ GTK_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_STORAGE_TYPE,
+ g_param_spec_enum ("storage-type",
+ P_("Storage type"),
+ P_("The representation being used for image data"),
+ GTK_TYPE_IMAGE_TYPE,
+ GTK_IMAGE_EMPTY,
+ GTK_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_SIZE,
+ g_param_spec_int ("size",
+ P_("Size"),
+ P_("The size of the icon"),
+ 0,
+ G_MAXINT,
+ 0,
+ GTK_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_BLINKING,
+ g_param_spec_boolean ("blinking",
+ P_("Blinking"),
+ P_("Whether or not the status icon is blinking"),
+ FALSE,
+ GTK_PARAM_READWRITE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_VISIBLE,
+ g_param_spec_boolean ("visible",
+ P_("Visible"),
+ P_("Whether or not the status icon is visible"),
+ TRUE,
+ GTK_PARAM_READWRITE));
+
+
+ status_icon_signals [ACTIVATE_SIGNAL] =
+ g_signal_new ("activate",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GtkStatusIconClass, activate),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ status_icon_signals [POPUP_MENU_SIGNAL] =
+ g_signal_new ("popup-menu",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GtkStatusIconClass, popup_menu),
+ NULL,
+ NULL,
+ _gtk_marshal_VOID__UINT_UINT,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_UINT,
+ G_TYPE_UINT);
+
+ status_icon_signals [SIZE_CHANGED_SIGNAL] =
+ g_signal_new ("size-changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkStatusIconClass, size_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_INT);
+
+ g_type_class_add_private (class, sizeof (GtkStatusIconPrivate));
+}
+
+static void
+gtk_status_icon_init (GtkStatusIcon *status_icon)
+{
+ status_icon->priv = G_TYPE_INSTANCE_GET_PRIVATE (status_icon, GTK_TYPE_STATUS_ICON,
+ GtkStatusIconPrivate);
+
+ status_icon->priv->storage_type = GTK_IMAGE_EMPTY;
+ status_icon->priv->size = 0;
+ status_icon->priv->image_width = 0;
+ status_icon->priv->image_height = 0;
+ status_icon->priv->visible = TRUE;
+
+ status_icon->priv->tray_icon = GTK_WIDGET (_gtk_tray_icon_new (NULL));
+
+ gtk_widget_add_events (GTK_WIDGET (status_icon->priv->tray_icon),
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+
+ g_signal_connect_swapped (status_icon->priv->tray_icon, "button-press-event",
+ G_CALLBACK (gtk_status_icon_button_press), status_icon);
+
+ status_icon->priv->image = gtk_image_new ();
+ gtk_container_add (GTK_CONTAINER (status_icon->priv->tray_icon),
+ status_icon->priv->image);
+
+ g_signal_connect_swapped (status_icon->priv->image, "size-allocate",
+ G_CALLBACK (gtk_status_icon_size_allocate), status_icon);
+
+ gtk_widget_show (status_icon->priv->image);
+ gtk_widget_show (status_icon->priv->tray_icon);
+
+ status_icon->priv->tooltips = gtk_tooltips_new ();
+ g_object_ref (status_icon->priv->tooltips);
+ gtk_object_sink (GTK_OBJECT (status_icon->priv->tooltips));
+}
+
+static void
+gtk_status_icon_finalize (GObject *object)
+{
+ GtkStatusIcon *status_icon = GTK_STATUS_ICON (object);
+
+ gtk_status_icon_disable_blinking (status_icon);
+
+ gtk_status_icon_reset_image_data (status_icon);
+
+ if (status_icon->priv->blank_icon)
+ g_object_unref (status_icon->priv->blank_icon);
+ status_icon->priv->blank_icon = NULL;
+
+ if (status_icon->priv->tooltips)
+ g_object_unref (status_icon->priv->tooltips);
+ status_icon->priv->tooltips = NULL;
+
+ gtk_widget_destroy (status_icon->priv->tray_icon);
+
+ G_OBJECT_CLASS (gtk_status_icon_parent_class)->finalize (object);
+}
+
+static void
+gtk_status_icon_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkStatusIcon *status_icon = GTK_STATUS_ICON (object);
+
+ switch (prop_id)
+ {
+ case PROP_PIXBUF:
+ gtk_status_icon_set_from_pixbuf (status_icon, g_value_get_object (value));
+ break;
+ case PROP_FILE:
+ gtk_status_icon_set_from_file (status_icon, g_value_get_string (value));
+ break;
+ case PROP_STOCK:
+ gtk_status_icon_set_from_stock (status_icon, g_value_get_string (value));
+ break;
+ case PROP_ICON_NAME:
+ gtk_status_icon_set_from_icon_name (status_icon, g_value_get_string (value));
+ break;
+ case PROP_BLINKING:
+ gtk_status_icon_set_blinking (status_icon, g_value_get_boolean (value));
+ break;
+ case PROP_VISIBLE:
+ gtk_status_icon_set_visible (status_icon, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_status_icon_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkStatusIcon *status_icon = GTK_STATUS_ICON (object);
+
+ switch (prop_id)
+ {
+ case PROP_PIXBUF:
+ g_value_set_object (value, gtk_status_icon_get_pixbuf (status_icon));
+ break;
+ case PROP_STOCK:
+ g_value_set_string (value, gtk_status_icon_get_stock (status_icon));
+ break;
+ case PROP_ICON_NAME:
+ g_value_set_string (value, gtk_status_icon_get_icon_name (status_icon));
+ break;
+ case PROP_STORAGE_TYPE:
+ g_value_set_enum (value, gtk_status_icon_get_storage_type (status_icon));
+ break;
+ case PROP_SIZE:
+ g_value_set_int (value, gtk_status_icon_get_size (status_icon));
+ break;
+ case PROP_BLINKING:
+ g_value_set_boolean (value, gtk_status_icon_get_blinking (status_icon));
+ break;
+ case PROP_VISIBLE:
+ g_value_set_boolean (value, gtk_status_icon_get_visible (status_icon));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+GtkStatusIcon *
+gtk_status_icon_new (void)
+{
+ return g_object_new (GTK_TYPE_STATUS_ICON, NULL);
+}
+
+GtkStatusIcon *
+gtk_status_icon_new_from_pixbuf (GdkPixbuf *pixbuf)
+{
+ return g_object_new (GTK_TYPE_STATUS_ICON,
+ "pixbuf", pixbuf,
+ NULL);
+}
+
+GtkStatusIcon *
+gtk_status_icon_new_from_file (const gchar *filename)
+{
+ return g_object_new (GTK_TYPE_STATUS_ICON,
+ "file", filename,
+ NULL);
+}
+
+GtkStatusIcon *
+gtk_status_icon_new_from_stock (const gchar *stock_id)
+{
+ return g_object_new (GTK_TYPE_STATUS_ICON,
+ "stock", stock_id,
+ NULL);
+}
+
+GtkStatusIcon *
+gtk_status_icon_new_from_icon_name (const gchar *icon_name)
+{
+ return g_object_new (GTK_TYPE_STATUS_ICON,
+ "icon-name", icon_name,
+ NULL);
+}
+
+static void
+emit_activate_signal (GtkStatusIcon *status_icon)
+{
+ g_signal_emit (status_icon,
+ status_icon_signals [ACTIVATE_SIGNAL], 0);
+}
+
+static void
+emit_popup_menu_signal (GtkStatusIcon *status_icon,
+ guint button,
+ guint32 activate_time)
+{
+ g_signal_emit (status_icon,
+ status_icon_signals [POPUP_MENU_SIGNAL], 0,
+ button,
+ activate_time);
+}
+
+static gboolean
+emit_size_changed_signal (GtkStatusIcon *status_icon,
+ gint size)
+{
+ gboolean handled = FALSE;
+
+ g_signal_emit (status_icon,
+ status_icon_signals [SIZE_CHANGED_SIGNAL], 0,
+ size,
+ &handled);
+
+ return handled;
+}
+
+static GdkPixbuf *
+gtk_status_icon_blank_icon (GtkStatusIcon *status_icon)
+{
+ if (status_icon->priv->blank_icon)
+ {
+ gint width, height;
+
+ width = gdk_pixbuf_get_width (status_icon->priv->blank_icon);
+ height = gdk_pixbuf_get_height (status_icon->priv->blank_icon);
+
+
+ if (width == status_icon->priv->image_width &&
+ height == status_icon->priv->image_height)
+ {
+ return status_icon->priv->blank_icon;
+ }
+ else
+ {
+ g_object_unref (status_icon->priv->blank_icon);
+ status_icon->priv->blank_icon = NULL;
+ }
+ }
+
+ status_icon->priv->blank_icon = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+ status_icon->priv->image_width,
+ status_icon->priv->image_height);
+ if (status_icon->priv->blank_icon)
+ gdk_pixbuf_fill (status_icon->priv->blank_icon, 0);
+
+ return status_icon->priv->blank_icon;
+}
+
+static GtkIconSize
+find_icon_size (GtkWidget *widget,
+ gint pixel_size)
+{
+ GdkScreen *screen;
+ GtkSettings *settings;
+ GtkIconSize s, size;
+ gint w, h, d, dist;
+
+ screen = gtk_widget_get_screen (widget);
+
+ if (!screen)
+ return GTK_ICON_SIZE_MENU;
+
+ settings = gtk_settings_get_for_screen (screen);
+
+ dist = G_MAXINT;
+ size = GTK_ICON_SIZE_MENU;
+
+ for (s = GTK_ICON_SIZE_MENU; s < GTK_ICON_SIZE_DIALOG; s++)
+ {
+ if (gtk_icon_size_lookup_for_settings (settings, s, &w, &h) &&
+ w <= pixel_size && h <= pixel_size)
+ {
+ d = MAX (pixel_size - w, pixel_size - h);
+ if (d < dist)
+ {
+ dist = d;
+ size = s;
+ }
+ }
+ }
+
+ return size;
+}
+
+static void
+gtk_status_icon_update_image (GtkStatusIcon *status_icon)
+{
+ if (status_icon->priv->blink_off)
+ {
+ gtk_image_set_from_pixbuf (GTK_IMAGE (status_icon->priv->image),
+ gtk_status_icon_blank_icon (status_icon));
+ return;
+ }
+
+ switch (status_icon->priv->storage_type)
+ {
+ case GTK_IMAGE_PIXBUF:
+ {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = status_icon->priv->image_data.pixbuf;
+
+ if (pixbuf)
+ {
+ GdkPixbuf *scaled;
+ gint size;
+ gint width;
+ gint height;
+
+ size = status_icon->priv->size;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ if (width > size || height > size)
+ {
+ scaled = gdk_pixbuf_scale_simple (pixbuf,
+ MIN (size, width),
+ MIN (size, height),
+ GDK_INTERP_BILINEAR);
+ }
+ else
+ {
+ scaled = g_object_ref (pixbuf);
+ }
+
+ gtk_image_set_from_pixbuf (GTK_IMAGE (status_icon->priv->image), scaled);
+
+ g_object_unref (scaled);
+ }
+ else
+ {
+ gtk_image_set_from_pixbuf (GTK_IMAGE (status_icon->priv->image), NULL);
+ }
+ }
+ break;
+
+ case GTK_IMAGE_STOCK:
+ {
+ GtkIconSize size = find_icon_size (status_icon->priv->image, status_icon->priv->size);
+ gtk_image_set_from_stock (GTK_IMAGE (status_icon->priv->image),
+ status_icon->priv->image_data.stock_id,
+ size);
+ }
+ break;
+
+ case GTK_IMAGE_ICON_NAME:
+ {
+ GtkIconSize size = find_icon_size (status_icon->priv->image, status_icon->priv->size);
+ gtk_image_set_from_icon_name (GTK_IMAGE (status_icon->priv->image),
+ status_icon->priv->image_data.icon_name,
+ size);
+ }
+ break;
+
+ case GTK_IMAGE_EMPTY:
+ gtk_image_set_from_pixbuf (GTK_IMAGE (status_icon->priv->image), NULL);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+gtk_status_icon_size_allocate (GtkStatusIcon *status_icon,
+ GtkAllocation *allocation)
+{
+ GtkOrientation orientation;
+ gint size;
+
+ orientation = _gtk_tray_icon_get_orientation (GTK_TRAY_ICON (status_icon->priv->tray_icon));
+
+ if (orientation == GTK_ORIENTATION_HORIZONTAL)
+ size = allocation->height;
+ else
+ size = allocation->width;
+
+ status_icon->priv->image_width = allocation->width - GTK_MISC (status_icon->priv->image)->xpad * 2;
+ status_icon->priv->image_height = allocation->height - GTK_MISC (status_icon->priv->image)->ypad * 2;
+
+ if (status_icon->priv->size != size)
+ {
+ status_icon->priv->size = size;
+
+ g_object_notify (G_OBJECT (status_icon), "size");
+
+ if (!emit_size_changed_signal (status_icon, size))
+ {
+ gtk_status_icon_update_image (status_icon);
+ }
+ }
+}
+
+static gboolean
+gtk_status_icon_button_press (GtkStatusIcon *status_icon,
+ GdkEventButton *event)
+{
+ if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
+ {
+ emit_activate_signal (status_icon);
+ return TRUE;
+ }
+ else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+ {
+ emit_popup_menu_signal (status_icon, event->button, event->time);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gtk_status_icon_reset_image_data (GtkStatusIcon *status_icon)
+{
+ status_icon->priv->storage_type = GTK_IMAGE_EMPTY;
+ g_object_notify (G_OBJECT (status_icon), "storage-type");
+
+ switch (status_icon->priv->storage_type)
+ {
+ case GTK_IMAGE_PIXBUF:
+ if (status_icon->priv->image_data.pixbuf)
+ g_object_unref (status_icon->priv->image_data.pixbuf);
+ status_icon->priv->image_data.pixbuf = NULL;
+ g_object_notify (G_OBJECT (status_icon), "pixbuf");
+ break;
+
+ case GTK_IMAGE_STOCK:
+ g_free (status_icon->priv->image_data.stock_id);
+ status_icon->priv->image_data.stock_id = NULL;
+
+ g_object_notify (G_OBJECT (status_icon), "stock");
+ break;
+
+ case GTK_IMAGE_ICON_NAME:
+ g_free (status_icon->priv->image_data.icon_name);
+ status_icon->priv->image_data.icon_name = NULL;
+
+ g_object_notify (G_OBJECT (status_icon), "icon-name");
+ break;
+
+ case GTK_IMAGE_EMPTY:
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+gtk_status_icon_set_image (GtkStatusIcon *status_icon,
+ GtkImageType storage_type,
+ gpointer data)
+{
+ g_object_freeze_notify (G_OBJECT (status_icon));
+
+ gtk_status_icon_reset_image_data (status_icon);
+
+ status_icon->priv->storage_type = storage_type;
+ g_object_notify (G_OBJECT (status_icon), "storage-type");
+
+ switch (storage_type)
+ {
+ case GTK_IMAGE_PIXBUF:
+ status_icon->priv->image_data.pixbuf = (GdkPixbuf *)data;
+ g_object_notify (G_OBJECT (status_icon), "pixbuf");
+ break;
+ case GTK_IMAGE_STOCK:
+ status_icon->priv->image_data.stock_id = g_strdup ((const gchar *)data);
+ g_object_notify (G_OBJECT (status_icon), "stock");
+ break;
+ case GTK_IMAGE_ICON_NAME:
+ status_icon->priv->image_data.icon_name = g_strdup ((const gchar *)data);
+ g_object_notify (G_OBJECT (status_icon), "icon-name");
+ break;
+ default:
+ g_warning ("Image type %d not handled by GtkStatusIcon", storage_type);
+ }
+
+ g_object_thaw_notify (G_OBJECT (status_icon));
+
+ gtk_status_icon_update_image (status_icon);
+}
+
+void
+gtk_status_icon_set_from_pixbuf (GtkStatusIcon *status_icon,
+ GdkPixbuf *pixbuf)
+{
+ g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
+ g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
+
+ if (pixbuf)
+ g_object_ref (pixbuf);
+
+ gtk_status_icon_set_image (status_icon, GTK_IMAGE_PIXBUF,
+ (gpointer) pixbuf);
+}
+
+void
+gtk_status_icon_set_from_file (GtkStatusIcon *status_icon,
+ const gchar *filename)
+{
+ GdkPixbuf *pixbuf;
+
+ g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
+ g_return_if_fail (filename != NULL);
+
+ pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+
+ gtk_status_icon_set_from_pixbuf (status_icon, pixbuf);
+
+ if (pixbuf)
+ g_object_unref (pixbuf);
+}
+
+void
+gtk_status_icon_set_from_stock (GtkStatusIcon *status_icon,
+ const gchar *stock_id)
+{
+ g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
+ g_return_if_fail (stock_id != NULL);
+
+ gtk_status_icon_set_image (status_icon, GTK_IMAGE_STOCK,
+ (gpointer) stock_id);
+}
+
+void
+gtk_status_icon_set_from_icon_name (GtkStatusIcon *status_icon,
+ const gchar *icon_name)
+{
+ g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
+ g_return_if_fail (icon_name != NULL);
+
+ gtk_status_icon_set_image (status_icon, GTK_IMAGE_ICON_NAME,
+ (gpointer) icon_name);
+}
+
+GtkImageType
+gtk_status_icon_get_storage_type (GtkStatusIcon *status_icon)
+{
+ g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), GTK_IMAGE_EMPTY);
+
+ return status_icon->priv->storage_type;
+}
+
+GdkPixbuf *
+gtk_status_icon_get_pixbuf (GtkStatusIcon *status_icon)
+{
+ g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
+ g_return_val_if_fail (status_icon->priv->storage_type == GTK_IMAGE_PIXBUF ||
+ status_icon->priv->storage_type == GTK_IMAGE_EMPTY, NULL);
+
+ if (status_icon->priv->storage_type == GTK_IMAGE_EMPTY)
+ status_icon->priv->image_data.pixbuf = NULL;
+
+ return status_icon->priv->image_data.pixbuf;
+}
+
+G_CONST_RETURN gchar *
+gtk_status_icon_get_stock (GtkStatusIcon *status_icon)
+{
+ g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
+ g_return_val_if_fail (status_icon->priv->storage_type == GTK_IMAGE_STOCK ||
+ status_icon->priv->storage_type == GTK_IMAGE_EMPTY, NULL);
+
+ if (status_icon->priv->storage_type == GTK_IMAGE_EMPTY)
+ status_icon->priv->image_data.stock_id = NULL;
+
+ return status_icon->priv->image_data.stock_id;
+}
+
+G_CONST_RETURN gchar *
+gtk_status_icon_get_icon_name (GtkStatusIcon *status_icon)
+{
+ g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), NULL);
+ g_return_val_if_fail (status_icon->priv->storage_type == GTK_IMAGE_ICON_NAME ||
+ status_icon->priv->storage_type == GTK_IMAGE_EMPTY, NULL);
+
+ if (status_icon->priv->storage_type == GTK_IMAGE_EMPTY)
+ status_icon->priv->image_data.icon_name = NULL;
+
+ return status_icon->priv->image_data.icon_name;
+}
+
+gint
+gtk_status_icon_get_size (GtkStatusIcon *status_icon)
+{
+ g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), 0);
+
+ return status_icon->priv->size;
+}
+
+void
+gtk_status_icon_set_tooltip (GtkStatusIcon *status_icon,
+ const gchar *tooltip_text)
+{
+ g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
+
+ gtk_tooltips_set_tip (status_icon->priv->tooltips,
+ status_icon->priv->tray_icon,
+ tooltip_text, NULL);
+}
+
+static gboolean
+gtk_status_icon_blinker (GtkStatusIcon *status_icon)
+{
+ status_icon->priv->blink_off = !status_icon->priv->blink_off;
+
+ gtk_status_icon_update_image (status_icon);
+
+ return TRUE;
+}
+
+static void
+gtk_status_icon_enable_blinking (GtkStatusIcon *status_icon)
+{
+ if (!status_icon->priv->blinking_timeout)
+ {
+ gtk_status_icon_blinker (status_icon);
+
+ status_icon->priv->blinking_timeout =
+ g_timeout_add (BLINK_TIMEOUT,
+ (GSourceFunc) gtk_status_icon_blinker,
+ status_icon);
+ }
+}
+
+static void
+gtk_status_icon_disable_blinking (GtkStatusIcon *status_icon)
+{
+ if (status_icon->priv->blinking_timeout)
+ {
+ g_source_remove (status_icon->priv->blinking_timeout);
+ status_icon->priv->blinking_timeout = 0;
+ status_icon->priv->blink_off = FALSE;
+
+ gtk_status_icon_update_image (status_icon);
+ }
+}
+
+void
+gtk_status_icon_set_visible (GtkStatusIcon *status_icon,
+ gboolean visible)
+{
+ g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
+
+ visible = visible != FALSE;
+
+ if (status_icon->priv->visible != visible)
+ {
+ status_icon->priv->visible = visible;
+
+ if (visible)
+ gtk_widget_show (status_icon->priv->tray_icon);
+ else
+ gtk_widget_hide (status_icon->priv->tray_icon);
+
+ g_object_notify (G_OBJECT (status_icon), "visible");
+ }
+}
+
+gboolean
+gtk_status_icon_get_visible (GtkStatusIcon *status_icon)
+{
+ g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), FALSE);
+
+ return status_icon->priv->visible;
+}
+
+void
+gtk_status_icon_set_blinking (GtkStatusIcon *status_icon,
+ gboolean blinking)
+{
+ g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
+
+ blinking = blinking != FALSE;
+
+ if (status_icon->priv->blinking != blinking)
+ {
+ status_icon->priv->blinking = blinking;
+
+ if (blinking)
+ gtk_status_icon_enable_blinking (status_icon);
+ else
+ gtk_status_icon_disable_blinking (status_icon);
+
+ g_object_notify (G_OBJECT (status_icon), "blinking");
+ }
+}
+
+gboolean
+gtk_status_icon_get_blinking (GtkStatusIcon *status_icon)
+{
+ g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), FALSE);
+
+ return status_icon->priv->blinking;
+}
+
+gboolean
+gtk_status_icon_is_embedded (GtkStatusIcon *status_icon)
+{
+ GtkPlug *plug;
+
+ g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), FALSE);
+
+ plug = GTK_PLUG (status_icon->priv->tray_icon);
+
+ if (plug->socket_window)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+#define __GTK_STATUS_ICON_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkstatusicon.h b/gtk/gtkstatusicon.h
new file mode 100755
index 000000000..f94f395d4
--- /dev/null
+++ b/gtk/gtkstatusicon.h
@@ -0,0 +1,108 @@
+/* gtkstatusicon.h:
+ *
+ * Copyright (C) 2003 Sun Microsystems, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Mark McLoughlin <mark@skynet.ie>
+ */
+
+#ifndef __GTK_STATUS_ICON_H__
+#define __GTK_STATUS_ICON_H__
+
+#include <gtk/gtkimage.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_STATUS_ICON (gtk_status_icon_get_type ())
+#define GTK_STATUS_ICON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_STATUS_ICON, GtkStatusIcon))
+#define GTK_STATUS_ICON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_STATUS_ICON, GtkStatusIconClass))
+#define GTK_IS_STATUS_ICON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_STATUS_ICON))
+#define GTK_IS_STATUS_ICON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_STATUS_ICON))
+#define GTK_STATUS_ICON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_STATUS_ICON, GtkStatusIconClass))
+
+typedef struct _GtkStatusIcon GtkStatusIcon;
+typedef struct _GtkStatusIconClass GtkStatusIconClass;
+typedef struct _GtkStatusIconPrivate GtkStatusIconPrivate;
+
+struct _GtkStatusIcon
+{
+ GObject parent_instance;
+
+ GtkStatusIconPrivate *priv;
+};
+
+struct _GtkStatusIconClass
+{
+ GObjectClass parent_class;
+
+ void (* activate) (GtkStatusIcon *status_icon);
+ void (* popup_menu) (GtkStatusIcon *status_icon,
+ guint buttton,
+ guint32 activate_time);
+ gboolean (* size_changed) (GtkStatusIcon *status_icon,
+ gint size);
+
+ void (*__gtk_reserved1);
+ void (*__gtk_reserved2);
+ void (*__gtk_reserved3);
+ void (*__gtk_reserved4);
+ void (*__gtk_reserved5);
+ void (*__gtk_reserved6);
+};
+
+GType gtk_status_icon_get_type (void) G_GNUC_CONST;
+
+GtkStatusIcon *gtk_status_icon_new (void);
+GtkStatusIcon *gtk_status_icon_new_from_pixbuf (GdkPixbuf *pixbuf);
+GtkStatusIcon *gtk_status_icon_new_from_file (const gchar *filename);
+GtkStatusIcon *gtk_status_icon_new_from_stock (const gchar *stock_id);
+GtkStatusIcon *gtk_status_icon_new_from_icon_name (const gchar *icon_name);
+
+void gtk_status_icon_set_from_pixbuf (GtkStatusIcon *status_icon,
+ GdkPixbuf *pixbuf);
+void gtk_status_icon_set_from_file (GtkStatusIcon *status_icon,
+ const gchar *filename);
+void gtk_status_icon_set_from_stock (GtkStatusIcon *status_icon,
+ const gchar *stock_id);
+void gtk_status_icon_set_from_icon_name (GtkStatusIcon *status_icon,
+ const gchar *icon_name);
+
+GtkImageType gtk_status_icon_get_storage_type (GtkStatusIcon *status_icon);
+
+GdkPixbuf *gtk_status_icon_get_pixbuf (GtkStatusIcon *status_icon);
+G_CONST_RETURN gchar *gtk_status_icon_get_stock (GtkStatusIcon *status_icon);
+G_CONST_RETURN gchar *gtk_status_icon_get_icon_name (GtkStatusIcon *status_icon);
+
+gint gtk_status_icon_get_size (GtkStatusIcon *status_icon);
+
+void gtk_status_icon_set_tooltip (GtkStatusIcon *status_icon,
+ const gchar *tooltip_text);
+
+void gtk_status_icon_set_visible (GtkStatusIcon *status_icon,
+ gboolean visible);
+gboolean gtk_status_icon_get_visible (GtkStatusIcon *status_icon);
+
+void gtk_status_icon_set_blinking (GtkStatusIcon *status_icon,
+ gboolean blinking);
+gboolean gtk_status_icon_get_blinking (GtkStatusIcon *status_icon);
+
+gboolean gtk_status_icon_is_embedded (GtkStatusIcon *status_icon);
+
+G_END_DECLS
+
+#endif /* __GTK_STATUS_ICON_H__ */
diff --git a/gtk/gtktrayicon-x11.c b/gtk/gtktrayicon-x11.c
new file mode 100755
index 000000000..8f42c9d39
--- /dev/null
+++ b/gtk/gtktrayicon-x11.c
@@ -0,0 +1,498 @@
+/* gtktrayicon.c
+ * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <libintl.h>
+
+#include "gtkintl.h"
+#include "gtkprivate.h"
+#include "gtktrayicon.h"
+
+#include "gtkalias.h"
+
+#include "x11/gdkx.h"
+#include <X11/Xatom.h>
+
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+
+#define SYSTEM_TRAY_ORIENTATION_HORZ 0
+#define SYSTEM_TRAY_ORIENTATION_VERT 1
+
+enum {
+ PROP_0,
+ PROP_ORIENTATION
+};
+
+struct _GtkTrayIconPrivate
+{
+ guint stamp;
+
+ Atom selection_atom;
+ Atom manager_atom;
+ Atom system_tray_opcode_atom;
+ Atom orientation_atom;
+ Window manager_window;
+
+ GtkOrientation orientation;
+};
+
+static void gtk_tray_icon_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gtk_tray_icon_realize (GtkWidget *widget);
+static void gtk_tray_icon_unrealize (GtkWidget *widget);
+static gboolean gtk_tray_icon_delete (GtkWidget *widget,
+ GdkEventAny *event);
+
+static void gtk_tray_icon_update_manager_window (GtkTrayIcon *icon,
+ gboolean dock_if_realized);
+static void gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon);
+
+G_DEFINE_TYPE (GtkTrayIcon, gtk_tray_icon, GTK_TYPE_PLUG);
+
+static void
+gtk_tray_icon_class_init (GtkTrayIconClass *class)
+{
+ GObjectClass *gobject_class = (GObjectClass *)class;
+ GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
+
+ gobject_class->get_property = gtk_tray_icon_get_property;
+
+ widget_class->realize = gtk_tray_icon_realize;
+ widget_class->unrealize = gtk_tray_icon_unrealize;
+ widget_class->delete_event = gtk_tray_icon_delete;
+
+ g_object_class_install_property (gobject_class,
+ PROP_ORIENTATION,
+ g_param_spec_enum ("orientation",
+ P_("Orientation"),
+ P_("The orientation of the tray"),
+ GTK_TYPE_ORIENTATION,
+ GTK_ORIENTATION_HORIZONTAL,
+ GTK_PARAM_READABLE));
+
+ g_type_class_add_private (class, sizeof (GtkTrayIconPrivate));
+}
+
+static void
+gtk_tray_icon_init (GtkTrayIcon *icon)
+{
+ icon->priv = G_TYPE_INSTANCE_GET_PRIVATE (icon, GTK_TYPE_TRAY_ICON,
+ GtkTrayIconPrivate);
+
+ icon->priv->stamp = 1;
+ icon->priv->orientation = GTK_ORIENTATION_HORIZONTAL;
+
+ gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
+}
+
+static void
+gtk_tray_icon_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkTrayIcon *icon = GTK_TRAY_ICON (object);
+
+ switch (prop_id)
+ {
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, icon->priv->orientation);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_tray_icon_get_orientation_property (GtkTrayIcon *icon)
+{
+ Display *xdisplay;
+ Atom type;
+ int format;
+ union {
+ gulong *prop;
+ guchar *prop_ch;
+ } prop = { NULL };
+ gulong nitems;
+ gulong bytes_after;
+ int error, result;
+
+ g_assert (icon->priv->manager_window != None);
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+
+ gdk_error_trap_push ();
+ type = None;
+ result = XGetWindowProperty (xdisplay,
+ icon->priv->manager_window,
+ icon->priv->orientation_atom,
+ 0, G_MAXLONG, FALSE,
+ XA_CARDINAL,
+ &type, &format, &nitems,
+ &bytes_after, &(prop.prop_ch));
+ error = gdk_error_trap_pop ();
+
+ if (error || result != Success)
+ return;
+
+ if (type == XA_CARDINAL)
+ {
+ GtkOrientation orientation;
+
+ orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
+ GTK_ORIENTATION_HORIZONTAL :
+ GTK_ORIENTATION_VERTICAL;
+
+ if (icon->priv->orientation != orientation)
+ {
+ icon->priv->orientation = orientation;
+
+ g_object_notify (G_OBJECT (icon), "orientation");
+ }
+ }
+
+ if (prop.prop)
+ XFree (prop.prop);
+}
+
+static GdkFilterReturn
+gtk_tray_icon_manager_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ GtkTrayIcon *icon = user_data;
+ XEvent *xev = (XEvent *)xevent;
+
+ if (xev->xany.type == ClientMessage &&
+ xev->xclient.message_type == icon->priv->manager_atom &&
+ xev->xclient.data.l[1] == icon->priv->selection_atom)
+ {
+ gtk_tray_icon_update_manager_window (icon, TRUE);
+ }
+ else if (xev->xany.window == icon->priv->manager_window)
+ {
+ if (xev->xany.type == PropertyNotify &&
+ xev->xproperty.atom == icon->priv->orientation_atom)
+ {
+ gtk_tray_icon_get_orientation_property (icon);
+ }
+ if (xev->xany.type == DestroyNotify)
+ {
+ gtk_tray_icon_manager_window_destroyed (icon);
+ }
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static void
+gtk_tray_icon_unrealize (GtkWidget *widget)
+{
+ GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
+ GdkWindow *root_window;
+
+ if (icon->priv->manager_window != None)
+ {
+ GdkWindow *gdkwin;
+
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
+ icon->priv->manager_window);
+
+ gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
+ }
+
+ root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
+
+ gdk_window_remove_filter (root_window, gtk_tray_icon_manager_filter, icon);
+
+ if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->unrealize)
+ (* GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->unrealize) (widget);
+}
+
+static void
+gtk_tray_icon_send_manager_message (GtkTrayIcon *icon,
+ long message,
+ Window window,
+ long data1,
+ long data2,
+ long data3)
+{
+ XClientMessageEvent ev;
+ Display *display;
+
+ ev.type = ClientMessage;
+ ev.window = window;
+ ev.message_type = icon->priv->system_tray_opcode_atom;
+ ev.format = 32;
+ ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
+ ev.data.l[1] = message;
+ ev.data.l[2] = data1;
+ ev.data.l[3] = data2;
+ ev.data.l[4] = data3;
+
+ display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+
+ gdk_error_trap_push ();
+ XSendEvent (display,
+ icon->priv->manager_window, False, NoEventMask, (XEvent *)&ev);
+ XSync (display, False);
+ gdk_error_trap_pop ();
+}
+
+static void
+gtk_tray_icon_send_dock_request (GtkTrayIcon *icon)
+{
+ gtk_tray_icon_send_manager_message (icon,
+ SYSTEM_TRAY_REQUEST_DOCK,
+ icon->priv->manager_window,
+ gtk_plug_get_id (GTK_PLUG (icon)),
+ 0, 0);
+}
+
+static void
+gtk_tray_icon_update_manager_window (GtkTrayIcon *icon,
+ gboolean dock_if_realized)
+{
+ Display *xdisplay;
+
+ if (icon->priv->manager_window != None)
+ return;
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+
+ XGrabServer (xdisplay);
+
+ icon->priv->manager_window = XGetSelectionOwner (xdisplay,
+ icon->priv->selection_atom);
+
+ if (icon->priv->manager_window != None)
+ XSelectInput (xdisplay,
+ icon->priv->manager_window, StructureNotifyMask|PropertyChangeMask);
+
+ XUngrabServer (xdisplay);
+ XFlush (xdisplay);
+
+ if (icon->priv->manager_window != None)
+ {
+ GdkWindow *gdkwin;
+
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+ icon->priv->manager_window);
+
+ gdk_window_add_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
+
+ if (dock_if_realized && GTK_WIDGET_REALIZED (icon))
+ gtk_tray_icon_send_dock_request (icon);
+
+ gtk_tray_icon_get_orientation_property (icon);
+ }
+}
+
+static void
+gtk_tray_icon_manager_window_destroyed (GtkTrayIcon *icon)
+{
+ GdkWindow *gdkwin;
+
+ g_return_if_fail (icon->priv->manager_window != None);
+
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+ icon->priv->manager_window);
+
+ gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
+
+ icon->priv->manager_window = None;
+
+ gtk_tray_icon_update_manager_window (icon, TRUE);
+}
+
+static gboolean
+gtk_tray_icon_delete (GtkWidget *widget,
+ GdkEventAny *event)
+{
+ GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
+ GdkWindow *gdkwin;
+
+ if (icon->priv->manager_window != None)
+ {
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+ icon->priv->manager_window);
+
+ gdk_window_remove_filter (gdkwin, gtk_tray_icon_manager_filter, icon);
+
+ icon->priv->manager_window = None;
+ }
+
+ gtk_tray_icon_update_manager_window (icon, TRUE);
+
+ return TRUE;
+}
+
+static void
+gtk_tray_icon_realize (GtkWidget *widget)
+{
+ GtkTrayIcon *icon = GTK_TRAY_ICON (widget);
+ GdkScreen *screen;
+ GdkDisplay *display;
+ Display *xdisplay;
+ char buffer[256];
+ GdkWindow *root_window;
+
+ if (GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->realize)
+ GTK_WIDGET_CLASS (gtk_tray_icon_parent_class)->realize (widget);
+
+ screen = gtk_widget_get_screen (widget);
+ display = gdk_screen_get_display (screen);
+ xdisplay = gdk_x11_display_get_xdisplay (display);
+
+ /* Now see if there's a manager window around */
+ g_snprintf (buffer, sizeof (buffer),
+ "_NET_SYSTEM_TRAY_S%d",
+ gdk_screen_get_number (screen));
+
+ icon->priv->selection_atom = XInternAtom (xdisplay, buffer, False);
+
+ icon->priv->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
+
+ icon->priv->system_tray_opcode_atom = XInternAtom (xdisplay,
+ "_NET_SYSTEM_TRAY_OPCODE",
+ False);
+
+ icon->priv->orientation_atom = XInternAtom (xdisplay,
+ "_NET_SYSTEM_TRAY_ORIENTATION",
+ False);
+
+ gtk_tray_icon_update_manager_window (icon, FALSE);
+ gtk_tray_icon_send_dock_request (icon);
+
+ root_window = gdk_screen_get_root_window (screen);
+
+ /* Add a root window filter so that we get changes on MANAGER */
+ gdk_window_add_filter (root_window,
+ gtk_tray_icon_manager_filter, icon);
+}
+
+guint
+_gtk_tray_icon_send_message (GtkTrayIcon *icon,
+ gint timeout,
+ const gchar *message,
+ gint len)
+{
+ guint stamp;
+
+ g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), 0);
+ g_return_val_if_fail (timeout >= 0, 0);
+ g_return_val_if_fail (message != NULL, 0);
+
+ if (icon->priv->manager_window == None)
+ return 0;
+
+ if (len < 0)
+ len = strlen (message);
+
+ stamp = icon->priv->stamp++;
+
+ /* Get ready to send the message */
+ gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
+ icon->priv->manager_window,
+ 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 = icon->priv->manager_window;
+ 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;
+ }
+
+ XSendEvent (xdisplay,
+ icon->priv->manager_window, False,
+ StructureNotifyMask, (XEvent *)&ev);
+ XSync (xdisplay, False);
+ }
+
+ gdk_error_trap_pop ();
+
+ return stamp;
+}
+
+void
+_gtk_tray_icon_cancel_message (GtkTrayIcon *icon,
+ guint id)
+{
+ g_return_if_fail (GTK_IS_TRAY_ICON (icon));
+ g_return_if_fail (id > 0);
+
+ gtk_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
+ icon->priv->manager_window,
+ id, 0, 0);
+}
+
+GtkTrayIcon *
+_gtk_tray_icon_new_for_screen (GdkScreen *screen,
+ const gchar *name)
+{
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ return g_object_new (GTK_TYPE_TRAY_ICON,
+ "screen", screen,
+ "title", name,
+ NULL);
+}
+
+GtkTrayIcon*
+_gtk_tray_icon_new (const gchar *name)
+{
+ return g_object_new (GTK_TYPE_TRAY_ICON,
+ "title", name,
+ NULL);
+}
+
+GtkOrientation
+_gtk_tray_icon_get_orientation (GtkTrayIcon *icon)
+{
+ g_return_val_if_fail (GTK_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
+
+ return icon->priv->orientation;
+}
+
diff --git a/gtk/gtktrayicon.h b/gtk/gtktrayicon.h
new file mode 100755
index 000000000..4c1e18489
--- /dev/null
+++ b/gtk/gtktrayicon.h
@@ -0,0 +1,75 @@
+/* gtktrayicon.h
+ * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TRAY_ICON_H__
+#define __GTK_TRAY_ICON_H__
+
+#include "gtkplug.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_TRAY_ICON (gtk_tray_icon_get_type ())
+#define GTK_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TRAY_ICON, GtkTrayIcon))
+#define GTK_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TRAY_ICON, GtkTrayIconClass))
+#define GTK_IS_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TRAY_ICON))
+#define GTK_IS_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TRAY_ICON))
+#define GTK_TRAY_ICON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TRAY_ICON, GtkTrayIconClass))
+
+typedef struct _GtkTrayIcon GtkTrayIcon;
+typedef struct _GtkTrayIconPrivate GtkTrayIconPrivate;
+typedef struct _GtkTrayIconClass GtkTrayIconClass;
+
+struct _GtkTrayIcon
+{
+ GtkPlug parent_instance;
+
+ GtkTrayIconPrivate *priv;
+};
+
+struct _GtkTrayIconClass
+{
+ GtkPlugClass parent_class;
+
+ void (*__gtk_reserved1);
+ void (*__gtk_reserved2);
+ void (*__gtk_reserved3);
+ void (*__gtk_reserved4);
+ void (*__gtk_reserved5);
+ void (*__gtk_reserved6);
+};
+
+GType gtk_tray_icon_get_type (void) G_GNUC_CONST;
+
+GtkTrayIcon *_gtk_tray_icon_new_for_screen (GdkScreen *screen,
+ const gchar *name);
+
+GtkTrayIcon *_gtk_tray_icon_new (const gchar *name);
+
+guint _gtk_tray_icon_send_message (GtkTrayIcon *icon,
+ gint timeout,
+ const gchar *message,
+ gint len);
+void _gtk_tray_icon_cancel_message (GtkTrayIcon *icon,
+ guint id);
+
+GtkOrientation _gtk_tray_icon_get_orientation (GtkTrayIcon *icon);
+
+G_END_DECLS
+
+#endif /* __GTK_TRAY_ICON_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index ea7a88551..c54663a99 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -50,6 +50,7 @@ noinst_PROGRAMS = \
testselection \
$(testsocket_programs) \
testspinbutton \
+ teststatusicon \
testtext \
testtextbuffer \
testtoolbar \
@@ -95,6 +96,7 @@ testselection_DEPENDENCIES = $(TEST_DEPS)
testsocket_DEPENDENCIES = $(DEPS)
testsocket_child_DEPENDENCIES = $(DEPS)
testspinbutton_DEPENDENCIES = $(TEST_DEPS)
+teststatusicon_DEPENDENCIES = $(TEST_DEPS)
testtext_DEPENDENCIES = $(TEST_DEPS)
testtextbuffer_DEPENDENCIES = $(TEST_DEPS)
testtreeedit_DEPENDENCIES = $(DEPS)
@@ -133,6 +135,7 @@ testselection_LDADD = $(LDADDS)
testsocket_LDADD = $(LDADDS)
testsocket_child_LDADD = $(LDADDS)
testspinbutton_LDADD = $(LDADDS)
+teststatusicon_LDADD = $(LDADDS)
testtextbuffer_LDADD = $(LDADDS)
testtoolbar_LDADD = $(LDADDS)
stresstest_toolbar_LDADD = $(LDADDS)
@@ -195,6 +198,9 @@ testsocket_child_SOURCES = \
testspinbutton_SOURCES = \
testspinbutton.c
+teststatusicon_SOURCES = \
+ teststatusicon.c
+
testmerge_SOURCES = \
testmerge.c
diff --git a/tests/teststatusicon.c b/tests/teststatusicon.c
new file mode 100755
index 000000000..467baf5dd
--- /dev/null
+++ b/tests/teststatusicon.c
@@ -0,0 +1,209 @@
+/* gtkstatusicon-x11.c:
+ *
+ * Copyright (C) 2003 Sun Microsystems, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * Mark McLoughlin <mark@skynet.ie>
+ */
+
+#include <gtk/gtk.h>
+
+typedef enum
+{
+ TEST_STATUS_FILE,
+ TEST_STATUS_DIRECTORY
+} TestStatus;
+
+static TestStatus status = TEST_STATUS_FILE;
+static gint timeout = 0;
+
+static void
+update_icon (GtkStatusIcon *status_icon)
+{
+ gchar *icon_name;
+ gchar *tooltip;
+
+ if (status == TEST_STATUS_FILE)
+ {
+ icon_name = "gnome-fs-regular";
+ tooltip = "Regular File";
+ }
+ else
+ {
+ icon_name = "gnome-fs-directory";
+ tooltip = "Directory";
+ }
+
+ gtk_status_icon_set_from_icon_name (status_icon, icon_name);
+ gtk_status_icon_set_tooltip (status_icon, tooltip);
+}
+
+static gboolean
+timeout_handler (gpointer data)
+{
+ GtkStatusIcon *icon = GTK_STATUS_ICON (data);
+
+ if (status == TEST_STATUS_FILE)
+ status = TEST_STATUS_DIRECTORY;
+ else
+ status = TEST_STATUS_FILE;
+
+ update_icon (icon);
+
+ return TRUE;
+}
+
+static void
+blink_toggle_toggled (GtkToggleButton *toggle,
+ GtkStatusIcon *icon)
+{
+ gtk_status_icon_set_blinking (icon,
+ gtk_toggle_button_get_active (toggle));
+}
+
+static void
+visible_toggle_toggled (GtkToggleButton *toggle,
+ GtkStatusIcon *icon)
+{
+ gtk_status_icon_set_visible (icon,
+ gtk_toggle_button_get_active (toggle));
+}
+
+static void
+timeout_toggle_toggled (GtkToggleButton *toggle,
+ GtkStatusIcon *icon)
+{
+ if (timeout)
+ {
+ g_source_remove (timeout);
+ timeout = 0;
+ }
+ else
+ {
+ timeout = g_timeout_add (2000, timeout_handler, icon);
+ }
+}
+
+static void
+icon_activated (GtkStatusIcon *icon)
+{
+ GtkWidget *dialog;
+ GtkWidget *toggle;
+
+ dialog = g_object_get_data (G_OBJECT (icon), "test-status-icon-dialog");
+ if (dialog == NULL)
+ {
+ dialog = gtk_message_dialog_new (NULL, 0,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_CLOSE,
+ "You wanna test the status icon ?");
+
+ g_object_set_data_full (G_OBJECT (icon), "test-status-icon-dialog",
+ dialog, (GDestroyNotify) gtk_widget_destroy);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gtk_widget_hide), NULL);
+ g_signal_connect (dialog, "delete_event",
+ G_CALLBACK (gtk_widget_hide_on_delete), NULL);
+
+ toggle = gtk_toggle_button_new_with_mnemonic ("_Show the icon");
+ gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox), toggle, TRUE, TRUE, 6);
+ gtk_widget_show (toggle);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ gtk_status_icon_get_visible (icon));
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (visible_toggle_toggled), icon);
+
+ toggle = gtk_toggle_button_new_with_mnemonic ("_Blink the icon");
+ gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox), toggle, TRUE, TRUE, 6);
+ gtk_widget_show (toggle);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ gtk_status_icon_get_blinking (icon));
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (blink_toggle_toggled), icon);
+
+ toggle = gtk_toggle_button_new_with_mnemonic ("_Change images");
+ gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox), toggle, TRUE, TRUE, 6);
+ gtk_widget_show (toggle);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ timeout != 0);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (timeout_toggle_toggled), icon);
+ }
+
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+check_activated (GtkCheckMenuItem *item,
+ GtkStatusIcon *icon)
+{
+ gtk_status_icon_set_blinking (icon,
+ gtk_check_menu_item_get_active (item));
+}
+
+static void
+popup_menu (GtkStatusIcon *icon,
+ guint button,
+ guint32 activate_time)
+{
+ GtkWidget *menu, *menuitem;
+
+ menu = gtk_menu_new ();
+
+ menuitem = gtk_check_menu_item_new_with_label ("Blink");
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem),
+ gtk_status_icon_get_blinking (icon));
+ g_signal_connect (menuitem, "activate", G_CALLBACK (check_activated), icon);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+
+ gtk_widget_show (menuitem);
+
+ gtk_menu_popup (GTK_MENU (menu),
+ NULL, NULL, NULL, NULL,
+ button, activate_time);
+}
+
+int
+main (int argc, char **argv)
+{
+ GtkStatusIcon *icon;
+
+ gtk_init (&argc, &argv);
+
+ icon = gtk_status_icon_new ();
+ update_icon (icon);
+
+ gtk_status_icon_set_blinking (GTK_STATUS_ICON (icon), TRUE);
+
+ g_signal_connect (icon, "activate",
+ G_CALLBACK (icon_activated), NULL);
+
+ g_signal_connect (icon, "popup-menu",
+ G_CALLBACK (popup_menu), NULL);
+
+ timeout = g_timeout_add (2000, timeout_handler, icon);
+
+ gtk_main ();
+
+ return 0;
+}