2321 lines
60 KiB
C
2321 lines
60 KiB
C
/* -*- 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 */
|