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