diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/egg/Makefile.am | 11 | ||||
-rw-r--r-- | lib/egg/eggstatusicon.c | 796 | ||||
-rw-r--r-- | lib/egg/eggstatusicon.h | 101 | ||||
-rw-r--r-- | lib/egg/eggtrayicon.c | 480 | ||||
-rw-r--r-- | lib/egg/eggtrayicon.h | 77 | ||||
-rw-r--r-- | lib/egg/eggtraymanager.c | 725 | ||||
-rw-r--r-- | lib/egg/eggtraymanager.h | 93 | ||||
-rwxr-xr-x | lib/egg/update-from-egg.sh | 11 |
8 files changed, 2284 insertions, 10 deletions
diff --git a/lib/egg/Makefile.am b/lib/egg/Makefile.am index 53a2dbf26..66e4b29fe 100644 --- a/lib/egg/Makefile.am +++ b/lib/egg/Makefile.am @@ -1,6 +1,5 @@ INCLUDES = \ $(EPIPHANY_DEPENDENCY_CFLAGS) \ - $(WARN_CFLAGS) \ -DCURSOR_DIR=\"$(pkgdatadir)\" \ -DGTK_DISABLE_DEPRECATED \ -DGDK_DISABLE_DEPRECATED \ @@ -12,7 +11,10 @@ EGGSOURCES = \ eggtreemultidnd.c \ egg-editable-toolbar.c \ egg-toolbars-model.c \ - egg-toolbar-editor.c + egg-toolbar-editor.c \ + eggstatusicon.c \ + eggtrayicon.c \ + eggtraymanager.c libegg_la_SOURCES = \ $(EGGSOURCES) \ @@ -22,7 +24,10 @@ EGGHEADERS = \ eggtreemultidnd.h \ egg-editable-toolbar.h \ egg-toolbars-model.h \ - egg-toolbar-editor.h + egg-toolbar-editor.h \ + eggstatusicon.h \ + eggtrayicon.h \ + eggtraymanager.h noinst_HEADERS = \ $(EGGHEADERS) \ diff --git a/lib/egg/eggstatusicon.c b/lib/egg/eggstatusicon.c new file mode 100644 index 000000000..72dcef518 --- /dev/null +++ b/lib/egg/eggstatusicon.c @@ -0,0 +1,796 @@ +/* eggstatusicon.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 <libintl.h> + +#include "eggstatusicon.h" + +#include <gtk/gtk.h> +#include "eggmarshalers.h" + +#ifndef EGG_COMPILATION +#ifndef _ +#define _(x) dgettext (GETTEXT_PACKAGE, x) +#define N_(x) x +#endif +#else +#define _(x) x +#define N_(x) x +#endif + +enum{ + PROP_0, + PROP_PIXBUF, + PROP_FILE, + PROP_STOCK, + PROP_PIXBUF_ANIMATION, + PROP_STORAGE_TYPE, + PROP_SIZE, + PROP_BLINKING +}; + +enum { + ACTIVATE_SIGNAL, + POPUP_MENU_SIGNAL, + SIZE_CHANGED_SIGNAL, + LAST_SIGNAL +}; + +struct _EggStatusIconPrivate +{ + GtkWidget *tray_icon; + GtkWidget *image; + gint size; + + GtkTooltips *tooltips; + + GtkImageType image_type; + + union + { + GdkPixbuf *pixbuf; + const gchar *stock_id; + GdkPixbufAnimation *animimation; + } image_data; + + GdkPixbuf *blank_icon; + guint blinking_timeout; + + guint blinking : 1; + guint blink_off : 1; + guint button_down : 1; +}; + +static void egg_status_icon_class_init (EggStatusIconClass *klass); +static void egg_status_icon_init (EggStatusIcon *status_icon); + +static void egg_status_icon_finalize (GObject *object); +static void egg_status_icon_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void egg_status_icon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void egg_status_icon_size_allocate (EggStatusIcon *status_icon, + GtkAllocation *allocation); +static gboolean egg_status_icon_button_press (EggStatusIcon *status_icon, + GdkEventButton *event); +static gboolean egg_status_icon_button_release (EggStatusIcon *status_icon, + GdkEventButton *event); +static void egg_status_icon_disable_blinking (EggStatusIcon *status_icon); +static void egg_status_icon_reset_image_data (EggStatusIcon *status_icon); + + +static GObjectClass *parent_class = NULL; +static guint status_icon_signals [LAST_SIGNAL] = { 0 }; + +GType +egg_status_icon_get_type (void) +{ + static GType status_icon_type = 0; + + if (!status_icon_type) + { + static const GTypeInfo status_icon_info = + { + sizeof (EggStatusIconClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) egg_status_icon_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EggStatusIcon), + 0, /* n_preallocs */ + (GInstanceInitFunc) egg_status_icon_init, + }; + + status_icon_type = g_type_register_static (G_TYPE_OBJECT, + "EggStatusIcon", + &status_icon_info, 0); + } + + return status_icon_type; +} + +static void +egg_status_icon_class_init (EggStatusIconClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = egg_status_icon_finalize; + gobject_class->set_property = egg_status_icon_set_property; + gobject_class->get_property = egg_status_icon_get_property; + + g_object_class_install_property (gobject_class, + PROP_PIXBUF, + g_param_spec_object ("pixbuf", + _("Pixbuf"), + _("A GdkPixbuf to display"), + GDK_TYPE_PIXBUF, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, + PROP_FILE, + g_param_spec_string ("file", + _("Filename"), + _("Filename to load and display"), + NULL, + G_PARAM_WRITABLE)); + + g_object_class_install_property (gobject_class, + PROP_STOCK, + g_param_spec_string ("stock", + _("Stock ID"), + _("Stock ID for a stock image to display"), + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, + PROP_PIXBUF_ANIMATION, + g_param_spec_object ("pixbuf-animation", + _("Animation"), + _("GdkPixbufAnimation to display"), + GDK_TYPE_PIXBUF_ANIMATION, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, + PROP_STORAGE_TYPE, + g_param_spec_enum ("image-type", + _("Image type"), + _("The representation being used for image data"), + GTK_TYPE_IMAGE_TYPE, + GTK_IMAGE_EMPTY, + G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, + PROP_SIZE, + g_param_spec_int ("size", + _("Size"), + _("The size of the icon"), + G_MININT, + G_MAXINT, + 0, + G_PARAM_READABLE)); + + g_object_class_install_property (gobject_class, + PROP_BLINKING, + g_param_spec_boolean ("blinking", + _("Blinking"), + _("Whether or not the status icon is blinking"), + FALSE, + G_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 (EggStatusIconClass, 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 (EggStatusIconClass, popup_menu), + NULL, + NULL, + _egg_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 (EggStatusIconClass, size_changed), + NULL, + NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, + 1, + G_TYPE_INT); +} + +static void +egg_status_icon_init (EggStatusIcon *status_icon) +{ + status_icon->priv = g_new0 (EggStatusIconPrivate, 1); + + status_icon->priv->image_type = GTK_IMAGE_EMPTY; + status_icon->priv->size = G_MAXINT; + + status_icon->priv->tray_icon = GTK_WIDGET (egg_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 (egg_status_icon_button_press), status_icon); + g_signal_connect_swapped (status_icon->priv->tray_icon, "button-release-event", + G_CALLBACK (egg_status_icon_button_release), 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 (egg_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 +egg_status_icon_finalize (GObject *object) +{ + EggStatusIcon *status_icon = EGG_STATUS_ICON (object); + + egg_status_icon_disable_blinking (status_icon); + + egg_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_free (status_icon->priv); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +egg_status_icon_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EggStatusIcon *status_icon = EGG_STATUS_ICON (object); + + switch (prop_id) + { + case PROP_PIXBUF: + egg_status_icon_set_from_pixbuf (status_icon, g_value_get_object (value)); + break; + case PROP_FILE: + egg_status_icon_set_from_file (status_icon, g_value_get_string (value)); + break; + case PROP_STOCK: + egg_status_icon_set_from_stock (status_icon, g_value_get_string (value)); + break; + case PROP_PIXBUF_ANIMATION: + egg_status_icon_set_from_animation (status_icon, g_value_get_object (value)); + break; + case PROP_BLINKING: + egg_status_icon_set_is_blinking (status_icon, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +egg_status_icon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EggStatusIcon *status_icon = EGG_STATUS_ICON (object); + + switch (prop_id) + { + case PROP_PIXBUF: + g_value_set_object (value, egg_status_icon_get_pixbuf (status_icon)); + break; + case PROP_STOCK: + g_value_set_string (value, egg_status_icon_get_stock (status_icon)); + break; + case PROP_PIXBUF_ANIMATION: + g_value_set_object (value, egg_status_icon_get_animation (status_icon)); + break; + case PROP_STORAGE_TYPE: + g_value_set_enum (value, egg_status_icon_get_image_type (status_icon)); + break; + case PROP_SIZE: + g_value_set_int (value, status_icon->priv->size); + break; + case PROP_BLINKING: + g_value_set_boolean (value, status_icon->priv->blinking); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +EggStatusIcon * +egg_status_icon_new (void) +{ + return g_object_new (EGG_TYPE_STATUS_ICON, NULL); +} + +EggStatusIcon * +egg_status_icon_new_from_pixbuf (GdkPixbuf *pixbuf) +{ + return g_object_new (EGG_TYPE_STATUS_ICON, + "pixbuf", pixbuf, + NULL); +} + +EggStatusIcon * +egg_status_icon_new_from_file (const gchar *filename) +{ + return g_object_new (EGG_TYPE_STATUS_ICON, + "file", filename, + NULL); +} + +EggStatusIcon * +egg_status_icon_new_from_stock (const gchar *stock_id) +{ + return g_object_new (EGG_TYPE_STATUS_ICON, + "stock", stock_id, + NULL); +} + +EggStatusIcon * +egg_status_icon_new_from_animation (GdkPixbufAnimation *animation) +{ + return g_object_new (EGG_TYPE_STATUS_ICON, + "pixbuf_animation", animation, + NULL); +} + +static void +emit_activate_signal (EggStatusIcon *status_icon) +{ + g_signal_emit (status_icon, + status_icon_signals [ACTIVATE_SIGNAL], 0); +} + +#ifdef UNUSED +static void +emit_popup_menu_signal (EggStatusIcon *status_icon, + guint button, + guint32 activate_time) +{ + g_signal_emit (status_icon, + status_icon_signals [POPUP_MENU_SIGNAL], 0, + button, + activate_time); +} +#endif + +static gboolean +emit_size_changed_signal (EggStatusIcon *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 * +egg_status_icon_blank_icon (EggStatusIcon *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_width (status_icon->priv->blank_icon); + + if (width == status_icon->priv->size && + height == status_icon->priv->size) + { + 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->size, + status_icon->priv->size); + if (status_icon->priv->blank_icon) + gdk_pixbuf_fill (status_icon->priv->blank_icon, 0); + + return status_icon->priv->blank_icon; +} + +static void +egg_status_icon_update_image (EggStatusIcon *status_icon) +{ + if (status_icon->priv->blink_off) + { + gtk_image_set_from_pixbuf (GTK_IMAGE (status_icon->priv->image), + egg_status_icon_blank_icon (status_icon)); + return; + } + + switch (status_icon->priv->image_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: + case GTK_IMAGE_ANIMATION: + 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 +egg_status_icon_size_allocate (EggStatusIcon *status_icon, + GtkAllocation *allocation) +{ + GtkOrientation orientation; + gint size; + + orientation = egg_tray_icon_get_orientation (EGG_TRAY_ICON (status_icon->priv->tray_icon)); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + size = allocation->height; + else + size = allocation->width; + + 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)) + { + egg_status_icon_update_image (status_icon); + } + } +} + +static gboolean +egg_status_icon_button_press (EggStatusIcon *status_icon, + GdkEventButton *event) +{ + if (event->button == 1 && !status_icon->priv->button_down) + { + status_icon->priv->button_down = TRUE; + return TRUE; + } + + return FALSE; +} + +static gboolean +egg_status_icon_button_release (EggStatusIcon *status_icon, + GdkEventButton *event) +{ + if (event->button == 1 && status_icon->priv->button_down) + { + status_icon->priv->button_down = FALSE; + emit_activate_signal (status_icon); + return TRUE; + } + + return FALSE; +} + +static void +egg_status_icon_reset_image_data (EggStatusIcon *status_icon) +{ + switch (status_icon->priv->image_type) + { + case GTK_IMAGE_PIXBUF: + status_icon->priv->image_type = GTK_IMAGE_EMPTY; + + 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), "image-type"); + g_object_notify (G_OBJECT (status_icon), "pixbuf"); + break; + case GTK_IMAGE_STOCK: + case GTK_IMAGE_ANIMATION: + case GTK_IMAGE_EMPTY: + break; + default: + g_assert_not_reached (); + break; + } +} + +void +egg_status_icon_set_from_pixbuf (EggStatusIcon *status_icon, + GdkPixbuf *pixbuf) +{ + g_return_if_fail (EGG_IS_STATUS_ICON (status_icon)); + g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf)); + + if (pixbuf) + g_object_ref (pixbuf); + + g_object_freeze_notify (G_OBJECT (status_icon)); + + egg_status_icon_reset_image_data (status_icon); + + status_icon->priv->image_type = GTK_IMAGE_PIXBUF; + status_icon->priv->image_data.pixbuf = pixbuf; + + g_object_notify (G_OBJECT (status_icon), "image-type"); + g_object_notify (G_OBJECT (status_icon), "pixbuf"); + + g_object_thaw_notify (G_OBJECT (status_icon)); + + egg_status_icon_update_image (status_icon); +} + +void +egg_status_icon_set_from_file (EggStatusIcon *status_icon, + const gchar *filename) +{ + g_return_if_fail (EGG_IS_STATUS_ICON (status_icon)); +} + +void +egg_status_icon_set_from_stock (EggStatusIcon *status_icon, + const gchar *stock_id) +{ + g_return_if_fail (EGG_IS_STATUS_ICON (status_icon)); +} + +void +egg_status_icon_set_from_animation (EggStatusIcon *status_icon, + GdkPixbufAnimation *animation) +{ + g_return_if_fail (EGG_IS_STATUS_ICON (status_icon)); + g_return_if_fail (animation == NULL || GDK_IS_PIXBUF_ANIMATION (animation)); +} + +GtkImageType +egg_status_icon_get_image_type (EggStatusIcon *status_icon) +{ + g_return_val_if_fail (EGG_IS_STATUS_ICON (status_icon), GTK_IMAGE_EMPTY); + + return status_icon->priv->image_type; +} + +GdkPixbuf * +egg_status_icon_get_pixbuf (EggStatusIcon *status_icon) +{ + g_return_val_if_fail (EGG_IS_STATUS_ICON (status_icon), NULL); + g_return_val_if_fail (status_icon->priv->image_type == GTK_IMAGE_PIXBUF || + status_icon->priv->image_type == GTK_IMAGE_EMPTY, NULL); + + if (status_icon->priv->image_type == GTK_IMAGE_EMPTY) + status_icon->priv->image_data.pixbuf = NULL; + + return status_icon->priv->image_data.pixbuf; +} + +G_CONST_RETURN gchar * +egg_status_icon_get_stock (EggStatusIcon *status_icon) +{ + g_return_val_if_fail (EGG_IS_STATUS_ICON (status_icon), NULL); + + return NULL; +} + +GdkPixbufAnimation * +egg_status_icon_get_animation (EggStatusIcon *status_icon) +{ + g_return_val_if_fail (EGG_IS_STATUS_ICON (status_icon), NULL); + + return NULL; +} + +gint +egg_status_icon_get_size (EggStatusIcon *status_icon) +{ + g_return_val_if_fail (EGG_IS_STATUS_ICON (status_icon), -1); + + return status_icon->priv->size; +} + +void +egg_status_icon_set_tooltip (EggStatusIcon *status_icon, + const gchar *tooltip_text, + const gchar *tooltip_private) +{ + g_return_if_fail (EGG_IS_STATUS_ICON (status_icon)); + + gtk_tooltips_set_tip (status_icon->priv->tooltips, + status_icon->priv->tray_icon, + tooltip_text, + tooltip_private); +} + +void +egg_status_icon_set_balloon_text (EggStatusIcon *status_icon, + const gchar *text) +{ + g_return_if_fail (EGG_IS_STATUS_ICON (status_icon)); +} + +G_CONST_RETURN gchar * +egg_status_icon_get_balloon_text (EggStatusIcon *status_icon) +{ + g_return_val_if_fail (EGG_IS_STATUS_ICON (status_icon), NULL); + + return NULL; +} + +static gboolean +egg_status_icon_blinker (EggStatusIcon *status_icon) +{ + status_icon->priv->blink_off = !status_icon->priv->blink_off; + + egg_status_icon_update_image (status_icon); + + return TRUE; +} + +static void +egg_status_icon_enable_blinking (EggStatusIcon *status_icon) +{ + if (!status_icon->priv->blinking_timeout) + { + egg_status_icon_blinker (status_icon); + + status_icon->priv->blinking_timeout = + g_timeout_add (500, (GSourceFunc) egg_status_icon_blinker, status_icon); + } +} + +static void +egg_status_icon_disable_blinking (EggStatusIcon *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; + + egg_status_icon_update_image (status_icon); + } +} + +void +egg_status_icon_set_is_blinking (EggStatusIcon *status_icon, + gboolean is_blinking) +{ + g_return_if_fail (EGG_IS_STATUS_ICON (status_icon)); + + is_blinking = is_blinking != FALSE; + + if (status_icon->priv->blinking != is_blinking) + { + status_icon->priv->blinking = is_blinking; + + if (is_blinking) + egg_status_icon_enable_blinking (status_icon); + else + egg_status_icon_disable_blinking (status_icon); + + g_object_notify (G_OBJECT (status_icon), "blinking"); + } +} + +gboolean +egg_status_icon_get_is_blinking (EggStatusIcon *status_icon) +{ + g_return_val_if_fail (EGG_IS_STATUS_ICON (status_icon), FALSE); + + return status_icon->priv->blinking; +} diff --git a/lib/egg/eggstatusicon.h b/lib/egg/eggstatusicon.h new file mode 100644 index 000000000..cde7ae9df --- /dev/null +++ b/lib/egg/eggstatusicon.h @@ -0,0 +1,101 @@ +/* eggstatusicon.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 __EGG_STATUS_ICON_H__ +#define __EGG_STATUS_ICON_H__ + +#include "eggtrayicon.h" +#include <gtk/gtkimage.h> + +G_BEGIN_DECLS + +#define EGG_TYPE_STATUS_ICON (egg_status_icon_get_type ()) +#define EGG_STATUS_ICON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EGG_TYPE_STATUS_ICON, EggStatusIcon)) +#define EGG_STATUS_ICON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), EGG_TYPE_STATUS_ICON, EggStatusIconClass)) +#define EGG_IS_STATUS_ICON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EGG_TYPE_STATUS_ICON)) +#define EGG_IS_STATUS_ICON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EGG_TYPE_STATUS_ICON)) +#define EGG_STATUS_ICON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EGG_TYPE_STATUS_ICON, EggStatusIconClass)) + +typedef struct _EggStatusIcon EggStatusIcon; +typedef struct _EggStatusIconClass EggStatusIconClass; +typedef struct _EggStatusIconPrivate EggStatusIconPrivate; + +struct _EggStatusIcon +{ + GObject parent_instance; + + EggStatusIconPrivate *priv; +}; + +struct _EggStatusIconClass +{ + GObjectClass parent_class; + + void (* activate) (EggStatusIcon *status_icon); + void (* popup_menu) (EggStatusIcon *status_icon, + guint buttton, + guint32 activate_time); + gboolean (* size_changed) (EggStatusIcon *status_icon, + gint size); +}; + +GType egg_status_icon_get_type (void); + +EggStatusIcon *egg_status_icon_new (void); +EggStatusIcon *egg_status_icon_new_from_pixbuf (GdkPixbuf *pixbuf); +EggStatusIcon *egg_status_icon_new_from_file (const gchar *filename); +EggStatusIcon *egg_status_icon_new_from_stock (const gchar *stock_id); +EggStatusIcon *egg_status_icon_new_from_animation (GdkPixbufAnimation *animation); + +void egg_status_icon_set_from_pixbuf (EggStatusIcon *status_icon, + GdkPixbuf *pixbuf); +void egg_status_icon_set_from_file (EggStatusIcon *status_icon, + const gchar *filename); +void egg_status_icon_set_from_stock (EggStatusIcon *status_icon, + const gchar *stock_id); +void egg_status_icon_set_from_animation (EggStatusIcon *status_icon, + GdkPixbufAnimation *animation); + +GtkImageType egg_status_icon_get_image_type (EggStatusIcon *status_icon); + +GdkPixbuf *egg_status_icon_get_pixbuf (EggStatusIcon *status_icon); +G_CONST_RETURN gchar *egg_status_icon_get_stock (EggStatusIcon *status_icon); +GdkPixbufAnimation *egg_status_icon_get_animation (EggStatusIcon *status_icon); + +gint egg_status_icon_get_size (EggStatusIcon *status_icon); + +void egg_status_icon_set_tooltip (EggStatusIcon *status_icon, + const gchar *tooltip_text, + const gchar *tooltip_private); + +void egg_status_icon_set_balloon_text (EggStatusIcon *status_icon, + const gchar *text); +G_CONST_RETURN gchar *egg_status_icon_get_balloon_text (EggStatusIcon *status_icon); + +void egg_status_icon_set_is_blinking (EggStatusIcon *status_icon, + gboolean enable_blinking); +gboolean egg_status_icon_get_is_blinking (EggStatusIcon *status_icon); + +G_END_DECLS + +#endif /* __EGG_STATUS_ICON_H__ */ diff --git a/lib/egg/eggtrayicon.c b/lib/egg/eggtrayicon.c new file mode 100644 index 000000000..6c309306e --- /dev/null +++ b/lib/egg/eggtrayicon.c @@ -0,0 +1,480 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* eggtrayicon.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 "eggtrayicon.h" + +#include <gdk/gdkx.h> +#include <X11/Xatom.h> + +#ifndef EGG_COMPILATION +#ifndef _ +#define _(x) dgettext (GETTEXT_PACKAGE, x) +#define N_(x) x +#endif +#else +#define _(x) x +#define N_(x) x +#endif + +#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 +}; + +static GtkPlugClass *parent_class = NULL; + +static void egg_tray_icon_init (EggTrayIcon *icon); +static void egg_tray_icon_class_init (EggTrayIconClass *klass); + +static void egg_tray_icon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void egg_tray_icon_realize (GtkWidget *widget); +static void egg_tray_icon_unrealize (GtkWidget *widget); + +static void egg_tray_icon_update_manager_window (EggTrayIcon *icon); + +GType +egg_tray_icon_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + { + static const GTypeInfo our_info = + { + sizeof (EggTrayIconClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) egg_tray_icon_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EggTrayIcon), + 0, /* n_preallocs */ + (GInstanceInitFunc) egg_tray_icon_init + }; + + our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0); + } + + return our_type; +} + +static void +egg_tray_icon_init (EggTrayIcon *icon) +{ + icon->stamp = 1; + icon->orientation = GTK_ORIENTATION_HORIZONTAL; + + gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK); +} + +static void +egg_tray_icon_class_init (EggTrayIconClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *)klass; + GtkWidgetClass *widget_class = (GtkWidgetClass *)klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->get_property = egg_tray_icon_get_property; + + widget_class->realize = egg_tray_icon_realize; + widget_class->unrealize = egg_tray_icon_unrealize; + + g_object_class_install_property (gobject_class, + PROP_ORIENTATION, + g_param_spec_enum ("orientation", + _("Orientation"), + _("The orientation of the tray."), + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_HORIZONTAL, + G_PARAM_READABLE)); +} + +static void +egg_tray_icon_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EggTrayIcon *icon = EGG_TRAY_ICON (object); + + switch (prop_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, icon->orientation); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +egg_tray_icon_get_orientation_property (EggTrayIcon *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->manager_window != None); + + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); + + gdk_error_trap_push (); + type = None; + result = XGetWindowProperty (xdisplay, + icon->manager_window, + icon->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->orientation != orientation) + { + icon->orientation = orientation; + + g_object_notify (G_OBJECT (icon), "orientation"); + } + } + + if (prop.prop) + XFree (prop.prop); +} + +static GdkFilterReturn +egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data) +{ + EggTrayIcon *icon = user_data; + XEvent *xev = (XEvent *)xevent; + + if (xev->xany.type == ClientMessage && + xev->xclient.message_type == icon->manager_atom && + xev->xclient.data.l[1] == icon->selection_atom) + { + egg_tray_icon_update_manager_window (icon); + } + else if (xev->xany.window == icon->manager_window) + { + if (xev->xany.type == PropertyNotify && + xev->xproperty.atom == icon->orientation_atom) + { + egg_tray_icon_get_orientation_property (icon); + } + if (xev->xany.type == DestroyNotify) + { + egg_tray_icon_update_manager_window (icon); + } + } + + return GDK_FILTER_CONTINUE; +} + +static void +egg_tray_icon_unrealize (GtkWidget *widget) +{ + EggTrayIcon *icon = EGG_TRAY_ICON (widget); + GdkWindow *root_window; + + if (icon->manager_window != None) + { + GdkWindow *gdkwin; + + gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget), + icon->manager_window); + + gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon); + } + + root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget)); + + gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon); + + if (GTK_WIDGET_CLASS (parent_class)->unrealize) + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); +} + +static void +egg_tray_icon_send_manager_message (EggTrayIcon *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->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->manager_window, False, NoEventMask, (XEvent *)&ev); + XSync (display, False); + gdk_error_trap_pop (); +} + +static void +egg_tray_icon_send_dock_request (EggTrayIcon *icon) +{ + egg_tray_icon_send_manager_message (icon, + SYSTEM_TRAY_REQUEST_DOCK, + icon->manager_window, + gtk_plug_get_id (GTK_PLUG (icon)), + 0, 0); +} + +static void +egg_tray_icon_update_manager_window (EggTrayIcon *icon) +{ + Display *xdisplay; + + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); + + if (icon->manager_window != None) + { + GdkWindow *gdkwin; + + gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)), + icon->manager_window); + + gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon); + } + + XGrabServer (xdisplay); + + icon->manager_window = XGetSelectionOwner (xdisplay, + icon->selection_atom); + + if (icon->manager_window != None) + XSelectInput (xdisplay, + icon->manager_window, StructureNotifyMask|PropertyChangeMask); + + XUngrabServer (xdisplay); + XFlush (xdisplay); + + if (icon->manager_window != None) + { + GdkWindow *gdkwin; + + gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)), + icon->manager_window); + + gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon); + + /* Send a request that we'd like to dock */ + egg_tray_icon_send_dock_request (icon); + + egg_tray_icon_get_orientation_property (icon); + } +} + +static void +egg_tray_icon_realize (GtkWidget *widget) +{ + EggTrayIcon *icon = EGG_TRAY_ICON (widget); + GdkScreen *screen; + GdkDisplay *display; + Display *xdisplay; + char buffer[256]; + GdkWindow *root_window; + + if (GTK_WIDGET_CLASS (parent_class)->realize) + GTK_WIDGET_CLASS (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->selection_atom = XInternAtom (xdisplay, buffer, False); + + icon->manager_atom = XInternAtom (xdisplay, "MANAGER", False); + + icon->system_tray_opcode_atom = XInternAtom (xdisplay, + "_NET_SYSTEM_TRAY_OPCODE", + False); + + icon->orientation_atom = XInternAtom (xdisplay, + "_NET_SYSTEM_TRAY_ORIENTATION", + False); + + egg_tray_icon_update_manager_window (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, + egg_tray_icon_manager_filter, icon); +} + +EggTrayIcon * +egg_tray_icon_new_for_xscreen (Screen *xscreen, const char *name) +{ + GdkDisplay *display; + GdkScreen *screen; + + display = gdk_x11_lookup_xdisplay (DisplayOfScreen (xscreen)); + screen = gdk_display_get_screen (display, XScreenNumberOfScreen (xscreen)); + + return egg_tray_icon_new_for_screen (screen, name); +} + +EggTrayIcon * +egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name) +{ + g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); + + return g_object_new (EGG_TYPE_TRAY_ICON, "screen", screen, "title", name, NULL); +} + +EggTrayIcon* +egg_tray_icon_new (const gchar *name) +{ + return g_object_new (EGG_TYPE_TRAY_ICON, "title", name, NULL); +} + +guint +egg_tray_icon_send_message (EggTrayIcon *icon, + gint timeout, + const gchar *message, + gint len) +{ + guint stamp; + + g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0); + g_return_val_if_fail (timeout >= 0, 0); + g_return_val_if_fail (message != NULL, 0); + + if (icon->manager_window == None) + return 0; + + if (len < 0) + len = strlen (message); + + stamp = icon->stamp++; + + /* Get ready to send the message */ + egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE, + (Window)gtk_plug_get_id (GTK_PLUG (icon)), + timeout, len, stamp); + + /* Now to send the actual message */ + gdk_error_trap_push (); + while (len > 0) + { + XClientMessageEvent ev; + Display *xdisplay; + + xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon))); + + ev.type = ClientMessage; + ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon)); + ev.format = 8; + ev.message_type = XInternAtom (xdisplay, + "_NET_SYSTEM_TRAY_MESSAGE_DATA", False); + if (len > 20) + { + memcpy (&ev.data, message, 20); + len -= 20; + message += 20; + } + else + { + memcpy (&ev.data, message, len); + len = 0; + } + + XSendEvent (xdisplay, + icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev); + XSync (xdisplay, False); + } + gdk_error_trap_pop (); + + return stamp; +} + +void +egg_tray_icon_cancel_message (EggTrayIcon *icon, + guint id) +{ + g_return_if_fail (EGG_IS_TRAY_ICON (icon)); + g_return_if_fail (id > 0); + + egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE, + (Window)gtk_plug_get_id (GTK_PLUG (icon)), + id, 0, 0); +} + +GtkOrientation +egg_tray_icon_get_orientation (EggTrayIcon *icon) +{ + g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL); + + return icon->orientation; +} diff --git a/lib/egg/eggtrayicon.h b/lib/egg/eggtrayicon.h new file mode 100644 index 000000000..007f4c18e --- /dev/null +++ b/lib/egg/eggtrayicon.h @@ -0,0 +1,77 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* eggtrayicon.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 __EGG_TRAY_ICON_H__ +#define __EGG_TRAY_ICON_H__ + +#include <gtk/gtkplug.h> +#include <gdk/gdkx.h> + +G_BEGIN_DECLS + +#define EGG_TYPE_TRAY_ICON (egg_tray_icon_get_type ()) +#define EGG_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TRAY_ICON, EggTrayIcon)) +#define EGG_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TRAY_ICON, EggTrayIconClass)) +#define EGG_IS_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TRAY_ICON)) +#define EGG_IS_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TRAY_ICON)) +#define EGG_TRAY_ICON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TRAY_ICON, EggTrayIconClass)) + +typedef struct _EggTrayIcon EggTrayIcon; +typedef struct _EggTrayIconClass EggTrayIconClass; + +struct _EggTrayIcon +{ + GtkPlug parent_instance; + + guint stamp; + + Atom selection_atom; + Atom manager_atom; + Atom system_tray_opcode_atom; + Atom orientation_atom; + Window manager_window; + + GtkOrientation orientation; +}; + +struct _EggTrayIconClass +{ + GtkPlugClass parent_class; +}; + +GType egg_tray_icon_get_type (void); + +EggTrayIcon *egg_tray_icon_new_for_screen (GdkScreen *screen, + const gchar *name); + +EggTrayIcon *egg_tray_icon_new (const gchar *name); + +guint egg_tray_icon_send_message (EggTrayIcon *icon, + gint timeout, + const char *message, + gint len); +void egg_tray_icon_cancel_message (EggTrayIcon *icon, + guint id); + +GtkOrientation egg_tray_icon_get_orientation (EggTrayIcon *icon); + +G_END_DECLS + +#endif /* __EGG_TRAY_ICON_H__ */ diff --git a/lib/egg/eggtraymanager.c b/lib/egg/eggtraymanager.c new file mode 100644 index 000000000..f076a64d3 --- /dev/null +++ b/lib/egg/eggtraymanager.c @@ -0,0 +1,725 @@ +/* eggtraymanager.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 "eggtraymanager.h" + +#include <gdk/gdkx.h> +#include <gtk/gtkinvisible.h> +#include <gtk/gtksocket.h> +#include <gtk/gtkwindow.h> +#include <X11/Xatom.h> + +#include "eggmarshalers.h" + +#ifndef EGG_COMPILATION +#ifndef _ +#define _(x) dgettext (GETTEXT_PACKAGE, x) +#define N_(x) x +#endif +#else +#define _(x) x +#define N_(x) x +#endif + +/* Signals */ +enum +{ + TRAY_ICON_ADDED, + TRAY_ICON_REMOVED, + MESSAGE_SENT, + MESSAGE_CANCELLED, + LOST_SELECTION, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_ORIENTATION +}; + +typedef struct +{ + long id, len; + long remaining_len; + + long timeout; + Window window; + char *str; +} PendingMessage; + +static GObjectClass *parent_class = NULL; +static guint manager_signals[LAST_SIGNAL] = { 0 }; + +#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 + +static gboolean egg_tray_manager_check_running_xscreen (Screen *xscreen); + +static void egg_tray_manager_init (EggTrayManager *manager); +static void egg_tray_manager_class_init (EggTrayManagerClass *klass); + +static void egg_tray_manager_finalize (GObject *object); +static void egg_tray_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void egg_tray_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void egg_tray_manager_unmanage (EggTrayManager *manager); + +GType +egg_tray_manager_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + { + static const GTypeInfo our_info = + { + sizeof (EggTrayManagerClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) egg_tray_manager_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (EggTrayManager), + 0, /* n_preallocs */ + (GInstanceInitFunc) egg_tray_manager_init + }; + + our_type = g_type_register_static (G_TYPE_OBJECT, "EggTrayManager", &our_info, 0); + } + + return our_type; + +} + +static void +egg_tray_manager_init (EggTrayManager *manager) +{ + manager->socket_table = g_hash_table_new (NULL, NULL); +} + +static void +egg_tray_manager_class_init (EggTrayManagerClass *klass) +{ + GObjectClass *gobject_class; + + parent_class = g_type_class_peek_parent (klass); + gobject_class = (GObjectClass *)klass; + + gobject_class->finalize = egg_tray_manager_finalize; + gobject_class->set_property = egg_tray_manager_set_property; + gobject_class->get_property = egg_tray_manager_get_property; + + g_object_class_install_property (gobject_class, + PROP_ORIENTATION, + g_param_spec_enum ("orientation", + _("Orientation"), + _("The orientation of the tray."), + GTK_TYPE_ORIENTATION, + GTK_ORIENTATION_HORIZONTAL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + manager_signals[TRAY_ICON_ADDED] = + g_signal_new ("tray_icon_added", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggTrayManagerClass, tray_icon_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GTK_TYPE_SOCKET); + + manager_signals[TRAY_ICON_REMOVED] = + g_signal_new ("tray_icon_removed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggTrayManagerClass, tray_icon_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + GTK_TYPE_SOCKET); + manager_signals[MESSAGE_SENT] = + g_signal_new ("message_sent", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggTrayManagerClass, message_sent), + NULL, NULL, + _egg_marshal_VOID__OBJECT_STRING_LONG_LONG, + G_TYPE_NONE, 4, + GTK_TYPE_SOCKET, + G_TYPE_STRING, + G_TYPE_LONG, + G_TYPE_LONG); + manager_signals[MESSAGE_CANCELLED] = + g_signal_new ("message_cancelled", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggTrayManagerClass, message_cancelled), + NULL, NULL, + _egg_marshal_VOID__OBJECT_LONG, + G_TYPE_NONE, 2, + GTK_TYPE_SOCKET, + G_TYPE_LONG); + manager_signals[LOST_SELECTION] = + g_signal_new ("lost_selection", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggTrayManagerClass, lost_selection), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +} + +static void +egg_tray_manager_finalize (GObject *object) +{ + EggTrayManager *manager; + + manager = EGG_TRAY_MANAGER (object); + + egg_tray_manager_unmanage (manager); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +egg_tray_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + EggTrayManager *manager = EGG_TRAY_MANAGER (object); + + switch (prop_id) + { + case PROP_ORIENTATION: + egg_tray_manager_set_orientation (manager, g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +egg_tray_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + EggTrayManager *manager = EGG_TRAY_MANAGER (object); + + switch (prop_id) + { + case PROP_ORIENTATION: + g_value_set_enum (value, manager->orientation); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +EggTrayManager * +egg_tray_manager_new (void) +{ + EggTrayManager *manager; + + manager = g_object_new (EGG_TYPE_TRAY_MANAGER, NULL); + + return manager; +} + +static gboolean +egg_tray_manager_plug_removed (GtkSocket *socket, + EggTrayManager *manager) +{ + Window *window; + + window = g_object_get_data (G_OBJECT (socket), "egg-tray-child-window"); + + g_hash_table_remove (manager->socket_table, GINT_TO_POINTER (*window)); + g_object_set_data (G_OBJECT (socket), "egg-tray-child-window", + NULL); + + g_signal_emit (manager, manager_signals[TRAY_ICON_REMOVED], 0, socket); + + /* This destroys the socket. */ + return FALSE; +} + +static void +egg_tray_manager_handle_dock_request (EggTrayManager *manager, + XClientMessageEvent *xevent) +{ + GtkWidget *socket; + Window *window; + + socket = gtk_socket_new (); + + /* We need to set the child window here + * so that the client can call _get functions + * in the signal handler + */ + window = g_new (Window, 1); + *window = xevent->data.l[2]; + + g_object_set_data_full (G_OBJECT (socket), + "egg-tray-child-window", + window, g_free); + g_signal_emit (manager, manager_signals[TRAY_ICON_ADDED], 0, + socket); + + /* Add the socket only if it's been attached */ + if (GTK_IS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket)))) + { + g_signal_connect (socket, "plug_removed", + G_CALLBACK (egg_tray_manager_plug_removed), manager); + + gtk_socket_add_id (GTK_SOCKET (socket), xevent->data.l[2]); + + g_hash_table_insert (manager->socket_table, GINT_TO_POINTER (xevent->data.l[2]), socket); + } + else + gtk_widget_destroy (socket); +} + +static void +pending_message_free (PendingMessage *message) +{ + g_free (message->str); + g_free (message); +} + +static void +egg_tray_manager_handle_message_data (EggTrayManager *manager, + XClientMessageEvent *xevent) +{ + GList *p; + int len; + + /* Try to see if we can find the + * pending message in the list + */ + for (p = manager->messages; p; p = p->next) + { + PendingMessage *msg = p->data; + + if (xevent->window == msg->window) + { + /* Append the message */ + len = MIN (msg->remaining_len, 20); + + memcpy ((msg->str + msg->len - msg->remaining_len), + &xevent->data, len); + msg->remaining_len -= len; + + if (msg->remaining_len == 0) + { + GtkSocket *socket; + + socket = g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (msg->window)); + + if (socket) + { + g_signal_emit (manager, manager_signals[MESSAGE_SENT], 0, + socket, msg->str, msg->id, msg->timeout); + } + manager->messages = g_list_remove_link (manager->messages, + p); + + pending_message_free (msg); + } + + return; + } + } +} + +static void +egg_tray_manager_handle_begin_message (EggTrayManager *manager, + XClientMessageEvent *xevent) +{ + GList *p; + PendingMessage *msg; + + /* Check if the same message is + * already in the queue and remove it if so + */ + for (p = manager->messages; p; p = p->next) + { + PendingMessage *msg = p->data; + + if (xevent->window == msg->window && + xevent->data.l[4] == msg->id) + { + /* Hmm, we found it, now remove it */ + pending_message_free (msg); + manager->messages = g_list_remove_link (manager->messages, p); + break; + } + } + + /* Now add the new message to the queue */ + msg = g_new0 (PendingMessage, 1); + msg->window = xevent->window; + msg->timeout = xevent->data.l[2]; + msg->len = xevent->data.l[3]; + msg->id = xevent->data.l[4]; + msg->remaining_len = msg->len; + msg->str = g_malloc (msg->len + 1); + msg->str[msg->len] = '\0'; + manager->messages = g_list_prepend (manager->messages, msg); +} + +static void +egg_tray_manager_handle_cancel_message (EggTrayManager *manager, + XClientMessageEvent *xevent) +{ + GtkSocket *socket; + + socket = g_hash_table_lookup (manager->socket_table, GINT_TO_POINTER (xevent->window)); + + if (socket) + { + g_signal_emit (manager, manager_signals[MESSAGE_CANCELLED], 0, + socket, xevent->data.l[2]); + } +} + +static GdkFilterReturn +egg_tray_manager_handle_event (EggTrayManager *manager, + XClientMessageEvent *xevent) +{ + switch (xevent->data.l[1]) + { + case SYSTEM_TRAY_REQUEST_DOCK: + egg_tray_manager_handle_dock_request (manager, xevent); + return GDK_FILTER_REMOVE; + + case SYSTEM_TRAY_BEGIN_MESSAGE: + egg_tray_manager_handle_begin_message (manager, xevent); + return GDK_FILTER_REMOVE; + + case SYSTEM_TRAY_CANCEL_MESSAGE: + egg_tray_manager_handle_cancel_message (manager, xevent); + return GDK_FILTER_REMOVE; + default: + break; + } + + return GDK_FILTER_CONTINUE; +} + +static GdkFilterReturn +egg_tray_manager_window_filter (GdkXEvent *xev, GdkEvent *event, gpointer data) +{ + XEvent *xevent = (GdkXEvent *)xev; + EggTrayManager *manager = data; + + if (xevent->type == ClientMessage) + { + if (xevent->xclient.message_type == manager->opcode_atom) + { + return egg_tray_manager_handle_event (manager, (XClientMessageEvent *)xevent); + } + else if (xevent->xclient.message_type == manager->message_data_atom) + { + egg_tray_manager_handle_message_data (manager, (XClientMessageEvent *)xevent); + return GDK_FILTER_REMOVE; + } + } + else if (xevent->type == SelectionClear) + { + g_signal_emit (manager, manager_signals[LOST_SELECTION], 0); + egg_tray_manager_unmanage (manager); + } + + return GDK_FILTER_CONTINUE; +} + +static void +egg_tray_manager_unmanage (EggTrayManager *manager) +{ + Display *display; + guint32 timestamp; + GtkWidget *invisible; + + if (manager->invisible == NULL) + return; + + invisible = manager->invisible; + g_assert (GTK_IS_INVISIBLE (invisible)); + g_assert (GTK_WIDGET_REALIZED (invisible)); + g_assert (GDK_IS_WINDOW (invisible->window)); + + display = GDK_WINDOW_XDISPLAY (invisible->window); + + if (XGetSelectionOwner (display, manager->selection_atom) == + GDK_WINDOW_XWINDOW (invisible->window)) + { + timestamp = gdk_x11_get_server_time (invisible->window); + XSetSelectionOwner (display, manager->selection_atom, None, timestamp); + } + + gdk_window_remove_filter (invisible->window, egg_tray_manager_window_filter, manager); + + manager->invisible = NULL; /* prior to destroy for reentrancy paranoia */ + gtk_widget_destroy (invisible); + g_object_unref (G_OBJECT (invisible)); +} + +static void +egg_tray_manager_set_orientation_property (EggTrayManager *manager) +{ + gulong data[1]; + + if (!manager->invisible || !manager->invisible->window) + return; + + g_assert (manager->orientation_atom != None); + + data[0] = manager->orientation == GTK_ORIENTATION_HORIZONTAL ? + SYSTEM_TRAY_ORIENTATION_HORZ : + SYSTEM_TRAY_ORIENTATION_VERT; + + XChangeProperty (GDK_WINDOW_XDISPLAY (manager->invisible->window), + GDK_WINDOW_XWINDOW (manager->invisible->window), + manager->orientation_atom, + XA_CARDINAL, 32, + PropModeReplace, + (guchar *) &data, 1); +} + +static gboolean +egg_tray_manager_manage_xscreen (EggTrayManager *manager, Screen *xscreen) +{ + GtkWidget *invisible; + char *selection_atom_name; + guint32 timestamp; + GdkScreen *screen; + + g_return_val_if_fail (EGG_IS_TRAY_MANAGER (manager), FALSE); + g_return_val_if_fail (manager->screen == NULL, FALSE); + + /* If there's already a manager running on the screen + * we can't create another one. + */ +#if 0 + if (egg_tray_manager_check_running_xscreen (xscreen)) + return FALSE; +#endif + screen = gdk_display_get_screen (gdk_x11_lookup_xdisplay (DisplayOfScreen (xscreen)), + XScreenNumberOfScreen (xscreen)); + + invisible = gtk_invisible_new_for_screen (screen); + gtk_widget_realize (invisible); + + gtk_widget_add_events (invisible, GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK); + + selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", + XScreenNumberOfScreen (xscreen)); + manager->selection_atom = XInternAtom (DisplayOfScreen (xscreen), selection_atom_name, False); + + g_free (selection_atom_name); + + manager->orientation_atom = XInternAtom (DisplayOfScreen (xscreen), + "_NET_SYSTEM_TRAY_ORIENTATION", + FALSE); + egg_tray_manager_set_orientation_property (manager); + + timestamp = gdk_x11_get_server_time (invisible->window); + XSetSelectionOwner (DisplayOfScreen (xscreen), manager->selection_atom, + GDK_WINDOW_XWINDOW (invisible->window), timestamp); + + /* Check if we were could set the selection owner successfully */ + if (XGetSelectionOwner (DisplayOfScreen (xscreen), manager->selection_atom) == + GDK_WINDOW_XWINDOW (invisible->window)) + { + XClientMessageEvent xev; + + xev.type = ClientMessage; + xev.window = RootWindowOfScreen (xscreen); + xev.message_type = XInternAtom (DisplayOfScreen (xscreen), "MANAGER", False); + + xev.format = 32; + xev.data.l[0] = timestamp; + xev.data.l[1] = manager->selection_atom; + xev.data.l[2] = GDK_WINDOW_XWINDOW (invisible->window); + xev.data.l[3] = 0; /* manager specific data */ + xev.data.l[4] = 0; /* manager specific data */ + + XSendEvent (DisplayOfScreen (xscreen), + RootWindowOfScreen (xscreen), + False, StructureNotifyMask, (XEvent *)&xev); + + manager->invisible = invisible; + g_object_ref (G_OBJECT (manager->invisible)); + + manager->opcode_atom = XInternAtom (DisplayOfScreen (xscreen), + "_NET_SYSTEM_TRAY_OPCODE", + False); + + manager->message_data_atom = XInternAtom (DisplayOfScreen (xscreen), + "_NET_SYSTEM_TRAY_MESSAGE_DATA", + False); + + /* Add a window filter */ + gdk_window_add_filter (invisible->window, egg_tray_manager_window_filter, manager); + return TRUE; + } + else + { + gtk_widget_destroy (invisible); + + return FALSE; + } +} + +gboolean +egg_tray_manager_manage_screen (EggTrayManager *manager, + GdkScreen *screen) +{ + g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); + g_return_val_if_fail (manager->screen == NULL, FALSE); + + return egg_tray_manager_manage_xscreen (manager, + GDK_SCREEN_XSCREEN (screen)); +} + +static gboolean +egg_tray_manager_check_running_xscreen (Screen *xscreen) +{ + Atom selection_atom; + char *selection_atom_name; + + selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", + XScreenNumberOfScreen (xscreen)); + selection_atom = XInternAtom (DisplayOfScreen (xscreen), selection_atom_name, False); + g_free (selection_atom_name); + + if (XGetSelectionOwner (DisplayOfScreen (xscreen), selection_atom)) + return TRUE; + else + return FALSE; +} + +gboolean +egg_tray_manager_check_running (GdkScreen *screen) +{ + g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); + + return egg_tray_manager_check_running_xscreen (GDK_SCREEN_XSCREEN (screen)); +} + +char * +egg_tray_manager_get_child_title (EggTrayManager *manager, + EggTrayManagerChild *child) +{ + Window *child_window; + Atom utf8_string, atom, type; + int result; + char *retval; + int format; + gulong nitems; + gulong bytes_after; + guchar *val; + + g_return_val_if_fail (EGG_IS_TRAY_MANAGER (manager), NULL); + g_return_val_if_fail (GTK_IS_SOCKET (child), NULL); + + child_window = g_object_get_data (G_OBJECT (child), + "egg-tray-child-window"); + + utf8_string = XInternAtom (GDK_DISPLAY (), "UTF8_STRING", False); + atom = XInternAtom (GDK_DISPLAY (), "_NET_WM_NAME", False); + + gdk_error_trap_push (); + + result = XGetWindowProperty (GDK_DISPLAY (), + *child_window, + atom, + 0, G_MAXLONG, + False, utf8_string, + &type, &format, &nitems, + &bytes_after, (guchar **)&val); + + if (gdk_error_trap_pop () || result != Success) + return NULL; + + if (type != utf8_string || + format != 8 || + nitems == 0) + { + if (val) + XFree (val); + return NULL; + } + + if (!g_utf8_validate (val, nitems, NULL)) + { + XFree (val); + return NULL; + } + + retval = g_strndup (val, nitems); + + XFree (val); + + return retval; + +} + +void +egg_tray_manager_set_orientation (EggTrayManager *manager, + GtkOrientation orientation) +{ + g_return_if_fail (EGG_IS_TRAY_MANAGER (manager)); + + if (manager->orientation != orientation) + { + manager->orientation = orientation; + + egg_tray_manager_set_orientation_property (manager); + + g_object_notify (G_OBJECT (manager), "orientation"); + } +} + +GtkOrientation +egg_tray_manager_get_orientation (EggTrayManager *manager) +{ + g_return_val_if_fail (EGG_IS_TRAY_MANAGER (manager), GTK_ORIENTATION_HORIZONTAL); + + return manager->orientation; +} diff --git a/lib/egg/eggtraymanager.h b/lib/egg/eggtraymanager.h new file mode 100644 index 000000000..672e9753c --- /dev/null +++ b/lib/egg/eggtraymanager.h @@ -0,0 +1,93 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* eggtraymanager.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 __EGG_TRAY_MANAGER_H__ +#define __EGG_TRAY_MANAGER_H__ + +#include <gtk/gtkwidget.h> +#include <gdk/gdkx.h> + +G_BEGIN_DECLS + +#define EGG_TYPE_TRAY_MANAGER (egg_tray_manager_get_type ()) +#define EGG_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TRAY_MANAGER, EggTrayManager)) +#define EGG_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TRAY_MANAGER, EggTrayManagerClass)) +#define EGG_IS_TRAY_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TRAY_MANAGER)) +#define EGG_IS_TRAY_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TRAY_MANAGER)) +#define EGG_TRAY_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TRAY_MANAGER, EggTrayManagerClass)) + +typedef struct _EggTrayManager EggTrayManager; +typedef struct _EggTrayManagerClass EggTrayManagerClass; +typedef struct _EggTrayManagerChild EggTrayManagerChild; + +struct _EggTrayManager +{ + GObject parent_instance; + + Atom opcode_atom; + Atom selection_atom; + Atom message_data_atom; + Atom orientation_atom; + + GtkWidget *invisible; + GdkScreen *screen; + GtkOrientation orientation; + + GList *messages; + GHashTable *socket_table; +}; + +struct _EggTrayManagerClass +{ + GObjectClass parent_class; + + void (* tray_icon_added) (EggTrayManager *manager, + EggTrayManagerChild *child); + void (* tray_icon_removed) (EggTrayManager *manager, + EggTrayManagerChild *child); + + void (* message_sent) (EggTrayManager *manager, + EggTrayManagerChild *child, + const gchar *message, + glong id, + glong timeout); + + void (* message_cancelled) (EggTrayManager *manager, + EggTrayManagerChild *child, + glong id); + + void (* lost_selection) (EggTrayManager *manager); +}; + +GType egg_tray_manager_get_type (void); + +gboolean egg_tray_manager_check_running (GdkScreen *screen); +EggTrayManager *egg_tray_manager_new (void); +gboolean egg_tray_manager_manage_screen (EggTrayManager *manager, + GdkScreen *screen); +char *egg_tray_manager_get_child_title (EggTrayManager *manager, + EggTrayManagerChild *child); +void egg_tray_manager_set_orientation (EggTrayManager *manager, + GtkOrientation orientation); +GtkOrientation egg_tray_manager_get_orientation (EggTrayManager *manager); + +G_END_DECLS + +#endif /* __EGG_TRAY_MANAGER_H__ */ diff --git a/lib/egg/update-from-egg.sh b/lib/egg/update-from-egg.sh index 40de7352d..50690440c 100755 --- a/lib/egg/update-from-egg.sh +++ b/lib/egg/update-from-egg.sh @@ -18,18 +18,15 @@ fi for FILE in $EGGFILES; do SRCFILE=$EGGDIR/$FILE if ! test -e $SRCFILE ; then - if test -e $EGGDIR/toolbar/$FILE ; then - SRCFILE=$EGGDIR/toolbar/$FILE + if test -e $EGGDIR/tray/$FILE ; then + SRCFILE=$EGGDIR/tray/$FILE fi - if test -e $EGGDIR/menu/$FILE ; then - SRCFILE=$EGGDIR/menu/$FILE + if test -e $EGGDIR/util/$FILE ; then + SRCFILE=$EGGDIR/util/$FILE fi if test -e $EGGDIR/toolbareditor/$FILE ; then SRCFILE=$EGGDIR/toolbareditor/$FILE fi - if test -e $EGGDIR/util/$FILE ; then - SRCFILE=$EGGDIR/util/$FILE - fi if test -e $EGGDIR/treeviewutils/$FILE ; then SRCFILE=$EGGDIR/treeviewutils/$FILE fi |