Fix crash on removing placeholder from launchbar.
[lxde/lxpanel.git] / src / plugins / launchbar.c
CommitLineData
239cb032 1/**
dd731fde 2 * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
a99ee9e1
JH
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 */
a52c2257 18
7e9e5845
GP
19/* Modified by Giuseppe Penone <giuspen@gmail.com> starting from 2012-08 and lxpanel 0.5.10 */
20
45f4b842
HJYP
21#ifdef HAVE_CONFIG_H
22#include <config.h>
23#endif
24
a52c2257
HJYP
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <sys/types.h>
30#include <sys/wait.h>
31#include <signal.h>
32#include <errno.h>
33
a52c2257 34#include <gdk-pixbuf/gdk-pixbuf.h>
e7cb732b 35#include <glib/gi18n.h>
a52c2257 36
9c218f1d 37#include <libfm/fm-gtk.h>
2918994e 38
a52c2257 39#include "misc.h"
9c218f1d 40#include "plugin.h"
2918994e 41#include "icon-grid.h"
a52c2257 42
9c218f1d 43#define PANEL_ICON_SIZE 24 /* see the private.h */
a52c2257 44
2918994e 45/* Column definitions for configuration dialogs. */
46enum {
47 COL_ICON,
48 COL_TITLE,
6ab0e0b8 49 COL_ICON_NAME,
2918994e 50 COL_BTN,
51 N_COLS
52};
53
ce306a98 54static const char DESKTOP_ENTRY[] = "Desktop Entry";
08ea5341 55
9c218f1d
AG
56typedef struct LaunchbarPlugin LaunchbarPlugin;
57
2918994e 58/* Representative of one launch button.
59 * Note that the launch parameters come from the specified desktop file, or from the configuration file.
60 * This structure is also used during the "add to launchbar" dialog to hold menu items. */
61typedef struct {
9c218f1d 62 LaunchbarPlugin * p; /* Back pointer to plugin */
2918994e 63 GtkWidget * widget; /* Pointer to button */
1a571d96 64 GtkWidget * image_widget; /* Pointer to image */
9c218f1d
AG
65 FmFileInfo * fi; /* Launcher application descriptor */
66 config_setting_t * settings; /* Pointer to settings */
a048a142 67 FmDndDest * dd; /* Drag and drop support */
2918994e 68} LaunchButton;
69
70/* Private context for launchbar plugin. */
9c218f1d
AG
71struct LaunchbarPlugin {
72 GtkWidget * plugin; /* Back pointer to plugin */
73 config_setting_t * settings;
74 Panel * panel; /* Back pointer to panel */
2918994e 75 IconGrid * icon_grid; /* Icon grid managing the container */
76 GSList * buttons; /* Launchbar buttons */
77 GtkWidget * config_dlg; /* Configuration dialog */
78 LaunchButton * bootstrap_button; /* Bootstrapping button for empty launchbar */
9c218f1d 79 FmIcon * add_icon; /* Icon for bootstrap_button */
98cbcd78 80 GtkWidget *p_button_add, *p_button_remove, *p_label_menu_app_exec, *p_label_def_app_exec;
9c218f1d 81};
2918994e 82
9c218f1d
AG
83static void launchbar_configure(Panel *panel, GtkWidget *p, GtkWindow *parent);
84static void launchbar_destructor(gpointer user_data);
2918994e 85
86/* Deallocate a LaunchButton. */
87static void launchbutton_free(LaunchButton * btn)
f1286efa 88{
b3c902db
AG
89 if (btn->fi)
90 fm_file_info_unref(btn->fi);
a048a142
AG
91 if (btn->dd)
92 g_object_unref(btn->dd);
2918994e 93 g_free(btn);
f1286efa 94}
f1286efa 95
444c15a5
GP
96static gboolean on_defined_view_button_press_event(GtkWidget *p_widget, GdkEventButton *p_event, gpointer p_data)
97{
98 if(p_event->button == 1)
99 {
100 if(p_event->type == GDK_2BUTTON_PRESS)
101 {
102 LaunchbarPlugin *lb = (LaunchbarPlugin *)p_data;
103 gtk_button_clicked(GTK_BUTTON(lb->p_button_remove));
104 }
105 }
106 return FALSE;
107}
108
98cbcd78
GP
109static void on_defined_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_data)
110{
111 gboolean label_set = FALSE;
112 LaunchbarPlugin *lb = (LaunchbarPlugin *)p_data;
113 GtkTreeIter tree_iter_sel;
114 GtkTreeModel* p_treemodel = gtk_tree_view_get_model(p_treeview);
115 GtkTreeSelection *p_treeselection = gtk_tree_view_get_selection(p_treeview);
116 if(gtk_tree_selection_get_selected(p_treeselection,
117 (GtkTreeModel **)(&p_treemodel),
118 &tree_iter_sel))
119 {
120 LaunchButton * p_btn;
121 gtk_tree_model_get(p_treemodel, &tree_iter_sel, COL_BTN, &p_btn, -1);
9c218f1d 122 if( (p_btn != NULL) && (p_btn->fi != NULL) )
98cbcd78
GP
123 {
124 GString *p_gstring = g_string_new("");
9c218f1d 125 g_string_printf(p_gstring, "<i>%s</i>", fm_file_info_get_disp_name(p_btn->fi));
98cbcd78
GP
126 gtk_label_set_markup(GTK_LABEL(lb->p_label_def_app_exec), p_gstring->str);
127 g_string_free(p_gstring, TRUE/*free also gstring->str*/);
98cbcd78
GP
128 label_set = TRUE;
129 }
130 }
9c218f1d
AG
131 gtk_widget_set_visible(lb->p_label_def_app_exec, label_set);
132 gtk_widget_set_sensitive(lb->p_button_remove, label_set);
98cbcd78
GP
133}
134
ce306a98
GP
135static void on_menu_view_cursor_changed(GtkTreeView *p_treeview, gpointer p_data)
136{
137 gboolean label_set = FALSE;
138 LaunchbarPlugin *lb = (LaunchbarPlugin *)p_data;
9c218f1d 139 GAppInfo *app = fm_app_menu_view_dup_selected_app(p_treeview);
ce306a98 140
9c218f1d 141 if (app)
ed0165d4 142 {
9c218f1d
AG
143 GString *p_gstring = g_string_new("");
144 if (g_app_info_get_description(app))
145 g_string_printf(p_gstring, "<i>%s</i>", g_app_info_get_description(app));
146 else
147 g_string_printf(p_gstring, "<i>%s</i>", g_app_info_get_name(app));
148 gtk_label_set_markup(GTK_LABEL(lb->p_label_menu_app_exec), p_gstring->str);
149 g_string_free(p_gstring, TRUE/*free also gstring->str*/);
150 label_set = TRUE;
ed0165d4 151 }
9c218f1d
AG
152 gtk_widget_set_visible(lb->p_label_menu_app_exec, label_set);
153 gtk_widget_set_sensitive(lb->p_button_add, label_set);
444c15a5
GP
154}
155
2918994e 156/* Handler for "button-press-event" event from launchbar button. */
157static gboolean launchbutton_press_event(GtkWidget * widget, GdkEventButton * event, LaunchButton * b)
1dac04fb 158{
2918994e 159 /* Standard right-click handling. */
9c218f1d 160 if (lxpanel_plugin_button_press_event(b->p->plugin, event, b->p->panel))
2918994e 161 return TRUE;
162
163 if (event->button == 1) /* left button */
164 {
9c218f1d
AG
165 if (b->fi == NULL) /* The bootstrap button */
166 launchbar_configure(b->p->panel, b->p->plugin, NULL);
167 else
168 lxpanel_launch_path(b->p->panel, fm_file_info_get_path(b->fi));
2918994e 169 }
170 return TRUE;
1dac04fb
HJYP
171}
172
a048a142
AG
173/* Handler for "drag-motion" event from launchbar button. */
174static gboolean launchbutton_drag_motion_event(
2918994e 175 GtkWidget * widget,
176 GdkDragContext * context,
177 gint x,
178 gint y,
2918994e 179 guint time,
180 LaunchButton * b)
a52c2257 181{
a048a142
AG
182 GdkAtom target;
183 GdkDragAction action = 0;
184
185 fm_dnd_dest_set_dest_file(b->dd, b->fi);
186 target = fm_dnd_dest_find_target(b->dd, context);
187 if (target != GDK_NONE && fm_dnd_dest_is_target_supported(b->dd, target))
188 action = fm_dnd_dest_get_default_action(b->dd, context, target);
189 gdk_drag_status(context, action, time);
190 /* g_debug("launchbutton_drag_motion_event: act=%u",action); */
191 return (action != 0);
2918994e 192}
193
194/* Build the graphic elements for the bootstrap launchbar button. */
9c218f1d 195static void launchbutton_build_bootstrap(LaunchbarPlugin * lb)
2918994e 196{
2918994e 197 if (lb->bootstrap_button == NULL)
3e7b8eb7 198 {
9c218f1d 199 GdkPixbuf * icon;
2918994e 200 /* Build a button that has the stock "Add" icon.
201 * The "desktop-id" being NULL is the marker that this is the bootstrap button. */
202 lb->bootstrap_button = g_new0(LaunchButton, 1);
9c218f1d 203 lb->bootstrap_button->p = lb;
e74ddd00 204
205 /* Create an event box. */
206 GtkWidget * event_box = gtk_event_box_new();
207 gtk_container_set_border_width(GTK_CONTAINER(event_box), 0);
d5c46ffc
JL
208#if GLIB_CHECK_VERSION(2,18,0)
209 gtk_widget_set_can_focus (event_box, FALSE);
210#else
e74ddd00 211 GTK_WIDGET_UNSET_FLAGS(event_box, GTK_CAN_FOCUS);
d5c46ffc 212#endif
e74ddd00 213 lb->bootstrap_button->widget = event_box;
214 g_signal_connect(event_box, "button-press-event", G_CALLBACK(launchbutton_press_event), lb->bootstrap_button);
215
216 /* Create an image containing the stock "Add" icon as a child of the event box. */
9c218f1d
AG
217 lb->add_icon = fm_icon_from_name(GTK_STOCK_ADD);
218 icon = fm_pixbuf_from_icon(lb->add_icon, panel_get_icon_size(lb->panel));
219 lb->bootstrap_button->image_widget = gtk_image_new_from_pixbuf(icon);
220 g_object_unref(icon);
1a571d96 221 gtk_misc_set_padding(GTK_MISC(lb->bootstrap_button->image_widget), 0, 0);
222 gtk_misc_set_alignment(GTK_MISC(lb->bootstrap_button->image_widget), 0, 0);
223 gtk_container_add(GTK_CONTAINER(event_box), lb->bootstrap_button->image_widget);
2918994e 224
225 /* Add the bootstrap button to the icon grid. By policy it is empty at this point. */
e74ddd00 226 icon_grid_add(lb->icon_grid, event_box, TRUE);
3e7b8eb7 227 }
2918994e 228 else
229 icon_grid_set_visible(lb->icon_grid, lb->bootstrap_button->widget, TRUE);
a52c2257
HJYP
230}
231
9c218f1d
AG
232/* Build the graphic elements for a launchbar button. The desktop_id field is already established. */
233static LaunchButton *launchbutton_build_gui(LaunchbarPlugin * lb, FmPath * id)
7ba0d418 234{
9c218f1d
AG
235 /* Try to get the file data */
236 FmFileInfoJob *job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
237 FmFileInfo *fi;
238 LaunchButton *btn;
239 GtkWidget *button;
240
241 fm_file_info_job_add(job, id);
242 if (!fm_job_run_sync(FM_JOB(job)))
7ba0d418 243 {
9c218f1d
AG
244 g_warning("launchbar: problem running file info job\n");
245 g_object_unref(job);
246 return NULL;
7ba0d418 247 }
9c218f1d
AG
248 fi = fm_file_info_list_pop_head(job->file_infos);
249 g_object_unref(job);
250 if (fi == NULL)
7ba0d418 251 {
9c218f1d
AG
252 g_warning("launchbar: desktop entry does not exist\n");
253 return NULL;
7ba0d418 254 }
2918994e 255
9c218f1d
AG
256 /* Allocate the LaunchButton structure. */
257 btn = g_new0(LaunchButton, 1);
258 btn->p = lb;
259 btn->fi = fi;
2918994e 260
261 /* Create a button with the specified icon. */
cf309228
AG
262 button = lxpanel_button_new_for_fm_icon(lb->panel, fm_file_info_get_icon(fi),
263 NULL, NULL);
2918994e 264 btn->widget = button;
d5c46ffc
JL
265#if GLIB_CHECK_VERSION(2,18,0)
266 gtk_widget_set_can_focus(button, FALSE);
267#else
268 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
269#endif
9c218f1d
AG
270
271 gtk_widget_set_tooltip_text(button, fm_file_info_get_disp_name(fi));
2918994e 272
273 /* Add the button to the icon grid. */
274 icon_grid_add(lb->icon_grid, button, TRUE);
275
276 /* Drag and drop support. */
a048a142 277 btn->dd = fm_dnd_dest_new_with_handlers(button);
2918994e 278
279 /* Connect signals. */
280 g_signal_connect(button, "button-press-event", G_CALLBACK(launchbutton_press_event), (gpointer) btn);
a048a142 281 g_signal_connect(button, "drag-motion", G_CALLBACK(launchbutton_drag_motion_event), btn);
2918994e 282
283 /* If the list goes from null to non-null, remove the bootstrap button. */
284 if ((lb->buttons == NULL) && (lb->bootstrap_button != NULL))
285 icon_grid_set_visible(lb->icon_grid, lb->bootstrap_button->widget, FALSE);
286
287 /* Append at end of list to preserve configured order. */
288 lb->buttons = g_slist_append(lb->buttons, btn);
289
290 /* Show the widget and return. */
291 gtk_widget_show(button);
9c218f1d
AG
292 plugin_widget_set_background(button, lb->panel);
293 return btn;
a52c2257
HJYP
294}
295
2918994e 296/* Read the configuration file entry for a launchbar button and create it. */
9c218f1d 297static gboolean launchbutton_constructor(LaunchbarPlugin * lb, config_setting_t * s)
a52c2257 298{
9c218f1d
AG
299 LaunchButton *btn;
300 const char *str;
301 FmPath *path;
1dac04fb 302
9c218f1d
AG
303 /* Read parameters from the configuration file and validate. */
304 if (!config_setting_lookup_string(s, "id", &str) || str[0] == '\0')
305 return FALSE;
a52c2257 306
2918994e 307 /* Build the structures and return. */
9c218f1d
AG
308 path = fm_path_new_for_str(str);
309 btn = launchbutton_build_gui(lb, path);
310 fm_path_unref(path);
311 if (btn)
312 btn->settings = s;
313 return (btn != NULL);
a52c2257
HJYP
314}
315
c6756f81
AG
316/* prototype of this is app_info_create_from_commandline() in libfm */
317static gboolean _launchbutton_create_id(LaunchbarPlugin * lb, config_setting_t * s)
318{
319 const char *icon = NULL, *name, *exec, *path = NULL;
320 char *dirname, *filename;
321 int fd, terminal = 0;
322 gboolean ret = FALSE;
323
324 if (!config_setting_lookup_string(s, "action", &exec) || exec[0] == '\0')
325 return FALSE;
326 if (!config_setting_lookup_string(s, "tooltip", &name) || name[0] == '\0')
327 name = "Launcher"; /* placeholder, XDG requires a non-empty name */
328 config_setting_lookup_string(s, "image", &icon);
329 config_setting_lookup_string(s, "path", &path);
330 config_setting_lookup_int(s, "terminal", &terminal);
331
332 dirname = g_build_filename(g_get_user_data_dir(), "applications", NULL);
333 if (g_mkdir_with_parents(dirname, 0700) == 0)
334 {
335 filename = g_strdup_printf("%s/lxpanel-launcher-XXXXXX.desktop", dirname);
336 fd = g_mkstemp (filename);
337 if (fd != -1)
338 {
339 GString* content = g_string_sized_new(256);
340
341 g_string_printf(content,
342 "[" G_KEY_FILE_DESKTOP_GROUP "]\n"
343 G_KEY_FILE_DESKTOP_KEY_TYPE "=" G_KEY_FILE_DESKTOP_TYPE_APPLICATION "\n"
344 G_KEY_FILE_DESKTOP_KEY_NAME "=%s\n"
345 G_KEY_FILE_DESKTOP_KEY_EXEC "=%s\n"
346 G_KEY_FILE_DESKTOP_KEY_CATEGORIES "=X-LXPanel;\n",
347 name, exec);
348 if (icon)
349 g_string_append_printf(content, "Icon=%s\n", icon);
350 if (terminal)
351 g_string_append(content, G_KEY_FILE_DESKTOP_KEY_TERMINAL "=true\n");
352 if (path && path[0] == '/')
353 g_string_append_printf(content, "Path=%s\n", path);
354 close(fd);
355 ret = g_file_set_contents(filename, content->str, content->len, NULL);
356 if (ret) {
357 config_group_set_string(s, "id", filename);
358 /* FIXME: is it reasonable to remove obsolete keys too? */
359 panel_config_save(lb->panel);
360 } else
361 g_unlink(filename);
362 g_string_free(content, TRUE);
363 }
364 g_free(filename);
365 }
366 g_free(dirname);
367 if (ret) /* we created it, let use it */
368 return launchbutton_constructor(lb, s);
369 return FALSE;
370}
371
2918994e 372/* Plugin constructor. */
9c218f1d 373static GtkWidget *launchbar_constructor(Panel *panel, config_setting_t *settings)
a52c2257 374{
2918994e 375 static gchar * launchbar_rc = "style 'launchbar-style'\n"
a52c2257
HJYP
376 "{\n"
377 "GtkWidget::focus-line-width = 0\n"
378 "GtkWidget::focus-padding = 0\n"
379 "GtkButton::default-border = { 0, 0, 0, 0 }\n"
380 "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
381 "}\n"
6e6ffd0e 382 "widget '*launchbar*' style 'launchbar-style'";
9c218f1d
AG
383 GtkWidget *p;
384 LaunchbarPlugin * lb;
385 config_setting_t *s;
da76d5cf 386
a52c2257 387 gtk_rc_parse_string(launchbar_rc);
4542c20d 388
2918994e 389 /* Allocate plugin context and set into Plugin private data pointer. */
9c218f1d
AG
390 lb = g_new0(LaunchbarPlugin, 1);
391
392 /* Save construction pointers */
393 lb->panel = panel;
394 lb->settings = settings;
4542c20d 395
2918994e 396 /* Allocate top level widget and set into Plugin widget pointer. */
9c218f1d
AG
397 lb->plugin = p = gtk_event_box_new();
398 lxpanel_plugin_set_data(p, lb, launchbar_destructor);
d5c46ffc 399#if GLIB_CHECK_VERSION(2,18,0)
9c218f1d 400 gtk_widget_set_has_window(p, FALSE);
d5c46ffc 401#else
9c218f1d 402 GTK_WIDGET_SET_FLAGS(p, GTK_NO_WINDOW);
d5c46ffc 403#endif
9c218f1d 404 gtk_widget_set_name(p, "launchbar");
da76d5cf 405
2918994e 406 /* Allocate an icon grid manager to manage the container. */
cfde283a
AG
407 lb->icon_grid = icon_grid_new(panel, p, panel_get_orientation(panel),
408 panel_get_icon_size(panel),
9c218f1d 409 panel_get_icon_size(panel), 3, 0, panel_get_height(panel));
5297da29 410
2918994e 411 /* Read parameters from the configuration file. */
9c218f1d
AG
412 settings = config_setting_get_member(settings, "");
413 if (settings && config_setting_is_list(settings))
2918994e 414 {
9c218f1d
AG
415 guint i;
416
417 for (i = 0; (s = config_setting_get_elem(settings, i)) != NULL; )
2918994e 418 {
9c218f1d 419 if (strcmp(config_setting_get_name(s), "Button") != 0)
2918994e 420 {
9c218f1d
AG
421 g_warning("launchbar: illegal token %s\n", config_setting_get_name(s));
422 config_setting_destroy(s);
2918994e 423 }
c6756f81
AG
424 else if (!launchbutton_constructor(lb, s) &&
425 /* try to create desktop id from old-style manual setup */
426 !_launchbutton_create_id(lb, s))
2918994e 427 {
9c218f1d
AG
428 g_warning( "launchbar: can't init button\n");
429 /* FIXME: show failed id to the user instead */
430 config_setting_destroy(s);
2918994e 431 }
9c218f1d
AG
432 else /* success, accept the setting */
433 i++;
a52c2257 434 }
da76d5cf 435 }
a52c2257 436
2918994e 437 if (lb->buttons == NULL)
9c218f1d
AG
438 launchbutton_build_bootstrap(lb);
439 return p;
a52c2257
HJYP
440}
441
2918994e 442/* Plugin destructor. */
9c218f1d 443static void launchbar_destructor(gpointer user_data)
9c97f69e 444{
9c218f1d 445 LaunchbarPlugin * lb = (LaunchbarPlugin *)user_data;
2918994e 446
e74ddd00 447 /* Free the launchbar. */
2918994e 448 g_slist_foreach(lb->buttons, (GFunc) launchbutton_free, NULL);
449 icon_grid_free(lb->icon_grid);
e74ddd00 450
451 /* Free the bootstrap button if it exists. */
2918994e 452 if (lb->bootstrap_button != NULL)
9c218f1d 453 launchbutton_free(lb->bootstrap_button);
e74ddd00 454
455 /* Ensure that the configuration dialog is dismissed. */
456 if (lb->config_dlg != NULL)
457 gtk_widget_destroy(lb->config_dlg);
458
9c218f1d
AG
459 if (lb->add_icon != NULL)
460 g_object_unref(lb->add_icon);
461
e74ddd00 462 /* Deallocate all memory. */
2918994e 463 g_free(lb);
9c97f69e 464}
a52c2257 465
2918994e 466/* Handler for "clicked" action on launchbar configuration dialog "Add" button. */
9c218f1d 467static void launchbar_configure_add_button(GtkButton * widget, LaunchbarPlugin * lb)
a97d06a6 468{
2918994e 469 GtkTreeView * menu_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(lb->config_dlg), "menu_view"));
470 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(lb->config_dlg), "defined_view"));
9c218f1d
AG
471 FmPath * sel_path = fm_app_menu_view_dup_selected_app_desktop_path(menu_view);
472 LaunchButton * btn;
473
474 if (sel_path != NULL && (btn = launchbutton_build_gui(lb, sel_path)) != NULL)
2918994e 475 {
2918994e 476 GtkListStore * list = GTK_LIST_STORE(gtk_tree_view_get_model(defined_view));
477 GtkTreeIter it;
87b6b3e9 478 GdkPixbuf* pix;
9c218f1d 479 char *path;
2918994e 480 gtk_list_store_append(list, &it);
9c218f1d 481 pix = fm_pixbuf_from_icon(fm_file_info_get_icon(btn->fi), PANEL_ICON_SIZE);
2918994e 482 gtk_list_store_set(list, &it,
87b6b3e9 483 COL_ICON, pix,
9c218f1d
AG
484 COL_TITLE, fm_file_info_get_disp_name(btn->fi),
485 COL_BTN, btn,
2918994e 486 -1);
87b6b3e9 487 g_object_unref(pix);
9c218f1d
AG
488 path = fm_path_to_str(sel_path);
489 /* g_debug("*** path '%s'",path); */
a8d4af54
AG
490 btn->settings = config_group_add_subgroup(lb->settings, "Button");
491 config_group_set_string(btn->settings, "id", path);
9c218f1d
AG
492 g_free(path);
493 fm_path_unref(sel_path);
a97d06a6
HJYP
494 }
495}
496
2918994e 497/* Handler for "clicked" action on launchbar configuration dialog "Remove" button. */
9c218f1d 498static void launchbar_configure_remove_button(GtkButton * widget, LaunchbarPlugin * lb)
1dac04fb 499{
2918994e 500 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(lb->config_dlg), "defined_view"));
501 GtkTreeModel * list;
502 GtkTreeIter it;
503 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
504 {
505 LaunchButton * btn;
506 gtk_tree_model_get(list, &it, COL_BTN, &btn, -1);
507
508 /* We have found a selected button.
509 * Remove it from the icon grid, the data structure, and the view. */
510 gtk_list_store_remove(GTK_LIST_STORE(list), &it);
511 icon_grid_remove(lb->icon_grid, btn->widget);
512 lb->buttons = g_slist_remove(lb->buttons, btn);
9c218f1d 513 config_setting_destroy(btn->settings);
2918994e 514 launchbutton_free(btn);
90fd2c33 515 gtk_widget_set_visible(lb->p_label_def_app_exec, FALSE);
2918994e 516
517 /* Put the bootstrap button back if the list becomes empty. */
518 if (lb->buttons == NULL)
9c218f1d 519 launchbutton_build_bootstrap(lb);
2918994e 520 }
1dac04fb
HJYP
521}
522
2918994e 523/* Handler for "clicked" action on launchbar configuration dialog "Move Up" button. */
9c218f1d 524static void launchbar_configure_move_up_button(GtkButton * widget, LaunchbarPlugin * lb)
1dac04fb 525{
2918994e 526 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(lb->config_dlg), "defined_view"));
527 GtkTreeModel * list;
1dac04fb 528 GtkTreeIter it;
2918994e 529 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
530 {
531 LaunchButton *btn;
532 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, COL_BTN, &btn, -1);
533 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &it);
534 if ((gtk_tree_path_get_indices(path)[0] > 0)
535 && (gtk_tree_path_prev(path)))
536 {
537 GtkTreeIter it2;
538 if (gtk_tree_model_get_iter(list, &it2, path))
539 {
540 /* We have found a selected button that can be moved.
541 * Reorder it in the icon grid, the data structure, and the view. */
542 int i = gtk_tree_path_get_indices(path)[0];
543 lb->buttons = g_slist_remove(lb->buttons, btn);
544 lb->buttons = g_slist_insert(lb->buttons, btn, i);
545 gtk_list_store_move_before(GTK_LIST_STORE(list), &it, &it2);
546 icon_grid_reorder_child(lb->icon_grid, btn->widget, i);
9c218f1d
AG
547 config_setting_move_elem(btn->settings,
548 config_setting_get_member(lb->settings, ""),
549 i);
1dac04fb 550 }
1dac04fb 551 }
2918994e 552 gtk_tree_path_free(path);
1dac04fb 553 }
1dac04fb
HJYP
554}
555
2918994e 556/* Handler for "clicked" action on launchbar configuration dialog "Move Down" button. */
9c218f1d 557static void launchbar_configure_move_down_button(GtkButton * widget, LaunchbarPlugin * lb)
1dac04fb 558{
2918994e 559 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(lb->config_dlg), "defined_view"));
560 GtkTreeModel * list;
1dac04fb 561 GtkTreeIter it;
2918994e 562 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
563 {
564 LaunchButton *btn;
565 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, COL_BTN, &btn, -1);
566 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &it);
567 int n = gtk_tree_model_iter_n_children(list, NULL);
568 if (gtk_tree_path_get_indices(path)[0] < (n - 1))
569 {
570 gtk_tree_path_next(path);
1dac04fb 571 GtkTreeIter it2;
2918994e 572 if (gtk_tree_model_get_iter( list, &it2, path))
573 {
574 /* We have found a selected button that can be moved.
575 * Reorder it in the icon grid, the data structure, and the view. */
1dac04fb 576 int i = gtk_tree_path_get_indices(path)[0];
2918994e 577 lb->buttons = g_slist_remove(lb->buttons, btn);
578 lb->buttons = g_slist_insert(lb->buttons, btn, i + 1);
579 gtk_list_store_move_after(GTK_LIST_STORE(list), &it, &it2);
580 icon_grid_reorder_child( lb->icon_grid, btn->widget, i);
9c218f1d
AG
581 config_setting_move_elem(btn->settings,
582 config_setting_get_member(lb->settings, ""),
583 i);
1dac04fb
HJYP
584 }
585 }
2918994e 586 gtk_tree_path_free(path);
1dac04fb 587 }
1dac04fb
HJYP
588}
589
45f4b842 590/* Handler for "response" signal from launchbar configuration dialog. */
9c218f1d 591static void launchbar_configure_response(GtkDialog * dlg, int response, LaunchbarPlugin * lb)
45f4b842 592{
2918994e 593 /* Deallocate the configuration dialog. */
594 lb->config_dlg = NULL;
595 gtk_widget_destroy(GTK_WIDGET(dlg));
1dac04fb
HJYP
596}
597
2918994e 598/* Initialize the list of existing launchbar buttons when the configuration dialog is shown. */
9c218f1d 599static void launchbar_configure_initialize_list(LaunchbarPlugin * lb, GtkWidget * dlg, GtkTreeView * view)
1dac04fb 600{
2918994e 601 /* Set the selection mode. */
602 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_BROWSE);
1dac04fb 603
2918994e 604 /* Define a column. */
45f4b842 605 GtkTreeViewColumn* col = gtk_tree_view_get_column(view, 0);
1dac04fb 606
2918994e 607 /* Establish the pixbuf column cell renderer. */
608 GtkCellRenderer * render = gtk_cell_renderer_pixbuf_new();
609 gtk_tree_view_column_pack_start(col, render, FALSE);
610 gtk_tree_view_column_set_attributes(col, render, "pixbuf", COL_ICON, NULL);
1dac04fb 611
2918994e 612 /* Establish the text column cell renderer. */
613 render = gtk_cell_renderer_text_new();
614 gtk_tree_view_column_pack_start(col, render, TRUE);
615 gtk_tree_view_column_add_attribute(col, render, "text", COL_TITLE);
1dac04fb 616
9c218f1d
AG
617 /* Establish the column data types. */
618 GtkListStore* list = GTK_LIST_STORE(gtk_tree_view_get_model(view));
6ab0e0b8 619
9c218f1d
AG
620 /* Initialize from defined launchbar buttons. */
621 GSList* l;
622 for (l = lb->buttons; l != NULL; l = l->next)
2918994e 623 {
9c218f1d
AG
624 LaunchButton * btn = (LaunchButton *) l->data;
625 GdkPixbuf * pix;
626 GtkTreeIter it;
627 gtk_list_store_append(list, &it);
628 pix = fm_pixbuf_from_icon(fm_file_info_get_icon(btn->fi), PANEL_ICON_SIZE);
629 gtk_list_store_set(list, &it,
630 COL_ICON, pix,
631 COL_TITLE, fm_file_info_get_disp_name(btn->fi),
632 COL_BTN, btn,
633 -1);
634 g_object_unref(pix);
2918994e 635 }
9c218f1d 636 g_object_set_data(G_OBJECT(dlg), "defined_view", view);
2918994e 637}
1dac04fb 638
2918994e 639/* Callback when the configuration dialog is to be shown. */
9c218f1d 640static void launchbar_configure(Panel *panel, GtkWidget *p, GtkWindow *parent)
2918994e 641{
9c218f1d 642 LaunchbarPlugin * lb = lxpanel_plugin_get_data(p);
1dac04fb 643
2918994e 644 if (lb->config_dlg == NULL)
645 {
9c218f1d 646 GtkWidget *dlg, *btn, *defined_view, *menu_view, *menu_view_window;
45f4b842 647 GtkBuilder *builder = gtk_builder_new();
2918994e 648
45f4b842
HJYP
649 gtk_builder_add_from_file(builder, PACKAGE_UI_DIR "/launchbar.ui", NULL);
650 dlg = (GtkWidget*)gtk_builder_get_object(builder, "dlg");
651 panel_apply_icon(GTK_WINDOW(dlg));
2918994e 652
45f4b842 653 defined_view = (GtkWidget*)gtk_builder_get_object(builder, "defined_view");
9c218f1d
AG
654 menu_view_window = (GtkWidget*)gtk_builder_get_object(builder, "menu_view_window");
655 if (menu_view_window == NULL) /* fallback for old glade file */
656 {
657 menu_view_window = (GtkWidget*)gtk_builder_get_object(builder, "scroll2");
658 gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(menu_view_window)));
659 }
660 menu_view = GTK_WIDGET(fm_app_menu_view_new());
661 gtk_container_add(GTK_CONTAINER(menu_view_window), menu_view);
662 gtk_widget_show(menu_view);
98cbcd78 663 lb->p_label_def_app_exec = (GtkWidget*)gtk_builder_get_object(builder, "label_def_app_exec");
ce306a98 664 lb->p_label_menu_app_exec = (GtkWidget*)gtk_builder_get_object(builder, "label_menu_app_exec");
2918994e 665
45f4b842 666 /* Connect signals. */
9c218f1d 667 g_signal_connect(dlg, "response", G_CALLBACK(launchbar_configure_response), lb);
2918994e 668
444c15a5 669 lb->p_button_add = (GtkWidget*)gtk_builder_get_object(builder, "add");
9c218f1d 670 g_signal_connect(lb->p_button_add, "clicked", G_CALLBACK(launchbar_configure_add_button), lb);
2918994e 671
444c15a5 672 lb->p_button_remove = (GtkWidget*)gtk_builder_get_object(builder, "remove");
9c218f1d 673 g_signal_connect(lb->p_button_remove, "clicked", G_CALLBACK(launchbar_configure_remove_button), lb);
2918994e 674
45f4b842 675 btn = (GtkWidget*)gtk_builder_get_object(builder, "up");
9c218f1d 676 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_up_button), lb);
2918994e 677
45f4b842 678 btn = (GtkWidget*)gtk_builder_get_object(builder, "down");
9c218f1d 679 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_down_button), lb);
2918994e 680
444c15a5 681 g_signal_connect(defined_view, "button-press-event", G_CALLBACK(on_defined_view_button_press_event), lb);
98cbcd78 682 g_signal_connect(defined_view, "cursor-changed", G_CALLBACK(on_defined_view_cursor_changed), lb);
ce306a98 683 g_signal_connect(menu_view, "cursor-changed", G_CALLBACK(on_menu_view_cursor_changed), lb);
444c15a5 684
45f4b842
HJYP
685 gtk_window_present(GTK_WINDOW(dlg));
686 lb->config_dlg = dlg;
687
688 /* Establish a callback when the dialog completes. */
9c218f1d 689 g_object_weak_ref(G_OBJECT(dlg), (GWeakNotify) panel_config_save, panel);
2918994e 690
691 /* Initialize the tree view contents. */
9c218f1d
AG
692 launchbar_configure_initialize_list(lb, dlg, GTK_TREE_VIEW(defined_view));
693 g_object_set_data(G_OBJECT(dlg), "menu_view", menu_view);
2918994e 694
ce306a98 695 gtk_widget_set_visible(lb->p_label_menu_app_exec, FALSE);
98cbcd78 696 gtk_widget_set_visible(lb->p_label_def_app_exec, FALSE);
9c218f1d
AG
697 gtk_widget_set_sensitive(lb->p_button_add, FALSE);
698 gtk_widget_set_sensitive(lb->p_button_remove, FALSE);
ce306a98 699
45f4b842
HJYP
700 g_object_unref(builder);
701 return;
2918994e 702 }
703}
930af9fd 704
2918994e 705/* Callback when panel configuration changes. */
9c218f1d 706static void launchbar_panel_configuration_changed(Panel *panel, GtkWidget *p)
2918994e 707{
708 /* Set orientation into the icon grid. */
9c218f1d 709 LaunchbarPlugin * lb = lxpanel_plugin_get_data(p);
cfde283a
AG
710 icon_grid_set_geometry(lb->icon_grid, panel_get_orientation(panel),
711 panel_get_icon_size(panel), panel_get_icon_size(panel),
712 3, 0, panel_get_height(panel));
8f9e6256 713
714 /* Reset all the images to resize them. */
715 GSList * l;
716 for (l = lb->buttons; l != NULL; l = l->next)
717 {
718 LaunchButton * btn = (LaunchButton *) l->data;
7969be34
AG
719 lxpanel_button_update_icon(btn->widget, fm_file_info_get_icon(btn->fi),
720 panel_get_icon_size(panel));
8f9e6256 721 }
722
723 /* Reset the bootstrap button. */
724 if (lb->bootstrap_button != NULL)
9c218f1d
AG
725 {
726 GdkPixbuf * icon = fm_pixbuf_from_icon(lb->add_icon, panel_get_icon_size(panel));
727 gtk_image_set_from_pixbuf(GTK_IMAGE(lb->bootstrap_button->image_widget), icon);
728 g_object_unref(icon);
729 }
2918994e 730}
731
732/* Plugin descriptor. */
9c218f1d 733LXPanelPluginInit lxpanel_static_plugin_launchbar = {
3c3e9c9e 734 .name = N_("Application Launch Bar"),
3c3e9c9e 735 .description = N_("Bar with buttons to launch application"),
a52c2257 736
9c218f1d 737 .new_instance = launchbar_constructor,
3c3e9c9e 738 .config = launchbar_configure,
9c218f1d 739 .reconfigure = launchbar_panel_configuration_changed
a52c2257 740};