Whirlwind new DND API tour
- From: Owen Taylor <otaylor redhat com>
- To: gtk-devel-list redhat com
- Subject: Whirlwind new DND API tour
- Date: 09 Oct 1998 18:02:43 -0400
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]