r3247: Experimental extended attributes support for setting MIME types
[rox-filer.git] / ROX-Filer / src / tasklist.c
blob2348772f2aa8cf996ea0c4e8ca54cec6ae8e6245
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* tasklist.c - code for tracking windows
24 * Loosly based on code in GNOME's libwnck.
27 #include "config.h"
29 #include <stdlib.h>
31 #include <gtk/gtk.h>
32 #include <gdk/gdk.h>
33 #include <gdk/gdkx.h>
35 #include <X11/Xlib.h>
36 #include <X11/Xatom.h>
38 #include "global.h"
40 #include "tasklist.h"
41 #include "wrapped.h"
42 #include "options.h"
43 #include "gui_support.h"
44 #include "main.h"
45 #include "pinboard.h"
46 #include "pixmaps.h"
47 #include "support.h"
49 /* There is one of these for each window controlled by the window
50 * manager (all tasks) in the _NET_CLIENT_LIST property.
52 typedef struct _IconWindow IconWindow;
54 struct _IconWindow {
55 GtkWidget *widget; /* Widget used for icon when iconified */
56 GtkWidget *label;
57 gchar *text;
58 Window xwindow;
59 gboolean iconified;
60 gint timeout_update; /* Non-zero => timeout callback in use */
63 /* If TRUE, only iconfied windows with _NET_WM_STATE_HIDDEN are really icons */
64 static gboolean wm_supports_hidden = FALSE;
66 static GdkAtom xa__NET_SUPPORTED = GDK_NONE;
67 static GdkAtom xa_WM_STATE = GDK_NONE;
68 static GdkAtom xa_WM_NAME = GDK_NONE;
69 static GdkAtom xa_WM_ICON_NAME = GDK_NONE;
70 static GdkAtom xa_UTF8_STRING = GDK_NONE;
71 static GdkAtom xa_TEXT = GDK_NONE;
72 static GdkAtom xa__NET_WM_VISIBLE_NAME = GDK_NONE;
73 static GdkAtom xa__NET_WM_ICON_NAME = GDK_NONE;
74 static GdkAtom xa__NET_CLIENT_LIST = GDK_NONE;
75 static GdkAtom xa__NET_WM_ICON_GEOMETRY = GDK_NONE;
76 static GdkAtom xa__NET_WM_STATE = GDK_NONE;
77 static GdkAtom xa__NET_WM_STATE_HIDDEN = GDK_NONE;
79 /* We have selected destroy and property events on every window in
80 * this table.
82 static GHashTable *known = NULL; /* XID -> IconWindow */
84 /* Static prototypes */
85 static void remove_window(Window win);
86 static void tasklist_update(gboolean to_empty);
87 static GdkFilterReturn window_filter(GdkXEvent *xevent,
88 GdkEvent *event,
89 gpointer data);
90 static guint xid_hash(XID *xid);
91 static gboolean xid_equal(XID *a, XID *b);
92 static void state_changed(IconWindow *win);
93 static void show_icon(IconWindow *win);
94 static void icon_win_free(IconWindow *win);
95 static void update_style(gpointer key, gpointer data, gpointer user_data);
96 static void update_supported(void);
97 static gboolean update_title(gpointer data);
99 /****************************************************************
100 * EXTERNAL INTERFACE *
101 ****************************************************************/
103 void tasklist_set_active(gboolean active)
105 static gboolean need_init = TRUE;
106 static gboolean tasklist_active = FALSE;
108 if (active == tasklist_active)
109 return;
110 tasklist_active = active;
112 if (need_init)
114 GdkWindow *root;
116 root = gdk_get_default_root_window();
118 xa__NET_SUPPORTED = gdk_atom_intern("_NET_SUPPORTED", FALSE);
119 xa_WM_STATE = gdk_atom_intern("WM_STATE", FALSE);
120 xa_WM_ICON_NAME = gdk_atom_intern("WM_ICON_NAME", FALSE);
121 xa_WM_NAME = gdk_atom_intern("WM_NAME", FALSE);
122 xa_UTF8_STRING = gdk_atom_intern("UTF8_STRING", FALSE);
123 xa_TEXT = gdk_atom_intern("TEXT", FALSE);
124 xa__NET_CLIENT_LIST =
125 gdk_atom_intern("_NET_CLIENT_LIST", FALSE);
126 xa__NET_WM_VISIBLE_NAME =
127 gdk_atom_intern("_NET_WM_VISIBLE_NAME", FALSE);
128 xa__NET_WM_ICON_NAME =
129 gdk_atom_intern("_NET_WM_ICON_NAME", FALSE);
130 xa__NET_WM_ICON_GEOMETRY =
131 gdk_atom_intern("_NET_WM_ICON_GEOMETRY", FALSE);
132 xa__NET_WM_STATE = gdk_atom_intern("_NET_WM_STATE", FALSE);
133 xa__NET_WM_STATE_HIDDEN =
134 gdk_atom_intern("_NET_WM_STATE_HIDDEN", FALSE);
136 known = g_hash_table_new_full((GHashFunc) xid_hash,
137 (GEqualFunc) xid_equal,
138 NULL,
139 (GDestroyNotify) icon_win_free);
140 gdk_window_set_events(root, gdk_window_get_events(root) |
141 GDK_PROPERTY_CHANGE_MASK);
142 need_init = FALSE;
145 if (active)
147 gdk_window_add_filter(NULL, window_filter, NULL);
148 update_supported();
150 else
151 gdk_window_remove_filter(NULL, window_filter, NULL);
153 tasklist_update(!active);
156 /* User has changes the colours in the options box... */
157 void tasklist_style_changed(void)
159 if (known)
160 g_hash_table_foreach(known, update_style, NULL);
163 /****************************************************************
164 * INTERNAL FUNCTIONS *
165 ****************************************************************/
167 static void icon_win_free(IconWindow *win)
169 g_return_if_fail(win->widget == NULL);
170 g_return_if_fail(win->label == NULL);
172 if (win->timeout_update)
173 gtk_timeout_remove(win->timeout_update);
175 g_free(win->text);
176 g_free(win);
179 /* From gdk */
180 static guint xid_hash(XID *xid)
182 return *xid;
185 /* From gdk */
186 static gboolean xid_equal(XID *a, XID *b)
188 return (*a == *b);
191 static int wincmp(const void *a, const void *b)
193 const Window *aw = a;
194 const Window *bw = b;
196 if (*aw < *bw)
197 return -1;
198 else if (*aw > *bw)
199 return 1;
200 else
201 return 0;
204 /* Read the list of WINDOWs from (xwindow,atom), returning them
205 * in a (sorted) Array of Windows. On error, an empty array is
206 * returned.
207 * Free the array afterwards.
209 static GArray *get_window_list(Window xwindow, GdkAtom atom)
211 GArray *array;
212 Atom type;
213 int format;
214 gulong nitems;
215 gulong bytes_after;
216 unsigned char *data;
217 int err, result;
218 int i;
220 array = g_array_new(FALSE, FALSE, sizeof(Window));
222 gdk_error_trap_push();
223 type = None;
224 result = XGetWindowProperty(gdk_display,
225 xwindow,
226 gdk_x11_atom_to_xatom(atom),
227 0, G_MAXLONG,
228 False, XA_WINDOW, &type, &format, &nitems,
229 &bytes_after, &data);
230 err = gdk_error_trap_pop();
232 if (err != Success || result != Success)
233 return array;
235 if (type == XA_WINDOW)
237 for (i = 0; i < nitems; i++)
238 g_array_append_val(array, ((Window *) data)[i]);
240 if (array->len)
241 g_array_sort(array, wincmp);
244 XFree(data);
246 return array;
249 static guchar *get_str(IconWindow *win, GdkAtom atom)
251 Atom rtype;
252 int format;
253 gulong nitems;
254 gulong bytes_after;
255 unsigned char *data, *str = NULL;
257 if (XGetWindowProperty(gdk_display, win->xwindow,
258 gdk_x11_atom_to_xatom(atom),
259 0, G_MAXLONG, False,
260 AnyPropertyType,
261 &rtype, &format, &nitems,
262 &bytes_after, &data) == Success && data)
264 if (*data)
265 str = g_strdup(data);
266 XFree(data);
269 return str;
272 static void get_icon_name(IconWindow *win)
274 null_g_free(&win->text);
276 /* Keep this list in sync with window_filter */
278 win->text = get_str(win, xa__NET_WM_ICON_NAME);
279 if (!win->text)
280 win->text = get_str(win, xa__NET_WM_VISIBLE_NAME);
281 if (!win->text)
282 win->text = get_str(win, xa_WM_ICON_NAME);
283 if (!win->text)
284 win->text = get_str(win, xa_WM_NAME);
285 if (!win->text)
286 win->text = g_strdup(_("Window"));
289 static gboolean update_title(gpointer data)
291 IconWindow *win = (IconWindow *) data;
293 if (!win->widget)
294 return FALSE; /* No longer an icon */
296 get_icon_name(win);
297 wrapped_label_set_text(WRAPPED_LABEL(win->label), win->text);
299 win->timeout_update = 0;
301 return FALSE;
304 /* Call from within error_push/pop */
305 static void window_check_status(IconWindow *win)
307 Atom type;
308 int format;
309 gulong nitems;
310 gulong bytes_after;
311 unsigned char *data;
312 gboolean iconic = FALSE;
314 if (wm_supports_hidden && XGetWindowProperty(gdk_display, win->xwindow,
315 gdk_x11_atom_to_xatom(xa__NET_WM_STATE),
316 0, G_MAXLONG, False,
317 XA_ATOM,
318 &type, &format, &nitems,
319 &bytes_after, &data) == Success && data)
321 GdkAtom state;
322 int i;
324 for (i = 0; i < nitems; i++)
326 state = gdk_x11_xatom_to_atom(((Atom *) data)[i]);
327 if (state == xa__NET_WM_STATE_HIDDEN)
329 iconic = TRUE;
330 break;
333 XFree(data);
335 else if (XGetWindowProperty(gdk_display, win->xwindow,
336 gdk_x11_atom_to_xatom(xa_WM_STATE),
337 0, 1, False,
338 gdk_x11_atom_to_xatom(xa_WM_STATE),
339 &type, &format, &nitems,
340 &bytes_after, &data) == Success && data)
342 iconic = ((guint32 *) data)[0] == 3;
343 XFree(data);
345 else
346 iconic = FALSE;
348 if (win->iconified == iconic)
349 return;
351 win->iconified = iconic;
353 state_changed(win);
356 /* Called for all events on all windows */
357 static GdkFilterReturn window_filter(GdkXEvent *xevent,
358 GdkEvent *event,
359 gpointer data)
361 XEvent *xev = (XEvent *) xevent;
362 IconWindow *w;
364 if (xev->type == PropertyNotify)
366 GdkAtom atom = gdk_x11_xatom_to_atom(xev->xproperty.atom);
367 Window win = ((XPropertyEvent *) xev)->window;
369 if (atom == xa_WM_STATE || atom == xa__NET_WM_STATE)
371 w = g_hash_table_lookup(known, &win);
373 if (w)
375 gdk_error_trap_push();
376 window_check_status(w);
377 if (gdk_error_trap_pop() != Success)
378 g_hash_table_remove(known, &win);
381 else if (atom == xa__NET_WM_ICON_NAME ||
382 atom == xa__NET_WM_VISIBLE_NAME ||
383 atom == xa_WM_ICON_NAME ||
384 atom == xa_WM_NAME)
386 /* Keep this list in sync with get_icon_name */
387 w = g_hash_table_lookup(known, &win);
389 if (w && w->widget && !w->timeout_update)
390 w->timeout_update = gtk_timeout_add(100,
391 update_title, w);
393 else if (atom == xa__NET_CLIENT_LIST)
394 tasklist_update(FALSE);
395 else if (atom == xa__NET_SUPPORTED)
396 update_supported();
399 return GDK_FILTER_CONTINUE;
402 /* Window has been added to list of managed windows */
403 static void add_window(Window win)
405 IconWindow *w;
407 /* g_print("[ New window %ld ]\n", (long) win); */
409 w = g_hash_table_lookup(known, &win);
411 if (!w)
413 XWindowAttributes attr;
415 gdk_error_trap_push();
417 XGetWindowAttributes(gdk_display, win, &attr);
419 if (gdk_error_trap_pop() != Success)
420 return;
421 gdk_error_trap_push();
423 XSelectInput(gdk_display, win, attr.your_event_mask |
424 PropertyChangeMask);
426 gdk_flush();
428 if (gdk_error_trap_pop() != Success)
429 return;
431 w = g_new(IconWindow, 1);
432 w->widget = NULL;
433 w->label = NULL;
434 w->text = NULL;
435 w->xwindow = win;
436 w->iconified = FALSE;
437 w->timeout_update = 0;
439 g_hash_table_insert(known, &w->xwindow, w);
442 gdk_error_trap_push();
444 window_check_status(w);
446 #if 0
447 set_iconify_pos(w);
448 #endif
450 if (gdk_error_trap_pop() != Success)
451 g_hash_table_remove(known, &win);
454 /* Window is no longer managed, but hasn't been destroyed yet */
455 static void remove_window(Window win)
457 IconWindow *w;
459 /* g_print("[ Remove window %ld ]\n", (long) win); */
461 w = g_hash_table_lookup(known, &win);
462 if (w)
464 if (w->iconified)
466 w->iconified = FALSE;
467 state_changed(w);
470 g_hash_table_remove(known, &win);
474 /* Make sure the window list is up-to-date. Call once to start, and then
475 * everytime _NET_CLIENT_LIST changes.
476 * If 'to_empty' is set them pretend all windows have disappeared.
478 static void tasklist_update(gboolean to_empty)
480 static GArray *old_mapping = NULL;
481 GArray *mapping = NULL;
482 int new_i, old_i;
484 if (!old_mapping)
486 old_mapping = g_array_new(FALSE, FALSE, sizeof(Window));
489 if (to_empty)
490 mapping = g_array_new(FALSE, FALSE, sizeof(Window));
491 else
492 mapping = get_window_list(gdk_x11_get_default_root_xwindow(),
493 gdk_atom_intern("_NET_CLIENT_LIST", FALSE));
495 new_i = 0;
496 old_i = 0;
497 while (new_i < mapping->len && old_i < old_mapping->len)
499 Window new = g_array_index(mapping, Window, new_i);
500 Window old = g_array_index(old_mapping, Window, old_i);
502 if (new == old)
504 new_i++;
505 old_i++;
507 else if (new < old)
509 add_window(new);
510 new_i++;
512 else
514 remove_window(old);
515 old_i++;
518 while (new_i < mapping->len)
520 add_window(g_array_index(mapping, Window, new_i));
521 new_i++;
523 while (old_i < old_mapping->len)
525 remove_window(g_array_index(old_mapping, Window, old_i));
526 old_i++;
529 g_array_free(old_mapping, TRUE);
530 old_mapping = mapping;
533 /* Called when the user clicks on the button */
534 static void uniconify(IconWindow *win)
536 XClientMessageEvent sev;
538 sev.type = ClientMessage;
539 sev.display = gdk_display;
540 sev.format = 32;
541 sev.window = win->xwindow;
542 sev.message_type = gdk_x11_atom_to_xatom(
543 gdk_atom_intern("_NET_ACTIVE_WINDOW", FALSE));
544 sev.data.l[0] = 0;
546 gdk_error_trap_push();
548 XSendEvent(gdk_display, DefaultRootWindow(gdk_display), False,
549 SubstructureNotifyMask | SubstructureRedirectMask,
550 (XEvent *) &sev);
551 XSync(gdk_display, False);
553 gdk_error_trap_pop();
556 static gint drag_start_x = -1;
557 static gint drag_start_y = -1;
558 static gboolean drag_started = FALSE;
559 static gint drag_off_x = -1;
560 static gint drag_off_y = -1;
562 static void icon_button_press(GtkWidget *widget,
563 GdkEventButton *event,
564 IconWindow *win)
566 if (event->button == 1)
568 drag_start_x = event->x_root;
569 drag_start_y = event->y_root;
570 drag_started = FALSE;
572 drag_off_x = event->x;
573 drag_off_y = event->y;
577 static gboolean icon_motion_notify(GtkWidget *widget,
578 GdkEventMotion *event,
579 IconWindow *win)
581 if (event->state & GDK_BUTTON1_MASK)
583 int dx = event->x_root - drag_start_x;
584 int dy = event->y_root - drag_start_y;
586 if (!drag_started)
588 if (abs(dx) < 5 && abs(dy) < 5)
589 return FALSE;
590 drag_started = TRUE;
593 fixed_move_fast(GTK_FIXED(win->widget->parent),
594 win->widget,
595 event->x_root - drag_off_x,
596 event->y_root - drag_off_y);
599 return FALSE;
602 static void button_released(GtkWidget *widget, IconWindow *win)
604 if (!drag_started)
605 uniconify(win);
608 static GdkColormap* get_cmap(GdkPixmap *pixmap)
610 GdkColormap *cmap;
612 cmap = gdk_drawable_get_colormap(pixmap);
613 if (cmap)
614 g_object_ref(G_OBJECT(cmap));
616 if (cmap == NULL)
618 if (gdk_drawable_get_depth(pixmap) == 1)
620 /* Masks don't need colourmaps */
621 cmap = NULL;
623 else
625 /* Try system cmap */
626 cmap = gdk_colormap_get_system();
627 g_object_ref(G_OBJECT(cmap));
631 /* Be sure we aren't going to blow up due to visual mismatch */
632 if (cmap && (gdk_colormap_get_visual(cmap)->depth !=
633 gdk_drawable_get_depth(pixmap)))
634 cmap = NULL;
636 return cmap;
639 /* Copy a pixmap from the server to a client-side pixbuf */
640 static GdkPixbuf* pixbuf_from_pixmap(Pixmap xpixmap)
642 GdkDrawable *drawable;
643 GdkPixbuf *retval;
644 GdkColormap *cmap;
645 int width, height;
647 retval = NULL;
649 drawable = gdk_xid_table_lookup(xpixmap);
651 if (drawable)
652 g_object_ref(G_OBJECT(drawable));
653 else
654 drawable = gdk_pixmap_foreign_new(xpixmap);
656 cmap = get_cmap(drawable);
658 /* GDK is supposed to do this but doesn't in GTK 2.0.2,
659 * fixed in 2.0.3
661 gdk_drawable_get_size(drawable, &width, &height);
663 retval = gdk_pixbuf_get_from_drawable(NULL, drawable, cmap,
664 0, 0, 0, 0, width, height);
666 if (cmap)
667 g_object_unref(G_OBJECT(cmap));
668 g_object_unref(G_OBJECT(drawable));
670 return retval;
673 /* Creates a new masked pixbuf from a non-masked pixbuf and a mask */
674 static GdkPixbuf* apply_mask(GdkPixbuf *pixbuf, GdkPixbuf *mask)
676 int w, h;
677 int i, j;
678 GdkPixbuf *with_alpha;
679 guchar *src;
680 guchar *dest;
681 int src_stride;
682 int dest_stride;
684 w = MIN(gdk_pixbuf_get_width(mask), gdk_pixbuf_get_width(pixbuf));
685 h = MIN(gdk_pixbuf_get_height(mask), gdk_pixbuf_get_height(pixbuf));
687 with_alpha = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
689 dest = gdk_pixbuf_get_pixels(with_alpha);
690 src = gdk_pixbuf_get_pixels(mask);
692 dest_stride = gdk_pixbuf_get_rowstride(with_alpha);
693 src_stride = gdk_pixbuf_get_rowstride(mask);
695 i = 0;
696 while (i < h)
698 j = 0;
699 while (j < w)
701 guchar *s = src + i * src_stride + j * 3;
702 guchar *d = dest + i * dest_stride + j * 4;
704 /* s[0] == s[1] == s[2], they are 255 if the bit was
705 * set, 0 otherwise
707 if (s[0] == 0)
708 d[3] = 0; /* transparent */
709 else
710 d[3] = 255; /* opaque */
712 ++j;
715 ++i;
718 return with_alpha;
721 #define BORDER_X 16
722 #define BORDER_Y 8
723 /* Take the icon that the iconified window has given us and modify it to make
724 * it obvious what it is.
726 static GdkPixbuf *apply_window_effect(GdkPixbuf *src)
728 GdkPixbuf *new;
729 int w, h;
731 w = gdk_pixbuf_get_width(src);
732 h = gdk_pixbuf_get_height(src);
734 new = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(src), TRUE,
735 8, w + BORDER_X * 2, h + BORDER_Y * 2);
736 gdk_pixbuf_fill(new, 0x88888888);
738 gdk_pixbuf_composite(src, new,
739 BORDER_X, BORDER_Y, w, h,
740 BORDER_X, BORDER_Y, 1, 1,
741 GDK_INTERP_NEAREST, 255);
743 return new;
746 /* Return a suitable icon for this window. unref the result.
747 * Never returns NULL.
749 static GdkPixbuf *get_image_for(IconWindow *win)
751 static MaskedPixmap *default_icon = NULL;
752 Pixmap pixmap = None;
753 Pixmap mask = None;
754 XWMHints *hints;
755 GdkPixbuf *retval = NULL;
757 /* Try the pixmap and mask in the old WMHints... */
758 gdk_error_trap_push();
759 hints = XGetWMHints(gdk_display, win->xwindow);
761 if (hints)
763 if (hints->flags & IconPixmapHint)
764 pixmap = hints->icon_pixmap;
765 if (hints->flags & IconMaskHint)
766 mask = hints->icon_mask;
768 XFree(hints);
769 hints = NULL;
772 if (pixmap != None)
774 GdkPixbuf *mask_pb = NULL;
776 retval = pixbuf_from_pixmap(pixmap);
778 if (retval && mask != None)
779 mask_pb = pixbuf_from_pixmap(mask);
781 if (mask_pb)
783 GdkPixbuf *masked;
785 masked = apply_mask(retval, mask_pb);
786 g_object_unref(G_OBJECT(mask_pb));
788 if (masked)
790 g_object_unref(G_OBJECT(retval));
791 retval = masked;
796 gdk_flush();
798 gdk_error_trap_pop();
800 if (!retval)
802 if (!default_icon)
803 default_icon = load_pixmap("iconified");
805 retval = default_icon->pixbuf;
806 g_object_ref(retval);
809 /* Apply a special effect to make this look different from normal
810 * pinboard icons.
813 GdkPixbuf *old = retval;
814 retval = apply_window_effect(old);
815 g_object_unref(old);
818 return retval;
821 /* Stop the button from highlighting */
822 static gint icon_expose(GtkWidget *widget, GdkEventExpose *event,
823 IconWindow *win)
825 static GtkWidgetClass *parent_class = NULL;
827 g_return_val_if_fail(win != NULL, TRUE);
828 g_return_val_if_fail(win->label != NULL, TRUE);
830 if (!parent_class)
832 gpointer c = ((GTypeInstance *) widget)->g_class;
833 parent_class = (GtkWidgetClass *) g_type_class_peek_parent(c);
836 draw_label_shadow((WrappedLabel *) win->label, event->region);
838 gdk_gc_set_clip_region(win->label->style->fg_gc[win->label->state],
839 event->region);
840 (parent_class->expose_event)(widget, event);
841 gdk_gc_set_clip_region(win->label->style->fg_gc[win->label->state],
842 NULL);
844 /* Stop the button effect */
845 return TRUE;
848 /* A window has been iconified -- display it on the screen */
849 static void show_icon(IconWindow *win)
851 GdkPixbuf *pixbuf;
852 GtkWidget *vbox;
854 g_return_if_fail(win->widget == NULL);
855 g_return_if_fail(win->label == NULL);
857 win->widget = gtk_button_new();
858 gtk_button_set_relief(GTK_BUTTON(win->widget), GTK_RELIEF_NONE);
859 g_signal_connect(win->widget, "expose-event",
860 G_CALLBACK(icon_expose), win);
861 vbox = gtk_vbox_new(FALSE, 0);
862 gtk_container_add(GTK_CONTAINER(win->widget), vbox);
864 pixbuf = get_image_for(win);
866 gtk_box_pack_start(GTK_BOX(vbox), simple_image_new(pixbuf),
867 FALSE, TRUE, 0);
868 g_object_unref(pixbuf);
870 win->label = wrapped_label_new(win->text, 180);
872 update_style(NULL, win, NULL);
874 gtk_box_pack_start(GTK_BOX(vbox), win->label, FALSE, TRUE, 0);
876 gtk_widget_add_events(win->widget, GDK_BUTTON1_MOTION_MASK);
877 g_signal_connect(win->widget, "button-press-event",
878 G_CALLBACK(icon_button_press), win);
879 g_signal_connect(win->widget, "motion-notify-event",
880 G_CALLBACK(icon_motion_notify), win);
881 g_signal_connect(win->widget, "released",
882 G_CALLBACK(button_released), win);
884 gtk_widget_show_all(vbox); /* So the size comes out right */
885 pinboard_add_widget(win->widget);
886 gtk_widget_show(win->widget);
889 /* A window has been destroyed/expanded -- remove its icon */
890 static void hide_icon(IconWindow *win)
892 g_return_if_fail(win->widget != NULL);
894 gtk_widget_hide(win->widget); /* Stops flicker - stupid GtkFixed! */
895 gtk_widget_destroy(win->widget);
896 win->widget = NULL;
897 win->label = NULL;
900 static void state_changed(IconWindow *win)
902 if (win->iconified)
904 get_icon_name(win);
905 show_icon(win);
907 else
908 hide_icon(win);
911 #if 0
912 /* Set the _NET_WM_ICON_GEOMETRY property, which indicates where this window
913 * will be iconified to. Should be inside a push/pop.
915 static void set_iconify_pos(IconWindow *win)
917 gint32 data[4];
919 data[0] = iconify_next_x;
920 data[1] = iconify_next_y;
921 data[2] = 100;
922 data[3] = 32;
924 XChangeProperty(gdk_display, win->xwindow,
925 gdk_x11_atom_to_xatom(xa__NET_WM_ICON_GEOMETRY),
926 XA_CARDINAL, 32, PropModeReplace, (guchar *) data, 4);
928 #endif
930 static void update_style(gpointer key, gpointer data, gpointer user_data)
932 IconWindow *win = (IconWindow *) data;
934 if (!win->widget)
935 return;
937 widget_modify_font(win->label, pinboard_font);
938 gtk_widget_modify_fg(win->label, GTK_STATE_NORMAL, &pin_text_fg_col);
939 gtk_widget_modify_bg(win->label, GTK_STATE_NORMAL, &pin_text_bg_col);
940 gtk_widget_modify_fg(win->label, GTK_STATE_PRELIGHT, &pin_text_fg_col);
941 gtk_widget_modify_bg(win->label, GTK_STATE_PRELIGHT, &pin_text_bg_col);
944 /* Find out what the new window manager can do... */
945 static void update_supported(void)
947 Atom type;
948 int format;
949 gulong nitems;
950 gulong bytes_after;
951 unsigned char *data;
952 int err, result;
953 int i;
954 gboolean old_supports_hidden = wm_supports_hidden;
956 wm_supports_hidden = FALSE;
958 gdk_error_trap_push();
959 type = None;
960 result = XGetWindowProperty(gdk_display,
961 gdk_x11_get_default_root_xwindow(),
962 gdk_x11_atom_to_xatom(xa__NET_SUPPORTED),
963 0, G_MAXLONG,
964 False, XA_ATOM, &type, &format, &nitems,
965 &bytes_after, &data);
966 err = gdk_error_trap_pop();
968 if (err != Success || result != Success)
969 goto out;
971 for (i = 0; i < nitems; i++)
973 GdkAtom atom = gdk_x11_xatom_to_atom(((Atom *) data)[i]);
975 if (atom == xa__NET_WM_STATE_HIDDEN)
976 wm_supports_hidden = TRUE;
979 XFree(data);
980 out:
982 if (wm_supports_hidden != old_supports_hidden)
984 tasklist_update(TRUE);
985 tasklist_update(FALSE);