2
* Copyright (c) 2006 LxDE Developers, see the file AUTHORS for details.
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.
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.
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.
25
#include <X11/Xutil.h>
27
#include <gdk-pixbuf/gdk-pixbuf.h>
28
#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
30
#include <glib/gi18n.h>
37
#include "icon-grid.h"
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.
53
/* Structure representing a class. This comes from WM_CLASS, and should identify windows that come from an application. */
54
typedef struct _task_class {
55
struct _task_class * res_class_flink; /* Forward link */
56
char * res_class; /* Class name */
57
struct _task * res_class_head; /* Head of list of tasks with this class */
58
struct _task * visible_task; /* Task that is visible in current desktop, if any */
59
char * visible_name; /* Name that will be visible for grouped tasks */
60
int visible_count; /* Count of tasks that are visible in current desktop */
63
/* Structure representing a "task", an open window. */
64
typedef struct _task {
65
struct _task * task_flink; /* Forward link to next task in X window ID order */
66
struct _taskbar * tb; /* Back pointer to taskbar */
67
Window win; /* X window ID */
68
char * name; /* Taskbar label when normal, from WM_NAME or NET_WM_NAME */
69
char * name_iconified; /* Taskbar label when iconified */
70
Atom name_source; /* Atom that is the source of taskbar label */
71
TaskClass * res_class; /* Class, from WM_CLASS */
72
struct _task * res_class_flink; /* Forward link to task in same class */
73
GtkWidget * button; /* Button representing task in taskbar */
74
GtkWidget * image; /* Icon for task, child of button */
75
Atom image_source; /* Atom that is the source of taskbar icon */
76
GtkWidget * label; /* Label for task, child of button */
77
int desktop; /* Desktop that contains task, needed to switch to it on Raise */
78
guint flash_timeout; /* Timer for urgency notification */
79
unsigned int focused : 1; /* True if window has focus */
80
unsigned int iconified : 1; /* True if window is iconified, from WM_STATE */
81
unsigned int urgency : 1; /* True if window has an urgency hint, from WM_HINTS */
82
unsigned int flash_state : 1; /* One-bit counter to flash taskbar */
83
unsigned int entered_state : 1; /* True if cursor is inside taskbar button */
84
unsigned int present_in_client_list : 1; /* State during WM_CLIENT_LIST processing to detect deletions */
87
/* Private context for taskbar plugin. */
88
typedef struct _taskbar {
89
Plugin * plug; /* Back pointer to Plugin */
90
Task * task_list; /* List of tasks to be displayed in taskbar */
91
TaskClass * res_class_list; /* Window class list */
92
IconGrid * icon_grid; /* Manager for taskbar buttons */
93
GtkWidget * menu; /* Popup menu for task control (Close, Raise, etc.) */
94
GtkWidget * group_menu; /* Popup menu for grouping selection */
95
GdkPixbuf * fallback_pixbuf; /* Fallback task icon when none is available */
96
int number_of_desktops; /* Number of desktops, from NET_WM_NUMBER_OF_DESKTOPS */
97
int current_desktop; /* Current desktop, from NET_WM_CURRENT_DESKTOP */
98
Task * focused; /* Task that has focus */
99
Task * focused_previous; /* Task that had focus just before panel got it */
100
Task * menutask; /* Task for which popup menu is open */
101
guint dnd_delay_timer; /* Timer for drag and drop delay */
102
int icon_size; /* Size of task icons */
103
gboolean show_all_desks; /* User preference: show windows from all desktops */
104
gboolean tooltips; /* User preference: show tooltips */
105
gboolean icons_only; /* User preference: show icons only, omit name */
106
gboolean use_mouse_wheel; /* User preference: scroll wheel does iconify and raise */
107
gboolean use_urgency_hint; /* User preference: windows with urgency will flash */
108
gboolean flat_button; /* User preference: taskbar buttons have visible background */
109
gboolean grouped_tasks; /* User preference: windows from same task are grouped onto a single button */
110
int task_width_max; /* Maximum width of a taskbar button in horizontal orientation */
111
int spacing; /* Spacing between taskbar buttons */
112
gboolean use_net_active; /* NET_WM_ACTIVE_WINDOW is supported by the window manager */
113
gboolean net_active_checked; /* True if use_net_active is valid */
116
static gchar *taskbar_rc = "style 'taskbar-style'\n"
118
"GtkWidget::focus-padding=0\n" /* FIXME: seem to fix #2821771, not sure if this is ok. */
119
"GtkWidget::focus-line-width=0\n"
120
"GtkWidget::focus-padding=0\n"
121
"GtkButton::default-border={0,0,0,0}\n"
122
"GtkButton::default-outside-border={0,0,0,0}\n"
123
"GtkButton::inner-border={0,0,0,0}\n" /* added in gtk+ 2.10 */
125
"widget '*.taskbar.*' style 'taskbar-style'";
127
#define DRAG_ACTIVE_DELAY 1000
128
#define TASK_WIDTH_MAX 200
129
#define ALL_WORKSPACES 0xFFFFFFFF /* 64-bit clean */
130
#define ICON_ONLY_EXTRA 6 /* Amount needed to have button lay out symmetrically */
131
#define ICON_BUTTON_TRIM 4 /* Amount needed to have button remain on panel */
133
static void set_timer_on_task(Task * tk);
134
static gboolean task_is_visible_on_current_desktop(TaskbarPlugin * tb, Task * tk);
135
static void recompute_group_visibility_for_class(TaskbarPlugin * tb, TaskClass * tc);
136
static void recompute_group_visibility_on_current_desktop(TaskbarPlugin * tb);
137
static void task_draw_label(Task * tk);
138
static gboolean task_is_visible(TaskbarPlugin * tb, Task * tk);
139
static void task_button_redraw(Task * tk, TaskbarPlugin * tb);
140
static void taskbar_redraw(TaskbarPlugin * tb);
141
static gboolean accept_net_wm_state(NetWMState * nws);
142
static gboolean accept_net_wm_window_type(NetWMWindowType * nwwt);
143
static void task_free_names(Task * tk);
144
static void task_set_names(Task * tk, Atom source);
145
static void task_unlink_class(Task * tk);
146
static TaskClass * taskbar_enter_res_class(TaskbarPlugin * tb, char * res_class, gboolean * name_consumed);
147
static void task_set_class(Task * tk);
148
static Task * task_lookup(TaskbarPlugin * tb, Window win);
149
static void task_delete(TaskbarPlugin * tb, Task * tk, gboolean unlink);
150
static GdkPixbuf * _wnck_gdk_pixbuf_get_from_pixmap(Pixmap xpixmap, int width, int height);
151
static GdkPixbuf * apply_mask(GdkPixbuf * pixbuf, GdkPixbuf * mask);
152
static GdkPixbuf * get_wm_icon(Window task_win, int required_width, int required_height, Atom source, Atom * current_source);
153
static GdkPixbuf * task_update_icon(TaskbarPlugin * tb, Task * tk, Atom source);
154
static gboolean flash_window_timeout(Task * tk);
155
static void task_set_urgency(Task * tk);
156
static void task_clear_urgency(Task * tk);
157
static void task_raise_window(Task * tk, guint32 time);
158
static void taskbar_popup_set_position(GtkWidget * menu, gint * px, gint * py, gboolean * push_in, gpointer data);
159
static void task_group_menu_destroy(TaskbarPlugin * tb);
160
static gboolean taskbar_task_control_event(GtkWidget * widget, GdkEventButton * event, Task * tk, gboolean popup_menu);
161
static gboolean taskbar_button_press_event(GtkWidget * widget, GdkEventButton * event, Task * tk);
162
static gboolean taskbar_popup_activate_event(GtkWidget * widget, GdkEventButton * event, Task * tk);
163
static gboolean taskbar_button_drag_motion_timeout(Task * tk);
164
static gboolean taskbar_button_drag_motion(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, Task * tk);
165
static void taskbar_button_drag_leave(GtkWidget * widget, GdkDragContext * drag_context, guint time, Task * tk);
166
static void taskbar_button_enter(GtkWidget * widget, Task * tk);
167
static void taskbar_button_leave(GtkWidget * widget, Task * tk);
168
static gboolean taskbar_button_scroll_event(GtkWidget * widget, GdkEventScroll * event, Task * tk);
169
static void taskbar_button_size_allocate(GtkWidget * btn, GtkAllocation * alloc, Task * tk);
170
static void taskbar_update_style(TaskbarPlugin * tb);
171
static void task_update_style(Task * tk, TaskbarPlugin * tb);
172
static void task_build_gui(TaskbarPlugin * tb, Task * tk);
173
static void taskbar_net_client_list(GtkWidget * widget, TaskbarPlugin * tb);
174
static void taskbar_net_current_desktop(GtkWidget * widget, TaskbarPlugin * tb);
175
static void taskbar_net_number_of_desktops(GtkWidget * widget, TaskbarPlugin * tb);
176
static void taskbar_net_active_window(GtkWidget * widget, TaskbarPlugin * tb);
177
static gboolean task_has_urgency(Task * tk);
178
static void taskbar_property_notify_event(TaskbarPlugin * tb, XEvent *ev);
179
static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, TaskbarPlugin * tb);
180
static void menu_raise_window(GtkWidget * widget, TaskbarPlugin * tb);
181
static void menu_restore_window(GtkWidget * widget, TaskbarPlugin * tb);
182
static void menu_maximize_window(GtkWidget * widget, TaskbarPlugin * tb);
183
static void menu_iconify_window(GtkWidget * widget, TaskbarPlugin * tb);
184
static void menu_move_to_workspace(GtkWidget * widget, TaskbarPlugin * tb);
185
static void menu_close_window(GtkWidget * widget, TaskbarPlugin * tb);
186
static void taskbar_make_menu(TaskbarPlugin * tb);
187
static void taskbar_window_manager_changed(GdkScreen * screen, TaskbarPlugin * tb);
188
static void taskbar_build_gui(Plugin * p);
189
static int taskbar_constructor(Plugin * p, char ** fp);
190
static void taskbar_destructor(Plugin * p);
191
static void taskbar_apply_configuration(Plugin * p);
192
static void taskbar_configure(Plugin * p, GtkWindow * parent);
193
static void taskbar_save_configuration(Plugin * p, FILE * fp);
194
static void taskbar_panel_configuration_changed(Plugin * p);
196
/* Set an urgency timer on a task. */
197
static void set_timer_on_task(Task * tk)
200
g_return_if_fail(tk->flash_timeout == 0);
201
g_object_get(gtk_widget_get_settings(tk->button), "gtk-cursor-blink-time", &interval, NULL);
202
tk->flash_timeout = g_timeout_add(interval, (GSourceFunc) flash_window_timeout, tk);
205
/* Determine if a task is visible considering only its desktop placement. */
206
static gboolean task_is_visible_on_current_desktop(TaskbarPlugin * tb, Task * tk)
208
return ((tk->desktop == ALL_WORKSPACES) || (tk->desktop == tb->current_desktop) || (tb->show_all_desks));
211
/* Recompute the visible task for a class when the class membership changes.
212
* Also transfer the urgency state to the visible task if necessary. */
213
static void recompute_group_visibility_for_class(TaskbarPlugin * tb, TaskClass * tc)
215
tc->visible_count = 0;
216
tc->visible_task = NULL;
217
tc->visible_name = NULL;
218
Task * flashing_task = NULL;
219
gboolean class_has_urgency = FALSE;
221
for (tk = tc->res_class_head; tk != NULL; tk = tk->res_class_flink)
223
if (task_is_visible_on_current_desktop(tb, tk))
225
/* Count visible tasks and make the first visible task the one that is used for display. */
226
if (tc->visible_count == 0)
227
tc->visible_task = tk;
228
tc->visible_count += 1;
230
/* Compute summary bit for urgency anywhere in the class. */
232
class_has_urgency = TRUE;
234
/* If there is urgency, record the currently flashing task. */
235
if (tk->flash_timeout != 0)
238
/* Compute the visible name. If all visible windows have the same title, use that.
239
* Otherwise, use the class name. This follows WNCK.
240
* Note that the visible name is not a separate string, but is set to point to one of the others. */
241
if (tc->visible_name == NULL)
242
tc->visible_name = tk->name;
243
else if ((tc->visible_name != tc->res_class)
244
&& (tc->visible_name != NULL) && (tk->name != NULL)
245
&& (strcmp(tc->visible_name, tk->name) != 0))
246
tc->visible_name = tc->res_class;
250
/* Transfer the flash timeout to the visible task. */
251
if (class_has_urgency)
253
if (flashing_task == NULL)
255
/* Set the flashing context and flash the window immediately. */
256
tc->visible_task->flash_state = TRUE;
257
flash_window_timeout(tc->visible_task);
259
/* Set the timer, since none is set. */
260
set_timer_on_task(tc->visible_task);
262
else if (flashing_task != tc->visible_task)
264
/* Reset the timer on the new representative.
265
* There will be a slight hiccup on the flash cadence. */
266
g_source_remove(flashing_task->flash_timeout);
267
flashing_task->flash_timeout = 0;
268
tc->visible_task->flash_state = flashing_task->flash_state;
269
flashing_task->flash_state = FALSE;
270
set_timer_on_task(tc->visible_task);
275
/* No task has urgency. Cancel the timer if one is set. */
276
if (flashing_task != NULL)
278
g_source_remove(flashing_task->flash_timeout);
279
flashing_task->flash_state = FALSE;
280
flashing_task->flash_timeout = 0;
285
/* Recompute the visible task for all classes when the desktop changes. */
286
static void recompute_group_visibility_on_current_desktop(TaskbarPlugin * tb)
289
for (tc = tb->res_class_list; tc != NULL; tc = tc->res_class_flink)
291
recompute_group_visibility_for_class(tb, tc);
295
/* Draw the label and tooltip on a taskbar button. */
296
static void task_draw_label(Task * tk)
298
TaskClass * tc = tk->res_class;
299
gboolean bold_style = (((tk->entered_state) || (tk->flash_state)) && (tk->tb->flat_button));
302
if ((tk->tb->grouped_tasks) && (tc != NULL) && (tc->visible_task == tk)
303
&& (tc->visible_count > 1))
305
label = g_strdup_printf("(%d) %s", tc->visible_count, tc->visible_name);
309
label = g_strdup(tk->iconified ? tk->name_iconified : tk->name);
312
if (tk->tb->tooltips)
313
gtk_widget_set_tooltip_text(tk->button, label);
315
panel_draw_label_text(tk->tb->plug->panel, tk->label, label, bold_style, 1,
316
tk->tb->flat_button);
321
/* Determine if a task is visible. */
322
static gboolean task_is_visible(TaskbarPlugin * tb, Task * tk)
324
/* Not visible due to grouping. */
325
if ((tb->grouped_tasks) && (tk->res_class != NULL) && (tk->res_class->visible_task != tk))
328
/* Desktop placement. */
329
return task_is_visible_on_current_desktop(tb, tk);
332
/* Redraw a task button. */
333
static void task_button_redraw(Task * tk, TaskbarPlugin * tb)
335
if (task_is_visible(tb, tk))
338
icon_grid_set_visible(tb->icon_grid, tk->button, TRUE);
341
icon_grid_set_visible(tb->icon_grid, tk->button, FALSE);
344
/* Redraw all tasks in the taskbar. */
345
static void taskbar_redraw(TaskbarPlugin * tb)
348
for (tk = tb->task_list; tk != NULL; tk = tk->task_flink)
349
task_button_redraw(tk, tb);
352
/* Determine if a task should be visible given its NET_WM_STATE. */
353
static gboolean accept_net_wm_state(NetWMState * nws)
355
return ( ! (nws->skip_taskbar));
358
/* Determine if a task should be visible given its NET_WM_WINDOW_TYPE. */
359
static gboolean accept_net_wm_window_type(NetWMWindowType * nwwt)
361
return ( ! ((nwwt->desktop) || (nwwt->dock) || (nwwt->splash)));
364
/* Free the names associated with a task. */
365
static void task_free_names(Task * tk)
368
g_free(tk->name_iconified);
369
tk->name = tk->name_iconified = NULL;
372
/* Set the names associated with a task.
373
* This is expected to be the same as the title the window manager is displaying. */
374
static void task_set_names(Task * tk, Atom source)
378
/* Try _NET_WM_VISIBLE_NAME, which supports UTF-8.
379
* If it is set, the window manager is displaying it as the window title. */
380
if ((source == None) || (source == a_NET_WM_VISIBLE_NAME))
382
name = get_utf8_property(tk->win, a_NET_WM_VISIBLE_NAME);
384
tk->name_source = a_NET_WM_VISIBLE_NAME;
387
/* Try _NET_WM_NAME, which supports UTF-8, but do not overwrite _NET_WM_VISIBLE_NAME. */
389
&& ((source == None) || (source == a_NET_WM_NAME))
390
&& ((tk->name_source == None) || (tk->name_source == a_NET_WM_NAME) || (tk->name_source == XA_WM_NAME)))
392
name = get_utf8_property(tk->win, a_NET_WM_NAME);
394
tk->name_source = a_NET_WM_NAME;
397
/* Try WM_NAME, which supports only ISO-8859-1, but do not overwrite _NET_WM_VISIBLE_NAME or _NET_WM_NAME. */
399
&& ((source == None) || (source == XA_WM_NAME))
400
&& ((tk->name_source == None) || (tk->name_source == XA_WM_NAME)))
402
name = get_textproperty(tk->win, XA_WM_NAME);
404
tk->name_source = XA_WM_NAME;
407
/* Set the name into the task context, and also on the tooltip. */
411
tk->name = g_strdup(name);
412
tk->name_iconified = g_strdup_printf("[%s]", name);
415
/* Redraw the button. */
416
task_button_redraw(tk, tk->tb);
420
/* Unlink a task from the class list because its class changed or it was deleted. */
421
static void task_unlink_class(Task * tk)
423
TaskClass * tc = tk->res_class;
426
/* Remove from per-class task list. */
427
if (tc->res_class_head == tk)
429
/* Removing the head of the list. This causes a new task to be the visible task, so we redraw. */
430
tc->res_class_head = tk->res_class_flink;
431
if (tc->res_class_head != NULL)
432
task_button_redraw(tc->res_class_head, tk->tb);
436
/* Locate the task and its predecessor in the list and then remove it. For safety, ensure it is found. */
437
Task * tk_pred = NULL;
440
tk_cursor = tc->res_class_head;
441
((tk_cursor != NULL) && (tk_cursor != tk));
442
tk_pred = tk_cursor, tk_cursor = tk_cursor->res_class_flink) ;
444
tk_pred->res_class_flink = tk->res_class_flink;
446
tk->res_class_flink = NULL;
448
/* Recompute group visibility. */
449
recompute_group_visibility_for_class(tk->tb, tc);
453
/* Enter class with specified name. */
454
static TaskClass * taskbar_enter_res_class(TaskbarPlugin * tb, char * res_class, gboolean * name_consumed)
456
/* Find existing entry or insertion point. */
457
*name_consumed = FALSE;
458
TaskClass * tc_pred = NULL;
460
for (tc = tb->res_class_list; tc != NULL; tc_pred = tc, tc = tc->res_class_flink)
462
int status = strcmp(res_class, tc->res_class);
469
/* Insert new entry. */
470
tc = g_new0(TaskClass, 1);
471
tc->res_class = res_class;
472
*name_consumed = TRUE;
475
tc->res_class_flink = tb->res_class_list;
476
tb->res_class_list = tc;
480
tc->res_class_flink = tc_pred->res_class_flink;
481
tc_pred->res_class_flink = tc;
486
/* Set the class associated with a task. */
487
static void task_set_class(Task * tk)
489
/* Read the WM_CLASS property. */
493
XGetClassHint(GDK_DISPLAY(), tk->win, &ch);
495
/* If the res_name was returned, free it. We make no use of it at this time. */
496
if (ch.res_name != NULL)
501
/* If the res_class was returned, process it.
502
* This identifies the application that created the window and is the basis for taskbar grouping. */
503
if (ch.res_class != NULL)
505
/* Convert the class to UTF-8 and enter it in the class table. */
506
gchar * res_class = g_locale_to_utf8(ch.res_class, -1, NULL, NULL, NULL);
507
if (res_class != NULL)
509
gboolean name_consumed;
510
TaskClass * tc = taskbar_enter_res_class(tk->tb, res_class, &name_consumed);
511
if ( ! name_consumed) g_free(res_class);
513
/* If the task changed class, update data structures. */
514
TaskClass * old_tc = tk->res_class;
517
/* Unlink from previous class, if any. */
518
task_unlink_class(tk);
520
/* Add to end of per-class task list. Do this to keep the popup menu in order of creation. */
521
if (tc->res_class_head == NULL)
522
tc->res_class_head = tk;
526
for (tk_pred = tc->res_class_head; tk_pred->res_class_flink != NULL; tk_pred = tk_pred->res_class_flink) ;
527
tk_pred->res_class_flink = tk;
528
task_button_redraw(tk, tk->tb);
532
/* Recompute group visibility. */
533
recompute_group_visibility_for_class(tk->tb, tc);
540
/* Look up a task in the task list. */
541
static Task * task_lookup(TaskbarPlugin * tb, Window win)
544
for (tk = tb->task_list; tk != NULL; tk = tk->task_flink)
554
/* Delete a task and optionally unlink it from the task list. */
555
static void task_delete(TaskbarPlugin * tb, Task * tk, gboolean unlink)
557
/* If we think this task had focus, remove that. */
558
if (tb->focused == tk)
561
/* If there is an urgency timeout, remove it. */
562
if (tk->flash_timeout != 0) {
563
g_source_remove(tk->flash_timeout);
564
tk->flash_timeout = 0;
567
/* Deallocate structures. */
568
icon_grid_remove(tb->icon_grid, tk->button);
570
task_unlink_class(tk);
572
/* If requested, unlink the task from the task list.
573
* If not requested, the caller will do this. */
576
if (tb->task_list == tk)
577
tb->task_list = tk->task_flink;
580
/* Locate the task and its predecessor in the list and then remove it. For safety, ensure it is found. */
581
Task * tk_pred = NULL;
584
tk_cursor = tb->task_list;
585
((tk_cursor != NULL) && (tk_cursor != tk));
586
tk_pred = tk_cursor, tk_cursor = tk_cursor->task_flink) ;
588
tk_pred->task_flink = tk->task_flink;
592
/* Deallocate the task structure. */
596
/* Get a pixbuf from a pixmap.
597
* Originally from libwnck, Copyright (C) 2001 Havoc Pennington. */
598
static GdkPixbuf * _wnck_gdk_pixbuf_get_from_pixmap(Pixmap xpixmap, int width, int height)
600
/* Get the drawable. */
601
GdkDrawable * drawable = gdk_xid_table_lookup(xpixmap);
602
if (drawable != NULL)
603
g_object_ref(G_OBJECT(drawable));
605
drawable = gdk_pixmap_foreign_new(xpixmap);
607
GdkColormap * colormap = NULL;
608
GdkPixbuf * retval = NULL;
609
if (drawable != NULL)
612
* If the drawable has no colormap, use no colormap or the system colormap as recommended in the documentation of gdk_drawable_get_colormap. */
613
colormap = gdk_drawable_get_colormap(drawable);
614
gint depth = gdk_drawable_get_depth(drawable);
615
if (colormap != NULL)
616
g_object_ref(G_OBJECT(colormap));
621
colormap = gdk_screen_get_system_colormap(gdk_drawable_get_screen(drawable));
622
g_object_ref(G_OBJECT(colormap));
625
/* Be sure we aren't going to fail due to visual mismatch. */
626
#if GTK_CHECK_VERSION(2,22,0)
627
if ((colormap != NULL) && (gdk_visual_get_depth(gdk_colormap_get_visual(colormap)) != depth))
629
if ((colormap != NULL) && (gdk_colormap_get_visual(colormap)->depth != depth))
632
g_object_unref(G_OBJECT(colormap));
636
/* Do the major work. */
637
retval = gdk_pixbuf_get_from_drawable(NULL, drawable, colormap, 0, 0, 0, 0, width, height);
640
/* Clean up and return. */
641
if (colormap != NULL)
642
g_object_unref(G_OBJECT(colormap));
643
if (drawable != NULL)
644
g_object_unref(G_OBJECT(drawable));
648
/* Apply a mask to a pixbuf.
649
* Originally from libwnck, Copyright (C) 2001 Havoc Pennington. */
650
static GdkPixbuf * apply_mask(GdkPixbuf * pixbuf, GdkPixbuf * mask)
653
int w = MIN(gdk_pixbuf_get_width(mask), gdk_pixbuf_get_width(pixbuf));
654
int h = MIN(gdk_pixbuf_get_height(mask), gdk_pixbuf_get_height(pixbuf));
655
GdkPixbuf * with_alpha = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
656
guchar * dst = gdk_pixbuf_get_pixels(with_alpha);
657
guchar * src = gdk_pixbuf_get_pixels(mask);
658
int dst_stride = gdk_pixbuf_get_rowstride(with_alpha);
659
int src_stride = gdk_pixbuf_get_rowstride(mask);
661
/* Loop to do the work. */
663
for (i = 0; i < h; i += 1)
666
for (j = 0; j < w; j += 1)
668
guchar * s = src + i * src_stride + j * 3;
669
guchar * d = dst + i * dst_stride + j * 4;
671
/* s[0] == s[1] == s[2], they are 255 if the bit was set, 0 otherwise. */
672
d[3] = ((s[0] == 0) ? 0 : 255); /* 0 = transparent, 255 = opaque */
679
/* Get an icon from the window manager for a task, and scale it to a specified size. */
680
static GdkPixbuf * get_wm_icon(Window task_win, int required_width, int required_height, Atom source, Atom * current_source)
683
GdkPixbuf * pixmap = NULL;
684
Atom possible_source = None;
687
if ((source == None) || (source == a_NET_WM_ICON))
690
* According to freedesktop.org document:
691
* http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html#id2552223
692
* _NET_WM_ICON contains an array of 32-bit packed CARDINAL ARGB.
693
* However, this is incorrect. Actually it's an array of long integers.
694
* Toolkits like gtk+ use unsigned long here to store icons.
695
* Besides, according to manpage of XGetWindowProperty, when returned format,
696
* is 32, the property data will be stored as an array of longs
697
* (which in a 64-bit application will be 64-bit values that are
698
* padded in the upper 4 bytes).
701
/* Get the window property _NET_WM_ICON, if possible. */
706
gulong * data = NULL;
707
result = XGetWindowProperty(
713
&type, &format, &nitems, &bytes_after, (void *) &data);
715
/* Inspect the result to see if it is usable. If not, and we got data, free it. */
716
if ((type != XA_CARDINAL) || (nitems <= 0))
723
/* If the result is usable, extract the icon from it. */
724
if (result == Success)
726
/* Get the largest icon available, unless there is one that is the desired size. */
727
/* FIXME: should we try to find an icon whose size is closest to
728
* required_width and required_height to reduce unnecessary resizing? */
729
gulong * pdata = data;
730
gulong * pdata_end = data + nitems;
731
gulong * max_icon = NULL;
734
while ((pdata + 2) < pdata_end)
736
/* Extract the width and height. */
742
/* Bounds check the icon. */
743
if (pdata + size > pdata_end)
746
/* Rare special case: the desired size is the same as icon size. */
747
if ((required_width == w) && (required_height == h))
755
/* If the icon is the largest so far, capture it. */
756
if ((w > max_w) && (h > max_h))
765
/* If an icon was extracted, convert it to a pixbuf.
766
* Its size is max_w and max_h. */
767
if (max_icon != NULL)
769
/* Allocate enough space for the pixel data. */
770
gulong len = max_w * max_h;
771
guchar * pixdata = g_new(guchar, len * 4);
773
/* Loop to convert the pixel data. */
774
guchar * p = pixdata;
776
for (i = 0; i < len; p += 4, i += 1)
778
guint argb = max_icon[i];
779
guint rgba = (argb << 8) | (argb >> 24);
781
p[1] = (rgba >> 16) & 0xff;
782
p[2] = (rgba >> 8) & 0xff;
786
/* Initialize a pixmap with the pixel data. */
787
pixmap = gdk_pixbuf_new_from_data(
790
TRUE, 8, /* has_alpha, bits_per_sample */
791
max_w, max_h, max_w * 4,
792
(GdkPixbufDestroyNotify) g_free,
794
possible_source = a_NET_WM_ICON;
799
/* Free the X property data. */
804
/* No icon available from _NET_WM_ICON. Next try WM_HINTS, but do not overwrite _NET_WM_ICON. */
805
if ((result != Success) && (*current_source != a_NET_WM_ICON)
806
&& ((source == None) || (source != a_NET_WM_ICON)))
808
XWMHints * hints = XGetWMHints(GDK_DISPLAY(), task_win);
809
result = (hints != NULL) ? Success : -1;
810
Pixmap xpixmap = None;
813
if (result == Success)
815
/* WM_HINTS is available. Extract the X pixmap and mask. */
816
if ((hints->flags & IconPixmapHint))
817
xpixmap = hints->icon_pixmap;
818
if ((hints->flags & IconMaskHint))
819
xmask = hints->icon_mask;
824
possible_source = XA_WM_HINTS;
830
if (result != Success)
832
/* No icon available from _NET_WM_ICON or WM_HINTS. Next try KWM_WIN_ICON. */
837
Pixmap *icons = NULL;
838
Atom kwin_win_icon_atom = gdk_x11_get_xatom_by_name("KWM_WIN_ICON");
839
result = XGetWindowProperty(
844
False, kwin_win_icon_atom,
845
&type, &format, &nitems, &bytes_after, (void *) &icons);
847
/* Inspect the result to see if it is usable. If not, and we got data, free it. */
848
if (type != kwin_win_icon_atom)
855
/* If the result is usable, extract the X pixmap and mask from it. */
856
if (result == Success)
863
possible_source = kwin_win_icon_atom;
870
/* If we have an X pixmap, get its geometry.*/
872
if (result == Success)
876
unsigned int unused_2;
877
result = XGetGeometry(
878
GDK_DISPLAY(), xpixmap,
879
&unused_win, &unused, &unused, &w, &h, &unused_2, &unused_2) ? Success : -1;
882
/* If we have an X pixmap and its geometry, convert it to a GDK pixmap. */
883
if (result == Success)
885
pixmap = _wnck_gdk_pixbuf_get_from_pixmap(xpixmap, w, h);
886
result = ((pixmap != NULL) ? Success : -1);
889
/* If we have success, see if the result needs to be masked.
890
* Failures here are implemented as nonfatal. */
891
if ((result == Success) && (xmask != None))
895
unsigned int unused_2;
897
GDK_DISPLAY(), xmask,
898
&unused_win, &unused, &unused, &w, &h, &unused_2, &unused_2))
900
/* Convert the X mask to a GDK pixmap. */
901
GdkPixbuf * mask = _wnck_gdk_pixbuf_get_from_pixmap(xmask, w, h);
904
/* Apply the mask. */
905
GdkPixbuf * masked_pixmap = apply_mask(pixmap, mask);
906
g_object_unref(G_OBJECT(pixmap));
907
g_object_unref(G_OBJECT(mask));
908
pixmap = masked_pixmap;
914
/* If we got a pixmap, scale it and return it. */
919
GdkPixbuf * ret = gdk_pixbuf_scale_simple(pixmap, required_width, required_height, GDK_INTERP_TILES);
920
g_object_unref(pixmap);
921
*current_source = possible_source;
926
/* Update the icon of a task. */
927
static GdkPixbuf * task_update_icon(TaskbarPlugin * tb, Task * tk, Atom source)
929
/* Get the icon from the window's hints. */
930
GdkPixbuf * pixbuf = get_wm_icon(tk->win, tb->icon_size - ICON_BUTTON_TRIM, tb->icon_size - ICON_BUTTON_TRIM, source, &tk->image_source);
932
/* If that fails, and we have no other icon yet, return the fallback icon. */
934
&& ((source == None) || (tk->image_source == None)))
936
/* Establish the fallback task icon. This is used when no other icon is available. */
937
if (tb->fallback_pixbuf == NULL)
938
tb->fallback_pixbuf = gdk_pixbuf_new_from_xpm_data((const char **) icon_xpm);
939
g_object_ref(tb->fallback_pixbuf);
940
pixbuf = tb->fallback_pixbuf;
943
/* Return what we have. This may be NULL to indicate that no change should be made to the icon. */
947
/* Timer expiration for urgency notification. Also used to draw the button in setting and clearing urgency. */
948
static gboolean flash_window_timeout(Task * tk)
950
/* Set state on the button and redraw. */
951
if ( ! tk->tb->flat_button)
952
gtk_widget_set_state(tk->button, tk->flash_state ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
955
/* Complement the flashing context. */
956
tk->flash_state = ! tk->flash_state;
960
/* Set urgency notification. */
961
static void task_set_urgency(Task * tk)
963
TaskbarPlugin * tb = tk->tb;
964
TaskClass * tc = tk->res_class;
965
if ((tb->grouped_tasks) && (tc != NULL) && (tc->visible_count > 1))
966
recompute_group_visibility_for_class(tk->tb, tk->res_class);
969
/* Set the flashing context and flash the window immediately. */
970
tk->flash_state = TRUE;
971
flash_window_timeout(tk);
973
/* Set the timer if none is set. */
974
if (tk->flash_timeout == 0)
975
set_timer_on_task(tk);
979
/* Clear urgency notification. */
980
static void task_clear_urgency(Task * tk)
982
TaskbarPlugin * tb = tk->tb;
983
TaskClass * tc = tk->res_class;
984
if ((tb->grouped_tasks) && (tc != NULL) && (tc->visible_count > 1))
985
recompute_group_visibility_for_class(tk->tb, tk->res_class);
988
/* Remove the timer if one is set. */
989
if (tk->flash_timeout != 0)
991
g_source_remove(tk->flash_timeout);
992
tk->flash_timeout = 0;
995
/* Clear the flashing context and unflash the window immediately. */
996
tk->flash_state = FALSE;
997
flash_window_timeout(tk);
998
tk->flash_state = FALSE;
1002
/* Do the proper steps to raise a window.
1003
* This means removing it from iconified state and bringing it to the front.
1004
* We also switch the active desktop and viewport if needed. */
1005
static void task_raise_window(Task * tk, guint32 time)
1007
/* Change desktop if needed. */
1008
if ((tk->desktop != ALL_WORKSPACES) && (tk->desktop != tk->tb->current_desktop))
1009
Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, tk->desktop, 0, 0, 0, 0);
1011
/* Evaluate use_net_active if not yet done. */
1012
if ( ! tk->tb->net_active_checked)
1014
TaskbarPlugin * tb = tk->tb;
1015
GdkAtom net_active_atom = gdk_x11_xatom_to_atom(a_NET_ACTIVE_WINDOW);
1016
tb->use_net_active = gdk_x11_screen_supports_net_wm_hint(gtk_widget_get_screen(tb->plug->pwid), net_active_atom);
1017
tb->net_active_checked = TRUE;
1020
/* Raise the window. We can use NET_ACTIVE_WINDOW if the window manager supports it.
1021
* Otherwise, do it the old way with XMapRaised and XSetInputFocus. */
1022
if (tk->tb->use_net_active)
1023
Xclimsg(tk->win, a_NET_ACTIVE_WINDOW, 2, time, 0, 0, 0);
1026
GdkWindow * gdkwindow = gdk_xid_table_lookup(tk->win);
1027
if (gdkwindow != NULL)
1028
gdk_window_show(gdkwindow);
1030
XMapRaised(GDK_DISPLAY(), tk->win);
1032
/* There is a race condition between the X server actually executing the XMapRaised and this code executing XSetInputFocus.
1033
* If the window is not viewable, the XSetInputFocus will fail with BadMatch. */
1034
XWindowAttributes attr;
1035
XGetWindowAttributes(GDK_DISPLAY(), tk->win, &attr);
1036
if (attr.map_state == IsViewable)
1037
XSetInputFocus(GDK_DISPLAY(), tk->win, RevertToNone, time);
1040
/* Change viewport if needed. */
1041
XWindowAttributes xwa;
1042
XGetWindowAttributes(GDK_DISPLAY(), tk->win, &xwa);
1043
Xclimsg(tk->win, a_NET_DESKTOP_VIEWPORT, xwa.x, xwa.y, 0, 0, 0);
1046
/* Position-calculation callback for grouped-task and window-management popup menu. */
1047
static void taskbar_popup_set_position(GtkWidget * menu, gint * px, gint * py, gboolean * push_in, gpointer data)
1049
Task * tk = (Task *) data;
1051
/* Get the allocation of the popup menu. */
1052
GtkRequisition popup_req;
1053
gtk_widget_size_request(menu, &popup_req);
1055
/* Determine the coordinates. */
1056
plugin_popup_set_position_helper(tk->tb->plug, tk->button, menu, &popup_req, px, py);
1060
/* Remove the grouped-task popup menu from the screen. */
1061
static void task_group_menu_destroy(TaskbarPlugin * tb)
1063
if (tb->group_menu != NULL)
1065
gtk_widget_destroy(tb->group_menu);
1066
tb->group_menu = NULL;
1070
/* Handler for "button-press-event" event from taskbar button,
1071
* or "activate" event from grouped-task popup menu item. */
1072
static gboolean taskbar_task_control_event(GtkWidget * widget, GdkEventButton * event, Task * tk, gboolean popup_menu)
1074
TaskbarPlugin * tb = tk->tb;
1075
TaskClass * tc = tk->res_class;
1076
if ((tb->grouped_tasks) && (tc != NULL) && (tc->visible_count > 1) && (GTK_IS_BUTTON(widget)))
1078
/* If this is a grouped-task representative, meaning that there is a class with at least two windows,
1079
* bring up a popup menu listing all the class members. */
1080
GtkWidget * menu = gtk_menu_new();
1082
for (tk_cursor = tc->res_class_head; tk_cursor != NULL; tk_cursor = tk_cursor->res_class_flink)
1084
if (task_is_visible_on_current_desktop(tb, tk_cursor))
1086
/* The menu item has the name, or the iconified name, and the icon of the application window. */
1087
GtkWidget * mi = gtk_image_menu_item_new_with_label(((tk_cursor->iconified) ? tk_cursor->name_iconified : tk_cursor->name));
1088
GtkWidget * im = gtk_image_new_from_pixbuf(gtk_image_get_pixbuf(GTK_IMAGE(tk_cursor->image)));
1089
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), im);
1090
g_signal_connect(mi, "button_press_event", G_CALLBACK(taskbar_popup_activate_event), (gpointer) tk_cursor);
1091
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1095
/* Show the menu. Set context so we can find the menu later to dismiss it.
1096
* Use a position-calculation callback to get the menu nicely positioned with respect to the button. */
1097
gtk_widget_show_all(menu);
1098
tb->group_menu = menu;
1099
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, (GtkMenuPositionFunc) taskbar_popup_set_position, (gpointer) tk, event->button, event->time);
1103
/* Not a grouped-task representative, or entered from the grouped-task popup menu. */
1104
Task * visible_task = (((tk->res_class == NULL) || ( ! tk->tb->grouped_tasks)) ? tk : tk->res_class->visible_task);
1105
task_group_menu_destroy(tb);
1107
if (event->button == 1)
1110
* If the task is iconified, raise it.
1111
* If the task is not iconified and has focus, iconify it.
1112
* If the task is not iconified and does not have focus, raise it. */
1114
task_raise_window(tk, event->time);
1115
else if ((tk->focused) || (tk == tb->focused_previous))
1116
XIconifyWindow(GDK_DISPLAY(), tk->win, DefaultScreen(GDK_DISPLAY()));
1118
task_raise_window(tk, event->time);
1120
else if (event->button == 2)
1122
/* Middle button. Toggle the shaded state of the window. */
1123
Xclimsg(tk->win, a_NET_WM_STATE,
1124
2, /* a_NET_WM_STATE_TOGGLE */
1125
a_NET_WM_STATE_SHADED,
1128
else if (event->button == 3)
1130
/* Right button. Bring up the window state popup menu. */
1131
tk->tb->menutask = tk;
1135
(GtkMenuPositionFunc) taskbar_popup_set_position, (gpointer) visible_task,
1136
event->button, event->time);
1140
/* As a matter of policy, avoid showing selected or prelight states on flat buttons. */
1141
if (tb->flat_button)
1142
gtk_widget_set_state(widget, GTK_STATE_NORMAL);
1146
/* Handler for "button-press-event" event from taskbar button. */
1147
static gboolean taskbar_button_press_event(GtkWidget * widget, GdkEventButton * event, Task * tk)
1149
return taskbar_task_control_event(widget, event, tk, FALSE);
1152
/* Handler for "activate" event from grouped-task popup menu item. */
1153
static gboolean taskbar_popup_activate_event(GtkWidget * widget, GdkEventButton * event, Task * tk)
1155
return taskbar_task_control_event(widget, event, tk, TRUE);
1158
/* Handler for "drag-motion" timeout. */
1159
static gboolean taskbar_button_drag_motion_timeout(Task * tk)
1161
guint time = gtk_get_current_event_time();
1162
task_raise_window(tk, ((time != 0) ? time : CurrentTime));
1163
tk->tb->dnd_delay_timer = 0;
1167
/* Handler for "drag-motion" event from taskbar button. */
1168
static gboolean taskbar_button_drag_motion(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, Task * tk)
1170
/* Prevent excessive motion notification. */
1171
if (tk->tb->dnd_delay_timer == 0)
1172
tk->tb->dnd_delay_timer = g_timeout_add(DRAG_ACTIVE_DELAY, (GSourceFunc) taskbar_button_drag_motion_timeout, tk);
1173
gdk_drag_status(drag_context, 0, time);
1177
/* Handler for "drag-leave" event from taskbar button. */
1178
static void taskbar_button_drag_leave(GtkWidget * widget, GdkDragContext * drag_context, guint time, Task * tk)
1180
/* Cancel the timer if set. */
1181
if (tk->tb->dnd_delay_timer != 0)
1183
g_source_remove(tk->tb->dnd_delay_timer);
1184
tk->tb->dnd_delay_timer = 0;
1189
/* Handler for "enter" event from taskbar button. This indicates that the cursor position has entered the button. */
1190
static void taskbar_button_enter(GtkWidget * widget, Task * tk)
1192
tk->entered_state = TRUE;
1193
if (tk->tb->flat_button)
1194
gtk_widget_set_state(widget, GTK_STATE_NORMAL);
1195
task_draw_label(tk);
1198
/* Handler for "leave" event from taskbar button. This indicates that the cursor position has left the button. */
1199
static void taskbar_button_leave(GtkWidget * widget, Task * tk)
1201
tk->entered_state = FALSE;
1202
task_draw_label(tk);
1205
/* Handler for "scroll-event" event from taskbar button. */
1206
static gboolean taskbar_button_scroll_event(GtkWidget * widget, GdkEventScroll * event, Task * tk)
1208
TaskbarPlugin * tb = tk->tb;
1209
TaskClass * tc = tk->res_class;
1210
if ((tb->use_mouse_wheel)
1211
&& (( ! tb->grouped_tasks) || (tc == NULL) || (tc->visible_count == 1)))
1213
if ((event->direction == GDK_SCROLL_UP) || (event->direction == GDK_SCROLL_LEFT))
1214
task_raise_window(tk, event->time);
1216
XIconifyWindow(GDK_DISPLAY(), tk->win, DefaultScreen(GDK_DISPLAY()));
1221
/* Handler for "size-allocate" event from taskbar button. */
1222
static void taskbar_button_size_allocate(GtkWidget * btn, GtkAllocation * alloc, Task * tk)
1224
if (GTK_WIDGET_REALIZED(btn))
1226
/* Get the coordinates of the button. */
1228
#if GTK_CHECK_VERSION(2,22,0)
1229
gdk_window_get_origin(gtk_button_get_event_window(GTK_BUTTON(btn)), &x, &y);
1231
gdk_window_get_origin(GTK_BUTTON(btn)->event_window, &x, &y);
1235
/* Send a NET_WM_ICON_GEOMETRY property change on the window. */
1239
data[2] = alloc->width;
1240
data[3] = alloc->height;
1241
XChangeProperty(GDK_DISPLAY(), tk->win,
1242
gdk_x11_get_xatom_by_name("_NET_WM_ICON_GEOMETRY"),
1243
XA_CARDINAL, 32, PropModeReplace, (guchar *) &data, 4);
1247
/* Update style on the taskbar when created or after a configuration change. */
1248
static void taskbar_update_style(TaskbarPlugin * tb)
1250
GtkOrientation bo = (tb->plug->panel->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1251
icon_grid_set_geometry(tb->icon_grid, bo,
1252
((tb->icons_only) ? tb->icon_size + ICON_ONLY_EXTRA : tb->task_width_max), tb->icon_size,
1253
tb->spacing, 0, tb->plug->panel->height);
1256
/* Update style on a task button when created or after a configuration change. */
1257
static void task_update_style(Task * tk, TaskbarPlugin * tb)
1260
gtk_widget_hide(tk->label);
1262
gtk_widget_show(tk->label);
1264
if( tb->flat_button )
1266
gtk_toggle_button_set_active((GtkToggleButton*)tk->button, FALSE);
1267
gtk_button_set_relief(GTK_BUTTON(tk->button), GTK_RELIEF_NONE);
1271
gtk_toggle_button_set_active((GtkToggleButton*)tk->button, tk->focused);
1272
gtk_button_set_relief(GTK_BUTTON(tk->button), GTK_RELIEF_NORMAL);
1275
task_draw_label(tk);
1278
/* Build graphic elements needed for a task button. */
1279
static void task_build_gui(TaskbarPlugin * tb, Task * tk)
1282
* 1. the extended mask is sum of taskbar and pager needs
1283
* see bug [ 940441 ] pager loose track of windows
1285
* Do not change event mask to gtk windows spawned by this gtk client
1286
* this breaks gtk internals */
1287
if ( ! FBPANEL_WIN(tk->win))
1288
XSelectInput(GDK_DISPLAY(), tk->win, PropertyChangeMask | StructureNotifyMask);
1290
/* Allocate a toggle button as the top level widget. */
1291
tk->button = gtk_toggle_button_new();
1292
gtk_container_set_border_width(GTK_CONTAINER(tk->button), 0);
1293
gtk_drag_dest_set(tk->button, 0, NULL, 0, 0);
1295
/* Connect signals to the button. */
1296
g_signal_connect(tk->button, "button_press_event", G_CALLBACK(taskbar_button_press_event), (gpointer) tk);
1297
g_signal_connect(G_OBJECT(tk->button), "drag-motion", G_CALLBACK(taskbar_button_drag_motion), (gpointer) tk);
1298
g_signal_connect(G_OBJECT(tk->button), "drag-leave", G_CALLBACK(taskbar_button_drag_leave), (gpointer) tk);
1299
g_signal_connect_after(G_OBJECT (tk->button), "enter", G_CALLBACK(taskbar_button_enter), (gpointer) tk);
1300
g_signal_connect_after(G_OBJECT (tk->button), "leave", G_CALLBACK(taskbar_button_leave), (gpointer) tk);
1301
g_signal_connect_after(G_OBJECT(tk->button), "scroll-event", G_CALLBACK(taskbar_button_scroll_event), (gpointer) tk);
1302
g_signal_connect(tk->button, "size-allocate", G_CALLBACK(taskbar_button_size_allocate), (gpointer) tk);
1304
/* Create a box to contain the application icon and window title. */
1305
GtkWidget * container = gtk_hbox_new(FALSE, 1);
1306
gtk_container_set_border_width(GTK_CONTAINER(container), 0);
1308
/* Create an image to contain the application icon and add it to the box. */
1309
GdkPixbuf* pixbuf = task_update_icon(tb, tk, None);
1310
tk->image = gtk_image_new_from_pixbuf(pixbuf);
1311
gtk_misc_set_padding(GTK_MISC(tk->image), 0, 0);
1312
g_object_unref(pixbuf);
1313
gtk_widget_show(tk->image);
1314
gtk_box_pack_start(GTK_BOX(container), tk->image, FALSE, FALSE, 0);
1316
/* Create a label to contain the window title and add it to the box. */
1317
tk->label = gtk_label_new(NULL);
1318
gtk_misc_set_alignment(GTK_MISC(tk->label), 0.0, 0.5);
1319
gtk_label_set_ellipsize(GTK_LABEL(tk->label), PANGO_ELLIPSIZE_END);
1320
gtk_box_pack_start(GTK_BOX(container), tk->label, TRUE, TRUE, 0);
1322
/* Add the box to the button. */
1323
gtk_widget_show(container);
1324
gtk_container_add(GTK_CONTAINER(tk->button), container);
1325
gtk_container_set_border_width(GTK_CONTAINER(tk->button), 0);
1327
/* Add the button to the taskbar. */
1328
icon_grid_add(tb->icon_grid, tk->button, TRUE);
1329
#if GTK_CHECK_VERSION(2,18,0)
1330
gtk_widget_set_can_focus(GTK_WIDGET(tk->button),FALSE);
1331
gtk_widget_set_can_default(GTK_WIDGET(tk->button),FALSE);
1333
GTK_WIDGET_UNSET_FLAGS(tk->button, GTK_CAN_FOCUS);
1334
GTK_WIDGET_UNSET_FLAGS(tk->button, GTK_CAN_DEFAULT);
1337
/* Update styles on the button. */
1338
task_update_style(tk, tb);
1340
/* Flash button for window with urgency hint. */
1342
task_set_urgency(tk);
1345
/*****************************************************
1346
* handlers for NET actions *
1347
*****************************************************/
1349
/* Handler for "client-list" event from root window listener. */
1350
static void taskbar_net_client_list(GtkWidget * widget, TaskbarPlugin * tb)
1352
/* Get the NET_CLIENT_LIST property. */
1354
Window * client_list = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_CLIENT_LIST, XA_WINDOW, &client_count);
1355
if (client_list != NULL)
1357
/* Loop over client list, correlating it with task list. */
1359
for (i = 0; i < client_count; i++)
1361
/* Search for the window in the task list. Set up context to do an insert right away if needed. */
1362
Task * tk_pred = NULL;
1365
for (tk_cursor = tb->task_list; tk_cursor != NULL; tk_pred = tk_cursor, tk_cursor = tk_cursor->task_flink)
1367
if (tk_cursor->win == client_list[i])
1372
if (tk_cursor->win > client_list[i])
1376
/* Task is already in task list. */
1378
tk->present_in_client_list = TRUE;
1380
/* Task is not in task list. */
1383
/* Evaluate window state and window type to see if it should be in task list. */
1384
NetWMWindowType nwwt;
1386
get_net_wm_state(client_list[i], &nws);
1387
get_net_wm_window_type(client_list[i], &nwwt);
1388
if ((accept_net_wm_state(&nws))
1389
&& (accept_net_wm_window_type(&nwwt)))
1391
/* Allocate and initialize new task structure. */
1392
tk = g_new0(Task, 1);
1393
tk->present_in_client_list = TRUE;
1394
tk->win = client_list[i];
1396
tk->name_source = None;
1397
tk->image_source = None;
1398
tk->iconified = (get_wm_state(tk->win) == IconicState);
1399
tk->desktop = get_net_wm_desktop(tk->win);
1400
if (tb->use_urgency_hint)
1401
tk->urgency = task_has_urgency(tk);
1402
task_build_gui(tb, tk);
1403
task_set_names(tk, None);
1406
/* Link the task structure into the task list. */
1407
if (tk_pred == NULL)
1409
tk->task_flink = tb->task_list;
1414
tk->task_flink = tk_pred->task_flink;
1415
tk_pred->task_flink = tk;
1423
/* Remove windows from the task list that are not present in the NET_CLIENT_LIST. */
1424
Task * tk_pred = NULL;
1425
Task * tk = tb->task_list;
1428
Task * tk_succ = tk->task_flink;
1429
if (tk->present_in_client_list)
1431
tk->present_in_client_list = FALSE;
1436
if (tk_pred == NULL)
1437
tb->task_list = tk_succ;
1438
else tk_pred->task_flink = tk_succ;
1439
task_delete(tb, tk, FALSE);
1444
/* Redraw the taskbar. */
1448
/* Handler for "current-desktop" event from root window listener. */
1449
static void taskbar_net_current_desktop(GtkWidget * widget, TaskbarPlugin * tb)
1451
/* Store the local copy of current desktops. Redisplay the taskbar. */
1452
tb->current_desktop = get_net_current_desktop();
1453
recompute_group_visibility_on_current_desktop(tb);
1457
/* Handler for "number-of-desktops" event from root window listener. */
1458
static void taskbar_net_number_of_desktops(GtkWidget * widget, TaskbarPlugin * tb)
1460
/* Store the local copy of number of desktops. Recompute the popup menu and redisplay the taskbar. */
1461
tb->number_of_desktops = get_net_number_of_desktops();
1462
taskbar_make_menu(tb);
1466
/* Handler for "active-window" event from root window listener. */
1467
static void taskbar_net_active_window(GtkWidget * widget, TaskbarPlugin * tb)
1469
gboolean drop_old = FALSE;
1470
gboolean make_new = FALSE;
1471
Task * ctk = tb->focused;
1474
/* Get the window that has focus. */
1475
Window * f = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_ACTIVE_WINDOW, XA_WINDOW, 0);
1478
/* No window has focus. */
1480
tb->focused_previous = NULL;
1484
if (*f == tb->plug->panel->topxwin)
1486
/* Taskbar window gained focus (this isn't supposed to be able to happen). Remember current focus. */
1489
tb->focused_previous = ctk;
1495
/* Identify task that gained focus. */
1496
tb->focused_previous = NULL;
1497
ntk = task_lookup(tb, *f);
1507
/* If our idea of the current task lost focus, update data structures. */
1508
if ((ctk != NULL) && (drop_old))
1510
ctk->focused = FALSE;
1512
if(!tb->flat_button) /* relieve the button if flat buttons is not used. */
1513
gtk_toggle_button_set_active((GtkToggleButton*)ctk->button, FALSE);
1515
task_button_redraw(ctk, tb);
1518
/* If a task gained focus, update data structures. */
1519
if ((ntk != NULL) && (make_new))
1521
if(!tb->flat_button) /* depress the button if flat buttons is not used. */
1522
gtk_toggle_button_set_active((GtkToggleButton*)ntk->button, TRUE);
1523
ntk->focused = TRUE;
1525
task_button_redraw(ntk, tb);
1529
/* Determine if the "urgency" hint is set on a window. */
1530
static gboolean task_has_urgency(Task * tk)
1532
gboolean result = FALSE;
1533
XWMHints * hints = (XWMHints *) get_xaproperty(tk->win, XA_WM_HINTS, XA_WM_HINTS, 0);
1536
if (hints->flags & XUrgencyHint)
1543
/* Handle PropertyNotify event.
1544
* http://tronche.com/gui/x/icccm/
1545
* http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html */
1546
static void taskbar_property_notify_event(TaskbarPlugin *tb, XEvent *ev)
1548
/* State may be PropertyNewValue, PropertyDeleted. */
1549
if (((XPropertyEvent*) ev)->state == PropertyNewValue)
1551
Atom at = ev->xproperty.atom;
1552
Window win = ev->xproperty.window;
1553
if (win != GDK_ROOT_WINDOW())
1555
/* Look up task structure by X window handle. */
1556
Task * tk = task_lookup(tb, win);
1559
/* Install an error handler that ignores BadWindow.
1560
* We frequently get a PropertyNotify event on deleted windows. */
1561
XErrorHandler previous_error_handler = XSetErrorHandler(panel_handle_x_error_swallow_BadWindow_BadDrawable);
1563
/* Dispatch on atom. */
1564
if (at == a_NET_WM_DESKTOP)
1566
/* Window changed desktop. */
1567
tk->desktop = get_net_wm_desktop(win);
1570
else if ((at == XA_WM_NAME) || (at == a_NET_WM_NAME) || (at == a_NET_WM_VISIBLE_NAME))
1572
/* Window changed name. */
1573
task_set_names(tk, at);
1574
if (tk->res_class != NULL)
1576
/* A change to the window name may change the visible name of the class. */
1577
recompute_group_visibility_for_class(tb, tk->res_class);
1578
if (tk->res_class->visible_task != NULL)
1579
task_draw_label(tk->res_class->visible_task);
1582
else if (at == XA_WM_CLASS)
1584
/* Window changed class. */
1588
else if (at == a_WM_STATE)
1590
/* Window changed state. */
1591
tk->iconified = (get_wm_state(win) == IconicState);
1592
task_draw_label(tk);
1594
else if (at == XA_WM_HINTS)
1596
/* Window changed "window manager hints".
1597
* Some windows set their WM_HINTS icon after mapping. */
1598
GdkPixbuf * pixbuf = task_update_icon(tb, tk, XA_WM_HINTS);
1601
gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
1602
g_object_unref(pixbuf);
1605
if (tb->use_urgency_hint)
1607
tk->urgency = task_has_urgency(tk);
1609
task_set_urgency(tk);
1611
task_clear_urgency(tk);
1614
else if (at == a_NET_WM_STATE)
1616
/* Window changed EWMH state. */
1618
get_net_wm_state(tk->win, &nws);
1619
if ( ! accept_net_wm_state(&nws))
1621
task_delete(tb, tk, TRUE);
1625
else if (at == a_NET_WM_ICON)
1627
/* Window changed EWMH icon. */
1628
GdkPixbuf * pixbuf = task_update_icon(tb, tk, a_NET_WM_ICON);
1631
gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
1632
g_object_unref(pixbuf);
1635
else if (at == a_NET_WM_WINDOW_TYPE)
1637
/* Window changed EWMH window type. */
1638
NetWMWindowType nwwt;
1639
get_net_wm_window_type(tk->win, &nwwt);
1640
if ( ! accept_net_wm_window_type(&nwwt))
1642
task_delete(tb, tk, TRUE);
1646
XSetErrorHandler(previous_error_handler);
1652
/* GDK event filter. */
1653
static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, TaskbarPlugin * tb)
1655
/* Look for PropertyNotify events and update state. */
1656
if (xev->type == PropertyNotify)
1657
taskbar_property_notify_event(tb, xev);
1658
return GDK_FILTER_CONTINUE;
1661
/* Handler for "activate" event on Raise item of right-click menu for task buttons. */
1662
static void menu_raise_window(GtkWidget * widget, TaskbarPlugin * tb)
1664
if ((tb->menutask->desktop != ALL_WORKSPACES) && (tb->menutask->desktop != tb->current_desktop))
1665
Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, tb->menutask->desktop, 0, 0, 0, 0);
1666
XMapRaised(GDK_DISPLAY(), tb->menutask->win);
1667
task_group_menu_destroy(tb);
1670
/* Handler for "activate" event on Restore item of right-click menu for task buttons. */
1671
static void menu_restore_window(GtkWidget * widget, TaskbarPlugin * tb)
1673
GdkWindow * win = gdk_window_foreign_new(tb->menutask->win);
1674
gdk_window_unmaximize(win);
1675
gdk_window_unref(win);
1676
task_group_menu_destroy(tb);
1679
/* Handler for "activate" event on Maximize item of right-click menu for task buttons. */
1680
static void menu_maximize_window(GtkWidget * widget, TaskbarPlugin * tb)
1682
GdkWindow * win = gdk_window_foreign_new(tb->menutask->win);
1683
gdk_window_maximize(win);
1684
gdk_window_unref(win);
1685
task_group_menu_destroy(tb);
1688
/* Handler for "activate" event on Iconify item of right-click menu for task buttons. */
1689
static void menu_iconify_window(GtkWidget * widget, TaskbarPlugin * tb)
1691
XIconifyWindow(GDK_DISPLAY(), tb->menutask->win, DefaultScreen(GDK_DISPLAY()));
1692
task_group_menu_destroy(tb);
1695
/* Handler for "activate" event on Move to Workspace item of right-click menu for task buttons. */
1696
static void menu_move_to_workspace(GtkWidget * widget, TaskbarPlugin * tb)
1698
int num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "num"));
1699
Xclimsg(tb->menutask->win, a_NET_WM_DESKTOP, num, 0, 0, 0, 0);
1700
task_group_menu_destroy(tb);
1703
/* Handler for "activate" event on Close item of right-click menu for task buttons. */
1704
static void menu_close_window(GtkWidget * widget, TaskbarPlugin * tb)
1706
Xclimsgwm(tb->menutask->win, a_WM_PROTOCOLS, a_WM_DELETE_WINDOW);
1707
task_group_menu_destroy(tb);
1710
/* Make right-click menu for task buttons.
1711
* This depends on number of desktops and edge. */
1712
static void taskbar_make_menu(TaskbarPlugin * tb)
1714
/* Deallocate old menu if present. */
1715
if (tb->menu != NULL)
1716
gtk_widget_destroy(tb->menu);
1718
/* Allocate menu. */
1719
GtkWidget * menu = gtk_menu_new();
1721
/* Add Raise menu item. */
1722
GtkWidget *mi = gtk_menu_item_new_with_mnemonic(_("_Raise"));
1723
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1724
g_signal_connect(G_OBJECT(mi), "activate", (GCallback) menu_raise_window, tb);
1726
/* Add Restore menu item. */
1727
mi = gtk_menu_item_new_with_mnemonic(_("R_estore"));
1728
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1729
g_signal_connect(G_OBJECT(mi), "activate", (GCallback) menu_restore_window, tb);
1731
/* Add Maximize menu item. */
1732
mi = gtk_menu_item_new_with_mnemonic(_("Ma_ximize"));
1733
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1734
g_signal_connect(G_OBJECT(mi), "activate", (GCallback) menu_maximize_window, tb);
1736
/* Add Iconify menu item. */
1737
mi = gtk_menu_item_new_with_mnemonic(_("Ico_nify"));
1738
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1739
g_signal_connect(G_OBJECT(mi), "activate", (GCallback) menu_iconify_window, tb);
1741
/* If multiple desktops are supported, add menu items to select them. */
1742
if (tb->number_of_desktops > 1)
1746
/* Allocate submenu. */
1747
GtkWidget * workspace_menu = gtk_menu_new();
1749
/* Loop over all desktops. */
1751
for (i = 1; i <= tb->number_of_desktops; i++)
1753
/* For the first 9 desktops, allow the desktop number as a keyboard shortcut. */
1756
g_snprintf(label, sizeof(label), _("Workspace _%d"), i);
1757
mi = gtk_menu_item_new_with_mnemonic(label);
1761
g_snprintf(label, sizeof(label), _("Workspace %d"), i);
1762
mi = gtk_menu_item_new_with_label(label);
1765
/* Set the desktop number as a property on the menu item. */
1766
g_object_set_data(G_OBJECT(mi), "num", GINT_TO_POINTER(i - 1));
1767
g_signal_connect(mi, "activate", G_CALLBACK(menu_move_to_workspace), tb);
1768
gtk_menu_shell_append(GTK_MENU_SHELL(workspace_menu), mi);
1771
/* Add a separator. */
1772
gtk_menu_shell_append(GTK_MENU_SHELL(workspace_menu), gtk_separator_menu_item_new());
1774
/* Add "move to all workspaces" item. This causes the window to be visible no matter what desktop is active. */
1775
mi = gtk_menu_item_new_with_mnemonic(_("_All workspaces"));
1776
g_object_set_data(G_OBJECT(mi), "num", GINT_TO_POINTER(ALL_WORKSPACES));
1777
g_signal_connect(mi, "activate", G_CALLBACK(menu_move_to_workspace), tb);
1778
gtk_menu_shell_append(GTK_MENU_SHELL(workspace_menu), mi);
1780
/* Add Move to Workspace menu item as a submenu. */
1781
mi = gtk_menu_item_new_with_mnemonic(_("_Move to Workspace"));
1782
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1783
gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), workspace_menu);
1786
/* Add Close menu item. By popular demand, we place this menu item closest to the cursor. */
1787
mi = gtk_menu_item_new_with_mnemonic (_("_Close Window"));
1788
if (tb->plug->panel->edge != EDGE_BOTTOM)
1790
gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
1791
gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
1795
gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
1796
gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1798
g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_close_window, tb);
1800
gtk_widget_show_all(menu);
1804
/* Handler for "window-manager-changed" event. */
1805
static void taskbar_window_manager_changed(GdkScreen * screen, TaskbarPlugin * tb)
1807
/* Force re-evaluation of use_net_active. */
1808
tb->net_active_checked = FALSE;
1811
/* Build graphic elements needed for the taskbar. */
1812
static void taskbar_build_gui(Plugin * p)
1814
TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
1816
/* Set up style for taskbar. */
1817
gtk_rc_parse_string(taskbar_rc);
1819
/* Allocate top level widget and set into Plugin widget pointer. */
1820
p->pwid = gtk_event_box_new();
1821
gtk_container_set_border_width(GTK_CONTAINER(p->pwid), 0);
1822
#if GTK_CHECK_VERSION(2,18,0)
1823
gtk_widget_set_has_window(GTK_WIDGET(p->pwid),FALSE);
1825
GTK_WIDGET_SET_FLAGS(p->pwid, GTK_NO_WINDOW);
1827
gtk_widget_set_name(p->pwid, "taskbar");
1829
/* Make container for task buttons as a child of top level widget. */
1830
GtkOrientation bo = (tb->plug->panel->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1831
tb->icon_grid = icon_grid_new(p->panel, p->pwid, bo, tb->task_width_max, tb->icon_size, tb->spacing, 0, p->panel->height);
1832
icon_grid_set_constrain_width(tb->icon_grid, TRUE);
1833
taskbar_update_style(tb);
1835
/* Add GDK event filter. */
1836
gdk_window_add_filter(NULL, (GdkFilterFunc) taskbar_event_filter, tb);
1838
/* Connect signal to receive mouse events on the unused portion of the taskbar. */
1839
g_signal_connect(p->pwid, "button-press-event", G_CALLBACK(plugin_button_press_event), p);
1841
/* Connect signals to receive root window events and initialize root window properties. */
1842
tb->number_of_desktops = get_net_number_of_desktops();
1843
tb->current_desktop = get_net_current_desktop();
1844
g_signal_connect(G_OBJECT(fbev), "current_desktop", G_CALLBACK(taskbar_net_current_desktop), (gpointer) tb);
1845
g_signal_connect(G_OBJECT(fbev), "active_window", G_CALLBACK(taskbar_net_active_window), (gpointer) tb);
1846
g_signal_connect(G_OBJECT(fbev), "number_of_desktops", G_CALLBACK(taskbar_net_number_of_desktops), (gpointer) tb);
1847
g_signal_connect(G_OBJECT(fbev), "client_list", G_CALLBACK(taskbar_net_client_list), (gpointer) tb);
1849
/* Make right-click menu for task buttons.
1850
* It is retained for the life of the taskbar and will be shown as needed.
1851
* Number of desktops and edge is needed for this operation. */
1852
taskbar_make_menu(tb);
1854
/* Connect a signal to be notified when the window manager changes. This causes re-evaluation of the "use_net_active" status. */
1855
g_signal_connect(gtk_widget_get_screen(p->pwid), "window-manager-changed", G_CALLBACK(taskbar_window_manager_changed), tb);
1858
/* Plugin constructor. */
1859
static int taskbar_constructor(Plugin * p, char ** fp)
1861
/* Allocate plugin context and set into Plugin private data pointer. */
1862
TaskbarPlugin * tb = g_new0(TaskbarPlugin, 1);
1866
/* Initialize to defaults. */
1867
tb->icon_size = p->panel->icon_size;
1868
tb->tooltips = TRUE;
1869
tb->icons_only = FALSE;
1870
tb->show_all_desks = FALSE;
1871
tb->task_width_max = TASK_WIDTH_MAX;
1873
tb->use_mouse_wheel = TRUE;
1874
tb->use_urgency_hint = TRUE;
1875
tb->grouped_tasks = FALSE;
1877
/* Process configuration file. */
1882
while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
1883
if (s.type == LINE_NONE) {
1884
ERR( "taskbar: illegal token %s\n", s.str);
1887
if (s.type == LINE_VAR)
1889
if (g_ascii_strcasecmp(s.t[0], "tooltips") == 0)
1890
tb->tooltips = str2num(bool_pair, s.t[1], 1);
1891
else if (g_ascii_strcasecmp(s.t[0], "IconsOnly") == 0)
1892
tb->icons_only = str2num(bool_pair, s.t[1], 0);
1893
else if (g_ascii_strcasecmp(s.t[0], "AcceptSkipPager") == 0) /* For backward compatibility */
1895
else if (g_ascii_strcasecmp(s.t[0], "ShowIconified") == 0) /* For backward compatibility */
1897
else if (g_ascii_strcasecmp(s.t[0], "ShowMapped") == 0) /* For backward compatibility */
1899
else if (g_ascii_strcasecmp(s.t[0], "ShowAllDesks") == 0)
1900
tb->show_all_desks = str2num(bool_pair, s.t[1], 0);
1901
else if (g_ascii_strcasecmp(s.t[0], "MaxTaskWidth") == 0)
1902
tb->task_width_max = atoi(s.t[1]);
1903
else if (g_ascii_strcasecmp(s.t[0], "spacing") == 0)
1904
tb->spacing = atoi(s.t[1]);
1905
else if (g_ascii_strcasecmp(s.t[0], "UseMouseWheel") == 0)
1906
tb->use_mouse_wheel = str2num(bool_pair, s.t[1], 1);
1907
else if (g_ascii_strcasecmp(s.t[0], "UseUrgencyHint") == 0)
1908
tb->use_urgency_hint = str2num(bool_pair, s.t[1], 1);
1909
else if (g_ascii_strcasecmp(s.t[0], "FlatButton") == 0)
1910
tb->flat_button = str2num(bool_pair, s.t[1], 1);
1911
else if (g_ascii_strcasecmp(s.t[0], "GroupedTasks") == 0)
1912
tb->grouped_tasks = str2num(bool_pair, s.t[1], 1);
1914
ERR( "taskbar: unknown var %s\n", s.t[0]);
1918
ERR( "taskbar: illegal in this context %s\n", s.str);
1924
/* Build the graphic elements. */
1925
taskbar_build_gui(p);
1927
/* Fetch the client list and redraw the taskbar. Then determine what window has focus. */
1928
taskbar_net_client_list(NULL, tb);
1929
taskbar_net_active_window(NULL, tb);
1933
/* Plugin destructor. */
1934
static void taskbar_destructor(Plugin * p)
1936
TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
1938
/* Remove GDK event filter. */
1939
gdk_window_remove_filter(NULL, (GdkFilterFunc) taskbar_event_filter, tb);
1941
/* Remove root window signal handlers. */
1942
g_signal_handlers_disconnect_by_func(fbev, taskbar_net_current_desktop, tb);
1943
g_signal_handlers_disconnect_by_func(fbev, taskbar_net_active_window, tb);
1944
g_signal_handlers_disconnect_by_func(fbev, taskbar_net_number_of_desktops, tb);
1945
g_signal_handlers_disconnect_by_func(fbev, taskbar_net_client_list, tb);
1947
/* Remove "window-manager-changed" handler. */
1948
g_signal_handlers_disconnect_by_func(gtk_widget_get_screen(p->pwid), taskbar_window_manager_changed, tb);
1950
/* Deallocate task list. */
1951
while (tb->task_list != NULL)
1952
task_delete(tb, tb->task_list, TRUE);
1954
/* Deallocate class list. */
1955
while (tb->res_class_list != NULL)
1957
TaskClass * tc = tb->res_class_list;
1958
tb->res_class_list = tc->res_class_flink;
1959
g_free(tc->res_class);
1963
/* Deallocate other memory. */
1964
gtk_widget_destroy(tb->menu);
1968
/* Callback from configuration dialog mechanism to apply the configuration. */
1969
static void taskbar_apply_configuration(Plugin * p)
1971
TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
1973
/* Update style on taskbar. */
1974
taskbar_update_style(tb);
1976
/* Update styles on each button. */
1978
for (tk = tb->task_list; tk != NULL; tk = tk->task_flink)
1979
task_update_style(tk, tb);
1981
/* Refetch the client list and redraw. */
1982
recompute_group_visibility_on_current_desktop(tb);
1983
taskbar_net_client_list(NULL, tb);
1986
/* Display the configuration dialog. */
1987
static void taskbar_configure(Plugin * p, GtkWindow * parent)
1989
TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
1990
GtkWidget* dlg = create_generic_config_dlg(
1993
(GSourceFunc) taskbar_apply_configuration, (gpointer) p,
1994
_("Show tooltips"), &tb->tooltips, CONF_TYPE_BOOL,
1995
_("Icons only"), &tb->icons_only, CONF_TYPE_BOOL,
1996
_("Flat buttons"), &tb->flat_button, CONF_TYPE_BOOL,
1997
_("Show windows from all desktops"), &tb->show_all_desks, CONF_TYPE_BOOL,
1998
_("Use mouse wheel"), &tb->use_mouse_wheel, CONF_TYPE_BOOL,
1999
_("Flash when there is any window requiring attention"), &tb->use_urgency_hint, CONF_TYPE_BOOL,
2000
_("Combine multiple application windows into a single button"), &tb->grouped_tasks, CONF_TYPE_BOOL,
2001
_("Maximum width of task button"), &tb->task_width_max, CONF_TYPE_INT,
2002
_("Spacing"), &tb->spacing, CONF_TYPE_INT,
2004
gtk_window_present(GTK_WINDOW(dlg));
2007
/* Save the configuration to the configuration file. */
2008
static void taskbar_save_configuration(Plugin * p, FILE * fp)
2010
TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
2011
lxpanel_put_bool(fp, "tooltips", tb->tooltips);
2012
lxpanel_put_bool(fp, "IconsOnly", tb->icons_only);
2013
lxpanel_put_bool(fp, "ShowAllDesks", tb->show_all_desks);
2014
lxpanel_put_bool(fp, "UseMouseWheel", tb->use_mouse_wheel);
2015
lxpanel_put_bool(fp, "UseUrgencyHint", tb->use_urgency_hint);
2016
lxpanel_put_bool(fp, "FlatButton", tb->flat_button);
2017
lxpanel_put_int(fp, "MaxTaskWidth", tb->task_width_max);
2018
lxpanel_put_int(fp, "spacing", tb->spacing);
2019
lxpanel_put_bool(fp, "GroupedTasks", tb->grouped_tasks);
2022
/* Callback when panel configuration changes. */
2023
static void taskbar_panel_configuration_changed(Plugin * p)
2025
TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
2026
taskbar_update_style(tb);
2027
taskbar_make_menu(tb);
2029
/* If the icon size changed, refetch all the icons. */
2030
if (tb->plug->panel->icon_size != tb->icon_size)
2032
tb->icon_size = tb->plug->panel->icon_size;
2034
for (tk = tb->task_list; tk != NULL; tk = tk->task_flink)
2036
GdkPixbuf * pixbuf = task_update_icon(tb, tk, None);
2039
gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
2040
g_object_unref(pixbuf);
2045
/* Redraw all the labels. Icon size or font color may have changed. */
2049
/* Plugin descriptor. */
2050
PluginClass taskbar_plugin_class = {
2052
PLUGINCLASS_VERSIONING,
2055
name : N_("Task Bar (Window List)"),
2057
description : N_("Taskbar shows all opened windows and allow to iconify them, shade or get focus"),
2059
/* Stretch is available and default for this plugin. */
2060
expand_available : TRUE,
2061
expand_default : TRUE,
2063
constructor : taskbar_constructor,
2064
destructor : taskbar_destructor,
2065
config : taskbar_configure,
2066
save : taskbar_save_configuration,
2067
panel_configuration_changed : taskbar_panel_configuration_changed
2071
/* vim: set sw=4 et sts=4 : */