Re: Calling gtk_drag_get_data() in a drag-motion signal handler.



As the attached test case shows, this problem is fixed by calling 
gtk_drag_dest_set() with 0 instead of GTK_DEST_DEFAULT_ALL (0 causes the
targets array to be ignored too, but just passing NULL for the targets
doesn't change the behaviour.)

Does this make any sense? What is the meaning of 0 for the flags? I
can't guess that from the documentation:
http://library.gnome.org/devel/gtk/unstable/gtk-Drag-and-Drop.html#gtk-drag-dest-set


On Thu, 2007-12-06 at 18:10 +0100, Murray Cumming wrote:
> The documentation for GtkWidget's drag-motion signal 
> http://library.gnome.org/devel/gtk/stable/GtkWidget.html#GtkWidget-drag-motion
> says
> "
> If the decision whether the drop will be accepted or rejected can't be
> made based solely on the cursor position and the type of the data, the
> handler may inspect the dragged data by calling gtk_drag_get_data() and
> defer the gdk_drag_status() call to the "drag-data-received" handler.
> "
> 
> But when I try to do this the cursor is grabbed while dragging and no
> further drag-and-drop signals are emitted. I guess I'm doing something
> wrong but i can't see what.
> 
> I attached a simple test case. Watch out - you'll have to Shift-Alt-F1
> to get a terminal so you can kill the test case after it grabs the
> cursor.
> 
> I'm actually trying to do this so I can show a preview item in a canvas
> before the actual item is created on the canvas when the drop happens.
> 
> _______________________________________________
> gtk-list mailing list
> gtk-list gnome org
> http://mail.gnome.org/mailman/listinfo/gtk-list
-- 
murrayc murrayc com
www.murrayc.com
www.openismus.com
/* Build with:
 * gcc test_get_data_in_drag_motion2.c `pkg-config gtk+-2.0 --cflags --libs`
 */

#include <gtk/gtk.h>
#include <stdlib.h>
#include <string.h>

GtkWidget *dest = NULL;
int drag_item = 0; /* We can't put an item on a button so this is just to show the idea. */
gboolean drag_preview_requested = FALSE;


const int DRAG_DATA_FORMAT = 8; /* 8 bits format */

static void 
on_button_drag_data_get(GtkWidget        *widget,
                        GdkDragContext   *drag_context,
                        GtkSelectionData *selection_data,
                        guint             info,
                        guint             time,
                        gpointer          user_data)
{
  GdkAtom target_atom = selection_data->target;
  const gchar *target_name = gdk_atom_name (target_atom);
  printf("on_button_drag_data_get(): target=%s\n", target_name);

  static gchar* drag_data = "something_to_drag";
 
  gtk_selection_data_set(selection_data, target_atom, DRAG_DATA_FORMAT, 
                         (const guchar*)drag_data, strlen (drag_data));
}

static gboolean
on_dest_drag_drop(GtkWidget      *widget,
                    GdkDragContext *drag_context,
                    gint            x,
                    gint            y,
                    guint           time,
                    gpointer        user_data)
{
  GdkAtom target_atom = gtk_drag_dest_find_target(dest, drag_context, NULL);
  const gchar *target_name = gdk_atom_name (target_atom);
  printf("on_dest_drag_drop(): target=%s\n", target_name);

  return TRUE; /* Allow the drop. */
}

static gboolean
on_dest_drag_motion(GtkWidget      *widget,
                      GdkDragContext *drag_context,
                      gint            x,
                      gint            y,
                      guint           timestamp,
                      gpointer        user_data)
{
  GdkAtom target_atom = gtk_drag_dest_find_target(dest, drag_context, NULL);
  const gchar *target_name = gdk_atom_name (target_atom);
  printf("on_dest_drag_motion(): target=%s\n", target_name);

  gtk_drag_highlight (dest);


  /* Create the temporary dest item if necessary: */
  if(!drag_item)
  {
    /* TODO: This stops the drop (or any further motion events) from happening: */
    /* We need to examine the SelectionData:
     * This will cause our drag_data_received callback to be called, with that information: */
    drag_preview_requested = TRUE;
    gtk_drag_get_data(dest, drag_context, target_atom, timestamp);
    return TRUE;
  }

  gdk_drag_status(drag_context, GDK_ACTION_COPY, timestamp);
  
  /* Here we would change the position of a temporary dest item. */

  return TRUE; /* Allow the drop. */
}

static void
on_dest_drag_data_received(GtkWidget        *widget,
                             GdkDragContext   *drag_context,
                             gint              x,
                             gint              y,
                             GtkSelectionData *selection_data,
                             guint             info,
                             guint             timestamp,
                             gpointer          user_data)
{
  /* This is called when an item is dropped on the dest,
   * or after our drag_motion handler has called drag_get_data().
   */
  
  /* Discover what toolbar item was dropped:
   * In a real application, we would this use (or ideally an ID) 
   * to identify what should be created on the dest.
   */
  gchar* item_name = NULL;
  if((selection_data->length >= 0) && (selection_data->format == DRAG_DATA_FORMAT))
  {
    item_name = g_strndup (selection_data->data, selection_data->length);
  }

  printf("on_dest_drag_data_received(): dragged item type=%s", item_name);
  g_free (item_name);

  if(drag_preview_requested)
  {
    printf("  on_dest_drag_data_received(): drag_preview_requested");

    /* Create the temporary drag item if necessary: */
    if(!drag_item)
    {
      drag_item = 1;
    }

    gdk_drag_status(drag_context, GDK_ACTION_COPY, timestamp);
    drag_preview_requested = FALSE;
  }
  else
  {
    gtk_drag_finish (drag_context, TRUE, FALSE, timestamp);
    gtk_drag_unhighlight (dest);

    /* Remove the temporary drag item: */
    drag_item = 0;

    /* Add the requested item to the dest: */
  }
}

static gboolean
on_delete_event (GtkWidget *window,
		 GdkEvent  *event,
		 gpointer   unused_data)
{
  gtk_main_quit ();
}


int
main (int argc, char *argv[])
{
  gtk_init (&argc, &argv);

  /* Create the widgets: */
  GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_default_size (GTK_WINDOW (window), 640, 600);
  g_signal_connect (window, "delete_event", (GtkSignalFunc) on_delete_event,
		    NULL);
  GtkWidget *vbox = gtk_vbox_new (FALSE, 6);
  gtk_widget_show (vbox);
  gtk_container_add (GTK_CONTAINER (window), vbox);

  GtkWidget *button = gtk_button_new_with_label("Drag Me");
  gtk_widget_show (button);
  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);

  dest = gtk_button_new_with_label ("drag dest");
  gtk_widget_set_size_request (dest, 400, 400);
  gtk_widget_show (dest);
  gtk_box_pack_end (GTK_BOX (vbox), dest, TRUE, TRUE, 0);

  /* Setup the drag source: */
  const GtkTargetEntry drag_targets[] = {
        { "my-drag-format", GTK_TARGET_SAME_APP, 0 } };
  gtk_drag_source_set (button, GDK_MODIFIER_MASK, drag_targets,  G_N_ELEMENTS (drag_targets), GDK_ACTION_COPY);
  /* gtk_drag_source_set_icon (); */

  /* Let the item supply some data when the destination asks for it: */
  g_signal_connect (button, "drag_data_get", (GtkSignalFunc) on_button_drag_data_get,
		    NULL);

  /* Setup the drag target: */
  /* BUG?: If we call gtk_drag_dest_set() with our actual target then 
   * our drag-data-received signal handler will not be called 
   * when we call gtk_drag_get_data() in our drag-motion signal handler, 
   * and the cursor will be irretrievably grabbed.
   * If we specify NULL for the targets then it works, 
   * though the meaning of NULL is not documented for gtk_drag_dest_set().
  /* gtk_drag_dest_set (dest, GTK_DEST_DEFAULT_ALL, drag_targets,  G_N_ELEMENTS (drag_targets), GDK_ACTION_COPY); */
  gtk_drag_dest_set (dest, 0, drag_targets,  G_N_ELEMENTS (drag_targets), GDK_ACTION_COPY);


  g_signal_connect (dest, "drag_drop", (GtkSignalFunc) on_dest_drag_drop,
		    NULL);
  g_signal_connect (dest, "drag_motion", (GtkSignalFunc) on_dest_drag_motion,
		    NULL);
  g_signal_connect (dest, "drag_data_received", (GtkSignalFunc) on_dest_drag_data_received,
		    NULL);


  /* Pass control to the GTK+ main event loop. */
  gtk_widget_show (window);
  gtk_main ();

  return 0;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]