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