[Xfce4-commits] <xfce4-panel:devel> Add most of the launchercode from svn trunk back in the experimental branch.

Nick Schermer nick at xfce.org
Tue Aug 11 20:28:37 CEST 2009


Updating branch refs/heads/devel
         to 7de46a62f3e69d5fa01ff689125554df9dc7bc1f (commit)
       from eab2c2dd87ef1f7696a0aecfc8da9d9d1d4eabae (commit)

commit 7de46a62f3e69d5fa01ff689125554df9dc7bc1f
Author: Nick Schermer <nick at xfce.org>
Date:   Thu Mar 5 22:21:01 2009 +0100

    Add most of the launchercode from svn trunk back in the experimental branch.

 plugins/launcher/launcher-dialog.c     |   19 +-
 plugins/launcher/launcher-dialog.glade |   13 +-
 plugins/launcher/launcher-dialog.h     |    2 +-
 plugins/launcher/launcher.c            |  974 +++++++++++++++++++++++++++----
 plugins/launcher/launcher.h            |   30 +-
 5 files changed, 886 insertions(+), 152 deletions(-)

diff --git a/plugins/launcher/launcher-dialog.c b/plugins/launcher/launcher-dialog.c
index 528e113..9b1c55c 100644
--- a/plugins/launcher/launcher-dialog.c
+++ b/plugins/launcher/launcher-dialog.c
@@ -1,7 +1,7 @@
 /* $Id$ */
 /*
  * Copyright (C) 2008-2009 Nick Schermer <nick at xfce.org>
- * 
+ *
  * This library is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
  * Software Foundation; either version 2 of the License, or (at your option)
@@ -38,10 +38,10 @@
 
 typedef struct
 {
-  LauncherPlugin *plugin;
-  GtkBuilder     *builder;
-  guint           idle_populate_id;
-  XfconfChannel  *channel;
+  XfceLauncherPlugin *plugin;
+  GtkBuilder         *builder;
+  guint               idle_populate_id;
+  XfconfChannel      *channel;
 }
 LauncherPluginDialog;
 
@@ -175,7 +175,7 @@ launcher_dialog_add_populate_model_idle (gpointer user_data)
   panel_return_val_if_fail (GTK_IS_BUILDER (dialog->builder), FALSE);
 
   GDK_THREADS_ENTER ();
-  
+
   /* initialize the menu library */
   xfce_menu_init (NULL);
 
@@ -199,7 +199,7 @@ launcher_dialog_add_populate_model_idle (gpointer user_data)
       /* TODO */
       g_error_free (error);
     }
-      
+
   /* shutdown menu library */
   xfce_menu_shutdown ();
 
@@ -656,8 +656,9 @@ launcher_dialog_items_changed (XfconfChannel        *channel,
 
 
 void
-launcher_dialog_show (LauncherPlugin *plugin)
+launcher_dialog_show (XfceLauncherPlugin *plugin)
 {
+  LauncherPluginDialog *dialog;
   GtkBuilder           *builder;
   GObject              *window, *object, *item;
   guint                 i;
@@ -665,7 +666,7 @@ launcher_dialog_show (LauncherPlugin *plugin)
   const gchar          *button_names[] = { "item-add", "item-delete",
                                            "item-move-up", "item-move-down",
                                            "item-edit", "item-new" };
-  LauncherPluginDialog *dialog;
+
 
   panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
 
diff --git a/plugins/launcher/launcher-dialog.glade b/plugins/launcher/launcher-dialog.glade
index b1ffac5..071a9c4 100644
--- a/plugins/launcher/launcher-dialog.glade
+++ b/plugins/launcher/launcher-dialog.glade
@@ -3,7 +3,7 @@
   <!-- interface-requires gtk+ 2.12 -->
   <!-- interface-requires libxfce4ui 0.0 -->
   <!-- interface-naming-policy project-wide -->
-  <object class="GtkListStore" id="arrow-position-store">
+  <object class="GtkListStore" id="arrow-position-model">
     <columns>
       <!-- column-name title -->
       <column type="gchararray"/>
@@ -13,16 +13,16 @@
         <col id="0" translatable="yes">Default</col>
       </row>
       <row>
-        <col id="0" translatable="yes">Left</col>
+        <col id="0" translatable="yes">North</col>
       </row>
       <row>
-        <col id="0" translatable="yes">Right</col>
+        <col id="0" translatable="yes">West</col>
       </row>
       <row>
-        <col id="0" translatable="yes">Top</col>
+        <col id="0" translatable="yes">East</col>
       </row>
       <row>
-        <col id="0" translatable="yes">Bottom</col>
+        <col id="0" translatable="yes">South</col>
       </row>
       <row>
         <col id="0" translatable="yes">Inside Button</col>
@@ -323,8 +323,7 @@
                     <child>
                       <object class="GtkComboBox" id="arrow-position">
                         <property name="visible">True</property>
-                        <property name="active">0</property>
-                        <property name="button_sensitivity">on</property>
+                        <property name="model">arrow-position-model</property>
                         <child>
                           <object class="GtkCellRendererText" id="cellrenderertext1"/>
                           <attributes>
diff --git a/plugins/launcher/launcher-dialog.h b/plugins/launcher/launcher-dialog.h
index 193571a..5be30e8 100644
--- a/plugins/launcher/launcher-dialog.h
+++ b/plugins/launcher/launcher-dialog.h
@@ -22,6 +22,6 @@
 
 #include "launcher.h"
 
-void launcher_dialog_show (LauncherPlugin *plugin);
+void launcher_dialog_show (XfceLauncherPlugin *plugin);
 
 #endif /* !__XFCE_LAUNCHER_DIALOG_H__ */
diff --git a/plugins/launcher/launcher.c b/plugins/launcher/launcher.c
index b836bba..5d2d300 100644
--- a/plugins/launcher/launcher.c
+++ b/plugins/launcher/launcher.c
@@ -1,7 +1,7 @@
 /* $Id$ */
 /*
  * Copyright (C) 2008-2009 Nick Schermer <nick at xfce.org>
- * 
+ *
  * This library is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
  * Software Foundation; either version 2 of the License, or (at your option)
@@ -34,7 +34,12 @@
 #include "launcher.h"
 #include "launcher-dialog.h"
 
+#define ARROW_BUTTON_SIZE (12)
+#define DEFAULT_MENU_ICON_SIZE (24)
+#define MENU_POPUP_DELAY (225)
+
 
+static void launcher_plugin_style_set (GtkWidget *widget, GtkStyle *previous_style);
 
 static void launcher_plugin_construct (XfcePanelPlugin *panel_plugin);
 static void launcher_plugin_free_data (XfcePanelPlugin *panel_plugin);
@@ -44,14 +49,30 @@ static void launcher_plugin_save (XfcePanelPlugin *panel_plugin);
 static void launcher_plugin_configure_plugin (XfcePanelPlugin *panel_plugin);
 static void launcher_plugin_screen_position_changed (XfcePanelPlugin *panel_plugin, gint position);
 
-static void launcher_plugin_button_set_icon (LauncherPlugin *plugin);
+static void launcher_plugin_property_changed (XfconfChannel *channel, const gchar *property_name, const GValue *value, XfceLauncherPlugin *plugin);
+static void launcher_plugin_icon_theme_changed (GtkIconTheme *icon_theme, XfceLauncherPlugin *plugin);
+
+static void launcher_plugin_pack_widgets (XfceLauncherPlugin *plugin);
+
+static void launcher_plugin_menu_deactivate (GtkWidget *menu, XfceLauncherPlugin *plugin);
+static gboolean launcher_plugin_menu_item_released (GtkMenuItem *widget, GdkEventButton *event, XfceMenuItem *item);
+static void launcher_plugin_menu_construct (XfceLauncherPlugin *plugin);
+static void launcher_plugin_menu_popup_destroyed (gpointer user_data);
+static gboolean launcher_plugin_menu_popup (gpointer user_data);
+static void launcher_plugin_menu_destroy (XfceLauncherPlugin *plugin);
+
+static void launcher_plugin_button_set_icon (XfceLauncherPlugin *plugin);
 static void launcher_plugin_button_state_changed (GtkWidget *button_a, GtkStateType state, GtkWidget *button_b);
-static gboolean launcher_plugin_button_press_event (GtkWidget *button, GdkEventButton *event, LauncherPlugin *plugin);
-static gboolean launcher_plugin_button_release_event (GtkWidget *button, GdkEventButton *event, LauncherPlugin *plugin);
-static gboolean launcher_plugin_button_query_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, LauncherPlugin *plugin);
-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);
+static gboolean launcher_plugin_button_press_event (GtkWidget *button, GdkEventButton *event, XfceLauncherPlugin *plugin);
+static gboolean launcher_plugin_button_release_event (GtkWidget *button, GdkEventButton *event, XfceLauncherPlugin *plugin);
+static gboolean launcher_plugin_button_query_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, XfceLauncherPlugin *plugin);
+static void launcher_plugin_button_drag_data_received (GtkWidget *widget,GdkDragContext *context, gint x,gint y,GtkSelectionData *selection_data, guint info, guint drag_time, XfceLauncherPlugin *plugin);
+static gboolean launcher_plugin_button_expose_event (GtkWidget *widget, GdkEventExpose *event, XfceLauncherPlugin *launcher);
+
+static void launcher_plugin_arrow_visibility (XfceLauncherPlugin *plugin);
+static gboolean launcher_plugin_arrow_press_event (GtkWidget *button, GdkEventButton *event, XfceLauncherPlugin *plugin);
 
-static void launcher_plugin_items_load (LauncherPlugin *plugin);
+static void launcher_plugin_items_load (XfceLauncherPlugin *plugin);
 static gboolean launcher_plugin_item_query_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, XfceMenuItem *item);
 
 static gboolean launcher_plugin_item_exec_on_screen (XfceMenuItem *item, guint32 event_time, GdkScreen *screen, GSList *uri_list);
@@ -59,33 +80,41 @@ static void launcher_plugin_item_exec (XfceMenuItem *item, guint32 event_time, G
 static void launcher_plugin_item_exec_from_clipboard (XfceMenuItem *item, guint32 event_time, GdkScreen *screen);
 static void launcher_plugin_exec_append_quoted (GString *string, const gchar *unquoted);
 static gboolean launcher_plugin_exec_parse (XfceMenuItem *item, GSList *uri_list, gint *argc, gchar ***argv, GError **error);
+static GSList *launcher_plugin_uri_list_extract (GtkSelectionData *data);
+static void launcher_plugin_uri_list_free (GSList *uri_list);
 
 
 
-struct _LauncherPluginClass
+struct _XfceLauncherPluginClass
 {
   XfcePanelPluginClass __parent__;
 };
 
-struct _LauncherPlugin
+struct _XfceLauncherPlugin
 {
   XfcePanelPlugin __parent__;
 
-  XfconfChannel *channel;
+  XfconfChannel     *channel;
 
-  GtkWidget *box;
-  GtkWidget *button;
-  GtkWidget *arrow;
-  GtkWidget *image;
+  GtkWidget         *box;
+  GtkWidget         *button;
+  GtkWidget         *arrow;
+  GtkWidget         *image;
+  GtkWidget         *menu;
 
-  GSList    *items;
+  GSList            *items;
 
-  guint      disable_tooltips : 1;
+  guint              menu_timeout_id;
+  gint               menu_icon_size;
+
+  guint              disable_tooltips : 1;
+  guint              move_first : 1;
+  LauncherArrowType  arrow_position;
 };
 
 
 
-G_DEFINE_TYPE (LauncherPlugin, launcher_plugin, XFCE_TYPE_PANEL_PLUGIN);
+G_DEFINE_TYPE (XfceLauncherPlugin, launcher_plugin, XFCE_TYPE_PANEL_PLUGIN);
 
 
 
@@ -94,11 +123,20 @@ XFCE_PANEL_PLUGIN_REGISTER_OBJECT (XFCE_TYPE_LAUNCHER_PLUGIN);
 
 
 
+/* quark to attach the plugin to menu items */
+GQuark launcher_plugin_quark = 0;
+
+
+
 static void
-launcher_plugin_class_init (LauncherPluginClass *klass)
+launcher_plugin_class_init (XfceLauncherPluginClass *klass)
 {
+  GtkWidgetClass       *gtkwidget_class;
   XfcePanelPluginClass *plugin_class;
 
+  gtkwidget_class = GTK_WIDGET_CLASS (klass);
+  gtkwidget_class->style_set = launcher_plugin_style_set;
+
   plugin_class = XFCE_PANEL_PLUGIN_CLASS (klass);
   plugin_class->construct = launcher_plugin_construct;
   plugin_class->free_data = launcher_plugin_free_data;
@@ -107,48 +145,107 @@ launcher_plugin_class_init (LauncherPluginClass *klass)
   plugin_class->save = launcher_plugin_save;
   plugin_class->configure_plugin = launcher_plugin_configure_plugin;
   plugin_class->screen_position_changed = launcher_plugin_screen_position_changed;
+
+  gtk_widget_class_install_style_property (gtkwidget_class,
+                                           g_param_spec_int ("menu-icon-size",
+                                                             NULL,
+                                                             "Icon size (pixels) used in the menu",
+                                                             16, 128,
+                                                             DEFAULT_MENU_ICON_SIZE,
+                                                             EXO_PARAM_READABLE));
+
+  /* initialize the quark */
+  launcher_plugin_quark = g_quark_from_static_string ("xfce-launcher-plugin");
 }
 
 
 
 static void
-launcher_plugin_init (LauncherPlugin *plugin)
+launcher_plugin_init (XfceLauncherPlugin *plugin)
 {
+  GtkIconTheme *icon_theme;
+
+  plugin->disable_tooltips = FALSE;
+  plugin->move_first = FALSE;
+  plugin->arrow_position = LAUNCHER_ARROW_DEFAULT;
+  plugin->menu = NULL;
+  plugin->menu_timeout_id = 0;
+  plugin->menu_icon_size = DEFAULT_MENU_ICON_SIZE;
+
   /* initialize xfconf */
   xfconf_init (NULL);
 
   /* show the configure menu item */
   xfce_panel_plugin_menu_show_configure (XFCE_PANEL_PLUGIN (plugin));
+
+  /* monitor the default icon theme for changes */
+  icon_theme = gtk_icon_theme_get_default ();
+  if (G_LIKELY (icon_theme != NULL))
+    g_signal_connect (G_OBJECT (icon_theme), "changed",
+                      G_CALLBACK (launcher_plugin_icon_theme_changed), plugin);
+
+  /* create the panel widgets */
+  plugin->box = xfce_hvbox_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (plugin), plugin->box);
+  gtk_widget_show (plugin->box);
+
+  plugin->button = xfce_panel_create_button ();
+  gtk_box_pack_start (GTK_BOX (plugin->box), plugin->button, TRUE, TRUE, 0);
+  xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), plugin->button);
+  gtk_widget_set_has_tooltip (plugin->button, TRUE);
+  gtk_widget_show (plugin->button);
+  g_signal_connect (G_OBJECT (plugin->button), "button-press-event",
+                    G_CALLBACK (launcher_plugin_button_press_event), plugin);
+  g_signal_connect (G_OBJECT (plugin->button), "button-release-event",
+                    G_CALLBACK (launcher_plugin_button_release_event), plugin);
+  g_signal_connect (G_OBJECT (plugin->button), "query-tooltip",
+                    G_CALLBACK (launcher_plugin_button_query_tooltip), plugin);
+  g_signal_connect (G_OBJECT (plugin->button), "drag-data-received",
+                    G_CALLBACK (launcher_plugin_button_drag_data_received), plugin);
+  g_signal_connect_after (G_OBJECT (plugin->button), "expose-event",
+                          G_CALLBACK (launcher_plugin_button_expose_event), plugin);
+
+  plugin->image = xfce_scaled_image_new ();
+  gtk_container_add (GTK_CONTAINER (plugin->button), plugin->image);
+  gtk_widget_show (plugin->image);
+
+  plugin->arrow = xfce_arrow_button_new (GTK_ARROW_UP);
+  gtk_box_pack_start (GTK_BOX (plugin->box), plugin->arrow, FALSE, FALSE, 0);
+  xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), plugin->arrow);
+  gtk_button_set_relief (GTK_BUTTON (plugin->arrow), GTK_RELIEF_NONE);
+  g_signal_connect (G_OBJECT (plugin->arrow), "button-press-event",
+                    G_CALLBACK (launcher_plugin_arrow_press_event), plugin);
+
+  /* sync button states */
+  g_signal_connect (G_OBJECT (plugin->button), "state-changed",
+                    G_CALLBACK (launcher_plugin_button_state_changed),
+                    plugin->arrow);
+  g_signal_connect (G_OBJECT (plugin->arrow), "state-changed",
+                    G_CALLBACK (launcher_plugin_button_state_changed),
+                    plugin->button);
 }
 
 
 
 static void
-launcher_plugin_property_changed (XfconfChannel  *channel,
-                                  const gchar    *property_name,
-                                  const GValue   *value,
-                                  LauncherPlugin *plugin)
+launcher_plugin_style_set (GtkWidget *widget,
+                           GtkStyle  *previous_style)
 {
-  panel_return_if_fail (XFCONF_IS_CHANNEL (channel));
-  panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
-  panel_return_if_fail (plugin->channel == channel);
+  XfceLauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (widget);
+  gint                menu_icon_size;
 
-  if (exo_str_is_equal (property_name, "/disable-tooltips"))
-    {
-      plugin->disable_tooltips = g_value_get_boolean (value);
-    }
-  else if (exo_str_is_equal (property_name, "/items"))
-    {
-      /* free items */
-      g_slist_foreach (plugin->items, (GFunc) g_object_unref, NULL);
-      g_slist_free (plugin->items);
-      plugin->items = NULL;
+  /* let gtk update the widget style */
+  (*GTK_WIDGET_CLASS (launcher_plugin_parent_class)->style_set) (widget, previous_style);
 
-      /* load the new items */
-      launcher_plugin_items_load (plugin);
+  /* read the style properties */
+  gtk_widget_style_get (widget,
+                        "menu-icon-size", &menu_icon_size,
+                        NULL);
 
-      /* update the icon */
-      launcher_plugin_button_set_icon (plugin);
+  if (plugin->menu_icon_size != menu_icon_size)
+    {
+      plugin->menu_icon_size = menu_icon_size;
+      launcher_plugin_menu_destroy (plugin);
     }
 }
 
@@ -157,65 +254,30 @@ launcher_plugin_property_changed (XfconfChannel  *channel,
 static void
 launcher_plugin_construct (XfcePanelPlugin *panel_plugin)
 {
-  LauncherPlugin      *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
-  GtkWidget           *widget;
+  XfceLauncherPlugin  *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
   const gchar * const *filenames;
   guint                i;
   XfceMenuItem        *item;
+  guint                value;
 
   /* open the xfconf channel */
   plugin->channel = xfce_panel_plugin_xfconf_channel_new (panel_plugin);
   g_signal_connect (G_OBJECT (plugin->channel), "property-changed",
                     G_CALLBACK (launcher_plugin_property_changed), plugin);
 
-  /* create the dialog widgets */
-  plugin->box = xfce_hvbox_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 0);
-  gtk_container_add (GTK_CONTAINER (plugin), plugin->box);
-  gtk_widget_show (plugin->box);
-
-  plugin->button = widget = xfce_panel_create_button ();
-  gtk_box_pack_start (GTK_BOX (plugin->box), widget, TRUE, TRUE, 0);
-  xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), widget);
-  gtk_widget_set_has_tooltip (widget, TRUE);
-  gtk_widget_show (widget);
-  g_signal_connect (G_OBJECT (widget), "button-press-event",
-                    G_CALLBACK (launcher_plugin_button_press_event),
-                    plugin);
-  g_signal_connect (G_OBJECT (widget), "button-release-event",
-                    G_CALLBACK (launcher_plugin_button_release_event),
-                    plugin);
-  g_signal_connect (G_OBJECT (widget), "query-tooltip",
-                    G_CALLBACK (launcher_plugin_button_query_tooltip),
-                    plugin);
-  g_signal_connect (G_OBJECT (widget), "drag-data-received",
-                    G_CALLBACK (launcher_plugin_button_drag_data_received),
-                    plugin);
-
-  plugin->image = xfce_scaled_image_new ();
-  gtk_container_add (GTK_CONTAINER (plugin->button), plugin->image);
-  gtk_widget_show (plugin->image);
-
-  plugin->arrow = xfce_arrow_button_new (GTK_ARROW_UP);
-  gtk_box_pack_start (GTK_BOX (plugin->box), plugin->arrow, FALSE, FALSE, 0);
-  xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), plugin->arrow);
-  gtk_button_set_relief (GTK_BUTTON (plugin->arrow), GTK_RELIEF_NONE);
-
-  /* sync button states */
-  g_signal_connect (G_OBJECT (plugin->button), "state-changed",
-                    G_CALLBACK (launcher_plugin_button_state_changed),
-                    plugin->arrow);
-  g_signal_connect (G_OBJECT (plugin->arrow), "state-changed",
-                    G_CALLBACK (launcher_plugin_button_state_changed),
-                    plugin->button);
-
   /* load global settings */
-  plugin->disable_tooltips = xfconf_channel_get_bool (plugin->channel,
-                                                      "/disable-tooltips",
-                                                      FALSE);
+  plugin->disable_tooltips =
+      xfconf_channel_get_bool (plugin->channel, "/disable-tooltips", FALSE);
+  plugin->move_first =
+      xfconf_channel_get_bool (plugin->channel, "/move-first", FALSE);
+  value = xfconf_channel_get_uint (plugin->channel, "/arrow-position",
+                                   LAUNCHER_ARROW_DEFAULT);
+  plugin->arrow_position = MIN (value, LAUNCHER_ARROW_MAX);
 
   /* load the items */
   launcher_plugin_items_load (plugin);
 
+  /* handle and empty plugin */
   if (G_UNLIKELY (plugin->items == NULL))
     {
       /* get the plugin arguments list */
@@ -234,6 +296,12 @@ launcher_plugin_construct (XfcePanelPlugin *panel_plugin)
 
   /* update the icon */
   launcher_plugin_button_set_icon (plugin);
+
+  /* update the arrow visibility */
+  launcher_plugin_arrow_visibility (plugin);
+
+  /* repack the widgets */
+  launcher_plugin_pack_widgets (plugin);
 }
 
 
@@ -241,11 +309,14 @@ launcher_plugin_construct (XfcePanelPlugin *panel_plugin)
 static void
 launcher_plugin_free_data (XfcePanelPlugin *panel_plugin)
 {
-  LauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
+  XfceLauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
 
   /* release the xfconf channel */
   g_object_unref (G_OBJECT (plugin->channel));
 
+  /* destroy the menu and timeout */
+  launcher_plugin_menu_destroy (plugin);
+
   /* shutdown xfconf */
   xfconf_shutdown ();
 
@@ -260,7 +331,16 @@ static void
 launcher_plugin_orientation_changed (XfcePanelPlugin *panel_plugin,
                                      GtkOrientation   orientation)
 {
+  /* update the widget order */
+  launcher_plugin_pack_widgets (XFCE_LAUNCHER_PLUGIN (panel_plugin));
 
+  /* update the arrow button */
+  launcher_plugin_screen_position_changed (panel_plugin,
+      xfce_panel_plugin_get_screen_position (panel_plugin));
+
+  /* update the plugin size */
+  launcher_plugin_size_changed (panel_plugin,
+      xfce_panel_plugin_get_size (panel_plugin));
 }
 
 
@@ -269,7 +349,56 @@ static gboolean
 launcher_plugin_size_changed (XfcePanelPlugin *panel_plugin,
                               gint             size)
 {
-  gtk_widget_set_size_request (GTK_WIDGET (panel_plugin), size, size);
+  XfceLauncherPlugin    *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
+  gint                   p_width, p_height;
+  gint                   a_width, a_height;
+  gboolean               horizontal;
+  LauncherArrowType      arrow_position;
+
+  /* initialize the plugin size */
+  p_width = p_height = size;
+  a_width = a_height = -1;
+
+  /* add the arrow size */
+  if (GTK_WIDGET_VISIBLE (plugin->arrow))
+    {
+      /* if the panel is horizontal */
+      horizontal = !!(xfce_panel_plugin_get_orientation (panel_plugin) ==
+          GTK_ORIENTATION_HORIZONTAL);
+
+      /* set tmp arrow position */
+      arrow_position = plugin->arrow_position;
+      if (arrow_position == LAUNCHER_ARROW_DEFAULT)
+        arrow_position = horizontal ? LAUNCHER_ARROW_WEST : LAUNCHER_ARROW_NORTH;
+
+      switch (plugin->arrow_position)
+        {
+          case LAUNCHER_ARROW_NORTH:
+          case LAUNCHER_ARROW_SOUTH:
+            a_height = ARROW_BUTTON_SIZE;
+
+            if (horizontal)
+              p_width -= ARROW_BUTTON_SIZE;
+            else
+              p_height += ARROW_BUTTON_SIZE;
+            break;
+
+          default:
+            a_width = ARROW_BUTTON_SIZE;
+
+            if (horizontal)
+              p_width += ARROW_BUTTON_SIZE;
+            else
+              p_height -= ARROW_BUTTON_SIZE;
+            break;
+        }
+
+      /* set the arrow size */
+      gtk_widget_set_size_request (plugin->arrow, a_width, a_height);
+    }
+
+  /* set the panel plugin size */
+  gtk_widget_set_size_request (GTK_WIDGET (panel_plugin), p_width, p_height);
 
   return TRUE;
 }
@@ -279,15 +408,19 @@ launcher_plugin_size_changed (XfcePanelPlugin *panel_plugin,
 static void
 launcher_plugin_save (XfcePanelPlugin *panel_plugin)
 {
-  LauncherPlugin  *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
-  gchar          **filenames;
-  guint            i, length;
-  GSList          *li;
-  XfceMenuItem    *item;
+  XfceLauncherPlugin  *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
+  gchar              **filenames;
+  guint                i, length;
+  GSList              *li;
+  XfceMenuItem        *item;
 
   /* save the global settings */
   xfconf_channel_set_bool (plugin->channel, "/disable-tooltips",
                            plugin->disable_tooltips);
+  xfconf_channel_set_bool (plugin->channel, "/move-first",
+                           plugin->move_first);
+  xfconf_channel_set_uint (plugin->channel, "/arrow-position",
+                           plugin->arrow_position);
 
   length = g_slist_length (plugin->items);
   if (G_LIKELY (length > 0))
@@ -322,13 +455,319 @@ static void
 launcher_plugin_screen_position_changed (XfcePanelPlugin *panel_plugin,
                                          gint             position)
 {
+  XfceLauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (panel_plugin);
+
+  /* set the new arrow direction */
+  xfce_arrow_button_set_arrow_type (XFCE_ARROW_BUTTON (plugin->arrow),
+      xfce_panel_plugin_arrow_type (panel_plugin));
 
+  /* destroy the menu */
+  launcher_plugin_menu_destroy (plugin);
 }
 
 
 
 static void
-launcher_plugin_button_set_icon (LauncherPlugin *plugin)
+launcher_plugin_property_changed (XfconfChannel      *channel,
+                                  const gchar        *property_name,
+                                  const GValue       *value,
+                                  XfceLauncherPlugin *plugin)
+{
+  panel_return_if_fail (XFCONF_IS_CHANNEL (channel));
+  panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
+  panel_return_if_fail (plugin->channel == channel);
+
+  /* destroy the menu, all the setting changes need this */
+  launcher_plugin_menu_destroy (plugin);
+
+  if (exo_str_is_equal (property_name, "/disable-tooltips"))
+    {
+      plugin->disable_tooltips = g_value_get_boolean (value);
+    }
+  else if (exo_str_is_equal (property_name, "/move-first"))
+    {
+      plugin->move_first = g_value_get_boolean (value);
+    }
+  else if (exo_str_is_equal (property_name, "/arrow-position"))
+    {
+      plugin->arrow_position = MIN (g_value_get_uint (value),
+                                    LAUNCHER_ARROW_MAX);
+
+      goto update_arrow;
+
+    }
+  else if (exo_str_is_equal (property_name, "/items"))
+    {
+      /* free items */
+      g_slist_foreach (plugin->items, (GFunc) g_object_unref, NULL);
+      g_slist_free (plugin->items);
+      plugin->items = NULL;
+
+      /* load the new items */
+      launcher_plugin_items_load (plugin);
+
+      /* update the icon */
+      launcher_plugin_button_set_icon (plugin);
+
+      update_arrow:
+
+      /* update the arrow button visibility */
+      launcher_plugin_arrow_visibility (plugin);
+
+      /* repack the widgets */
+      launcher_plugin_pack_widgets (plugin);
+
+      /* update the plugin size */
+      launcher_plugin_size_changed (XFCE_PANEL_PLUGIN (plugin),
+          xfce_panel_plugin_get_size (XFCE_PANEL_PLUGIN (plugin)));
+    }
+}
+
+
+
+static void
+launcher_plugin_icon_theme_changed (GtkIconTheme       *icon_theme,
+                                    XfceLauncherPlugin *plugin)
+{
+  panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
+  panel_return_if_fail (GTK_IS_ICON_THEME (icon_theme));
+
+  /* update the button icon */
+  launcher_plugin_button_set_icon (plugin);
+
+  /* destroy the menu */
+  launcher_plugin_menu_destroy (plugin);
+}
+
+
+
+static void
+launcher_plugin_pack_widgets (XfceLauncherPlugin *plugin)
+{
+  LauncherArrowType pos = plugin->arrow_position;
+  gboolean          rtl;
+
+  panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
+
+  /* leave when the arrow button is not visible */
+  if (!GTK_WIDGET_VISIBLE (plugin->arrow)
+      || pos == LAUNCHER_ARROW_INTERNAL)
+    return;
+
+  if (pos == LAUNCHER_ARROW_DEFAULT)
+    {
+      /* get the plugin direction */
+      rtl = !!(gtk_widget_get_direction (GTK_WIDGET (plugin)) == GTK_TEXT_DIR_RTL);
+
+      if (xfce_panel_plugin_get_orientation (XFCE_PANEL_PLUGIN (plugin)) ==
+              GTK_ORIENTATION_HORIZONTAL)
+        pos = rtl ? LAUNCHER_ARROW_WEST : LAUNCHER_ARROW_EAST;
+      else
+        pos = rtl ? LAUNCHER_ARROW_NORTH : LAUNCHER_ARROW_SOUTH;
+    }
+
+  /* set the position of the arrow button in the box */
+  gtk_box_reorder_child (GTK_BOX (plugin->box), plugin->arrow,
+      (pos == LAUNCHER_ARROW_WEST || pos == LAUNCHER_ARROW_NORTH) ? 0 : -1);
+
+  /* set the orientation of the hvbox */
+  xfce_hvbox_set_orientation (XFCE_HVBOX (plugin->box),
+      !!(pos == LAUNCHER_ARROW_WEST || pos == LAUNCHER_ARROW_EAST) ?
+          GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
+}
+
+
+
+static void
+launcher_plugin_menu_deactivate (GtkWidget          *menu,
+                                 XfceLauncherPlugin *plugin)
+{
+  panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
+  panel_return_if_fail (plugin->menu == menu);
+
+  /* deactivate the arrow button */
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (plugin->arrow), FALSE);
+}
+
+
+
+static gboolean
+launcher_plugin_menu_item_released (GtkMenuItem    *widget,
+                                    GdkEventButton *event,
+                                    XfceMenuItem   *item)
+{
+  XfceLauncherPlugin *plugin;
+  GdkScreen          *screen;
+
+  panel_return_val_if_fail (GTK_IS_MENU_ITEM (widget), FALSE);
+  panel_return_val_if_fail (XFCE_IS_MENU_ITEM (item), FALSE);
+
+  /* get the widget screen */
+  screen = gtk_widget_get_screen (GTK_WIDGET (widget));
+
+  /* launch the command */
+  if (G_UNLIKELY (event->button == 2))
+    launcher_plugin_item_exec_from_clipboard (item, event->time, screen);
+  else
+    launcher_plugin_item_exec (item, event->time, screen, NULL);
+
+  /* get the plugin */
+  plugin = g_object_get_qdata (G_OBJECT (widget), launcher_plugin_quark);
+  panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
+
+  /* move the item to the first position if enabled */
+  if (G_UNLIKELY (plugin->move_first))
+    {
+      /* prepend the item in the list */
+      plugin->items = g_slist_remove (plugin->items, item);
+      plugin->items = g_slist_prepend (plugin->items, item);
+
+      /* destroy the menu and update the icon */
+      launcher_plugin_menu_destroy (plugin);
+      launcher_plugin_button_set_icon (plugin);
+    }
+
+  return FALSE;
+}
+
+
+
+static void
+launcher_plugin_menu_construct (XfceLauncherPlugin *plugin)
+{
+  GtkArrowType  arrow_type;
+  guint         n;
+  XfceMenuItem *item;
+  GtkWidget    *mi, *image;
+  const gchar  *name, *icon_name;
+  GSList       *li;
+  GdkPixbuf    *pixbuf;
+
+  panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
+  panel_return_if_fail (plugin->menu == NULL);
+
+  /* create a new menu */
+  plugin->menu = gtk_menu_new ();
+  gtk_menu_attach_to_widget (GTK_MENU (plugin->menu), GTK_WIDGET (plugin), NULL);
+  g_signal_connect (G_OBJECT (plugin->menu), "deactivate",
+                    G_CALLBACK (launcher_plugin_menu_deactivate), plugin);
+
+  /* get the arrow type of the plugin */
+  arrow_type = xfce_arrow_button_get_arrow_type (XFCE_ARROW_BUTTON (plugin->arrow));
+
+  /* walk through the menu entries */
+  for (li = plugin->items, n = 0; li != NULL; li = li->next, n++)
+    {
+      /* skip the first entry when the arrow is visible */
+      if (n == 0 && plugin->arrow_position != LAUNCHER_ARROW_INTERNAL)
+        continue;
+
+      /* get the item data */
+      item = XFCE_MENU_ITEM (li->data);
+
+      /* create the menu item */
+      name = xfce_menu_item_get_name (item);
+      mi = gtk_image_menu_item_new_with_label (IS_STRING (name) ? name :
+                                               _("Unnamed Item"));
+      g_object_set_qdata (G_OBJECT (mi), launcher_plugin_quark, plugin);
+      gtk_widget_set_has_tooltip (mi, TRUE);
+      gtk_widget_show (mi);
+      g_signal_connect (G_OBJECT (mi), "button-release-event",
+          G_CALLBACK (launcher_plugin_menu_item_released), item);
+
+      /* only connect the tooltip signal if tips are enabled */
+      if (plugin->disable_tooltips == FALSE)
+        g_signal_connect (G_OBJECT (mi), "query-tooltip",
+            G_CALLBACK (launcher_plugin_item_query_tooltip), item);
+
+      /* depending on the menu position we prepend or append */
+      if (G_UNLIKELY (arrow_type == GTK_ARROW_DOWN))
+        gtk_menu_shell_append (GTK_MENU_SHELL (plugin->menu), mi);
+      else
+        gtk_menu_shell_prepend (GTK_MENU_SHELL (plugin->menu), mi);
+
+      /* set the icon if one is set */
+      icon_name = xfce_menu_item_get_icon_name (item);
+      if (IS_STRING (icon_name))
+        {
+          pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+                                             icon_name, plugin->menu_icon_size,
+                                             0, NULL);
+          if (G_LIKELY (pixbuf != NULL))
+            {
+              image = gtk_image_new_from_pixbuf (pixbuf);
+              gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), image);
+              gtk_widget_show (image);
+
+              g_object_unref (G_OBJECT (pixbuf));
+            }
+        }
+    }
+}
+
+
+
+static void
+launcher_plugin_menu_popup_destroyed (gpointer user_data)
+{
+   XFCE_LAUNCHER_PLUGIN (user_data)->menu_timeout_id = 0;
+}
+
+
+
+static gboolean
+launcher_plugin_menu_popup (gpointer user_data)
+{
+  XfceLauncherPlugin *plugin = XFCE_LAUNCHER_PLUGIN (user_data);
+
+  panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
+
+  GDK_THREADS_ENTER ();
+
+  /* construct the menu if needed */
+  if (plugin->menu == NULL)
+    launcher_plugin_menu_construct (plugin);
+
+  /* toggle the arrow button */
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (plugin->arrow), TRUE);
+
+  /* popup the menu */
+  gtk_menu_popup (GTK_MENU (plugin->menu), NULL, NULL,
+                  xfce_panel_plugin_position_menu,
+                  XFCE_PANEL_PLUGIN (plugin), 1,
+                  gtk_get_current_event_time ());
+
+  GDK_THREADS_LEAVE ();
+
+  return FALSE;
+}
+
+
+
+static void
+launcher_plugin_menu_destroy (XfceLauncherPlugin *plugin)
+{
+  panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
+
+  /* stop pending timeout */
+  if (plugin->menu_timeout_id != 0)
+    g_source_remove (plugin->menu_timeout_id);
+
+  if (plugin->menu != NULL)
+    {
+      /* destroy the menu */
+      gtk_widget_destroy (plugin->menu);
+      plugin->menu = NULL;
+
+      /* deactivate the toggle button */
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (plugin->arrow), FALSE);
+    }
+}
+
+
+
+static void
+launcher_plugin_button_set_icon (XfceLauncherPlugin *plugin)
 {
   XfceMenuItem *item;
   const gchar  *icon_name;
@@ -374,37 +813,72 @@ launcher_plugin_button_state_changed (GtkWidget    *button_a,
 
 
 static gboolean
-launcher_plugin_button_press_event (GtkWidget      *button,
-                                    GdkEventButton *event,
-                                    LauncherPlugin *plugin)
+launcher_plugin_button_press_event (GtkWidget          *button,
+                                    GdkEventButton     *event,
+                                    XfceLauncherPlugin *plugin)
 {
+  guint modifiers;
+
   panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
 
   /* do nothing on anything else then a single click */
   if (event->type != GDK_BUTTON_PRESS)
     return FALSE;
 
+  /* get the default accelerator modifier mask */
+  modifiers = event->state & gtk_accelerator_get_default_mod_mask ();
+
+  /* leave when button 1 is not pressed or shift is pressed */
+  if (event->button != 1 || modifiers == GDK_CONTROL_MASK)
+    return FALSE;
+
+  if (plugin->arrow_position == LAUNCHER_ARROW_INTERNAL
+      && LIST_HAS_TWO_OR_MORE_ENTRIES (plugin->items))
+    {
+      /* directly popup the menu */
+      launcher_plugin_menu_popup (plugin);
+    }
+  else if (plugin->menu_timeout_id == 0
+           && LIST_HAS_TWO_OR_MORE_ENTRIES (plugin->items))
+    {
+      /* start the popup timeout */
+      plugin->menu_timeout_id =
+          g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
+                              MENU_POPUP_DELAY,
+                              launcher_plugin_menu_popup, plugin,
+                              launcher_plugin_menu_popup_destroyed);
+    }
+
   return FALSE;
 }
 
 
 
 static gboolean
-launcher_plugin_button_release_event (GtkWidget      *button,
-                                      GdkEventButton *event,
-                                      LauncherPlugin *plugin)
+launcher_plugin_button_release_event (GtkWidget          *button,
+                                      GdkEventButton     *event,
+                                      XfceLauncherPlugin *plugin)
 {
   XfceMenuItem *item;
   GdkScreen    *screen;
 
   panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
 
-  if (G_UNLIKELY (plugin->items == NULL))
+  /* remove a delayed popup timeout */
+  if (plugin->menu_timeout_id != 0)
+    g_source_remove (plugin->menu_timeout_id);
+
+  /* leave when there are no menu items or there is an internal arrow */
+  if (plugin->items == NULL || GTK_BUTTON (button)->in_button == FALSE
+      || (plugin->arrow_position == LAUNCHER_ARROW_INTERNAL
+          && LIST_HAS_TWO_OR_MORE_ENTRIES (plugin->items)))
     return FALSE;
 
+  /* get the menu item and the screen */
   item = XFCE_MENU_ITEM (plugin->items->data);
   screen = gtk_widget_get_screen (button);
 
+  /* launcher the entry */
   if (event->button == 1)
     launcher_plugin_item_exec (item, event->time, screen, NULL);
   else if (event->button == 2)
@@ -418,12 +892,12 @@ launcher_plugin_button_release_event (GtkWidget      *button,
 
 
 static gboolean
-launcher_plugin_button_query_tooltip (GtkWidget      *widget,
-                                      gint            x,
-                                      gint            y,
-                                      gboolean        keyboard_mode,
-                                      GtkTooltip     *tooltip,
-                                      LauncherPlugin *plugin)
+launcher_plugin_button_query_tooltip (GtkWidget          *widget,
+                                      gint                x,
+                                      gint                y,
+                                      gboolean            keyboard_mode,
+                                      GtkTooltip         *tooltip,
+                                      XfceLauncherPlugin *plugin)
 {
   panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
 
@@ -441,24 +915,141 @@ launcher_plugin_button_query_tooltip (GtkWidget      *widget,
 
 
 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)
+launcher_plugin_button_drag_data_received (GtkWidget            *widget,
+                                           GdkDragContext       *context,
+                                           gint                  x,
+                                           gint                  y,
+                                           GtkSelectionData     *selection_data,
+                                           guint                 info,
+                                           guint                 drag_time,
+                                           XfceLauncherPlugin   *plugin)
 {
+  GSList *uri_list;
+
   panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
 
+  /* leave when there are not items or the arrow is internal */
+  if (plugin->arrow_position == LAUNCHER_ARROW_INTERNAL
+      || plugin->items == NULL)
+    return;
+
+  /* get the list of uris from the selection data */
+  uri_list = launcher_plugin_uri_list_extract (selection_data);
+  if (G_LIKELY (uri_list != NULL))
+    {
+      /* execute */
+      launcher_plugin_item_exec (XFCE_MENU_ITEM (plugin->items->data),
+                                 gtk_get_current_event_time (),
+                                 gtk_widget_get_screen (widget),
+                                 uri_list);
+
+      /* cleanup */
+      launcher_plugin_uri_list_free (uri_list);
+    }
+
   gtk_drag_finish (context, TRUE, FALSE, drag_time);
 }
 
 
 
+static gboolean
+launcher_plugin_button_expose_event (GtkWidget          *widget,
+                                     GdkEventExpose     *event,
+                                     XfceLauncherPlugin *plugin)
+{
+  GtkArrowType arrow_type;
+  gint         size, x, y, thickness, offset;
+
+  panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
+
+  /* leave when the arrow is not shown inside the button or there are
+   * no menu items */
+  if (plugin->arrow_position != LAUNCHER_ARROW_INTERNAL
+      || !LIST_HAS_TWO_OR_MORE_ENTRIES (plugin->items))
+    return FALSE;
+
+  /* get the arrow type */
+  arrow_type = xfce_arrow_button_get_arrow_type (XFCE_ARROW_BUTTON (plugin->arrow));
+
+  /* style thickness */
+  thickness = MAX (widget->style->xthickness, widget->style->ythickness);
+
+  /* size of the arrow and the start coordinates */
+  size = widget->allocation.width / 3;
+  x = widget->allocation.x + thickness;
+  y = widget->allocation.y + thickness;
+  offset = size + 2 * thickness;
+
+  /* calculate the position based on the arrow type */
+  switch (arrow_type)
+    {
+      case GTK_ARROW_UP:
+        /* north east */
+        x += widget->allocation.width - offset;
+        break;
+
+      case GTK_ARROW_DOWN:
+        /* south west */
+        y += widget->allocation.height - offset;
+        break;
+
+      case GTK_ARROW_RIGHT:
+        /* south east */
+        x += widget->allocation.width - offset;
+        y += widget->allocation.height - offset;
+        break;
+
+      default:
+        /* north west */
+        break;
+    }
+
+  /* paint the arrow */
+  gtk_paint_arrow (widget->style, widget->window,
+                   GTK_WIDGET_STATE (widget), GTK_SHADOW_IN,
+                   &(event->area), widget, "launcher_button",
+                   arrow_type, TRUE, x, y, size, size);
+
+  return FALSE;
+}
+
+
+
+static void
+launcher_plugin_arrow_visibility (XfceLauncherPlugin *plugin)
+{
+  panel_return_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin));
+
+  if (plugin->arrow_position != LAUNCHER_ARROW_INTERNAL
+       && LIST_HAS_TWO_OR_MORE_ENTRIES (plugin->items))
+    gtk_widget_show (plugin->arrow);
+  else
+    gtk_widget_hide (plugin->arrow);
+}
+
+
+
+static gboolean
+launcher_plugin_arrow_press_event (GtkWidget          *button,
+                                   GdkEventButton     *event,
+                                   XfceLauncherPlugin *plugin)
+{
+  panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), FALSE);
+
+  /* only popup when button 1 is pressed */
+  if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
+    {
+      launcher_plugin_menu_popup (plugin);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+
+
 static void
-launcher_plugin_items_load (LauncherPlugin *plugin)
+launcher_plugin_items_load (XfceLauncherPlugin *plugin)
 {
   gchar        **filenames;
   guint          i;
@@ -541,6 +1132,7 @@ launcher_plugin_item_exec_on_screen (XfceMenuItem *item,
   gboolean   succeed = FALSE;
 
   panel_return_val_if_fail (XFCE_IS_MENU_ITEM (item), FALSE);
+  panel_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
 
   /* parse the execute command */
   if (launcher_plugin_exec_parse (item, uri_list, NULL, &argv, &error))
@@ -560,8 +1152,11 @@ launcher_plugin_item_exec_on_screen (XfceMenuItem *item,
 
   if (G_UNLIKELY (succeed == FALSE))
     {
-      /* TODO make this some nice error dialog */
-      g_message ("Failed to launch.... (%s)", error->message);
+      /* show an error dialog */
+      xfce_dialog_show_error (screen, error, _("Failed to execute command \"%s\"."),
+                              xfce_menu_item_get_command (item));
+
+      /* cleanup */
       g_error_free (error);
     }
 
@@ -581,6 +1176,7 @@ launcher_plugin_item_exec (XfceMenuItem *item,
   const gchar *command;
 
   panel_return_if_fail (XFCE_IS_MENU_ITEM (item));
+  panel_return_if_fail (GDK_IS_SCREEN (screen));
 
   /* leave when there is nothing to execute */
   command = xfce_menu_item_get_command (item);
@@ -597,15 +1193,14 @@ launcher_plugin_item_exec (XfceMenuItem *item,
       for (li = uri_list; li != NULL && proceed; li = li->next)
         {
           fake.data = li->data;
-          proceed = launcher_plugin_item_exec_on_screen (item,
-                                                         event_time,
+          proceed = launcher_plugin_item_exec_on_screen (item, event_time,
                                                          screen, &fake);
         }
     }
   else
     {
-      launcher_plugin_item_exec_on_screen (item, event_time, screen,
-                                           uri_list);
+      launcher_plugin_item_exec_on_screen (item, event_time,
+                                           screen, uri_list);
     }
 }
 
@@ -613,12 +1208,51 @@ launcher_plugin_item_exec (XfceMenuItem *item,
 
 static void
 launcher_plugin_item_exec_from_clipboard (XfceMenuItem *item,
-                                          guint32        event_time,
-                                          GdkScreen     *screen)
+                                          guint32       event_time,
+                                          GdkScreen    *screen)
 {
+  GtkClipboard     *clipboard;
+  gchar            *text = NULL;
+  GSList           *uri_list;
+  GtkSelectionData  data;
+
   panel_return_if_fail (XFCE_IS_MENU_ITEM (item));
+  panel_return_if_fail (GDK_IS_SCREEN (screen));
+
+  /* get the primary clipboard text */
+  clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+  if (G_LIKELY (clipboard))
+    text = gtk_clipboard_wait_for_text (clipboard);
+
+  /* try the secondary keayboard if the text is empty */
+  if (IS_STRING (text))
+    {
+      /* get the secondary clipboard text */
+      clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+      if (G_LIKELY (clipboard))
+        text = gtk_clipboard_wait_for_text (clipboard);
+    }
 
-  /* TODO */
+  if (IS_STRING (text))
+    {
+      /* create fake selection data */
+      data.data = (guchar *) text;
+      data.length = strlen (text);
+      data.target = GDK_NONE;
+
+      /* extract the uris from the selection data */
+      uri_list = launcher_plugin_uri_list_extract (&data);
+
+      /* launch with the uri list */
+      launcher_plugin_item_exec (item, event_time,
+                                 screen, uri_list);
+
+      /* cleanup */
+      launcher_plugin_uri_list_free (uri_list);
+    }
+
+  /* cleanup */
+  g_free (text);
 }
 
 
@@ -740,8 +1374,96 @@ launcher_plugin_exec_parse (XfceMenuItem   *item,
 
 
 
+static GSList *
+launcher_plugin_uri_list_extract (GtkSelectionData *data)
+{
+  GSList  *list = NULL;
+  gchar  **array;
+  guint    i;
+  gchar   *uri;
+  gint     j;
+
+  /* leave if there is no data */
+  if (data->length <= 0)
+    return NULL;
+
+  /* extract the files */
+  if (data->target == gdk_atom_intern_static_string ("text/uri-list"))
+    {
+      /* extract the list of uris */
+      array = g_uri_list_extract_uris ((gchar *) data->data);
+      if (G_UNLIKELY (array == NULL))
+        return NULL;
+
+      /* create the list of uris */
+      for (i = 0; array[i] != NULL; i++)
+        {
+          if (IS_STRING (array[i]))
+            list = g_slist_prepend (list, array[i]);
+          else
+            g_free (array[i]);
+        }
+
+      /* cleanup */
+      g_free (array);
+    }
+  else
+    {
+      /* split the data on new lines */
+      array = g_strsplit_set ((const gchar *) data->data, "\n\r", -1);
+      if (G_UNLIKELY (array == NULL))
+        return NULL;
+
+      /* create the list of uris */
+      for (i = 0; array[i] != NULL; i++)
+        {
+          /* skip empty strings */
+          if (!IS_STRING (array[i]))
+            continue;
+
+          uri = NULL;
+
+          if (g_path_is_absolute (array[i]))
+            {
+              /* convert the filename to an uri */
+              uri = g_filename_to_uri (array[i], NULL, NULL);
+            }
+          else if (data->length > 6)
+            {
+              /* check if this looks like an other uri, if it does, use it */
+              for (j = 3; uri == NULL && j <= 5; j++)
+                if (g_str_has_prefix (array[i] + j, "://"))
+                  uri = g_strdup (array[i]);
+            }
+
+          /* append the uri if we extracted one */
+          if (G_LIKELY (uri != NULL))
+            list = g_slist_prepend (list, uri);
+        }
+
+      /* cleanup */
+      g_strfreev (array);
+    }
+
+  return g_slist_reverse (list);
+}
+
+
+
+static void
+launcher_plugin_uri_list_free (GSList *uri_list)
+{
+  if (uri_list != NULL)
+    {
+      g_slist_foreach (uri_list, (GFunc) g_free, NULL);
+      g_slist_free (uri_list);
+    }
+}
+
+
+
 XfconfChannel *
-launcher_plugin_get_channel (LauncherPlugin *plugin)
+launcher_plugin_get_channel (XfceLauncherPlugin *plugin)
 {
   panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), NULL);
   return plugin->channel;
@@ -750,7 +1472,7 @@ launcher_plugin_get_channel (LauncherPlugin *plugin)
 
 
 GSList *
-launcher_plugin_get_items (LauncherPlugin *plugin)
+launcher_plugin_get_items (XfceLauncherPlugin *plugin)
 {
   panel_return_val_if_fail (XFCE_IS_LAUNCHER_PLUGIN (plugin), NULL);
   return plugin->items;
diff --git a/plugins/launcher/launcher.h b/plugins/launcher/launcher.h
index 67b1964..4b6eae9 100644
--- a/plugins/launcher/launcher.h
+++ b/plugins/launcher/launcher.h
@@ -1,7 +1,7 @@
 /* $Id$ */
 /*
  * Copyright (C) 2008-2009 Nick Schermer <nick at xfce.org>
- * 
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the Free
  * Software Foundation; either version 2 of the License, or (at your option)
@@ -27,22 +27,34 @@
 
 G_BEGIN_DECLS
 
-typedef struct _LauncherPluginClass    LauncherPluginClass;
-typedef struct _LauncherPlugin         LauncherPlugin;
-typedef struct _LauncherEntry          LauncherEntry;
+typedef struct _XfceLauncherPluginClass XfceLauncherPluginClass;
+typedef struct _XfceLauncherPlugin      XfceLauncherPlugin;
+typedef enum   _LauncherArrowType       LauncherArrowType;
 
 #define XFCE_TYPE_LAUNCHER_PLUGIN            (launcher_plugin_get_type ())
-#define XFCE_LAUNCHER_PLUGIN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), XFCE_TYPE_LAUNCHER_PLUGIN, LauncherPlugin))
-#define XFCE_LAUNCHER_PLUGIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), XFCE_TYPE_LAUNCHER_PLUGIN, LauncherPluginClass))
+#define XFCE_LAUNCHER_PLUGIN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), XFCE_TYPE_LAUNCHER_PLUGIN, XfceLauncherPlugin))
+#define XFCE_LAUNCHER_PLUGIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), XFCE_TYPE_LAUNCHER_PLUGIN, XfceLauncherPluginClass))
 #define XFCE_IS_LAUNCHER_PLUGIN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XFCE_TYPE_LAUNCHER_PLUGIN))
 #define XFCE_IS_LAUNCHER_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XFCE_TYPE_LAUNCHER_PLUGIN))
-#define XFCE_LAUNCHER_PLUGIN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), XFCE_TYPE_LAUNCHER_PLUGIN, LauncherPluginClass))
+#define XFCE_LAUNCHER_PLUGIN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), XFCE_TYPE_LAUNCHER_PLUGIN, XfceLauncherPluginClass))
+
+enum _LauncherArrowType
+{
+  LAUNCHER_ARROW_DEFAULT = 0,
+  LAUNCHER_ARROW_NORTH,
+  LAUNCHER_ARROW_WEST,
+  LAUNCHER_ARROW_EAST,
+  LAUNCHER_ARROW_SOUTH,
+  LAUNCHER_ARROW_INTERNAL,
+
+  LAUNCHER_ARROW_MAX = LAUNCHER_ARROW_INTERNAL
+};
 
 GType             launcher_plugin_get_type    (void) G_GNUC_CONST;
 
-XfconfChannel    *launcher_plugin_get_channel (LauncherPlugin *plugin);
+XfconfChannel    *launcher_plugin_get_channel (XfceLauncherPlugin *plugin);
 
-GSList           *launcher_plugin_get_items   (LauncherPlugin *plugin);
+GSList           *launcher_plugin_get_items   (XfceLauncherPlugin *plugin);
 
 G_END_DECLS
 



More information about the Xfce4-commits mailing list