[Xfce4-commits] <thunar:jannis/new-shortcuts-pane> Squashme: Initial work on DND in ThunarShortcut.

Jannis Pohlmann noreply at xfce.org
Fri Dec 9 02:58:01 CET 2011


Updating branch refs/heads/jannis/new-shortcuts-pane
         to f6f91378b09c350752caa2cf2454f5b506fa3b28 (commit)
       from 92ff6996ea58f5f772164f803c19978eb82ad54c (commit)

commit f6f91378b09c350752caa2cf2454f5b506fa3b28
Author: Jannis Pohlmann <jannis.pohlmann at codethink.co.uk>
Date:   Fri Dec 9 02:46:22 2011 +0100

    Squashme: Initial work on DND in ThunarShortcut.
    
    Already implemented is the typical DND data negotiation code. Shortcuts
    with a ThunarFile already compute and suggest possible drop actions back
    to GDK.
    
    What is missing is the same for not-yet-mounted volumes, mounted
    volumes and mounts. Not-yet-mounted volumes and basically everything
    that has not finished resolving need to suggest all possible actions.
    Mounted volumes and mounts need to properly set the ThunarFile
    independently of the DND changes.
    
    We might have to delay asking what to do, performing the drop action,
    and calling drag_leave until resolving has finished after a drop. This
    might be tricky as the DND callbacks are synchronous. In any case, care
    needs to be taken of not destroying the drop file list too early when
    delaying the actual drop action.

 thunar/thunar-shortcut.c |  436 +++++++++++++++++++++++++++++++++++++--------
 1 files changed, 358 insertions(+), 78 deletions(-)

diff --git a/thunar/thunar-shortcut.c b/thunar/thunar-shortcut.c
index 80f49d9..12ed3e2 100644
--- a/thunar/thunar-shortcut.c
+++ b/thunar/thunar-shortcut.c
@@ -74,6 +74,12 @@ enum
   LAST_SIGNAL,
 };
 
+/* identifiers for DnD target types */
+enum
+{
+  TARGET_TEXT_URI_LIST,
+};
+
 /* row states */
 typedef enum
 {
@@ -84,81 +90,104 @@ typedef enum
 
 
 
-static void         thunar_shortcut_constructed             (GObject            *object);
-static void         thunar_shortcut_finalize                (GObject            *object);
-static void         thunar_shortcut_get_property            (GObject            *object,
-                                                             guint               prop_id,
-                                                             GValue             *value,
-                                                             GParamSpec         *pspec);
-static void         thunar_shortcut_set_property            (GObject            *object,
-                                                             guint               prop_id,
-                                                             const GValue       *value,
-                                                             GParamSpec         *pspec);
-static gboolean     thunar_shortcut_button_press_event      (GtkWidget          *widget,
-                                                             GdkEventButton     *event);
-static gboolean     thunar_shortcut_button_release_event    (GtkWidget          *widget,
-                                                             GdkEventButton     *event);
-static gboolean     thunar_shortcut_key_press_event         (GtkWidget          *widget,
-                                                             GdkEventKey        *event);
-static gboolean     thunar_shortcut_enter_notify_event      (GtkWidget          *widget,
-                                                             GdkEventCrossing   *event);
-static gboolean     thunar_shortcut_leave_notify_event      (GtkWidget          *widget,
-                                                             GdkEventCrossing   *event);
-static gboolean     thunar_shortcut_expose_event            (GtkWidget          *widget,
-                                                             GdkEventExpose     *event);
-static gboolean     thunar_shortcut_focus                   (GtkWidget          *widget,
-                                                             GtkDirectionType    direction);
-static gboolean     thunar_shortcut_focus_in_event          (GtkWidget          *widget,
-                                                             GdkEventFocus      *event);
-static void         thunar_shortcut_size_request            (GtkWidget          *widget,
-                                                             GtkRequisition     *requisition);
-static void         thunar_shortcut_button_state_changed    (ThunarShortcut     *shortcut,
-                                                             GtkStateType        previous_state,
-                                                             GtkWidget          *button);
-static void         thunar_shortcut_button_clicked          (ThunarShortcut     *shortcut,
-                                                             GtkButton          *button);
-static gboolean     thunar_shortcut_matches_types           (ThunarShortcut     *shortcut,
-                                                             ThunarShortcutType  types);
-static void         thunar_shortcut_location_changed        (ThunarShortcut     *shortcut);
-static void         thunar_shortcut_file_changed            (ThunarShortcut     *shortcut);
-static void         thunar_shortcut_shortcut_type_changed   (ThunarShortcut     *shortcut);
-static void         thunar_shortcut_icon_changed            (ThunarShortcut     *shortcut);
-static void         thunar_shortcut_resolve_location_finish (ThunarBrowser      *browser,
-                                                             GFile              *location,
-                                                             ThunarFile         *file,
-                                                             ThunarFile         *target_file,
-                                                             GError             *error,
-                                                             gpointer            user_data);
-static void         thunar_shortcut_mount_unmount_finish    (GObject            *object,
-                                                             GAsyncResult       *result,
-                                                             gpointer            user_data);
-static void         thunar_shortcut_mount_eject_finish      (GObject            *object,
-                                                             GAsyncResult       *result,
-                                                             gpointer            user_data);
-static void         thunar_shortcut_volume_eject_finish     (GObject            *object,
-                                                             GAsyncResult       *result,
-                                                             gpointer            user_data);
-static void         thunar_shortcut_poke_volume_finish      (ThunarBrowser      *browser,
-                                                             GVolume            *volume,
-                                                             ThunarFile         *file,
-                                                             GError             *error,
-                                                             gpointer            user_data);
-static void         thunar_shortcut_poke_file_finish        (ThunarBrowser      *browser,
-                                                             ThunarFile         *file,
-                                                             ThunarFile         *target_file,
-                                                             GError             *error,
-                                                             gpointer            user_data);
-static void         thunar_shortcut_poke_location_finish    (ThunarBrowser      *browser,
-                                                             GFile              *location,
-                                                             ThunarFile         *file,
-                                                             ThunarFile         *target_file,
-                                                             GError             *error,
-                                                             gpointer            user_data);
-static void         thunar_shortcut_set_spinning            (ThunarShortcut     *shortcut,
-                                                             gboolean            spinning,
-                                                             ThunarShortcutState new_state);
-static const gchar *thunar_shortcut_get_display_name        (ThunarShortcut     *shortcut);
-static GIcon       *thunar_shortcut_get_display_icon        (ThunarShortcut     *shortcut);
+static void          thunar_shortcut_constructed             (GObject            *object);
+static void          thunar_shortcut_finalize                (GObject            *object);
+static void          thunar_shortcut_get_property            (GObject            *object,
+                                                              guint               prop_id,
+                                                              GValue             *value,
+                                                              GParamSpec         *pspec);
+static void          thunar_shortcut_set_property            (GObject            *object,
+                                                              guint               prop_id,
+                                                              const GValue       *value,
+                                                              GParamSpec         *pspec);
+static gboolean      thunar_shortcut_button_press_event      (GtkWidget          *widget,
+                                                              GdkEventButton     *event);
+static gboolean      thunar_shortcut_button_release_event    (GtkWidget          *widget,
+                                                              GdkEventButton     *event);
+static void          thunar_shortcut_drag_data_received      (GtkWidget          *widget,
+                                                              GdkDragContext     *context,
+                                                              gint                x,
+                                                              gint                y,
+                                                              GtkSelectionData   *selection_data,
+                                                              guint               info,
+                                                              guint               timestamp);
+static gboolean      thunar_shortcut_drag_drop               (GtkWidget          *widget,
+                                                              GdkDragContext     *context,
+                                                              gint                x,
+                                                              gint                y,
+                                                              guint               timestamp);
+static void          thunar_shortcut_drag_leave              (GtkWidget          *widget,
+                                                              GdkDragContext     *context,
+                                                              guint               timestamp);
+static gboolean      thunar_shortcut_drag_motion             (GtkWidget          *widget,
+                                                              GdkDragContext     *context,
+                                                              gint                x,
+                                                              gint                y,
+                                                              guint               timestamp);
+static GdkDragAction thunar_shortcut_compute_drop_actions    (ThunarShortcut     *shortcut,
+                                                              GdkDragContext     *context,
+                                                              GdkDragAction      *suggested_action);
+static gboolean      thunar_shortcut_key_press_event         (GtkWidget          *widget,
+                                                              GdkEventKey        *event);
+static gboolean      thunar_shortcut_enter_notify_event      (GtkWidget          *widget,
+                                                              GdkEventCrossing   *event);
+static gboolean      thunar_shortcut_leave_notify_event      (GtkWidget          *widget,
+                                                              GdkEventCrossing   *event);
+static gboolean      thunar_shortcut_expose_event            (GtkWidget          *widget,
+                                                              GdkEventExpose     *event);
+static gboolean      thunar_shortcut_focus                   (GtkWidget          *widget,
+                                                              GtkDirectionType    direction);
+static gboolean      thunar_shortcut_focus_in_event          (GtkWidget          *widget,
+                                                              GdkEventFocus      *event);
+static void          thunar_shortcut_size_request            (GtkWidget          *widget,
+                                                              GtkRequisition     *requisition);
+static void          thunar_shortcut_button_state_changed    (ThunarShortcut     *shortcut,
+                                                              GtkStateType        previous_state,
+                                                              GtkWidget          *button);
+static void          thunar_shortcut_button_clicked          (ThunarShortcut     *shortcut,
+                                                              GtkButton          *button);
+static gboolean      thunar_shortcut_matches_types           (ThunarShortcut     *shortcut,
+                                                              ThunarShortcutType  types);
+static void          thunar_shortcut_location_changed        (ThunarShortcut     *shortcut);
+static void          thunar_shortcut_file_changed            (ThunarShortcut     *shortcut);
+static void          thunar_shortcut_shortcut_type_changed   (ThunarShortcut     *shortcut);
+static void          thunar_shortcut_icon_changed            (ThunarShortcut     *shortcut);
+static void          thunar_shortcut_resolve_location_finish (ThunarBrowser      *browser,
+                                                              GFile              *location,
+                                                              ThunarFile         *file,
+                                                              ThunarFile         *target_file,
+                                                              GError             *error,
+                                                              gpointer            user_data);
+static void          thunar_shortcut_mount_unmount_finish    (GObject            *object,
+                                                              GAsyncResult       *result,
+                                                              gpointer            user_data);
+static void          thunar_shortcut_mount_eject_finish      (GObject            *object,
+                                                              GAsyncResult       *result,
+                                                              gpointer            user_data);
+static void          thunar_shortcut_volume_eject_finish     (GObject            *object,
+                                                              GAsyncResult       *result,
+                                                              gpointer            user_data);
+static void          thunar_shortcut_poke_volume_finish      (ThunarBrowser      *browser,
+                                                              GVolume            *volume,
+                                                              ThunarFile         *file,
+                                                              GError             *error,
+                                                              gpointer            user_data);
+static void          thunar_shortcut_poke_file_finish        (ThunarBrowser      *browser,
+                                                              ThunarFile         *file,
+                                                              ThunarFile         *target_file,
+                                                              GError             *error,
+                                                              gpointer            user_data);
+static void          thunar_shortcut_poke_location_finish    (ThunarBrowser      *browser,
+                                                              GFile              *location,
+                                                              ThunarFile         *file,
+                                                              ThunarFile         *target_file,
+                                                              GError             *error,
+                                                              gpointer            user_data);
+static void          thunar_shortcut_set_spinning            (ThunarShortcut     *shortcut,
+                                                              gboolean            spinning,
+                                                              ThunarShortcutState new_state);
+static const gchar  *thunar_shortcut_get_display_name        (ThunarShortcut     *shortcut);
+static GIcon        *thunar_shortcut_get_display_icon        (ThunarShortcut     *shortcut);
 
 
 
@@ -203,6 +232,11 @@ struct _ThunarShortcut
 
   GCancellable       *cancellable;
 
+  guint               drag_highlight : 1;
+  guint               drop_occurred : 1;
+  guint               drop_data_ready : 1;
+  GList              *drop_file_list;
+
   ThunarShortcutState state;
 };
 
@@ -215,6 +249,11 @@ G_DEFINE_TYPE_WITH_CODE (ThunarShortcut, thunar_shortcut, GTK_TYPE_EVENT_BOX,
 
 static guint shortcut_signals[LAST_SIGNAL];
 
+static const GtkTargetEntry drop_targets[] =
+{
+  { "text/uri-list", 0, TARGET_TEXT_URI_LIST }
+};
+
 
 
 static void
@@ -235,6 +274,10 @@ thunar_shortcut_class_init (ThunarShortcutClass *klass)
   gtkwidget_class = GTK_WIDGET_CLASS (klass);
   gtkwidget_class->button_press_event = thunar_shortcut_button_press_event;
   gtkwidget_class->button_release_event = thunar_shortcut_button_release_event;
+  gtkwidget_class->drag_data_received = thunar_shortcut_drag_data_received;
+  gtkwidget_class->drag_drop = thunar_shortcut_drag_drop;
+  gtkwidget_class->drag_leave = thunar_shortcut_drag_leave;
+  gtkwidget_class->drag_motion = thunar_shortcut_drag_motion;
   gtkwidget_class->key_press_event = thunar_shortcut_key_press_event;
   gtkwidget_class->enter_notify_event = thunar_shortcut_enter_notify_event;
   gtkwidget_class->leave_notify_event = thunar_shortcut_leave_notify_event;
@@ -467,6 +510,16 @@ thunar_shortcut_init (ThunarShortcut *shortcut)
   shortcut->preferences = thunar_preferences_get ();
   exo_binding_new (G_OBJECT (shortcut->preferences), "shortcuts-icon-size",
                    G_OBJECT (shortcut), "icon-size");
+
+  /* set up drop support for the shortcut */
+  gtk_drag_dest_set (GTK_WIDGET (shortcut), 0,
+                     drop_targets, G_N_ELEMENTS (drop_targets),
+                     GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
+
+  /* reset drop information */
+  shortcut->drop_occurred = FALSE;
+  shortcut->drop_data_ready = FALSE;
+  shortcut->drop_file_list = NULL;
 }
 
 
@@ -749,6 +802,213 @@ thunar_shortcut_button_release_event (GtkWidget      *widget,
 
 
 
+static void
+thunar_shortcut_drag_data_received (GtkWidget        *widget,
+                                    GdkDragContext   *context,
+                                    gint              x,
+                                    gint              y,
+                                    GtkSelectionData *selection_data,
+                                    guint             info,
+                                    guint             timestamp)
+{
+  ThunarShortcut *shortcut = THUNAR_SHORTCUT (widget);
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUT (widget));
+  _thunar_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+
+  /* check if we don't already know the drop data */
+  if (!shortcut->drop_data_ready)
+    {
+      /* extract the URI list from the selection data (if valid) */
+      if (info == TARGET_TEXT_URI_LIST
+          && selection_data->format == 8
+          && selection_data->length > 0)
+        {
+          shortcut->drop_file_list = 
+            thunar_g_file_list_new_from_string ((const gchar *) selection_data->data);
+        }
+
+      /* reset the state */
+      shortcut->drop_data_ready = TRUE;
+    }
+
+  /* check if the data was dropped */
+  if (shortcut->drop_occurred)
+    {
+      /* reset the drop state */
+      shortcut->drop_occurred = FALSE;
+
+      /* make sure we only handle text/uri-list */
+      if (info == TARGET_TEXT_URI_LIST)
+        {
+          if (shortcut->drop_file_list != NULL)
+            g_debug ("  have drop files, create bookmarks now");
+          else
+            g_debug ("  don't have drop files, abort drag and drop");
+        }
+
+      /* disable highlighting and release the drag data */
+      /* TODO we might have to delay this until the shortcut has been resolved */
+      thunar_shortcut_drag_leave (widget, context, timestamp);
+
+      /* tell the peer that we handled the copy */
+      /* TODO we might have to delay this until the shortcut has been resolved */
+      gtk_drag_finish (context, shortcut->drop_file_list != NULL, FALSE, timestamp);
+    }
+  else
+    {
+      gdk_drag_status (context, 0, timestamp);
+    }
+}
+
+
+
+static gboolean
+thunar_shortcut_drag_drop (GtkWidget      *widget,
+                           GdkDragContext *context,
+                           gint            x,
+                           gint            y,
+                           guint           timestamp)
+{
+  ThunarShortcut *shortcut = THUNAR_SHORTCUT (widget);
+  GdkAtom         target;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUT (widget), FALSE);
+  _thunar_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), FALSE);
+
+  target = gtk_drag_dest_find_target (widget, context, NULL);
+  if (target == gdk_atom_intern_static_string ("text/uri-list"))
+    {
+      /* set the state so that drag_data_received knows that it needs to drop now */
+      shortcut->drop_occurred = TRUE;
+
+      /* request the drag data from the source and perform the drop */
+      gtk_drag_get_data (widget, context, target, timestamp);
+
+      return  TRUE;
+    }
+  else
+    {
+      /* we cannot handle the drop */
+      return FALSE;
+    }
+}
+
+
+
+static void
+thunar_shortcut_drag_leave (GtkWidget      *widget,
+                            GdkDragContext *context,
+                            guint           timestamp)
+{
+  ThunarShortcut *shortcut = THUNAR_SHORTCUT (widget);
+
+  _thunar_return_if_fail (THUNAR_IS_SHORTCUT (widget));
+  _thunar_return_if_fail (GDK_IS_DRAG_CONTEXT (context));
+
+  /* reset highlighting */
+  shortcut->drag_highlight = FALSE;
+
+  /* reset the "drop data ready" status and free the file list */
+  if (shortcut->drop_data_ready)
+    {
+      thunar_g_file_list_free (shortcut->drop_file_list);
+      shortcut->drop_file_list = NULL;
+      shortcut->drop_data_ready = FALSE;
+    }
+
+  /* schedule a repaint to make sure the shortcut is no longer highlighted */
+  gtk_widget_queue_draw (widget);
+
+  /* call the parent's handler */
+  if (GTK_WIDGET_CLASS (thunar_shortcut_parent_class)->drag_leave != NULL)
+    (*GTK_WIDGET_CLASS (thunar_shortcut_parent_class)->drag_leave) (widget, context, timestamp);
+}
+
+
+
+static gboolean
+thunar_shortcut_drag_motion (GtkWidget      *widget,
+                             GdkDragContext *context,
+                             gint            x,
+                             gint            y,
+                             guint           timestamp)
+{
+  ThunarShortcut *shortcut = THUNAR_SHORTCUT (widget);
+  GdkDragAction   actions;
+  GdkAtom         target;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUT (widget), FALSE);
+  _thunar_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), FALSE);
+
+  /* determine the drag target */
+  target = gtk_drag_dest_find_target (widget, context, NULL);
+
+  /* abort if the target is unsupported */
+  if (target != gdk_atom_intern_static_string ("text/uri-list"))
+    {
+      /* unsupported target, can't handle it, sorry */
+      return FALSE;
+    }
+
+  /* request the drop data on-demand (if we don't have it yet) */
+  if (!shortcut->drop_data_ready)
+    {
+      /* request the drag data from the source */
+      gtk_drag_get_data (widget, context, target, timestamp);
+      return TRUE;
+    }
+  else
+    {
+      /* check whether we have any files at all */
+      if (shortcut->drop_file_list != NULL)
+        thunar_shortcut_compute_drop_actions (shortcut, context, &actions);
+    }
+
+  /* tell Gdk whether we can drop here */
+  gdk_drag_status (context, actions, timestamp);
+
+  /* highlight the shortcut */
+  shortcut->drag_highlight = TRUE;
+  gtk_widget_queue_draw (widget);
+
+  return TRUE;
+}
+
+
+
+static GdkDragAction
+thunar_shortcut_compute_drop_actions (ThunarShortcut *shortcut,
+                                      GdkDragContext *context,
+                                      GdkDragAction  *suggested_action)
+{
+  GdkDragAction actions;
+
+  _thunar_return_val_if_fail (THUNAR_IS_SHORTCUT (shortcut), 0);
+  _thunar_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), 0);
+
+  if (shortcut->file != NULL)
+    {
+      actions = thunar_file_accepts_drop (shortcut->file,
+                                          shortcut->drop_file_list,
+                                          context,
+                                          suggested_action);
+
+      g_debug ("  check whether %s accepts: move %s, copy %s, link %s, ask %s",
+               thunar_file_get_display_name (shortcut->file),
+               (actions & GDK_ACTION_MOVE) != 0 ? "yes" : "no",
+               (actions & GDK_ACTION_COPY) != 0 ? "yes" : "no",
+               (actions & GDK_ACTION_LINK) != 0 ? "yes" : "no",
+               (actions & GDK_ACTION_ASK ) != 0 ? "yes" : "no");
+    }
+
+  /* TODO handle everything else */
+
+  return actions;
+}
+
+
+
 static gboolean
 thunar_shortcut_key_press_event (GtkWidget   *widget,
                                  GdkEventKey *event)
@@ -824,9 +1084,12 @@ static gboolean
 thunar_shortcut_expose_event (GtkWidget      *widget,
                               GdkEventExpose *event)
 {
-  GtkStateType state;
-  GList       *children;
-  GList       *lp;
+  ThunarShortcut *shortcut = THUNAR_SHORTCUT (widget);
+  GtkStateType    state;
+  cairo_t        *cr;
+  gdouble         dashes[] = { 0.0, 2.0 };
+  GList          *children;
+  GList          *lp;
 
   _thunar_return_val_if_fail (THUNAR_IS_SHORTCUT (widget), FALSE);
 
@@ -847,6 +1110,23 @@ thunar_shortcut_expose_event (GtkWidget      *widget,
                       event->area.width,
                       event->area.height);
 
+  /* draw a dotted border while waiting for a drop */
+  if (shortcut->drag_highlight)
+    {
+      cr = gdk_cairo_create (widget->window);
+      cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0); /* black */
+      cairo_set_line_width (cr, 1.0);
+      cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+      cairo_set_dash (cr, dashes, 2, 0);
+      cairo_rectangle (cr,
+                       event->area.x + 0.5,
+                       event->area.y + 0.5,
+                       event->area.width - 1,
+                       event->area.height - 1);
+      cairo_stroke (cr);
+      cairo_destroy (cr);
+    }
+
   /* propagate the expose event to all children */
   children = gtk_container_get_children (GTK_CONTAINER (widget));
   for (lp = children; lp != NULL; lp = lp->next)


More information about the Xfce4-commits mailing list