Whirlwind new DND API tour




Here's a quick tour of the new GTK+ DND API I've been
working on. 

I'm getting pretty happy with the API, things mostly function
as they should, and I've converted most of the DND in GNOME
to use the new API locally. I hope to release the code in
the very near future.

What's New
==========

- Actions - the user can select "move/copy/link" by holding
  down modifier keys, or right-drag to indicate "ask" - 
  that is: pop up a dialog with the possible actions.
- Transparent support for Motif and Xdnd protocols.
- Much more customizable, you can have multiple drag/drop sites
  per widget, among other things.
- More convenient interface - widgets don't need to be realized
  in advance, drop sites don't need to be window widgets.

The simple stuff:
================

To identify lists of drag types ("targets") we use, from gtkselection.h:
  
  struct _GtkTargetEntry {
    gchar *target;
    guint  info;
  };

in this structures, 'target' is a string indicating the mime-type
of the dragged data. In most other places, these strings are
represented as atoms, which are indices into a table of
strings on the server. 

You can convert back and forth with gdk_atom_intern() and
gdk_atom_name(), as always. But, usually, you won't need to, because
you've supplied something meaningful for the 'info' field, and that
will be returned to you in callbacks. (One would usually do:

enum {
  TARGET_STRING,
  TARGET_URL
};

and use those values for 'info'. Values of 'info' above
0x80000000 are reserved for internal use.)

Then, to enable drags from a widget (a "drag source") we use:

void gtk_drag_source_set  (GtkWidget         *widget,
			   GdkModifierType    start_button_mask,
			   GtkTargetEntry    *targets,
			   gint               n_targets,
			   GdkDragAction      actions);

'start_button_mask' specifies the buttons that can be used to
initiate a drag, 'actions' is a bitmask:

typedef enum {
  GDK_ACTION_COPY    = 1 << 1,
  GDK_ACTION_MOVE    = 1 << 2,
  GDK_ACTION_LINK    = 1 << 3,
  GDK_ACTION_ASK     = 1 << 5
} GdkDragAction;

When the data is dropped, we get a "drag_data_get" signal:

  void "drag_data_get"	           (GtkWidget          *widget,
				    GdkDragContext     *context,
				    GtkSelectionData   *selection_data,
				    guint               info,
				    guint32             time);

One can tell the requested type from "info", and/or 
selection_data->type (a GdkAtom). One then calls 
gtk_selection_data_set() to supply the data.


To enable drags to a widget, we use:

void gtk_drag_dest_set   (GtkWidget          *widget,
			  GtkDestDefaults     flags,
  		          GtkTargetEntry     *targets,
			  gint                n_targets,
			  GdkDragAction       actions);

"flags" is a bitmask indicating how much GTK+ should do for you, 
for now, we'll assume that you want everything default and
use GTK_DEST_DEFAULT_ALL for this value.

"targets" "n_targets" and "actions" are above. When a drop
is received, one gets a "drag_data_received" signal:

  void "drag_data_received"      (GtkWidget          *widget,
				  GdkDragContext     *context,
				  GtkSelectionData   *selection_data,
				  guint               info,
				  guint32             time);

One can tell which target was dropped from 'info' or 
selection_data->type as above. The data is in
selection_data->data.

That's all you need to go and write DND applications, kids.
But there's more, of course...


Icons, icons
============

To use set an icon for a drag source, use:

void gtk_drag_source_set_icon (GtkWidget     *widget,
			       GdkColormap   *colormap,
			       GdkPixmap     *pixmap,
			       GdkBitmap     *mask);

We can also set the default icon:

void gtk_drag_set_default_icon (GdkColormap   *colormap,
				GdkPixmap     *pixmap,
				GdkBitmap     *mask,
			        gint           hot_x,
			        gint           hot_y);

Digging deeper:
===============

The interfaces above are merely the "default handlers" - 
by using the lower level interfaces, DND can be extensively
customized.

A few additional concepts, first. At the lower level, lists of targets
are represented as GtkTargetList's -

GtkTargetList *gtk_target_list_new       (GtkTargetEntry *targets,
					  guint           ntargets);
void           gtk_target_list_ref       (GtkTargetList  *list);
void           gtk_target_list_unref     (GtkTargetList  *list);
void           gtk_target_list_add       (GtkTargetList  *list,
				  	  GdkAtom         target,
					  guint           info);
void           gtk_target_list_add_table (GtkTargetList  *list,
					  GtkTargetEntry *targets,
					  guint           ntargets);
void           gtk_target_list_remove    (GtkTargetList  *list,
					  GdkAtom         target);

A drag in progress on either side is represented by a GdkDragContext *.
There are some publically accessible fields for this structure:

 struct _GdkDragContext {
  [...]
  GList *targets;                 /* List of target atoms (cast to pointer) */
  GdkDragAction actions;          /* Actions source supports */
  GdkDragAction suggested_action; /* Action source suggests */
  GdkDragAction action;           /* Action destination has selected */
 };

You can also attach your own data to a DragContext using
gtk_dataset_set_data(), etc. DragContexts are passed as parameters
to all the DND signals. In addition to the two described
above, those are, on the source side:

  void "drag_begin"	           (GtkWidget	       *widget,
				    GdkDragContext     *context);

  This one is useful for setting the icon to a something special.

  void "drag_end"	           (GtkWidget	       *widget,
				    GdkDragContext     *context);

  void "drag_data_delete"          (GtkWidget	       *widget,
				    GdkDragContext     *context);

  Called when the destination asks that the data be deleted. (Presumably
  because the action was MOVE)

On the destination side:

  gboolean "drag_motion"           (GtkWidget	       *widget,
				    GdkDragContext     *context,
				    gint                x,
				    gint                y,
				    guint               time);

  Called during the drag when the mouse is in a widget registered
  as a drop target. If (x,y) [allocation relative] is inside
  a drop site in the widget, the handler should return TRUE, then 
  call:

    void         gdk_drag_status        (GdkDragContext   *context,
				         GdkDragAction     action,
					 guint32           time);

  with the action it selects.

  void "drag_leave"	           (GtkWidget	       *widget,
				    GdkDragContext     *context);

  Called when the drag leaves a widget. Any highlight applied
  in response to "drag_motion" should be removed.

  gboolean "drag_drop"             (GtkWidget	       *widget,
				    GdkDragContext     *context,
				    gint                x,
				    gint                y,
				    guint               time);

  Called when a drop occurs. If (x,y) is inside a drop site,
  it should return TRUE, and either get the data with:
  
  void gtk_drag_get_data (GtkWidget      *widget,
			  GdkDragContext *context,
			  GdkAtom         target,
			  guint32         time);

  and, when the data is received, call:

  void gtk_drag_finish   (GdkDragContext *context,
			  gboolean        success,
			  gboolean        delete,
			  guint32         time);

  with "success = TRUE", or call gtk_drag_finish() with
  "sucess = FALSE" immediately to reject the drag.

Whew. You're probably glad for the default handlers now. 
On the destination side, you can select more precisely what
the default handlers do via the "flags" argument to 
gtk_drag_dest_set()

typedef enum {
  GTK_DEST_DEFAULT_MOTION     = 1 << 0, /* respond to "drag_motion" */
  GTK_DEST_DEFAULT_HIGHLIGHT  = 1 << 1, /* auto-highlight */
  GTK_DEST_DEFAULT_DROP       = 1 << 2  /* respond to "drag_drop" */
} GtkDestDefaults;

if DEFAULT_DROP is set, then the default handler also takes
care of calling gtk_drag_finish() as appropriate.

and more icons
==============

If we aren't using the default handlers for drag sources,
we need to set the icons for drags ourselves. We do
this with:

void gtk_drag_set_icon_pixmap  (GdkDragContext    *context,
				GdkColormap       *colormap,
				GdkPixmap         *pixmap,
				GdkBitmap         *mask,
				gint               hot_x,
				gint               hot_y);

void gtk_drag_set_icon_default (GdkDragContext    *context);

There's another one which you might well want to use even
if you do use the default drag source handler - if you want
to drag around things other than icons, you can call:

void gtk_drag_set_icon_widget  (GdkDragContext    *context,
				GtkWidget         *widget,
				gint               hot_x,
				gint               hot_y);

in the "drag_begin" signal handler. (e.g., GtkColorSelector now
displayed the dragged colors as color swatches)

Regards,
                                        Owen



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