r3768: Updated years.
[rox-filer.git] / ROX-Filer / src / tasklist.c
blob267492fccf4a9c8b237552287d36350b3cb3496e
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2005, 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 g_source_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;
256 int err, result;
258 gdk_error_trap_push();
260 result = XGetWindowProperty(gdk_display, win->xwindow,
261 gdk_x11_atom_to_xatom(atom),
262 0, G_MAXLONG, False,
263 AnyPropertyType,
264 &rtype, &format, &nitems,
265 &bytes_after, &data);
267 err = gdk_error_trap_pop();
269 if (err == Success && result == Success && data)
271 if (*data)
272 str = g_strdup(data);
273 XFree(data);
276 return str;
279 static void get_icon_name(IconWindow *win)
281 null_g_free(&win->text);
283 /* Keep this list in sync with window_filter */
285 win->text = get_str(win, xa__NET_WM_ICON_NAME);
286 if (!win->text)
287 win->text = get_str(win, xa__NET_WM_VISIBLE_NAME);
288 if (!win->text)
289 win->text = get_str(win, xa_WM_ICON_NAME);
290 if (!win->text)
291 win->text = get_str(win, xa_WM_NAME);
292 if (!win->text)
293 win->text = g_strdup(_("Window"));
296 static gboolean update_title(gpointer data)
298 IconWindow *win = (IconWindow *) data;
300 if (!win->widget)
301 return FALSE; /* No longer an icon */
303 get_icon_name(win);
304 wrapped_label_set_text(WRAPPED_LABEL(win->label), win->text);
306 win->timeout_update = 0;
308 return FALSE;
311 /* Call from within error_push/pop */
312 static void window_check_status(IconWindow *win)
314 Atom type;
315 int format;
316 gulong nitems;
317 gulong bytes_after;
318 unsigned char *data;
319 gboolean iconic = FALSE;
321 if (wm_supports_hidden && XGetWindowProperty(gdk_display, win->xwindow,
322 gdk_x11_atom_to_xatom(xa__NET_WM_STATE),
323 0, G_MAXLONG, False,
324 XA_ATOM,
325 &type, &format, &nitems,
326 &bytes_after, &data) == Success && data)
328 GdkAtom state;
329 int i;
331 for (i = 0; i < nitems; i++)
333 state = gdk_x11_xatom_to_atom(((Atom *) data)[i]);
334 if (state == xa__NET_WM_STATE_HIDDEN)
336 iconic = TRUE;
337 break;
340 XFree(data);
342 else if (XGetWindowProperty(gdk_display, win->xwindow,
343 gdk_x11_atom_to_xatom(xa_WM_STATE),
344 0, 1, False,
345 gdk_x11_atom_to_xatom(xa_WM_STATE),
346 &type, &format, &nitems,
347 &bytes_after, &data) == Success && data)
349 iconic = ((guint32 *) data)[0] == 3;
350 XFree(data);
352 else
353 iconic = FALSE;
355 if (win->iconified == iconic)
356 return;
358 win->iconified = iconic;
360 state_changed(win);
362 gdk_flush();
365 /* Called for all events on all windows */
366 static GdkFilterReturn window_filter(GdkXEvent *xevent,
367 GdkEvent *event,
368 gpointer data)
370 XEvent *xev = (XEvent *) xevent;
371 IconWindow *w;
373 if (xev->type == PropertyNotify)
375 GdkAtom atom = gdk_x11_xatom_to_atom(xev->xproperty.atom);
376 Window win = ((XPropertyEvent *) xev)->window;
378 if (atom == xa_WM_STATE || atom == xa__NET_WM_STATE)
380 w = g_hash_table_lookup(known, &win);
382 if (w)
384 gdk_error_trap_push();
385 window_check_status(w);
386 if (gdk_error_trap_pop() != Success)
387 g_hash_table_remove(known, &win);
390 else if (atom == xa__NET_WM_ICON_NAME ||
391 atom == xa__NET_WM_VISIBLE_NAME ||
392 atom == xa_WM_ICON_NAME ||
393 atom == xa_WM_NAME)
395 /* Keep this list in sync with get_icon_name */
396 w = g_hash_table_lookup(known, &win);
398 if (w && w->widget && !w->timeout_update)
399 w->timeout_update = g_timeout_add(100,
400 update_title, w);
402 else if (atom == xa__NET_CLIENT_LIST)
403 tasklist_update(FALSE);
404 else if (atom == xa__NET_SUPPORTED)
405 update_supported();
408 return GDK_FILTER_CONTINUE;
411 /* Window has been added to list of managed windows */
412 static void add_window(Window win)
414 IconWindow *w;
416 /* g_print("[ New window %ld ]\n", (long) win); */
418 w = g_hash_table_lookup(known, &win);
420 if (!w)
422 XWindowAttributes attr;
424 gdk_error_trap_push();
426 XGetWindowAttributes(gdk_display, win, &attr);
428 if (gdk_error_trap_pop() != Success)
429 return;
430 gdk_error_trap_push();
432 XSelectInput(gdk_display, win, attr.your_event_mask |
433 PropertyChangeMask);
435 gdk_flush();
437 if (gdk_error_trap_pop() != Success)
438 return;
440 w = g_new(IconWindow, 1);
441 w->widget = NULL;
442 w->label = NULL;
443 w->text = NULL;
444 w->xwindow = win;
445 w->iconified = FALSE;
446 w->timeout_update = 0;
448 g_hash_table_insert(known, &w->xwindow, w);
451 gdk_error_trap_push();
453 window_check_status(w);
455 #if 0
456 set_iconify_pos(w);
457 #endif
459 if (gdk_error_trap_pop() != Success)
460 g_hash_table_remove(known, &win);
463 /* Window is no longer managed, but hasn't been destroyed yet */
464 static void remove_window(Window win)
466 IconWindow *w;
468 /* g_print("[ Remove window %ld ]\n", (long) win); */
470 w = g_hash_table_lookup(known, &win);
471 if (w)
473 if (w->iconified)
475 w->iconified = FALSE;
476 state_changed(w);
479 g_hash_table_remove(known, &win);
483 /* Make sure the window list is up-to-date. Call once to start, and then
484 * everytime _NET_CLIENT_LIST changes.
485 * If 'to_empty' is set them pretend all windows have disappeared.
487 static void tasklist_update(gboolean to_empty)
489 static GArray *old_mapping = NULL;
490 GArray *mapping = NULL;
491 int new_i, old_i;
493 if (!old_mapping)
495 old_mapping = g_array_new(FALSE, FALSE, sizeof(Window));
498 if (to_empty)
499 mapping = g_array_new(FALSE, FALSE, sizeof(Window));
500 else
501 mapping = get_window_list(gdk_x11_get_default_root_xwindow(),
502 gdk_atom_intern("_NET_CLIENT_LIST", FALSE));
504 new_i = 0;
505 old_i = 0;
506 while (new_i < mapping->len && old_i < old_mapping->len)
508 Window new = g_array_index(mapping, Window, new_i);
509 Window old = g_array_index(old_mapping, Window, old_i);
511 if (new == old)
513 new_i++;
514 old_i++;
516 else if (new < old)
518 add_window(new);
519 new_i++;
521 else
523 remove_window(old);
524 old_i++;
527 while (new_i < mapping->len)
529 add_window(g_array_index(mapping, Window, new_i));
530 new_i++;
532 while (old_i < old_mapping->len)
534 remove_window(g_array_index(old_mapping, Window, old_i));
535 old_i++;
538 g_array_free(old_mapping, TRUE);
539 old_mapping = mapping;
542 /* Called when the user clicks on the button */
543 static void uniconify(IconWindow *win)
545 XClientMessageEvent sev;
547 sev.type = ClientMessage;
548 sev.display = gdk_display;
549 sev.format = 32;
550 sev.window = win->xwindow;
551 sev.message_type = gdk_x11_atom_to_xatom(
552 gdk_atom_intern("_NET_ACTIVE_WINDOW", FALSE));
553 sev.data.l[0] = 0;
555 gdk_error_trap_push();
557 XSendEvent(gdk_display, DefaultRootWindow(gdk_display), False,
558 SubstructureNotifyMask | SubstructureRedirectMask,
559 (XEvent *) &sev);
560 XSync(gdk_display, False);
562 gdk_error_trap_pop();
565 static gint drag_start_x = -1;
566 static gint drag_start_y = -1;
567 static gboolean drag_started = FALSE;
568 static gint drag_off_x = -1;
569 static gint drag_off_y = -1;
571 static void icon_button_press(GtkWidget *widget,
572 GdkEventButton *event,
573 IconWindow *win)
575 if (event->button == 1)
577 drag_start_x = event->x_root;
578 drag_start_y = event->y_root;
579 drag_started = FALSE;
581 drag_off_x = event->x;
582 drag_off_y = event->y;
586 static gboolean icon_motion_notify(GtkWidget *widget,
587 GdkEventMotion *event,
588 IconWindow *win)
590 if (event->state & GDK_BUTTON1_MASK)
592 int dx = event->x_root - drag_start_x;
593 int dy = event->y_root - drag_start_y;
595 if (!drag_started)
597 if (abs(dx) < 5 && abs(dy) < 5)
598 return FALSE;
599 drag_started = TRUE;
602 fixed_move_fast(GTK_FIXED(win->widget->parent),
603 win->widget,
604 event->x_root - drag_off_x,
605 event->y_root - drag_off_y);
607 pinboard_moved_widget(win->widget, win->text,
608 event->x_root - drag_off_x,
609 event->y_root - drag_off_y);
612 return FALSE;
615 static void button_released(GtkWidget *widget, IconWindow *win)
617 if (!drag_started)
618 uniconify(win);
621 static GdkColormap* get_cmap(GdkPixmap *pixmap)
623 GdkColormap *cmap;
625 cmap = gdk_drawable_get_colormap(pixmap);
626 if (cmap)
627 g_object_ref(G_OBJECT(cmap));
629 if (cmap == NULL)
631 if (gdk_drawable_get_depth(pixmap) == 1)
633 /* Masks don't need colourmaps */
634 cmap = NULL;
636 else
638 /* Try system cmap */
639 cmap = gdk_colormap_get_system();
640 g_object_ref(G_OBJECT(cmap));
644 /* Be sure we aren't going to blow up due to visual mismatch */
645 if (cmap && (gdk_colormap_get_visual(cmap)->depth !=
646 gdk_drawable_get_depth(pixmap)))
647 cmap = NULL;
649 return cmap;
652 /* Copy a pixmap from the server to a client-side pixbuf */
653 static GdkPixbuf* pixbuf_from_pixmap(Pixmap xpixmap)
655 GdkDrawable *drawable;
656 GdkPixbuf *retval;
657 GdkColormap *cmap;
658 int width, height;
660 retval = NULL;
662 drawable = gdk_xid_table_lookup(xpixmap);
664 if (drawable)
665 g_object_ref(G_OBJECT(drawable));
666 else
667 drawable = gdk_pixmap_foreign_new(xpixmap);
669 cmap = get_cmap(drawable);
671 /* GDK is supposed to do this but doesn't in GTK 2.0.2,
672 * fixed in 2.0.3
674 gdk_drawable_get_size(drawable, &width, &height);
676 retval = gdk_pixbuf_get_from_drawable(NULL, drawable, cmap,
677 0, 0, 0, 0, width, height);
679 if (cmap)
680 g_object_unref(G_OBJECT(cmap));
681 g_object_unref(G_OBJECT(drawable));
683 return retval;
686 /* Creates a new masked pixbuf from a non-masked pixbuf and a mask */
687 static GdkPixbuf* apply_mask(GdkPixbuf *pixbuf, GdkPixbuf *mask)
689 int w, h;
690 int i, j;
691 GdkPixbuf *with_alpha;
692 guchar *src;
693 guchar *dest;
694 int src_stride;
695 int dest_stride;
697 w = MIN(gdk_pixbuf_get_width(mask), gdk_pixbuf_get_width(pixbuf));
698 h = MIN(gdk_pixbuf_get_height(mask), gdk_pixbuf_get_height(pixbuf));
700 with_alpha = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
702 dest = gdk_pixbuf_get_pixels(with_alpha);
703 src = gdk_pixbuf_get_pixels(mask);
705 dest_stride = gdk_pixbuf_get_rowstride(with_alpha);
706 src_stride = gdk_pixbuf_get_rowstride(mask);
708 i = 0;
709 while (i < h)
711 j = 0;
712 while (j < w)
714 guchar *s = src + i * src_stride + j * 3;
715 guchar *d = dest + i * dest_stride + j * 4;
717 /* s[0] == s[1] == s[2], they are 255 if the bit was
718 * set, 0 otherwise
720 if (s[0] == 0)
721 d[3] = 0; /* transparent */
722 else
723 d[3] = 255; /* opaque */
725 ++j;
728 ++i;
731 return with_alpha;
734 #define BORDER_X 16
735 #define BORDER_Y 8
736 /* Take the icon that the iconified window has given us and modify it to make
737 * it obvious what it is.
739 static GdkPixbuf *apply_window_effect(GdkPixbuf *src)
741 GdkPixbuf *new;
742 int w, h;
744 w = gdk_pixbuf_get_width(src);
745 h = gdk_pixbuf_get_height(src);
747 new = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(src), TRUE,
748 8, w + BORDER_X * 2, h + BORDER_Y * 2);
749 gdk_pixbuf_fill(new, 0x88888888);
751 gdk_pixbuf_composite(src, new,
752 BORDER_X, BORDER_Y, w, h,
753 BORDER_X, BORDER_Y, 1, 1,
754 GDK_INTERP_NEAREST, 255);
756 return new;
759 /* Return a suitable icon for this window. unref the result.
760 * Never returns NULL.
762 static GdkPixbuf *get_image_for(IconWindow *win)
764 static MaskedPixmap *default_icon = NULL;
765 Pixmap pixmap = None;
766 Pixmap mask = None;
767 XWMHints *hints;
768 GdkPixbuf *retval = NULL;
770 /* Try the pixmap and mask in the old WMHints... */
771 gdk_error_trap_push();
772 hints = XGetWMHints(gdk_display, win->xwindow);
774 if (hints)
776 if (hints->flags & IconPixmapHint)
777 pixmap = hints->icon_pixmap;
778 if (hints->flags & IconMaskHint)
779 mask = hints->icon_mask;
781 XFree(hints);
782 hints = NULL;
785 if (pixmap != None)
787 GdkPixbuf *mask_pb = NULL;
789 retval = pixbuf_from_pixmap(pixmap);
791 if (retval && mask != None)
792 mask_pb = pixbuf_from_pixmap(mask);
794 if (mask_pb)
796 GdkPixbuf *masked;
798 masked = apply_mask(retval, mask_pb);
799 g_object_unref(G_OBJECT(mask_pb));
801 if (masked)
803 g_object_unref(G_OBJECT(retval));
804 retval = masked;
809 gdk_flush();
811 gdk_error_trap_pop();
813 if (!retval)
815 if (!default_icon)
816 default_icon = load_pixmap("iconified");
818 retval = default_icon->pixbuf;
819 g_object_ref(retval);
822 /* Apply a special effect to make this look different from normal
823 * pinboard icons.
826 GdkPixbuf *old = retval;
827 GdkPixbuf *small;
828 small = scale_pixbuf(old, ICON_WIDTH, ICON_HEIGHT);
829 g_object_unref(old);
830 retval = apply_window_effect(small);
831 g_object_unref(small);
834 return retval;
837 /* Stop the button from highlighting */
838 static gint icon_expose(GtkWidget *widget, GdkEventExpose *event,
839 IconWindow *win)
841 static GtkWidgetClass *parent_class = NULL;
843 g_return_val_if_fail(win != NULL, TRUE);
844 g_return_val_if_fail(win->label != NULL, TRUE);
846 if (!parent_class)
848 gpointer c = ((GTypeInstance *) widget)->g_class;
849 parent_class = (GtkWidgetClass *) g_type_class_peek_parent(c);
852 draw_label_shadow((WrappedLabel *) win->label, event->region);
854 gdk_gc_set_clip_region(win->label->style->fg_gc[win->label->state],
855 event->region);
856 (parent_class->expose_event)(widget, event);
857 gdk_gc_set_clip_region(win->label->style->fg_gc[win->label->state],
858 NULL);
860 /* Stop the button effect */
861 return TRUE;
864 /* A window has been iconified -- display it on the screen */
865 static void show_icon(IconWindow *win)
867 GdkPixbuf *pixbuf;
868 GtkWidget *vbox;
870 g_return_if_fail(win->widget == NULL);
871 g_return_if_fail(win->label == NULL);
873 win->widget = gtk_button_new();
874 gtk_button_set_relief(GTK_BUTTON(win->widget), GTK_RELIEF_NONE);
875 g_signal_connect(win->widget, "expose-event",
876 G_CALLBACK(icon_expose), win);
877 vbox = gtk_vbox_new(FALSE, 0);
878 gtk_container_add(GTK_CONTAINER(win->widget), vbox);
880 pixbuf = get_image_for(win);
882 gtk_box_pack_start(GTK_BOX(vbox), simple_image_new(pixbuf),
883 FALSE, TRUE, 0);
884 g_object_unref(pixbuf);
886 win->label = wrapped_label_new(win->text, 180);
888 update_style(NULL, win, NULL);
890 gtk_box_pack_start(GTK_BOX(vbox), win->label, FALSE, TRUE, 0);
892 gtk_widget_add_events(win->widget, GDK_BUTTON1_MOTION_MASK);
893 g_signal_connect(win->widget, "button-press-event",
894 G_CALLBACK(icon_button_press), win);
895 g_signal_connect(win->widget, "motion-notify-event",
896 G_CALLBACK(icon_motion_notify), win);
897 g_signal_connect(win->widget, "released",
898 G_CALLBACK(button_released), win);
900 gtk_widget_show_all(vbox); /* So the size comes out right */
901 pinboard_add_widget(win->widget, win->text);
902 gtk_widget_show(win->widget);
905 /* A window has been destroyed/expanded -- remove its icon */
906 static void hide_icon(IconWindow *win)
908 g_return_if_fail(win->widget != NULL);
910 gtk_widget_hide(win->widget); /* Stops flicker - stupid GtkFixed! */
911 gtk_widget_destroy(win->widget);
912 win->widget = NULL;
913 win->label = NULL;
916 static void state_changed(IconWindow *win)
918 if (win->iconified)
920 get_icon_name(win);
921 show_icon(win);
923 else
924 hide_icon(win);
927 #if 0
928 /* Set the _NET_WM_ICON_GEOMETRY property, which indicates where this window
929 * will be iconified to. Should be inside a push/pop.
931 static void set_iconify_pos(IconWindow *win)
933 gint32 data[4];
935 data[0] = iconify_next_x;
936 data[1] = iconify_next_y;
937 data[2] = 100;
938 data[3] = 32;
940 XChangeProperty(gdk_display, win->xwindow,
941 gdk_x11_atom_to_xatom(xa__NET_WM_ICON_GEOMETRY),
942 XA_CARDINAL, 32, PropModeReplace, (guchar *) data, 4);
944 #endif
946 static void update_style(gpointer key, gpointer data, gpointer user_data)
948 IconWindow *win = (IconWindow *) data;
950 if (!win->widget)
951 return;
953 widget_modify_font(win->label, pinboard_font);
954 gtk_widget_modify_fg(win->label, GTK_STATE_NORMAL, &pin_text_fg_col);
955 gtk_widget_modify_bg(win->label, GTK_STATE_NORMAL, &pin_text_bg_col);
956 gtk_widget_modify_fg(win->label, GTK_STATE_PRELIGHT, &pin_text_fg_col);
957 gtk_widget_modify_bg(win->label, GTK_STATE_PRELIGHT, &pin_text_bg_col);
960 /* Find out what the new window manager can do... */
961 static void update_supported(void)
963 Atom type;
964 int format;
965 gulong nitems;
966 gulong bytes_after;
967 unsigned char *data;
968 int err, result;
969 int i;
970 gboolean old_supports_hidden = wm_supports_hidden;
972 wm_supports_hidden = FALSE;
974 gdk_error_trap_push();
975 type = None;
976 result = XGetWindowProperty(gdk_display,
977 gdk_x11_get_default_root_xwindow(),
978 gdk_x11_atom_to_xatom(xa__NET_SUPPORTED),
979 0, G_MAXLONG,
980 False, XA_ATOM, &type, &format, &nitems,
981 &bytes_after, &data);
982 err = gdk_error_trap_pop();
984 if (err != Success || result != Success)
985 goto out;
987 for (i = 0; i < nitems; i++)
989 GdkAtom atom = gdk_x11_xatom_to_atom(((Atom *) data)[i]);
991 if (atom == xa__NET_WM_STATE_HIDDEN)
992 wm_supports_hidden = TRUE;
995 XFree(data);
996 out:
998 if (wm_supports_hidden != old_supports_hidden)
1000 tasklist_update(TRUE);
1001 tasklist_update(FALSE);