~ubuntu-branches/ubuntu/karmic/lxpanel/karmic

« back to all changes in this revision

Viewing changes to src/plugins/taskbar.c

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Lee (李健秋)
  • Date: 2009-08-10 03:13:50 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20090810031350-wnq2zxam30t340re
Tags: 0.5.3-1
* New upstream release
  - Added documentation of \n to dclock dialog
  - Added configuration dialog to deskno plugin
  - Improved usability on narrow vertical panel support, now it's possible
    to configure a vertical panel as small as 16 pixels.
* debian/control:
  - Recommends on manpages-dev to be able to have 'man 3 strftime' to
    check the format codes of digital clock plugin. (Closes:#493245)

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
#include "plugin.h"
35
35
#include "icon.xpm"
36
36
#include "gtkbar.h"
 
37
#include "icon-grid.h"
37
38
 
38
39
/*
39
40
 * 2006.09.10 modified by Hong Jen Yee (PCMan) pcman.tw (AT) gmail.com
46
47
#include "dbg.h"
47
48
 
48
49
struct _taskbar;
49
 
typedef struct _task{
50
 
    struct _taskbar *tb;
51
 
    Window win;
52
 
    char *name, *iname;
53
 
    GtkWidget *button, *label;
54
 
    GtkWidget *image;
55
 
 
56
 
    GdkPixbuf *pixbuf;
57
 
 
58
 
    int refcount;
59
 
    XClassHint ch;
60
 
    int pos_x;
61
 
    int width;
62
 
    int desktop;
63
 
    NetWMState nws;
64
 
    NetWMWindowType nwwt;
65
 
    guint flash_timeout;
66
 
    unsigned int focused:1;
67
 
    unsigned int iconified:1;
68
 
    unsigned int urgency:1;
69
 
    unsigned int using_netwm_icon:1;
70
 
    unsigned int flash:1;
71
 
    unsigned int flash_state:1;
72
 
} task;
73
 
 
74
 
typedef struct _taskbar{
75
 
    Plugin *plug;
76
 
    Window *wins;
77
 
    Window topxwin;
78
 
    int win_num;
79
 
    GHashTable  *task_list;
80
 
    GtkWidget *bar, *menu;
81
 
    GdkPixbuf *gen_pixbuf;
82
 
    GtkStateType normal_state;
83
 
    GtkStateType focused_state;
84
 
    int num_tasks;
85
 
    int vis_task_num;
86
 
    int spacing;
87
 
    int cur_desk;
88
 
    task *focused;
89
 
    task *ptk;
90
 
    task *menutask;
91
 
    char **desk_names;
92
 
    int desk_namesno;
93
 
    int desk_num;
94
 
    guint dnd_activate;
95
 
 
96
 
    gboolean iconsize;
97
 
    int task_width_max;
98
 
    gboolean accept_skip_pager;// : 1;
99
 
    gboolean show_iconified;// : 1;
100
 
    gboolean show_mapped;// : 1;
101
 
    gboolean show_all_desks;// : 1;
102
 
    gboolean tooltips;// : 1;
103
 
    gboolean icons_only;// : 1;
104
 
    gboolean use_mouse_wheel;// : 1;
105
 
    gboolean use_urgency_hint;// : 1;
106
 
    gboolean flat_button;
107
 
} 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
} TaskbarPlugin;
108
114
 
109
115
static gchar *taskbar_rc = "style 'taskbar-style'\n"
110
116
"{\n"
111
 
"GtkWidget::focus-line-width = 0\n"
112
 
"GtkWidget::focus-padding = 0\n"
113
 
"GtkButton::default-border = { 0, 0, 0, 0 }\n"
114
 
"GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
115
 
"GtkButton::default_border = { 0, 0, 0, 0 }\n"
116
 
"GtkButton::default_outside_border = { 0, 0, 0, 0 }\n"
 
117
"GtkWidget::focus-padding=0\n" /* FIXME: seem to fix #2821771, not sure if this is ok. */
 
118
"GtkWidget::focus-line-width=0\n"
 
119
"GtkWidget::focus-padding=0\n"
 
120
"GtkButton::default-border={0,0,0,0}\n"
 
121
"GtkButton::default-outside-border={0,0,0,0}\n"
 
122
"GtkButton::inner-border={0,0,0,0}\n" /* added in gtk+ 2.10 */
117
123
"}\n"
118
124
"widget '*.taskbar.*' style 'taskbar-style'";
119
125
 
120
 
static gboolean use_net_active=FALSE;
121
 
 
122
 
#define DRAG_ACTIVE_DELAY   1000
123
 
#define TASK_WIDTH_MAX   200
124
 
#define TASK_PADDING     4
125
 
 
126
 
#define ALL_WORKSPACES (0xFFFFFFFF)
127
 
 
128
 
static void tk_display(taskbar *tb, task *tk);
129
 
static void tb_propertynotify(taskbar *tb, XEvent *ev);
130
 
static GdkFilterReturn tb_event_filter( XEvent *, GdkEvent *, taskbar *);
131
 
static void taskbar_destructor(Plugin *p);
132
 
 
133
 
static gboolean tk_has_urgency( task* tk );
134
 
 
135
 
static void tk_flash_window( task *tk );
136
 
static void tk_unflash_window( task *tk );
137
 
static void tk_raise_window( task *tk, guint32 time );
138
 
 
139
 
static void
140
 
update_label_orient( GtkWidget* child, gpointer user_data );
141
 
 
142
 
 
143
 
#define TASK_VISIBLE(tb, tk) \
144
 
 ((tk)->desktop == (tb)->cur_desk || (tk)->desktop == -1 /* 0xFFFFFFFF */ )
145
 
 
146
 
static int
147
 
task_visible(taskbar *tb, task *tk)
148
 
{
149
 
    ENTER;
150
 
    if (tk->desktop != -1 && !tb->show_all_desks && tk->desktop != tb->cur_desk)
151
 
        RET(0);
152
 
    if (tk->iconified) {
153
 
        if (!tb->show_iconified)
154
 
            RET(0);
155
 
    } else {
156
 
        if (!tb->show_mapped)
157
 
            RET(0);
158
 
    }
159
 
    RET(1);
160
 
}
161
 
 
162
 
static int
163
 
accept_net_wm_state(NetWMState *nws, int accept_skip_pager)
164
 
{
165
 
    ENTER;
166
 
    RET(!(nws->skip_taskbar || (accept_skip_pager && nws->skip_pager)));
167
 
}
168
 
 
169
 
static int
170
 
accept_net_wm_window_type(NetWMWindowType *nwwt)
171
 
{
172
 
    ENTER;
173
 
    RET(!(nwwt->desktop || nwwt->dock || nwwt->splash));
174
 
}
175
 
 
176
 
 
177
 
 
178
 
inline static void
179
 
tk_free_names(task *tk)
180
 
{
181
 
    ENTER;
182
 
    DBG("tk->name %s\n", tk->name);
183
 
    DBG("tk->iname %s\n", tk->iname);
 
126
#define DRAG_ACTIVE_DELAY    1000
 
127
#define TASK_WIDTH_MAX       200
 
128
#define TASK_PADDING         4
 
129
#define ALL_WORKSPACES       (-1)
 
130
#define ICON_ONLY_EXTRA      6          /* Amount needed to have button lay out symmetrically */
 
131
#define BUTTON_HEIGHT_EXTRA  4          /* Amount needed to have button not clip icon */
 
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 GdkColormap * get_colormap_from_pixmap(GdkPixmap * pixmap);
 
151
static GdkPixbuf * _wnck_gdk_pixbuf_get_from_pixmap(
 
152
    GdkPixbuf * dest, Pixmap xpixmap, int src_x, int src_y, int dest_x, int dest_y, int width, int height);
 
153
static GdkPixbuf * apply_mask(GdkPixbuf * pixbuf, GdkPixbuf * mask);
 
154
static GdkPixbuf * get_wm_icon(Window task_win, int required_width, int required_height, Atom source, Atom * current_source);
 
155
static GdkPixbuf * task_update_icon(TaskbarPlugin * tb, Task * tk, Atom source);
 
156
static gboolean flash_window_timeout(Task * tk);
 
157
static void task_set_urgency(Task * tk);
 
158
static void task_clear_urgency(Task * tk);
 
159
static void task_raise_window(Task * tk, guint32 time);
 
160
static void taskbar_popup_set_position(GtkWidget * menu, gint * px, gint * py, gboolean * push_in, gpointer data);
 
161
static void task_group_menu_destroy(TaskbarPlugin * tb);
 
162
static gboolean taskbar_task_control_event(GtkWidget * widget, GdkEventButton * event, Task * tk, gboolean popup_menu);
 
163
static gboolean taskbar_button_press_event(GtkWidget * widget, GdkEventButton * event, Task * tk);
 
164
static gboolean taskbar_popup_activate_event(GtkWidget * widget, GdkEventButton * event, Task * tk);
 
165
static gboolean taskbar_button_drag_motion_timeout(Task * tk);
 
166
static gboolean taskbar_button_drag_motion(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, Task * tk);
 
167
static void taskbar_button_enter(GtkWidget * widget, Task * tk);
 
168
static void taskbar_button_leave(GtkWidget * widget, Task * tk);
 
169
static gboolean taskbar_button_scroll_event(GtkWidget * widget, GdkEventScroll * event, Task * tk);
 
170
static void taskbar_button_size_allocate(GtkWidget * btn, GtkAllocation * alloc, Task * tk);
 
171
static void taskbar_update_style(TaskbarPlugin * tb);
 
172
static void task_update_style(Task * tk, TaskbarPlugin * tb);
 
173
static void task_build_gui(TaskbarPlugin * tb, Task * tk);
 
174
static void taskbar_net_client_list(GtkWidget * widget, TaskbarPlugin * tb);
 
175
static void taskbar_net_current_desktop(GtkWidget * widget, TaskbarPlugin * tb);
 
176
static void taskbar_net_number_of_desktops(GtkWidget * widget, TaskbarPlugin * tb);
 
177
static void taskbar_net_active_window(GtkWidget * widget, TaskbarPlugin * tb);
 
178
static gboolean task_has_urgency(Task * tk);
 
179
static void taskbar_property_notify_event(TaskbarPlugin * tb, XEvent *ev);
 
180
static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, TaskbarPlugin * tb);
 
181
static void menu_raise_window(GtkWidget * widget, TaskbarPlugin * tb);
 
182
static void menu_restore_window(GtkWidget * widget, TaskbarPlugin * tb);
 
183
static void menu_maximize_window(GtkWidget * widget, TaskbarPlugin * tb);
 
184
static void menu_iconify_window(GtkWidget * widget, TaskbarPlugin * tb);
 
185
static void menu_move_to_workspace(GtkWidget * widget, TaskbarPlugin * tb);
 
186
static void menu_close_window(GtkWidget * widget, TaskbarPlugin * tb);
 
187
static void taskbar_make_menu(TaskbarPlugin * tb);
 
188
static void taskbar_build_gui(Plugin * p);
 
189
static gboolean net_active_supported(void);
 
190
static int taskbar_constructor(Plugin * p, char ** fp);
 
191
static void taskbar_destructor(Plugin * p);
 
192
static void taskbar_apply_configuration(Plugin * p);
 
193
static void taskbar_configure(Plugin * p, GtkWindow * parent);
 
194
static void taskbar_save_configuration(Plugin * p, FILE * fp);
 
195
static void taskbar_panel_configuration_changed(Plugin * p);
 
196
 
 
197
/* Set an urgency timer on a task. */
 
198
static void set_timer_on_task(Task * tk)
 
199
{
 
200
    gint interval;
 
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
        }
 
281
    }
 
282
}
 
283
 
 
284
/* Recompute the visible task for all classes when the desktop changes. */
 
285
static void recompute_group_visibility_on_current_desktop(TaskbarPlugin * tb)
 
286
{
 
287
    TaskClass * tc;
 
288
    for (tc = tb->res_class_list; tc != NULL; tc = tc->res_class_flink)
 
289
    {
 
290
        recompute_group_visibility_for_class(tb, tc);
 
291
    }
 
292
}
 
293
 
 
294
/* Draw the label and tooltip on a taskbar button. */
 
295
static void task_draw_label(Task * tk)
 
296
{
 
297
    TaskClass * tc = tk->res_class;
 
298
    gboolean bold_style = (((tk->entered_state) && (tk->tb->flat_button)) || tk->flash_state);
 
299
    if ((tk->tb->grouped_tasks) && (tc != NULL) && (tc->visible_task == tk) && (tc->visible_count > 1))
 
300
        {
 
301
        char * label = g_strdup_printf("(%d) %s", tc->visible_count, tc->visible_name);
 
302
        gtk_widget_set_tooltip_text(tk->button, label);
 
303
        panel_draw_label_text(tk->tb->plug->panel, tk->label, label, bold_style, tk->tb->flat_button);
 
304
        g_free(label);
 
305
        }
 
306
    else
 
307
    {
 
308
        char * name = tk->iconified ? tk->name_iconified : tk->name;
 
309
        if (tk->tb->tooltips)
 
310
            gtk_widget_set_tooltip_text(tk->button, name);
 
311
        panel_draw_label_text(tk->tb->plug->panel, tk->label, name, bold_style, tk->tb->flat_button);
 
312
    }
 
313
}
 
314
 
 
315
/* Determine if a task is visible. */
 
316
static gboolean task_is_visible(TaskbarPlugin * tb, Task * tk)
 
317
{
 
318
    /* Not visible due to grouping. */
 
319
    if ((tb->grouped_tasks) && (tk->res_class != NULL) && (tk->res_class->visible_task != tk))
 
320
        return FALSE;
 
321
 
 
322
    /* Desktop placement. */
 
323
    return task_is_visible_on_current_desktop(tb, tk);
 
324
}
 
325
 
 
326
/* Redraw a task button. */
 
327
static void task_button_redraw(Task * tk, TaskbarPlugin * tb)
 
328
{
 
329
    if (task_is_visible(tb, tk))
 
330
    {
 
331
        task_draw_label(tk);
 
332
        icon_grid_set_visible(tb->icon_grid, tk->button, TRUE);
 
333
    }
 
334
    else
 
335
        icon_grid_set_visible(tb->icon_grid, tk->button, FALSE);
 
336
}
 
337
 
 
338
/* Redraw all tasks in the taskbar. */
 
339
static void taskbar_redraw(TaskbarPlugin * tb)
 
340
{
 
341
    Task * tk;
 
342
    for (tk = tb->task_list; tk != NULL; tk = tk->task_flink)
 
343
        task_button_redraw(tk, tb);
 
344
}
 
345
 
 
346
/* Determine if a task should be visible given its NET_WM_STATE. */
 
347
static gboolean accept_net_wm_state(NetWMState * nws)
 
348
{
 
349
    return ( ! (nws->skip_taskbar));
 
350
}
 
351
 
 
352
/* Determine if a task should be visible given its NET_WM_WINDOW_TYPE. */
 
353
static gboolean accept_net_wm_window_type(NetWMWindowType * nwwt)
 
354
{
 
355
    return ( ! ((nwwt->desktop) || (nwwt->dock) || (nwwt->splash)));
 
356
}
 
357
 
 
358
/* Free the names associated with a task. */
 
359
static void task_free_names(Task * tk)
 
360
{
184
361
    g_free(tk->name);
185
 
    g_free(tk->iname);
186
 
 
187
 
    tk->name = tk->iname = NULL;
188
 
    RET();
 
362
    g_free(tk->name_iconified);
 
363
    tk->name = tk->name_iconified = NULL;
189
364
}
190
365
 
191
 
static void
192
 
tk_set_names(task *tk)
 
366
/* Set the names associated with a task.
 
367
 * This is expected to be the same as the title the window manager is displaying. */
 
368
static void task_set_names(Task * tk, Atom source)
193
369
{
194
 
    char *name;
195
 
 
196
 
    ENTER;
197
 
    tk_free_names(tk);
198
 
 
199
 
    /*name = get_utf8_property(tk->win,  a_NET_WM_VISIBLE_NAME);
200
 
    DBG2("a_NET_WM_VISIBLE_NAME:%s\n", name);
201
 
    if (!name) {
202
 
    */
203
 
    name = get_utf8_property(tk->win,  a_NET_WM_NAME);
204
 
    DBG("a_NET_WM_NAME:%s\n", name);
205
 
    if (!name) {
 
370
    char * name = NULL;
 
371
 
 
372
    /* Try _NET_WM_VISIBLE_NAME, which supports UTF-8.
 
373
     * If it is set, the window manager is displaying it as the window title. */
 
374
    if ((source == None) || (source == a_NET_WM_VISIBLE_NAME))
 
375
    {
 
376
        name = get_utf8_property(tk->win,  a_NET_WM_VISIBLE_NAME);
 
377
        if (name != NULL)
 
378
            tk->name_source = a_NET_WM_VISIBLE_NAME;
 
379
    }
 
380
 
 
381
    /* Try _NET_WM_NAME, which supports UTF-8, but do not overwrite _NET_WM_VISIBLE_NAME. */
 
382
    if ((name == NULL)
 
383
    && ((source == None) || (source == a_NET_WM_NAME))
 
384
    && ((tk->name_source == None) || (tk->name_source == a_NET_WM_NAME) || (tk->name_source == XA_WM_NAME)))
 
385
    {
 
386
        name = get_utf8_property(tk->win,  a_NET_WM_NAME);
 
387
        if (name != NULL)
 
388
            tk->name_source = a_NET_WM_NAME;
 
389
    }
 
390
 
 
391
    /* Try WM_NAME, which supports only ISO-8859-1, but do not overwrite _NET_WM_VISIBLE_NAME or _NET_WM_NAME. */
 
392
    if ((name == NULL)
 
393
    && ((source == None) || (source == XA_WM_NAME))
 
394
    && ((tk->name_source == None) || (tk->name_source == XA_WM_NAME)))
 
395
    {
206
396
        name = get_textproperty(tk->win,  XA_WM_NAME);
207
 
        DBG("XA_WM_NAME:%s\n", name);
208
 
    }
209
 
 
210
 
    if (name) {
211
 
    tk->name = g_strdup_printf(" %s ", name);
212
 
    tk->iname = g_strdup_printf("[%s]", name);
213
 
    g_free(name);
214
 
        name = tk->iconified ? tk->iname : tk->name;
215
 
    }
216
 
    gtk_label_set_text(GTK_LABEL(tk->label), name);
217
 
    if (tk->tb->tooltips)
218
 
        gtk_widget_set_tooltip_text( tk->button, tk->name );
219
 
    RET();
220
 
}
221
 
 
222
 
 
223
 
 
224
 
static task *
225
 
find_task (taskbar * tb, Window win)
226
 
{
227
 
    ENTER;
228
 
    RET(g_hash_table_lookup(tb->task_list, &win));
229
 
}
230
 
 
231
 
 
232
 
static void
233
 
del_task (taskbar * tb, task *tk, int hdel)
234
 
{
235
 
    ENTER;
236
 
    DBG("deleting(%d)  %08x %s\n", hdel, tk->win, tk->name);
237
 
    if( tk->flash_timeout )
238
 
        g_source_remove( tk->flash_timeout );
239
 
    gtk_widget_destroy(tk->button);
240
 
    --tb->num_tasks;
241
 
    tk_free_names(tk);
 
397
        if (name != NULL)
 
398
            tk->name_source = XA_WM_NAME;
 
399
    }
 
400
 
 
401
    /* Set the name into the task context, and also on the tooltip. */
 
402
    if (name != NULL)
 
403
    {
 
404
        task_free_names(tk);
 
405
        tk->name = g_strdup(name);
 
406
        tk->name_iconified = g_strdup_printf("[%s]", name);
 
407
        g_free(name);
 
408
 
 
409
        /* Redraw the button. */
 
410
        task_button_redraw(tk, tk->tb);
 
411
    }
 
412
}
 
413
 
 
414
/* Unlink a task from the class list because its class changed or it was deleted. */
 
415
static void task_unlink_class(Task * tk)
 
416
{
 
417
    TaskClass * tc = tk->res_class;
 
418
    if (tc != NULL)
 
419
    {
 
420
        /* Remove from per-class task list. */
 
421
        if (tc->res_class_head == tk)
 
422
        {
 
423
            /* Removing the head of the list.  This causes a new task to be the visible task, so we redraw. */
 
424
            tc->res_class_head = tk->res_class_flink;
 
425
            if (tc->res_class_head != NULL)
 
426
                task_button_redraw(tc->res_class_head, tk->tb);
 
427
        }
 
428
        else
 
429
        {
 
430
            /* Locate the task and its predecessor in the list and then remove it.  For safety, ensure it is found. */
 
431
            Task * tk_pred = NULL;
 
432
            Task * tk_cursor;
 
433
            for (
 
434
              tk_cursor = tc->res_class_head;
 
435
              ((tk_cursor != NULL) && (tk_cursor != tk));
 
436
              tk_pred = tk_cursor, tk_cursor = tk_cursor->res_class_flink) ;
 
437
            if (tk_cursor == tk)
 
438
                tk_pred->res_class_flink = tk->res_class_flink;
 
439
        }
 
440
 
 
441
        /* Recompute group visibility. */
 
442
        recompute_group_visibility_for_class(tk->tb, tc);
 
443
    }
 
444
}
 
445
 
 
446
/* Enter class with specified name. */
 
447
static TaskClass * taskbar_enter_res_class(TaskbarPlugin * tb, char * res_class, gboolean * name_consumed)
 
448
    {
 
449
    /* Find existing entry or insertion point. */
 
450
    *name_consumed = FALSE;
 
451
    TaskClass * tc_pred = NULL;
 
452
    TaskClass * tc;
 
453
    for (tc = tb->res_class_list; tc != NULL; tc_pred = tc, tc = tc->res_class_flink)
 
454
    {
 
455
        int status = strcmp(res_class, tc->res_class);
 
456
        if (status == 0)
 
457
            return tc;
 
458
        if (status < 0)
 
459
            break;
 
460
    }
 
461
 
 
462
    /* Insert new entry. */
 
463
    tc = g_new0(TaskClass, 1);
 
464
    tc->res_class = res_class;
 
465
    *name_consumed = TRUE;
 
466
    if (tc_pred == NULL)
 
467
    {
 
468
        tc->res_class_flink = tb->res_class_list;
 
469
        tb->res_class_list = tc;
 
470
    }
 
471
    else
 
472
    {
 
473
        tc->res_class_flink = tc_pred->res_class_flink;
 
474
        tc_pred->res_class_flink = tc;
 
475
    }
 
476
    return tc;
 
477
}
 
478
 
 
479
/* Set the class associated with a task. */
 
480
static void task_set_class(Task * tk)
 
481
{
 
482
    /* Read the WM_CLASS property. */
 
483
    XClassHint ch;
 
484
    ch.res_name = NULL;
 
485
    ch.res_class = NULL;
 
486
    XGetClassHint(GDK_DISPLAY(), tk->win, &ch);
 
487
 
 
488
    /* If the res_name was returned, free it.  We make no use of it at this time. */
 
489
    if (ch.res_name != NULL)
 
490
    {
 
491
        XFree(ch.res_name);
 
492
    }
 
493
 
 
494
    /* If the res_class was returned, process it.
 
495
     * This identifies the application that created the window and is the basis for taskbar grouping. */
 
496
    if (ch.res_class != NULL)
 
497
    {
 
498
        /* Convert the class to UTF-8 and enter it in the class table. */
 
499
        gchar * res_class = g_locale_to_utf8(ch.res_class, -1, NULL, NULL, NULL);
 
500
        gboolean name_consumed;
 
501
        TaskClass * tc = taskbar_enter_res_class(tk->tb, res_class, &name_consumed);
 
502
        if ( ! name_consumed) g_free(res_class);
 
503
 
 
504
        /* If the task changed class, update data structures. */
 
505
        TaskClass * old_tc = tk->res_class;
 
506
        if (old_tc != tc)
 
507
        {
 
508
            /* Unlink from previous class, if any. */
 
509
            task_unlink_class(tk);
 
510
 
 
511
            /* Add to end of per-class task list.  Do this to keep the popup menu in order of creation. */
 
512
            if (tc->res_class_head == NULL)
 
513
                tc->res_class_head = tk;
 
514
            else
 
515
            {
 
516
                Task * tk_pred;
 
517
                for (tk_pred = tc->res_class_head; tk_pred->res_class_flink != NULL; tk_pred = tk_pred->res_class_flink) ;
 
518
                tk_pred->res_class_flink = tk;
 
519
                task_button_redraw(tk, tk->tb);
 
520
            }
 
521
            tk->res_class = tc;
 
522
 
 
523
            /* Recompute group visibility. */
 
524
            recompute_group_visibility_for_class(tk->tb, tc);
 
525
        }
 
526
        XFree(ch.res_class);
 
527
    }
 
528
}
 
529
 
 
530
/* Look up a task in the task list. */
 
531
static Task * task_lookup(TaskbarPlugin * tb, Window win)
 
532
{
 
533
    Task * tk;
 
534
    for (tk = tb->task_list; tk != NULL; tk = tk->task_flink)
 
535
        {
 
536
        if (tk->win == win)
 
537
            return tk;
 
538
        if (tk->win > win)
 
539
            break;
 
540
        }
 
541
    return NULL;
 
542
}
 
543
 
 
544
/* Delete a task and optionally unlink it from the task list. */
 
545
static void task_delete(TaskbarPlugin * tb, Task * tk, gboolean unlink)
 
546
{
 
547
    /* If we think this task had focus, remove that. */
242
548
    if (tb->focused == tk)
243
549
        tb->focused = NULL;
244
 
    if (hdel)
245
 
        g_hash_table_remove(tb->task_list, &tk->win);
 
550
 
 
551
    /* Deallocate structures. */
 
552
    icon_grid_remove(tb->icon_grid, tk->button);
 
553
    task_free_names(tk);
 
554
    task_unlink_class(tk);
 
555
 
 
556
    /* If there is an urgency timeout, remove it. */
 
557
    if (tk->flash_timeout != 0)
 
558
        g_source_remove(tk->flash_timeout);
 
559
 
 
560
    /* If requested, unlink the task from the task list.
 
561
     * If not requested, the caller will do this. */
 
562
    if (unlink)
 
563
    {
 
564
        if (tb->task_list == tk)
 
565
            tb->task_list = tk->task_flink;
 
566
        else
 
567
        {
 
568
            /* Locate the task and its predecessor in the list and then remove it.  For safety, ensure it is found. */
 
569
            Task * tk_pred = NULL;
 
570
            Task * tk_cursor;
 
571
            for (
 
572
              tk_cursor = tb->task_list;
 
573
              ((tk_cursor != NULL) && (tk_cursor != tk));
 
574
              tk_pred = tk_cursor, tk_cursor = tk_cursor->task_flink) ;
 
575
            if (tk_cursor == tk)
 
576
                tk_pred->task_flink = tk->task_flink;
 
577
        }
 
578
    }
 
579
 
 
580
    /* Deallocate the task structure. */
246
581
    g_free(tk);
247
 
    RET();
248
582
}
249
583
 
250
 
 
251
 
 
252
 
static GdkColormap*
253
 
get_cmap (GdkPixmap *pixmap)
 
584
/* Get the color map from a pixmap.
 
585
 * From libwnck, Copyright (C) 2001 Havoc Pennington. */
 
586
static GdkColormap * get_colormap_from_pixmap(GdkPixmap * pixmap)
254
587
{
255
 
  GdkColormap *cmap;
256
 
 
257
 
  ENTER;
258
 
  cmap = gdk_drawable_get_colormap (pixmap);
259
 
  if (cmap)
260
 
    g_object_ref (G_OBJECT (cmap));
261
 
 
262
 
  if (cmap == NULL)
 
588
    GdkColormap * colormap = gdk_drawable_get_colormap(pixmap);
 
589
    if (colormap != NULL)
 
590
        g_object_ref(G_OBJECT(colormap));
 
591
    else
263
592
    {
264
 
      if (gdk_drawable_get_depth (pixmap) == 1)
 
593
        if (gdk_drawable_get_depth(pixmap) == 1)
265
594
        {
266
 
          /* try null cmap */
267
 
          cmap = NULL;
 
595
            /* Try null colormap. */
 
596
            colormap = NULL;
268
597
        }
269
 
      else
 
598
        else
270
599
        {
271
 
          /* Try system cmap */
272
 
          GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (pixmap));
273
 
          cmap = gdk_screen_get_system_colormap (screen);
274
 
          g_object_ref (G_OBJECT (cmap));
 
600
            /* Try system colormap. */
 
601
            GdkScreen * screen = gdk_drawable_get_screen(GDK_DRAWABLE(pixmap));
 
602
            colormap = gdk_screen_get_system_colormap(screen);
 
603
            g_object_ref(G_OBJECT(colormap));
275
604
        }
276
605
    }
277
606
 
278
 
  /* Be sure we aren't going to blow up due to visual mismatch */
279
 
  if (cmap &&
280
 
      (gdk_colormap_get_visual (cmap)->depth !=
281
 
       gdk_drawable_get_depth (pixmap)))
282
 
    cmap = NULL;
 
607
    /* Be sure we aren't going to blow up due to visual mismatch. */
 
608
    if ((colormap != NULL) && (gdk_colormap_get_visual(colormap)->depth != gdk_drawable_get_depth(pixmap)))
 
609
        colormap = NULL;
283
610
 
284
 
  RET(cmap);
 
611
    return colormap;
285
612
}
286
613
 
287
614
/* These functions with the prefix wnck are taken from libwnck
288
615
 * Copyright (C) 2001 Havoc Pennington
289
616
 * slightly modified by Hong Jen Yee for LXPanel
290
617
 */
291
 
void
292
 
_wnck_change_workspace (Screen     *screen,
293
 
            Window      xwindow,
294
 
                        int         new_space)
295
 
{
296
 
  XEvent xev;
297
 
 
298
 
  xev.xclient.type = ClientMessage;
299
 
  xev.xclient.serial = 0;
300
 
  xev.xclient.send_event = True;
301
 
  xev.xclient.display = gdk_display;
302
 
  xev.xclient.window = xwindow;
303
 
  xev.xclient.message_type = a_NET_WM_DESKTOP;
304
 
  xev.xclient.format = 32;
305
 
  xev.xclient.data.l[0] = new_space;
306
 
  xev.xclient.data.l[1] = 0;
307
 
  xev.xclient.data.l[2] = 0;
308
 
  xev.xclient.data.l[3] = 0;
309
 
  xev.xclient.data.l[4] = 0;
310
 
 
311
 
  XSendEvent (gdk_display,
312
 
          RootWindowOfScreen (screen),
313
 
              False,
314
 
          SubstructureRedirectMask | SubstructureNotifyMask,
315
 
          &xev);
316
 
}
317
 
 
318
 
static GdkPixbuf*
319
 
_wnck_gdk_pixbuf_get_from_pixmap (GdkPixbuf   *dest,
320
 
                                  Pixmap       xpixmap,
321
 
                                  int          src_x,
322
 
                                  int          src_y,
323
 
                                  int          dest_x,
324
 
                                  int          dest_y,
325
 
                                  int          width,
326
 
                                  int          height)
327
 
{
328
 
    GdkDrawable *drawable;
329
 
    GdkPixbuf *retval;
330
 
    GdkColormap *cmap;
331
 
 
332
 
    ENTER;
333
 
    retval = NULL;
334
 
 
335
 
    drawable = gdk_xid_table_lookup (xpixmap);
336
 
 
337
 
    if (drawable)
338
 
        g_object_ref (G_OBJECT (drawable));
 
618
 
 
619
/* Get a pixbuf from a pixmap. */
 
620
static GdkPixbuf * _wnck_gdk_pixbuf_get_from_pixmap(
 
621
    GdkPixbuf * dest, Pixmap xpixmap, int src_x, int src_y, int dest_x, int dest_y, int width, int height)
 
622
{
 
623
    /* Initialize; get the drawable and its colormap. */
 
624
    GdkPixbuf * retval = NULL;
 
625
    GdkDrawable * drawable = gdk_xid_table_lookup(xpixmap);
 
626
    if (drawable != NULL)
 
627
        g_object_ref(G_OBJECT(drawable));
339
628
    else
340
 
        drawable = gdk_pixmap_foreign_new (xpixmap);
341
 
 
342
 
    cmap = get_cmap (drawable);
343
 
 
344
 
    /* GDK is supposed to do this but doesn't in GTK 2.0.2,
345
 
     * fixed in 2.0.3
346
 
     */
347
 
    if (width < 0)
348
 
        gdk_drawable_get_size (drawable, &width, NULL);
349
 
    if (height < 0)
350
 
        gdk_drawable_get_size (drawable, NULL, &height);
351
 
 
352
 
    retval = gdk_pixbuf_get_from_drawable (dest,
353
 
          drawable,
354
 
          cmap,
355
 
          src_x, src_y,
356
 
          dest_x, dest_y,
357
 
          width, height);
358
 
 
359
 
    if (cmap)
360
 
        g_object_unref (G_OBJECT (cmap));
361
 
    g_object_unref (G_OBJECT (drawable));
362
 
 
363
 
    RET(retval);
364
 
}
365
 
 
366
 
static GdkPixbuf*
367
 
apply_mask (GdkPixbuf *pixbuf,
368
 
            GdkPixbuf *mask)
369
 
{
370
 
  int w, h;
371
 
  int i, j;
372
 
  GdkPixbuf *with_alpha;
373
 
  guchar *src;
374
 
  guchar *dest;
375
 
  int src_stride;
376
 
  int dest_stride;
377
 
 
378
 
  ENTER;
379
 
  w = MIN (gdk_pixbuf_get_width (mask), gdk_pixbuf_get_width (pixbuf));
380
 
  h = MIN (gdk_pixbuf_get_height (mask), gdk_pixbuf_get_height (pixbuf));
381
 
 
382
 
  with_alpha = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
383
 
 
384
 
  dest = gdk_pixbuf_get_pixels (with_alpha);
385
 
  src = gdk_pixbuf_get_pixels (mask);
386
 
 
387
 
  dest_stride = gdk_pixbuf_get_rowstride (with_alpha);
388
 
  src_stride = gdk_pixbuf_get_rowstride (mask);
389
 
 
390
 
  i = 0;
391
 
  while (i < h)
392
 
    {
393
 
      j = 0;
394
 
      while (j < w)
395
 
        {
396
 
          guchar *s = src + i * src_stride + j * 3;
397
 
          guchar *d = dest + i * dest_stride + j * 4;
398
 
 
399
 
          /* s[0] == s[1] == s[2], they are 255 if the bit was set, 0
400
 
           * otherwise
401
 
           */
402
 
          if (s[0] == 0)
403
 
            d[3] = 0;   /* transparent */
404
 
          else
405
 
            d[3] = 255; /* opaque */
406
 
 
407
 
          ++j;
408
 
        }
409
 
 
410
 
      ++i;
411
 
    }
412
 
 
413
 
  RET(with_alpha);
414
 
}
415
 
 
416
 
/* set the iconification destination */
417
 
static void set_iconification_dest( Window win, int x, int y, int w, int h )
418
 
{
419
 
  guint32 data[4];
420
 
 
421
 
  data[0] = x;
422
 
  data[1] = y;
423
 
  data[2] = w;
424
 
  data[3] = h;
425
 
  XChangeProperty(GDK_DISPLAY(), win,
426
 
        gdk_x11_get_xatom_by_name("_NET_WM_ICON_GEOMETRY"),
427
 
        XA_CARDINAL, 32, PropModeReplace, (guchar *)&data, 4);
428
 
}
429
 
 
430
 
static GdkPixbuf *
431
 
get_netwm_icon(Window tkwin, int iw, int ih)
432
 
{
433
 
    XWMHints *wmhints;
434
 
    GdkPixbuf *ret = NULL;
435
 
 
436
 
    ENTER;
437
 
    wmhints = XGetWMHints( GDK_DISPLAY(), GDK_ROOT_WINDOW() );
438
 
 
439
 
    /*
440
 
     * IconPixmapHint flag indicates that wmhints->icon_pixmap contains
441
 
     * valid data that is already in pixdata format, so we could process
442
 
     * it and turn it into a GTK image.
443
 
     */
444
 
    if( wmhints && (wmhints->flags & IconPixmapHint) ) {
445
 
        GdkPixmap *gdkPixmap;
446
 
        GdkPixbuf *gdkPixbuf = NULL;
447
 
    GdkColormap *colormap;
448
 
 
449
 
        colormap = gdk_colormap_get_system();
450
 
 
451
 
        gdkPixmap = gdk_pixmap_foreign_new(wmhints->icon_pixmap);
452
 
        gdkPixbuf = gdk_pixbuf_get_from_drawable(
453
 
                    NULL, gdkPixmap, colormap, 0, 0, 0, 0, iw, ih );
454
 
        ret = gdk_pixbuf_scale_simple( gdkPixbuf, 24, 24, GDK_INTERP_BILINEAR );
455
 
        gdk_pixbuf_unref(gdkPixbuf);
456
 
    XFree(wmhints);
457
 
    }
458
 
 
459
 
    RET(ret);
460
 
}
461
 
 
462
 
static void
463
 
free_pixels(guchar *pixels, gpointer data)
464
 
{
465
 
    g_free(pixels);
466
 
}
467
 
 
468
 
static GdkPixbuf *
469
 
get_wm_icon(Window tkwin, int iw, int ih)
470
 
{
471
 
    XWMHints *hints;
472
 
    Pixmap xpixmap = None, xmask = None;
473
 
    Window win;
474
 
    unsigned int w, h;
475
 
    int sd, result, format;
476
 
    GdkPixbuf *ret=NULL, *masked=NULL, *pixmap = NULL, *mask = NULL;
477
 
    Atom type = None;
478
 
    gulong *data = NULL;
479
 
    gulong  nitems;
480
 
    gulong  bytes_after;
481
 
    guchar *pixdata=NULL, *p=NULL;
482
 
    int     i;
483
 
 
484
 
    /* Important Notes:
485
 
     * According to freedesktop.org document:
486
 
     * http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html#id2552223
487
 
     * _NET_WM_ICON contains an array of 32-bit packed CARDINAL ARGB.
488
 
     * However, this is incorrect. Actually it's an array of long integers.
489
 
     * Toolkits like gtk+ use unsigned long here to store icons.
490
 
     * Besides, according to manpage of XGetWindowProperty, when returned format,
491
 
     * is 32, the property data will be stored as an array of longs
492
 
     * (which in a 64-bit application will be 64-bit values that are
493
 
     * padded in the upper 4 bytes).
494
 
     */
495
 
 
496
 
    result = XGetWindowProperty(GDK_DISPLAY(),
497
 
                                tkwin,
498
 
                                gdk_x11_get_xatom_by_name("_NET_WM_ICON"),
499
 
                                0, G_MAXLONG,
500
 
                                0, XA_CARDINAL, &type, &format, &nitems,
501
 
                                &bytes_after, (void*)&data);
502
 
    /* g_debug("type=%d, format=%d, nitems=%d", type, format, nitems); */
503
 
    if(type != XA_CARDINAL || nitems <= 0 )
504
 
    {
505
 
        LOG(LOG_WARN, "lxpanel : type is not XA_CARDINAL\n");
506
 
        if(data) {
507
 
            XFree(data);
508
 
            data=NULL;
509
 
        }
510
 
        result = -1;
511
 
    }
512
 
 
513
 
    if(result == Success)
514
 
    {
515
 
        gulong* pdata = data;
516
 
        gulong* pdata_end = data + nitems;
517
 
        gulong* max_icon = NULL;
518
 
        gulong max_w = 0, max_h = 0;
519
 
 
520
 
        /* get the largest icon available. */
521
 
        /* FIXME: should we try to find an icon whose size is closest to
522
 
         * iw and ih to reduce unnecessary resizing? */
523
 
        while(pdata + 2 < pdata_end)
524
 
        {
525
 
            gulong w = pdata[0];
526
 
            gulong h = pdata[1];
527
 
            gulong size = (w * h);
528
 
 
529
 
            pdata += 2;
530
 
            if( pdata + size > pdata_end ) /* corrupt icon */
531
 
                break;
532
 
 
533
 
            if( w > max_w && h > max_h )
534
 
            {
535
 
                max_icon = pdata;
536
 
                max_w = w;
537
 
                max_h = h;
538
 
            }
539
 
 
540
 
            /* rare special case: the desire size is the same as icon size */
541
 
            if( iw == w && ih == h )
542
 
                break;
543
 
 
544
 
            pdata += size;
545
 
        }
546
 
 
547
 
        if( max_icon )
548
 
        {
549
 
            gulong len = max_w * max_h;
550
 
            pixdata = g_new(guchar, len * 4);
551
 
            p = pixdata;
552
 
 
553
 
            i = 0;
554
 
            while(i < len)
555
 
            {
556
 
                guint argb, rgba;
557
 
                
558
 
                argb = max_icon[i];
559
 
                rgba = (argb << 8) | (argb >> 24);
560
 
 
561
 
                *p = rgba >> 24;
562
 
                ++p;
563
 
                *p = (rgba >> 16) & 0xff;
564
 
                ++p;
565
 
                *p = (rgba >> 8) & 0xff;
566
 
                ++p;
567
 
                *p = rgba & 0xff;
568
 
                ++p;
569
 
 
570
 
                ++i;
571
 
            }
 
629
        drawable = gdk_pixmap_foreign_new(xpixmap);
 
630
    GdkColormap * colormap = get_colormap_from_pixmap(drawable);
 
631
 
 
632
    /* Do the major work. */
 
633
    retval = gdk_pixbuf_get_from_drawable(dest,
 
634
        drawable,
 
635
        colormap,
 
636
        src_x, src_y,
 
637
        dest_x, dest_y,
 
638
        width, height);
 
639
 
 
640
    /* Clean up and return. */
 
641
    if (colormap != NULL)
 
642
        g_object_unref(G_OBJECT(colormap));
 
643
    g_object_unref(G_OBJECT(drawable));
 
644
    return retval;
 
645
}
 
646
 
 
647
/* Apply a mask to a pixbuf.
 
648
 * From libwnck, Copyright (C) 2001 Havoc Pennington. */
 
649
static GdkPixbuf * apply_mask(GdkPixbuf * pixbuf, GdkPixbuf * mask)
 
650
{
 
651
    /* Initialize. */
 
652
    int w = MIN(gdk_pixbuf_get_width(mask), gdk_pixbuf_get_width(pixbuf));
 
653
    int h = MIN(gdk_pixbuf_get_height(mask), gdk_pixbuf_get_height(pixbuf));
 
654
    GdkPixbuf * with_alpha = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
 
655
    guchar * dst = gdk_pixbuf_get_pixels(with_alpha);
 
656
    guchar * src = gdk_pixbuf_get_pixels(mask);
 
657
    int dst_stride = gdk_pixbuf_get_rowstride(with_alpha);
 
658
    int src_stride = gdk_pixbuf_get_rowstride(mask);
 
659
 
 
660
    /* Loop to do the work. */
 
661
    int i;
 
662
    for (i = 0; i < h; i += 1)
 
663
    {
 
664
        int j;
 
665
        for (j = 0; j < w; j += 1)
 
666
        {
 
667
            guchar * s = src + i * src_stride + j * 3;
 
668
            guchar * d = dst + i * dst_stride + j * 4;
 
669
 
 
670
            /* s[0] == s[1] == s[2], they are 255 if the bit was set, 0 otherwise. */
 
671
            d[3] = ((s[0] == 0) ? 0 : 255);     /* 0 = transparent, 255 = opaque */
 
672
        }
 
673
    }
 
674
 
 
675
    return with_alpha;
 
676
}
 
677
 
 
678
/* Get an icon from the window manager for a task, and scale it to a specified size. */
 
679
static GdkPixbuf * get_wm_icon(Window task_win, int required_width, int required_height, Atom source, Atom * current_source)
 
680
{
 
681
    /* The result. */
 
682
    GdkPixbuf * pixmap = NULL;
 
683
    Atom possible_source = None;
 
684
    int result = -1;
 
685
 
 
686
    if ((source == None) || (source == a_NET_WM_ICON))
 
687
    {
 
688
        /* Important Notes:
 
689
         * According to freedesktop.org document:
 
690
         * http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html#id2552223
 
691
         * _NET_WM_ICON contains an array of 32-bit packed CARDINAL ARGB.
 
692
         * However, this is incorrect. Actually it's an array of long integers.
 
693
         * Toolkits like gtk+ use unsigned long here to store icons.
 
694
         * Besides, according to manpage of XGetWindowProperty, when returned format,
 
695
         * is 32, the property data will be stored as an array of longs
 
696
         * (which in a 64-bit application will be 64-bit values that are
 
697
         * padded in the upper 4 bytes).
 
698
         */
 
699
 
 
700
        /* Get the window property _NET_WM_ICON, if possible. */
 
701
        Atom type = None;
 
702
        int format;
 
703
        gulong nitems;
 
704
        gulong bytes_after;
 
705
        gulong * data = NULL;
 
706
        result = XGetWindowProperty(
 
707
            GDK_DISPLAY(),
 
708
            task_win,
 
709
            a_NET_WM_ICON,
 
710
            0, G_MAXLONG,
 
711
            False, XA_CARDINAL,
 
712
            &type, &format, &nitems, &bytes_after, (void *) &data);
 
713
 
 
714
        /* Inspect the result to see if it is usable.  If not, and we got data, free it. */
 
715
        if ((type != XA_CARDINAL) || (nitems <= 0))
 
716
        {
 
717
            if (data != NULL)
 
718
                XFree(data);
 
719
            result = -1;
 
720
        }
 
721
 
 
722
        /* If the result is usable, extract the icon from it. */
 
723
        if (result == Success)
 
724
        {
 
725
            /* Get the largest icon available, unless there is one that is the desired size. */
 
726
            /* FIXME: should we try to find an icon whose size is closest to
 
727
             * required_width and required_height to reduce unnecessary resizing? */
 
728
            gulong * pdata = data;
 
729
            gulong * pdata_end = data + nitems;
 
730
            gulong * max_icon = NULL;
 
731
            gulong max_w = 0;
 
732
            gulong max_h = 0;
 
733
            while ((pdata + 2) < pdata_end)
 
734
            {
 
735
                /* Extract the width and height. */
 
736
                gulong w = pdata[0];
 
737
                gulong h = pdata[1];
 
738
                gulong size = w * h;
 
739
                pdata += 2;
 
740
 
 
741
                /* Bounds check the icon. */
 
742
                if (pdata + size > pdata_end)
 
743
                    break;
 
744
 
 
745
                /* Rare special case: the desired size is the same as icon size. */
 
746
                if ((required_width == w) && (required_height == h))
 
747
                {
 
748
                    max_icon = pdata;
 
749
                    max_w = w;
 
750
                    max_h = h;
 
751
                    break;
 
752
                }
 
753
 
 
754
                /* If the icon is the largest so far, capture it. */
 
755
                if ((w > max_w) && (h > max_h))
 
756
                {
 
757
                    max_icon = pdata;
 
758
                    max_w = w;
 
759
                    max_h = h;
 
760
                }
 
761
                pdata += size;
 
762
            }
 
763
 
 
764
            /* If an icon was extracted, convert it to a pixbuf.
 
765
             * Its size is max_w and max_h. */
 
766
            if (max_icon != NULL)
 
767
            {
 
768
                /* Allocate enough space for the pixel data. */
 
769
                gulong len = max_w * max_h;
 
770
                guchar * pixdata = g_new(guchar, len * 4);
 
771
 
 
772
                /* Loop to convert the pixel data. */
 
773
                guchar * p = pixdata;
 
774
                int i;
 
775
                for (i = 0; i < len; p += 4, i += 1)
 
776
                {
 
777
                    guint argb = max_icon[i];
 
778
                    guint rgba = (argb << 8) | (argb >> 24);
 
779
                    p[0] = rgba >> 24;
 
780
                    p[1] = (rgba >> 16) & 0xff;
 
781
                    p[2] = (rgba >> 8) & 0xff;
 
782
                    p[3] = rgba & 0xff;
 
783
                }
572
784
            
573
 
            pixmap = gdk_pixbuf_new_from_data(pixdata,
574
 
                                              GDK_COLORSPACE_RGB,
575
 
                                              1, 8,
576
 
                                              max_w, max_h, max_w * 4,
577
 
                                              free_pixels,
578
 
                                              NULL);
 
785
                /* Initialize a pixmap with the pixel data. */
 
786
                pixmap = gdk_pixbuf_new_from_data(
 
787
                    pixdata,
 
788
                    GDK_COLORSPACE_RGB,
 
789
                    TRUE, 8,    /* has_alpha, bits_per_sample */
 
790
                    max_w, max_h, max_w * 4,
 
791
                    (GdkPixbufDestroyNotify) g_free,
 
792
                    NULL);
 
793
                possible_source = a_NET_WM_ICON;
 
794
            }
 
795
            else
 
796
                result = -1;
 
797
 
 
798
            /* Free the X property data. */
 
799
            XFree(data);
579
800
        }
580
 
        else
581
 
        {
582
 
            result = -1;
583
 
        }
584
 
        XFree(data);
585
 
        data=NULL;
586
801
    }
587
802
 
588
 
    if(result != Success)
 
803
    /* No icon available from _NET_WM_ICON.  Next try WM_HINTS, but do not overwrite _NET_WM_ICON. */
 
804
    if ((result != Success) && (*current_source != a_NET_WM_ICON)
 
805
    && ((source == None) || (source != a_NET_WM_ICON)))
589
806
    {
590
 
        LOG(LOG_WARN, "lxpanel : Can't read _NET_WM_ICON, try to read pixmap icon\n");
591
 
 
592
 
        hints = XGetWMHints(GDK_DISPLAY(), tkwin);
 
807
        XWMHints * hints = XGetWMHints(GDK_DISPLAY(), task_win);
593
808
        result = (hints != NULL) ? Success : -1;
 
809
        Pixmap xpixmap = None;
 
810
        Pixmap xmask = None;
594
811
 
595
 
        if(result == Success)
 
812
        if (result == Success)
596
813
        {
 
814
            /* WM_HINTS is available.  Extract the X pixmap and mask. */
597
815
            if ((hints->flags & IconPixmapHint))
598
816
                xpixmap = hints->icon_pixmap;
599
817
            if ((hints->flags & IconMaskHint))
600
818
                xmask = hints->icon_mask;
601
819
            XFree(hints);
602
 
            hints=NULL;
603
 
            result = (xpixmap != None)?Success:-1;
 
820
            if (xpixmap != None)
 
821
            {
 
822
                result = Success;
 
823
                possible_source = XA_WM_HINTS;
 
824
            }
 
825
            else
 
826
                result = -1;
604
827
        }
605
828
 
606
 
        if(result != Success)
 
829
        if (result != Success)
607
830
        {
 
831
            /* No icon available from _NET_WM_ICON or WM_HINTS.  Next try KWM_WIN_ICON. */
 
832
            Atom type = None;
 
833
            int format;
 
834
            gulong nitems;
 
835
            gulong bytes_after;
608
836
            Pixmap *icons = NULL;
609
 
            LOG(LOG_WARN, "lxpanel : can't get icon using HINTS try to use KWM_WIN_ICON\n");
610
837
            Atom kwin_win_icon_atom = gdk_x11_get_xatom_by_name("KWM_WIN_ICON");
611
 
            result = XGetWindowProperty(GDK_DISPLAY(), tkwin,
612
 
                                        kwin_win_icon_atom,
613
 
                                        0, G_MAXLONG,
614
 
                                        False,
615
 
                                        kwin_win_icon_atom,
616
 
                                        &type, &format, &nitems,
617
 
                                        &bytes_after, (void*)&icons);
618
 
            if(type != kwin_win_icon_atom)
 
838
            result = XGetWindowProperty(
 
839
                GDK_DISPLAY(),
 
840
                task_win,
 
841
                kwin_win_icon_atom,
 
842
                0, G_MAXLONG,
 
843
                False, kwin_win_icon_atom,
 
844
                &type, &format, &nitems, &bytes_after, (void *) &icons);
 
845
 
 
846
            /* Inspect the result to see if it is usable.  If not, and we got data, free it. */
 
847
            if (type != kwin_win_icon_atom)
619
848
            {
620
 
                if( icons ) {
 
849
                if (icons != NULL)
621
850
                    XFree(icons);
622
 
                    icons=NULL;
623
 
                }
624
851
                result = -1;
625
852
            }
626
 
            if(result == Success)
 
853
 
 
854
            /* If the result is usable, extract the X pixmap and mask from it. */
 
855
            if (result == Success)
627
856
            {
628
857
                xpixmap = icons[0];
629
 
                xmask   = icons[1];
630
 
                result = (xpixmap != None) ? Success : -1;
 
858
                xmask = icons[1];
 
859
                if (xpixmap != None)
 
860
                {
 
861
                    result = Success;
 
862
                    possible_source = kwin_win_icon_atom;
 
863
                }
 
864
                else
 
865
                    result = -1;
631
866
            }
632
867
        }
633
868
 
634
 
        if(result == Success)
635
 
        {
636
 
            result = XGetGeometry(GDK_DISPLAY(),
637
 
                                  xpixmap, &win,
638
 
                                  &sd, &sd, &w, &h,
639
 
                                  (guint *)&sd, (guint *)&sd) ? Success : -1;
640
 
        }
641
 
 
642
 
        if(result != Success) 
643
 
        {
644
 
            LOG(LOG_WARN,"lxpanel : XGetGeometry failed for %x pixmap\n", (unsigned int)xpixmap);
645
 
        }
646
 
        else
647
 
        {
648
 
            DBG("tkwin=%x icon pixmap w=%d h=%d\n", tkwin, w, h);
649
 
            pixmap = _wnck_gdk_pixbuf_get_from_pixmap (NULL, xpixmap, 0, 0, 0, 0, w, h);
650
 
            result = pixmap?Success:-1;
651
 
        }
652
 
 
653
 
        if(result == Success)
654
 
        {
655
 
            if (xmask != None && XGetGeometry(GDK_DISPLAY(), xmask,
656
 
                                              &win, &sd, &sd, &w, &h,
657
 
                                              (guint *)&sd, (guint *)&sd))
 
869
        /* If we have an X pixmap, get its geometry.*/
 
870
        unsigned int w, h;
 
871
        if (result == Success)
 
872
        {
 
873
            Window unused_win;
 
874
            int unused;
 
875
            unsigned int unused_2;
 
876
            result = XGetGeometry(
 
877
                GDK_DISPLAY(), xpixmap,
 
878
                &unused_win, &unused, &unused, &w, &h, &unused_2, &unused_2) ? Success : -1;
 
879
        }
 
880
 
 
881
        /* If we have an X pixmap and its geometry, convert it to a GDK pixmap. */
 
882
        if (result == Success) 
 
883
        {
 
884
            pixmap = _wnck_gdk_pixbuf_get_from_pixmap(NULL, xpixmap, 0, 0, 0, 0, w, h);
 
885
            result = ((pixmap != NULL) ? Success : -1);
 
886
        }
 
887
 
 
888
        /* If we have success, see if the result needs to be masked.
 
889
         * Failures here are implemented as nonfatal. */
 
890
        if ((result == Success) && (xmask != None))
 
891
        {
 
892
            Window unused_win;
 
893
            int unused;
 
894
            unsigned int unused_2;
 
895
            if (XGetGeometry(
 
896
                GDK_DISPLAY(), xmask,
 
897
                &unused_win, &unused, &unused, &w, &h, &unused_2, &unused_2))
658
898
            {
659
 
                mask = _wnck_gdk_pixbuf_get_from_pixmap (NULL, xmask, 0, 0, 0, 0, w, h);
660
 
                if (mask)
 
899
                /* Convert the X mask to a GDK pixmap. */
 
900
                GdkPixbuf * mask = _wnck_gdk_pixbuf_get_from_pixmap(NULL, xmask, 0, 0, 0, 0, w, h);
 
901
                if (mask != NULL)
661
902
                {
662
 
                    masked = apply_mask (pixmap, mask);
663
 
                    g_object_unref (G_OBJECT (pixmap));
664
 
                    g_object_unref (G_OBJECT (mask));
665
 
                    pixmap = masked;
 
903
                    /* Apply the mask. */
 
904
                    GdkPixbuf * masked_pixmap = apply_mask(pixmap, mask);
 
905
                    g_object_unref(G_OBJECT(pixmap));
 
906
                    g_object_unref(G_OBJECT(mask));
 
907
                    pixmap = masked_pixmap;
666
908
                }
667
909
            }
668
910
        }
669
911
    }
670
912
 
671
 
    if (!pixmap)
672
 
        RET(NULL);
673
 
 
674
 
    ret = gdk_pixbuf_scale_simple (pixmap, iw, ih, GDK_INTERP_TILES);
675
 
    g_object_unref(pixmap);
676
 
 
677
 
    return ret;
678
 
}
679
 
 
680
 
inline static GdkPixbuf*
681
 
get_generic_icon(taskbar *tb)
682
 
{
683
 
    ENTER;
684
 
    g_object_ref(tb->gen_pixbuf);
685
 
    RET(tb->gen_pixbuf);
686
 
}
687
 
 
688
 
static void
689
 
tk_update_icon (taskbar *tb, task *tk, Atom a)
690
 
{
691
 
    GdkPixbuf *pixbuf;
692
 
 
693
 
    ENTER;
694
 
    g_assert ((tb != NULL) && (tk != NULL));
695
 
    g_return_if_fail(tk != NULL);
696
 
 
697
 
    pixbuf = tk->pixbuf;
698
 
    if (a == a_NET_WM_ICON || a == None) {
699
 
        tk->pixbuf = get_netwm_icon(tk->win, tb->iconsize, tb->iconsize);
700
 
        tk->using_netwm_icon = (tk->pixbuf != NULL);
701
 
    }
702
 
    if (!tk->using_netwm_icon)
703
 
        tk->pixbuf = get_wm_icon(tk->win, tb->iconsize, tb->iconsize);
704
 
    if (!tk->pixbuf)
705
 
        tk->pixbuf = get_generic_icon(tb); // always exists
706
 
    if (pixbuf != tk->pixbuf) {
707
 
        if (pixbuf)
708
 
            g_object_unref(pixbuf);
709
 
    }
710
 
    RET();
711
 
}
712
 
 
713
 
static gboolean on_flash_win( task *tk )
714
 
{
715
 
    tk->flash_state = !tk->flash_state;
716
 
    gtk_widget_set_state(tk->button,
717
 
          tk->flash_state ? GTK_STATE_SELECTED : tk->tb->normal_state);
718
 
    gtk_widget_queue_draw(tk->button);
 
913
    /* If we got a pixmap, scale it and return it. */
 
914
    if (pixmap == NULL)
 
915
        return NULL;
 
916
    else
 
917
    {
 
918
        GdkPixbuf * ret = gdk_pixbuf_scale_simple(pixmap, required_width, required_height, GDK_INTERP_TILES);
 
919
        g_object_unref(pixmap);
 
920
        *current_source = possible_source;
 
921
        return ret;
 
922
    }
 
923
}
 
924
 
 
925
/* Update the icon of a task. */
 
926
static GdkPixbuf * task_update_icon(TaskbarPlugin * tb, Task * tk, Atom source)
 
927
{
 
928
    /* Get the icon from the window's hints. */
 
929
    GdkPixbuf * pixbuf = get_wm_icon(tk->win, tb->icon_size, tb->icon_size, source, &tk->image_source);
 
930
 
 
931
    /* If that fails, and we have no other icon yet, return the fallback icon. */
 
932
    if ((pixbuf == NULL)
 
933
    && ((source == None) || (tk->image_source == None)))
 
934
    {
 
935
        /* Establish the fallback task icon.  This is used when no other icon is available. */
 
936
        if (tb->fallback_pixbuf == NULL)
 
937
            tb->fallback_pixbuf = gdk_pixbuf_new_from_xpm_data((const char **) icon_xpm);
 
938
        g_object_ref(tb->fallback_pixbuf);
 
939
        pixbuf = tb->fallback_pixbuf;
 
940
    }
 
941
 
 
942
    /* Return what we have.  This may be NULL to indicate that no change should be made to the icon. */
 
943
    return pixbuf;
 
944
}
 
945
 
 
946
/* Timer expiration for urgency notification.  Also used to draw the button in setting and clearing urgency. */
 
947
static gboolean flash_window_timeout(Task * tk)
 
948
{
 
949
    /* Set state on the button and redraw. */
 
950
    if (tk->tb->flat_button)
 
951
        task_draw_label(tk);
 
952
    else
 
953
    {
 
954
        gtk_widget_set_state(tk->button, tk->flash_state ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
 
955
        gtk_widget_queue_draw(tk->button);
 
956
    }
 
957
 
 
958
    /* Complement the flashing context. */
 
959
    tk->flash_state = ! tk->flash_state;
719
960
    return TRUE;
720
961
}
721
962
 
722
 
static void
723
 
tk_flash_window( task *tk )
724
 
{
725
 
    gint interval;
726
 
    tk->flash = 1;
727
 
    tk->flash_state = !tk->flash_state;
728
 
    if (tk->flash_timeout)
729
 
        return;
730
 
    g_object_get( gtk_widget_get_settings(tk->button),
731
 
          "gtk-cursor-blink-time", &interval, NULL );
732
 
    tk->flash_timeout = g_timeout_add(interval, (GSourceFunc)on_flash_win, tk);
733
 
}
734
 
 
735
 
static void
736
 
tk_unflash_window( task *tk )
737
 
{
738
 
    tk->flash = tk->flash_state = 0;
739
 
    if (tk->flash_timeout) {
740
 
        g_source_remove(tk->flash_timeout);
741
 
        tk->flash_timeout = 0;
742
 
    }
743
 
}
744
 
 
745
 
static void
746
 
tk_raise_window( task *tk, guint32 time )
747
 
{
748
 
    if (tk->desktop != -1 && tk->desktop != tk->tb->cur_desk){
 
963
/* Set urgency notification. */
 
964
static void task_set_urgency(Task * tk)
 
965
{
 
966
    if (( ! tk->tb->grouped_tasks) || (tk->res_class == NULL))
 
967
    {
 
968
        /* Set the flashing context and flash the window immediately. */
 
969
        tk->flash_state = TRUE;
 
970
        flash_window_timeout(tk);
 
971
 
 
972
        /* Set the timer if none is set. */
 
973
        if (tk->flash_timeout == 0)
 
974
            set_timer_on_task(tk);
 
975
    }
 
976
    else
 
977
        recompute_group_visibility_for_class(tk->tb, tk->res_class);
 
978
}
 
979
 
 
980
/* Clear urgency notification. */
 
981
static void task_clear_urgency(Task * tk)
 
982
{
 
983
    if (( ! tk->tb->grouped_tasks) || (tk->res_class == NULL))
 
984
    {
 
985
        /* Remove the timer if one is set. */
 
986
        if (tk->flash_timeout != 0)
 
987
        {
 
988
            g_source_remove(tk->flash_timeout);
 
989
            tk->flash_timeout = 0;
 
990
        }
 
991
 
 
992
        /* Clear the flashing context and unflash the window immediately. */
 
993
        tk->flash_state = FALSE;
 
994
        flash_window_timeout(tk);
 
995
        tk->flash_state = FALSE;
 
996
    }
 
997
    else
 
998
        recompute_group_visibility_for_class(tk->tb, tk->res_class);
 
999
}
 
1000
 
 
1001
/* Do the proper steps to raise a window.
 
1002
 * This means removing it from iconified state and bringing it to the front.
 
1003
 * We also switch the active desktop and viewport if needed. */
 
1004
static void task_raise_window(Task * tk, guint32 time)
 
1005
{
 
1006
    /* Change desktop if needed. */
 
1007
    if ((tk->desktop != -1) && (tk->desktop != tk->tb->current_desktop))
749
1008
        Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, tk->desktop, 0, 0, 0, 0);
750
 
        XSync (gdk_display, False);
751
 
    }
752
 
    XSetInputFocus (GDK_DISPLAY(), tk->win, RevertToNone, CurrentTime);
753
 
    XRaiseWindow (GDK_DISPLAY(), tk->win);
754
 
    Xclimsg(tk->win, a_NET_ACTIVE_WINDOW, 2, time, 0, 0, 0);
755
 
    DBG("XRaiseWindow %x\n", tk->win);
756
 
}
757
 
 
758
 
static void
759
 
on_tk_leave( GtkWidget *widget, task *tk)
760
 
{
761
 
    ENTER;
762
 
/*
763
 
    gtk_widget_set_state(widget,
764
 
          (tk->focused) ? tk->tb->focused_state : tk->tb->normal_state);
765
 
*/
766
 
    RET();
767
 
}
768
 
 
769
 
 
770
 
static void
771
 
on_tk_enter( GtkWidget *widget, task *tk )
772
 
{
773
 
    ENTER;
774
 
/*
775
 
    gtk_widget_set_state(widget,
776
 
          (tk->focused) ? tk->tb->focused_state : tk->tb->normal_state);
777
 
*/
778
 
    RET();
779
 
}
780
 
 
781
 
static gboolean delay_active_win(task* tk)
782
 
{
783
 
    /* FIXME: gtk_get_current_event_time() often returns 0.
784
 
       However, passing 0 as time for this function is not OK. */
785
 
    tk_raise_window(tk, gtk_get_current_event_time() );
786
 
    tk->tb->dnd_activate = 0;
787
 
    return FALSE;
788
 
}
789
 
 
790
 
static gboolean
791
 
on_tk_drag_motion( GtkWidget *widget,
792
 
      GdkDragContext *drag_context,
793
 
      gint x, gint y,
794
 
      guint time, task *tk)
795
 
{
796
 
    /* prevent excessive motion notification */
797
 
    if (!tk->tb->dnd_activate) {
798
 
        tk->tb->dnd_activate = g_timeout_add(DRAG_ACTIVE_DELAY,
799
 
              (GSourceFunc)delay_active_win, tk);
800
 
    }
801
 
    gdk_drag_status (drag_context,0,time);
802
 
    return TRUE;
803
 
}
804
 
 
805
 
static void
806
 
on_tk_drag_leave (GtkWidget *widget,
807
 
      GdkDragContext *drag_context,
808
 
      guint time, task *tk)
809
 
{
810
 
    if (tk->tb->dnd_activate) {
811
 
        g_source_remove(tk->tb->dnd_activate);
812
 
        tk->tb->dnd_activate = 0;
813
 
    }
814
 
    return;
815
 
}
816
 
 
817
 
#if 0
818
 
static gboolean
819
 
on_tk_expose(GtkWidget *widget, GdkEventExpose *event, task *tk)
820
 
{
821
 
    GtkStateType state;
822
 
    ENTER;
823
 
    state = (tk->focused) ? tk->tb->focused_state : tk->tb->normal_state;
824
 
    if (GTK_WIDGET_STATE(widget) != state) {
825
 
        gtk_widget_set_state(widget, state);
826
 
        gtk_widget_queue_draw(widget);
827
 
    } else {
828
 
        if( ! tk->flash || 0 == tk->flash_state ) {
829
 
            gtk_paint_box (widget->style, widget->window,
830
 
                  state,
831
 
                  (tk->focused) ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
832
 
                  &event->area, widget, "button",
833
 
                  widget->allocation.x, widget->allocation.y,
834
 
                  widget->allocation.width, widget->allocation.height);
835
 
        } else {
836
 
            gdk_draw_rectangle( widget->window,
837
 
                                widget->style->bg_gc[GTK_STATE_SELECTED],
838
 
                                TRUE, 0, 0,
839
 
                                widget->allocation.width,
840
 
                                widget->allocation.height );
841
 
        }
842
 
        /*
843
 
        _gtk_button_paint(GTK_BUTTON(widget), &event->area, state,
844
 
              (tk->focused) ? GTK_SHADOW_IN : GTK_SHADOW_OUT,
845
 
              "button",  "buttondefault");
846
 
        */
847
 
        gtk_container_propagate_expose(GTK_CONTAINER(widget), GTK_BIN(widget)->child, event);
848
 
    }
849
 
    RET(FALSE);
850
 
}
851
 
#endif
852
 
 
853
 
static gint
854
 
on_tk_scroll_event (GtkWidget *widget, GdkEventScroll *event, task *tk)
855
 
{
856
 
    ENTER;
857
 
    if( ! tk->tb->use_mouse_wheel )
858
 
        return TRUE;
859
 
    if (event->direction == GDK_SCROLL_UP) {
860
 
        GdkWindow *gdkwindow;
861
 
 
862
 
        gdkwindow = gdk_xid_table_lookup (tk->win);
863
 
        if (gdkwindow)
864
 
            gdk_window_show (gdkwindow);
 
1009
 
 
1010
    /* Raise the window.  We can use NET_ACTIVE_WINDOW if the window manager supports it.
 
1011
     * Otherwise, do it the old way with XMapRaised and XSetInputFocus. */
 
1012
    if (tk->tb->use_net_active)
 
1013
        Xclimsg(tk->win, a_NET_ACTIVE_WINDOW, 2, time, 0, 0, 0);
 
1014
    else
 
1015
    {
 
1016
        GdkWindow * gdkwindow = gdk_xid_table_lookup(tk->win);
 
1017
        if (gdkwindow != NULL)
 
1018
            gdk_window_show(gdkwindow);
865
1019
        else
866
 
            XMapRaised (GDK_DISPLAY(), tk->win);
867
 
        XSetInputFocus (GDK_DISPLAY(), tk->win, RevertToNone, CurrentTime);
868
 
        DBG("XMapRaised  %x\n", tk->win);
869
 
    } else if (event->direction == GDK_SCROLL_DOWN) {
870
 
        DBG("tb->ptk = %x\n", (tk->tb->ptk) ? tk->tb->ptk->win : 0);
871
 
        XIconifyWindow (GDK_DISPLAY(), tk->win, DefaultScreen(GDK_DISPLAY()));
872
 
        DBG("XIconifyWindow %x\n", tk->win);
873
 
    }
874
 
 
875
 
    XSync (gdk_display, False);
876
 
    RET(TRUE);
877
 
}
878
 
 
879
 
static gboolean
880
 
on_tk_btn_press_event(GtkWidget *widget, GdkEventButton *event, task *tk)
881
 
{
882
 
    if( event->type == GDK_BUTTON_PRESS && event->button == 3 )
883
 
    {
884
 
        tk->tb->menutask = tk;
885
 
        gtk_menu_popup (GTK_MENU (tk->tb->menu), NULL, NULL, NULL, NULL, event->button, event->time);
886
 
        return TRUE;
887
 
    }
888
 
    return FALSE;
889
 
}
890
 
 
891
 
static gboolean
892
 
on_tk_btn_release_event(GtkWidget *widget, GdkEventButton *event, task *tk)
893
 
{
 
1020
            XMapRaised(GDK_DISPLAY(), tk->win);
 
1021
        XSync(GDK_DISPLAY(), False);    /* This we need to avoid BadMatch */
 
1022
        XSetInputFocus(GDK_DISPLAY(), tk->win, RevertToNone, time);
 
1023
    }
 
1024
 
 
1025
    /* Change viewport if needed. */
894
1026
    XWindowAttributes xwa;
895
 
 
896
 
    if( event->type != GDK_BUTTON_RELEASE )
897
 
        return FALSE;
898
 
 
899
 
    if( event->button == 1 )
900
 
    {
901
 
        if (tk->iconified)
 
1027
    XGetWindowAttributes(GDK_DISPLAY(), tk->win, &xwa);
 
1028
    Xclimsg(tk->win, a_NET_DESKTOP_VIEWPORT, xwa.x, xwa.y, 0, 0, 0);
 
1029
}
 
1030
 
 
1031
/* Position-calculation callback for grouped-task and window-management popup menu. */
 
1032
static void taskbar_popup_set_position(GtkWidget * menu, gint * px, gint * py, gboolean * push_in, gpointer data)
 
1033
{
 
1034
    Task * tk = (Task *) data;
 
1035
 
 
1036
    /* Get the allocation of the popup menu. */
 
1037
    GtkRequisition popup_req;
 
1038
    gtk_widget_size_request(menu, &popup_req);
 
1039
 
 
1040
    /* Determine the coordinates. */
 
1041
    plugin_popup_set_position_helper(tk->tb->plug, tk->button, menu, &popup_req, px, py);
 
1042
    *push_in = TRUE;
 
1043
}
 
1044
 
 
1045
/* Remove the grouped-task popup menu from the screen. */
 
1046
static void task_group_menu_destroy(TaskbarPlugin * tb)
 
1047
{
 
1048
    if (tb->group_menu != NULL)
 
1049
    {
 
1050
        gtk_widget_destroy(tb->group_menu);
 
1051
        tb->group_menu = NULL;
 
1052
    }
 
1053
}
 
1054
 
 
1055
/* Handler for "button-press-event" event from taskbar button,
 
1056
 * or "activate" event from grouped-task popup menu item. */
 
1057
static gboolean taskbar_task_control_event(GtkWidget * widget, GdkEventButton * event, Task * tk, gboolean popup_menu)
 
1058
{
 
1059
    TaskbarPlugin * tb = tk->tb;
 
1060
    TaskClass * tc = tk->res_class;
 
1061
    if ((tb->grouped_tasks) && (tc != NULL) && (tc->visible_count > 1) && (GTK_IS_BUTTON(widget)))
 
1062
    {
 
1063
        /* If this is a grouped-task representative, meaning that there is a class with at least two windows,
 
1064
         * bring up a popup menu listing all the class members. */
 
1065
        GtkWidget * menu = gtk_menu_new();
 
1066
        Task * tk_cursor;
 
1067
        for (tk_cursor = tc->res_class_head; tk_cursor != NULL; tk_cursor = tk_cursor->res_class_flink)
902
1068
        {
903
 
            if(use_net_active)
904
 
                Xclimsg(tk->win, a_NET_ACTIVE_WINDOW, 2, event->time, 0, 0, 0);
905
 
            else
 
1069
            if (task_is_visible_on_current_desktop(tb, tk_cursor))
906
1070
            {
907
 
                GdkWindow *gdkwindow;
908
 
                gdkwindow = gdk_xid_table_lookup (tk->win);
909
 
                if (gdkwindow)
910
 
                    gdk_window_show (gdkwindow);
911
 
                else
912
 
                    XMapRaised (GDK_DISPLAY(), tk->win);
913
 
                XSync (GDK_DISPLAY(), False);
 
1071
                /* The menu item has the name, or the iconified name, and the icon of the application window. */
 
1072
                GtkWidget * mi = gtk_image_menu_item_new_with_label(((tk_cursor->iconified) ? tk_cursor->name_iconified : tk_cursor->name));
 
1073
                GtkWidget * im = gtk_image_new_from_pixbuf(gtk_image_get_pixbuf(GTK_IMAGE(tk_cursor->image)));
 
1074
                gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), im);
 
1075
                g_signal_connect(mi, "button_press_event", G_CALLBACK(taskbar_popup_activate_event), (gpointer) tk_cursor);
 
1076
                gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
914
1077
            }
915
 
            /* if window isn't on current viewport, we change viewport */
916
 
            XGetWindowAttributes(GDK_DISPLAY(), tk->win, &xwa);
917
 
            Xclimsg(tk->win, a_NET_DESKTOP_VIEWPORT, xwa.x, xwa.y, 0, 0, 0);
918
 
        }
 
1078
        }
 
1079
 
 
1080
        /* Show the menu.  Set context so we can find the menu later to dismiss it.
 
1081
         * Use a position-calculation callback to get the menu nicely positioned with respect to the button. */
 
1082
        gtk_widget_show_all(menu);
 
1083
        tb->group_menu = menu;
 
1084
        gtk_menu_popup(GTK_MENU(menu), NULL, NULL, (GtkMenuPositionFunc) taskbar_popup_set_position, (gpointer) tk, event->button, event->time);
 
1085
    }
 
1086
    else
 
1087
    {
 
1088
        /* Not a grouped-task representative, or entered from the grouped-task popup menu. */
 
1089
        Task * visible_task = (((tk->res_class == NULL) || ( ! tk->tb->grouped_tasks)) ? tk : tk->res_class->visible_task);
 
1090
        task_group_menu_destroy(tb);
 
1091
 
 
1092
        if (event->button == 1)
 
1093
        {
 
1094
            /* Left button.
 
1095
             * If the task is iconified, raise it.
 
1096
             * If the task is not iconified and has focus, iconify it.
 
1097
             * If the task is not iconified and does not have focus, raise it. */
 
1098
            if (tk->iconified)
 
1099
                task_raise_window(tk, event->time);
 
1100
            else if ((tk->focused) || (tk == tb->focused_previous))
 
1101
                XIconifyWindow(GDK_DISPLAY(), tk->win, DefaultScreen(GDK_DISPLAY()));
 
1102
            else
 
1103
                task_raise_window(tk, event->time);
 
1104
        }
 
1105
        else if (event->button == 2)
 
1106
        {
 
1107
            /* Middle button.  Toggle the shaded state of the window. */
 
1108
            Xclimsg(tk->win, a_NET_WM_STATE,
 
1109
                2,              /* a_NET_WM_STATE_TOGGLE */
 
1110
                a_NET_WM_STATE_SHADED,
 
1111
                0, 0, 0);
 
1112
        }
 
1113
        else if (event->button == 3)
 
1114
        {
 
1115
            /* Right button.  Bring up the window state popup menu. */
 
1116
            tk->tb->menutask = tk;
 
1117
            gtk_menu_popup(
 
1118
                GTK_MENU(tb->menu),
 
1119
                NULL, NULL,
 
1120
                (GtkMenuPositionFunc) taskbar_popup_set_position, (gpointer) visible_task,
 
1121
                event->button, event->time);
 
1122
        }
 
1123
    }
 
1124
 
 
1125
    /* As a matter of policy, avoid showing selected or prelight states on flat buttons. */
 
1126
    if (tb->flat_button)
 
1127
        gtk_widget_set_state(widget, GTK_STATE_NORMAL);
 
1128
    return TRUE;
 
1129
}
 
1130
 
 
1131
/* Handler for "button-press-event" event from taskbar button. */
 
1132
static gboolean taskbar_button_press_event(GtkWidget * widget, GdkEventButton * event, Task * tk)
 
1133
{
 
1134
    return taskbar_task_control_event(widget, event, tk, FALSE);
 
1135
}
 
1136
 
 
1137
/* Handler for "activate" event from grouped-task popup menu item. */
 
1138
static gboolean taskbar_popup_activate_event(GtkWidget * widget, GdkEventButton * event, Task * tk)
 
1139
{
 
1140
    return taskbar_task_control_event(widget, event, tk, TRUE);
 
1141
}
 
1142
 
 
1143
/* Handler for "drag-motion" timeout. */
 
1144
static gboolean taskbar_button_drag_motion_timeout(Task * tk)
 
1145
{
 
1146
    guint time = gtk_get_current_event_time();
 
1147
    task_raise_window(tk, ((time != 0) ? time : CurrentTime));
 
1148
    tk->tb->dnd_delay_timer = 0;
 
1149
    return FALSE;
 
1150
}
 
1151
 
 
1152
/* Handler for "drag-motion" event from taskbar button. */
 
1153
static gboolean taskbar_button_drag_motion(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, Task * tk)
 
1154
{
 
1155
    /* Prevent excessive motion notification. */
 
1156
    if (tk->tb->dnd_delay_timer == 0)
 
1157
        tk->tb->dnd_delay_timer = g_timeout_add(DRAG_ACTIVE_DELAY, (GSourceFunc) taskbar_button_drag_motion_timeout, tk);
 
1158
    gdk_drag_status(drag_context, 0, time);
 
1159
    return TRUE;
 
1160
}
 
1161
 
 
1162
/* Handler for "drag-leave" event from taskbar button. */
 
1163
static void taskbar_button_drag_leave(GtkWidget * widget, GdkDragContext * drag_context, guint time, Task * tk)
 
1164
{
 
1165
    /* Cancel the timer if set. */
 
1166
    if (tk->tb->dnd_delay_timer != 0)
 
1167
    {
 
1168
        g_source_remove(tk->tb->dnd_delay_timer);
 
1169
        tk->tb->dnd_delay_timer = 0;
 
1170
    }
 
1171
    return;
 
1172
}
 
1173
 
 
1174
/* Handler for "enter" event from taskbar button.  This indicates that the cursor position has entered the button. */
 
1175
static void taskbar_button_enter(GtkWidget * widget, Task * tk)
 
1176
{
 
1177
    tk->entered_state = TRUE;
 
1178
    if (tk->tb->flat_button)
 
1179
        gtk_widget_set_state(widget, GTK_STATE_NORMAL);
 
1180
    task_draw_label(tk);
 
1181
}
 
1182
 
 
1183
/* Handler for "leave" event from taskbar button.  This indicates that the cursor position has left the button. */
 
1184
static void taskbar_button_leave(GtkWidget * widget, Task * tk)
 
1185
{
 
1186
    tk->entered_state = FALSE;
 
1187
    task_draw_label(tk);
 
1188
}
 
1189
 
 
1190
/* Handler for "scroll-event" event from taskbar button. */
 
1191
static gboolean taskbar_button_scroll_event(GtkWidget * widget, GdkEventScroll * event, Task * tk)
 
1192
{
 
1193
    TaskbarPlugin * tb = tk->tb;
 
1194
    TaskClass * tc = tk->res_class;
 
1195
    if ((tb->use_mouse_wheel)
 
1196
    && (( ! tb->grouped_tasks) || (tc == NULL) || (tc->visible_count == 1)))
 
1197
    {
 
1198
        if ((event->direction == GDK_SCROLL_UP) || (event->direction == GDK_SCROLL_LEFT))
 
1199
            task_raise_window(tk, event->time);
919
1200
        else
920
 
        {
921
 
            if (tk->focused || tk == tk->tb->ptk)
922
 
                XIconifyWindow (GDK_DISPLAY(), tk->win, DefaultScreen(GDK_DISPLAY()));
923
 
            else
924
 
                tk_raise_window( tk, event->time );
925
 
        }
926
 
    }
927
 
    else if (event->button == 2)
928
 
    {
929
 
        Xclimsg(tk->win, a_NET_WM_STATE,
930
 
              2 /*a_NET_WM_STATE_TOGGLE*/,
931
 
              a_NET_WM_STATE_SHADED,
932
 
              0, 0, 0);
933
 
    }
934
 
    XSync (gdk_display, False);
935
 
 
936
 
    return FALSE;
937
 
}
938
 
 
939
 
 
940
 
static void
941
 
tk_update(gpointer key, task *tk, taskbar *tb)
942
 
{
943
 
    ENTER;
944
 
    g_assert ((tb != NULL) && (tk != NULL));
945
 
    if (task_visible(tb, tk)) {
946
 
        /* g_debug( "SET_ACTIVE: %p, %d", tk->button, tk->focused ); */
947
 
        if( gtk_toggle_button_get_active( (GtkToggleButton*)tk->button) != tk->focused )
948
 
            gtk_toggle_button_set_active( (GtkToggleButton*)tk->button, tk->focused );
949
 
 
950
 
        gtk_widget_show(tk->button);
951
 
        if (tb->tooltips) {
952
 
            //DBG2("tip %x %s\n", tk->win, tk->name);
953
 
            gtk_widget_set_tooltip_text( tk->button, tk->name );
954
 
        }
955
 
    RET();
956
 
    }
957
 
    gtk_widget_hide(tk->button);
958
 
    RET();
959
 
}
960
 
 
961
 
static void
962
 
tk_display(taskbar *tb, task *tk)
963
 
{
964
 
    ENTER;
965
 
    tk_update(NULL, tk, tb);
966
 
    RET();
967
 
}
968
 
 
969
 
static void
970
 
tb_display(taskbar *tb)
971
 
{
972
 
    ENTER;
973
 
    if (tb->wins)
974
 
        g_hash_table_foreach(tb->task_list, (GHFunc) tk_update, (gpointer) tb);
975
 
    RET();
976
 
 
977
 
}
978
 
 
979
 
static void on_tk_btn_size_allocate(GtkWidget* btn, GtkAllocation* alloc, task* tk)
980
 
{
981
 
    int x, y;
982
 
    if( ! GTK_WIDGET_REALIZED(btn) )
983
 
        return;
984
 
    gdk_window_get_origin(GTK_BUTTON(btn)->event_window, &x, &y);
985
 
    set_iconification_dest( tk->win, x, y, alloc->width, alloc->height );
986
 
}
987
 
 
988
 
static void
989
 
tk_build_gui(taskbar *tb, task *tk)
990
 
{
991
 
    GtkWidget *w1;
992
 
 
993
 
    ENTER;
994
 
    g_assert ((tb != NULL) && (tk != NULL));
995
 
 
 
1201
            XIconifyWindow(GDK_DISPLAY(), tk->win, DefaultScreen(GDK_DISPLAY()));
 
1202
    }
 
1203
    return TRUE;
 
1204
}
 
1205
 
 
1206
/* Handler for "size-allocate" event from taskbar button. */
 
1207
static void taskbar_button_size_allocate(GtkWidget * btn, GtkAllocation * alloc, Task * tk)
 
1208
{
 
1209
    if (GTK_WIDGET_REALIZED(btn))
 
1210
    {
 
1211
        /* Get the coordinates of the button. */
 
1212
        int x, y;
 
1213
        gdk_window_get_origin(GTK_BUTTON(btn)->event_window, &x, &y);
 
1214
 
 
1215
        /* Send a NET_WM_ICON_GEOMETRY property change on the window. */
 
1216
        guint32 data[4];
 
1217
        data[0] = x;
 
1218
        data[1] = y;
 
1219
        data[2] = alloc->width;
 
1220
        data[3] = alloc->height;
 
1221
        XChangeProperty(GDK_DISPLAY(), tk->win,
 
1222
            gdk_x11_get_xatom_by_name("_NET_WM_ICON_GEOMETRY"),
 
1223
            XA_CARDINAL, 32, PropModeReplace, (guchar *) &data, 4);
 
1224
    }
 
1225
}
 
1226
 
 
1227
/* Update style on the taskbar when created or after a configuration change. */
 
1228
static void taskbar_update_style(TaskbarPlugin * tb)
 
1229
{
 
1230
    GtkOrientation bo = (tb->plug->panel->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
 
1231
    icon_grid_set_geometry(tb->icon_grid, bo,
 
1232
        ((tb->icons_only) ? tb->icon_size + ICON_ONLY_EXTRA : tb->task_width_max), tb->icon_size + BUTTON_HEIGHT_EXTRA,
 
1233
        tb->spacing, 0, tb->plug->panel->height);
 
1234
}
 
1235
 
 
1236
/* Update style on a task button when created or after a configuration change. */
 
1237
static void task_update_style(Task * tk, TaskbarPlugin * tb)
 
1238
{
 
1239
    if (tb->icons_only)
 
1240
        gtk_widget_hide(tk->label);
 
1241
    else
 
1242
        gtk_widget_show(tk->label);
 
1243
 
 
1244
    if( tb->flat_button )
 
1245
    {
 
1246
        gtk_toggle_button_set_active((GtkToggleButton*)tk->button, FALSE);
 
1247
        gtk_button_set_relief(GTK_BUTTON(tk->button), GTK_RELIEF_NONE);
 
1248
    }
 
1249
    else
 
1250
    {
 
1251
        gtk_toggle_button_set_active((GtkToggleButton*)tk->button, tk->focused);
 
1252
        gtk_button_set_relief(GTK_BUTTON(tk->button), GTK_RELIEF_NORMAL);
 
1253
    }
 
1254
 
 
1255
    task_draw_label(tk);
 
1256
}
 
1257
 
 
1258
/* Build graphic elements needed for a task button. */
 
1259
static void task_build_gui(TaskbarPlugin * tb, Task * tk)
 
1260
{
996
1261
    /* NOTE
997
1262
     * 1. the extended mask is sum of taskbar and pager needs
998
1263
     * see bug [ 940441 ] pager loose track of windows
999
1264
     *
1000
 
     * Do not change event mask to gtk windows spwaned by this gtk client
 
1265
     * Do not change event mask to gtk windows spawned by this gtk client
1001
1266
     * this breaks gtk internals */
1002
 
    if (!FBPANEL_WIN(tk->win))
1003
 
        XSelectInput (GDK_DISPLAY(), tk->win, PropertyChangeMask | StructureNotifyMask);
1004
 
 
1005
 
    /* button */
1006
 
 
 
1267
    if ( ! FBPANEL_WIN(tk->win))
 
1268
        XSelectInput(GDK_DISPLAY(), tk->win, PropertyChangeMask | StructureNotifyMask);
 
1269
 
 
1270
    /* Allocate a toggle button as the top level widget. */
1007
1271
    tk->button = gtk_toggle_button_new();
1008
 
    if( tb->flat_button )
1009
 
        gtk_button_set_relief( (GtkButton*)tk->button, GTK_RELIEF_NONE );
1010
 
    else
1011
 
        gtk_button_set_relief( (GtkButton*)tk->button, GTK_RELIEF_NORMAL);
1012
 
 
1013
 
    gtk_widget_show(tk->button);
1014
1272
    gtk_container_set_border_width(GTK_CONTAINER(tk->button), 0);
1015
 
    gtk_widget_add_events (tk->button, GDK_BUTTON_RELEASE_MASK );
1016
 
    g_signal_connect(tk->button, "button_press_event",
1017
 
          G_CALLBACK(on_tk_btn_press_event), (gpointer)tk);
1018
 
    g_signal_connect(tk->button, "button_release_event",
1019
 
          G_CALLBACK(on_tk_btn_release_event), (gpointer)tk);
1020
 
/*
1021
 
    g_signal_connect_after (G_OBJECT (tk->button), "leave",
1022
 
          G_CALLBACK (on_tk_leave), (gpointer) tk);
1023
 
    g_signal_connect_after (G_OBJECT (tk->button), "enter",
1024
 
          G_CALLBACK (on_tk_enter), (gpointer) tk);
1025
 
*/
1026
 
    g_signal_connect(tk->button, "size-allocate",
1027
 
          G_CALLBACK(on_tk_btn_size_allocate), (gpointer)tk);
1028
 
 
1029
 
#if 0
1030
 
    g_signal_connect_after (G_OBJECT (tk->button), "expose-event",
1031
 
          G_CALLBACK (on_tk_expose), (gpointer) tk);
1032
 
#endif
1033
 
    gtk_drag_dest_set( tk->button, 0, NULL, 0, 0);
1034
 
    g_signal_connect (G_OBJECT (tk->button), "drag-motion",
1035
 
          G_CALLBACK (on_tk_drag_motion), (gpointer) tk);
1036
 
    g_signal_connect (G_OBJECT (tk->button), "drag-leave",
1037
 
          G_CALLBACK (on_tk_drag_leave), (gpointer) tk);
1038
 
    g_signal_connect_after(G_OBJECT(tk->button), "scroll-event",
1039
 
            G_CALLBACK(on_tk_scroll_event), (gpointer)tk);
1040
 
 
1041
 
    /* pix and name */
1042
 
    w1 = tb->plug->panel->my_box_new(FALSE, 1);
1043
 
    gtk_container_set_border_width(GTK_CONTAINER(w1), 0);
1044
 
 
1045
 
    /* pix */
1046
 
    //get_wmclass(tk);
1047
 
    tk_update_icon(tb, tk, None);
1048
 
    tk->image = gtk_image_new_from_pixbuf(tk->pixbuf );
 
1273
    gtk_drag_dest_set(tk->button, 0, NULL, 0, 0);
 
1274
 
 
1275
    /* Connect signals to the button. */
 
1276
    g_signal_connect(tk->button, "button_press_event", G_CALLBACK(taskbar_button_press_event), (gpointer) tk);
 
1277
    g_signal_connect(G_OBJECT(tk->button), "drag-motion", G_CALLBACK(taskbar_button_drag_motion), (gpointer) tk);
 
1278
    g_signal_connect(G_OBJECT(tk->button), "drag-leave", G_CALLBACK(taskbar_button_drag_leave), (gpointer) tk);
 
1279
    g_signal_connect_after(G_OBJECT (tk->button), "enter", G_CALLBACK(taskbar_button_enter), (gpointer) tk);
 
1280
    g_signal_connect_after(G_OBJECT (tk->button), "leave", G_CALLBACK(taskbar_button_leave), (gpointer) tk);
 
1281
    g_signal_connect_after(G_OBJECT(tk->button), "scroll-event", G_CALLBACK(taskbar_button_scroll_event), (gpointer) tk);
 
1282
    g_signal_connect(tk->button, "size-allocate", G_CALLBACK(taskbar_button_size_allocate), (gpointer) tk);
 
1283
 
 
1284
    /* Create a box to contain the application icon and window title. */
 
1285
    GtkWidget * container = gtk_hbox_new(FALSE, 1);
 
1286
    gtk_container_set_border_width(GTK_CONTAINER(container), 0);
 
1287
 
 
1288
    /* Create an image to contain the application icon and add it to the box. */
 
1289
    GdkPixbuf* pixbuf = task_update_icon(tb, tk, None);
 
1290
    tk->image = gtk_image_new_from_pixbuf(pixbuf);
 
1291
    gtk_misc_set_padding(GTK_MISC(tk->image), 0, 0);
 
1292
    g_object_unref(pixbuf);
1049
1293
    gtk_widget_show(tk->image);
1050
 
    gtk_box_pack_start(GTK_BOX(w1), tk->image, FALSE, FALSE, 0);
1051
 
 
1052
 
    /* name */
1053
 
    tk->label = gtk_label_new(tk->iconified ? tk->iname : tk->name);
1054
 
    update_label_orient( tk->label, tb->plug );
1055
 
    if (!tb->icons_only)
1056
 
        gtk_widget_show(tk->label);
1057
 
    gtk_box_pack_start(GTK_BOX(w1), tk->label, TRUE, TRUE, 0);
1058
 
    gtk_widget_show(w1);
1059
 
    gtk_container_add (GTK_CONTAINER (tk->button), w1);
1060
 
 
1061
 
    //gtk_container_add (GTK_CONTAINER (tk->eb), tk->button);
1062
 
    gtk_box_pack_start(GTK_BOX(tb->bar), tk->button, FALSE, TRUE, 0);
1063
 
    GTK_WIDGET_UNSET_FLAGS (tk->button, GTK_CAN_FOCUS);
1064
 
    GTK_WIDGET_UNSET_FLAGS (tk->button, GTK_CAN_DEFAULT);
1065
 
 
1066
 
    gtk_widget_show(tk->button);
1067
 
    if (!task_visible(tb, tk)) {
1068
 
        gtk_widget_hide(tk->button);
1069
 
    }
1070
 
 
1071
 
    if (tk->urgency) {
1072
 
        /* Flash button for window with urgency hint */
1073
 
        tk_flash_window(tk);
1074
 
    }
1075
 
    RET();
1076
 
}
1077
 
 
1078
 
/* tell to remove element with zero refcount */
1079
 
static gboolean
1080
 
tb_remove_stale_tasks(Window *win, task *tk, gpointer data)
1081
 
{
1082
 
    ENTER;
1083
 
    if (tk->refcount-- == 0) {
1084
 
        //DBG("tb_net_list <del>: 0x%x %s\n", tk->win, tk->name);
1085
 
        del_task(tk->tb, tk, 0);
1086
 
        RET(TRUE);
1087
 
    }
1088
 
    RET(FALSE);
 
1294
    gtk_box_pack_start(GTK_BOX(container), tk->image, FALSE, FALSE, 0);
 
1295
 
 
1296
    /* Create a label to contain the window title and add it to the box. */
 
1297
    tk->label = gtk_label_new(NULL);
 
1298
    gtk_misc_set_alignment(GTK_MISC(tk->label), 0.0, 0.5);
 
1299
    gtk_label_set_ellipsize(GTK_LABEL(tk->label), PANGO_ELLIPSIZE_END);
 
1300
    gtk_box_pack_start(GTK_BOX(container), tk->label, TRUE, TRUE, 0);
 
1301
 
 
1302
    /* Add the box to the button. */
 
1303
    gtk_widget_show(container);
 
1304
    gtk_container_add(GTK_CONTAINER(tk->button), container);
 
1305
    gtk_container_set_border_width(GTK_CONTAINER(tk->button), 0);
 
1306
 
 
1307
    /* Add the button to the taskbar. */
 
1308
    icon_grid_add(tb->icon_grid, tk->button, TRUE); 
 
1309
    GTK_WIDGET_UNSET_FLAGS(tk->button, GTK_CAN_FOCUS);
 
1310
    GTK_WIDGET_UNSET_FLAGS(tk->button, GTK_CAN_DEFAULT);
 
1311
 
 
1312
    /* Update styles on the button. */
 
1313
    task_update_style(tk, tb);
 
1314
 
 
1315
    /* Flash button for window with urgency hint. */
 
1316
    if (tk->urgency)
 
1317
        task_set_urgency(tk);
1089
1318
}
1090
1319
 
1091
1320
/*****************************************************
1092
1321
 * handlers for NET actions                          *
1093
1322
 *****************************************************/
1094
1323
 
1095
 
 
1096
 
static void
1097
 
tb_net_client_list(GtkWidget *widget, taskbar *tb)
1098
 
{
1099
 
    int i;
1100
 
    task *tk;
1101
 
 
1102
 
    ENTER;
1103
 
    if (tb->wins)
1104
 
        XFree(tb->wins);
1105
 
    tb->wins = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CLIENT_LIST, XA_WINDOW, &tb->win_num);
1106
 
    if (!tb->wins)
1107
 
        RET();
1108
 
    for (i = 0; i < tb->win_num; i++) {
1109
 
        if ((tk = g_hash_table_lookup(tb->task_list, &tb->wins[i]))) {
1110
 
            ++tk->refcount;
1111
 
        } else {
1112
 
            NetWMWindowType nwwt;
1113
 
            NetWMState nws;
1114
 
 
1115
 
            get_net_wm_state(tb->wins[i], &nws);
1116
 
            if (!accept_net_wm_state(&nws, tb->accept_skip_pager))
1117
 
                continue;
1118
 
            get_net_wm_window_type(tb->wins[i], &nwwt);
1119
 
            if (!accept_net_wm_window_type(&nwwt))
1120
 
                continue;
1121
 
 
1122
 
            tk = g_new0(task, 1);
1123
 
            tk->refcount = 1;
1124
 
            ++tb->num_tasks;
1125
 
            tk->win = tb->wins[i];
1126
 
            tk->tb = tb;
1127
 
            tk->iconified = (get_wm_state(tk->win) == IconicState);
1128
 
            tk->desktop = get_net_wm_desktop(tk->win);
1129
 
            tk->nws = nws;
1130
 
            tk->nwwt = nwwt;
1131
 
            if( tb->use_urgency_hint && tk_has_urgency(tk)) {
1132
 
                tk->urgency = 1;
1133
 
            }
1134
 
 
1135
 
            tk_build_gui(tb, tk);
1136
 
            tk_set_names(tk);
1137
 
            g_hash_table_insert(tb->task_list, &tk->win, tk);
1138
 
            DBG("adding %08x(%p) %s\n", tk->win, FBPANEL_WIN(tk->win), tk->name);
1139
 
        }
1140
 
    }
1141
 
 
1142
 
    /* remove windows that arn't in the NET_CLIENT_LIST anymore */
1143
 
    g_hash_table_foreach_remove(tb->task_list, (GHRFunc) tb_remove_stale_tasks, NULL);
1144
 
    tb_display(tb);
1145
 
    RET();
1146
 
}
1147
 
 
1148
 
static void
1149
 
tb_net_current_desktop(GtkWidget *widget, taskbar *tb)
1150
 
{
1151
 
    ENTER;
1152
 
    tb->cur_desk = get_net_current_desktop();
1153
 
    tb_display(tb);
1154
 
    RET();
1155
 
}
1156
 
 
1157
 
static void
1158
 
tb_net_number_of_desktops(GtkWidget *widget, taskbar *tb)
1159
 
{
1160
 
    ENTER;
1161
 
    tb->desk_num = get_net_number_of_desktops();
1162
 
    tb_display(tb);
1163
 
    RET();
1164
 
}
1165
 
 
1166
 
 
1167
 
/* set new active window. if that happens to be us, then remeber
1168
 
 * current focus to use it for iconify command */
1169
 
static void
1170
 
tb_net_active_window(GtkWidget *widget, taskbar *tb)
1171
 
{
1172
 
    Window *f;
1173
 
    task *ntk, *ctk;
1174
 
    int drop_old, make_new;
1175
 
 
1176
 
    ENTER;
1177
 
    g_assert (tb != NULL);
1178
 
    drop_old = make_new = 0;
1179
 
    ctk = tb->focused;
1180
 
    ntk = NULL;
1181
 
    f = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_ACTIVE_WINDOW, XA_WINDOW, 0);
1182
 
    DBG("FOCUS=%x\n", f ? *f : 0);
1183
 
    if (!f) {
1184
 
        drop_old = 1;
1185
 
        tb->ptk = NULL;
1186
 
    } else {
1187
 
        if (*f == tb->topxwin) {
1188
 
            if (ctk) {
1189
 
                tb->ptk = ctk;
1190
 
                drop_old = 1;
1191
 
            }
1192
 
        } else {
1193
 
            tb->ptk = NULL;
1194
 
            ntk = find_task(tb, *f);
1195
 
            if (ntk != ctk) {
1196
 
                drop_old = 1;
1197
 
                make_new = 1;
 
1324
/* Handler for "client-list" event from root window listener. */
 
1325
static void taskbar_net_client_list(GtkWidget * widget, TaskbarPlugin * tb)
 
1326
{
 
1327
    /* Get the NET_CLIENT_LIST property. */
 
1328
    int client_count;
 
1329
    Window * client_list = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_CLIENT_LIST, XA_WINDOW, &client_count);
 
1330
    if (client_list != NULL)
 
1331
    {
 
1332
        /* Loop over client list, correlating it with task list. */
 
1333
        int i;
 
1334
        for (i = 0; i < client_count; i++)
 
1335
        {
 
1336
            /* Search for the window in the task list.  Set up context to do an insert right away if needed. */
 
1337
            Task * tk_pred = NULL;
 
1338
            Task * tk_cursor;
 
1339
            Task * tk = NULL;
 
1340
            for (tk_cursor = tb->task_list; tk_cursor != NULL; tk_pred = tk_cursor, tk_cursor = tk_cursor->task_flink)
 
1341
            {
 
1342
                if (tk_cursor->win == client_list[i])
 
1343
                {
 
1344
                    tk = tk_cursor;
 
1345
                    break;
 
1346
                }
 
1347
                if (tk_cursor->win > client_list[i])
 
1348
                    break;
 
1349
            }
 
1350
 
 
1351
            /* Task is already in task list. */
 
1352
            if (tk != NULL)
 
1353
                tk->present_in_client_list = TRUE;
 
1354
 
 
1355
            /* Task is not in task list. */
 
1356
            else
 
1357
            {
 
1358
                /* Evaluate window state and window type to see if it should be in task list. */
 
1359
                NetWMWindowType nwwt;
 
1360
                NetWMState nws;
 
1361
                get_net_wm_state(client_list[i], &nws);
 
1362
                get_net_wm_window_type(client_list[i], &nwwt);
 
1363
                if ((accept_net_wm_state(&nws))
 
1364
                && (accept_net_wm_window_type(&nwwt)))
 
1365
                {
 
1366
                    /* Allocate and initialize new task structure. */
 
1367
                    tk = g_new0(Task, 1);
 
1368
                    tk->present_in_client_list = TRUE;
 
1369
                    tk->win = client_list[i];
 
1370
                    tk->tb = tb;
 
1371
                    tk->name_source = None;
 
1372
                    tk->image_source = None;
 
1373
                    tk->iconified = (get_wm_state(tk->win) == IconicState);
 
1374
                    tk->desktop = get_net_wm_desktop(tk->win);
 
1375
                    if (tb->use_urgency_hint)
 
1376
                        tk->urgency = task_has_urgency(tk);
 
1377
                    task_build_gui(tb, tk);
 
1378
                    task_set_names(tk, None);
 
1379
                    task_set_class(tk);
 
1380
 
 
1381
                    /* Link the task structure into the task list. */
 
1382
                    if (tk_pred == NULL)
 
1383
                    {
 
1384
                        tk->task_flink = tb->task_list;
 
1385
                        tb->task_list = tk;
 
1386
                    }
 
1387
                    else
 
1388
                    {
 
1389
                        tk->task_flink = tk_pred->task_flink;
 
1390
                        tk_pred->task_flink = tk;
 
1391
                    }
 
1392
                }
 
1393
            }
 
1394
        }
 
1395
        XFree(client_list);
 
1396
    }
 
1397
 
 
1398
    /* Remove windows from the task list that are not present in the NET_CLIENT_LIST. */
 
1399
    Task * tk_pred = NULL;
 
1400
    Task * tk = tb->task_list;
 
1401
    while (tk != NULL)
 
1402
    {
 
1403
        Task * tk_succ = tk->task_flink;
 
1404
        if (tk->present_in_client_list)
 
1405
        {
 
1406
            tk->present_in_client_list = FALSE;
 
1407
            tk_pred = tk;
 
1408
        }
 
1409
        else
 
1410
        {
 
1411
            if (tk_pred == NULL)
 
1412
                tb->task_list = tk_succ;
 
1413
                else tk_pred->task_flink = tk_succ;
 
1414
            task_delete(tb, tk, FALSE);
 
1415
        }
 
1416
        tk = tk_succ;
 
1417
    }
 
1418
 
 
1419
    /* Redraw the taskbar. */
 
1420
    taskbar_redraw(tb);
 
1421
}
 
1422
 
 
1423
/* Handler for "current-desktop" event from root window listener. */
 
1424
static void taskbar_net_current_desktop(GtkWidget * widget, TaskbarPlugin * tb)
 
1425
{
 
1426
    /* Store the local copy of current desktops.  Redisplay the taskbar. */
 
1427
    tb->current_desktop = get_net_current_desktop();
 
1428
    recompute_group_visibility_on_current_desktop(tb);
 
1429
    taskbar_redraw(tb);
 
1430
}
 
1431
 
 
1432
/* Handler for "number-of-desktops" event from root window listener. */
 
1433
static void taskbar_net_number_of_desktops(GtkWidget * widget, TaskbarPlugin * tb)
 
1434
{
 
1435
    /* Store the local copy of number of desktops.  Recompute the popup menu and redisplay the taskbar. */
 
1436
    tb->number_of_desktops = get_net_number_of_desktops();
 
1437
    taskbar_make_menu(tb);
 
1438
    taskbar_redraw(tb);
 
1439
}
 
1440
 
 
1441
/* Handler for "active-window" event from root window listener. */
 
1442
static void taskbar_net_active_window(GtkWidget * widget, TaskbarPlugin * tb)
 
1443
{
 
1444
    gboolean drop_old = FALSE;
 
1445
    gboolean make_new = FALSE;
 
1446
    Task * ctk = tb->focused;
 
1447
    Task * ntk = NULL;
 
1448
 
 
1449
    /* Get the window that has focus. */
 
1450
    Window * f = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_ACTIVE_WINDOW, XA_WINDOW, 0);
 
1451
    if (f == NULL)
 
1452
    {
 
1453
        /* No window has focus. */
 
1454
        drop_old = TRUE;
 
1455
        tb->focused_previous = NULL;
 
1456
    }
 
1457
    else
 
1458
    {
 
1459
        if (*f == tb->plug->panel->topxwin)
 
1460
        {
 
1461
            /* Taskbar window gained focus (this isn't supposed to be able to happen).  Remember current focus. */
 
1462
            if (ctk != NULL)
 
1463
            {
 
1464
                tb->focused_previous = ctk;
 
1465
                drop_old = TRUE;
 
1466
            }
 
1467
        }
 
1468
        else
 
1469
        {
 
1470
            /* Identify task that gained focus. */
 
1471
            tb->focused_previous = NULL;
 
1472
            ntk = task_lookup(tb, *f);
 
1473
            if (ntk != ctk)
 
1474
            {
 
1475
                drop_old = TRUE;
 
1476
                make_new = TRUE;
1198
1477
            }
1199
1478
        }
1200
1479
        XFree(f);
1201
1480
    }
1202
 
    if (ctk && drop_old) {
1203
 
        ctk->focused = 0;
 
1481
 
 
1482
    /* If our idea of the current task lost focus, update data structures. */
 
1483
    if ((ctk != NULL) && (drop_old))
 
1484
    {
 
1485
        ctk->focused = FALSE;
1204
1486
        tb->focused = NULL;
1205
 
        tk_display(tb, ctk);
1206
 
        DBG("old focus was dropped\n");
 
1487
        if(!tb->flat_button) /* relieve the button if flat buttons is not used. */
 
1488
            gtk_toggle_button_set_active((GtkToggleButton*)ctk->button, FALSE);
 
1489
 
 
1490
        task_button_redraw(ctk, tb);
1207
1491
    }
1208
 
    if (ntk && make_new) {
1209
 
        ntk->focused = 1;
 
1492
 
 
1493
    /* If a task gained focus, update data structures. */
 
1494
    if ((ntk != NULL) && (make_new))
 
1495
    {
 
1496
        if(!tb->flat_button) /* depress the button if flat buttons is not used. */
 
1497
            gtk_toggle_button_set_active((GtkToggleButton*)ntk->button, TRUE);
 
1498
        ntk->focused = TRUE;
1210
1499
        tb->focused = ntk;
1211
 
        tk_display(tb, ntk);
1212
 
        DBG("new focus was set\n");
1213
 
    }
1214
 
    RET();
1215
 
}
1216
 
 
1217
 
/* For older Xlib headers */
1218
 
#ifndef XUrgencyHint
1219
 
#define XUrgencyHint (1 << 8)
1220
 
#endif
1221
 
 
1222
 
static gboolean
1223
 
tk_has_urgency( task* tk )
1224
 
{
1225
 
    XWMHints* hints;
1226
 
 
1227
 
    tk->urgency = 0;
1228
 
    hints = (XWMHints *) get_xaproperty (tk->win, XA_WM_HINTS, XA_WM_HINTS, 0);
1229
 
    if (hints) {
1230
 
        if (hints->flags & XUrgencyHint) /* Got urgency hint */
1231
 
            tk->urgency = 1;
1232
 
        XFree( hints );
1233
 
    }
1234
 
    return tk->urgency;
1235
 
}
1236
 
 
1237
 
static void
1238
 
tb_propertynotify(taskbar *tb, XEvent *ev)
1239
 
{
1240
 
    Atom at;
1241
 
    Window win;
1242
 
 
1243
 
    ENTER;
1244
 
    DBG("win=%x\n", ev->xproperty.window);
1245
 
 
1246
 
    /* The property is deleted */
1247
 
    if( ((XPropertyEvent*)ev)->state == 1 )
1248
 
        return;
1249
 
 
1250
 
    at = ev->xproperty.atom;
1251
 
    win = ev->xproperty.window;
1252
 
    if (win != GDK_ROOT_WINDOW()) {
1253
 
        task *tk = find_task(tb, win);
1254
 
 
1255
 
        if (!tk) RET();
1256
 
            DBG("win=%x\n", ev->xproperty.window);
1257
 
        if (at == a_NET_WM_DESKTOP) {
1258
 
                DBG("NET_WM_DESKTOP\n");
1259
 
            tk->desktop = get_net_wm_desktop(win);
1260
 
            tb_display(tb);
1261
 
        }  else if (at == XA_WM_NAME) {
1262
 
                DBG("WM_NAME\n");
1263
 
            tk_set_names(tk);
1264
 
            //tk_display(tb, tk);
1265
 
        }  else if (at == XA_WM_CLASS) {
1266
 
                DBG("WM_CLASS\n");
1267
 
                //get_wmclass(tk);
1268
 
        } else if (at == a_WM_STATE)    {
1269
 
                DBG("WM_STATE\n");
1270
 
            /* iconified state changed? */
1271
 
            tk->iconified = (get_wm_state (tk->win) == IconicState);
1272
 
                tk_set_names(tk);
1273
 
            //tk_display(tb, tk);
1274
 
        } else if (at == XA_WM_HINTS)   {
1275
 
            /* some windows set their WM_HINTS icon after mapping */
1276
 
            DBG("XA_WM_HINTS\n");
1277
 
                //get_wmclass(tk);
1278
 
            tk_update_icon (tb, tk, XA_WM_HINTS);
1279
 
            gtk_image_set_from_pixbuf (GTK_IMAGE(tk->image), tk->pixbuf);
1280
 
                if (tb->use_urgency_hint) {
1281
 
                    if (tk_has_urgency(tk)) {
1282
 
                        //tk->urgency = 1;
1283
 
                        tk_flash_window(tk);
1284
 
                    } else {
1285
 
                        //tk->urgency = 0;
1286
 
                        tk_unflash_window(tk);
1287
 
                    }
1288
 
                }
1289
 
            } else if (at == a_NET_WM_STATE) {
1290
 
                NetWMState nws;
1291
 
 
1292
 
            DBG("_NET_WM_STATE\n");
1293
 
            get_net_wm_state(tk->win, &nws);
1294
 
                if (!accept_net_wm_state(&nws, tb->accept_skip_pager)) {
1295
 
            del_task(tb, tk, 1);
1296
 
            tb_display(tb);
1297
 
            }
1298
 
        } else if (at == a_NET_WM_ICON) {
1299
 
            DBG("_NET_WM_ICON\n");
1300
 
                DBG("#0 %d\n", GDK_IS_PIXBUF (tk->pixbuf));
1301
 
                tk_update_icon (tb, tk, a_NET_WM_ICON);
1302
 
                DBG("#1 %d\n", GDK_IS_PIXBUF (tk->pixbuf));
1303
 
            gtk_image_set_from_pixbuf (GTK_IMAGE(tk->image), tk->pixbuf);
1304
 
                DBG("#2 %d\n", GDK_IS_PIXBUF (tk->pixbuf));
1305
 
        } else if (at == a_NET_WM_WINDOW_TYPE) {
1306
 
                NetWMWindowType nwwt;
1307
 
 
1308
 
            DBG("_NET_WM_WINDOW_TYPE\n");
1309
 
            get_net_wm_window_type(tk->win, &nwwt);
1310
 
                if (!accept_net_wm_window_type(&nwwt)) {
1311
 
            del_task(tb, tk, 1);
1312
 
            tb_display(tb);
1313
 
            }
1314
 
        } else {
1315
 
                DBG("at = %d\n", at);
 
1500
        task_button_redraw(ntk, tb);
 
1501
    }
 
1502
}
 
1503
 
 
1504
/* Determine if the "urgency" hint is set on a window. */
 
1505
static gboolean task_has_urgency(Task * tk)
 
1506
{
 
1507
    gboolean result = FALSE;
 
1508
    XWMHints * hints = (XWMHints *) get_xaproperty(tk->win, XA_WM_HINTS, XA_WM_HINTS, 0);
 
1509
    if (hints != NULL)
 
1510
    {
 
1511
        if (hints->flags & XUrgencyHint)
 
1512
            result = TRUE;
 
1513
        XFree(hints);
 
1514
    }
 
1515
    return result;
 
1516
}
 
1517
 
 
1518
/* Handle PropertyNotify event.
 
1519
 * http://tronche.com/gui/x/icccm/
 
1520
 * http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html */
 
1521
static void taskbar_property_notify_event(TaskbarPlugin *tb, XEvent *ev)
 
1522
{
 
1523
    /* State may be PropertyNewValue, PropertyDeleted. */
 
1524
    if (((XPropertyEvent*) ev)->state == PropertyNewValue)
 
1525
    {
 
1526
        Atom at = ev->xproperty.atom;
 
1527
        Window win = ev->xproperty.window;
 
1528
        if (win != GDK_ROOT_WINDOW())
 
1529
        {
 
1530
            /* Look up task structure by X window handle. */
 
1531
            Task * tk = task_lookup(tb, win);
 
1532
            if (tk != NULL)
 
1533
            {
 
1534
                /* Install an error handler that ignores BadWindow.
 
1535
                 * We frequently get a PropertyNotify event on deleted windows. */
 
1536
                XErrorHandler previous_error_handler = XSetErrorHandler(panel_handle_x_error_swallow_BadWindow_BadDrawable);
 
1537
 
 
1538
                /* Dispatch on atom. */
 
1539
                if (at == a_NET_WM_DESKTOP)
 
1540
                {
 
1541
                    /* Window changed desktop. */
 
1542
                    tk->desktop = get_net_wm_desktop(win);
 
1543
                    taskbar_redraw(tb);
 
1544
                }
 
1545
                else if ((at == XA_WM_NAME) || (at == a_NET_WM_NAME) || (at == a_NET_WM_VISIBLE_NAME))
 
1546
                {
 
1547
                    /* Window changed name. */
 
1548
                    task_set_names(tk, at);
 
1549
                    if (tk->res_class != NULL)
 
1550
                    {
 
1551
                        /* A change to the window name may change the visible name of the class. */
 
1552
                        recompute_group_visibility_for_class(tb, tk->res_class);
 
1553
                        if (tk->res_class->visible_task != NULL)
 
1554
                            task_draw_label(tk->res_class->visible_task);
 
1555
                    }
 
1556
                }
 
1557
                else if (at == XA_WM_CLASS)
 
1558
                {
 
1559
                    /* Window changed class. */
 
1560
                    task_set_class(tk);
 
1561
                    taskbar_redraw(tb);
 
1562
                }
 
1563
                else if (at == a_WM_STATE)
 
1564
                {
 
1565
                    /* Window changed state. */
 
1566
                    tk->iconified = (get_wm_state(win) == IconicState);
 
1567
                    task_draw_label(tk);
 
1568
                }
 
1569
                else if (at == XA_WM_HINTS)
 
1570
                {
 
1571
                    /* Window changed "window manager hints".
 
1572
                     * Some windows set their WM_HINTS icon after mapping. */
 
1573
                    GdkPixbuf * pixbuf = task_update_icon(tb, tk, XA_WM_HINTS);
 
1574
                    if (pixbuf != NULL)
 
1575
                    {
 
1576
                        gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
 
1577
                        g_object_unref(pixbuf);
 
1578
                    }
 
1579
 
 
1580
                    if (tb->use_urgency_hint)
 
1581
                    {
 
1582
                        tk->urgency = task_has_urgency(tk);
 
1583
                        if (tk->urgency)
 
1584
                            task_set_urgency(tk);
 
1585
                        else
 
1586
                            task_clear_urgency(tk);
 
1587
                    }
 
1588
                }
 
1589
                else if (at == a_NET_WM_STATE)
 
1590
                {
 
1591
                    /* Window changed EWMH state. */
 
1592
                    NetWMState nws;
 
1593
                    get_net_wm_state(tk->win, &nws);
 
1594
                    if ( ! accept_net_wm_state(&nws))
 
1595
                    {
 
1596
                        task_delete(tb, tk, TRUE);
 
1597
                        taskbar_redraw(tb);
 
1598
                    }
 
1599
                }
 
1600
                else if (at == a_NET_WM_ICON)
 
1601
                {
 
1602
                    /* Window changed EWMH icon. */
 
1603
                    GdkPixbuf * pixbuf = task_update_icon(tb, tk, a_NET_WM_ICON);
 
1604
                    if (pixbuf != NULL)
 
1605
                    {
 
1606
                        gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
 
1607
                        g_object_unref(pixbuf);
 
1608
                    }
 
1609
                }
 
1610
                else if (at == a_NET_WM_WINDOW_TYPE)
 
1611
                {
 
1612
                    /* Window changed EWMH window type. */
 
1613
                    NetWMWindowType nwwt;
 
1614
                    get_net_wm_window_type(tk->win, &nwwt);
 
1615
                    if ( ! accept_net_wm_window_type(&nwwt))
 
1616
                    {
 
1617
                        task_delete(tb, tk, TRUE);
 
1618
                        taskbar_redraw(tb);
 
1619
                    }
 
1620
                }
 
1621
                XSetErrorHandler(previous_error_handler);
 
1622
            }
1316
1623
        }
1317
1624
    }
1318
 
    RET();
1319
 
}
1320
 
 
1321
 
static GdkFilterReturn
1322
 
tb_event_filter( XEvent *xev, GdkEvent *event, taskbar *tb)
1323
 
{
1324
 
 
1325
 
    ENTER;
1326
 
    //RET(GDK_FILTER_CONTINUE);
1327
 
    g_assert(tb != NULL);
1328
 
    if (xev->type == PropertyNotify )
1329
 
        tb_propertynotify(tb, xev);
1330
 
    RET(GDK_FILTER_CONTINUE);
1331
 
}
1332
 
 
1333
 
static void
1334
 
menu_close_window(GtkWidget *widget, taskbar *tb)
1335
 
{
1336
 
    ENTER;
1337
 
    DBG("win %x\n", tb->menutask->win);
1338
 
    XSync (GDK_DISPLAY(), 0);
1339
 
    //XKillClient(GDK_DISPLAY(), tb->menutask->win);
 
1625
}
 
1626
 
 
1627
/* GDK event filter. */
 
1628
static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, TaskbarPlugin * tb)
 
1629
{
 
1630
    /* Look for PropertyNotify events and update state. */
 
1631
    if (xev->type == PropertyNotify)
 
1632
        taskbar_property_notify_event(tb, xev);
 
1633
    return GDK_FILTER_CONTINUE;
 
1634
}
 
1635
 
 
1636
/* Handler for "activate" event on Raise item of right-click menu for task buttons. */
 
1637
static void menu_raise_window(GtkWidget * widget, TaskbarPlugin * tb)
 
1638
{
 
1639
    if ((tb->menutask->desktop != -1) && (tb->menutask->desktop != tb->current_desktop))
 
1640
        Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, tb->menutask->desktop, 0, 0, 0, 0);
 
1641
    XMapRaised(GDK_DISPLAY(), tb->menutask->win);
 
1642
    task_group_menu_destroy(tb);
 
1643
}
 
1644
 
 
1645
/* Handler for "activate" event on Restore item of right-click menu for task buttons. */
 
1646
static void menu_restore_window(GtkWidget * widget, TaskbarPlugin * tb)
 
1647
{
 
1648
    GdkWindow * win = gdk_window_foreign_new(tb->menutask->win);
 
1649
    gdk_window_unmaximize(win);
 
1650
    gdk_window_unref(win);
 
1651
    task_group_menu_destroy(tb);
 
1652
}
 
1653
 
 
1654
/* Handler for "activate" event on Maximize item of right-click menu for task buttons. */
 
1655
static void menu_maximize_window(GtkWidget * widget, TaskbarPlugin * tb)
 
1656
{
 
1657
    GdkWindow * win = gdk_window_foreign_new(tb->menutask->win);
 
1658
    gdk_window_maximize(win);
 
1659
    gdk_window_unref(win);
 
1660
    task_group_menu_destroy(tb);
 
1661
}
 
1662
 
 
1663
/* Handler for "activate" event on Iconify item of right-click menu for task buttons. */
 
1664
static void menu_iconify_window(GtkWidget * widget, TaskbarPlugin * tb)
 
1665
{
 
1666
    XIconifyWindow(GDK_DISPLAY(), tb->menutask->win, DefaultScreen(GDK_DISPLAY()));
 
1667
    task_group_menu_destroy(tb);
 
1668
}
 
1669
 
 
1670
/* Handler for "activate" event on Move to Workspace item of right-click menu for task buttons. */
 
1671
static void menu_move_to_workspace(GtkWidget * widget, TaskbarPlugin * tb)
 
1672
{
 
1673
    int num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "num"));
 
1674
    Xclimsg(tb->menutask->win, a_NET_WM_DESKTOP, num, 0, 0, 0, 0);
 
1675
    task_group_menu_destroy(tb);
 
1676
}
 
1677
 
 
1678
/* Handler for "activate" event on Close item of right-click menu for task buttons. */
 
1679
static void menu_close_window(GtkWidget * widget, TaskbarPlugin * tb)
 
1680
{
1340
1681
    Xclimsgwm(tb->menutask->win, a_WM_PROTOCOLS, a_WM_DELETE_WINDOW);
1341
 
    XSync (GDK_DISPLAY(), 0);
1342
 
    RET();
1343
 
}
1344
 
 
1345
 
 
1346
 
static void
1347
 
menu_raise_window(GtkWidget *widget, taskbar *tb)
1348
 
{
1349
 
    ENTER;
1350
 
    DBG("win %x\n", tb->menutask->win);
1351
 
    XMapRaised(GDK_DISPLAY(), tb->menutask->win);
1352
 
    RET();
1353
 
}
1354
 
 
1355
 
 
1356
 
static void
1357
 
menu_iconify_window(GtkWidget *widget, taskbar *tb)
1358
 
{
1359
 
    ENTER;
1360
 
    DBG("win %x\n", tb->menutask->win);
1361
 
    XIconifyWindow (GDK_DISPLAY(), tb->menutask->win, DefaultScreen(GDK_DISPLAY()));
1362
 
    RET();
1363
 
}
1364
 
 
1365
 
static void
1366
 
menu_restore_window(GtkWidget *widget, taskbar *tb)
1367
 
{
1368
 
    GdkWindow* win;
1369
 
    ENTER;
1370
 
    DBG("win %x\n", tb->menutask->win);
1371
 
    win = gdk_window_foreign_new( tb->menutask->win );
1372
 
    gdk_window_unmaximize( win );
1373
 
    gdk_window_unref( win );
1374
 
    RET();
1375
 
}
1376
 
 
1377
 
static void
1378
 
menu_maximize_window(GtkWidget *widget, taskbar *tb)
1379
 
{
1380
 
    GdkWindow* win;
1381
 
    ENTER;
1382
 
    DBG("win %x\n", tb->menutask->win);
1383
 
    win = gdk_window_foreign_new( tb->menutask->win );
1384
 
    gdk_window_maximize( win );
1385
 
    gdk_window_unref( win );
1386
 
    RET();
1387
 
}
1388
 
 
1389
 
static void
1390
 
menu_move_to_workspace( GtkWidget* mi, taskbar* tb )
1391
 
{
1392
 
    GdkWindow* win;
1393
 
    int num = GPOINTER_TO_INT( g_object_get_data( G_OBJECT(mi), "num" ) );
1394
 
    _wnck_change_workspace( DefaultScreenOfDisplay(GDK_DISPLAY()), tb->menutask->win, num );
1395
 
}
1396
 
 
1397
 
static GtkWidget *
1398
 
taskbar_make_menu(taskbar *tb)
1399
 
{
1400
 
    GtkWidget *mi, *menu, *workspace_menu = NULL;
1401
 
    int i;
1402
 
    char label[128];
1403
 
 
1404
 
    ENTER;
1405
 
    menu = gtk_menu_new ();
1406
 
 
1407
 
    mi = gtk_menu_item_new_with_label (_("Raise"));
1408
 
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1409
 
    g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_raise_window, tb);
1410
 
 
1411
 
    mi = gtk_menu_item_new_with_label (_("Restore"));
1412
 
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1413
 
    g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_restore_window, tb);
1414
 
 
1415
 
    mi = gtk_menu_item_new_with_label (_("Maximize"));
1416
 
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1417
 
    g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_maximize_window, tb);
1418
 
 
1419
 
    mi = gtk_menu_item_new_with_label (_("Iconify"));
1420
 
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1421
 
    g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_iconify_window, tb);
1422
 
 
1423
 
    if( tb->desk_num > 1 )
 
1682
    task_group_menu_destroy(tb);
 
1683
}
 
1684
 
 
1685
/* Make right-click menu for task buttons.
 
1686
 * This depends on number of desktops and edge. */
 
1687
static void taskbar_make_menu(TaskbarPlugin * tb)
 
1688
{
 
1689
    /* Deallocate old menu if present. */
 
1690
    if (tb->menu != NULL)
 
1691
        gtk_widget_destroy(tb->menu);
 
1692
 
 
1693
    /* Allocate menu. */
 
1694
    GtkWidget * menu = gtk_menu_new();
 
1695
 
 
1696
    /* Add Raise menu item. */
 
1697
    GtkWidget *mi = gtk_menu_item_new_with_mnemonic(_("_Raise"));
 
1698
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
 
1699
    g_signal_connect(G_OBJECT(mi), "activate", (GCallback) menu_raise_window, tb);
 
1700
 
 
1701
    /* Add Restore menu item. */
 
1702
    mi = gtk_menu_item_new_with_mnemonic(_("R_estore"));
 
1703
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
 
1704
    g_signal_connect(G_OBJECT(mi), "activate", (GCallback) menu_restore_window, tb);
 
1705
 
 
1706
    /* Add Maximize menu item. */
 
1707
    mi = gtk_menu_item_new_with_mnemonic(_("Ma_ximize"));
 
1708
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
 
1709
    g_signal_connect(G_OBJECT(mi), "activate", (GCallback) menu_maximize_window, tb);
 
1710
 
 
1711
    /* Add Iconify menu item. */
 
1712
    mi = gtk_menu_item_new_with_mnemonic(_("Ico_nify"));
 
1713
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
 
1714
    g_signal_connect(G_OBJECT(mi), "activate", (GCallback) menu_iconify_window, tb);
 
1715
 
 
1716
    /* If multiple desktops are supported, add menu items to select them. */
 
1717
    if (tb->number_of_desktops > 1)
1424
1718
    {
1425
 
        workspace_menu = gtk_menu_new();
1426
 
        for( i = 1; i <= tb->desk_num; ++i )
 
1719
        char label[128];
 
1720
 
 
1721
        /* Allocate submenu. */
 
1722
        GtkWidget * workspace_menu = gtk_menu_new();
 
1723
 
 
1724
        /* Loop over all desktops. */
 
1725
        int i;
 
1726
        for (i = 1; i <= tb->number_of_desktops; i++)
1427
1727
        {
1428
 
            g_snprintf( label, 128, _("Workspace %d"), i);
1429
 
            mi = gtk_menu_item_new_with_label( label );
1430
 
            g_object_set_data( G_OBJECT(mi), "num", GINT_TO_POINTER(i - 1) );
1431
 
            g_signal_connect( mi, "activate", G_CALLBACK(menu_move_to_workspace), tb );
1432
 
            gtk_menu_shell_append( (GtkMenuShell*)workspace_menu, mi );
 
1728
            /* For the first 9 desktops, allow the desktop number as a keyboard shortcut. */
 
1729
            if (i <= 9)
 
1730
            {
 
1731
                g_snprintf(label, sizeof(label), _("Workspace _%d"), i);
 
1732
                mi = gtk_menu_item_new_with_mnemonic(label);
 
1733
            }
 
1734
            else
 
1735
            {
 
1736
                g_snprintf(label, sizeof(label), _("Workspace %d"), i);
 
1737
                mi = gtk_menu_item_new_with_label(label);
 
1738
            }
 
1739
 
 
1740
            /* Set the desktop number as a property on the menu item. */
 
1741
            g_object_set_data(G_OBJECT(mi), "num", GINT_TO_POINTER(i - 1));
 
1742
            g_signal_connect(mi, "activate", G_CALLBACK(menu_move_to_workspace), tb);
 
1743
            gtk_menu_shell_append(GTK_MENU_SHELL(workspace_menu), mi);
1433
1744
        }
1434
 
        gtk_menu_shell_append( GTK_MENU_SHELL (workspace_menu),
1435
 
                                                   gtk_separator_menu_item_new());
1436
 
        mi = gtk_menu_item_new_with_label(_("All workspaces"));
1437
 
        g_object_set_data( G_OBJECT(mi), "num", GINT_TO_POINTER(ALL_WORKSPACES) );
1438
 
        g_signal_connect( mi, "activate", G_CALLBACK(menu_move_to_workspace), tb );
1439
 
        gtk_menu_shell_append( (GtkMenuShell*)workspace_menu, mi );
1440
 
 
1441
 
        gtk_widget_show_all( workspace_menu );
1442
 
 
1443
 
        mi = gtk_menu_item_new_with_label (_("Move to Workspace"));
1444
 
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1445
 
 
1446
 
        gtk_menu_item_set_submenu( GTK_MENU_ITEM(mi), workspace_menu );
1447
 
        workspace_menu = mi;
 
1745
 
 
1746
        /* Add a separator. */
 
1747
        gtk_menu_shell_append(GTK_MENU_SHELL(workspace_menu), gtk_separator_menu_item_new());
 
1748
 
 
1749
        /* Add "move to all workspaces" item.  This causes the window to be visible no matter what desktop is active. */
 
1750
        mi = gtk_menu_item_new_with_mnemonic(_("_All workspaces"));
 
1751
        g_object_set_data(G_OBJECT(mi), "num", GINT_TO_POINTER(ALL_WORKSPACES));
 
1752
        g_signal_connect(mi, "activate", G_CALLBACK(menu_move_to_workspace), tb);
 
1753
        gtk_menu_shell_append(GTK_MENU_SHELL(workspace_menu), mi);
 
1754
 
 
1755
        /* Add Move to Workspace menu item as a submenu. */
 
1756
        mi = gtk_menu_item_new_with_mnemonic(_("_Move to Workspace"));
 
1757
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
 
1758
        gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), workspace_menu);
1448
1759
    }
1449
1760
 
1450
 
    /* we want this item to be farest from mouse pointer */
1451
 
    mi = gtk_menu_item_new_with_label (_("Close Window"));
1452
 
    if (tb->plug->panel->edge == EDGE_BOTTOM)
 
1761
    /* Add Close menu item.  By popular demand, we place this menu item closest to the cursor. */
 
1762
    mi = gtk_menu_item_new_with_mnemonic (_("_Close Window"));
 
1763
    if (tb->plug->panel->edge != EDGE_BOTTOM)
1453
1764
    {
1454
 
        gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
1455
 
        gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), mi);
 
1765
        gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
 
1766
        gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), mi);
1456
1767
    }
1457
1768
    else
1458
1769
    {
1459
 
//        gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), workspace_menu);
1460
 
 
1461
 
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new());
1462
 
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
 
1770
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
 
1771
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
1463
1772
    }
1464
1773
    g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_close_window, tb);
1465
 
    gtk_widget_show_all (menu);
1466
 
 
1467
 
    RET(menu);
1468
 
}
1469
 
 
1470
 
 
1471
 
static void
1472
 
taskbar_build_gui(Plugin *p)
1473
 
{
1474
 
    taskbar *tb = (taskbar *)p->priv;
1475
 
    GtkOrientation  bo;
1476
 
 
1477
 
    ENTER;
1478
 
 
1479
 
    bo = (tb->plug->panel->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1480
 
    tb->bar = gtk_bar_new(bo, tb->spacing);
1481
 
    if (tb->icons_only) {
1482
 
        gtk_bar_set_max_child_size(GTK_BAR(tb->bar),
1483
 
              GTK_WIDGET(p->panel->box)->allocation.height -2);
1484
 
     } else
1485
 
        gtk_bar_set_max_child_size(GTK_BAR(tb->bar), tb->task_width_max);
1486
 
    gtk_container_add (GTK_CONTAINER (p->pwid), tb->bar);
1487
 
    gtk_widget_show(tb->bar);
1488
 
 
1489
 
    tb->gen_pixbuf =  gdk_pixbuf_new_from_xpm_data((const char **)icon_xpm);
1490
 
 
1491
 
    gdk_window_add_filter(NULL, (GdkFilterFunc)tb_event_filter, tb );
1492
 
 
1493
 
    g_signal_connect (G_OBJECT (fbev), "current_desktop",
1494
 
          G_CALLBACK (tb_net_current_desktop), (gpointer) tb);
1495
 
    g_signal_connect (G_OBJECT (fbev), "active_window",
1496
 
          G_CALLBACK (tb_net_active_window), (gpointer) tb);
1497
 
    g_signal_connect (G_OBJECT (fbev), "number_of_desktops",
1498
 
          G_CALLBACK (tb_net_number_of_desktops), (gpointer) tb);
1499
 
    g_signal_connect (G_OBJECT (fbev), "client_list",
1500
 
          G_CALLBACK (tb_net_client_list), (gpointer) tb);
1501
 
 
1502
 
    tb->desk_num = get_net_number_of_desktops();
1503
 
    tb->cur_desk = get_net_current_desktop();
1504
 
    tb->focused = NULL;
1505
 
 
1506
 
    tb->menu = taskbar_make_menu(tb);
1507
 
    gtk_container_set_border_width(GTK_CONTAINER(p->pwid), 0);
1508
 
    gtk_widget_show_all(tb->bar);
1509
 
    RET();
1510
 
}
1511
 
 
1512
 
void net_active_detect()
1513
 
{
1514
 
    int nitens;
1515
 
    Atom *data;
1516
 
 
1517
 
    data = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_SUPPORTED, XA_ATOM, &nitens);
1518
 
    if (!data)
1519
 
    return;
1520
 
 
1521
 
    while (nitens > 0)
1522
 
    if(data[--nitens]==a_NET_ACTIVE_WINDOW) {
1523
 
        use_net_active = TRUE;
1524
 
            break;
1525
 
        }
1526
 
 
1527
 
    XFree(data);
1528
 
}
1529
 
 
1530
 
static int
1531
 
taskbar_constructor(Plugin *p, char** fp)
1532
 
{
1533
 
    taskbar *tb;
1534
 
    line s;
1535
 
    GtkRequisition req;
1536
 
 
1537
 
    ENTER;
1538
 
 
 
1774
 
 
1775
    gtk_widget_show_all(menu);
 
1776
    tb->menu = menu;
 
1777
}
 
1778
 
 
1779
/* Build graphic elements needed for the taskbar. */
 
1780
static void taskbar_build_gui(Plugin * p)
 
1781
{
 
1782
    TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
 
1783
 
 
1784
    /* Set up style for taskbar. */
1539
1785
    gtk_rc_parse_string(taskbar_rc);
1540
1786
 
1541
 
    /* FIXME: Is there any better way to do this? */
 
1787
    /* Allocate top level widget and set into Plugin widget pointer. */
1542
1788
    p->pwid = gtk_event_box_new();
1543
 
    GTK_WIDGET_SET_FLAGS( p->pwid, GTK_NO_WINDOW );
 
1789
    gtk_container_set_border_width(GTK_CONTAINER(p->pwid), 0);
 
1790
    GTK_WIDGET_SET_FLAGS(p->pwid, GTK_NO_WINDOW);
1544
1791
    gtk_widget_set_name(p->pwid, "taskbar");
1545
1792
 
1546
 
    get_button_spacing(&req, GTK_CONTAINER(p->pwid), "");
1547
 
 
1548
 
    net_active_detect();
1549
 
 
1550
 
    tb = g_new0(taskbar, 1);
 
1793
    /* Make container for task buttons as a child of top level widget. */
 
1794
    GtkOrientation bo = (tb->plug->panel->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
 
1795
    tb->icon_grid = icon_grid_new(p->panel, p->pwid, bo, tb->task_width_max, tb->icon_size, tb->spacing, 0, p->panel->height);
 
1796
    icon_grid_set_constrain_width(tb->icon_grid, TRUE);
 
1797
    taskbar_update_style(tb);
 
1798
 
 
1799
    /* Add GDK event filter. */
 
1800
    gdk_window_add_filter(NULL, (GdkFilterFunc) taskbar_event_filter, tb);
 
1801
 
 
1802
    /* Connect signal to receive mouse events on the unused portion of the taskbar. */
 
1803
    g_signal_connect(p->pwid, "button-press-event", G_CALLBACK(plugin_button_press_event), p);
 
1804
 
 
1805
    /* Connect signals to receive root window events and initialize root window properties. */
 
1806
    tb->number_of_desktops = get_net_number_of_desktops();
 
1807
    tb->current_desktop = get_net_current_desktop();
 
1808
    g_signal_connect(G_OBJECT(fbev), "current_desktop", G_CALLBACK(taskbar_net_current_desktop), (gpointer) tb);
 
1809
    g_signal_connect(G_OBJECT(fbev), "active_window", G_CALLBACK(taskbar_net_active_window), (gpointer) tb);
 
1810
    g_signal_connect(G_OBJECT(fbev), "number_of_desktops", G_CALLBACK(taskbar_net_number_of_desktops), (gpointer) tb);
 
1811
    g_signal_connect(G_OBJECT(fbev), "client_list", G_CALLBACK(taskbar_net_client_list), (gpointer) tb);
 
1812
 
 
1813
    /* Make right-click menu for task buttons.
 
1814
     * It is retained for the life of the taskbar and will be shown as needed.
 
1815
     * Number of desktops and edge is needed for this operation. */
 
1816
    taskbar_make_menu(tb);
 
1817
}
 
1818
 
 
1819
/* Determine if the window manager supports NET_ACTIVE_WINDOW. */
 
1820
static gboolean net_active_supported(void)
 
1821
{
 
1822
    int nitems;
 
1823
    Atom * data = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_SUPPORTED, XA_ATOM, &nitems);
 
1824
    if (data == NULL)
 
1825
        return FALSE;
 
1826
 
 
1827
    while (nitems > 0)
 
1828
    {
 
1829
        if (data[--nitems] == a_NET_ACTIVE_WINDOW)
 
1830
        {
 
1831
            XFree(data);
 
1832
            return TRUE;
 
1833
        }
 
1834
    }
 
1835
    XFree(data);
 
1836
    return FALSE;
 
1837
}
 
1838
 
 
1839
/* Plugin constructor. */
 
1840
static int taskbar_constructor(Plugin * p, char ** fp)
 
1841
{
 
1842
    /* Allocate plugin context and set into Plugin private data pointer. */
 
1843
    TaskbarPlugin * tb = g_new0(TaskbarPlugin, 1);
1551
1844
    tb->plug = p;
1552
1845
    p->priv = tb;
1553
1846
 
1554
 
    if (p->panel->orientation == ORIENT_HORIZ) {
1555
 
        tb->iconsize = GTK_WIDGET(p->panel->box)->allocation.height - req.height;
1556
 
        DBG("pwid height = %d\n", GTK_WIDGET(p->pwid)->allocation.height);
1557
 
    } else
1558
 
        tb->iconsize = 24;
1559
 
    tb->topxwin           = p->panel->topxwin;
1560
 
    tb->tooltips          = 1;
1561
 
    tb->icons_only        = 0;
1562
 
    tb->accept_skip_pager = 1;
1563
 
    tb->show_iconified    = 1;
1564
 
    tb->show_mapped       = 1;
1565
 
    tb->show_all_desks    = 0;
 
1847
    /* Initialize to defaults. */
 
1848
    tb->icon_size         = p->panel->icon_size;
 
1849
    tb->tooltips          = TRUE;
 
1850
    tb->icons_only        = FALSE;
 
1851
    tb->show_all_desks    = FALSE;
1566
1852
    tb->task_width_max    = TASK_WIDTH_MAX;
1567
 
    tb->task_list         = g_hash_table_new(g_int_hash, g_int_equal);
1568
 
    tb->focused_state     = GTK_STATE_ACTIVE;
1569
 
    tb->normal_state      = GTK_STATE_NORMAL;
1570
1853
    tb->spacing           = 1;
1571
 
    tb->use_mouse_wheel   = 1;
1572
 
    tb->use_urgency_hint  = 1;
 
1854
    tb->use_mouse_wheel   = TRUE;
 
1855
    tb->use_urgency_hint  = TRUE;
 
1856
    tb->grouped_tasks     = FALSE;
 
1857
    tb->use_net_active    = net_active_supported();
 
1858
 
 
1859
    /* Process configuration file. */
 
1860
    line s;
1573
1861
    s.len = 256;
1574
1862
    if( fp )
1575
1863
    {
1576
1864
        while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
1577
1865
            if (s.type == LINE_NONE) {
1578
1866
                ERR( "taskbar: illegal token %s\n", s.str);
1579
 
                goto error;
 
1867
                return 0;
1580
1868
            }
1581
 
            if (s.type == LINE_VAR) {
1582
 
                if (!g_ascii_strcasecmp(s.t[0], "tooltips")) {
 
1869
            if (s.type == LINE_VAR)
 
1870
            {
 
1871
                if (g_ascii_strcasecmp(s.t[0], "tooltips") == 0)
1583
1872
                    tb->tooltips = str2num(bool_pair, s.t[1], 1);
1584
 
                } else if (!g_ascii_strcasecmp(s.t[0], "IconsOnly")) {
 
1873
                else if (g_ascii_strcasecmp(s.t[0], "IconsOnly") == 0)
1585
1874
                    tb->icons_only = str2num(bool_pair, s.t[1], 0);
1586
 
                } else if (!g_ascii_strcasecmp(s.t[0], "AcceptSkipPager")) {
1587
 
                    tb->accept_skip_pager = str2num(bool_pair, s.t[1], 1);
1588
 
                } else if (!g_ascii_strcasecmp(s.t[0], "ShowIconified")) {
1589
 
                    tb->show_iconified = str2num(bool_pair, s.t[1], 1);
1590
 
                } else if (!g_ascii_strcasecmp(s.t[0], "ShowMapped")) {
1591
 
                    tb->show_mapped = str2num(bool_pair, s.t[1], 1);
1592
 
                } else if (!g_ascii_strcasecmp(s.t[0], "ShowAllDesks")) {
 
1875
                else if (g_ascii_strcasecmp(s.t[0], "AcceptSkipPager") == 0)            /* For backward compatibility */
 
1876
                    ;
 
1877
                else if (g_ascii_strcasecmp(s.t[0], "ShowIconified") == 0)              /* For backward compatibility */
 
1878
                    ;
 
1879
                else if (g_ascii_strcasecmp(s.t[0], "ShowMapped") == 0)                 /* For backward compatibility */
 
1880
                    ;
 
1881
                else if (g_ascii_strcasecmp(s.t[0], "ShowAllDesks") == 0)
1593
1882
                    tb->show_all_desks = str2num(bool_pair, s.t[1], 0);
1594
 
                } else if (!g_ascii_strcasecmp(s.t[0], "MaxTaskWidth")) {
 
1883
                else if (g_ascii_strcasecmp(s.t[0], "MaxTaskWidth") == 0)
1595
1884
                    tb->task_width_max = atoi(s.t[1]);
1596
 
                    DBG("task_width_max = %d\n", tb->task_width_max);
1597
 
                } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
 
1885
                else if (g_ascii_strcasecmp(s.t[0], "spacing") == 0)
1598
1886
                    tb->spacing = atoi(s.t[1]);
1599
 
                } else if (!g_ascii_strcasecmp(s.t[0], "UseMouseWheel")) {
 
1887
                else if (g_ascii_strcasecmp(s.t[0], "UseMouseWheel") == 0)
1600
1888
                    tb->use_mouse_wheel = str2num(bool_pair, s.t[1], 1);
1601
 
                } else if (!g_ascii_strcasecmp(s.t[0], "UseUrgencyHint")) {
 
1889
                else if (g_ascii_strcasecmp(s.t[0], "UseUrgencyHint") == 0)
1602
1890
                    tb->use_urgency_hint = str2num(bool_pair, s.t[1], 1);
1603
 
                } else if (!g_ascii_strcasecmp(s.t[0], "FlatButton")) {
 
1891
                else if (g_ascii_strcasecmp(s.t[0], "FlatButton") == 0)
1604
1892
                    tb->flat_button = str2num(bool_pair, s.t[1], 1);
1605
 
                } else {
 
1893
                else if (g_ascii_strcasecmp(s.t[0], "GroupedTasks") == 0)
 
1894
                    tb->grouped_tasks = str2num(bool_pair, s.t[1], 1);
 
1895
                else
1606
1896
                    ERR( "taskbar: unknown var %s\n", s.t[0]);
1607
 
                    goto error;
1608
 
                }
1609
 
            } else {
 
1897
            }
 
1898
            else
 
1899
            {
1610
1900
                ERR( "taskbar: illegal in this context %s\n", s.str);
1611
 
                goto error;
 
1901
                return 0;
1612
1902
            }
1613
1903
        }
1614
1904
    }
 
1905
 
 
1906
    /* Build the graphic elements. */
1615
1907
    taskbar_build_gui(p);
1616
 
    tb_net_client_list(NULL, tb);
1617
 
    tb_display(tb);
1618
 
    tb_net_active_window(NULL, tb);
1619
 
    RET(1);
1620
1908
 
1621
 
 error:
1622
 
    taskbar_destructor(p);
1623
 
    RET(0);
 
1909
    /* Fetch the client list and redraw the taskbar.  Then determine what window has focus. */
 
1910
    taskbar_net_client_list(NULL, tb);
 
1911
    taskbar_net_active_window(NULL, tb);
 
1912
    return 1;
1624
1913
}
1625
1914
 
1626
 
 
1627
 
static void
1628
 
taskbar_destructor(Plugin *p)
 
1915
/* Plugin destructor. */
 
1916
static void taskbar_destructor(Plugin * p)
1629
1917
{
1630
 
    taskbar *tb = (taskbar *)p->priv;
1631
 
    g_hash_table_foreach( tb->task_list, (GHFunc)del_task, NULL );
1632
 
 
1633
 
    g_signal_handlers_disconnect_by_func(G_OBJECT (fbev), tb_net_current_desktop, tb);
1634
 
    g_signal_handlers_disconnect_by_func(G_OBJECT (fbev), tb_net_active_window, tb);
1635
 
    g_signal_handlers_disconnect_by_func(G_OBJECT (fbev), tb_net_number_of_desktops, tb);
1636
 
    g_signal_handlers_disconnect_by_func(G_OBJECT (fbev), tb_net_client_list, tb);
1637
 
    gdk_window_remove_filter(NULL, (GdkFilterFunc)tb_event_filter, tb );
1638
 
    g_hash_table_destroy(tb->task_list);
1639
 
    /* The widget is destroyed in plugin_stop().
1640
 
    gtk_widget_destroy(tb->bar);
1641
 
    */
 
1918
    TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
 
1919
 
 
1920
    /* Remove GDK event filter. */
 
1921
    gdk_window_remove_filter(NULL, (GdkFilterFunc) taskbar_event_filter, tb);
 
1922
 
 
1923
    /* Remove root window signal handlers. */
 
1924
    g_signal_handlers_disconnect_by_func(fbev, taskbar_net_current_desktop, tb);
 
1925
    g_signal_handlers_disconnect_by_func(fbev, taskbar_net_active_window, tb);
 
1926
    g_signal_handlers_disconnect_by_func(fbev, taskbar_net_number_of_desktops, tb);
 
1927
    g_signal_handlers_disconnect_by_func(fbev, taskbar_net_client_list, tb);
 
1928
 
 
1929
    /* Deallocate task list. */
 
1930
    while (tb->task_list != NULL)
 
1931
        task_delete(tb, tb->task_list, TRUE);
 
1932
 
 
1933
    /* Deallocate class list. */
 
1934
    while (tb->res_class_list != NULL)
 
1935
    {
 
1936
        TaskClass * tc = tb->res_class_list;
 
1937
        tb->res_class_list = tc->res_class_flink;
 
1938
        g_free(tc->res_class);
 
1939
        g_free(tc);
 
1940
    }
 
1941
 
 
1942
    /* Deallocate other memory. */
1642
1943
    gtk_widget_destroy(tb->menu);
1643
 
}
1644
 
 
1645
 
static void
1646
 
update_task_button( gpointer key, task* tk, taskbar* tb )
1647
 
{
1648
 
    if( tb->icons_only )
1649
 
        gtk_widget_hide( tk->label );
1650
 
    else
1651
 
        gtk_widget_show( tk->label );
1652
 
 
1653
 
    if( tb->flat_button )
1654
 
        gtk_button_set_relief( (GtkButton*)tk->button, GTK_RELIEF_NONE );
1655
 
    else
1656
 
        gtk_button_set_relief( (GtkButton*)tk->button, GTK_RELIEF_NORMAL);
1657
 
}
1658
 
 
1659
 
static void apply_config( Plugin* p )
1660
 
{
1661
 
    taskbar *tb = (taskbar *)p->priv;
1662
 
    if( tb->tooltips )
1663
 
        gtk_container_foreach( GTK_CONTAINER(tb->bar), (GtkCallback)gtk_widget_set_has_tooltip, (gpointer)TRUE );
1664
 
    else
1665
 
        gtk_container_foreach( GTK_CONTAINER(tb->bar), (GtkCallback)gtk_widget_set_has_tooltip, (gpointer)FALSE );
1666
 
 
1667
 
    if (tb->icons_only)
 
1944
    g_free(tb);
 
1945
}
 
1946
 
 
1947
/* Callback from configuration dialog mechanism to apply the configuration. */
 
1948
static void taskbar_apply_configuration(Plugin * p)
 
1949
{
 
1950
    TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
 
1951
 
 
1952
    /* Update style on taskbar. */
 
1953
    taskbar_update_style(tb);
 
1954
 
 
1955
    /* Update styles on each button. */
 
1956
    Task * tk;
 
1957
    for (tk = tb->task_list; tk != NULL; tk = tk->task_flink)
 
1958
        task_update_style(tk, tb);
 
1959
 
 
1960
    /* Refetch the client list and redraw. */
 
1961
    recompute_group_visibility_on_current_desktop(tb);
 
1962
    taskbar_net_client_list(NULL, tb);
 
1963
}
 
1964
 
 
1965
/* Display the configuration dialog. */
 
1966
static void taskbar_configure(Plugin * p, GtkWindow * parent)
 
1967
{
 
1968
    TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
 
1969
    GtkWidget* dlg = create_generic_config_dlg(
 
1970
        _(p->class->name),
 
1971
        GTK_WIDGET(parent),
 
1972
        (GSourceFunc) taskbar_apply_configuration, (gpointer) p,
 
1973
        _("Show tooltips"), &tb->tooltips, CONF_TYPE_BOOL,
 
1974
        _("Icons only"), &tb->icons_only, CONF_TYPE_BOOL,
 
1975
        _("Flat buttons"), &tb->flat_button, CONF_TYPE_BOOL,
 
1976
        _("Show windows from all desktops"), &tb->show_all_desks, CONF_TYPE_BOOL,
 
1977
        _("Use mouse wheel"), &tb->use_mouse_wheel, CONF_TYPE_BOOL,
 
1978
        _("Flash when there is any window requiring attention"), &tb->use_urgency_hint, CONF_TYPE_BOOL,
 
1979
        _("Combine multiple application windows into a single button"), &tb->grouped_tasks, CONF_TYPE_BOOL,
 
1980
        _("Maximum width of task button"), &tb->task_width_max, CONF_TYPE_INT,
 
1981
        _("Spacing"), &tb->spacing, CONF_TYPE_INT,
 
1982
        NULL);
 
1983
    gtk_window_present(GTK_WINDOW(dlg));
 
1984
}
 
1985
 
 
1986
/* Save the configuration to the configuration file. */
 
1987
static void taskbar_save_configuration(Plugin * p, FILE * fp)
 
1988
{
 
1989
    TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
 
1990
    lxpanel_put_bool(fp, "tooltips", tb->tooltips);
 
1991
    lxpanel_put_bool(fp, "IconsOnly", tb->icons_only);
 
1992
    lxpanel_put_bool(fp, "ShowAllDesks", tb->show_all_desks);
 
1993
    lxpanel_put_bool(fp, "UseMouseWheel", tb->use_mouse_wheel);
 
1994
    lxpanel_put_bool(fp, "UseUrgencyHint", tb->use_urgency_hint);
 
1995
    lxpanel_put_bool(fp, "FlatButton", tb->flat_button);
 
1996
    lxpanel_put_int(fp, "MaxTaskWidth", tb->task_width_max);
 
1997
    lxpanel_put_int(fp, "spacing", tb->spacing);
 
1998
    lxpanel_put_bool(fp, "GroupedTasks", tb->grouped_tasks);
 
1999
}
 
2000
 
 
2001
/* Callback when panel configuration changes. */
 
2002
static void taskbar_panel_configuration_changed(Plugin * p)
 
2003
{
 
2004
    TaskbarPlugin * tb = (TaskbarPlugin *) p->priv;
 
2005
    taskbar_update_style(tb);
 
2006
    taskbar_make_menu(tb);
 
2007
    GtkOrientation bo = (tb->plug->panel->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
 
2008
    icon_grid_set_geometry(tb->icon_grid, bo,
 
2009
        ((tb->icons_only) ? tb->plug->panel->icon_size + ICON_ONLY_EXTRA : tb->task_width_max), tb->plug->panel->icon_size + BUTTON_HEIGHT_EXTRA,
 
2010
        tb->spacing, 0, tb->plug->panel->height);
 
2011
 
 
2012
    /* If the icon size changed, refetch all the icons. */
 
2013
    if (tb->plug->panel->icon_size != tb->icon_size)
1668
2014
    {
1669
 
        gtk_bar_set_max_child_size(GTK_BAR(tb->bar),
1670
 
          GTK_WIDGET(p->panel->box)->allocation.height -2);
1671
 
    }
1672
 
    else
1673
 
        gtk_bar_set_max_child_size(GTK_BAR(tb->bar), tb->task_width_max);
1674
 
 
1675
 
    gtk_box_set_spacing( GTK_BOX(tb->bar), tb->spacing );
1676
 
    tb_net_client_list(NULL, tb);
1677
 
    g_hash_table_foreach( tb->task_list,
1678
 
                          (GHFunc)update_task_button,
1679
 
                          (gpointer)tb );
1680
 
}
1681
 
 
1682
 
static void taskbar_config( Plugin* p, GtkWindow* parent )
1683
 
{
1684
 
    GtkWidget* dlg;
1685
 
    taskbar *tb = (taskbar *)p->priv;
1686
 
 
1687
 
    dlg =  create_generic_config_dlg(
1688
 
                _(p->class->name),
1689
 
                GTK_WIDGET(parent),
1690
 
                (GSourceFunc) apply_config, (gpointer) p,
1691
 
                _("Show tooltips"), &tb->tooltips, CONF_TYPE_BOOL,
1692
 
                _("Icons only"), &tb->icons_only, CONF_TYPE_BOOL,
1693
 
                 _("Flat Buttons"), &tb->flat_button, CONF_TYPE_BOOL,
1694
 
               _("Accept SkipPager"), &tb->accept_skip_pager, CONF_TYPE_BOOL,
1695
 
                _("Show Iconified windows"), &tb->show_iconified, CONF_TYPE_BOOL,
1696
 
                _("Show mapped windows"), &tb->show_mapped, CONF_TYPE_BOOL,
1697
 
                _("Show windows from all desktops"), &tb->show_all_desks, CONF_TYPE_BOOL,
1698
 
                _("Use mouse wheel"), &tb->use_mouse_wheel, CONF_TYPE_BOOL,
1699
 
                _("Flash when there is any window requiring attention"), &tb->use_urgency_hint, CONF_TYPE_BOOL,
1700
 
                _("Max width of task button"), &tb->task_width_max, CONF_TYPE_INT,
1701
 
                _("Spacing"), &tb->spacing, CONF_TYPE_INT,
1702
 
                NULL );
1703
 
    gtk_window_present( GTK_WINDOW(dlg) );
1704
 
}
1705
 
 
1706
 
static void save_config( Plugin* p, FILE* fp )
1707
 
{
1708
 
    taskbar *tb = (taskbar *)p->priv;
1709
 
    lxpanel_put_bool( fp, "tooltips", tb->tooltips );
1710
 
    lxpanel_put_bool( fp, "IconsOnly", tb->icons_only );
1711
 
    lxpanel_put_bool( fp, "AcceptSkipPager", tb->accept_skip_pager );
1712
 
    lxpanel_put_bool( fp, "ShowIconified", tb->show_iconified );
1713
 
    lxpanel_put_bool( fp, "ShowMapped", tb->show_mapped );
1714
 
    lxpanel_put_bool( fp, "ShowAllDesks", tb->show_all_desks );
1715
 
    lxpanel_put_bool( fp, "UseMouseWheel", tb->use_mouse_wheel );
1716
 
    lxpanel_put_bool( fp, "UseUrgencyHint", tb->use_urgency_hint );
1717
 
    lxpanel_put_bool( fp, "FlatButton", tb->flat_button );
1718
 
    lxpanel_put_int( fp, "MaxTaskWidth", tb->task_width_max );
1719
 
    lxpanel_put_int( fp, "spacing", tb->spacing );
1720
 
}
1721
 
 
1722
 
static void
1723
 
update_label_orient( GtkWidget* child, gpointer user_data )
1724
 
{
1725
 
    /* FIXME: gtk+ has only limited support for this, sigh! */
1726
 
    Plugin* p = (Plugin*)user_data;
1727
 
    if( GTK_IS_LABEL(child) ) {
1728
 
        gdouble angle;
1729
 
        if( p->panel->edge == EDGE_LEFT ) {
1730
 
            angle = 90.0;
1731
 
            /* FIXME: ellipsize cannot be used in conjunction with angle.
1732
 
                      This is the limit of gtk+, and turn off ellipsize do
1733
 
                      cause problems here. How can this be solved? Sigh!
1734
 
            */
1735
 
            gtk_label_set_ellipsize( GTK_LABEL(child), PANGO_ELLIPSIZE_NONE );
1736
 
        }
1737
 
        else if( p->panel->edge == EDGE_RIGHT ) {
1738
 
            angle = 270.0;
1739
 
            gtk_label_set_ellipsize( GTK_LABEL(child), PANGO_ELLIPSIZE_NONE );
1740
 
        }
1741
 
        else {
1742
 
            angle = 0.0;
1743
 
            gtk_label_set_ellipsize( GTK_LABEL(child), PANGO_ELLIPSIZE_END );
1744
 
        }
1745
 
        gtk_label_set_angle( GTK_LABEL(child), angle );
1746
 
        gtk_misc_set_alignment(GTK_MISC(child), 0.0, 0.5);
1747
 
    }
1748
 
}
1749
 
 
1750
 
static void orientation_changed( Plugin* p )
1751
 
{
1752
 
    taskbar *tb = (taskbar *)p->priv;
1753
 
    GList *child, *children;
1754
 
 
1755
 
    children = gtk_container_get_children( GTK_CONTAINER (tb->bar) );
1756
 
    for( child = children; child; child = child->next ) {
1757
 
        GtkWidget *button = GTK_WIDGET(child->data);
1758
 
        GtkBox *box = (GtkBox*)gtk_bin_get_child( GTK_BIN(button) );
1759
 
        GtkBox *newbox = GTK_BOX(recreate_box( box, p->panel->orientation ));
1760
 
        if( newbox != box ) {
1761
 
            gtk_container_add( GTK_CONTAINER(button), GTK_WIDGET(newbox) );
1762
 
        }
1763
 
        gtk_container_foreach( GTK_CONTAINER(newbox),
1764
 
                               update_label_orient, p );
1765
 
    }
1766
 
    g_list_free( children );
1767
 
 
1768
 
    gtk_widget_destroy( tb->menu );
1769
 
    tb->menu = taskbar_make_menu( tb );
1770
 
 
1771
 
    gtk_bar_set_orientation( GTK_BAR(tb->bar), p->panel->orientation );
1772
 
}
1773
 
 
 
2015
        tb->icon_size = tb->plug->panel->icon_size;
 
2016
        Task * tk;
 
2017
        for (tk = tb->task_list; tk != NULL; tk = tk->task_flink)
 
2018
        {
 
2019
            GdkPixbuf * pixbuf = task_update_icon(tb, tk, None);
 
2020
            if (pixbuf != NULL)
 
2021
            {
 
2022
                gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
 
2023
                g_object_unref(pixbuf);
 
2024
            }
 
2025
        }
 
2026
    }
 
2027
 
 
2028
    /* Redraw all the labels.  Icon size or font color may have changed. */
 
2029
    taskbar_redraw(tb);
 
2030
}
 
2031
 
 
2032
/* Plugin descriptor. */
1774
2033
PluginClass taskbar_plugin_class = {
1775
 
    fname: NULL,
1776
 
    count: 0,
 
2034
 
 
2035
    PLUGINCLASS_VERSIONING,
1777
2036
 
1778
2037
    type : "taskbar",
1779
2038
    name : N_("Task Bar (Window List)"),
1780
2039
    version: "1.0",
1781
2040
    description : N_("Taskbar shows all opened windows and allow to iconify them, shade or get focus"),
1782
2041
 
 
2042
    /* Stretch is available and default for this plugin. */
 
2043
    expand_available : TRUE,
 
2044
    expand_default : TRUE,
 
2045
 
1783
2046
    constructor : taskbar_constructor,
1784
2047
    destructor  : taskbar_destructor,
1785
 
    config : taskbar_config,
1786
 
    save : save_config,
1787
 
    orientation : orientation_changed
 
2048
    config : taskbar_configure,
 
2049
    save : taskbar_save_configuration,
 
2050
    panel_configuration_changed : taskbar_panel_configuration_changed
 
2051
 
1788
2052
};
1789