Merge branch 'use-libfm'
[lxde/lxpanel.git] / src / plugins / launchtaskbar.c
1 /**
2 * Copyright (c) 2006 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 /*
20 * Started by Giuseppe Penone <giuspen@gmail.com> merging launchbar and taskbar
21 * and adding interoperability between them.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 #include <signal.h>
35 #include <errno.h>
36 #include <X11/Xlib.h>
37 #include <X11/Xutil.h>
38
39 #include <gdk-pixbuf/gdk-pixbuf.h>
40 #include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
41 #include <gdk/gdk.h>
42 #include <glib/gi18n.h>
43
44 #include <menu-cache.h>
45
46 #include "panel.h"
47 #include "misc.h"
48 #include "plugin.h"
49 #include "icon.xpm"
50 #include "gtkbar.h"
51 #include "icon-grid.h"
52 #include "menu-policy.h"
53
54 #include "dbg.h"
55
56
57 struct _taskbar;
58 struct _task_class;
59 struct _task;
60
61 /* Drag and drop target info. */
62 enum {
63 TARGET_URILIST,
64 TARGET_UTF8_STRING,
65 TARGET_STRING,
66 TARGET_TEXT,
67 TARGET_COMPOUND_TEXT
68 };
69
70 static const GtkTargetEntry target_table[] = {
71 { "text/uri-list", 0, TARGET_URILIST},
72 { "UTF8_STRING", 0, TARGET_UTF8_STRING },
73 { "COMPOUND_TEXT", 0, 0 },
74 { "TEXT", 0, 0 },
75 { "STRING", 0, 0 }
76 };
77
78 /* Column definitions for configuration dialogs. */
79 enum {
80 COL_ICON,
81 COL_TITLE,
82 COL_ICON_NAME,
83 COL_BTN,
84 N_COLS
85 };
86
87 static const char DESKTOP_ENTRY[] = "Desktop Entry";
88
89 /* Structure representing a class. This comes from WM_CLASS, and should identify windows that come from an application. */
90 typedef struct _task_class {
91 struct _task_class *p_taskclass_flink; /* Forward link */
92 char * res_class; /* Class name */
93 struct _task * p_task_head; /* Head of list of tasks with this class */
94 struct _task * p_task_visible; /* Task that is visible in current desktop, if any */
95 char * visible_name; /* Name that will be visible for grouped tasks */
96 int visible_count; /* Count of tasks that are visible in current desktop */
97 } TaskClass;
98
99 /* Structure representing a "task", an open window. */
100 typedef struct _task {
101 struct _task * p_task_flink_xwid; /* Forward link to next task in X window ID order */
102 struct _taskbar * tb; /* Back pointer to taskbar */
103 Window win; /* X window ID */
104 char * name; /* Taskbar label when normal, from WM_NAME or NET_WM_NAME */
105 char * name_iconified; /* Taskbar label when iconified */
106 char exec_bin[128]; /* Exec bin associated to Window */
107 Atom name_source; /* Atom that is the source of taskbar label */
108 TaskClass * p_taskclass; /* Class, from WM_CLASS */
109 struct _task * p_task_flink_same_class; /* Forward link to task in same class */
110 GtkWidget * button; /* Button representing task in taskbar */
111 GtkWidget * image; /* Icon for task, child of button */
112 Atom image_source; /* Atom that is the source of taskbar icon */
113 GtkWidget * label; /* Label for task, child of button */
114 int desktop; /* Desktop that contains task, needed to switch to it on Raise */
115 gint monitor; /* Monitor that the window is on or closest to */
116 guint flash_timeout; /* Timer for urgency notification */
117 unsigned int focused :1; /* True if window has focus */
118 unsigned int iconified :1; /* True if window is iconified, from WM_STATE */
119 unsigned int urgency :1; /* True if window has an urgency hint, from WM_HINTS */
120 unsigned int flash_state :1; /* One-bit counter to flash taskbar */
121 unsigned int entered_state :1; /* True if cursor is inside taskbar button */
122 unsigned int present_in_client_list :1; /* State during WM_CLIENT_LIST processing to detect deletions */
123 } Task;
124
125 /* Private context for taskbar plugin. */
126 typedef struct _taskbar {
127 Plugin * plug; /* Back pointer to Plugin */
128 Task * p_task_list; /* List of tasks to be displayed in taskbar */
129 TaskClass * p_taskclass_list; /* Window class list */
130 IconGrid * icon_grid; /* Manager for taskbar buttons */
131 GtkWidget * menu; /* Popup menu for task control (Close, Raise, etc.) */
132 GtkWidget * group_menu; /* Popup menu for grouping selection */
133 GdkPixbuf * fallback_pixbuf; /* Fallback task icon when none is available */
134 int number_of_desktops; /* Number of desktops, from NET_WM_NUMBER_OF_DESKTOPS */
135 int current_desktop; /* Current desktop, from NET_WM_CURRENT_DESKTOP */
136 Task * focused; /* Task that has focus */
137 Task * focused_previous; /* Task that had focus just before panel got it */
138 Task * menutask; /* Task for which popup menu is open */
139 guint dnd_delay_timer; /* Timer for drag and drop delay */
140 int icon_size; /* Size of task icons */
141 gboolean show_all_desks; /* User preference: show windows from all desktops */
142 gboolean tooltips; /* User preference: show tooltips */
143 gboolean icons_only; /* User preference: show icons only, omit name */
144 gboolean use_mouse_wheel; /* User preference: scroll wheel does iconify and raise */
145 gboolean use_urgency_hint; /* User preference: windows with urgency will flash */
146 gboolean flat_button; /* User preference: taskbar buttons have visible background */
147 gboolean grouped_tasks; /* User preference: windows from same task are grouped onto a single button */
148 gboolean same_monitor_only; /* User preference: only show windows that are in the same monitor as the taskbar */
149 int task_width_max; /* Maximum width of a taskbar button in horizontal orientation */
150 int spacing; /* Spacing between taskbar buttons */
151 gboolean use_net_active; /* NET_WM_ACTIVE_WINDOW is supported by the window manager */
152 gboolean net_active_checked; /* True if use_net_active is valid */
153 GtkWidget *p_menuitem_lock_tbp;
154 GtkWidget *p_menuitem_unlock_tbp;
155 GtkWidget *p_menuitem_new_instance;
156 } TaskbarPlugin;
157
158 static gchar *launchtaskbar_rc = "style 'launchtaskbar-style'\n"
159 "{\n"
160 "GtkWidget::focus-line-width=0\n"
161 "GtkWidget::focus-padding=0\n"
162 "GtkButton::default-border={0,0,0,0}\n"
163 "GtkWidget::focus-padding=0\n"
164 "GtkButton::default-outside-border={0,0,0,0}\n"
165 "GtkButton::inner-border={0,0,0,0}\n"
166 "}\n"
167 "widget '*launchtaskbar*' style 'launchtaskbar-style'";
168
169 #define DRAG_ACTIVE_DELAY 1000
170 #define TASK_WIDTH_MAX 200
171 #define ALL_WORKSPACES 0xFFFFFFFF /* 64-bit clean */
172 #define ICON_ONLY_EXTRA 6 /* Amount needed to have button lay out symmetrically */
173 #define ICON_BUTTON_TRIM 4 /* Amount needed to have button remain on panel */
174
175 /* Representative of one launch button.
176 * Note that the launch parameters come from the specified desktop file, or from the configuration file.
177 * This structure is also used during the "add to launchtaskbar" dialog to hold menu items. */
178 typedef struct {
179 Plugin * plugin; /* Back pointer to plugin */
180 GtkWidget * widget; /* Pointer to button */
181 GtkWidget * image_widget; /* Pointer to image */
182 gchar * desktop_id; /* Name of application (desktop file name less the .desktop) */
183 gchar * image; /* Image icon (from Icon entry) */
184 gchar * action; /* Action (from Exec entry) */
185 gchar * exec_bin; /* Exec bin associated to desktop file */
186 gchar * tooltip; /* Tooltip (from Name entry) */
187 gchar * path; /* Working directory requested in .desktop file */
188 guchar use_terminal : 1; /* True if Terminal=true or from configuration file */
189 guchar customize_image : 1; /* True if image icon from configuration file */
190 guchar customize_action : 1; /* True if action from configuration file */
191 guchar customize_tooltip : 1; /* True if tooltip from configuration file */
192 guchar customize_path : 1; /* True if path from configuration file */
193 } LaunchButton;
194
195 /* Private context for launchtaskbar plugin. */
196 typedef struct {
197 Plugin *plug; /* Back pointer to Plugin */
198 IconGrid *icon_grid; /* Icon grid managing the container */
199 GSList *buttons; /* Launchbar buttons */
200 LaunchButton *bootstrap_button; /* Bootstrapping button for empty launchtaskbar */
201 GtkWidget *p_button_add, *p_button_remove, *p_label_menu_app_exec, *p_label_def_app_exec;
202 } LaunchbarPlugin;
203
204 typedef struct
205 {
206 LaunchbarPlugin lbp;
207 TaskbarPlugin tbp;
208 GtkWidget *p_evbox_launchbar;
209 GtkWidget *p_evbox_taskbar;
210 GtkWidget *config_dlg; /* Configuration dialog */
211 GtkWidget *p_notebook_page_launch;
212 GtkWidget *p_notebook_page_task;
213 gchar *exec_bin_mb;
214 gboolean add_mb_to_lb;
215 gboolean execute_mb;
216 gboolean found_mb;
217 GKeyFile *p_key_file_special_cases;
218 gboolean lb_on;
219 gboolean lb_built;
220 gboolean tb_on;
221 gboolean tb_built;
222
223 } LaunchTaskBarPlugin;
224
225 void panel_config_save(Panel * panel); /* defined in configurator.c */
226
227 static LaunchButton *launchbar_add_button(LaunchTaskBarPlugin *ltbp, gchar *desktop_id);
228 static void launchbar_remove_button(LaunchTaskBarPlugin *ltbp, LaunchButton *btn);
229 static void launchbutton_free(LaunchButton * btn);
230 static gboolean launchbutton_press_event(GtkWidget * widget, GdkEventButton * event, LaunchButton * b);
231 static void launchbutton_drag_data_received_event(
232 GtkWidget * widget,
233 GdkDragContext * context,
234 gint x,
235 gint y,
236 GtkSelectionData * sd,
237 guint info,
238 guint time,
239 LaunchButton * b);
240 static void launchbutton_build_bootstrap(Plugin * p);
241 static gboolean launchbutton_build_gui(Plugin * p, LaunchButton * btn);
242 static int launchbutton_constructor(Plugin * p, char ** fp);
243 static int launchtaskbar_constructor(Plugin * p, char ** fp);
244 static void launchtaskbar_destructor(Plugin * p);
245 static void launchbar_configure_add_button(GtkButton * widget, Plugin * p);
246 static void launchbar_configure_remove_button(GtkButton * widget, Plugin * p);
247 static void launchbar_configure_move_up_button(GtkButton * widget, Plugin * p);
248 static void launchbar_configure_move_down_button(GtkButton * widget, Plugin * p);
249 static void launchbar_configure_response(GtkDialog * dlg, int response, Plugin * p);
250 static void launchbar_configure_initialize_list(Plugin * p, GtkWidget * dlg, GtkTreeView * view, gboolean from_menu);
251 static void launchtaskbar_configure(Plugin * p, GtkWindow * parent);
252 static void launchtaskbar_save_configuration(Plugin * p, FILE * fp);
253 static void launchtaskbar_panel_configuration_changed(Plugin * p);
254 static void launchbar_update_after_taskbar_class_added(LaunchTaskBarPlugin * ltbp, Task *tk);
255 static void launchbar_update_after_taskbar_class_removed(LaunchTaskBarPlugin * ltbp, Task *tk);
256
257 static void set_timer_on_task(Task * tk);
258 static gboolean task_is_visible_on_current_desktop(TaskbarPlugin * tb, Task * tk);
259 static void recompute_group_visibility_for_class(TaskbarPlugin * tb, TaskClass * tc);
260 static void recompute_group_visibility_on_current_desktop(TaskbarPlugin * tb);
261 static void task_draw_label(Task * tk);
262 static gboolean task_is_visible(TaskbarPlugin * tb, Task * tk);
263 static void task_button_redraw(Task * tk, TaskbarPlugin * tb);
264 static void taskbar_redraw(TaskbarPlugin * tb);
265 static gboolean accept_net_wm_state(NetWMState * nws);
266 static gboolean accept_net_wm_window_type(NetWMWindowType * nwwt);
267 static void task_free_names(Task * tk);
268 static void task_set_names(Task * tk, Atom source);
269 static void task_unlink_class(Task * tk);
270 static TaskClass * taskbar_enter_res_class(TaskbarPlugin * tb, char * res_class, gboolean * name_consumed);
271 static void task_set_class(Task * tk);
272 static Task * task_lookup(TaskbarPlugin * tb, Window win);
273 static void task_delete(TaskbarPlugin * tb, Task * tk, gboolean unlink);
274 static GdkPixbuf * _wnck_gdk_pixbuf_get_from_pixmap(Pixmap xpixmap, int width, int height);
275 static GdkPixbuf * apply_mask(GdkPixbuf * pixbuf, GdkPixbuf * mask);
276 static GdkPixbuf * get_wm_icon(Window task_win, int required_width, int required_height, Atom source, Atom * current_source);
277 static GdkPixbuf * task_update_icon(TaskbarPlugin * tb, Task * tk, Atom source);
278 static gboolean flash_window_timeout(Task * tk);
279 static void task_set_urgency(Task * tk);
280 static void task_clear_urgency(Task * tk);
281 static void task_raise_window(Task * tk, guint32 time);
282 static void taskbar_popup_set_position(GtkWidget * menu, gint * px, gint * py, gboolean * push_in, gpointer data);
283 static void task_group_menu_destroy(TaskbarPlugin * tb);
284 static gboolean taskbar_task_control_event(GtkWidget * widget, GdkEventButton * event, Task * tk, gboolean popup_menu);
285 static gboolean taskbar_button_press_event(GtkWidget * widget, GdkEventButton * event, Task * tk);
286 static gboolean taskbar_popup_activate_event(GtkWidget * widget, GdkEventButton * event, Task * tk);
287 static gboolean taskbar_button_drag_motion_timeout(Task * tk);
288 static gboolean taskbar_button_drag_motion(GtkWidget * widget, GdkDragContext * drag_context, gint x, gint y, guint time, Task * tk);
289 static void taskbar_button_drag_leave(GtkWidget * widget, GdkDragContext * drag_context, guint time, Task * tk);
290 static void taskbar_button_enter(GtkWidget * widget, Task * tk);
291 static void taskbar_button_leave(GtkWidget * widget, Task * tk);
292 static gboolean taskbar_button_scroll_event(GtkWidget * widget, GdkEventScroll * event, Task * tk);
293 static void taskbar_button_size_allocate(GtkWidget * btn, GtkAllocation * alloc, Task * tk);
294 static void taskbar_update_style(TaskbarPlugin * tb);
295 static void task_update_style(Task * tk, TaskbarPlugin * tb);
296 static void task_build_gui(TaskbarPlugin * tb, Task * tk);
297 static void taskbar_close_all_windows(GtkWidget * widget, Task * tk);
298 static void taskbar_net_client_list(GtkWidget * widget, TaskbarPlugin * tb);
299 static void taskbar_net_current_desktop(GtkWidget * widget, TaskbarPlugin * tb);
300 static void taskbar_net_number_of_desktops(GtkWidget * widget, TaskbarPlugin * tb);
301 static void taskbar_net_active_window(GtkWidget * widget, TaskbarPlugin * tb);
302 static gboolean task_has_urgency(Task * tk);
303 static void taskbar_property_notify_event(TaskbarPlugin * tb, XEvent *ev);
304 static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, TaskbarPlugin * tb);
305 static void menu_raise_window(GtkWidget * widget, TaskbarPlugin * tb);
306 static void menu_restore_window(GtkWidget * widget, TaskbarPlugin * tb);
307 static void menu_maximize_window(GtkWidget * widget, TaskbarPlugin * tb);
308 static void menu_iconify_window(GtkWidget * widget, TaskbarPlugin * tb);
309 static void menu_move_to_workspace(GtkWidget * widget, TaskbarPlugin * tb);
310 static void menu_close_window(GtkWidget * widget, TaskbarPlugin * tb);
311 static void on_menuitem_lock_tbp_clicked(GtkWidget * widget, TaskbarPlugin * tb);
312 static void on_menuitem_unlock_tbp_clicked(GtkWidget * widget, TaskbarPlugin * tb);
313 static void on_menuitem_new_instance_clicked(GtkWidget * widget, TaskbarPlugin * tb);
314 static void taskbar_make_menu(TaskbarPlugin * tb);
315 static void taskbar_window_manager_changed(GdkScreen * screen, TaskbarPlugin * tb);
316 static void taskbar_apply_configuration(Plugin * p);
317 static gboolean load_app_key_file(gchar *filepath, GKeyFile *p_gkeyfile);
318
319 static void f_get_exec_cmd_from_pid(GPid pid, gchar *buffer_128, const gchar *proc_file)
320 {
321 buffer_128[0] = '\0';
322 FILE *pipe;
323 gchar command[64];
324 snprintf(command, 64, "cat /proc/%u/%s", pid, proc_file);
325 pipe = popen(command, "r");
326 if(pipe == NULL)
327 ERR("ltbp: popen '%s'\n", command);
328 else if(fgets(buffer_128, 128, pipe) == NULL)
329 ERR("ltbp: fgets '%s'\n", command);
330 else
331 {
332 gchar *p_char = strchr(buffer_128, '\n');
333 if(p_char != NULL) *p_char = '\0';
334 }
335 if(pipe != NULL) pclose(pipe);
336 }
337
338 static gchar *f_get_clean_exec_bin(const gchar *exec_in, gchar *buffer_128)
339 {
340 snprintf(buffer_128, 128, "%s", exec_in);
341
342 gchar *p_char;
343 if( (p_char = strchr(buffer_128, ' ')) != NULL )
344 {
345 *p_char = '\0';
346 }
347 p_char = strrchr(buffer_128, '/');
348 if(p_char == NULL) p_char = buffer_128;
349 else p_char++;
350
351 return p_char;
352 }
353
354 static void f_find_menu_launchbutton_recursive(MenuCacheDir *menu_dir, LaunchTaskBarPlugin *ltbp)
355 {
356 /* Iterate over all menu items in this directory. */
357 GSList * l;
358 for(l = menu_cache_dir_get_children(menu_dir); l != NULL; l = l->next)
359 {
360 /* Get the next menu item. */
361 MenuCacheItem * item = MENU_CACHE_ITEM(l->data);
362 switch(menu_cache_item_get_type(item))
363 {
364 case MENU_CACHE_TYPE_NONE:
365 case MENU_CACHE_TYPE_SEP:
366 break;
367
368 case MENU_CACHE_TYPE_APP:
369 {
370 /* If an application, build a LaunchButton data structure so we can identify
371 * the button in the handler. In this application, the desktop_id is the
372 * fully qualified desktop file path. The image and tooltip are what is displayed in the view. */
373 gchar *desktop_id = menu_cache_item_get_file_path(item);
374
375 GKeyFile *p_key_desktop = g_key_file_new();
376 gboolean loaded = load_app_key_file(desktop_id, p_key_desktop);
377 gchar *exec = NULL;
378 gboolean in_terminal = TRUE;
379 if(loaded)
380 {
381 exec = g_key_file_get_string(p_key_desktop, DESKTOP_ENTRY, "Exec", NULL);
382 in_terminal = g_key_file_get_boolean(p_key_desktop, DESKTOP_ENTRY, "Terminal", NULL);
383 g_key_file_free(p_key_desktop);
384 }
385 gchar buffer_128[128];
386 gchar *p_char = f_get_clean_exec_bin(exec, buffer_128);
387 if(strcmp(p_char, ltbp->exec_bin_mb) == 0)
388 {
389 if(ltbp->add_mb_to_lb) launchbar_add_button(ltbp, desktop_id);
390 if(ltbp->execute_mb) lxpanel_launch_app(exec, NULL, in_terminal, menu_cache_app_get_working_dir(MENU_CACHE_APP(item)));
391 //g_print("FOUND '%s' in MB\n", p_char);
392 ltbp->found_mb = TRUE;
393 }
394 //else
395 //{
396 //g_print("---'%s' != '%s' in MB\n", p_char, ltbp->exec_bin_mb);
397 //}
398 g_free(exec);
399 g_free(desktop_id);
400 break;
401 }
402 case MENU_CACHE_TYPE_DIR:
403 {
404 f_find_menu_launchbutton_recursive(MENU_CACHE_DIR(item), ltbp);
405 break;
406 }
407 }
408 if(ltbp->found_mb) break;
409 }
410 }
411
412 /* Deallocate a LaunchButton. */
413 static void launchbutton_free(LaunchButton * btn)
414 {
415 g_free(btn->desktop_id);
416 g_free(btn->image);
417 g_free(btn->action);
418 g_free(btn->exec_bin);
419 g_free(btn->tooltip);
420 g_free(btn->path);
421 g_free(btn);
422 }
423
424 /* Handler for "button-press-event" event from launchtaskbar button. */
425 static gboolean launchbutton_press_event(GtkWidget * widget, GdkEventButton * event, LaunchButton * b)
426 {
427 /* Standard right-click handling. */
428 if (plugin_button_press_event(widget, event, b->plugin))
429 return TRUE;
430
431 if (event->button == 1) /* left button */
432 {
433 if (b->desktop_id == NULL) /* The bootstrap button */
434 launchtaskbar_configure(b->plugin, NULL);
435 else if (b->action != NULL)
436 lxpanel_launch_app(b->action, NULL, b->use_terminal, b->path);
437 }
438 return TRUE;
439 }
440
441 /* Handler for "drag-data-received" event from launchtaskbar button. */
442 static void launchbutton_drag_data_received_event(
443 GtkWidget * widget,
444 GdkDragContext * context,
445 gint x,
446 gint y,
447 GtkSelectionData * sd,
448 guint info,
449 guint time,
450 LaunchButton * b)
451 {
452 if (!b->action)
453 {
454 LOG(LOG_WARN, "launchtaskbar: Button '%s' has no action (%s)\n",
455 b->desktop_id, b->action);
456 return;
457 }
458 #if GTK_CHECK_VERSION(2,14,0)
459 if (gtk_selection_data_get_length(sd) > 0)
460 #else
461 if (sd->lengh > 0)
462 #endif
463 {
464 if (info == TARGET_URILIST)
465 {
466 #if GTK_CHECK_VERSION(2,14,0)
467 gchar * s = (gchar *) gtk_selection_data_get_data(sd);
468 #else
469 gchar * s = (gchar *) sd->data;
470 #endif
471 #if GTK_CHECK_VERSION(2,14,0)
472 gchar * end = s + gtk_selection_data_get_length(sd);
473 #else
474 gchar * end = s + sd->lenght;
475 #endif
476 gchar * str = g_strdup(b->action);
477 while (s < end)
478 {
479 while (s < end && g_ascii_isspace(*s))
480 s++;
481 gchar * e = s;
482 while (e < end && !g_ascii_isspace(*e))
483 e++;
484 if (s != e)
485 {
486 *e = 0;
487 s = g_filename_from_uri(s, NULL, NULL);
488 if (s)
489 {
490 gchar * tmp = g_strconcat(str, " '", s, "'", NULL);
491 g_free(str);
492 g_free(s);
493 str = tmp;
494 }
495 }
496 s = e+1;
497 }
498
499 spawn_command_async(NULL, NULL, str);
500 g_free(str);
501 }
502 }
503 }
504
505 /* Build the graphic elements for the bootstrap launchtaskbar button. */
506 static void launchbutton_build_bootstrap(Plugin * p)
507 {
508 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p->priv;
509
510 if(ltbp->lbp.bootstrap_button == NULL)
511 {
512 /* Build a button that has the stock "Add" icon.
513 * The "desktop-id" being NULL is the marker that this is the bootstrap button. */
514 ltbp->lbp.bootstrap_button = g_new0(LaunchButton, 1);
515 ltbp->lbp.bootstrap_button->plugin = p;
516
517 /* Create an event box. */
518 GtkWidget * event_box = gtk_event_box_new();
519 gtk_container_set_border_width(GTK_CONTAINER(event_box), 0);
520 #if GLIB_CHECK_VERSION(2,18,0)
521 gtk_widget_set_can_focus (event_box, FALSE);
522 #else
523 GTK_WIDGET_UNSET_FLAGS(event_box, GTK_CAN_FOCUS);
524 #endif
525 ltbp->lbp.bootstrap_button->widget = event_box;
526 g_signal_connect(event_box, "button-press-event", G_CALLBACK(launchbutton_press_event), ltbp->lbp.bootstrap_button);
527
528 /* Create an image containing the stock "Add" icon as a child of the event box. */
529 ltbp->lbp.bootstrap_button->image_widget = gtk_image_new_from_pixbuf(
530 lxpanel_load_icon(GTK_STOCK_ADD, p->panel->icon_size, p->panel->icon_size, FALSE));
531 gtk_misc_set_padding(GTK_MISC(ltbp->lbp.bootstrap_button->image_widget), 0, 0);
532 gtk_misc_set_alignment(GTK_MISC(ltbp->lbp.bootstrap_button->image_widget), 0, 0);
533 gtk_container_add(GTK_CONTAINER(event_box), ltbp->lbp.bootstrap_button->image_widget);
534
535 /* Add the bootstrap button to the icon grid. By policy it is empty at this point. */
536 icon_grid_add(ltbp->lbp.icon_grid, event_box, TRUE);
537 }
538 else
539 icon_grid_set_visible(ltbp->lbp.icon_grid, ltbp->lbp.bootstrap_button->widget, TRUE);
540 }
541
542 static LaunchButton *launchbar_exec_bin_exists(LaunchbarPlugin *lb, gchar *exec_bin)
543 {
544 if(!exec_bin) return NULL;
545
546 LaunchButton *ret_val = NULL;
547 GSList* l;
548 for(l = lb->buttons; l != NULL; l = l->next)
549 {
550 LaunchButton *btn = (LaunchButton *)l->data;
551 if(strcmp(btn->exec_bin, exec_bin) == 0)
552 {
553 ret_val = btn;
554 break;
555 }
556 }
557 return ret_val;
558 }
559
560 static void launchbar_update_after_taskbar_class_added(LaunchTaskBarPlugin *ltbp, Task *tk)
561 {
562 GPid pid = get_net_wm_pid(tk->win);
563 gchar exec_bin_full[128];
564 f_get_exec_cmd_from_pid(pid, exec_bin_full, "cmdline");
565 gchar *p_char = strrchr(exec_bin_full, '/');
566 if(p_char == NULL) p_char = exec_bin_full;
567 else p_char++;
568 if(strcmp(p_char, "python") == 0)
569 {
570 f_get_exec_cmd_from_pid(pid, exec_bin_full, "comm");
571 p_char = strrchr(exec_bin_full, '/');
572 if(p_char == NULL) p_char = exec_bin_full;
573 else p_char++;
574 }
575 snprintf(tk->exec_bin, 128, "%s", p_char);
576
577 if(ltbp->p_key_file_special_cases != NULL)
578 {
579 gchar *converted_tb_exec_bin = g_key_file_get_string(ltbp->p_key_file_special_cases, "special_cases", tk->exec_bin, NULL);
580 if(converted_tb_exec_bin != NULL)
581 {
582 snprintf(tk->exec_bin, 128, "%s", converted_tb_exec_bin);
583 g_free(converted_tb_exec_bin);
584 }
585 }
586
587 if(ltbp->lb_on)
588 {
589 LaunchButton *btn = launchbar_exec_bin_exists(&ltbp->lbp, tk->exec_bin);
590 g_print("\nTB '%s' OPEN (pid=%u), in LB: %c\n",
591 tk->exec_bin, pid, btn != NULL ? 'Y':'N');
592 }
593 }
594
595 static void launchbar_update_after_taskbar_class_removed(LaunchTaskBarPlugin *ltbp, Task *tk)
596 {
597 if(ltbp->lb_on)
598 {
599 LaunchButton *btn = launchbar_exec_bin_exists(&ltbp->lbp, tk->exec_bin);
600 g_print("\nTB '%s' CLOSE, in LB: %c\n", tk->exec_bin, btn != NULL ? 'Y':'N');
601 }
602 }
603
604 static gboolean load_app_key_file(gchar *filepath, GKeyFile *p_gkeyfile)
605 {
606 gboolean loaded;
607 if(g_path_is_absolute(filepath))
608 {
609 loaded = g_key_file_load_from_file(p_gkeyfile, filepath, G_KEY_FILE_NONE, NULL);
610 }
611 else
612 {
613 /* Load from the freedesktop.org specified data directories. */
614 gchar * full_id = g_strconcat("applications/", filepath, NULL);
615 loaded = g_key_file_load_from_data_dirs(
616 p_gkeyfile, full_id, &filepath, G_KEY_FILE_NONE, NULL);
617 g_free(full_id);
618 }
619 return loaded;
620 }
621
622 /* Build the graphic elements for a launchtaskbar button. The desktop_id field is already established. */
623 static gboolean launchbutton_build_gui(Plugin * p, LaunchButton * btn)
624 {
625 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p->priv;
626
627 if(btn->desktop_id != NULL)
628 {
629 /* There is a valid desktop file name. Try to open it. */
630 GKeyFile *p_key_desktop = g_key_file_new();
631 gboolean loaded = load_app_key_file(btn->desktop_id, p_key_desktop);
632 if(loaded)
633 {
634 /* Desktop file located. Get Icon, Name, Exec, and Terminal parameters. */
635 gchar * icon = g_key_file_get_string(p_key_desktop, DESKTOP_ENTRY, "Icon", NULL);
636 gchar * title = g_key_file_get_locale_string(p_key_desktop, DESKTOP_ENTRY, "Name", NULL, NULL);
637 if ((btn->image == NULL) && (icon != NULL))
638 btn->image = icon;
639
640 gchar * exec = g_key_file_get_string(p_key_desktop, DESKTOP_ENTRY, "Exec", NULL);
641 if( ! btn->customize_action )
642 {
643 gchar * exec = g_key_file_get_string(p_key_desktop, DESKTOP_ENTRY, "Exec", NULL);
644 btn->action = translate_exec_to_cmd(exec, icon, title, btn->desktop_id);
645 g_free(exec);
646 }
647
648 gchar buffer_128[128];
649 gchar *p_char = f_get_clean_exec_bin(exec, buffer_128);
650 btn->exec_bin = strdup(p_char);
651 g_free(exec);
652 //g_print("\nLB '%s' FOUND\n", btn->exec_bin);
653
654 btn->use_terminal = g_key_file_get_boolean(p_key_desktop, DESKTOP_ENTRY, "Terminal", NULL);
655
656 if ( ! btn->customize_tooltip)
657 btn->tooltip = title;
658 if (btn->image != icon)
659 g_free(icon);
660 if (btn->tooltip != title)
661 g_free(title);
662 }
663 g_key_file_free(p_key_desktop);
664 if(!loaded) return FALSE;
665 }
666
667 /* Create a button with the specified icon. */
668 GtkWidget * button = fb_button_new_from_file(btn->image, p->panel->icon_size, p->panel->icon_size, PANEL_ICON_HIGHLIGHT, TRUE);
669 btn->widget = button;
670 #if GLIB_CHECK_VERSION(2,18,0)
671 gtk_widget_set_can_focus(button, FALSE);
672 #else
673 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
674 #endif
675
676 if (btn->tooltip != NULL)
677 gtk_widget_set_tooltip_text(button, btn->tooltip);
678
679 /* Add the button to the icon grid. */
680 icon_grid_add(ltbp->lbp.icon_grid, button, TRUE);
681
682 /* Drag and drop support. */
683 gtk_drag_dest_set(GTK_WIDGET(button),
684 GTK_DEST_DEFAULT_ALL,
685 target_table, G_N_ELEMENTS(target_table),
686 GDK_ACTION_COPY);
687
688 /* Connect signals. */
689 g_signal_connect(button, "button-press-event", G_CALLBACK(launchbutton_press_event), (gpointer) btn);
690 g_signal_connect(button, "drag_data_received", G_CALLBACK(launchbutton_drag_data_received_event), (gpointer) btn);
691
692 /* If the list goes from null to non-null, remove the bootstrap button. */
693 if ((ltbp->lbp.buttons == NULL) && (ltbp->lbp.bootstrap_button != NULL))
694 icon_grid_set_visible(ltbp->lbp.icon_grid, ltbp->lbp.bootstrap_button->widget, FALSE);
695
696 /* Append at end of list to preserve configured order. */
697 ltbp->lbp.buttons = g_slist_append(ltbp->lbp.buttons, btn);
698
699 /* Show the widget and return. */
700 gtk_widget_show(button);
701 plugin_widget_set_background(button, p->panel);
702
703 return TRUE;
704 }
705
706 /* Read the configuration file entry for a launchtaskbar button and create it. */
707 static int launchbutton_constructor(Plugin * p, char ** fp)
708 {
709 /* Allocate the LaunchButton structure. */
710 LaunchButton * btn = g_new0(LaunchButton, 1);
711 btn->plugin = p;
712
713 /* Read parameters from the configuration file. */
714 line s;
715 s.len = 256;
716 if(fp != NULL)
717 {
718 while(lxpanel_get_line(fp, &s) != LINE_BLOCK_END)
719 {
720 if(s.type == LINE_NONE)
721 {
722 ERR("ltbp: illegal token %s\n", s.str);
723 launchbutton_free(btn);
724 return 0;
725 }
726 if(s.type == LINE_VAR)
727 {
728 if(g_ascii_strcasecmp(s.t[0], "id") == 0)
729 {
730 btn->desktop_id = g_strdup(s.t[1]);
731 //g_print("%s\n", btn->desktop_id);
732 }
733 else if(g_ascii_strcasecmp(s.t[0], "image") == 0)
734 {
735 btn->customize_image = TRUE;
736 btn->image = expand_tilda(g_strdup(s.t[1]));
737 }
738 else if(g_ascii_strcasecmp(s.t[0], "tooltip") == 0)
739 {
740 btn->customize_tooltip = TRUE;
741 btn->tooltip = g_strdup(s.t[1]);
742 }
743 else if(g_ascii_strcasecmp(s.t[0], "path") == 0)
744 {
745 btn->customize_path = TRUE;
746 btn->path = g_strdup(s.t[1]);
747 }
748 else if(g_ascii_strcasecmp(s.t[0], "action") == 0)
749 {
750 btn->customize_action = TRUE;
751 btn->action = g_strdup(s.t[1]);
752 }
753 else if(g_ascii_strcasecmp(s.t[0], "terminal") == 0)
754 {
755 btn->use_terminal = str2num(bool_pair, s.t[1], 0);
756 }
757 else
758 ERR("ltbp: unknown var %s\n", s.t[0]);
759 }
760 else
761 {
762 ERR("ltbp: illegal in this context %s\n", s.str);
763 launchbutton_free(btn);
764 return 0;
765 }
766 }
767 }
768
769 /* Build the structures and return. */
770 if(!launchbutton_build_gui(p, btn))
771 {
772 launchbutton_free(btn);
773 }
774 return 1;
775 }
776
777 static void launchtaskbar_constructor_add_default_special_case(LaunchTaskBarPlugin *ltbp, const gchar *tk_exec, const gchar *mb_exec)
778 {
779 g_key_file_set_value(ltbp->p_key_file_special_cases, "special_cases", tk_exec, mb_exec);
780 }
781
782 static void launchtaskbar_constructor_launch(LaunchTaskBarPlugin *ltbp, gboolean build_bootstrap)
783 {
784 if(!ltbp->lb_built)
785 {
786 ltbp->lb_built = TRUE;
787
788 Plugin * p = ltbp->lbp.plug;
789
790 if(ltbp->lbp.icon_grid == NULL)
791 {
792 /* Allocate an icon grid manager to manage the container. */
793 GtkOrientation bo = (p->panel->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
794 ltbp->lbp.icon_grid = icon_grid_new(p->panel, ltbp->p_evbox_launchbar, bo, p->panel->icon_size, p->panel->icon_size, 3, 0, p->panel->height);
795 }
796
797 if(build_bootstrap)
798 {
799 if(ltbp->lbp.buttons == NULL)
800 launchbutton_build_bootstrap(p);
801 }
802 }
803 else
804 {
805 gtk_widget_set_visible(ltbp->p_evbox_launchbar, TRUE);
806 }
807 }
808
809 static void launchtaskbar_constructor_task(LaunchTaskBarPlugin *ltbp)
810 {
811 if(!ltbp->tb_built)
812 {
813 ltbp->tb_built = TRUE;
814
815 Plugin * p = ltbp->tbp.plug;
816
817 /* Make container for task buttons as a child of top level widget. */
818 GtkOrientation bo = (p->panel->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
819 ltbp->tbp.icon_grid = icon_grid_new(p->panel, ltbp->p_evbox_taskbar, bo, ltbp->tbp.task_width_max, ltbp->tbp.icon_size, ltbp->tbp.spacing, 0, p->panel->height);
820 icon_grid_set_constrain_width(ltbp->tbp.icon_grid, TRUE);
821 taskbar_update_style(&ltbp->tbp);
822
823 /* Add GDK event filter. */
824 gdk_window_add_filter(NULL, (GdkFilterFunc) taskbar_event_filter, &ltbp->tbp);
825
826 /* Connect signal to receive mouse events on the unused portion of the taskbar. */
827 g_signal_connect(ltbp->p_evbox_taskbar, "button-press-event", G_CALLBACK(plugin_button_press_event), p);
828
829 /* Connect signals to receive root window events and initialize root window properties. */
830 ltbp->tbp.number_of_desktops = get_net_number_of_desktops();
831 ltbp->tbp.current_desktop = get_net_current_desktop();
832 g_signal_connect(G_OBJECT(fbev), "current_desktop", G_CALLBACK(taskbar_net_current_desktop), (gpointer) &ltbp->tbp);
833 g_signal_connect(G_OBJECT(fbev), "active_window", G_CALLBACK(taskbar_net_active_window), (gpointer) &ltbp->tbp);
834 g_signal_connect(G_OBJECT(fbev), "number_of_desktops", G_CALLBACK(taskbar_net_number_of_desktops), (gpointer) &ltbp->tbp);
835 g_signal_connect(G_OBJECT(fbev), "client_list", G_CALLBACK(taskbar_net_client_list), (gpointer) &ltbp->tbp);
836
837 /* Make right-click menu for task buttons.
838 * It is retained for the life of the taskbar and will be shown as needed.
839 * Number of desktops and edge is needed for this operation. */
840 taskbar_make_menu(&ltbp->tbp);
841
842 /* Connect a signal to be notified when the window manager changes. This causes re-evaluation of the "use_net_active" status. */
843 g_signal_connect(gtk_widget_get_screen(ltbp->p_evbox_taskbar), "window-manager-changed", G_CALLBACK(taskbar_window_manager_changed), &ltbp->tbp);
844
845 /* Fetch the client list and redraw the taskbar. Then determine what window has focus. */
846 taskbar_net_client_list(NULL, &ltbp->tbp);
847 taskbar_net_active_window(NULL, &ltbp->tbp);
848 }
849 else
850 {
851 gtk_widget_set_visible(ltbp->p_evbox_taskbar, TRUE);
852 }
853 }
854
855 /* Plugin constructor. */
856 static int launchtaskbar_constructor(Plugin * p, char ** fp)
857 {
858 gtk_rc_parse_string(launchtaskbar_rc);
859
860 /* Allocate plugin context and set into Plugin private data pointer. */
861 LaunchTaskBarPlugin *ltbp = g_new0(LaunchTaskBarPlugin, 1);
862 ltbp->lbp.plug = p;
863 ltbp->tbp.plug = p;
864 ltbp->lbp.icon_grid = NULL;
865 ltbp->tbp.icon_grid = NULL;
866 p->priv = ltbp;
867
868 /* Initialize to defaults. */
869 ltbp->tbp.icon_size = p->panel->icon_size;
870 ltbp->tbp.tooltips = TRUE;
871 ltbp->tbp.icons_only = FALSE;
872 ltbp->tbp.show_all_desks = TRUE;
873 ltbp->tbp.task_width_max = TASK_WIDTH_MAX;
874 ltbp->tbp.spacing = 1;
875 ltbp->tbp.use_mouse_wheel = TRUE;
876 ltbp->tbp.use_urgency_hint = TRUE;
877 ltbp->tbp.grouped_tasks = FALSE;
878
879 /* Special cases key file */
880 ltbp->p_key_file_special_cases = g_key_file_new();
881 gchar *special_cases_filepath = g_build_filename(g_get_user_config_dir(),
882 "lxpanel", "launchtaskbar.cfg", NULL);
883 if(g_file_test(special_cases_filepath, G_FILE_TEST_EXISTS))
884 {
885 gboolean loaded = load_app_key_file(special_cases_filepath,
886 ltbp->p_key_file_special_cases);
887 if(!loaded) ltbp->p_key_file_special_cases = NULL;
888 }
889 else
890 {
891 launchtaskbar_constructor_add_default_special_case(ltbp, "synaptic", "synaptic-pkexec");
892 launchtaskbar_constructor_add_default_special_case(ltbp, "soffice.bin", "libreoffice");
893 launchtaskbar_constructor_add_default_special_case(ltbp, "x-terminal-emulator", "lxterminal");
894 gchar *key_file_data = g_key_file_to_data(ltbp->p_key_file_special_cases, NULL, NULL);
895 g_file_set_contents(special_cases_filepath, key_file_data, -1, NULL);
896 g_free(key_file_data);
897 }
898 g_free(special_cases_filepath);
899
900 /* Allocate top level widget and set into Plugin widget pointer. */
901 p->pwid = p->panel->orientation == ORIENT_HORIZ ? gtk_hbox_new(FALSE, 5):gtk_vbox_new(FALSE, 5);
902 ltbp->p_evbox_launchbar = gtk_event_box_new();
903 ltbp->p_evbox_taskbar = gtk_event_box_new();
904 gtk_box_pack_start(GTK_BOX(p->pwid), ltbp->p_evbox_launchbar, FALSE, TRUE, 0);
905 gtk_box_pack_start(GTK_BOX(p->pwid), ltbp->p_evbox_taskbar, TRUE, TRUE, 0);
906
907 gtk_container_set_border_width(GTK_CONTAINER(p->pwid), 0);
908 gtk_container_set_border_width(GTK_CONTAINER(ltbp->p_evbox_launchbar), 0);
909 gtk_container_set_border_width(GTK_CONTAINER(ltbp->p_evbox_taskbar), 0);
910 ltbp->lb_on = TRUE;
911 ltbp->lb_built = FALSE;
912 ltbp->tb_on = TRUE;
913 ltbp->tb_built = FALSE;
914 #if GLIB_CHECK_VERSION(2,18,0)
915 gtk_widget_set_has_window(p->pwid, FALSE);
916 #else
917 GTK_WIDGET_SET_FLAGS(p->pwid, GTK_NO_WINDOW);
918 #endif
919
920 if(fp != NULL)
921 {
922 line s;
923 s.len = 256;
924 while(lxpanel_get_line(fp, &s) != LINE_BLOCK_END)
925 {
926 if(s.type == LINE_NONE)
927 {
928 ERR("ltbp: illegal token %s\n", s.str);
929 return FALSE;
930 }
931 if(s.type == LINE_VAR)
932 {
933 if(g_ascii_strcasecmp(s.t[0], "LaunchBarON") == 0)
934 {
935 ltbp->lb_on = str2num(bool_pair, s.t[1], 0);
936 }
937 else if(g_ascii_strcasecmp(s.t[0], "TaskBarON") == 0)
938 {
939 ltbp->tb_on = str2num(bool_pair, s.t[1], 0);
940 }
941 else if(g_ascii_strcasecmp(s.t[0], "tooltips") == 0)
942 {
943 ltbp->tbp.tooltips = str2num(bool_pair, s.t[1], 1);
944 }
945 else if(g_ascii_strcasecmp(s.t[0], "IconsOnly") == 0)
946 {
947 ltbp->tbp.icons_only = str2num(bool_pair, s.t[1], 0);
948 }
949 else if(g_ascii_strcasecmp(s.t[0], "ShowAllDesks") == 0)
950 {
951 ltbp->tbp.show_all_desks = str2num(bool_pair, s.t[1], 0);
952 }
953 else if(g_ascii_strcasecmp(s.t[0], "SameMonitorOnly") == 0)
954 {
955 ltbp->tbp.same_monitor_only = str2num(bool_pair, s.t[1], 0);
956 }
957 else if(g_ascii_strcasecmp(s.t[0], "MaxTaskWidth") == 0)
958 {
959 ltbp->tbp.task_width_max = atoi(s.t[1]);
960 }
961 else if(g_ascii_strcasecmp(s.t[0], "spacing") == 0)
962 {
963 ltbp->tbp.spacing = atoi(s.t[1]);
964 }
965 else if(g_ascii_strcasecmp(s.t[0], "UseMouseWheel") == 0)
966 {
967 ltbp->tbp.use_mouse_wheel = str2num(bool_pair, s.t[1], 1);
968 }
969 else if(g_ascii_strcasecmp(s.t[0], "UseUrgencyHint") == 0)
970 {
971 ltbp->tbp.use_urgency_hint = str2num(bool_pair, s.t[1], 1);
972 }
973 else if(g_ascii_strcasecmp(s.t[0], "FlatButton") == 0)
974 {
975 ltbp->tbp.flat_button = str2num(bool_pair, s.t[1], 1);
976 }
977 else if(g_ascii_strcasecmp(s.t[0], "GroupedTasks") == 0)
978 {
979 ltbp->tbp.grouped_tasks = str2num(bool_pair, s.t[1], 1);
980 }
981 }
982 else if(s.type == LINE_BLOCK_START)
983 {
984 if(ltbp->lb_on)
985 {
986 if(g_ascii_strcasecmp(s.t[0], "Button") == 0)
987 {
988 launchtaskbar_constructor_launch(ltbp, FALSE/*build_bootstrap*/);
989 if(!launchbutton_constructor(p, fp))
990 {
991 ERR("ltbp: can't init button\n");
992 return FALSE;
993 }
994 }
995 else
996 {
997 ERR("ltbp: unknown var %s\n", s.t[0]);
998 return FALSE;
999 }
1000 }
1001 }
1002 }
1003 }
1004
1005 if(ltbp->lb_on) launchtaskbar_constructor_launch(ltbp, TRUE/*build_bootstrap*/);
1006 if(ltbp->tb_on) launchtaskbar_constructor_task(ltbp);
1007
1008 return TRUE;
1009 }
1010
1011 static void launchtaskbar_destructor_launch(LaunchTaskBarPlugin *ltbp)
1012 {
1013 /* Free the launchbar. */
1014 g_slist_foreach(ltbp->lbp.buttons, (GFunc) launchbutton_free, NULL);
1015 icon_grid_free(ltbp->lbp.icon_grid);
1016 ltbp->lbp.icon_grid = NULL;
1017
1018 /* Free the bootstrap button if it exists. */
1019 if(ltbp->lbp.bootstrap_button != NULL)
1020 {
1021 g_free(ltbp->lbp.bootstrap_button);
1022 ltbp->lbp.bootstrap_button = NULL;
1023 }
1024 }
1025
1026 static void launchtaskbar_destructor_task(LaunchTaskBarPlugin *ltbp)
1027 {
1028 /* Remove GDK event filter. */
1029 gdk_window_remove_filter(NULL, (GdkFilterFunc) taskbar_event_filter, &ltbp->tbp);
1030
1031 /* Remove root window signal handlers. */
1032 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_current_desktop, &ltbp->tbp);
1033 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_active_window, &ltbp->tbp);
1034 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_number_of_desktops, &ltbp->tbp);
1035 g_signal_handlers_disconnect_by_func(fbev, taskbar_net_client_list, &ltbp->tbp);
1036
1037 /* Remove "window-manager-changed" handler. */
1038 g_signal_handlers_disconnect_by_func(gtk_widget_get_screen(ltbp->p_evbox_taskbar), taskbar_window_manager_changed, &ltbp->tbp);
1039
1040 /* Deallocate task list. */
1041 while(ltbp->tbp.p_task_list != NULL)
1042 task_delete(&ltbp->tbp, ltbp->tbp.p_task_list, TRUE);
1043
1044 /* Deallocate class list. */
1045 while(ltbp->tbp.p_taskclass_list != NULL)
1046 {
1047 TaskClass * tc = ltbp->tbp.p_taskclass_list;
1048 ltbp->tbp.p_taskclass_list = tc->p_taskclass_flink;
1049 g_free(tc->res_class);
1050 g_free(tc);
1051 }
1052
1053 /* Deallocate other memory. */
1054 gtk_widget_destroy(ltbp->tbp.menu);
1055 icon_grid_free(ltbp->tbp.icon_grid);
1056 }
1057
1058 /* Plugin destructor. */
1059 static void launchtaskbar_destructor(Plugin * p)
1060 {
1061 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p->priv;
1062
1063 // TASKBAR
1064 if(ltbp->tb_built) launchtaskbar_destructor_task(ltbp);
1065
1066 // LAUNCHBAR
1067 if(ltbp->lb_built) launchtaskbar_destructor_launch(ltbp);
1068
1069 // LAUNCHTASKBAR
1070 /* Ensure that the configuration dialog is dismissed. */
1071 if (ltbp->config_dlg != NULL)
1072 gtk_widget_destroy(ltbp->config_dlg);
1073
1074 /* Deallocate all memory. */
1075 if(ltbp->p_key_file_special_cases != NULL) g_key_file_free(ltbp->p_key_file_special_cases);
1076 g_free(ltbp);
1077 }
1078
1079 static LaunchButton *launchbar_add_button(LaunchTaskBarPlugin *ltbp, gchar *desktop_id)
1080 {
1081 LaunchButton *defined_button = g_new0(LaunchButton, 1);
1082 defined_button->plugin = ltbp->lbp.plug;
1083 defined_button->desktop_id = g_strdup(desktop_id);
1084 if(!launchbutton_build_gui(ltbp->lbp.plug, defined_button))
1085 {
1086 launchbutton_free(defined_button);
1087 defined_button = NULL;
1088 }
1089 return defined_button;
1090 }
1091
1092 /* Handler for "clicked" action on launchtaskbar configuration dialog "Add" button. */
1093 static void launchbar_configure_add_button(GtkButton * widget, Plugin * p)
1094 {
1095 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p->priv;
1096
1097 GtkTreeView * menu_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "menu_view"));
1098 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
1099 GtkTreeModel * list;
1100 GtkTreeIter it;
1101 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(menu_view), &list, &it))
1102 {
1103 LaunchButton * btn;
1104 gtk_tree_model_get(list, &it, COL_BTN, &btn, -1);
1105 if( btn == NULL )
1106 return;
1107
1108 /* We have located a selected button.
1109 * Add a launch button to the launchtaskbar and refresh the view in the configuration dialog. */
1110 LaunchButton *defined_button = launchbar_add_button(ltbp, btn->desktop_id);
1111 if(defined_button == NULL) return;
1112
1113 GtkListStore * list = GTK_LIST_STORE(gtk_tree_view_get_model(defined_view));
1114 GtkTreeIter it;
1115 GdkPixbuf* pix;
1116 gtk_list_store_append(list, &it);
1117 pix = lxpanel_load_icon(btn->image, PANEL_ICON_SIZE, PANEL_ICON_SIZE, TRUE);
1118 gtk_list_store_set(list, &it,
1119 COL_ICON, pix,
1120 COL_TITLE, ((btn->tooltip != NULL) ? btn->tooltip : btn->action),
1121 COL_BTN, defined_button,
1122 -1);
1123 g_object_unref(pix);
1124 }
1125 }
1126
1127 static void launchbar_remove_button(LaunchTaskBarPlugin *ltbp, LaunchButton *btn)
1128 {
1129 icon_grid_remove(ltbp->lbp.icon_grid, btn->widget);
1130 ltbp->lbp.buttons = g_slist_remove(ltbp->lbp.buttons, btn);
1131 launchbutton_free(btn);
1132 /* Put the bootstrap button back if the list becomes empty. */
1133 if(ltbp->lbp.buttons == NULL)
1134 launchbutton_build_bootstrap(ltbp->lbp.plug);
1135 }
1136
1137 /* Handler for "clicked" action on launchtaskbar configuration dialog "Remove" button. */
1138 static void launchbar_configure_remove_button(GtkButton * widget, Plugin * p)
1139 {
1140 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p->priv;
1141
1142 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
1143 GtkTreeModel * list;
1144 GtkTreeIter it;
1145 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
1146 {
1147 LaunchButton * btn;
1148 gtk_tree_model_get(list, &it, COL_BTN, &btn, -1);
1149
1150 /* We have found a selected button.
1151 * Remove it from the icon grid, the data structure, and the view. */
1152 gtk_list_store_remove(GTK_LIST_STORE(list), &it);
1153 gtk_widget_set_visible(ltbp->lbp.p_label_def_app_exec, FALSE);
1154
1155 launchbar_remove_button(ltbp, btn);
1156 }
1157 }
1158
1159 /* Handler for "clicked" action on launchtaskbar configuration dialog "Move Up" button. */
1160 static void launchbar_configure_move_up_button(GtkButton * widget, Plugin * p)
1161 {
1162 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p->priv;
1163
1164 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
1165 GtkTreeModel * list;
1166 GtkTreeIter it;
1167 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
1168 {
1169 LaunchButton *btn;
1170 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, COL_BTN, &btn, -1);
1171 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &it);
1172 if ((gtk_tree_path_get_indices(path)[0] > 0)
1173 && (gtk_tree_path_prev(path)))
1174 {
1175 GtkTreeIter it2;
1176 if (gtk_tree_model_get_iter(list, &it2, path))
1177 {
1178 /* We have found a selected button that can be moved.
1179 * Reorder it in the icon grid, the data structure, and the view. */
1180 int i = gtk_tree_path_get_indices(path)[0];
1181 ltbp->lbp.buttons = g_slist_remove(ltbp->lbp.buttons, btn);
1182 ltbp->lbp.buttons = g_slist_insert(ltbp->lbp.buttons, btn, i);
1183 gtk_list_store_move_before(GTK_LIST_STORE(list), &it, &it2);
1184 icon_grid_reorder_child(ltbp->lbp.icon_grid, btn->widget, i);
1185 }
1186 }
1187 gtk_tree_path_free(path);
1188 }
1189 }
1190
1191 /* Handler for "clicked" action on launchtaskbar configuration dialog "Move Down" button. */
1192 static void launchbar_configure_move_down_button(GtkButton * widget, Plugin * p)
1193 {
1194 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p->priv;
1195
1196 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "defined_view"));
1197 GtkTreeModel * list;
1198 GtkTreeIter it;
1199 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
1200 {
1201 LaunchButton *btn;
1202 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, COL_BTN, &btn, -1);
1203 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &it);
1204 int n = gtk_tree_model_iter_n_children(list, NULL);
1205 if (gtk_tree_path_get_indices(path)[0] < (n - 1))
1206 {
1207 gtk_tree_path_next(path);
1208 GtkTreeIter it2;
1209 if (gtk_tree_model_get_iter( list, &it2, path))
1210 {
1211 /* We have found a selected button that can be moved.
1212 * Reorder it in the icon grid, the data structure, and the view. */
1213 int i = gtk_tree_path_get_indices(path)[0];
1214 ltbp->lbp.buttons = g_slist_remove(ltbp->lbp.buttons, btn);
1215 ltbp->lbp.buttons = g_slist_insert(ltbp->lbp.buttons, btn, i + 1);
1216 gtk_list_store_move_after(GTK_LIST_STORE(list), &it, &it2);
1217 icon_grid_reorder_child( ltbp->lbp.icon_grid, btn->widget, i);
1218 }
1219 }
1220 gtk_tree_path_free(path);
1221 }
1222 }
1223
1224 static void launchbar_configure_free_btns_in_model(GtkTreeModel* model, GtkTreeIter *parent_it)
1225 {
1226 GtkTreeIter it;
1227 if (gtk_tree_model_iter_children(model, &it, parent_it))
1228 {
1229 do
1230 {
1231 LaunchButton * btn;
1232 gtk_tree_model_get(model, &it, COL_BTN, &btn, -1);
1233 if(G_LIKELY(btn))
1234 launchbutton_free(btn);
1235 if( gtk_tree_model_iter_has_child(model, &it) )
1236 launchbar_configure_free_btns_in_model(model, &it);
1237 }
1238 while (gtk_tree_model_iter_next(model, &it));
1239 }
1240 }
1241
1242 /* Handler for "response" signal from launchtaskbar configuration dialog. */
1243 static void launchbar_configure_response(GtkDialog * dlg, int response, Plugin * p)
1244 {
1245 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p->priv;
1246
1247 /* Deallocate LaunchButtons that were loaded from the menu. */
1248 GtkTreeView * menu_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(ltbp->config_dlg), "menu_view"));
1249 GtkTreeModel * model = gtk_tree_view_get_model(menu_view);
1250 launchbar_configure_free_btns_in_model(model, NULL);
1251
1252 /* Deallocate the configuration dialog. */
1253 ltbp->config_dlg = NULL;
1254 gtk_widget_destroy(GTK_WIDGET(dlg));
1255 }
1256
1257 static void launchbar_configure_update_icons(GtkTreeStore* tree, GtkTreeIter* parent_it)
1258 {
1259 GtkTreeIter it;
1260 if(gtk_tree_model_iter_children(GTK_TREE_MODEL(tree), &it, parent_it))
1261 {
1262 do
1263 {
1264 char* name;
1265 GdkPixbuf* pix;
1266 gtk_tree_model_get(GTK_TREE_MODEL(tree), &it, COL_ICON, &pix, -1);
1267 if(!pix)
1268 {
1269 gtk_tree_model_get(GTK_TREE_MODEL(tree), &it, COL_ICON_NAME, &name, -1);
1270 pix = lxpanel_load_icon(name, PANEL_ICON_SIZE, PANEL_ICON_SIZE, TRUE);
1271 gtk_tree_store_set(tree, &it, COL_ICON, pix, -1);
1272 g_free(name);
1273 }
1274 if(pix)
1275 g_object_unref(pix);
1276 }while(gtk_tree_model_iter_next(GTK_TREE_MODEL(tree), &it));
1277 }
1278 }
1279
1280 static void on_app_tree_row_expanded(GtkTreeView* view, GtkTreeIter* it, GtkTreePath* tp, gpointer user_data)
1281 {
1282 launchbar_configure_update_icons((GtkTreeStore*)user_data, it);
1283 }
1284
1285 static void launchbar_configure_add_menu_recursive(GtkTreeStore * tree, GtkTreeIter* parent_it, MenuCacheDir * menu_dir)
1286 {
1287 /* Iterate over all menu items in this directory. */
1288 GSList * l;
1289 for (l = menu_cache_dir_get_children(menu_dir); l != NULL; l = l->next)
1290 {
1291 /* Get the next menu item. */
1292 MenuCacheItem * item = MENU_CACHE_ITEM(l->data);
1293 switch (menu_cache_item_get_type(item))
1294 {
1295 case MENU_CACHE_TYPE_NONE:
1296 case MENU_CACHE_TYPE_SEP:
1297 break;
1298
1299 case MENU_CACHE_TYPE_APP:
1300 {
1301 /* If an application, build a LaunchButton data structure so we can identify
1302 * the button in the handler. In this application, the desktop_id is the
1303 * fully qualified desktop file path. The image and tooltip are what is displayed in the view. */
1304 LaunchButton * btn = g_new0(LaunchButton, 1);
1305 btn->desktop_id = menu_cache_item_get_file_path(item);
1306 btn->image = g_strdup(menu_cache_item_get_icon(item));
1307 btn->tooltip = g_strdup(menu_cache_item_get_name(item));
1308 btn->path = g_strdup(menu_cache_app_get_working_dir(MENU_CACHE_APP(item)));
1309
1310 GKeyFile *p_key_desktop = g_key_file_new();
1311 gboolean loaded = load_app_key_file(btn->desktop_id, p_key_desktop);
1312 btn->action = loaded ? g_key_file_get_string(p_key_desktop, DESKTOP_ENTRY, "Exec", NULL) : NULL;
1313 g_key_file_free(p_key_desktop);
1314
1315 /* Add the row to the view. */
1316 GtkTreeIter it;
1317 gtk_tree_store_append(tree, &it, parent_it);
1318 gtk_tree_store_set(tree, &it,
1319 COL_ICON_NAME, menu_cache_item_get_icon(item),
1320 COL_TITLE, menu_cache_item_get_name(item),
1321 COL_BTN, btn,
1322 -1);
1323 }
1324 break;
1325
1326 case MENU_CACHE_TYPE_DIR:
1327 {
1328 GtkTreeIter it;
1329 gtk_tree_store_append(tree, &it, parent_it);
1330 gtk_tree_store_set(tree, &it,
1331 COL_ICON_NAME, menu_cache_item_get_icon(item),
1332 COL_TITLE, menu_cache_item_get_name(item),
1333 -1);
1334 /* If a directory, recursively add its menu items. */
1335 launchbar_configure_add_menu_recursive(tree, &it, MENU_CACHE_DIR(item));
1336 }
1337 break;
1338 }
1339 }
1340 if(!parent_it)
1341 launchbar_configure_update_icons(tree, parent_it);
1342 }
1343
1344 static void destroy_menu_cache(gpointer* param, GObject* tree)
1345 {
1346 MenuCache* mc = (MenuCache*)param[0];
1347 gpointer id = param[1];
1348 menu_cache_remove_reload_notify(mc, id);
1349 menu_cache_unref(mc);
1350 g_slice_free1(sizeof(gpointer) * 2, param);
1351 }
1352
1353 static void on_menu_cache_reload(MenuCache* menu_cache, gpointer tree)
1354 {
1355 MenuCacheDir * dir = menu_cache_get_root_dir(menu_cache);
1356 gtk_tree_store_clear(tree);
1357 if(dir)
1358 launchbar_configure_add_menu_recursive(tree, NULL, dir);
1359 }
1360
1361 /* Initialize the list of existing launchtaskbar buttons when the configuration dialog is shown. */
1362 static void launchbar_configure_initialize_list(Plugin * p, GtkWidget * dlg, GtkTreeView * view, gboolean from_menu)
1363 {
1364 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p->priv;
1365
1366 /* Set the selection mode. */
1367 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_BROWSE);
1368
1369 /* Define a column. */
1370 GtkTreeViewColumn* col = gtk_tree_view_get_column(view, 0);
1371
1372 /* Establish the pixbuf column cell renderer. */
1373 GtkCellRenderer * render = gtk_cell_renderer_pixbuf_new();
1374 gtk_tree_view_column_pack_start(col, render, FALSE);
1375 gtk_tree_view_column_set_attributes(col, render, "pixbuf", COL_ICON, NULL);
1376
1377 /* Establish the text column cell renderer. */
1378 render = gtk_cell_renderer_text_new();
1379 gtk_tree_view_column_pack_start(col, render, TRUE);
1380 gtk_tree_view_column_add_attribute(col, render, "text", COL_TITLE);
1381
1382 if (from_menu)
1383 {
1384 GtkTreeStore* tree = GTK_TREE_STORE(gtk_tree_view_get_model(view));
1385 /* Initialize from all menu items. */
1386 guint32 flags;
1387 MenuCache *menu_cache = panel_menu_cache_new(&flags);
1388
1389 g_signal_connect(view, "row-expanded", G_CALLBACK(on_app_tree_row_expanded), tree);
1390
1391 if (menu_cache != NULL)
1392 {
1393 MenuCacheDir * dir = menu_cache_get_root_dir(menu_cache);
1394 gpointer id = menu_cache_add_reload_notify(menu_cache, on_menu_cache_reload, tree);
1395 gpointer *param = g_slice_alloc(sizeof(gpointer) * 2);
1396 if(dir)
1397 launchbar_configure_add_menu_recursive(tree, NULL, dir);
1398 param[0] = menu_cache;
1399 param[1] = id;
1400 g_object_weak_ref(G_OBJECT(tree), (GWeakNotify)destroy_menu_cache, param);
1401 }
1402 g_object_set_data(G_OBJECT(dlg), "menu_view", view);
1403 }
1404 else
1405 {
1406 /* Establish the column data types. */
1407 GtkListStore* list = GTK_LIST_STORE(gtk_tree_view_get_model(view));
1408
1409 /* Initialize from defined launchtaskbar buttons. */
1410 GSList* l;
1411 for (l = ltbp->lbp.buttons; l != NULL; l = l->next)
1412 {
1413 LaunchButton * btn = (LaunchButton *) l->data;
1414 GtkTreeIter it;
1415 gtk_list_store_append(list, &it);
1416 gtk_list_store_set(list, &it,
1417 COL_ICON, lxpanel_load_icon(btn->image, PANEL_ICON_SIZE, PANEL_ICON_SIZE, TRUE),
1418 COL_TITLE, ((btn->tooltip != NULL) ? btn->tooltip : btn->action),
1419 COL_BTN, btn,
1420 -1);
1421 }
1422 g_object_set_data(G_OBJECT(dlg), "defined_view", view);
1423 }
1424 }
1425
1426 static void plugin_set_expand_status(LaunchTaskBarPlugin *ltbp, gboolean expand_new)
1427 {
1428 Plugin *pl = ltbp->lbp.plug;
1429 pl->expand = expand_new;
1430 gboolean old_expand, fill;
1431 guint padding;
1432 GtkPackType pack_type;
1433 gtk_box_query_child_packing(GTK_BOX(pl->panel->box), pl->pwid, &old_expand, &fill, &padding, &pack_type);
1434 gtk_box_set_child_packing(GTK_BOX(pl->panel->box), pl->pwid, pl->expand, fill, padding, pack_type);
1435 }
1436
1437 static void on_radiobutton_launch_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1438 {
1439 if(!gtk_toggle_button_get_active(p_togglebutton)) return;
1440 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1441
1442 if(!ltbp->lb_on)
1443 {
1444 ltbp->lb_on = TRUE;
1445 launchtaskbar_constructor_launch(ltbp, TRUE/*build_bootstrap*/);
1446 }
1447 if(ltbp->tb_on)
1448 {
1449 ltbp->tb_on = FALSE;
1450 gtk_widget_set_visible(ltbp->p_evbox_taskbar, FALSE);
1451 }
1452 gtk_widget_set_visible(ltbp->p_notebook_page_launch, TRUE);
1453 gtk_widget_set_visible(ltbp->p_notebook_page_task, FALSE);
1454
1455 plugin_set_expand_status(ltbp, FALSE);
1456 }
1457
1458 static void on_radiobutton_task_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1459 {
1460 if(!gtk_toggle_button_get_active(p_togglebutton)) return;
1461 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1462
1463 if(ltbp->lb_on)
1464 {
1465 ltbp->lb_on = FALSE;
1466 gtk_widget_set_visible(ltbp->p_evbox_launchbar, FALSE);
1467 }
1468 if(!ltbp->tb_on)
1469 {
1470 ltbp->tb_on = TRUE;
1471 launchtaskbar_constructor_task(ltbp);
1472 }
1473 gtk_widget_set_visible(ltbp->p_notebook_page_launch, FALSE);
1474 gtk_widget_set_visible(ltbp->p_notebook_page_task, TRUE);
1475
1476 plugin_set_expand_status(ltbp, TRUE);
1477 }
1478
1479 static void on_radiobutton_launchtask_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1480 {
1481 if(!gtk_toggle_button_get_active(p_togglebutton)) return;
1482 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p_data;
1483
1484 if(!ltbp->lb_on)
1485 {
1486 ltbp->lb_on = TRUE;
1487 launchtaskbar_constructor_launch(ltbp, TRUE/*build_bootstrap*/);
1488 }
1489 if(!ltbp->tb_on)
1490 {
1491 ltbp->tb_on = TRUE;
1492 launchtaskbar_constructor_task(ltbp);
1493 }
1494 gtk_widget_set_visible(ltbp->p_notebook_page_launch, TRUE);
1495 gtk_widget_set_visible(ltbp->p_notebook_page_task, TRUE);
1496
1497 plugin_set_expand_status(ltbp, TRUE);
1498 }
1499
1500 static void on_checkbutton_show_tooltips_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1501 {
1502 TaskbarPlugin *tb = (TaskbarPlugin *)p_data;
1503 tb->tooltips = gtk_toggle_button_get_active(p_togglebutton);
1504 //g_print("\ntb->tooltips upd\n");
1505 taskbar_apply_configuration(tb->plug);
1506 }
1507
1508 static void on_checkbutton_icons_only_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1509 {
1510 TaskbarPlugin *tb = (TaskbarPlugin *)p_data;
1511 tb->icons_only = gtk_toggle_button_get_active(p_togglebutton);
1512 //g_print("\ntb->icons_only upd\n");
1513 taskbar_apply_configuration(tb->plug);
1514 }
1515
1516 static void on_checkbutton_flat_buttons_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1517 {
1518 TaskbarPlugin *tb = (TaskbarPlugin *)p_data;
1519 tb->flat_button = gtk_toggle_button_get_active(p_togglebutton);
1520 //g_print("\ntb->flat_button upd\n");
1521 taskbar_apply_configuration(tb->plug);
1522 }
1523
1524 static void on_checkbutton_show_all_desks_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1525 {
1526 TaskbarPlugin *tb = (TaskbarPlugin *)p_data;
1527 tb->show_all_desks = gtk_toggle_button_get_active(p_togglebutton);
1528 //g_print("\ntb->show_all_desks upd\n");
1529 taskbar_apply_configuration(tb->plug);
1530 }
1531
1532 static void on_checkbutton_same_monitor_only_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1533 {
1534 TaskbarPlugin *tb = (TaskbarPlugin *)p_data;
1535 tb->same_monitor_only = gtk_toggle_button_get_active(p_togglebutton);
1536 //g_print("\ntb->same_monitor_only upd\n");
1537 taskbar_apply_configuration(tb->plug);
1538 }
1539
1540 static void on_checkbutton_mouse_wheel_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1541 {
1542 TaskbarPlugin *tb = (TaskbarPlugin *)p_data;
1543 tb->use_mouse_wheel = gtk_toggle_button_get_active(p_togglebutton);
1544 //g_print("\ntb->use_mouse_wheel upd\n");
1545 taskbar_apply_configuration(tb->plug);
1546 }
1547
1548 static void on_checkbutton_urgency_hint_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1549 {
1550 TaskbarPlugin *tb = (TaskbarPlugin *)p_data;
1551 tb->use_urgency_hint = gtk_toggle_button_get_active(p_togglebutton);
1552 //g_print("\ntb->use_urgency_hint upd\n");
1553 taskbar_apply_configuration(tb->plug);
1554 }
1555
1556 static void on_checkbutton_grouped_tasks_toggled(GtkToggleButton *p_togglebutton, gpointer p_data)
1557 {
1558 TaskbarPlugin *tb = (TaskbarPlugin *)p_data;
1559 tb->grouped_tasks = gtk_toggle_button_get_active(p_togglebutton);
1560 //g_print("\ntb->grouped_tasks upd\n");
1561 taskbar_apply_configuration(tb->plug);
1562 }
1563
1564 static void on_spinbutton_max_width_value_changed(GtkSpinButton *p_spinbutton, gpointer p_data)
1565 {
1566 TaskbarPlugin *tb = (TaskbarPlugin *)p_data;
1567 tb->task_width_max = gtk_spin_button_get_value(p_spinbutton);
1568 //g_print("\ntb->task_width_max upd\n");
1569 taskbar_apply_configuration(tb->plug);
1570 }
1571
1572 static void on_spinbutton_spacing_value_changed(GtkSpinButton *p_spinbutton, gpointer p_data)
1573 {
1574 TaskbarPlugin *tb = (TaskbarPlugin *)p_data;
1575 tb->spacing = gtk_spin_button_get_value(p_spinbutton);
1576 //g_print("\ntb->spacing upd\n");
1577 taskbar_apply_configuration(tb->plug);
1578 }
1579
1580 static gboolean on_defined_view_button_press_event(GtkWidget *p_widget, GdkEventButton *p_event, gpointer p_data)
1581 {
1582 LaunchbarPlugin *lb = (LaunchbarPlugin *)p_data;
1583 if(p_event->button == 1)
1584 {
1585 if(p_event->type == GDK_2BUTTON_PRESS)
1586 {
1587 gtk_button_clicked(GTK_BUTTON(lb->p_button_remove));
1588 }
1589 }
1590 return FALSE;
1591 }
1592
1593 static void on_defined_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_data)
1594 {
1595 gboolean label_set = FALSE;
1596 LaunchbarPlugin *lb = (LaunchbarPlugin *)p_data;
1597 GtkTreeIter tree_iter_sel;
1598 GtkTreeModel* p_treemodel = gtk_tree_view_get_model(p_treeview);
1599 GtkTreeSelection *p_treeselection = gtk_tree_view_get_selection(p_treeview);
1600 if(gtk_tree_selection_get_selected(p_treeselection,
1601 (GtkTreeModel **)(&p_treemodel),
1602 &tree_iter_sel))
1603 {
1604 LaunchButton * p_btn;
1605 gtk_tree_model_get(p_treemodel, &tree_iter_sel, COL_BTN, &p_btn, -1);
1606 if( (p_btn != NULL) && (p_btn->action != NULL) )
1607 {
1608 GString *p_gstring = g_string_new("");
1609 g_string_printf(p_gstring, "<i>Exec =</i> %s", p_btn->action);
1610 gtk_label_set_markup(GTK_LABEL(lb->p_label_def_app_exec), p_gstring->str);
1611 g_string_free(p_gstring, TRUE/*free also gstring->str*/);
1612 gtk_widget_set_visible(lb->p_label_def_app_exec, TRUE);
1613 label_set = TRUE;
1614 }
1615 }
1616 if(!label_set)
1617 {
1618 gtk_widget_set_visible(lb->p_label_def_app_exec, FALSE);
1619 }
1620 }
1621
1622 static void on_menu_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_data)
1623 {
1624 gboolean label_set = FALSE;
1625 LaunchbarPlugin *lb = (LaunchbarPlugin *)p_data;
1626 GtkTreeIter tree_iter_sel;
1627 GtkTreeModel* p_treemodel = gtk_tree_view_get_model(p_treeview);
1628 GtkTreeSelection *p_treeselection = gtk_tree_view_get_selection(p_treeview);
1629 if(gtk_tree_selection_get_selected(p_treeselection,
1630 (GtkTreeModel **)(&p_treemodel),
1631 &tree_iter_sel))
1632 {
1633 LaunchButton * p_btn;
1634 gtk_tree_model_get(p_treemodel, &tree_iter_sel, COL_BTN, &p_btn, -1);
1635 if( (p_btn != NULL) && (p_btn->action != NULL) )
1636 {
1637 GString *p_gstring = g_string_new("");
1638 g_string_printf(p_gstring, "<i>Exec =</i> %s", p_btn->action);
1639 gtk_label_set_markup(GTK_LABEL(lb->p_label_menu_app_exec), p_gstring->str);
1640 g_string_free(p_gstring, TRUE/*free also gstring->str*/);
1641 gtk_widget_set_visible(lb->p_label_menu_app_exec, TRUE);
1642 label_set = TRUE;
1643 }
1644 }
1645 if(!label_set)
1646 {
1647 gtk_widget_set_visible(lb->p_label_menu_app_exec, FALSE);
1648 }
1649 }
1650
1651 static gboolean on_menu_view_button_press_event(GtkWidget *p_widget, GdkEventButton *p_event, gpointer p_data)
1652 {
1653 LaunchbarPlugin *lb = (LaunchbarPlugin *)p_data;
1654 if(p_event->button == 1)
1655 {
1656 if(p_event->type == GDK_2BUTTON_PRESS)
1657 {
1658 gtk_button_clicked(GTK_BUTTON(lb->p_button_add));
1659 }
1660 }
1661 else if(p_event->button == 2)
1662 {
1663 GtkTreePath *p_tree_path;
1664 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(p_widget),
1665 p_event->x, p_event->y,
1666 &p_tree_path,
1667 NULL, NULL, NULL))
1668 {
1669 if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(p_widget), p_tree_path))
1670 gtk_tree_view_collapse_row(GTK_TREE_VIEW(p_widget), p_tree_path);
1671 else
1672 gtk_tree_view_expand_row(GTK_TREE_VIEW(p_widget), p_tree_path, FALSE);
1673 gtk_tree_path_free(p_tree_path);
1674 }
1675 }
1676 return FALSE;
1677 }
1678
1679 /* Callback when the configuration dialog is to be shown. */
1680 static void launchtaskbar_configure(Plugin * p, GtkWindow * parent)
1681 {
1682 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p->priv;
1683
1684 if (ltbp->config_dlg == NULL)
1685 {
1686 GtkWidget *dlg, *btn, *defined_view, *menu_view;
1687 GtkBuilder *builder = gtk_builder_new();
1688
1689 gtk_builder_add_from_file(builder, PACKAGE_UI_DIR "/launchtaskbar.ui", NULL);
1690 dlg = (GtkWidget *)gtk_builder_get_object(builder, "dlg");
1691 panel_apply_icon(GTK_WINDOW(dlg));
1692
1693 defined_view = (GtkWidget *)gtk_builder_get_object(builder, "defined_view");
1694 menu_view = (GtkWidget *)gtk_builder_get_object(builder, "menu_view");
1695 ltbp->lbp.p_label_def_app_exec = (GtkWidget*)gtk_builder_get_object(builder, "label_def_app_exec");
1696 ltbp->lbp.p_label_menu_app_exec = (GtkWidget*)gtk_builder_get_object(builder, "label_menu_app_exec");
1697
1698 /* Connect signals. */
1699 g_signal_connect(dlg, "response", G_CALLBACK(launchbar_configure_response), p);
1700
1701 ltbp->lbp.p_button_add = (GtkWidget *)gtk_builder_get_object(builder, "button_add");
1702 g_signal_connect(ltbp->lbp.p_button_add, "clicked", G_CALLBACK(launchbar_configure_add_button), p);
1703
1704 ltbp->lbp.p_button_remove = (GtkWidget *)gtk_builder_get_object(builder, "button_remove");
1705 g_signal_connect(ltbp->lbp.p_button_remove, "clicked", G_CALLBACK(launchbar_configure_remove_button), p);
1706
1707 btn = (GtkWidget *)gtk_builder_get_object(builder, "button_up");
1708 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_up_button), p);
1709
1710 btn = (GtkWidget *)gtk_builder_get_object(builder, "button_down");
1711 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_down_button), p);
1712
1713 g_signal_connect(defined_view, "button-press-event", G_CALLBACK(on_defined_view_button_press_event), &ltbp->lbp);
1714 g_signal_connect(defined_view, "cursor-changed", G_CALLBACK(on_defined_view_cursor_changed), &ltbp->lbp);
1715 g_signal_connect(menu_view, "button-press-event", G_CALLBACK(on_menu_view_button_press_event), &ltbp->lbp);
1716 g_signal_connect(menu_view, "cursor-changed", G_CALLBACK(on_menu_view_cursor_changed), &ltbp->lbp);
1717
1718 GtkWidget *p_checkbutton_show_tooltips, *p_checkbutton_icons_only, *p_checkbutton_flat_buttons;
1719 GtkWidget *p_checkbutton_show_all_desks, *p_checkbutton_same_monitor_only;
1720 GtkWidget *p_checkbutton_mouse_wheel, *p_checkbutton_urgency_hint;
1721 GtkWidget *p_checkbutton_grouped_tasks, *p_spinbutton_max_width, *p_spinbutton_spacing;
1722 GtkWidget *p_notebook, *p_radiobutton_launch, *p_radiobutton_task, *p_radiobutton_launchtask;
1723
1724 p_notebook = (GtkWidget *)gtk_builder_get_object(builder, "notebook");
1725 ltbp->p_notebook_page_launch = gtk_notebook_get_nth_page(GTK_NOTEBOOK(p_notebook), 0);
1726 ltbp->p_notebook_page_task = gtk_notebook_get_nth_page(GTK_NOTEBOOK(p_notebook), 1);
1727 gtk_widget_set_visible(ltbp->p_notebook_page_launch, ltbp->lb_on);
1728 gtk_widget_set_visible(ltbp->p_notebook_page_task, ltbp->tb_on);
1729
1730 p_radiobutton_launch = (GtkWidget *)gtk_builder_get_object(builder, "radiobutton_launch");
1731 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_radiobutton_launch), !ltbp->tb_on);
1732 p_radiobutton_task = (GtkWidget *)gtk_builder_get_object(builder, "radiobutton_task");
1733 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_radiobutton_task), !ltbp->lb_on);
1734 p_radiobutton_launchtask = (GtkWidget *)gtk_builder_get_object(builder, "radiobutton_launchtask");
1735 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_radiobutton_launchtask), ltbp->lb_on && ltbp->tb_on);
1736 g_signal_connect(GTK_TOGGLE_BUTTON(p_radiobutton_launch), "toggled",
1737 G_CALLBACK(on_radiobutton_launch_toggled), ltbp);
1738 g_signal_connect(GTK_TOGGLE_BUTTON(p_radiobutton_task), "toggled",
1739 G_CALLBACK(on_radiobutton_task_toggled), ltbp);
1740 g_signal_connect(GTK_TOGGLE_BUTTON(p_radiobutton_launchtask), "toggled",
1741 G_CALLBACK(on_radiobutton_launchtask_toggled), ltbp);
1742
1743 p_checkbutton_show_tooltips = (GtkWidget *)gtk_builder_get_object(builder, "checkbutton_show_tooltips");
1744 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_checkbutton_show_tooltips), ltbp->tbp.tooltips);
1745 g_signal_connect(GTK_TOGGLE_BUTTON(p_checkbutton_show_tooltips), "toggled",
1746 G_CALLBACK(on_checkbutton_show_tooltips_toggled), &ltbp->tbp);
1747
1748 p_checkbutton_icons_only = (GtkWidget *)gtk_builder_get_object(builder, "checkbutton_icons_only");
1749 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_checkbutton_icons_only), ltbp->tbp.icons_only);
1750 g_signal_connect(GTK_TOGGLE_BUTTON(p_checkbutton_icons_only), "toggled",
1751 G_CALLBACK(on_checkbutton_icons_only_toggled), &ltbp->tbp);
1752
1753 p_checkbutton_flat_buttons = (GtkWidget *)gtk_builder_get_object(builder, "checkbutton_flat_buttons");
1754 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_checkbutton_flat_buttons), ltbp->tbp.flat_button);
1755 g_signal_connect(GTK_TOGGLE_BUTTON(p_checkbutton_flat_buttons), "toggled",
1756 G_CALLBACK(on_checkbutton_flat_buttons_toggled), &ltbp->tbp);
1757
1758 p_checkbutton_show_all_desks = (GtkWidget *)gtk_builder_get_object(builder, "checkbutton_show_all_desks");
1759 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_checkbutton_show_all_desks), ltbp->tbp.show_all_desks);
1760 g_signal_connect(GTK_TOGGLE_BUTTON(p_checkbutton_show_all_desks), "toggled",
1761 G_CALLBACK(on_checkbutton_show_all_desks_toggled), &ltbp->tbp);
1762
1763 p_checkbutton_same_monitor_only = (GtkWidget *)gtk_builder_get_object(builder, "checkbutton_same_monitor_only");
1764 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_checkbutton_same_monitor_only), ltbp->tbp.same_monitor_only);
1765 g_signal_connect(GTK_TOGGLE_BUTTON(p_checkbutton_same_monitor_only), "toggled",
1766 G_CALLBACK(on_checkbutton_same_monitor_only_toggled), &ltbp->tbp);
1767
1768 p_checkbutton_mouse_wheel = (GtkWidget *)gtk_builder_get_object(builder, "checkbutton_mouse_wheel");
1769 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_checkbutton_mouse_wheel), ltbp->tbp.use_mouse_wheel);
1770 g_signal_connect(GTK_TOGGLE_BUTTON(p_checkbutton_mouse_wheel), "toggled",
1771 G_CALLBACK(on_checkbutton_mouse_wheel_toggled), &ltbp->tbp);
1772
1773 p_checkbutton_urgency_hint = (GtkWidget *)gtk_builder_get_object(builder, "checkbutton_urgency_hint");
1774 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_checkbutton_urgency_hint), ltbp->tbp.use_urgency_hint);
1775 g_signal_connect(GTK_TOGGLE_BUTTON(p_checkbutton_urgency_hint), "toggled",
1776 G_CALLBACK(on_checkbutton_urgency_hint_toggled), &ltbp->tbp);
1777
1778 p_checkbutton_grouped_tasks = (GtkWidget *)gtk_builder_get_object(builder, "checkbutton_grouped_tasks");
1779 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p_checkbutton_grouped_tasks), ltbp->tbp.grouped_tasks);
1780 g_signal_connect(GTK_TOGGLE_BUTTON(p_checkbutton_grouped_tasks), "toggled",
1781 G_CALLBACK(on_checkbutton_grouped_tasks_toggled), &ltbp->tbp);
1782
1783 p_spinbutton_max_width = (GtkWidget *)gtk_builder_get_object(builder, "spinbutton_max_width");
1784 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p_spinbutton_max_width), ltbp->tbp.task_width_max);
1785 g_signal_connect(GTK_SPIN_BUTTON(p_spinbutton_max_width), "value-changed",
1786 G_CALLBACK(on_spinbutton_max_width_value_changed), &ltbp->tbp);
1787
1788 p_spinbutton_spacing = (GtkWidget *)gtk_builder_get_object(builder, "spinbutton_spacing");
1789 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p_spinbutton_spacing), ltbp->tbp.spacing);
1790 g_signal_connect(GTK_SPIN_BUTTON(p_spinbutton_spacing), "value-changed",
1791 G_CALLBACK(on_spinbutton_spacing_value_changed), &ltbp->tbp);
1792
1793 gtk_window_present(GTK_WINDOW(dlg));
1794 ltbp->config_dlg = dlg;
1795
1796 /* Establish a callback when the dialog completes. */
1797 g_object_weak_ref(G_OBJECT(dlg), (GWeakNotify) panel_config_save, p->panel);
1798
1799 /* Initialize the tree view contents. */
1800 launchbar_configure_initialize_list(p, dlg, GTK_TREE_VIEW(defined_view), FALSE);
1801 launchbar_configure_initialize_list(p, dlg, GTK_TREE_VIEW(menu_view), TRUE);
1802
1803 gtk_widget_set_visible(ltbp->lbp.p_label_menu_app_exec, FALSE);
1804 gtk_widget_set_visible(ltbp->lbp.p_label_def_app_exec, FALSE);
1805
1806 g_object_unref(builder);
1807 return;
1808 }
1809 }
1810
1811 /* Callback when the configuration is to be saved. */
1812 static void launchtaskbar_save_configuration(Plugin * p, FILE * fp)
1813 {
1814 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p->priv;
1815
1816 lxpanel_put_bool(fp, "LaunchBarON", ltbp->lb_on);
1817 lxpanel_put_bool(fp, "TaskBarON", ltbp->tb_on);
1818
1819 if(ltbp->lb_on)
1820 {
1821 GSList * l;
1822 for (l = ltbp->lbp.buttons; l != NULL; l = l->next)
1823 {
1824 LaunchButton * btn = (LaunchButton *) l->data;
1825 lxpanel_put_line(fp, "Button {");
1826 if (btn->desktop_id != NULL)
1827 lxpanel_put_str(fp, "id", btn->desktop_id);
1828 if (btn->customize_image)
1829 lxpanel_put_str(fp, "image", btn->image);
1830 if(btn->customize_tooltip)
1831 lxpanel_put_str(fp, "tooltip", btn->tooltip);
1832 if(btn->customize_path)
1833 lxpanel_put_str(fp, "path", btn->path);
1834 if (btn->customize_action)
1835 lxpanel_put_str(fp, "action", btn->action);
1836 if (btn->use_terminal)
1837 lxpanel_put_bool(fp, "terminal", TRUE);
1838 lxpanel_put_line(fp, "}");
1839 }
1840 }
1841
1842 if(ltbp->tb_on)
1843 {
1844 lxpanel_put_bool(fp, "tooltips", ltbp->tbp.tooltips);
1845 lxpanel_put_bool(fp, "IconsOnly", ltbp->tbp.icons_only);
1846 lxpanel_put_bool(fp, "ShowAllDesks", ltbp->tbp.show_all_desks);
1847 lxpanel_put_bool(fp, "SameMonitorOnly", ltbp->tbp.same_monitor_only);
1848 lxpanel_put_bool(fp, "UseMouseWheel", ltbp->tbp.use_mouse_wheel);
1849 lxpanel_put_bool(fp, "UseUrgencyHint", ltbp->tbp.use_urgency_hint);
1850 lxpanel_put_bool(fp, "FlatButton", ltbp->tbp.flat_button);
1851 lxpanel_put_int(fp, "MaxTaskWidth", ltbp->tbp.task_width_max);
1852 lxpanel_put_int(fp, "spacing", ltbp->tbp.spacing);
1853 lxpanel_put_bool(fp, "GroupedTasks", ltbp->tbp.grouped_tasks);
1854 }
1855 }
1856
1857 /* Callback when panel configuration changes. */
1858 static void launchtaskbar_panel_configuration_changed(Plugin * p)
1859 {
1860 /* Set orientation into the icon grid. */
1861 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)p->priv;
1862
1863 GtkOrientation bo = (p->panel->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1864 icon_grid_set_geometry(ltbp->lbp.icon_grid, bo,
1865 p->panel->icon_size, p->panel->icon_size, 3, 0, p->panel->height);
1866
1867 /* Reset all the images to resize them. */
1868 GSList * l;
1869 for (l = ltbp->lbp.buttons; l != NULL; l = l->next)
1870 {
1871 LaunchButton * btn = (LaunchButton *) l->data;
1872 fb_button_set_from_file(btn->widget, btn->image, p->panel->icon_size, p->panel->icon_size, TRUE);
1873 }
1874
1875 /* Reset the bootstrap button. */
1876 if (ltbp->lbp.bootstrap_button != NULL)
1877 gtk_image_set_from_pixbuf(GTK_IMAGE(ltbp->lbp.bootstrap_button->image_widget),
1878 lxpanel_load_icon(GTK_STOCK_ADD, p->panel->icon_size, p->panel->icon_size, FALSE));
1879
1880 taskbar_update_style(&ltbp->tbp);
1881 taskbar_make_menu(&ltbp->tbp);
1882
1883 /* If the icon size changed, refetch all the icons. */
1884 if (p->panel->icon_size != ltbp->tbp.icon_size)
1885 {
1886 ltbp->tbp.icon_size = p->panel->icon_size;
1887 Task * tk;
1888 for (tk = ltbp->tbp.p_task_list; tk != NULL; tk = tk->p_task_flink_xwid)
1889 {
1890 GdkPixbuf * pixbuf = task_update_icon(&ltbp->tbp, tk, None);
1891 if (pixbuf != NULL)
1892 {
1893 gtk_image_set_from_pixbuf(GTK_IMAGE(tk->image), pixbuf);
1894 g_object_unref(pixbuf);
1895 }
1896 }
1897 }
1898
1899 /* Redraw all the labels. Icon size or font color may have changed. */
1900 taskbar_redraw(&ltbp->tbp);
1901 }
1902
1903 /* Set an urgency timer on a task. */
1904 static void set_timer_on_task(Task * tk)
1905 {
1906 gint interval;
1907 g_return_if_fail(tk->flash_timeout == 0);
1908 g_object_get(gtk_widget_get_settings(tk->button), "gtk-cursor-blink-time", &interval, NULL);
1909 tk->flash_timeout = g_timeout_add(interval, (GSourceFunc) flash_window_timeout, tk);
1910 }
1911
1912 /* Determine if a task is visible considering only its desktop placement. */
1913 static gboolean task_is_visible_on_current_desktop(TaskbarPlugin * tb, Task * tk)
1914 {
1915 return ((tk->desktop == ALL_WORKSPACES) || (tk->desktop == tb->current_desktop) || (tb->show_all_desks));
1916 }
1917
1918 /* Recompute the visible task for a class when the class membership changes.
1919 * Also transfer the urgency state to the visible task if necessary. */
1920 static void recompute_group_visibility_for_class(TaskbarPlugin * tb, TaskClass * tc)
1921 {
1922 tc->visible_count = 0;
1923 tc->p_task_visible = NULL;
1924 tc->visible_name = NULL;
1925 Task * flashing_task = NULL;
1926 gboolean class_has_urgency = FALSE;
1927 Task * tk;
1928 for (tk = tc->p_task_head; tk != NULL; tk = tk->p_task_flink_same_class)
1929 {
1930 if (task_is_visible_on_current_desktop(tb, tk))
1931 {
1932 /* Count visible tasks and make the first visible task the one that is used for display. */
1933 if (tc->visible_count == 0)
1934 tc->p_task_visible = tk;
1935 tc->visible_count += 1;
1936
1937 /* Compute summary bit for urgency anywhere in the class. */
1938 if (tk->urgency)
1939 class_has_urgency = TRUE;
1940
1941 /* If there is urgency, record the currently flashing task. */
1942 if (tk->flash_timeout != 0)
1943 flashing_task = tk;
1944
1945 /* Compute the visible name. If all visible windows have the same title, use that.
1946 * Otherwise, use the class name. This follows WNCK.
1947 * Note that the visible name is not a separate string, but is set to point to one of the others. */
1948 if (tc->visible_name == NULL)
1949 tc->visible_name = tk->name;
1950 else if ((tc->visible_name != tc->res_class)
1951 && (tc->visible_name != NULL) && (tk->name != NULL)
1952 && (strcmp(tc->visible_name, tk->name) != 0))
1953 tc->visible_name = tc->res_class;
1954 }
1955 }
1956
1957 /* Transfer the flash timeout to the visible task. */
1958 if (class_has_urgency)
1959 {
1960 if (flashing_task == NULL)
1961 {
1962 /* Set the flashing context and flash the window immediately. */
1963 tc->p_task_visible->flash_state = TRUE;
1964 flash_window_timeout(tc->p_task_visible);
1965
1966 /* Set the timer, since none is set. */
1967 set_timer_on_task(tc->p_task_visible);
1968 }
1969 else if (flashing_task != tc->p_task_visible)
1970 {
1971 /* Reset the timer on the new representative.
1972 * There will be a slight hiccup on the flash cadence. */
1973 g_source_remove(flashing_task->flash_timeout);
1974 flashing_task->flash_timeout = 0;
1975 tc->p_task_visible->flash_state = flashing_task->flash_state;
1976 flashing_task->flash_state = FALSE;
1977 set_timer_on_task(tc->p_task_visible);
1978 }
1979 }
1980 else
1981 {
1982 /* No task has urgency. Cancel the timer if one is set. */
1983 if (flashing_task != NULL)
1984 {
1985 g_source_remove(flashing_task->flash_timeout);
1986 flashing_task->flash_state = FALSE;
1987 flashing_task->flash_timeout = 0;
1988 }
1989 }
1990 }
1991
1992 /* Recompute the visible task for all classes when the desktop changes. */
1993 static void recompute_group_visibility_on_current_desktop(TaskbarPlugin * tb)
1994 {
1995 TaskClass * tc;
1996 for (tc = tb->p_taskclass_list; tc != NULL; tc = tc->p_taskclass_flink)
1997 {
1998 recompute_group_visibility_for_class(tb, tc);
1999 }
2000 }
2001
2002 /* Draw the label and tooltip on a taskbar button. */
2003 static void task_draw_label(Task * tk)
2004 {
2005 TaskClass * tc = tk->p_taskclass;
2006 gboolean bold_style = (((tk->entered_state) || (tk->flash_state)) && (tk->tb->flat_button));
2007 char *label;
2008
2009 if ((tk->tb->grouped_tasks) && (tc != NULL) && (tc->p_task_visible == tk) && (tc->visible_count > 1))
2010 {
2011 label = g_strdup_printf("(%d) %s", tc->visible_count, tc->visible_name);
2012 }
2013 else
2014 {
2015 label = g_strdup(tk->iconified ? tk->name_iconified : tk->name);
2016 }
2017
2018 if (tk->tb->tooltips)
2019 gtk_widget_set_tooltip_text(tk->button, label);
2020
2021 panel_draw_label_text(tk->tb->plug->panel, tk->label, label, bold_style, 1,
2022 tk->tb->flat_button);
2023
2024 g_free(label);
2025 }
2026
2027 /* Determine if a task is visible. */
2028 static gboolean task_is_visible(TaskbarPlugin * tb, Task * tk)
2029 {
2030 /* Not visible due to grouping. */
2031 if ((tb->grouped_tasks) && (tk->p_taskclass != NULL) && (tk->p_taskclass->p_task_visible != tk))
2032 return FALSE;
2033
2034 /* Not on same monitor */
2035 if (tb->same_monitor_only && tb->plug->panel->monitor != tk->monitor)
2036 return FALSE;
2037
2038 /* Desktop placement. */
2039 return task_is_visible_on_current_desktop(tb, tk);
2040 }
2041
2042 /* Redraw a task button. */
2043 static void task_button_redraw(Task * tk, TaskbarPlugin * tb)
2044 {
2045 if (task_is_visible(tb, tk))
2046 {
2047 task_draw_label(tk);
2048 icon_grid_set_visible(tb->icon_grid, tk->button, TRUE);
2049 }
2050 else
2051 icon_grid_set_visible(tb->icon_grid, tk->button, FALSE);
2052 }
2053
2054 /* Redraw all tasks in the taskbar. */
2055 static void taskbar_redraw(TaskbarPlugin * tb)
2056 {
2057 Task * tk;
2058 for (tk = tb->p_task_list; tk != NULL; tk = tk->p_task_flink_xwid)
2059 task_button_redraw(tk, tb);
2060 }
2061
2062 /* Determine if a task should be visible given its NET_WM_STATE. */
2063 static gboolean accept_net_wm_state(NetWMState * nws)
2064 {
2065 return ( ! (nws->skip_taskbar));
2066 }
2067
2068 /* Determine if a task should be visible given its NET_WM_WINDOW_TYPE. */
2069 static gboolean accept_net_wm_window_type(NetWMWindowType * nwwt)
2070 {
2071 return ( ! ((nwwt->desktop) || (nwwt->dock) || (nwwt->splash)));
2072 }
2073
2074 /* Free the names associated with a task. */
2075 static void task_free_names(Task * tk)
2076 {
2077 g_free(tk->name);
2078 g_free(tk->name_iconified);
2079 tk->name = tk->name_iconified = NULL;
2080 }
2081
2082 /* Set the names associated with a task.
2083 * This is expected to be the same as the title the window manager is displaying. */
2084 static void task_set_names(Task * tk, Atom source)
2085 {
2086 char * name = NULL;
2087
2088 /* Try _NET_WM_VISIBLE_NAME, which supports UTF-8.
2089 * If it is set, the window manager is displaying it as the window title. */
2090 if ((source == None) || (source == a_NET_WM_VISIBLE_NAME))
2091 {
2092 name = get_utf8_property(tk->win, a_NET_WM_VISIBLE_NAME);
2093 if (name != NULL)
2094 tk->name_source = a_NET_WM_VISIBLE_NAME;
2095 }
2096
2097 /* Try _NET_WM_NAME, which supports UTF-8, but do not overwrite _NET_WM_VISIBLE_NAME. */
2098 if ((name == NULL)
2099 && ((source == None) || (source == a_NET_WM_NAME))
2100 && ((tk->name_source == None) || (tk->name_source == a_NET_WM_NAME) || (tk->name_source == XA_WM_NAME)))
2101 {
2102 name = get_utf8_property(tk->win, a_NET_WM_NAME);
2103 if (name != NULL)
2104 tk->name_source = a_NET_WM_NAME;
2105 }
2106
2107 /* Try WM_NAME, which supports only ISO-8859-1, but do not overwrite _NET_WM_VISIBLE_NAME or _NET_WM_NAME. */
2108 if ((name == NULL)
2109 && ((source == None) || (source == XA_WM_NAME))
2110 && ((tk->name_source == None) || (tk->name_source == XA_WM_NAME)))
2111 {
2112 name = get_textproperty(tk->win, XA_WM_NAME);
2113 if (name != NULL)
2114 tk->name_source = XA_WM_NAME;
2115 }
2116
2117 /* Set the name into the task context, and also on the tooltip. */
2118 if (name != NULL)
2119 {
2120 task_free_names(tk);
2121 tk->name = g_strdup(name);
2122 tk->name_iconified = g_strdup_printf("[%s]", name);
2123 g_free(name);
2124
2125 /* Redraw the button. */
2126 task_button_redraw(tk, tk->tb);
2127 }
2128 }
2129
2130 /* Unlink a task from the class list because its class changed or it was deleted. */
2131 static void task_unlink_class(Task * tk)
2132 {
2133 TaskClass * tc = tk->p_taskclass;
2134 if (tc != NULL)
2135 {
2136 /* Action in Launchbar after class removed */
2137 launchbar_update_after_taskbar_class_removed(tk->tb->plug->priv, tk);
2138
2139 /* Remove from per-class task list. */
2140 if (tc->p_task_head == tk)
2141 {
2142 /* Removing the head of the list. This causes a new task to be the visible task, so we redraw. */
2143 tc->p_task_head = tk->p_task_flink_same_class;
2144 if (tc->p_task_head != NULL)
2145 task_button_redraw(tc->p_task_head, tk->tb);
2146 }
2147 else
2148 {
2149 /* Locate the task and its predecessor in the list and then remove it. For safety, ensure it is found. */
2150 Task * tk_pred = NULL;
2151 Task * tk_cursor;
2152 for (
2153 tk_cursor = tc->p_task_head;
2154 ((tk_cursor != NULL) && (tk_cursor != tk));
2155 tk_pred = tk_cursor, tk_cursor = tk_cursor->p_task_flink_same_class) ;
2156 if (tk_cursor == tk)
2157 tk_pred->p_task_flink_same_class = tk->p_task_flink_same_class;
2158 }
2159 tk->p_task_flink_same_class = NULL;
2160
2161 /* Recompute group visibility. */
2162 recompute_group_visibility_for_class(tk->tb, tc);
2163 }
2164 }
2165
2166 /* Enter class with specified name. */
2167 static TaskClass * taskbar_enter_res_class(TaskbarPlugin * tb, char * res_class, gboolean * p_name_consumed)
2168 {
2169 /* Find existing entry or insertion point. */
2170 *p_name_consumed = FALSE;
2171 TaskClass * tc_pred = NULL;
2172 TaskClass * tc;
2173 for (tc = tb->p_taskclass_list; tc != NULL; tc_pred = tc, tc = tc->p_taskclass_flink)
2174 {
2175 int status = strcmp(res_class, tc->res_class);
2176 if (status == 0)
2177 return tc;
2178 if (status < 0)
2179 break;
2180 }
2181
2182 /* Insert new entry. */
2183 tc = g_new0(TaskClass, 1);
2184 tc->res_class = res_class;
2185 *p_name_consumed = TRUE;
2186 if (tc_pred == NULL)
2187 {
2188 tc->p_taskclass_flink = tb->p_taskclass_list;
2189 tb->p_taskclass_list = tc;
2190 }
2191 else
2192 {
2193 tc->p_taskclass_flink = tc_pred->p_taskclass_flink;
2194 tc_pred->p_taskclass_flink = tc;
2195 }
2196 return tc;
2197 }
2198
2199 /* Set the class associated with a task. */
2200 static void task_set_class(Task * tk)
2201 {
2202 /* Read the WM_CLASS property. */
2203 XClassHint ch;
2204 ch.res_name = NULL;
2205 ch.res_class = NULL;
2206 XGetClassHint(GDK_DISPLAY(), tk->win, &ch);
2207
2208 /* If the res_name was returned, free it. We make no use of it at this time. */
2209 if (ch.res_name != NULL)
2210 {
2211 XFree(ch.res_name);
2212 }
2213
2214 /* If the res_class was returned, process it.
2215 * This identifies the application that created the window and is the basis for taskbar grouping. */
2216 if (ch.res_class != NULL)
2217 {
2218 /* Convert the class to UTF-8 and enter it in the class table. */
2219 gchar * res_class = g_locale_to_utf8(ch.res_class, -1, NULL, NULL, NULL);
2220 if (res_class != NULL)
2221 {
2222 gboolean name_consumed;
2223 TaskClass * tc = taskbar_enter_res_class(tk->tb, res_class, &name_consumed);
2224 if ( ! name_consumed) g_free(res_class);
2225
2226 /* If the task changed class, update data structures. */
2227 TaskClass * old_tc = tk->p_taskclass;
2228 if (old_tc != tc)
2229 {
2230 /* Unlink from previous class, if any. */
2231 task_unlink_class(tk);
2232
2233 /* Add to end of per-class task list. Do this to keep the popup menu in order of creation. */
2234 if (tc->p_task_head == NULL)
2235 tc->p_task_head = tk;
2236 else
2237 {
2238 Task * tk_pred;
2239 for (tk_pred = tc->p_task_head; tk_pred->p_task_flink_same_class != NULL; tk_pred = tk_pred->p_task_flink_same_class) ;
2240 tk_pred->p_task_flink_same_class = tk;
2241 task_button_redraw(tk, tk->tb);
2242 }
2243 tk->p_taskclass = tc;
2244
2245 /* Recompute group visibility. */
2246 recompute_group_visibility_for_class(tk->tb, tc);
2247
2248 /* Action in Launchbar after class added */
2249 launchbar_update_after_taskbar_class_added(tk->tb->plug->priv, tk);
2250 }
2251 }
2252 XFree(ch.res_class);
2253 }
2254 }
2255
2256 /* Look up a task in the task list. */
2257 static Task * task_lookup(TaskbarPlugin * tb, Window win)
2258 {
2259 Task * tk;
2260 for (tk = tb->p_task_list; tk != NULL; tk = tk->p_task_flink_xwid)
2261 {
2262 if (tk->win == win)
2263 return tk;
2264 if (tk->win > win)
2265 break;
2266 }
2267 return NULL;
2268 }
2269
2270 /* Delete a task and optionally unlink it from the task list. */
2271 static void task_delete(TaskbarPlugin * tb, Task * tk, gboolean unlink)
2272 {
2273 /* If we think this task had focus, remove that. */
2274 if (tb->focused == tk)
2275 tb->focused = NULL;
2276
2277 /* If there is an urgency timeout, remove it. */
2278 if (tk->flash_timeout != 0) {
2279 g_source_remove(tk->flash_timeout);
2280 tk->flash_timeout = 0;
2281 }
2282
2283 /* Deallocate structures. */
2284 icon_grid_remove(tb->icon_grid, tk->button);
2285 task_free_names(tk);
2286 task_unlink_class(tk);
2287
2288 /* If requested, unlink the task from the task list.
2289 * If not requested, the caller will do this. */
2290 if (unlink)
2291 {
2292 if (tb->p_task_list == tk)
2293 tb->p_task_list = tk->p_task_flink_xwid;
2294 else
2295 {
2296 /* Locate the task and its predecessor in the list and then remove it. For safety, ensure it is found. */
2297 Task * tk_pred = NULL;
2298 Task * tk_cursor;
2299 for (
2300 tk_cursor = tb->p_task_list;
2301 ((tk_cursor != NULL) && (tk_cursor != tk));
2302 tk_pred = tk_cursor, tk_cursor = tk_cursor->p_task_flink_xwid) ;
2303 if (tk_cursor == tk)
2304 tk_pred->p_task_flink_xwid = tk->p_task_flink_xwid;
2305 }
2306 }
2307
2308 /* Deallocate the task structure. */
2309 g_free(tk);
2310 }
2311
2312 /* Get a pixbuf from a pixmap.
2313 * Originally from libwnck, Copyright (C) 2001 Havoc Pennington. */
2314 static GdkPixbuf * _wnck_gdk_pixbuf_get_from_pixmap(Pixmap xpixmap, int width, int height)
2315 {
2316 /* Get the drawable. */
2317 GdkDrawable * drawable = gdk_xid_table_lookup(xpixmap);
2318 if (drawable != NULL)
2319 g_object_ref(G_OBJECT(drawable));
2320 else
2321 drawable = gdk_pixmap_foreign_new(xpixmap);
2322
2323 GdkColormap * colormap = NULL;
2324 GdkPixbuf * retval = NULL;
2325 if (drawable != NULL)
2326 {
2327 /* Get the colormap.
2328 * If the drawable has no colormap, use no colormap or the system colormap as recommended in the documentation of gdk_drawable_get_colormap. */
2329 colormap = gdk_drawable_get_colormap(drawable);
2330 gint depth = gdk_drawable_get_depth(drawable);
2331 if (colormap != NULL)
2332 g_object_ref(G_OBJECT(colormap));
2333 else if (depth == 1)
2334 colormap = NULL;
2335 else
2336 {
2337 colormap = gdk_screen_get_system_colormap(gdk_drawable_get_screen(drawable));
2338 g_object_ref(G_OBJECT(colormap));
2339 }
2340
2341 /* Be sure we aren't going to fail due to visual mismatch. */
2342 #if GTK_CHECK_VERSION(2,22,0)
2343 if ((colormap != NULL) && (gdk_visual_get_depth(gdk_colormap_get_visual(colormap)) != depth))
2344 #else
2345 if ((colormap != NULL) && (gdk_colormap_get_visual(colormap)->depth != depth))
2346 #endif
2347 {
2348 g_object_unref(G_OBJECT(colormap));
2349 colormap = NULL;
2350 }
2351
2352 /* Do the major work. */
2353 retval = gdk_pixbuf_get_from_drawable(NULL, drawable, colormap, 0, 0, 0, 0, width, height);
2354 }
2355
2356 /* Clean up and return. */
2357 if (colormap != NULL)
2358 g_object_unref(G_OBJECT(colormap));
2359 if (drawable != NULL)
2360 g_object_unref(G_OBJECT(drawable));
2361 return retval;
2362 }
2363
2364 /* Apply a mask to a pixbuf.
2365 * Originally from libwnck, Copyright (C) 2001 Havoc Pennington. */
2366 static GdkPixbuf * apply_mask(GdkPixbuf * pixbuf, GdkPixbuf * mask)
2367 {
2368 /* Initialize. */
2369 int w = MIN(gdk_pixbuf_get_width(mask), gdk_pixbuf_get_width(pixbuf));
2370 int h = MIN(gdk_pixbuf_get_height(mask), gdk_pixbuf_get_height(pixbuf));
2371 GdkPixbuf * with_alpha = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
2372 guchar * dst = gdk_pixbuf_get_pixels(with_alpha);
2373 guchar * src = gdk_pixbuf_get_pixels(mask);
2374 int dst_stride = gdk_pixbuf_get_rowstride(with_alpha);
2375 int src_stride = gdk_pixbuf_get_rowstride(mask);
2376
2377 /* Loop to do the work. */
2378 int i;
2379 for (i = 0; i < h; i += 1)
2380 {
2381 int j;
2382 for (j = 0; j < w; j += 1)
2383 {
2384 guchar * s = src + i * src_stride + j * 3;
2385 guchar * d = dst + i * dst_stride + j * 4;
2386
2387 /* s[0] == s[1] == s[2], they are 255 if the bit was set, 0 otherwise. */
2388 d[3] = ((s[0] == 0) ? 0 : 255); /* 0 = transparent, 255 = opaque */
2389 }
2390 }
2391
2392 return with_alpha;
2393 }
2394
2395 /* Get an icon from the window manager for a task, and scale it to a specified size. */
2396 static GdkPixbuf * get_wm_icon(Window task_win, int required_width, int required_height, Atom source, Atom * current_source)
2397 {
2398 /* The result. */
2399 GdkPixbuf * pixmap = NULL;
2400 Atom possible_source = None;
2401 int result = -1;
2402
2403 if ((source == None) || (source == a_NET_WM_ICON))
2404 {
2405 /* Important Notes:
2406 * According to freedesktop.org document:
2407 * http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html#id2552223
2408 * _NET_WM_ICON contains an array of 32-bit packed CARDINAL ARGB.
2409 * However, this is incorrect. Actually it's an array of long integers.
2410 * Toolkits like gtk+ use unsigned long here to store icons.
2411 * Besides, according to manpage of XGetWindowProperty, when returned format,
2412 * is 32, the property data will be stored as an array of longs
2413 * (which in a 64-bit application will be 64-bit values that are
2414 * padded in the upper 4 bytes).
2415 */
2416
2417 /* Get the window property _NET_WM_ICON, if possible. */
2418 Atom type = None;
2419 int format;
2420 gulong nitems;
2421 gulong bytes_after;
2422 gulong * data = NULL;
2423 result = XGetWindowProperty(
2424 GDK_DISPLAY(),
2425 task_win,
2426 a_NET_WM_ICON,
2427 0, G_MAXLONG,
2428 False, XA_CARDINAL,
2429 &type, &format, &nitems, &bytes_after, (void *) &data);
2430
2431 /* Inspect the result to see if it is usable. If not, and we got data, free it. */
2432 if ((type != XA_CARDINAL) || (nitems <= 0))
2433 {
2434 if (data != NULL)
2435 XFree(data);
2436 result = -1;
2437 }
2438
2439 /* If the result is usable, extract the icon from it. */
2440 if (result == Success)
2441 {
2442 /* Get the largest icon available, unless there is one that is the desired size. */
2443 /* FIXME: should we try to find an icon whose size is closest to
2444 * required_width and required_height to reduce unnecessary resizing? */
2445 gulong * pdata = data;
2446 gulong * pdata_end = data + nitems;
2447 gulong * max_icon = NULL;
2448 gulong max_w = 0;
2449 gulong max_h = 0;
2450 while ((pdata + 2) < pdata_end)
2451 {
2452 /* Extract the width and height. */
2453 gulong w = pdata[0];
2454 gulong h = pdata[1];
2455 gulong size = w * h;
2456 pdata += 2;
2457
2458 /* Bounds check the icon. */
2459 if (pdata + size > pdata_end)
2460 break;
2461
2462 /* Rare special case: the desired size is the same as icon size. */
2463 if ((required_width == w) && (required_height == h))
2464 {
2465 max_icon = pdata;
2466 max_w = w;
2467 max_h = h;
2468 break;
2469 }
2470
2471 /* If the icon is the largest so far, capture it. */
2472 if ((w > max_w) && (h > max_h))
2473 {
2474 max_icon = pdata;
2475 max_w = w;
2476 max_h = h;
2477 }
2478 pdata += size;
2479 }
2480
2481 /* If an icon was extracted, convert it to a pixbuf.
2482 * Its size is max_w and max_h. */
2483 if (max_icon != NULL)
2484 {
2485 /* Allocate enough space for the pixel data. */
2486 gulong len = max_w * max_h;
2487 guchar * pixdata = g_new(guchar, len * 4);
2488
2489 /* Loop to convert the pixel data. */
2490 guchar * p = pixdata;
2491 int i;
2492 for (i = 0; i < len; p += 4, i += 1)
2493 {
2494 guint argb = max_icon[i];
2495 guint rgba = (argb << 8) | (argb >> 24);
2496 p[0] = rgba >> 24;
2497 p[1] = (rgba >> 16) & 0xff;
2498 p[2] = (rgba >> 8) & 0xff;
2499 p[3] = rgba & 0xff;
2500 }
2501
2502 /* Initialize a pixmap with the pixel data. */
2503 pixmap = gdk_pixbuf_new_from_data(
2504 pixdata,
2505 GDK_COLORSPACE_RGB,
2506 TRUE, 8, /* has_alpha, bits_per_sample */
2507 max_w, max_h, max_w * 4,
2508 (GdkPixbufDestroyNotify) g_free,
2509 NULL);
2510 possible_source = a_NET_WM_ICON;
2511 }
2512 else
2513 result = -1;
2514
2515 /* Free the X property data. */
2516 XFree(data);
2517 }
2518 }
2519
2520 /* No icon available from _NET_WM_ICON. Next try WM_HINTS, but do not overwrite _NET_WM_ICON. */
2521 if ((result != Success) && (*current_source != a_NET_WM_ICON)
2522 && ((source == None) || (source != a_NET_WM_ICON)))
2523 {
2524 XWMHints * hints = XGetWMHints(GDK_DISPLAY(), task_win);
2525 result = (hints != NULL) ? Success : -1;
2526 Pixmap xpixmap = None;
2527 Pixmap xmask = None;
2528
2529 if (result == Success)
2530 {
2531 /* WM_HINTS is available. Extract the X pixmap and mask. */
2532 if ((hints->flags & IconPixmapHint))
2533 xpixmap = hints->icon_pixmap;
2534 if ((hints->flags & IconMaskHint))
2535 xmask = hints->icon_mask;
2536 XFree(hints);
2537 if (xpixmap != None)
2538 {
2539 result = Success;
2540 possible_source = XA_WM_HINTS;
2541 }
2542 else
2543 result = -1;
2544 }
2545
2546 if (result != Success)
2547 {
2548 /* No icon available from _NET_WM_ICON or WM_HINTS. Next try KWM_WIN_ICON. */
2549 Atom type = None;
2550 int format;
2551 gulong nitems;
2552 gulong bytes_after;
2553 Pixmap *icons = NULL;
2554 Atom kwin_win_icon_atom = gdk_x11_get_xatom_by_name("KWM_WIN_ICON");
2555 result = XGetWindowProperty(
2556 GDK_DISPLAY(),
2557 task_win,
2558 kwin_win_icon_atom,
2559 0, G_MAXLONG,
2560 False, kwin_win_icon_atom,
2561 &type, &format, &nitems, &bytes_after, (void *) &icons);
2562
2563 /* Inspect the result to see if it is usable. If not, and we got data, free it. */
2564 if (type != kwin_win_icon_atom)
2565 {
2566 if (icons != NULL)
2567 XFree(icons);
2568 result = -1;
2569 }
2570
2571 /* If the result is usable, extract the X pixmap and mask from it. */
2572 if (result == Success)
2573 {
2574 xpixmap = icons[0];
2575 xmask = icons[1];
2576 if (xpixmap != None)
2577 {
2578 result = Success;
2579 possible_source = kwin_win_icon_atom;
2580 }
2581 else
2582 result = -1;
2583 }
2584 }
2585
2586 /* If we have an X pixmap, get its geometry.*/
2587 unsigned int w, h;
2588 if (result == Success)
2589 {
2590 Window unused_win;
2591 int unused;
2592 unsigned int unused_2;
2593 result = XGetGeometry(
2594 GDK_DISPLAY(), xpixmap,
2595 &unused_win, &unused, &unused, &w, &h, &unused_2, &unused_2) ? Success : -1;
2596 }
2597
2598 /* If we have an X pixmap and its geometry, convert it to a GDK pixmap. */
2599 if (result == Success)
2600 {
2601 pixmap = _wnck_gdk_pixbuf_get_from_pixmap(xpixmap, w, h);
2602 result = ((pixmap != NULL) ? Success : -1);
2603 }
2604
2605 /* If we have success, see if the result needs to be masked.
2606 * Failures here are implemented as nonfatal. */
2607 if ((result == Success) && (xmask != None))
2608 {
2609 Window unused_win;
2610 int unused;
2611 unsigned int unused_2;
2612 if (XGetGeometry(
2613 GDK_DISPLAY(), xmask,
2614 &unused_win, &unused, &unused, &w, &h, &unused_2, &unused_2))
2615 {
2616 /* Convert the X mask to a GDK pixmap. */
2617 GdkPixbuf * mask = _wnck_gdk_pixbuf_get_from_pixmap(xmask, w, h);
2618 if (mask != NULL)
2619 {
2620 /* Apply the mask. */
2621 GdkPixbuf * masked_pixmap = apply_mask(pixmap, mask);
2622 g_object_unref(G_OBJECT(pixmap));
2623 g_object_unref(G_OBJECT(mask));
2624 pixmap = masked_pixmap;
2625 }
2626 }
2627 }
2628 }
2629
2630 /* If we got a pixmap, scale it and return it. */
2631 if (pixmap == NULL)
2632 return NULL;
2633 else
2634 {
2635 GdkPixbuf * ret = gdk_pixbuf_scale_simple(pixmap, required_width, required_height, GDK_INTERP_TILES);
2636 g_object_unref(pixmap);
2637 *current_source = possible_source;
2638 return ret;
2639 }
2640 }
2641
2642 /* Update the icon of a task. */
2643 static GdkPixbuf * task_update_icon(TaskbarPlugin * tb, Task * tk, Atom source)
2644 {
2645 /* Get the icon from the window's hints. */
2646 GdkPixbuf * pixbuf = get_wm_icon(tk->win, tb->icon_size - ICON_BUTTON_TRIM, tb->icon_size - ICON_BUTTON_TRIM, source, &tk->image_source);
2647
2648 /* If that fails, and we have no other icon yet, return the fallback icon. */
2649 if ((pixbuf == NULL)
2650 && ((source == None) || (tk->image_source == None)))
2651 {
2652 /* Establish the fallback task icon. This is used when no other icon is available. */
2653 if (tb->fallback_pixbuf == NULL)
2654 tb->fallback_pixbuf = gdk_pixbuf_new_from_xpm_data((const char **) icon_xpm);
2655 g_object_ref(tb->fallback_pixbuf);
2656 pixbuf = tb->fallback_pixbuf;
2657 }
2658
2659 /* Return what we have. This may be NULL to indicate that no change should be made to the icon. */
2660 return pixbuf;
2661 }
2662
2663 /* Timer expiration for urgency notification. Also used to draw the button in setting and clearing urgency. */
2664 static gboolean flash_window_timeout(Task * tk)
2665 {
2666 /* Set state on the button and redraw. */
2667 if ( ! tk->tb->flat_button)
2668 gtk_widget_set_state(tk->button, tk->flash_state ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
2669 task_draw_label(tk);
2670
2671 /* Complement the flashing context. */
2672 tk->flash_state = ! tk->flash_state;
2673 return TRUE;
2674 }
2675
2676 /* Set urgency notification. */
2677 static void task_set_urgency(Task * tk)
2678 {
2679 TaskbarPlugin * tb = tk->tb;
2680 TaskClass * tc = tk->p_taskclass;
2681 if ((tb->grouped_tasks) && (tc != NULL) && (tc->visible_count > 1))
2682 recompute_group_visibility_for_class(tk->tb, tk->p_taskclass);
2683 else
2684 {
2685 /* Set the flashing context and flash the window immediately. */
2686 tk->flash_state = TRUE;
2687 flash_window_timeout(tk);
2688
2689 /* Set the timer if none is set. */
2690 if (tk->flash_timeout == 0)
2691 set_timer_on_task(tk);
2692 }
2693 }
2694
2695 /* Clear urgency notification. */
2696 static void task_clear_urgency(Task * tk)
2697 {
2698 TaskbarPlugin * tb = tk->tb;
2699 TaskClass * tc = tk->p_taskclass;
2700 if ((tb->grouped_tasks) && (tc != NULL) && (tc->visible_count > 1))
2701 recompute_group_visibility_for_class(tk->tb, tk->p_taskclass);
2702 else
2703 {
2704 /* Remove the timer if one is set. */
2705 if (tk->flash_timeout != 0)
2706 {
2707 g_source_remove(tk->flash_timeout);
2708 tk->flash_timeout = 0;
2709 }
2710
2711 /* Clear the flashing context and unflash the window immediately. */
2712 tk->flash_state = FALSE;
2713 flash_window_timeout(tk);
2714 tk->flash_state = FALSE;
2715 }
2716 }
2717
2718 /* Do the proper steps to raise a window.
2719 * This means removing it from iconified state and bringing it to the front.
2720 * We also switch the active desktop and viewport if needed. */
2721 static void task_raise_window(Task * tk, guint32 time)
2722 {
2723 /* Change desktop if needed. */
2724 if ((tk->desktop != ALL_WORKSPACES) && (tk->desktop != tk->tb->current_desktop))
2725 Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, tk->desktop, 0, 0, 0, 0);
2726
2727 /* Evaluate use_net_active if not yet done. */
2728 if ( ! tk->tb->net_active_checked)
2729 {
2730 TaskbarPlugin * tb = tk->tb;
2731 GdkAtom net_active_atom = gdk_x11_xatom_to_atom(a_NET_ACTIVE_WINDOW);
2732 tb->use_net_active = gdk_x11_screen_supports_net_wm_hint(gtk_widget_get_screen(tb->plug->pwid), net_active_atom);
2733 tb->net_active_checked = TRUE;
2734 }
2735
2736 /* Raise the window. We can use NET_ACTIVE_WINDOW if the window manager supports it.
2737 * Otherwise, do it the old way with XMapRaised and XSetInputFocus. */
2738 if (tk->tb->use_net_active)
2739 Xclimsg(tk->win, a_NET_ACTIVE_WINDOW, 2, time, 0, 0, 0);
2740 else
2741 {
2742 GdkWindow * gdkwindow = gdk_xid_table_lookup(tk->win);
2743 if (gdkwindow != NULL)
2744 gdk_window_show(gdkwindow);
2745 else
2746 XMapRaised(GDK_DISPLAY(), tk->win);
2747
2748 /* There is a race condition between the X server actually executing the XMapRaised and this code executing XSetInputFocus.
2749 * If the window is not viewable, the XSetInputFocus will fail with BadMatch. */
2750 XWindowAttributes attr;
2751 XGetWindowAttributes(GDK_DISPLAY(), tk->win, &attr);
2752 if (attr.map_state == IsViewable)
2753 XSetInputFocus(GDK_DISPLAY(), tk->win, RevertToNone, time);
2754 }
2755
2756 /* Change viewport if needed. */
2757 XWindowAttributes xwa;
2758 XGetWindowAttributes(GDK_DISPLAY(), tk->win, &xwa);
2759 Xclimsg(tk->win, a_NET_DESKTOP_VIEWPORT, xwa.x, xwa.y, 0, 0, 0);
2760 }
2761
2762 /* Position-calculation callback for grouped-task and window-management popup menu. */
2763 static void taskbar_popup_set_position(GtkWidget * menu, gint * px, gint * py, gboolean * push_in, gpointer data)
2764 {
2765 Task * tk = (Task *) data;
2766
2767 /* Get the allocation of the popup menu. */
2768 GtkRequisition popup_req;
2769 gtk_widget_size_request(menu, &popup_req);
2770
2771 /* Determine the coordinates. */
2772 plugin_popup_set_position_helper(tk->tb->plug, tk->button, menu, &popup_req, px, py);
2773 *push_in = TRUE;
2774 }
2775
2776 /* Handler for "activate" event from "close all windows" menu*/
2777 static void taskbar_close_all_windows (GtkWidget * widget, Task * tk )
2778 {
2779 Task * tk_cursor;
2780 for (tk_cursor = tk->p_taskclass->p_task_head; tk_cursor != NULL;
2781 tk_cursor = tk_cursor->p_task_flink_same_class)
2782 {
2783 if (task_is_visible_on_current_desktop(tk->tb, tk_cursor))
2784 {
2785 Xclimsgwm(tk_cursor->win, a_WM_PROTOCOLS, a_WM_DELETE_WINDOW);
2786 }
2787 }
2788 task_group_menu_destroy(tk->tb);
2789 }
2790
2791 /* Remove the grouped-task popup menu from the screen. */
2792 static void task_group_menu_destroy(TaskbarPlugin * tb)
2793 {
2794 if (tb->group_menu != NULL)
2795 {
2796 gtk_widget_destroy(tb->group_menu);
2797 tb->group_menu = NULL;
2798 }
2799 }
2800
2801 /* Handler for "button-press-event" event from taskbar button,
2802 * or "activate" event from grouped-task popup menu item. */
2803 static gboolean taskbar_task_control_event(GtkWidget * widget, GdkEventButton * event, Task * tk, gboolean popup_menu)
2804 {
2805 TaskbarPlugin * tb = tk->tb;
2806 TaskClass * tc = tk->p_taskclass;
2807 if ((tb->grouped_tasks) && (tc != NULL) && (tc->visible_count > 1) && (GTK_IS_BUTTON(widget)))
2808 {
2809 /* This is grouped-task representative, meaning that there is a class
2810 * with at least two windows. */
2811 GtkWidget * menu = NULL;
2812 if( event->button == 1 ) /* Left click */
2813 {
2814 menu = gtk_menu_new();
2815 /* Bring up a popup menu listing all the class members. */
2816 Task * tk_cursor;
2817 for (tk_cursor = tc->p_task_head; tk_cursor != NULL;
2818 tk_cursor = tk_cursor->p_task_flink_same_class)
2819 {
2820 if (task_is_visible_on_current_desktop(tb, tk_cursor))
2821 {
2822 /* The menu item has the name, or the iconified name, and
2823 * the icon of the application window. */
2824 GtkWidget * mi = gtk_image_menu_item_new_with_label(((tk_cursor->iconified) ?
2825 tk_cursor->name_iconified : tk_cursor->name));
2826 GtkWidget * im = gtk_image_new_from_pixbuf(gtk_image_get_pixbuf(
2827 GTK_IMAGE(tk_cursor->image)));
2828 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), im);
2829 g_signal_connect(mi, "button_press_event",
2830 G_CALLBACK(taskbar_popup_activate_event), (gpointer) tk_cursor);
2831 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2832 }
2833 }
2834 }
2835 else if(event->button == 3) /* Right click */
2836 {
2837 menu = gtk_menu_new();
2838 GtkWidget * mi = gtk_menu_item_new_with_mnemonic (_("_Close all windows"));
2839 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), mi);
2840 g_signal_connect( mi, "activate", G_CALLBACK(taskbar_close_all_windows), tk);
2841 }
2842
2843 /* Show the menu. Set context so we can find the menu later to dismiss it.
2844 * Use a position-calculation callback to get the menu nicely
2845 * positioned with respect to the button. */
2846 if (menu) {
2847 gtk_widget_show_all(menu);
2848 tb->group_menu = menu;
2849 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
2850 (GtkMenuPositionFunc) taskbar_popup_set_position, (gpointer) tk,
2851 event->button, event->time);
2852 }
2853 }
2854 else
2855 {
2856 /* Not a grouped-task representative, or entered from the grouped-task popup menu. */
2857 Task * visible_task = (((tk->p_taskclass == NULL) || ( ! tk->tb->grouped_tasks)) ? tk : tk->p_taskclass->p_task_visible);
2858 task_group_menu_destroy(tb);
2859
2860 if (event->button == 1)
2861 {
2862 /* Left button.
2863 * If the task is iconified, raise it.
2864 * If the task is not iconified and has focus, iconify it.
2865 * If the task is not iconified and does not have focus, raise it. */
2866 if (tk->iconified)
2867 task_raise_window(tk, event->time);
2868 else if ((tk->focused) || (tk == tb->focused_previous))
2869 XIconifyWindow(GDK_DISPLAY(), tk->win, DefaultScreen(GDK_DISPLAY()));
2870 else
2871 task_raise_window(tk, event->time);
2872 }
2873 else if (event->button == 2)
2874 {
2875 /* Middle button. Toggle the shaded state of the window. */
2876 Xclimsg(tk->win, a_NET_WM_STATE,
2877 2, /* a_NET_WM_STATE_TOGGLE */
2878 a_NET_WM_STATE_SHADED,
2879 0, 0, 0);
2880 }
2881 else if(event->button == 3)
2882 {
2883 /* Right button. Bring up the window state popup menu. */
2884 tk->tb->menutask = tk;
2885 LaunchTaskBarPlugin *ltbp = (LaunchTaskBarPlugin *)tk->tb->plug->priv;
2886 if(ltbp->lb_on)
2887 {
2888 LaunchButton *btn = launchbar_exec_bin_exists(&ltbp->lbp, tk->exec_bin);
2889 //g_print("\nTB '%s' right-click, in LB: %c\n", tk->exec_bin, btn != NULL ? 'Y':'N');
2890 if(btn != NULL)
2891 {
2892 gtk_widget_set_visible(ltbp->tbp.p_menuitem_lock_tbp, FALSE);
2893 gtk_widget_set_visible(ltbp->tbp.p_menuitem_unlock_tbp, TRUE);
2894 gtk_widget_set_visible(ltbp->