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