3a0902bbcfd3421c2521c8d91a801fa8cd27b790
[lxde/lxpanel.git] / plugins / launchtaskbar.c
1 /*
2 * Copyright (C) 2006-2009 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
3 * 2006-2008 Jim Huang <jserv.tw@gmail.com>
4 * 2008 Fred Chien <fred@lxde.org>
5 * 2009 Andrew Lee <ajqlee@debian.org>
6 * 2009 Jürgen Hötzel <juergen@archlinux.org>
7 * 2009 Ying-Chun Liu (PaulLiu) <grandpaul@gmail.com>
8 * 2009-2010 Marty Jack <martyj19@comcast.net>
9 * 2010 Julien Lavergne <julien.lavergne@gmail.com>
10 * 2011-2014 Henry Gebhardt <hsggebhardt@gmail.com>
11 * 2012 Piotr Sipika <Piotr.Sipika@gmail.com>
12 * 2012-2014 Giuseppe Penone <giuspen@gmail.com>
13 * 2013 Vincenzo di Cicco <enzodicicco@gmail.com>
14 * 2013 Rouslan <rouslan-k@users.sourceforge.net>
15 * 2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
16 * 2014 Vladimír Pýcha <vpycha@gmail.com>
17 * 2014 Raimar Bühmann <raimar@buehmann.de>
18 * 2014 Andy Balaam <axis3x3@users.sf.net>
19 * 2015 Balló György <ballogyor@gmail.com>
20 * 2015 Rafał Mużyło <galtgendo@gmail.com>
21 *
22 * This file is a part of LXPanel project.
23 *
24 * This program is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation; either version 2 of the License, or
27 * (at your option) any later version.
28 *
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
33 *
34 * You should have received a copy of the GNU General Public License
35 * along with this program; if not, write to the Free Software Foundation,
36 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
37 */
38
39 /*
40 * Started by Giuseppe Penone <giuspen@gmail.com> merging launchbar and taskbar
41 * and adding interoperability between them.
42 */
43
44 /*
45 * Taskbar plugin:
46 * 2006.09.10 modified by Hong Jen Yee (PCMan) pcman.tw (AT) gmail.com
47 * Following features are added:
48 * 1. Add XUrgencyHint support. (Flashing task bar buttons, can be disabled)
49 * 2. Raise window when files get dragged over taskbar buttons.
50 * 3. Add Restore & Maximize menu items to popup menu of task bar buttons.
51 */
52
53 //#define DEBUG // killall lxpanel && lxpanel --profile Lubuntu &
54
55 #ifdef HAVE_CONFIG_H
56 #include <config.h>
57 #endif
58
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 #include <sys/types.h>
64 #include <sys/wait.h>
65 #include <signal.h>
66 #include <errno.h>
67 #include <X11/Xlib.h>
68 #include <X11/Xutil.h>
69
70 #include <gdk-pixbuf/gdk-pixbuf.h>
71 #include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
72 #include <gdk/gdk.h>
73 #include <glib/gi18n.h>
74
75 #include <libfm/fm-gtk.h>
76
77 #include "misc.h"
78 #include "ev.h"
79 #include "plugin.h"
80 #include "task-button.h"
81 #include "launch-button.h"
82 #include "icon-grid.h"
83 #ifndef DISABLE_MENU
84 # include "menu-policy.h"
85 #endif
86 #include "gtk-compat.h"
87
88 #define PANEL_ICON_SIZE 24 /* see the private.h */
89
90 /* Column definitions for configuration dialogs. */
91 enum {
92 COL_ICON,
93 COL_TITLE,
94 COL_ICON_NAME,
95 COL_BTN,
96 N_COLS
97 };
98
99 typedef enum {
100 LAUNCHBAR = 0, /* GtkComboBox is 0-indexed. */
101 TASKBAR,
102 LAUNCHTASKBAR
103 } LtbMode;
104
105 typedef struct LaunchTaskBarPlugin LaunchTaskBarPlugin;
106
107 /* Private context for taskbar plugin. */
108 struct LaunchTaskBarPlugin {
109 /* LAUNCHBAR */
110 GtkWidget *lb_icon_grid; /* Icon grid managing the container */
111 GtkWidget *p_button_add, *p_button_remove, *p_label_menu_app_exec, *p_label_def_app_exec;
112 FmDndDest *dd; /* Drag & drop on launchbar */
113 /* TASKBAR */
114 GtkWidget * tb_icon_grid; /* Manager for taskbar buttons */
115 int number_of_desktops; /* Number of desktops, from NET_WM_NUMBER_OF_DESKTOPS */
116 int current_desktop; /* Current desktop, from NET_WM_CURRENT_DESKTOP */
117 guint dnd_delay_timer; /* Timer for drag and drop delay */
118 gboolean dnd_task_moving; /* User is currently moving a task button */
119 int icon_size; /* Size of task icons */
120 gboolean grouped_tasks; /* User preference: windows from same task are grouped onto a single button */
121 TaskShowFlags flags; /* User preferences flags */
122 int task_width_max; /* Maximum width of a taskbar button in horizontal orientation */
123 int spacing; /* Spacing between taskbar buttons */
124 guint flash_timeout; /* Timer for urgency notification */
125 gboolean flash_state; /* One-bit counter to flash taskbar */
126 /* COMMON */
127 #ifndef DISABLE_MENU
128 FmPath * path; /* Current menu item path */
129 GtkWidget *p_menuitem_lock_tbp;
130 GtkWidget *p_menuitem_unlock_tbp;
131 GtkWidget *p_menuitem_new_instance;
132 GtkWidget *p_menuitem_separator;
133 #endif
134 GtkWidget * plugin; /* Back pointer to Plugin */
135 LXPanel * panel; /* Back pointer to panel */
136 config_setting_t * settings;
137 GdkScreen *screen;
138 GtkWidget *config_dlg; /* Configuration dialog */
139 GtkNotebook *p_notebook;
140 GtkWidget *p_notebook_page_launch;
141 GtkWidget *p_notebook_page_task;
142 GKeyFile *p_key_file_special_cases;
143 int mode;
144 gboolean lb_built;
145 gboolean tb_built;
146 gboolean fixed_mode; /* if mode cannot be changed */
147 };
148
149 static gchar *launchtaskbar_rc = "style 'launchtaskbar-style' = 'theme-panel'\n"
150 "{\n"
151 "GtkWidget::focus-padding=0\n"
152 "GtkButton::default-border={0,0,0,0}\n"
153 "GtkButton::default-outside-border={0,0,0,0}\n"
154 "GtkButton::inner-border={0,0,0,0}\n"
155 "}\n"
156 "widget '*launchbar.*' style 'launchtaskbar-style'\n"
157 "widget '*taskbar.*' style 'launchtaskbar-style'";
158
159 #define DRAG_ACTIVE_DELAY 1000
160 #define TASK_WIDTH_MAX 200
161 #define ALL_WORKSPACES -1
162 #define ICON_ONLY_EXTRA 6 /* Amount needed to have button lay out symmetrically */
163 #define ICON_BUTTON_TRIM 4 /* Amount needed to have button remain on panel */
164
165 static void launchtaskbar_destructor(gpointer user_data);
166
167 static void taskbar_redraw(LaunchTaskBarPlugin * tb);
168 static void taskbar_net_client_list(GtkWidget * widget, LaunchTaskBarPlugin * tb);
169 static void taskbar_net_current_desktop(GtkWidget * widget, LaunchTaskBarPlugin * tb);
170 static void taskbar_net_number_of_desktops(GtkWidget * widget, LaunchTaskBarPlugin * tb);
171 static void taskbar_net_active_window(GtkWidget * widget, LaunchTaskBarPlugin * tb);
172 static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, LaunchTaskBarPlugin * tb);
173 static void taskbar_window_manager_changed(GdkScreen * screen, LaunchTaskBarPlugin * tb);
174 static void taskbar_apply_configuration(LaunchTaskBarPlugin * ltbp);
175 static void taskbar_add_task_button(LaunchTaskBarPlugin * tb, TaskButton * task);
176
177 #define taskbar_reset_menu(tb) if (tb->tb_built) task_button_reset_menu(tb->tb_icon_grid)
178
179
180 #ifndef DISABLE_MENU
181 static char *task_get_cmdline(Window win, LaunchTaskBarPlugin *ltbp)
182 {
183 GPid pid = get_net_wm_pid(win);
184 char proc_path[64];
185 gchar *cmdline = NULL;
186 gchar *p_char = NULL;
187
188 snprintf(proc_path, sizeof(proc_path),
189 G_DIR_SEPARATOR_S "proc" G_DIR_SEPARATOR_S "%lu" G_DIR_SEPARATOR_S "cmdline",
190 (gulong)pid);
191 g_file_get_contents(proc_path, &cmdline, NULL, NULL);
192 if (cmdline)
193 {
194 p_char = strchr(cmdline, '\n');
195 if(p_char != NULL) *p_char = '\0';
196 p_char = strrchr(cmdline, G_DIR_SEPARATOR);
197 if (p_char != NULL) p_char++;
198 else p_char = cmdline;
199 if(strcmp(p_char, "python") == 0)
200 {
201 snprintf(proc_path, sizeof(proc_path),
202 G_DIR_SEPARATOR_S "proc" G_DIR_SEPARATOR_S "%lu" G_DIR_SEPARATOR_S "comm",
203 (gulong)pid);
204 g_free(cmdline);
205 cmdline = NULL;
206 g_file_get_contents(proc_path, &cmdline, NULL, NULL);
207 if (cmdline)
208 {
209 p_char = strchr(cmdline, '\n');
210 if(p_char != NULL) *p_char = '\0';
211 }
212 }
213 else
214 {
215 p_char = g_key_file_get_string(ltbp->p_key_file_special_cases,
216 "special_cases", p_char, NULL);
217 if (p_char != NULL) /* found this key */
218 {
219 g_free(cmdline);
220 cmdline = p_char;
221 }
222 }
223 }
224 return cmdline;
225 }
226
227 static FmPath *f_find_menu_launchbutton_recursive(Window win, LaunchTaskBarPlugin *ltbp)
228 {
229 MenuCache *mc;
230 guint32 flags;
231 GSList *apps, *l;
232 size_t len;
233 char *exec_bin = task_get_cmdline(win, ltbp);
234 const char *exec, *short_exec;
235 char *str_path;
236 FmPath *path = NULL;
237
238 /* FIXME: cache it in Task object */
239 mc = panel_menu_cache_new(&flags);
240 /* FIXME: if menu plugin wasn't loaded yet we'll get NULL list here */
241 apps = menu_cache_list_all_apps(mc);
242 short_exec = strrchr(exec_bin, '/');
243 if (short_exec != NULL)
244 short_exec++;
245 else
246 short_exec = exec_bin;
247 len = strlen(short_exec);
248 /* the same executable may be used in numerous applications so wild guess
249 estimation check for desktop id equal to short_exec+".desktop" first */
250 for (l = apps; l; l = l->next)
251 {
252 exec = menu_cache_item_get_id(MENU_CACHE_ITEM(l->data));
253 /* we don't check flags here because user always can manually
254 start any app that isn't visible in the desktop menu */
255 if (strncmp(exec, short_exec, len) == 0 && exec[len] == '.')
256 break;
257 }
258 /* if not found then check for non-absolute exec name in application
259 since it usually is expanded by application starting functions */
260 if (l == NULL) for (l = apps; l; l = l->next)
261 {
262 exec = menu_cache_app_get_exec(MENU_CACHE_APP(l->data));
263 if (exec[0] != '/' && strncmp(exec, short_exec, len) == 0 &&
264 (exec[len] == ' ' || exec[len] == 0))
265 break;
266 }
267 /* well, not matched, let try full path, we assume here if application
268 starts executable by full path then process cannot have short name */
269 if (l == NULL && exec_bin[0] == '/')
270 {
271 len = strlen(exec_bin);
272 for (l = apps; l; l = l->next)
273 {
274 exec = menu_cache_app_get_exec(MENU_CACHE_APP(l->data));
275 if (exec[0] == '/' && strncmp(exec, exec_bin, len) == 0 &&
276 (exec[len] == ' ' || exec[len] == 0))
277 break;
278 }
279 }
280 if (l)
281 {
282 str_path = menu_cache_dir_make_path(MENU_CACHE_DIR(l->data));
283 path = fm_path_new_relative(fm_path_get_apps_menu(), str_path+13); /* skip /Applications */
284 g_free(str_path);
285 }
286 g_slist_foreach(apps, (GFunc)menu_cache_item_unref, NULL);
287 g_slist_free(apps);
288 menu_cache_unref(mc);
289 g_debug("f_find_menu_launchbutton_recursive: search '%s' found=%d", exec_bin, (path != NULL));
290 g_free(exec_bin);
291 return path;
292 }
293 #endif
294
295 /* Handler for "drag-motion" event from launchtaskbar button. */
296 static gboolean on_launchbar_drag_motion(
297 GtkWidget * widget,
298 GdkDragContext * context,
299 gint x,
300 gint y,
301 guint time,
302 LaunchTaskBarPlugin * b)
303 {
304 GdkAtom target;
305 GdkDragAction action = 0;
306 GtkWidget *btn;
307 PanelIconGridDropPosition pos;
308
309 if (!panel_icon_grid_get_dest_at_pos(PANEL_ICON_GRID(b->lb_icon_grid), x, y,
310 &btn, &pos) || !btn)
311 {
312 panel_icon_grid_set_drag_dest(PANEL_ICON_GRID(b->lb_icon_grid), NULL, 0);
313 fm_dnd_dest_set_dest_file(b->dd, NULL);
314 gdk_drag_status(context, 0, time);
315 return TRUE;
316 }
317 panel_icon_grid_set_drag_dest(PANEL_ICON_GRID(b->lb_icon_grid), btn, pos);
318 if (!PANEL_IS_LAUNCH_BUTTON(btn) || pos != PANEL_ICON_GRID_DROP_INTO)
319 {
320 fm_dnd_dest_set_dest_file(b->dd, NULL);
321 gdk_drag_status(context, 0, time);
322 return TRUE;
323 }
324 fm_dnd_dest_set_dest_file(b->dd, launch_button_get_file_info((LaunchButton *)btn));
325 target = fm_dnd_dest_find_target(b->dd, context);
326 if (target != GDK_NONE && fm_dnd_dest_is_target_supported(b->dd, target))
327 action = fm_dnd_dest_get_default_action(b->dd, context, target);
328 gdk_drag_status(context, action, time);
329 /* g_debug("launchbutton_drag_motion_event: act=%u",action); */
330 return TRUE;
331 }
332
333 static void on_launchbar_drag_leave(GtkWidget * widget, GdkDragContext * drag_context,
334 guint time, LaunchTaskBarPlugin * lb)
335 {
336 panel_icon_grid_set_drag_dest(PANEL_ICON_GRID(lb->lb_icon_grid), NULL, 0);
337 fm_dnd_dest_set_dest_file(lb->dd, NULL);
338 }
339
340 #ifndef DISABLE_MENU
341 static LaunchButton *launchbar_exec_bin_exists(LaunchTaskBarPlugin *lb, FmPath *path)
342 {
343 LaunchButton *ret_val = NULL;
344 GList *children, *l;
345 FmFileInfo *fi;
346
347 if (!path)
348 return NULL;
349 children = gtk_container_get_children(GTK_CONTAINER(lb->lb_icon_grid));
350 for (l = children; l != NULL; l = l->next)
351 {
352 fi = launch_button_get_file_info(PANEL_LAUNCH_BUTTON(l->data));
353 if (fi && fm_path_equal(path, fm_file_info_get_path(fi)))
354 {
355 ret_val = l->data;
356 break;
357 }
358 }
359 g_list_free(children);
360 return ret_val;
361 }
362 #endif
363
364 /* Read the configuration file entry for a launchtaskbar button and create it. */
365 static gboolean launchbutton_constructor(LaunchTaskBarPlugin * lb, config_setting_t * s)
366 {
367 LaunchButton *btn = NULL;
368 const char *str;
369 char *str_path = NULL;
370 FmPath *path;
371
372 /* Read parameters from the configuration file and validate. */
373 if (!config_setting_lookup_string(s, "id", &str) || str[0] == '\0')
374 return FALSE;
375
376 /* Build the structures and return. */
377 if (str[0] == '~')
378 {
379 str_path = expand_tilda(str);
380 path = fm_path_new_for_path(str_path);
381 }
382 else if (strchr(str, '/') != NULL)
383 {
384 path = fm_path_new_for_str(str);
385 /* FIXME: check if str contains invalid path */
386 }
387 else
388 {
389 str_path = g_strdup_printf("search://menu://applications/?recursive=1&show_hidden=1&name=%s", str);
390 path = fm_path_new_for_uri(str_path);
391 }
392 btn = launch_button_new(lb->panel, lb->plugin, path, s);
393 g_free(str_path);
394 fm_path_unref(path);
395 if (btn)
396 gtk_container_add(GTK_CONTAINER(lb->lb_icon_grid), GTK_WIDGET(btn));
397 return (btn != NULL);
398 }
399
400 /* prototype of this is app_info_create_from_commandline() in libfm */
401 static gboolean _launchbutton_create_id(LaunchTaskBarPlugin * lb, config_setting_t * s)
402 {
403 const char *icon = NULL, *name, *exec, *path = NULL;
404 char *dirname, *filename;
405 int fd, terminal = 0;
406 gboolean ret = FALSE;
407
408 if (!config_setting_lookup_string(s, "action", &exec) || exec[0] == '\0')
409 return FALSE;
410 if (!config_setting_lookup_string(s, "tooltip", &name) || name[0] == '\0')
411 name = "Launcher"; /* placeholder, XDG requires a non-empty name */
412 config_setting_lookup_string(s, "image", &icon);
413 config_setting_lookup_string(s, "path", &path);
414 config_setting_lookup_int(s, "terminal", &terminal);
415
416 dirname = g_build_filename(g_get_user_data_dir(), "applications", NULL);
417 if (g_mkdir_with_parents(dirname, 0700) == 0)
418 {
419 filename = g_strdup_printf("%s/lxpanel-launcher-XXXXXX.desktop", dirname);
420 fd = g_mkstemp (filename);
421 if (fd != -1)
422 {
423 GString* content = g_string_sized_new(256);
424
425 g_string_printf(content,
426 "[" G_KEY_FILE_DESKTOP_GROUP "]\n"
427 G_KEY_FILE_DESKTOP_KEY_TYPE "=" G_KEY_FILE_DESKTOP_TYPE_APPLICATION "\n"
428 G_KEY_FILE_DESKTOP_KEY_NAME "=%s\n"
429 G_KEY_FILE_DESKTOP_KEY_EXEC "=%s\n"
430 G_KEY_FILE_DESKTOP_KEY_CATEGORIES "=X-LXPanel;\n",
431 name, exec);
432 if (icon)
433 g_string_append_printf(content, "Icon=%s\n", icon);
434 if (terminal)
435 g_string_append(content, G_KEY_FILE_DESKTOP_KEY_TERMINAL "=true\n");
436 if (path && path[0] == '/')
437 g_string_append_printf(content, "Path=%s\n", path);
438 close(fd);
439 ret = g_file_set_contents(filename, content->str, content->len, NULL);
440 if (ret) {
441 config_group_set_string(s, "id", filename);
442 /* FIXME: is it reasonable to remove obsolete keys too? */
443 lxpanel_config_save(lb->panel);
444 } else
445 g_unlink(filename);
446 g_string_free(content, TRUE);
447 }
448 g_free(filename);
449 }
450 g_free(dirname);
451 if (ret) /* we created it, let use it */
452 return launchbutton_constructor(lb, s);
453 return FALSE;
454 }
455
456 static void launchtaskbar_constructor_add_default_special_case(LaunchTaskBarPlugin *ltbp, const gchar *tk_exec, const gchar *mb_exec)
457 {
458 g_key_file_set_value(ltbp->p_key_file_special_cases, "special_cases", tk_exec, mb_exec);
459 }
460
461 static void launchtaskbar_constructor_launch(LaunchTaskBarPlugin *ltbp)
462 {
463 config_setting_t *settings;
464 guint i = 0;
465
466 if(!ltbp->lb_built)
467 {
468 ltbp->lb_built = TRUE;
469 /* Read parameters from the configuration file. */
470 settings = config_setting_get_member(ltbp->settings, "");
471 if (settings && config_setting_is_list(settings))
472 {
473 config_setting_t *s;
474
475 while ((s = config_setting_get_elem(settings, i)) != NULL)
476 {
477 if (strcmp(config_setting_get_name(s), "Button") != 0)
478 {
479 g_warning("launchtaskbar: illegal token %s\n", config_setting_get_name(s));
480 config_setting_destroy(s);
481 }
482 else if (!launchbutton_constructor(ltbp, s) &&
483 /* try to create desktop id from old-style manual setup */
484 !_launchbutton_create_id(ltbp, s))
485 {
486 g_warning( "launchtaskbar: can't init button\n");
487 /* FIXME: show failed id to the user instead */
488 config_setting_destroy(s);
489 }
490 else /* success, accept the setting */
491 i++;
492 }
493 }
494 if (i == 0)
495 {
496 /* build bootstrap button */
497 LaunchButton *btn = launch_button_new(ltbp->panel, ltbp->plugin, NULL, NULL);
498 gtk_container_add(GTK_CONTAINER(ltbp->lb_icon_grid), GTK_WIDGET(btn));
499 g_debug("launchtaskbar: added bootstrap button %p", btn);
500 }
501 /* Drag and drop support. */
502 ltbp->dd = fm_dnd_dest_new_with_handlers(ltbp->lb_icon_grid);
503 g_signal_connect(ltbp->lb_icon_grid, "drag-motion",
504 G_CALLBACK(on_launchbar_drag_motion), ltbp);
505 g_signal_connect(ltbp->lb_icon_grid, "drag-leave",
506 G_CALLBACK(on_launchbar_drag_leave), ltbp);
507 }
508 gtk_widget_set_visible(ltbp->lb_icon_grid, TRUE);
509 }
510
511 static void flash_window_update_iter(GtkWidget *widget, gpointer data)
512 {
513 task_button_set_flash_state(PANEL_TASK_BUTTON(widget), GPOINTER_TO_INT(data));
514 }
515
516 static gboolean flash_window_timeout(gpointer user_data)
517 {
518 LaunchTaskBarPlugin *tb;
519
520 if (g_source_is_destroyed(g_main_current_source()))
521 return FALSE;
522 tb = user_data;
523 tb->flash_state = !tb->flash_state;
524 gtk_container_foreach(GTK_CONTAINER(tb->tb_icon_grid),
525 flash_window_update_iter, GINT_TO_POINTER(tb->flash_state));
526 return TRUE;
527 }
528
529 static void on_gtk_cursor_blink_time_changed(GObject *gsettings, GParamSpec *pspec,
530 LaunchTaskBarPlugin *tb)
531 {
532 gint interval;
533
534 if (tb->flash_timeout == 0) /* nothing to do? */
535 return;
536 g_source_remove(tb->flash_timeout);
537 g_object_get(gtk_widget_get_settings(GTK_WIDGET(tb)), "gtk-cursor-blink-time",
538 &interval, NULL);
539 tb->flash_timeout = g_timeout_add(interval / 2, flash_window_timeout, tb);
540 }
541
542 /* Set an urgency timer on a task. */
543 static void set_timer_on_task(LaunchTaskBarPlugin *tb)
544 {
545 gint interval;
546
547 if (tb->flash_timeout != 0)
548 return;
549 g_object_get(gtk_widget_get_settings(GTK_WIDGET(tb->plugin)),
550 "gtk-cursor-blink-time", &interval, NULL);
551 g_signal_connect(gtk_widget_get_settings(GTK_WIDGET(tb->plugin)),
552 "notify::gtk-cursor-blink-time",
553 G_CALLBACK(on_gtk_cursor_blink_time_changed), tb);
554 tb->flash_timeout = g_timeout_add(interval / 2, flash_window_timeout, tb);
555 }
556
557 static void reset_timer_on_task(LaunchTaskBarPlugin *tb)
558 {
559 if (tb->flash_timeout == 0)
560 return;
561 g_source_remove(tb->flash_timeout);
562 tb->flash_timeout = 0;
563 g_signal_handlers_disconnect_by_func(gtk_widget_get_settings(GTK_WIDGET(tb->plugin)),
564 on_gtk_cursor_blink_time_changed, tb);
565 }
566
567
568 static void launchtaskbar_constructor_task(LaunchTaskBarPlugin *ltbp)
569 {
570 if(!ltbp->tb_built)
571 {
572 config_setting_t *s = ltbp->settings;
573 gint tmp_int;
574
575 ltbp->tb_built = TRUE;
576
577 /* Parse configuration now */
578 if (config_setting_lookup_int(s, "tooltips", &tmp_int))
579 ltbp->flags.tooltips = (tmp_int != 0);
580 if (config_setting_lookup_int(s, "IconsOnly", &tmp_int))
581 ltbp->flags.icons_only = (tmp_int != 0);
582 if (config_setting_lookup_int(s, "ShowAllDesks", &tmp_int))
583 ltbp->flags.show_all_desks = (tmp_int != 0);
584 if (config_setting_lookup_int(s, "SameMonitorOnly", &tmp_int))
585 ltbp->flags.same_monitor_only = (tmp_int != 0);
586 if (config_setting_lookup_int(s, "DisableUpscale", &tmp_int))
587 ltbp->flags.disable_taskbar_upscale = (tmp_int != 0);
588 config_setting_lookup_int(s, "MaxTaskWidth", &ltbp->task_width_max);
589 config_setting_lookup_int(s, "spacing", &ltbp->spacing);
590 if (config_setting_lookup_int(s, "UseMouseWheel", &tmp_int))
591 ltbp->flags.use_mouse_wheel = (tmp_int != 0);
592 if (config_setting_lookup_int(s, "UseUrgencyHint", &tmp_int))
593 ltbp->flags.use_urgency_hint = (tmp_int != 0);
594 if (config_setting_lookup_int(s, "FlatButton", &tmp_int))
595 ltbp->flags.flat_button = (tmp_int != 0);
596 if (config_setting_lookup_int(s, "GroupedTasks", &tmp_int))
597 ltbp->grouped_tasks = (tmp_int != 0);
598
599 /* Make container for task buttons as a child of top level widget. */
600 ltbp->tb_icon_grid = panel_icon_grid_new(panel_get_orientation(ltbp->panel),
601 ltbp->task_width_max,
602 ltbp->icon_size, ltbp->spacing, 0,
603 panel_get_height(ltbp->panel));
604 panel_icon_grid_set_constrain_width(PANEL_ICON_GRID(ltbp->tb_icon_grid), TRUE);
605 gtk_box_pack_start(GTK_BOX(ltbp->plugin), ltbp->tb_icon_grid, TRUE, TRUE, 0);
606 /* taskbar_update_style(ltbp); */
607
608 /* Add GDK event filter. */
609 gdk_window_add_filter(NULL, (GdkFilterFunc) taskbar_event_filter, ltbp);
610
611 /* Connect signals to receive root window events and initialize root window properties. */
612 ltbp->number_of_desktops = get_net_number_of_desktops();
613 ltbp->current_desktop = get_net_current_desktop();
614 g_signal_connect(G_OBJECT(fbev), "current-desktop", G_CALLBACK(taskbar_net_current_desktop), (gpointer) ltbp);
615 g_signal_connect(G_OBJECT(fbev), "active-window", G_CALLBACK(taskbar_net_active_window), (gpointer) ltbp);
616 g_signal_connect(G_OBJECT(fbev), "number-of-desktops", G_CALLBACK(taskbar_net_number_of_desktops), (gpointer) ltbp);
617 g_signal_connect(G_OBJECT(fbev), "client-list", G_CALLBACK(taskbar_net_client_list), (gpointer) ltbp);
618
619 /* Connect a signal to be notified when the window manager changes. This causes re-evaluation of the "use_net_active" status. */
620 g_signal_connect(ltbp->screen, "window-manager-changed", G_CALLBACK(taskbar_window_manager_changed), ltbp);
621
622 /* Start blinking timeout if configured */
623 if (ltbp->flags.use_urgency_hint)
624 set_timer_on_task(ltbp);
625
626 /* Fetch the client list and redraw the taskbar. Then determine what window has focus. */
627 taskbar_net_client_list(NULL, ltbp);
628 taskbar_net_active_window(NULL, ltbp);
629 }
630 gtk_widget_set_visible(ltbp->tb_icon_grid, TRUE);
631 }
632
633 /* Plugin constructor. */
634 static GtkWidget *_launchtaskbar_constructor(LXPanel *panel, config_setting_t *settings,
635 LtbMode mode)
636 {
637 GtkWidget *p;
638 LaunchTaskBarPlugin *ltbp;
639 int height, border;
640
641 gtk_rc_parse_string(launchtaskbar_rc);
642
643 /* Allocate plugin context and set into Plugin private data pointer. */
644 ltbp = g_new0(LaunchTaskBarPlugin, 1);
645 ltbp->panel = panel;
646 ltbp->settings = settings;
647 ltbp->mode = mode;
648 ltbp->screen = gtk_widget_get_screen((GtkWidget*)panel);
649
650 /* Initialize to defaults. */
651 ltbp->icon_size = panel_get_icon_size(panel);
652 ltbp->flags.tooltips = TRUE;
653 ltbp->flags.icons_only = FALSE;
654 ltbp->flags.show_all_desks = TRUE;
655 ltbp->task_width_max = TASK_WIDTH_MAX;
656 ltbp->spacing = 1;
657 ltbp->flags.use_mouse_wheel = TRUE;
658 ltbp->flags.use_urgency_hint = TRUE;
659 ltbp->grouped_tasks = FALSE;
660 ltbp->fixed_mode = (mode == LAUNCHBAR) || (mode == TASKBAR);
661
662 /* Special cases key file */
663 ltbp->p_key_file_special_cases = g_key_file_new();
664 gchar *special_cases_filepath = g_build_filename(g_get_user_config_dir(),
665 "lxpanel", "launchtaskbar.cfg", NULL);
666 if (!g_key_file_load_from_file(ltbp->p_key_file_special_cases,
667 special_cases_filepath,
668 G_KEY_FILE_KEEP_COMMENTS, NULL))
669 {
670 launchtaskbar_constructor_add_default_special_case(ltbp, "synaptic", "synaptic-pkexec");
671 launchtaskbar_constructor_add_default_special_case(ltbp, "soffice.bin", "libreoffice");
672 launchtaskbar_constructor_add_default_special_case(ltbp, "x-terminal-emulator", "lxterminal");
673 gchar *key_file_data = g_key_file_to_data(ltbp->p_key_file_special_cases, NULL, NULL);
674 g_file_set_contents(special_cases_filepath, key_file_data, -1, NULL);
675 g_free(key_file_data);
676 }
677 g_free(special_cases_filepath);
678
679 /* Allocate top level widget and set into Plugin widget pointer. */
680 ltbp->plugin = p = panel_box_new(panel, FALSE, 5);
681 lxpanel_plugin_set_data(p, ltbp, launchtaskbar_destructor);
682 /* Allocate an icon grid manager to manage the container. */
683 height = panel_get_height(panel);
684 border = (height - ltbp->icon_size) > 1 ? 1 : 0;
685 ltbp->lb_icon_grid = panel_icon_grid_new(panel_get_orientation(panel),
686 ltbp->icon_size, ltbp->icon_size,
687 3 - 2 * border, border, height);
688 gtk_box_pack_start(GTK_BOX(p), ltbp->lb_icon_grid, FALSE, TRUE, 0);
689
690 /* Read parameters from the configuration file. */
691 config_setting_lookup_int(settings, "LaunchTaskBarMode", &ltbp->mode);
692 switch (ltbp->mode) {
693 case LAUNCHBAR:
694 launchtaskbar_constructor_launch(ltbp);
695 gtk_widget_set_name(p, "launchbar");
696 break;
697 default:
698 ltbp->mode = LAUNCHTASKBAR; /* reset invalid value */
699 case LAUNCHTASKBAR:
700 launchtaskbar_constructor_launch(ltbp);
701 gtk_widget_set_name(p, "launchtaskbar");
702 case TASKBAR:
703 launchtaskbar_constructor_task(ltbp);
704 if (ltbp->mode == TASKBAR)
705 gtk_widget_set_name(p, "taskbar");
706 }
707
708 return p;
709 }
710
711 static GtkWidget *launchtaskbar_constructor(LXPanel *panel, config_setting_t *settings)
712 {
713 return _launchtaskbar_constructor(panel, settings, LAUNCHTASKBAR);
714 }
715
716 static void launchtaskbar_destructor_launch(LaunchTaskBarPlugin *ltbp)
717 {
718 if (ltbp->dd)
719 g_object_unref(ltbp->dd);
720 /* do not disconnect handler on child widget - it is already destroyed */
721 }
722
723 static void launchtaskbar_destructor_task(LaunchTaskBarPlugin *ltbp)
724 {
725 /* Remove GDK event filter. */
726 gdk_window_remove_filter(NULL, (GdkFilterFunc) taskbar_event_filter, ltbp);
727
728 /* Remove root window signal handlers. */
729 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_current_desktop, ltbp);
730 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_active_window, ltbp);
731 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_number_of_desktops, ltbp);
732 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_client_list, ltbp);
733
734 /* Remove "window-manager-changed" handler. */
735 g_signal_handlers_disconnect_by_func(ltbp->screen, taskbar_window_manager_changed, ltbp);
736
737 /* Stop blinking timeout */
738 reset_timer_on_task(ltbp);
739 #ifndef DISABLE_MENU
740 if (ltbp->path)
741 fm_path_unref(ltbp->path);
742 #endif
743 }
744
745 /* Plugin destructor. */
746 static void launchtaskbar_destructor(gpointer user_data)
747 {
748 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)user_data;
749
750 // TASKBAR
751 if(ltbp->tb_built) launchtaskbar_destructor_task(ltbp);
752
753 // LAUNCHBAR
754 if(ltbp->lb_built) launchtaskbar_destructor_launch(ltbp);
755
756 // LAUNCHTASKBAR
757
758 /* Deallocate all memory. */
759 if (ltbp->p_key_file_special_cases != NULL)
760 g_key_file_free(ltbp->p_key_file_special_cases);
761 g_free(ltbp);
762 }
763
764 static void launchbar_remove_bootstrap(LaunchTaskBarPlugin *ltbp)
765 {
766 GList *btns = gtk_container_get_children(GTK_CONTAINER(ltbp->lb_icon_grid));
767 GList *l;
768
769 for (l = btns; l; l = l->next)
770 if (launch_button_get_settings(l->data) == NULL)
771 {
772 gtk_widget_destroy(l->data);
773 g_debug("launchtaskbar: removed bootstrap button %p", l->data);
774 }
775 g_list_free(btns);
776 }
777
778 static void _launchbar_configure_add(GtkTreeView *menu_view, LaunchTaskBarPlugin *ltbp)
779 {
780 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
781 FmPath * sel_path = fm_app_menu_view_dup_selected_app_desktop_path(menu_view);
782 LaunchButton * btn;
783
784 if (sel_path != NULL &&
785 (btn = launch_button_new(ltbp->panel, ltbp->plugin, sel_path, NULL)) != NULL &&
786 launch_button_wait_load(btn))
787 {
788 GtkListStore * list = GTK_LIST_STORE(gtk_tree_view_get_model(defined_view));
789 GtkTreeIter it;
790 GdkPixbuf* pix;
791 char *path;
792 config_setting_t *settings;
793 gtk_container_add(GTK_CONTAINER(ltbp->lb_icon_grid), GTK_WIDGET(btn));
794 gtk_list_store_append(list, &it);
795 pix = fm_pixbuf_from_icon(launch_button_get_icon(btn), PANEL_ICON_SIZE);
796 gtk_list_store_set(list, &it,
797 COL_ICON, pix,
798 COL_TITLE, launch_button_get_disp_name(btn),
799 COL_BTN, btn,
800 -1);
801 g_object_unref(pix);
802 path = fm_path_to_str(sel_path);
803 /* g_debug("*** path '%s'",path); */
804 settings = config_group_add_subgroup(ltbp->settings, "Button");
805 config_group_set_string(settings, "id", path);
806 launch_button_set_settings(btn, settings);
807 g_free(path);
808 fm_path_unref(sel_path);
809 launchbar_remove_bootstrap(ltbp);
810 }
811 }
812
813 /* Handler for "clicked" action on launchtaskbar configuration dialog "Add" button. */
814 static void launchbar_configure_add_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
815 {
816 GtkTreeView * menu_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "menu_view"));
817
818 _launchbar_configure_add(menu_view, ltbp);
819 }
820
821 static void launchbar_remove_button(LaunchTaskBarPlugin *ltbp, LaunchButton *btn)
822 {
823 GList *list;
824
825 config_setting_destroy(launch_button_get_settings(btn));
826 gtk_widget_destroy(GTK_WIDGET(btn));
827 /* Put the bootstrap button back if the list becomes empty. */
828 list = gtk_container_get_children(GTK_CONTAINER(ltbp->lb_icon_grid));
829 if (list == NULL)
830 {
831 btn = launch_button_new(ltbp->panel, ltbp->plugin, NULL, NULL);
832 gtk_container_add(GTK_CONTAINER(ltbp->lb_icon_grid), GTK_WIDGET(btn));
833 g_debug("launchtaskbar: added bootstrap button %p", btn);
834 }
835 else
836 g_list_free(list);
837 }
838
839 /* Handler for "clicked" action on launchtaskbar configuration dialog "Remove" button. */
840 static void launchbar_configure_remove_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
841 {
842 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
843 GtkTreeModel * list;
844 GtkTreeIter it;
845 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
846 {
847 LaunchButton * btn;
848 gtk_tree_model_get(list, &it, COL_BTN, &btn, -1);
849
850 /* We have found a selected button.
851 * Remove it from the icon grid, the data structure, and the view. */
852 gtk_list_store_remove(GTK_LIST_STORE(list), &it);
853 gtk_widget_set_visible(ltbp->p_label_def_app_exec, FALSE);
854
855 launchbar_remove_button(ltbp, btn);
856 }
857 }
858
859 /* Handler for "clicked" action on launchtaskbar configuration dialog "Move Up" button. */
860 static void launchbar_configure_move_up_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
861 {
862 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
863 GtkTreeModel * list;
864 GtkTreeIter it;
865 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
866 {
867 LaunchButton *btn;
868 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, COL_BTN, &btn, -1);
869 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &it);
870 if ((gtk_tree_path_get_indices(path)[0] > 0)
871 && (gtk_tree_path_prev(path)))
872 {
873 GtkTreeIter it2;
874 if (gtk_tree_model_get_iter(list, &it2, path))
875 {
876 /* We have found a selected button that can be moved.
877 * Reorder it in the icon grid, the data structure, and the view. */
878 int i = gtk_tree_path_get_indices(path)[0];
879 config_setting_t *settings = launch_button_get_settings(btn);
880 gtk_list_store_move_before(GTK_LIST_STORE(list), &it, &it2);
881 panel_icon_grid_reorder_child(PANEL_ICON_GRID(ltbp->lb_icon_grid),
882 GTK_WIDGET(btn), i);
883 config_setting_move_elem(settings,
884 config_setting_get_parent(settings), i);
885 }
886 }
887 gtk_tree_path_free(path);
888 }
889 }
890
891 /* Handler for "clicked" action on launchtaskbar configuration dialog "Move Down" button. */
892 static void launchbar_configure_move_down_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
893 {
894 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
895 GtkTreeModel * list;
896 GtkTreeIter it;
897 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
898 {
899 LaunchButton *btn;
900 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, COL_BTN, &btn, -1);
901 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &it);
902 int n = gtk_tree_model_iter_n_children(list, NULL);
903 if (gtk_tree_path_get_indices(path)[0] < (n - 1))
904 {
905 gtk_tree_path_next(path);
906 GtkTreeIter it2;
907 if (gtk_tree_model_get_iter( list, &it2, path))
908 {
909 /* We have found a selected button that can be moved.
910 * Reorder it in the icon grid, the data structure, and the view. */
911 int i = gtk_tree_path_get_indices(path)[0];
912 config_setting_t *settings = launch_button_get_settings(btn);
913 gtk_list_store_move_after(GTK_LIST_STORE(list), &it, &it2);
914 panel_icon_grid_reorder_child(PANEL_ICON_GRID(ltbp->lb_icon_grid),
915 GTK_WIDGET(btn), i);
916 config_setting_move_elem(settings,
917 config_setting_get_parent(settings), i);
918 }
919 }
920 gtk_tree_path_free(path);
921 }
922 }
923
924 /* Initialize the list of existing launchtaskbar buttons when the configuration dialog is shown. */
925 static void launchbar_configure_initialize_list(LaunchTaskBarPlugin *ltbp, GtkWidget * dlg, GtkTreeView * view)
926 {
927 /* Set the selection mode. */
928 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_BROWSE);
929
930 /* Define a column. */
931 GtkTreeViewColumn* col = gtk_tree_view_get_column(view, 0);
932
933 /* Establish the pixbuf column cell renderer. */
934 GtkCellRenderer * render = gtk_cell_renderer_pixbuf_new();
935 gtk_tree_view_column_pack_start(col, render, FALSE);
936 gtk_tree_view_column_set_attributes(col, render, "pixbuf", COL_ICON, NULL);
937
938 /* Establish the text column cell renderer. */
939 render = gtk_cell_renderer_text_new();
940 gtk_tree_view_column_pack_start(col, render, TRUE);
941 gtk_tree_view_column_add_attribute(col, render, "text", COL_TITLE);
942
943 /* Establish the column data types. */
944 GtkListStore* list = GTK_LIST_STORE(gtk_tree_view_get_model(view));
945
946 /* Initialize from defined launchtaskbar buttons. */
947 GList *children = gtk_container_get_children(GTK_CONTAINER(ltbp->lb_icon_grid));
948 GList *l;
949 for (l = children; l != NULL; l = l->next)
950 {
951 LaunchButton * btn = (LaunchButton *) l->data;
952 GdkPixbuf * pix;
953 GtkTreeIter it;
954 if (launch_button_get_settings(btn) == NULL) /* bootstrap button */
955 continue;
956 gtk_list_store_append(list, &it);
957 pix = fm_pixbuf_from_icon(launch_button_get_icon(btn), PANEL_ICON_SIZE);
958 gtk_list_store_set(list, &it,
959 COL_ICON, pix,
960 COL_TITLE, launch_button_get_disp_name(btn),
961 COL_BTN, btn,
962 -1);
963 g_object_unref(pix);
964 }
965 g_list_free(children);
966 g_object_set_data(G_OBJECT(dlg), "defined_view", view);
967 }
968
969 static void plugin_set_expand_status(LaunchTaskBarPlugin *ltbp, gboolean expand_new)
970 {
971 gboolean old_expand, fill;
972 guint padding;
973 GtkPackType pack_type;
974 GtkWidget *box = gtk_widget_get_parent(ltbp->plugin);
975 g_return_if_fail(box);
976 gtk_box_query_child_packing(GTK_BOX(box), ltbp->plugin, &old_expand, &fill, &padding, &pack_type);
977 gtk_box_set_child_packing(GTK_BOX(box), ltbp->plugin, expand_new, fill, padding, pack_type);
978 }
979
980 static void set_config_visibility(LaunchTaskBarPlugin *ltbp)
981 {
982 switch (ltbp->mode) {
983 default:
984 case LAUNCHTASKBAR:
985 gtk_widget_set_visible(ltbp->p_notebook_page_launch, TRUE);
986 gtk_widget_set_visible(ltbp->p_notebook_page_task, TRUE);
987 gtk_notebook_set_show_tabs(ltbp->p_notebook, TRUE);
988 break;
989 case TASKBAR:
990 gtk_widget_set_visible(ltbp->p_notebook_page_launch, FALSE);
991 gtk_widget_set_visible(ltbp->p_notebook_page_task, TRUE);
992 gtk_notebook_set_show_tabs(ltbp->p_notebook, FALSE);
993 break;
994 case LAUNCHBAR:
995 gtk_widget_set_visible(ltbp->p_notebook_page_launch, TRUE);
996 gtk_widget_set_visible(ltbp->p_notebook_page_task, FALSE);
997 gtk_notebook_set_show_tabs(ltbp->p_notebook, FALSE);
998 }
999 }
1000
1001 static void on_combobox_mode_changed(GtkComboBox *p_combobox, gpointer p_data)
1002 {
1003 LaunchTaskBarPlugin *ltbp = p_data;
1004 int new_mode = gtk_combo_box_get_active(GTK_COMBO_BOX(p_combobox));
1005
1006 if (new_mode < 0 || new_mode == ltbp->mode) /* no change was made */
1007 return;
1008
1009 ltbp->mode = new_mode;
1010
1011 set_config_visibility(ltbp);
1012
1013 switch (ltbp->mode) {
1014 case LAUNCHBAR:
1015 if (ltbp->tb_icon_grid)
1016 gtk_widget_set_visible(ltbp->tb_icon_grid, FALSE);
1017 launchtaskbar_constructor_launch(ltbp);
1018 plugin_set_expand_status(ltbp, FALSE);
1019 gtk_widget_set_name(ltbp->plugin, "launchbar");
1020 break;
1021 case TASKBAR:
1022 gtk_widget_set_visible(ltbp->lb_icon_grid, FALSE);
1023 launchtaskbar_constructor_task(ltbp);
1024 plugin_set_expand_status(ltbp, TRUE);
1025 gtk_widget_set_name(ltbp->plugin, "taskbar");
1026 break;
1027 default:
1028 ltbp->mode = LAUNCHTASKBAR;
1029 case LAUNCHTASKBAR:
1030 launchtaskbar_constructor_launch(ltbp);
1031 launchtaskbar_constructor_task(ltbp);
1032 plugin_set_expand_status(ltbp, TRUE);
1033 gtk_widget_set_name(ltbp->plugin, "launchtaskbar");
1034 break;
1035 }
1036
1037 config_group_set_int(ltbp->settings, "LaunchTaskBarMode", ltbp->mode);
1038 }
1039
1040 static void on_checkbutton_show_tooltips_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1041 {
1042 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1043 ltbp->flags.tooltips = gtk_toggle_button_get_active(p_togglebutton);
1044 //g_print("\nltbp->flags.tooltips upd\n");
1045 config_group_set_int(ltbp->settings, "tooltips", ltbp->flags.tooltips);
1046 taskbar_apply_configuration(ltbp);
1047 }
1048
1049 static void on_checkbutton_icons_only_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1050 {
1051 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1052 ltbp->flags.icons_only = gtk_toggle_button_get_active(p_togglebutton);
1053 //g_print("\ntb->flags.icons_only upd\n");
1054 config_group_set_int(ltbp->settings, "IconsOnly", ltbp->flags.icons_only);
1055 taskbar_apply_configuration(ltbp);
1056 }
1057
1058 static void on_checkbutton_flat_buttons_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1059 {
1060 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1061 ltbp->flags.flat_button = gtk_toggle_button_get_active(p_togglebutton);
1062 //g_print("\ntb->flags.flat_button upd\n");
1063 config_group_set_int(ltbp->settings, "FlatButton", ltbp->flags.flat_button);
1064 taskbar_apply_configuration(ltbp);
1065 }
1066
1067 static void on_checkbutton_show_all_desks_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1068 {
1069 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1070 ltbp->flags.show_all_desks = gtk_toggle_button_get_active(p_togglebutton);
1071 //g_print("\ntb->flags.show_all_desks upd\n");
1072 config_group_set_int(ltbp->settings, "ShowAllDesks", ltbp->flags.show_all_desks);
1073 taskbar_apply_configuration(ltbp);
1074 }
1075
1076 static void on_checkbutton_same_monitor_only_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1077 {
1078 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1079 ltbp->flags.same_monitor_only = gtk_toggle_button_get_active(p_togglebutton);
1080 //g_print("\ntb->flags.same_monitor_only upd\n");
1081 config_group_set_int(ltbp->settings, "SameMonitorOnly", ltbp->flags.same_monitor_only);
1082 taskbar_apply_configuration(ltbp);
1083 }
1084
1085 static void on_checkbutton_disable_taskbar_upscale_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1086 {
1087 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1088 ltbp->flags.disable_taskbar_upscale = gtk_toggle_button_get_active(p_togglebutton);
1089 //g_print("\ntb->flags.disable_taskbar_upscale upd\n");
1090 config_group_set_int(ltbp->settings, "DisableUpscale", ltbp->flags.disable_taskbar_upscale);
1091 taskbar_apply_configuration(ltbp);
1092 }
1093
1094 static void on_checkbutton_mouse_wheel_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1095 {
1096 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1097 ltbp->flags.use_mouse_wheel = gtk_toggle_button_get_active(p_togglebutton);
1098 //g_print("\ntb->flags.use_mouse_wheel upd\n");
1099 config_group_set_int(ltbp->settings, "UseMouseWheel", ltbp->flags.use_mouse_wheel);
1100 taskbar_apply_configuration(ltbp);
1101 }
1102
1103 static void on_checkbutton_urgency_hint_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1104 {
1105 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1106 ltbp->flags.use_urgency_hint = gtk_toggle_button_get_active(p_togglebutton);
1107 //g_print("\ntb->flags.use_urgency_hint upd\n");
1108 config_group_set_int(ltbp->settings, "UseUrgencyHint", ltbp->flags.use_urgency_hint);
1109 taskbar_apply_configuration(ltbp);
1110 /* Start/stop blinking timeout if configured */
1111 if (ltbp->flags.use_urgency_hint)
1112 set_timer_on_task(ltbp);
1113 else
1114 reset_timer_on_task(ltbp);
1115 }
1116
1117 static void on_checkbutton_grouped_tasks_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1118 {
1119 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1120 ltbp->grouped_tasks = gtk_toggle_button_get_active(p_togglebutton);
1121 //g_print("\ntb->grouped_tasks upd\n");
1122 config_group_set_int(ltbp->settings, "GroupedTasks", ltbp->grouped_tasks);
1123 taskbar_apply_configuration(ltbp);
1124 if (ltbp->grouped_tasks)
1125 {
1126 gboolean changed = FALSE;
1127 GList *children, *this, *l;
1128 int i = 0;
1129
1130 children = gtk_container_get_children(GTK_CONTAINER(ltbp->tb_icon_grid));
1131 /* merge buttons with the same class into first of that class */
1132 while ((this = g_list_nth(children, i++)))
1133 {
1134 for (l = this->next; l; l = l->next)
1135 if (task_button_merge(this->data, l->data))
1136 changed = TRUE;
1137 if (changed)
1138 {
1139 /* some button was consumed, need to reload buttons list */
1140 g_list_free(children);
1141 children = gtk_container_get_children(GTK_CONTAINER(ltbp->tb_icon_grid));
1142 changed = FALSE;
1143 }
1144 }
1145 g_list_free(children);
1146 }
1147 else
1148 {
1149 TaskButton *old_btn, *new_btn;
1150 GList *children, *this;
1151 int i;
1152
1153 children = gtk_container_get_children(GTK_CONTAINER(ltbp->tb_icon_grid));
1154 /* split each button starting from last one */
1155 for (this = g_list_last(children); this; this = this->prev)
1156 {
1157 old_btn = this->data;
1158 i = panel_icon_grid_get_child_position(PANEL_ICON_GRID(ltbp->tb_icon_grid),
1159 GTK_WIDGET(old_btn));
1160 while ((new_btn = task_button_split(old_btn)))
1161 {
1162 /* insert rest before old_btn */
1163 taskbar_add_task_button(ltbp, new_btn);
1164 panel_icon_grid_reorder_child(PANEL_ICON_GRID(ltbp->tb_icon_grid),
1165 GTK_WIDGET(new_btn), i);
1166 /* continue split with the rest */
1167 old_btn = new_btn;
1168 }
1169 }
1170 g_list_free(children);
1171 }
1172 }
1173
1174 static void on_spinbutton_max_width_value_changed(GtkSpinButton *p_spinbutton, gpointer p_data)
1175 {
1176 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1177 ltbp->task_width_max = gtk_spin_button_get_value(p_spinbutton);
1178 //g_print("\ntb->task_width_max upd\n");
1179 config_group_set_int(ltbp->settings, "MaxTaskWidth", ltbp->task_width_max);
1180 taskbar_apply_configuration(ltbp);
1181 }
1182
1183 static void on_spinbutton_spacing_value_changed(GtkSpinButton *p_spinbutton, gpointer p_data)
1184 {
1185 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1186 ltbp->spacing = gtk_spin_button_get_value(p_spinbutton);
1187 //g_print("\ntb->spacing upd\n");
1188 config_group_set_int(ltbp->settings, "spacing", ltbp->spacing);
1189 taskbar_apply_configuration(ltbp);
1190 }
1191
1192 static gboolean on_defined_view_button_press_event(GtkWidget *p_widget, GdkEventButton *p_event, gpointer p_data)
1193 {
1194 LaunchTaskBarPlugin *lb = (LaunchTaskBarPlugin *)p_data;
1195 if(p_event->button == 1)
1196 {
1197 if(p_event->type == GDK_2BUTTON_PRESS)
1198 {
1199 gtk_button_clicked(GTK_BUTTON(lb->p_button_remove));
1200 }
1201 }
1202 return FALSE;
1203 }
1204
1205 static void on_defined_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_data)
1206 {
1207 gboolean label_set = FALSE;
1208 LaunchTaskBarPlugin *lb = (LaunchTaskBarPlugin *)p_data;
1209 GtkTreeIter tree_iter_sel;
1210 GtkTreeModel* p_treemodel = gtk_tree_view_get_model(p_treeview);
1211 GtkTreeSelection *p_treeselection = gtk_tree_view_get_selection(p_treeview);
1212 if(gtk_tree_selection_get_selected(p_treeselection,
1213 (GtkTreeModel **)(&p_treemodel),
1214 &tree_iter_sel))
1215 {
1216 LaunchButton * p_btn;
1217 FmFileInfo *fi;
1218 gtk_tree_model_get(p_treemodel, &tree_iter_sel, COL_BTN, &p_btn, -1);
1219 if ((p_btn != NULL) && ((fi = launch_button_get_file_info(p_btn)) != NULL))
1220 {
1221 GString *p_gstring = g_string_new("");
1222 g_string_printf(p_gstring, "<i>%s</i>", fm_file_info_get_disp_name(fi));
1223 gtk_label_set_markup(GTK_LABEL(lb->p_label_def_app_exec), p_gstring->str);
1224 g_string_free(p_gstring, TRUE/*free also gstring->str*/);
1225 label_set = TRUE;
1226 }
1227 }
1228 gtk_widget_set_visible(lb->p_label_def_app_exec, label_set);
1229 gtk_widget_set_sensitive(lb->p_button_remove, label_set);
1230 }
1231
1232 static void on_menu_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_data)
1233 {
1234 gboolean label_set = FALSE;
1235 LaunchTaskBarPlugin *lb = (LaunchTaskBarPlugin *)p_data;
1236 GAppInfo *app = fm_app_menu_view_dup_selected_app(p_treeview);
1237
1238 if (app)
1239 {
1240 GString *p_gstring = g_string_new("");
1241 if (g_app_info_get_description(app))
1242 g_string_printf(p_gstring, "<i>%s</i>", g_app_info_get_description(app));
1243 else
1244 g_string_printf(p_gstring, "<i>%s</i>", g_app_info_get_name(app));
1245 gtk_label_set_markup(GTK_LABEL(lb->p_label_menu_app_exec), p_gstring->str);
1246 g_string_free(p_gstring, TRUE/*free also gstring->str*/);
1247 label_set = TRUE;
1248 }
1249 gtk_widget_set_visible(lb->p_label_menu_app_exec, label_set);
1250 gtk_widget_set_sensitive(lb->p_button_add, label_set);
1251 }
1252
1253 static void on_menu_view_row_activated(GtkTreeView *tree_view, GtkTreePath *path,
1254 GtkTreeViewColumn *column,
1255 LaunchTaskBarPlugin *ltbp)
1256 {
1257 _launchbar_configure_add(tree_view, ltbp);
1258 }
1259
1260 /* FIXME: add support for global hotkeys for launchers */
1261
1262 /* Callback when the configuration dialog is to be shown. */
1263 static GtkWidget *launchtaskbar_configure(LXPanel *panel, GtkWidget *p)
1264 {
1265 LaunchTaskBarPlugin *ltbp = lxpanel_plugin_get_data(p);
1266
1267 {
1268 GtkWidget *dlg, *btn, *defined_view, *menu_view, *menu_view_window;
1269 GtkBuilder *builder = gtk_builder_new();
1270 GObject *object;
1271
1272 gtk_builder_add_from_file(builder, PACKAGE_UI_DIR "/launchtaskbar.ui", NULL);
1273 dlg = (GtkWidget *)gtk_builder_get_object(builder, "dlg");
1274 panel_apply_icon(GTK_WINDOW(dlg));
1275
1276 defined_view = (GtkWidget *)gtk_builder_get_object(builder, "defined_view");
1277 menu_view_window = (GtkWidget*)gtk_builder_get_object(builder, "menu_view_window");
1278 if (menu_view_window == NULL) /* fallback for old glade file */
1279 {
1280 menu_view_window = (GtkWidget*)gtk_builder_get_object(builder, "scroll2");
1281 gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(menu_view_window)));
1282 }
1283 menu_view = GTK_WIDGET(fm_app_menu_view_new());
1284 gtk_container_add(GTK_CONTAINER(menu_view_window), menu_view);
1285 gtk_widget_show(menu_view);
1286 ltbp->p_label_def_app_exec = (GtkWidget*)gtk_builder_get_object(builder, "label_def_app_exec");
1287 ltbp->p_label_menu_app_exec = (GtkWidget*)gtk_builder_get_object(builder, "label_menu_app_exec");
1288
1289 /* Connect signals. */
1290 ltbp->p_button_add = (GtkWidget *)gtk_builder_get_object(builder, "button_add");
1291 g_signal_connect(ltbp->p_button_add, "clicked", G_CALLBACK(launchbar_configure_add_button), ltbp);
1292
1293 ltbp->p_button_remove = (GtkWidget *)gtk_builder_get_object(builder, "button_remove");
1294 g_signal_connect(ltbp->p_button_remove, "clicked", G_CALLBACK(launchbar_configure_remove_button), ltbp);
1295
1296 btn = (GtkWidget *)gtk_builder_get_object(builder, "button_up");
1297 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_up_button), ltbp);
1298
1299 btn = (GtkWidget *)gtk_builder_get_object(builder, "button_down");
1300 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_down_button), ltbp);
1301
1302 /* FIXME: add a button 'New' with launcher creation dialog */
1303
1304 g_signal_connect(defined_view, "button-press-event", G_CALLBACK(on_defined_view_button_press_event), ltbp);
1305 g_signal_connect(defined_view, "cursor-changed", G_CALLBACK(on_defined_view_cursor_changed), ltbp);
1306 g_signal_connect(menu_view, "cursor-changed", G_CALLBACK(on_menu_view_cursor_changed), ltbp);
1307 g_signal_connect(menu_view, "row-activated", G_CALLBACK(on_menu_view_row_activated), ltbp);
1308
1309 ltbp->p_notebook = GTK_NOTEBOOK(gtk_builder_get_object(builder, "notebook"));
1310 ltbp->p_notebook_page_launch = gtk_notebook_get_nth_page(ltbp->p_notebook, 0);
1311 ltbp->p_notebook_page_task = gtk_notebook_get_nth_page(ltbp->p_notebook, 1);
1312 set_config_visibility(ltbp);
1313 object = gtk_builder_get_object(builder, "combobox_mode");
1314 gtk_combo_box_set_active(GTK_COMBO_BOX(object), ltbp->mode);
1315 g_signal_connect(object, "changed",
1316 G_CALLBACK(on_combobox_mode_changed), ltbp);
1317
1318 #define SETUP_TOGGLE_BUTTON(button,member) \
1319 object = gtk_builder_get_object(builder, #button); \
1320 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object), ltbp->flags.member); \
1321 g_signal_connect(object, "toggled", G_CALLBACK(on_##button##_toggled), ltbp)
1322
1323 SETUP_TOGGLE_BUTTON(checkbutton_show_tooltips, tooltips);
1324 SETUP_TOGGLE_BUTTON(checkbutton_icons_only, icons_only);
1325 SETUP_TOGGLE_BUTTON(checkbutton_flat_buttons, flat_button);
1326 SETUP_TOGGLE_BUTTON(checkbutton_show_all_desks, show_all_desks);
1327 SETUP_TOGGLE_BUTTON(checkbutton_same_monitor_only, same_monitor_only);
1328 SETUP_TOGGLE_BUTTON(checkbutton_mouse_wheel, use_mouse_wheel);
1329 SETUP_TOGGLE_BUTTON(checkbutton_urgency_hint, use_urgency_hint);
1330 //SETUP_TOGGLE_BUTTON(checkbutton_disable_taskbar_upscale, disable_taskbar_upscale);
1331 #undef SETUP_TOGGLE_BUTTON
1332 object = gtk_builder_get_object(builder, "checkbutton_grouped_tasks");
1333 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object), ltbp->grouped_tasks);
1334 g_signal_connect(object, "toggled", G_CALLBACK(on_checkbutton_grouped_tasks_toggled), ltbp);
1335 /* FIXME: for transitional period, turn into SETUP_TOGGLE_BUTTON later */
1336 object = gtk_builder_get_object(builder, "checkbutton_disable_taskbar_upscale");
1337 if (object)
1338 {
1339 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object), ltbp->flags.disable_taskbar_upscale);
1340 g_signal_connect(object, "toggled", G_CALLBACK(on_checkbutton_disable_taskbar_upscale_toggled), ltbp);
1341 }
1342
1343 #define SETUP_SPIN_BUTTON(button,member) \
1344 object = gtk_builder_get_object(builder, #button); \
1345 gtk_spin_button_set_value(GTK_SPIN_BUTTON(object), ltbp->member); \
1346 g_signal_connect(object, "value-changed", \
1347 G_CALLBACK(on_##button##_value_changed), ltbp)
1348
1349 SETUP_SPIN_BUTTON(spinbutton_max_width, task_width_max);
1350 SETUP_SPIN_BUTTON(spinbutton_spacing, spacing);
1351 #undef SETUP_SPIN_BUTTON
1352
1353 ltbp->config_dlg = dlg;
1354
1355 /* Initialize the tree view contents. */
1356 launchbar_configure_initialize_list(ltbp, dlg, GTK_TREE_VIEW(defined_view));
1357 g_object_set_data(G_OBJECT(dlg), "menu_view", menu_view);
1358
1359 gtk_widget_set_visible(ltbp->p_label_menu_app_exec, FALSE);
1360 gtk_widget_set_visible(ltbp->p_label_def_app_exec, FALSE);
1361 gtk_widget_set_sensitive(ltbp->p_button_add, FALSE);
1362 gtk_widget_set_sensitive(ltbp->p_button_remove, FALSE);
1363 if (ltbp->fixed_mode)
1364 {
1365 object = gtk_builder_get_object(builder, "hbox_mode");
1366 if (object)
1367 gtk_widget_destroy(GTK_WIDGET(object));
1368 if (ltbp->mode == LAUNCHBAR)
1369 gtk_window_set_title(GTK_WINDOW(ltbp->config_dlg),
1370 _("Application Launch Bar"));
1371 else
1372 gtk_window_set_title(GTK_WINDOW(ltbp->config_dlg),
1373 _("Task Bar (Window List)"));
1374 }
1375
1376 g_object_unref(builder);
1377 }
1378 return ltbp->config_dlg;
1379 }
1380
1381 /* Callback when panel configuration changes. */
1382 static void launchtaskbar_panel_configuration_changed(LXPanel *panel, GtkWidget *p)
1383 {
1384 /* Set orientation into the icon grid. */
1385 LaunchTaskBarPlugin *ltbp = lxpanel_plugin_get_data(p);
1386 int new_icon_size = panel_get_icon_size(panel);
1387 int height = panel_get_height(panel);
1388 int border = (height - new_icon_size) > 1 ? 1 : 0;
1389
1390 if (ltbp->lb_built)
1391 panel_icon_grid_set_geometry(PANEL_ICON_GRID(ltbp->lb_icon_grid),
1392 panel_get_orientation(panel),
1393 new_icon_size, new_icon_size,
1394 3 - 2 * border, border, height);
1395
1396 /* Redraw all the labels. Icon size or font color may have changed. */
1397 if (ltbp->tb_built)
1398 {
1399 ltbp->icon_size = new_icon_size;
1400 panel_icon_grid_set_geometry(PANEL_ICON_GRID(ltbp->tb_icon_grid),
1401 panel_get_orientation(ltbp->panel),
1402 ((ltbp->flags.icons_only) ? ltbp->icon_size + ICON_ONLY_EXTRA : ltbp->task_width_max),
1403 ltbp->icon_size, ltbp->spacing, 0, panel_get_height(ltbp->panel));
1404 taskbar_reset_menu(ltbp);
1405 taskbar_redraw(ltbp);
1406 }
1407 }
1408
1409 /* Redraw all tasks in the taskbar. */
1410 static void taskbar_redraw(LaunchTaskBarPlugin * tb)
1411 {
1412 GList *children = gtk_container_get_children(GTK_CONTAINER(tb->tb_icon_grid));
1413 GList *l;
1414 guint mon = panel_get_monitor(tb->panel);
1415 guint icon_size = panel_get_icon_size(tb->panel);
1416
1417 for (l = children; l; l = l->next)
1418 task_button_update(l->data, tb->current_desktop, tb->number_of_desktops,
1419 mon, icon_size, tb->flags);
1420 g_list_free(children);
1421 }
1422
1423 /* Determine if a task should be visible given its NET_WM_STATE. */
1424 static gboolean accept_net_wm_state(NetWMState * nws)
1425 {
1426 return ( ! (nws->skip_taskbar));
1427 }
1428
1429 /* Determine if a task should be visible given its NET_WM_WINDOW_TYPE. */
1430 static gboolean accept_net_wm_window_type(NetWMWindowType * nwwt)
1431 {
1432 return ( ! ((nwwt->desktop) || (nwwt->dock) || (nwwt->splash)));
1433 }
1434
1435 /* Set the class associated with a task. */
1436 static char *task_get_class(Window win)
1437 {
1438 /* Read the WM_CLASS property. */
1439 XClassHint ch;
1440 ch.res_name = NULL;
1441 ch.res_class = NULL;
1442 char *res_class = NULL;
1443 XGetClassHint(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, &ch);
1444
1445 /* If the res_name was returned, free it. We make no use of it at this time. */
1446 if (ch.res_name != NULL)
1447 {
1448 XFree(ch.res_name);
1449 }
1450
1451 /* If the res_class was returned, process it.
1452 * This identifies the application that created the window and is the basis for taskbar grouping. */
1453 if (ch.res_class != NULL)
1454 {
1455 /* Convert the class to UTF-8 and enter it in the class table. */
1456 res_class = g_locale_to_utf8(ch.res_class, -1, NULL, NULL, NULL);
1457 XFree(ch.res_class);
1458 }
1459 return res_class;
1460 }
1461
1462 /* Look up a task in the task list. */
1463 static TaskButton *task_lookup(LaunchTaskBarPlugin * tb, Window win)
1464 {
1465 TaskButton *task = NULL;
1466 GList *children = gtk_container_get_children(GTK_CONTAINER(tb->tb_icon_grid));
1467 GList *l;
1468
1469 for (l = children; l; l = l->next)
1470 if (task_button_has_window(l->data, win))
1471 {
1472 task = l->data;
1473 break;
1474 }
1475 g_list_free(children);
1476 return task;
1477 }
1478
1479
1480 #ifndef DISABLE_MENU
1481 static void on_menuitem_lock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb);
1482 static void on_menuitem_unlock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb);
1483 static void on_menuitem_new_instance_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb);
1484 #endif
1485
1486 static void on_task_menu_built(GtkWidget *unused, GtkMenu *menu, LaunchTaskBarPlugin *tb)
1487 {
1488 #ifndef DISABLE_MENU
1489 /* add callbacks for task-to-launcher items with weak pointers */
1490 void (*_m_add)(GtkMenuShell *self, GtkWidget* child);
1491
1492 if (panel_is_at_bottom(tb->panel))
1493 _m_add = gtk_menu_shell_append;
1494 else
1495 _m_add = gtk_menu_shell_prepend;
1496
1497 tb->p_menuitem_lock_tbp = gtk_menu_item_new_with_mnemonic(_("A_dd to Launcher"));
1498 g_object_add_weak_pointer(G_OBJECT(menu), (void **)&tb->p_menuitem_lock_tbp);
1499 tb->p_menuitem_unlock_tbp = gtk_menu_item_new_with_mnemonic(_("Rem_ove from Launcher"));
1500 g_object_add_weak_pointer(G_OBJECT(menu), (void **)&tb->p_menuitem_lock_tbp);
1501 tb->p_menuitem_new_instance = gtk_menu_item_new_with_mnemonic(_("_New Instance"));
1502 g_object_add_weak_pointer(G_OBJECT(menu), (void **)&tb->p_menuitem_lock_tbp);
1503 tb->p_menuitem_separator = gtk_separator_menu_item_new();
1504 _m_add(GTK_MENU_SHELL(menu), tb->p_menuitem_separator);
1505 _m_add(GTK_MENU_SHELL(menu), tb->p_menuitem_lock_tbp);
1506 _m_add(GTK_MENU_SHELL(menu), tb->p_menuitem_unlock_tbp);
1507 _m_add(GTK_MENU_SHELL(menu), tb->p_menuitem_new_instance);
1508 g_signal_connect(G_OBJECT(tb->p_menuitem_lock_tbp), "activate", (GCallback)on_menuitem_lock_tbp_clicked, tb);
1509 g_signal_connect(G_OBJECT(tb->p_menuitem_unlock_tbp), "activate", (GCallback)on_menuitem_unlock_tbp_clicked, tb);
1510 g_signal_connect(G_OBJECT(tb->p_menuitem_new_instance), "activate", (GCallback)on_menuitem_new_instance_clicked, tb);
1511 #endif
1512 }
1513
1514 static void on_task_menu_target_set(TaskButton *btn, gulong win, LaunchTaskBarPlugin *ltbp)
1515 {
1516 #ifndef DISABLE_MENU
1517 if(ltbp->mode == LAUNCHTASKBAR)
1518 {
1519 FmPath *path = f_find_menu_launchbutton_recursive(win, ltbp);
1520 LaunchButton *btn = launchbar_exec_bin_exists(ltbp, path);
1521 /* FIXME: shouldn't we make file info at task button creation? */
1522 #ifdef DEBUG
1523 g_print("\nTB '%s' right-click, in LB: %c\n", tk->exec_bin, btn != NULL ? 'Y':'N');
1524 #endif
1525 if(btn != NULL)
1526 {
1527 gtk_widget_set_visible(ltbp->p_menuitem_lock_tbp, FALSE);
1528 gtk_widget_set_visible(ltbp->p_menuitem_unlock_tbp, TRUE);
1529 gtk_widget_set_visible(ltbp->p_menuitem_new_instance, TRUE);
1530 }
1531 else
1532 {
1533 gtk_widget_set_visible(ltbp->p_menuitem_lock_tbp, path != NULL);
1534 gtk_widget_set_visible(ltbp->p_menuitem_unlock_tbp, FALSE);
1535 gtk_widget_set_visible(ltbp->p_menuitem_new_instance, path != NULL);
1536 }
1537 gtk_widget_set_visible(ltbp->p_menuitem_separator, TRUE);
1538 if (ltbp->path)
1539 fm_path_unref(ltbp->path);
1540 ltbp->path = path;
1541 }
1542 else
1543 {
1544 gtk_widget_set_visible(ltbp->p_menuitem_lock_tbp, FALSE);
1545 gtk_widget_set_visible(ltbp->p_menuitem_unlock_tbp, FALSE);
1546 gtk_widget_set_visible(ltbp->p_menuitem_new_instance, FALSE);
1547 gtk_widget_set_visible(ltbp->p_menuitem_separator, FALSE);
1548 }
1549 #endif
1550 }
1551
1552 /* Handler for "drag-motion" timeout. */
1553 static gboolean taskbar_button_drag_motion_timeout(LaunchTaskBarPlugin * tb)
1554 {
1555 //guint time;
1556 if (g_source_is_destroyed(g_main_current_source()))
1557 return FALSE;
1558 //time = gtk_get_current_event_time();
1559 //task_raise_window(tk, ((time != 0) ? time : CurrentTime)); // ???
1560 tb->dnd_delay_timer = 0;
1561 return FALSE;
1562 }
1563
1564 /* Handler for "drag-motion" event from taskbar button. */
1565 static gboolean taskbar_button_drag_motion(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, LaunchTaskBarPlugin * tb)
1566 {
1567 GtkWidget * drag_source = gtk_drag_get_source_widget(drag_context);
1568 if (drag_source != NULL && gtk_widget_get_parent(drag_source) == gtk_widget_get_parent(widget))
1569 {
1570 tb->dnd_task_moving = TRUE;
1571 gdk_drag_status(drag_context, GDK_ACTION_MOVE, time);
1572 }
1573 else
1574 {
1575 /* Prevent excessive motion notification. */
1576 if (tb->dnd_delay_timer == 0)
1577 tb->dnd_delay_timer = g_timeout_add(DRAG_ACTIVE_DELAY, (GSourceFunc) taskbar_button_drag_motion_timeout, tb);
1578
1579 gdk_drag_status(drag_context, 0, time);
1580 }
1581 return TRUE;
1582 }
1583
1584 /* Handler for "drag-drop" event from taskbar button. */
1585 static gboolean taskbar_button_drag_drop(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, LaunchTaskBarPlugin * tb)
1586 {
1587 tb->dnd_task_moving = FALSE;
1588 GtkWidget * drag_source = gtk_drag_get_source_widget(drag_context);
1589 if (drag_source != NULL && gtk_widget_get_parent(drag_source) == gtk_widget_get_parent(widget))
1590 {
1591 if (drag_source != widget)
1592 {
1593 PanelIconGrid *ig = PANEL_ICON_GRID(tb->tb_icon_grid);
1594 gint i = panel_icon_grid_get_child_position(ig, widget);
1595 panel_icon_grid_reorder_child(ig, drag_source, i);
1596 }
1597 gtk_drag_finish(drag_context, TRUE, TRUE, time);
1598 return TRUE;
1599 }
1600
1601 return FALSE;
1602 }
1603
1604 /* Handler for "drag-leave" event from taskbar button. */
1605 static void taskbar_button_drag_leave(GtkWidget * widget, GdkDragContext * drag_context, guint time, LaunchTaskBarPlugin * tb)
1606 {
1607 /* Cancel the timer if set. */
1608 if (tb->dnd_delay_timer != 0)
1609 {
1610 g_source_remove(tb->dnd_delay_timer);
1611 tb->dnd_delay_timer = 0;
1612 }
1613 return;
1614 }
1615
1616 /* Handler for "enter" event from taskbar button. This indicates that the cursor position has entered the button. */
1617 static void taskbar_button_enter(GtkWidget * widget, GdkEvent *event, LaunchTaskBarPlugin * tb)
1618 {
1619 tb->dnd_task_moving = FALSE;
1620 }
1621
1622 /* Handler for "button-release-event" event from taskbar button. */
1623 static gboolean taskbar_button_release_event(GtkWidget * widget, GdkEventButton * event, LaunchTaskBarPlugin * tb)
1624 {
1625 if (tb->dnd_task_moving)
1626 /* SF bug#731: don't process button release with DND. Also if button was
1627 released outside of widget but DND wasn't activated: this might happen
1628 if drag started at edge of button so drag treshold wasn't reached. */
1629 return TRUE;
1630 return FALSE;
1631 }
1632
1633 enum {
1634 TARGET_TASK_BUTTON
1635 };
1636
1637 static GtkTargetEntry task_button_target_list[] = {
1638 { "task_button", GTK_TARGET_SAME_APP, TARGET_TASK_BUTTON }
1639 };
1640
1641 static guint task_button_n_targets = G_N_ELEMENTS(task_button_target_list);
1642
1643 /* Build graphic elements needed for a task button. */
1644 static void taskbar_add_task_button(LaunchTaskBarPlugin * tb, TaskButton * task)
1645 {
1646 /* Allocate a toggle button as the top level widget. */
1647 gtk_container_add(GTK_CONTAINER(tb->tb_icon_grid), GTK_WIDGET(task));
1648 gtk_widget_show(GTK_WIDGET(task));
1649
1650 /* Connect signals to the button. */
1651 /* handle menu callbacks */
1652 g_signal_connect(G_OBJECT(task), "menu-built",
1653 (GCallback)on_task_menu_built, tb);
1654 g_signal_connect(G_OBJECT(task), "menu-target-set",
1655 (GCallback)on_task_menu_target_set, tb);
1656 /* handle drag & drop on task buttons */
1657 gtk_drag_dest_set(GTK_WIDGET(task), 0, NULL, 0, 0);
1658 gtk_drag_source_set(GTK_WIDGET(task), GDK_BUTTON1_MASK,
1659 task_button_target_list, task_button_n_targets,
1660 GDK_ACTION_MOVE);
1661 g_signal_connect(G_OBJECT(task), "drag-motion",
1662 G_CALLBACK(taskbar_button_drag_motion), tb);
1663 g_signal_connect(G_OBJECT(task), "drag-leave",
1664 G_CALLBACK(taskbar_button_drag_leave), tb);
1665 g_signal_connect(G_OBJECT(task), "drag-drop",
1666 G_CALLBACK(taskbar_button_drag_drop), tb);
1667 g_signal_connect(task, "button-release-event",
1668 G_CALLBACK(taskbar_button_release_event), tb);
1669 g_signal_connect_after(G_OBJECT(task), "enter-notify-event",
1670 G_CALLBACK(taskbar_button_enter), tb);
1671 }
1672
1673 /* add win to tb, using list of task buttons */
1674 static void taskbar_add_new_window(LaunchTaskBarPlugin * tb, Window win, GList *list)
1675 {
1676 gchar *res_class = task_get_class(win);
1677 TaskButton *task;
1678
1679 if (!tb->grouped_tasks || res_class == NULL)
1680 list = NULL;
1681 else for (; list; list = list->next)
1682 if (task_button_add_window(list->data, win, res_class))
1683 break;
1684 if (list != NULL)
1685 return; /* some button accepted it, done */
1686
1687 task = task_button_new(win, tb->current_desktop, tb->number_of_desktops,
1688 tb->panel, res_class, tb->flags);
1689 taskbar_add_task_button(tb, task);
1690 }
1691
1692 /*****************************************************
1693 * handlers for NET actions *
1694 *****************************************************/
1695
1696 /* Handler for "client-list" event from root window listener. */
1697 static void taskbar_net_client_list(GtkWidget * widget, LaunchTaskBarPlugin * tb)
1698 {
1699 LaunchTaskBarPlugin *ltbp = tb;
1700 if(ltbp->mode == LAUNCHBAR) return;
1701
1702 /* Get the NET_CLIENT_LIST property. */
1703 int client_count;
1704 Window * client_list = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_CLIENT_LIST, XA_WINDOW, &client_count);
1705 if (client_list != NULL)
1706 {
1707 GList *children = gtk_container_get_children(GTK_CONTAINER(tb->tb_icon_grid)), *l;
1708 /* Remove windows from the task list that are not present in the NET_CLIENT_LIST. */
1709 for (l = children; l; l = l->next)
1710 task_button_update_windows_list(l->data, client_list, client_count);
1711 g_list_free(children);
1712 children = gtk_container_get_children(GTK_CONTAINER(tb->tb_icon_grid));
1713 /* Loop over client list, correlating it with task list. */
1714 int i;
1715 for (i = 0; i < client_count; i++)
1716 {
1717 /* Search for the window in the task list. Set up context to do an insert right away if needed. */
1718 for (l = children; l; l = l->next)
1719 {
1720 if (task_button_has_window(l->data, client_list[i]))
1721 break;
1722 }
1723
1724 /* Task is not in task list. */
1725 if (l == NULL)
1726 {
1727 /* Evaluate window state and window type to see if it should be in task list. */
1728 NetWMWindowType nwwt;
1729 NetWMState nws;
1730 get_net_wm_state(client_list[i], &nws);
1731 get_net_wm_window_type(client_list[i], &nwwt);
1732 if ((accept_net_wm_state(&nws))
1733 && (accept_net_wm_window_type(&nwwt)))
1734 {
1735 /* Allocate and initialize new task structure. */
1736 taskbar_add_new_window(tb, client_list[i], children);
1737 g_list_free(children);
1738 children = gtk_container_get_children(GTK_CONTAINER(tb->tb_icon_grid));
1739 }
1740 }
1741 }
1742 g_list_free(children);
1743 XFree(client_list);
1744 }
1745
1746 else /* clear taskbar */
1747 gtk_container_foreach(GTK_CONTAINER(tb->tb_icon_grid),
1748 (GtkCallback)gtk_widget_destroy, NULL);
1749 }
1750
1751 /* Handler for "current-desktop" event from root window listener. */
1752 static void taskbar_net_current_desktop(GtkWidget * widget, LaunchTaskBarPlugin * tb)
1753 {
1754 LaunchTaskBarPlugin *ltbp = tb;
1755 if(ltbp->mode == LAUNCHBAR) return;
1756
1757 /* Store the local copy of current desktops. Redisplay the taskbar. */
1758 tb->current_desktop = get_net_current_desktop();
1759 taskbar_redraw(tb);
1760 }
1761
1762 /* Handler for "number-of-desktops" event from root window listener. */
1763 static void taskbar_net_number_of_desktops(GtkWidget * widget, LaunchTaskBarPlugin * tb)
1764 {
1765 LaunchTaskBarPlugin *ltbp = tb;
1766 if(ltbp->mode == LAUNCHBAR) return;
1767
1768 /* Store the local copy of number of desktops. Recompute the popup menu and redisplay the taskbar. */
1769 tb->number_of_desktops = get_net_number_of_desktops();
1770 taskbar_reset_menu(tb);
1771 taskbar_redraw(tb);
1772 }
1773
1774 /* Handler for "active-window" event from root window listener. */
1775 static void taskbar_net_active_window(GtkWidget * widget, LaunchTaskBarPlugin * tb)
1776 {
1777 LaunchTaskBarPlugin *ltbp = tb;
1778 if(ltbp->mode == LAUNCHBAR) return;
1779
1780 /* Get the window that has focus. */
1781 Window * f = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_ACTIVE_WINDOW, XA_WINDOW, 0);
1782
1783 gtk_container_foreach(GTK_CONTAINER(tb->tb_icon_grid),
1784 (GtkCallback)task_button_window_focus_changed, f);
1785 if (f != NULL)
1786 XFree(f);
1787 }
1788
1789 /* Handle PropertyNotify event.
1790 * http://tronche.com/gui/x/icccm/
1791 * http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html */
1792 static void taskbar_property_notify_event(LaunchTaskBarPlugin *tb, XEvent *ev)
1793 {
1794 /* State may be PropertyNewValue, PropertyDeleted. */
1795 if (((XPropertyEvent*) ev)->state == PropertyNewValue)
1796 {
1797 Atom at = ev->xproperty.atom;
1798 Window win = ev->xproperty.window;
1799 if (win != GDK_ROOT_WINDOW())
1800 {
1801 /* Look up task structure by X window handle. */
1802 TaskButton * tk = task_lookup(tb, win);
1803 if (tk != NULL)
1804 {
1805 /* Install an error handler that ignores BadWindow.
1806 * We frequently get a PropertyNotify event on deleted windows. */
1807 XErrorHandler previous_error_handler = XSetErrorHandler(panel_handle_x_error_swallow_BadWindow_BadDrawable);
1808
1809 /* Dispatch on atom. */
1810 if (at == a_NET_WM_STATE)
1811 {
1812 /* Window changed EWMH state. */
1813 NetWMState nws;
1814 get_net_wm_state(win, &nws);
1815 if ( ! accept_net_wm_state(&nws))
1816 task_button_drop_window(tk, win, FALSE);
1817 }
1818 else if (at == a_NET_WM_WINDOW_TYPE)
1819 {
1820 /* Window changed EWMH window type. */
1821 NetWMWindowType nwwt;
1822 get_net_wm_window_type(win, &nwwt);
1823 if ( ! accept_net_wm_window_type(&nwwt))
1824 task_button_drop_window(tk, win, FALSE);
1825 }
1826 else if (at == XA_WM_CLASS && tb->grouped_tasks
1827 && task_button_drop_window(tk, win, TRUE))
1828 {
1829 GList *children = gtk_container_get_children(GTK_CONTAINER(tb->tb_icon_grid));
1830 /* if Window was not single window of that class then
1831 add it to another class or make another button */
1832 taskbar_add_new_window(tb, win, children);
1833 g_list_free(children);
1834 }
1835 else
1836 {
1837 /* simply notify button, it will handle the event */
1838 task_button_window_xprop_changed(tk, win, at);
1839 }
1840
1841 XSetErrorHandler(previous_error_handler);
1842 }
1843 }
1844 }
1845 }
1846
1847 /* Handle ConfigureNotify events */
1848 static void taskbar_configure_notify_event(LaunchTaskBarPlugin * tb, XConfigureEvent * ev)
1849 {
1850 /* If the same_monitor_only option is set and the window is on a different
1851 monitor than before, redraw the taskbar */
1852 TaskButton *task;
1853
1854 if (ev->window != GDK_ROOT_WINDOW())
1855 {
1856 task = task_lookup(tb, ev->window);
1857 if (task)
1858 {
1859 /* Deleted windows seem to get ConfigureNotify events too. */
1860 XErrorHandler previous_error_handler = XSetErrorHandler(panel_handle_x_error_swallow_BadWindow_BadDrawable);
1861
1862 /* Monitor might be changed so button might need update */
1863 task_button_window_reconfigured(task, ev->window);
1864
1865 XSetErrorHandler(previous_error_handler);
1866 }
1867 }
1868 }
1869
1870 /* GDK event filter. */
1871 static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, LaunchTaskBarPlugin * tb)
1872 {
1873 if (tb->mode == LAUNCHBAR)
1874 return GDK_FILTER_CONTINUE;
1875
1876 if (xev->type == PropertyNotify)
1877 taskbar_property_notify_event(tb, xev);
1878 else if (xev->type == ConfigureNotify)
1879 taskbar_configure_notify_event(tb, &xev->xconfigure);
1880
1881 return GDK_FILTER_CONTINUE;
1882 }
1883
1884 #ifndef DISABLE_MENU
1885 static void on_menuitem_lock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
1886 {
1887 LaunchButton *btn;
1888 char *path;
1889 config_setting_t *settings;
1890
1891 if (tb->path)
1892 {
1893 /* Create a button and add settings for it */
1894 path = fm_path_to_str(tb->path);
1895 /* g_debug("*** path '%s'",path); */
1896 settings = config_group_add_subgroup(tb->settings, "Button");
1897 config_group_set_string(settings, "id", path);
1898 g_free(path);
1899 btn = launch_button_new(tb->panel, tb->plugin, tb->path, settings);
1900 if (btn)
1901 gtk_container_add(GTK_CONTAINER(tb->lb_icon_grid), GTK_WIDGET(btn));
1902 else
1903 config_setting_destroy(settings);
1904 lxpanel_config_save(tb->panel);
1905 }
1906 }
1907
1908 static void on_menuitem_unlock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
1909 {
1910 LaunchButton *btn = launchbar_exec_bin_exists(tb, tb->path);
1911 if(btn != NULL)
1912 {
1913 launchbar_remove_button(tb, btn);
1914 lxpanel_config_save(tb->panel);
1915 }
1916 }
1917
1918 static void on_menuitem_new_instance_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
1919 {
1920 if (tb->path)
1921 {
1922 lxpanel_launch_path(tb->panel, tb->path);
1923 }
1924 }
1925 #endif
1926
1927 /* Handler for "window-manager-changed" event. */
1928 static void taskbar_window_manager_changed(GdkScreen * screen, LaunchTaskBarPlugin * tb)
1929 {
1930 /* Force re-evaluation of use_net_active. */
1931 GdkAtom net_active_atom = gdk_x11_xatom_to_atom(a_NET_ACTIVE_WINDOW);
1932 tb->flags.use_net_active = gdk_x11_screen_supports_net_wm_hint(tb->screen, net_active_atom);
1933 taskbar_redraw(tb);
1934 }
1935
1936 /* Callback from configuration dialog mechanism to apply the configuration. */
1937 static void taskbar_apply_configuration(LaunchTaskBarPlugin *ltbp)
1938 {
1939 taskbar_redraw(ltbp);
1940 }
1941
1942 static GtkWidget *launchbar_constructor(LXPanel *panel, config_setting_t *settings)
1943 {
1944 return _launchtaskbar_constructor(panel, settings, LAUNCHBAR);
1945 }
1946
1947 static GtkWidget *taskbar_constructor(LXPanel *panel, config_setting_t *settings)
1948 {
1949 return _launchtaskbar_constructor(panel, settings, TASKBAR);
1950 }
1951
1952 static LXPanelPluginInit _launchbar_init = {
1953 .name = N_("Application Launch Bar"),
1954 .description = N_("Bar with buttons to launch application"),
1955
1956 .new_instance = launchbar_constructor,
1957 .config = launchtaskbar_configure,
1958 .reconfigure = launchtaskbar_panel_configuration_changed
1959 };
1960
1961 static LXPanelPluginInit _taskbar_init = {
1962 .name = N_("Task Bar (Window List)"),
1963 .description = N_("Taskbar shows all opened windows and allow to iconify them, shade or get focus"),
1964
1965 .expand_available = TRUE,
1966 .expand_default = TRUE,
1967
1968 .new_instance = taskbar_constructor,
1969 .config = launchtaskbar_configure,
1970 .reconfigure = launchtaskbar_panel_configuration_changed
1971 };
1972
1973 static void launchtaskbar_init(void)
1974 {
1975 lxpanel_register_plugin_type("launchbar", &_launchbar_init);
1976 lxpanel_register_plugin_type("taskbar", &_taskbar_init);
1977 }
1978
1979 /* Plugin descriptor. */
1980 LXPanelPluginInit lxpanel_static_plugin_launchtaskbar = {
1981 .name = N_("Application Launch and Task Bar"),
1982 .description = N_("Bar with buttons to launch application and/or show all opened windows"),
1983
1984 .expand_available = TRUE,
1985 .expand_default = TRUE,
1986
1987 .init = launchtaskbar_init,
1988 .new_instance = launchtaskbar_constructor,
1989 .config = launchtaskbar_configure,
1990 /* .update_context_menu = launchtaskbar_update_context_menu, */
1991 .reconfigure = launchtaskbar_panel_configuration_changed
1992 };
1993
1994
1995 /* vim: set sw=4 sts=4 et : */