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