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