aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/eggtrayicon.c
diff options
context:
space:
mode:
Diffstat (limited to 'e-util/eggtrayicon.c')
-rw-r--r--e-util/eggtrayicon.c1841
1 files changed, 1841 insertions, 0 deletions
diff --git a/e-util/eggtrayicon.c b/e-util/eggtrayicon.c
index 6c309306e9..6c4a20e96a 100644
--- a/e-util/eggtrayicon.c
+++ b/e-util/eggtrayicon.c
@@ -24,6 +24,8 @@
#include "eggtrayicon.h"
+#ifdef GDK_WINDOWING_X11
+
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
@@ -478,3 +480,1842 @@ egg_tray_icon_get_orientation (EggTrayIcon *icon)
return icon->orientation;
}
+
+#endif /* GDK_WINDOWING_X11 */
+
+#ifdef GDK_WINDOWING_WIN32
+
+#if !GTK_CHECK_VERSION (2, 9, 0)
+
+/* gtkstatusicon implementation lifted from HEAD GTK+ (2.9). Plus
+ * helper functions from GDK. Can be remove when Evo uses GTK+ 2.10.
+ */
+
+/* gtkstatusicon.c:
+ *
+ * Copyright (C) 2003 Sun Microsystems, Inc.
+ * Copyright (C) 2005 Hans Breuer <hans@breuer.org>
+ * Copyright (C) 2005 Novell, 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>
+ * Hans Breuer <hans@breuer.org>
+ * Tor Lillqvist <tml@novell.com>
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#define P_(x) x
+#define I_(x) x
+
+#define GTK_PARAM_READABLE G_PARAM_READABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+#define GTK_PARAM_WRITABLE G_PARAM_WRITABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+#define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB
+
+#define g_marshal_value_peek_uint(v) g_value_get_uint (v)
+
+static void
+_gtk_marshal_VOID__UINT_UINT (GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ typedef void (*GMarshalFunc_VOID__UINT_UINT) (gpointer data1,
+ guint arg_1,
+ guint arg_2,
+ gpointer data2);
+ register GMarshalFunc_VOID__UINT_UINT callback;
+ register GCClosure *cc = (GCClosure*) closure;
+ register gpointer data1, data2;
+
+ g_return_if_fail (n_param_values == 3);
+
+ if (G_CCLOSURE_SWAP_DATA (closure))
+ {
+ data1 = closure->data;
+ data2 = g_value_peek_pointer (param_values + 0);
+ }
+ else
+ {
+ data1 = g_value_peek_pointer (param_values + 0);
+ data2 = closure->data;
+ }
+ callback = (GMarshalFunc_VOID__UINT_UINT) (marshal_data ? marshal_data : cc->callback);
+
+ callback (data1,
+ g_marshal_value_peek_uint (param_values + 1),
+ g_marshal_value_peek_uint (param_values + 2),
+ data2);
+}
+
+#ifdef GDK_WINDOWING_WIN32
+
+#include <gdk/gdkwin32.h>
+#define WM_GTK_TRAY_NOTIFICATION (WM_USER+1)
+
+#define WIN32_GDI_FAILED(api) g_warning ("%s:%d: %s failed", __FILE__, __LINE__, api)
+
+#if defined(__MINGW32__) || (defined(_MSC_VER) && (WINVER < 0x0500))
+typedef struct {
+ DWORD bV5Size;
+ LONG bV5Width;
+ LONG bV5Height;
+ WORD bV5Planes;
+ WORD bV5BitCount;
+ DWORD bV5Compression;
+ DWORD bV5SizeImage;
+ LONG bV5XPelsPerMeter;
+ LONG bV5YPelsPerMeter;
+ DWORD bV5ClrUsed;
+ DWORD bV5ClrImportant;
+ DWORD bV5RedMask;
+ DWORD bV5GreenMask;
+ DWORD bV5BlueMask;
+ DWORD bV5AlphaMask;
+ DWORD bV5CSType;
+ CIEXYZTRIPLE bV5Endpoints;
+ DWORD bV5GammaRed;
+ DWORD bV5GammaGreen;
+ DWORD bV5GammaBlue;
+ DWORD bV5Intent;
+ DWORD bV5ProfileData;
+ DWORD bV5ProfileSize;
+ DWORD bV5Reserved;
+} BITMAPV5HEADER;
+#endif
+
+static HBITMAP
+create_alpha_bitmap (gint width, gint height, guchar **outdata)
+{
+ BITMAPV5HEADER bi;
+ HDC hdc;
+ HBITMAP hBitmap;
+
+ ZeroMemory (&bi, sizeof (BITMAPV5HEADER));
+ bi.bV5Size = sizeof (BITMAPV5HEADER);
+ bi.bV5Width = width;
+ bi.bV5Height = height;
+ bi.bV5Planes = 1;
+ bi.bV5BitCount = 32;
+ bi.bV5Compression = BI_BITFIELDS;
+ /* The following mask specification specifies a supported 32 BPP
+ * alpha format for Windows XP (BGRA format).
+ */
+ bi.bV5RedMask = 0x00FF0000;
+ bi.bV5GreenMask = 0x0000FF00;
+ bi.bV5BlueMask = 0x000000FF;
+ bi.bV5AlphaMask = 0xFF000000;
+
+ /* Create the DIB section with an alpha channel. */
+ hdc = GetDC (NULL);
+ if (!hdc)
+ {
+ WIN32_GDI_FAILED ("GetDC");
+ return NULL;
+ }
+ hBitmap = CreateDIBSection (hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS,
+ (PVOID *) outdata, NULL, (DWORD)0);
+ if (hBitmap == NULL)
+ WIN32_GDI_FAILED ("CreateDIBSection");
+ ReleaseDC (NULL, hdc);
+
+ return hBitmap;
+}
+
+static HBITMAP
+create_color_bitmap (gint width,
+ gint height,
+ guchar **outdata,
+ gint bits)
+{
+ struct {
+ BITMAPV4HEADER bmiHeader;
+ RGBQUAD bmiColors[2];
+ } bmi;
+ HDC hdc;
+ HBITMAP hBitmap;
+
+ ZeroMemory (&bmi, sizeof (bmi));
+ bmi.bmiHeader.bV4Size = sizeof (BITMAPV4HEADER);
+ bmi.bmiHeader.bV4Width = width;
+ bmi.bmiHeader.bV4Height = height;
+ bmi.bmiHeader.bV4Planes = 1;
+ bmi.bmiHeader.bV4BitCount = bits;
+ bmi.bmiHeader.bV4V4Compression = BI_RGB;
+
+ /* when bits is 1, these will be used.
+ * bmiColors[0] already zeroed from ZeroMemory()
+ */
+ bmi.bmiColors[1].rgbBlue = 0xFF;
+ bmi.bmiColors[1].rgbGreen = 0xFF;
+ bmi.bmiColors[1].rgbRed = 0xFF;
+
+ hdc = GetDC (NULL);
+ if (!hdc)
+ {
+ WIN32_GDI_FAILED ("GetDC");
+ return NULL;
+ }
+ hBitmap = CreateDIBSection (hdc, (BITMAPINFO *)&bmi, DIB_RGB_COLORS,
+ (PVOID *) outdata, NULL, (DWORD)0);
+ if (hBitmap == NULL)
+ WIN32_GDI_FAILED ("CreateDIBSection");
+ ReleaseDC (NULL, hdc);
+
+ return hBitmap;
+}
+
+static gboolean
+pixbuf_to_hbitmaps_alpha_winxp (GdkPixbuf *pixbuf,
+ HBITMAP *color,
+ HBITMAP *mask)
+{
+ /* Based on code from
+ * http://www.dotnet247.com/247reference/msgs/13/66301.aspx
+ */
+ HBITMAP hColorBitmap, hMaskBitmap;
+ guchar *indata, *inrow;
+ guchar *colordata, *colorrow, *maskdata, *maskbyte;
+ gint width, height, i, j, rowstride;
+ guint maskstride, mask_bit;
+
+ width = gdk_pixbuf_get_width (pixbuf); /* width of icon */
+ height = gdk_pixbuf_get_height (pixbuf); /* height of icon */
+
+ hColorBitmap = create_alpha_bitmap (width, height, &colordata);
+ if (!hColorBitmap)
+ return FALSE;
+ hMaskBitmap = create_color_bitmap (width, height, &maskdata, 1);
+ if (!hMaskBitmap)
+ {
+ DeleteObject (hColorBitmap);
+ return FALSE;
+ }
+
+ /* MSDN says mask rows are aligned to "LONG" boundaries */
+ maskstride = (((width + 31) & ~31) >> 3);
+
+ indata = gdk_pixbuf_get_pixels (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ for (j=0; j<height; j++)
+ {
+ colorrow = colordata + 4*j*width;
+ maskbyte = maskdata + j*maskstride;
+ mask_bit = 0x80;
+ inrow = indata + (height-j-1)*rowstride;
+ for (i=0; i<width; i++)
+ {
+ colorrow[4*i+0] = inrow[4*i+2];
+ colorrow[4*i+1] = inrow[4*i+1];
+ colorrow[4*i+2] = inrow[4*i+0];
+ colorrow[4*i+3] = inrow[4*i+3];
+ if (inrow[4*i+3] == 0)
+ maskbyte[0] |= mask_bit; /* turn ON bit */
+ else
+ maskbyte[0] &= ~mask_bit; /* turn OFF bit */
+ mask_bit >>= 1;
+ if (mask_bit == 0)
+ {
+ mask_bit = 0x80;
+ maskbyte++;
+ }
+ }
+ }
+
+ *color = hColorBitmap;
+ *mask = hMaskBitmap;
+
+ return TRUE;
+}
+
+static gboolean
+pixbuf_to_hbitmaps_normal (GdkPixbuf *pixbuf,
+ HBITMAP *color,
+ HBITMAP *mask)
+{
+ /* Based on code from
+ * http://www.dotnet247.com/247reference/msgs/13/66301.aspx
+ */
+ HBITMAP hColorBitmap, hMaskBitmap;
+ guchar *indata, *inrow;
+ guchar *colordata, *colorrow, *maskdata, *maskbyte;
+ gint width, height, i, j, rowstride, nc, bmstride;
+ gboolean has_alpha;
+ guint maskstride, mask_bit;
+
+ width = gdk_pixbuf_get_width (pixbuf); /* width of icon */
+ height = gdk_pixbuf_get_height (pixbuf); /* height of icon */
+
+ hColorBitmap = create_color_bitmap (width, height, &colordata, 24);
+ if (!hColorBitmap)
+ return FALSE;
+ hMaskBitmap = create_color_bitmap (width, height, &maskdata, 1);
+ if (!hMaskBitmap)
+ {
+ DeleteObject (hColorBitmap);
+ return FALSE;
+ }
+
+ /* rows are always aligned on 4-byte boundarys */
+ bmstride = width * 3;
+ if (bmstride % 4 != 0)
+ bmstride += 4 - (bmstride % 4);
+
+ /* MSDN says mask rows are aligned to "LONG" boundaries */
+ maskstride = (((width + 31) & ~31) >> 3);
+
+ indata = gdk_pixbuf_get_pixels (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ nc = gdk_pixbuf_get_n_channels (pixbuf);
+ has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+
+ for (j=0; j<height; j++)
+ {
+ colorrow = colordata + j*bmstride;
+ maskbyte = maskdata + j*maskstride;
+ mask_bit = 0x80;
+ inrow = indata + (height-j-1)*rowstride;
+ for (i=0; i<width; i++)
+ {
+ if (has_alpha && inrow[nc*i+3] < 128)
+ {
+ colorrow[3*i+0] = colorrow[3*i+1] = colorrow[3*i+2] = 0;
+ maskbyte[0] |= mask_bit; /* turn ON bit */
+ }
+ else
+ {
+ colorrow[3*i+0] = inrow[nc*i+2];
+ colorrow[3*i+1] = inrow[nc*i+1];
+ colorrow[3*i+2] = inrow[nc*i+0];
+ maskbyte[0] &= ~mask_bit; /* turn OFF bit */
+ }
+ mask_bit >>= 1;
+ if (mask_bit == 0)
+ {
+ mask_bit = 0x80;
+ maskbyte++;
+ }
+ }
+ }
+
+ *color = hColorBitmap;
+ *mask = hMaskBitmap;
+
+ return TRUE;
+}
+
+gboolean
+_gdk_win32_pixbuf_to_hicon_supports_alpha (void)
+{
+ static gboolean is_win_xp=FALSE, is_win_xp_checked=FALSE;
+
+ if (!is_win_xp_checked)
+ {
+ is_win_xp_checked = TRUE;
+
+ if (!G_WIN32_IS_NT_BASED ())
+ is_win_xp = FALSE;
+ else
+ {
+ OSVERSIONINFO version;
+
+ memset (&version, 0, sizeof (version));
+ version.dwOSVersionInfoSize = sizeof (version);
+ is_win_xp = GetVersionEx (&version)
+ && version.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && (version.dwMajorVersion > 5
+ || (version.dwMajorVersion == 5 && version.dwMinorVersion >= 1));
+ }
+ }
+ return is_win_xp;
+}
+
+static HICON
+pixbuf_to_hicon (GdkPixbuf *pixbuf,
+ gboolean is_icon,
+ gint x,
+ gint y)
+{
+ ICONINFO ii;
+ HICON icon;
+ gboolean success;
+
+ if (pixbuf == NULL)
+ return NULL;
+
+ if (_gdk_win32_pixbuf_to_hicon_supports_alpha() && gdk_pixbuf_get_has_alpha (pixbuf))
+ success = pixbuf_to_hbitmaps_alpha_winxp (pixbuf, &ii.hbmColor, &ii.hbmMask);
+ else
+ success = pixbuf_to_hbitmaps_normal (pixbuf, &ii.hbmColor, &ii.hbmMask);
+
+ if (!success)
+ return NULL;
+
+ ii.fIcon = is_icon;
+ ii.xHotspot = x;
+ ii.yHotspot = y;
+ icon = CreateIconIndirect (&ii);
+ DeleteObject (ii.hbmColor);
+ DeleteObject (ii.hbmMask);
+ return icon;
+}
+
+
+static HICON
+_gdk_win32_pixbuf_to_hicon (GdkPixbuf *pixbuf)
+{
+ return pixbuf_to_hicon (pixbuf, TRUE, 0, 0);
+}
+
+static HICON
+gdk_win32_pixbuf_to_hicon_libgtk_only (GdkPixbuf *pixbuf)
+{
+ return _gdk_win32_pixbuf_to_hicon (pixbuf);
+}
+
+#endif
+
+#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
+{
+#ifdef GDK_WINDOWING_X11
+ GtkWidget *tray_icon;
+ GtkWidget *image;
+ GtkTooltips *tooltips;
+#endif
+
+#ifdef GDK_WINDOWING_WIN32
+ GtkWidget *dummy_widget;
+ NOTIFYICONDATAW nid;
+#endif
+
+ gint size;
+
+ gint image_width;
+ gint image_height;
+
+ 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);
+
+#ifdef GDK_WINDOWING_X11
+static void gtk_status_icon_size_allocate (GtkStatusIcon *status_icon,
+ GtkAllocation *allocation);
+#endif
+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));
+
+
+ /**
+ * GtkStatusIcon::activate:
+ * @status_icon: the object which received the signal
+ *
+ * Gets emitted when the user activates the status icon.
+ * If and how status icons can activated is platform-dependent.
+ *
+ * Since: 2.10
+ */
+ status_icon_signals [ACTIVATE_SIGNAL] =
+ g_signal_new (I_("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);
+
+ /**
+ * GtkStatusIcon::popup-menu:
+ * @status_icon: the object which received the signal
+ * @button: the button that was pressed, or 0 if the
+ * signal is not emitted in response to a button press event
+ * @activate_time: the timestamp of the event that
+ * triggered the signal emission
+ *
+ * Gets emitted when the user brings up the context menu
+ * of the status icon. Whether status icons can have context
+ * menus and how these are activated is platform-dependent.
+ *
+ * The @button and @activate_timeout parameters should be
+ * passed as the last to arguments to gtk_menu_popup().
+ *
+ * Since: 2.10
+ */
+ status_icon_signals [POPUP_MENU_SIGNAL] =
+ g_signal_new (I_("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);
+
+ /**
+ * GtkStatusIcon::size-changed:
+ * @status_icon: the object which received the signal
+ * @size: the new size
+ *
+ * Gets emitted when the size available for the image
+ * changes, e.g. because the notification area got resized.
+ *
+ * Since: 2.10
+ */
+ status_icon_signals [SIZE_CHANGED_SIGNAL] =
+ g_signal_new (I_("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));
+}
+
+#ifdef GDK_WINDOWING_WIN32
+
+static void
+build_button_event (GdkEventButton *e,
+ GdkEventType type,
+ guint button)
+{
+ POINT pos;
+ GdkRectangle monitor0;
+
+ /* We know that gdk/win32 puts the primary monitor at index 0 */
+ gdk_screen_get_monitor_geometry (gdk_screen_get_default (), 0, &monitor0);
+ e->type = type;
+ e->window = gdk_get_default_root_window ();
+ e->send_event = TRUE;
+ e->time = GetTickCount ();
+ GetCursorPos (&pos);
+ e->x = pos.x + monitor0.x;
+ e->y = pos.y + monitor0.y;
+ e->axes = NULL;
+ e->state = 0;
+ e->button = button;
+ e->device = gdk_display_get_default ()->core_pointer;
+ e->x_root = e->x;
+ e->y_root = e->y;
+}
+
+static LRESULT CALLBACK
+wndproc (HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam)
+{
+ if (message == WM_GTK_TRAY_NOTIFICATION)
+ {
+ GdkEventButton e;
+ GtkStatusIcon *status_icon = GTK_STATUS_ICON (wparam);
+
+ switch (lparam)
+ {
+ case WM_LBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ build_button_event (&e, GDK_BUTTON_PRESS,
+ (lparam == WM_LBUTTONDOWN) ? 1 : 3);
+ gtk_status_icon_button_press (status_icon, &e);
+ break;
+ default :
+ break;
+ }
+ return 0;
+ }
+ else
+ {
+ return DefWindowProc (hwnd, message, wparam, lparam);
+ }
+}
+
+static HWND
+create_tray_observer (void)
+{
+ WNDCLASS wclass;
+ static HWND hwnd = NULL;
+ ATOM klass;
+ HINSTANCE hmodule = GetModuleHandle (NULL);
+
+ if (hwnd)
+ return hwnd;
+
+ memset (&wclass, 0, sizeof(WNDCLASS));
+ wclass.lpszClassName = "gtkstatusicon-observer";
+ wclass.lpfnWndProc = wndproc;
+ wclass.hInstance = hmodule;
+
+ klass = RegisterClass (&wclass);
+ if (!klass)
+ return NULL;
+
+ hwnd = CreateWindow (MAKEINTRESOURCE(klass),
+ NULL, WS_POPUP,
+ 0, 0, 1, 1, NULL, NULL,
+ hmodule, NULL);
+ if (!hwnd)
+ {
+ UnregisterClass (MAKEINTRESOURCE(klass), hmodule);
+ return NULL;
+ }
+
+ return hwnd;
+}
+
+#endif
+
+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->visible = TRUE;
+
+#ifdef GDK_WINDOWING_X11
+ status_icon->priv->size = 0;
+ status_icon->priv->image_width = 0;
+ status_icon->priv->image_height = 0;
+
+ 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));
+#endif
+
+#ifdef GDK_WINDOWING_WIN32
+
+ /* Code to get position and orientation of Windows taskbar. Not needed
+ * currently, kept for reference.
+ */
+#if 0
+ {
+ APPBARDATA abd;
+
+ abd.cbSize = sizeof (abd);
+ SHAppBarMessage (ABM_GETTASKBARPOS, &abd);
+ if (abd.rc.bottom - abd.rc.top > abd.rc.right - abd.rc.left)
+ orientation = GTK_ORIENTATION_VERTICAL;
+ else
+ orientation = GTK_ORIENTATION_HORIZONTAL;
+ }
+#endif
+
+ /* Are the system tray icons always 16 pixels square? */
+ status_icon->priv->size = 16;
+ status_icon->priv->image_width = 16;
+ status_icon->priv->image_height = 16;
+
+ status_icon->priv->dummy_widget = gtk_label_new ("");
+
+ memset (&status_icon->priv->nid, 0, sizeof (status_icon->priv->nid));
+
+ status_icon->priv->nid.hWnd = create_tray_observer ();
+ status_icon->priv->nid.uID = GPOINTER_TO_UINT (status_icon);
+ status_icon->priv->nid.uCallbackMessage = WM_GTK_TRAY_NOTIFICATION;
+ status_icon->priv->nid.uFlags = NIF_MESSAGE;
+
+ if (!Shell_NotifyIconW (NIM_ADD, &status_icon->priv->nid))
+ {
+ g_warning ("%s:%d:Shell_NotifyIcon(NIM_ADD) failed", __FILE__, __LINE__-2);
+ status_icon->priv->nid.hWnd = NULL;
+ }
+#endif
+}
+
+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;
+
+#ifdef GDK_WINDOWING_X11
+ 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);
+#endif
+
+#ifdef GDK_WINDOWING_WIN32
+ if (status_icon->priv->nid.hWnd != NULL && status_icon->priv->visible)
+ Shell_NotifyIconW (NIM_DELETE, &status_icon->priv->nid);
+
+ gtk_widget_destroy (status_icon->priv->dummy_widget);
+#endif
+
+ 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;
+ }
+}
+
+/**
+ * gtk_status_icon_new:
+ *
+ * Creates an empty status icon object.
+ *
+ * Return value: a new #GtkStatusIcon
+ *
+ * Since: 2.10
+ **/
+GtkStatusIcon *
+gtk_status_icon_new (void)
+{
+ return g_object_new (GTK_TYPE_STATUS_ICON, NULL);
+}
+
+/**
+ * gtk_status_icon_new_from_pixbuf:
+ * @pixbuf: a #GdkPixbuf
+ *
+ * Creates a status icon displaying @pixbuf.
+ *
+ * The image will be scaled down to fit in the available
+ * space in the notification area, if necessary.
+ *
+ * Return value: a new #GtkStatusIcon
+ *
+ * Since: 2.10
+ **/
+GtkStatusIcon *
+gtk_status_icon_new_from_pixbuf (GdkPixbuf *pixbuf)
+{
+ return g_object_new (GTK_TYPE_STATUS_ICON,
+ "pixbuf", pixbuf,
+ NULL);
+}
+
+/**
+ * gtk_status_icon_new_from_file:
+ * @filename: a filename
+ *
+ * Creates a status icon displaying the file @filename.
+ *
+ * The image will be scaled down to fit in the available
+ * space in the notification area, if necessary.
+ *
+ * Return value: a new #GtkStatusIcon
+ *
+ * Since: 2.10
+ **/
+GtkStatusIcon *
+gtk_status_icon_new_from_file (const gchar *filename)
+{
+ return g_object_new (GTK_TYPE_STATUS_ICON,
+ "file", filename,
+ NULL);
+}
+
+/**
+ * gtk_status_icon_new_from_stock:
+ * @stock_id: a stock icon id
+ *
+ * Creates a status icon displaying a stock icon. Sample stock icon
+ * names are #GTK_STOCK_OPEN, #GTK_STOCK_QUIT. You can register your
+ * own stock icon names, see gtk_icon_factory_add_default() and
+ * gtk_icon_factory_add().
+ *
+ * Return value: a new #GtkStatusIcon
+ *
+ * Since: 2.10
+ **/
+GtkStatusIcon *
+gtk_status_icon_new_from_stock (const gchar *stock_id)
+{
+ return g_object_new (GTK_TYPE_STATUS_ICON,
+ "stock", stock_id,
+ NULL);
+}
+
+/**
+ * gtk_status_icon_new_from_icon_name:
+ * @icon_name: an icon name
+ *
+ * Creates a status icon displaying an icon from the current icon theme.
+ * If the current icon theme is changed, the icon will be updated
+ * appropriately.
+ *
+ * Return value: a new #GtkStatusIcon
+ *
+ * Since: 2.10
+ **/
+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);
+}
+
+#ifdef GDK_WINDOWING_X11
+
+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;
+}
+
+#endif
+
+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;
+}
+
+#ifdef GDK_WINDOWING_X11
+
+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;
+}
+
+#endif
+
+static void
+gtk_status_icon_update_image (GtkStatusIcon *status_icon)
+{
+ if (status_icon->priv->blink_off)
+ {
+#ifdef GDK_WINDOWING_X11
+ gtk_image_set_from_pixbuf (GTK_IMAGE (status_icon->priv->image),
+ gtk_status_icon_blank_icon (status_icon));
+#endif
+#ifdef GDK_WINDOWING_WIN32
+ status_icon->priv->nid.hIcon = gdk_win32_pixbuf_to_hicon_libgtk_only (gtk_status_icon_blank_icon (status_icon));
+ status_icon->priv->nid.uFlags |= NIF_ICON;
+ if (status_icon->priv->nid.hWnd != NULL && status_icon->priv->visible)
+ if (!Shell_NotifyIconW (NIM_MODIFY, &status_icon->priv->nid))
+ g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1);
+#endif
+ 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);
+ }
+
+#ifdef GDK_WINDOWING_X11
+ gtk_image_set_from_pixbuf (GTK_IMAGE (status_icon->priv->image), scaled);
+#endif
+#ifdef GDK_WINDOWING_WIN32
+ status_icon->priv->nid.hIcon = gdk_win32_pixbuf_to_hicon_libgtk_only (scaled);
+ status_icon->priv->nid.uFlags |= NIF_ICON;
+ if (status_icon->priv->nid.hWnd != NULL && status_icon->priv->visible)
+ if (!Shell_NotifyIconW (NIM_MODIFY, &status_icon->priv->nid))
+ g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1);
+#endif
+ g_object_unref (scaled);
+ }
+ else
+ {
+#ifdef GDK_WINDOWING_X11
+ gtk_image_set_from_pixbuf (GTK_IMAGE (status_icon->priv->image), NULL);
+#endif
+#ifdef GDK_WINDOWING_WIN32
+ status_icon->priv->nid.uFlags &= ~NIF_ICON;
+ if (status_icon->priv->nid.hWnd != NULL && status_icon->priv->visible)
+ if (!Shell_NotifyIconW (NIM_MODIFY, &status_icon->priv->nid))
+ g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1);
+#endif
+ }
+ }
+ break;
+
+ case GTK_IMAGE_STOCK:
+ {
+#ifdef GDK_WINDOWING_X11
+ 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);
+#endif
+#ifdef GDK_WINDOWING_WIN32
+ {
+ GdkPixbuf *pixbuf =
+ gtk_widget_render_icon (status_icon->priv->dummy_widget,
+ status_icon->priv->image_data.stock_id,
+ GTK_ICON_SIZE_SMALL_TOOLBAR,
+ NULL);
+ status_icon->priv->nid.hIcon = gdk_win32_pixbuf_to_hicon_libgtk_only (pixbuf);
+ status_icon->priv->nid.uFlags |= NIF_ICON;
+ if (status_icon->priv->nid.hWnd != NULL && status_icon->priv->visible)
+ if (!Shell_NotifyIconW (NIM_MODIFY, &status_icon->priv->nid))
+ g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1);
+ g_object_unref (pixbuf);
+ }
+#endif
+ }
+ break;
+
+ case GTK_IMAGE_ICON_NAME:
+ {
+#ifdef GDK_WINDOWING_X11
+ 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);
+#endif
+#ifdef GDK_WINDOWING_WIN32
+ {
+ GdkPixbuf *pixbuf =
+ gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+ status_icon->priv->image_data.icon_name,
+ status_icon->priv->size,
+ 0, NULL);
+
+ status_icon->priv->nid.hIcon = gdk_win32_pixbuf_to_hicon_libgtk_only (pixbuf);
+ status_icon->priv->nid.uFlags |= NIF_ICON;
+ if (status_icon->priv->nid.hWnd != NULL && status_icon->priv->visible)
+ if (!Shell_NotifyIconW (NIM_MODIFY, &status_icon->priv->nid))
+ g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1);
+ g_object_unref (pixbuf);
+ }
+#endif
+ }
+ break;
+
+ case GTK_IMAGE_EMPTY:
+#ifdef GDK_WINDOWING_X11
+ gtk_image_set_from_pixbuf (GTK_IMAGE (status_icon->priv->image), NULL);
+#endif
+#ifdef GDK_WINDOWING_WIN32
+ status_icon->priv->nid.uFlags &= ~NIF_ICON;
+ if (status_icon->priv->nid.hWnd != NULL && status_icon->priv->visible)
+ if (!Shell_NotifyIconW (NIM_MODIFY, &status_icon->priv->nid))
+ g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1);
+#endif
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+#ifdef GDK_WINDOWING_X11
+
+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);
+ }
+ }
+}
+
+#endif
+
+static gboolean
+gtk_status_icon_button_press (GtkStatusIcon *status_icon,
+ GdkEventButton *event)
+{
+ if (event->button == 1 && event->type == GDK_BUTTON_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);
+}
+
+/**
+ * gtk_status_icon_set_from_pixbuf:
+ * @status_icon: a #GtkStatusIcon
+ * @pixbuf: a #GdkPixbuf or %NULL
+ *
+ * Makes @status_icon display @pixbuf.
+ * See gtk_status_icon_new_from_pixbuf() for details.
+ *
+ * Since: 2.10
+ **/
+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);
+}
+
+/**
+ * gtk_status_icon_set_from_file:
+ * @status_icon: a #GtkStatusIcon
+ * @filename: a filename
+ *
+ * Makes @status_icon display the file @filename.
+ * See gtk_status_icon_new_from_file() for details.
+ *
+ * Since: 2.10
+ **/
+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);
+}
+
+/**
+ * gtk_status_icon_set_from_stock:
+ * @status_icon: a #GtkStatusIcon
+ * @stock_id: a stock icon id
+ *
+ * Makes @status_icon display the stock icon with the id @stock_id.
+ * See gtk_status_icon_new_from_stock() for details.
+ *
+ * Since: 2.10
+ **/
+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);
+}
+
+/**
+ * gtk_status_icon_set_from_icon_name:
+ * @status_icon: a #GtkStatusIcon
+ * @icon_name: an icon name
+ *
+ * Makes @status_icon display the icon named @icon_name from the
+ * current icon theme.
+ * See gtk_status_icon_new_from_icon_name() for details.
+ *
+ * Since: 2.10
+ **/
+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);
+}
+
+/**
+ * gtk_status_icon_get_storage_type:
+ * @status_icon:
+ *
+ * Gets the type of representation being used by the #GtkStatusIcon
+ * to store image data. If the #GtkStatusIcon has no image data,
+ * the return value will be %GTK_IMAGE_EMPTY.
+ *
+ * Return value: the image representation being used
+ *
+ * Since: 2.10
+ **/
+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;
+}
+/**
+ * gtk_status_icon_get_pixbuf:
+ * @status_icon: a #GtkStatusIcon
+ *
+ * Gets the #GdkPixbuf being displayed by the #GtkStatusIcon.
+ * The storage type of the status icon must be %GTK_IMAGE_EMPTY or
+ * %GTK_IMAGE_PIXBUF (see gtk_status_icon_get_storage_type()).
+ * The caller of this function does not own a reference to the
+ * returned pixbuf.
+ *
+ * Return value: the displayed pixbuf, or %NULL if the image is empty.
+ *
+ * Since: 2.10
+ **/
+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;
+}
+
+/**
+ * gtk_status_icon_get_stock:
+ * @status_icon: a #GtkStatusIcon
+ *
+ * Gets the id of the stock icon being displayed by the #GtkStatusIcon.
+ * The storage type of the status icon must be %GTK_IMAGE_EMPTY or
+ * %GTK_IMAGE_STOCK (see gtk_status_icon_get_storage_type()).
+ * The returned string is owned by the #GtkStatusIcon and should not
+ * be freed or modified.
+ *
+ * Return value: stock id of the displayed stock icon,
+ * or %NULL if the image is empty.
+ *
+ * Since: 2.10
+ **/
+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;
+}
+
+/**
+ * gtk_status_icon_get_icon_name:
+ * @status_icon: a #GtkStatusIcon
+ *
+ * Gets the name of the icon being displayed by the #GtkStatusIcon.
+ * The storage type of the status icon must be %GTK_IMAGE_EMPTY or
+ * %GTK_IMAGE_ICON_NAME (see gtk_status_icon_get_storage_type()).
+ * The returned string is owned by the #GtkStatusIcon and should not
+ * be freed or modified.
+ *
+ * Return value: name of the displayed icon, or %NULL if the image is empty.
+ *
+ * Since: 2.10
+ **/
+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;
+}
+
+/**
+ * gtk_status_icon_get_size:
+ * @status_icon: a #GtkStatusIcon
+ *
+ * Gets the size in pixels that is available for the image.
+ * Stock icons and named icons adapt their size automatically
+ * if the size of the notification area changes. For other
+ * storage types, the size-changed signal can be used to
+ * react to size changes.
+ *
+ * Return value: the size that is available for the image
+ *
+ * Since: 2.10
+ **/
+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;
+}
+
+/**
+ * gtk_status_icon_set_tooltip:
+ * @status_icon: a #GtkStatusIcon
+ * @tooltip_text: the tooltip text, or %NULL
+ *
+ * Sets the tooltip of the status icon.
+ *
+ * Since: 2.10
+ **/
+void
+gtk_status_icon_set_tooltip (GtkStatusIcon *status_icon,
+ const gchar *tooltip_text)
+{
+ g_return_if_fail (GTK_IS_STATUS_ICON (status_icon));
+
+#ifdef GDK_WINDOWING_X11
+ gtk_tooltips_set_tip (status_icon->priv->tooltips,
+ status_icon->priv->tray_icon,
+ tooltip_text, NULL);
+#endif
+#ifdef GDK_WINDOWING_WIN32
+ if (tooltip_text == NULL)
+ status_icon->priv->nid.uFlags &= ~NIF_TIP;
+ else
+ {
+ WCHAR *wcs = g_utf8_to_utf16 (tooltip_text, -1, NULL, NULL, NULL);
+
+ status_icon->priv->nid.uFlags |= NIF_TIP;
+ wcsncpy (status_icon->priv->nid.szTip, wcs,
+ G_N_ELEMENTS (status_icon->priv->nid.szTip) - 1);
+ status_icon->priv->nid.szTip[G_N_ELEMENTS (status_icon->priv->nid.szTip) - 1] = 0;
+ g_free (wcs);
+ }
+ if (status_icon->priv->nid.hWnd != NULL && status_icon->priv->visible)
+ if (!Shell_NotifyIconW (NIM_MODIFY, &status_icon->priv->nid))
+ g_warning ("%s:%d:Shell_NotifyIconW(NIM_MODIFY) failed", __FILE__, __LINE__-1);
+#endif
+}
+
+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);
+ }
+}
+
+/**
+ * gtk_status_icon_set_visible:
+ * @status_icon: a #GtkStatusIcon
+ * @visible: %TRUE to show the status icon, %FALSE to hide it
+ *
+ * Shows or hides a status icon.
+ *
+ * Since: 2.10
+ **/
+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;
+
+#ifdef GDK_WINDOWING_X11
+ if (visible)
+ gtk_widget_show (status_icon->priv->tray_icon);
+ else
+ gtk_widget_hide (status_icon->priv->tray_icon);
+#endif
+#ifdef GDK_WINDOWING_WIN32
+ if (status_icon->priv->nid.hWnd != NULL)
+ {
+ if (visible)
+ Shell_NotifyIconW (NIM_ADD, &status_icon->priv->nid);
+ else
+ Shell_NotifyIconW (NIM_DELETE, &status_icon->priv->nid);
+ }
+#endif
+ g_object_notify (G_OBJECT (status_icon), "visible");
+ }
+}
+
+/**
+ * gtk_status_icon_get_visible:
+ * @status_icon: a #GtkStatusIcon
+ *
+ * Returns wether the status icon is visible or not.
+ * Note that being visible does not guarantee that
+ * the user can actually see the icon, see also
+ * gtk_status_icon_is_embedded().
+ *
+ * Return value: %TRUE if the status icon is visible
+ *
+ * Since: 2.10
+ **/
+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;
+}
+
+/**
+ * gtk_status_icon_set_blinking:
+ * @status_icon: a #GtkStatusIcon
+ * @blinking: %TRUE to turn blinking on, %FALSE to turn it off
+ *
+ * Makes the status icon start or stop blinking.
+ * Note that blinking user interface elements may be problematic
+ * for some users, and thus may be turned off, in which case
+ * this setting has no effect.
+ *
+ * Since: 2.10
+ **/
+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");
+ }
+}
+
+/**
+ * gtk_status_icon_get_blinking:
+ * @status_icon: a #GtkStatusIcon
+ *
+ * Returns whether the icon is blinking, see
+ * gtk_status_icon_set_blinking().
+ *
+ * Return value: %TRUE if the icon is blinking
+ *
+ * Since: 2.10
+ **/
+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;
+}
+
+/**
+ * gtk_status_icon_is_embedded:
+ * @status_icon: a #GtkStatusIcon
+ *
+ * Returns whether the status icon is embedded in a notification
+ * area.
+ *
+ * Return value: %TRUE if the status icon is embedded in
+ * a notification area.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gtk_status_icon_is_embedded (GtkStatusIcon *status_icon)
+{
+#ifdef GDK_WINDOWING_X11
+ GtkPlug *plug;
+#endif
+
+ g_return_val_if_fail (GTK_IS_STATUS_ICON (status_icon), FALSE);
+
+#ifdef GDK_WINDOWING_X11
+ plug = GTK_PLUG (status_icon->priv->tray_icon);
+
+ if (plug->socket_window)
+ return TRUE;
+ else
+ return FALSE;
+#endif
+#ifdef GDK_WINDOWING_WIN32
+ return TRUE;
+#endif
+}
+
+#endif /* !GTK_CHECK_VERSION (2, 9, 0) */
+
+#endif /* GDK_WINDOWING_WIN32 */