~ubuntu-branches/ubuntu/wily/lxpanel/wily-proposed

« back to all changes in this revision

Viewing changes to src/plugins/taskbar.c

  • Committer: Package Import Robot
  • Author(s): Julien Lavergne
  • Date: 2015-01-31 15:30:45 UTC
  • mfrom: (1.1.19) (44.1.1 vivid-proposed)
  • Revision ID: package-import@ubuntu.com-20150131153045-iabx5uuxwf2p9sl3
Tags: 0.7.2-1ubuntu1
* Merge with Debian. Ubuntu remaining changes:
* debian/control:
 - Add libindicator-dev build-depends.
 - Add a recommend on xterm | pavucontrol | gnome-alsamixer to enable the
   mixer on the sound applet. (LP: #957749).
 - Add indicator plugin binary.
 - Add build-depends on libicu-dev for weather plugin.
* debian/local/source_lxpanel.py:
 - Add apport hook.
* debian/lxpanel.install:
 - Install all files except indicators.
* debian/lxpanel-indicator-applet-plugin.install:
 - Install indicator plugin.
* debian/rules:
 - Add --enable-indicator-support flag.
 - Add dh_install --fail-missing.
* debian/patches:
 - 04_disable_gtk3_indicators.patch: Hide incompatible indicators in the
   preference menu (LP: #1165245).

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
 
27
 
#include <gdk-pixbuf/gdk-pixbuf.h>
28
 
#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
29
 
#include <gdk/gdk.h>
30
 
#include <glib/gi18n.h>
31
 
 
32
 
#include "panel.h"
33
 
#include "misc.h"
34
 
#include "plugin.h"
35
 
#include "icon.xpm"
36
 
#include "gtkbar.h"
37
 
#include "icon-grid.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
 
#include "dbg.h"
48
 
 
49
 
struct _taskbar;
50
 
struct _task_class;
51
 
struct _task;
52
 
 
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 */
61
 
} TaskClass;
62
 
 
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 */
85
 
} Task;
86
 
 
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 */
114
 
} TaskbarPlugin;
115
 
 
116
 
static gchar *taskbar_rc = "style 'taskbar-style'\n"
117
 
"{\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 */
124
 
"}\n"
125
 
"widget '*.taskbar.*' style 'taskbar-style'";
126
 
 
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 */
132
 
 
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);
195
 
 
196
 
/* Set an urgency timer on a task. */
197
 
static void set_timer_on_task(Task * tk)
198
 
{
199
 
    gint interval;
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);
203
 
}
204
 
 
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)
207
 
{
208
 
    return ((tk->desktop == ALL_WORKSPACES) || (tk->desktop == tb->current_desktop) || (tb->show_all_desks));
209
 
}
210
 
 
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)
214
 
{
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;
220
 
    Task * tk;
221
 
    for (tk = tc->res_class_head; tk != NULL; tk = tk->res_class_flink)
222
 
    {
223
 
        if (task_is_visible_on_current_desktop(tb, tk))
224
 
        {
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;
229
 
 
230
 
            /* Compute summary bit for urgency anywhere in the class. */
231
 
            if (tk->urgency)
232
 
                class_has_urgency = TRUE;
233
 
 
234
 
            /* If there is urgency, record the currently flashing task. */
235
 
            if (tk->flash_timeout != 0)
236
 
                flashing_task = tk;
237
 
 
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;
247
 
        }
248
 
    }
249
 
 
250
 
    /* Transfer the flash timeout to the visible task. */
251
 
    if (class_has_urgency)
252
 
    {
253
 
        if (flashing_task == NULL)
254
 
        {
255
 
            /* Set the flashing context and flash the window immediately. */
256
 
            tc->visible_task->flash_state = TRUE;
257
 
            flash_window_timeout(tc->visible_task);
258
 
 
259
 
            /* Set the timer, since none is set. */
260
 
            set_timer_on_task(tc->visible_task);
261
 
        }
262
 
        else if (flashing_task != tc->visible_task)
263
 
        {
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);
271
 
        }   
272
 
    }
273
 
    else
274
 
    {
275
 
        /* No task has urgency.  Cancel the timer if one is set. */
276
 
        if (flashing_task != NULL)
277
 
        {
278
 
            g_source_remove(flashing_task->flash_timeout);
279
 
            flashing_task->flash_state = FALSE;
280
 
            flashing_task->flash_timeout = 0;
281
 
        }
282
 
    }
283
 
}
284
 
 
285
 
/* Recompute the visible task for all classes when the desktop changes. */
286
 
static void recompute_group_visibility_on_current_desktop(TaskbarPlugin * tb)
287
 
{
288
 
    TaskClass * tc;
289
 
    for (tc = tb->res_class_list; tc != NULL; tc = tc->res_class_flink)
290
 
    {
291
 
        recompute_group_visibility_for_class(tb, tc);
292
 
    }
293
 
}
294
 
 
295
 
/* Draw the label and tooltip on a taskbar button. */
296
 
static void task_draw_label(Task * tk)
297
 
{
298
 
    TaskClass * tc = tk->res_class;
299
 
    gboolean bold_style = (((tk->entered_state) || (tk->flash_state)) && (tk->tb->flat_button));
300
 
    char *label;
301
 
 
302
 
    if ((tk->tb->grouped_tasks) && (tc != NULL) && (tc->visible_task == tk)
303
 
            && (tc->visible_count > 1))
304
 
    {
305
 
        label = g_strdup_printf("(%d) %s", tc->visible_count, tc->visible_name);
306
 
    }
307
 
    else
308
 
    {
309
 
        label = g_strdup(tk->iconified ? tk->name_iconified : tk->name);
310
 
    }
311
 
 
312
 
    if (tk->tb->tooltips)
313
 
        gtk_widget_set_tooltip_text(tk->button, label);
314
 
 
315
 
    panel_draw_label_text(tk->tb->plug->panel, tk->label, label, bold_style, 1,
316
 
            tk->tb->flat_button);
317
 
 
318
 
    g_free(label);
319
 
}
320
 
 
321
 
/* Determine if a task is visible. */
322
 
static gboolean task_is_visible(TaskbarPlugin * tb, Task * tk)
323
 
{
324
 
    /* Not visible due to grouping. */
325
 
    if ((tb->grouped_tasks) && (tk->res_class != NULL) && (tk->res_class->visible_task != tk))
326
 
        return FALSE;
327
 
 
328
 
    /* Desktop placement. */
329
 
    return task_is_visible_on_current_desktop(tb, tk);
330
 
}
331
 
 
332
 
/* Redraw a task button. */
333
 
static void task_button_redraw(Task * tk, TaskbarPlugin * tb)
334
 
{
335
 
    if (task_is_visible(tb, tk))
336
 
    {
337
 
        task_draw_label(tk);
338
 
        icon_grid_set_visible(tb->icon_grid, tk->button, TRUE);
339
 
    }
340
 
    else
341
 
        icon_grid_set_visible(tb->icon_grid, tk->button, FALSE);
342
 
}
343
 
 
344
 
/* Redraw all tasks in the taskbar. */
345
 
static void taskbar_redraw(TaskbarPlugin * tb)
346
 
{
347
 
    Task * tk;
348
 
    for (tk = tb->task_list; tk != NULL; tk = tk->task_flink)
349
 
        task_button_redraw(tk, tb);
350
 
}
351
 
 
352
 
/* Determine if a task should be visible given its NET_WM_STATE. */
353
 
static gboolean accept_net_wm_state(NetWMState * nws)
354
 
{
355
 
    return ( ! (nws->skip_taskbar));
356
 
}
357
 
 
358
 
/* Determine if a task should be visible given its NET_WM_WINDOW_TYPE. */
359
 
static gboolean accept_net_wm_window_type(NetWMWindowType * nwwt)
360
 
{
361
 
    return ( ! ((nwwt->desktop) || (nwwt->dock) || (nwwt->splash)));
362
 
}
363
 
 
364
 
/* Free the names associated with a task. */
365
 
static void task_free_names(Task * tk)
366
 
{
367
 
    g_free(tk->name);
368
 
    g_free(tk->name_iconified);
369
 
    tk->name = tk->name_iconified = NULL;
370
 
}
371
 
 
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)
375
 
{
376
 
    char * name = NULL;
377
 
 
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))
381
 
    {
382
 
        name = get_utf8_property(tk->win,  a_NET_WM_VISIBLE_NAME);
383
 
        if (name != NULL)
384
 
            tk->name_source = a_NET_WM_VISIBLE_NAME;
385
 
    }
386
 
 
387
 
    /* Try _NET_WM_NAME, which supports UTF-8, but do not overwrite _NET_WM_VISIBLE_NAME. */
388
 
    if ((name == NULL)
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)))
391
 
    {
392
 
        name = get_utf8_property(tk->win,  a_NET_WM_NAME);
393
 
        if (name != NULL)
394
 
            tk->name_source = a_NET_WM_NAME;
395
 
    }
396
 
 
397
 
    /* Try WM_NAME, which supports only ISO-8859-1, but do not overwrite _NET_WM_VISIBLE_NAME or _NET_WM_NAME. */
398
 
    if ((name == NULL)
399
 
    && ((source == None) || (source == XA_WM_NAME))
400
 
    && ((tk->name_source == None) || (tk->name_source == XA_WM_NAME)))
401
 
    {
402
 
        name = get_textproperty(tk->win,  XA_WM_NAME);
403
 
        if (name != NULL)
404
 
            tk->name_source = XA_WM_NAME;
405
 
    }
406
 
 
407
 
    /* Set the name into the task context, and also on the tooltip. */
408
 
    if (name != NULL)
409
 
    {
410
 
        task_free_names(tk);
411
 
        tk->name = g_strdup(name);
412
 
        tk->name_iconified = g_strdup_printf("[%s]", name);
413
 
        g_free(name);
414
 
 
415
 
        /* Redraw the button. */
416
 
        task_button_redraw(tk, tk->tb);
417
 
    }
418
 
}
419
 
 
420
 
/* Unlink a task from the class list because its class changed or it was deleted. */
421
 
static void task_unlink_class(Task * tk)
422
 
{
423
 
    TaskClass * tc = tk->res_class;
424
 
    if (tc != NULL)
425
 
    {
426
 
        /* Remove from per-class task list. */
427
 
        if (tc->res_class_head == tk)
428
 
        {
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);
433
 
        }
434
 
        else
435
 
        {
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;
438
 
            Task * tk_cursor;
439
 
            for (
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) ;
443
 
            if (tk_cursor == tk)
444
 
                tk_pred->res_class_flink = tk->res_class_flink;
445
 
        }
446
 
        tk->res_class_flink = NULL;
447
 
 
448
 
        /* Recompute group visibility. */
449
 
        recompute_group_visibility_for_class(tk->tb, tc);
450
 
    }
451
 
}
452
 
 
453
 
/* Enter class with specified name. */
454
 
static TaskClass * taskbar_enter_res_class(TaskbarPlugin * tb, char * res_class, gboolean * name_consumed)
455
 
    {
456
 
    /* Find existing entry or insertion point. */
457
 
    *name_consumed = FALSE;
458
 
    TaskClass * tc_pred = NULL;
459
 
    TaskClass * tc;
460
 
    for (tc = tb->res_class_list; tc != NULL; tc_pred = tc, tc = tc->res_class_flink)
461
 
    {
462
 
        int status = strcmp(res_class, tc->res_class);
463
 
        if (status == 0)
464
 
            return tc;
465
 
        if (status < 0)
466
 
            break;
467
 
    }
468
 
 
469
 
    /* Insert new entry. */
470
 
    tc = g_new0(TaskClass, 1);
471
 
    tc->res_class = res_class;
472
 
    *name_consumed = TRUE;
473
 
    if (tc_pred == NULL)
474
 
    {
475
 
        tc->res_class_flink = tb->res_class_list;
476
 
        tb->res_class_list = tc;
477
 
    }
478
 
    else
479
 
    {
480
 
        tc->res_class_flink = tc_pred->res_class_flink;
481
 
        tc_pred->res_class_flink = tc;
482
 
    }
483
 
    return tc;
484
 
}
485
 
 
486
 
/* Set the class associated with a task. */
487
 
static void task_set_class(Task * tk)
488
 
{
489
 
    /* Read the WM_CLASS property. */
490
 
    XClassHint ch;
491
 
    ch.res_name = NULL;
492
 
    ch.res_class = NULL;
493
 
    XGetClassHint(GDK_DISPLAY(), tk->win, &ch);
494
 
 
495
 
    /* If the res_name was returned, free it.  We make no use of it at this time. */
496
 
    if (ch.res_name != NULL)
497
 
    {
498
 
        XFree(ch.res_name);
499
 
    }
500
 
 
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)
504
 
    {
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)
508
 
        {
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);
512
 
 
513
 
            /* If the task changed class, update data structures. */
514
 
            TaskClass * old_tc = tk->res_class;
515
 
            if (old_tc != tc)
516
 
            {
517
 
                /* Unlink from previous class, if any. */
518
 
                task_unlink_class(tk);
519
 
 
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;
523
 
                else
524
 
                {
525
 
                    Task * tk_pred;
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);
529
 
                }
530
 
                tk->res_class = tc;
531
 
 
532
 
                /* Recompute group visibility. */
533
 
                recompute_group_visibility_for_class(tk->tb, tc);
534
 
            }
535
 
        }
536
 
        XFree(ch.res_class);
537
 
    }
538
 
}
539
 
 
540
 
/* Look up a task in the task list. */
541
 
static Task * task_lookup(TaskbarPlugin * tb, Window win)
542
 
{
543
 
    Task * tk;
544
 
    for (tk = tb->task_list; tk != NULL; tk = tk->task_flink)
545
 
        {
546
 
        if (tk->win == win)
547
 
            return tk;
548
 
        if (tk->win > win)
549
 
            break;
550
 
        }
551
 
    return NULL;
552
 
}
553
 
 
554
 
/* Delete a task and optionally unlink it from the task list. */
555
 
static void task_delete(TaskbarPlugin * tb, Task * tk, gboolean unlink)
556
 
{
557
 
    /* If we think this task had focus, remove that. */
558
 
    if (tb->focused == tk)
559
 
        tb->focused = NULL;
560
 
 
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;
565
 
    }
566
 
 
567
 
    /* Deallocate structures. */
568
 
    icon_grid_remove(tb->icon_grid, tk->button);
569
 
    task_free_names(tk);
570
 
    task_unlink_class(tk);
571
 
 
572
 
    /* If requested, unlink the task from the task list.
573
 
     * If not requested, the caller will do this. */
574
 
    if (unlink)
575
 
    {
576
 
        if (tb->task_list == tk)
577
 
            tb->task_list = tk->task_flink;
578
 
        else
579
 
        {
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;
582
 
            Task * tk_cursor;
583
 
            for (
584
 
              tk_cursor = tb->task_list;
585
 
              ((tk_cursor != NULL) && (tk_cursor != tk));
586
 
              tk_pred = tk_cursor, tk_cursor = tk_cursor->task_flink) ;
587
 
            if (tk_cursor == tk)
588
 
                tk_pred->task_flink = tk->task_flink;
589
 
        }
590
 
    }
591
 
 
592
 
    /* Deallocate the task structure. */
593
 
    g_free(tk);
594
 
}
595
 
 
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)
599
 
{
600
 
    /* Get the drawable. */
601
 
    GdkDrawable * drawable = gdk_xid_table_lookup(xpixmap);
602
 
    if (drawable != NULL)
603
 
        g_object_ref(G_OBJECT(drawable));
604
 
    else
605
 
        drawable = gdk_pixmap_foreign_new(xpixmap);
606
 
 
607
 
    GdkColormap * colormap = NULL;
608
 
    GdkPixbuf * retval = NULL;
609
 
    if (drawable != NULL)
610
 
    {
611
 
        /* Get the colormap.
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));
617
 
        else if (depth == 1)
618
 
            colormap = NULL;
619
 
        else
620
 
        {
621
 
            colormap = gdk_screen_get_system_colormap(gdk_drawable_get_screen(drawable));
622
 
            g_object_ref(G_OBJECT(colormap));
623
 
        }
624
 
 
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))
628
 
#else
629
 
        if ((colormap != NULL) && (gdk_colormap_get_visual(colormap)->depth != depth))
630
 
#endif
631
 
        {
632
 
            g_object_unref(G_OBJECT(colormap));
633
 
            colormap = NULL;
634
 
        }
635
 
 
636
 
        /* Do the major work. */
637
 
        retval = gdk_pixbuf_get_from_drawable(NULL, drawable, colormap, 0, 0, 0, 0, width, height);
638
 
    }
639
 
 
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));
645
 
    return retval;
646
 
}
647
 
 
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)
651
 
{
652
 
    /* Initialize. */
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);
660
 
 
661
 
    /* Loop to do the work. */
662
 
    int i;
663
 
    for (i = 0; i < h; i += 1)
664
 
    {
665
 
        int j;
666
 
        for (j = 0; j < w; j += 1)
667
 
        {
668
 
            guchar * s = src + i * src_stride + j * 3;
669
 
            guchar * d = dst + i * dst_stride + j * 4;
670
 
 
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 */
673
 
        }
674
 
    }
675
 
 
676
 
    return with_alpha;
677
 
}
678
 
 
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)
681
 
{
682
 
    /* The result. */
683
 
    GdkPixbuf * pixmap = NULL;
684
 
    Atom possible_source = None;
685
 
    int result = -1;
686
 
 
687
 
    if ((source == None) || (source == a_NET_WM_ICON))
688
 
    {
689
 
        /* Important Notes:
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).
699
 
         */
700
 
 
701
 
        /* Get the window property _NET_WM_ICON, if possible. */
702
 
        Atom type = None;
703
 
        int format;
704
 
        gulong nitems;
705
 
        gulong bytes_after;
706
 
        gulong * data = NULL;
707
 
        result = XGetWindowProperty(
708
 
            GDK_DISPLAY(),
709
 
            task_win,
710
 
            a_NET_WM_ICON,
711
 
            0, G_MAXLONG,
712
 
            False, XA_CARDINAL,
713
 
            &type, &format, &nitems, &bytes_after, (void *) &data);
714
 
 
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))
717
 
        {
718
 
            if (data != NULL)
719
 
                XFree(data);
720
 
            result = -1;
721
 
        }
722
 
 
723
 
        /* If the result is usable, extract the icon from it. */
724
 
        if (result == Success)
725
 
        {
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;
732
 
            gulong max_w = 0;
733
 
            gulong max_h = 0;
734
 
            while ((pdata + 2) < pdata_end)
735
 
            {
736
 
                /* Extract the width and height. */
737
 
                gulong w = pdata[0];
738
 
                gulong h = pdata[1];
739
 
                gulong size = w * h;
740
 
                pdata += 2;
741
 
 
742
 
                /* Bounds check the icon. */
743
 
                if (pdata + size > pdata_end)
744
 
                    break;
745
 
 
746
 
                /* Rare special case: the desired size is the same as icon size. */
747
 
                if ((required_width == w) && (required_height == h))
748
 
                {
749
 
                    max_icon = pdata;
750
 
                    max_w = w;
751
 
                    max_h = h;
752
 
                    break;
753
 
                }
754
 
 
755
 
                /* If the icon is the largest so far, capture it. */
756
 
                if ((w > max_w) && (h > max_h))
757
 
                {
758
 
                    max_icon = pdata;
759
 
                    max_w = w;
760
 
                    max_h = h;
761
 
                }
762
 
                pdata += size;
763
 
            }
764
 
 
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)
768
 
            {
769
 
                /* Allocate enough space for the pixel data. */
770
 
                gulong len = max_w * max_h;
771
 
                guchar * pixdata = g_new(guchar, len * 4);
772
 
 
773
 
                /* Loop to convert the pixel data. */
774
 
                guchar * p = pixdata;
775
 
                int i;
776
 
                for (i = 0; i < len; p += 4, i += 1)
777
 
                {
778
 
                    guint argb = max_icon[i];
779
 
                    guint rgba = (argb << 8) | (argb >> 24);
780
 
                    p[0] = rgba >> 24;
781
 
                    p[1] = (rgba >> 16) & 0xff;
782
 
                    p[2] = (rgba >> 8) & 0xff;
783
 
                    p[3] = rgba & 0xff;
784
 
                }
785
 
            
786
 
                /* Initialize a pixmap with the pixel data. */
787
 
                pixmap = gdk_pixbuf_new_from_data(
788
 
                    pixdata,
789
 
                    GDK_COLORSPACE_RGB,
790
 
                    TRUE, 8,    /* has_alpha, bits_per_sample */
791
 
                    max_w, max_h, max_w * 4,
792
 
                    (GdkPixbufDestroyNotify) g_free,
793
 
                    NULL);
794
 
                possible_source = a_NET_WM_ICON;
795
 
            }
796
 
            else
797
 
                result = -1;
798
 
 
799
 
            /* Free the X property data. */
800
 
            XFree(data);
801
 
        }
802
 
    }
803
 
 
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)))
807
 
    {
808
 
        XWMHints * hints = XGetWMHints(GDK_DISPLAY(), task_win);
809
 
        result = (hints != NULL) ? Success : -1;
810
 
        Pixmap xpixmap = None;
811
 
        Pixmap xmask = None;
812
 
 
813
 
        if (result == Success)
814
 
        {
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;
820
 
            XFree(hints);
821
 
            if (xpixmap != None)
822
 
            {
823
 
                result = Success;
824
 
                possible_source = XA_WM_HINTS;
825
 
            }
826
 
            else
827
 
                result = -1;
828
 
        }
829
 
 
830
 
        if (result != Success)
831
 
        {
832
 
            /* No icon available from _NET_WM_ICON or WM_HINTS.  Next try KWM_WIN_ICON. */
833
 
            Atom type = None;
834
 
            int format;
835
 
            gulong nitems;
836
 
            gulong bytes_after;
837
 
            Pixmap *icons = NULL;
838
 
            Atom kwin_win_icon_atom = gdk_x11_get_xatom_by_name("KWM_WIN_ICON");
839
 
            result = XGetWindowProperty(
840
 
                GDK_DISPLAY(),
841
 
                task_win,
842
 
                kwin_win_icon_atom,
843
 
                0, G_MAXLONG,
844
 
                False, kwin_win_icon_atom,
845
 
                &type, &format, &nitems, &bytes_after, (void *) &icons);
846
 
 
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)
849
 
            {
850
 
                if (icons != NULL)
851
 
                    XFree(icons);
852
 
                result = -1;
853
 
            }
854
 
 
855
 
            /* If the result is usable, extract the X pixmap and mask from it. */
856
 
            if (result == Success)
857
 
            {
858
 
                xpixmap = icons[0];
859
 
                xmask = icons[1];
860
 
                if (xpixmap != None)
861
 
                {
862
 
                    result = Success;
863
 
                    possible_source = kwin_win_icon_atom;
864
 
                }
865
 
                else
866
 
                    result = -1;
867
 
            }
868
 
        }
869
 
 
870
 
        /* If we have an X pixmap, get its geometry.*/
871
 
        unsigned int w, h;
872
 
        if (result == Success)
873
 
        {
874
 
            Window unused_win;
875
 
            int unused;
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;
880
 
        }
881
 
 
882
 
        /* If we have an X pixmap and its geometry, convert it to a GDK pixmap. */
883
 
        if (result == Success) 
884
 
        {
885
 
            pixmap = _wnck_gdk_pixbuf_get_from_pixmap(xpixmap, w, h);
886
 
            result = ((pixmap != NULL) ? Success : -1);
887
 
        }
888
 
 
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))
892
 
        {
893
 
            Window unused_win;
894
 
            int unused;
895
 
            unsigned int unused_2;
896
 
            if (XGetGeometry(
897
 
                GDK_DISPLAY(), xmask,
898
 
                &unused_win, &unused, &unused, &w, &h, &unused_2, &unused_2))
899
 
            {
900
 
                /* Convert the X mask to a GDK pixmap. */
901
 
                GdkPixbuf * mask = _wnck_gdk_pixbuf_get_from_pixmap(xmask, w, h);
902
 
                if (mask != NULL)
903
 
                {
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;
909
 
                }
910
 
            }
911
 
        }
912
 
    }
913
 
 
914
 
    /* If we got a pixmap, scale it and return it. */
915
 
    if (pixmap == NULL)
916
 
        return NULL;
917
 
    else
918
 
    {
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;
922
 
        return ret;
923
 
    }
924
 
}
925
 
 
926
 
/* Update the icon of a task. */
927
 
static GdkPixbuf * task_update_icon(TaskbarPlugin * tb, Task * tk, Atom source)
928
 
{
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);
931
 
 
932
 
    /* If that fails, and we have no other icon yet, return the fallback icon. */
933
 
    if ((pixbuf == NULL)
934
 
    && ((source == None) || (tk->image_source == None)))
935
 
    {
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;
941
 
    }
942
 
 
943
 
    /* Return what we have.  This may be NULL to indicate that no change should be made to the icon. */
944
 
    return pixbuf;
945
 
}
946
 
 
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)
949
 
{
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);
953
 
    task_draw_label(tk);
954
 
 
955
 
    /* Complement the flashing context. */
956
 
    tk->flash_state = ! tk->flash_state;
957
 
    return TRUE;
958
 
}
959
 
 
960
 
/* Set urgency notification. */
961
 
static void task_set_urgency(Task * tk)
962
 
{
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);
967
 
    else
968
 
    {
969
 
        /* Set the flashing context and flash the window immediately. */
970
 
        tk->flash_state = TRUE;
971
 
        flash_window_timeout(tk);
972
 
 
973
 
        /* Set the timer if none is set. */
974
 
        if (tk->flash_timeout == 0)
975
 
            set_timer_on_task(tk);
976
 
    }
977
 
}
978
 
 
979
 
/* Clear urgency notification. */
980
 
static void task_clear_urgency(Task * tk)
981
 
{
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);
986
 
    else
987
 
    {
988
 
        /* Remove the timer if one is set. */
989
 
        if (tk->flash_timeout != 0)
990
 
        {
991
 
            g_source_remove(tk->flash_timeout);
992
 
            tk->flash_timeout = 0;
993
 
        }
994
 
 
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;
999
 
    }
1000
 
}
1001
 
 
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)
1006
 
{
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);
1010
 
 
1011
 
    /* Evaluate use_net_active if not yet done. */
1012
 
    if ( ! tk->tb->net_active_checked)
1013
 
    {
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;
1018
 
    }
1019
 
 
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);
1024
 
    else
1025
 
    {
1026
 
        GdkWindow * gdkwindow = gdk_xid_table_lookup(tk->win);
1027
 
        if (gdkwindow != NULL)
1028
 
            gdk_window_show(gdkwindow);
1029
 
        else
1030
 
            XMapRaised(GDK_DISPLAY(), tk->win);
1031
 
 
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);
1038
 
    }
1039
 
 
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);
1044
 
}
1045
 
 
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)
1048
 
{
1049
 
    Task * tk = (Task *) data;
1050
 
 
1051
 
    /* Get the allocation of the popup menu. */
1052
 
    GtkRequisition popup_req;
1053
 
    gtk_widget_size_request(menu, &popup_req);
1054
 
 
1055
 
    /* Determine the coordinates. */
1056
 
    plugin_popup_set_position_helper(tk->tb->plug, tk->button, menu, &popup_req, px, py);
1057
 
    *push_in = TRUE;
1058
 
}
1059
 
 
1060
 
/* Remove the grouped-task popup menu from the screen. */
1061
 
static void task_group_menu_destroy(TaskbarPlugin * tb)
1062
 
{
1063
 
    if (tb->group_menu != NULL)
1064
 
    {
1065
 
        gtk_widget_destroy(tb->group_menu);
1066
 
        tb->group_menu = NULL;
1067
 
    }
1068
 
}
1069
 
 
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)
1073
 
{
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)))
1077
 
    {
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();
1081
 
        Task * tk_cursor;
1082
 
        for (tk_cursor = tc->res_class_head; tk_cursor != NULL; tk_cursor = tk_cursor->res_class_flink)
1083
 
        {
1084
 
            if (task_is_visible_on_current_desktop(tb, tk_cursor))
1085
 
            {
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);
1092
 
            }
1093
 
        }
1094
 
 
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);
1100
 
    }
1101
 
    else
1102
 
    {
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);
1106
 
 
1107
 
        if (event->button == 1)
1108
 
        {
1109
 
            /* Left button.
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. */
1113
 
            if (tk->iconified)
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()));
1117
 
            else
1118
 
                task_raise_window(tk, event->time);
1119
 
        }
1120
 
        else if (event->button == 2)
1121
 
        {
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,
1126
 
                0, 0, 0);
1127
 
        }
1128
 
        else if (event->button == 3)
1129
 
        {
1130
 
            /* Right button.  Bring up the window state popup menu. */
1131
 
            tk->tb->menutask = tk;
1132
 
            gtk_menu_popup(
1133
 
                GTK_MENU(tb->menu),
1134
 
                NULL, NULL,
1135
 
                (GtkMenuPositionFunc) taskbar_popup_set_position, (gpointer) visible_task,
1136
 
                event->button, event->time);
1137
 
        }
1138
 
    }
1139
 
 
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);
1143
 
    return TRUE;
1144
 
}
1145
 
 
1146
 
/* Handler for "button-press-event" event from taskbar button. */
1147
 
static gboolean taskbar_button_press_event(GtkWidget * widget, GdkEventButton * event, Task * tk)
1148
 
{
1149
 
    return taskbar_task_control_event(widget, event, tk, FALSE);
1150
 
}
1151
 
 
1152
 
/* Handler for "activate" event from grouped-task popup menu item. */
1153
 
static gboolean taskbar_popup_activate_event(GtkWidget * widget, GdkEventButton * event, Task * tk)
1154
 
{
1155
 
    return taskbar_task_control_event(widget, event, tk, TRUE);
1156
 
}
1157
 
 
1158
 
/* Handler for "drag-motion" timeout. */
1159
 
static gboolean taskbar_button_drag_motion_timeout(Task * tk)
1160
 
{
1161
 
    guint time = gtk_get_current_event_time();
1162
 
    task_raise_window(tk, ((time != 0) ? time : CurrentTime));
1163
 
    tk->tb->dnd_delay_timer = 0;
1164
 
    return FALSE;
1165
 
}
1166
 
 
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)
1169
 
{
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);
1174
 
    return TRUE;
1175
 
}
1176
 
 
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)
1179
 
{
1180
 
    /* Cancel the timer if set. */
1181
 
    if (tk->tb->dnd_delay_timer != 0)
1182
 
    {
1183
 
        g_source_remove(tk->tb->dnd_delay_timer);
1184
 
        tk->tb->dnd_delay_timer = 0;
1185
 
    }
1186
 
    return;
1187
 
}
1188
 
 
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)
1191
 
{
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);
1196
 
}
1197
 
 
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)
1200
 
{
1201
 
    tk->entered_state = FALSE;
1202
 
    task_draw_label(tk);
1203
 
}
1204
 
 
1205
 
/* Handler for "scroll-event" event from taskbar button. */
1206
 
static gboolean taskbar_button_scroll_event(GtkWidget * widget, GdkEventScroll * event, Task * tk)
1207
 
{
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)))
1212
 
    {
1213
 
        if ((event->direction == GDK_SCROLL_UP) || (event->direction == GDK_SCROLL_LEFT))
1214
 
            task_raise_window(tk, event->time);
1215
 
        else
1216
 
            XIconifyWindow(GDK_DISPLAY(), tk->win, DefaultScreen(GDK_DISPLAY()));
1217
 
    }
1218
 
    return TRUE;
1219
 
}
1220
 
 
1221
 
/* Handler for "size-allocate" event from taskbar button. */
1222
 
static void taskbar_button_size_allocate(GtkWidget * btn, GtkAllocation * alloc, Task * tk)
1223
 
{
1224
 
    if (GTK_WIDGET_REALIZED(btn))
1225
 
    {
1226
 
        /* Get the coordinates of the button. */
1227
 
        int x, y;
1228
 
#if GTK_CHECK_VERSION(2,22,0)
1229
 
        gdk_window_get_origin(gtk_button_get_event_window(GTK_BUTTON(btn)), &x, &y);
1230
 
#else
1231
 
        gdk_window_get_origin(GTK_BUTTON(btn)->event_window, &x, &y);
1232
 
#endif
1233
 
 
1234
 
 
1235
 
        /* Send a NET_WM_ICON_GEOMETRY property change on the window. */
1236
 
        guint32 data[4];
1237
 
        data[0] = x;
1238
 
        data[1] = y;
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);
1244
 
    }
1245
 
}
1246
 
 
1247
 
/* Update style on the taskbar when created or after a configuration change. */
1248
 
static void taskbar_update_style(TaskbarPlugin * tb)
1249
 
{
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);
1254
 
}
1255
 
 
1256
 
/* Update style on a task button when created or after a configuration change. */
1257
 
static void task_update_style(Task * tk, TaskbarPlugin * tb)
1258
 
{
1259
 
    if (tb->icons_only)
1260
 
        gtk_widget_hide(tk->label);
1261
 
    else
1262
 
        gtk_widget_show(tk->label);
1263
 
 
1264
 
    if( tb->flat_button )
1265
 
    {
1266
 
        gtk_toggle_button_set_active((GtkToggleButton*)tk->button, FALSE);
1267
 
        gtk_button_set_relief(GTK_BUTTON(tk->button), GTK_RELIEF_NONE);
1268
 
    }
1269
 
    else
1270
 
    {
1271
 
        gtk_toggle_button_set_active((GtkToggleButton*)tk->button, tk->focused);
1272
 
        gtk_button_set_relief(GTK_BUTTON(tk->button), GTK_RELIEF_NORMAL);
1273
 
    }
1274
 
 
1275
 
    task_draw_label(tk);
1276
 
}
1277
 
 
1278
 
/* Build graphic elements needed for a task button. */
1279
 
static void task_build_gui(TaskbarPlugin * tb, Task * tk)
1280
 
{
1281
 
    /* NOTE
1282
 
     * 1. the extended mask is sum of taskbar and pager needs
1283
 
     * see bug [ 940441 ] pager loose track of windows
1284
 
     *
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);
1289
 
 
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);
1294
 
 
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);
1303
 
 
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);
1307
 
 
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);
1315
 
 
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);
1321
 
 
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);
1326
 
 
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);
1332
 
#else
1333
 
    GTK_WIDGET_UNSET_FLAGS(tk->button, GTK_CAN_FOCUS);
1334
 
    GTK_WIDGET_UNSET_FLAGS(tk->button, GTK_CAN_DEFAULT);
1335
 
#endif
1336
 
 
1337
 
    /* Update styles on the button. */
1338
 
    task_update_style(tk, tb);
1339
 
 
1340
 
    /* Flash button for window with urgency hint. */
1341
 
    if (tk->urgency)
1342
 
        task_set_urgency(tk);
1343
 
}
1344
 
 
1345
 
/*****************************************************
1346
 
 * handlers for NET actions                          *
1347
 
 *****************************************************/
1348
 
 
1349
 
/* Handler for "client-list" event from root window listener. */
1350
 
static void taskbar_net_client_list(GtkWidget * widget, TaskbarPlugin * tb)
1351
 
{
1352
 
    /* Get the NET_CLIENT_LIST property. */
1353
 
    int client_count;
1354
 
    Window * client_list = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_CLIENT_LIST, XA_WINDOW, &client_count);
1355
 
    if (client_list != NULL)
1356
 
    {
1357
 
        /* Loop over client list, correlating it with task list. */
1358
 
        int i;
1359
 
        for (i = 0; i < client_count; i++)
1360
 
        {
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;
1363
 
            Task * tk_cursor;
1364
 
            Task * tk = NULL;
1365
 
            for (tk_cursor = tb->task_list; tk_cursor != NULL; tk_pred = tk_cursor, tk_cursor = tk_cursor->task_flink)
1366
 
            {
1367
 
                if (tk_cursor->win == client_list[i])
1368
 
                {
1369
 
                    tk = tk_cursor;
1370
 
                    break;
1371
 
                }
1372
 
                if (tk_cursor->win > client_list[i])
1373
 
                    break;
1374
 
            }
1375
 
 
1376
 
            /* Task is already in task list. */
1377
 
            if (tk != NULL)
1378
 
                tk->present_in_client_list = TRUE;
1379
 
 
1380
 
            /* Task is not in task list. */
1381
 
            else
1382
 
            {
1383
 
                /* Evaluate window state and window type to see if it should be in task list. */
1384
 
                NetWMWindowType nwwt;
1385
 
                NetWMState nws;
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)))
1390
 
                {
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];
1395
 
                    tk->tb = tb;
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);
1404
 
                    task_set_class(tk);
1405
 
 
1406
 
                    /* Link the task structure into the task list. */
1407
 
                    if (tk_pred == NULL)
1408
 
                    {
1409
 
                        tk->task_flink = tb->task_list;
1410
 
                        tb->task_list = tk;
1411
 
                    }
1412
 
                    else
1413
 
                    {
1414
 
                        tk->task_flink = tk_pred->task_flink;
1415
 
                        tk_pred->task_flink = tk;
1416
 
                    }
1417
 
                }
1418
 
            }
1419
 
        }
1420
 
        XFree(client_list);
1421
 
    }
1422
 
 
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;
1426
 
    while (tk != NULL)
1427
 
    {
1428
 
        Task * tk_succ = tk->task_flink;
1429
 
        if (tk->present_in_client_list)
1430
 
        {
1431
 
            tk->present_in_client_list = FALSE;
1432
 
            tk_pred = tk;
1433
 
        }
1434
 
        else
1435
 
        {
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);
1440
 
        }
1441
 
        tk = tk_succ;
1442
 
    }
1443
 
 
1444
 
    /* Redraw the taskbar. */
1445
 
    taskbar_redraw(tb);
1446
 
}
1447
 
 
1448
 
/* Handler for "current-desktop" event from root window listener. */
1449
 
static void taskbar_net_current_desktop(GtkWidget * widget, TaskbarPlugin * tb)
1450
 
{
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);
1454
 
    taskbar_redraw(tb);
1455
 
}
1456
 
 
1457
 
/* Handler for "number-of-desktops" event from root window listener. */
1458
 
static void taskbar_net_number_of_desktops(GtkWidget * widget, TaskbarPlugin * tb)
1459
 
{
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);
1463
 
    taskbar_redraw(tb);
1464
 
}
1465
 
 
1466
 
/* Handler for "active-window" event from root window listener. */
1467
 
static void taskbar_net_active_window(GtkWidget * widget, TaskbarPlugin * tb)
1468
 
{
1469
 
    gboolean drop_old = FALSE;
1470
 
    gboolean make_new = FALSE;
1471
 
    Task * ctk = tb->focused;
1472
 
    Task * ntk = NULL;
1473
 
 
1474
 
    /* Get the window that has focus. */
1475
 
    Window * f = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_ACTIVE_WINDOW, XA_WINDOW, 0);
1476
 
    if (f == NULL)
1477
 
    {
1478
 
        /* No window has focus. */
1479
 
        drop_old = TRUE;
1480
 
        tb->focused_previous = NULL;
1481
 
    }
1482
 
    else
1483
 
    {
1484
 
        if (*f == tb->plug->panel->topxwin)
1485
 
        {
1486
 
            /* Taskbar window gained focus (this isn't supposed to be able to happen).  Remember current focus. */
1487
 
            if (ctk != NULL)
1488
 
            {
1489
 
                tb->focused_previous = ctk;
1490
 
                drop_old = TRUE;
1491
 
            }
1492
 
        }
1493
 
        else
1494
 
        {
1495
 
            /* Identify task that gained focus. */
1496
 
            tb->focused_previous = NULL;
1497
 
            ntk = task_lookup(tb, *f);
1498
 
            if (ntk != ctk)
1499
 
            {
1500
 
                drop_old = TRUE;
1501
 
                make_new = TRUE;
1502
 
            }
1503
 
        }
1504
 
        XFree(f);
1505
 
    }
1506
 
 
1507
 
    /* If our idea of the current task lost focus, update data structures. */
1508
 
    if ((ctk != NULL) && (drop_old))
1509
 
    {
1510
 
        ctk->focused = FALSE;
1511
 
        tb->focused = NULL;
1512
 
        if(!tb->flat_button) /* relieve the button if flat buttons is not used. */
1513
 
            gtk_toggle_button_set_active((GtkToggleButton*)ctk->button, FALSE);
1514
 
 
1515
 
        task_button_redraw(ctk, tb);
1516
 
    }
1517
 
 
1518
 
    /* If a task gained focus, update data structures. */
1519
 
    if ((ntk != NULL) && (make_new))
1520
 
    {
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;
1524
 
        tb->focused = ntk;
1525
 
        task_button_redraw(ntk, tb);
1526
 
    }
1527
 
}
1528
 
 
1529
 
/* Determine if the "urgency" hint is set on a window. */
1530
 
static gboolean task_has_urgency(Task * tk)
1531
 
{
1532
 
    gboolean result = FALSE;
1533
 
    XWMHints * hints = (XWMHints *) get_xaproperty(tk->win, XA_WM_HINTS, XA_WM_HINTS, 0);
1534
 
    if (hints != NULL)
1535
 
    {
1536
 
        if (hints->flags & XUrgencyHint)
1537
 
            result = TRUE;
1538
 
        XFree(hints);
1539
 
    }
1540
 
    return result;
1541
 
}
1542
 
 
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)
1547
 
{
1548
 
    /* State may be PropertyNewValue, PropertyDeleted. */
1549
 
    if (((XPropertyEvent*) ev)->state == PropertyNewValue)
1550
 
    {
1551
 
        Atom at = ev->xproperty.atom;
1552
 
        Window win = ev->xproperty.window;
1553
 
        if (win != GDK_ROOT_WINDOW())
1554
 
        {
1555
 
            /* Look up task structure by X window handle. */
1556
 
            Task * tk = task_lookup(tb, win);
1557
 
            if (tk != NULL)
1558
 
            {
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);
1562
 
 
1563
 
                /* Dispatch on atom. */
1564
 
                if (at == a_NET_WM_DESKTOP)
1565
 
                {
1566
 
                    /* Window changed desktop. */
1567
 
                    tk->desktop = get_net_wm_desktop(win);
1568
 
                    taskbar_redraw(tb);
1569
 
                }
1570
 
                else if ((at == XA_WM_NAME) || (at == a_NET_WM_NAME) || (at == a_NET_WM_VISIBLE_NAME))
1571
 
                {
1572
 
                    /* Window changed name. */
1573
 
                    task_set_names(tk, at);
1574
 
                    if (tk->res_class != NULL)
1575
 
                    {
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);
1580
 
                    }
1581
 
                }
1582
 
                else if (at == XA_WM_CLASS)
1583
 
                {
1584
 
                    /* Window changed class. */
1585
 
                    task_set_class(tk);
1586
 
                    taskbar_redraw(tb);
1587
 
                }
1588
 
                else if (at == a_WM_STATE)
1589
 
                {
1590
 
                    /* Window changed state. */
1591
 
                    tk->iconified = (get_wm_state(win) == IconicState);
1592
 
                    task_draw_label(tk);
1593
 
                }
1594
 
                else if (at == XA_WM_HINTS)
1595
 
                {
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);
1599
 
                    if (pixbuf != NULL)
1600
 
                    {
1601
 
                        gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
1602
 
                        g_object_unref(pixbuf);
1603
 
                    }
1604
 
 
1605
 
                    if (tb->use_urgency_hint)
1606
 
                    {
1607
 
                        tk->urgency = task_has_urgency(tk);
1608
 
                        if (tk->urgency)
1609
 
                            task_set_urgency(tk);
1610
 
                        else
1611
 
                            task_clear_urgency(tk);
1612
 
                    }
1613
 
                }
1614
 
                else if (at == a_NET_WM_STATE)
1615
 
                {
1616
 
                    /* Window changed EWMH state. */
1617
 
                    NetWMState nws;
1618
 
                    get_net_wm_state(tk->win, &nws);
1619
 
                    if ( ! accept_net_wm_state(&nws))
1620
 
                    {
1621
 
                        task_delete(tb, tk, TRUE);
1622
 
                        taskbar_redraw(tb);
1623
 
                    }
1624
 
                }
1625
 
                else if (at == a_NET_WM_ICON)
1626
 
                {
1627
 
                    /* Window changed EWMH icon. */
1628
 
                    GdkPixbuf * pixbuf = task_update_icon(tb, tk, a_NET_WM_ICON);
1629
 
                    if (pixbuf != NULL)
1630
 
                    {
1631
 
                        gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
1632
 
                        g_object_unref(pixbuf);
1633
 
                    }
1634
 
                }
1635
 
                else if (at == a_NET_WM_WINDOW_TYPE)
1636
 
                {
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))
1641
 
                    {
1642
 
                        task_delete(tb, tk, TRUE);
1643
 
                        taskbar_redraw(tb);
1644
 
                    }
1645
 
                }
1646
 
                XSetErrorHandler(previous_error_handler);
1647
 
            }
1648
 
        }
1649
 
    }
1650
 
}
1651
 
 
1652
 
/* GDK event filter. */
1653
 
static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, TaskbarPlugin * tb)
1654
 
{
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;
1659
 
}
1660
 
 
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)
1663
 
{
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);
1668
 
}
1669
 
 
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)
1672
 
{
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);
1677
 
}
1678
 
 
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)
1681
 
{
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);
1686
 
}
1687
 
 
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)
1690
 
{
1691
 
    XIconifyWindow(GDK_DISPLAY(), tb->menutask->win, DefaultScreen(GDK_DISPLAY()));
1692
 
    task_group_menu_destroy(tb);
1693
 
}
1694
 
 
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)
1697
 
{
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);
1701
 
}
1702
 
 
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)
1705
 
{
1706
 
    Xclimsgwm(tb->menutask->win, a_WM_PROTOCOLS, a_WM_DELETE_WINDOW);
1707
 
    task_group_menu_destroy(tb);
1708
 
}
1709
 
 
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)
1713
 
{
1714
 
    /* Deallocate old menu if present. */
1715
 
    if (tb->menu != NULL)
1716
 
        gtk_widget_destroy(tb->menu);
1717
 
 
1718
 
    /* Allocate menu. */
1719
 
    GtkWidget * menu = gtk_menu_new();
1720
 
 
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);
1725
 
 
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);
1730
 
 
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);
1735
 
 
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);
1740
 
 
1741
 
    /* If multiple desktops are supported, add menu items to select them. */
1742
 
    if (tb->number_of_desktops > 1)
1743
 
    {
1744
 
        char label[128];
1745
 
 
1746
 
        /* Allocate submenu. */
1747
 
        GtkWidget * workspace_menu = gtk_menu_new();
1748
 
 
1749
 
        /* Loop over all desktops. */
1750
 
        int i;
1751
 
        for (i = 1; i <= tb->number_of_desktops; i++)
1752
 
        {
1753
 
            /* For the first 9 desktops, allow the desktop number as a keyboard shortcut. */
1754
 
            if (i <= 9)
1755
 
            {
1756
 
                g_snprintf(label, sizeof(label), _("Workspace _%d"), i);
1757
 
                mi = gtk_menu_item_new_with_mnemonic(label);
1758
 
            }
1759
 
            else
1760
 
            {
1761
 
                g_snprintf(label, sizeof(label), _("Workspace %d"), i);
1762
 
                mi = gtk_menu_item_new_with_label(label);
1763
 
            }
1764
 
 
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);
1769
 
        }
1770
 
 
1771
 
        /* Add a separator. */
1772
 
        gtk_menu_shell_append(GTK_MENU_SHELL(workspace_menu), gtk_separator_menu_item_new());
1773
 
 
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);
1779
 
 
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);
1784
 
    }
1785
 
 
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)
1789
 
    {
1790
 
        gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
1791
 
        gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
1792
 
    }
1793
 
    else
1794
 
    {
1795
 
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
1796
 
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1797
 
    }
1798
 
    g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_close_window, tb);
1799
 
 
1800
 
    gtk_widget_show_all(menu);
1801
 
    tb->menu = menu;
1802
 
}
1803
 
 
1804
 
/* Handler for "window-manager-changed" event. */
1805
 
static void taskbar_window_manager_changed(GdkScreen * screen, TaskbarPlugin * tb)
1806
 
{
1807
 
    /* Force re-evaluation of use_net_active. */
1808
 
    tb->net_active_checked = FALSE;
1809
 
}
1810
 
 
1811
 
/* Build graphic elements needed for the taskbar. */
1812
 
static void taskbar_build_gui(Plugin * p)
1813
 
{
1814
 
    TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
1815
 
 
1816
 
    /* Set up style for taskbar. */
1817
 
    gtk_rc_parse_string(taskbar_rc);
1818
 
 
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);
1824
 
#else
1825
 
    GTK_WIDGET_SET_FLAGS(p->pwid, GTK_NO_WINDOW);
1826
 
#endif
1827
 
    gtk_widget_set_name(p->pwid, "taskbar");
1828
 
 
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);
1834
 
 
1835
 
    /* Add GDK event filter. */
1836
 
    gdk_window_add_filter(NULL, (GdkFilterFunc) taskbar_event_filter, tb);
1837
 
 
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);
1840
 
 
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);
1848
 
 
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);
1853
 
 
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);
1856
 
}
1857
 
 
1858
 
/* Plugin constructor. */
1859
 
static int taskbar_constructor(Plugin * p, char ** fp)
1860
 
{
1861
 
    /* Allocate plugin context and set into Plugin private data pointer. */
1862
 
    TaskbarPlugin * tb = g_new0(TaskbarPlugin, 1);
1863
 
    tb->plug = p;
1864
 
    p->priv = tb;
1865
 
 
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;
1872
 
    tb->spacing           = 1;
1873
 
    tb->use_mouse_wheel   = TRUE;
1874
 
    tb->use_urgency_hint  = TRUE;
1875
 
    tb->grouped_tasks     = FALSE;
1876
 
 
1877
 
    /* Process configuration file. */
1878
 
    line s;
1879
 
    s.len = 256;
1880
 
    if( fp )
1881
 
    {
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);
1885
 
                return 0;
1886
 
            }
1887
 
            if (s.type == LINE_VAR)
1888
 
            {
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 */
1894
 
                    ;
1895
 
                else if (g_ascii_strcasecmp(s.t[0], "ShowIconified") == 0)              /* For backward compatibility */
1896
 
                    ;
1897
 
                else if (g_ascii_strcasecmp(s.t[0], "ShowMapped") == 0)                 /* For backward compatibility */
1898
 
                    ;
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);
1913
 
                else
1914
 
                    ERR( "taskbar: unknown var %s\n", s.t[0]);
1915
 
            }
1916
 
            else
1917
 
            {
1918
 
                ERR( "taskbar: illegal in this context %s\n", s.str);
1919
 
                return 0;
1920
 
            }
1921
 
        }
1922
 
    }
1923
 
 
1924
 
    /* Build the graphic elements. */
1925
 
    taskbar_build_gui(p);
1926
 
 
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);
1930
 
    return 1;
1931
 
}
1932
 
 
1933
 
/* Plugin destructor. */
1934
 
static void taskbar_destructor(Plugin * p)
1935
 
{
1936
 
    TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
1937
 
 
1938
 
    /* Remove GDK event filter. */
1939
 
    gdk_window_remove_filter(NULL, (GdkFilterFunc) taskbar_event_filter, tb);
1940
 
 
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);
1946
 
 
1947
 
    /* Remove "window-manager-changed" handler. */
1948
 
    g_signal_handlers_disconnect_by_func(gtk_widget_get_screen(p->pwid), taskbar_window_manager_changed, tb);
1949
 
 
1950
 
    /* Deallocate task list. */
1951
 
    while (tb->task_list != NULL)
1952
 
        task_delete(tb, tb->task_list, TRUE);
1953
 
 
1954
 
    /* Deallocate class list. */
1955
 
    while (tb->res_class_list != NULL)
1956
 
    {
1957
 
        TaskClass * tc = tb->res_class_list;
1958
 
        tb->res_class_list = tc->res_class_flink;
1959
 
        g_free(tc->res_class);
1960
 
        g_free(tc);
1961
 
    }
1962
 
 
1963
 
    /* Deallocate other memory. */
1964
 
    gtk_widget_destroy(tb->menu);
1965
 
    g_free(tb);
1966
 
}
1967
 
 
1968
 
/* Callback from configuration dialog mechanism to apply the configuration. */
1969
 
static void taskbar_apply_configuration(Plugin * p)
1970
 
{
1971
 
    TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
1972
 
 
1973
 
    /* Update style on taskbar. */
1974
 
    taskbar_update_style(tb);
1975
 
 
1976
 
    /* Update styles on each button. */
1977
 
    Task * tk;
1978
 
    for (tk = tb->task_list; tk != NULL; tk = tk->task_flink)
1979
 
        task_update_style(tk, tb);
1980
 
 
1981
 
    /* Refetch the client list and redraw. */
1982
 
    recompute_group_visibility_on_current_desktop(tb);
1983
 
    taskbar_net_client_list(NULL, tb);
1984
 
}
1985
 
 
1986
 
/* Display the configuration dialog. */
1987
 
static void taskbar_configure(Plugin * p, GtkWindow * parent)
1988
 
{
1989
 
    TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
1990
 
    GtkWidget* dlg = create_generic_config_dlg(
1991
 
        _(p->class->name),
1992
 
        GTK_WIDGET(parent),
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,
2003
 
        NULL);
2004
 
    gtk_window_present(GTK_WINDOW(dlg));
2005
 
}
2006
 
 
2007
 
/* Save the configuration to the configuration file. */
2008
 
static void taskbar_save_configuration(Plugin * p, FILE * fp)
2009
 
{
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);
2020
 
}
2021
 
 
2022
 
/* Callback when panel configuration changes. */
2023
 
static void taskbar_panel_configuration_changed(Plugin * p)
2024
 
{
2025
 
    TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
2026
 
    taskbar_update_style(tb);
2027
 
    taskbar_make_menu(tb);
2028
 
 
2029
 
    /* If the icon size changed, refetch all the icons. */
2030
 
    if (tb->plug->panel->icon_size != tb->icon_size)
2031
 
    {
2032
 
        tb->icon_size = tb->plug->panel->icon_size;
2033
 
        Task * tk;
2034
 
        for (tk = tb->task_list; tk != NULL; tk = tk->task_flink)
2035
 
        {
2036
 
            GdkPixbuf * pixbuf = task_update_icon(tb, tk, None);
2037
 
            if (pixbuf != NULL)
2038
 
            {
2039
 
                gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
2040
 
                g_object_unref(pixbuf);
2041
 
            }
2042
 
        }
2043
 
    }
2044
 
 
2045
 
    /* Redraw all the labels.  Icon size or font color may have changed. */
2046
 
    taskbar_redraw(tb);
2047
 
}
2048
 
 
2049
 
/* Plugin descriptor. */
2050
 
PluginClass taskbar_plugin_class = {
2051
 
 
2052
 
    PLUGINCLASS_VERSIONING,
2053
 
 
2054
 
    type : "taskbar",
2055
 
    name : N_("Task Bar (Window List)"),
2056
 
    version: "1.0",
2057
 
    description : N_("Taskbar shows all opened windows and allow to iconify them, shade or get focus"),
2058
 
 
2059
 
    /* Stretch is available and default for this plugin. */
2060
 
    expand_available : TRUE,
2061
 
    expand_default : TRUE,
2062
 
 
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
2068
 
 
2069
 
};
2070
 
 
2071
 
/* vim: set sw=4 et sts=4 : */