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



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.

-- 
murrayc murrayc com
www.murrayc.com
www.openismus.com
/* Build with:
 * gcc test_get_data_in_drag_motion.cc.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: */
  gtk_drag_dest_set (dest, GTK_DEST_DEFAULT_ALL, 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]