Thumbnail file hits. Based on a patch from D Bera
[beagle.git] / glue / eggtrayicon.c
blobb48dee4c00435ffc422fe3a069fe1fd0f0f23d3d
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* eggtrayicon.c
3 * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
21 #include <config.h>
22 #include <string.h>
23 #include <libintl.h>
25 #include "eggtrayicon.h"
27 #include <gdk/gdkx.h>
28 #include <X11/Xatom.h>
31 #if 0
32 #ifndef _
33 #define _(x) dgettext (GETTEXT_PACKAGE, x)
34 #define N_(x) x
35 #endif
36 #else
37 #define _(x) x
38 #define N_(x) x
39 #endif
41 #define SYSTEM_TRAY_REQUEST_DOCK 0
42 #define SYSTEM_TRAY_BEGIN_MESSAGE 1
43 #define SYSTEM_TRAY_CANCEL_MESSAGE 2
45 #define SYSTEM_TRAY_ORIENTATION_HORZ 0
46 #define SYSTEM_TRAY_ORIENTATION_VERT 1
48 enum {
49 PROP_0,
50 PROP_ORIENTATION
53 static GtkPlugClass *parent_class = NULL;
55 static void egg_tray_icon_init (EggTrayIcon *icon);
56 static void egg_tray_icon_class_init (EggTrayIconClass *klass);
58 static void egg_tray_icon_get_property (GObject *object,
59 guint prop_id,
60 GValue *value,
61 GParamSpec *pspec);
63 static void egg_tray_icon_realize (GtkWidget *widget);
64 static void egg_tray_icon_unrealize (GtkWidget *widget);
66 static void egg_tray_icon_update_manager_window (EggTrayIcon *icon);
68 GType
69 egg_tray_icon_get_type (void)
71 static GType our_type = 0;
73 if (our_type == 0)
75 static const GTypeInfo our_info =
77 sizeof (EggTrayIconClass),
78 (GBaseInitFunc) NULL,
79 (GBaseFinalizeFunc) NULL,
80 (GClassInitFunc) egg_tray_icon_class_init,
81 NULL, /* class_finalize */
82 NULL, /* class_data */
83 sizeof (EggTrayIcon),
84 0, /* n_preallocs */
85 (GInstanceInitFunc) egg_tray_icon_init,
86 NULL
89 our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0);
92 return our_type;
95 static void
96 egg_tray_icon_init (EggTrayIcon *icon)
98 icon->stamp = 1;
99 icon->orientation = GTK_ORIENTATION_HORIZONTAL;
101 gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
104 static void
105 egg_tray_icon_class_init (EggTrayIconClass *klass)
107 GObjectClass *gobject_class = (GObjectClass *)klass;
108 GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
110 parent_class = g_type_class_peek_parent (klass);
112 gobject_class->get_property = egg_tray_icon_get_property;
114 widget_class->realize = egg_tray_icon_realize;
115 widget_class->unrealize = egg_tray_icon_unrealize;
117 g_object_class_install_property (gobject_class,
118 PROP_ORIENTATION,
119 g_param_spec_enum ("orientation",
120 _("Orientation"),
121 _("The orientation of the tray."),
122 GTK_TYPE_ORIENTATION,
123 GTK_ORIENTATION_HORIZONTAL,
124 G_PARAM_READABLE));
127 static void
128 egg_tray_icon_get_property (GObject *object,
129 guint prop_id,
130 GValue *value,
131 GParamSpec *pspec)
133 EggTrayIcon *icon = EGG_TRAY_ICON (object);
135 switch (prop_id)
137 case PROP_ORIENTATION:
138 g_value_set_enum (value, icon->orientation);
139 break;
140 default:
141 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
142 break;
146 static void
147 egg_tray_icon_get_orientation_property (EggTrayIcon *icon)
149 Display *xdisplay;
150 Atom type;
151 int format;
152 union {
153 gulong *prop;
154 guchar *prop_ch;
155 } prop = { NULL };
156 gulong nitems;
157 gulong bytes_after;
158 int error, result;
160 g_assert (icon->manager_window != None);
162 xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
164 gdk_error_trap_push ();
165 type = None;
166 result = XGetWindowProperty (xdisplay,
167 icon->manager_window,
168 icon->orientation_atom,
169 0, G_MAXLONG, FALSE,
170 XA_CARDINAL,
171 &type, &format, &nitems,
172 &bytes_after, &(prop.prop_ch));
173 error = gdk_error_trap_pop ();
175 if (error || result != Success)
176 return;
178 if (type == XA_CARDINAL)
180 GtkOrientation orientation;
182 orientation = (prop.prop [0] == SYSTEM_TRAY_ORIENTATION_HORZ) ?
183 GTK_ORIENTATION_HORIZONTAL :
184 GTK_ORIENTATION_VERTICAL;
186 if (icon->orientation != orientation)
188 icon->orientation = orientation;
190 g_object_notify (G_OBJECT (icon), "orientation");
194 if (prop.prop)
195 XFree (prop.prop);
198 static GdkFilterReturn
199 egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event G_GNUC_UNUSED,
200 gpointer user_data)
202 EggTrayIcon *icon = user_data;
203 XEvent *xev = (XEvent *)xevent;
205 if (xev->xany.type == ClientMessage &&
206 xev->xclient.message_type == icon->manager_atom &&
207 (unsigned int) xev->xclient.data.l[1] == icon->selection_atom)
209 egg_tray_icon_update_manager_window (icon);
211 else if (xev->xany.window == icon->manager_window)
213 if (xev->xany.type == PropertyNotify &&
214 xev->xproperty.atom == icon->orientation_atom)
216 egg_tray_icon_get_orientation_property (icon);
218 if (xev->xany.type == DestroyNotify)
220 egg_tray_icon_update_manager_window (icon);
224 return GDK_FILTER_CONTINUE;
227 static void
228 egg_tray_icon_unrealize (GtkWidget *widget)
230 EggTrayIcon *icon = EGG_TRAY_ICON (widget);
231 GdkWindow *root_window;
233 if (icon->manager_window != None)
235 GdkWindow *gdkwin;
237 gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
238 icon->manager_window);
240 gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
243 root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
245 gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon);
247 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
248 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
251 static void
252 egg_tray_icon_send_manager_message (EggTrayIcon *icon,
253 long message,
254 Window window,
255 long data1,
256 long data2,
257 long data3)
259 XClientMessageEvent ev;
260 Display *display;
262 ev.type = ClientMessage;
263 ev.window = window;
264 ev.message_type = icon->system_tray_opcode_atom;
265 ev.format = 32;
266 ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
267 ev.data.l[1] = message;
268 ev.data.l[2] = data1;
269 ev.data.l[3] = data2;
270 ev.data.l[4] = data3;
272 display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
274 gdk_error_trap_push ();
275 XSendEvent (display,
276 icon->manager_window, False, NoEventMask, (XEvent *)&ev);
277 XSync (display, False);
278 gdk_error_trap_pop ();
281 static void
282 egg_tray_icon_send_dock_request (EggTrayIcon *icon)
284 egg_tray_icon_send_manager_message (icon,
285 SYSTEM_TRAY_REQUEST_DOCK,
286 icon->manager_window,
287 gtk_plug_get_id (GTK_PLUG (icon)),
288 0, 0);
291 static void
292 egg_tray_icon_update_manager_window (EggTrayIcon *icon)
294 Display *xdisplay;
296 xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
298 if (icon->manager_window != None)
300 GdkWindow *gdkwin;
302 gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
303 icon->manager_window);
305 gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
308 XGrabServer (xdisplay);
310 icon->manager_window = XGetSelectionOwner (xdisplay,
311 icon->selection_atom);
313 if (icon->manager_window != None)
314 XSelectInput (xdisplay,
315 icon->manager_window, StructureNotifyMask|PropertyChangeMask);
317 XUngrabServer (xdisplay);
318 XFlush (xdisplay);
320 if (icon->manager_window != None)
322 GdkWindow *gdkwin;
324 gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
325 icon->manager_window);
327 gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon);
329 /* Send a request that we'd like to dock */
330 egg_tray_icon_send_dock_request (icon);
332 egg_tray_icon_get_orientation_property (icon);
336 static void
337 egg_tray_icon_realize (GtkWidget *widget)
339 EggTrayIcon *icon = EGG_TRAY_ICON (widget);
340 GdkScreen *screen;
341 GdkDisplay *display;
342 Display *xdisplay;
343 char buffer[256];
344 GdkWindow *root_window;
346 if (GTK_WIDGET_CLASS (parent_class)->realize)
347 GTK_WIDGET_CLASS (parent_class)->realize (widget);
349 screen = gtk_widget_get_screen (widget);
350 display = gdk_screen_get_display (screen);
351 xdisplay = gdk_x11_display_get_xdisplay (display);
353 /* Now see if there's a manager window around */
354 g_snprintf (buffer, sizeof (buffer),
355 "_NET_SYSTEM_TRAY_S%d",
356 gdk_screen_get_number (screen));
358 icon->selection_atom = XInternAtom (xdisplay, buffer, False);
360 icon->manager_atom = XInternAtom (xdisplay, "MANAGER", False);
362 icon->system_tray_opcode_atom = XInternAtom (xdisplay,
363 "_NET_SYSTEM_TRAY_OPCODE",
364 False);
366 icon->orientation_atom = XInternAtom (xdisplay,
367 "_NET_SYSTEM_TRAY_ORIENTATION",
368 False);
370 egg_tray_icon_update_manager_window (icon);
372 root_window = gdk_screen_get_root_window (screen);
374 /* Add a root window filter so that we get changes on MANAGER */
375 gdk_window_add_filter (root_window,
376 egg_tray_icon_manager_filter, icon);
379 EggTrayIcon *
380 egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name)
382 g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
384 return g_object_new (EGG_TYPE_TRAY_ICON, "screen", screen, "title", name, NULL);
387 EggTrayIcon*
388 egg_tray_icon_new (const gchar *name)
390 return g_object_new (EGG_TYPE_TRAY_ICON, "title", name, NULL);
393 guint
394 egg_tray_icon_send_message (EggTrayIcon *icon,
395 gint timeout,
396 const gchar *message,
397 gint len)
399 guint stamp;
401 g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0);
402 g_return_val_if_fail (timeout >= 0, 0);
403 g_return_val_if_fail (message != NULL, 0);
405 if (icon->manager_window == None)
406 return 0;
408 if (len < 0)
409 len = strlen (message);
411 stamp = icon->stamp++;
413 /* Get ready to send the message */
414 egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
415 (Window)gtk_plug_get_id (GTK_PLUG (icon)),
416 timeout, len, stamp);
418 /* Now to send the actual message */
419 gdk_error_trap_push ();
420 while (len > 0)
422 XClientMessageEvent ev;
423 Display *xdisplay;
425 xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
427 ev.type = ClientMessage;
428 ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
429 ev.format = 8;
430 ev.message_type = XInternAtom (xdisplay,
431 "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
432 if (len > 20)
434 memcpy (&ev.data, message, 20);
435 len -= 20;
436 message += 20;
438 else
440 memcpy (&ev.data, message, len);
441 len = 0;
444 XSendEvent (xdisplay,
445 icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev);
446 XSync (xdisplay, False);
448 gdk_error_trap_pop ();
450 return stamp;
453 void
454 egg_tray_icon_cancel_message (EggTrayIcon *icon,
455 guint id)
457 g_return_if_fail (EGG_IS_TRAY_ICON (icon));
458 g_return_if_fail (id > 0);
460 egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
461 (Window)gtk_plug_get_id (GTK_PLUG (icon)),
462 id, 0, 0);
465 GtkOrientation
466 egg_tray_icon_get_orientation (EggTrayIcon *icon)
468 g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), GTK_ORIENTATION_HORIZONTAL);
470 return icon->orientation;