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