launchbar plugin: use FmDndDest for better drag & drop handling.
[lxde/lxpanel.git] / src / plugins / launchbar.c
CommitLineData
239cb032 1/**
a99ee9e1
JH
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 */
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{
9c218f1d 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
AG
164 if (b->fi == NULL) /* The bootstrap button */
165 launchbar_configure(b->p->panel, b->p->plugin, NULL);
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
AG
231/* Build the graphic elements for a launchbar button. The desktop_id field is already established. */
232static LaunchButton *launchbutton_build_gui(LaunchbarPlugin * lb, FmPath * id)
7ba0d418 233{
9c218f1d
AG
234 /* Try to get the file data */
235 FmFileInfoJob *job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
236 FmFileInfo *fi;
237 LaunchButton *btn;
238 GtkWidget *button;
239
240 fm_file_info_job_add(job, id);
241 if (!fm_job_run_sync(FM_JOB(job)))
7ba0d418 242 {
9c218f1d
AG
243 g_warning("launchbar: problem running file info job\n");
244 g_object_unref(job);
245 return NULL;
7ba0d418 246 }
9c218f1d
AG
247 fi = fm_file_info_list_pop_head(job->file_infos);
248 g_object_unref(job);
249 if (fi == NULL)
7ba0d418 250 {
9c218f1d
AG
251 g_warning("launchbar: desktop entry does not exist\n");
252 return NULL;
7ba0d418 253 }
2918994e 254
9c218f1d
AG
255 /* Allocate the LaunchButton structure. */
256 btn = g_new0(LaunchButton, 1);
257 btn->p = lb;
258 btn->fi = fi;
2918994e 259
260 /* Create a button with the specified icon. */
cf309228
AG
261 button = lxpanel_button_new_for_fm_icon(lb->panel, fm_file_info_get_icon(fi),
262 NULL, NULL);
2918994e 263 btn->widget = button;
d5c46ffc
JL
264#if GLIB_CHECK_VERSION(2,18,0)
265 gtk_widget_set_can_focus(button, FALSE);
266#else
267 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
268#endif
9c218f1d
AG
269
270 gtk_widget_set_tooltip_text(button, fm_file_info_get_disp_name(fi));
2918994e 271
272 /* Add the button to the icon grid. */
273 icon_grid_add(lb->icon_grid, button, TRUE);
274
275 /* Drag and drop support. */
a048a142 276 btn->dd = fm_dnd_dest_new_with_handlers(button);
2918994e 277
278 /* Connect signals. */
279 g_signal_connect(button, "button-press-event", G_CALLBACK(launchbutton_press_event), (gpointer) btn);
a048a142 280 g_signal_connect(button, "drag-motion", G_CALLBACK(launchbutton_drag_motion_event), btn);
2918994e 281
282 /* If the list goes from null to non-null, remove the bootstrap button. */
283 if ((lb->buttons == NULL) && (lb->bootstrap_button != NULL))
284 icon_grid_set_visible(lb->icon_grid, lb->bootstrap_button->widget, FALSE);
285
286 /* Append at end of list to preserve configured order. */
287 lb->buttons = g_slist_append(lb->buttons, btn);
288
289 /* Show the widget and return. */
290 gtk_widget_show(button);
9c218f1d
AG
291 plugin_widget_set_background(button, lb->panel);
292 return btn;
a52c2257
HJYP
293}
294
2918994e 295/* Read the configuration file entry for a launchbar button and create it. */
9c218f1d 296static gboolean launchbutton_constructor(LaunchbarPlugin * lb, config_setting_t * s)
a52c2257 297{
9c218f1d
AG
298 LaunchButton *btn;
299 const char *str;
300 FmPath *path;
1dac04fb 301
9c218f1d
AG
302 /* Read parameters from the configuration file and validate. */
303 if (!config_setting_lookup_string(s, "id", &str) || str[0] == '\0')
304 return FALSE;
a52c2257 305
2918994e 306 /* Build the structures and return. */
9c218f1d
AG
307 path = fm_path_new_for_str(str);
308 btn = launchbutton_build_gui(lb, path);
309 fm_path_unref(path);
310 if (btn)
311 btn->settings = s;
312 return (btn != NULL);
a52c2257
HJYP
313}
314
2918994e 315/* Plugin constructor. */
9c218f1d 316static GtkWidget *launchbar_constructor(Panel *panel, config_setting_t *settings)
a52c2257 317{
2918994e 318 static gchar * launchbar_rc = "style 'launchbar-style'\n"
a52c2257
HJYP
319 "{\n"
320 "GtkWidget::focus-line-width = 0\n"
321 "GtkWidget::focus-padding = 0\n"
322 "GtkButton::default-border = { 0, 0, 0, 0 }\n"
323 "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
324 "}\n"
6e6ffd0e 325 "widget '*launchbar*' style 'launchbar-style'";
9c218f1d
AG
326 GtkWidget *p;
327 LaunchbarPlugin * lb;
328 config_setting_t *s;
da76d5cf 329
a52c2257 330 gtk_rc_parse_string(launchbar_rc);
4542c20d 331
2918994e 332 /* Allocate plugin context and set into Plugin private data pointer. */
9c218f1d
AG
333 lb = g_new0(LaunchbarPlugin, 1);
334
335 /* Save construction pointers */
336 lb->panel = panel;
337 lb->settings = settings;
4542c20d 338
2918994e 339 /* Allocate top level widget and set into Plugin widget pointer. */
9c218f1d
AG
340 lb->plugin = p = gtk_event_box_new();
341 lxpanel_plugin_set_data(p, lb, launchbar_destructor);
d5c46ffc 342#if GLIB_CHECK_VERSION(2,18,0)
9c218f1d 343 gtk_widget_set_has_window(p, FALSE);
d5c46ffc 344#else
9c218f1d 345 GTK_WIDGET_SET_FLAGS(p, GTK_NO_WINDOW);
d5c46ffc 346#endif
9c218f1d 347 gtk_widget_set_name(p, "launchbar");
da76d5cf 348
2918994e 349 /* Allocate an icon grid manager to manage the container. */
cfde283a
AG
350 lb->icon_grid = icon_grid_new(panel, p, panel_get_orientation(panel),
351 panel_get_icon_size(panel),
9c218f1d 352 panel_get_icon_size(panel), 3, 0, panel_get_height(panel));
5297da29 353
2918994e 354 /* Read parameters from the configuration file. */
9c218f1d
AG
355 settings = config_setting_get_member(settings, "");
356 if (settings && config_setting_is_list(settings))
2918994e 357 {
9c218f1d
AG
358 guint i;
359
360 for (i = 0; (s = config_setting_get_elem(settings, i)) != NULL; )
2918994e 361 {
9c218f1d 362 if (strcmp(config_setting_get_name(s), "Button") != 0)
2918994e 363 {
9c218f1d
AG
364 g_warning("launchbar: illegal token %s\n", config_setting_get_name(s));
365 config_setting_destroy(s);
2918994e 366 }
9c218f1d 367 else if (!launchbutton_constructor(lb, s))
2918994e 368 {
9c218f1d
AG
369 g_warning( "launchbar: can't init button\n");
370 /* FIXME: show failed id to the user instead */
371 config_setting_destroy(s);
2918994e 372 }
9c218f1d
AG
373 else /* success, accept the setting */
374 i++;
a52c2257 375 }
da76d5cf 376 }
a52c2257 377
2918994e 378 if (lb->buttons == NULL)
9c218f1d
AG
379 launchbutton_build_bootstrap(lb);
380 return p;
a52c2257
HJYP
381}
382
2918994e 383/* Plugin destructor. */
9c218f1d 384static void launchbar_destructor(gpointer user_data)
9c97f69e 385{
9c218f1d 386 LaunchbarPlugin * lb = (LaunchbarPlugin *)user_data;
2918994e 387
e74ddd00 388 /* Free the launchbar. */
2918994e 389 g_slist_foreach(lb->buttons, (GFunc) launchbutton_free, NULL);
390 icon_grid_free(lb->icon_grid);
e74ddd00 391
392 /* Free the bootstrap button if it exists. */
2918994e 393 if (lb->bootstrap_button != NULL)
9c218f1d 394 launchbutton_free(lb->bootstrap_button);
e74ddd00 395
396 /* Ensure that the configuration dialog is dismissed. */
397 if (lb->config_dlg != NULL)
398 gtk_widget_destroy(lb->config_dlg);
399
9c218f1d
AG
400 if (lb->add_icon != NULL)
401 g_object_unref(lb->add_icon);
402
e74ddd00 403 /* Deallocate all memory. */
2918994e 404 g_free(lb);
9c97f69e 405}
a52c2257 406
2918994e 407/* Handler for "clicked" action on launchbar configuration dialog "Add" button. */
9c218f1d 408static void launchbar_configure_add_button(GtkButton * widget, LaunchbarPlugin * lb)
a97d06a6 409{
2918994e 410 GtkTreeView * menu_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(lb->config_dlg), "menu_view"));
411 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(lb->config_dlg), "defined_view"));
9c218f1d
AG
412 FmPath * sel_path = fm_app_menu_view_dup_selected_app_desktop_path(menu_view);
413 LaunchButton * btn;
414
415 if (sel_path != NULL && (btn = launchbutton_build_gui(lb, sel_path)) != NULL)
2918994e 416 {
2918994e 417 GtkListStore * list = GTK_LIST_STORE(gtk_tree_view_get_model(defined_view));
418 GtkTreeIter it;
87b6b3e9 419 GdkPixbuf* pix;
9c218f1d 420 char *path;
2918994e 421 gtk_list_store_append(list, &it);
9c218f1d 422 pix = fm_pixbuf_from_icon(fm_file_info_get_icon(btn->fi), PANEL_ICON_SIZE);
2918994e 423 gtk_list_store_set(list, &it,
87b6b3e9 424 COL_ICON, pix,
9c218f1d
AG
425 COL_TITLE, fm_file_info_get_disp_name(btn->fi),
426 COL_BTN, btn,
2918994e 427 -1);
87b6b3e9 428 g_object_unref(pix);
9c218f1d
AG
429 path = fm_path_to_str(sel_path);
430 /* g_debug("*** path '%s'",path); */
431 btn->settings = config_setting_add(config_setting_add(lb->settings, "",
432 PANEL_CONF_TYPE_LIST),
433 "Button", PANEL_CONF_TYPE_GROUP);
434 config_setting_set_string(config_setting_add(btn->settings, "id",
435 PANEL_CONF_TYPE_STRING),
436 path);
437 g_free(path);
438 fm_path_unref(sel_path);
a97d06a6
HJYP
439 }
440}
441
2918994e 442/* Handler for "clicked" action on launchbar configuration dialog "Remove" button. */
9c218f1d 443static void launchbar_configure_remove_button(GtkButton * widget, LaunchbarPlugin * lb)
1dac04fb 444{
2918994e 445 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(lb->config_dlg), "defined_view"));
446 GtkTreeModel * list;
447 GtkTreeIter it;
448 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
449 {
450 LaunchButton * btn;
451 gtk_tree_model_get(list, &it, COL_BTN, &btn, -1);
452
453 /* We have found a selected button.
454 * Remove it from the icon grid, the data structure, and the view. */
455 gtk_list_store_remove(GTK_LIST_STORE(list), &it);
456 icon_grid_remove(lb->icon_grid, btn->widget);
457 lb->buttons = g_slist_remove(lb->buttons, btn);
9c218f1d 458 config_setting_destroy(btn->settings);
2918994e 459 launchbutton_free(btn);
90fd2c33 460 gtk_widget_set_visible(lb->p_label_def_app_exec, FALSE);
2918994e 461
462 /* Put the bootstrap button back if the list becomes empty. */
463 if (lb->buttons == NULL)
9c218f1d 464 launchbutton_build_bootstrap(lb);
2918994e 465 }
1dac04fb
HJYP
466}
467
2918994e 468/* Handler for "clicked" action on launchbar configuration dialog "Move Up" button. */
9c218f1d 469static void launchbar_configure_move_up_button(GtkButton * widget, LaunchbarPlugin * lb)
1dac04fb 470{
2918994e 471 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(lb->config_dlg), "defined_view"));
472 GtkTreeModel * list;
1dac04fb 473 GtkTreeIter it;
2918994e 474 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
475 {
476 LaunchButton *btn;
477 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, COL_BTN, &btn, -1);
478 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &it);
479 if ((gtk_tree_path_get_indices(path)[0] > 0)
480 && (gtk_tree_path_prev(path)))
481 {
482 GtkTreeIter it2;
483 if (gtk_tree_model_get_iter(list, &it2, path))
484 {
485 /* We have found a selected button that can be moved.
486 * Reorder it in the icon grid, the data structure, and the view. */
487 int i = gtk_tree_path_get_indices(path)[0];
488 lb->buttons = g_slist_remove(lb->buttons, btn);
489 lb->buttons = g_slist_insert(lb->buttons, btn, i);
490 gtk_list_store_move_before(GTK_LIST_STORE(list), &it, &it2);
491 icon_grid_reorder_child(lb->icon_grid, btn->widget, i);
9c218f1d
AG
492 config_setting_move_elem(btn->settings,
493 config_setting_get_member(lb->settings, ""),
494 i);
1dac04fb 495 }
1dac04fb 496 }
2918994e 497 gtk_tree_path_free(path);
1dac04fb 498 }
1dac04fb
HJYP
499}
500
2918994e 501/* Handler for "clicked" action on launchbar configuration dialog "Move Down" button. */
9c218f1d 502static void launchbar_configure_move_down_button(GtkButton * widget, LaunchbarPlugin * lb)
1dac04fb 503{
2918994e 504 GtkTreeView * defined_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(lb->config_dlg), "defined_view"));
505 GtkTreeModel * list;
1dac04fb 506 GtkTreeIter it;
2918994e 507 if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(defined_view), &list, &it))
508 {
509 LaunchButton *btn;
510 gtk_tree_model_get(GTK_TREE_MODEL(list), &it, COL_BTN, &btn, -1);
511 GtkTreePath * path = gtk_tree_model_get_path(GTK_TREE_MODEL(list), &it);
512 int n = gtk_tree_model_iter_n_children(list, NULL);
513 if (gtk_tree_path_get_indices(path)[0] < (n - 1))
514 {
515 gtk_tree_path_next(path);
1dac04fb 516 GtkTreeIter it2;
2918994e 517 if (gtk_tree_model_get_iter( list, &it2, path))
518 {
519 /* We have found a selected button that can be moved.
520 * Reorder it in the icon grid, the data structure, and the view. */
1dac04fb 521 int i = gtk_tree_path_get_indices(path)[0];
2918994e 522 lb->buttons = g_slist_remove(lb->buttons, btn);
523 lb->buttons = g_slist_insert(lb->buttons, btn, i + 1);
524 gtk_list_store_move_after(GTK_LIST_STORE(list), &it, &it2);
525 icon_grid_reorder_child( lb->icon_grid, btn->widget, i);
9c218f1d
AG
526 config_setting_move_elem(btn->settings,
527 config_setting_get_member(lb->settings, ""),
528 i);
1dac04fb
HJYP
529 }
530 }
2918994e 531 gtk_tree_path_free(path);
1dac04fb 532 }
1dac04fb
HJYP
533}
534
45f4b842 535/* Handler for "response" signal from launchbar configuration dialog. */
9c218f1d 536static void launchbar_configure_response(GtkDialog * dlg, int response, LaunchbarPlugin * lb)
45f4b842 537{
2918994e 538 /* Deallocate the configuration dialog. */
539 lb->config_dlg = NULL;
540 gtk_widget_destroy(GTK_WIDGET(dlg));
1dac04fb
HJYP
541}
542
2918994e 543/* Initialize the list of existing launchbar buttons when the configuration dialog is shown. */
9c218f1d 544static void launchbar_configure_initialize_list(LaunchbarPlugin * lb, GtkWidget * dlg, GtkTreeView * view)
1dac04fb 545{
2918994e 546 /* Set the selection mode. */
547 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_BROWSE);
1dac04fb 548
2918994e 549 /* Define a column. */
45f4b842 550 GtkTreeViewColumn* col = gtk_tree_view_get_column(view, 0);
1dac04fb 551
2918994e 552 /* Establish the pixbuf column cell renderer. */
553 GtkCellRenderer * render = gtk_cell_renderer_pixbuf_new();
554 gtk_tree_view_column_pack_start(col, render, FALSE);
555 gtk_tree_view_column_set_attributes(col, render, "pixbuf", COL_ICON, NULL);
1dac04fb 556
2918994e 557 /* Establish the text column cell renderer. */
558 render = gtk_cell_renderer_text_new();
559 gtk_tree_view_column_pack_start(col, render, TRUE);
560 gtk_tree_view_column_add_attribute(col, render, "text", COL_TITLE);
1dac04fb 561
9c218f1d
AG
562 /* Establish the column data types. */
563 GtkListStore* list = GTK_LIST_STORE(gtk_tree_view_get_model(view));
6ab0e0b8 564
9c218f1d
AG
565 /* Initialize from defined launchbar buttons. */
566 GSList* l;
567 for (l = lb->buttons; l != NULL; l = l->next)
2918994e 568 {
9c218f1d
AG
569 LaunchButton * btn = (LaunchButton *) l->data;
570 GdkPixbuf * pix;
571 GtkTreeIter it;
572 gtk_list_store_append(list, &it);
573 pix = fm_pixbuf_from_icon(fm_file_info_get_icon(btn->fi), PANEL_ICON_SIZE);
574 gtk_list_store_set(list, &it,
575 COL_ICON, pix,
576 COL_TITLE, fm_file_info_get_disp_name(btn->fi),
577 COL_BTN, btn,
578 -1);
579 g_object_unref(pix);
2918994e 580 }
9c218f1d 581 g_object_set_data(G_OBJECT(dlg), "defined_view", view);
2918994e 582}
1dac04fb 583
2918994e 584/* Callback when the configuration dialog is to be shown. */
9c218f1d 585static void launchbar_configure(Panel *panel, GtkWidget *p, GtkWindow *parent)
2918994e 586{
9c218f1d 587 LaunchbarPlugin * lb = lxpanel_plugin_get_data(p);
1dac04fb 588
2918994e 589 if (lb->config_dlg == NULL)
590 {
9c218f1d 591 GtkWidget *dlg, *btn, *defined_view, *menu_view, *menu_view_window;
45f4b842 592 GtkBuilder *builder = gtk_builder_new();
2918994e 593
45f4b842
HJYP
594 gtk_builder_add_from_file(builder, PACKAGE_UI_DIR "/launchbar.ui", NULL);
595 dlg = (GtkWidget*)gtk_builder_get_object(builder, "dlg");
596 panel_apply_icon(GTK_WINDOW(dlg));
2918994e 597
45f4b842 598 defined_view = (GtkWidget*)gtk_builder_get_object(builder, "defined_view");
9c218f1d
AG
599 menu_view_window = (GtkWidget*)gtk_builder_get_object(builder, "menu_view_window");
600 if (menu_view_window == NULL) /* fallback for old glade file */
601 {
602 menu_view_window = (GtkWidget*)gtk_builder_get_object(builder, "scroll2");
603 gtk_widget_destroy(gtk_bin_get_child(GTK_BIN(menu_view_window)));
604 }
605 menu_view = GTK_WIDGET(fm_app_menu_view_new());
606 gtk_container_add(GTK_CONTAINER(menu_view_window), menu_view);
607 gtk_widget_show(menu_view);
98cbcd78 608 lb->p_label_def_app_exec = (GtkWidget*)gtk_builder_get_object(builder, "label_def_app_exec");
ce306a98 609 lb->p_label_menu_app_exec = (GtkWidget*)gtk_builder_get_object(builder, "label_menu_app_exec");
2918994e 610
45f4b842 611 /* Connect signals. */
9c218f1d 612 g_signal_connect(dlg, "response", G_CALLBACK(launchbar_configure_response), lb);
2918994e 613
444c15a5 614 lb->p_button_add = (GtkWidget*)gtk_builder_get_object(builder, "add");
9c218f1d 615 g_signal_connect(lb->p_button_add, "clicked", G_CALLBACK(launchbar_configure_add_button), lb);
2918994e 616
444c15a5 617 lb->p_button_remove = (GtkWidget*)gtk_builder_get_object(builder, "remove");
9c218f1d 618 g_signal_connect(lb->p_button_remove, "clicked", G_CALLBACK(launchbar_configure_remove_button), lb);
2918994e 619
45f4b842 620 btn = (GtkWidget*)gtk_builder_get_object(builder, "up");
9c218f1d 621 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_up_button), lb);
2918994e 622
45f4b842 623 btn = (GtkWidget*)gtk_builder_get_object(builder, "down");
9c218f1d 624 g_signal_connect(btn, "clicked", G_CALLBACK(launchbar_configure_move_down_button), lb);
2918994e 625
444c15a5 626 g_signal_connect(defined_view, "button-press-event", G_CALLBACK(on_defined_view_button_press_event), lb);
98cbcd78 627 g_signal_connect(defined_view, "cursor-changed", G_CALLBACK(on_defined_view_cursor_changed), lb);
ce306a98 628 g_signal_connect(menu_view, "cursor-changed", G_CALLBACK(on_menu_view_cursor_changed), lb);
444c15a5 629
45f4b842
HJYP
630 gtk_window_present(GTK_WINDOW(dlg));
631 lb->config_dlg = dlg;
632
633 /* Establish a callback when the dialog completes. */
9c218f1d 634 g_object_weak_ref(G_OBJECT(dlg), (GWeakNotify) panel_config_save, panel);
2918994e 635
636 /* Initialize the tree view contents. */
9c218f1d
AG
637 launchbar_configure_initialize_list(lb, dlg, GTK_TREE_VIEW(defined_view));
638 g_object_set_data(G_OBJECT(dlg), "menu_view", menu_view);
2918994e 639
ce306a98 640 gtk_widget_set_visible(lb->p_label_menu_app_exec, FALSE);
98cbcd78 641 gtk_widget_set_visible(lb->p_label_def_app_exec, FALSE);
9c218f1d
AG
642 gtk_widget_set_sensitive(lb->p_button_add, FALSE);
643 gtk_widget_set_sensitive(lb->p_button_remove, FALSE);
ce306a98 644
45f4b842
HJYP
645 g_object_unref(builder);
646 return;
2918994e 647 }
648}
930af9fd 649
2918994e 650/* Callback when panel configuration changes. */
9c218f1d 651static void launchbar_panel_configuration_changed(Panel *panel, GtkWidget *p)
2918994e 652{
653 /* Set orientation into the icon grid. */
9c218f1d 654 LaunchbarPlugin * lb = lxpanel_plugin_get_data(p);
cfde283a
AG
655 icon_grid_set_geometry(lb->icon_grid, panel_get_orientation(panel),
656 panel_get_icon_size(panel), panel_get_icon_size(panel),
657 3, 0, panel_get_height(panel));
8f9e6256 658
659 /* Reset all the images to resize them. */
660 GSList * l;
661 for (l = lb->buttons; l != NULL; l = l->next)
662 {
663 LaunchButton * btn = (LaunchButton *) l->data;
7969be34
AG
664 lxpanel_button_update_icon(btn->widget, fm_file_info_get_icon(btn->fi),
665 panel_get_icon_size(panel));
8f9e6256 666 }
667
668 /* Reset the bootstrap button. */
669 if (lb->bootstrap_button != NULL)
9c218f1d
AG
670 {
671 GdkPixbuf * icon = fm_pixbuf_from_icon(lb->add_icon, panel_get_icon_size(panel));
672 gtk_image_set_from_pixbuf(GTK_IMAGE(lb->bootstrap_button->image_widget), icon);
673 g_object_unref(icon);
674 }
2918994e 675}
676
677/* Plugin descriptor. */
9c218f1d 678LXPanelPluginInit lxpanel_static_plugin_launchbar = {
3c3e9c9e 679 .name = N_("Application Launch Bar"),
3c3e9c9e 680 .description = N_("Bar with buttons to launch application"),
a52c2257 681
9c218f1d 682 .new_instance = launchbar_constructor,
3c3e9c9e 683 .config = launchbar_configure,
9c218f1d 684 .reconfigure = launchbar_panel_configuration_changed
a52c2257 685};