/* -*- 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"
#ifdef GDK_WINDOWING_X11
#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;
}
#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_sink (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 */