81af6768815c05cd94103d52c533c6dfe33cde2f
[debian/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 panel_icon_grid_set_geometry(PANEL_ICON_GRID(ltbp->lb_icon_grid),
1072 panel_get_orientation(ltbp->panel),
1073 ltbp->icon_size, ltbp->icon_size,
1074 3, 0, panel_get_height(ltbp->panel));
1075 if (ltbp->tb_built && gtk_widget_get_visible(ltbp->tb_icon_grid))
1076 panel_icon_grid_set_geometry(PANEL_ICON_GRID(ltbp->tb_icon_grid),
1077 panel_get_orientation(ltbp->panel),
1078 ((ltbp->flags.icons_only) ? ltbp->icon_size + ICON_ONLY_EXTRA : ltbp->task_width_max),
1079 ((ltbp->flags.icons_only) ? ltbp->icon_size + ICON_ONLY_EXTRA : ltbp->icon_size + ICON_BUTTON_TRIM),
1080 ltbp->spacing, 0,
1081 panel_get_height(ltbp->panel));
1082 }
1083 }
1084
1085 /* Plugin constructor. */
1086 static GtkWidget *_launchtaskbar_constructor(LXPanel *panel, config_setting_t *settings,
1087 LtbMode mode)
1088 {
1089 GtkWidget *p;
1090 LaunchTaskBarPlugin *ltbp;
1091 int height;
1092
1093 gtk_rc_parse_string(launchtaskbar_rc);
1094
1095 /* Allocate plugin context and set into Plugin private data pointer. */
1096 ltbp = g_new0(LaunchTaskBarPlugin, 1);
1097 ltbp->panel = panel;
1098 ltbp->settings = settings;
1099 ltbp->mode = mode;
1100 ltbp->screen = gtk_widget_get_screen((GtkWidget*)panel);
1101
1102 /* Initialize to defaults. */
1103 ltbp->icon_size = panel_get_icon_size(panel);
1104 ltbp->flags.tooltips = TRUE;
1105 ltbp->flags.icons_only = FALSE;
1106 ltbp->flags.show_all_desks = TRUE;
1107 ltbp->task_width_max = TASK_WIDTH_MAX;
1108 ltbp->spacing = 1;
1109 ltbp->flags.use_mouse_wheel = TRUE;
1110 ltbp->flags.use_urgency_hint = TRUE;
1111 ltbp->flags.use_smaller_icons = TRUE;
1112 ltbp->grouped_tasks = FALSE;
1113 ltbp->fixed_mode = (mode == LAUNCHBAR) || (mode == TASKBAR);
1114
1115 /* Special cases key file */
1116 ltbp->p_key_file_special_cases = g_key_file_new();
1117 gchar *special_cases_filepath = g_build_filename(g_get_user_config_dir(),
1118 "lxpanel", "launchtaskbar.cfg", NULL);
1119 if (!g_key_file_load_from_file(ltbp->p_key_file_special_cases,
1120 special_cases_filepath,
1121 G_KEY_FILE_KEEP_COMMENTS, NULL))
1122 {
1123 launchtaskbar_constructor_add_default_special_case(ltbp, "synaptic", "synaptic-pkexec");
1124 launchtaskbar_constructor_add_default_special_case(ltbp, "soffice.bin", "libreoffice");
1125 launchtaskbar_constructor_add_default_special_case(ltbp, "x-terminal-emulator", "lxterminal");
1126 gchar *key_file_data = g_key_file_to_data(ltbp->p_key_file_special_cases, NULL, NULL);
1127 g_file_set_contents(special_cases_filepath, key_file_data, -1, NULL);
1128 g_free(key_file_data);
1129 }
1130 g_free(special_cases_filepath);
1131
1132 /* Allocate top level widget and set into Plugin widget pointer. */
1133 ltbp->plugin = p = panel_box_new(panel, FALSE, 5);
1134 lxpanel_plugin_set_data(p, ltbp, launchtaskbar_destructor);
1135 /* Allocate an icon grid manager to manage the container. */
1136 height = panel_get_height(panel);
1137 ltbp->lb_icon_grid = panel_icon_grid_new(panel_get_orientation(panel),
1138 ltbp->icon_size, ltbp->icon_size,
1139 3, 0, height);
1140 gtk_box_pack_start(GTK_BOX(p), ltbp->lb_icon_grid, FALSE, TRUE, 0);
1141
1142 /* Setup override on GtkBox bug */
1143 g_signal_connect(p, "size-allocate", G_CALLBACK(on_size_allocation), ltbp);
1144
1145 /* Read parameters from the configuration file. */
1146 config_setting_lookup_int(settings, "LaunchTaskBarMode", &ltbp->mode);
1147 switch (ltbp->mode) {
1148 case LAUNCHBAR:
1149 launchtaskbar_constructor_launch(ltbp);
1150 gtk_widget_set_name(p, "launchbar");
1151 break;
1152 default:
1153 ltbp->mode = LAUNCHTASKBAR; /* reset invalid value */
1154 case LAUNCHTASKBAR:
1155 launchtaskbar_constructor_launch(ltbp);
1156 gtk_widget_set_name(p, "launchtaskbar");
1157 case TASKBAR:
1158 launchtaskbar_constructor_task(ltbp);
1159 if (ltbp->mode == TASKBAR)
1160 gtk_widget_set_name(p, "taskbar");
1161 }
1162
1163 return p;
1164 }
1165
1166 static GtkWidget *launchtaskbar_constructor(LXPanel *panel, config_setting_t *settings)
1167 {
1168 return _launchtaskbar_constructor(panel, settings, LAUNCHTASKBAR);
1169 }
1170
1171 static void launchtaskbar_destructor_launch(LaunchTaskBarPlugin *ltbp)
1172 {
1173 if (ltbp->dd)
1174 {
1175 g_signal_handlers_disconnect_by_func(ltbp->dd, on_launchbar_files_dropped, ltbp);
1176 g_object_unref(ltbp->dd);
1177 }
1178 /* do not disconnect handler on child widget - it is already destroyed */
1179 if (ltbp->dragged_launcher)
1180 fm_path_unref(ltbp->dragged_launcher);
1181 }
1182
1183 static void launchtaskbar_destructor_task(LaunchTaskBarPlugin *ltbp)
1184 {
1185 /* Remove GDK event filter. */
1186 gdk_window_remove_filter(NULL, (GdkFilterFunc) taskbar_event_filter, ltbp);
1187
1188 /* Remove root window signal handlers. */
1189 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_current_desktop, ltbp);
1190 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_active_window, ltbp);
1191 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_number_of_desktops, ltbp);
1192 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_client_list, ltbp);
1193
1194 /* Remove "window-manager-changed" handler. */
1195 g_signal_handlers_disconnect_by_func(ltbp->screen, taskbar_window_manager_changed, ltbp);
1196
1197 /* Stop blinking timeout */
1198 reset_timer_on_task(ltbp);
1199 #ifndef DISABLE_MENU
1200 if (ltbp->path)
1201 fm_path_unref(ltbp->path);
1202 #endif
1203 }
1204
1205 /* Plugin destructor. */
1206 static void launchtaskbar_destructor(gpointer user_data)
1207 {
1208 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)user_data;
1209
1210 // TASKBAR
1211 if(ltbp->tb_built) launchtaskbar_destructor_task(ltbp);
1212
1213 // LAUNCHBAR
1214 if(ltbp->lb_built) launchtaskbar_destructor_launch(ltbp);
1215
1216 // LAUNCHTASKBAR
1217
1218 /* Deallocate all memory. */
1219 if (ltbp->p_key_file_special_cases != NULL)
1220 g_key_file_free(ltbp->p_key_file_special_cases);
1221 g_free(ltbp);
1222 }
1223
1224 static void _launchbar_configure_add(GtkTreeView *menu_view, LaunchTaskBarPlugin *ltbp)
1225 {
1226 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
1227 FmPath * sel_path = fm_app_menu_view_dup_selected_app_desktop_path(menu_view);
1228 LaunchButton * btn;
1229
1230 if (sel_path != NULL &&
1231 (btn = launch_button_new(ltbp->panel, ltbp->plugin, sel_path, NULL)) != NULL &&
1232 launch_button_wait_load(btn))
1233 {
1234 GtkListStore * list = GTK_LIST_STORE(gtk_tree_view_get_model(defined_view));
1235 GtkTreeIter it;
1236 GdkPixbuf* pix;
1237 char *path;
1238 config_setting_t *settings;
1239 gtk_container_add(GTK_CONTAINER(ltbp->lb_icon_grid), GTK_WIDGET(btn));
1240 gtk_list_store_append(list, &it);
1241 pix = fm_pixbuf_from_icon(launch_button_get_icon(btn), PANEL_ICON_SIZE);
1242 gtk_list_store_set(list, &it,
1243 COL_ICON, pix,
1244 COL_TITLE, launch_button_get_disp_name(btn),
1245 COL_BTN, btn,
1246 -1);
1247 g_object_unref(pix);
1248 settings = config_group_add_subgroup(ltbp->settings, "Button");
1249 if (fm_path_equal(fm_path_get_scheme_path(sel_path), fm_path_get_apps_menu()))
1250 config_group_set_string(settings, "id", fm_path_get_basename(sel_path));
1251 else
1252 {
1253 path = fm_path_to_str(sel_path);
1254 /* g_debug("*** path '%s'",path); */
1255 config_group_set_string(settings, "id", path);
1256 g_free(path);
1257 }
1258 launch_button_set_settings(btn, settings);
1259 lxpanel_config_save(ltbp->panel);
1260 fm_path_unref(sel_path);
1261 launchbar_remove_bootstrap(ltbp);
1262 }
1263 }
1264
1265 /* Handler for "clicked" action on launchtaskbar configuration dialog "Add" button. */
1266 static void launchbar_configure_add_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
1267 {
1268 GtkTreeView * menu_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "menu_view"));
1269
1270 _launchbar_configure_add(menu_view, ltbp);
1271 }
1272
1273 static void launchbar_remove_button(LaunchTaskBarPlugin *ltbp, LaunchButton *btn)
1274 {
1275 config_setting_destroy(launch_button_get_settings(btn));
1276 lxpanel_config_save(ltbp->panel);
1277 gtk_widget_destroy(GTK_WIDGET(btn));
1278 }
1279
1280 /* Handler for "clicked" action on launchtaskbar configuration dialog "Remove" button. */
1281 static void launchbar_configure_remove_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
1282 {
1283 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
1284 GtkTreeModel * list;
1285 GtkTreeIter it;
1286 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
1287 {
1288 LaunchButton * btn;
1289 gtk_tree_model_get(list, &it, COL_BTN, &btn, -1);
1290
1291 /* We have found a selected button.
1292 * Remove it from the icon grid, the data structure, and the view. */
1293 gtk_list_store_remove(GTK_LIST_STORE(list), &it);
1294 gtk_widget_set_visible(ltbp->p_label_def_app_exec, FALSE);
1295
1296 launchbar_remove_button(ltbp, btn);
1297 launchbar_check_bootstrap(ltbp);
1298 }
1299 }
1300
1301 /* Handler for "clicked" action on launchtaskbar configuration dialog "Move Up" button. */
1302 static void launchbar_configure_move_up_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
1303 {
1304 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
1305 GtkTreeModel * list;
1306 GtkTreeIter it;
1307 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
1308 {
1309 LaunchButton *btn;
1310 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, COL_BTN, &btn, -1);
1311 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &it);
1312 if ((gtk_tree_path_get_indices(path)[0] > 0)
1313 && (gtk_tree_path_prev(path)))
1314 {
1315 GtkTreeIter it2;
1316 if (gtk_tree_model_get_iter(list, &it2, path))
1317 {
1318 /* We have found a selected button that can be moved.
1319 * Reorder it in the icon grid, the data structure, and the view. */
1320 int i = gtk_tree_path_get_indices(path)[0];
1321 config_setting_t *settings = launch_button_get_settings(btn);
1322 gtk_list_store_move_before(GTK_LIST_STORE(list), &it, &it2);
1323 panel_icon_grid_reorder_child(PANEL_ICON_GRID(ltbp->lb_icon_grid),
1324 GTK_WIDGET(btn), i);
1325 config_setting_move_elem(settings,
1326 config_setting_get_parent(settings), i);
1327 }
1328 }
1329 gtk_tree_path_free(path);
1330 }
1331 }
1332
1333 /* Handler for "clicked" action on launchtaskbar configuration dialog "Move Down" button. */
1334 static void launchbar_configure_move_down_button(GtkButton * widget, LaunchTaskBarPlugin *ltbp)
1335 {
1336 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
1337 GtkTreeModel * list;
1338 GtkTreeIter it;
1339 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
1340 {
1341 LaunchButton *btn;
1342 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, COL_BTN, &btn, -1);
1343 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &it);
1344 int n = gtk_tree_model_iter_n_children(list, NULL);
1345 if (gtk_tree_path_get_indices(path)[0] < (n - 1))
1346 {
1347 gtk_tree_path_next(path);
1348 GtkTreeIter it2;
1349 if (gtk_tree_model_get_iter( list, &it2, path))
1350 {
1351 /* We have found a selected button that can be moved.
1352 * Reorder it in the icon grid, the data structure, and the view. */
1353 int i = gtk_tree_path_get_indices(path)[0];
1354 config_setting_t *settings = launch_button_get_settings(btn);
1355 gtk_list_store_move_after(GTK_LIST_STORE(list), &it, &it2);
1356 panel_icon_grid_reorder_child(PANEL_ICON_GRID(ltbp->lb_icon_grid),
1357 GTK_WIDGET(btn), i);
1358 config_setting_move_elem(settings,
1359 config_setting_get_parent(settings), i);
1360 }
1361 }
1362 gtk_tree_path_free(path);
1363 }
1364 }
1365
1366 /* Initialize the list of existing launchtaskbar buttons when the configuration dialog is shown. */
1367 static void launchbar_configure_initialize_list(LaunchTaskBarPlugin *ltbp, GtkWidget * dlg, GtkTreeView * view)
1368 {
1369 /* Set the selection mode. */
1370 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_BROWSE);
1371
1372 /* Define a column. */
1373 GtkTreeViewColumn* col = gtk_tree_view_get_column(view, 0);
1374
1375 /* Establish the pixbuf column cell renderer. */
1376 GtkCellRenderer * render = gtk_cell_renderer_pixbuf_new();
1377 gtk_tree_view_column_pack_start(col, render, FALSE);
1378 gtk_tree_view_column_set_attributes(col, render, "pixbuf", COL_ICON, NULL);
1379
1380 /* Establish the text column cell renderer. */
1381 render = gtk_cell_renderer_text_new();
1382 gtk_tree_view_column_pack_start(col, render, TRUE);
1383 gtk_tree_view_column_add_attribute(col, render, "text", COL_TITLE);
1384
1385 /* Establish the column data types. */
1386 GtkListStore* list = GTK_LIST_STORE(gtk_tree_view_get_model(view));
1387
1388 /* Initialize from defined launchtaskbar buttons. */
1389 GList *children = gtk_container_get_children(GTK_CONTAINER(ltbp->lb_icon_grid));
1390 GList *l;
1391 for (l = children; l != NULL; l = l->next)
1392 {
1393 LaunchButton * btn = (LaunchButton *) l->data;
1394 GdkPixbuf * pix;
1395 GtkTreeIter it;
1396 if (launch_button_get_settings(btn) == NULL) /* bootstrap button */
1397 continue;
1398 gtk_list_store_append(list, &it);
1399 pix = fm_pixbuf_from_icon(launch_button_get_icon(btn), PANEL_ICON_SIZE);
1400 gtk_list_store_set(list, &it,
1401 COL_ICON, pix,
1402 COL_TITLE, launch_button_get_disp_name(btn),
1403 COL_BTN, btn,
1404 -1);
1405 g_object_unref(pix);
1406 }
1407 g_list_free(children);
1408 g_object_set_data(G_OBJECT(dlg), "defined_view", view);
1409 }
1410
1411 static void plugin_set_expand_status(LaunchTaskBarPlugin *ltbp, gboolean expand_new)
1412 {
1413 gboolean old_expand, fill;
1414 guint padding;
1415 GtkPackType pack_type;
1416 GtkWidget *box = gtk_widget_get_parent(ltbp->plugin);
1417 g_return_if_fail(box);
1418 gtk_box_query_child_packing(GTK_BOX(box), ltbp->plugin, &old_expand, &fill, &padding, &pack_type);
1419 gtk_box_set_child_packing(GTK_BOX(box), ltbp->plugin, expand_new, fill, padding, pack_type);
1420 }
1421
1422 static void set_config_visibility(LaunchTaskBarPlugin *ltbp)
1423 {
1424 switch (ltbp->mode) {
1425 default:
1426 case LAUNCHTASKBAR:
1427 gtk_widget_set_visible(ltbp->p_notebook_page_launch, TRUE);
1428 gtk_widget_set_visible(ltbp->p_notebook_page_task, TRUE);
1429 gtk_notebook_set_show_tabs(ltbp->p_notebook, TRUE);
1430 break;
1431 case TASKBAR:
1432 gtk_widget_set_visible(ltbp->p_notebook_page_launch, FALSE);
1433 gtk_widget_set_visible(ltbp->p_notebook_page_task, TRUE);
1434 gtk_notebook_set_show_tabs(ltbp->p_notebook, FALSE);
1435 break;
1436 case LAUNCHBAR:
1437 gtk_widget_set_visible(ltbp->p_notebook_page_launch, TRUE);
1438 gtk_widget_set_visible(ltbp->p_notebook_page_task, FALSE);
1439 gtk_notebook_set_show_tabs(ltbp->p_notebook, FALSE);
1440 }
1441 }
1442
1443 static void on_combobox_mode_changed(GtkComboBox *p_combobox, gpointer p_data)
1444 {
1445 LaunchTaskBarPlugin *ltbp = p_data;
1446 int new_mode = gtk_combo_box_get_active(GTK_COMBO_BOX(p_combobox));
1447
1448 if (new_mode < 0 || new_mode == ltbp->mode) /* no change was made */
1449 return;
1450
1451 ltbp->mode = new_mode;
1452
1453 set_config_visibility(ltbp);
1454
1455 switch (ltbp->mode) {
1456 case LAUNCHBAR:
1457 if (ltbp->tb_icon_grid)
1458 gtk_widget_set_visible(ltbp->tb_icon_grid, FALSE);
1459 launchtaskbar_constructor_launch(ltbp);
1460 plugin_set_expand_status(ltbp, FALSE);
1461 gtk_widget_set_name(ltbp->plugin, "launchbar");
1462 break;
1463 case TASKBAR:
1464 gtk_widget_set_visible(ltbp->lb_icon_grid, FALSE);
1465 launchtaskbar_constructor_task(ltbp);
1466 plugin_set_expand_status(ltbp, TRUE);
1467 gtk_widget_set_name(ltbp->plugin, "taskbar");
1468 break;
1469 default:
1470 ltbp->mode = LAUNCHTASKBAR;
1471 case LAUNCHTASKBAR:
1472 launchtaskbar_constructor_launch(ltbp);
1473 launchtaskbar_constructor_task(ltbp);
1474 plugin_set_expand_status(ltbp, TRUE);
1475 gtk_widget_set_name(ltbp->plugin, "launchtaskbar");
1476 break;
1477 }
1478
1479 config_group_set_int(ltbp->settings, "LaunchTaskBarMode", ltbp->mode);
1480 }
1481
1482 static void on_checkbutton_show_tooltips_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1483 {
1484 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1485 ltbp->flags.tooltips = gtk_toggle_button_get_active(p_togglebutton);
1486 //g_print("\nltbp->flags.tooltips upd\n");
1487 config_group_set_int(ltbp->settings, "tooltips", ltbp->flags.tooltips);
1488 taskbar_apply_configuration(ltbp);
1489 }
1490
1491 static void on_checkbutton_icons_only_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1492 {
1493 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1494 ltbp->flags.icons_only = gtk_toggle_button_get_active(p_togglebutton);
1495 //g_print("\ntb->flags.icons_only upd\n");
1496 config_group_set_int(ltbp->settings, "IconsOnly", ltbp->flags.icons_only);
1497 taskbar_apply_configuration(ltbp);
1498 }
1499
1500 static void on_checkbutton_flat_buttons_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1501 {
1502 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1503 ltbp->flags.flat_button = gtk_toggle_button_get_active(p_togglebutton);
1504 //g_print("\ntb->flags.flat_button upd\n");
1505 config_group_set_int(ltbp->settings, "FlatButton", ltbp->flags.flat_button);
1506 taskbar_apply_configuration(ltbp);
1507 }
1508
1509 static void on_checkbutton_show_all_desks_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1510 {
1511 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1512 ltbp->flags.show_all_desks = gtk_toggle_button_get_active(p_togglebutton);
1513 //g_print("\ntb->flags.show_all_desks upd\n");
1514 config_group_set_int(ltbp->settings, "ShowAllDesks", ltbp->flags.show_all_desks);
1515 taskbar_apply_configuration(ltbp);
1516 }
1517
1518 static void on_checkbutton_same_monitor_only_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1519 {
1520 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1521 ltbp->flags.same_monitor_only = gtk_toggle_button_get_active(p_togglebutton);
1522 //g_print("\ntb->flags.same_monitor_only upd\n");
1523 config_group_set_int(ltbp->settings, "SameMonitorOnly", ltbp->flags.same_monitor_only);
1524 taskbar_apply_configuration(ltbp);
1525 }
1526
1527 static void on_checkbutton_disable_taskbar_upscale_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1528 {
1529 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1530 ltbp->flags.disable_taskbar_upscale = gtk_toggle_button_get_active(p_togglebutton);
1531 //g_print("\ntb->flags.disable_taskbar_upscale upd\n");
1532 config_group_set_int(ltbp->settings, "DisableUpscale", ltbp->flags.disable_taskbar_upscale);
1533 taskbar_apply_configuration(ltbp);
1534 }
1535
1536 static void on_checkbutton_use_smaller_icons_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1537 {
1538 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1539 ltbp->flags.use_smaller_icons = gtk_toggle_button_get_active(p_togglebutton);
1540 config_group_set_int(ltbp->settings, "UseSmallerIcons", ltbp->flags.use_smaller_icons);
1541 taskbar_apply_configuration(ltbp);
1542 }
1543
1544 static void on_checkbutton_mouse_wheel_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1545 {
1546 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1547 ltbp->flags.use_mouse_wheel = gtk_toggle_button_get_active(p_togglebutton);
1548 //g_print("\ntb->flags.use_mouse_wheel upd\n");
1549 config_group_set_int(ltbp->settings, "UseMouseWheel", ltbp->flags.use_mouse_wheel);
1550 taskbar_apply_configuration(ltbp);
1551 }
1552
1553 static void on_checkbutton_urgency_hint_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1554 {
1555 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1556 ltbp->flags.use_urgency_hint = gtk_toggle_button_get_active(p_togglebutton);
1557 //g_print("\ntb->flags.use_urgency_hint upd\n");
1558 config_group_set_int(ltbp->settings, "UseUrgencyHint", ltbp->flags.use_urgency_hint);
1559 taskbar_apply_configuration(ltbp);
1560 /* Start/stop blinking timeout if configured */
1561 if (ltbp->flags.use_urgency_hint)
1562 set_timer_on_task(ltbp);
1563 else
1564 reset_timer_on_task(ltbp);
1565 }
1566
1567 static void on_checkbutton_grouped_tasks_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1568 {
1569 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1570 ltbp->grouped_tasks = gtk_toggle_button_get_active(p_togglebutton);
1571 //g_print("\ntb->grouped_tasks upd\n");
1572 config_group_set_int(ltbp->settings, "GroupedTasks", ltbp->grouped_tasks);
1573 taskbar_apply_configuration(ltbp);
1574 if (ltbp->grouped_tasks)
1575 {
1576 gboolean changed = FALSE;
1577 GList *children, *this, *l;
1578 int i = 0;
1579
1580 children = gtk_container_get_children(GTK_CONTAINER(ltbp->tb_icon_grid));
1581 /* merge buttons with the same class into first of that class */
1582 while ((this = g_list_nth(children, i++)))
1583 {
1584 for (l = this->next; l; l = l->next)
1585 if (task_button_merge(this->data, l->data))
1586 changed = TRUE;
1587 if (changed)
1588 {
1589 /* some button was consumed, need to reload buttons list */
1590 g_list_free(children);
1591 children = gtk_container_get_children(GTK_CONTAINER(ltbp->tb_icon_grid));
1592 changed = FALSE;
1593 }
1594 }
1595 g_list_free(children);
1596 }
1597 else
1598 {
1599 TaskButton *old_btn, *new_btn;
1600 GList *children, *this;
1601 int i;
1602
1603 children = gtk_container_get_children(GTK_CONTAINER(ltbp->tb_icon_grid));
1604 /* split each button starting from last one */
1605 for (this = g_list_last(children); this; this = this->prev)
1606 {
1607 old_btn = this->data;
1608 i = panel_icon_grid_get_child_position(PANEL_ICON_GRID(ltbp->tb_icon_grid),
1609 GTK_WIDGET(old_btn));
1610 while ((new_btn = task_button_split(old_btn)))
1611 {
1612 /* insert rest before old_btn */
1613 taskbar_add_task_button(ltbp, new_btn);
1614 panel_icon_grid_reorder_child(PANEL_ICON_GRID(ltbp->tb_icon_grid),
1615 GTK_WIDGET(new_btn), i);
1616 /* continue split with the rest */
1617 old_btn = new_btn;
1618 }
1619 }
1620 g_list_free(children);
1621 }
1622 }
1623
1624 static void on_spinbutton_max_width_value_changed(GtkSpinButton *p_spinbutton, gpointer p_data)
1625 {
1626 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1627 ltbp->task_width_max = gtk_spin_button_get_value(p_spinbutton);
1628 //g_print("\ntb->task_width_max upd\n");
1629 config_group_set_int(ltbp->settings, "MaxTaskWidth", ltbp->task_width_max);
1630 taskbar_apply_configuration(ltbp);
1631 }
1632
1633 static void on_spinbutton_spacing_value_changed(GtkSpinButton *p_spinbutton, gpointer p_data)
1634 {
1635 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1636 ltbp->spacing = gtk_spin_button_get_value(p_spinbutton);
1637 //g_print("\ntb->spacing upd\n");
1638 config_group_set_int(ltbp->settings, "spacing", ltbp->spacing);
1639 taskbar_apply_configuration(ltbp);
1640 }
1641
1642 static gboolean on_defined_view_button_press_event(GtkWidget *p_widget, GdkEventButton *p_event, gpointer p_data)
1643 {
1644 LaunchTaskBarPlugin *lb = (LaunchTaskBarPlugin *)p_data;
1645 if(p_event->button == 1)
1646 {
1647 if(p_event->type == GDK_2BUTTON_PRESS)
1648 {
1649 gtk_button_clicked(GTK_BUTTON(lb->p_button_remove));
1650 }
1651 }
1652 return FALSE;
1653 }
1654
1655 static void on_defined_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_data)
1656 {
1657 gboolean label_set = FALSE;
1658 LaunchTaskBarPlugin *lb = (LaunchTaskBarPlugin *)p_data;
1659 GtkTreeIter tree_iter_sel;
1660 GtkTreeModel* p_treemodel = gtk_tree_view_get_model(p_treeview);
1661 GtkTreeSelection *p_treeselection = gtk_tree_view_get_selection(p_treeview);
1662 if(gtk_tree_selection_get_selected(p_treeselection,
1663 (GtkTreeModel **)(&p_treemodel),
1664 &tree_iter_sel))
1665 {
1666 LaunchButton * p_btn;
1667 FmFileInfo *fi;
1668 gtk_tree_model_get(p_treemodel, &tree_iter_sel, COL_BTN, &p_btn, -1);
1669 if ((p_btn != NULL) && ((fi = launch_button_get_file_info(p_btn)) != NULL))
1670 {
1671 GString *p_gstring = g_string_new("");
1672 g_string_printf(p_gstring, "<i>%s</i>", fm_file_info_get_disp_name(fi));
1673 gtk_label_set_markup(GTK_LABEL(lb->p_label_def_app_exec), p_gstring->str);
1674 g_string_free(p_gstring, TRUE/*free also gstring->str*/);
1675 label_set = TRUE;
1676 }
1677 }
1678 gtk_widget_set_visible(lb->p_label_def_app_exec, label_set);
1679 gtk_widget_set_sensitive(lb->p_button_remove, label_set);
1680 }
1681
1682 static void on_menu_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_data)
1683 {
1684 gboolean label_set = FALSE;
1685 LaunchTaskBarPlugin *lb = (LaunchTaskBarPlugin *)p_data;
1686 GAppInfo *app = fm_app_menu_view_dup_selected_app(p_treeview);
1687
1688 if (app)
1689 {
1690 GString *p_gstring = g_string_new("");
1691 if (g_app_info_get_description(app))
1692 g_string_printf(p_gstring, "<i>%s</i>", g_app_info_get_description(app));
1693 else
1694 g_string_printf(p_gstring, "<i>%s</i>", g_app_info_get_name(app));
1695 gtk_label_set_markup(GTK_LABEL(lb->p_label_menu_app_exec), p_gstring->str);
1696 g_string_free(p_gstring, TRUE/*free also gstring->str*/);
1697 label_set = TRUE;
1698 }
1699 gtk_widget_set_visible(lb->p_label_menu_app_exec, label_set);
1700 gtk_widget_set_sensitive(lb->p_button_add, label_set);
1701 }
1702
1703 static void on_menu_view_row_activated(GtkTreeView *tree_view, GtkTreePath *path,
1704 GtkTreeViewColumn *column,
1705 LaunchTaskBarPlugin *ltbp)
1706 {
1707 _launchbar_configure_add(tree_view, ltbp);
1708 }
1709
1710 /* FIXME: add support for global hotkeys for launchers */
1711
1712 /* Callback when the configuration dialog is to be shown. */
1713 static GtkWidget *launchtaskbar_configure(LXPanel *panel, GtkWidget *p)
1714 {
1715 LaunchTaskBarPlugin *ltbp = lxpanel_plugin_get_data(p);
1716
1717 {
1718 GtkWidget *dlg, *btn, *defined_view, *menu_view, *menu_view_window;
1719 GtkBuilder *builder = gtk_builder_new();
1720 GObject *object;
1721
1722 gtk_builder_add_from_file(builder, PACKAGE_UI_DIR "/launchtaskbar.ui", NULL);
1723 dlg = (GtkWidget *)gtk_builder_get_object(builder, "dlg");
1724 panel_apply_icon(GTK_WINDOW(dlg));
1725
1726 defined_view = (GtkWidget *)gtk_builder_get_object(builder, "defined_view");
1727 menu_view_window = (GtkWidget*)gtk_builder_get_object(builder, "menu_view_window");
1728 if (menu_view_window == NULL) /* fallback for old glade file */
1729 {
1730 menu_view_window = (GtkWidget*)gtk_builder_get_object(builder, "scroll2");
1731 gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(menu_view_window)));
1732 }
1733 menu_view = GTK_WIDGET(fm_app_menu_view_new());
1734 gtk_container_add(GTK_CONTAINER(menu_view_window), menu_view);
1735 gtk_widget_show(menu_view);
1736 ltbp->p_label_def_app_exec = (GtkWidget*)gtk_builder_get_object(builder, "label_def_app_exec");
1737 ltbp->p_label_menu_app_exec = (GtkWidget*)gtk_builder_get_object(builder, "label_menu_app_exec");
1738
1739 /* Connect signals. */
1740 ltbp->p_button_add = (GtkWidget *)gtk_builder_get_object(builder, "button_add");
1741 g_signal_connect(ltbp->p_button_add, "clicked", G_CALLBACK(launchbar_configure_add_button), ltbp);
1742
1743 ltbp->p_button_remove = (GtkWidget *)gtk_builder_get_object(builder, "button_remove");
1744 g_signal_connect(ltbp->p_button_remove, "clicked", G_CALLBACK(launchbar_configure_remove_button), ltbp);
1745
1746 btn = (GtkWidget *)gtk_builder_get_object(builder, "button_up");
1747 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_up_button), ltbp);
1748
1749 btn = (GtkWidget *)gtk_builder_get_object(builder, "button_down");
1750 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_down_button), ltbp);
1751
1752 /* FIXME: add a button 'New' with launcher creation dialog */
1753
1754 g_signal_connect(defined_view, "button-press-event", G_CALLBACK(on_defined_view_button_press_event), ltbp);
1755 g_signal_connect(defined_view, "cursor-changed", G_CALLBACK(on_defined_view_cursor_changed), ltbp);
1756 g_signal_connect(menu_view, "cursor-changed", G_CALLBACK(on_menu_view_cursor_changed), ltbp);
1757 g_signal_connect(menu_view, "row-activated", G_CALLBACK(on_menu_view_row_activated), ltbp);
1758
1759 ltbp->p_notebook = GTK_NOTEBOOK(gtk_builder_get_object(builder, "notebook"));
1760 ltbp->p_notebook_page_launch = gtk_notebook_get_nth_page(ltbp->p_notebook, 0);
1761 ltbp->p_notebook_page_task = gtk_notebook_get_nth_page(ltbp->p_notebook, 1);
1762 set_config_visibility(ltbp);
1763 object = gtk_builder_get_object(builder, "combobox_mode");
1764 gtk_combo_box_set_active(GTK_COMBO_BOX(object), ltbp->mode);
1765 g_signal_connect(object, "changed",
1766 G_CALLBACK(on_combobox_mode_changed), ltbp);
1767
1768 #define SETUP_TOGGLE_BUTTON(button,member) \
1769 object = gtk_builder_get_object(builder, #button); \
1770 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object), ltbp->flags.member); \
1771 g_signal_connect(object, "toggled", G_CALLBACK(on_##button##_toggled), ltbp)
1772
1773 SETUP_TOGGLE_BUTTON(checkbutton_show_tooltips, tooltips);
1774 SETUP_TOGGLE_BUTTON(checkbutton_icons_only, icons_only);
1775 SETUP_TOGGLE_BUTTON(checkbutton_flat_buttons, flat_button);
1776 SETUP_TOGGLE_BUTTON(checkbutton_show_all_desks, show_all_desks);
1777 SETUP_TOGGLE_BUTTON(checkbutton_same_monitor_only, same_monitor_only);
1778 SETUP_TOGGLE_BUTTON(checkbutton_mouse_wheel, use_mouse_wheel);
1779 SETUP_TOGGLE_BUTTON(checkbutton_urgency_hint, use_urgency_hint);
1780 SETUP_TOGGLE_BUTTON(checkbutton_disable_taskbar_upscale, disable_taskbar_upscale);
1781 //SETUP_TOGGLE_BUTTON(checkbutton_use_smaller_icons, use_smaller_icons);
1782 #undef SETUP_TOGGLE_BUTTON
1783 object = gtk_builder_get_object(builder, "checkbutton_grouped_tasks");
1784 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object), ltbp->grouped_tasks);
1785 g_signal_connect(object, "toggled", G_CALLBACK(on_checkbutton_grouped_tasks_toggled), ltbp);
1786 /* FIXME: for transitional period, turn into SETUP_TOGGLE_BUTTON later */
1787 object = gtk_builder_get_object(builder, "checkbutton_use_smaller_icons");
1788 if (object)
1789 {
1790 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(object), ltbp->flags.use_smaller_icons);
1791 g_signal_connect(object, "toggled", G_CALLBACK(on_checkbutton_use_smaller_icons_toggled), ltbp);
1792 }
1793
1794 #define SETUP_SPIN_BUTTON(button,member) \
1795 object = gtk_builder_get_object(builder, #button); \
1796 gtk_spin_button_set_value(GTK_SPIN_BUTTON(object), ltbp->member); \
1797 g_signal_connect(object, "value-changed", \
1798 G_CALLBACK(on_##button##_value_changed), ltbp)
1799
1800 SETUP_SPIN_BUTTON(spinbutton_max_width, task_width_max);
1801 SETUP_SPIN_BUTTON(spinbutton_spacing, spacing);
1802 #undef SETUP_SPIN_BUTTON
1803
1804 ltbp->config_dlg = dlg;
1805
1806 /* Initialize the tree view contents. */
1807 launchbar_configure_initialize_list(ltbp, dlg, GTK_TREE_VIEW(defined_view));
1808 g_object_set_data(G_OBJECT(dlg), "menu_view", menu_view);
1809
1810 gtk_widget_set_visible(ltbp->p_label_menu_app_exec, FALSE);
1811 gtk_widget_set_visible(ltbp->p_label_def_app_exec, FALSE);
1812 gtk_widget_set_sensitive(ltbp->p_button_add, FALSE);
1813 gtk_widget_set_sensitive(ltbp->p_button_remove, FALSE);
1814 if (ltbp->fixed_mode)
1815 {
1816 object = gtk_builder_get_object(builder, "hbox_mode");
1817 if (object)
1818 gtk_widget_destroy(GTK_WIDGET(object));
1819 if (ltbp->mode == LAUNCHBAR)
1820 gtk_window_set_title(GTK_WINDOW(ltbp->config_dlg),
1821 _("Application Launch Bar"));
1822 else
1823 gtk_window_set_title(GTK_WINDOW(ltbp->config_dlg),
1824 _("Task Bar (Window List)"));
1825 }
1826
1827 g_object_unref(builder);
1828 }
1829 return ltbp->config_dlg;
1830 }
1831
1832 /* Callback when panel configuration changes. */
1833 static void launchtaskbar_panel_configuration_changed(LXPanel *panel, GtkWidget *p)
1834 {
1835 /* Set orientation into the icon grid. */
1836 LaunchTaskBarPlugin *ltbp = lxpanel_plugin_get_data(p);
1837 int new_icon_size = panel_get_icon_size(panel);
1838 int height = panel_get_height(panel);
1839
1840 if (ltbp->lb_built)
1841 panel_icon_grid_set_geometry(PANEL_ICON_GRID(ltbp->lb_icon_grid),
1842 panel_get_orientation(panel),
1843 new_icon_size, new_icon_size,
1844 3, 0, height);
1845
1846 /* Redraw all the labels. Icon size or font color may have changed. */
1847 if (ltbp->tb_built)
1848 {
1849 ltbp->icon_size = new_icon_size;
1850 panel_icon_grid_set_geometry(PANEL_ICON_GRID(ltbp->tb_icon_grid),
1851 panel_get_orientation(panel),
1852 ((ltbp->flags.icons_only) ? ltbp->icon_size + ICON_ONLY_EXTRA : ltbp->task_width_max),
1853 ((ltbp->flags.icons_only) ? ltbp->icon_size + ICON_ONLY_EXTRA : ltbp->icon_size + ICON_BUTTON_TRIM),
1854 ltbp->spacing, 0, height);
1855 taskbar_reset_menu(ltbp);
1856 taskbar_redraw(ltbp);
1857 }
1858 }
1859
1860 static gboolean launchtaskbar_control(GtkWidget *p, const char *cmd)
1861 {
1862 LaunchTaskBarPlugin *ltbp = lxpanel_plugin_get_data(p);
1863
1864 if (ltbp->mode == LAUNCHBAR || ltbp->mode == LAUNCHTASKBAR)
1865 {
1866 if (strncmp(cmd, "add ", 4) == 0)
1867 {
1868 config_setting_t *s;
1869
1870 s = config_group_add_subgroup(ltbp->settings, "Button");
1871 config_group_set_string(s, "id", &cmd[4]);
1872 if (launchbutton_constructor(ltbp, s))
1873 {
1874 launchbar_remove_bootstrap(ltbp);
1875 lxpanel_config_save(ltbp->panel);
1876 return TRUE;
1877 }
1878 else
1879 config_setting_destroy(s);
1880 }
1881 }
1882 return FALSE;
1883 }
1884
1885 /* Redraw all tasks in the taskbar. */
1886 static void taskbar_redraw(LaunchTaskBarPlugin * tb)
1887 {
1888 GList *children = gtk_container_get_children(GTK_CONTAINER(tb->tb_icon_grid));
1889 GList *l;
1890 guint mon = panel_get_monitor(tb->panel);
1891 guint icon_size = panel_get_icon_size(tb->panel);
1892
1893 if (tb->flags.use_smaller_icons)
1894 icon_size -= 4;
1895 for (l = children; l; l = l->next)
1896 task_button_update(l->data, tb->current_desktop, tb->number_of_desktops,
1897 mon, icon_size, tb->flags);
1898 g_list_free(children);
1899 }
1900
1901 /* Determine if a task should be visible given its NET_WM_STATE. */
1902 static gboolean accept_net_wm_state(NetWMState * nws)
1903 {
1904 return ( ! (nws->skip_taskbar));
1905 }
1906
1907 /* Determine if a task should be visible given its NET_WM_WINDOW_TYPE. */
1908 static gboolean accept_net_wm_window_type(NetWMWindowType * nwwt)
1909 {
1910 return ( ! ((nwwt->desktop) || (nwwt->dock) || (nwwt->splash)));
1911 }
1912
1913 /* Set the class associated with a task. */
1914 static char *task_get_class(Window win)
1915 {
1916 /* Read the WM_CLASS property. */
1917 XClassHint ch;
1918 ch.res_name = NULL;
1919 ch.res_class = NULL;
1920 char *res_class = NULL;
1921 XGetClassHint(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, &ch);
1922
1923 /* If the res_name was returned, free it. We make no use of it at this time. */
1924 if (ch.res_name != NULL)
1925 {
1926 XFree(ch.res_name);
1927 }
1928
1929 /* If the res_class was returned, process it.
1930 * This identifies the application that created the window and is the basis for taskbar grouping. */
1931 if (ch.res_class != NULL)
1932 {
1933 /* Convert the class to UTF-8 and enter it in the class table. */
1934 res_class = g_locale_to_utf8(ch.res_class, -1, NULL, NULL, NULL);
1935 XFree(ch.res_class);
1936 }
1937 return res_class;
1938 }
1939
1940 /* Look up a task in the task list. */
1941 static TaskButton *task_lookup(LaunchTaskBarPlugin * tb, Window win)
1942 {
1943 TaskButton *task = NULL;
1944 GList *children = gtk_container_get_children(GTK_CONTAINER(tb->tb_icon_grid));
1945 GList *l;
1946
1947 for (l = children; l; l = l->next)
1948 if (task_button_has_window(l->data, win))
1949 {
1950 task = l->data;
1951 break;
1952 }
1953 g_list_free(children);
1954 return task;
1955 }
1956
1957
1958 #ifndef DISABLE_MENU
1959 static void on_menuitem_lock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb);
1960 static void on_menuitem_unlock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb);
1961 static void on_menuitem_new_instance_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb);
1962 #endif
1963
1964 static void on_task_menu_built(GtkWidget *unused, GtkMenu *menu, LaunchTaskBarPlugin *tb)
1965 {
1966 #ifndef DISABLE_MENU
1967 /* add callbacks for task-to-launcher items with weak pointers */
1968 void (*_m_add)(GtkMenuShell *self, GtkWidget* child);
1969
1970 if (panel_is_at_bottom(tb->panel))
1971 _m_add = gtk_menu_shell_append;
1972 else
1973 _m_add = gtk_menu_shell_prepend;
1974
1975 tb->p_menuitem_lock_tbp = gtk_menu_item_new_with_mnemonic(_("A_dd to Launcher"));
1976 g_object_add_weak_pointer(G_OBJECT(menu), (void **)&tb->p_menuitem_lock_tbp);
1977 tb->p_menuitem_unlock_tbp = gtk_menu_item_new_with_mnemonic(_("Rem_ove from Launcher"));
1978 g_object_add_weak_pointer(G_OBJECT(menu), (void **)&tb->p_menuitem_lock_tbp);
1979 tb->p_menuitem_new_instance = gtk_menu_item_new_with_mnemonic(_("_New Instance"));
1980 g_object_add_weak_pointer(G_OBJECT(menu), (void **)&tb->p_menuitem_lock_tbp);
1981 tb->p_menuitem_separator = gtk_separator_menu_item_new();
1982 _m_add(GTK_MENU_SHELL(menu), tb->p_menuitem_separator);
1983 _m_add(GTK_MENU_SHELL(menu), tb->p_menuitem_lock_tbp);
1984 _m_add(GTK_MENU_SHELL(menu), tb->p_menuitem_unlock_tbp);
1985 _m_add(GTK_MENU_SHELL(menu), tb->p_menuitem_new_instance);
1986 g_signal_connect(G_OBJECT(tb->p_menuitem_lock_tbp), "activate", (GCallback)on_menuitem_lock_tbp_clicked, tb);
1987 g_signal_connect(G_OBJECT(tb->p_menuitem_unlock_tbp), "activate", (GCallback)on_menuitem_unlock_tbp_clicked, tb);
1988 g_signal_connect(G_OBJECT(tb->p_menuitem_new_instance), "activate", (GCallback)on_menuitem_new_instance_clicked, tb);
1989 #endif
1990 }
1991
1992 static void on_task_menu_target_set(TaskButton *btn, gulong win, LaunchTaskBarPlugin *ltbp)
1993 {
1994 #ifndef DISABLE_MENU
1995 if(ltbp->mode == LAUNCHTASKBAR)
1996 {
1997 FmPath *path = f_find_menu_launchbutton_recursive(win, ltbp);
1998 LaunchButton *btn = launchbar_exec_bin_exists(ltbp, path);
1999 /* FIXME: shouldn't we make file info at task button creation? */
2000 #ifdef DEBUG
2001 g_print("\nTB '%s' right-click, in LB: %c\n", tk->exec_bin, btn != NULL ? 'Y':'N');
2002 #endif
2003 if(btn != NULL)
2004 {
2005 gtk_widget_set_visible(ltbp->p_menuitem_lock_tbp, FALSE);
2006 gtk_widget_set_visible(ltbp->p_menuitem_unlock_tbp, TRUE);
2007 gtk_widget_set_visible(ltbp->p_menuitem_new_instance, TRUE);
2008 }
2009 else
2010 {
2011 gtk_widget_set_visible(ltbp->p_menuitem_lock_tbp, path != NULL);
2012 gtk_widget_set_visible(ltbp->p_menuitem_unlock_tbp, FALSE);
2013 gtk_widget_set_visible(ltbp->p_menuitem_new_instance, path != NULL);
2014 }
2015 gtk_widget_set_visible(ltbp->p_menuitem_separator, TRUE);
2016 if (ltbp->path)
2017 fm_path_unref(ltbp->path);
2018 ltbp->path = path;
2019 }
2020 else
2021 {
2022 gtk_widget_set_visible(ltbp->p_menuitem_lock_tbp, FALSE);
2023 gtk_widget_set_visible(ltbp->p_menuitem_unlock_tbp, FALSE);
2024 gtk_widget_set_visible(ltbp->p_menuitem_new_instance, FALSE);
2025 gtk_widget_set_visible(ltbp->p_menuitem_separator, FALSE);
2026 }
2027 #endif
2028 }
2029
2030 /* Handler for "drag-motion" timeout. */
2031 static gboolean taskbar_button_drag_motion_timeout(LaunchTaskBarPlugin * tb)
2032 {
2033 //guint time;
2034 if (g_source_is_destroyed(g_main_current_source()))
2035 return FALSE;
2036 //time = gtk_get_current_event_time();
2037 //task_raise_window(tk, ((time != 0) ? time : CurrentTime)); // ???
2038 tb->dnd_delay_timer = 0;
2039 return FALSE;
2040 }
2041
2042 /* Handler for "drag-motion" event from taskbar button. */
2043 static gboolean taskbar_button_drag_motion(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, LaunchTaskBarPlugin * tb)
2044 {
2045 GtkWidget * drag_source = gtk_drag_get_source_widget(drag_context);
2046 if (drag_source != NULL && gtk_widget_get_parent(drag_source) == gtk_widget_get_parent(widget))
2047 {
2048 tb->dnd_task_moving = TRUE;
2049 gdk_drag_status(drag_context, GDK_ACTION_MOVE, time);
2050 }
2051 else
2052 {
2053 /* Prevent excessive motion notification. */
2054 if (tb->dnd_delay_timer == 0)
2055 tb->dnd_delay_timer = g_timeout_add(DRAG_ACTIVE_DELAY, (GSourceFunc) taskbar_button_drag_motion_timeout, tb);
2056
2057 gdk_drag_status(drag_context, 0, time);
2058 }
2059 return TRUE;
2060 }
2061
2062 /* Handler for "drag-drop" event from taskbar button. */
2063 static gboolean taskbar_button_drag_drop(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, LaunchTaskBarPlugin * tb)
2064 {
2065 tb->dnd_task_moving = FALSE;
2066 GtkWidget * drag_source = gtk_drag_get_source_widget(drag_context);
2067 if (drag_source != NULL && gtk_widget_get_parent(drag_source) == gtk_widget_get_parent(widget))
2068 {
2069 if (drag_source != widget)
2070 {
2071 PanelIconGrid *ig = PANEL_ICON_GRID(tb->tb_icon_grid);
2072 gint i = panel_icon_grid_get_child_position(ig, widget);
2073 panel_icon_grid_reorder_child(ig, drag_source, i);
2074 }
2075 gtk_drag_finish(drag_context, TRUE, TRUE, time);
2076 return TRUE;
2077 }
2078
2079 return FALSE;
2080 }
2081
2082 /* Handler for "drag-leave" event from taskbar button. */
2083 static void taskbar_button_drag_leave(GtkWidget * widget, GdkDragContext * drag_context, guint time, LaunchTaskBarPlugin * tb)
2084 {
2085 /* Cancel the timer if set. */
2086 if (tb->dnd_delay_timer != 0)
2087 {
2088 g_source_remove(tb->dnd_delay_timer);
2089 tb->dnd_delay_timer = 0;
2090 }
2091 return;
2092 }
2093
2094 /* Handler for "enter" event from taskbar button. This indicates that the cursor position has entered the button. */
2095 static void taskbar_button_enter(GtkWidget * widget, GdkEvent *event, LaunchTaskBarPlugin * tb)
2096 {
2097 tb->dnd_task_moving = FALSE;
2098 }
2099
2100 /* Handler for "button-release-event" event from taskbar button. */
2101 static gboolean taskbar_button_release_event(GtkWidget * widget, GdkEventButton * event, LaunchTaskBarPlugin * tb)
2102 {
2103 if (tb->dnd_task_moving)
2104 /* SF bug#731: don't process button release with DND. Also if button was
2105 released outside of widget but DND wasn't activated: this might happen
2106 if drag started at edge of button so drag treshold wasn't reached. */
2107 return TRUE;
2108 return FALSE;
2109 }
2110
2111 enum {
2112 TARGET_TASK_BUTTON
2113 };
2114
2115 static GtkTargetEntry task_button_target_list[] = {
2116 { "task_button", GTK_TARGET_SAME_APP, TARGET_TASK_BUTTON }
2117 };
2118
2119 static guint task_button_n_targets = G_N_ELEMENTS(task_button_target_list);
2120
2121 /* Build graphic elements needed for a task button. */
2122 static void taskbar_add_task_button(LaunchTaskBarPlugin * tb, TaskButton * task)
2123 {
2124 /* Allocate a toggle button as the top level widget. */
2125 gtk_container_add(GTK_CONTAINER(tb->tb_icon_grid), GTK_WIDGET(task));
2126
2127 /* Connect signals to the button. */
2128 /* handle menu callbacks */
2129 g_signal_connect(G_OBJECT(task), "menu-built",
2130 (GCallback)on_task_menu_built, tb);
2131 g_signal_connect(G_OBJECT(task), "menu-target-set",
2132 (GCallback)on_task_menu_target_set, tb);
2133 /* handle drag & drop on task buttons */
2134 gtk_drag_dest_set(GTK_WIDGET(task), 0, NULL, 0, 0);
2135 gtk_drag_source_set(GTK_WIDGET(task), GDK_BUTTON1_MASK,
2136 task_button_target_list, task_button_n_targets,
2137 GDK_ACTION_MOVE);
2138 g_signal_connect(G_OBJECT(task), "drag-motion",
2139 G_CALLBACK(taskbar_button_drag_motion), tb);
2140 g_signal_connect(G_OBJECT(task), "drag-leave",
2141 G_CALLBACK(taskbar_button_drag_leave), tb);
2142 g_signal_connect(G_OBJECT(task), "drag-drop",
2143 G_CALLBACK(taskbar_button_drag_drop), tb);
2144 g_signal_connect(task, "button-release-event",
2145 G_CALLBACK(taskbar_button_release_event), tb);
2146 g_signal_connect_after(G_OBJECT(task), "enter-notify-event",
2147 G_CALLBACK(taskbar_button_enter), tb);
2148 }
2149
2150 /* add win to tb, using list of task buttons */
2151 static void taskbar_add_new_window(LaunchTaskBarPlugin * tb, Window win, GList *list)
2152 {
2153 gchar *res_class = task_get_class(win);
2154 TaskButton *task;
2155
2156 if (!tb->grouped_tasks || res_class == NULL)
2157 list = NULL;
2158 else for (; list; list = list->next)
2159 if (task_button_add_window(list->data, win, res_class))
2160 break;
2161 if (list != NULL)
2162 return; /* some button accepted it, done */
2163
2164 task = task_button_new(win, tb->current_desktop, tb->number_of_desktops,
2165 tb->panel, res_class, tb->flags);
2166 taskbar_add_task_button(tb, task);
2167 }
2168
2169 /*****************************************************
2170 * handlers for NET actions *
2171 *****************************************************/
2172
2173 /* Handler for "client-list" event from root window listener. */
2174 static void taskbar_net_client_list(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2175 {
2176 LaunchTaskBarPlugin *ltbp = tb;
2177 if(ltbp->mode == LAUNCHBAR) return;
2178
2179 /* Get the NET_CLIENT_LIST property. */
2180 int client_count;
2181 Window * client_list = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_CLIENT_LIST, XA_WINDOW, &client_count);
2182 if (client_list != NULL)
2183 {
2184 GList *children = gtk_container_get_children(GTK_CONTAINER(tb->tb_icon_grid)), *l;
2185 /* Remove windows from the task list that are not present in the NET_CLIENT_LIST. */
2186 for (l = children; l; l = l->next)
2187 task_button_update_windows_list(l->data, client_list, client_count);
2188 g_list_free(children);
2189 children = gtk_container_get_children(GTK_CONTAINER(tb->tb_icon_grid));
2190 /* Loop over client list, correlating it with task list. */
2191 int i;
2192 for (i = 0; i < client_count; i++)
2193 {
2194 /* Search for the window in the task list. Set up context to do an insert right away if needed. */
2195 for (l = children; l; l = l->next)
2196 {
2197 if (task_button_has_window(l->data, client_list[i]))
2198 break;
2199 }
2200
2201 /* Task is not in task list. */
2202 if (l == NULL)
2203 {
2204 /* Evaluate window state and window type to see if it should be in task list. */
2205 NetWMWindowType nwwt;
2206 NetWMState nws;
2207 get_net_wm_state(client_list[i], &nws);
2208 get_net_wm_window_type(client_list[i], &nwwt);
2209 if ((accept_net_wm_state(&nws))
2210 && (accept_net_wm_window_type(&nwwt)))
2211 {
2212 /* Allocate and initialize new task structure. */
2213 taskbar_add_new_window(tb, client_list[i], children);
2214 g_list_free(children);
2215 children = gtk_container_get_children(GTK_CONTAINER(tb->tb_icon_grid));
2216 }
2217 }
2218 }
2219 g_list_free(children);
2220 XFree(client_list);
2221 }
2222
2223 else /* clear taskbar */
2224 gtk_container_foreach(GTK_CONTAINER(tb->tb_icon_grid),
2225 (GtkCallback)gtk_widget_destroy, NULL);
2226 }
2227
2228 /* Handler for "current-desktop" event from root window listener. */
2229 static void taskbar_net_current_desktop(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2230 {
2231 LaunchTaskBarPlugin *ltbp = tb;
2232 if(ltbp->mode == LAUNCHBAR) return;
2233
2234 /* Store the local copy of current desktops. Redisplay the taskbar. */
2235 tb->current_desktop = get_net_current_desktop();
2236 taskbar_redraw(tb);
2237 }
2238
2239 /* Handler for "number-of-desktops" event from root window listener. */
2240 static void taskbar_net_number_of_desktops(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2241 {
2242 LaunchTaskBarPlugin *ltbp = tb;
2243 if(ltbp->mode == LAUNCHBAR) return;
2244
2245 /* Store the local copy of number of desktops. Recompute the popup menu and redisplay the taskbar. */
2246 tb->number_of_desktops = get_net_number_of_desktops();
2247 taskbar_reset_menu(tb);
2248 taskbar_redraw(tb);
2249 }
2250
2251 /* Handler for "active-window" event from root window listener. */
2252 static void taskbar_net_active_window(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2253 {
2254 LaunchTaskBarPlugin *ltbp = tb;
2255 if(ltbp->mode == LAUNCHBAR) return;
2256
2257 /* Get the window that has focus. */
2258 Window * f = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_ACTIVE_WINDOW, XA_WINDOW, 0);
2259
2260 gtk_container_foreach(GTK_CONTAINER(tb->tb_icon_grid),
2261 (GtkCallback)task_button_window_focus_changed, f);
2262 if (f != NULL)
2263 XFree(f);
2264 }
2265
2266 /* Handle PropertyNotify event.
2267 * http://tronche.com/gui/x/icccm/
2268 * http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html */
2269 static void taskbar_property_notify_event(LaunchTaskBarPlugin *tb, XEvent *ev)
2270 {
2271 /* State may be PropertyNewValue, PropertyDeleted. */
2272 if (((XPropertyEvent*) ev)->state == PropertyNewValue)
2273 {
2274 Atom at = ev->xproperty.atom;
2275 Window win = ev->xproperty.window;
2276 if (win != GDK_ROOT_WINDOW())
2277 {
2278 /* Look up task structure by X window handle. */
2279 TaskButton * tk = task_lookup(tb, win);
2280 if (tk != NULL)
2281 {
2282 /* Install an error handler that ignores BadWindow.
2283 * We frequently get a PropertyNotify event on deleted windows. */
2284 XErrorHandler previous_error_handler = XSetErrorHandler(panel_handle_x_error_swallow_BadWindow_BadDrawable);
2285
2286 /* Dispatch on atom. */
2287 if (at == a_NET_WM_STATE)
2288 {
2289 /* Window changed EWMH state. */
2290 NetWMState nws;
2291 get_net_wm_state(win, &nws);
2292 if ( ! accept_net_wm_state(&nws))
2293 task_button_drop_window(tk, win, FALSE);
2294 /* else
2295 task_button_window_state_changed(tk, win, nws); */
2296 }
2297 else if (at == a_NET_WM_WINDOW_TYPE)
2298 {
2299 /* Window changed EWMH window type. */
2300 NetWMWindowType nwwt;
2301 get_net_wm_window_type(win, &nwwt);
2302 if ( ! accept_net_wm_window_type(&nwwt))
2303 task_button_drop_window(tk, win, FALSE);
2304 }
2305 else if (at == XA_WM_CLASS && tb->grouped_tasks
2306 && task_button_drop_window(tk, win, TRUE))
2307 {
2308 GList *children = gtk_container_get_children(GTK_CONTAINER(tb->tb_icon_grid));
2309 /* if Window was not single window of that class then
2310 add it to another class or make another button */
2311 taskbar_add_new_window(tb, win, children);
2312 g_list_free(children);
2313 }
2314 else
2315 {
2316 /* simply notify button, it will handle the event */
2317 task_button_window_xprop_changed(tk, win, at);
2318 }
2319
2320 XSetErrorHandler(previous_error_handler);
2321 }
2322 }
2323 }
2324 }
2325
2326 /* Handle ConfigureNotify events */
2327 static void taskbar_configure_notify_event(LaunchTaskBarPlugin * tb, XConfigureEvent * ev)
2328 {
2329 /* If the same_monitor_only option is set and the window is on a different
2330 monitor than before, redraw the taskbar */
2331 TaskButton *task;
2332
2333 if (ev->window != GDK_ROOT_WINDOW())
2334 {
2335 task = task_lookup(tb, ev->window);
2336 if (task)
2337 {
2338 /* Deleted windows seem to get ConfigureNotify events too. */
2339 XErrorHandler previous_error_handler = XSetErrorHandler(panel_handle_x_error_swallow_BadWindow_BadDrawable);
2340
2341 /* Monitor might be changed so button might need update */
2342 task_button_window_reconfigured(task, ev->window);
2343
2344 XSetErrorHandler(previous_error_handler);
2345 }
2346 }
2347 }
2348
2349 /* GDK event filter. */
2350 static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, LaunchTaskBarPlugin * tb)
2351 {
2352 if (tb->mode == LAUNCHBAR)
2353 return GDK_FILTER_CONTINUE;
2354
2355 if (xev->type == PropertyNotify)
2356 taskbar_property_notify_event(tb, xev);
2357 else if (xev->type == ConfigureNotify)
2358 taskbar_configure_notify_event(tb, &xev->xconfigure);
2359
2360 return GDK_FILTER_CONTINUE;
2361 }
2362
2363 #ifndef DISABLE_MENU
2364 static void on_menuitem_lock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2365 {
2366 LaunchButton *btn;
2367 char *path;
2368 config_setting_t *settings;
2369
2370 if (tb->path)
2371 {
2372 /* Create a button and add settings for it */
2373 path = fm_path_to_str(tb->path);
2374 /* g_debug("*** path '%s'",path); */
2375 settings = config_group_add_subgroup(tb->settings, "Button");
2376 config_group_set_string(settings, "id", path);
2377 g_free(path);
2378 btn = launch_button_new(tb->panel, tb->plugin, tb->path, settings);
2379 if (btn)
2380 gtk_container_add(GTK_CONTAINER(tb->lb_icon_grid), GTK_WIDGET(btn));
2381 else
2382 config_setting_destroy(settings);
2383 lxpanel_config_save(tb->panel);
2384 }
2385 }
2386
2387 static void on_menuitem_unlock_tbp_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2388 {
2389 launchbar_remove_launcher(tb, tb->path);
2390 launchbar_check_bootstrap(tb);
2391 }
2392
2393 static void on_menuitem_new_instance_clicked(GtkWidget * widget, LaunchTaskBarPlugin * tb)
2394 {
2395 if (tb->path)
2396 {
2397 lxpanel_launch_path(tb->panel, tb->path);
2398 }
2399 }
2400 #endif
2401
2402 /* Handler for "window-manager-changed" event. */
2403 static void taskbar_window_manager_changed(GdkScreen * screen, LaunchTaskBarPlugin * tb)
2404 {
2405 /* Force re-evaluation of use_net_active. */
2406 GdkAtom net_active_atom = gdk_x11_xatom_to_atom(a_NET_ACTIVE_WINDOW);
2407 tb->flags.use_net_active = gdk_x11_screen_supports_net_wm_hint(tb->screen, net_active_atom);
2408 taskbar_redraw(tb);
2409 }
2410
2411 /* Callback from configuration dialog mechanism to apply the configuration. */
2412 static void taskbar_apply_configuration(LaunchTaskBarPlugin *ltbp)
2413 {
2414 taskbar_redraw(ltbp);
2415 }
2416
2417 static GtkWidget *launchbar_constructor(LXPanel *panel, config_setting_t *settings)
2418 {
2419 return _launchtaskbar_constructor(panel, settings, LAUNCHBAR);
2420 }
2421
2422 static GtkWidget *taskbar_constructor(LXPanel *panel, config_setting_t *settings)
2423 {
2424 return _launchtaskbar_constructor(panel, settings, TASKBAR);
2425 }
2426
2427 static LXPanelPluginInit _launchbar_init = {
2428 .name = N_("Application Launch Bar"),
2429 .description = N_("Bar with buttons to launch application"),
2430
2431 .new_instance = launchbar_constructor,
2432 .config = launchtaskbar_configure,
2433 .reconfigure = launchtaskbar_panel_configuration_changed,
2434 .control = launchtaskbar_control
2435 };
2436
2437 static LXPanelPluginInit _taskbar_init = {
2438 .name = N_("Task Bar (Window List)"),
2439 .description = N_("Taskbar shows all opened windows and allow to iconify them, shade or get focus"),
2440
2441 .expand_available = TRUE,
2442 .expand_default = TRUE,
2443
2444 .new_instance = taskbar_constructor,
2445 .config = launchtaskbar_configure,
2446 .reconfigure = launchtaskbar_panel_configuration_changed,
2447 .control = launchtaskbar_control
2448 };
2449
2450 static void launchtaskbar_init(void)
2451 {
2452 lxpanel_register_plugin_type("launchbar", &_launchbar_init);
2453 lxpanel_register_plugin_type("taskbar", &_taskbar_init);
2454 }
2455
2456 /* Plugin descriptor. */
2457 LXPanelPluginInit lxpanel_static_plugin_launchtaskbar = {
2458 .name = N_("Application Launch and Task Bar"),
2459 .description = N_("Bar with buttons to launch application and/or show all opened windows"),
2460
2461 .expand_available = TRUE,
2462 .expand_default = TRUE,
2463
2464 .init = launchtaskbar_init,
2465 .new_instance = launchtaskbar_constructor,
2466 .config = launchtaskbar_configure,
2467 /* .update_context_menu = launchtaskbar_update_context_menu, */
2468 .reconfigure = launchtaskbar_panel_configuration_changed,
2469 .control = launchtaskbar_control
2470 };
2471
2472
2473 /* vim: set sw=4 sts=4 et : */