Rename panel, plugin, and plugin_class to Panel, Plugin, and
[lxde/lxpanel.git] / src / plugins / taskbar.c
1 /**
2 * Copyright (c) 2006 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23
24 #include <X11/Xlib.h>
25 #include <X11/Xutil.h>
26 //#include <X11/xpm.h>
27
28 #include <gdk-pixbuf/gdk-pixbuf.h>
29 #include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
30 #include <gdk/gdk.h>
31 #include <glib/gi18n.h>
32
33 #include "panel.h"
34 #include "misc.h"
35 #include "plugin.h"
36 #include "icon.xpm"
37 #include "gtkbar.h"
38
39 /*
40 * 2006.09.10 modified by Hong Jen Yee (PCMan) pcman.tw (AT) gmail.com
41 * Following features are added:
42 * 1. Add XUrgencyHint support. (Flashing task bar buttons, can be disabled)
43 * 2. Raise window when files get dragged over taskbar buttons.
44 * 3. Add Restore & Maximize menu items to popup menu of task bar buttons.
45 */
46
47 //#define DEBUG
48 #include "dbg.h"
49
50 struct _taskbar;
51 typedef struct _task{
52 struct _taskbar *tb;
53 Window win;
54 char *name, *iname;
55 GtkWidget *button, *label, *eb;
56 GtkWidget *image;
57
58 GdkPixbuf *pixbuf;
59
60 int refcount;
61 XClassHint ch;
62 int pos_x;
63 int width;
64 int desktop;
65 NetWMState nws;
66 NetWMWindowType nwwt;
67 guint flash_timeout;
68 unsigned int focused:1;
69 unsigned int iconified:1;
70 unsigned int urgency:1;
71 unsigned int using_netwm_icon:1;
72 unsigned int flash:1;
73 unsigned int flash_state:1;
74 } task;
75
76 typedef struct _taskbar{
77 Plugin *plug;
78 Window *wins;
79 Window topxwin;
80 int win_num;
81 GHashTable *task_list;
82 GtkWidget *bar, *menu;
83 GtkTooltips *tips;
84 GdkPixbuf *gen_pixbuf;
85 GtkStateType normal_state;
86 GtkStateType focused_state;
87 int num_tasks;
88 int vis_task_num;
89 int spacing;
90 int cur_desk;
91 task *focused;
92 task *ptk;
93 task *menutask;
94 char **desk_names;
95 int desk_namesno;
96 int desk_num;
97 guint dnd_activate;
98
99 gboolean iconsize;
100 gboolean task_width_max;
101 gboolean accept_skip_pager;// : 1;
102 gboolean show_iconified;// : 1;
103 gboolean show_mapped;// : 1;
104 gboolean show_all_desks;// : 1;
105 gboolean tooltips;// : 1;
106 gboolean icons_only;// : 1;
107 gboolean use_mouse_wheel;// : 1;
108 gboolean use_urgency_hint;// : 1;
109 } taskbar;
110
111 static gchar *taskbar_rc = "style 'taskbar-style'\n"
112 "{\n"
113 "GtkWidget::focus-line-width = 0\n"
114 "GtkWidget::focus-padding = 0\n"
115 "GtkButton::default-border = { 0, 0, 0, 0 }\n"
116 "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
117 "GtkButton::default_border = { 0, 0, 0, 0 }\n"
118 "GtkButton::default_outside_border = { 0, 0, 0, 0 }\n"
119 "}\n"
120 "widget '*.taskbar.*' style 'taskbar-style'";
121
122 static gboolean use_net_active=FALSE;
123
124 #define DRAG_ACTIVE_DELAY 1000
125 #define TASK_WIDTH_MAX 200
126 #define TASK_PADDING 4
127
128 #define ALL_WORKSPACES (0xFFFFFFFF)
129
130 static void tk_display(taskbar *tb, task *tk);
131 static void tb_propertynotify(taskbar *tb, XEvent *ev);
132 static GdkFilterReturn tb_event_filter( XEvent *, GdkEvent *, taskbar *);
133 static void taskbar_destructor(Plugin *p);
134
135 static gboolean tk_has_urgency( task* tk );
136
137 static void tk_flash_window( task *tk );
138 static void tk_unflash_window( task *tk );
139 static void tk_raise_window( task *tk, guint32 time );
140
141 static void
142 update_label_orient( GtkWidget* child, gpointer user_data );
143
144
145 #define TASK_VISIBLE(tb, tk) \
146 ((tk)->desktop == (tb)->cur_desk || (tk)->desktop == -1 /* 0xFFFFFFFF */ )
147
148 static int
149 task_visible(taskbar *tb, task *tk)
150 {
151 ENTER;
152 if (tk->desktop != -1 && !tb->show_all_desks && tk->desktop != tb->cur_desk)
153 RET(0);
154 if (tk->iconified) {
155 if (!tb->show_iconified)
156 RET(0);
157 } else {
158 if (!tb->show_mapped)
159 RET(0);
160 }
161 RET(1);
162 }
163
164 static int
165 accept_net_wm_state(NetWMState *nws, int accept_skip_pager)
166 {
167 ENTER;
168 RET(!(nws->skip_taskbar || (accept_skip_pager && nws->skip_pager)));
169 }
170
171 static int
172 accept_net_wm_window_type(NetWMWindowType *nwwt)
173 {
174 ENTER;
175 RET(!(nwwt->desktop || nwwt->dock || nwwt->splash));
176 }
177
178
179
180 inline static void
181 tk_free_names(task *tk)
182 {
183 ENTER;
184 DBG("tk->name %s\n", tk->name);
185 DBG("tk->iname %s\n", tk->iname);
186 g_free(tk->name);
187 g_free(tk->iname);
188
189 tk->name = tk->iname = NULL;
190 RET();
191 }
192
193 static void
194 tk_set_names(task *tk)
195 {
196 char *name;
197
198 ENTER;
199 tk_free_names(tk);
200
201 /*name = get_utf8_property(tk->win, a_NET_WM_VISIBLE_NAME);
202 DBG2("a_NET_WM_VISIBLE_NAME:%s\n", name);
203 if (!name) {
204 */
205 name = get_utf8_property(tk->win, a_NET_WM_NAME);
206 DBG("a_NET_WM_NAME:%s\n", name);
207 if (!name) {
208 name = get_textproperty(tk->win, XA_WM_NAME);
209 DBG("XA_WM_NAME:%s\n", name);
210 }
211
212 if (name) {
213 tk->name = g_strdup_printf(" %s ", name);
214 tk->iname = g_strdup_printf("[%s]", name);
215 g_free(name);
216 name = tk->iconified ? tk->iname : tk->name;
217 }
218 gtk_label_set_text(GTK_LABEL(tk->label), name);
219 if (tk->tb->tooltips)
220 gtk_tooltips_set_tip(tk->tb->tips, tk->button, tk->name, NULL);
221 RET();
222 }
223
224
225
226 static task *
227 find_task (taskbar * tb, Window win)
228 {
229 ENTER;
230 RET(g_hash_table_lookup(tb->task_list, &win));
231 }
232
233
234 static void
235 del_task (taskbar * tb, task *tk, int hdel)
236 {
237 ENTER;
238 DBG("deleting(%d) %08x %s\n", hdel, tk->win, tk->name);
239 if( tk->flash_timeout )
240 g_source_remove( tk->flash_timeout );
241 gtk_widget_destroy(tk->button);
242 --tb->num_tasks;
243 tk_free_names(tk);
244 if (tb->focused == tk)
245 tb->focused = NULL;
246 if (hdel)
247 g_hash_table_remove(tb->task_list, &tk->win);
248 g_free(tk);
249 RET();
250 }
251
252
253
254 static GdkColormap*
255 get_cmap (GdkPixmap *pixmap)
256 {
257 GdkColormap *cmap;
258
259 ENTER;
260 cmap = gdk_drawable_get_colormap (pixmap);
261 if (cmap)
262 g_object_ref (G_OBJECT (cmap));
263
264 if (cmap == NULL)
265 {
266 if (gdk_drawable_get_depth (pixmap) == 1)
267 {
268 /* try null cmap */
269 cmap = NULL;
270 }
271 else
272 {
273 /* Try system cmap */
274 GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (pixmap));
275 cmap = gdk_screen_get_system_colormap (screen);
276 g_object_ref (G_OBJECT (cmap));
277 }
278 }
279
280 /* Be sure we aren't going to blow up due to visual mismatch */
281 if (cmap &&
282 (gdk_colormap_get_visual (cmap)->depth !=
283 gdk_drawable_get_depth (pixmap)))
284 cmap = NULL;
285
286 RET(cmap);
287 }
288
289 /* These functions with the prefix wnck are taken from libwnck
290 * Copyright (C) 2001 Havoc Pennington
291 * slightly modified by Hong Jen Yee for LXPanel
292 */
293 void
294 _wnck_change_workspace (Screen *screen,
295 Window xwindow,
296 int new_space)
297 {
298 XEvent xev;
299
300 xev.xclient.type = ClientMessage;
301 xev.xclient.serial = 0;
302 xev.xclient.send_event = True;
303 xev.xclient.display = gdk_display;
304 xev.xclient.window = xwindow;
305 xev.xclient.message_type = a_NET_WM_DESKTOP;
306 xev.xclient.format = 32;
307 xev.xclient.data.l[0] = new_space;
308 xev.xclient.data.l[1] = 0;
309 xev.xclient.data.l[2] = 0;
310 xev.xclient.data.l[3] = 0;
311 xev.xclient.data.l[4] = 0;
312
313 XSendEvent (gdk_display,
314 RootWindowOfScreen (screen),
315 False,
316 SubstructureRedirectMask | SubstructureNotifyMask,
317 &xev);
318 }
319
320 static GdkPixbuf*
321 _wnck_gdk_pixbuf_get_from_pixmap (GdkPixbuf *dest,
322 Pixmap xpixmap,
323 int src_x,
324 int src_y,
325 int dest_x,
326 int dest_y,
327 int width,
328 int height)
329 {
330 GdkDrawable *drawable;
331 GdkPixbuf *retval;
332 GdkColormap *cmap;
333
334 ENTER;
335 retval = NULL;
336
337 drawable = gdk_xid_table_lookup (xpixmap);
338
339 if (drawable)
340 g_object_ref (G_OBJECT (drawable));
341 else
342 drawable = gdk_pixmap_foreign_new (xpixmap);
343
344 cmap = get_cmap (drawable);
345
346 /* GDK is supposed to do this but doesn't in GTK 2.0.2,
347 * fixed in 2.0.3
348 */
349 if (width < 0)
350 gdk_drawable_get_size (drawable, &width, NULL);
351 if (height < 0)
352 gdk_drawable_get_size (drawable, NULL, &height);
353
354 retval = gdk_pixbuf_get_from_drawable (dest,
355 drawable,
356 cmap,
357 src_x, src_y,
358 dest_x, dest_y,
359 width, height);
360
361 if (cmap)
362 g_object_unref (G_OBJECT (cmap));
363 g_object_unref (G_OBJECT (drawable));
364
365 RET(retval);
366 }
367
368 static GdkPixbuf*
369 apply_mask (GdkPixbuf *pixbuf,
370 GdkPixbuf *mask)
371 {
372 int w, h;
373 int i, j;
374 GdkPixbuf *with_alpha;
375 guchar *src;
376 guchar *dest;
377 int src_stride;
378 int dest_stride;
379
380 ENTER;
381 w = MIN (gdk_pixbuf_get_width (mask), gdk_pixbuf_get_width (pixbuf));
382 h = MIN (gdk_pixbuf_get_height (mask), gdk_pixbuf_get_height (pixbuf));
383
384 with_alpha = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
385
386 dest = gdk_pixbuf_get_pixels (with_alpha);
387 src = gdk_pixbuf_get_pixels (mask);
388
389 dest_stride = gdk_pixbuf_get_rowstride (with_alpha);
390 src_stride = gdk_pixbuf_get_rowstride (mask);
391
392 i = 0;
393 while (i < h)
394 {
395 j = 0;
396 while (j < w)
397 {
398 guchar *s = src + i * src_stride + j * 3;
399 guchar *d = dest + i * dest_stride + j * 4;
400
401 /* s[0] == s[1] == s[2], they are 255 if the bit was set, 0
402 * otherwise
403 */
404 if (s[0] == 0)
405 d[3] = 0; /* transparent */
406 else
407 d[3] = 255; /* opaque */
408
409 ++j;
410 }
411
412 ++i;
413 }
414
415 RET(with_alpha);
416 }
417
418
419 static GdkPixbuf *
420 get_netwm_icon(Window tkwin, int iw, int ih)
421 {
422 XWMHints *wmhints;
423 GdkPixbuf *ret = NULL;
424
425 ENTER;
426 wmhints = XGetWMHints( GDK_DISPLAY(), GDK_ROOT_WINDOW() );
427
428 /*
429 * IconPixmapHint flag indicates that wmhints->icon_pixmap contains
430 * valid data that is already in pixdata format, so we could process
431 * it and turn it into a GTK image.
432 */
433 if( wmhints && (wmhints->flags & IconPixmapHint) ) {
434 GdkPixmap *gdkPixmap;
435 GdkPixbuf *gdkPixbuf = NULL;
436 GdkColormap *colormap;
437
438 colormap = gdk_colormap_get_system();
439
440 gdkPixmap = gdk_pixmap_foreign_new(wmhints->icon_pixmap);
441 gdkPixbuf = gdk_pixbuf_get_from_drawable(
442 NULL, gdkPixmap, colormap, 0, 0, 0, 0, iw, ih );
443 ret = gdk_pixbuf_scale_simple( gdkPixbuf, 24, 24, GDK_INTERP_BILINEAR );
444 gdk_pixbuf_unref(gdkPixbuf);
445 XFree(wmhints);
446 }
447
448 RET(ret);
449 }
450
451 static GdkPixbuf *
452 get_wm_icon(Window tkwin, int iw, int ih)
453 {
454 XWMHints *hints;
455 Pixmap xpixmap = None, xmask = None;
456 Window win;
457 unsigned int w, h;
458 int sd;
459 GdkPixbuf *ret, *masked, *pixmap, *mask = NULL;
460
461 ENTER;
462 hints = (XWMHints *) get_xaproperty (tkwin, XA_WM_HINTS, XA_WM_HINTS, 0);
463 if (!hints)
464 RET(NULL);
465
466 if ((hints->flags & IconPixmapHint))
467 xpixmap = hints->icon_pixmap;
468 if ((hints->flags & IconMaskHint))
469 xmask = hints->icon_mask;
470
471 XFree(hints);
472 if (xpixmap == None)
473 RET(NULL);
474
475 if (!XGetGeometry (GDK_DISPLAY(), xpixmap, &win, &sd, &sd, &w, &h,
476 (guint *)&sd, (guint *)&sd)) {
477 LOG(LOG_WARN,"XGetGeometry failed for %x pixmap\n", (unsigned int)xpixmap);
478 RET(NULL);
479 }
480 DBG("tkwin=%x icon pixmap w=%d h=%d\n", tkwin, w, h);
481 pixmap = _wnck_gdk_pixbuf_get_from_pixmap (NULL, xpixmap, 0, 0, 0, 0, w, h);
482 if (!pixmap)
483 RET(NULL);
484 if (xmask != None && XGetGeometry (GDK_DISPLAY(), xmask,
485 &win, &sd, &sd, &w, &h, (guint *)&sd, (guint *)&sd)) {
486 mask = _wnck_gdk_pixbuf_get_from_pixmap (NULL, xmask, 0, 0, 0, 0, w, h);
487
488 if (mask) {
489 masked = apply_mask (pixmap, mask);
490 g_object_unref (G_OBJECT (pixmap));
491 g_object_unref (G_OBJECT (mask));
492 pixmap = masked;
493 }
494 }
495 if (!pixmap)
496 RET(NULL);
497 ret = gdk_pixbuf_scale_simple (pixmap, iw, ih, GDK_INTERP_TILES);
498 g_object_unref(pixmap);
499
500 RET(ret);
501 }
502
503 inline static GdkPixbuf*
504 get_generic_icon(taskbar *tb)
505 {
506 ENTER;
507 g_object_ref(tb->gen_pixbuf);
508 RET(tb->gen_pixbuf);
509 }
510
511 static void
512 tk_update_icon (taskbar *tb, task *tk, Atom a)
513 {
514 GdkPixbuf *pixbuf;
515
516 ENTER;
517 g_assert ((tb != NULL) && (tk != NULL));
518 g_return_if_fail(tk != NULL);
519
520 pixbuf = tk->pixbuf;
521 if (a == a_NET_WM_ICON || a == None) {
522 tk->pixbuf = get_netwm_icon(tk->win, tb->iconsize, tb->iconsize);
523 tk->using_netwm_icon = (tk->pixbuf != NULL);
524 }
525 if (!tk->using_netwm_icon)
526 tk->pixbuf = get_wm_icon(tk->win, tb->iconsize, tb->iconsize);
527 if (!tk->pixbuf)
528 tk->pixbuf = get_generic_icon(tb); // always exists
529 if (pixbuf != tk->pixbuf) {
530 if (pixbuf)
531 g_object_unref(pixbuf);
532 }
533 RET();
534 }
535
536 static gboolean on_flash_win( task *tk )
537 {
538 tk->flash_state = !tk->flash_state;
539 gtk_widget_set_state(tk->button,
540 tk->flash_state ? GTK_STATE_SELECTED : tk->tb->normal_state);
541 gtk_widget_queue_draw(tk->button);
542 return TRUE;
543 }
544
545 static void
546 tk_flash_window( task *tk )
547 {
548 gint interval;
549 tk->flash = 1;
550 tk->flash_state = !tk->flash_state;
551 if (tk->flash_timeout)
552 return;
553 g_object_get( gtk_widget_get_settings(tk->button),
554 "gtk-cursor-blink-time", &interval, NULL );
555 tk->flash_timeout = g_timeout_add(interval, (GSourceFunc)on_flash_win, tk);
556 }
557
558 static void
559 tk_unflash_window( task *tk )
560 {
561 tk->flash = tk->flash_state = 0;
562 if (tk->flash_timeout) {
563 g_source_remove(tk->flash_timeout);
564 tk->flash_timeout = 0;
565 }
566 }
567
568 static void
569 tk_raise_window( task *tk, guint32 time )
570 {
571 if (tk->desktop != -1 && tk->desktop != tk->tb->cur_desk){
572 Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, tk->desktop, 0, 0, 0, 0);
573 XSync (gdk_display, False);
574 }
575 if(use_net_active) {
576 Xclimsg(tk->win, a_NET_ACTIVE_WINDOW, 2, time, 0, 0, 0);
577 }
578 else {
579 XRaiseWindow (GDK_DISPLAY(), tk->win);
580 XSetInputFocus (GDK_DISPLAY(), tk->win, RevertToNone, CurrentTime);
581 }
582 DBG("XRaiseWindow %x\n", tk->win);
583 }
584
585 static void
586 tk_callback_leave( GtkWidget *widget, task *tk)
587 {
588 ENTER;
589 gtk_widget_set_state(widget,
590 (tk->focused) ? tk->tb->focused_state : tk->tb->normal_state);
591 RET();
592 }
593
594
595 static void
596 tk_callback_enter( GtkWidget *widget, task *tk )
597 {
598 ENTER;
599 gtk_widget_set_state(widget,
600 (tk->focused) ? tk->tb->focused_state : tk->tb->normal_state);
601 RET();
602 }
603
604 static gboolean delay_active_win(task* tk)
605 {
606 tk_raise_window(tk, CurrentTime);
607 tk->tb->dnd_activate = 0;
608 return FALSE;
609 }
610
611 static gboolean
612 tk_callback_drag_motion( GtkWidget *widget,
613 GdkDragContext *drag_context,
614 gint x, gint y,
615 guint time, task *tk)
616 {
617 /* prevent excessive motion notification */
618 if (!tk->tb->dnd_activate) {
619 tk->tb->dnd_activate = g_timeout_add(DRAG_ACTIVE_DELAY,
620 (GSourceFunc)delay_active_win, tk);
621 }
622 gdk_drag_status (drag_context,0,time);
623 return TRUE;
624 }
625
626 static void
627 tk_callback_drag_leave (GtkWidget *widget,
628 GdkDragContext *drag_context,
629 guint time, task *tk)
630 {
631 if (tk->tb->dnd_activate) {
632 g_source_remove(tk->tb->dnd_activate);
633 tk->tb->dnd_activate = 0;
634 }
635 return;
636 }
637
638 #if 0
639 static gboolean
640 tk_callback_expose(GtkWidget *widget, GdkEventExpose *event, task *tk)
641 {
642 GtkStateType state;
643 ENTER;
644 state = (tk->focused) ? tk->tb->focused_state : tk->tb->normal_state;
645 if (GTK_WIDGET_STATE(widget) != state) {
646 gtk_widget_set_state(widget, state);
647 gtk_widget_queue_draw(widget);
648 } else {
649 if( ! tk->flash || 0 == tk->flash_state ) {
650 gtk_paint_box (widget->style, widget->window,
651 state,
652 (tk->focused) ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
653 &event->area, widget, "button",
654 widget->allocation.x, widget->allocation.y,
655 widget->allocation.width, widget->allocation.height);
656 } else {
657 gdk_draw_rectangle( widget->window,
658 widget->style->bg_gc[GTK_STATE_SELECTED],
659 TRUE, 0, 0,
660 widget->allocation.width,
661 widget->allocation.height );
662 }
663 /*
664 _gtk_button_paint(GTK_BUTTON(widget), &event->area, state,
665 (tk->focused) ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
666 "button", "buttondefault");
667 */
668 gtk_container_propagate_expose(GTK_CONTAINER(widget), GTK_BIN(widget)->child, event);
669 }
670 RET(FALSE);
671 }
672 #endif
673
674 static gint
675 tk_callback_scroll_event (GtkWidget *widget, GdkEventScroll *event, task *tk)
676 {
677 ENTER;
678 if( ! tk->tb->use_mouse_wheel )
679 return TRUE;
680 if (event->direction == GDK_SCROLL_UP) {
681 GdkWindow *gdkwindow;
682
683 gdkwindow = gdk_xid_table_lookup (tk->win);
684 if (gdkwindow)
685 gdk_window_show (gdkwindow);
686 else
687 XMapRaised (GDK_DISPLAY(), tk->win);
688 XSetInputFocus (GDK_DISPLAY(), tk->win, RevertToNone, CurrentTime);
689 DBG("XMapRaised %x\n", tk->win);
690 } else if (event->direction == GDK_SCROLL_DOWN) {
691 DBG("tb->ptk = %x\n", (tk->tb->ptk) ? tk->tb->ptk->win : 0);
692 XIconifyWindow (GDK_DISPLAY(), tk->win, DefaultScreen(GDK_DISPLAY()));
693 DBG("XIconifyWindow %x\n", tk->win);
694 }
695
696 XSync (gdk_display, False);
697 RET(TRUE);
698 }
699
700 static gboolean
701 tk_callback_button_press_event(GtkWidget *widget, GdkEventButton *event, task *tk)
702 {
703 if ((event->type != GDK_BUTTON_PRESS) || (!GTK_BUTTON(widget)->in_button)|| event->button !=3 )
704 RET(FALSE);
705
706 /*
707 XLowerWindow (GDK_DISPLAY(), tk->win);
708 DBG("XLowerWindow %x\n", tk->win);
709 */
710 tk->tb->menutask = tk;
711 gtk_menu_popup (GTK_MENU (tk->tb->menu), NULL, NULL, NULL, NULL,
712 event->button, event->time);
713 return FALSE;
714 }
715
716 static gboolean
717 tk_callback_button_release_event(GtkWidget *widget, GdkEventButton *event, task *tk)
718 {
719 XWindowAttributes xwa;
720 ENTER;
721 if ((event->type != GDK_BUTTON_RELEASE) || (!GTK_BUTTON(widget)->in_button))
722 RET(FALSE);
723 DBG("win=%x\n", tk->win);
724 if (event->button == 1) {
725 if (tk->iconified) {
726 if(use_net_active) {
727 Xclimsg(tk->win, a_NET_ACTIVE_WINDOW, 2, event->time, 0, 0, 0);
728 } else {
729 GdkWindow *gdkwindow;
730
731 gdkwindow = gdk_xid_table_lookup (tk->win);
732 if (gdkwindow)
733 gdk_window_show (gdkwindow);
734 else
735 XMapRaised (GDK_DISPLAY(), tk->win);
736 XSync (GDK_DISPLAY(), False);
737 DBG("XMapRaised %x\n", tk->win);
738 }
739 /* if window isn't on current viewport, we change viewport */
740 XGetWindowAttributes(GDK_DISPLAY(), tk->win, &xwa);
741 Xclimsg(tk->win, a_NET_DESKTOP_VIEWPORT, xwa.x, xwa.y, 0, 0, 0);
742 } else {
743 DBG("tb->ptk = %x\n", (tk->tb->ptk) ? tk->tb->ptk->win : 0);
744 if (tk->focused || tk == tk->tb->ptk) {
745 //tk->iconified = 1;
746 XIconifyWindow (GDK_DISPLAY(), tk->win, DefaultScreen(GDK_DISPLAY()));
747 DBG("XIconifyWindow %x\n", tk->win);
748 } else {
749 tk_raise_window( tk, event->time );
750 }
751 }
752 } else if (event->button == 2) {
753 Xclimsg(tk->win, a_NET_WM_STATE,
754 2 /*a_NET_WM_STATE_TOGGLE*/,
755 a_NET_WM_STATE_SHADED,
756 0, 0, 0);
757 }
758 XSync (gdk_display, False);
759 gtk_button_released(GTK_BUTTON(widget));
760 RET(FALSE);
761 }
762
763
764 static void
765 tk_update(gpointer key, task *tk, taskbar *tb)
766 {
767 ENTER;
768 g_assert ((tb != NULL) && (tk != NULL));
769 if (task_visible(tb, tk)) {
770 gtk_widget_set_state (tk->button,
771 (tk->focused) ? tb->focused_state : tb->normal_state);
772 gtk_widget_queue_draw(tk->button);
773 //_gtk_button_set_depressed(GTK_BUTTON(tk->button), tk->focused);
774 gtk_widget_show(tk->button);
775
776 if (tb->tooltips) {
777 //DBG2("tip %x %s\n", tk->win, tk->name);
778 gtk_tooltips_set_tip(tb->tips, tk->button, tk->name, NULL);
779 }
780 RET();
781 }
782 gtk_widget_hide(tk->button);
783 RET();
784 }
785
786 static void
787 tk_display(taskbar *tb, task *tk)
788 {
789 ENTER;
790 tk_update(NULL, tk, tb);
791 RET();
792 }
793
794 static void
795 tb_display(taskbar *tb)
796 {
797 ENTER;
798 if (tb->wins)
799 g_hash_table_foreach(tb->task_list, (GHFunc) tk_update, (gpointer) tb);
800 RET();
801
802 }
803
804 static void
805 tk_build_gui(taskbar *tb, task *tk)
806 {
807 GtkWidget *w1;
808
809 ENTER;
810 g_assert ((tb != NULL) && (tk != NULL));
811
812 /* NOTE
813 * 1. the extended mask is sum of taskbar and pager needs
814 * see bug [ 940441 ] pager loose track of windows
815 *
816 * Do not change event mask to gtk windows spwaned by this gtk client
817 * this breaks gtk internals */
818 if (!FBPANEL_WIN(tk->win))
819 XSelectInput (GDK_DISPLAY(), tk->win, PropertyChangeMask | StructureNotifyMask);
820
821 /* button */
822 //tk->eb = gtk_event_box_new();
823 //gtk_container_set_border_width(GTK_CONTAINER(tk->eb), 0);
824 tk->button = gtk_button_new();
825 gtk_widget_show(tk->button);
826 gtk_container_set_border_width(GTK_CONTAINER(tk->button), 0);
827 gtk_widget_add_events (tk->button, GDK_BUTTON_RELEASE_MASK );
828 g_signal_connect(G_OBJECT(tk->button), "button_press_event",
829 G_CALLBACK(tk_callback_button_press_event), (gpointer)tk);
830 g_signal_connect(G_OBJECT(tk->button), "button_release_event",
831 G_CALLBACK(tk_callback_button_release_event), (gpointer)tk);
832 g_signal_connect_after (G_OBJECT (tk->button), "leave",
833 G_CALLBACK (tk_callback_leave), (gpointer) tk);
834 g_signal_connect_after (G_OBJECT (tk->button), "enter",
835 G_CALLBACK (tk_callback_enter), (gpointer) tk);
836 #if 0
837 g_signal_connect_after (G_OBJECT (tk->button), "expose-event",
838 G_CALLBACK (tk_callback_expose), (gpointer) tk);
839 #endif
840 gtk_drag_dest_set( tk->button, 0, NULL, 0, 0);
841 g_signal_connect (G_OBJECT (tk->button), "drag-motion",
842 G_CALLBACK (tk_callback_drag_motion), (gpointer) tk);
843 g_signal_connect (G_OBJECT (tk->button), "drag-leave",
844 G_CALLBACK (tk_callback_drag_leave), (gpointer) tk);
845 g_signal_connect_after(G_OBJECT(tk->button), "scroll-event",
846 G_CALLBACK(tk_callback_scroll_event), (gpointer)tk);
847
848
849 /* pix and name */
850 w1 = tb->plug->panel->my_box_new(FALSE, 1);
851 gtk_container_set_border_width(GTK_CONTAINER(w1), 0);
852
853 /* pix */
854 //get_wmclass(tk);
855 tk_update_icon(tb, tk, None);
856 tk->image = gtk_image_new_from_pixbuf(tk->pixbuf );
857 gtk_widget_show(tk->image);
858 gtk_box_pack_start(GTK_BOX(w1), tk->image, FALSE, FALSE, 0);
859
860 /* name */
861 tk->label = gtk_label_new(tk->iconified ? tk->iname : tk->name);
862 update_label_orient( tk->label, tb->plug );
863 if (!tb->icons_only)
864 gtk_widget_show(tk->label);
865 gtk_box_pack_start(GTK_BOX(w1), tk->label, TRUE, TRUE, 0);
866 gtk_widget_show(w1);
867 gtk_container_add (GTK_CONTAINER (tk->button), w1);
868
869 //gtk_container_add (GTK_CONTAINER (tk->eb), tk->button);
870 gtk_box_pack_start(GTK_BOX(tb->bar), tk->button, FALSE, TRUE, 0);
871 GTK_WIDGET_UNSET_FLAGS (tk->button, GTK_CAN_FOCUS);
872 GTK_WIDGET_UNSET_FLAGS (tk->button, GTK_CAN_DEFAULT);
873
874 gtk_widget_show(tk->button);
875 if (!task_visible(tb, tk)) {
876 gtk_widget_hide(tk->button);
877 }
878
879 if (tk->urgency) {
880 /* Flash button for window with urgency hint */
881 tk_flash_window(tk);
882 }
883 RET();
884 }
885
886 /* tell to remove element with zero refcount */
887 static gboolean
888 tb_remove_stale_tasks(Window *win, task *tk, gpointer data)
889 {
890 ENTER;
891 if (tk->refcount-- == 0) {
892 //DBG("tb_net_list <del>: 0x%x %s\n", tk->win, tk->name);
893 del_task(tk->tb, tk, 0);
894 RET(TRUE);
895 }
896 RET(FALSE);
897 }
898
899 /*****************************************************
900 * handlers for NET actions *
901 *****************************************************/
902
903
904 static void
905 tb_net_client_list(GtkWidget *widget, taskbar *tb)
906 {
907 int i;
908 task *tk;
909
910 ENTER;
911 if (tb->wins)
912 XFree(tb->wins);
913 tb->wins = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CLIENT_LIST, XA_WINDOW, &tb->win_num);
914 if (!tb->wins)
915 RET();
916 for (i = 0; i < tb->win_num; i++) {
917 if ((tk = g_hash_table_lookup(tb->task_list, &tb->wins[i]))) {
918 ++tk->refcount;
919 } else {
920 NetWMWindowType nwwt;
921 NetWMState nws;
922
923 get_net_wm_state(tb->wins[i], &nws);
924 if (!accept_net_wm_state(&nws, tb->accept_skip_pager))
925 continue;
926 get_net_wm_window_type(tb->wins[i], &nwwt);
927 if (!accept_net_wm_window_type(&nwwt))
928 continue;
929
930 tk = g_new0(task, 1);
931 tk->refcount = 1;
932 ++tb->num_tasks;
933 tk->win = tb->wins[i];
934 tk->tb = tb;
935 tk->iconified = (get_wm_state(tk->win) == IconicState);
936 tk->desktop = get_net_wm_desktop(tk->win);
937 tk->nws = nws;
938 tk->nwwt = nwwt;
939 if( tb->use_urgency_hint && tk_has_urgency(tk)) {
940 tk->urgency = 1;
941 }
942
943 tk_build_gui(tb, tk);
944 tk_set_names(tk);
945 g_hash_table_insert(tb->task_list, &tk->win, tk);
946 DBG("adding %08x(%p) %s\n", tk->win, FBPANEL_WIN(tk->win), tk->name);
947 }
948 }
949
950 /* remove windows that arn't in the NET_CLIENT_LIST anymore */
951 g_hash_table_foreach_remove(tb->task_list, (GHRFunc) tb_remove_stale_tasks, NULL);
952 tb_display(tb);
953 RET();
954 }
955
956 static void
957 tb_net_current_desktop(GtkWidget *widget, taskbar *tb)
958 {
959 ENTER;
960 tb->cur_desk = get_net_current_desktop();
961 tb_display(tb);
962 RET();
963 }
964
965 static void
966 tb_net_number_of_desktops(GtkWidget *widget, taskbar *tb)
967 {
968 ENTER;
969 tb->desk_num = get_net_number_of_desktops();
970 tb_display(tb);
971 RET();
972 }
973
974
975 /* set new active window. if that happens to be us, then remeber
976 * current focus to use it for iconify command */
977 static void
978 tb_net_active_window(GtkWidget *widget, taskbar *tb)
979 {
980 Window *f;
981 task *ntk, *ctk;
982 int drop_old, make_new;
983
984 ENTER;
985 g_assert (tb != NULL);
986 drop_old = make_new = 0;
987 ctk = tb->focused;
988 ntk = NULL;
989 f = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_ACTIVE_WINDOW, XA_WINDOW, 0);
990 DBG("FOCUS=%x\n", f ? *f : 0);
991 if (!f) {
992 drop_old = 1;
993 tb->ptk = NULL;
994 } else {
995 if (*f == tb->topxwin) {
996 if (ctk) {
997 tb->ptk = ctk;
998 drop_old = 1;
999 }
1000 } else {
1001 tb->ptk = NULL;
1002 ntk = find_task(tb, *f);
1003 if (ntk != ctk) {
1004 drop_old = 1;
1005 make_new = 1;
1006 }
1007 }
1008 XFree(f);
1009 }
1010 if (ctk && drop_old) {
1011 ctk->focused = 0;
1012 tb->focused = NULL;
1013 tk_display(tb, ctk);
1014 DBG("old focus was dropped\n");
1015 }
1016 if (ntk && make_new) {
1017 ntk->focused = 1;
1018 tb->focused = ntk;
1019 tk_display(tb, ntk);
1020 DBG("new focus was set\n");
1021 }
1022 RET();
1023 }
1024
1025 /* For older Xlib headers */
1026 #ifndef XUrgencyHint
1027 #define XUrgencyHint (1 << 8)
1028 #endif
1029
1030 static gboolean
1031 tk_has_urgency( task* tk )
1032 {
1033 XWMHints* hints;
1034
1035 tk->urgency = 0;
1036 hints = (XWMHints *) get_xaproperty (tk->win, XA_WM_HINTS, XA_WM_HINTS, 0);
1037 if (hints) {
1038 if (hints->flags & XUrgencyHint) /* Got urgency hint */
1039 tk->urgency = 1;
1040 XFree( hints );
1041 }
1042 return tk->urgency;
1043 }
1044
1045 static void
1046 tb_propertynotify(taskbar *tb, XEvent *ev)
1047 {
1048 Atom at;
1049 Window win;
1050
1051 ENTER;
1052 DBG("win=%x\n", ev->xproperty.window);
1053
1054 /* The property is deleted */
1055 if( ((XPropertyEvent*)ev)->state == 1 )
1056 return;
1057
1058 at = ev->xproperty.atom;
1059 win = ev->xproperty.window;
1060 if (win != GDK_ROOT_WINDOW()) {
1061 task *tk = find_task(tb, win);
1062
1063 if (!tk) RET();
1064 DBG("win=%x\n", ev->xproperty.window);
1065 if (at == a_NET_WM_DESKTOP) {
1066 DBG("NET_WM_DESKTOP\n");
1067 tk->desktop = get_net_wm_desktop(win);
1068 tb_display(tb);
1069 } else if (at == XA_WM_NAME) {
1070 DBG("WM_NAME\n");
1071 tk_set_names(tk);
1072 //tk_display(tb, tk);
1073 } else if (at == XA_WM_CLASS) {
1074 DBG("WM_CLASS\n");
1075 //get_wmclass(tk);
1076 } else if (at == a_WM_STATE) {
1077 DBG("WM_STATE\n");
1078 /* iconified state changed? */
1079 tk->iconified = (get_wm_state (tk->win) == IconicState);
1080 tk_set_names(tk);
1081 //tk_display(tb, tk);
1082 } else if (at == XA_WM_HINTS) {
1083 /* some windows set their WM_HINTS icon after mapping */
1084 DBG("XA_WM_HINTS\n");
1085 //get_wmclass(tk);
1086 tk_update_icon (tb, tk, XA_WM_HINTS);
1087 gtk_image_set_from_pixbuf (GTK_IMAGE(tk->image), tk->pixbuf);
1088 if (tb->use_urgency_hint) {
1089 if (tk_has_urgency(tk)) {
1090 //tk->urgency = 1;
1091 tk_flash_window(tk);
1092 } else {
1093 //tk->urgency = 0;
1094 tk_unflash_window(tk);
1095 }
1096 }
1097 } else if (at == a_NET_WM_STATE) {
1098 NetWMState nws;
1099
1100 DBG("_NET_WM_STATE\n");
1101 get_net_wm_state(tk->win, &nws);
1102 if (!accept_net_wm_state(&nws, tb->accept_skip_pager)) {
1103 del_task(tb, tk, 1);
1104 tb_display(tb);
1105 }
1106 } else if (at == a_NET_WM_ICON) {
1107 DBG("_NET_WM_ICON\n");
1108 DBG("#0 %d\n", GDK_IS_PIXBUF (tk->pixbuf));
1109 tk_update_icon (tb, tk, a_NET_WM_ICON);
1110 DBG("#1 %d\n", GDK_IS_PIXBUF (tk->pixbuf));
1111 gtk_image_set_from_pixbuf (GTK_IMAGE(tk->image), tk->pixbuf);
1112 DBG("#2 %d\n", GDK_IS_PIXBUF (tk->pixbuf));
1113 } else if (at == a_NET_WM_WINDOW_TYPE) {
1114 NetWMWindowType nwwt;
1115
1116 DBG("_NET_WM_WINDOW_TYPE\n");
1117 get_net_wm_window_type(tk->win, &nwwt);
1118 if (!accept_net_wm_window_type(&nwwt)) {
1119 del_task(tb, tk, 1);
1120 tb_display(tb);
1121 }
1122 } else {
1123 DBG("at = %d\n", at);
1124 }
1125 }
1126 RET();
1127 }
1128
1129 static GdkFilterReturn
1130 tb_event_filter( XEvent *xev, GdkEvent *event, taskbar *tb)
1131 {
1132
1133 ENTER;
1134 //RET(GDK_FILTER_CONTINUE);
1135 g_assert(tb != NULL);
1136 if (xev->type == PropertyNotify )
1137 tb_propertynotify(tb, xev);
1138 RET(GDK_FILTER_CONTINUE);
1139 }
1140
1141 static void
1142 menu_close_window(GtkWidget *widget, taskbar *tb)
1143 {
1144 ENTER;
1145 DBG("win %x\n", tb->menutask->win);
1146 XSync (GDK_DISPLAY(), 0);
1147 //XKillClient(GDK_DISPLAY(), tb->menutask->win);
1148 Xclimsgwm(tb->menutask->win, a_WM_PROTOCOLS, a_WM_DELETE_WINDOW);
1149 XSync (GDK_DISPLAY(), 0);
1150 RET();
1151 }
1152
1153
1154 static void
1155 menu_raise_window(GtkWidget *widget, taskbar *tb)
1156 {
1157 ENTER;
1158 DBG("win %x\n", tb->menutask->win);
1159 XMapRaised(GDK_DISPLAY(), tb->menutask->win);
1160 RET();
1161 }
1162
1163
1164 static void
1165 menu_iconify_window(GtkWidget *widget, taskbar *tb)
1166 {
1167 ENTER;
1168 DBG("win %x\n", tb->menutask->win);
1169 XIconifyWindow (GDK_DISPLAY(), tb->menutask->win, DefaultScreen(GDK_DISPLAY()));
1170 RET();
1171 }
1172
1173 static void
1174 menu_restore_window(GtkWidget *widget, taskbar *tb)
1175 {
1176 GdkWindow* win;
1177 ENTER;
1178 DBG("win %x\n", tb->menutask->win);
1179 win = gdk_window_foreign_new( tb->menutask->win );
1180 gdk_window_unmaximize( win );
1181 gdk_window_unref( win );
1182 RET();
1183 }
1184
1185 static void
1186 menu_maximize_window(GtkWidget *widget, taskbar *tb)
1187 {
1188 GdkWindow* win;
1189 ENTER;
1190 DBG("win %x\n", tb->menutask->win);
1191 win = gdk_window_foreign_new( tb->menutask->win );
1192 gdk_window_maximize( win );
1193 gdk_window_unref( win );
1194 RET();
1195 }
1196
1197 static void
1198 menu_move_to_workspace( GtkWidget* mi, taskbar* tb )
1199 {
1200 GdkWindow* win;
1201 int num = GPOINTER_TO_INT( g_object_get_data( mi, "num" ) );
1202 _wnck_change_workspace( DefaultScreenOfDisplay(GDK_DISPLAY()), tb->menutask->win, num );
1203 }
1204
1205 static GtkWidget *
1206 taskbar_make_menu(taskbar *tb)
1207 {
1208 GtkWidget *mi, *menu, *workspace_menu = NULL;
1209 int i;
1210 char label[128];
1211
1212 ENTER;
1213 menu = gtk_menu_new ();
1214
1215 mi = gtk_menu_item_new_with_label (_("Raise"));
1216 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1217 g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_raise_window, tb);
1218
1219 mi = gtk_menu_item_new_with_label (_("Restore"));
1220 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1221 g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_restore_window, tb);
1222
1223 mi = gtk_menu_item_new_with_label (_("Maximize"));
1224 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1225 g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_maximize_window, tb);
1226
1227 mi = gtk_menu_item_new_with_label (_("Iconify"));
1228 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1229 g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_iconify_window, tb);
1230
1231 if( tb->desk_num > 1 )
1232 {
1233 workspace_menu = gtk_menu_new();
1234 for( i = 1; i <= tb->desk_num; ++i )
1235 {
1236 g_snprintf( label, 128, _("Workspace %d"), i);
1237 mi = gtk_menu_item_new_with_label( label );
1238 g_object_set_data( mi, "num", GINT_TO_POINTER(i - 1) );
1239 g_signal_connect( mi, "activate", G_CALLBACK(menu_move_to_workspace), tb );
1240 gtk_menu_shell_append( (GtkMenuShell*)workspace_menu, mi );
1241 }
1242 gtk_menu_shell_append( GTK_MENU_SHELL (workspace_menu),
1243 gtk_separator_menu_item_new());
1244 mi = gtk_menu_item_new_with_label(_("All workspaces"));
1245 g_object_set_data( mi, "num", GINT_TO_POINTER(ALL_WORKSPACES) );
1246 g_signal_connect( mi, "activate", G_CALLBACK(menu_move_to_workspace), tb );
1247 gtk_menu_shell_append( (GtkMenuShell*)workspace_menu, mi );
1248
1249 gtk_widget_show_all( workspace_menu );
1250
1251 mi = gtk_menu_item_new_with_label (_("Move to Workspace"));
1252 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1253
1254 gtk_menu_item_set_submenu( mi, workspace_menu );
1255 workspace_menu = mi;
1256 }
1257
1258 /* we want this item to be farest from mouse pointer */
1259 mi = gtk_menu_item_new_with_label (_("Close Window"));
1260 if (tb->plug->panel->edge == EDGE_BOTTOM)
1261 {
1262 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
1263 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), mi);
1264 }
1265 else
1266 {
1267 // gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), workspace_menu);
1268
1269 gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
1270 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1271 }
1272 g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_close_window, tb);
1273 gtk_widget_show_all (menu);
1274
1275 RET(menu);
1276 }
1277
1278
1279 static void
1280 taskbar_build_gui(Plugin *p)
1281 {
1282 taskbar *tb = (taskbar *)p->priv;
1283 GtkOrientation bo;
1284
1285 ENTER;
1286
1287 bo = (tb->plug->panel->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1288 tb->bar = gtk_bar_new(bo, tb->spacing);
1289 if (tb->icons_only) {
1290 gtk_bar_set_max_child_size(GTK_BAR(tb->bar),
1291 GTK_WIDGET(p->panel->box)->allocation.height -2);
1292 } else
1293 gtk_bar_set_max_child_size(GTK_BAR(tb->bar), tb->task_width_max);
1294 gtk_container_add (GTK_CONTAINER (p->pwid), tb->bar);
1295 gtk_widget_show(tb->bar);
1296
1297 tb->gen_pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)icon_xpm);
1298
1299 gdk_window_add_filter(NULL, (GdkFilterFunc)tb_event_filter, tb );
1300
1301 g_signal_connect (G_OBJECT (fbev), "current_desktop",
1302 G_CALLBACK (tb_net_current_desktop), (gpointer) tb);
1303 g_signal_connect (G_OBJECT (fbev), "active_window",
1304 G_CALLBACK (tb_net_active_window), (gpointer) tb);
1305 g_signal_connect (G_OBJECT (fbev), "number_of_desktops",
1306 G_CALLBACK (tb_net_number_of_desktops), (gpointer) tb);
1307 g_signal_connect (G_OBJECT (fbev), "client_list",
1308 G_CALLBACK (tb_net_client_list), (gpointer) tb);
1309
1310 tb->desk_num = get_net_number_of_desktops();
1311 tb->cur_desk = get_net_current_desktop();
1312 tb->focused = NULL;
1313
1314 /* FIXME:
1315 Can we delete the tooltip object if tooltips is not enabled?
1316 if (tb->tooltips) */
1317 tb->tips = gtk_tooltips_new();
1318 #if GLIB_CHECK_VERSION( 2, 10, 0 )
1319 g_object_ref_sink( tb->tips );
1320 #else
1321 g_object_ref( tb->tips );
1322 gtk_object_sink( tb->tips );
1323 #endif
1324 tb->menu = taskbar_make_menu(tb);
1325 gtk_container_set_border_width(GTK_CONTAINER(p->pwid), 0);
1326 gtk_widget_show_all(tb->bar);
1327 RET();
1328 }
1329
1330 void net_active_detect()
1331 {
1332 int nitens;
1333 Atom *data;
1334
1335 data = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_SUPPORTED, XA_ATOM, &nitens);
1336 if (!data)
1337 return;
1338
1339 while (nitens > 0)
1340 if(data[--nitens]==a_NET_ACTIVE_WINDOW) {
1341 use_net_active = TRUE;
1342 break;
1343 }
1344
1345 XFree(data);
1346 }
1347
1348 static int
1349 taskbar_constructor(Plugin *p, char** fp)
1350 {
1351 taskbar *tb;
1352 line s;
1353 GtkRequisition req;
1354
1355 ENTER;
1356
1357 gtk_rc_parse_string(taskbar_rc);
1358
1359 /* FIXME: Is there any better way to do this? */
1360 p->pwid = gtk_event_box_new();
1361 GTK_WIDGET_SET_FLAGS( p->pwid, GTK_NO_WINDOW );
1362 gtk_widget_set_name(p->pwid, "taskbar");
1363
1364 get_button_spacing(&req, GTK_CONTAINER(p->pwid), "");
1365
1366 net_active_detect();
1367
1368 tb = g_new0(taskbar, 1);
1369 tb->plug = p;
1370 p->priv = tb;
1371
1372 if (p->panel->orientation == ORIENT_HORIZ) {
1373 tb->iconsize = GTK_WIDGET(p->panel->box)->allocation.height - req.height;
1374 DBG("pwid height = %d\n", GTK_WIDGET(p->pwid)->allocation.height);
1375 } else
1376 tb->iconsize = 24;
1377 tb->topxwin = p->panel->topxwin;
1378 tb->tooltips = 1;
1379 tb->icons_only = 0;
1380 tb->accept_skip_pager = 1;
1381 tb->show_iconified = 1;
1382 tb->show_mapped = 1;
1383 tb->show_all_desks = 0;
1384 tb->task_width_max = TASK_WIDTH_MAX;
1385 tb->task_list = g_hash_table_new(g_int_hash, g_int_equal);
1386 tb->focused_state = GTK_STATE_ACTIVE;
1387 tb->normal_state = GTK_STATE_NORMAL;
1388 tb->spacing = 1;
1389 tb->use_mouse_wheel = 1;
1390 tb->use_urgency_hint = 1;
1391 s.len = 256;
1392 if( fp )
1393 {
1394 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
1395 if (s.type == LINE_NONE) {
1396 ERR( "taskbar: illegal token %s\n", s.str);
1397 goto error;
1398 }
1399 if (s.type == LINE_VAR) {
1400 if (!g_ascii_strcasecmp(s.t[0], "tooltips")) {
1401 tb->tooltips = str2num(bool_pair, s.t[1], 1);
1402 } else if (!g_ascii_strcasecmp(s.t[0], "IconsOnly")) {
1403 tb->icons_only = str2num(bool_pair, s.t[1], 0);
1404 } else if (!g_ascii_strcasecmp(s.t[0], "AcceptSkipPager")) {
1405 tb->accept_skip_pager = str2num(bool_pair, s.t[1], 1);
1406 } else if (!g_ascii_strcasecmp(s.t[0], "ShowIconified")) {
1407 tb->show_iconified = str2num(bool_pair, s.t[1], 1);
1408 } else if (!g_ascii_strcasecmp(s.t[0], "ShowMapped")) {
1409 tb->show_mapped = str2num(bool_pair, s.t[1], 1);
1410 } else if (!g_ascii_strcasecmp(s.t[0], "ShowAllDesks")) {
1411 tb->show_all_desks = str2num(bool_pair, s.t[1], 0);
1412 } else if (!g_ascii_strcasecmp(s.t[0], "MaxTaskWidth")) {
1413 tb->task_width_max = atoi(s.t[1]);
1414 DBG("task_width_max = %d\n", tb->task_width_max);
1415 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
1416 tb->spacing = atoi(s.t[1]);
1417 } else if (!g_ascii_strcasecmp(s.t[0], "UseMouseWheel")) {
1418 tb->use_mouse_wheel = str2num(bool_pair, s.t[1], 1);
1419 } else if (!g_ascii_strcasecmp(s.t[0], "UseUrgencyHint")) {
1420 tb->use_urgency_hint = str2num(bool_pair, s.t[1], 1);
1421 } else {
1422 ERR( "taskbar: unknown var %s\n", s.t[0]);
1423 goto error;
1424 }
1425 } else {
1426 ERR( "taskbar: illegal in this context %s\n", s.str);
1427 goto error;
1428 }
1429 }
1430 }
1431 taskbar_build_gui(p);
1432 tb_net_client_list(NULL, tb);
1433 tb_display(tb);
1434 tb_net_active_window(NULL, tb);
1435 RET(1);
1436
1437 error:
1438 taskbar_destructor(p);
1439 RET(0);
1440 }
1441
1442
1443 static void
1444 taskbar_destructor(Plugin *p)
1445 {
1446 taskbar *tb = (taskbar *)p->priv;
1447
1448 ENTER;
1449 g_signal_handlers_disconnect_by_func(G_OBJECT (fbev), tb_net_current_desktop, tb);
1450 g_signal_handlers_disconnect_by_func(G_OBJECT (fbev), tb_net_active_window, tb);
1451 g_signal_handlers_disconnect_by_func(G_OBJECT (fbev), tb_net_number_of_desktops, tb);
1452 g_signal_handlers_disconnect_by_func(G_OBJECT (fbev), tb_net_client_list, tb);
1453 gdk_window_remove_filter(NULL, (GdkFilterFunc)tb_event_filter, tb );
1454 g_object_unref( tb->tips );
1455 g_hash_table_destroy(tb->task_list);
1456 /* The widget is destroyed in plugin_stop().
1457 gtk_widget_destroy(tb->bar);
1458 */
1459 gtk_widget_destroy(tb->menu);
1460 RET();
1461 }
1462
1463 static void
1464 update_icons_only( gpointer key, task* tk, gpointer icons_only )
1465 {
1466 if( icons_only )
1467 gtk_widget_hide( tk->label );
1468 else
1469 gtk_widget_show( tk->label );
1470 }
1471
1472 static void apply_config( Plugin* p )
1473 {
1474 taskbar *tb = (taskbar *)p->priv;
1475 if( tb->tooltips )
1476 gtk_tooltips_enable(tb->tips);
1477 else
1478 gtk_tooltips_disable(tb->tips);
1479 if (tb->icons_only) {
1480 gtk_bar_set_max_child_size(GTK_BAR(tb->bar),
1481 GTK_WIDGET(p->panel->box)->allocation.height -2);
1482 } else
1483 gtk_bar_set_max_child_size(GTK_BAR(tb->bar), tb->task_width_max);
1484
1485 gtk_box_set_spacing( GTK_BOX(tb->bar), tb->spacing );
1486 tb_net_client_list(NULL, tb);
1487 g_hash_table_foreach( tb->task_list,
1488 (GHFunc)update_icons_only,
1489 (gpointer)tb->icons_only );
1490 }
1491
1492 static void taskbar_config( Plugin* p, GtkWindow* parent )
1493 {
1494 GtkWidget* dlg;
1495 taskbar *tb = (taskbar *)p->priv;
1496
1497 dlg = create_generic_config_dlg(
1498 _(p->class->name),
1499 GTK_WIDGET(parent),
1500 (GSourceFunc) apply_config, (gpointer) p,
1501 _("Show tooltips"), &tb->tooltips, G_TYPE_BOOLEAN,
1502 _("Icons only"), &tb->icons_only, G_TYPE_BOOLEAN,
1503 _("Accept SkipPager"), &tb->accept_skip_pager, G_TYPE_BOOLEAN,
1504 _("Show Iconified windows"), &tb->show_iconified, G_TYPE_BOOLEAN,
1505 _("Show mapped windows"), &tb->show_mapped, G_TYPE_BOOLEAN,
1506 _("Show windows from all desktops"), &tb->show_all_desks, G_TYPE_BOOLEAN,
1507 _("Use mouse wheel"), &tb->use_mouse_wheel, G_TYPE_BOOLEAN,
1508 _("Flash when there is any window requiring attention"), &tb->use_urgency_hint, G_TYPE_BOOLEAN,
1509 _("Max width of task button"), &tb->task_width_max, G_TYPE_INT,
1510 _("Spacing"), &tb->spacing, G_TYPE_INT,
1511 NULL );
1512 gtk_window_present( GTK_WINDOW(dlg) );
1513 }
1514
1515 static void save_config( Plugin* p, FILE* fp )
1516 {
1517 taskbar *tb = (taskbar *)p->priv;
1518 lxpanel_put_bool( fp, "tooltips", tb->tooltips );
1519 lxpanel_put_bool( fp, "IconsOnly", tb->icons_only );
1520 lxpanel_put_bool( fp, "AcceptSkipPager", tb->accept_skip_pager );
1521 lxpanel_put_bool( fp, "ShowIconified", tb->show_iconified );
1522 lxpanel_put_bool( fp, "ShowMapped", tb->show_mapped );
1523 lxpanel_put_bool( fp, "ShowAllDesks", tb->show_all_desks );
1524 lxpanel_put_bool( fp, "UseMouseWheel", tb->use_mouse_wheel );
1525 lxpanel_put_bool( fp, "UseUrgencyHint", tb->use_urgency_hint );
1526 lxpanel_put_int( fp, "MaxTaskWidth", tb->task_width_max );
1527 lxpanel_put_int( fp, "spacing", tb->spacing );
1528 }
1529
1530 static void
1531 update_label_orient( GtkWidget* child, gpointer user_data )
1532 {
1533 /* FIXME: gtk+ has only limited support for this, sigh! */
1534 Plugin* p = (Plugin*)user_data;
1535 if( GTK_IS_LABEL(child) ) {
1536 gdouble angle;
1537 if( p->panel->edge == EDGE_LEFT ) {
1538 angle = 90.0;
1539 /* FIXME: ellipsize cannot be used in conjunction with angle.
1540 This is the limit of gtk+, and turn off ellipsize do
1541 cause problems here. How can this be solved? Sigh!
1542 */
1543 gtk_label_set_ellipsize( GTK_LABEL(child), PANGO_ELLIPSIZE_NONE );
1544 }
1545 else if( p->panel->edge == EDGE_RIGHT ) {
1546 angle = 270.0;
1547 gtk_label_set_ellipsize( GTK_LABEL(child), PANGO_ELLIPSIZE_NONE );
1548 }
1549 else {
1550 angle = 0.0;
1551 gtk_label_set_ellipsize( GTK_LABEL(child), PANGO_ELLIPSIZE_END );
1552 }
1553 gtk_label_set_angle( GTK_LABEL(child), angle );
1554 gtk_misc_set_alignment(GTK_MISC(child), 0.0, 0.5);
1555 }
1556 }
1557
1558 static void orientation_changed( Plugin* p )
1559 {
1560 taskbar *tb = (taskbar *)p->priv;
1561 GList *child, *children;
1562
1563 children = gtk_container_get_children( GTK_CONTAINER (tb->bar) );
1564 for( child = children; child; child = child->next ) {
1565 GtkWidget *button = GTK_WIDGET(child->data);
1566 GtkBox *box = (GtkBox*)gtk_bin_get_child( GTK_BIN(button) );
1567 GtkBox *newbox = GTK_BOX(recreate_box( box, p->panel->orientation ));
1568 if( newbox != box ) {
1569 gtk_container_add( GTK_CONTAINER(button), GTK_WIDGET(newbox) );
1570 }
1571 gtk_container_foreach( GTK_CONTAINER(newbox),
1572 update_label_orient, p );
1573 }
1574 g_list_free( children );
1575
1576 gtk_widget_destroy( tb->menu );
1577 tb->menu = taskbar_make_menu( tb );
1578
1579 gtk_bar_set_orientation( GTK_BAR(tb->bar), p->panel->orientation );
1580 }
1581
1582 PluginClass taskbar_plugin_class = {
1583 fname: NULL,
1584 count: 0,
1585
1586 type : "taskbar",
1587 name : N_("Task Bar (Window List)"),
1588 version: "1.0",
1589 description : N_("Taskbar shows all opened windows and allow to iconify them, shade or get focus"),
1590
1591 constructor : taskbar_constructor,
1592 destructor : taskbar_destructor,
1593 config : taskbar_config,
1594 save : save_config,
1595 orientation : orientation_changed
1596 };
1597