fb0dd2a6a2334f8a2aae78b51fa1b02c1cb73ed4
[lxde/lxpanel.git] / plugins / launchtaskbar.c
1 /**
2 * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 /*
20 * Started by Giuseppe Penone <giuspen@gmail.com> merging launchbar and taskbar
21 * and adding interoperability between them.
22 */
23
24 /*
25 * Taskbar plugin:
26 * 2006.09.10 modified by Hong Jen Yee (PCMan) pcman.tw (AT) gmail.com
27 * Following features are added:
28 * 1. Add XUrgencyHint support. (Flashing task bar buttons, can be disabled)
29 * 2. Raise window when files get dragged over taskbar buttons.
30 * 3. Add Restore & Maximize menu items to popup menu of task bar buttons.
31 */
32
33 //#define DEBUG // killall lxpanel && lxpanel --profile Lubuntu &
34
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <sys/types.h>
44 #include <sys/wait.h>
45 #include <signal.h>
46 #include <errno.h>
47 #include <X11/Xlib.h>
48 #include <X11/Xutil.h>
49
50 #include <gdk-pixbuf/gdk-pixbuf.h>
51 #include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
52 #include <gdk/gdk.h>
53 #include <glib/gi18n.h>
54
55 #include <libfm/fm-gtk.h>
56
57 #include "misc.h"
58 #include "ev.h"
59 #include "plugin.h"
60 #include "icon.xpm"
61 #include "icon-grid.h"
62 #ifndef DISABLE_MENU
63 # include "menu-policy.h"
64 #endif
65
66
67 #define PANEL_ICON_SIZE 24 /* see the private.h */
68
69 /* Column definitions for configuration dialogs. */
70 enum {
71 COL_ICON,
72 COL_TITLE,
73 COL_ICON_NAME,
74 COL_BTN,
75 N_COLS
76 };
77
78 typedef enum {
79 LAUNCHBAR = 0, /* GtkComboBox is 0-indexed. */
80 TASKBAR,
81 LAUNCHTASKBAR
82 } LtbMode;
83
84 typedef struct LaunchTaskBarPlugin LaunchTaskBarPlugin;
85
86 /* Structure representing a class. This comes from WM_CLASS, and should identify windows that come from an application. */
87 typedef struct _task_class {
88 struct _task_class *p_taskclass_flink; /* Forward link */
89 char * res_class; /* Class name */
90 struct _task * p_task_head; /* Head of list of tasks with this class */
91 struct _task * p_task_visible; /* Task that is visible in current desktop, if any */
92 char * visible_name; /* Name that will be visible for grouped tasks */
93 int visible_count; /* Count of tasks that are visible in current desktop */
94 } TaskClass;
95
96 /* Structure representing a "task", an open window. */
97 typedef struct _task {
98 struct _task * p_task_flink_xwid; /* Forward link to next task in X window ID order */
99 LaunchTaskBarPlugin * tb; /* Back pointer to plugin */
100 Window win; /* X window ID */
101 char * name; /* Taskbar label when normal, from WM_NAME or NET_WM_NAME */
102 char * name_iconified; /* Taskbar label when iconified */
103 char * exec_bin; /* Exec bin associated to Window */
104 Atom name_source; /* Atom that is the source of taskbar label */
105 TaskClass * p_taskclass; /* Class, from WM_CLASS */
106 struct _task * p_task_flink_same_class; /* Forward link to task in same class */
107 GtkWidget * button; /* Button representing task in taskbar */
108 GtkWidget * image; /* Icon for task, child of button */
109 Atom image_source; /* Atom that is the source of taskbar icon */
110 GtkWidget * label; /* Label for task, child of button */
111 int desktop; /* Desktop that contains task, needed to switch to it on Raise */
112 gint monitor; /* Monitor that the window is on or closest to */
113 guint flash_timeout; /* Timer for urgency notification */
114 unsigned int focused :1; /* True if window has focus */
115 unsigned int iconified :1; /* True if window is iconified, from WM_STATE */
116 unsigned int urgency :1; /* True if window has an urgency hint, from WM_HINTS */
117 unsigned int flash_state :1; /* One-bit counter to flash taskbar */
118 unsigned int entered_state :1; /* True if cursor is inside taskbar button */
119 unsigned int present_in_client_list :1; /* State during WM_CLIENT_LIST processing to detect deletions */
120 } Task;
121
122 /* Representative of one launch button.
123 * Note that the launch parameters come from the specified desktop file, or from the configuration file.
124 * This structure is also used during the "add to launchtaskbar" dialog to hold menu items. */
125 typedef struct {
126 LaunchTaskBarPlugin * p; /* Back pointer to plugin */
127 GtkWidget * widget; /* Pointer to button */
128 GtkWidget * image_widget; /* Pointer to image */
129 FmFileInfo * fi; /* Launcher application descriptor */
130 config_setting_t * settings; /* Pointer to settings */
131 FmDndDest * dd; /* Drag and drop support */
132 } LaunchButton;
133
134 /* Private context for taskbar plugin. */
135 struct LaunchTaskBarPlugin {
136 /* LAUNCHBAR */
137 GtkWidget *lb_icon_grid; /* Icon grid managing the container */
138 GSList *buttons; /* Launchbar buttons */
139 LaunchButton *bootstrap_button; /* Bootstrapping button for empty launchtaskbar */
140 FmIcon * add_icon; /* Icon for bootstrap_button */
141 GtkWidget *p_button_add, *p_button_remove, *p_label_menu_app_exec, *p_label_def_app_exec;
142 /* TASKBAR */
143 Task * p_task_list; /* List of tasks to be displayed in taskbar */
144 TaskClass * p_taskclass_list; /* Window class list */
145 GtkWidget * tb_icon_grid; /* Manager for taskbar buttons */
146 GtkWidget * menu; /* Popup menu for task control (Close, Raise, etc.) */
147 GtkWidget * group_menu; /* Popup menu for grouping selection */
148 GtkWidget * workspace_menu0; /* "Workspace 1" menu item */
149 GdkPixbuf * fallback_pixbuf; /* Fallback task icon when none is available */
150 int number_of_desktops; /* Number of desktops, from NET_WM_NUMBER_OF_DESKTOPS */
151 int current_desktop; /* Current desktop, from NET_WM_CURRENT_DESKTOP */
152 Task * focused; /* Task that has focus */
153 Task * focused_previous; /* Task that had focus just before panel got it */
154 Task * menutask; /* Task for which popup menu is open */
155 guint dnd_delay_timer; /* Timer for drag and drop delay */
156 gboolean dnd_task_moving; /* User is currently moving a task button */
157 int icon_size; /* Size of task icons */
158 gboolean show_all_desks; /* User preference: show windows from all desktops */
159 gboolean tooltips; /* User preference: show tooltips */
160 gboolean icons_only; /* User preference: show icons only, omit name */
161 gboolean use_mouse_wheel; /* User preference: scroll wheel does iconify and raise */
162 gboolean use_urgency_hint; /* User preference: windows with urgency will flash */
163 gboolean flat_button; /* User preference: taskbar buttons have visible background */
164 gboolean grouped_tasks; /* User preference: windows from same task are grouped onto a single button */
165 gboolean same_monitor_only; /* User preference: only show windows that are in the same monitor as the taskbar */
166 int task_width_max; /* Maximum width of a taskbar button in horizontal orientation */
167 int spacing; /* Spacing between taskbar buttons */
168 gboolean use_net_active; /* NET_WM_ACTIVE_WINDOW is supported by the window manager */
169 gboolean net_active_checked; /* True if use_net_active is valid */
170 /* COMMON */
171 #ifndef DISABLE_MENU
172 GtkWidget *p_menuitem_lock_tbp;
173 GtkWidget *p_menuitem_unlock_tbp;
174 GtkWidget *p_menuitem_new_instance;
175 GtkWidget *p_menuitem_separator;
176 #endif
177 GtkWidget * plugin; /* Back pointer to Plugin */
178 LXPanel * panel; /* Back pointer to panel */
179 config_setting_t * settings;
180 GdkScreen *screen;
181 GtkWidget *config_dlg; /* Configuration dialog */
182 GtkNotebook *p_notebook;
183 GtkWidget *p_notebook_page_launch;
184 GtkWidget *p_notebook_page_task;
185 GKeyFile *p_key_file_special_cases;
186 int mode;
187 gboolean lb_built;
188 gboolean tb_built;
189 gboolean fixed_mode; /* if mode cannot be changed */
190 };
191
192 static gchar *launchtaskbar_rc = "style 'launchtaskbar-style' = 'theme-panel'\n"
193 "{\n"
194 "GtkWidget::focus-line-width=0\n"
195 "GtkWidget::focus-padding=0\n"
196 "GtkButton::default-border={0,0,0,0}\n"
197 "GtkButton::default-outside-border={0,0,0,0}\n"
198 "GtkButton::inner-border={0,0,0,0}\n"
199 "}\n"
200 "widget '*launchbar.*' style 'launchtaskbar-style'\n"
201 "widget '*taskbar.*' style 'launchtaskbar-style'";
202
203 #define DRAG_ACTIVE_DELAY 1000
204 #define TASK_WIDTH_MAX 200
205 #define ALL_WORKSPACES -1
206 #define ICON_ONLY_EXTRA 6 /* Amount needed to have button lay out symmetrically */
207 #define ICON_BUTTON_TRIM 4 /* Amount needed to have button remain on panel */
208
209 static void launchtaskbar_destructor(gpointer user_data);
210
211 static void taskbar_redraw(LaunchTaskBarPlugin * tb);
212 static void task_delete(LaunchTaskBarPlugin * tb, Task * tk, gboolean unlink, gboolean remove);
213 static GdkPixbuf * task_update_icon(LaunchTaskBarPlugin * tb, Task * tk, Atom source);
214 static void flash_window_update(Task * tk);
215 static gboolean flash_window_timeout(gpointer tk);
216 static void task_group_menu_destroy(LaunchTaskBarPlugin * tb);
217 static gboolean taskbar_popup_activate_event(GtkWidget * widget, GdkEventButton * event, Task * tk);
218 static void taskbar_update_style(LaunchTaskBarPlugin * tb);
219 static void taskbar_net_client_list(GtkWidget * widget, LaunchTaskBarPlugin * tb);
220 static void taskbar_net_current_desktop(GtkWidget * widget, LaunchTaskBarPlugin * tb);
221 static void taskbar_net_number_of_desktops(GtkWidget * widget, LaunchTaskBarPlugin * tb);
222 static void taskbar_net_active_window(GtkWidget * widget, LaunchTaskBarPlugin * tb);
223 static gboolean task_has_urgency(Task * tk);
224 static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, LaunchTaskBarPlugin * tb);
225 static void taskbar_make_menu(LaunchTaskBarPlugin * tb);
226 static void taskbar_window_manager_changed(GdkScreen * screen, LaunchTaskBarPlugin * tb);
227 static void taskbar_apply_configuration(LaunchTaskBarPlugin * ltbp);
228
229 static void f_get_exec_cmd_from_pid(GPid pid, gchar *buffer_128, const gchar *proc_file)
230 {
231 buffer_128[0] = '\0';
232 FILE *pipe;
233 gchar command[64];
234 snprintf(command, 64, "cat /proc/%u/%s", pid, proc_file);
235 pipe = popen(command, "r");
236 if(pipe == NULL)
237 g_warning("ltbp: popen '%s'", command);
238 else if(fgets(buffer_128, 128, pipe) == NULL)
239 g_warning("ltbp: fgets '%s'", command);
240 else
241 {
242 gchar *p_char = strchr(buffer_128, '\n');
243 if(p_char != NULL) *p_char = '\0';
244 }
245 if(pipe != NULL) pclose(pipe);
246 }
247
248 #ifndef DISABLE_MENU
249 static FmFileInfo *f_find_menu_launchbutton_recursive(const char *exec_bin)
250 {
251 MenuCache *mc;
252 guint32 flags;
253 GSList *apps, *l;
254 size_t len;
255 const char *exec, *short_exec;
256 char *str_path;
257 FmPath *path;
258 FmFileInfoJob *job;
259 FmFileInfo *fi = NULL;
260
261 /* FIXME: cache it in Task object */
262 mc = panel_menu_cache_new(&flags);
263 /* FIXME: if menu plugin wasn't loaded yet we'll get NULL list here */
264 apps = menu_cache_list_all_apps(mc);
265 short_exec = strrchr(exec_bin, '/');
266 if (short_exec != NULL)
267 short_exec++;
268 else
269 short_exec = exec_bin;
270 len = strlen(short_exec);
271 /* the same executable may be used in numerous applications so wild guess
272 estimation check for desktop id equal to short_exec+".desktop" first */
273 for (l = apps; l; l = l->next)
274 {
275 exec = menu_cache_item_get_id(MENU_CACHE_ITEM(l->data));
276 /* we don't check flags here because user always can manually
277 start any app that isn't visible in the desktop menu */
278 if (strncmp(exec, short_exec, len) == 0 && exec[len] == '.')
279 break;
280 }
281 /* if not found then check for non-absolute exec name in application
282 since it usually is expanded by application starting functions */
283 if (l == NULL) for (l = apps; l; l = l->next)
284 {
285 exec = menu_cache_app_get_exec(MENU_CACHE_APP(l->data));
286 if (exec[0] != '/' && strncmp(exec, short_exec, len) == 0 &&
287 (exec[len] == ' ' || exec[len] == 0))
288 break;
289 }
290 /* well, not matched, let try full path, we assume here if application
291 starts executable by full path then process cannot have short name */
292 if (l == NULL && exec_bin[0] == '/')
293 {
294 len = strlen(exec_bin);
295 for (l = apps; l; l = l->next)
296 {
297 exec = menu_cache_app_get_exec(MENU_CACHE_APP(l->data));
298 if (exec[0] == '/' && strncmp(exec, exec_bin, len) == 0 &&
299 (exec[len] == ' ' || exec[len] == 0))
300 break;
301 }
302 }
303 if (l)
304 {
305 str_path = menu_cache_dir_make_path(MENU_CACHE_DIR(l->data));
306 path = fm_path_new_relative(fm_path_get_apps_menu(), str_path+13); /* skip /Applications */
307 g_free(str_path);
308 job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
309 fm_file_info_job_add(job, path);
310 fm_path_unref(path);
311 if (!fm_job_run_sync(FM_JOB(job)))
312 g_warning("launchtaskbar: problem running file info job");
313 else
314 fi = fm_file_info_list_pop_head(job->file_infos);
315 g_object_unref(job);
316 }
317 g_slist_foreach(apps, (GFunc)menu_cache_item_unref, NULL);
318 g_slist_free(apps);
319 menu_cache_unref(mc);
320 g_debug("f_find_menu_launchbutton_recursive: search '%s' found=%d", exec_bin, (fi != NULL));
321 return fi;
322 }
323 #endif
324
325 /* Deallocate a LaunchButton. */
326 static void launchbutton_free(LaunchButton * btn)
327 {
328 if (btn->fi)
329 fm_file_info_unref(btn->fi);
330 if (btn->dd)
331 g_object_unref(btn->dd);
332 g_free(btn);
333 }
334
335 /* Handler for "button-press-event" event from launchtaskbar button. */
336 static gboolean launchbutton_press_event(GtkWidget * widget, GdkEventButton * event, LaunchButton * b)
337 {
338 if (event->button == 1 && event->type == GDK_BUTTON_PRESS) /* left button */
339 {
340 if (b->fi == NULL) /* The bootstrap button */
341 lxpanel_plugin_show_config_dialog(b->p->plugin);
342 else
343 lxpanel_launch_path(b->p->panel, fm_file_info_get_path(b->fi));
344 return TRUE;
345 }
346 return FALSE;
347 }
348
349 /* Handler for "drag-motion" event from launchtaskbar button. */
350 static gboolean launchbutton_drag_motion_event(
351 GtkWidget * widget,
352 GdkDragContext * context,
353 gint x,
354 gint y,
355 guint time,
356 LaunchButton * b)
357 {
358 GdkAtom target;
359 GdkDragAction action = 0;
360
361 fm_dnd_dest_set_dest_file(b->dd, b->fi);
362 target = fm_dnd_dest_find_target(b->dd, context);
363 if (target != GDK_NONE && fm_dnd_dest_is_target_supported(b->dd, target))
364 action = fm_dnd_dest_get_default_action(b->dd, context, target);
365 gdk_drag_status(context, action, time);
366 /* g_debug("launchbutton_drag_motion_event: act=%u",action); */
367 return (action != 0);
368 }
369
370 /* Build the graphic elements for the bootstrap launchtaskbar button. */
371 static void launchbutton_build_bootstrap(LaunchTaskBarPlugin *lb)
372 {
373 if(lb->bootstrap_button == NULL)
374 {
375 GdkPixbuf * icon;
376 /* Build a button that has the stock "Add" icon.
377 * The "desktop-id" being NULL is the marker that this is the bootstrap button. */
378 lb->bootstrap_button = g_new0(LaunchButton, 1);
379 lb->bootstrap_button->p = lb;
380
381 /* Create an event box. */
382 GtkWidget * event_box = gtk_event_box_new();
383 gtk_container_set_border_width(GTK_CONTAINER(event_box), 0);
384 gtk_widget_set_can_focus (event_box, FALSE);
385 lb->bootstrap_button->widget = event_box;
386 g_signal_connect(event_box, "button-press-event", G_CALLBACK(launchbutton_press_event), lb->bootstrap_button);
387
388 /* Create an image containing the stock "Add" icon as a child of the event box. */
389 lb->add_icon = fm_icon_from_name(GTK_STOCK_ADD);
390 icon = fm_pixbuf_from_icon(lb->add_icon, lb->icon_size);
391 lb->bootstrap_button->image_widget = gtk_image_new_from_pixbuf(icon);
392 g_object_unref(icon);
393 gtk_misc_set_padding(GTK_MISC(lb->bootstrap_button->image_widget), 0, 0);
394 gtk_misc_set_alignment(GTK_MISC(lb->bootstrap_button->image_widget), 0, 0);
395 gtk_container_add(GTK_CONTAINER(event_box), lb->bootstrap_button->image_widget);
396
397 /* Add the bootstrap button to the icon grid. By policy it is empty at this point. */
398 gtk_container_add(GTK_CONTAINER(lb->lb_icon_grid), event_box);
399 gtk_widget_show_all(event_box);
400 }
401 else
402 gtk_widget_show(lb->bootstrap_button->widget);
403 }
404
405 #ifndef DISABLE_MENU
406 static LaunchButton *launchbar_exec_bin_exists(LaunchTaskBarPlugin *lb, FmFileInfo *fi)
407 {
408 LaunchButton *ret_val = NULL;
409 FmPath *path;
410 GSList* l;
411
412 if (!fi)
413 return NULL;
414 path = fm_file_info_get_path(fi);
415 for(l = lb->buttons; l != NULL; l = l->next)
416 {
417 LaunchButton *btn = (LaunchButton *)l->data;
418 if (btn->fi && fm_path_equal(path, fm_file_info_get_path(btn->fi)))
419 {
420 ret_val = btn;
421 break;
422 }
423 }
424 return ret_val;
425 }
426 #endif
427
428 static void launchbar_update_after_taskbar_class_added(LaunchTaskBarPlugin *ltbp, Task *tk)
429 {
430 GPid pid = get_net_wm_pid(tk->win);
431 gchar exec_bin_full[128];
432 f_get_exec_cmd_from_pid(pid, exec_bin_full, "cmdline");
433 gchar *p_char = strrchr(exec_bin_full, '/');
434 if(p_char == NULL) p_char = exec_bin_full;
435 else p_char++;
436 g_free(tk->exec_bin);
437 if(strcmp(p_char, "python") == 0)
438 {
439 f_get_exec_cmd_from_pid(pid, exec_bin_full, "comm");
440 }
441 else
442 {
443 tk->exec_bin = g_key_file_get_string(ltbp->p_key_file_special_cases,
444 "special_cases", p_char, NULL);
445 if (tk->exec_bin != NULL) /* found this key */
446 return;
447 }
448 tk->exec_bin = g_strdup(exec_bin_full);
449
450 #ifdef DEBUG
451 if(ltbp->mode == LAUNCHTASKBAR)
452 {
453 FmFileInfo *fi = f_find_menu_launchbutton_recursive(tk->exec_bin);
454 LaunchButton *btn = launchbar_exec_bin_exists(ltbp, fi);
455 g_print("\nTB '%s' OPEN (pid=%u), in LB: %c\n",
456 tk->exec_bin, pid, btn != NULL ? 'Y':'N');
457 if (fi)
458 fm_file_info_unref(fi);
459 }
460 #endif
461 }
462
463 static void launchbar_update_after_taskbar_class_removed(LaunchTaskBarPlugin *ltbp, Task *tk)
464 {
465 #ifdef DEBUG
466 if(ltbp->mode == LAUNCHTASKBAR)
467 {
468 FmFileInfo *fi = f_find_menu_launchbutton_recursive(tk->exec_bin);
469 LaunchButton *btn = launchbar_exec_bin_exists(ltbp, fi);
470 g_print("\nTB '%s' CLOSE, in LB: %c\n", tk->exec_bin, btn != NULL ? 'Y':'N');
471 if (fi)
472 fm_file_info_unref(fi);
473 }
474 #endif
475 }
476
477 /* Build the graphic elements for a launchtaskbar button. The desktop_id field is already established. */
478 /* NOTE: this func consumes reference on fi */
479 static LaunchButton *launchbutton_for_file_info(LaunchTaskBarPlugin * lb, FmFileInfo * fi)
480 {
481 LaunchButton *btn;
482 GtkWidget *button;
483
484 if (fi == NULL)
485 {
486 g_warning("launchbar: desktop entry does not exist\n");
487 return NULL;
488 }
489
490 /* Allocate the LaunchButton structure. */
491 btn = g_new0(LaunchButton, 1);
492 btn->p = lb;
493 btn->fi = fi;
494
495 /* Create a button with the specified icon. */
496 button = lxpanel_button_new_for_fm_icon(lb->panel, fm_file_info_get_icon(fi),
497 NULL, NULL);
498 btn->widget = button;
499 gtk_widget_set_can_focus(button, FALSE);
500
501 gtk_widget_set_tooltip_text(button, fm_file_info_get_disp_name(fi));
502
503 /* Add the button to the icon grid. */
504 gtk_container_add(GTK_CONTAINER(lb->lb_icon_grid), button);
505
506 /* Drag and drop support. */
507 btn->dd = fm_dnd_dest_new_with_handlers(button);
508
509 /* Connect signals. */
510 g_signal_connect(button, "button-press-event", G_CALLBACK(launchbutton_press_event), (gpointer) btn);
511 g_signal_connect(button, "drag-motion", G_CALLBACK(launchbutton_drag_motion_event), btn);
512
513 /* If the list goes from null to non-null, remove the bootstrap button. */
514 if ((lb->buttons == NULL) && (lb->bootstrap_button != NULL))
515 gtk_widget_hide(lb->bootstrap_button->widget);
516
517 /* Append at end of list to preserve configured order. */
518 lb->buttons = g_slist_append(lb->buttons, btn);
519
520 /* Show the widget and return. */
521 gtk_widget_show(button);
522 plugin_widget_set_background(button, lb->panel);
523 return btn;
524 }
525
526 static LaunchButton *launchbutton_build_gui(LaunchTaskBarPlugin * lb, FmPath * id)
527 {
528 /* Try to get the file data */
529 FmFileInfoJob *job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
530 FmFileInfo *fi;
531
532 fm_file_info_job_add(job, id);
533 if (!fm_job_run_sync(FM_JOB(job)))
534 {
535 g_warning("launchbar: problem running file info job\n");
536 g_object_unref(job);
537 return NULL;
538 }
539 fi = fm_file_info_list_pop_head(job->file_infos);
540 g_object_unref(job);
541 return launchbutton_for_file_info(lb, fi);
542 }
543
544 static LaunchButton *launchbutton_search_and_build_gui(LaunchTaskBarPlugin * lb, FmPath * id)
545 {
546 FmDirListJob *job = fm_dir_list_job_new2(id, FM_DIR_LIST_JOB_FAST);
547 FmFileInfo *fi;
548
549 if (!fm_job_run_sync(FM_JOB(job)))
550 {
551 g_warning("launchbar: problem running file search job\n");
552 g_object_unref(job);
553 return NULL;
554 }
555 fi = fm_file_info_list_pop_head(job->files);
556 g_object_unref(job);
557 return launchbutton_for_file_info(lb, fi);
558 }
559
560 /* Read the configuration file entry for a launchtaskbar button and create it. */
561 static gboolean launchbutton_constructor(LaunchTaskBarPlugin * lb, config_setting_t * s)
562 {
563 LaunchButton *btn = NULL;
564 const char *str;
565 char *str_path = NULL;
566 FmPath *path;
567
568 /* Read parameters from the configuration file and validate. */
569 if (!config_setting_lookup_string(s, "id", &str) || str[0] == '\0')
570 return FALSE;
571
572 /* Build the structures and return. */
573 if (str[0] == '~')
574 {
575 str_path = expand_tilda(str);
576 path = fm_path_new_for_path(str_path);
577 btn = launchbutton_build_gui(lb, path);
578 }
579 else if (strchr(str, '/') != NULL)
580 {
581 path = fm_path_new_for_str(str);
582 /* FIXME: check if str contains invalid path */
583 btn = launchbutton_build_gui(lb, path);
584 }
585 else
586 {
587 str_path = g_strdup_printf("search://menu://applications/?recursive=1&show_hidden=1&name=%s", str);
588 path = fm_path_new_for_uri(str_path);
589 btn = launchbutton_search_and_build_gui(lb, path);
590 }
591 g_free(str_path);
592 fm_path_unref(path);
593 if (btn)
594 btn->settings = s;
595 return (btn != NULL);
596 }
597
598 /* prototype of this is app_info_create_from_commandline() in libfm */
599 static gboolean _launchbutton_create_id(LaunchTaskBarPlugin * lb, config_setting_t * s)
600 {
601 const char *icon = NULL, *name, *exec, *path = NULL;
602 char *dirname, *filename;
603 int fd, terminal = 0;
604 gboolean ret = FALSE;
605
606 if (!config_setting_lookup_string(s, "action", &exec) || exec[0] == '\0')
607 return FALSE;
608 if (!config_setting_lookup_string(s, "tooltip", &name) || name[0] == '\0')
609 name = "Launcher"; /* placeholder, XDG requires a non-empty name */
610 config_setting_lookup_string(s, "image", &icon);
611 config_setting_lookup_string(s, "path", &path);
612 config_setting_lookup_int(s, "terminal", &terminal);
613
614 dirname = g_build_filename(g_get_user_data_dir(), "applications", NULL);
615 if (g_mkdir_with_parents(dirname, 0700) == 0)
616 {
617 filename = g_strdup_printf("%s/lxpanel-launcher-XXXXXX.desktop", dirname);
618 fd = g_mkstemp (filename);
619 if (fd != -1)
620 {
621 GString* content = g_string_sized_new(256);
622
623 g_string_printf(content,
624 "[" G_KEY_FILE_DESKTOP_GROUP "]\n"
625 G_KEY_FILE_DESKTOP_KEY_TYPE "=" G_KEY_FILE_DESKTOP_TYPE_APPLICATION "\n"
626 G_KEY_FILE_DESKTOP_KEY_NAME "=%s\n"
627 G_KEY_FILE_DESKTOP_KEY_EXEC "=%s\n"
628 G_KEY_FILE_DESKTOP_KEY_CATEGORIES "=X-LXPanel;\n",
629 name, exec);
630 if (icon)
631 g_string_append_printf(content, "Icon=%s\n", icon);
632 if (terminal)
633 g_string_append(content, G_KEY_FILE_DESKTOP_KEY_TERMINAL "=true\n");
634 if (path && path[0] == '/')
635 g_string_append_printf(content, "Path=%s\n", path);
636 close(fd);
637 ret = g_file_set_contents(filename, content->str, content->len, NULL);
638 if (ret) {
639 config_group_set_string(s, "id", filename);
640 /* FIXME: is it reasonable to remove obsolete keys too? */
641 lxpanel_config_save(lb->panel);
642 } else
643 g_unlink(filename);
644 g_string_free(content, TRUE);
645 }
646 g_free(filename);
647 }
648 g_free(dirname);
649 if (ret) /* we created it, let use it */
650 return launchbutton_constructor(lb, s);
651 return FALSE;
652 }
653
654 static void launchtaskbar_constructor_add_default_special_case(LaunchTaskBarPlugin *ltbp, const gchar *tk_exec, const gchar *mb_exec)
655 {
656 g_key_file_set_value(ltbp->p_key_file_special_cases, "special_cases", tk_exec, mb_exec);
657 }
658
659 static void launchtaskbar_constructor_launch(LaunchTaskBarPlugin *ltbp, gboolean build_bootstrap)
660 {
661 config_setting_t *settings;
662
663 if(!ltbp->lb_built)
664 {
665 ltbp->lb_built = TRUE;
666 /* Read parameters from the configuration file. */
667 settings = config_setting_get_member(ltbp->settings, "");
668 if (settings && config_setting_is_list(settings))
669 {
670 config_setting_t *s;
671 guint i;
672
673 for (i = 0; (s = config_setting_get_elem(settings, i)) != NULL; )
674 {
675 if (strcmp(config_setting_get_name(s), "Button") != 0)
676 {
677 g_warning("launchtaskbar: illegal token %s\n", config_setting_get_name(s));
678 config_setting_destroy(s);
679 }
680 else if (!launchbutton_constructor(ltbp, s) &&
681 /* try to create desktop id from old-style manual setup */
682 !_launchbutton_create_id(ltbp, s))
683 {
684 g_warning( "launchtaskbar: can't init button\n");
685 /* FIXME: show failed id to the user instead */
686 config_setting_destroy(s);
687 }
688 else /* success, accept the setting */
689 i++;
690 }
691 }
692 if(build_bootstrap)
693 {
694 if(ltbp->buttons == NULL)
695 launchbutton_build_bootstrap(ltbp);
696 }
697 }
698 gtk_widget_set_visible(ltbp->lb_icon_grid, TRUE);
699 }
700
701 static void launchtaskbar_constructor_task(LaunchTaskBarPlugin *ltbp)
702 {
703 if(!ltbp->tb_built)
704 {
705 config_setting_t *s = ltbp->settings;
706 gint tmp_int;
707
708 ltbp->tb_built = TRUE;
709
710 /* Parse configuration now */
711 if (config_setting_lookup_int(s, "tooltips", &tmp_int))
712 ltbp->tooltips = (tmp_int != 0);
713 if (config_setting_lookup_int(s, "IconsOnly", &tmp_int))
714 ltbp->icons_only = (tmp_int != 0);
715 if (config_setting_lookup_int(s, "ShowAllDesks", &tmp_int))
716 ltbp->show_all_desks = (tmp_int != 0);
717 if (config_setting_lookup_int(s, "SameMonitorOnly", &tmp_int))
718 ltbp->same_monitor_only = (tmp_int != 0);
719 config_setting_lookup_int(s, "MaxTaskWidth", &ltbp->task_width_max);
720 config_setting_lookup_int(s, "spacing", &ltbp->spacing);
721 if (config_setting_lookup_int(s, "UseMouseWheel", &tmp_int))
722 ltbp->use_mouse_wheel = (tmp_int != 0);
723 if (config_setting_lookup_int(s, "UseUrgencyHint", &tmp_int))
724 ltbp->use_urgency_hint = (tmp_int != 0);
725 if (config_setting_lookup_int(s, "FlatButton", &tmp_int))
726 ltbp->flat_button = (tmp_int != 0);
727 if (config_setting_lookup_int(s, "GroupedTasks", &tmp_int))
728 ltbp->grouped_tasks = (tmp_int != 0);
729
730 /* Make container for task buttons as a child of top level widget. */
731 ltbp->tb_icon_grid = panel_icon_grid_new(panel_get_orientation(ltbp->panel),
732 ltbp->task_width_max,
733 ltbp->icon_size, ltbp->spacing, 0,
734 panel_get_height(ltbp->panel));
735 panel_icon_grid_set_constrain_width(PANEL_ICON_GRID(ltbp->tb_icon_grid), TRUE);
736 gtk_box_pack_start(GTK_BOX(ltbp->plugin), ltbp->tb_icon_grid, TRUE, TRUE, 0);
737 gtk_container_set_border_width(GTK_CONTAINER(ltbp->tb_icon_grid), 0);
738 taskbar_update_style(ltbp);
739
740 /* Add GDK event filter. */
741 gdk_window_add_filter(NULL, (GdkFilterFunc) taskbar_event_filter, ltbp);
742
743 /* Connect signals to receive root window events and initialize root window properties. */
744 ltbp->number_of_desktops = get_net_number_of_desktops();
745 ltbp->current_desktop = get_net_current_desktop();
746 g_signal_connect(G_OBJECT(fbev), "current-desktop", G_CALLBACK(taskbar_net_current_desktop), (gpointer) ltbp);
747 g_signal_connect(G_OBJECT(fbev), "active-window", G_CALLBACK(taskbar_net_active_window), (gpointer) ltbp);
748 g_signal_connect(G_OBJECT(fbev), "number-of-desktops", G_CALLBACK(taskbar_net_number_of_desktops), (gpointer) ltbp);
749 g_signal_connect(G_OBJECT(fbev), "client-list", G_CALLBACK(taskbar_net_client_list), (gpointer) ltbp);
750
751 /* Make right-click menu for task buttons.
752 * It is retained for the life of the taskbar and will be shown as needed.
753 * Number of desktops and edge is needed for this operation. */
754 taskbar_make_menu(ltbp);
755
756 /* Connect a signal to be notified when the window manager changes. This causes re-evaluation of the "use_net_active" status. */
757 g_signal_connect(ltbp->screen, "window-manager-changed", G_CALLBACK(taskbar_window_manager_changed), ltbp);
758
759 /* Fetch the client list and redraw the taskbar. Then determine what window has focus. */
760 taskbar_net_client_list(NULL, ltbp);
761 taskbar_net_active_window(NULL, ltbp);
762 }
763 gtk_widget_set_visible(ltbp->tb_icon_grid, TRUE);
764 }
765
766 /* Plugin constructor. */
767 static GtkWidget *_launchtaskbar_constructor(LXPanel *panel, config_setting_t *settings,
768 LtbMode mode)
769 {
770 GtkWidget *p;
771 LaunchTaskBarPlugin *ltbp;
772
773 gtk_rc_parse_string(launchtaskbar_rc);
774
775 /* Allocate plugin context and set into Plugin private data pointer. */
776 ltbp = g_new0(LaunchTaskBarPlugin, 1);
777 ltbp->panel = panel;
778 ltbp->settings = settings;
779 ltbp->mode = mode;
780 ltbp->screen = gtk_widget_get_screen((GtkWidget*)panel);
781
782 /* Initialize to defaults. */
783 ltbp->icon_size = panel_get_icon_size(panel);
784 ltbp->tooltips = TRUE;
785 ltbp->icons_only = FALSE;
786 ltbp->show_all_desks = TRUE;
787 ltbp->task_width_max = TASK_WIDTH_MAX;
788 ltbp->spacing = 1;
789 ltbp->use_mouse_wheel = TRUE;
790 ltbp->use_urgency_hint = TRUE;
791 ltbp->grouped_tasks = FALSE;
792 ltbp->fixed_mode = (mode == LAUNCHBAR) || (mode == TASKBAR);
793
794 /* Special cases key file */
795 ltbp->p_key_file_special_cases = g_key_file_new();
796 gchar *special_cases_filepath = g_build_filename(g_get_user_config_dir(),
797 "lxpanel", "launchtaskbar.cfg", NULL);
798 if (!g_key_file_load_from_file(ltbp->p_key_file_special_cases,
799 special_cases_filepath,
800 G_KEY_FILE_KEEP_COMMENTS, NULL))
801 {
802 launchtaskbar_constructor_add_default_special_case(ltbp, "synaptic", "synaptic-pkexec");
803 launchtaskbar_constructor_add_default_special_case(ltbp, "soffice.bin", "libreoffice");
804 launchtaskbar_constructor_add_default_special_case(ltbp, "x-terminal-emulator", "lxterminal");
805 gchar *key_file_data = g_key_file_to_data(ltbp->p_key_file_special_cases, NULL, NULL);
806 g_file_set_contents(special_cases_filepath, key_file_data, -1, NULL);
807 g_free(key_file_data);
808 }
809 g_free(special_cases_filepath);
810
811 /* Allocate top level widget and set into Plugin widget pointer. */
812 ltbp->plugin = p = panel_box_new(panel, FALSE, 5);
813 lxpanel_plugin_set_data(p, ltbp, launchtaskbar_destructor);
814 /* Allocate an icon grid manager to manage the container. */
815 ltbp->lb_icon_grid = panel_icon_grid_new(panel_get_orientation(panel),
816 ltbp->icon_size, ltbp->icon_size,
817 3, 0, panel_get_height(panel));
818 gtk_box_pack_start(GTK_BOX(p), ltbp->lb_icon_grid, FALSE, TRUE, 0);
819
820 gtk_container_set_border_width(GTK_CONTAINER(p), 0);
821 gtk_container_set_border_width(GTK_CONTAINER(ltbp->lb_icon_grid), 0);
822
823 /* Read parameters from the configuration file. */
824 config_setting_lookup_int(settings, "LaunchTaskBarMode", &ltbp->mode);
825 switch (ltbp->mode) {
826 case LAUNCHBAR:
827 launchtaskbar_constructor_launch(ltbp, TRUE/*build_bootstrap*/);
828 gtk_widget_set_name(p, "launchbar");
829 break;
830 default:
831 ltbp->mode = LAUNCHTASKBAR; /* reset invalid value */
832 case LAUNCHTASKBAR:
833 launchtaskbar_constructor_launch(ltbp, TRUE/*build_bootstrap*/);
834 gtk_widget_set_name(p, "launchtaskbar");
835 case TASKBAR:
836 launchtaskbar_constructor_task(ltbp);
837 if (ltbp->mode == TASKBAR)
838 gtk_widget_set_name(p, "taskbar");
839 }
840
841 return p;
842 }
843
844 static GtkWidget *launchtaskbar_constructor(LXPanel *panel, config_setting_t *settings)
845 {
846 return _launchtaskbar_constructor(panel, settings, LAUNCHTASKBAR);
847 }
848
849 static void launchtaskbar_destructor_launch(LaunchTaskBarPlugin *ltbp)
850 {
851 /* Free the launchbar. */
852 g_slist_foreach(ltbp->buttons, (GFunc) launchbutton_free, NULL);
853
854 /* Free the bootstrap button if it exists. */
855 if(ltbp->bootstrap_button != NULL)
856 {
857 launchbutton_free(ltbp->bootstrap_button);
858 ltbp->bootstrap_button = NULL;
859 }
860
861 if (ltbp->add_icon != NULL)
862 g_object_unref(ltbp->add_icon);
863 }
864
865 static void launchtaskbar_destructor_task(LaunchTaskBarPlugin *ltbp)
866 {
867 /* Remove GDK event filter. */
868 gdk_window_remove_filter(NULL, (GdkFilterFunc) taskbar_event_filter, ltbp);
869
870 /* Remove root window signal handlers. */
871 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_current_desktop, ltbp);
872 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_active_window, ltbp);
873 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_number_of_desktops, ltbp);
874 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_client_list, ltbp);
875
876 /* Remove "window-manager-changed" handler. */
877 g_signal_handlers_disconnect_by_func(ltbp->screen, taskbar_window_manager_changed, ltbp);
878
879 /* Deallocate task list - widgets are already destroyed there. */
880 while(ltbp->p_task_list != NULL)
881 task_delete(ltbp, ltbp->p_task_list, TRUE, FALSE);
882
883 /* Deallocate class list. */
884 while(ltbp->p_taskclass_list != NULL)
885 {
886 TaskClass * tc = ltbp->p_taskclass_list;
887 ltbp->p_taskclass_list = tc->p_taskclass_flink;
888 g_free(tc->res_class);
889 g_free(tc);
890 }
891
892 /* Deallocate other memory. */
893 gtk_widget_destroy(ltbp->menu);
894 task_group_menu_destroy(ltbp);
895 }
896
897 /* Plugin destructor. */
898 static void launchtaskbar_destructor(gpointer user_data)
899 {
900 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)user_data;
901
902 // TASKBAR
903 if(ltbp->tb_built) launchtaskbar_destructor_task(ltbp);
904
905 // LAUNCHBAR
906 if(ltbp->lb_built) launchtaskbar_destructor_launch(ltbp);
907
908 // LAUNCHTASKBAR
909
910 /* Deallocate all memory. */
911 if (ltbp->p_key_file_special_cases != NULL)
912 g_key_file_free(ltbp->p_key_file_special_cases);
913 g_free(ltbp);
914 }
915
916 static void _launchbar_configure_add(GtkTreeView *menu_view, LaunchTaskBarPlugin *ltbp)
917 {
918 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
919 FmPath * sel_path = fm_app_menu_view_dup_selected_app_desktop_path(menu_view);
920 LaunchButton * btn;
921
922 if (sel_path != NULL && (btn = launchbutton_build_gui(ltbp, sel_path)) != NULL)
923 {
924 GtkListStore * list = GTK_LIST_STORE(gtk_tree_view_get_model(defined_view));
925 GtkTreeIter it;
926 GdkPixbuf* pix;
927 char *path;
928 gtk_list_store_append(list, &it);
929 pix = fm_pixbuf_from_icon(fm_file_info_get_icon(btn->fi), PANEL_ICON_SIZE);
930 gtk_list_store_set(list, &it,
931 COL_ICON, pix,
932 COL_TITLE, fm_file_info_get_disp_name(btn->fi),
933 COL_BTN, btn,
934 -1);
935 g_object_unref(pix);
936 path = fm_path_to_str(sel_path);
937 /* g_debug("*** path '%s'",path); */
938 btn->settings = config_group_add_subgroup(ltbp->settings, "Button");
939 config_group_set_string(btn->settings, "id", path);
940 g_free(path);
941 fm_path_unref(sel_path);
942 }
943 }
944
945 /* Handler for "clicked" action on launchtaskbar configuration dialog "Add" button. */
946 static void launchbar_configure_add_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
947 {
948 GtkTreeView * menu_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "menu_view"));
949
950 _launchbar_configure_add(menu_view, ltbp);
951 }
952
953 static void launchbar_remove_button(LaunchTaskBarPlugin *ltbp, LaunchButton *btn)
954 {
955 ltbp->buttons = g_slist_remove(ltbp->buttons, btn);
956 gtk_widget_destroy(btn->widget);
957 config_setting_destroy(btn->settings);
958 launchbutton_free(btn);
959 /* Put the bootstrap button back if the list becomes empty. */
960 if(ltbp->buttons == NULL)
961 launchbutton_build_bootstrap(ltbp);
962 }
963
964 /* Handler for "clicked" action on launchtaskbar configuration dialog "Remove" button. */
965 static void launchbar_configure_remove_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
966 {
967 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
968 GtkTreeModel * list;
969 GtkTreeIter it;
970 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
971 {
972 LaunchButton * btn;
973 gtk_tree_model_get(list, &it, COL_BTN, &btn, -1);
974
975 /* We have found a selected button.
976 * Remove it from the icon grid, the data structure, and the view. */
977 gtk_list_store_remove(GTK_LIST_STORE(list), &it);
978 gtk_widget_set_visible(ltbp->p_label_def_app_exec, FALSE);
979
980 launchbar_remove_button(ltbp, btn);
981 }
982 }
983
984 /* Handler for "clicked" action on launchtaskbar configuration dialog "Move Up" button. */
985 static void launchbar_configure_move_up_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
986 {
987 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
988 GtkTreeModel * list;
989 GtkTreeIter it;
990 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
991 {
992 LaunchButton *btn;
993 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, COL_BTN, &btn, -1);
994 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &it);
995 if ((gtk_tree_path_get_indices(path)[0] > 0)
996 && (gtk_tree_path_prev(path)))
997 {
998 GtkTreeIter it2;
999 if (gtk_tree_model_get_iter(list, &it2, path))
1000 {
1001 /* We have found a selected button that can be moved.
1002 * Reorder it in the icon grid, the data structure, and the view. */
1003 int i = gtk_tree_path_get_indices(path)[0];
1004 ltbp->buttons = g_slist_remove(ltbp->buttons, btn);
1005 ltbp->buttons = g_slist_insert(ltbp->buttons, btn, i);
1006 gtk_list_store_move_before(GTK_LIST_STORE(list), &it, &it2);
1007 panel_icon_grid_reorder_child(PANEL_ICON_GRID(ltbp->lb_icon_grid),
1008 btn->widget, i);
1009 config_setting_move_elem(btn->settings,
1010 config_setting_get_parent(btn->settings),
1011 i);
1012 }
1013 }
1014 gtk_tree_path_free(path);
1015 }
1016 }
1017
1018 /* Handler for "clicked" action on launchtaskbar configuration dialog "Move Down" button. */
1019 static void launchbar_configure_move_down_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
1020 {
1021 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
1022 GtkTreeModel * list;
1023 GtkTreeIter it;
1024 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
1025 {
1026 LaunchButton *btn;
1027 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, COL_BTN, &btn, -1);
1028 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &it);
1029 int n = gtk_tree_model_iter_n_children(list, NULL);
1030 if (gtk_tree_path_get_indices(path)[0] < (n - 1))
1031 {
1032 gtk_tree_path_next(path);
1033 GtkTreeIter it2;
1034 if (gtk_tree_model_get_iter( list, &it2, path))
1035 {
1036 /* We have found a selected button that can be moved.
1037 * Reorder it in the icon grid, the data structure, and the view. */
1038 int i = gtk_tree_path_get_indices(path)[0];
1039 ltbp->buttons = g_slist_remove(ltbp->buttons, btn);
1040 ltbp->buttons = g_slist_insert(ltbp->buttons, btn, i + 1);
1041 gtk_list_store_move_after(GTK_LIST_STORE(list), &it, &it2);
1042 panel_icon_grid_reorder_child(PANEL_ICON_GRID(ltbp->lb_icon_grid),
1043 btn->widget, i);
1044 config_setting_move_elem(btn->settings,
1045 config_setting_get_parent(btn->settings),
1046 i);
1047 }
1048 }
1049 gtk_tree_path_free(path);
1050 }
1051 }
1052
1053 /* Initialize the list of existing launchtaskbar buttons when the configuration dialog is shown. */
1054 static void launchbar_configure_initialize_list(LaunchTaskBarPlugin *ltbp, GtkWidget * dlg, GtkTreeView * view)
1055 {
1056 /* Set the selection mode. */
1057 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_BROWSE);
1058
1059 /* Define a column. */
1060 GtkTreeViewColumn* col = gtk_tree_view_get_column(view, 0);
1061
1062 /* Establish the pixbuf column cell renderer. */
1063 GtkCellRenderer * render = gtk_cell_renderer_pixbuf_new();
1064 gtk_tree_view_column_pack_start(col, render, FALSE);
1065 gtk_tree_view_column_set_attributes(col, render, "pixbuf", COL_ICON, NULL);
1066
1067 /* Establish the text column cell renderer. */
1068 render = gtk_cell_renderer_text_new();
1069 gtk_tree_view_column_pack_start(col, render, TRUE);
1070 gtk_tree_view_column_add_attribute(col, render, "text", COL_TITLE);
1071
1072 /* Establish the column data types. */
1073 GtkListStore* list = GTK_LIST_STORE(gtk_tree_view_get_model(view));
1074
1075 /* Initialize from defined launchtaskbar buttons. */
1076 GSList* l;
1077 for (l = ltbp->buttons; l != NULL; l = l->next)
1078 {
1079 LaunchButton * btn = (LaunchButton *) l->data;
1080 GdkPixbuf * pix;
1081 GtkTreeIter it;
1082 gtk_list_store_append(list, &it);
1083 pix = fm_pixbuf_from_icon(fm_file_info_get_icon(btn->fi), PANEL_ICON_SIZE);
1084 gtk_list_store_set(list, &it,
1085 COL_ICON, pix,
1086 COL_TITLE, fm_file_info_get_disp_name(btn->fi),
1087 COL_BTN, btn,
1088 -1);
1089 g_object_unref(pix);
1090 }
1091 g_object_set_data(G_OBJECT(dlg), "defined_view", view);
1092 }
1093
1094 static void plugin_set_expand_status(LaunchTaskBarPlugin *ltbp, gboolean expand_new)
1095 {
1096 gboolean old_expand, fill;
1097 guint padding;
1098 GtkPackType pack_type;
1099 GtkWidget *box = gtk_widget_get_parent(ltbp->plugin);
1100 g_return_if_fail(box);
1101 gtk_box_query_child_packing(GTK_BOX(box), ltbp->plugin, &old_expand, &fill, &padding, &pack_type);
1102 gtk_box_set_child_packing(GTK_BOX(box), ltbp->plugin, expand_new, fill, padding, pack_type);
1103 }
1104
1105 static void set_config_visibility(LaunchTaskBarPlugin *ltbp)
1106 {
1107 switch (ltbp->mode) {
1108 default:
1109 case LAUNCHTASKBAR:
1110 gtk_widget_set_visible(ltbp->p_notebook_page_launch, TRUE);
1111 gtk_widget_set_visible(ltbp->p_notebook_page_task, TRUE);
1112 gtk_notebook_set_show_tabs(ltbp->p_notebook, TRUE);
1113 break;
1114 case TASKBAR:
1115 gtk_widget_set_visible(ltbp->p_notebook_page_launch, FALSE);
1116 gtk_widget_set_visible(ltbp->p_notebook_page_task, TRUE);
1117 gtk_notebook_set_show_tabs(ltbp->p_notebook, FALSE);
1118 break;
1119 case LAUNCHBAR:
1120 gtk_widget_set_visible(ltbp->p_notebook_page_launch, TRUE);
1121 gtk_widget_set_visible(ltbp->p_notebook_page_task, FALSE);
1122 gtk_notebook_set_show_tabs(ltbp->p_notebook, FALSE);
1123 }
1124 }
1125
1126 static void on_combobox_mode_changed(GtkComboBox *p_combobox, gpointer p_data)
1127 {
1128 LaunchTaskBarPlugin *ltbp = p_data;
1129 int new_mode = gtk_combo_box_get_active(GTK_COMBO_BOX(p_combobox));
1130
1131 if (new_mode < 0 || new_mode == ltbp->mode) /* no change was made */
1132 return;
1133
1134 ltbp->mode = new_mode;
1135
1136 set_config_visibility(ltbp);
1137
1138 switch (ltbp->mode) {
1139 case LAUNCHBAR:
1140 if (ltbp->tb_icon_grid)
1141 gtk_widget_set_visible(ltbp->tb_icon_grid, FALSE);
1142 launchtaskbar_constructor_launch(ltbp, TRUE/*build_bootstrap*/);
1143 plugin_set_expand_status(ltbp, FALSE);
1144 gtk_widget_set_name(ltbp->plugin, "launchbar");
1145 break;
1146 case TASKBAR:
1147 gtk_widget_set_visible(ltbp->lb_icon_grid, FALSE);
1148 launchtaskbar_constructor_task(ltbp);
1149 plugin_set_expand_status(ltbp, TRUE);
1150 gtk_widget_set_name(ltbp->plugin, "taskbar");
1151 break;
1152 default:
1153 ltbp->mode = LAUNCHTASKBAR;
1154 case LAUNCHTASKBAR:
1155 launchtaskbar_constructor_launch(ltbp, TRUE/*build_bootstrap*/);
1156 launchtaskbar_constructor_task(ltbp);
1157 plugin_set_expand_status(ltbp, TRUE);
1158 gtk_widget_set_name(ltbp->plugin, "launchtaskbar");
1159 break;
1160 }
1161
1162 config_group_set_int(ltbp->settings, "LaunchTaskBarMode", ltbp->mode);
1163 }
1164
1165 static void on_checkbutton_show_tooltips_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1166 {
1167 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1168 ltbp->tooltips = gtk_toggle_button_get_active(p_togglebutton);
1169 //g_print("\nltbp->tooltips upd\n");
1170 config_group_set_int(ltbp->settings, "tooltips", ltbp->tooltips);
1171 taskbar_apply_configuration(ltbp);
1172 }
1173
1174 static void on_checkbutton_icons_only_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1175 {
1176 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1177 ltbp->icons_only = gtk_toggle_button_get_active(p_togglebutton);
1178 //g_print("\ntb->icons_only upd\n");
1179 config_group_set_int(ltbp->settings, "IconsOnly", ltbp->icons_only);
1180 taskbar_apply_configuration(ltbp);
1181 }
1182
1183 static void on_checkbutton_flat_buttons_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1184 {
1185 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1186 ltbp->flat_button = gtk_toggle_button_get_active(p_togglebutton);
1187 //g_print("\ntb->flat_button upd\n");
1188 config_group_set_int(ltbp->settings, "FlatButton", ltbp->flat_button);
1189 taskbar_apply_configuration(ltbp);
1190 }
1191
1192 static void on_checkbutton_show_all_desks_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1193 {
1194 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1195 ltbp->show_all_desks = gtk_toggle_button_get_active(p_togglebutton);
1196 //g_print("\ntb->show_all_desks upd\n");
1197 config_group_set_int(ltbp->settings, "ShowAllDesks", ltbp->show_all_desks);
1198 taskbar_apply_configuration(ltbp);
1199 }
1200
1201 static void on_checkbutton_same_monitor_only_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1202 {
1203 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1204 ltbp->same_monitor_only = gtk_toggle_button_get_active(p_togglebutton);
1205 //g_print("\ntb->same_monitor_only upd\n");
1206 config_group_set_int(ltbp->settings, "SameMonitorOnly", ltbp->same_monitor_only);
1207 taskbar_apply_configuration(ltbp);
1208 }
1209
1210 static void on_checkbutton_mouse_wheel_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1211 {
1212 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1213 ltbp->use_mouse_wheel = gtk_toggle_button_get_active(p_togglebutton);
1214 //g_print("\ntb->use_mouse_wheel upd\n");
1215 config_group_set_int(ltbp->settings, "UseMouseWheel", ltbp->use_mouse_wheel);
1216 taskbar_apply_configuration(ltbp);
1217 }
1218
1219 static void on_checkbutton_urgency_hint_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1220 {
1221 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1222 ltbp->use_urgency_hint = gtk_toggle_button_get_active(p_togglebutton);
1223 //g_print("\ntb->use_urgency_hint upd\n");
1224 config_group_set_int(ltbp->settings, "UseUrgencyHint", ltbp->use_urgency_hint);
1225 taskbar_apply_configuration(ltbp);
1226 }
1227
1228 static void on_checkbutton_grouped_tasks_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1229 {
1230 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1231 ltbp->grouped_tasks = gtk_toggle_button_get_active(p_togglebutton);
1232 //g_print("\ntb->grouped_tasks upd\n");
1233 config_group_set_int(ltbp->settings, "GroupedTasks", ltbp->grouped_tasks);
1234 taskbar_apply_configuration(ltbp);
1235 }
1236
1237 static void on_spinbutton_max_width_value_changed(GtkSpinButton *p_spinbutton, gpointer p_data)
1238 {
1239 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1240 ltbp->task_width_max = gtk_spin_button_get_value(p_spinbutton);
1241 //g_print("\ntb->task_width_max upd\n");
1242 config_group_set_int(ltbp->settings, "MaxTaskWidth", ltbp->task_width_max);
1243 taskbar_apply_configuration(ltbp);
1244 }
1245
1246 static void on_spinbutton_spacing_value_changed(GtkSpinButton *p_spinbutton, gpointer p_data)
1247 {
1248 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1249 ltbp->spacing = gtk_spin_button_get_value(p_spinbutton);
1250 //g_print("\ntb->spacing upd\n");
1251 config_group_set_int(ltbp->settings, "spacing", ltbp->spacing);
1252 taskbar_apply_configuration(ltbp);
1253 }
1254
1255 static gboolean on_defined_view_button_press_event(GtkWidget *p_widget, GdkEventButton *p_event, gpointer p_data)
1256 {
1257 LaunchTaskBarPlugin *lb = (LaunchTaskBarPlugin *)p_data;
1258 if(p_event->button == 1)
1259 {
1260 if(p_event->type == GDK_2BUTTON_PRESS)
1261 {
1262 gtk_button_clicked(GTK_BUTTON(lb->p_button_remove));
1263 }
1264 }
1265 return FALSE;
1266 }
1267
1268 static void on_defined_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_data)
1269 {
1270 gboolean label_set = FALSE;
1271 LaunchTaskBarPlugin *lb = (LaunchTaskBarPlugin *)p_data;
1272 GtkTreeIter tree_iter_sel;
1273 GtkTreeModel* p_treemodel = gtk_tree_view_get_model(p_treeview);
1274 GtkTreeSelection *p_treeselection = gtk_tree_view_get_selection(p_treeview);
1275 if(gtk_tree_selection_get_selected(p_treeselection,
1276 (GtkTreeModel **)(&p_treemodel),
1277 &tree_iter_sel))
1278 {
1279 LaunchButton * p_btn;
1280 gtk_tree_model_get(p_treemodel, &tree_iter_sel, COL_BTN, &p_btn, -1);
1281 if( (p_btn != NULL) && (p_btn->fi != NULL) )
1282 {
1283 GString *p_gstring = g_string_new("");
1284 g_string_printf(p_gstring, "<i>%s</i>", fm_file_info_get_disp_name(p_btn->fi));
1285 gtk_label_set_markup(GTK_LABEL(lb->p_label_def_app_exec), p_gstring->str);
1286 g_string_free(p_gstring, TRUE/*free also gstring->str*/);
1287 label_set = TRUE;
1288 }
1289 }
1290 gtk_widget_set_visible(lb->p_label_def_app_exec, label_set);
1291 gtk_widget_set_sensitive(lb->p_button_remove, label_set);
1292 }
1293
1294 static void on_menu_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_data)
1295 {
1296 gboolean label_set = FALSE;
1297 LaunchTaskBarPlugin *lb = (LaunchTaskBarPlugin *)p_data;
1298 GAppInfo *app = fm_app_menu_view_dup_selected_app(p_treeview);
1299
1300 if (app)
1301 {
1302 GString *p_gstring = g_string_new("");
1303 if (g_app_info_get_description(app))
1304 g_string_printf(p_gstring, "<i>%s</i>", g_app_info_get_description(app));
1305 else
1306 g_string_printf(p_gstring, "<i>%s</i>", g_app_info_get_name(app));
1307 gtk_label_set_markup(GTK_LABEL(lb->p_label_menu_app_exec), p_gstring->str);
1308 g_string_free(p_gstring, TRUE/*free also gstring->str*/);
1309 label_set = TRUE;
1310 }
1311 gtk_widget_set_visible(lb->p_label_menu_app_exec, label_set);
1312 gtk_widget_set_sensitive(lb->p_button_add, label_set);
1313 }
1314
1315 static void on_menu_view_row_activated(GtkTreeView *tree_view, GtkTreePath *path,
1316 GtkTreeViewColumn *column,
1317 LaunchTaskBarPlugin *ltbp)
1318 {
1319 _launchbar_configure_add(tree_view, ltbp);
1320 }
1321
1322 /* Callback when the configuration dialog is to be shown. */
1323 static GtkWidget *launchtaskbar_configure(LXPanel *panel, GtkWidget *p)
1324 {
1325 LaunchTaskBarPlugin *ltbp = lxpanel_plugin_get_data(p);
1326
1327 {
1328 GtkWidget *dlg, *btn, *defined_view, *menu_view, *menu_view_window;
1329 GtkBuilder *builder = gtk_builder_new();
1330 GObject *object;
1331
1332 gtk_builder_add_from_file(builder, PACKAGE_UI_DIR "/launchtaskbar.ui", NULL);
1333 dlg = (GtkWidget *)gtk_builder_get_object(builder, "dlg");
1334 panel_apply_icon(GTK_WINDOW(dlg));
1335
1336 defined_view = (GtkWidget *)gtk_builder_get_object(builder, "defined_view");
1337 menu_view_window = (GtkWidget*)gtk_builder_get_object(builder, "menu_view_window");
1338 if (menu_view_window == NULL) /* fallback for old glade file */
1339 {
1340 menu_view_window = (GtkWidget*)gtk_builder_get_object(builder, "scroll2");
1341 gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(menu_view_window)));
1342 }
1343 menu_view = GTK_WIDGET(fm_app_menu_view_new());
1344 gtk_container_add(GTK_CONTAINER(menu_view_window), menu_view);
1345 gtk_widget_show(menu_view);
1346 ltbp->p_label_def_app_exec = (GtkWidget*)gtk_builder_get_object(builder, "label_def_app_exec");
1347 ltbp->p_label_menu_app_exec = (GtkWidget*)gtk_builder_get_object(builder, "label_menu_app_exec");
1348
1349 /* Connect signals. */
1350 ltbp->p_button_add = (GtkWidget *)gtk_builder_get_object(builder, "button_add");
1351 g_signal_connect(ltbp->p_button_add, "clicked", G_CALLBACK(launchbar_configure_add_button), ltbp);
1352
1353 ltbp->p_button_remove = (GtkWidget *)gtk_builder_get_object(builder, "button_remove");
1354 g_signal_connect(ltbp->p_button_remove, "clicked", G_CALLBACK(launchbar_configure_remove_button), ltbp);
1355
1356 btn = (GtkWidget *)gtk_builder_get_object(builder, "button_up");
1357 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_up_button), ltbp);
1358
1359 btn = (GtkWidget *)gtk_builder_get_object(builder, "button_down");
1360 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_down_button), ltbp);
1361
1362 /* FIXME: add a button 'New' with launcher creation dialog */
1363
1364 g_signal_connect(defined_view, "button-press-event", G_CALLBACK(on_defined_view_button_press_event), ltbp);
1365 g_signal_connect(defined_view, "cursor-changed", G_CALLBACK(on_defined_view_cursor_changed), ltbp);
1366 g_signal_connect(menu_view, "cursor-changed", G_CALLBACK(on_menu_view_cursor_changed), ltbp);
1367 g_signal_connect(menu_view, "row-activated", G_CALLBACK(on_menu_view_row_activated), ltbp);
1368
1369 ltbp->p_notebook = GTK_NOTEBOOK(gtk_builder_get_object(builder, "notebook"));
1370 ltbp->p_notebook_page_launch = gtk_notebook_get_nth_page(ltbp->p_notebook, 0);
1371 ltbp->p_notebook_page_task = gtk_notebook_get_nth_page(ltbp->p_notebook, 1);
1372 set_config_visibility(ltbp);
1373 object = gtk_builder_get_object(builder, "combobox_mode");
1374 gtk_combo_box_set_active(GTK_COMBO_BOX(object), ltbp->mode);
1375 g_signal_connect(object, "changed",
1376 G_CALLBACK(on_combobox_mode_changed), ltbp);
1377
1378 #define SETUP_TOGGLE_BUTTON(button,member) \
1379 object = gtk_builder_get_object(builder, #button); \
1380 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object), ltbp->member); \
1381 g_signal_connect(object, "toggled", G_CALLBACK(on_##button##_toggled), ltbp)
1382
1383 SETUP_TOGGLE_BUTTON(checkbutton_show_tooltips, tooltips);
1384 SETUP_TOGGLE_BUTTON(checkbutton_icons_only, icons_only);
1385 SETUP_TOGGLE_BUTTON(checkbutton_flat_buttons, flat_button);
1386 SETUP_TOGGLE_BUTTON(checkbutton_show_all_desks, show_all_desks);
1387 SETUP_TOGGLE_BUTTON(checkbutton_same_monitor_only, same_monitor_only);
1388 SETUP_TOGGLE_BUTTON(checkbutton_mouse_wheel, use_mouse_wheel);
1389 SETUP_TOGGLE_BUTTON(checkbutton_urgency_hint, use_urgency_hint);
1390 SETUP_TOGGLE_BUTTON(checkbutton_grouped_tasks, grouped_tasks);
1391 #undef SETUP_TOGGLE_BUTTON
1392
1393 #define SETUP_SPIN_BUTTON(button,member) \
1394 object = gtk_builder_get_object(builder, #button); \
1395 gtk_spin_button_set_value(GTK_SPIN_BUTTON(object), ltbp->member); \
1396 g_signal_connect(object, "value-changed", \
1397 G_CALLBACK(on_##button##_value_changed), ltbp)
1398
1399 SETUP_SPIN_BUTTON(spinbutton_max_width, task_width_max);
1400 SETUP_SPIN_BUTTON(spinbutton_spacing, spacing);
1401 #undef SETUP_SPIN_BUTTON
1402
1403 ltbp->config_dlg = dlg;
1404
1405 /* Initialize the tree view contents. */
1406 launchbar_configure_initialize_list(ltbp, dlg, GTK_TREE_VIEW(defined_view));
1407 g_object_set_data(G_OBJECT(dlg), "menu_view", menu_view);
1408
1409 gtk_widget_set_visible(ltbp->p_label_menu_app_exec, FALSE);
1410 gtk_widget_set_visible(ltbp->p_label_def_app_exec, FALSE);
1411 gtk_widget_set_sensitive(ltbp->p_button_add, FALSE);
1412 gtk_widget_set_sensitive(ltbp->p_button_remove, FALSE);
1413 if (ltbp->fixed_mode)
1414 {
1415 object = gtk_builder_get_object(builder, "hbox_mode");
1416 if (object)
1417 gtk_widget_destroy(GTK_WIDGET(object));
1418 if (ltbp->mode == LAUNCHBAR)
1419 gtk_window_set_title(GTK_WINDOW(ltbp->config_dlg),
1420 _("Application Launch Bar"));
1421 else
1422 gtk_window_set_title(GTK_WINDOW(ltbp->config_dlg),
1423 _("Task Bar (Window List)"));
1424 }
1425
1426 g_object_unref(builder);
1427 }
1428 return ltbp->config_dlg;
1429 }
1430
1431 /* Callback when panel configuration changes. */
1432 static void launchtaskbar_panel_configuration_changed(LXPanel *panel, GtkWidget *p)
1433 {
1434 /* Set orientation into the icon grid. */
1435 LaunchTaskBarPlugin *ltbp = lxpanel_plugin_get_data(p);
1436 int new_icon_size = panel_get_icon_size(panel);
1437
1438 if (ltbp->lb_built)
1439 panel_icon_grid_set_geometry(PANEL_ICON_GRID(ltbp->lb_icon_grid),
1440 panel_get_orientation(panel),
1441 new_icon_size, new_icon_size, 3, 0,
1442 panel_get_height(panel));
1443
1444 /* Reset the bootstrap button. */
1445 if (ltbp->bootstrap_button != NULL)
1446 {
1447 GdkPixbuf * icon = fm_pixbuf_from_icon(ltbp->add_icon, new_icon_size);
1448 gtk_image_set_from_pixbuf(GTK_IMAGE(ltbp->bootstrap_button->image_widget), icon);
1449 g_object_unref(icon);
1450 }
1451
1452 if (ltbp->tb_built)
1453 {
1454 taskbar_update_style(ltbp);
1455 taskbar_make_menu(ltbp);
1456 }
1457
1458 /* If the icon size changed, refetch all the icons. */
1459 if (new_icon_size != ltbp->icon_size)
1460 {
1461 Task * tk;
1462 GSList * l;
1463 ltbp->icon_size = new_icon_size;
1464 for (tk = ltbp->p_task_list; tk != NULL; tk = tk->p_task_flink_xwid)
1465 {
1466 GdkPixbuf * pixbuf = task_update_icon(ltbp, tk, None);
1467 if (pixbuf != NULL)
1468 {
1469 gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
1470 g_object_unref(pixbuf);
1471 }
1472 }
1473 for (l = ltbp->buttons; l != NULL; l = l->next)
1474 {
1475 LaunchButton * btn = (LaunchButton *) l->data;
1476 lxpanel_button_update_icon(btn->widget, fm_file_info_get_icon(btn->fi),
1477 new_icon_size);
1478 }
1479
1480 }
1481
1482 /* Redraw all the labels. Icon size or font color may have changed. */
1483 if (ltbp->tb_built)
1484 taskbar_redraw(ltbp);
1485 }
1486
1487 /* Set an urgency timer on a task. */
1488 static void set_timer_on_task(Task * tk)
1489 {
1490 gint interval;
1491 g_return_if_fail(tk->flash_timeout == 0);
1492 g_object_get(gtk_widget_get_settings(tk->button), "gtk-cursor-blink-time", &interval, NULL);
1493 tk->flash_timeout = g_timeout_add(interval, flash_window_timeout, tk);
1494 }
1495
1496 /* Determine if a task is visible considering only its desktop placement. */
1497 static gboolean task_is_visible_on_current_desktop(LaunchTaskBarPlugin * tb, Task * tk)
1498 {
1499 return ((tk->desktop == ALL_WORKSPACES) || (tk->desktop == tb->current_desktop) || (tb->show_all_desks));
1500 }
1501
1502 /* Recompute the visible task for a class when the class membership changes.
1503 * Also transfer the urgency state to the visible task if necessary. */
1504 static void recompute_group_visibility_for_class(LaunchTaskBarPlugin * tb, TaskClass * tc)
1505 {
1506 tc->visible_count = 0;
1507 tc->p_task_visible = NULL;
1508 tc->visible_name = NULL;
1509 Task * flashing_task = NULL;
1510 gboolean class_has_urgency = FALSE;
1511 Task * tk;
1512 for (tk = tc->p_task_head; tk != NULL; tk = tk->p_task_flink_same_class)
1513 {
1514 if (task_is_visible_on_current_desktop(tb, tk))
1515 {
1516 /* Count visible tasks and make the first visible task the one that is used for display. */
1517 if (tc->visible_count == 0)
1518 tc->p_task_visible = tk;
1519 tc->visible_count += 1;
1520
1521 /* Compute summary bit for urgency anywhere in the class. */
1522 if (tk->urgency)
1523 class_has_urgency = TRUE;
1524
1525 /* If there is urgency, record the currently flashing task. */
1526 if (tk->flash_timeout != 0)
1527 flashing_task = tk;
1528
1529 /* Compute the visible name. If all visible windows have the same title, use that.
1530 * Otherwise, use the class name. This follows WNCK.
1531 * Note that the visible name is not a separate string, but is set to point to one of the others. */
1532 if (tc->visible_name == NULL)
1533 tc->visible_name = tk->name;
1534 else if ((tc->visible_name != tc->res_class)
1535 && (tc->visible_name != NULL) && (tk->name != NULL)
1536 && (strcmp(tc->visible_name, tk->name) != 0))
1537 tc->visible_name = tc->res_class;
1538 }
1539 }
1540
1541 /* Transfer the flash timeout to the visible task. */
1542 if (class_has_urgency)
1543 {
1544 if (flashing_task == NULL)
1545 {
1546 /* Set the flashing context and flash the window immediately. */
1547 tc->p_task_visible->flash_state = TRUE;
1548 flash_window_update(tc->p_task_visible);
1549
1550 /* Set the timer, since none is set. */
1551 set_timer_on_task(tc->p_task_visible);
1552 }
1553 else if (flashing_task != tc->p_task_visible)
1554 {
1555 /* Reset the timer on the new representative.
1556 * There will be a slight hiccup on the flash cadence. */
1557 g_source_remove(flashing_task->flash_timeout);
1558 flashing_task->flash_timeout = 0;
1559 tc->p_task_visible->flash_state = flashing_task->flash_state;
1560 flashing_task->flash_state = FALSE;
1561 set_timer_on_task(tc->p_task_visible);
1562 }
1563 }
1564 else
1565 {
1566 /* No task has urgency. Cancel the timer if one is set. */
1567 if (flashing_task != NULL)
1568 {
1569 g_source_remove(flashing_task->flash_timeout);
1570 flashing_task->flash_state = FALSE;
1571 flashing_task->flash_timeout = 0;
1572 }
1573 }
1574 }
1575
1576 /* Recompute the visible task for all classes when the desktop changes. */
1577 static void recompute_group_visibility_on_current_desktop(LaunchTaskBarPlugin * tb)
1578 {
1579 TaskClass * tc;
1580 for (tc = tb->p_taskclass_list; tc != NULL; tc = tc->p_taskclass_flink)
1581 {
1582 recompute_group_visibility_for_class(tb, tc);
1583 }
1584 }
1585
1586 /* Draw the label and tooltip on a taskbar button. */
1587 static void task_draw_label(Task * tk)
1588 {
1589 TaskClass * tc = tk->p_taskclass;
1590 gboolean bold_style = (((tk->entered_state) || (tk->flash_state)) && (tk->tb->flat_button));
1591 char *label;
1592
1593 if ((tk->tb->grouped_tasks) && (tc != NULL) && (tc->p_task_visible == tk) && (tc->visible_count > 1))
1594 {
1595 label = g_strdup_printf("(%d) %s", tc->visible_count, tc->visible_name);
1596 }
1597 else
1598 {
1599 label = g_strdup(tk->iconified ? tk->name_iconified : tk->name);
1600 }
1601
1602 if (tk->tb->tooltips)
1603 gtk_widget_set_tooltip_text(tk->button, label);
1604
1605 lxpanel_draw_label_text(tk->tb->panel, tk->label, label, bold_style, 1,
1606 tk->tb->flat_button);
1607
1608 g_free(label);
1609 }
1610
1611 /* Determine if a task is visible. */
1612 static gboolean task_is_visible(LaunchTaskBarPlugin * tb, Task * tk)
1613 {
1614 /* Not visible due to grouping. */
1615 if ((tb->grouped_tasks) && (tk->p_taskclass != NULL) && (tk->p_taskclass->p_task_visible != tk))
1616 return FALSE;
1617
1618 /* Not on same monitor */
1619 if (tb->same_monitor_only && panel_get_monitor(tb->panel) != tk->monitor)
1620 return FALSE;
1621
1622 /* Desktop placement. */
1623 return task_is_visible_on_current_desktop(tb, tk);
1624 }
1625
1626 /* Redraw a task button. */
1627 static void task_button_redraw(Task * tk, LaunchTaskBarPlugin * tb)
1628 {
1629 if (task_is_visible(tb, tk))
1630 {
1631 task_draw_label(tk);
1632 gtk_widget_set_visible(tk->button, TRUE);
1633 }
1634 else
1635 gtk_widget_set_visible(tk->button, FALSE);
1636 }
1637
1638 /* Redraw all tasks in the taskbar. */
1639 static void taskbar_redraw(LaunchTaskBarPlugin * tb)
1640 {
1641 Task * tk;
1642 for (tk = tb->p_task_list; tk != NULL; tk = tk->p_task_flink_xwid)
1643 task_button_redraw(tk, tb);
1644 }
1645
1646 /* Determine if a task should be visible given its NET_WM_STATE. */
1647 static gboolean accept_net_wm_state(NetWMState * nws)
1648 {
1649 return ( ! (nws->skip_taskbar));
1650 }
1651
1652 /* Determine if a task should be visible given its NET_WM_WINDOW_TYPE. */
1653 static gboolean accept_net_wm_window_type(NetWMWindowType * nwwt)
1654 {
1655 return ( ! ((nwwt->desktop) || (nwwt->dock) || (nwwt->splash)));
1656 }
1657
1658 /* Free the names associated with a task. */
1659 static void task_free_names(Task * tk)
1660 {
1661 TaskClass * tc = tk->p_taskclass;
1662
1663 if (tc != NULL && tk->name != NULL)
1664 /* Reset the name from class */
1665 if (tc->visible_name == tk->name)
1666 tc->visible_name = tc->res_class;
1667 g_free(tk->name);
1668 g_free(tk->name_iconified);
1669 tk->name = tk->name_iconified = NULL;
1670 }
1671
1672 /* Set the names associated with a task.
1673 * This is expected to be the same as the title the window manager is displaying. */
1674 static void task_set_names(Task * tk, Atom source)
1675 {
1676 char * name = NULL;
1677
1678 /* Try _NET_WM_VISIBLE_NAME, which supports UTF-8.
1679 * If it is set, the window manager is displaying it as the window title. */
1680 if ((source == None) || (source == a_NET_WM_VISIBLE_NAME))
1681 {
1682 name = get_utf8_property(tk->win, a_NET_WM_VISIBLE_NAME);
1683 if (name != NULL)
1684 tk->name_source = a_NET_WM_VISIBLE_NAME;
1685 }
1686
1687 /* Try _NET_WM_NAME, which supports UTF-8, but do not overwrite _NET_WM_VISIBLE_NAME. */
1688 if ((name == NULL)
1689 && ((source == None) || (source == a_NET_WM_NAME))
1690 && ((tk->name_source == None) || (tk->name_source == a_NET_WM_NAME) || (tk->name_source == XA_WM_NAME)))
1691 {
1692 name = get_utf8_property(tk->win, a_NET_WM_NAME);
1693 if (name != NULL)
1694 tk->name_source = a_NET_WM_NAME;
1695 }
1696
1697 /* Try WM_NAME, which supports only ISO-8859-1, but do not overwrite _NET_WM_VISIBLE_NAME or _NET_WM_NAME. */
1698 if ((name == NULL)
1699 && ((source == None) || (source == XA_WM_NAME))
1700 && ((tk->name_source == None) || (tk->name_source == XA_WM_NAME)))
1701 {
1702 name = get_textproperty(tk->win, XA_WM_NAME);
1703 if (name != NULL)
1704 tk->name_source = XA_WM_NAME;
1705 }
1706
1707 /* Set the name into the task context, and also on the tooltip. */
1708 if (name != NULL)
1709 {
1710 task_free_names(tk);
1711 tk->name = name;
1712 tk->name_iconified = g_strdup_printf("[%s]", name);
1713
1714 /* Redraw the button. */
1715 task_button_redraw(tk, tk->tb);
1716 }
1717 }
1718
1719 /* Unlink a task from the class list because its class changed or it was deleted. */
1720 static void task_unlink_class(Task * tk)
1721 {
1722 TaskClass * tc = tk->p_taskclass;
1723 if (tc != NULL)
1724 {
1725 /* Reset the name from class */
1726 if (tc->visible_name == tk->name)
1727 tc->visible_name = tc->res_class;
1728
1729 /* Action in Launchbar after class removed */
1730 launchbar_update_after_taskbar_class_removed(tk->tb, tk);
1731
1732 /* Remove from per-class task list. */
1733 if (tc->p_task_head == tk)
1734 {
1735 /* Removing the head of the list. This causes a new task to be the visible task, so we redraw. */
1736 tc->p_task_head = tk->p_task_flink_same_class;
1737 if (tc->p_task_head != NULL)
1738 task_button_redraw(tc->p_task_head, tk->tb);
1739 }
1740 else
1741 {
1742 /* Locate the task and its predecessor in the list and then remove it. For safety, ensure it is found. */
1743 Task * tk_pred = NULL;
1744 Task * tk_cursor;
1745 for (
1746 tk_cursor = tc->p_task_head;
1747 ((tk_cursor != NULL) && (tk_cursor != tk));
1748 tk_pred = tk_cursor, tk_cursor = tk_cursor->p_task_flink_same_class) ;
1749 if (tk_cursor == tk)
1750 tk_pred->p_task_flink_same_class = tk->p_task_flink_same_class;
1751 }
1752 tk->p_task_flink_same_class = NULL;
1753 tk->p_taskclass = NULL;
1754
1755 /* Recompute group visibility. */
1756 recompute_group_visibility_for_class(tk->tb, tc);
1757 }
1758 }
1759
1760 /* Enter class with specified name. */
1761 static TaskClass * taskbar_enter_res_class(LaunchTaskBarPlugin * tb, char * res_class, gboolean * p_name_consumed)
1762 {
1763 /* Find existing entry or insertion point. */
1764 *p_name_consumed = FALSE;
1765 TaskClass * tc_pred = NULL;
1766 TaskClass * tc;
1767 for (tc = tb->p_taskclass_list; tc != NULL; tc_pred = tc, tc = tc->p_taskclass_flink)
1768 {
1769 int status = strcmp(res_class, tc->res_class);
1770 if (status == 0)
1771 return tc;
1772 if (status < 0)
1773 break;
1774 }
1775
1776 /* Insert new entry. */
1777 tc = g_new0(TaskClass, 1);
1778 tc->res_class = res_class;
1779 *p_name_consumed = TRUE;
1780 if (tc_pred == NULL)
1781 {
1782 tc->p_taskclass_flink = tb->p_taskclass_list;
1783 tb->p_taskclass_list = tc;
1784 }
1785 else
1786 {
1787 tc->p_taskclass_flink = tc_pred->p_taskclass_flink;
1788 tc_pred->p_taskclass_flink = tc;
1789 }
1790 return tc;
1791 }
1792
1793 /* Set the class associated with a task. */
1794 static void task_set_class(Task * tk)
1795 {
1796 /* Read the WM_CLASS property. */
1797 XClassHint ch;
1798 ch.res_name = NULL;
1799 ch.res_class = NULL;
1800 XGetClassHint(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), tk->win, &ch);
1801
1802 /* If the res_name was returned, free it. We make no use of it at this time. */
1803 if (ch.res_name != NULL)
1804 {
1805 XFree(ch.res_name);
1806 }
1807
1808 /* If the res_class was returned, process it.
1809 * This identifies the application that created the window and is the basis for taskbar grouping. */
1810 if (ch.res_class != NULL)
1811 {
1812 /* Convert the class to UTF-8 and enter it in the class table. */
1813 gchar * res_class = g_locale_to_utf8(ch.res_class, -1, NULL, NULL, NULL);
1814 if (res_class != NULL)
1815 {
1816 gboolean name_consumed;
1817 TaskClass * tc = taskbar_enter_res_class(tk->tb, res_class, &name_consumed);
1818 if ( ! name_consumed) g_free(res_class);
1819
1820 /* If the task changed class, update data structures. */
1821 TaskClass * old_tc = tk->p_taskclass;
1822 if (old_tc != tc)
1823 {
1824 /* Unlink from previous class, if any. */
1825 task_unlink_class(tk);
1826
1827 /* Add to end of per-class task list. Do this to keep the popup menu in order of creation. */
1828 if (tc->p_task_head == NULL)
1829 tc->p_task_head = tk;
1830 else
1831 {
1832 Task * tk_pred;
1833 for (tk_pred = tc->p_task_head; tk_pred->p_task_flink_same_class != NULL; tk_pred = tk_pred->p_task_flink_same_class) ;
1834 tk_pred->p_task_flink_same_class = tk;
1835 task_button_redraw(tk, tk->tb);
1836 }
1837 tk->p_taskclass = tc;
1838
1839 /* Recompute group visibility. */
1840 recompute_group_visibility_for_class(tk->tb, tc);
1841
1842 /* Action in Launchbar after class added */
1843 launchbar_update_after_taskbar_class_added(tk->tb, tk);
1844 }
1845 }
1846 XFree(ch.res_class);
1847 }
1848 }
1849
1850 /* Look up a task in the task list. */
1851 static Task * task_lookup(LaunchTaskBarPlugin * tb, Window win)
1852 {
1853 Task * tk;
1854 for (tk = tb->p_task_list; tk != NULL; tk = tk->p_task_flink_xwid)
1855 {
1856 if (tk->win == win)
1857 return tk;
1858 if (tk->win > win)
1859 break;
1860 }
1861 return NULL;
1862 }
1863
1864 /* Delete a task and optionally unlink it from the task list. */
1865 static void task_delete(LaunchTaskBarPlugin * tb, Task * tk, gboolean unlink, gboolean remove)
1866 {
1867 /* If we think this task had focus, remove that. */
1868 if (tb->focused == tk)
1869 tb->focused = NULL;
1870
1871 /* If there is an urgency timeout, remove it. */
1872 if (tk->flash_timeout != 0) {
1873 g_source_remove(tk->flash_timeout);
1874 tk->flash_timeout = 0;
1875 }
1876
1877 /* Deallocate structures. */
1878 if (remove)
1879 {
1880 gtk_widget_destroy(tk->button);
1881 task_unlink_class(tk);
1882 }
1883 task_free_names(tk);
1884 g_free(tk->exec_bin);
1885
1886 /* If requested, unlink the task from the task list.
1887 * If not requested, the caller will do this. */
1888 if (unlink)
1889 {
1890 if (tb->p_task_list == tk)
1891 tb->p_task_list = tk->p_task_flink_xwid;
1892 else
1893 {
1894 /* Locate the task and its predecessor in the list and then remove it. For safety, ensure it is found. */
1895 Task * tk_pred = NULL;
1896 Task * tk_cursor;
1897 for (
1898 tk_cursor = tb->p_task_list;
1899 ((tk_cursor != NULL) && (tk_cursor != tk));
1900 tk_pred = tk_cursor, tk_cursor = tk_cursor->p_task_flink_xwid) ;
1901 if (tk_cursor == tk)
1902 tk_pred->p_task_flink_xwid = tk->p_task_flink_xwid;
1903 }
1904 }
1905
1906 /* Deallocate the task structure. */
1907 g_free(tk);
1908 }
1909
1910 /* Get a pixbuf from a pixmap.
1911 * Originally from libwnck, Copyright (C) 2001 Havoc Pennington. */
1912 static GdkPixbuf * _wnck_gdk_pixbuf_get_from_pixmap(Pixmap xpixmap, int width, int height)
1913 {
1914 /* Get the drawable. */
1915 #if GTK_CHECK_VERSION(2, 24, 0)
1916 GdkDrawable * drawable = gdk_x11_window_lookup_for_display(gdk_display_get_default(), xpixmap);
1917 #else
1918 GdkDrawable * drawable = gdk_xid_table_lookup(xpixmap);
1919 #endif
1920 if (drawable != NULL)
1921 g_object_ref(G_OBJECT(drawable));
1922 else
1923 drawable = gdk_pixmap_foreign_new(xpixmap);
1924
1925 GdkColormap * colormap = NULL;
1926 GdkPixbuf * retval = NULL;
1927 if (drawable != NULL)
1928 {
1929 /* Get the colormap.
1930 * If the drawable has no colormap, use no colormap or the system colormap as recommended in the documentation of gdk_drawable_get_colormap. */
1931 colormap = gdk_drawable_get_colormap(drawable);
1932 gint depth = gdk_drawable_get_depth(drawable);
1933 if (colormap != NULL)
1934 g_object_ref(G_OBJECT(colormap));
1935 else if (depth == 1)
1936 colormap = NULL;
1937 else
1938 {
1939 #if GTK_CHECK_VERSION(2, 24, 0)
1940 colormap = gdk_screen_get_system_colormap(gdk_window_get_screen(drawable));
1941 #else
1942 colormap = gdk_screen_get_system_colormap(gdk_drawable_get_screen(drawable));
1943 #endif
1944 g_object_ref(G_OBJECT(colormap));
1945 }
1946
1947 /* Be sure we aren't going to fail due to visual mismatch. */
1948 #if GTK_CHECK_VERSION(2,22,0)
1949 if ((colormap != NULL) && (gdk_visual_get_depth(gdk_colormap_get_visual(colormap)) != depth))
1950 #else
1951 if ((colormap != NULL) && (gdk_colormap_get_visual(colormap)->depth != depth))
1952 #endif
1953 {
1954 g_object_unref(G_OBJECT(colormap));
1955 colormap = NULL;
1956 }
1957
1958 /* Do the major work. */
1959 retval = gdk_pixbuf_get_from_drawable(NULL, drawable, colormap, 0, 0, 0, 0, width, height);
1960 }
1961
1962 /* Clean up and return. */
1963 if (colormap != NULL)
1964 g_object_unref(G_OBJECT(colormap));
1965 if (drawable != NULL)
1966 g_object_unref(G_OBJECT(drawable));
1967 return retval;
1968 }
1969
1970 /* Apply a mask to a pixbuf.
1971 * Originally from libwnck, Copyright (C) 2001 Havoc Pennington. */
1972 static GdkPixbuf * apply_mask(GdkPixbuf * pixbuf, GdkPixbuf * mask)
1973 {
1974 /* Initialize. */
1975 int w = MIN(gdk_pixbuf_get_width(mask), gdk_pixbuf_get_width(pixbuf));
1976 int h = MIN(gdk_pixbuf_get_height(mask), gdk_pixbuf_get_height(pixbuf));
1977 GdkPixbuf * with_alpha = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
1978 guchar * dst = gdk_pixbuf_get_pixels(with_alpha);
1979 guchar * src = gdk_pixbuf_get_pixels(mask);
1980 int dst_stride = gdk_pixbuf_get_rowstride(with_alpha);
1981 int src_stride = gdk_pixbuf_get_rowstride(mask);
1982
1983 /* Loop to do the work. */
1984 int i;
1985 for (i = 0; i < h; i += 1)
1986 {
1987 int j;
1988 for (j = 0; j < w; j += 1)
1989 {
1990 guchar * s = src + i * src_stride + j * 3;
1991 guchar * d = dst + i * dst_stride + j * 4;
1992
1993 /* s[0] == s[1] == s[2], they are 255 if the bit was set, 0 otherwise. */
1994 d[3] = ((s[0] == 0) ? 0 : 255); /* 0 = transparent, 255 = opaque */
1995 }
1996 }
1997
1998 return with_alpha;
1999 }
2000
2001 /* Get an icon from the window manager for a task, and scale it to a specified size. */
2002 static GdkPixbuf * get_wm_icon(Window task_win, guint required_width, guint required_height, Atom source, Atom * current_source)
2003 {
2004 /* The result. */
2005 GdkPixbuf * pixmap = NULL;
2006 Atom possible_source = None;
2007 int result = -1;
2008 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
2009
2010 if ((source == None) || (source == a_NET_WM_ICON))
2011 {
2012 /* Important Notes:
2013 * According to freedesktop.org document:
2014 * http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html#id2552223
2015 * _NET_WM_ICON contains an array of 32-bit packed CARDINAL ARGB.
2016 * However, this is incorrect. Actually it's an array of long integers.
2017 * Toolkits like gtk+ use unsigned long here to store icons.
2018 * Besides, according to manpage of XGetWindowProperty, when returned format,
2019 * is 32, the property data will be stored as an array of longs
2020 * (which in a 64-bit application will be 64-bit values that are
2021 * padded in the upper 4 bytes).
2022 */
2023
2024 /* Get the window property _NET_WM_ICON, if possible. */
2025 Atom type = None;
2026 int format;
2027 gulong nitems;
2028 gulong bytes_after;
2029 gulong * data = NULL;
2030 result = XGetWindowProperty(
2031 xdisplay,
2032 task_win,
2033 a_NET_WM_ICON,
2034 0, G_MAXLONG,
2035 False, XA_CARDINAL,
2036 &type, &format, &nitems, &bytes_after, (void *) &data);
2037
2038 /* Inspect the result to see if it is usable. If not, and we got data, free it. */
2039 if ((type != XA_CARDINAL) || (nitems <= 0))
2040 {
2041 if (data != NULL)
2042 XFree(data);
2043 result = -1;
2044 }
2045
2046 /* If the result is usable, extract the icon from it. */
2047 if (result == Success)
2048 {
2049 /* Get the largest icon available, unless there is one that is the desired size. */
2050 /* FIXME: should we try to find an icon whose size is closest to
2051 * required_width and required_height to reduce unnecessary resizing? */
2052 gulong * pdata = data;
2053 gulong * pdata_end = data + nitems;
2054 gulong * max_icon = NULL;
2055 gulong max_w = 0;
2056 gulong max_h = 0;
2057 while ((pdata + 2) < pdata_end)
2058 {
2059 /* Extract the width and height. */
2060 guint w = pdata[0];
2061 guint h = pdata[1];
2062 gulong size = w * h;
2063 pdata += 2;
2064
2065 /* Bounds check the icon. */
2066 if (pdata + size > pdata_end)
2067 break;
2068
2069 /* Rare special case: the desired size is the same as icon size. */
2070 if ((required_width == w) && (required_height == h))
2071 {
2072 max_icon = pdata;
2073 max_w = w;
2074 max_h = h;
2075 break;
2076 }
2077
2078 /* If the icon is the largest so far, capture it. */
2079 if ((w > max_w) && (h > max_h))
2080 {
2081 max_icon = pdata;
2082 max_w = w;
2083 max_h = h;
2084 }
2085 pdata += size;
2086 }
2087
2088 /* If an icon was extracted, convert it to a pixbuf.
2089 * Its size is max_w and max_h. */
2090 if (max_icon != NULL)
2091 {
2092 /* Allocate enough space for the pixel data. */
2093 gulong len = max_w * max_h;
2094 guchar * pixdata = g_new(guchar, len * 4);
2095
2096 /* Loop to convert the pixel data. */
2097 guchar * p = pixdata;
2098 gulong i;
2099 for (i = 0; i < len; p += 4, i += 1)
2100 {
2101 guint argb = max_icon[i];
2102 guint rgba = (argb << 8) | (argb >> 24);
2103 p[0] = rgba >> 24;
2104 p[1] = (rgba >> 16) & 0xff;
2105 p[2] = (rgba >> 8) & 0xff;
2106 p[3] = rgba & 0xff;
2107 }
2108
2109 /* Initialize a pixmap with the pixel data. */
2110 pixmap = gdk_pixbuf_new_from_data(
2111 pixdata,
2112 GDK_COLORSPACE_RGB,
2113 TRUE, 8, /* has_alpha, bits_per_sample */
2114 max_w, max_h, max_w * 4,
2115 (GdkPixbufDestroyNotify) g_free,
2116 NULL);
2117 possible_source = a_NET_WM_ICON;
2118 }
2119 else
2120 result = -1;
2121
2122 /* Free the X property data. */
2123 XFree(data);
2124 }
2125 }
2126
2127 /* No icon available from _NET_WM_ICON. Next try WM_HINTS, but do not overwrite _NET_WM_ICON. */
2128 if ((result != Success) && (*current_source != a_NET_WM_ICON)
2129 && ((source == None) || (source != a_NET_WM_ICON)))
2130 {
2131 XWMHints * hints = XGetWMHints(xdisplay, task_win);
2132 result = (hints != NULL) ? Success : -1;
2133 Pixmap xpixmap = None;
2134 Pixmap xmask = None;
2135
2136 if (result == Success)
2137 {
2138 /* WM_HINTS is available. Extract the X pixmap and mask. */
2139 if ((hints->flags & IconPixmapHint))
2140 xpixmap = hints->icon_pixmap;
2141 if ((hints->flags & IconMaskHint))
2142 xmask = hints->icon_mask;
2143 XFree(hints);
2144 if (xpixmap != None)
2145 {
2146 result = Success;
2147 possible_source = XA_WM_HINTS;
2148 }
2149 else
2150 result = -1;
2151 }
2152
2153 if (result != Success)
2154 {
2155 /* No icon available from _NET_WM_ICON or WM_HINTS. Next try KWM_WIN_ICON. */
2156 Atom type = None;
2157 int format;
2158 gulong nitems;
2159 gulong bytes_after;
2160 Pixmap *icons = NULL;
2161 Atom kwin_win_icon_atom = gdk_x11_get_xatom_by_name("KWM_WIN_ICON");
2162 result = XGetWindowProperty(
2163 xdisplay,
2164 task_win,
2165 kwin_win_icon_atom,
2166 0, G_MAXLONG,
2167 False, kwin_win_icon_atom,
2168 &type, &format, &nitems, &bytes_after, (void *) &icons);
2169
2170 /* Inspect the result to see if it is usable. If not, and we got data, free it. */
2171 if (type != kwin_win_icon_atom)
2172 {
2173 if (icons != NULL)
2174 XFree(icons);
2175 result = -1;
2176 }
2177
2178 /* If the result is usable, extract the X pixmap and mask from it. */
2179 if (result == Success)
2180 {
2181 xpixmap = icons[0];
2182 xmask = icons[1];
2183 if (xpixmap != None)
2184 {
2185 result = Success;
2186 possible_source = kwin_win_icon_atom;
2187 }
2188 else
2189 result = -1;
2190 }
2191 }
2192
2193 /* If we have an X pixmap, get its geometry.*/
2194 unsigned int w, h;
2195 if (result == Success)
2196 {
2197 Window unused_win;
2198 int unused;
2199 unsigned int unused_2;
2200 result = XGetGeometry(
2201 xdisplay, xpixmap,
2202 &unused_win, &unused, &unused, &w, &h, &unused_2, &unused_2) ? Success : -1;
2203 }
2204
2205 /* If we have an X pixmap and its geometry, convert it to a GDK pixmap. */
2206 if (result == Success)
2207 {
2208 pixmap = _wnck_gdk_pixbuf_get_from_pixmap(xpixmap, w, h);
2209 result = ((pixmap != NULL) ? Success : -1);
2210 }
2211
2212 /* If we have success, see if the result needs to be masked.
2213 * Failures here are implemented as nonfatal. */
2214 if ((result == Success) && (xmask != None))
2215 {
2216 Window unused_win;
2217 int unused;
2218 unsigned int unused_2;
2219 if (XGetGeometry(
2220 xdisplay, xmask,
2221 &unused_win, &unused, &unused, &w, &h, &unused_2, &unused_2))
2222 {
2223 /* Convert the X mask to a GDK pixmap. */
2224 GdkPixbuf * mask = _wnck_gdk_pixbuf_get_from_pixmap(xmask, w, h);
2225 if (mask != NULL)
2226 {
2227 /* Apply the mask. */
2228 GdkPixbuf * masked_pixmap = apply_mask(pixmap, mask);
2229 g_object_unref(G_OBJECT(pixmap));
2230 g_object_unref(G_OBJECT(mask));
2231 pixmap = masked_pixmap;
2232 }
2233 }
2234 }
2235 }
2236
2237 /* If we got a pixmap, scale it and return it. */
2238 if (pixmap == NULL)
2239 return NULL;
2240 else
2241 {
2242 GdkPixbuf * ret = gdk_pixbuf_scale_simple(pixmap, required_width, required_height, GDK_INTERP_TILES);
2243 g_object_unref(pixmap);
2244 *current_source = possible_source;
2245 return ret;
2246 }
2247 }
2248
2249 /* Update the icon of a task. */
2250 static GdkPixbuf * task_update_icon(LaunchTaskBarPlugin * tb, Task * tk, Atom source)
2251 {
2252 /* Get the icon from the window's hints. */
2253 GdkPixbuf * pixbuf = get_wm_icon(tk->win, MAX(0, tb->icon_size - ICON_BUTTON_TRIM),
2254 MAX(0, tb->icon_size - ICON_BUTTON_TRIM),
2255 source, &tk->image_source);
2256
2257 /* If that fails, and we have no other icon yet, return the fallback icon. */
2258 if ((pixbuf == NULL)
2259 && ((source == None) || (tk->image_source == None)))
2260 {
2261 /* Establish the fallback task icon. This is used when no other icon is available. */
2262 if (tb->fallback_pixbuf == NULL)
2263 tb->fallback_pixbuf = gdk_pixbuf_new_from_xpm_data((const char **) icon_xpm);
2264 g_object_ref(tb->fallback_pixbuf);
2265 pixbuf = tb->fallback_pixbuf;
2266 }
2267
2268 /* Return what we have. This may be NULL to indicate that no change should be made to the icon. */
2269 return pixbuf;
2270 }
2271
2272 /* Timer expiration for urgency notification. Also used to draw the button in setting and clearing urgency. */
2273 static void flash_window_update(Task * tk)
2274 {
2275 /* Set state on the button and redraw. */
2276 if ( ! tk->tb->flat_button)
2277 gtk_widget_set_state(tk->button, tk->flash_state ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
2278 task_draw_label(tk);
2279
2280 /* Complement the flashing context. */
2281 tk->flash_state = ! tk->flash_state;
2282 }
2283
2284 static gboolean flash_window_timeout(gpointer tk)
2285 {
2286 if (g_source_is_destroyed(g_main_current_source()))
2287 return FALSE;
2288 flash_window_update(tk);
2289 return TRUE;
2290 }
2291
2292 /* Set urgency notification. */
2293 static void task_set_urgency(Task * tk)
2294 {
2295 LaunchTaskBarPlugin * tb = tk->tb;
2296 TaskClass * tc = tk->p_taskclass;
2297 if ((tb->grouped_tasks) && (tc != NULL) && (tc->visible_count > 1))
2298 recompute_group_visibility_for_class(tk->tb, tk->p_taskclass);
2299 else
2300 {
2301 /* Set the flashing context and flash the window immediately. */
2302 tk->flash_state = TRUE;
2303 flash_window_update(tk);
2304
2305 /* Set the timer if none is set. */
2306 if (tk->flash_timeout == 0)
2307 set_timer_on_task(tk);
2308 }
2309 }
2310
2311 /* Clear urgency notification. */
2312 static void task_clear_urgency(Task * tk)
2313 {
2314 LaunchTaskBarPlugin * tb = tk->tb;
2315 TaskClass * tc = tk->p_taskclass;
2316 if ((tb->grouped_tasks) && (tc != NULL) && (tc->visible_count > 1))
2317 recompute_group_visibility_for_class(tk->tb, tk->p_taskclass);
2318 else
2319 {
2320 /* Remove the timer if one is set. */
2321 if (tk->flash_timeout != 0)
2322 {
2323 g_source_remove(tk->flash_timeout);
2324 tk->flash_timeout = 0;
2325 }
2326
2327 /* Clear the flashing context and unflash the window immediately. */
2328 tk->flash_state = FALSE;
2329 flash_window_update(tk);
2330 tk->flash_state = FALSE;
2331 }
2332 }
2333
2334 /* Do the proper steps to raise a window.
2335 * This means removing it from iconified state and bringing it to the front.
2336 * We also switch the active desktop and viewport if needed. */
2337 static void task_raise_window(Task * tk, guint32 time)
2338 {
2339 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
2340
2341 /* Change desktop if needed. */
2342 if ((tk->desktop != ALL_WORKSPACES) && (tk->desktop != tk->tb->current_desktop))
2343 Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, tk->desktop, 0, 0, 0, 0);
2344
2345 /* Evaluate use_net_active if not yet done. */
2346 if ( ! tk->tb->net_active_checked)
2347 {
2348 LaunchTaskBarPlugin * tb = tk->tb;
2349 GdkAtom net_active_atom = gdk_x11_xatom_to_atom(a_NET_ACTIVE_WINDOW);
2350 tb->use_net_active = gdk_x11_screen_supports_net_wm_hint(tb->screen, net_active_atom);
2351 tb->net_active_checked = TRUE;
2352 }
2353
2354 /* Raise the window. We can use NET_ACTIVE_WINDOW if the window manager supports it.
2355 * Otherwise, do it the old way with XMapRaised and XSetInputFocus. */
2356 if (tk->tb->use_net_active)
2357 Xclimsg(tk->win, a_NET_ACTIVE_WINDOW, 2, time, 0, 0, 0);
2358 else
2359 {
2360 #if GTK_CHECK_VERSION(2, 24, 0)
2361 GdkWindow * gdkwindow = gdk_x11_window_lookup_for_display(gdk_display_get_default(), tk->win);
2362 #else
2363 GdkWindow * gdkwindow = gdk_xid_table_lookup(tk->win);
2364 #endif
2365 if (gdkwindow != NULL)
2366 gdk_window_show(gdkwindow);
2367 else
2368 XMapRaised(xdisplay, tk->win);
2369
2370 /* There is a race condition between the X server actually executing the XMapRaised and this code executing XSetInputFocus.
2371 * If the window is not viewable, the XSetInputFocus will fail with BadMatch. */
2372 XWindowAttributes attr;
2373 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
2374 XGetWindowAttributes(xdisplay, tk->win, &attr);
2375 if (attr.map_state == IsViewable)
2376 XSetInputFocus(xdisplay, tk->win, RevertToNone, time);
2377 }
2378
2379 /* Change viewport if needed. */
2380 XWindowAttributes xwa;
2381 XGetWindowAttributes(xdisplay, tk->win, &xwa);
2382 Xclimsg(tk->win, a_NET_DESKTOP_VIEWPORT, xwa.x, xwa.y, 0, 0, 0);
2383 }
2384
2385 /* Position-calculation callback for grouped-task and window-management popup menu. */
2386 static void taskbar_popup_set_position(GtkWidget * menu, gint * px, gint * py, gboolean * push_in, gpointer data)
2387 {
2388 Task * tk = (Task *) data;
2389
2390 /* Determine the coordinates. */
2391 lxpanel_plugin_popup_set_position_helper(tk->tb->panel, tk->button, menu,
2392 px, py);
2393 *push_in = TRUE;
2394 }
2395
2396 /* Handler for "activate" event from "close all windows" menu*/
2397 static void taskbar_close_all_windows (GtkWidget * widget, Task * tk )
2398 {
2399 Task * tk_cursor;
2400 for (tk_cursor = tk->p_taskclass->p_task_head; tk_cursor != NULL;
2401 tk_cursor = tk_cursor->p_task_flink_same_class)
2402 {
2403 if (task_is_visible_on_current_desktop(tk->tb, tk_cursor))
2404 {
2405 Xclimsgwm(tk_cursor->win, a_WM_PROTOCOLS, a_WM_DELETE_WINDOW);
2406 }
2407 }
2408 task_group_menu_destroy(tk->tb);
2409 }
2410
2411 /* Remove the grouped-task popup menu from the screen. */
2412 static void task_group_menu_destroy(LaunchTaskBarPlugin * tb)
2413 {
2414 if (tb->group_menu != NULL)
2415 {
2416 gtk_widget_destroy(tb->group_menu);
2417 tb->group_menu = NULL;
2418 }
2419 }
2420
2421 /* Handler for "button-press-event" event from taskbar button,
2422 * or "activate" event from grouped-task popup menu item. */
2423 static gboolean taskbar_task_control_event(GtkWidget * widget, GdkEventButton * event, Task * tk, gboolean popup_menu)
2424 {
2425 LaunchTaskBarPlugin * tb = tk->tb;
2426 TaskClass * tc = tk->p_taskclass;
2427 if ((tb->grouped_tasks) && (tc != NULL) && (tc->visible_count > 1) && (GTK_IS_BUTTON(widget)))
2428 {
2429 /* This is grouped-task representative, meaning that there is a class
2430 * with at least two windows. */
2431 GtkWidget * menu = NULL;
2432 if( event->button == 1 ) /* Left click */
2433 {
2434 menu = gtk_menu_new();
2435 /* Bring up a popup menu listing all the class members. */
2436 Task * tk_cursor;
2437 for (tk_cursor = tc->p_task_head; tk_cursor != NULL;
2438 tk_cursor = tk_cursor->p_task_flink_same_class)
2439 {
2440 if (task_is_visible_on_current_desktop(tb, tk_cursor))
2441 {
2442 /* The menu item has the name, or the iconified name, and
2443 * the icon of the application window. */
2444 GtkWidget * mi = gtk_image_menu_item_new_with_label(((tk_cursor->iconified) ?
2445 tk_cursor->name_iconified : tk_cursor->name));
2446 GtkWidget * im = gtk_image_new_from_pixbuf(gtk_image_get_pixbuf(
2447 GTK_IMAGE(tk_cursor->image)));
2448 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), im);
2449 g_signal_connect(mi, "button-press-event",
2450 G_CALLBACK(taskbar_popup_activate_event), (gpointer) tk_cursor);
2451 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2452 }
2453 }
2454 }
2455 else if(event->button == 3) /* Right click */
2456 {
2457 menu = gtk_menu_new();
2458 GtkWidget * mi = gtk_menu_item_new_with_mnemonic (_("_Close all windows"));
2459 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), mi);
2460 g_signal_connect( mi, "activate", G_CALLBACK(taskbar_close_all_windows), tk);
2461 }
2462
2463 /* Show the menu. Set context so we can find the menu later to dismiss it.
2464 * Use a position-calculation callback to get the menu nicely
2465 * positioned with respect to the button. */
2466 if (menu) {
2467 gtk_widget_show_all(menu);
2468 task_group_menu_destroy(tb);
2469 tb->group_menu = menu;
2470 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
2471 (GtkMenuPositionFunc) taskbar_popup_set_position, (gpointer) tk,
2472 event->button, event->time);
2473 }
2474 }
2475 else
2476 {
2477 /* Not a grouped-task representative, or entered from the grouped-task popup menu. */
2478 Task * visible_task = (((tk->p_taskclass == NULL) || ( ! tk->tb->grouped_tasks)) ? tk : tk->p_taskclass->p_task_visible);
2479 task_group_menu_destroy(tb);
2480
2481 if (event->button == 1)
2482 {
2483 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
2484 /* Left button.
2485 * If the task is iconified, raise it.
2486 * If the task is not iconified and has focus, iconify it.
2487 * If the task is not iconified and does not have focus, raise it. */
2488 if (tk->iconified)
2489 task_raise_window(tk, event->time);
2490 else if ((tk->focused) || (tk == tb->focused_previous))
2491 XIconifyWindow(xdisplay, tk->win, DefaultScreen(xdisplay));
2492 else
2493 task_raise_window(tk, event->time);
2494 }
2495 else if (event->button == 2)
2496 {
2497 /* Middle button. Toggle the shaded state of the window. */
2498 Xclimsg(tk->win, a_NET_WM_STATE,
2499 2, /* a_NET_WM_STATE_TOGGLE */
2500 a_NET_WM_STATE_SHADED,
2501 0, 0, 0);
2502 }
2503 else if(event->button == 3)
2504 {
2505 /* Right button. Bring up the window state popup menu. */
2506 tk->tb->menutask = tk;
2507 #ifndef DISABLE_MENU
2508 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)tk->tb;
2509 if(ltbp->mode == LAUNCHTASKBAR)
2510 {
2511 FmFileInfo *fi = f_find_menu_launchbutton_recursive(tk->exec_bin);
2512 LaunchButton *btn = launchbar_exec_bin_exists(ltbp, fi);
2513 /* FIXME: shouldn't we make file info at task button creation? */
2514 #ifdef DEBUG
2515 g_print("\nTB '%s' right-click, in LB: %c\n", tk->exec_bin, btn != NULL ? 'Y':'N');
2516 #endif
2517 if(btn != NULL)
2518 {
2519 gtk_widget_set_visible(ltbp->p_menuitem_lock_tbp, FALSE);
2520 gtk_widget_set_visible(ltbp->p_menuitem_unlock_tbp, TRUE);
2521 gtk_widget_set_visible(ltbp->p_menuitem_new_instance, TRUE);
2522 }
2523 else
2524 {
2525 gtk_widget_set_visible(ltbp->p_menuitem_lock_tbp, fi != NULL);
2526 gtk_widget_set_visible(ltbp->p_menuitem_unlock_tbp, FALSE);
2527 gtk_widget_set_visible(ltbp->p_menuitem_new_instance, fi != NULL);
2528 }
2529 gtk_widget_set_visible(ltbp->p_menuitem_separator, TRUE);
2530 if (fi)
2531 fm_file_info_unref(fi);
2532 }
2533 else
2534 {
2535 gtk_widget_set_visible(ltbp->p_menuitem_lock_tbp, FALSE);
2536 gtk_widget_set_visible(ltbp->p_menuitem_unlock_tbp, FALSE);
2537 gtk_widget_set_visible(ltbp->p_menuitem_new_instance, FALSE);
2538 gtk_widget_set_visible(ltbp->p_menuitem_separator, FALSE);
2539 }
2540 #endif
2541 if (tb->workspace_menu0)
2542 {
2543 GList *items = gtk_container_get_children(GTK_CONTAINER(gtk_widget_get_parent(tb->workspace_menu0)));
2544 GList *item = g_list_find(items, tb->workspace_menu0);
2545 int i;
2546 if (item != NULL) /* else error */
2547 for (i = 0; i < tb->number_of_desktops; i++, item = item->next)
2548 gtk_widget_set_sensitive(item->data, i != tk->desktop);
2549 g_list_free(items);
2550 }
2551 gtk_menu_popup(
2552 GTK_MENU(tb->menu),
2553 NULL, NULL,
2554 (GtkMenuPositionFunc) taskbar_popup_set_position, (gpointer) visible_task,
2555 0, event->time);
2556 }
2557 }
2558
2559 /* As a matter of policy, avoid showing selected or prelight states on flat buttons. */
2560 if (tb->flat_button)
2561 gtk_widget_set_state(widget, GTK_STATE_NORMAL);
2562 return TRUE;
2563 }
2564
2565 /* Handler for "button-press-event" event from taskbar button. */
2566 static gboolean taskbar_button_press_event(GtkWidget * widget, GdkEventButton * event, Task * tk)
2567 {
2568 // needed to prevent releasing focused task button
2569 return TRUE;
2570 }
2571
2572 /* Handler for "button-release-event" event from taskbar button. */
2573 static gboolean taskbar_button_release_event(GtkWidget * widget, GdkEventButton * event, Task * tk)
2574 {
2575 if (!tk->tb->dnd_task_moving)
2576 return taskbar_task_control_event(widget, event, tk, FALSE);
2577 return TRUE;
2578 }
2579
2580 /* Handler for "activate" event from grouped-task popup menu item. */
2581 static gboolean taskbar_popup_activate_event(GtkWidget * widget, GdkEventButton * event, Task * tk)
2582 {
2583 return taskbar_task_control_event(widget, event, tk, TRUE);
2584 }
2585
2586 /* Handler for "drag-motion" timeout. */
2587 static gboolean taskbar_button_drag_motion_timeout(Task * tk)
2588 {
2589 guint time;
2590 if (g_source_is_destroyed(g_main_current_source()))
2591 return FALSE;
2592 time = gtk_get_current_event_time();
2593 task_raise_window(tk, ((time != 0) ? time : CurrentTime));
2594 tk->tb->dnd_delay_timer = 0;
2595 return FALSE;
2596 }
2597
2598 /* Handler for "drag-motion" event from taskbar button. */
2599 static gboolean taskbar_button_drag_motion(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, Task * tk)
2600 {
2601 GtkWidget * drag_source = gtk_drag_get_source_widget(drag_context);
2602 if (drag_source != NULL && gtk_widget_get_parent(drag_source) == gtk_widget_get_parent(tk->button))
2603 {
2604 tk->tb->dnd_task_moving = TRUE;
2605 gdk_drag_status(drag_context, GDK_ACTION_MOVE, time);
2606 }
2607 else
2608 {
2609 /* Prevent excessive motion notification. */
2610 if (tk->tb->dnd_delay_timer == 0)
2611 tk->tb->dnd_delay_timer = g_timeout_add(DRAG_ACTIVE_DELAY, (GSourceFunc) taskbar_button_drag_motion_timeout, tk);
2612
2613 gdk_drag_status(drag_context, 0, time);
2614 }
2615 return TRUE;
2616 }
2617
2618 /* Handler for "drag-drop" event from taskbar button. */
2619 static gboolean taskbar_button_drag_drop(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, Task * tk)
2620 {
2621 tk->tb->dnd_task_moving = FALSE;
2622 GtkWidget * drag_source = gtk_drag_get_source_widget(drag_context);
2623 if (drag_source != NULL && gtk_widget_get_parent(drag_source) == gtk_widget_get_parent(tk->button))
2624 {
2625 if (drag_source != tk->button)
2626 {
2627 PanelIconGrid *ig = PANEL_ICON_GRID(tk->tb->tb_icon_grid);
2628 gint i = panel_icon_grid_get_child_position(ig, tk->button);
2629 panel_icon_grid_reorder_child(ig, drag_source, i);
2630 }
2631 gtk_drag_finish(drag_context, TRUE, TRUE, time);
2632 return TRUE;
2633 }
2634
2635 return FALSE;
2636 }
2637
2638 /* Handler for "drag-leave" event from taskbar button. */
2639 static void taskbar_button_drag_leave(GtkWidget * widget, GdkDragContext * drag_context, guint time, Task * tk)
2640 {
2641 /* Cancel the timer if set. */
2642 if (tk->tb->dnd_delay_timer != 0)
2643 {
2644 g_source_remove(tk->tb->dnd_delay_timer);
2645 tk->tb->dnd_delay_timer = 0;
2646 }
2647 return;
2648 }
2649
2650 /* Handler for "enter" event from taskbar button. This indicates that the cursor position has entered the button. */
2651 static void taskbar_button_enter(GtkWidget * widget, Task * tk)
2652 {
2653 tk->tb->dnd_task_moving = FALSE;
2654 tk->entered_state = TRUE;
2655 if (tk->tb->flat_button)
2656 gtk_widget_set_state(widget, GTK_STATE_NORMAL);
2657 task_draw_label(tk);
2658 }
2659
2660 /* Handler for "leave" event from taskbar button. This indicates that the cursor position has left the button. */
2661 static void taskbar_button_leave(GtkWidget * widget, Task * tk)
2662 {
2663 tk->entered_state = FALSE;
2664 task_draw_label(tk);
2665 }
2666
2667 /* Handler for "scroll-event" event from taskbar button. */
2668 static gboolean taskbar_button_scroll_event(GtkWidget * widget, GdkEventScroll * event, Task * tk)
2669 {
2670 LaunchTaskBarPlugin * tb = tk->tb;
2671 TaskClass * tc = tk->p_taskclass;
2672 if ((tb->use_mouse_wheel)
2673 && (( ! tb->grouped_tasks) || (tc == NULL) || (tc->visible_count == 1)))
2674 {
2675 if ((event->direction == GDK_SCROLL_UP) || (event->direction == GDK_SCROLL_LEFT))
2676 task_raise_window(tk, event->time);
2677 else
2678 {
2679 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
2680 XIconifyWindow(xdisplay, tk->win, DefaultScreen(xdisplay));
2681 }
2682 }
2683 return TRUE;
2684 }
2685
2686 /* Handler for "size-allocate" event from taskbar button. */
2687 static void taskbar_button_size_allocate(GtkWidget * btn, GtkAllocation * alloc, Task * tk)
2688 {
2689 #if GTK_CHECK_VERSION(2, 20, 0)
2690 if (gtk_widget_get_realized(btn))
2691 #else
2692 if (GTK_WIDGET_REALIZED(btn))
2693 #endif
2694 {
2695 /* Get the coordinates of the button. */
2696 int x, y;
2697 #if GTK_CHECK_VERSION(2,22,0)
2698 gdk_window_get_origin(gtk_button_get_event_window(GTK_BUTTON(btn)), &x, &y);
2699 #else
2700 gdk_window_get_origin(GTK_BUTTON(btn)->event_window, &x, &y);
2701 #endif
2702
2703
2704 /* Send a NET_WM_ICON_GEOMETRY property change on the window. */
2705 gulong data[4];
2706 data[0] = x;
2707 data[1] = y;
2708 data[2] = alloc->width;
2709 data[3] = alloc->height;
2710 XChangeProperty(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), tk->win,
2711 gdk_x11_get_xatom_by_name("_NET_WM_ICON_GEOMETRY"),
2712 XA_CARDINAL, 32, PropModeReplace, (guchar *) &data, 4);
2713 }
2714 }
2715
2716 /* Update style on the taskbar when created or after a configuration change. */
2717 static void taskbar_update_style(LaunchTaskBarPlugin * tb)
2718 {
2719 panel_icon_grid_set_geometry(PANEL_ICON_GRID(tb->tb_icon_grid),
2720 panel_get_orientation(tb->panel),
2721 ((tb->icons_only) ? tb->icon_size + ICON_ONLY_EXTRA : tb->task_width_max),
2722 tb->icon_size, tb->spacing, 0, panel_get_height(tb->panel));
2723 }
2724
2725 /* Update style on a task button when created or after a configuration change. */
2726 static void task_update_style(Task * tk, LaunchTaskBarPlugin * tb)
2727 {
2728 if (tb->icons_only)
2729 gtk_widget_hide(tk->label);
2730 else
2731 gtk_widget_show(tk->label);
2732
2733 if( tb->flat_button )
2734 {
2735 gtk_toggle_button_set_active((GtkToggleButton*)tk->button, FALSE);
2736 gtk_button_set_relief(GTK_BUTTON(tk->button), GTK_RELIEF_NONE);
2737 }
2738 else
2739 {
2740 gtk_toggle_button_set_active((GtkToggleButton*)tk->button, tk->focused);
2741 gtk_button_set_relief(GTK_BUTTON(tk->button), GTK_RELIEF_NORMAL);
2742 }
2743
2744 task_draw_label(tk);
2745 }
2746
2747 enum {
2748 TARGET_TASK_BUTTON
2749 };
2750
2751 static GtkTargetEntry task_button_target_list[] = {
2752 { "task_button", GTK_TARGET_SAME_APP, TARGET_TASK_BUTTON }
2753 };
2754
2755 static guint task_button_n_targets = G_N_ELEMENTS(task_button_target_list);
2756
2757 /* Build graphic elements needed for a task button. */
2758 static void task_build_gui(LaunchTaskBarPlugin * tb, Task * tk)
2759 {
2760 GdkDisplay *display = gdk_display_get_default();
2761 /* NOTE
2762 * 1. the extended mask is sum of taskbar and pager needs
2763 * see bug [ 940441 ] pager loose track of windows
2764 *
2765 * Do not change event mask to gtk windows spawned by this gtk client
2766 * this breaks gtk internals */
2767 #if GTK_CHECK_VERSION(2, 24, 0)
2768 if (!gdk_x11_window_lookup_for_display(display, tk->win))
2769 #else
2770 if (! gdk_window_lookup(tk->win))
2771 #endif
2772 XSelectInput(GDK_DISPLAY_XDISPLAY(display), tk->win,
2773 PropertyChangeMask | StructureNotifyMask);
2774
2775 /* Allocate a toggle button as the top level widget. */
2776 tk->button = gtk_toggle_button_new();
2777 gtk_container_set_border_width(GTK_CONTAINER(tk->button), 0);
2778 if (!tb->flat_button)
2779 gtk_widget_set_state(tk->button, GTK_STATE_NORMAL);
2780 gtk_drag_dest_set(tk->button, 0, NULL, 0, 0);
2781 gtk_drag_source_set(tk->button, GDK_BUTTON1_MASK, task_button_target_list, task_button_n_targets, GDK_ACTION_MOVE);
2782
2783 /* Connect signals to the button. */
2784 g_signal_connect(tk->button, "button-press-event", G_CALLBACK(taskbar_button_press_event), (gpointer) tk);
2785 g_signal_connect(tk->button, "button-release-event", G_CALLBACK(taskbar_button_release_event), (gpointer) tk);
2786 g_signal_connect(G_OBJECT(tk->button), "drag-motion", G_CALLBACK(taskbar_button_drag_motion), (gpointer) tk);
2787 g_signal_connect(G_OBJECT(tk->button), "drag-leave", G_CALLBACK(taskbar_button_drag_leave), (gpointer) tk);
2788 g_signal_connect(G_OBJECT(tk->button), "drag-drop", G_CALLBACK(taskbar_button_drag_drop), (gpointer) tk);
2789 g_signal_connect_after(G_OBJECT (tk->button), "enter", G_CALLBACK(taskbar_button_enter), (gpointer) tk);
2790 g_signal_connect_after(G_OBJECT (tk->button), "leave", G_CALLBACK(taskbar_button_leave), (gpointer) tk);
2791 g_signal_connect_after(G_OBJECT(tk->button), "scroll-event", G_CALLBACK(taskbar_button_scroll_event), (gpointer) tk);
2792 g_signal_connect(tk->button, "size-allocate", G_CALLBACK(taskbar_button_size_allocate), (gpointer) tk);
2793
2794 /* Create a box to contain the application icon and window title. */
2795 GtkWidget * container = gtk_hbox_new(FALSE, 1);
2796 gtk_container_set_border_width(GTK_CONTAINER(container), 0);
2797
2798 /* Create an image to contain the application icon and add it to the box. */
2799 GdkPixbuf* pixbuf = task_update_icon(tb, tk, None);
2800 tk->image = gtk_image_new_from_pixbuf(pixbuf);
2801 gtk_misc_set_padding(GTK_MISC(tk->image), 0, 0);
2802 g_object_unref(pixbuf);
2803 gtk_widget_show(tk->image);
2804 gtk_box_pack_start(GTK_BOX(container), tk->image, FALSE, FALSE, 0);
2805
2806 /* Create a label to contain the window title and add it to the box. */
2807 tk->label = gtk_label_new(NULL);
2808 gtk_misc_set_alignment(GTK_MISC(tk->label), 0.0, 0.5);
2809 gtk_label_set_ellipsize(GTK_LABEL(tk->label), PANGO_ELLIPSIZE_END);
2810 gtk_box_pack_start(GTK_BOX(container), tk->label, TRUE, TRUE, 0);
2811
2812 /* Add the box to the button. */
2813 gtk_container_add(GTK_CONTAINER(tk->button), container);
2814 gtk_container_set_border_width(GTK_CONTAINER(tk->button), 0);
2815
2816 /* Add the button to the taskbar. */
2817 gtk_container_add(GTK_CONTAINER(tb->tb_icon_grid), tk->button);
2818 gtk_widget_show_all(tk->button);
2819 gtk_widget_set_can_focus(GTK_WIDGET(tk->button),FALSE);
2820 gtk_widget_set_can_default(GTK_WIDGET(tk->button),FALSE);
2821
2822 /* Update styles on the button. */
2823 task_update_style(tk, tb);
2824
2825 /* Flash button for window with urgency hint. */
2826 if (tk->urgency)
2827 task_set_urgency(tk);
2828 }
2829
2830 /* Determine which monitor a given window is associated with */
2831 static gint get_window_monitor(Window win)
2832 {
2833 GdkDisplay *display;
2834 GdkWindow *gwin;
2835 gint m;
2836
2837 display = gdk_display_get_default();
2838 g_assert(display);
2839 gwin = gdk_x11_window_foreign_new_for_display(display,win);
2840 g_assert(gwin);
2841 m = gdk_screen_get_monitor_at_window(gdk_window_get_screen(gwin),gwin);
2842 g_object_unref(gwin);
2843 return m;
2844 }
2845
2846 /*****************************************************
2847 * handlers for NET actions *
2848 *****************************************************/
2849
2850 /* Handler for "client-list" event from root window listener. */
2851 static void taskbar_net_client_list(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2852 {
2853 LaunchTaskBarPlugin *ltbp = tb;
2854 if(ltbp->mode == LAUNCHBAR) return;
2855
2856 /* Get the NET_CLIENT_LIST property. */
2857 int client_count;
2858 Window * client_list = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_CLIENT_LIST, XA_WINDOW, &client_count);
2859 if (client_list != NULL)
2860 {
2861 /* Loop over client list, correlating it with task list. */
2862 int i;
2863 for (i = 0; i < client_count; i++)
2864 {
2865 /* Search for the window in the task list. Set up context to do an insert right away if needed. */
2866 Task * tk_pred = NULL;
2867 Task * tk_cursor;
2868 Task * tk = NULL;
2869 for (tk_cursor = tb->p_task_list; tk_cursor != NULL; tk_pred = tk_cursor, tk_cursor = tk_cursor->p_task_flink_xwid)
2870 {
2871 if (tk_cursor->win == client_list[i])
2872 {
2873 tk = tk_cursor;
2874 break;
2875 }
2876 if (tk_cursor->win > client_list[i])
2877 break;
2878 }
2879
2880 /* Task is already in task list. */
2881 if (tk != NULL)
2882 tk->present_in_client_list = TRUE;
2883
2884 /* Task is not in task list. */
2885 else
2886 {
2887 /* Evaluate window state and window type to see if it should be in task list. */
2888 NetWMWindowType nwwt;
2889 NetWMState nws;
2890 get_net_wm_state(client_list[i], &nws);
2891 get_net_wm_window_type(client_list[i], &nwwt);
2892 if ((accept_net_wm_state(&nws))
2893 && (accept_net_wm_window_type(&nwwt)))
2894 {
2895 /* Allocate and initialize new task structure. */
2896 tk = g_new0(Task, 1);
2897 tk->present_in_client_list = TRUE;
2898 tk->win = client_list[i];
2899 tk->tb = tb;
2900 tk->name_source = None;
2901 tk->image_source = None;
2902 tk->iconified = (get_wm_state(tk->win) == IconicState);
2903 tk->desktop = get_net_wm_desktop(tk->win);
2904 if (tb->same_monitor_only)
2905 tk->monitor = get_window_monitor(tk->win);
2906 if (tb->use_urgency_hint)
2907 tk->urgency = task_has_urgency(tk);
2908 task_build_gui(tb, tk);
2909 task_set_names(tk, None);
2910 task_set_class(tk);
2911
2912 /* Link the task structure into the task list. */
2913 if (tk_pred == NULL)
2914 {
2915 tk->p_task_flink_xwid = tb->p_task_list;
2916 tb->p_task_list = tk;
2917 }
2918 else
2919 {
2920 tk->p_task_flink_xwid = tk_pred->p_task_flink_xwid;
2921 tk_pred->p_task_flink_xwid = tk;
2922 }
2923 }
2924 }
2925 }
2926 XFree(client_list);
2927 }
2928
2929 /* Remove windows from the task list that are not present in the NET_CLIENT_LIST. */
2930 Task * tk_pred = NULL;
2931 Task * tk = tb->p_task_list;
2932 while (tk != NULL)
2933 {
2934 Task * tk_succ = tk->p_task_flink_xwid;
2935 if (tk->present_in_client_list)
2936 {
2937 tk->present_in_client_list = FALSE;
2938 tk_pred = tk;
2939 }
2940 else
2941 {
2942 if (tk_pred == NULL)
2943 tb->p_task_list = tk_succ;
2944 else tk_pred->p_task_flink_xwid = tk_succ;
2945 task_delete(tb, tk, FALSE, TRUE);
2946 }
2947 tk = tk_succ;
2948 }
2949
2950 /* Redraw the taskbar. */
2951 taskbar_redraw(tb);
2952 }
2953
2954 /* Handler for "current-desktop" event from root window listener. */
2955 static void taskbar_net_current_desktop(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2956 {
2957 LaunchTaskBarPlugin *ltbp = tb;
2958 if(ltbp->mode == LAUNCHBAR) return;
2959
2960 /* Store the local copy of current desktops. Redisplay the taskbar. */
2961 tb->current_desktop = get_net_current_desktop();
2962 recompute_group_visibility_on_current_desktop(tb);
2963 taskbar_redraw(tb);
2964 }
2965
2966 /* Handler for "number-of-desktops" event from root window listener. */
2967 static void taskbar_net_number_of_desktops(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2968 {
2969 LaunchTaskBarPlugin *ltbp = tb;
2970 if(ltbp->mode == LAUNCHBAR) return;
2971
2972 /* Store the local copy of number of desktops. Recompute the popup menu and redisplay the taskbar. */
2973 tb->number_of_desktops = get_net_number_of_desktops();
2974 taskbar_make_menu(tb);
2975 taskbar_redraw(tb);
2976 }
2977
2978 /* Handler for "active-window" event from root window listener. */
2979 static void taskbar_net_active_window(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2980 {
2981 LaunchTaskBarPlugin *ltbp = tb;
2982 if(ltbp->mode == LAUNCHBAR) return;
2983
2984 gboolean drop_old = FALSE;
2985 gboolean make_new = FALSE;
2986 Task * ctk = tb->focused;
2987 Task * ntk = NULL;
2988
2989 /* Get the window that has focus. */
2990 Window * f = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_ACTIVE_WINDOW, XA_WINDOW, 0);
2991 if (f == NULL)
2992 {
2993 /* No window has focus. */
2994 drop_old = TRUE;
2995 tb->focused_previous = NULL;
2996 }
2997 else
2998 {
2999 if (*f == panel_get_xwindow(tb->panel))
3000 {
3001 /* Taskbar window gained focus (this isn't supposed to be able to happen). Remember current focus. */
3002 if (ctk != NULL)
3003 {
3004 tb->focused_previous = ctk;
3005 drop_old = TRUE;
3006 }
3007 }
3008 else
3009 {
3010 /* Identify task that gained focus. */
3011 tb->focused_previous = NULL;
3012 ntk = task_lookup(tb, *f);
3013 if (ntk != ctk)
3014 {
3015 drop_old = TRUE;
3016 make_new = TRUE;
3017 }
3018 }
3019 XFree(f);
3020 }
3021
3022 /* If our idea of the current task lost focus, update data structures. */
3023 if ((ctk != NULL) && (drop_old))
3024 {
3025 ctk->focused = FALSE;
3026 tb->focused = NULL;
3027 if(!tb->flat_button) /* relieve the button if flat buttons is not used. */
3028 gtk_toggle_button_set_active((GtkToggleButton*)ctk->button, FALSE);
3029
3030 task_button_redraw(ctk, tb);
3031 }
3032
3033 /* If a task gained focus, update data structures. */
3034 if ((ntk != NULL) && (make_new))
3035 {
3036 if(!tb->flat_button) /* depress the button if flat buttons is not used. */
3037 gtk_toggle_button_set_active((GtkToggleButton*)ntk->button, TRUE);
3038 ntk->focused = TRUE;
3039 tb->focused = ntk;
3040 task_button_redraw(ntk, tb);
3041 }
3042 }
3043
3044 /* Determine if the "urgency" hint is set on a window. */
3045 static gboolean task_has_urgency(Task * tk)
3046 {
3047 gboolean result = FALSE;
3048 XWMHints * hints = (XWMHints *) get_xaproperty(tk->win, XA_WM_HINTS, XA_WM_HINTS, 0);
3049 if (hints != NULL)
3050 {
3051 if (hints->flags & XUrgencyHint)
3052 result = TRUE;
3053 XFree(hints);
3054 }
3055 return result;
3056 }
3057
3058 /* Handle PropertyNotify event.
3059 * http://tronche.com/gui/x/icccm/
3060 * http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html */
3061 static void taskbar_property_notify_event(LaunchTaskBarPlugin *tb, XEvent *ev)
3062 {
3063 /* State may be PropertyNewValue, PropertyDeleted. */
3064 if (((XPropertyEvent*) ev)->state == PropertyNewValue)
3065 {
3066 Atom at = ev->xproperty.atom;
3067 Window win = ev->xproperty.window;
3068 if (win != GDK_ROOT_WINDOW())
3069 {
3070 /* Look up task structure by X window handle. */
3071 Task * tk = task_lookup(tb, win);
3072 if (tk != NULL)
3073 {
3074 /* Install an error handler that ignores BadWindow.
3075 * We frequently get a PropertyNotify event on deleted windows. */
3076 XErrorHandler previous_error_handler = XSetErrorHandler(panel_handle_x_error_swallow_BadWindow_BadDrawable);
3077
3078 /* Dispatch on atom. */
3079 if (at == a_NET_WM_DESKTOP)
3080 {
3081 /* Window changed desktop. */
3082 tk->desktop = get_net_wm_desktop(win);
3083 taskbar_redraw(tb);
3084 }
3085 else if ((at == XA_WM_NAME) || (at == a_NET_WM_NAME) || (at == a_NET_WM_VISIBLE_NAME))
3086 {
3087 /* Window changed name. */
3088 task_set_names(tk, at);
3089 if (tk->p_taskclass != NULL)
3090 {
3091 /* A change to the window name may change the visible name of the class. */
3092 recompute_group_visibility_for_class(tb, tk->p_taskclass);
3093 if (tk->p_taskclass->p_task_visible != NULL)
3094 task_draw_label(tk->p_taskclass->p_task_visible);
3095 }
3096 }
3097 else if (at == XA_WM_CLASS)
3098 {
3099 /* Window changed class. */
3100 task_set_class(tk);
3101 taskbar_redraw(tb);
3102 }
3103 else if (at == a_WM_STATE)
3104 {
3105 /* Window changed state. */
3106 tk->iconified = (get_wm_state(win) == IconicState);
3107 task_draw_label(tk);
3108 }
3109 else if (at == XA_WM_HINTS)
3110 {
3111 /* Window changed "window manager hints".
3112 * Some windows set their WM_HINTS icon after mapping. */
3113 GdkPixbuf * pixbuf = task_update_icon(tb, tk, XA_WM_HINTS);
3114 if (pixbuf != NULL)
3115 {
3116 gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
3117 g_object_unref(pixbuf);
3118 }
3119
3120 if (tb->use_urgency_hint)
3121 {
3122 tk->urgency = task_has_urgency(tk);
3123 if (tk->urgency)
3124 task_set_urgency(tk);
3125 else
3126 task_clear_urgency(tk);
3127 }
3128 }
3129 else if (at == a_NET_WM_STATE)
3130 {
3131 /* Window changed EWMH state. */
3132 NetWMState nws;
3133 get_net_wm_state(tk->win, &nws);
3134 if ( ! accept_net_wm_state(&nws))
3135 {
3136 task_delete(tb, tk, TRUE, TRUE);
3137 taskbar_redraw(tb);
3138 }
3139 }
3140 else if (at == a_NET_WM_ICON)
3141 {
3142 /* Window changed EWMH icon. */
3143 GdkPixbuf * pixbuf = task_update_icon(tb, tk, a_NET_WM_ICON);
3144 if (pixbuf != NULL)
3145 {
3146 gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
3147 g_object_unref(pixbuf);
3148 }
3149 }
3150 else if (at == a_NET_WM_WINDOW_TYPE)
3151 {
3152 /* Window changed EWMH window type. */
3153 NetWMWindowType nwwt;
3154 get_net_wm_window_type(tk->win, &nwwt);
3155 if ( ! accept_net_wm_window_type(&nwwt))
3156 {
3157 task_delete(tb, tk, TRUE, TRUE);
3158 taskbar_redraw(tb);
3159 }
3160 }
3161 XSetErrorHandler(previous_error_handler);
3162 }
3163 }
3164 }
3165 }
3166
3167 /* Handle ConfigureNotify events */
3168 static void taskbar_configure_notify_event(LaunchTaskBarPlugin * tb, XConfigureEvent * ev)
3169 {
3170 /* If the same_monitor_only option is set and the window is on a different
3171 monitor than before, redraw the taskbar */
3172 Task *task;
3173 gint m;
3174 if(tb->same_monitor_only && ev->window != GDK_ROOT_WINDOW())
3175 {
3176 task = task_lookup(tb, ev->window);
3177 if(task)
3178 {
3179 /* Deleted windows seem to get ConfigureNotify events too. */
3180 XErrorHandler previous_error_handler = XSetErrorHandler(panel_handle_x_error_swallow_BadWindow_BadDrawable);
3181
3182 m = get_window_monitor(task->win);
3183 if(m != task->monitor)
3184 {
3185 task->monitor = m;
3186 taskbar_redraw(tb);
3187 }
3188
3189 XSetErrorHandler(previous_error_handler);
3190 }
3191 }
3192 }
3193
3194 /* GDK event filter. */
3195 static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, LaunchTaskBarPlugin * tb)
3196 {
3197 LaunchTaskBarPlugin *ltbp = tb;
3198 if(ltbp->mode == LAUNCHBAR) return GDK_FILTER_CONTINUE;
3199
3200 if (xev->type == PropertyNotify)
3201 taskbar_property_notify_event(tb, xev);
3202 else if (xev->type == ConfigureNotify)
3203 taskbar_configure_notify_event(tb, &xev->xconfigure);
3204
3205 return GDK_FILTER_CONTINUE;
3206 }
3207
3208 /* Handler for "activate" event on Raise item of right-click menu for task buttons. */
3209 static void menu_raise_window(GtkWidget * widget, LaunchTaskBarPlugin * tb)
3210 {
3211 if ((tb->menutask->desktop != ALL_WORKSPACES) && (tb->menutask->desktop != tb->current_desktop))
3212 Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, tb->menutask->desktop, 0, 0, 0, 0);
3213 XMapRaised(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), tb->menutask->win);
3214 task_group_menu_destroy(tb);
3215 }
3216
3217 /* Handler for "activate" event on Restore item of right-click menu for task buttons. */
3218 static void menu_restore_window(GtkWidget * widget, LaunchTaskBarPlugin * tb)
3219 {
3220 #if GTK_CHECK_VERSION(2, 24, 0)
3221 GdkWindow * win = gdk_x11_window_foreign_new_for_display(gdk_display_get_default(),
3222 tb->menutask->win);
3223 #else
3224 GdkWindow * win = gdk_window_foreign_new(tb->menutask->win);
3225 #endif
3226
3227 gdk_window_unmaximize(win);
3228 g_object_unref(win);
3229 task_group_menu_destroy(tb);
3230 }
3231
3232 /* Handler for "activate" event on Maximize item of right-click menu for task buttons. */
3233 static void menu_maximize_window(GtkWidget * widget, LaunchTaskBarPlugin * tb)
3234 {
3235 #if GTK_CHECK_VERSION(2, 24, 0)
3236 GdkWindow * win = gdk_x11_window_foreign_new_for_display(gdk_display_get_default(),
3237 tb->menutask->win);
3238 #else
3239 GdkWindow * win = gdk_window_foreign_new(tb->menutask->win);
3240 #endif
3241
3242 gdk_window_maximize(win);
3243 g_object_unref(win);
3244 task_group_menu_destroy(tb);
3245 }
3246
3247 /* Handler for "activate" event on Iconify item of right-click menu for task buttons. */
3248 static void menu_iconify_window(GtkWidget * widget, LaunchTaskBarPlugin * tb)
3249 {
3250 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
3251 XIconifyWindow(xdisplay, tb->menutask->win, DefaultScreen(xdisplay));
3252 task_group_menu_destroy(tb);
3253 }
3254
3255 /* Handler for "activate" event on Move to Workspace item of right-click menu for task buttons. */
3256 static void menu_move_to_workspace(GtkWidget * widget, LaunchTaskBarPlugin * tb)
3257 {
3258 int num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "num"));
3259 Xclimsg(tb->menutask->win, a_NET_WM_DESKTOP, num, 0, 0, 0, 0);
3260 task_group_menu_destroy(tb);
3261 }
3262
3263 /* Handler for "activate" event on Close item of right-click menu for task buttons. */
3264 static void menu_close_window(GtkWidget * widget, LaunchTaskBarPlugin * tb)
3265 {
3266 Xclimsgwm(tb->menutask->win, a_WM_PROTOCOLS, a_WM_DELETE_WINDOW);
3267 task_group_menu_destroy(tb);
3268 }
3269
3270 #ifndef DISABLE_MENU
3271 static void on_menuitem_lock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
3272 {
3273 FmFileInfo *fi = f_find_menu_launchbutton_recursive(tb->menutask->exec_bin);
3274 LaunchButton *btn;
3275 char *path;
3276
3277 if (fi)
3278 {
3279 /* Create a button and add settings for it */
3280 btn = launchbutton_for_file_info(tb, fi);
3281 path = fm_path_to_str(fm_file_info_get_path(fi));
3282 /* g_debug("*** path '%s'",path); */
3283 btn->settings = config_group_add_subgroup(tb->settings, "Button");
3284 config_group_set_string(btn->settings, "id", path);
3285 g_free(path);
3286 lxpanel_config_save(tb->panel);
3287 }
3288 }
3289
3290 static void on_menuitem_unlock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
3291 {
3292 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)tb;
3293 FmFileInfo *fi = f_find_menu_launchbutton_recursive(tb->menutask->exec_bin);
3294 LaunchButton *btn = launchbar_exec_bin_exists(ltbp, fi);
3295 if(btn != NULL)
3296 {
3297 launchbar_remove_button(ltbp, btn);
3298 lxpanel_config_save(tb->panel);
3299 }
3300 if (fi)
3301 fm_file_info_unref(fi);
3302 }
3303
3304 static void on_menuitem_new_instance_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
3305 {
3306 FmFileInfo *fi = f_find_menu_launchbutton_recursive(tb->menutask->exec_bin);
3307
3308 if (fi)
3309 {
3310 lxpanel_launch_path(tb->panel, fm_file_info_get_path(fi));
3311 fm_file_info_unref(fi);
3312 }
3313 }
3314 #endif
3315
3316 /* Make right-click menu for task buttons.
3317 * This depends on number of desktops and edge. */
3318 static void taskbar_make_menu(LaunchTaskBarPlugin * tb)
3319 {
3320 void (*_m_add)(GtkMenuShell *self, GtkWidget* child);
3321
3322 /* Deallocate old menu if present. */
3323 if (tb->menu != NULL)
3324 gtk_widget_destroy(tb->menu);
3325
3326 /* Allocate menu. */
3327 GtkWidget * menu = gtk_menu_new();
3328
3329 /* Add Raise menu item. */
3330 GtkWidget *mi = gtk_menu_item_new_with_mnemonic(_("_Raise"));
3331 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3332 g_signal_connect(G_OBJECT(mi), "activate", (GCallback) menu_raise_window, tb);
3333
3334 /* Add Restore menu item. */
3335 mi = gtk_menu_item_new_with_mnemonic(_("R_estore"));
3336 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3337 g_signal_connect(G_OBJECT(mi), "activate", (GCallback) menu_restore_window, tb);
3338
3339 /* Add Maximize menu item. */
3340 mi = gtk_menu_item_new_with_mnemonic(_("Ma_ximize"));
3341 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3342 g_signal_connect(G_OBJECT(mi), "activate", (GCallback) menu_maximize_window, tb);
3343
3344 /* Add Iconify menu item. */
3345 mi = gtk_menu_item_new_with_mnemonic(_("Ico_nify"));
3346 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3347 g_signal_connect(G_OBJECT(mi), "activate", (GCallback) menu_iconify_window, tb);
3348
3349 /* If multiple desktops are supported, add menu items to select them. */
3350 if (tb->number_of_desktops > 1)
3351 {
3352 char label[128];
3353
3354 /* Allocate submenu. */
3355 GtkWidget * workspace_menu = gtk_menu_new();
3356
3357 /* Loop over all desktops. */
3358 int i;
3359 for (i = 1; i <= tb->number_of_desktops; i++)
3360 {
3361 /* For the first 9 desktops, allow the desktop number as a keyboard shortcut. */
3362 if (i <= 9)
3363 {
3364 g_snprintf(label, sizeof(label), _("Workspace _%d"), i);
3365 mi = gtk_menu_item_new_with_mnemonic(label);
3366 }
3367 else
3368 {
3369 g_snprintf(label, sizeof(label), _("Workspace %d"), i);
3370 mi = gtk_menu_item_new_with_label(label);
3371 }
3372
3373 /* Set the desktop number as a property on the menu item. */
3374 g_object_set_data(G_OBJECT(mi), "num", GINT_TO_POINTER(i - 1));
3375 g_signal_connect(mi, "activate", G_CALLBACK(menu_move_to_workspace), tb);
3376 gtk_menu_shell_append(GTK_MENU_SHELL(workspace_menu), mi);
3377 if (G_UNLIKELY(tb->workspace_menu0 == NULL))
3378 tb->workspace_menu0 = mi;
3379 }
3380
3381 /* Add a separator. */
3382 gtk_menu_shell_append(GTK_MENU_SHELL(workspace_menu), gtk_separator_menu_item_new());
3383
3384 /* Add "move to all workspaces" item. This causes the window to be visible no matter what desktop is active. */
3385 mi = gtk_menu_item_new_with_mnemonic(_("_All workspaces"));
3386 g_object_set_data(G_OBJECT(mi), "num", GINT_TO_POINTER(ALL_WORKSPACES));
3387 g_signal_connect(mi, "activate", G_CALLBACK(menu_move_to_workspace), tb);
3388 gtk_menu_shell_append(GTK_MENU_SHELL(workspace_menu), mi);
3389
3390 /* Add Move to Workspace menu item as a submenu. */
3391 mi = gtk_menu_item_new_with_mnemonic(_("_Move to Workspace"));
3392 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3393 gtk_menu_item_set_submenu(GTK_MENU_ITEM(mi), workspace_menu);
3394 }
3395
3396 /* Add Close menu item. By popular demand, we place this menu item closest to the cursor. */
3397 mi = gtk_menu_item_new_with_mnemonic (_("_Close Window"));
3398 #ifndef DISABLE_MENU
3399 tb->p_menuitem_lock_tbp = gtk_menu_item_new_with_mnemonic(_("A_dd to Launcher"));
3400 tb->p_menuitem_unlock_tbp = gtk_menu_item_new_with_mnemonic(_("Rem_ove from Launcher"));
3401 tb->p_menuitem_new_instance = gtk_menu_item_new_with_mnemonic(_("_New Instance"));
3402 tb->p_menuitem_separator = gtk_separator_menu_item_new();
3403 #endif
3404
3405 if (panel_is_at_bottom(tb->panel))
3406 _m_add = gtk_menu_shell_append;
3407 else
3408 _m_add = gtk_menu_shell_prepend;
3409
3410 #ifndef DISABLE_MENU
3411 _m_add(GTK_MENU_SHELL(menu), tb->p_menuitem_separator);
3412 _m_add(GTK_MENU_SHELL(menu), tb->p_menuitem_lock_tbp);
3413 _m_add(GTK_MENU_SHELL(menu), tb->p_menuitem_unlock_tbp);
3414 _m_add(GTK_MENU_SHELL(menu), tb->p_menuitem_new_instance);
3415 #endif
3416 _m_add(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
3417 _m_add(GTK_MENU_SHELL(menu), mi);
3418
3419 g_signal_connect(G_OBJECT(mi), "activate", (GCallback)menu_close_window, tb);
3420 #ifndef DISABLE_MENU
3421 g_signal_connect(G_OBJECT(tb->p_menuitem_lock_tbp), "activate", (GCallback)on_menuitem_lock_tbp_clicked, tb);
3422 g_signal_connect(G_OBJECT(tb->p_menuitem_unlock_tbp), "activate", (GCallback)on_menuitem_unlock_tbp_clicked, tb);
3423 g_signal_connect(G_OBJECT(tb->p_menuitem_new_instance), "activate", (GCallback)on_menuitem_new_instance_clicked, tb);
3424 #endif
3425 gtk_widget_show_all(menu);
3426 tb->menu = menu;
3427 }
3428
3429 /* Handler for "window-manager-changed" event. */
3430 static void taskbar_window_manager_changed(GdkScreen * screen, LaunchTaskBarPlugin * tb)
3431 {
3432 /* Force re-evaluation of use_net_active. */
3433 tb->net_active_checked =