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