Add a workaround on GtkBox behavior which does not send allocation requests to children.
[lxde/lxpanel.git] / plugins / launchtaskbar.c
1 /*
2 * Copyright (C) 2006-2009 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
3 * 2006-2008 Jim Huang <jserv.tw@gmail.com>
4 * 2008 Fred Chien <fred@lxde.org>
5 * 2009 Andrew Lee <ajqlee@debian.org>
6 * 2009 Jürgen Hötzel <juergen@archlinux.org>
7 * 2009 Ying-Chun Liu (PaulLiu) <grandpaul@gmail.com>
8 * 2009-2010 Marty Jack <martyj19@comcast.net>
9 * 2010 Julien Lavergne <julien.lavergne@gmail.com>
10 * 2011-2014 Henry Gebhardt <hsggebhardt@gmail.com>
11 * 2012 Piotr Sipika <Piotr.Sipika@gmail.com>
12 * 2012-2014 Giuseppe Penone <giuspen@gmail.com>
13 * 2013 Vincenzo di Cicco <enzodicicco@gmail.com>
14 * 2013 Rouslan <rouslan-k@users.sourceforge.net>
15 * 2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
16 * 2014 Vladimír Pýcha <vpycha@gmail.com>
17 * 2014 Raimar Bühmann <raimar@buehmann.de>
18 * 2014 Andy Balaam <axis3x3@users.sf.net>
19 * 2015 Balló György <ballogyor@gmail.com>
20 * 2015 Rafał Mużyło <galtgendo@gmail.com>
21 *
22 * This file is a part of LXPanel project.
23 *
24 * This program is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation; either version 2 of the License, or
27 * (at your option) any later version.
28 *
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
33 *
34 * You should have received a copy of the GNU General Public License
35 * along with this program; if not, write to the Free Software Foundation,
36 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
37 */
38
39 /*
40 * Started by Giuseppe Penone <giuspen@gmail.com> merging launchbar and taskbar
41 * and adding interoperability between them.
42 */
43
44 /*
45 * Taskbar plugin:
46 * 2006.09.10 modified by Hong Jen Yee (PCMan) pcman.tw (AT) gmail.com
47 * Following features are added:
48 * 1. Add XUrgencyHint support. (Flashing task bar buttons, can be disabled)
49 * 2. Raise window when files get dragged over taskbar buttons.
50 * 3. Add Restore & Maximize menu items to popup menu of task bar buttons.
51 */
52
53 //#define DEBUG // killall lxpanel && lxpanel --profile Lubuntu &
54
55 #ifdef HAVE_CONFIG_H
56 #include <config.h>
57 #endif
58
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 #include <sys/types.h>
64 #include <sys/wait.h>
65 #include <signal.h>
66 #include <errno.h>
67 #include <X11/Xlib.h>
68 #include <X11/Xutil.h>
69
70 #include <gdk-pixbuf/gdk-pixbuf.h>
71 #include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
72 #include <gdk/gdk.h>
73 #include <glib/gi18n.h>
74
75 #include <libfm/fm-gtk.h>
76
77 #include "misc.h"
78 #include "ev.h"
79 #include "plugin.h"
80 #include "task-button.h"
81 #include "launch-button.h"
82 #include "icon-grid.h"
83 #ifndef DISABLE_MENU
84 # include "menu-policy.h"
85 #endif
86 #include "gtk-compat.h"
87
88 #define PANEL_ICON_SIZE 24 /* see the private.h */
89
90 /* Column definitions for configuration dialogs. */
91 enum {
92 COL_ICON,
93 COL_TITLE,
94 COL_ICON_NAME,
95 COL_BTN,
96 N_COLS
97 };
98
99 typedef enum {
100 LAUNCHBAR = 0, /* GtkComboBox is 0-indexed. */
101 TASKBAR,
102 LAUNCHTASKBAR
103 } LtbMode;
104
105 typedef struct LaunchTaskBarPlugin LaunchTaskBarPlugin;
106
107 /* Private context for taskbar plugin. */
108 struct LaunchTaskBarPlugin {
109 /* LAUNCHBAR */
110 GtkWidget *lb_icon_grid; /* Icon grid managing the container */
111 GtkWidget *p_button_add, *p_button_remove, *p_label_menu_app_exec, *p_label_def_app_exec;
112 FmDndDest *dd; /* Drag & drop on launchbar */
113 GtkWidget *last_lb_drag_dest; /* Last launch button near drag on launchbar */
114 PanelIconGridDropPosition last_lb_drag_pos;
115 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 */
118 /* TASKBAR */
119 GtkWidget * tb_icon_grid; /* Manager for taskbar buttons */
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 */
122 guint dnd_delay_timer; /* Timer for drag and drop delay */
123 gboolean dnd_task_moving; /* User is currently moving a task button */
124 int icon_size; /* Size of task icons */
125 gboolean grouped_tasks; /* User preference: windows from same task are grouped onto a single button */
126 TaskShowFlags flags; /* User preferences flags */
127 int task_width_max; /* Maximum width of a taskbar button in horizontal orientation */
128 int spacing; /* Spacing between taskbar buttons */
129 guint flash_timeout; /* Timer for urgency notification */
130 gboolean flash_state; /* One-bit counter to flash taskbar */
131 /* COMMON */
132 #ifndef DISABLE_MENU
133 FmPath * path; /* Current menu item path */
134 GtkWidget *p_menuitem_lock_tbp;
135 GtkWidget *p_menuitem_unlock_tbp;
136 GtkWidget *p_menuitem_new_instance;
137 GtkWidget *p_menuitem_separator;
138 #endif
139 GtkWidget * plugin; /* Back pointer to Plugin */
140 LXPanel * panel; /* Back pointer to panel */
141 config_setting_t * settings;
142 GdkScreen *screen;
143 GtkWidget *config_dlg; /* Configuration dialog */
144 GtkNotebook *p_notebook;
145 GtkWidget *p_notebook_page_launch;
146 GtkWidget *p_notebook_page_task;
147 GKeyFile *p_key_file_special_cases;
148 int mode;
149 gboolean lb_built;
150 gboolean tb_built;
151 gboolean fixed_mode; /* if mode cannot be changed */
152 int w, h; /* override GtkBox bug with allocation */
153 };
154
155 static gchar *launchtaskbar_rc = "style 'launchtaskbar-style' = 'theme-panel'\n"
156 "{\n"
157 "GtkWidget::focus-padding=0\n"
158 "GtkButton::default-border={0,0,0,0}\n"
159 "GtkButton::default-outside-border={0,0,0,0}\n"
160 "GtkButton::inner-border={0,0,0,0}\n"
161 "}\n"
162 "widget '*launchbar.*' style 'launchtaskbar-style'\n"
163 "widget '*taskbar.*' style 'launchtaskbar-style'";
164
165 #define DRAG_ACTIVE_DELAY 1000
166 #define TASK_WIDTH_MAX 200
167 #define ALL_WORKSPACES -1
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
171 static void launchtaskbar_destructor(gpointer user_data);
172
173 static LaunchButton *launchbar_exec_bin_exists(LaunchTaskBarPlugin *lb, FmPath *path);
174 static void launchbar_remove_button(LaunchTaskBarPlugin *ltbp, LaunchButton *btn);
175
176 static void taskbar_redraw(LaunchTaskBarPlugin * tb);
177 static void taskbar_net_client_list(GtkWidget * widget, LaunchTaskBarPlugin * tb);
178 static void taskbar_net_current_desktop(GtkWidget * widget, LaunchTaskBarPlugin * tb);
179 static void taskbar_net_number_of_desktops(GtkWidget * widget, LaunchTaskBarPlugin * tb);
180 static void taskbar_net_active_window(GtkWidget * widget, LaunchTaskBarPlugin * tb);
181 static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, LaunchTaskBarPlugin * tb);
182 static void taskbar_window_manager_changed(GdkScreen * screen, LaunchTaskBarPlugin * tb);
183 static void taskbar_apply_configuration(LaunchTaskBarPlugin * ltbp);
184 static void taskbar_add_task_button(LaunchTaskBarPlugin * tb, TaskButton * task);
185
186 #define taskbar_reset_menu(tb) if (tb->tb_built) task_button_reset_menu(tb->tb_icon_grid)
187
188
189 #ifndef DISABLE_MENU
190 static 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)
202 {
203 p_char = strchr(cmdline, '\n');
204 if(p_char != NULL) *p_char = '\0';
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 }
232 }
233 return cmdline;
234 }
235
236 static FmPath *f_find_menu_launchbutton_recursive(Window win, LaunchTaskBarPlugin *ltbp)
237 {
238 MenuCache *mc;
239 guint32 flags;
240 GSList *apps, *l;
241 size_t len;
242 char *exec_bin = task_get_cmdline(win, ltbp);
243 const char *exec, *short_exec;
244 char *str_path;
245 FmPath *path = NULL;
246
247 /* FIXME: cache it in Task object */
248 mc = panel_menu_cache_new(&flags);
249 /* FIXME: if menu plugin wasn't loaded yet we'll get NULL list here */
250 apps = menu_cache_list_all_apps(mc);
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 */
259 for (l = apps; l; l = l->next)
260 {
261 exec = menu_cache_item_get_id(MENU_CACHE_ITEM(l->data));
262 /* we don't check flags here because user always can manually
263 start any app that isn't visible in the desktop menu */
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 {
271 exec = menu_cache_app_get_exec(MENU_CACHE_APP(l->data));
272 if (exec && exec[0] != '/' && strncmp(exec, short_exec, len) == 0 &&
273 (exec[len] == ' ' || exec[len] == 0))
274 break;
275 }
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] == '/')
279 {
280 len = strlen(exec_bin);
281 for (l = apps; l; l = l->next)
282 {
283 exec = menu_cache_app_get_exec(MENU_CACHE_APP(l->data));
284 if (exec && exec[0] == '/' && strncmp(exec, exec_bin, len) == 0 &&
285 (exec[len] == ' ' || exec[len] == 0))
286 break;
287 }
288 }
289 if (l)
290 {
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);
294 }
295 g_slist_foreach(apps, (GFunc)menu_cache_item_unref, NULL);
296 g_slist_free(apps);
297 menu_cache_unref(mc);
298 g_debug("f_find_menu_launchbutton_recursive: search '%s' found=%d", exec_bin, (path != NULL));
299 g_free(exec_bin);
300 return path;
301 }
302 #endif
303
304 static 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
318 static 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
329 static 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 */
340 enum {
341 LAUNCHBUTTON_DND_TARGET = N_FM_DND_DEST_DEFAULT_TARGETS
342 };
343
344 /* Target types for dragging items of list */
345 static const GtkTargetEntry dnd_targets[] = {
346 { "application/x-lxpanel-launcher", GTK_TARGET_SAME_APP, LAUNCHBUTTON_DND_TARGET }
347 };
348
349 static GdkAtom launch_button_dnd_atom;
350 static GtkTargetList *drag_src_target_list = NULL;
351
352
353 /* source side -- form and send path */
354 static 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;
361 GdkWindow *win;
362 gdouble x, y;
363 #if !GTK_CHECK_VERSION(2, 22, 0)
364 gint px, py;
365 #endif
366
367 switch (event->type)
368 {
369 case GDK_BUTTON_PRESS:
370 if (event->button.button != 1)
371 break;
372 if (lb->in_drag)
373 break;
374 if (lb->dragged_launcher)
375 fm_path_unref(lb->dragged_launcher);
376 lb->dragged_launcher = NULL;
377 /* check if it is a launcher where the drag begins */
378 ig = PANEL_ICON_GRID(widget);
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))
383 {
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
393 }
394 if (win == NULL)
395 /* this should never happen */
396 break;
397 if (!panel_icon_grid_get_dest_at_pos(ig, (int)x, (int)y, &btn, &pos) ||
398 !PANEL_IS_LAUNCH_BUTTON(btn))
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;
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;
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;
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;
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 }
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
457 static 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
486 static 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
509 static 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;
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);
527 }
528
529 static 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 */
540 static 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
559 static 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");
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);
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
622 /* Handler for "drag-motion" event from launchtaskbar button. */
623 static gboolean on_launchbar_drag_motion(
624 GtkWidget * widget,
625 GdkDragContext * context,
626 gint x,
627 gint y,
628 guint time,
629 LaunchTaskBarPlugin * b)
630 {
631 GdkAtom target;
632 GdkDragAction action = 0, lb_atom_action = 0;
633 GtkWidget *btn;
634 FmFileInfo *fi = NULL;
635 PanelIconGridDropPosition pos;
636
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);
642 gdk_drag_status(context, 0, time);
643 return TRUE;
644 }
645 b->last_lb_drag_dest = btn;
646 b->last_lb_drag_pos = pos;
647 panel_icon_grid_set_drag_dest(PANEL_ICON_GRID(b->lb_icon_grid), btn, pos);
648 if (PANEL_IS_LAUNCH_BUTTON(btn))
649 {
650 if (launch_button_get_settings((LaunchButton *)btn) == NULL)
651 {
652 /* bootstrap button */
653 b->last_lb_drag_pos = PANEL_ICON_GRID_DROP_LEFT_BEFORE;
654 lb_atom_action = GDK_ACTION_MOVE;
655 }
656 else if (pos == PANEL_ICON_GRID_DROP_INTO)
657 fi = launch_button_get_file_info((LaunchButton *)btn);
658 else
659 lb_atom_action = GDK_ACTION_MOVE;
660 }
661 fm_dnd_dest_set_dest_file(b->dd, fi);
662 target = fm_dnd_dest_find_target(b->dd, context);
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))
670 {
671 action = fm_dnd_dest_get_default_action(b->dd, context, target);
672 if (fi == NULL && PANEL_IS_LAUNCH_BUTTON(btn))
673 /* dropping on free place */
674 action = GDK_ACTION_COPY;
675 }
676 gdk_drag_status(context, action, time);
677 /* g_debug("launchbutton_drag_motion_event: act=%u",action); */
678 return TRUE;
679 }
680
681 static void on_launchbar_drag_leave(GtkWidget * widget, GdkDragContext * drag_context,
682 guint time, LaunchTaskBarPlugin * lb)
683 {
684 fm_dnd_dest_drag_leave(lb->dd, drag_context, time);
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);
687 }
688
689 static 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 }
723 s = config_group_add_subgroup(lb->settings, "Button");
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 }
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
748 static LaunchButton *launchbar_exec_bin_exists(LaunchTaskBarPlugin *lb, FmPath *path)
749 {
750 LaunchButton *ret_val = NULL;
751 GList *children, *l;
752 FmFileInfo *fi;
753
754 if (!path)
755 return NULL;
756 children = gtk_container_get_children(GTK_CONTAINER(lb->lb_icon_grid));
757 for (l = children; l != NULL; l = l->next)
758 {
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)))
761 {
762 ret_val = l->data;
763 break;
764 }
765 }
766 g_list_free(children);
767 return ret_val;
768 }
769
770 /* Read the configuration file entry for a launchtaskbar button and create it. */
771 static gboolean launchbutton_constructor(LaunchTaskBarPlugin * lb, config_setting_t * s)
772 {
773 LaunchButton *btn = NULL;
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;
781
782 /* Build the structures and return. */
783 if (str[0] == '~')
784 {
785 str_path = expand_tilda(str);
786 path = fm_path_new_for_path(str_path);
787 }
788 else if (strchr(str, '/') != NULL)
789 {
790 path = fm_path_new_for_str(str);
791 /* FIXME: check if str contains invalid path */
792 }
793 else
794 {
795 str_path = g_strdup_printf("search://menu://applications/?recursive=1&show_hidden=1&name=%s", str);
796 path = fm_path_new_for_uri(str_path);
797 }
798 btn = launch_button_new(lb->panel, lb->plugin, path, s);
799 g_free(str_path);
800 fm_path_unref(path);
801 if (btn)
802 gtk_container_add(GTK_CONTAINER(lb->lb_icon_grid), GTK_WIDGET(btn));
803 return (btn != NULL);
804 }
805
806 /* prototype of this is app_info_create_from_commandline() in libfm */
807 static gboolean _launchbutton_create_id(LaunchTaskBarPlugin * lb, config_setting_t * s)
808 {
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? */
849 lxpanel_config_save(lb->panel);
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;
860 }
861
862 static void launchtaskbar_constructor_add_default_special_case(LaunchTaskBarPlugin *ltbp, const gchar *tk_exec, const gchar *mb_exec)
863 {
864 g_key_file_set_value(ltbp->p_key_file_special_cases, "special_cases", tk_exec, mb_exec);
865 }
866
867 static void launchtaskbar_constructor_launch(LaunchTaskBarPlugin *ltbp)
868 {
869 config_setting_t *settings;
870 guint i = 0;
871
872 if(!ltbp->lb_built)
873 {
874 ltbp->lb_built = TRUE;
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;
880
881 while ((s = config_setting_get_elem(settings, i)) != NULL)
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 }
899 }
900 if (i == 0)
901 {
902 /* build bootstrap button */
903 launchbar_check_bootstrap(ltbp);
904 }
905 /* Drag and drop support. */
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));
923 g_signal_connect(ltbp->lb_icon_grid, "drag-motion",
924 G_CALLBACK(on_launchbar_drag_motion), ltbp);
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);
929 g_signal_connect(ltbp->lb_icon_grid, "drag-leave",
930 G_CALLBACK(on_launchbar_drag_leave), ltbp);
931 g_signal_connect(ltbp->dd, "files-dropped",
932 G_CALLBACK(on_launchbar_files_dropped), ltbp);
933 launch_button_dnd_atom = gdk_atom_intern("application/x-lxpanel-launcher", FALSE);
934 }
935 gtk_widget_set_visible(ltbp->lb_icon_grid, TRUE);
936 }
937
938 static 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
943 static 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
956 static 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. */
970 static 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
984 static 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
995 static void launchtaskbar_constructor_task(LaunchTaskBarPlugin *ltbp)
996 {
997 if(!ltbp->tb_built)
998 {
999 config_setting_t *s = ltbp->settings;
1000 gint tmp_int;
1001
1002 ltbp->tb_built = TRUE;
1003
1004 /* Parse configuration now */
1005 if (config_setting_lookup_int(s, "tooltips", &tmp_int))
1006 ltbp->flags.tooltips = (tmp_int != 0);
1007 if (config_setting_lookup_int(s, "IconsOnly", &tmp_int))
1008 ltbp->flags.icons_only = (tmp_int != 0);
1009 if (config_setting_lookup_int(s, "ShowAllDesks", &tmp_int))
1010 ltbp->flags.show_all_desks = (tmp_int != 0);
1011 if (config_setting_lookup_int(s, "SameMonitorOnly", &tmp_int))
1012 ltbp->flags.same_monitor_only = (tmp_int != 0);
1013 if (config_setting_lookup_int(s, "DisableUpscale", &tmp_int))
1014 ltbp->flags.disable_taskbar_upscale = (tmp_int != 0);
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))
1018 ltbp->flags.use_mouse_wheel = (tmp_int != 0);
1019 if (config_setting_lookup_int(s, "UseUrgencyHint", &tmp_int))
1020 ltbp->flags.use_urgency_hint = (tmp_int != 0);
1021 if (config_setting_lookup_int(s, "FlatButton", &tmp_int))
1022 ltbp->flags.flat_button = (tmp_int != 0);
1023 if (config_setting_lookup_int(s, "GroupedTasks", &tmp_int))
1024 ltbp->grouped_tasks = (tmp_int != 0);
1025 if (config_setting_lookup_int(s, "UseSmallerIcons", &tmp_int))
1026 ltbp->flags.use_smaller_icons = (tmp_int != 0);
1027
1028 /* Make container for task buttons as a child of top level widget. */
1029 ltbp->tb_icon_grid = panel_icon_grid_new(panel_get_orientation(ltbp->panel),
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,
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);
1036 /* taskbar_update_style(ltbp); */
1037
1038 /* Add GDK event filter. */
1039 gdk_window_add_filter(NULL, (GdkFilterFunc) taskbar_event_filter, ltbp);
1040
1041 /* Connect signals to receive root window events and initialize root window properties. */
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);
1048
1049 /* Connect a signal to be notified when the window manager changes. This causes re-evaluation of the "use_net_active" status. */
1050 g_signal_connect(ltbp->screen, "window-manager-changed", G_CALLBACK(taskbar_window_manager_changed), ltbp);
1051
1052 /* Start blinking timeout if configured */
1053 if (ltbp->flags.use_urgency_hint)
1054 set_timer_on_task(ltbp);
1055
1056 /* Fetch the client list and redraw the taskbar. Then determine what window has focus. */
1057 taskbar_net_client_list(NULL, ltbp);
1058 taskbar_net_active_window(NULL, ltbp);
1059 }
1060 gtk_widget_set_visible(ltbp->tb_icon_grid, TRUE);
1061 }
1062
1063 /* Override GtkBox bug - it does not always propagate allocation to children */
1064 static 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
1077 /* Plugin constructor. */
1078 static GtkWidget *_launchtaskbar_constructor(LXPanel *panel, config_setting_t *settings,
1079 LtbMode mode)
1080 {
1081 GtkWidget *p;
1082 LaunchTaskBarPlugin *ltbp;
1083 int height;
1084
1085 gtk_rc_parse_string(launchtaskbar_rc);
1086
1087 /* Allocate plugin context and set into Plugin private data pointer. */
1088 ltbp = g_new0(LaunchTaskBarPlugin, 1);
1089 ltbp->panel = panel;
1090 ltbp->settings = settings;
1091 ltbp->mode = mode;
1092 ltbp->screen = gtk_widget_get_screen((GtkWidget*)panel);
1093
1094 /* Initialize to defaults. */
1095 ltbp->icon_size = panel_get_icon_size(panel);
1096 ltbp->flags.tooltips = TRUE;
1097 ltbp->flags.icons_only = FALSE;
1098 ltbp->flags.show_all_desks = TRUE;
1099 ltbp->task_width_max = TASK_WIDTH_MAX;
1100 ltbp->spacing = 1;
1101 ltbp->flags.use_mouse_wheel = TRUE;
1102 ltbp->flags.use_urgency_hint = TRUE;
1103 ltbp->flags.use_smaller_icons = TRUE;
1104 ltbp->grouped_tasks = FALSE;
1105 ltbp->fixed_mode = (mode == LAUNCHBAR) || (mode == TASKBAR);
1106
1107 /* Special cases key file */
1108 ltbp->p_key_file_special_cases = g_key_file_new();
1109 gchar *special_cases_filepath = g_build_filename(g_get_user_config_dir(),
1110 "lxpanel", "launchtaskbar.cfg", NULL);
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))
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 }
1122 g_free(special_cases_filepath);
1123
1124 /* Allocate top level widget and set into Plugin widget pointer. */
1125 ltbp->plugin = p = panel_box_new(panel, FALSE, 5);
1126 lxpanel_plugin_set_data(p, ltbp, launchtaskbar_destructor);
1127 /* Allocate an icon grid manager to manage the container. */
1128 height = panel_get_height(panel);
1129 ltbp->lb_icon_grid = panel_icon_grid_new(panel_get_orientation(panel),
1130 ltbp->icon_size, ltbp->icon_size,
1131 3, 0, height);
1132 gtk_box_pack_start(GTK_BOX(p), ltbp->lb_icon_grid, FALSE, TRUE, 0);
1133
1134 /* Setup override on GtkBox bug */
1135 g_signal_connect(p, "size-allocate", G_CALLBACK(on_size_allocation), ltbp);
1136
1137 /* Read parameters from the configuration file. */
1138 config_setting_lookup_int(settings, "LaunchTaskBarMode", &ltbp->mode);
1139 switch (ltbp->mode) {
1140 case LAUNCHBAR:
1141 launchtaskbar_constructor_launch(ltbp);
1142 gtk_widget_set_name(p, "launchbar");
1143 break;
1144 default:
1145 ltbp->mode = LAUNCHTASKBAR; /* reset invalid value */
1146 case LAUNCHTASKBAR:
1147 launchtaskbar_constructor_launch(ltbp);
1148 gtk_widget_set_name(p, "launchtaskbar");
1149 case TASKBAR:
1150 launchtaskbar_constructor_task(ltbp);
1151 if (ltbp->mode == TASKBAR)
1152 gtk_widget_set_name(p, "taskbar");
1153 }
1154
1155 return p;
1156 }
1157
1158 static GtkWidget *launchtaskbar_constructor(LXPanel *panel, config_setting_t *settings)
1159 {
1160 return _launchtaskbar_constructor(panel, settings, LAUNCHTASKBAR);
1161 }
1162
1163 static void launchtaskbar_destructor_launch(LaunchTaskBarPlugin *ltbp)
1164 {
1165 if (ltbp->dd)
1166 {
1167 g_signal_handlers_disconnect_by_func(ltbp->dd, on_launchbar_files_dropped, ltbp);
1168 g_object_unref(ltbp->dd);
1169 }
1170 /* do not disconnect handler on child widget - it is already destroyed */
1171 if (ltbp->dragged_launcher)
1172 fm_path_unref(ltbp->dragged_launcher);
1173 }
1174
1175 static void launchtaskbar_destructor_task(LaunchTaskBarPlugin *ltbp)
1176 {
1177 /* Remove GDK event filter. */
1178 gdk_window_remove_filter(NULL, (GdkFilterFunc) taskbar_event_filter, ltbp);
1179
1180 /* Remove root window signal handlers. */
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);
1185
1186 /* Remove "window-manager-changed" handler. */
1187 g_signal_handlers_disconnect_by_func(ltbp->screen, taskbar_window_manager_changed, ltbp);
1188
1189 /* Stop blinking timeout */
1190 reset_timer_on_task(ltbp);
1191 #ifndef DISABLE_MENU
1192 if (ltbp->path)
1193 fm_path_unref(ltbp->path);
1194 #endif
1195 }
1196
1197 /* Plugin destructor. */
1198 static void launchtaskbar_destructor(gpointer user_data)
1199 {
1200 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)user_data;
1201
1202 // TASKBAR
1203 if(ltbp->tb_built) launchtaskbar_destructor_task(ltbp);
1204
1205 // LAUNCHBAR
1206 if(ltbp->lb_built) launchtaskbar_destructor_launch(ltbp);
1207
1208 // LAUNCHTASKBAR
1209
1210 /* Deallocate all memory. */
1211 if (ltbp->p_key_file_special_cases != NULL)
1212 g_key_file_free(ltbp->p_key_file_special_cases);
1213 g_free(ltbp);
1214 }
1215
1216 static void _launchbar_configure_add(GtkTreeView *menu_view, LaunchTaskBarPlugin *ltbp)
1217 {
1218 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
1219 FmPath * sel_path = fm_app_menu_view_dup_selected_app_desktop_path(menu_view);
1220 LaunchButton * btn;
1221
1222 if (sel_path != NULL &&
1223 (btn = launch_button_new(ltbp->panel, ltbp->plugin, sel_path, NULL)) != NULL &&
1224 launch_button_wait_load(btn))
1225 {
1226 GtkListStore * list = GTK_LIST_STORE(gtk_tree_view_get_model(defined_view));
1227 GtkTreeIter it;
1228 GdkPixbuf* pix;
1229 char *path;
1230 config_setting_t *settings;
1231 gtk_container_add(GTK_CONTAINER(ltbp->lb_icon_grid), GTK_WIDGET(btn));
1232 gtk_list_store_append(list, &it);
1233 pix = fm_pixbuf_from_icon(launch_button_get_icon(btn), PANEL_ICON_SIZE);
1234 gtk_list_store_set(list, &it,
1235 COL_ICON, pix,
1236 COL_TITLE, launch_button_get_disp_name(btn),
1237 COL_BTN, btn,
1238 -1);
1239 g_object_unref(pix);
1240 settings = config_group_add_subgroup(ltbp->settings, "Button");
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 }
1250 launch_button_set_settings(btn, settings);
1251 lxpanel_config_save(ltbp->panel);
1252 fm_path_unref(sel_path);
1253 launchbar_remove_bootstrap(ltbp);
1254 }
1255 }
1256
1257 /* Handler for "clicked" action on launchtaskbar configuration dialog "Add" button. */
1258 static 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
1265 static void launchbar_remove_button(LaunchTaskBarPlugin *ltbp, LaunchButton *btn)
1266 {
1267 config_setting_destroy(launch_button_get_settings(btn));
1268 lxpanel_config_save(ltbp->panel);
1269 gtk_widget_destroy(GTK_WIDGET(btn));
1270 }
1271
1272 /* Handler for "clicked" action on launchtaskbar configuration dialog "Remove" button. */
1273 static void launchbar_configure_remove_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
1274 {
1275 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
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);
1286 gtk_widget_set_visible(ltbp->p_label_def_app_exec, FALSE);
1287
1288 launchbar_remove_button(ltbp, btn);
1289 launchbar_check_bootstrap(ltbp);
1290 }
1291 }
1292
1293 /* Handler for "clicked" action on launchtaskbar configuration dialog "Move Up" button. */
1294 static void launchbar_configure_move_up_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
1295 {
1296 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
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];
1313 config_setting_t *settings = launch_button_get_settings(btn);
1314 gtk_list_store_move_before(GTK_LIST_STORE(list), &it, &it2);
1315 panel_icon_grid_reorder_child(PANEL_ICON_GRID(ltbp->lb_icon_grid),
1316 GTK_WIDGET(btn), i);
1317 config_setting_move_elem(settings,
1318 config_setting_get_parent(settings), i);
1319 }
1320 }
1321 gtk_tree_path_free(path);
1322 }
1323 }
1324
1325 /* Handler for "clicked" action on launchtaskbar configuration dialog "Move Down" button. */
1326 static void launchbar_configure_move_down_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
1327 {
1328 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
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];
1346 config_setting_t *settings = launch_button_get_settings(btn);
1347 gtk_list_store_move_after(GTK_LIST_STORE(list), &it, &it2);
1348 panel_icon_grid_reorder_child(PANEL_ICON_GRID(ltbp->lb_icon_grid),
1349 GTK_WIDGET(btn), i);
1350 config_setting_move_elem(settings,
1351 config_setting_get_parent(settings), i);
1352 }
1353 }
1354 gtk_tree_path_free(path);
1355 }
1356 }
1357
1358 /* Initialize the list of existing launchtaskbar buttons when the configuration dialog is shown. */
1359 static void launchbar_configure_initialize_list(LaunchTaskBarPlugin *ltbp, GtkWidget * dlg, GtkTreeView * view)
1360 {
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
1377 /* Establish the column data types. */
1378 GtkListStore* list = GTK_LIST_STORE(gtk_tree_view_get_model(view));
1379
1380 /* Initialize from defined launchtaskbar buttons. */
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)
1384 {
1385 LaunchButton * btn = (LaunchButton *) l->data;
1386 GdkPixbuf * pix;
1387 GtkTreeIter it;
1388 if (launch_button_get_settings(btn) == NULL) /* bootstrap button */
1389 continue;
1390 gtk_list_store_append(list, &it);
1391 pix = fm_pixbuf_from_icon(launch_button_get_icon(btn), PANEL_ICON_SIZE);
1392 gtk_list_store_set(list, &it,
1393 COL_ICON, pix,
1394 COL_TITLE, launch_button_get_disp_name(btn),
1395 COL_BTN, btn,
1396 -1);
1397 g_object_unref(pix);
1398 }
1399 g_list_free(children);
1400 g_object_set_data(G_OBJECT(dlg), "defined_view", view);
1401 }
1402
1403 static void plugin_set_expand_status(LaunchTaskBarPlugin *ltbp, gboolean expand_new)
1404 {
1405 gboolean old_expand, fill;
1406 guint padding;
1407 GtkPackType pack_type;
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);
1412 }
1413
1414 static void set_config_visibility(LaunchTaskBarPlugin *ltbp)
1415 {
1416 switch (ltbp->mode) {
1417 default:
1418 case LAUNCHTASKBAR:
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);
1422 break;
1423 case TASKBAR:
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);
1427 break;
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);
1432 }
1433 }
1434
1435 static void on_combobox_mode_changed(GtkComboBox *p_combobox, gpointer p_data)
1436 {
1437 LaunchTaskBarPlugin *ltbp = p_data;
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;
1442
1443 ltbp->mode = new_mode;
1444
1445 set_config_visibility(ltbp);
1446
1447 switch (ltbp->mode) {
1448 case LAUNCHBAR:
1449 if (ltbp->tb_icon_grid)
1450 gtk_widget_set_visible(ltbp->tb_icon_grid, FALSE);
1451 launchtaskbar_constructor_launch(ltbp);
1452 plugin_set_expand_status(ltbp, FALSE);
1453 gtk_widget_set_name(ltbp->plugin, "launchbar");
1454 break;
1455 case TASKBAR:
1456 gtk_widget_set_visible(ltbp->lb_icon_grid, FALSE);
1457 launchtaskbar_constructor_task(ltbp);
1458 plugin_set_expand_status(ltbp, TRUE);
1459 gtk_widget_set_name(ltbp->plugin, "taskbar");
1460 break;
1461 default:
1462 ltbp->mode = LAUNCHTASKBAR;
1463 case LAUNCHTASKBAR:
1464 launchtaskbar_constructor_launch(ltbp);
1465 launchtaskbar_constructor_task(ltbp);
1466 plugin_set_expand_status(ltbp, TRUE);
1467 gtk_widget_set_name(ltbp->plugin, "launchtaskbar");
1468 break;
1469 }
1470
1471 config_group_set_int(ltbp->settings, "LaunchTaskBarMode", ltbp->mode);
1472 }
1473
1474 static void on_checkbutton_show_tooltips_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1475 {
1476 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
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);
1480 taskbar_apply_configuration(ltbp);
1481 }
1482
1483 static void on_checkbutton_icons_only_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1484 {
1485 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
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);
1489 taskbar_apply_configuration(ltbp);
1490 }
1491
1492 static void on_checkbutton_flat_buttons_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1493 {
1494 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
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);
1498 taskbar_apply_configuration(ltbp);
1499 }
1500
1501 static void on_checkbutton_show_all_desks_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1502 {
1503 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
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);
1507 taskbar_apply_configuration(ltbp);
1508 }
1509
1510 static void on_checkbutton_same_monitor_only_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1511 {
1512 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
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);
1516 taskbar_apply_configuration(ltbp);
1517 }
1518
1519 static void on_checkbutton_disable_taskbar_upscale_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1520 {
1521 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
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);
1525 taskbar_apply_configuration(ltbp);
1526 }
1527
1528 static 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
1536 static void on_checkbutton_mouse_wheel_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1537 {
1538 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
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);
1542 taskbar_apply_configuration(ltbp);
1543 }
1544
1545 static void on_checkbutton_urgency_hint_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1546 {
1547 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
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);
1551 taskbar_apply_configuration(ltbp);
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);
1557 }
1558
1559 static void on_checkbutton_grouped_tasks_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1560 {
1561 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1562 ltbp->grouped_tasks = gtk_toggle_button_get_active(p_togglebutton);
1563 //g_print("\ntb->grouped_tasks upd\n");
1564 config_group_set_int(ltbp->settings, "GroupedTasks", ltbp->grouped_tasks);
1565 taskbar_apply_configuration(ltbp);
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 }
1614 }
1615
1616 static void on_spinbutton_max_width_value_changed(GtkSpinButton *p_spinbutton, gpointer p_data)
1617 {
1618 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1619 ltbp->task_width_max = gtk_spin_button_get_value(p_spinbutton);
1620 //g_print("\ntb->task_width_max upd\n");
1621 config_group_set_int(ltbp->settings, "MaxTaskWidth", ltbp->task_width_max);
1622 taskbar_apply_configuration(ltbp);
1623 }
1624
1625 static void on_spinbutton_spacing_value_changed(GtkSpinButton *p_spinbutton, gpointer p_data)
1626 {
1627 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1628 ltbp->spacing = gtk_spin_button_get_value(p_spinbutton);
1629 //g_print("\ntb->spacing upd\n");
1630 config_group_set_int(ltbp->settings, "spacing", ltbp->spacing);
1631 taskbar_apply_configuration(ltbp);
1632 }
1633
1634 static gboolean on_defined_view_button_press_event(GtkWidget *p_widget, GdkEventButton *p_event, gpointer p_data)
1635 {
1636 LaunchTaskBarPlugin *lb = (LaunchTaskBarPlugin *)p_data;
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
1647 static void on_defined_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_data)
1648 {
1649 gboolean label_set = FALSE;
1650 LaunchTaskBarPlugin *lb = (LaunchTaskBarPlugin *)p_data;
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;
1659 FmFileInfo *fi;
1660 gtk_tree_model_get(p_treemodel, &tree_iter_sel, COL_BTN, &p_btn, -1);
1661 if ((p_btn != NULL) && ((fi = launch_button_get_file_info(p_btn)) != NULL))
1662 {
1663 GString *p_gstring = g_string_new("");
1664 g_string_printf(p_gstring, "<i>%s</i>", fm_file_info_get_disp_name(fi));
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*/);
1667 label_set = TRUE;
1668 }
1669 }
1670 gtk_widget_set_visible(lb->p_label_def_app_exec, label_set);
1671 gtk_widget_set_sensitive(lb->p_button_remove, label_set);
1672 }
1673
1674 static void on_menu_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_data)
1675 {
1676 gboolean label_set = FALSE;
1677 LaunchTaskBarPlugin *lb = (LaunchTaskBarPlugin *)p_data;
1678 GAppInfo *app = fm_app_menu_view_dup_selected_app(p_treeview);
1679
1680 if (app)
1681 {
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;
1690 }
1691 gtk_widget_set_visible(lb->p_label_menu_app_exec, label_set);
1692 gtk_widget_set_sensitive(lb->p_button_add, label_set);
1693 }
1694
1695 static 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
1702 /* FIXME: add support for global hotkeys for launchers */
1703
1704 /* Callback when the configuration dialog is to be shown. */
1705 static GtkWidget *launchtaskbar_configure(LXPanel *panel, GtkWidget *p)
1706 {
1707 LaunchTaskBarPlugin *ltbp = lxpanel_plugin_get_data(p);
1708
1709 {
1710 GtkWidget *dlg, *btn, *defined_view, *menu_view, *menu_view_window;
1711 GtkBuilder *builder = gtk_builder_new();
1712 GObject *object;
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");
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");
1730
1731 /* Connect signals. */
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);
1734
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);
1737
1738 btn = (GtkWidget *)gtk_builder_get_object(builder, "button_up");
1739 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_up_button), ltbp);
1740
1741 btn = (GtkWidget *)gtk_builder_get_object(builder, "button_down");
1742 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_down_button), ltbp);
1743
1744 /* FIXME: add a button 'New' with launcher creation dialog */
1745
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);
1749 g_signal_connect(menu_view, "row-activated", G_CALLBACK(on_menu_view_row_activated), ltbp);
1750
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);
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);
1759
1760 #define SETUP_TOGGLE_BUTTON(button,member) \
1761 object = gtk_builder_get_object(builder, #button); \
1762 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object), ltbp->flags.member); \
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);
1772 SETUP_TOGGLE_BUTTON(checkbutton_disable_taskbar_upscale, disable_taskbar_upscale);
1773 //SETUP_TOGGLE_BUTTON(checkbutton_use_smaller_icons, use_smaller_icons);
1774 #undef SETUP_TOGGLE_BUTTON
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);
1778 /* FIXME: for transitional period, turn into SETUP_TOGGLE_BUTTON later */
1779 object = gtk_builder_get_object(builder, "checkbutton_use_smaller_icons");
1780 if (object)
1781 {
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);
1784 }
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
1795
1796 ltbp->config_dlg = dlg;
1797
1798 /* Initialize the tree view contents. */
1799 launchbar_configure_initialize_list(ltbp, dlg, GTK_TREE_VIEW(defined_view));
1800 g_object_set_data(G_OBJECT(dlg), "menu_view", menu_view);
1801
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);
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 }
1818
1819 g_object_unref(builder);
1820 }
1821 return ltbp->config_dlg;
1822 }
1823
1824 /* Callback when panel configuration changes. */
1825 static void launchtaskbar_panel_configuration_changed(LXPanel *panel, GtkWidget *p)
1826 {
1827 /* Set orientation into the icon grid. */
1828 LaunchTaskBarPlugin *ltbp = lxpanel_plugin_get_data(p);
1829 int new_icon_size = panel_get_icon_size(panel);
1830 int height = panel_get_height(panel);
1831
1832 if (ltbp->lb_built)
1833 panel_icon_grid_set_geometry(PANEL_ICON_GRID(ltbp->lb_icon_grid),
1834 panel_get_orientation(panel),
1835 new_icon_size, new_icon_size,
1836 3, 0, height);
1837
1838 /* Redraw all the labels. Icon size or font color may have changed. */
1839 if (ltbp->tb_built)
1840 {
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),
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));
1847 taskbar_reset_menu(ltbp);
1848 taskbar_redraw(ltbp);
1849 }
1850 }
1851
1852 static 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
1877 /* Redraw all tasks in the taskbar. */
1878 static void taskbar_redraw(LaunchTaskBarPlugin * tb)
1879 {
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
1885 if (tb->flags.use_smaller_icons)
1886 icon_size -= 4;
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);
1891 }
1892
1893 /* Determine if a task should be visible given its NET_WM_STATE. */
1894 static 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. */
1900 static gboolean accept_net_wm_window_type(NetWMWindowType * nwwt)
1901 {
1902 return ( ! ((nwwt->desktop) || (nwwt->dock) || (nwwt->splash)));
1903 }
1904
1905 /* Set the class associated with a task. */
1906 static char *task_get_class(Window win)
1907 {
1908 /* Read the WM_CLASS property. */
1909 XClassHint ch;
1910 ch.res_name = NULL;
1911 ch.res_class = NULL;
1912 char *res_class = NULL;
1913 XGetClassHint(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, &ch);
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. */
1926 res_class = g_locale_to_utf8(ch.res_class, -1, NULL, NULL, NULL);
1927 XFree(ch.res_class);
1928 }
1929 return res_class;
1930 }
1931
1932 /* Look up a task in the task list. */
1933 static TaskButton *task_lookup(LaunchTaskBarPlugin * tb, Window win)
1934 {
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))
1941 {
1942 task = l->data;
1943 break;
1944 }
1945 g_list_free(children);
1946 return task;
1947 }
1948
1949
1950 #ifndef DISABLE_MENU
1951 static void on_menuitem_lock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb);
1952 static void on_menuitem_unlock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb);
1953 static void on_menuitem_new_instance_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb);
1954 #endif
1955
1956 static 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);
1961
1962 if (panel_is_at_bottom(tb->panel))
1963 _m_add = gtk_menu_shell_append;
1964 else
1965 _m_add = gtk_menu_shell_prepend;
1966
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
1982 }
1983
1984 static void on_task_menu_target_set(TaskButton *btn, gulong win, LaunchTaskBarPlugin *ltbp)
1985 {
1986 #ifndef DISABLE_MENU
1987 if(ltbp->mode == LAUNCHTASKBAR)
1988 {
1989 FmPath *path = f_find_menu_launchbutton_recursive(win, ltbp);
1990 LaunchButton *btn = launchbar_exec_bin_exists(ltbp, path);
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');
1994 #endif
1995 if(btn != NULL)
1996 {
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);
2000 }
2001 else
2002 {
2003 gtk_widget_set_visible(ltbp->p_menuitem_lock_tbp, path != NULL);
2004 gtk_widget_set_visible(ltbp->p_menuitem_unlock_tbp, FALSE);
2005 gtk_widget_set_visible(ltbp->p_menuitem_new_instance, path != NULL);
2006 }
2007 gtk_widget_set_visible(ltbp->p_menuitem_separator, TRUE);
2008 if (ltbp->path)
2009 fm_path_unref(ltbp->path);
2010 ltbp->path = path;
2011 }
2012 else
2013 {
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);
2017 gtk_widget_set_visible(ltbp->p_menuitem_separator, FALSE);
2018 }
2019 #endif
2020 }
2021
2022 /* Handler for "drag-motion" timeout. */
2023 static gboolean taskbar_button_drag_motion_timeout(LaunchTaskBarPlugin * tb)
2024 {
2025 //guint time;
2026 if (g_source_is_destroyed(g_main_current_source()))
2027 return FALSE;
2028 //time = gtk_get_current_event_time();
2029 //task_raise_window(tk, ((time != 0) ? time : CurrentTime)); // ???
2030 tb->dnd_delay_timer = 0;
2031 return FALSE;
2032 }
2033
2034 /* Handler for "drag-motion" event from taskbar button. */
2035 static gboolean taskbar_button_drag_motion(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, LaunchTaskBarPlugin * tb)
2036 {
2037 GtkWidget * drag_source = gtk_drag_get_source_widget(drag_context);
2038 if (drag_source != NULL && gtk_widget_get_parent(drag_source) == gtk_widget_get_parent(widget))
2039 {
2040 tb->dnd_task_moving = TRUE;
2041 gdk_drag_status(drag_context, GDK_ACTION_MOVE, time);
2042 }
2043 else
2044 {
2045 /* Prevent excessive motion notification. */
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);
2048
2049 gdk_drag_status(drag_context, 0, time);
2050 }
2051 return TRUE;
2052 }
2053
2054 /* Handler for "drag-drop" event from taskbar button. */
2055 static gboolean taskbar_button_drag_drop(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, LaunchTaskBarPlugin * tb)
2056 {
2057 tb->dnd_task_moving = FALSE;
2058 GtkWidget * drag_source = gtk_drag_get_source_widget(drag_context);
2059 if (drag_source != NULL && gtk_widget_get_parent(drag_source) == gtk_widget_get_parent(widget))
2060 {
2061 if (drag_source != widget)
2062 {
2063 PanelIconGrid *ig = PANEL_ICON_GRID(tb->tb_icon_grid);
2064 gint i = panel_icon_grid_get_child_position(ig, widget);
2065 panel_icon_grid_reorder_child(ig, drag_source, i);
2066 }
2067 gtk_drag_finish(drag_context, TRUE, TRUE, time);
2068 return TRUE;
2069 }
2070
2071 return FALSE;
2072 }
2073
2074 /* Handler for "drag-leave" event from taskbar button. */
2075 static void taskbar_button_drag_leave(GtkWidget * widget, GdkDragContext * drag_context, guint time, LaunchTaskBarPlugin * tb)
2076 {
2077 /* Cancel the timer if set. */
2078 if (tb->dnd_delay_timer != 0)
2079 {
2080 g_source_remove(tb->dnd_delay_timer);
2081 tb->dnd_delay_timer = 0;
2082 }
2083 return;
2084 }
2085
2086 /* Handler for "enter" event from taskbar button. This indicates that the cursor position has entered the button. */
2087 static void taskbar_button_enter(GtkWidget * widget, GdkEvent *event, LaunchTaskBarPlugin * tb)
2088 {
2089 tb->dnd_task_moving = FALSE;
2090 }
2091
2092 /* Handler for "button-release-event" event from taskbar button. */
2093 static gboolean taskbar_button_release_event(GtkWidget * widget, GdkEventButton * event, LaunchTaskBarPlugin * tb)
2094 {
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;
2101 }
2102
2103 enum {
2104 TARGET_TASK_BUTTON
2105 };
2106
2107 static GtkTargetEntry task_button_target_list[] = {
2108 { "task_button", GTK_TARGET_SAME_APP, TARGET_TASK_BUTTON }
2109 };
2110
2111 static guint task_button_n_targets = G_N_ELEMENTS(task_button_target_list);
2112
2113 /* Build graphic elements needed for a task button. */
2114 static void taskbar_add_task_button(LaunchTaskBarPlugin * tb, TaskButton * task)
2115 {
2116 /* Allocate a toggle button as the top level widget. */
2117 gtk_container_add(GTK_CONTAINER(tb->tb_icon_grid), GTK_WIDGET(task));
2118
2119 /* Connect signals to the button. */
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 */
2143 static 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);
2159 }
2160
2161 /*****************************************************
2162 * handlers for NET actions *
2163 *****************************************************/
2164
2165 /* Handler for "client-list" event from root window listener. */
2166 static void taskbar_net_client_list(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2167 {
2168 LaunchTaskBarPlugin *ltbp = tb;
2169 if(ltbp->mode == LAUNCHBAR) return;
2170
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 {
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));
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. */
2187 for (l = children; l; l = l->next)
2188 {
2189 if (task_button_has_window(l->data, client_list[i]))
2190 break;
2191 }
2192
2193 /* Task is not in task list. */
2194 if (l == NULL)
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. */
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));
2208 }
2209 }
2210 }
2211 g_list_free(children);
2212 XFree(client_list);
2213 }
2214
2215 else /* clear taskbar */
2216 gtk_container_foreach(GTK_CONTAINER(tb->tb_icon_grid),
2217 (GtkCallback)gtk_widget_destroy, NULL);
2218 }
2219
2220 /* Handler for "current-desktop" event from root window listener. */
2221 static void taskbar_net_current_desktop(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2222 {
2223 LaunchTaskBarPlugin *ltbp = tb;
2224 if(ltbp->mode == LAUNCHBAR) return;
2225
2226 /* Store the local copy of current desktops. Redisplay the taskbar. */
2227 tb->current_desktop = get_net_current_desktop();
2228 taskbar_redraw(tb);
2229 }
2230
2231 /* Handler for "number-of-desktops" event from root window listener. */
2232 static void taskbar_net_number_of_desktops(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2233 {
2234 LaunchTaskBarPlugin *ltbp = tb;
2235 if(ltbp->mode == LAUNCHBAR) return;
2236
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();
2239 taskbar_reset_menu(tb);
2240 taskbar_redraw(tb);
2241 }
2242
2243 /* Handler for "active-window" event from root window listener. */
2244 static void taskbar_net_active_window(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2245 {
2246 LaunchTaskBarPlugin *ltbp = tb;
2247 if(ltbp->mode == LAUNCHBAR) return;
2248
2249 /* Get the window that has focus. */
2250 Window * f = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_ACTIVE_WINDOW, XA_WINDOW, 0);
2251
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);
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 */
2261 static void taskbar_property_notify_event(LaunchTaskBarPlugin *tb, XEvent *ev)
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. */
2271 TaskButton * tk = task_lookup(tb, win);
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. */
2279 if (at == a_NET_WM_STATE)
2280 {
2281 /* Window changed EWMH state. */
2282 NetWMState nws;
2283 get_net_wm_state(win, &nws);
2284 if ( ! accept_net_wm_state(&nws))
2285 task_button_drop_window(tk, win, FALSE);
2286 }
2287 else if (at == a_NET_WM_WINDOW_TYPE)
2288 {
2289 /* Window changed EWMH window type. */
2290 NetWMWindowType nwwt;
2291 get_net_wm_window_type(win, &nwwt);
2292 if ( ! accept_net_wm_window_type(&nwwt))
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);
2308 }
2309
2310 XSetErrorHandler(previous_error_handler);
2311 }
2312 }
2313 }
2314 }
2315
2316 /* Handle ConfigureNotify events */
2317 static void taskbar_configure_notify_event(LaunchTaskBarPlugin * tb, XConfigureEvent * ev)
2318 {
2319 /* If the same_monitor_only option is set and the window is on a different
2320 monitor than before, redraw the taskbar */
2321 TaskButton *task;
2322
2323 if (ev->window != GDK_ROOT_WINDOW())
2324 {
2325 task = task_lookup(tb, ev->window);
2326 if (task)
2327 {
2328 /* Deleted windows seem to get ConfigureNotify events too. */
2329 XErrorHandler previous_error_handler = XSetErrorHandler(panel_handle_x_error_swallow_BadWindow_BadDrawable);
2330
2331 /* Monitor might be changed so button might need update */
2332 task_button_window_reconfigured(task, ev->window);
2333
2334 XSetErrorHandler(previous_error_handler);
2335 }
2336 }
2337 }
2338
2339 /* GDK event filter. */
2340 static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, LaunchTaskBarPlugin * tb)
2341 {
2342 if (tb->mode == LAUNCHBAR)
2343 return GDK_FILTER_CONTINUE;
2344
2345 if (xev->type == PropertyNotify)
2346 taskbar_property_notify_event(tb, xev);
2347 else if (xev->type == ConfigureNotify)
2348 taskbar_configure_notify_event(tb, &xev->xconfigure);
2349
2350 return GDK_FILTER_CONTINUE;
2351 }
2352
2353 #ifndef DISABLE_MENU
2354 static void on_menuitem_lock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2355 {
2356 LaunchButton *btn;
2357 char *path;
2358 config_setting_t *settings;
2359
2360 if (tb->path)
2361 {
2362 /* Create a button and add settings for it */
2363 path = fm_path_to_str(tb->path);
2364 /* g_debug("*** path '%s'",path); */
2365 settings = config_group_add_subgroup(tb->settings, "Button");
2366 config_group_set_string(settings, "id", path);
2367 g_free(path);
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);
2373 lxpanel_config_save(tb->panel);
2374 }
2375 }
2376
2377 static void on_menuitem_unlock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2378 {
2379 launchbar_remove_launcher(tb, tb->path);
2380 launchbar_check_bootstrap(tb);
2381 }
2382
2383 static void on_menuitem_new_instance_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2384 {
2385 if (tb->path)
2386 {
2387 lxpanel_launch_path(tb->panel, tb->path);
2388 }
2389 }
2390 #endif
2391
2392 /* Handler for "window-manager-changed" event. */
2393 static void taskbar_window_manager_changed(GdkScreen * screen, LaunchTaskBarPlugin * tb)
2394 {
2395 /* Force re-evaluation of use_net_active. */
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);
2399 }
2400
2401 /* Callback from configuration dialog mechanism to apply the configuration. */
2402 static void taskbar_apply_configuration(LaunchTaskBarPlugin *ltbp)
2403 {
2404 taskbar_redraw(ltbp);
2405 }
2406
2407 static GtkWidget *launchbar_constructor(LXPanel *panel, config_setting_t *settings)
2408 {
2409 return _launchtaskbar_constructor(panel, settings, LAUNCHBAR);
2410 }
2411
2412 static GtkWidget *taskbar_constructor(LXPanel *panel, config_setting_t *settings)
2413 {
2414 return _launchtaskbar_constructor(panel, settings, TASKBAR);
2415 }
2416
2417 static LXPanelPluginInit _launchbar_init = {
2418 .name = N_("Application Launch Bar"),
2419 .description = N_("Bar with buttons to launch application"),
2420
2421 .new_instance = launchbar_constructor,
2422 .config = launchtaskbar_configure,
2423 .reconfigure = launchtaskbar_panel_configuration_changed,
2424 .control = launchtaskbar_control
2425 };
2426
2427 static 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,
2436 .reconfigure = launchtaskbar_panel_configuration_changed,
2437 .control = launchtaskbar_control
2438 };
2439
2440 static void launchtaskbar_init(void)
2441 {
2442 lxpanel_register_plugin_type("launchbar", &_launchbar_init);
2443 lxpanel_register_plugin_type("taskbar", &_taskbar_init);
2444 }
2445
2446 /* Plugin descriptor. */
2447 LXPanelPluginInit lxpanel_static_plugin_launchtaskbar = {
2448 .name = N_("Application Launch and Task Bar"),
2449 .description = N_("Bar with buttons to launch application and/or show all opened windows"),
2450
2451 .expand_available = TRUE,
2452 .expand_default = TRUE,
2453
2454 .init = launchtaskbar_init,
2455 .new_instance = launchtaskbar_constructor,
2456 .config = launchtaskbar_configure,
2457 /* .update_context_menu = launchtaskbar_update_context_menu, */
2458 .reconfigure = launchtaskbar_panel_configuration_changed,
2459 .control = launchtaskbar_control
2460 };
2461
2462
2463 /* vim: set sw=4 sts=4 et : */