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