1 /*
2 * Copyright (C) 2008-2010 Nick Schermer <nick@xfce.org>
3 *
4 * This library is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This library is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #ifdef HAVE_STRING_H
24 #include <string.h>
25 #endif
26
27 #include <gio/gio.h>
28 #include <libxfce4util/libxfce4util.h>
29 #include <libxfce4ui/libxfce4ui.h>
30 #include <garcon/garcon.h>
31 #include <garcon-gtk/garcon-gtk.h>
32 #include <xfconf/xfconf.h>
33
34 #include <libxfce4panel/libxfce4panel.h>
35 #include <common/panel-private.h>
36 #include <common/panel-xfconf.h>
37 #include <common/panel-utils.h>
38
39 #include "launcher.h"
40 #include "launcher-dialog.h"
41
42 #define ARROW_BUTTON_SIZE (12)
43 #define MENU_POPUP_DELAY (225)
44 #define NO_ARROW_INSIDE_BUTTON(plugin) ((plugin)->arrow_position != LAUNCHER_ARROW_INTERNAL \
45 || LIST_HAS_ONE_OR_NO_ENTRIES ((plugin)->items))
46 #define ARROW_INSIDE_BUTTON(plugin) (!NO_ARROW_INSIDE_BUTTON (plugin))
47 #define RELATIVE_CONFIG_PATH PANEL_PLUGIN_RELATIVE_PATH G_DIR_SEPARATOR_S "%s-%d"
48
49
50
51 static void launcher_plugin_get_property (GObject *object,
52 guint prop_id,
53 GValue *value,
54 GParamSpec *pspec);
55 static void launcher_plugin_set_property (GObject *object,
56 guint prop_id,
57 const GValue *value,
58 GParamSpec *pspec);
59 static void launcher_plugin_construct (XfcePanelPlugin *panel_plugin);
60 static void launcher_plugin_free_data (XfcePanelPlugin *panel_plugin);
61 static void launcher_plugin_removed (XfcePanelPlugin *panel_plugin);
62 static gboolean launcher_plugin_remote_event (XfcePanelPlugin *panel_plugin,
63 const gchar *name,
64 const GValue *value);
65 static gboolean launcher_plugin_save_delayed_timeout (gpointer user_data);
66 static void launcher_plugin_save_delayed (LauncherPlugin *plugin);
67 static void launcher_plugin_mode_changed (XfcePanelPlugin *panel_plugin,
68 XfcePanelPluginMode mode);
69 static gboolean launcher_plugin_size_changed (XfcePanelPlugin *panel_plugin,
70 gint size);
71 static void launcher_plugin_configure_plugin (XfcePanelPlugin *panel_plugin);
72 static void launcher_plugin_screen_position_changed (XfcePanelPlugin *panel_plugin,
73 XfceScreenPosition position);
74 static void launcher_plugin_icon_theme_changed (GtkIconTheme *icon_theme,
75 LauncherPlugin *plugin);
76 static LauncherArrowType launcher_plugin_default_arrow_type (LauncherPlugin *plugin);
77 static void launcher_plugin_pack_widgets (LauncherPlugin *plugin);
78 static GdkPixbuf *launcher_plugin_tooltip_pixbuf (GdkScreen *screen,
79 const gchar *icon_name);
80 static void launcher_plugin_menu_deactivate (GtkWidget *menu,
81 LauncherPlugin *plugin);
82 static void launcher_plugin_menu_item_activate (GtkMenuItem *widget,
83 GarconMenuItem *item);
84 static void launcher_plugin_menu_item_drag_data_received (GtkWidget *widget,
85 GdkDragContext *context,
86 gint x,
87 gint y,
88 GtkSelectionData *data,
89 guint info,
90 guint drag_time,
91 GarconMenuItem *item);
92 static void launcher_plugin_menu_construct (LauncherPlugin *plugin);
93 static void launcher_plugin_menu_popup_destroyed (gpointer user_data);
94 static gboolean launcher_plugin_menu_popup (gpointer user_data);
95 static void launcher_plugin_menu_destroy (LauncherPlugin *plugin);
96 static void launcher_plugin_button_update (LauncherPlugin *plugin);
97 #if GARCON_CHECK_VERSION(0,7,0)
98 static void launcher_plugin_button_update_action_menu (LauncherPlugin *plugin);
99 #endif
100 static void launcher_plugin_button_state_changed (GtkWidget *button_a,
101 GtkStateType state,
102 GtkWidget *button_b);
103 static gboolean launcher_plugin_button_press_event (GtkWidget *button,
104 GdkEventButton *event,
105 LauncherPlugin *plugin);
106 static gboolean launcher_plugin_button_release_event (GtkWidget *button,
107 GdkEventButton *event,
108 LauncherPlugin *plugin);
109 static gboolean launcher_plugin_button_query_tooltip (GtkWidget *widget,
110 gint x,
111 gint y,
112 gboolean keyboard_mode,
113 GtkTooltip *tooltip,
114 LauncherPlugin *plugin);
115 static void launcher_plugin_button_drag_data_received (GtkWidget *widget,
116 GdkDragContext *context,
117 gint x,
118 gint y,
119 GtkSelectionData *selection_data,
120 guint info,
121 guint drag_time,
122 LauncherPlugin *plugin);
123 static gboolean launcher_plugin_button_drag_motion (GtkWidget *widget,
124 GdkDragContext *context,
125 gint x,
126 gint y,
127 guint drag_time,
128 LauncherPlugin *plugin);
129 static gboolean launcher_plugin_button_drag_drop (GtkWidget *widget,
130 GdkDragContext *context,
131 gint x,
132 gint y,
133 guint drag_time,
134 LauncherPlugin *plugin);
135 static void launcher_plugin_button_drag_leave (GtkWidget *widget,
136 GdkDragContext *context,
137 guint drag_time,
138 LauncherPlugin *plugin);
139 static gboolean launcher_plugin_button_draw (GtkWidget *widget,
140 cairo_t *cr,
141 LauncherPlugin *plugin);
142 static void launcher_plugin_arrow_visibility (LauncherPlugin *plugin);
143 static gboolean launcher_plugin_arrow_press_event (GtkWidget *button,
144 GdkEventButton *event,
145 LauncherPlugin *plugin);
146 static gboolean launcher_plugin_arrow_drag_motion (GtkWidget *widget,
147 GdkDragContext *context,
148 gint x,
149 gint y,
150 guint drag_time,
151 LauncherPlugin *plugin);
152 static void launcher_plugin_arrow_drag_leave (GtkWidget *widget,
153 GdkDragContext *context,
154 guint drag_time,
155 LauncherPlugin *plugin);
156 static gboolean launcher_plugin_item_query_tooltip (GtkWidget *widget,
157 gint x,
158 gint y,
159 gboolean keyboard_mode,
160 GtkTooltip *tooltip,
161 GarconMenuItem *item);
162 static gboolean launcher_plugin_item_exec_on_screen (GarconMenuItem *item,
163 guint32 event_time,
164 GdkScreen *screen,
165 GSList *uri_list);
166 static void launcher_plugin_item_exec (GarconMenuItem *item,
167 guint32 event_time,
168 GdkScreen *screen,
169 GSList *uri_list);
170 static void launcher_plugin_item_exec_from_clipboard (GarconMenuItem *item,
171 guint32 event_time,
172 GdkScreen *screen);
173 static GSList *launcher_plugin_uri_list_extract (GtkSelectionData *data);
174 static void launcher_plugin_uri_list_free (GSList *uri_list);
175
176
177
178 struct _LauncherPluginClass
179 {
180 XfcePanelPluginClass __parent__;
181 };
182
183 struct _LauncherPlugin
184 {
185 XfcePanelPlugin __parent__;
186
187 GtkWidget *box;
188 GtkWidget *button;
189 GtkWidget *arrow;
190 GtkWidget *child;
191 GtkWidget *menu;
192 #if GARCON_CHECK_VERSION(0,7,0)
193 GtkWidget *action_menu;
194 #endif
195
196 GSList *items;
197
198 GdkPixbuf *pixbuf;
199 gchar *icon_name;
200 GdkPixbuf *tooltip_cache;
201
202 gulong theme_change_id;
203
204 guint menu_timeout_id;
205
206 guint disable_tooltips : 1;
207 guint move_first : 1;
208 guint show_label : 1;
209 LauncherArrowType arrow_position;
210
211 GFile *config_directory;
212 GFileMonitor *config_monitor;
213
214 guint save_timeout_id;
215 };
216
217 enum
218 {
219 PROP_0,
220 PROP_ITEMS,
221 PROP_DISABLE_TOOLTIPS,
222 PROP_MOVE_FIRST,
223 PROP_SHOW_LABEL,
224 PROP_ARROW_POSITION
225 };
226
227 enum
228 {
229 ITEMS_CHANGED,
230 LAST_SIGNAL
231 };
232
233
234 /* define the plugin */
235 XFCE_PANEL_DEFINE_PLUGIN_RESIDENT (LauncherPlugin, launcher_plugin)
236
237
238
239 /* quark to attach the plugin to menu items */
240 static GQuark launcher_plugin_quark = 0;
241 static guint launcher_signals[LAST_SIGNAL];
242
243
244
245 /* target types for dropping in the launcher plugin */
246 static const GtkTargetEntry drop_targets[] =
247 {
248 { "text/uri-list", 0, 0, },
249 { "STRING", 0, 0 },
250 { "UTF8_STRING", 0, 0 },
251 { "text/plain", 0, 0 },
252 };
253
254
255
256 static void
launcher_plugin_class_init(LauncherPluginClass * klass)257 launcher_plugin_class_init (LauncherPluginClass *klass)
258 {
259 GObjectClass *gobject_class;
260 XfcePanelPluginClass *plugin_class;
261
262 gobject_class = G_OBJECT_CLASS (klass);
263 gobject_class->get_property = launcher_plugin_get_property;
264 gobject_class->set_property = launcher_plugin_set_property;
265
266 plugin_class = XFCE_PANEL_PLUGIN_CLASS (klass);
267 plugin_class->construct = launcher_plugin_construct;
268 plugin_class->free_data = launcher_plugin_free_data;
269 plugin_class->mode_changed = launcher_plugin_mode_changed;
270 plugin_class->size_changed = launcher_plugin_size_changed;
271 plugin_class->configure_plugin = launcher_plugin_configure_plugin;
272 plugin_class->screen_position_changed = launcher_plugin_screen_position_changed;
273 plugin_class->removed = launcher_plugin_removed;
274 plugin_class->remote_event = launcher_plugin_remote_event;
275
276 g_object_class_install_property (gobject_class,
277 PROP_ITEMS,
278 g_param_spec_boxed ("items",
279 NULL, NULL,
280 G_TYPE_PTR_ARRAY,
281 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
282
283 g_object_class_install_property (gobject_class,
284 PROP_DISABLE_TOOLTIPS,
285 g_param_spec_boolean ("disable-tooltips",
286 NULL, NULL,
287 FALSE,
288 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
289
290 g_object_class_install_property (gobject_class,
291 PROP_MOVE_FIRST,
292 g_param_spec_boolean ("move-first",
293 NULL, NULL,
294 FALSE,
295 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
296
297 g_object_class_install_property (gobject_class,
298 PROP_SHOW_LABEL,
299 g_param_spec_boolean ("show-label",
300 NULL, NULL,
301 FALSE,
302 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
303
304 g_object_class_install_property (gobject_class,
305 PROP_ARROW_POSITION,
306 g_param_spec_uint ("arrow-position",
307 NULL, NULL,
308 LAUNCHER_ARROW_DEFAULT,
309 LAUNCHER_ARROW_INTERNAL,
310 LAUNCHER_ARROW_DEFAULT,
311 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
312
313 launcher_signals[ITEMS_CHANGED] =
314 g_signal_new (g_intern_static_string ("items-changed"),
315 G_TYPE_FROM_CLASS (klass),
316 G_SIGNAL_RUN_FIRST,
317 0, NULL, NULL,
318 g_cclosure_marshal_VOID__VOID,
319 G_TYPE_NONE, 0);
320
321 /* initialize the quark */
322 launcher_plugin_quark = g_quark_from_static_string ("xfce-launcher-plugin");
323 }
324
325
326
327 static void
launcher_plugin_init(LauncherPlugin * plugin)328 launcher_plugin_init (LauncherPlugin *plugin)
329 {
330 GtkIconTheme *icon_theme;
331 GtkCssProvider *css_provider;
332 GtkStyleContext *context;
333 gchar *css_string;
334
335 plugin->disable_tooltips = FALSE;
336 plugin->move_first = FALSE;
337 plugin->show_label = FALSE;
338 plugin->arrow_position = LAUNCHER_ARROW_DEFAULT;
339 plugin->menu = NULL;
340 #if GARCON_CHECK_VERSION(0,7,0)
341 plugin->action_menu = NULL;
342 #endif
343 plugin->items = NULL;
344 plugin->child = NULL;
345 plugin->tooltip_cache = NULL;
346 plugin->pixbuf = NULL;
347 plugin->icon_name = NULL;
348 plugin->menu_timeout_id = 0;
349 plugin->save_timeout_id = 0;
350
351 /* monitor the default icon theme for changes */
352 icon_theme = gtk_icon_theme_get_default ();
353 plugin->theme_change_id = g_signal_connect (G_OBJECT (icon_theme), "changed",
354 G_CALLBACK (launcher_plugin_icon_theme_changed), plugin);
355
356 /* create the panel widgets */
357 plugin->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
358 gtk_container_add (GTK_CONTAINER (plugin), plugin->box);
359
360 plugin->button = xfce_panel_create_button ();
361 gtk_box_pack_start (GTK_BOX (plugin->box), plugin->button, TRUE, TRUE, 0);
362 xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), plugin->button);
363 gtk_widget_set_has_tooltip (plugin->button, TRUE);
364 gtk_widget_set_name (plugin->button, "launcher-button");
365 g_signal_connect (G_OBJECT (plugin->button), "button-press-event",
366 G_CALLBACK (launcher_plugin_button_press_event), plugin);
367 g_signal_connect (G_OBJECT (plugin->button), "button-release-event",
368 G_CALLBACK (launcher_plugin_button_release_event), plugin);
369 g_signal_connect (G_OBJECT (plugin->button), "query-tooltip",
370 G_CALLBACK (launcher_plugin_button_query_tooltip), plugin);
371 g_signal_connect (G_OBJECT (plugin->button), "drag-data-received",
372 G_CALLBACK (launcher_plugin_button_drag_data_received), plugin);
373 g_signal_connect (G_OBJECT (plugin->button), "drag-motion",
374 G_CALLBACK (launcher_plugin_button_drag_motion), plugin);
375 g_signal_connect (G_OBJECT (plugin->button), "drag-drop",
376 G_CALLBACK (launcher_plugin_button_drag_drop), plugin);
377 g_signal_connect (G_OBJECT (plugin->button), "drag-leave",
378 G_CALLBACK (launcher_plugin_button_drag_leave), plugin);
379 g_signal_connect_after (G_OBJECT (plugin->button), "draw",
380 G_CALLBACK (launcher_plugin_button_draw), plugin);
381
382 /* Make sure there aren't any constraints set on buttons by themes (Adwaita sets those minimum sizes) */
383 context = gtk_widget_get_style_context (plugin->button);
384 css_provider = gtk_css_provider_new ();
385 css_string = g_strdup_printf ("#launcher-arrow { min-height: 0; min-width: 0; }");
386 gtk_css_provider_load_from_data (css_provider, css_string, -1, NULL);
387 gtk_style_context_add_provider (context,
388 GTK_STYLE_PROVIDER (css_provider),
389 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
390 g_free (css_string);
391
392 plugin->child = gtk_image_new ();
393 gtk_container_add (GTK_CONTAINER (plugin->button), plugin->child);
394
395 plugin->arrow = xfce_arrow_button_new (GTK_ARROW_UP);
396 gtk_box_pack_start (GTK_BOX (plugin->box), plugin->arrow, FALSE, FALSE, 0);
397 xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), plugin->arrow);
398 gtk_button_set_relief (GTK_BUTTON (plugin->arrow), GTK_RELIEF_NONE);
399 gtk_widget_set_name (plugin->button, "launcher-arrow");
400 g_signal_connect (G_OBJECT (plugin->arrow), "button-press-event",
401 G_CALLBACK (launcher_plugin_arrow_press_event), plugin);
402 g_signal_connect (G_OBJECT (plugin->arrow), "drag-motion",
403 G_CALLBACK (launcher_plugin_arrow_drag_motion), plugin);
404 g_signal_connect (G_OBJECT (plugin->button), "drag-drop",
405 G_CALLBACK (launcher_plugin_button_drag_drop), plugin);
406 g_signal_connect (G_OBJECT (plugin->arrow), "drag-leave",
407 G_CALLBACK (launcher_plugin_arrow_drag_leave), plugin);
408
409 panel_utils_set_atk_info (plugin->arrow, _("Open launcher menu"), NULL);
410
411 /* accept all sorts of drag data, but filter in drag-drop, so we can
412 * send other sorts of drops to parent widgets */
413 gtk_drag_dest_set (plugin->button, 0, NULL, 0, 0);
414 gtk_drag_dest_set (plugin->arrow, 0, NULL, 0, 0);
415
416 /* sync button states */
417 g_signal_connect (G_OBJECT (plugin->button), "state-changed",
418 G_CALLBACK (launcher_plugin_button_state_changed), plugin->arrow);
419 g_signal_connect (G_OBJECT (plugin->arrow), "state-changed",
420 G_CALLBACK (launcher_plugin_button_state_changed), plugin->button);
421 }
422
423
424
425 static void
launcher_free_array_element(gpointer data)426 launcher_free_array_element (gpointer data)
427 {
428 GValue *value = (GValue *)data;
429
430 g_value_unset (value);
431 g_free (value);
432 }
433
434
435
436 static void
launcher_plugin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)437 launcher_plugin_get_property (GObject *object,
438 guint prop_id,
439 GValue *value,
440 GParamSpec *pspec)
441 {
442 LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (object);
443 GPtrArray *array;
444 GValue *tmp;
445 GSList *li;
446 GFile *item_file;
447
448 switch (prop_id)
449 {
450 case PROP_ITEMS:
451 array = g_ptr_array_new_full (1, (GDestroyNotify) launcher_free_array_element);
452 for (li = plugin->items; li != NULL; li = li->next)
453 {
454 tmp = g_new0 (GValue, 1);
455 g_value_init (tmp, G_TYPE_STRING);
456 panel_return_if_fail (GARCON_IS_MENU_ITEM (li->data));
457 item_file = garcon_menu_item_get_file (li->data);
458 if (g_file_has_prefix (item_file, plugin->config_directory))
459 g_value_take_string (tmp, g_file_get_basename (item_file));
460 else
461 g_value_take_string (tmp, g_file_get_uri (item_file));
462 g_object_unref (G_OBJECT (item_file));
463 g_ptr_array_add (array, tmp);
464 }
465 g_value_set_boxed (value, array);
466 g_ptr_array_unref (array);
467 break;
468
469 case PROP_DISABLE_TOOLTIPS:
470 g_value_set_boolean (value, plugin->disable_tooltips);
471 break;
472
473 case PROP_MOVE_FIRST:
474 g_value_set_boolean (value, plugin->move_first);
475 break;
476
477 case PROP_SHOW_LABEL:
478 g_value_set_boolean (value, plugin->show_label);
479 break;
480
481 case PROP_ARROW_POSITION:
482 g_value_set_uint (value, plugin->arrow_position);
483 break;
484
485 default:
486 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
487 break;
488 }
489 }
490
491
492
493 static void
launcher_plugin_item_changed(GarconMenuItem * item,LauncherPlugin * plugin)494 launcher_plugin_item_changed (GarconMenuItem *item,
495 LauncherPlugin *plugin)
496 {
497 GSList *li;
498
499 panel_return_if_fail (GARCON_IS_MENU_ITEM (item));
500 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
501
502 /* find the item */
503 li = g_slist_find (plugin->items, item);
504 if (G_LIKELY (li != NULL))
505 {
506 /* update the button or destroy the menu */
507 if (plugin->items == li)
508 {
509 launcher_plugin_button_update (plugin);
510 #if GARCON_CHECK_VERSION(0,7,0)
511 launcher_plugin_button_update_action_menu (plugin);
512 #endif
513 }
514 else
515 launcher_plugin_menu_destroy (plugin);
516 }
517 else
518 {
519 panel_assert_not_reached ();
520 }
521 }
522
523
524
525 static gboolean
launcher_plugin_item_duplicate(GFile * src_file,GFile * dst_file,GError ** error)526 launcher_plugin_item_duplicate (GFile *src_file,
527 GFile *dst_file,
528 GError **error)
529 {
530 GKeyFile *key_file;
531 gchar *contents = NULL;
532 gsize length;
533 gboolean result = FALSE;
534 gchar *uri;
535
536 panel_return_val_if_fail (G_IS_FILE (src_file), FALSE);
537 panel_return_val_if_fail (G_IS_FILE (dst_file), FALSE);
538 panel_return_val_if_fail (error == NULL || *error == NULL, FALSE);
539
540 if (!g_file_load_contents (src_file, NULL, &contents, &length, NULL, error))
541 return FALSE;
542
543 /* note that we don't load the key file with preserving the translations
544 * and comments, this way we save a small desktop file in the user's language */
545 key_file = g_key_file_new ();
546 if (!g_key_file_load_from_data (key_file, contents, length, 0, error))
547 goto err1;
548
549 /* store the source uri in the desktop file for restore purposes */
550 uri = g_file_get_uri (src_file);
551 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-XFCE-Source", uri);
552 g_free (uri);
553
554 contents = g_key_file_to_data (key_file, &length, error);
555 if (contents == NULL)
556 goto err1;
557
558 result = g_file_replace_contents (dst_file, contents, length, NULL, FALSE,
559 G_FILE_CREATE_REPLACE_DESTINATION,
560 NULL, NULL, error);
561
562 err1:
563 g_free (contents);
564 g_key_file_free (key_file);
565
566 return result;
567 }
568
569
570 static gboolean
_exo_str_looks_like_an_uri(const gchar * str)571 _exo_str_looks_like_an_uri (const gchar *str)
572 {
573 const gchar *s = str;
574
575 if (G_UNLIKELY (str == NULL))
576 return FALSE;
577
578 /* <scheme> starts with an alpha character */
579 if (g_ascii_isalpha (*s))
580 {
581 /* <scheme> continues with (alpha | digit | "+" | "-" | ".")* */
582 for (++s; g_ascii_isalnum (*s) || *s == '+' || *s == '-' || *s == '.'; ++s);
583
584 /* <scheme> must be followed by ":" */
585 return (*s == ':' && *(s+1) == '/');
586 }
587
588 return FALSE;
589 }
590
591
592 static GarconMenuItem *
launcher_plugin_item_load(LauncherPlugin * plugin,const gchar * str,gboolean * desktop_id_return,gboolean * location_changed)593 launcher_plugin_item_load (LauncherPlugin *plugin,
594 const gchar *str,
595 gboolean *desktop_id_return,
596 gboolean *location_changed)
597 {
598 GFile *src_file, *dst_file;
599 gchar *src_path, *dst_path;
600 GSList *li, *lnext;
601 GarconMenuItem *item = NULL;
602 GError *error = NULL;
603
604 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), NULL);
605 panel_return_val_if_fail (str != NULL, NULL);
606 panel_return_val_if_fail (G_IS_FILE (plugin->config_directory), NULL);
607
608 if (G_UNLIKELY (g_path_is_absolute (str) || _exo_str_looks_like_an_uri (str)))
609 {
610 src_file = g_file_new_for_commandline_arg (str);
611 if (g_file_has_prefix (src_file, plugin->config_directory))
612 {
613 /* nothing, we use the file below */
614 }
615 else if (g_file_query_exists (src_file, NULL))
616 {
617 /* create a unique file in the config directory */
618 dst_path = launcher_plugin_unique_filename (plugin);
619 dst_file = g_file_new_for_path (dst_path);
620
621 /* create a duplicate in the config directory */
622 if (launcher_plugin_item_duplicate (src_file, dst_file, &error))
623 {
624 /* use the new file */
625 g_object_unref (G_OBJECT (src_file));
626 src_file = dst_file;
627
628 if (G_LIKELY (location_changed != NULL))
629 *location_changed = TRUE;
630 }
631 else
632 {
633 src_path = g_file_get_parse_name (src_file);
634 g_warning ("Failed to create duplicate of desktop file \"%s\" "
635 "to \"%s\": %s", src_path, dst_path, error->message);
636 g_error_free (error);
637 g_free (src_path);
638
639 /* continue using the source file, the user won't be able to
640 * edit the item, but atleast we have something that works in
641 * the panel */
642 g_object_unref (G_OBJECT (dst_file));
643 }
644
645 g_free (dst_path);
646 }
647 else
648 {
649 /* nothing we can do with this file */
650 src_path = g_file_get_parse_name (src_file);
651 g_warning ("Failed to load desktop file \"%s\". It will be removed "
652 "from the configuration", src_path);
653 g_free (src_path);
654 g_object_unref (G_OBJECT (src_file));
655
656 return NULL;
657 }
658 }
659 else
660 {
661 /* assume the file is a child in the config directory */
662 src_file = g_file_get_child (plugin->config_directory, str);
663
664 /* str might also be a global desktop id */
665 if (G_LIKELY (desktop_id_return != NULL))
666 *desktop_id_return = TRUE;
667 }
668
669 panel_assert (G_IS_FILE (src_file));
670
671 /* maybe we have this file in the launcher configuration, then we don't
672 * have to load it again from the harddisk */
673 for (li = plugin->items; item == NULL && li != NULL; li = lnext)
674 {
675 lnext = li->next;
676 dst_file = garcon_menu_item_get_file (GARCON_MENU_ITEM (li->data));
677 if (g_file_equal (src_file, dst_file))
678 {
679 item = GARCON_MENU_ITEM (li->data);
680 plugin->items = g_slist_delete_link (plugin->items, li);
681 }
682 g_object_unref (G_OBJECT (dst_file));
683 }
684
685 /* load the file from the disk */
686 if (item == NULL)
687 item = garcon_menu_item_new (src_file);
688
689 g_object_unref (G_OBJECT (src_file));
690
691 return item;
692 }
693
694
695
696 static void
launcher_plugin_items_delete_configs(LauncherPlugin * plugin)697 launcher_plugin_items_delete_configs (LauncherPlugin *plugin)
698 {
699 GSList *li;
700 GFile *file;
701 gboolean succeed = TRUE;
702 GError *error = NULL;
703
704 panel_return_if_fail (G_IS_FILE (plugin->config_directory));
705
706 /* cleanup desktop files in the config dir */
707 for (li = plugin->items; succeed && li != NULL; li = li->next)
708 {
709 file = garcon_menu_item_get_file (li->data);
710 if (g_file_has_prefix (file, plugin->config_directory))
711 succeed = g_file_delete (file, NULL, &error);
712 g_object_unref (G_OBJECT (file));
713 }
714
715 if (!succeed)
716 {
717 g_message ("launcher-%d: Failed to cleanup the configuration: %s",
718 xfce_panel_plugin_get_unique_id (XFCE_PANEL_PLUGIN (plugin)),
719 error->message);
720 g_error_free (error);
721 }
722 }
723
724
725
726 static void
launcher_plugin_items_free(LauncherPlugin * plugin)727 launcher_plugin_items_free (LauncherPlugin *plugin)
728 {
729 if (G_LIKELY (plugin->items != NULL))
730 {
731 g_slist_foreach (plugin->items, (GFunc) (void (*)(void)) g_object_unref, NULL);
732 g_slist_free (plugin->items);
733 plugin->items = NULL;
734 }
735 }
736
737
738
739 static void
launcher_plugin_items_load(LauncherPlugin * plugin,GPtrArray * array)740 launcher_plugin_items_load (LauncherPlugin *plugin,
741 GPtrArray *array)
742 {
743 guint i;
744 const GValue *value;
745 const gchar *str;
746 GarconMenuItem *item;
747 GarconMenuItem *pool_item;
748 GSList *items = NULL;
749 GHashTable *pool = NULL;
750 gboolean desktop_id;
751 gchar *uri;
752 gboolean items_modified = FALSE;
753 gboolean location_changed;
754
755 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
756 panel_return_if_fail (array != NULL);
757
758 for (i = 0; i < array->len; i++)
759 {
760 value = g_ptr_array_index (array, i);
761 panel_assert (G_VALUE_HOLDS_STRING (value));
762 str = g_value_get_string (value);
763
764 /* only accept desktop files */
765 if (str == NULL || !g_str_has_suffix (str, ".desktop"))
766 continue;
767
768 /* try to load the item */
769 desktop_id = FALSE;
770 location_changed = FALSE;
771 item = launcher_plugin_item_load (plugin, str, &desktop_id, &location_changed);
772 if (G_LIKELY (item == NULL))
773 {
774 /* str did not look like a desktop-id, so no need to look
775 * for it in the application pool */
776 if (!desktop_id)
777 continue;
778
779 /* we are going to load an desktop_id from the item pool,
780 * even if this failes, save the new item list, so we don't
781 * try this again in the future */
782 items_modified = TRUE;
783
784 /* load the pool with desktop items */
785 if (pool == NULL)
786 pool = launcher_plugin_garcon_menu_pool ();
787
788 /* lookup the item in the item pool */
789 pool_item = g_hash_table_lookup (pool, str);
790 if (pool_item != NULL)
791 {
792 /* we want an editable file, so try to make a copy */
793 uri = garcon_menu_item_get_uri (pool_item);
794 item = launcher_plugin_item_load (plugin, uri, NULL, NULL);
795 g_free (uri);
796
797 /* if something failed, use the pool item, but this one
798 * won't be editable in the dialog */
799 if (G_UNLIKELY (item == NULL))
800 item = GARCON_MENU_ITEM (g_object_ref (G_OBJECT (pool_item)));
801 }
802
803 /* skip this item if still not found */
804 if (item == NULL)
805 continue;
806 }
807 else if (location_changed)
808 {
809 items_modified = TRUE;
810 }
811
812 /* add the item to the list */
813 panel_assert (GARCON_IS_MENU_ITEM (item));
814 items = g_slist_append (items, item);
815 g_signal_connect (G_OBJECT (item), "changed",
816 G_CALLBACK (launcher_plugin_item_changed), plugin);
817 }
818
819 if (G_UNLIKELY (pool != NULL))
820 g_hash_table_destroy (pool);
821
822 /* remove config files of items not in the new config */
823 launcher_plugin_items_delete_configs (plugin);
824
825 /* release the old menu items and set new one */
826 launcher_plugin_items_free (plugin);
827 plugin->items = items;
828
829 /* store the new item list */
830 if (items_modified)
831 launcher_plugin_save_delayed (plugin);
832 }
833
834
835
836 static void
launcher_plugin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)837 launcher_plugin_set_property (GObject *object,
838 guint prop_id,
839 const GValue *value,
840 GParamSpec *pspec)
841 {
842 LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (object);
843 GPtrArray *array;
844
845 panel_return_if_fail (G_IS_FILE (plugin->config_directory));
846
847 /* destroy the menu, all the setting changes need this */
848 launcher_plugin_menu_destroy (plugin);
849
850 switch (prop_id)
851 {
852 case PROP_ITEMS:
853 /* load new items from the array */
854 array = g_value_get_boxed (value);
855 if (G_LIKELY (array != NULL))
856 {
857 launcher_plugin_items_load (plugin, array);
858 }
859 else
860 {
861 launcher_plugin_items_delete_configs (plugin);
862 launcher_plugin_items_free (plugin);
863 }
864
865 /* emit signal */
866 g_signal_emit (G_OBJECT (plugin), launcher_signals[ITEMS_CHANGED], 0);
867
868 /* update the button */
869 launcher_plugin_button_update (plugin);
870 #if GARCON_CHECK_VERSION(0,7,0)
871 launcher_plugin_button_update_action_menu (plugin);
872 #endif
873
874 /* update the widget packing */
875 goto update_arrow;
876 break;
877
878 case PROP_DISABLE_TOOLTIPS:
879 plugin->disable_tooltips = g_value_get_boolean (value);
880 gtk_widget_set_has_tooltip (plugin->button, !plugin->disable_tooltips);
881 break;
882
883 case PROP_MOVE_FIRST:
884 plugin->move_first = g_value_get_boolean (value);
885 break;
886
887 case PROP_SHOW_LABEL:
888 plugin->show_label = g_value_get_boolean (value);
889
890 /* destroy the old child */
891 if (plugin->child != NULL)
892 gtk_widget_destroy (plugin->child);
893
894 /* create child */
895 if (G_UNLIKELY (plugin->show_label))
896 plugin->child = gtk_label_new (NULL);
897 else
898 plugin->child = gtk_image_new ();
899 gtk_container_add (GTK_CONTAINER (plugin->button), plugin->child);
900 gtk_widget_show (plugin->child);
901
902 /* update size */
903 launcher_plugin_size_changed (XFCE_PANEL_PLUGIN (plugin),
904 xfce_panel_plugin_get_size (XFCE_PANEL_PLUGIN (plugin)));
905
906 /* update the button */
907 launcher_plugin_button_update (plugin);
908 break;
909
910 case PROP_ARROW_POSITION:
911 plugin->arrow_position = g_value_get_uint (value);
912
913 update_arrow:
914 /* update the arrow button visibility */
915 launcher_plugin_arrow_visibility (plugin);
916
917 /* repack the widgets */
918 launcher_plugin_pack_widgets (plugin);
919
920 /* update the plugin size */
921 launcher_plugin_size_changed (XFCE_PANEL_PLUGIN (plugin),
922 xfce_panel_plugin_get_size (XFCE_PANEL_PLUGIN (plugin)));
923 break;
924
925 default:
926 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
927 break;
928 }
929 }
930
931
932
933 static void
launcher_plugin_file_changed(GFileMonitor * monitor,GFile * changed_file,GFile * other_file,GFileMonitorEvent event_type,LauncherPlugin * plugin)934 launcher_plugin_file_changed (GFileMonitor *monitor,
935 GFile *changed_file,
936 GFile *other_file,
937 GFileMonitorEvent event_type,
938 LauncherPlugin *plugin)
939 {
940 GSList *li, *lnext;
941 GarconMenuItem *item;
942 GFile *item_file;
943 gboolean found;
944 GError *error = NULL;
945 gchar *base_name;
946 gboolean result;
947 gboolean exists;
948 gboolean update_plugin = FALSE;
949
950 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
951 panel_return_if_fail (plugin->config_monitor == monitor);
952
953 /* waited until all events are proccessed */
954 if (event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
955 && event_type != G_FILE_MONITOR_EVENT_DELETED
956 && event_type != G_FILE_MONITOR_EVENT_CREATED)
957 return;
958
959 /* we only act on desktop files */
960 base_name = g_file_get_basename (changed_file);
961 result = g_str_has_suffix (base_name, ".desktop");
962 g_free (base_name);
963 if (!result)
964 return;
965
966 exists = g_file_query_exists (changed_file, NULL);
967
968 /* lookup the file in the menu items */
969 for (li = plugin->items, found = FALSE; !found && li != NULL; li = lnext)
970 {
971 lnext = li->next;
972 item = GARCON_MENU_ITEM (li->data);
973 item_file = garcon_menu_item_get_file (item);
974 found = g_file_equal (changed_file, item_file);
975 if (found)
976 {
977 if (exists)
978 {
979 /* reload the file */
980 if (!garcon_menu_item_reload (item, NULL, &error))
981 {
982 g_critical ("Failed to reload menu item: %s", error->message);
983 g_error_free (error);
984 }
985 }
986 else
987 {
988 /* remove from the list */
989 plugin->items = g_slist_delete_link (plugin->items, li);
990 g_object_unref (G_OBJECT (item));
991 update_plugin = TRUE;
992 }
993 }
994 g_object_unref (G_OBJECT (item_file));
995 }
996
997 if (!found && exists)
998 {
999 /* add the new file to the config */
1000 item = garcon_menu_item_new (changed_file);
1001 if (G_LIKELY (item != NULL))
1002 {
1003 plugin->items = g_slist_append (plugin->items, item);
1004 g_signal_connect (G_OBJECT (item), "changed",
1005 G_CALLBACK (launcher_plugin_item_changed), plugin);
1006 update_plugin = TRUE;
1007 }
1008 }
1009
1010 if (update_plugin)
1011 {
1012 launcher_plugin_button_update (plugin);
1013 launcher_plugin_menu_destroy (plugin);
1014 #if GARCON_CHECK_VERSION(0,7,0)
1015 launcher_plugin_button_update_action_menu (plugin);
1016 #endif
1017
1018 /* save the new config */
1019 launcher_plugin_save_delayed (plugin);
1020
1021 /* update the dialog */
1022 g_signal_emit (G_OBJECT (plugin), launcher_signals[ITEMS_CHANGED], 0);
1023 }
1024 }
1025
1026
1027
1028 static void
launcher_plugin_construct(XfcePanelPlugin * panel_plugin)1029 launcher_plugin_construct (XfcePanelPlugin *panel_plugin)
1030 {
1031 LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
1032 const gchar * const *uris;
1033 guint i;
1034 GPtrArray *array;
1035 GValue *value;
1036 gchar *file, *path;
1037 GError *error = NULL;
1038 const PanelProperty properties[] =
1039 {
1040 { "show-label", G_TYPE_BOOLEAN },
1041 { "items", G_TYPE_PTR_ARRAY },
1042 { "disable-tooltips", G_TYPE_BOOLEAN },
1043 { "move-first", G_TYPE_BOOLEAN },
1044 { "arrow-position", G_TYPE_UINT },
1045 { NULL }
1046 };
1047
1048 /* show the configure menu item */
1049 xfce_panel_plugin_menu_show_configure (panel_plugin);
1050
1051 xfce_panel_plugin_set_small (panel_plugin, TRUE);
1052
1053 /* lookup the config directory where this launcher stores it's desktop files */
1054 file = g_strdup_printf (RELATIVE_CONFIG_PATH,
1055 xfce_panel_plugin_get_name (XFCE_PANEL_PLUGIN (plugin)),
1056 xfce_panel_plugin_get_unique_id (XFCE_PANEL_PLUGIN (plugin)));
1057 path = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, file, FALSE);
1058 plugin->config_directory = g_file_new_for_path (path);
1059 g_free (file);
1060 g_free (path);
1061
1062 /* bind all properties */
1063 panel_properties_bind (NULL, G_OBJECT (plugin),
1064 xfce_panel_plugin_get_property_base (panel_plugin),
1065 properties, FALSE);
1066
1067 /* handle and empty plugin */
1068 if (G_UNLIKELY (plugin->items == NULL))
1069 {
1070 /* get the plugin arguments list */
1071 uris = xfce_panel_plugin_get_arguments (panel_plugin);
1072 if (G_LIKELY (uris != NULL))
1073 {
1074 /* create array with all the uris */
1075 array = g_ptr_array_new ();
1076 for (i = 0; uris[i] != NULL; i++)
1077 {
1078 value = g_new0 (GValue, 1);
1079 g_value_init (value, G_TYPE_STRING);
1080 g_value_set_static_string (value, uris[i]);
1081 g_ptr_array_add (array, value);
1082 }
1083
1084 /* set new file list */
1085 if (G_LIKELY (array->len > 0))
1086 g_object_set (G_OBJECT (plugin), "items", array, NULL);
1087 xfconf_array_free (array);
1088 }
1089 else
1090 {
1091 /* update the icon */
1092 launcher_plugin_button_update (plugin);
1093 }
1094 }
1095
1096 /* start file monitor in our config directory */
1097 plugin->config_monitor = g_file_monitor_directory (plugin->config_directory,
1098 G_FILE_MONITOR_NONE, NULL, &error);
1099 if (G_LIKELY (plugin->config_monitor != NULL))
1100 {
1101 g_signal_connect (G_OBJECT (plugin->config_monitor), "changed",
1102 G_CALLBACK (launcher_plugin_file_changed), plugin);
1103 }
1104 else
1105 {
1106 g_critical ("Failed to start file monitor: %s", error->message);
1107 g_error_free (error);
1108 }
1109
1110 /* show the beast */
1111 gtk_widget_show (plugin->box);
1112 gtk_widget_show (plugin->button);
1113 gtk_widget_show (plugin->child);
1114 }
1115
1116
1117
1118 static void
launcher_plugin_free_data(XfcePanelPlugin * panel_plugin)1119 launcher_plugin_free_data (XfcePanelPlugin *panel_plugin)
1120 {
1121 LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
1122 GtkIconTheme *icon_theme;
1123
1124 /* stop monitoring */
1125 if (plugin->config_monitor != NULL)
1126 {
1127 g_file_monitor_cancel (plugin->config_monitor);
1128 g_object_unref (G_OBJECT (plugin->config_monitor));
1129 }
1130
1131 if (plugin->save_timeout_id != 0)
1132 {
1133 g_source_remove (plugin->save_timeout_id);
1134 launcher_plugin_save_delayed_timeout (plugin);
1135 }
1136
1137 /* destroy the menu and timeout */
1138 launcher_plugin_menu_destroy (plugin);
1139
1140 launcher_plugin_items_free (plugin);
1141
1142 if (plugin->config_directory != NULL)
1143 g_object_unref (G_OBJECT (plugin->config_directory));
1144
1145 /* stop watching the icon theme */
1146 if (plugin->theme_change_id != 0)
1147 {
1148 icon_theme = gtk_icon_theme_get_default ();
1149 g_signal_handler_disconnect (G_OBJECT (icon_theme), plugin->theme_change_id);
1150 }
1151
1152 /* release the cached tooltip */
1153 if (plugin->tooltip_cache != NULL)
1154 g_object_unref (G_OBJECT (plugin->tooltip_cache));
1155 /* release the cached pixbuf */
1156 if (plugin->pixbuf != NULL)
1157 g_object_unref (G_OBJECT (plugin->pixbuf));
1158 if (plugin->icon_name != NULL)
1159 g_free (plugin->icon_name);
1160 }
1161
1162
1163
1164 static void
launcher_plugin_removed(XfcePanelPlugin * panel_plugin)1165 launcher_plugin_removed (XfcePanelPlugin *panel_plugin)
1166 {
1167 LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
1168 GError *error = NULL;
1169
1170 panel_return_if_fail (G_IS_FILE (plugin->config_directory));
1171
1172 /* leave if there is not config */
1173 if (!g_file_query_exists (plugin->config_directory, NULL))
1174 return;
1175
1176 /* stop monitoring */
1177 if (plugin->config_monitor != NULL)
1178 {
1179 g_file_monitor_cancel (plugin->config_monitor);
1180 g_object_unref (G_OBJECT (plugin->config_monitor));
1181 plugin->config_monitor = NULL;
1182 }
1183
1184 /* cleanup desktop files in the config dir */
1185 launcher_plugin_items_delete_configs (plugin);
1186
1187 if (!g_file_delete (plugin->config_directory, NULL, &error))
1188 {
1189 g_message ("launcher-%d: Failed to cleanup the configuration: %s",
1190 xfce_panel_plugin_get_unique_id (panel_plugin),
1191 error->message);
1192 g_error_free (error);
1193 }
1194 }
1195
1196
1197
1198 static gboolean
launcher_plugin_remote_event(XfcePanelPlugin * panel_plugin,const gchar * name,const GValue * value)1199 launcher_plugin_remote_event (XfcePanelPlugin *panel_plugin,
1200 const gchar *name,
1201 const GValue *value)
1202 {
1203 LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
1204
1205 panel_return_val_if_fail (value == NULL || G_IS_VALUE (value), FALSE);
1206
1207 if (g_strcmp0 (name, "popup") == 0
1208 && LIST_HAS_TWO_OR_MORE_ENTRIES (plugin->items)
1209 && (plugin->menu == NULL || !gtk_widget_get_visible (plugin->menu)))
1210 {
1211 launcher_plugin_menu_popup (plugin);
1212
1213 return TRUE;
1214 }
1215
1216 if (g_strcmp0 (name, "disable-tooltips") == 0
1217 && value != NULL
1218 && G_VALUE_HOLDS_BOOLEAN (value))
1219 {
1220 g_object_set_property (G_OBJECT (plugin), "disable-tooltips", value);
1221
1222 return FALSE;
1223 }
1224
1225 return FALSE;
1226 }
1227
1228
1229
1230 static void
launcher_plugin_save_delayed_timeout_destroyed(gpointer user_data)1231 launcher_plugin_save_delayed_timeout_destroyed (gpointer user_data)
1232 {
1233 XFCE_LAUNCHER_PLUGIN (user_data)->save_timeout_id = 0;
1234 }
1235
1236
1237
1238 static gboolean
launcher_plugin_save_delayed_timeout(gpointer user_data)1239 launcher_plugin_save_delayed_timeout (gpointer user_data)
1240 {
1241 /* make sure the items are stored */
1242 g_object_notify (G_OBJECT (user_data), "items");
1243
1244 return FALSE;
1245 }
1246
1247
1248
1249 static void
launcher_plugin_save_delayed(LauncherPlugin * plugin)1250 launcher_plugin_save_delayed (LauncherPlugin *plugin)
1251 {
1252 if (plugin->save_timeout_id != 0)
1253 g_source_remove (plugin->save_timeout_id);
1254
1255 plugin->save_timeout_id = gdk_threads_add_timeout_seconds_full (G_PRIORITY_LOW, 1,
1256 launcher_plugin_save_delayed_timeout, plugin,
1257 launcher_plugin_save_delayed_timeout_destroyed);
1258 }
1259
1260
1261
1262 static void
launcher_plugin_mode_changed(XfcePanelPlugin * panel_plugin,XfcePanelPluginMode mode)1263 launcher_plugin_mode_changed (XfcePanelPlugin *panel_plugin,
1264 XfcePanelPluginMode mode)
1265 {
1266 /* update label orientation */
1267 launcher_plugin_button_update (XFCE_LAUNCHER_PLUGIN (panel_plugin));
1268
1269 /* update the widget order */
1270 launcher_plugin_pack_widgets (XFCE_LAUNCHER_PLUGIN (panel_plugin));
1271
1272 /* update the arrow button */
1273 launcher_plugin_screen_position_changed (panel_plugin,
1274 xfce_panel_plugin_get_screen_position (panel_plugin));
1275
1276 /* update the plugin size */
1277 launcher_plugin_size_changed (panel_plugin,
1278 xfce_panel_plugin_get_size (panel_plugin));
1279 }
1280
1281
1282
1283 static gboolean
launcher_plugin_size_changed(XfcePanelPlugin * panel_plugin,gint size)1284 launcher_plugin_size_changed (XfcePanelPlugin *panel_plugin,
1285 gint size)
1286 {
1287 LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
1288 gint p_width, p_height;
1289 gint a_width, a_height;
1290 gboolean horizontal;
1291 LauncherArrowType arrow_position;
1292
1293 /* initialize the plugin size */
1294 size /= xfce_panel_plugin_get_nrows (panel_plugin);
1295 p_width = p_height = size;
1296 a_width = a_height = -1;
1297
1298 /* add the arrow size */
1299 if (gtk_widget_get_visible (plugin->arrow))
1300 {
1301 /* if the panel is horizontal */
1302 horizontal = !!(xfce_panel_plugin_get_orientation (panel_plugin) ==
1303 GTK_ORIENTATION_HORIZONTAL);
1304
1305 /* translate default direction */
1306 arrow_position = launcher_plugin_default_arrow_type (plugin);
1307
1308 switch (arrow_position)
1309 {
1310 case LAUNCHER_ARROW_NORTH:
1311 case LAUNCHER_ARROW_SOUTH:
1312 a_height = ARROW_BUTTON_SIZE;
1313 if (!horizontal)
1314 p_height += ARROW_BUTTON_SIZE;
1315 break;
1316
1317 case LAUNCHER_ARROW_EAST:
1318 case LAUNCHER_ARROW_WEST:
1319 a_width = ARROW_BUTTON_SIZE;
1320 if (horizontal)
1321 p_width += ARROW_BUTTON_SIZE;
1322 break;
1323
1324 default:
1325 /* the default position should never be returned */
1326 panel_assert_not_reached ();
1327 break;
1328 }
1329
1330 /* set the arrow size */
1331 gtk_widget_set_size_request (plugin->arrow, a_width, a_height);
1332
1333 }
1334
1335 /* set the panel plugin size */
1336 if (plugin->show_label) {
1337 gtk_widget_set_size_request (GTK_WIDGET (panel_plugin), -1, -1);
1338 }
1339 else {
1340 gint icon_size;
1341
1342 gtk_widget_set_size_request (GTK_WIDGET (panel_plugin), p_width, p_height);
1343
1344 icon_size = xfce_panel_plugin_get_icon_size (panel_plugin);
1345 /* if the icon is a pixbuf we have to recreate and scale it */
1346 if (plugin->pixbuf != NULL &&
1347 plugin->icon_name != NULL) {
1348 g_object_unref (plugin->pixbuf);
1349 plugin->pixbuf = gdk_pixbuf_new_from_file_at_size (plugin->icon_name,
1350 icon_size, icon_size,
1351 NULL);
1352 gtk_image_set_from_pixbuf (GTK_IMAGE (plugin->child), plugin->pixbuf);
1353 }
1354 /* set the panel plugin icon size */
1355 else {
1356 gtk_image_set_pixel_size (GTK_IMAGE (plugin->child), MIN (icon_size, icon_size));
1357 }
1358 }
1359
1360 return TRUE;
1361 }
1362
1363
1364
1365 static void
launcher_plugin_configure_plugin(XfcePanelPlugin * panel_plugin)1366 launcher_plugin_configure_plugin (XfcePanelPlugin *panel_plugin)
1367 {
1368 /* run the configure dialog */
1369 launcher_dialog_show (XFCE_LAUNCHER_PLUGIN (panel_plugin));
1370 }
1371
1372
1373
1374 static void
launcher_plugin_screen_position_changed(XfcePanelPlugin * panel_plugin,XfceScreenPosition position)1375 launcher_plugin_screen_position_changed (XfcePanelPlugin *panel_plugin,
1376 XfceScreenPosition position)
1377 {
1378 LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
1379
1380 /* set the new arrow direction */
1381 xfce_arrow_button_set_arrow_type (XFCE_ARROW_BUTTON (plugin->arrow),
1382 xfce_panel_plugin_arrow_type (panel_plugin));
1383
1384 /* destroy the menu to update sort order */
1385 launcher_plugin_menu_destroy (plugin);
1386 }
1387
1388
1389
1390 static void
launcher_plugin_icon_theme_changed(GtkIconTheme * icon_theme,LauncherPlugin * plugin)1391 launcher_plugin_icon_theme_changed (GtkIconTheme *icon_theme,
1392 LauncherPlugin *plugin)
1393 {
1394 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
1395 panel_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
1396
1397 /* invalid the icon cache */
1398 if (plugin->tooltip_cache != NULL)
1399 {
1400 g_object_unref (G_OBJECT (plugin->tooltip_cache));
1401 plugin->tooltip_cache = NULL;
1402 }
1403 }
1404
1405
1406
1407 static LauncherArrowType
launcher_plugin_default_arrow_type(LauncherPlugin * plugin)1408 launcher_plugin_default_arrow_type (LauncherPlugin *plugin)
1409 {
1410 LauncherArrowType pos = plugin->arrow_position;
1411 gboolean rtl;
1412
1413 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), LAUNCHER_ARROW_NORTH);
1414
1415 if (pos == LAUNCHER_ARROW_DEFAULT)
1416 {
1417 /* get the plugin direction */
1418 rtl = !!(gtk_widget_get_direction (GTK_WIDGET (plugin)) == GTK_TEXT_DIR_RTL);
1419
1420 if (xfce_panel_plugin_get_orientation (XFCE_PANEL_PLUGIN (plugin)) ==
1421 GTK_ORIENTATION_HORIZONTAL)
1422 pos = rtl ? LAUNCHER_ARROW_WEST : LAUNCHER_ARROW_EAST;
1423 else
1424 pos = rtl ? LAUNCHER_ARROW_NORTH : LAUNCHER_ARROW_SOUTH;
1425 }
1426
1427 return pos;
1428 }
1429
1430
1431
1432 static void
launcher_plugin_pack_widgets(LauncherPlugin * plugin)1433 launcher_plugin_pack_widgets (LauncherPlugin *plugin)
1434 {
1435 LauncherArrowType pos;
1436
1437 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
1438
1439 /* leave when the arrow button is not visible */
1440 if (!gtk_widget_get_visible (plugin->arrow)
1441 || plugin->arrow_position == LAUNCHER_ARROW_INTERNAL)
1442 return;
1443
1444 pos = launcher_plugin_default_arrow_type (plugin);
1445 panel_assert (pos != LAUNCHER_ARROW_DEFAULT);
1446
1447 /* set the position of the arrow button in the box */
1448 gtk_box_set_child_packing (GTK_BOX (plugin->box), plugin->arrow, TRUE, TRUE, 0,
1449 (pos == LAUNCHER_ARROW_SOUTH || pos == LAUNCHER_ARROW_EAST) ? GTK_PACK_END : GTK_PACK_START);
1450 gtk_box_set_child_packing (GTK_BOX (plugin->box), plugin->button, FALSE, FALSE, 0,
1451 (pos == LAUNCHER_ARROW_SOUTH || pos == LAUNCHER_ARROW_EAST) ? GTK_PACK_START : GTK_PACK_END);
1452
1453 /* set the orientation */
1454 gtk_orientable_set_orientation (GTK_ORIENTABLE (plugin->box),
1455 !!(pos == LAUNCHER_ARROW_WEST || pos == LAUNCHER_ARROW_EAST) ?
1456 GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
1457 }
1458
1459
1460
1461 static GdkPixbuf *
launcher_plugin_tooltip_pixbuf(GdkScreen * screen,const gchar * icon_name)1462 launcher_plugin_tooltip_pixbuf (GdkScreen *screen,
1463 const gchar *icon_name)
1464 {
1465 GtkIconTheme *theme;
1466
1467 panel_return_val_if_fail (screen == NULL || GDK_IS_SCREEN (screen), NULL);
1468
1469 if (panel_str_is_empty (icon_name))
1470 return NULL;
1471
1472 /* load directly from a file */
1473 if (G_UNLIKELY (g_path_is_absolute (icon_name)))
1474 return gdk_pixbuf_new_from_file_at_scale (icon_name, 32, 32, TRUE, NULL);
1475
1476 if (G_LIKELY (screen != NULL))
1477 theme = gtk_icon_theme_get_for_screen (screen);
1478 else
1479 theme = gtk_icon_theme_get_default ();
1480
1481 return gtk_icon_theme_load_icon_for_scale (theme, icon_name, GTK_ICON_SIZE_DND,
1482 GTK_ICON_SIZE_DND,
1483 GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
1484 }
1485
1486
1487
1488 static void
launcher_plugin_menu_deactivate(GtkWidget * menu,LauncherPlugin * plugin)1489 launcher_plugin_menu_deactivate (GtkWidget *menu,
1490 LauncherPlugin *plugin)
1491 {
1492 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
1493 panel_return_if_fail (plugin->menu == menu);
1494
1495 /* deactivate the arrow button */
1496 if (plugin->arrow_position != LAUNCHER_ARROW_INTERNAL)
1497 {
1498 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (plugin->arrow), FALSE);
1499 gtk_widget_unset_state_flags (GTK_WIDGET (plugin->arrow), GTK_STATE_FLAG_PRELIGHT);
1500 }
1501 else
1502 {
1503 gtk_widget_set_state_flags (GTK_WIDGET (plugin->button), GTK_STATE_FLAG_NORMAL, TRUE);
1504 }
1505 }
1506
1507
1508
1509 static void
launcher_plugin_menu_item_activate(GtkMenuItem * widget,GarconMenuItem * item)1510 launcher_plugin_menu_item_activate (GtkMenuItem *widget,
1511 GarconMenuItem *item)
1512 {
1513 LauncherPlugin *plugin;
1514 GdkScreen *screen;
1515 GdkEvent *event;
1516 guint32 event_time;
1517
1518 panel_return_if_fail (GTK_IS_MENU_ITEM (widget));
1519 panel_return_if_fail (GARCON_IS_MENU_ITEM (item));
1520
1521 /* get a copy of the event causing the menu item to activate */
1522 event = gtk_get_current_event ();
1523 event_time = gdk_event_get_time (event);
1524
1525 /* get the widget screen */
1526 screen = gtk_widget_get_screen (GTK_WIDGET (widget));
1527
1528 /* launch the command */
1529 if (event != NULL
1530 && event->type == GDK_BUTTON_RELEASE
1531 && event->button.button == 2)
1532 launcher_plugin_item_exec_from_clipboard (item, event_time, screen);
1533 else
1534 launcher_plugin_item_exec (item, event_time, screen, NULL);
1535
1536 if (event != NULL)
1537 gdk_event_free (event);
1538
1539 /* get the plugin */
1540 plugin = g_object_get_qdata (G_OBJECT (widget), launcher_plugin_quark);
1541 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
1542
1543 /* move the item to the first position if enabled */
1544 if (G_UNLIKELY (plugin->move_first))
1545 {
1546 /* prepend the item in the list */
1547 plugin->items = g_slist_remove (plugin->items, item);
1548 plugin->items = g_slist_prepend (plugin->items, item);
1549
1550 /* destroy the menu and update the icon */
1551 launcher_plugin_menu_destroy (plugin);
1552 launcher_plugin_button_update (plugin);
1553 }
1554 }
1555
1556
1557
1558 static void
launcher_plugin_menu_item_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * data,guint info,guint drag_time,GarconMenuItem * item)1559 launcher_plugin_menu_item_drag_data_received (GtkWidget *widget,
1560 GdkDragContext *context,
1561 gint x,
1562 gint y,
1563 GtkSelectionData *data,
1564 guint info,
1565 guint drag_time,
1566 GarconMenuItem *item)
1567 {
1568 LauncherPlugin *plugin;
1569 GSList *uri_list;
1570
1571 panel_return_if_fail (GTK_IS_MENU_ITEM (widget));
1572 panel_return_if_fail (GARCON_IS_MENU_ITEM (item));
1573
1574 /* get the plugin */
1575 plugin = g_object_get_qdata (G_OBJECT (widget), launcher_plugin_quark);
1576 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
1577
1578 /* extract the uris from the selection data */
1579 uri_list = launcher_plugin_uri_list_extract (data);
1580 if (G_LIKELY (uri_list != NULL))
1581 {
1582 /* execute the menu item */
1583 launcher_plugin_item_exec (item, drag_time,
1584 gtk_widget_get_screen (widget),
1585 uri_list);
1586
1587 launcher_plugin_uri_list_free (uri_list);
1588 }
1589
1590 /* hide the menu */
1591 gtk_widget_hide (gtk_widget_get_toplevel (plugin->menu));
1592 gtk_widget_hide (plugin->menu);
1593
1594 /* deactivate the toggle button */
1595 if (plugin->arrow_position != LAUNCHER_ARROW_INTERNAL)
1596 {
1597 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (plugin->arrow), FALSE);
1598 gtk_widget_unset_state_flags (GTK_WIDGET (plugin->arrow), GTK_STATE_FLAG_PRELIGHT);
1599 }
1600 else
1601 {
1602 gtk_widget_set_state_flags (GTK_WIDGET (plugin->button), GTK_STATE_FLAG_NORMAL, TRUE);
1603 }
1604
1605 /* finish the drag */
1606 gtk_drag_finish (context, TRUE, FALSE, drag_time);
1607 }
1608
1609
1610
1611 static void
launcher_plugin_menu_construct(LauncherPlugin * plugin)1612 launcher_plugin_menu_construct (LauncherPlugin *plugin)
1613 {
1614 GtkArrowType arrow_type;
1615 guint n;
1616 GarconMenuItem *item;
1617 GtkWidget *mi, *box, *label, *image;
1618 const gchar *name, *icon_name;
1619 GSList *li;
1620
1621 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
1622 panel_return_if_fail (plugin->menu == NULL);
1623
1624 /* create a new menu */
1625 plugin->menu = gtk_menu_new ();
1626 gtk_menu_set_reserve_toggle_size (GTK_MENU (plugin->menu), FALSE);
1627 gtk_menu_attach_to_widget (GTK_MENU (plugin->menu), GTK_WIDGET (plugin), NULL);
1628 g_signal_connect (G_OBJECT (plugin->menu), "deactivate",
1629 G_CALLBACK (launcher_plugin_menu_deactivate), plugin);
1630
1631 /* get the arrow type of the plugin */
1632 arrow_type = xfce_arrow_button_get_arrow_type (XFCE_ARROW_BUTTON (plugin->arrow));
1633
1634 /* walk through the menu entries */
1635 for (li = plugin->items, n = 0; li != NULL; li = li->next, n++)
1636 {
1637 /* skip the first entry when the arrow is visible */
1638 if (n == 0 && plugin->arrow_position != LAUNCHER_ARROW_INTERNAL)
1639 continue;
1640
1641 /* get the item data */
1642 item = GARCON_MENU_ITEM (li->data);
1643
1644 /* create the menu item */
1645 name = garcon_menu_item_get_name (item);
1646 mi = gtk_menu_item_new ();
1647 label = gtk_label_new (panel_str_is_empty (name) ? _("Unnamed Item") : name);
1648 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
1649 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
1650 gtk_box_pack_end (GTK_BOX (box), label, TRUE, TRUE, 0);
1651 gtk_container_add (GTK_CONTAINER (mi), box);
1652 g_object_set_qdata (G_OBJECT (mi), launcher_plugin_quark, plugin);
1653 gtk_widget_show_all (mi);
1654 gtk_drag_dest_set (mi, GTK_DEST_DEFAULT_ALL, drop_targets,
1655 G_N_ELEMENTS (drop_targets), GDK_ACTION_COPY);
1656 g_signal_connect (G_OBJECT (mi), "activate",
1657 G_CALLBACK (launcher_plugin_menu_item_activate), item);
1658 g_signal_connect (G_OBJECT (mi), "drag-data-received",
1659 G_CALLBACK (launcher_plugin_menu_item_drag_data_received), item);
1660 g_signal_connect (G_OBJECT (mi), "drag-leave",
1661 G_CALLBACK (launcher_plugin_arrow_drag_leave), plugin);
1662
1663 /* only connect the tooltip signal if tips are enabled */
1664 if (!plugin->disable_tooltips)
1665 {
1666 gtk_widget_set_has_tooltip (mi, TRUE);
1667 g_signal_connect (G_OBJECT (mi), "query-tooltip",
1668 G_CALLBACK (launcher_plugin_item_query_tooltip), item);
1669 }
1670
1671 /* depending on the menu position we prepend or append */
1672 if (G_UNLIKELY (arrow_type == GTK_ARROW_UP))
1673 gtk_menu_shell_prepend (GTK_MENU_SHELL (plugin->menu), mi);
1674 else
1675 gtk_menu_shell_append (GTK_MENU_SHELL (plugin->menu), mi);
1676
1677 /* set the icon if one is set */
1678 icon_name = garcon_menu_item_get_icon_name (item);
1679
1680 if (panel_str_is_empty (icon_name))
1681 {
1682 /* use an empty placeholder icon */
1683 image = gtk_image_new_from_icon_name ("", GTK_ICON_SIZE_MENU);
1684 }
1685 else if (g_path_is_absolute (icon_name))
1686 {
1687 /* remember the icon name for recreating the pixbuf when panel
1688 size changes */
1689 plugin->icon_name = g_strdup (icon_name);
1690 plugin->pixbuf = gdk_pixbuf_new_from_file_at_size (icon_name, 16, 16, NULL);
1691 image = gtk_image_new_from_pixbuf (plugin->pixbuf);
1692 }
1693 else
1694 {
1695 image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
1696 gtk_image_set_pixel_size (GTK_IMAGE (image), 16);
1697 plugin->icon_name = NULL;
1698 }
1699
1700 gtk_box_pack_start (GTK_BOX (box), image, FALSE, TRUE, 3);
1701 gtk_widget_show (image);
1702 }
1703 }
1704
1705
1706
1707 static void
launcher_plugin_menu_popup_destroyed(gpointer user_data)1708 launcher_plugin_menu_popup_destroyed (gpointer user_data)
1709 {
1710 XFCE_LAUNCHER_PLUGIN (user_data)->menu_timeout_id = 0;
1711 }
1712
1713
1714
1715 static gboolean
launcher_plugin_menu_popup(gpointer user_data)1716 launcher_plugin_menu_popup (gpointer user_data)
1717 {
1718 LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (user_data);
1719 gint x, y;
1720
1721 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
1722
1723 /* construct the menu if needed */
1724 if (plugin->menu == NULL)
1725 launcher_plugin_menu_construct (plugin);
1726
1727 /* toggle the arrow button */
1728 if (plugin->arrow_position != LAUNCHER_ARROW_INTERNAL)
1729 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (plugin->arrow), TRUE);
1730 else
1731 gtk_widget_set_state_flags (GTK_WIDGET (plugin->button), GTK_STATE_FLAG_CHECKED, TRUE);
1732
1733 /* popup the menu */
1734 gtk_menu_popup_at_widget (GTK_MENU (plugin->menu),
1735 plugin->button,
1736 xfce_panel_plugin_get_orientation (XFCE_PANEL_PLUGIN (plugin)) == GTK_ORIENTATION_VERTICAL
1737 ? GDK_GRAVITY_NORTH_EAST : GDK_GRAVITY_SOUTH_WEST,
1738 GDK_GRAVITY_NORTH_WEST,
1739 NULL);
1740
1741 /* fallback to manual positioning, this is used with
1742 * drag motion over the arrow button */
1743 if (!gtk_widget_get_visible (plugin->menu))
1744 {
1745 /* make sure the size is allocated */
1746 if (!gtk_widget_get_realized (plugin->menu))
1747 gtk_widget_realize (plugin->menu);
1748
1749 /* use the widget position function to get the coordinates */
1750 xfce_panel_plugin_position_widget (XFCE_PANEL_PLUGIN (plugin),
1751 plugin->menu, NULL, &x, &y);
1752
1753 /* bit ugly... but show the menu */
1754 gtk_widget_show (plugin->menu);
1755 gtk_window_move (GTK_WINDOW (gtk_widget_get_toplevel (plugin->menu)), x, y);
1756 gtk_widget_show (gtk_widget_get_toplevel (plugin->menu));
1757 }
1758
1759 return FALSE;
1760 }
1761
1762
1763
1764 static void
launcher_plugin_menu_destroy(LauncherPlugin * plugin)1765 launcher_plugin_menu_destroy (LauncherPlugin *plugin)
1766 {
1767 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
1768
1769 /* stop pending timeout */
1770 if (plugin->menu_timeout_id != 0)
1771 g_source_remove (plugin->menu_timeout_id);
1772
1773 if (plugin->menu != NULL)
1774 {
1775 /* destroy the menu */
1776 gtk_widget_destroy (plugin->menu);
1777 plugin->menu = NULL;
1778
1779 /* deactivate the toggle button */
1780 if (plugin->arrow_position != LAUNCHER_ARROW_INTERNAL)
1781 {
1782 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (plugin->arrow), FALSE);
1783 gtk_widget_unset_state_flags (GTK_WIDGET (plugin->arrow), GTK_STATE_FLAG_PRELIGHT);
1784 }
1785 else
1786 {
1787 gtk_widget_set_state_flags (GTK_WIDGET (plugin->button), GTK_STATE_FLAG_NORMAL, TRUE);
1788 }
1789 }
1790 }
1791
1792
1793
1794 static void
launcher_plugin_button_update(LauncherPlugin * plugin)1795 launcher_plugin_button_update (LauncherPlugin *plugin)
1796 {
1797 GarconMenuItem *item = NULL;
1798 const gchar *icon_name;
1799 XfcePanelPluginMode mode;
1800 gint icon_size;
1801
1802 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
1803
1804 /* invalate the tooltip icon cache */
1805 if (plugin->tooltip_cache != NULL)
1806 {
1807 g_object_unref (G_OBJECT (plugin->tooltip_cache));
1808 plugin->tooltip_cache = NULL;
1809 }
1810 if (plugin->pixbuf != NULL)
1811 {
1812 g_object_unref (G_OBJECT (plugin->pixbuf));
1813 plugin->pixbuf = NULL;
1814 }
1815 /* get first item */
1816 if (G_LIKELY (plugin->items != NULL))
1817 item = GARCON_MENU_ITEM (plugin->items->data);
1818
1819 mode = xfce_panel_plugin_get_mode (XFCE_PANEL_PLUGIN (plugin));
1820 icon_size = xfce_panel_plugin_get_icon_size (XFCE_PANEL_PLUGIN (plugin));
1821
1822 /* disable the "small" property in the deskbar mode and the label visible */
1823 if (G_UNLIKELY (plugin->show_label && mode == XFCE_PANEL_PLUGIN_MODE_DESKBAR))
1824 xfce_panel_plugin_set_small (XFCE_PANEL_PLUGIN (plugin), FALSE);
1825 else
1826 xfce_panel_plugin_set_small (XFCE_PANEL_PLUGIN (plugin), TRUE);
1827
1828 if (G_UNLIKELY (plugin->show_label))
1829 {
1830 panel_return_if_fail (GTK_IS_LABEL (plugin->child));
1831
1832 gtk_label_set_angle (GTK_LABEL (plugin->child),
1833 (mode == XFCE_PANEL_PLUGIN_MODE_VERTICAL) ? 270 : 0);
1834 gtk_label_set_text (GTK_LABEL (plugin->child),
1835 item != NULL ? garcon_menu_item_get_name (item) : _("No items"));
1836 }
1837 else if (G_LIKELY (item != NULL))
1838 {
1839 panel_return_if_fail (GTK_IS_WIDGET (plugin->child));
1840
1841 icon_name = garcon_menu_item_get_icon_name (item);
1842 if (!panel_str_is_empty (icon_name))
1843 {
1844 if (g_path_is_absolute (icon_name)) {
1845 /* remember the icon name for recreating the pixbuf when panel
1846 size changes */
1847 plugin->icon_name = g_strdup (icon_name);
1848 plugin->pixbuf = gdk_pixbuf_new_from_file_at_size (icon_name, icon_size, icon_size, NULL);
1849 gtk_image_set_from_pixbuf (GTK_IMAGE (plugin->child), plugin->pixbuf);
1850 }
1851 else {
1852 gtk_image_set_from_icon_name (GTK_IMAGE (plugin->child), icon_name,
1853 icon_size);
1854 gtk_image_set_pixel_size (GTK_IMAGE (plugin->child), icon_size);
1855 }
1856 }
1857
1858 panel_utils_set_atk_info (plugin->button,
1859 garcon_menu_item_get_name (item),
1860 garcon_menu_item_get_comment (item));
1861 }
1862 else
1863 {
1864 /* set fallback icon if there is no application icon (yet) */
1865 panel_return_if_fail (GTK_IS_WIDGET (plugin->child));
1866 gtk_image_set_from_icon_name (GTK_IMAGE (plugin->child),
1867 "org.xfce.panel.launcher", icon_size);
1868 }
1869 }
1870
1871
1872
1873 #if GARCON_CHECK_VERSION(0,7,0)
1874 static void
launcher_plugin_add_desktop_actions(GtkWidget * widget,gpointer user_data)1875 launcher_plugin_add_desktop_actions (GtkWidget *widget, gpointer user_data)
1876 {
1877 LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (user_data);
1878
1879 panel_return_if_fail (GTK_IS_WIDGET (widget));
1880 panel_return_if_fail (GTK_IS_MENU (plugin->action_menu));
1881 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
1882
1883 /* Pack the action menu item into the plugin's context menu */
1884 g_object_ref (widget);
1885 gtk_container_remove (GTK_CONTAINER (plugin->action_menu), widget);
1886 xfce_panel_plugin_menu_insert_item (XFCE_PANEL_PLUGIN (plugin), GTK_MENU_ITEM (widget));
1887 g_object_unref (widget);
1888 }
1889
1890
1891
1892 static void
launcher_plugin_button_update_action_menu(LauncherPlugin * plugin)1893 launcher_plugin_button_update_action_menu (LauncherPlugin *plugin)
1894 {
1895 GarconMenuItem *item = NULL;
1896 GList *list;
1897
1898 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
1899 panel_return_if_fail (plugin->menu == NULL);
1900
1901 /* If there are >1 items in the launcher don't show the action menu */
1902 if (LIST_HAS_TWO_OR_MORE_ENTRIES (plugin->items))
1903 {
1904 xfce_panel_plugin_menu_destroy (XFCE_PANEL_PLUGIN (plugin));
1905 plugin->action_menu = NULL;
1906 return;
1907 }
1908
1909 if (G_LIKELY (plugin->items != NULL))
1910 item = GARCON_MENU_ITEM (plugin->items->data);
1911
1912 xfce_panel_plugin_menu_destroy (XFCE_PANEL_PLUGIN (plugin));
1913 if (plugin->action_menu)
1914 {
1915 gtk_widget_destroy (GTK_WIDGET (plugin->action_menu));
1916 }
1917 else if (item != NULL && (list = garcon_menu_item_get_actions (item)) != NULL)
1918 {
1919 g_list_free (list);
1920 plugin->action_menu = GTK_WIDGET (garcon_gtk_menu_get_desktop_actions_menu (item));
1921 if (plugin->action_menu)
1922 {
1923 gtk_menu_set_reserve_toggle_size (GTK_MENU (plugin->action_menu), FALSE);
1924 gtk_container_foreach (GTK_CONTAINER (plugin->action_menu),
1925 launcher_plugin_add_desktop_actions,
1926 plugin);
1927 }
1928 }
1929 }
1930 #endif
1931
1932
1933
1934 static void
launcher_plugin_button_state_changed(GtkWidget * button_a,GtkStateType state,GtkWidget * button_b)1935 launcher_plugin_button_state_changed (GtkWidget *button_a,
1936 GtkStateType state,
1937 GtkWidget *button_b)
1938 {
1939 if (gtk_widget_get_state_flags (button_a) != gtk_widget_get_state_flags (button_b)
1940 && (gtk_widget_get_state_flags (button_a) & GTK_STATE_INSENSITIVE))
1941 gtk_widget_set_state_flags (button_b, gtk_widget_get_state_flags (button_a), TRUE);
1942 }
1943
1944
1945
1946 static gboolean
launcher_plugin_button_press_event(GtkWidget * button,GdkEventButton * event,LauncherPlugin * plugin)1947 launcher_plugin_button_press_event (GtkWidget *button,
1948 GdkEventButton *event,
1949 LauncherPlugin *plugin)
1950 {
1951 guint modifiers;
1952
1953 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
1954
1955 /* do nothing on anything else then a single click */
1956 if (event->type != GDK_BUTTON_PRESS)
1957 return FALSE;
1958
1959 /* get the default accelerator modifier mask */
1960 modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
1961
1962 /* leave when button 1 is not pressed or shift is pressed */
1963 if (event->button != 1 || modifiers == GDK_CONTROL_MASK)
1964 return FALSE;
1965
1966 if (ARROW_INSIDE_BUTTON (plugin))
1967 {
1968 /* directly popup the menu */
1969 launcher_plugin_menu_popup (plugin);
1970 }
1971 else if (plugin->menu_timeout_id == 0
1972 && LIST_HAS_TWO_OR_MORE_ENTRIES (plugin->items))
1973 {
1974 /* start the popup timeout */
1975 plugin->menu_timeout_id =
1976 gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
1977 MENU_POPUP_DELAY,
1978 launcher_plugin_menu_popup, plugin,
1979 launcher_plugin_menu_popup_destroyed);
1980 }
1981
1982 return FALSE;
1983 }
1984
1985
1986
1987 static gboolean
launcher_plugin_button_release_event(GtkWidget * button,GdkEventButton * event,LauncherPlugin * plugin)1988 launcher_plugin_button_release_event (GtkWidget *button,
1989 GdkEventButton *event,
1990 LauncherPlugin *plugin)
1991 {
1992 GarconMenuItem *item;
1993 GdkScreen *screen;
1994
1995 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
1996
1997 /* remove a delayed popup timeout */
1998 if (plugin->menu_timeout_id != 0)
1999 g_source_remove (plugin->menu_timeout_id);
2000
2001 /* leave when there are no menu items or there is an internal arrow */
2002 if (plugin->items == NULL
2003 || ARROW_INSIDE_BUTTON (plugin))
2004 return FALSE;
2005
2006 /* get the menu item and the screen */
2007 item = GARCON_MENU_ITEM (plugin->items->data);
2008 screen = gtk_widget_get_screen (button);
2009
2010 /* launcher the entry */
2011 if (event->button == 1)
2012 launcher_plugin_item_exec (item, event->time, screen, NULL);
2013 else if (event->button == 2)
2014 launcher_plugin_item_exec_from_clipboard (item, event->time, screen);
2015 else
2016 return TRUE;
2017
2018 return FALSE;
2019 }
2020
2021
2022
2023 static gboolean
launcher_plugin_button_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,LauncherPlugin * plugin)2024 launcher_plugin_button_query_tooltip (GtkWidget *widget,
2025 gint x,
2026 gint y,
2027 gboolean keyboard_mode,
2028 GtkTooltip *tooltip,
2029 LauncherPlugin *plugin)
2030 {
2031 gboolean result;
2032 GarconMenuItem *item;
2033
2034 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
2035 panel_return_val_if_fail (!plugin->disable_tooltips, FALSE);
2036
2037 /* check if we show tooltips */
2038 if (plugin->arrow_position == LAUNCHER_ARROW_INTERNAL
2039 || plugin->items == NULL
2040 || plugin->items->data == NULL)
2041 return FALSE;
2042
2043 /* get the first item */
2044 item = GARCON_MENU_ITEM (plugin->items->data);
2045
2046 /* handle the basic tooltip data */
2047 result = launcher_plugin_item_query_tooltip (widget, x, y, keyboard_mode, tooltip, item);
2048 if (G_LIKELY (result))
2049 {
2050 /* set the cached icon if not already set */
2051 if (G_UNLIKELY (plugin->tooltip_cache == NULL))
2052 plugin->tooltip_cache =
2053 launcher_plugin_tooltip_pixbuf (gtk_widget_get_screen (widget),
2054 garcon_menu_item_get_icon_name (item));
2055
2056 if (G_LIKELY (plugin->tooltip_cache != NULL))
2057 gtk_tooltip_set_icon (tooltip, plugin->tooltip_cache);
2058 }
2059
2060 return result;
2061 }
2062
2063
2064
2065 static void
launcher_plugin_button_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint drag_time,LauncherPlugin * plugin)2066 launcher_plugin_button_drag_data_received (GtkWidget *widget,
2067 GdkDragContext *context,
2068 gint x,
2069 gint y,
2070 GtkSelectionData *selection_data,
2071 guint info,
2072 guint drag_time,
2073 LauncherPlugin *plugin)
2074 {
2075 GSList *uri_list;
2076
2077 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
2078
2079 /* leave when there are not items or the arrow is internal */
2080 if (ARROW_INSIDE_BUTTON (plugin) || plugin->items == NULL)
2081 return;
2082
2083 /* get the list of uris from the selection data */
2084 uri_list = launcher_plugin_uri_list_extract (selection_data);
2085 if (G_LIKELY (uri_list != NULL))
2086 {
2087 /* execute */
2088 launcher_plugin_item_exec (GARCON_MENU_ITEM (plugin->items->data),
2089 gtk_get_current_event_time (),
2090 gtk_widget_get_screen (widget),
2091 uri_list);
2092
2093 launcher_plugin_uri_list_free (uri_list);
2094 }
2095
2096 /* finish the drag */
2097 gtk_drag_finish (context, TRUE, FALSE, drag_time);
2098 }
2099
2100
2101
2102 static GdkAtom
launcher_plugin_supported_drop(GdkDragContext * context,GtkWidget * widget)2103 launcher_plugin_supported_drop (GdkDragContext *context,
2104 GtkWidget *widget)
2105 {
2106 GList *li;
2107 GdkAtom target;
2108 guint i;
2109 GdkModifierType modifiers = 0;
2110
2111 /* do not handle drops if control is pressed */
2112 gdk_window_get_device_position (gtk_widget_get_window (widget),
2113 gdk_drag_context_get_device(context),
2114 NULL, NULL, &modifiers);
2115 if (PANEL_HAS_FLAG (modifiers, GDK_CONTROL_MASK))
2116 return GDK_NONE;
2117
2118 /* check if we support the target */
2119 for (li = gdk_drag_context_list_targets (context); li; li = li->next)
2120 {
2121 target = GDK_POINTER_TO_ATOM (li->data);
2122 for (i = 0; i < G_N_ELEMENTS (drop_targets); i++)
2123 if (target == gdk_atom_intern_static_string (drop_targets[i].target))
2124 return target;
2125 }
2126
2127 return GDK_NONE;
2128 }
2129
2130
2131
2132 static gboolean
launcher_plugin_button_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint drag_time,LauncherPlugin * plugin)2133 launcher_plugin_button_drag_motion (GtkWidget *widget,
2134 GdkDragContext *context,
2135 gint x,
2136 gint y,
2137 guint drag_time,
2138 LauncherPlugin *plugin)
2139 {
2140 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
2141
2142 if (launcher_plugin_supported_drop (context, widget) == GDK_NONE)
2143 return FALSE;
2144
2145 /* do nothing if the plugin is empty */
2146 if (plugin->items == NULL)
2147 {
2148 /* not a drop zone */
2149 gdk_drag_status (context, 0, drag_time);
2150 return FALSE;
2151 }
2152
2153 /* highlight the button if this is a launcher button */
2154 if (NO_ARROW_INSIDE_BUTTON (plugin))
2155 {
2156 gdk_drag_status (context, GDK_ACTION_COPY, drag_time);
2157 gtk_drag_highlight (widget);
2158 return TRUE;
2159 }
2160
2161 /* handle the popup menu */
2162 return launcher_plugin_arrow_drag_motion (widget, context, x, y,
2163 drag_time, plugin);
2164 }
2165
2166
2167
2168 static gboolean
launcher_plugin_button_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint drag_time,LauncherPlugin * plugin)2169 launcher_plugin_button_drag_drop (GtkWidget *widget,
2170 GdkDragContext *context,
2171 gint x,
2172 gint y,
2173 guint drag_time,
2174 LauncherPlugin *plugin)
2175 {
2176 GdkAtom target;
2177
2178 target = launcher_plugin_supported_drop (context, widget);
2179 if (target == GDK_NONE)
2180 return FALSE;
2181
2182 gtk_drag_get_data (widget, context, target, drag_time);
2183
2184 return TRUE;
2185 }
2186
2187
2188
2189 static void
launcher_plugin_button_drag_leave(GtkWidget * widget,GdkDragContext * context,guint drag_time,LauncherPlugin * plugin)2190 launcher_plugin_button_drag_leave (GtkWidget *widget,
2191 GdkDragContext *context,
2192 guint drag_time,
2193 LauncherPlugin *plugin)
2194 {
2195 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
2196
2197 /* unhighlight the widget or make sure the menu is deactivated */
2198 if (NO_ARROW_INSIDE_BUTTON (plugin))
2199 gtk_drag_unhighlight (widget);
2200 else
2201 launcher_plugin_arrow_drag_leave (widget, context, drag_time, plugin);
2202 }
2203
2204
2205
2206 static gboolean
launcher_plugin_button_draw(GtkWidget * widget,cairo_t * cr,LauncherPlugin * plugin)2207 launcher_plugin_button_draw (GtkWidget *widget,
2208 cairo_t *cr,
2209 LauncherPlugin *plugin)
2210 {
2211 GtkArrowType arrow_type;
2212 gdouble angle;
2213 gint size, x, y, offset;
2214 GtkAllocation allocation;
2215 GtkStyleContext *ctx;
2216 GtkBorder padding;
2217
2218 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
2219
2220 /* leave when the arrow is not shown inside the button */
2221 if (NO_ARROW_INSIDE_BUTTON (plugin))
2222 return FALSE;
2223
2224 /* get the arrow type */
2225 arrow_type = xfce_arrow_button_get_arrow_type (XFCE_ARROW_BUTTON (plugin->arrow));
2226
2227 /* style thickness */
2228 ctx = gtk_widget_get_style_context (widget);
2229 gtk_style_context_get_padding (ctx, gtk_widget_get_state_flags (widget), &padding);
2230
2231 /* size of the arrow and the start coordinates */
2232 gtk_widget_get_allocation (widget, &allocation);
2233
2234 size = allocation.width / 3;
2235 x = padding.left;
2236 y = padding.top;
2237 offset = size + padding.left + padding.right;
2238 angle = 1.5 * G_PI;
2239
2240 /* calculate the position based on the arrow type */
2241 switch (arrow_type)
2242 {
2243 case GTK_ARROW_UP:
2244 /* north east */
2245 x += allocation.width - offset;
2246 angle = 0.0 * G_PI;
2247 break;
2248
2249 case GTK_ARROW_DOWN:
2250 /* south west */
2251 y += allocation.height - offset;
2252 angle = 1.0 * G_PI;
2253 break;
2254
2255 case GTK_ARROW_RIGHT:
2256 /* south east */
2257 x += allocation.width - offset;
2258 y += allocation.height - offset;
2259 angle = 0.5 * G_PI;
2260 break;
2261
2262 default:
2263 /* north west */
2264 break;
2265 }
2266
2267 /* paint the arrow */
2268 gtk_render_arrow (ctx, cr, angle, (gdouble) x, (gdouble) y, (gdouble) size);
2269
2270 return FALSE;
2271 }
2272
2273
2274
2275 static void
launcher_plugin_arrow_visibility(LauncherPlugin * plugin)2276 launcher_plugin_arrow_visibility (LauncherPlugin *plugin)
2277 {
2278 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
2279
2280 if (plugin->arrow_position != LAUNCHER_ARROW_INTERNAL
2281 && LIST_HAS_TWO_OR_MORE_ENTRIES (plugin->items))
2282 gtk_widget_show (plugin->arrow);
2283 else
2284 gtk_widget_hide (plugin->arrow);
2285 }
2286
2287
2288
2289 static gboolean
launcher_plugin_arrow_press_event(GtkWidget * button,GdkEventButton * event,LauncherPlugin * plugin)2290 launcher_plugin_arrow_press_event (GtkWidget *button,
2291 GdkEventButton *event,
2292 LauncherPlugin *plugin)
2293 {
2294 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
2295
2296 /* only popup when button 1 is pressed */
2297 if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
2298 {
2299 launcher_plugin_menu_popup (plugin);
2300 return FALSE;
2301 }
2302
2303 return TRUE;
2304 }
2305
2306
2307
2308
2309 static gboolean
launcher_plugin_arrow_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint drag_time,LauncherPlugin * plugin)2310 launcher_plugin_arrow_drag_motion (GtkWidget *widget,
2311 GdkDragContext *context,
2312 gint x,
2313 gint y,
2314 guint drag_time,
2315 LauncherPlugin *plugin)
2316 {
2317 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
2318
2319 if (launcher_plugin_supported_drop (context, widget) == GDK_NONE)
2320 return FALSE;
2321
2322 /* the arrow is not a drop zone */
2323 gdk_drag_status (context, 0, drag_time);
2324
2325 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (plugin->arrow)))
2326 {
2327 /* make the toggle button active */
2328 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (plugin->arrow), TRUE);
2329
2330 /* start the popup timeout */
2331 plugin->menu_timeout_id =
2332 gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE, MENU_POPUP_DELAY,
2333 launcher_plugin_menu_popup, plugin,
2334 launcher_plugin_menu_popup_destroyed);
2335 }
2336
2337 return TRUE;
2338 }
2339
2340
2341
2342 static gboolean
launcher_plugin_arrow_drag_leave_timeout(gpointer user_data)2343 launcher_plugin_arrow_drag_leave_timeout (gpointer user_data)
2344 {
2345 LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (user_data);
2346 gint pointer_x, pointer_y;
2347 GtkWidget *menu = plugin->menu;
2348 gint menu_x, menu_y, menu_w, menu_h;
2349
2350 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
2351 panel_return_val_if_fail (menu == NULL || gtk_widget_get_has_window (menu), FALSE);
2352
2353 /* leave when the menu is destroyed */
2354 if (G_UNLIKELY (plugin->menu == NULL))
2355 return FALSE;
2356
2357 /* get the pointer position */
2358 gdk_device_get_position (gdk_seat_get_pointer (gdk_display_get_default_seat (gtk_widget_get_display (menu))),
2359 NULL, &pointer_x, &pointer_y);
2360
2361 /* get the menu position */
2362 gdk_window_get_root_origin (gtk_widget_get_window (menu), &menu_x, &menu_y);
2363 menu_w = gdk_window_get_width (gtk_widget_get_window (menu));
2364 menu_h = gdk_window_get_height (gtk_widget_get_window (menu));
2365
2366 /* check if we should hide the menu */
2367 if (pointer_x < menu_x || pointer_x > menu_x + menu_w
2368 || pointer_y < menu_y || pointer_y > menu_y + menu_h)
2369 {
2370 /* hide the menu */
2371 gtk_widget_hide (gtk_widget_get_toplevel (menu));
2372 gtk_widget_hide (menu);
2373
2374 /* inactive the toggle button */
2375 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (plugin->arrow), FALSE);
2376 }
2377
2378 return FALSE;
2379 }
2380
2381
2382
2383 static void
launcher_plugin_arrow_drag_leave(GtkWidget * widget,GdkDragContext * context,guint drag_time,LauncherPlugin * plugin)2384 launcher_plugin_arrow_drag_leave (GtkWidget *widget,
2385 GdkDragContext *context,
2386 guint drag_time,
2387 LauncherPlugin *plugin)
2388 {
2389 panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
2390
2391 if (plugin->menu_timeout_id != 0)
2392 {
2393 /* stop the popup timeout */
2394 g_source_remove (plugin->menu_timeout_id);
2395
2396 /* inactive the toggle button */
2397 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (plugin->arrow), FALSE);
2398 }
2399 else
2400 {
2401 /* start a timeout to give the user some time to drag to the menu */
2402 gdk_threads_add_timeout (MENU_POPUP_DELAY, launcher_plugin_arrow_drag_leave_timeout, plugin);
2403 }
2404 }
2405
2406
2407
2408 static gboolean
launcher_plugin_item_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip,GarconMenuItem * item)2409 launcher_plugin_item_query_tooltip (GtkWidget *widget,
2410 gint x,
2411 gint y,
2412 gboolean keyboard_mode,
2413 GtkTooltip *tooltip,
2414 GarconMenuItem *item)
2415 {
2416 gchar *markup;
2417 const gchar *name, *comment;
2418 GdkPixbuf *pixbuf;
2419
2420 panel_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
2421
2422 /* require atleast an item name */
2423 name = garcon_menu_item_get_name (item);
2424 if (panel_str_is_empty (name))
2425 return FALSE;
2426
2427 comment = garcon_menu_item_get_comment (item);
2428 if (!panel_str_is_empty (comment))
2429 {
2430 markup = g_markup_printf_escaped ("<b>%s</b>\n%s", name, comment);
2431 gtk_tooltip_set_markup (tooltip, markup);
2432 g_free (markup);
2433 }
2434 else
2435 {
2436 gtk_tooltip_set_text (tooltip, name);
2437 }
2438
2439 /* the button uses a custom cache because the button widget is never
2440 * destroyed, for menu items we cache the pixbuf by attaching the
2441 * data on the menu item widget */
2442 if (GTK_IS_MENU_ITEM (widget))
2443 {
2444 pixbuf = g_object_get_data (G_OBJECT (widget), "pixbuf-cache");
2445 if (G_LIKELY (pixbuf != NULL))
2446 {
2447 gtk_tooltip_set_icon (tooltip, pixbuf);
2448 }
2449 else
2450 {
2451 pixbuf = launcher_plugin_tooltip_pixbuf (gtk_widget_get_screen (widget),
2452 garcon_menu_item_get_icon_name (item));
2453 if (G_LIKELY (pixbuf != NULL))
2454 {
2455 gtk_tooltip_set_icon (tooltip, pixbuf);
2456 g_object_set_data_full (G_OBJECT (widget), "pixbuf-cache", pixbuf,
2457 (GDestroyNotify) g_object_unref);
2458 }
2459 }
2460 }
2461
2462 return TRUE;
2463 }
2464
2465
2466
2467 static gboolean
launcher_plugin_item_exec_on_screen(GarconMenuItem * item,guint32 event_time,GdkScreen * screen,GSList * uri_list)2468 launcher_plugin_item_exec_on_screen (GarconMenuItem *item,
2469 guint32 event_time,
2470 GdkScreen *screen,
2471 GSList *uri_list)
2472 {
2473 GError *error = NULL;
2474 gchar **argv;
2475 gboolean succeed = FALSE;
2476 gchar *command, *uri;
2477 const gchar *icon;
2478
2479 panel_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
2480 panel_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
2481
2482 /* get the command */
2483 command = (gchar*) garcon_menu_item_get_command (item);
2484 panel_return_val_if_fail (!panel_str_is_empty (command), FALSE);
2485
2486 /* expand the field codes */
2487 icon = garcon_menu_item_get_icon_name (item);
2488 uri = garcon_menu_item_get_uri (item);
2489 command = xfce_expand_desktop_entry_field_codes (command, uri_list, icon,
2490 garcon_menu_item_get_name (item),
2491 uri,
2492 garcon_menu_item_requires_terminal (item));
2493 g_free (uri);
2494
2495 /* parse the execute command */
2496 if (g_shell_parse_argv (command, NULL, &argv, &error))
2497 {
2498 /* launch the command on the screen */
2499 succeed = xfce_spawn (screen,
2500 garcon_menu_item_get_path (item),
2501 argv, NULL, G_SPAWN_SEARCH_PATH,
2502 garcon_menu_item_supports_startup_notification (item),
2503 event_time, icon, TRUE, &error);
2504
2505 g_strfreev (argv);
2506 }
2507
2508 if (G_UNLIKELY (!succeed))
2509 {
2510 /* show an error dialog */
2511 xfce_dialog_show_error (NULL, error, _("Failed to execute command \"%s\"."), command);
2512 g_error_free (error);
2513 }
2514
2515 g_free (command);
2516
2517 return succeed;
2518 }
2519
2520
2521
2522 static void
launcher_plugin_item_exec(GarconMenuItem * item,guint32 event_time,GdkScreen * screen,GSList * uri_list)2523 launcher_plugin_item_exec (GarconMenuItem *item,
2524 guint32 event_time,
2525 GdkScreen *screen,
2526 GSList *uri_list)
2527 {
2528 GSList *li, fake;
2529 gboolean proceed = TRUE;
2530 const gchar *command;
2531
2532 panel_return_if_fail (GARCON_IS_MENU_ITEM (item));
2533 panel_return_if_fail (GDK_IS_SCREEN (screen));
2534
2535 /* leave when there is nothing to execute */
2536 command = garcon_menu_item_get_command (item);
2537 if (panel_str_is_empty (command))
2538 return;
2539
2540 if (G_UNLIKELY (uri_list != NULL
2541 && strstr (command, "%F") == NULL
2542 && strstr (command, "%U") == NULL))
2543 {
2544 fake.next = NULL;
2545
2546 /* run an instance for each file, break on the first error */
2547 for (li = uri_list; li != NULL && proceed; li = li->next)
2548 {
2549 fake.data = li->data;
2550 proceed = launcher_plugin_item_exec_on_screen (item, event_time, screen, &fake);
2551 }
2552 }
2553 else
2554 {
2555 launcher_plugin_item_exec_on_screen (item, event_time, screen, uri_list);
2556 }
2557 }
2558
2559
2560
2561 static void
launcher_plugin_item_exec_from_clipboard(GarconMenuItem * item,guint32 event_time,GdkScreen * screen)2562 launcher_plugin_item_exec_from_clipboard (GarconMenuItem *item,
2563 guint32 event_time,
2564 GdkScreen *screen)
2565 {
2566 GtkClipboard *clipboard;
2567 gchar *text = NULL;
2568 //GSList *uri_list;
2569 //GtkSelectionData data;
2570
2571 panel_return_if_fail (GARCON_IS_MENU_ITEM (item));
2572 panel_return_if_fail (GDK_IS_SCREEN (screen));
2573
2574 /* get the primary clipboard text */
2575 clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
2576 if (G_LIKELY (clipboard))
2577 text = gtk_clipboard_wait_for_text (clipboard);
2578
2579 /* try the secondary keayboard if the text is empty */
2580 if (panel_str_is_empty (text))
2581 {
2582 /* get the secondary clipboard text */
2583 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
2584 if (G_LIKELY (clipboard))
2585 text = gtk_clipboard_wait_for_text (clipboard);
2586 }
2587
2588 if (!panel_str_is_empty (text))
2589 {
2590 /* create fake selection data */
2591 //data.data = (guchar *) text; //HOWTO?
2592 //data.length = strlen (text);
2593 //data.target = GDK_NONE;
2594
2595 /* extract the uris from the selection data */
2596 //uri_list = launcher_plugin_uri_list_extract (&data);
2597
2598 /* launch with the uri list */
2599 //launcher_plugin_item_exec (item, event_time,
2600 // screen, uri_list);
2601
2602 //launcher_plugin_uri_list_free (uri_list);
2603 }
2604
2605 g_free (text);
2606 }
2607
2608
2609
2610 static GSList *
launcher_plugin_uri_list_extract(GtkSelectionData * data)2611 launcher_plugin_uri_list_extract (GtkSelectionData *data)
2612 {
2613 GSList *list = NULL;
2614 gchar **array;
2615 guint i;
2616 gchar *uri;
2617
2618 /* leave if there is no data */
2619 if (gtk_selection_data_get_length (data) <= 0)
2620 return NULL;
2621
2622 /* extract the files */
2623 if (gtk_selection_data_get_target (data) == gdk_atom_intern_static_string ("text/uri-list"))
2624 {
2625 /* extract the list of uris */
2626 array = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (data));
2627 if (G_UNLIKELY (array == NULL))
2628 return NULL;
2629
2630 /* create the list of uris */
2631 for (i = 0; array[i] != NULL; i++)
2632 {
2633 if (!panel_str_is_empty (array[i]))
2634 list = g_slist_prepend (list, array[i]);
2635 else
2636 g_free (array[i]);
2637 }
2638
2639 g_free (array);
2640 }
2641 else
2642 {
2643 /* split the data on new lines */
2644 array = g_strsplit_set ((const gchar *) gtk_selection_data_get_data (data), "\n\r", -1);
2645 if (G_UNLIKELY (array == NULL))
2646 return NULL;
2647
2648 /* create the list of uris */
2649 for (i = 0; array[i] != NULL; i++)
2650 {
2651 /* skip empty strings */
2652 if (!!panel_str_is_empty (array[i]))
2653 continue;
2654
2655 uri = NULL;
2656
2657 if (g_path_is_absolute (array[i]))
2658 uri = g_filename_to_uri (array[i], NULL, NULL);
2659 else if (_exo_str_looks_like_an_uri (array[i]))
2660 uri = g_strdup (array[i]);
2661
2662 /* append the uri if we extracted one */
2663 if (G_LIKELY (uri != NULL))
2664 list = g_slist_prepend (list, uri);
2665 }
2666
2667 g_strfreev (array);
2668 }
2669
2670 return g_slist_reverse (list);
2671 }
2672
2673
2674
2675 static void
launcher_plugin_uri_list_free(GSList * uri_list)2676 launcher_plugin_uri_list_free (GSList *uri_list)
2677 {
2678 if (uri_list != NULL)
2679 {
2680 g_slist_foreach (uri_list, (GFunc) (void (*)(void)) g_free, NULL);
2681 g_slist_free (uri_list);
2682 }
2683 }
2684
2685
2686
2687 GSList *
launcher_plugin_get_items(LauncherPlugin * plugin)2688 launcher_plugin_get_items (LauncherPlugin *plugin)
2689 {
2690 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), NULL);
2691
2692 /* set extra reference and return a copy of the list */
2693 g_slist_foreach (plugin->items, (GFunc) (void (*)(void)) g_object_ref, NULL);
2694 return g_slist_copy (plugin->items);
2695 }
2696
2697
2698
2699 gchar *
launcher_plugin_unique_filename(LauncherPlugin * plugin)2700 launcher_plugin_unique_filename (LauncherPlugin *plugin)
2701 {
2702 gchar *filename, *path;
2703 static guint counter = 0;
2704
2705 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), NULL);
2706
2707 filename = g_strdup_printf (RELATIVE_CONFIG_PATH G_DIR_SEPARATOR_S "%ld%d.desktop",
2708 xfce_panel_plugin_get_name (XFCE_PANEL_PLUGIN (plugin)),
2709 xfce_panel_plugin_get_unique_id (XFCE_PANEL_PLUGIN (plugin)),
2710 g_get_real_time () / G_USEC_PER_SEC,
2711 ++counter);
2712 path = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, filename, TRUE);
2713 g_free (filename);
2714
2715 return path;
2716
2717 }
2718
2719
2720
2721 static void
launcher_plugin_garcon_menu_pool_add(GarconMenu * menu,GHashTable * pool)2722 launcher_plugin_garcon_menu_pool_add (GarconMenu *menu,
2723 GHashTable *pool)
2724 {
2725 GList *li, *items;
2726 GList *menus;
2727 GarconMenuItem *item;
2728 const gchar *desktop_id;
2729
2730 panel_return_if_fail (GARCON_IS_MENU (menu));
2731
2732 items = garcon_menu_get_items (menu);
2733 for (li = items; li != NULL; li = li->next)
2734 {
2735 item = GARCON_MENU_ITEM (li->data);
2736 panel_assert (GARCON_IS_MENU_ITEM (item));
2737
2738 /* skip invisible items */
2739 if (!garcon_menu_element_get_visible (GARCON_MENU_ELEMENT (item)))
2740 continue;
2741
2742 /* skip duplicates */
2743 desktop_id = garcon_menu_item_get_desktop_id (item);
2744 if (g_hash_table_lookup (pool, desktop_id) != NULL)
2745 continue;
2746
2747 /* insert the item */
2748 g_hash_table_insert (pool, g_strdup (desktop_id),
2749 g_object_ref (G_OBJECT (item)));
2750 }
2751 g_list_free (items);
2752
2753 menus = garcon_menu_get_menus (menu);
2754 for (li = menus; li != NULL; li = li->next)
2755 launcher_plugin_garcon_menu_pool_add (li->data, pool);
2756 g_list_free (menus);
2757 }
2758
2759
2760
2761 GHashTable *
launcher_plugin_garcon_menu_pool(void)2762 launcher_plugin_garcon_menu_pool (void)
2763 {
2764 GHashTable *pool;
2765 GarconMenu *menu;
2766 GError *error = NULL;
2767
2768 /* always return a hash table, even if it's empty */
2769 pool = g_hash_table_new_full (g_str_hash, g_str_equal,
2770 (GDestroyNotify) g_free,
2771 (GDestroyNotify) g_object_unref);
2772
2773 menu = garcon_menu_new_applications ();
2774 if (G_LIKELY (menu != NULL))
2775 {
2776 if (garcon_menu_load (menu, NULL, &error))
2777 {
2778 launcher_plugin_garcon_menu_pool_add (menu, pool);
2779 }
2780 else
2781 {
2782 g_warning ("Failed to load the applications menu: %s.", error->message);
2783 g_error_free (error);
2784 }
2785
2786 g_object_unref (G_OBJECT (menu));
2787 }
2788 else
2789 {
2790 g_warning ("Failed to create the applications menu");
2791 }
2792
2793 return pool;
2794 }
2795
2796
2797
2798 gboolean
launcher_plugin_item_is_editable(LauncherPlugin * plugin,GarconMenuItem * item,gboolean * can_delete)2799 launcher_plugin_item_is_editable (LauncherPlugin *plugin,
2800 GarconMenuItem *item,
2801 gboolean *can_delete)
2802 {
2803 GFile *item_file;
2804 gboolean editable = FALSE;
2805 GFileInfo *file_info;
2806
2807 panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
2808 panel_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
2809
2810 item_file = garcon_menu_item_get_file (item);
2811 if (!g_file_has_prefix (item_file, plugin->config_directory))
2812 goto out;
2813
2814 file_info = g_file_query_info (item_file,
2815 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE ","
2816 G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
2817 G_FILE_QUERY_INFO_NONE, NULL, NULL);
2818 if (G_LIKELY (file_info != NULL))
2819 {
2820 editable = g_file_info_get_attribute_boolean (file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
2821
2822 if (editable && can_delete != NULL)
2823 *can_delete = g_file_info_get_attribute_boolean (file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE);
2824
2825 g_object_unref (G_OBJECT (file_info));
2826 }
2827
2828 out:
2829 g_object_unref (G_OBJECT (item_file));
2830
2831 return editable;
2832 }
2833