diff options
author | Owen Taylor <otaylor@gtk.org> | 1998-10-18 22:51:24 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 1998-10-18 22:51:24 +0000 |
commit | f7bcb456072dac59b1ce5bd5329282ba95a3b495 (patch) | |
tree | a9085af76de35b5c883776673b7a59521e52c043 /gtk | |
parent | 7dbb5755a4cafc45108ec66ac89dfc1f11639494 (diff) | |
download | gtk+-f7bcb456072dac59b1ce5bd5329282ba95a3b495.tar.gz |
Added a modular client-message-filter mechanism, that is used for the DND
Sun Oct 18 18:16:39 1998 Owen Taylor <otaylor@gtk.org>
* gdk/gdk.c gdkprivate.h: Added a modular client-message-filter
mechanism, that is used for the DND messages.
Removed all the old DND code.
* gdk/gdkcolormap.c gdk/gdkcolormap.h: Add a function to
get the visual of a given colormap.
* gtk/gtkcolorsel.c: Conversion to new DND, drag
a color-swatch.
* gdk/gdk.h gdk/gdkdnd.c: The low-level
X oriented portions of drag and drop protocols.
Sending and receiving client messages, and navigating
window trees.
* gdk/gdkimage.c: added a gdk_flush() when destroying
SHM images to hopefully make it more likely that
X will gracefully handle the segment being destroyed.
* gdk/gdkprivate.h gtk/gtkdebug.h: Add new
DND debugging flags.
* gtk/gtkeditable.[ch]: Updates for the selection handling
changes.
* gtk/gtkselection.[ch]: Added GtkTargetList, a
refcounted data structure for keeping track of lists
of GdkAtom + information. Removed selection_handler_add
in favor of a "drag_data_get" signal.
* gtk/gtkdnd.[ch] gtk/gtk.h: New files - highlevel (event loop
dependent) parts of the DND protocols, display of drag icons,
drag-under highlighting, and the "default handlers".
* gtk/gtkinvisible.[ch]: New widget - InputOnly offscreen
windows that are used for reliable pointer grabs and
selection handling in the DND code.
* gtk/testdnd.c: New test program for new DND. (Old
DND tests in testgtk still need to be converted.)
* gtk/testselection.c: Use the new selection API.
* docs/dnd_internals: Start at describing how
all the new code works inside.
* docs/Changes-1.2.txt: New file describing source-incompatible
changes in GTK+-1.2.
Sat Oct 17 22:50:34 1998 Owen Taylor <otaylor@gtk.org>
* gdk/gdkwindow.c (gdk_window_remove_filter): Free
the right list node.
* gdk/gdkwindow.c (gdk_window_init): Add gdk_root_parent
to the XID table so we can receive events on it.
Wed Oct 14 12:57:40 1998 Owen Taylor <otaylor@redhat.com>
* gdk/gdk.c gdk/gdk.h (gdk_event_get_time): New function
to get the timestamp from a generic event.
Fri Oct 9 13:16:04 1998 Owen Taylor <otaylor@redhat.com>
* gtk/gtkwidget.c (gtk_widget_add_events): Added function
that safely adds additional events to a widget's event
mask, even if the widget has previously been realized.
(We can do this, but not remove events from the event
mask).
Fri Oct 2 17:35:35 1998 Owen Taylor <otaylor@redhat.com>
* gdk/gdkproperty.c (gdk_property_get): Allow type == 0,
for AnyPropertyType.
Fri Oct 2 10:32:21 1998 Owen Taylor <otaylor@redhat.com>
* gdk/gdkproperty.c (gdk_atom_intern): Add client-local
hashing.
Thu Sep 24 20:33:54 1998 Owen Taylor <otaylor@redhat.com>
* gdk/gdk.c (gdk_event_send_clientmessage_toall): serial
isn't a timestamp.
Thu Sep 17 14:23:03 1998 Owen Taylor <otaylor@redhat.com>
* gdk/gdk.c (gdk_event_translate): Removed printing
of unknown window lookup warnings. (Made it
a GDK_NOTE) - they happen in many circumstances.
Diffstat (limited to 'gtk')
-rw-r--r-- | gtk/Makefile.am | 10 | ||||
-rw-r--r-- | gtk/gtk-boxed.defs | 3 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtkcolorsel.c | 180 | ||||
-rw-r--r-- | gtk/gtkdebug.h | 3 | ||||
-rw-r--r-- | gtk/gtkdnd.c | 2343 | ||||
-rw-r--r-- | gtk/gtkdnd.h | 129 | ||||
-rw-r--r-- | gtk/gtkeditable.c | 87 | ||||
-rw-r--r-- | gtk/gtkmain.c | 24 | ||||
-rw-r--r-- | gtk/gtkmarshal.list | 4 | ||||
-rw-r--r-- | gtk/gtkmarshalers.list | 4 | ||||
-rw-r--r-- | gtk/gtkmenu.h | 4 | ||||
-rw-r--r-- | gtk/gtkselection.c | 430 | ||||
-rw-r--r-- | gtk/gtkselection.h | 69 | ||||
-rw-r--r-- | gtk/gtksignal.c | 2 | ||||
-rw-r--r-- | gtk/gtkstyle.h | 1 | ||||
-rw-r--r-- | gtk/gtkwidget.c | 310 | ||||
-rw-r--r-- | gtk/gtkwidget.h | 89 | ||||
-rw-r--r-- | gtk/testgtk.c | 4 | ||||
-rw-r--r-- | gtk/testselection.c | 112 |
20 files changed, 3259 insertions, 550 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 6eed5d25a8..5a5cd804e5 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -29,6 +29,7 @@ libgtk_la_SOURCES = \ gtkcurve.c \ gtkdata.c \ gtkdialog.c \ + gtkdnd.c \ gtkdrawingarea.c \ gtkeditable.c \ gtkentry.c \ @@ -49,6 +50,7 @@ libgtk_la_SOURCES = \ gtkhseparator.c \ gtkimage.c \ gtkinputdialog.c \ + gtkinvisible.c \ gtkitem.c \ gtkitemfactory.c \ gtklabel.c \ @@ -135,6 +137,7 @@ source_headers = \ gtkdata.h \ gtkdebug.h \ gtkdialog.h \ + gtkdnd.h \ gtkdrawingarea.h \ gtkeditable.h \ gtkentry.h \ @@ -156,6 +159,7 @@ source_headers = \ gtkhseparator.h \ gtkimage.h \ gtkinputdialog.h \ + gtkinvisible.h \ gtkitem.h \ gtkitemfactory.h \ gtklabel.h \ @@ -306,7 +310,9 @@ EXTRA_DIST = \ INCLUDES = -I$(top_srcdir) @GLIB_CFLAGS@ @x_cflags@ -noinst_PROGRAMS = testgtk testinput testselection testthreads testrgb simple +noinst_PROGRAMS = testgtk testinput testselection testthreads testrgb testdnd simple + +# FIXME, we currently rely on linking against libglib-1.1 DEPS = \ libgtk.la \ @@ -325,6 +331,7 @@ testinput_DEPENDENCIES = $(DEPS) testthreads_DEPENDENCIES = $(DEPS) testselection_DEPENDENCIES = $(DEPS) testrgb_DEPENDENCIES = $(DEPS) +testdnd_DEPENDENCIES = $(DEPS) simple_DEPENDENCIES = $(DEPS) testgtk_LDADD = $(LDADDS) @@ -332,6 +339,7 @@ testinput_LDADD = $(LDADDS) testthreads_LDADD = $(LDADDS) testselection_LDADD = $(LDADDS) testrgb_LDADD = $(LDADDS) +testdnd_LDADD = $(LDADDS) simple_LDADD = $(LDADDS) .PHONY: files test test-debug diff --git a/gtk/gtk-boxed.defs b/gtk/gtk-boxed.defs index 353bc91250..1555e8beee 100644 --- a/gtk/gtk-boxed.defs +++ b/gtk/gtk-boxed.defs @@ -42,6 +42,9 @@ gdk_window_ref gdk_window_unref) +(define-boxed GdkDragContext + gdk_drag_context_ref + gdk_drag_context_unref) (define-boxed GdkEvent gdk_event_copy gdk_event_free @@ -44,6 +44,7 @@ #include <gtk/gtkcurve.h> #include <gtk/gtkdata.h> #include <gtk/gtkdialog.h> +#include <gtk/gtkdnd.h> #include <gtk/gtkdrawingarea.h> #include <gtk/gtkeditable.h> #include <gtk/gtkentry.h> diff --git a/gtk/gtkcolorsel.c b/gtk/gtkcolorsel.c index b07d953ae2..c8822743a9 100644 --- a/gtk/gtkcolorsel.c +++ b/gtk/gtkcolorsel.c @@ -22,6 +22,8 @@ #include <gdk/gdk.h> #include "gtkcolorsel.h" #include "gtkhbbox.h" +#include "gtkdnd.h" +#include "gtkselection.h" /* * If you change the way the color values are stored, @@ -144,12 +146,23 @@ static gint gtk_color_selection_wheel_events (GtkWidget *area, static gint gtk_color_selection_wheel_timeout (GtkColorSelection *colorsel); static void gtk_color_selection_sample_resize (GtkWidget *widget, gpointer data); -static void gtk_color_selection_drop_handle (GtkWidget *widget, - GdkEvent *event, - GtkWidget *theclorsel); -static void gtk_color_selection_drag_handle (GtkWidget *widget, - GdkEvent *event, - GtkWidget *thecolorsel); +static void gtk_color_selection_drag_begin (GtkWidget *widget, + GdkDragContext *context, + gpointer data); +static void gtk_color_selection_drop_handle (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time, + gpointer data); +static void gtk_color_selection_drag_handle (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time, + gpointer data); static void gtk_color_selection_draw_wheel_marker (GtkColorSelection *colorsel); static void gtk_color_selection_draw_wheel_frame (GtkColorSelection *colorsel); static void gtk_color_selection_draw_value_marker (GtkColorSelection *colorsel); @@ -502,7 +515,10 @@ static void gtk_color_selection_realize (GtkWidget *widget) { GtkColorSelection *colorsel; - gchar *type_accept_list[] = {"application/x-color"}; + + GtkTargetEntry targets[] = { + { "application/x-color", 0 } + }; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_COLOR_SELECTION (widget)); @@ -512,18 +528,30 @@ gtk_color_selection_realize (GtkWidget *widget) if (GTK_WIDGET_CLASS (color_selection_parent_class)->realize) (*GTK_WIDGET_CLASS (color_selection_parent_class)->realize) (widget); - gtk_widget_dnd_drag_set (colorsel->sample_area, - 1, type_accept_list, 1); - gtk_widget_dnd_drop_set (colorsel->sample_area, - 1, type_accept_list, 1, 0); - gtk_signal_connect_after (GTK_OBJECT (colorsel->sample_area), - "drop_data_available_event", - GTK_SIGNAL_FUNC (gtk_color_selection_drop_handle), - colorsel); - gtk_signal_connect_after (GTK_OBJECT (colorsel->sample_area), - "drag_request_event", - GTK_SIGNAL_FUNC (gtk_color_selection_drag_handle), - colorsel); + gtk_drag_dest_set (colorsel->sample_area, + GTK_DEST_DEFAULT_HIGHLIGHT | + GTK_DEST_DEFAULT_MOTION | + GTK_DEST_DEFAULT_DROP, + targets, 1, + GDK_ACTION_COPY); + + gtk_drag_source_set (colorsel->sample_area, + GDK_BUTTON1_MASK | GDK_BUTTON3_MASK, + targets, 1, + GDK_ACTION_COPY | GDK_ACTION_MOVE); + + gtk_signal_connect (GTK_OBJECT (colorsel->sample_area), + "drag_begin", + GTK_SIGNAL_FUNC (gtk_color_selection_drag_begin), + colorsel); + gtk_signal_connect (GTK_OBJECT (colorsel->sample_area), + "drag_data_get", + GTK_SIGNAL_FUNC (gtk_color_selection_drag_handle), + colorsel); + gtk_signal_connect (GTK_OBJECT (colorsel->sample_area), + "drag_data_received", + GTK_SIGNAL_FUNC (gtk_color_selection_drop_handle), + colorsel); } static void @@ -824,39 +852,97 @@ gtk_color_selection_sample_resize (GtkWidget *widget, } static void -gtk_color_selection_drop_handle (GtkWidget *widget, GdkEvent *event, - GtkWidget *thecolorsel) +gtk_color_selection_drag_begin (GtkWidget *widget, + GdkDragContext *context, + gpointer data) +{ + GtkColorSelection *colorsel = data; + GtkWidget *window; + gdouble colors[4]; + GdkColor bg; + + window = gtk_window_new(GTK_WINDOW_POPUP); + gtk_widget_set_usize (window, 48, 32); + gtk_widget_realize (window); + + gtk_color_selection_get_color (colorsel, colors); + bg.red = 0xffff * colors[0]; + bg.green = 0xffff * colors[1]; + bg.blue = 0xffff * colors[2]; + + gdk_color_alloc (gtk_widget_get_colormap (window), &bg); + gdk_window_set_background (window->window, &bg); + + gtk_drag_set_icon_widget (context, window, -2, -2); +} + +static void +gtk_color_selection_drop_handle (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *selection_data, + guint info, + guint time, + gpointer data) { - /* This is currently a gdouble array of the format (I think): - use_opacity + GtkColorSelection *colorsel = data; + guint16 *vals; + gdouble colors[4]; + + /* This is currently a guint16 array of the format: R G B opacity */ - gdouble *v = event->dropdataavailable.data; - gtk_color_selection_set_opacity(GTK_COLOR_SELECTION(thecolorsel), - (v[0]==1.0)?TRUE:FALSE); - gtk_color_selection_set_color(GTK_COLOR_SELECTION(thecolorsel), - v + 1); - g_free(event->dropdataavailable.data); - g_free(event->dropdataavailable.data_type); + + if (selection_data->length < 0) + return; + + if ((selection_data->format != 16) || + (selection_data->length != 8)) + { + g_warning ("Received invalid color data\n"); + return; + } + + vals = (guint16 *)selection_data->data; + + colors[0] = (gdouble)vals[0] / 0xffff; + colors[1] = (gdouble)vals[1] / 0xffff; + colors[2] = (gdouble)vals[2] / 0xffff; + colors[3] = (gdouble)vals[3] / 0xffff; + + gtk_color_selection_set_color(colorsel, colors); } static void -gtk_color_selection_drag_handle (GtkWidget *widget, GdkEvent *event, - GtkWidget *thecolorsel) +gtk_color_selection_drag_handle (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time, + gpointer data) { - gdouble sendvals[(BLUE - RED + 1) + 3]; + GtkColorSelection *colorsel = data; + guint16 vals[4]; + gdouble colors[4]; + + gtk_color_selection_get_color(colorsel, colors); + + vals[0] = colors[0] * 0xffff; + vals[1] = colors[1] * 0xffff; + vals[2] = colors[2] * 0xffff; + vals[3] = colorsel->use_opacity ? colors[3] * 0xffff : 0xffff; - sendvals[0] = (GTK_COLOR_SELECTION(thecolorsel)->use_opacity)?1.0:0.0; - gtk_color_selection_get_color(GTK_COLOR_SELECTION(thecolorsel), - sendvals + 1); + g_print ("%x %x\n", vals[0], vals[4]); + g_print ("%g\n", colorsel->values[OPACITY]); + g_print ("%d\n", colorsel->use_opacity); - gtk_widget_dnd_data_set(widget, - event, - (gpointer)sendvals, - sizeof(sendvals)); + gtk_selection_data_set (selection_data, + gdk_atom_intern ("application/x-color", FALSE), + 16, (guchar *)vals, 8); } static void @@ -984,6 +1070,13 @@ gtk_color_selection_value_events (GtkWidget *area, gtk_color_selection_color_changed (colorsel); break; case GDK_MOTION_NOTIFY: + /* Even though we select BUTTON_MOTION_MASK, we seem to need + * to occasionally get events without buttons pressed. + */ + if (!(event->motion.state & + (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK))) + return FALSE; + y = event->motion.y; if (event->motion.is_hint || (event->motion.window != area->window)) @@ -1074,6 +1167,13 @@ gtk_color_selection_wheel_events (GtkWidget *area, gtk_color_selection_color_changed (colorsel); break; case GDK_MOTION_NOTIFY: + /* Even though we select BUTTON_MOTION_MASK, we seem to need + * to occasionally get events without buttons pressed. + */ + if (!(event->motion.state & + (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK))) + return FALSE; + x = event->motion.x; y = event->motion.y; diff --git a/gtk/gtkdebug.h b/gtk/gtkdebug.h index 545df04256..d85ebfef06 100644 --- a/gtk/gtkdebug.h +++ b/gtk/gtkdebug.h @@ -26,7 +26,8 @@ extern "C" { typedef enum { GTK_DEBUG_OBJECTS = 1 << 0, GTK_DEBUG_MISC = 1 << 1, - GTK_DEBUG_SIGNALS = 1 << 2 + GTK_DEBUG_SIGNALS = 1 << 2, + GTK_DEBUG_DND = 1 << 3 } GtkDebugFlag; #ifdef G_ENABLE_DEBUG diff --git a/gtk/gtkdnd.c b/gtk/gtkdnd.c new file mode 100644 index 0000000000..e6cda51332 --- /dev/null +++ b/gtk/gtkdnd.c @@ -0,0 +1,2343 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gdk/gdkx.h" + +#include "gtkdnd.h" +#include "gtkinvisible.h" +#include "gtkmain.h" +#include "gtksignal.h" +#include "gtkwindow.h" + +static GSList *drag_widgets = NULL; + +typedef struct _GtkDragSourceSite GtkDragSourceSite; +typedef struct _GtkDragSourceInfo GtkDragSourceInfo; +typedef struct _GtkDragDestSite GtkDragDestSite; +typedef struct _GtkDragDestInfo GtkDragDestInfo; +typedef struct _GtkDragAnim GtkDragAnim; +typedef struct _GtkDragFindData GtkDragFindData; + + +typedef enum { + GTK_DRAG_STATUS_DRAG, + GTK_DRAG_STATUS_WAIT, + GTK_DRAG_STATUS_DROP +} GtkDragStatus; + +struct _GtkDragSourceSite { + GdkModifierType start_button_mask; + GtkTargetList *target_list; /* Targets for drag data */ + GdkDragAction actions; /* Possible actions */ + GdkColormap *colormap; /* Colormap for drag icon */ + GdkPixmap *pixmap; /* Icon for drag data */ + GdkBitmap *mask; + + /* Stored button press information to detect drag beginning */ + gint state; + gint x, y; +}; + +struct _GtkDragSourceInfo { + GtkWidget *widget; + GtkTargetList *target_list; /* Targets for drag data */ + GdkDragContext *context; /* drag context */ + GtkWidget *icon_window; /* Window for drag */ + GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */ + GdkCursor *cursor; /* Cursor for drag */ + gint hot_x, hot_y; /* Hot spot for drag */ + gint button; /* mouse button starting drag */ + + GtkDragStatus status; /* drag status */ + GdkEvent *last_event; /* motion event waiting for response */ + + gint start_x, start_y; /* Initial position */ + gint cur_x, cur_y; /* Current Position */ + + GList *selections; /* selections we've claimed */ + + GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */ + + guint drop_timeout; /* Timeout for aborting drop */ +}; + +struct _GtkDragDestSite { + GtkDestDefaults flags; + GtkTargetList *target_list; + GdkDragAction actions; + GdkWindow *proxy_window; + GdkDragProtocol proxy_protocol; + gboolean proxy_coords : 1; + gboolean have_drag : 1; +}; + +struct _GtkDragDestInfo { + GtkWidget *widget; /* Widget in which drag is in */ + GdkDragContext *context; /* Drag context */ + GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */ + GtkSelectionData *proxy_data; /* Set while retrieving proxied data */ + gboolean dropped : 1; /* Set after we receive a drop */ + guint32 proxy_drop_time; /* Timestamp for proxied drop */ + gboolean proxy_drop_wait : 1; /* Set if we are waiting for a + * status reply before sending + * a proxied drop on. + */ + gint drop_x, drop_y; /* Position of drop */ +}; + +#define DROP_ABORT_TIME 10000 + +#define ANIM_STEP_TIME 50 +#define ANIM_STEP_LENGTH 50 +#define ANIM_MIN_STEPS 5 +#define ANIM_MAX_STEPS 10 + +struct _GtkDragAnim { + GtkDragSourceInfo *info; + gint step; + gint n_steps; +}; + +struct _GtkDragFindData { + gint x; + gint y; + GdkDragContext *context; + GtkDragDestInfo *info; + gboolean found; + gboolean (*callback) (GtkWidget *widget, GdkDragContext *context, + gint x, gint y, guint32 time); + guint32 time; +}; + +/* Enumeration for some targets we handle internally */ + +enum { + TARGET_MOTIF_SUCCESS = 0x80000000, + TARGET_MOTIF_FAILURE, + TARGET_DELETE +}; + +/* Drag icons */ + +static GdkPixmap *default_icon_pixmap = NULL; +static GdkPixmap *default_icon_mask = NULL; +static GdkColormap *default_icon_colormap = NULL; +static gint default_icon_hot_x; +static gint default_icon_hot_y; + +/* Forward declarations */ +static GdkDragAction gtk_drag_get_event_action (GdkEvent *event, + gint button, + GdkDragAction actions); +static GdkCursor * gtk_drag_get_cursor (GdkDragAction action); +static GtkWidget *gtk_drag_get_ipc_widget (void); +static void gtk_drag_release_ipc_widget (GtkWidget *widget); + +static GdkAtom gtk_drag_dest_find_target (GtkDragDestSite *site, + GdkDragContext *context); +static void gtk_drag_selection_received (GtkWidget *widget, + GtkSelectionData *selection_data, + guint32 time, + gpointer data); +static void gtk_drag_find_widget (GtkWidget *widget, + GtkDragFindData *data); +static void gtk_drag_proxy_begin (GtkWidget *widget, + GtkDragDestInfo *dest_info); +static void gtk_drag_dest_info_destroy (gpointer data); +static void gtk_drag_dest_realized (GtkWidget *widget); +static void gtk_drag_dest_site_destroy (gpointer data); +static void gtk_drag_dest_leave (GtkWidget *widget, + GdkDragContext *context, + guint time); +static gboolean gtk_drag_dest_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time); +static gboolean gtk_drag_dest_drop (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time); + +static void gtk_drag_source_check_selection (GtkDragSourceInfo *info, + GdkAtom selection, + guint32 time); +static void gtk_drag_source_release_selections (GtkDragSourceInfo *info, + guint32 time); +static void gtk_drag_drop (GtkDragSourceInfo *info, + guint32 time); +static void gtk_drag_drop_finished (GtkDragSourceInfo *info, + gboolean success, + guint time); + +static gint gtk_drag_source_event_cb (GtkWidget *widget, + GdkEvent *event, + gpointer data); +static void gtk_drag_source_site_destroy (gpointer data); +static void gtk_drag_selection_get (GtkWidget *widget, + GtkSelectionData *selection_data, + guint sel_info, + guint32 time, + gpointer data); +static gint gtk_drag_anim_timeout (gpointer data); +static void gtk_drag_remove_icon (GtkDragSourceInfo *info); +static void gtk_drag_source_info_destroy (gpointer data); +static gint gtk_drag_motion_cb (GtkWidget *widget, + GdkEventMotion *event, + gpointer data); +static gint gtk_drag_button_release_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer data); +static gint gtk_drag_abort_timeout (gpointer data); + +/************************ + * Cursor and Icon data * + ************************/ + +#define action_ask_width 16 +#define action_ask_height 16 +static char action_ask_bits[] = { + 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xf8, 0xb6, 0xf7, + 0xd6, 0xec, 0x66, 0xdb, 0x66, 0xdb, 0x96, 0xec, 0x76, 0xf7, 0x76, 0xfb, + 0xf6, 0xfc, 0x72, 0xfb, 0x7a, 0xfb, 0xf8, 0xfc, }; + +#define action_ask_mask_width 16 +#define action_ask_mask_height 16 +static char action_ask_mask_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0xcf, 0x0f, + 0xef, 0x1f, 0xff, 0x3c, 0xff, 0x3c, 0x6f, 0x1f, 0x8f, 0x0f, 0x8f, 0x07, + 0x0f, 0x03, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x03, }; + +#define action_copy_width 16 +#define action_copy_height 16 +static char action_copy_bits[] = { + 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x76, 0xfb, 0x76, 0xfb, + 0x76, 0xfb, 0x06, 0x83, 0xf6, 0xbf, 0xf6, 0xbf, 0x06, 0x83, 0x76, 0xfb, + 0x76, 0xfb, 0x72, 0xfb, 0x7a, 0xf8, 0xf8, 0xff, }; + +#define action_copy_mask_width 16 +#define action_copy_mask_height 16 +static char action_copy_mask_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x8f, 0x07, 0x8f, 0x07, + 0x8f, 0x07, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x8f, 0x07, + 0x8f, 0x07, 0x8f, 0x07, 0x87, 0x07, 0x07, 0x00, }; + +#define action_move_width 16 +#define action_move_height 16 +static char action_move_bits[] = { + 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x96, 0xff, 0x26, 0xff, + 0xc6, 0xf8, 0xd6, 0xe3, 0x96, 0x8f, 0xb6, 0xbf, 0x36, 0xc3, 0x76, 0xfb, + 0x76, 0xfa, 0xf2, 0xfa, 0xfa, 0xf8, 0xf8, 0xff, }; + +#define action_move_mask_width 16 +#define action_move_mask_height 16 +static char action_move_mask_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x6f, 0x00, 0xff, 0x00, + 0xff, 0x07, 0xef, 0x1f, 0xef, 0x7f, 0xcf, 0x7f, 0xcf, 0x3f, 0x8f, 0x07, + 0x8f, 0x07, 0x0f, 0x07, 0x07, 0x07, 0x07, 0x00, }; + +#define action_link_width 16 +#define action_link_height 16 +static char action_link_bits[] = { + 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0x36, 0xf8, 0xd6, 0xf7, + 0x66, 0xec, 0xa6, 0xe8, 0x26, 0xdf, 0xe6, 0xbd, 0xd6, 0xa7, 0xb6, 0xa8, + 0xb6, 0xb1, 0x72, 0xdf, 0xfa, 0xe0, 0xf8, 0xff, }; + +#define action_link_mask_width 16 +#define action_link_mask_height 16 +static char action_link_mask_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xcf, 0x07, 0xef, 0x0f, + 0xff, 0x1f, 0x7f, 0x1f, 0xff, 0x3f, 0xff, 0x7f, 0xef, 0x7f, 0xcf, 0x77, + 0xcf, 0x7f, 0x8f, 0x3f, 0x07, 0x1f, 0x07, 0x00, }; + +#define action_none_width 16 +#define action_none_height 16 +static char action_none_bits[] = { + 0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x1f, 0x06, 0xc0, 0xf6, 0xff, 0xf6, 0xff, + 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, + 0xf6, 0xff, 0xf2, 0xff, 0xfa, 0xff, 0xf8, 0xff, }; + +#define action_none_mask_width 16 +#define action_none_mask_height 16 +static char action_none_mask_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x0f, 0x00, + 0x0f, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x07, 0x00, }; + +static char empty_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; + +#define CURSOR_WIDTH 16 +#define CURSOR_HEIGHT 16 + +static struct { + GdkDragAction action; + char *bits; + char *mask; + GdkCursor *cursor; +} drag_cursors[] = { + { GDK_ACTION_DEFAULT, 0 }, + { GDK_ACTION_ASK, action_ask_bits, action_ask_mask_bits, NULL }, + { GDK_ACTION_COPY, action_copy_bits, action_copy_mask_bits, NULL }, + { GDK_ACTION_MOVE, action_move_bits, action_move_mask_bits, NULL }, + { GDK_ACTION_LINK, action_link_bits, action_link_mask_bits, NULL }, + { 0 , action_none_bits, action_none_mask_bits, NULL }, +}; + +static gint n_drag_cursors = sizeof(drag_cursors) / sizeof(drag_cursors[0]); + +/* XPM */ +static char * drag_default_xpm[] = { +"32 32 3 1", +" c None", +". c #000000", +"+ c #FFFFFF", +" ", +" ", +" .. ", +" ..+. ", +" ..++.. ", +" ...++++. ", +" ...++++++.. ", +" ...+++++++++. ", +" ...+++++++++++.. ", +" ..+.++++++++++++.. ", +" .++.++++++++++++.. ", +" .+++.++++++++++++.. ", +" .++++.++++++++++++. ", +" .+++.+++++++++++++.. ", +" .++.+++++++++++++++.. ", +" .+.+++++++++++++++++.. ", +" ..+++++++++++++++++++.. ", +" ..++++++++++++++++++++. ", +" .++++++++++++++++++++.. ", +" ..+++++++++++++++++.. ", +" .++++++++++++++++.. ", +" ..+++++++++++++... ", +" .++++++++++++.. ", +" ..+++++++++.. ", +" .++++++++.. ", +" ..++++++.. ", +" .+++++.. ", +" .++.. ", +" ... ", +" .. ", +" ", +" "}; + +/********************* + * Utility functions * + *********************/ + +/************************************************************* + * gtk_drag_get_ipc_widget: + * Return a invisible, off-screen, override-redirect + * widget for IPC. + * arguments: + * + * results: + *************************************************************/ + +static GtkWidget * +gtk_drag_get_ipc_widget(void) +{ + GtkWidget *result; + + if (drag_widgets) + { + GSList *tmp = drag_widgets; + result = drag_widgets->data; + drag_widgets = drag_widgets->next; + g_slist_free_1 (tmp); + } + else + { + result = gtk_invisible_new(); + gtk_widget_show (result); + } + + return result; +} + +/************************************************************* + * gtk_drag_release_ipc_widget: + * Releases widget retrieved with gtk_drag_get_ipc_widget() + * arguments: + * widget: the widget to release. + * results: + *************************************************************/ + +static void +gtk_drag_release_ipc_widget (GtkWidget *widget) +{ + drag_widgets = g_slist_prepend (drag_widgets, widget); +} + +static GdkDragAction +gtk_drag_get_event_action (GdkEvent *event, gint button, GdkDragAction actions) +{ + if (event) + { + GdkModifierType state = 0; + + switch (event->type) + { + case GDK_MOTION_NOTIFY: + state = event->motion.state; + break; + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + state = event->button.state; + break; + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + state = event->key.state; + break; + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + state = event->crossing.state; + break; + default: + break; + } + + if (button == 3) + return GDK_ACTION_ASK; + + if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) + { + if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK)) + return (actions & GDK_ACTION_LINK) ? GDK_ACTION_LINK : 0; + else if (state & GDK_CONTROL_MASK) + return (actions & GDK_ACTION_COPY) ? GDK_ACTION_COPY : 0; + else + return (actions & GDK_ACTION_MOVE) ? GDK_ACTION_MOVE : 0; + } + else + { + if (actions & GDK_ACTION_COPY) + return GDK_ACTION_COPY; + else if (actions & GDK_ACTION_MOVE) + return GDK_ACTION_MOVE; + else if (actions & GDK_ACTION_LINK) + return GDK_ACTION_LINK; + } + } + + return 0; +} + +static GdkCursor * +gtk_drag_get_cursor (GdkDragAction action) +{ + gint i; + + for (i = 0 ; i < n_drag_cursors - 1; i++) + if (drag_cursors[i].action == action) + break; + + if (drag_cursors[i].cursor == NULL) + { + GdkColor fg, bg; + + GdkPixmap *pixmap = + gdk_bitmap_create_from_data (NULL, + drag_cursors[i].bits, + CURSOR_WIDTH, CURSOR_HEIGHT); + GdkPixmap *mask = + gdk_bitmap_create_from_data (NULL, + drag_cursors[i].mask, + CURSOR_WIDTH, CURSOR_HEIGHT); + + gdk_color_white (gdk_colormap_get_system(), &bg); + gdk_color_black (gdk_colormap_get_system(), &fg); + + drag_cursors[i].cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, 0, 0); + + gdk_pixmap_unref (pixmap); + gdk_pixmap_unref (mask); + } + + return drag_cursors[i].cursor; +} + +/******************** + * Destination side * + ********************/ + +/************************************************************* + * gtk_drag_get_data: + * Get the data for a drag or drop + * arguments: + * context - drag context + * target - format to retrieve the data in. + * time - timestamp of triggering event. + * + * results: + *************************************************************/ + +void +gtk_drag_get_data (GtkWidget *widget, + GdkDragContext *context, + GdkAtom target, + guint32 time) +{ + GtkWidget *selection_widget; + + g_return_if_fail (widget != NULL); + g_return_if_fail (context != NULL); + + selection_widget = gtk_drag_get_ipc_widget(); + + gdk_drag_context_ref (context); + gtk_widget_ref (widget); + + gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received", + GTK_SIGNAL_FUNC (gtk_drag_selection_received), widget); + + gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context); + + gtk_selection_convert (selection_widget, + gdk_drag_get_selection(context), + target, + time); +} + +/************************************************************* + * gtk_drag_finish: + * Notify the drag source that the transfer of data + * is complete. + * arguments: + * context: The drag context for this drag + * success: Was the data successfully transferred? + * time: The timestamp to use when notifying the destination. + * results: + *************************************************************/ + +void +gtk_drag_finish (GdkDragContext *context, + gboolean success, + gboolean delete, + guint32 time) +{ + GdkAtom target = GDK_NONE; + + g_return_if_fail (context != NULL); + + if (success && delete) + { + target = gdk_atom_intern ("DELETE", FALSE); + } + else if (context->protocol == GDK_DRAG_PROTO_MOTIF) + { + target = gdk_atom_intern (success ? + "XmTRANSFER_SUCCESS" : + "XmTRANSFER_FAILURE", + FALSE); + } + + if (target != GDK_NONE) + { + GtkWidget *selection_widget = gtk_drag_get_ipc_widget(); + + gdk_drag_context_ref (context); + + gtk_object_set_data (GTK_OBJECT (selection_widget), "drag-context", context); + gtk_signal_connect (GTK_OBJECT (selection_widget), "selection_received", + GTK_SIGNAL_FUNC (gtk_drag_selection_received), + NULL); + + gtk_selection_convert (selection_widget, + gdk_drag_get_selection(context), + target, + time); + } + + if (!delete) + gdk_drop_finish (context, success, time); +} + +/************************************************************* + * gtk_drag_highlight: + * Highlight the given widget in the default manner. + * arguments: + * widget: + * results: + *************************************************************/ + +void +gtk_drag_highlight (GtkWidget *widget) +{ + gint x, y; + + g_return_if_fail (widget != NULL); + + if (GTK_WIDGET_NO_WINDOW (widget)) + { + x = widget->allocation.x; + y = widget->allocation.y; + } + else + { + x = 0; + y = 0; + } + + gtk_draw_shadow (widget->style, widget->window, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + x, y, + widget->allocation.width, + widget->allocation.height); + + gdk_draw_rectangle (widget->window, + widget->style->black_gc, + FALSE, + x, y, + widget->allocation.width - 1, + widget->allocation.height - 1); +} + +/************************************************************* + * gtk_drag_unhighlight: + * Refresh the given widget to remove the highlight. + * arguments: + * widget: + * results: + *************************************************************/ + +void +gtk_drag_unhighlight (GtkWidget *widget) +{ + gint x, y; + + g_return_if_fail (widget != NULL); + + if (GTK_WIDGET_NO_WINDOW (widget)) + { + x = widget->allocation.x; + y = widget->allocation.y; + } + else + { + x = 0; + y = 0; + } + + gdk_window_clear_area_e (widget->window, + x, y, + widget->allocation.width, + widget->allocation.height); +} + +/************************************************************* + * gtk_drag_dest_set: + * Register a drop site, and possibly add default behaviors. + * arguments: + * widget: + * flags: Which types of default drag behavior to use + * targets: Table of targets that can be accepted + * n_targets: Number of of entries in targets + * actions: + * results: + *************************************************************/ + +void +gtk_drag_dest_set (GtkWidget *widget, + GtkDestDefaults flags, + GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions) +{ + GtkDragDestSite *site; + + g_return_if_fail (widget != NULL); + + /* HACK, do this in the destroy */ + site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"); + if (site) + gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site); + + if (GTK_WIDGET_REALIZED (widget)) + gtk_drag_dest_realized (widget); + + gtk_signal_connect (GTK_OBJECT (widget), "realize", + GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL); + + site = g_new (GtkDragDestSite, 1); + + site->flags = flags; + site->have_drag = FALSE; + if (targets) + site->target_list = gtk_target_list_new (targets, n_targets); + else + site->target_list = NULL; + + site->actions = actions; + site->proxy_window = NULL; + + gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest", + site, gtk_drag_dest_site_destroy); +} + +/************************************************************* + * gtk_drag_dest_set_proxy: + * Set up this widget to proxy drags elsewhere. + * arguments: + * widget: + * proxy_window: window to which forward drag events + * protocol: Drag protocol which the dest widget accepts + * use_coordinates: If true, send the same coordinates to the + * destination, because it is a embedded + * subwindow. + * results: + *************************************************************/ + +void +gtk_drag_dest_set_proxy (GtkWidget *widget, + GdkWindow *proxy_window, + GdkDragProtocol protocol, + gboolean use_coordinates) +{ + GtkDragDestSite *site; + + g_return_if_fail (widget != NULL); + + /* HACK, do this in the destroy */ + site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"); + if (site) + gtk_signal_disconnect_by_data (GTK_OBJECT (widget), site); + + if (GTK_WIDGET_REALIZED (widget)) + gtk_drag_dest_realized (widget); + + gtk_signal_connect (GTK_OBJECT (widget), "realize", + GTK_SIGNAL_FUNC (gtk_drag_dest_realized), NULL); + + site = g_new (GtkDragDestSite, 1); + + site->flags = 0; + site->have_drag = FALSE; + site->target_list = NULL; + site->actions = 0; + site->proxy_window = proxy_window; + gdk_window_ref (proxy_window); + site->proxy_protocol = protocol; + site->proxy_coords = use_coordinates; + + gtk_object_set_data_full (GTK_OBJECT (widget), "gtk-drag-dest", + site, gtk_drag_dest_site_destroy); +} + +/************************************************************* + * gtk_drag_dest_unset + * Unregister this widget as a drag target. + * arguments: + * widget: + * results: + *************************************************************/ + +void +gtk_drag_dest_unset (GtkWidget *widget) +{ + g_return_if_fail (widget != NULL); + + gtk_object_set_data (GTK_OBJECT (widget), "gtk-drag-dest", NULL); +} + +/************************************************************* + * gtk_drag_dest_handle_event: + * Called from widget event handling code on Drag events + * for destinations. + * + * arguments: + * toplevel: Toplevel widget that received the event + * event: + * results: + *************************************************************/ + +void +gtk_drag_dest_handle_event (GtkWidget *toplevel, + GdkEvent *event) +{ + GtkDragDestInfo *info; + GdkDragContext *context; + + g_return_if_fail (toplevel != NULL); + g_return_if_fail (event != NULL); + + context = event->dnd.context; + + info = g_dataset_get_data (context, "gtk-info"); + if (!info) + { + info = g_new (GtkDragDestInfo, 1); + info->widget = NULL; + info->context = event->dnd.context; + info->proxy_source = NULL; + info->proxy_data = NULL; + info->dropped = FALSE; + info->proxy_drop_wait = FALSE; + g_dataset_set_data_full (context, + "gtk-info", + info, + gtk_drag_dest_info_destroy); + } + + /* Find the widget for the event */ + switch (event->type) + { + case GDK_DRAG_ENTER: + break; + + case GDK_DRAG_LEAVE: + if (info->widget) + { + gtk_drag_dest_leave (info->widget, context, event->dnd.time); + info->widget = NULL; + } + break; + + case GDK_DRAG_MOTION: + case GDK_DROP_START: + { + GtkDragFindData data; + gint tx, ty; + + if (event->type == GDK_DROP_START) + info->dropped = TRUE; + + gdk_window_get_position (toplevel->window, &tx, &ty); + + data.x = event->dnd.x_root - tx; + data.y = event->dnd.y_root - ty; + data.context = context; + data.info = info; + data.found = FALSE; + data.callback = (event->type == GDK_DRAG_MOTION) ? + gtk_drag_dest_motion : gtk_drag_dest_drop; + data.time = event->dnd.time; + + gtk_drag_find_widget (toplevel, &data); + + /* We send a leave here so that the widget unhighlights + * properly + */ + if (info->widget && + ((event->type == GDK_DROP_START) || (!data.found))) + { + gtk_drag_dest_leave (info->widget, context, event->dnd.time); + info->widget = NULL; + } + + /* Send a reply. + */ + if (event->type == GDK_DRAG_MOTION) + { + if (!data.found) + gdk_drag_status (context, 0, event->dnd.time); + } + else if (event->type == GDK_DROP_START) + gdk_drop_reply (context, data.found, event->dnd.time); + } + break; + + default: + g_assert_not_reached(); + } +} + +/************************************************************* + * gtk_drag_dest_find_target: + * Decide on a target for the drag. + * arguments: + * site: + * context: + * results: + *************************************************************/ + +static GdkAtom +gtk_drag_dest_find_target (GtkDragDestSite *site, + GdkDragContext *context) +{ + GList *tmp_target; + GList *tmp_source = NULL; + + tmp_target = site->target_list->list; + while (tmp_target) + { + GtkTargetPair *pair = tmp_target->data; + tmp_source = context->targets; + while (tmp_source) + { + if (tmp_source->data == GUINT_TO_POINTER (pair->target)) + return pair->target; + tmp_source = tmp_source->next; + } + tmp_target = tmp_target->next; + } + + return GDK_NONE; +} + +static void +gtk_drag_selection_received (GtkWidget *widget, + GtkSelectionData *selection_data, + guint32 time, + gpointer data) +{ + GdkDragContext *context; + GtkDragDestInfo *info; + GtkWidget *drop_widget; + + drop_widget = data; + + context = gtk_object_get_data (GTK_OBJECT (widget), "drag-context"); + info = g_dataset_get_data (context, "gtk-info"); + + if (info->proxy_data && + info->proxy_data->target == selection_data->target) + { + gtk_selection_data_set (info->proxy_data, + selection_data->type, + selection_data->format, + selection_data->data, + selection_data->length); + gtk_main_quit(); + return; + } + + if (selection_data->target == gdk_atom_intern ("DELETE", FALSE)) + { + gtk_drag_finish (context, TRUE, FALSE, time); + } + else if ((selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE)) || + (selection_data->target == gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE))) + { + /* Do nothing */ + } + else + { + GtkDragDestSite *site; + + site = gtk_object_get_data (GTK_OBJECT (drop_widget), "gtk-drag-dest"); + + if (site->target_list) + { + guint target_info; + + if (gtk_target_list_find (site->target_list, + selection_data->target, + &target_info)) + { + if (!(site->flags & GTK_DEST_DEFAULT_DROP) || + selection_data->length >= 0) + gtk_signal_emit_by_name (GTK_OBJECT (drop_widget), + "drag_data_received", + context, info->drop_x, info->drop_y, + selection_data, + target_info, time); + } + } + else + { + gtk_signal_emit_by_name (GTK_OBJECT (drop_widget), + "drag_data_received", + context, info->drop_x, info->drop_y, + selection_data, 0, time); + } + + if (site->flags & GTK_DEST_DEFAULT_DROP) + { + + gtk_drag_finish (context, + (selection_data->length >= 0), + (context->action == GDK_ACTION_MOVE), + time); + } + + gtk_widget_unref (drop_widget); + } + + gtk_signal_disconnect_by_func (GTK_OBJECT (widget), + GTK_SIGNAL_FUNC (gtk_drag_selection_received), + data); + + gtk_object_set_data (GTK_OBJECT (widget), "drag-context", NULL); + gdk_drag_context_unref (context); + + gtk_drag_release_ipc_widget (widget); +} + +/************************************************************* + * gtk_drag_find_widget: + * Recursive callback used to locate widgets for + * DRAG_MOTION and DROP_START events. + * arguments: + * + * results: + *************************************************************/ + +static void +gtk_drag_find_widget (GtkWidget *widget, + GtkDragFindData *data) +{ + GtkAllocation new_allocation; + gint x_offset = 0; + gint y_offset = 0; + + new_allocation = widget->allocation; + + if (!GTK_WIDGET_VISIBLE (widget)) + return; + + if (!GTK_WIDGET_NO_WINDOW (widget)) + { + new_allocation.x = 0; + new_allocation.y = 0; + } + + if (widget->parent) + { + GdkWindow *window = widget->window; + while (window != widget->parent->window) + { + gint tx, ty, twidth, theight; + gdk_window_get_size (window, &twidth, &theight); + + if (new_allocation.x < 0) + { + new_allocation.width += new_allocation.x; + new_allocation.x = 0; + } + if (new_allocation.y < 0) + { + new_allocation.height += new_allocation.y; + new_allocation.y = 0; + } + if (new_allocation.x + new_allocation.width > twidth) + new_allocation.width = twidth - new_allocation.x; + if (new_allocation.y + new_allocation.height > theight) + new_allocation.height = theight - new_allocation.y; + + gdk_window_get_position (window, &tx, &ty); + new_allocation.x += tx; + x_offset += tx; + new_allocation.y += ty; + y_offset += ty; + + window = gdk_window_get_parent (window); + } + } + + if ((data->x >= new_allocation.x) && (data->y >= new_allocation.y) && + (data->x < new_allocation.x + new_allocation.width) && + (data->y < new_allocation.y + new_allocation.height)) + { + /* First, check if the drag is in a valid drop site in + * one of our children + */ + if (GTK_IS_CONTAINER (widget)) + { + GtkDragFindData new_data = *data; + + new_data.x -= x_offset; + new_data.y -= y_offset; + new_data.found = FALSE; + + gtk_container_foreach (GTK_CONTAINER (widget), + (GtkCallback)gtk_drag_find_widget, + &new_data); + + data->found = new_data.found; + } + + /* If not, and this widget is registered as a drop site, check to + * emit "drag_motion" to check if we are actually in + * a drop site. + */ + if (!data->found && + gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest")) + { + data->found = data->callback (widget, + data->context, + data->x - new_allocation.x, + data->y - new_allocation.y, + data->time); + /* If so, send a "drag_leave" to the last widget */ + if (data->found) + { + if (data->info->widget && data->info->widget != widget) + { + gtk_drag_dest_leave (data->info->widget, data->context, data->time); + } + data->info->widget = widget; + } + } + } +} + +static void +gtk_drag_proxy_begin (GtkWidget *widget, GtkDragDestInfo *dest_info) +{ + GtkDragSourceInfo *source_info; + GList *tmp_list; + + source_info = g_new0 (GtkDragSourceInfo, 1); + source_info->ipc_widget = gtk_drag_get_ipc_widget (); + + source_info->widget = widget; + gtk_widget_ref (source_info->widget); + source_info->context = gdk_drag_begin (source_info->ipc_widget->window, + dest_info->context->targets, + dest_info->context->actions); + + source_info->target_list = gtk_target_list_new (NULL, 0); + tmp_list = dest_info->context->targets; + while (tmp_list) + { + gtk_target_list_add (source_info->target_list, + GPOINTER_TO_UINT (tmp_list->data), 0, 0); + tmp_list = tmp_list->next; + } + + source_info->proxy_dest = dest_info; + + g_dataset_set_data (source_info->context, "gtk-info", source_info); + + gtk_signal_connect (GTK_OBJECT (source_info->ipc_widget), + "selection_get", + GTK_SIGNAL_FUNC (gtk_drag_selection_get), + source_info); + + dest_info->proxy_source = source_info; +} + +static void +gtk_drag_dest_info_destroy (gpointer data) +{ + GtkDragDestInfo *info = data; + + g_free (info); +} + +static void +gtk_drag_dest_realized (GtkWidget *widget) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (widget); + gdk_window_register_dnd (toplevel->window); +} + +static void +gtk_drag_dest_site_destroy (gpointer data) +{ + GtkDragDestSite *site = data; + + if (site->target_list) + gtk_target_list_unref (site->target_list); + + g_free (site); +} + +/* + * Default drag handlers + */ +static void +gtk_drag_dest_leave (GtkWidget *widget, + GdkDragContext *context, + guint time) +{ + GtkDragDestSite *site; + + site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"); + g_return_if_fail (site != NULL); + + if (site->proxy_window) + { + GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info"); + + if (info->proxy_source && !info->dropped) + gdk_drag_abort (info->proxy_source->context, time); + + return; + } + else if (site->have_drag) + { + site->have_drag = FALSE; + + if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) + gtk_drag_unhighlight (widget); + + gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_leave", + context, time); + } +} + +static gboolean +gtk_drag_dest_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + GtkDragDestSite *site; + GdkDragAction action = 0; + gboolean retval; + + site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"); + g_return_val_if_fail (site != NULL, FALSE); + + if (site->proxy_window) + { + GdkAtom selection; + GdkEvent *current_event; + GtkDragDestInfo *info = g_dataset_get_data (context, "gtk-info"); + + if (!info->proxy_source) + gtk_drag_proxy_begin (widget, info); + + current_event = gtk_get_current_event (); + + gdk_drag_motion (info->proxy_source->context, + site->proxy_window, + site->proxy_protocol, + current_event->dnd.x_root, + current_event->dnd.y_root, + context->suggested_action, time); + + selection = gdk_drag_get_selection (info->proxy_source->context); + if (selection && + selection != gdk_drag_get_selection (info->context)) + gtk_drag_source_check_selection (info->proxy_source, selection, time); + + gdk_event_free (current_event); + + return TRUE; + } + + if (site->flags & GTK_DEST_DEFAULT_MOTION) + { + if (context->suggested_action & site->actions) + action = context->suggested_action; + else + { + gint i; + + for (i=0; i<8; i++) + { + if ((site->actions & (1 << i)) && + (context->actions & (1 << i))) + { + action = (1 << i); + break; + } + } + } + + if (action && gtk_drag_dest_find_target (site, context)) + { + if (!site->have_drag) + { + site->have_drag = TRUE; + if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) + gtk_drag_highlight (widget); + } + + gdk_drag_status (context, action, time); + } + else + { + gdk_drag_status (context, 0, time); + return TRUE; + } + } + + gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_motion", + context, x, y, time, &retval); + + return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval; +} + +static gboolean +gtk_drag_dest_drop (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time) +{ + GtkDragDestSite *site; + GtkDragDestInfo *info; + + site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-drag-dest"); + g_return_val_if_fail (site != NULL, FALSE); + + info = g_dataset_get_data (context, "gtk-info"); + g_return_val_if_fail (info != NULL, FALSE); + + info->drop_x = x; + info->drop_y = y; + + if (site->proxy_window) + { + if (info->proxy_source || + (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN)) + { + gtk_drag_drop (info->proxy_source, time); + } + else + { + /* We need to synthesize a motion event, wait for a status, + * and, if we get one a good one, do a drop. + */ + + GdkEvent *current_event; + GdkAtom selection; + + gtk_drag_proxy_begin (widget, info); + info->proxy_drop_wait = TRUE; + info->proxy_drop_time = time; + + current_event = gtk_get_current_event (); + + gdk_drag_motion (info->proxy_source->context, + site->proxy_window, + site->proxy_protocol, + current_event->dnd.x_root, + current_event->dnd.y_root, + context->suggested_action, time); + + selection = gdk_drag_get_selection (info->proxy_source->context); + if (selection && + selection != gdk_drag_get_selection (info->context)) + gtk_drag_source_check_selection (info->proxy_source, selection, time); + + gdk_event_free (current_event); + + } + + return TRUE; + } + else + { + gboolean retval; + + if (site->flags & GTK_DEST_DEFAULT_MOTION) + { + GdkAtom target = gtk_drag_dest_find_target (site, context); + + if (target == GDK_NONE) + return FALSE; + + gtk_drag_get_data (widget, context, target, time); + } + + gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_drop", + context, x, y, time, &retval); + + return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval; + } +} + +/*************** + * Source side * + ***************/ + +/************************************************************* + * gtk_drag_begin: Start a drag operation + * + * arguments: + * widget: Widget from which drag starts + * handlers: List of handlers to supply the data for the drag + * button: Button user used to start drag + * time: Time of event starting drag + * + * results: + *************************************************************/ + +GdkDragContext * +gtk_drag_begin (GtkWidget *widget, + GtkTargetList *target_list, + GdkDragAction actions, + gint button, + GdkEvent *event) +{ + GtkDragSourceInfo *info; + GList *targets = NULL; + GList *tmp_list; + guint32 time = GDK_CURRENT_TIME; + + g_return_val_if_fail (widget != NULL, NULL); + g_return_val_if_fail (GTK_WIDGET_REALIZED (widget), NULL); + g_return_val_if_fail (target_list != NULL, NULL); + + if (event) + time = gdk_event_get_time (event); + + info = g_new0 (GtkDragSourceInfo, 1); + info->ipc_widget = gtk_drag_get_ipc_widget (); + + tmp_list = g_list_last (target_list->list); + while (tmp_list) + { + GtkTargetPair *pair = tmp_list->data; + targets = g_list_prepend (targets, + GINT_TO_POINTER (pair->target)); + tmp_list = tmp_list->prev; + } + + info->widget = widget; + gtk_widget_ref (info->widget); + + info->context = gdk_drag_begin (info->ipc_widget->window, + targets, actions); + g_list_free (targets); + + g_dataset_set_data (info->context, "gtk-info", info); + + info->button = button; + info->target_list = target_list; + gtk_target_list_ref (target_list); + + info->cursor = NULL; + info->status = GTK_DRAG_STATUS_DRAG; + info->last_event = NULL; + info->selections = NULL; + info->icon_window = NULL; + + if (event) + info->cursor = gtk_drag_get_cursor ( + gtk_drag_get_event_action (event, info->button, actions)); + + gtk_signal_emit_by_name (GTK_OBJECT (widget), "drag_begin", + info->context, NULL); + + /* We use a GTK grab here to override any grabs that the widget + * we are dragging from might have held + */ + + gtk_grab_add (info->ipc_widget); + gdk_pointer_grab (info->ipc_widget->window, FALSE, + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | + GDK_BUTTON_RELEASE_MASK, NULL, + info->cursor, time); + + if (event->type == GDK_MOTION_NOTIFY) + gtk_drag_motion_cb (info->ipc_widget, (GdkEventMotion *)event, info); + else + { + gint x, y; + gdk_window_get_pointer (GDK_ROOT_PARENT(), &x, &y, NULL); + + info->cur_x = x; + info->cur_y = y; + + if (info->icon_window) + { + gdk_window_raise (info->icon_window->window); + gtk_widget_set_uposition (info->icon_window, x - info->hot_x, y - info->hot_y); + } + } + + info->start_x = info->cur_x; + info->start_y = info->cur_y; + + gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "button_release_event", + GTK_SIGNAL_FUNC (gtk_drag_button_release_cb), info); + gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "motion_notify_event", + GTK_SIGNAL_FUNC (gtk_drag_motion_cb), info); + gtk_signal_connect (GTK_OBJECT (info->ipc_widget), "selection_get", + GTK_SIGNAL_FUNC (gtk_drag_selection_get), info); + + return info->context; +} + +/************************************************************* + * gtk_drag_source_set: + * Register a drop site, and possibly add default behaviors. + * arguments: + * widget: + * start_button_mask: Mask of allowed buttons to start drag + * targets: Table of targets for this source + * n_targets: + * actions: Actions allowed for this source + * results: + *************************************************************/ + +void +gtk_drag_source_set (GtkWidget *widget, + GdkModifierType start_button_mask, + GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions) +{ + GtkDragSourceSite *site; + + g_return_if_fail (widget != NULL); + + site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data"); + + gtk_widget_add_events (widget, + gtk_widget_get_events (widget) | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_MOTION_MASK); + + if (site) + { + if (site->target_list) + gtk_target_list_unref (site->target_list); + } + else + { + site = g_new0 (GtkDragSourceSite, 1); + + gtk_signal_connect (GTK_OBJECT (widget), "button_press_event", + GTK_SIGNAL_FUNC (gtk_drag_source_event_cb), + site); + gtk_signal_connect (GTK_OBJECT (widget), "motion_notify_event", + GTK_SIGNAL_FUNC (gtk_drag_source_event_cb), + site); + + gtk_object_set_data_full (GTK_OBJECT (widget), + "gtk-site-data", + site, gtk_drag_source_site_destroy); + } + + site->start_button_mask = start_button_mask; + + if (targets) + site->target_list = gtk_target_list_new (targets, n_targets); + else + site->target_list = NULL; + + site->actions = actions; + +} + +/************************************************************* + * gtk_drag_source_set_icon: + * Set an icon for drags from this source. + * arguments: + * colormap: Colormap for this icon + * pixmap: + * mask + * results: + *************************************************************/ + +void +gtk_drag_source_set_icon (GtkWidget *widget, + GdkColormap *colormap, + GdkPixmap *pixmap, + GdkBitmap *mask) +{ + GtkDragSourceSite *site; + + g_return_if_fail (widget != NULL); + + site = gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data"); + g_return_if_fail (site != NULL); + + if (site->colormap) + gdk_colormap_unref (site->colormap); + if (site->pixmap) + gdk_pixmap_unref (site->pixmap); + if (site->mask) + gdk_pixmap_unref (site->mask); + + site->colormap = colormap; + if (colormap) + gdk_colormap_ref (colormap); + + site->pixmap = pixmap; + if (pixmap) + gdk_pixmap_ref (pixmap); + + site->mask = mask; + if (mask) + gdk_pixmap_ref (mask); +} + +/************************************************************* + * gtk_drag_set_icon_widget: + * Set a widget as the icon for a drag. + * arguments: + * context: + * widget: + * hot_x: Hot spot + * hot_y: + * results: + *************************************************************/ + +void +gtk_drag_set_icon_widget (GdkDragContext *context, + GtkWidget *widget, + gint hot_x, + gint hot_y) +{ + GtkDragSourceInfo *info; + + g_return_if_fail (context != NULL); + g_return_if_fail (widget != NULL); + + info = g_dataset_get_data (context, "gtk-info"); + gtk_drag_remove_icon (info); + + info->icon_window = widget; + if (widget) + { + gtk_widget_set_uposition (widget, info->cur_x, info->cur_y); + gtk_widget_ref (widget); + gdk_window_raise (widget->window); + gtk_widget_show (widget); + } + + info->hot_x = hot_x; + info->hot_y = hot_y; +} + +/************************************************************* + * gtk_drag_set_icon_pixmap: + * Set a widget as the icon for a drag. + * arguments: + * context: + * colormap: Colormap for the icon window. + * pixmap: + * mask: + * hot_x: Hot spot + * hot_y: + * results: + *************************************************************/ + +void +gtk_drag_set_icon_pixmap (GdkDragContext *context, + GdkColormap *colormap, + GdkPixmap *pixmap, + GdkBitmap *mask, + gint hot_x, + gint hot_y) +{ + GtkWidget *window; + gint width, height; + + g_return_if_fail (context != NULL); + g_return_if_fail (colormap != NULL); + g_return_if_fail (pixmap != NULL); + + gdk_window_get_size (pixmap, &width, &height); + + gtk_widget_push_visual (gdk_colormap_get_visual(colormap)); + gtk_widget_push_colormap (colormap); + + window = gtk_window_new (GTK_WINDOW_POPUP); + + gtk_widget_pop_visual (); + gtk_widget_pop_colormap (); + + gtk_widget_set_usize (window, width, height); + gtk_widget_realize (window); + + gdk_window_set_back_pixmap (window->window, pixmap, FALSE); + + if (mask) + gtk_widget_shape_combine_mask (window, mask, 0, 0); + + gtk_drag_set_icon_widget (context, window, hot_x, hot_y); +} + +/************************************************************* + * gtk_drag_set_icon_default: + * Set the icon for a drag to the default icon. + * arguments: + * context: + * results: + *************************************************************/ + +void +gtk_drag_set_icon_default (GdkDragContext *context) +{ + g_return_if_fail (context != NULL); + + if (!default_icon_pixmap) + { + default_icon_colormap = gdk_colormap_get_system (); + default_icon_pixmap = + gdk_pixmap_colormap_create_from_xpm_d (NULL, + default_icon_colormap, + &default_icon_mask, + NULL, drag_default_xpm); + default_icon_hot_x = -2; + default_icon_hot_y = -2; + } + + gtk_drag_set_icon_pixmap (context, + default_icon_colormap, + default_icon_pixmap, + default_icon_mask, + default_icon_hot_x, + default_icon_hot_y); +} + +/************************************************************* + * gtk_drag_set_default_icon: + * Set a default icon for all drags as a pixmap. + * arguments: + * colormap: Colormap for the icon window. + * pixmap: + * mask: + * hot_x: Hot spot + * hot_y: + * results: + *************************************************************/ + +void +gtk_drag_set_default_icon (GdkColormap *colormap, + GdkPixmap *pixmap, + GdkBitmap *mask, + gint hot_x, + gint hot_y) +{ + g_return_if_fail (colormap != NULL); + g_return_if_fail (pixmap != NULL); + + if (default_icon_colormap) + gdk_colormap_unref (default_icon_colormap); + if (default_icon_pixmap) + gdk_pixmap_unref (default_icon_pixmap); + if (default_icon_mask) + gdk_pixmap_unref (default_icon_pixmap); + + default_icon_colormap = colormap; + gdk_colormap_ref (colormap); + + default_icon_pixmap = pixmap; + gdk_pixmap_ref (pixmap); + + default_icon_mask = mask; + if (mask) + gdk_pixmap_ref (mask); + + default_icon_hot_x = hot_x; + default_icon_hot_y = hot_y; +} + + +/************************************************************* + * gtk_drag_source_handle_event: + * Called from widget event handling code on Drag events + * for drag sources. + * + * arguments: + * toplevel: Toplevel widget that received the event + * event: + * results: + *************************************************************/ + +void +gtk_drag_source_handle_event (GtkWidget *widget, + GdkEvent *event) +{ + GtkDragSourceInfo *info; + GdkDragContext *context; + + g_return_if_fail (widget != NULL); + g_return_if_fail (event != NULL); + + context = event->dnd.context; + info = g_dataset_get_data (context, "gtk-info"); + if (!info) + return; + + switch (event->type) + { + case GDK_DRAG_STATUS: + { + GdkCursor *cursor; + + if (info->proxy_dest) + { + if (!event->dnd.send_event) + { + if (info->proxy_dest->proxy_drop_wait) + { + /* Aha - we can finally pass the MOTIF DROP on... */ + gdk_drag_drop (info->context, info->proxy_dest->proxy_drop_time); + } + else + { + gdk_drag_status (info->proxy_dest->context, + event->dnd.context->action, + event->dnd.time); + } + } + } + else + { + cursor = gtk_drag_get_cursor (event->dnd.context->action); + if (info->cursor != cursor) + { + XChangeActivePointerGrab (GDK_WINDOW_XDISPLAY (widget->window), + PointerMotionMask | PointerMotionHintMask | ButtonReleaseMask, + ((GdkCursorPrivate *)cursor)->xcursor, + event->dnd.time); + info->cursor = cursor; + } + + if (info->last_event) + { + gtk_drag_motion_cb (info->widget, + (GdkEventMotion *)info->last_event, + info); + info->last_event = NULL; + } + } + } + break; + + case GDK_DROP_FINISHED: + gtk_drag_drop_finished (info, TRUE, event->dnd.time); + break; + default: + g_assert_not_reached(); + } +} + +/************************************************************* + * gtk_drag_source_check_selection: + * Check if we've set up handlers/claimed the selection + * for a given drag. If not, add them. + * arguments: + * + * results: + *************************************************************/ + +static void +gtk_drag_source_check_selection (GtkDragSourceInfo *info, + GdkAtom selection, + guint32 time) +{ + GList *tmp_list; + + tmp_list = info->selections; + while (tmp_list) + { + if (GPOINTER_TO_UINT (tmp_list->data) == selection) + return; + tmp_list = tmp_list->next; + } + + gtk_selection_owner_set (info->ipc_widget, selection, time); + info->selections = g_list_prepend (info->selections, + GUINT_TO_POINTER (selection)); + + tmp_list = info->target_list->list; + while (tmp_list) + { + GtkTargetPair *pair = tmp_list->data; + + gtk_selection_add_target (info->ipc_widget, + selection, + pair->target, + pair->info); + tmp_list = tmp_list->next; + } + + if (info->context->protocol == GDK_DRAG_PROTO_MOTIF) + { + gtk_selection_add_target (info->ipc_widget, + selection, + gdk_atom_intern ("XmTRANSFER_SUCCESS", FALSE), + TARGET_MOTIF_SUCCESS); + gtk_selection_add_target (info->ipc_widget, + selection, + gdk_atom_intern ("XmTRANSFER_FAILURE", FALSE), + TARGET_MOTIF_FAILURE); + } + + gtk_selection_add_target (info->ipc_widget, + selection, + gdk_atom_intern ("DELETE", FALSE), + TARGET_DELETE); +} + +/************************************************************* + * gtk_drag_drop_finished: + * Clean up from the drag, and display snapback, if necessary. + * arguments: + * info: + * success: + * time: + * results: + *************************************************************/ + +static void +gtk_drag_drop_finished (GtkDragSourceInfo *info, + gboolean success, + guint time) +{ + if (info->proxy_dest) + { + /* The time from the event isn't reliable for Xdnd drags */ + gtk_drag_finish (info->proxy_dest->context, success, FALSE, + info->proxy_dest->proxy_drop_time); + } + else + { + if (success) + { + gtk_drag_source_info_destroy (info); + } + else + { + GtkDragAnim *anim = g_new (GtkDragAnim, 1); + anim->info = info; + anim->step = 0; + + anim->n_steps = MAX (info->cur_x - info->start_x, + info->cur_y - info->start_y) / ANIM_STEP_LENGTH; + anim->n_steps = CLAMP (anim->n_steps, ANIM_MIN_STEPS, ANIM_MAX_STEPS); + + gtk_timeout_add (ANIM_STEP_TIME, gtk_drag_anim_timeout, anim); + } + } + + gtk_drag_source_release_selections (info, GDK_CURRENT_TIME); /* fixme */ +} + +static void +gtk_drag_source_release_selections (GtkDragSourceInfo *info, + guint32 time) +{ + GList *tmp_list = info->selections; + while (tmp_list) + { + GdkAtom selection = GPOINTER_TO_UINT (tmp_list->data); + if (gdk_selection_owner_get (selection) == info->ipc_widget->window) + gtk_selection_owner_set (NULL, selection, time); + tmp_list = tmp_list->next; + } + + g_list_free (info->selections); + info->selections = NULL; +} + +/************************************************************* + * gtk_drag_drop: + * Send a drop event. + * arguments: + * + * results: + *************************************************************/ + +static void +gtk_drag_drop (GtkDragSourceInfo *info, guint32 time) +{ + if (info->context->protocol == GDK_DRAG_PROTO_ROOTWIN) + { + GtkSelectionData selection_data; + GList *tmp_list; + GdkAtom target = gdk_atom_intern ("application/x-rootwin-drop", FALSE); + + tmp_list = info->target_list->list; + while (tmp_list) + { + GtkTargetPair *pair = tmp_list->data; + + if (pair->target == target) + { + selection_data.selection = GDK_NONE; + selection_data.target = target; + selection_data.data = NULL; + selection_data.length = -1; + + gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get", + info->context, &selection_data, + pair->info, + time); + + /* FIXME: Should we check for length >= 0 here? */ + gtk_drag_drop_finished (info, TRUE, time); + return; + } + tmp_list = tmp_list->next; + } + gtk_drag_drop_finished (info, FALSE, time); + } + else + { + gdk_drag_drop (info->context, time); + info->drop_timeout = gtk_timeout_add (DROP_ABORT_TIME, + gtk_drag_abort_timeout, + info); + } +} + +/* + * Source side callbacks. + */ + +static gint +gtk_drag_source_event_cb (GtkWidget *widget, + GdkEvent *event, + gpointer data) +{ + GtkDragSourceSite *site; + site = (GtkDragSourceSite *)data; + + switch (event->type) + { + case GDK_BUTTON_PRESS: + if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask) + { + site->x = event->button.x; + site->y = event->button.y; + } + + case GDK_MOTION_NOTIFY: + if (event->motion.state & site->start_button_mask) + { + /* FIXME: This is really broken and can leave us + * with a stuck grab + */ + int i; + for (i=1; i<6; i++) + { + if (event->motion.state & GDK_BUTTON1_MASK << (i - 1)) + break; + } + + if (MAX (abs(site->x - event->motion.x), + abs(site->y - event->motion.y)) > 3) + { + GtkDragSourceInfo *info; + GdkDragContext *context; + + context = gtk_drag_begin (widget, site->target_list, + site->actions, + i, event); + + + info = g_dataset_get_data (context, "gtk-info"); + + if (!info->icon_window) + { + if (site->pixmap) + gtk_drag_set_icon_pixmap (context, + site->colormap, + site->pixmap, + site->mask, -2, -2); + else + gtk_drag_set_icon_default (context); + } + + return TRUE; + } + } + break; + + default: /* hit for 2/3BUTTON_PRESS */ + } + return FALSE; +} + +static void +gtk_drag_source_site_destroy (gpointer data) +{ + GtkDragSourceSite *site = data; + + if (site->target_list) + gtk_target_list_unref (site->target_list); + + if (site->pixmap) + gdk_pixmap_unref (site->pixmap); + + if (site->mask) + gdk_pixmap_unref (site->mask); + + g_free (site); +} + +static void +gtk_drag_selection_get (GtkWidget *widget, + GtkSelectionData *selection_data, + guint sel_info, + guint32 time, + gpointer data) +{ + GtkDragSourceInfo *info = data; + static GdkAtom null_atom = GDK_NONE; + guint target_info; + + if (!null_atom) + null_atom = gdk_atom_intern ("NULL", FALSE); + + switch (sel_info) + { + case TARGET_DELETE: + gtk_signal_emit_by_name (GTK_OBJECT (info->widget), + "drag_data_delete", + info->context); + gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0); + break; + case TARGET_MOTIF_SUCCESS: + gtk_drag_drop_finished (info, TRUE, time); + gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0); + break; + case TARGET_MOTIF_FAILURE: + gtk_drag_drop_finished (info, FALSE, time); + gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0); + break; + default: + if (info->proxy_dest) + { + /* This is sort of dangerous and needs to be thought + * through better + */ + info->proxy_dest->proxy_data = selection_data; + gtk_drag_get_data (info->widget, + info->proxy_dest->context, + selection_data->target, + time); + gtk_main(); + info->proxy_dest->proxy_data = NULL; + } + else + { + if (gtk_target_list_find (info->target_list, + selection_data->target, + &target_info)) + { + gtk_signal_emit_by_name (GTK_OBJECT (info->widget), "drag_data_get", + info->context, + selection_data, + target_info, + time); + } + } + break; + } +} + +static gint +gtk_drag_anim_timeout (gpointer data) +{ + GtkDragAnim *anim = data; + gint x, y; + + if (anim->step == anim->n_steps) + { + gtk_drag_source_info_destroy (anim->info); + g_free (anim); + + return FALSE; + } + else + { + x = (anim->info->start_x * (anim->step + 1) + + anim->info->cur_x * (anim->n_steps - anim->step - 1)) / anim->n_steps; + y = (anim->info->start_y * (anim->step + 1) + + anim->info->cur_y * (anim->n_steps - anim->step - 1)) / anim->n_steps; + if (anim->info->icon_window) + gtk_widget_set_uposition (anim->info->icon_window, x, y); + + anim->step++; + + return TRUE; + } +} + +static void +gtk_drag_remove_icon (GtkDragSourceInfo *info) +{ + if (info->icon_window) + { + gtk_widget_hide (info->icon_window); + gtk_widget_unref (info->icon_window); + + info->icon_window = NULL; + } +} + +static void +gtk_drag_source_info_destroy (gpointer data) +{ + GtkDragSourceInfo *info = data; + + gtk_drag_remove_icon (data); + + if (info->widget) + gtk_widget_unref (info->widget); + + gtk_signal_disconnect_by_data (GTK_OBJECT (info->ipc_widget), info); + gtk_selection_remove_all (info->ipc_widget); + gtk_drag_release_ipc_widget (info->ipc_widget); + + gtk_target_list_unref (info->target_list); + + g_dataset_set_data (info->context, "gtk-info", NULL); + gdk_drag_context_unref (info->context); + + if (info->drop_timeout) + gtk_timeout_remove (info->drop_timeout); + + g_free (info); +} + +/************************************************************* + * gtk_drag_motion_cb: + * "motion_notify_event" callback during drag. + * arguments: + * + * results: + *************************************************************/ + +static gint +gtk_drag_motion_cb (GtkWidget *widget, + GdkEventMotion *event, + gpointer data) +{ + GtkDragSourceInfo *info = (GtkDragSourceInfo *)data; + GdkAtom selection; + GdkDragAction action; + GdkWindow *window = NULL; + GdkWindow *dest_window; + GdkDragProtocol protocol; + gint x_root, y_root; + + if (event->is_hint) + { + gdk_window_get_pointer (GDK_ROOT_PARENT(), &x_root, &y_root, NULL); + event->x_root = x_root; + event->y_root = y_root; + } + + action = gtk_drag_get_event_action ((GdkEvent *)event, + info->button, + info->context->actions); + + info->cur_x = event->x_root - info->hot_x; + info->cur_y = event->y_root - info->hot_y; + + if (info->icon_window) + { + gdk_window_raise (info->icon_window->window); + gtk_widget_set_uposition (info->icon_window, info->cur_x, info->cur_y); + window = info->icon_window->window; + } + + gdk_drag_find_window (info->context, + window, event->x_root, event->y_root, + &dest_window, &protocol); + + if (gdk_drag_motion (info->context, dest_window, protocol, + event->x_root, event->y_root, action, + event->time)) + { + if (info->last_event) + gdk_event_free ((GdkEvent *)info->last_event); + + info->last_event = gdk_event_copy ((GdkEvent *)event); + } + + if (dest_window) + gdk_window_unref (dest_window); + + selection = gdk_drag_get_selection (info->context); + if (selection) + gtk_drag_source_check_selection (info, selection, event->time); + +#if 0 + /* We ignore the response, so we can respond precisely to the drop + */ + if (event->is_hint) + gdk_window_get_pointer (widget->window, NULL, NULL, NULL); +#endif + + return TRUE; +} + +/************************************************************* + * gtk_drag_motion_cb: + * "button_release_event" callback during drag. + * arguments: + * + * results: + *************************************************************/ + +static gint +gtk_drag_button_release_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + GtkDragSourceInfo *info = (GtkDragSourceInfo *)data; + GtkWidget *source_widget = info->widget; + GdkEvent send_event; + + gtk_widget_ref (source_widget); + + if (event->button != info->button) + return FALSE; + + gdk_pointer_ungrab (event->time); + + if ((info->context->action != 0) && (info->context->dest_window != NULL)) + { + gtk_drag_drop (info, event->time); + } + else + { + gdk_drag_abort (info->context, event->time); + gtk_drag_drop_finished (info, FALSE, event->time); + } + + gtk_grab_remove (widget); + + send_event.button.type = GDK_BUTTON_RELEASE; + send_event.button.window = source_widget->window; + send_event.button.x = 0; + send_event.button.y = 0; + send_event.button.state = event->state; + send_event.button.button = event->button; + + send_event.button.time = event->time; + + /* Send on the button release to the original widget to + * convince it to release its grab + */ + gtk_widget_event (source_widget, &send_event); + gtk_widget_unref (source_widget); + + return TRUE; +} + +static gint +gtk_drag_abort_timeout (gpointer data) +{ + GtkDragSourceInfo *info = data; + guint32 time = GDK_CURRENT_TIME; + + if (info->proxy_dest) + time = info->proxy_dest->proxy_drop_time; + + info->drop_timeout = 0; + gtk_drag_drop_finished (info, FALSE, time); + + return FALSE; +} diff --git a/gtk/gtkdnd.h b/gtk/gtkdnd.h new file mode 100644 index 0000000000..8f313e70ee --- /dev/null +++ b/gtk/gtkdnd.h @@ -0,0 +1,129 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __GTK_DND_H__ +#define __GTK_DND_H__ + +#include <gdk/gdk.h> +#include <gtk/gtkenums.h> +#include <gtk/gtkwidget.h> +#include <gtk/gtkselection.h> + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +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" */ + GTK_DEST_DEFAULT_ALL = 0x07 +} GtkDestDefaults; + +/* Destination side */ + +void gtk_drag_get_data (GtkWidget *widget, + GdkDragContext *context, + GdkAtom target, + guint32 time); +void gtk_drag_finish (GdkDragContext *context, + gboolean success, + gboolean delete, + guint32 time); + +void gtk_drag_highlight (GtkWidget *widget); +void gtk_drag_unhighlight (GtkWidget *widget); + +void gtk_drag_dest_set (GtkWidget *widget, + GtkDestDefaults flags, + GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions); + +void gtk_drag_dest_set_proxy (GtkWidget *widget, + GdkWindow *proxy_window, + GdkDragProtocol protocol, + gboolean use_coordinates); + +/* There probably should be functions for setting the targets + * as a GtkTargetList + */ + +void gtk_drag_dest_unset (GtkWidget *widget); + +/* Source side */ + +void gtk_drag_source_set (GtkWidget *widget, + GdkModifierType start_button_mask, + GtkTargetEntry *targets, + gint n_targets, + GdkDragAction actions); + +void gtk_drag_source_set_icon (GtkWidget *widget, + GdkColormap *colormap, + GdkPixmap *pixmap, + GdkBitmap *mask); + +/* There probably should be functions for setting the targets + * as a GtkTargetList + */ + +GdkDragContext *gtk_drag_begin (GtkWidget *widget, + GtkTargetList *targets, + GdkDragAction actions, + gint button, + GdkEvent *event); + +/* Set the image being dragged around + */ +void gtk_drag_set_icon_widget (GdkDragContext *context, + GtkWidget *widget, + gint hot_x, + gint hot_y); + +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); + +void gtk_drag_set_default_icon (GdkColormap *colormap, + GdkPixmap *pixmap, + GdkBitmap *mask, + gint hot_x, + gint hot_y); + + +/* Internal functions */ +void gtk_drag_source_handle_event (GtkWidget *widget, + GdkEvent *event); +void gtk_drag_dest_handle_event (GtkWidget *toplevel, + GdkEvent *event); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __GTK_DND_H__ */ + + + diff --git a/gtk/gtkeditable.c b/gtk/gtkeditable.c index 09d28a9ab3..e720395676 100644 --- a/gtk/gtkeditable.c +++ b/gtk/gtkeditable.c @@ -58,7 +58,14 @@ enum { ARG_TEXT_POSITION, ARG_EDITABLE }; - + +/* values for selection info */ + +enum { + TARGET_STRING, + TARGET_TEXT, + TARGET_COMPOUND_TEXT, +}; static void gtk_editable_class_init (GtkEditableClass *klass); static void gtk_editable_init (GtkEditable *editable); @@ -71,11 +78,13 @@ static void gtk_editable_get_arg (GtkObject *object, static void gtk_editable_finalize (GtkObject *object); static gint gtk_editable_selection_clear (GtkWidget *widget, GdkEventSelection *event); -static void gtk_editable_selection_handler (GtkWidget *widget, - GtkSelectionData *selection_data, - gpointer data); +static void gtk_editable_selection_get (GtkWidget *widget, + GtkSelectionData *selection_data, + guint info, + guint time); static void gtk_editable_selection_received (GtkWidget *widget, - GtkSelectionData *selection_data); + GtkSelectionData *selection_data, + guint time); static void gtk_editable_set_selection (GtkEditable *editable, gint start, @@ -90,8 +99,7 @@ static void gtk_editable_real_set_editable (GtkEditable *editable, static GtkWidgetClass *parent_class = NULL; static guint editable_signals[LAST_SIGNAL] = { 0 }; -static GdkAtom ctext_atom = GDK_NONE; -static GdkAtom text_atom = GDK_NONE; + static GdkAtom clipboard_atom = GDK_NONE; GtkType @@ -287,6 +295,7 @@ gtk_editable_class_init (GtkEditableClass *class) widget_class->selection_clear_event = gtk_editable_selection_clear; widget_class->selection_received = gtk_editable_selection_received; + widget_class->selection_get = gtk_editable_selection_get; class->insert_text = NULL; class->delete_text = NULL; @@ -362,6 +371,13 @@ gtk_editable_get_arg (GtkObject *object, static void gtk_editable_init (GtkEditable *editable) { + static GtkTargetEntry targets[] = { + { "STRING", TARGET_STRING }, + { "TEXT", TARGET_TEXT }, + { "COMPOUND_TEXT", TARGET_COMPOUND_TEXT } + }; + static gint n_targets = sizeof(targets) / sizeof(targets[0]); + GTK_WIDGET_SET_FLAGS (editable, GTK_CAN_FOCUS); editable->selection_start_pos = 0; @@ -377,36 +393,10 @@ gtk_editable_init (GtkEditable *editable) if (!clipboard_atom) clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE); - gtk_selection_add_handler (GTK_WIDGET(editable), GDK_SELECTION_PRIMARY, - GDK_TARGET_STRING, gtk_editable_selection_handler, - NULL); - gtk_selection_add_handler (GTK_WIDGET(editable), clipboard_atom, - GDK_TARGET_STRING, gtk_editable_selection_handler, - NULL); - - if (!text_atom) - text_atom = gdk_atom_intern ("TEXT", FALSE); - - gtk_selection_add_handler (GTK_WIDGET(editable), GDK_SELECTION_PRIMARY, - text_atom, - gtk_editable_selection_handler, - NULL); - gtk_selection_add_handler (GTK_WIDGET(editable), clipboard_atom, - text_atom, - gtk_editable_selection_handler, - NULL); - - if (!ctext_atom) - ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE); - - gtk_selection_add_handler (GTK_WIDGET(editable), GDK_SELECTION_PRIMARY, - ctext_atom, - gtk_editable_selection_handler, - NULL); - gtk_selection_add_handler (GTK_WIDGET(editable), clipboard_atom, - ctext_atom, - gtk_editable_selection_handler, - NULL); + gtk_selection_add_targets (GTK_WIDGET (editable), GDK_SELECTION_PRIMARY, + targets, n_targets); + gtk_selection_add_targets (GTK_WIDGET (editable), clipboard_atom, + targets, n_targets); } static void @@ -580,9 +570,10 @@ gtk_editable_selection_clear (GtkWidget *widget, } static void -gtk_editable_selection_handler (GtkWidget *widget, - GtkSelectionData *selection_data, - gpointer data) +gtk_editable_selection_get (GtkWidget *widget, + GtkSelectionData *selection_data, + guint info, + guint time) { GtkEditable *editable; gint selection_start_pos; @@ -614,14 +605,13 @@ gtk_editable_selection_handler (GtkWidget *widget, length = strlen (editable->clipboard_text); } - if (selection_data->target == GDK_SELECTION_TYPE_STRING) + if (info == TARGET_STRING) { gtk_selection_data_set (selection_data, GDK_SELECTION_TYPE_STRING, 8*sizeof(gchar), (guchar *)str, length); } - else if (selection_data->target == text_atom || - selection_data->target == ctext_atom) + else if ((info == TARGET_TEXT) || (info == TARGET_COMPOUND_TEXT)) { guchar *text; gchar c; @@ -643,7 +633,8 @@ gtk_editable_selection_handler (GtkWidget *widget, static void gtk_editable_selection_received (GtkWidget *widget, - GtkSelectionData *selection_data) + GtkSelectionData *selection_data, + guint time) { GtkEditable *editable; gint reselect; @@ -658,7 +649,8 @@ gtk_editable_selection_received (GtkWidget *widget, if (selection_data->type == GDK_TARGET_STRING) type = STRING; - else if (selection_data->type == ctext_atom) + else if ((selection_data->type == gdk_atom_intern ("COMPOUND_TEXT", FALSE)) || + (selection_data->type == gdk_atom_intern ("TEXT", FALSE))) type = CTEXT; else type = INVALID; @@ -668,7 +660,7 @@ gtk_editable_selection_received (GtkWidget *widget, /* avoid infinite loop */ if (selection_data->target != GDK_TARGET_STRING) gtk_selection_convert (widget, selection_data->selection, - GDK_TARGET_STRING, GDK_CURRENT_TIME); + GDK_TARGET_STRING, time); return; } @@ -934,7 +926,8 @@ gtk_editable_real_paste_clipboard (GtkEditable *editable) time = gtk_editable_get_event_time (editable); if (editable->editable) gtk_selection_convert (GTK_WIDGET(editable), - clipboard_atom, ctext_atom, time); + clipboard_atom, + gdk_atom_intern ("COMPOUND_TEXT", FALSE), time); } void diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 7d7c7915b2..a7cc42426b 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -22,6 +22,7 @@ #include <string.h> #include <gmodule.h> #include "gtkbutton.h" +#include "gtkdnd.h" #include "gtkfeatures.h" #include "gtkhscrollbar.h" #include "gtkhseparator.h" @@ -224,7 +225,8 @@ guint gtk_debug_flags = 0; /* Global GTK debug flag */ static GDebugKey gtk_debug_keys[] = { {"objects", GTK_DEBUG_OBJECTS}, {"misc", GTK_DEBUG_MISC}, - {"signals", GTK_DEBUG_SIGNALS} + {"signals", GTK_DEBUG_SIGNALS}, + {"dnd", GTK_DEBUG_DND} }; static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey); @@ -764,6 +766,9 @@ gtk_main_iteration_do (gboolean blocking) * 1) these events have no meaning for the grabbing widget * and 2) redirecting these events to the grabbing widget * could cause the display to be messed up. + * + * Drag events are also not redirected, since it isn't + * clear what the semantics of that would be. */ switch (event->type) { @@ -797,11 +802,6 @@ gtk_main_iteration_do (gboolean blocking) case GDK_SELECTION_REQUEST: case GDK_SELECTION_NOTIFY: case GDK_CLIENT_EVENT: - case GDK_DRAG_BEGIN: - case GDK_DRAG_REQUEST: - case GDK_DROP_ENTER: - case GDK_DROP_LEAVE: - case GDK_DROP_DATA_AVAIL: case GDK_VISIBILITY_NOTIFY: gtk_widget_event (event_widget, event); break; @@ -821,7 +821,6 @@ gtk_main_iteration_do (gboolean blocking) case GDK_BUTTON_RELEASE: case GDK_PROXIMITY_IN: case GDK_PROXIMITY_OUT: - case GDK_OTHER_EVENT: gtk_propagate_event (grab_widget, event); break; @@ -843,6 +842,17 @@ gtk_main_iteration_do (gboolean blocking) else if (GTK_WIDGET_IS_SENSITIVE (grab_widget)) gtk_widget_event (grab_widget, event); break; + + case GDK_DRAG_STATUS: + case GDK_DROP_FINISHED: + gtk_drag_source_handle_event (event_widget, event); + break; + case GDK_DRAG_ENTER: + case GDK_DRAG_LEAVE: + case GDK_DRAG_MOTION: + case GDK_DROP_START: + gtk_drag_dest_handle_event (event_widget, event); + break; } tmp_list = current_events; diff --git a/gtk/gtkmarshal.list b/gtk/gtkmarshal.list index 0eaac95c3f..f4ddc3b8f6 100644 --- a/gtk/gtkmarshal.list +++ b/gtk/gtkmarshal.list @@ -1,6 +1,8 @@ BOOL:NONE BOOL:POINTER BOOL:POINTER,POINTER,INT,INT +BOOL:POINTER,INT,INT +BOOL:POINTER,INT,INT,UINT BOOL:POINTER,STRING,STRING,POINTER ENUM:ENUM INT:POINTER @@ -24,6 +26,8 @@ NONE:POINTER,POINTER,POINTER NONE:POINTER,STRING,STRING NONE:POINTER,UINT NONE:POINTER,UINT,ENUM +NONE:POINTER,POINTER,UINT,UINT +NONE:POINTER,UINT,UINT NONE:POINTER,UINT,UINT NONE:STRING NONE:STRING,INT,POINTER diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list index 0eaac95c3f..f4ddc3b8f6 100644 --- a/gtk/gtkmarshalers.list +++ b/gtk/gtkmarshalers.list @@ -1,6 +1,8 @@ BOOL:NONE BOOL:POINTER BOOL:POINTER,POINTER,INT,INT +BOOL:POINTER,INT,INT +BOOL:POINTER,INT,INT,UINT BOOL:POINTER,STRING,STRING,POINTER ENUM:ENUM INT:POINTER @@ -24,6 +26,8 @@ NONE:POINTER,POINTER,POINTER NONE:POINTER,STRING,STRING NONE:POINTER,UINT NONE:POINTER,UINT,ENUM +NONE:POINTER,POINTER,UINT,UINT +NONE:POINTER,UINT,UINT NONE:POINTER,UINT,UINT NONE:STRING NONE:STRING,INT,POINTER diff --git a/gtk/gtkmenu.h b/gtk/gtkmenu.h index a3d1db74f1..5b046403ec 100644 --- a/gtk/gtkmenu.h +++ b/gtk/gtkmenu.h @@ -58,6 +58,10 @@ struct _GtkMenu GtkMenuPositionFunc position_func; gpointer position_func_data; + /* Do _not_ touch these widgets directly. We hide the reference + * count from the toplevel to the menu, so it must be restored + * before operating on these widgets + */ GtkWidget *toplevel; GtkWidget *tearoff_window; diff --git a/gtk/gtkselection.c b/gtk/gtkselection.c index 506168f90d..f0094b0218 100644 --- a/gtk/gtkselection.c +++ b/gtk/gtkselection.c @@ -70,7 +70,6 @@ typedef struct _GtkSelectionInfo GtkSelectionInfo; typedef struct _GtkIncrConversion GtkIncrConversion; typedef struct _GtkIncrInfo GtkIncrInfo; typedef struct _GtkRetrievalInfo GtkRetrievalInfo; -typedef struct _GtkSelectionHandler GtkSelectionHandler; struct _GtkSelectionInfo { @@ -117,16 +116,7 @@ struct _GtkRetrievalInfo guchar *buffer; /* Buffer in which to accumulate results */ gint offset; /* Current offset in buffer, -1 indicates not yet started */ -}; - -struct _GtkSelectionHandler -{ - GdkAtom selection; /* selection thats handled */ - GdkAtom target; /* target thats handled */ - GtkSelectionFunction function; /* callback function */ - GtkCallbackMarshal marshal; /* Marshalling function */ - gpointer data; /* callback data */ - GtkDestroyNotify destroy; /* called when callback is removed */ + guint32 notify_time; /* Timestamp from SelectionNotify */ }; /* Local Functions */ @@ -135,9 +125,11 @@ static gint gtk_selection_incr_timeout (GtkIncrInfo *info); static gint gtk_selection_retrieval_timeout (GtkRetrievalInfo *info); static void gtk_selection_retrieval_report (GtkRetrievalInfo *info, GdkAtom type, gint format, - guchar *buffer, gint length); + guchar *buffer, gint length, + guint32 time); static void gtk_selection_invoke_handler (GtkWidget *widget, - GtkSelectionData *data); + GtkSelectionData *data, + guint time); static void gtk_selection_default_handler (GtkWidget *widget, GtkSelectionData *data); @@ -150,6 +142,136 @@ static GList *current_selections = NULL; static GdkAtom gtk_selection_atoms[LAST_ATOM]; static const char *gtk_selection_handler_key = "gtk-selection-handlers"; +/**************** + * Target Lists * + ****************/ + +/* + * Target lists + */ + +GtkTargetList * +gtk_target_list_new (GtkTargetEntry *targets, + guint ntargets) +{ + GtkTargetList *result = g_new (GtkTargetList, 1); + result->list = NULL; + result->ref_count = 1; + + if (targets) + gtk_target_list_add_table (result, targets, ntargets); + + return result; +} + +void +gtk_target_list_ref (GtkTargetList *list) +{ + list->ref_count++; +} + +void +gtk_target_list_unref (GtkTargetList *list) +{ + list->ref_count--; + if (list->ref_count == 0) + { + GList *tmp_list = list->list; + while (tmp_list) + { + GtkTargetEntry *entry = tmp_list->data; + g_free (entry); + + tmp_list = tmp_list->next; + } + } +} + +void +gtk_target_list_add (GtkTargetList *list, + GdkAtom target, + guint flags, + guint info) +{ + GtkTargetPair *pair; + + g_return_if_fail (list != NULL); + + pair = g_new (GtkTargetPair, 1); + pair->target = target; + pair->flags = flags; + pair->info = info; + + list->list = g_list_append (list->list, pair); +} + +void +gtk_target_list_add_table (GtkTargetList *list, + GtkTargetEntry *targets, + guint ntargets) +{ + gint i; + + for (i=ntargets-1; i >= 0; i--) + { + GtkTargetPair *pair = g_new (GtkTargetPair, 1); + pair->target = gdk_atom_intern (targets[i].target, FALSE); + pair->flags = targets[i].flags; + pair->info = targets[i].info; + + list->list = g_list_prepend (list->list, pair); + } +} + +void +gtk_target_list_remove (GtkTargetList *list, + GdkAtom target) +{ + GList *tmp_list; + + g_return_if_fail (list != NULL); + + tmp_list = list->list; + while (tmp_list) + { + GtkTargetPair *pair = tmp_list->data; + + if (pair->target == target) + { + g_free (pair); + + list->list = g_list_remove (list->list, tmp_list); + g_list_free_1 (tmp_list); + + return; + } + + tmp_list = tmp_list->next; + } +} + +gboolean +gtk_target_list_find (GtkTargetList *list, + GdkAtom target, + guint *info) +{ + GList *tmp_list = list->list; + while (tmp_list) + { + GtkTargetPair *pair = tmp_list->data; + + if (pair->target == target) + { + *info = pair->info; + return TRUE; + } + tmp_list = tmp_list->next; + } + + return FALSE; +} + + /************************************************************* * gtk_selection_owner_set: * Claim ownership of a selection. @@ -252,95 +374,105 @@ gtk_selection_owner_set (GtkWidget *widget, } /************************************************************* - * gtk_selection_add_handler_full: - * Add a handler for a specified selection/target pair + * gtk_selection_add_target + * Add specified target to list of supported targets * * arguments: - * widget: The widget the handler applies to + * widget: The widget for which this target applies * selection: * target: - * format: Format in which this handler will return data - * function: Callback function (can be NULL) - * marshal: Callback marshal function - * data: User data for callback - * destroy: Called when handler removed + * info: guint to pass to to the selection_get signal * * results: *************************************************************/ -void -gtk_selection_add_handler (GtkWidget *widget, - GdkAtom selection, - GdkAtom target, - GtkSelectionFunction function, - gpointer data) -{ - gtk_selection_add_handler_full (widget, selection, target, function, - NULL, data, NULL); -} +typedef struct _GtkSelectionTargetList GtkSelectionTargetList; -void -gtk_selection_add_handler_full (GtkWidget *widget, - GdkAtom selection, - GdkAtom target, - GtkSelectionFunction function, - GtkCallbackMarshal marshal, - gpointer data, - GtkDestroyNotify destroy) +struct _GtkSelectionTargetList { + GdkAtom selection; + GtkTargetList *list; +}; + +static GtkTargetList * +gtk_selection_target_list_get (GtkWidget *widget, + GdkAtom selection) { - GList *selection_handlers; + GtkSelectionTargetList *sellist; GList *tmp_list; - GtkSelectionHandler *handler; - - g_return_if_fail (widget != NULL); - if (initialize) - gtk_selection_init (); - - selection_handlers = gtk_object_get_data (GTK_OBJECT (widget), - gtk_selection_handler_key); + GList *lists; + + lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key); - /* Reuse old handler structure, if present */ - tmp_list = selection_handlers; + tmp_list = lists; while (tmp_list) { - handler = (GtkSelectionHandler *)tmp_list->data; - if ((handler->selection == selection) && (handler->target == target)) - { - if (handler->destroy) - (*handler->destroy)(handler->data); - if (function) - { - handler->function = function; - handler->marshal = marshal; - handler->data = data; - handler->destroy = destroy; - } - else - { - selection_handlers = g_list_remove_link (selection_handlers, - tmp_list); - g_list_free (tmp_list); - g_free (handler); - } - return; - } + sellist = tmp_list->data; + if (sellist->selection == selection) + return sellist->list; tmp_list = tmp_list->next; } + + sellist = g_new (GtkSelectionTargetList, 1); + sellist->selection = selection; + sellist->list = gtk_target_list_new (NULL, 0); + + lists = g_list_prepend (lists, sellist); + gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists); + + return sellist->list; +} + +static void +gtk_selection_target_list_remove (GtkWidget *widget) +{ + GtkSelectionTargetList *sellist; + GList *tmp_list; + GList *lists; + + lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key); - if (tmp_list == NULL && function) + tmp_list = lists; + while (tmp_list) { - handler = g_new (GtkSelectionHandler, 1); - handler->selection = selection; - handler->target = target; - handler->function = function; - handler->marshal = marshal; - handler->data = data; - handler->destroy = destroy; - selection_handlers = g_list_append (selection_handlers, handler); + sellist = tmp_list->data; + + gtk_target_list_unref (sellist->list); + + g_free (sellist); + tmp_list = tmp_list->next; } + + g_list_free (lists); + gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL); +} + +void +gtk_selection_add_target (GtkWidget *widget, + GdkAtom selection, + GdkAtom target, + guint info) +{ + GtkTargetList *list; + + g_return_if_fail (widget != NULL); + + list = gtk_selection_target_list_get (widget, selection); + gtk_target_list_add (list, target, 0, info); +} + +void +gtk_selection_add_targets (GtkWidget *widget, + GdkAtom selection, + GtkTargetEntry *targets, + guint ntargets) +{ + GtkTargetList *list; + + g_return_if_fail (widget != NULL); + g_return_if_fail (targets != NULL); - gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, - selection_handlers); + list = gtk_selection_target_list_get (widget, selection); + gtk_target_list_add_table (list, targets, ntargets); } /************************************************************* @@ -360,8 +492,6 @@ gtk_selection_remove_all (GtkWidget *widget) GList *tmp_list; GList *next; GtkSelectionInfo *selection_info; - GList *selection_handlers; - GtkSelectionHandler *handler; /* Remove pending requests/incrs for this widget */ @@ -413,28 +543,9 @@ gtk_selection_remove_all (GtkWidget *widget) tmp_list = next; } - - /* Now remove all handlers */ - - selection_handlers = gtk_object_get_data (GTK_OBJECT (widget), - gtk_selection_handler_key); - gtk_object_remove_data (GTK_OBJECT (widget), gtk_selection_handler_key); - - tmp_list = selection_handlers; - while (tmp_list) - { - next = tmp_list->next; - handler = (GtkSelectionHandler *)tmp_list->data; - - if (handler->destroy) - (*handler->destroy)(handler->data); - - g_free (handler); - - tmp_list = next; - } - - g_list_free (selection_handlers); + + /* Remove all selection lists */ + gtk_selection_target_list_remove (widget); } /************************************************************* @@ -516,13 +627,15 @@ gtk_selection_convert (GtkWidget *widget, if (owner_widget != NULL) { gtk_selection_invoke_handler (owner_widget, - &selection_data); + &selection_data, + time); gtk_selection_retrieval_report (info, selection_data.type, selection_data.format, selection_data.data, - selection_data.length); + selection_data.length, + time); g_free (selection_data.data); @@ -666,6 +779,9 @@ gtk_selection_request (GtkWidget *widget, guchar *mult_atoms; int i; + if (initialize) + gtk_selection_init (); + /* Check if we own selection */ tmp_list = current_selections; @@ -704,10 +820,9 @@ gtk_selection_request (GtkWidget *widget, gint length; mult_atoms = NULL; - if (!gdk_property_get (info->requestor, event->property, GDK_SELECTION_TYPE_ATOM, + if (!gdk_property_get (info->requestor, event->property, 0, /* AnyPropertyType */ 0, GTK_SELECTION_MAX_SIZE, FALSE, - &type, &format, &length, &mult_atoms) || - type != GDK_SELECTION_TYPE_ATOM || format != 8*sizeof(GdkAtom)) + &type, &format, &length, &mult_atoms)) { gdk_selection_send_notify (event->requestor, event->selection, event->target, GDK_NONE, event->time); @@ -753,7 +868,7 @@ gtk_selection_request (GtkWidget *widget, event->requestor, event->property); #endif - gtk_selection_invoke_handler (widget, &data); + gtk_selection_invoke_handler (widget, &data, event->time); if (data.length < 0) { @@ -822,7 +937,7 @@ gtk_selection_request (GtkWidget *widget, gdk_property_change (info->requestor, event->property, GDK_SELECTION_TYPE_ATOM, 8*sizeof(GdkAtom), GDK_PROP_MODE_REPLACE, - mult_atoms, info->num_conversions); + mult_atoms, 2*info->num_conversions); g_free (mult_atoms); } @@ -1051,7 +1166,7 @@ gtk_selection_notify (GtkWidget *widget, g_list_free (tmp_list); /* structure will be freed in timeout */ gtk_selection_retrieval_report (info, - GDK_NONE, 0, NULL, -1); + GDK_NONE, 0, NULL, -1, event->time); return TRUE; } @@ -1063,7 +1178,8 @@ gtk_selection_notify (GtkWidget *widget, { /* The remainder of the selection will come through PropertyNotify events */ - + + info->notify_time = event->time; info->idle_time = 0; info->offset = 0; /* Mark as OK to proceed */ gdk_window_set_events (widget->window, @@ -1079,7 +1195,7 @@ gtk_selection_notify (GtkWidget *widget, info->offset = length; gtk_selection_retrieval_report (info, type, format, - buffer, length); + buffer, length, event->time); } gdk_property_delete (widget->window, event->property); @@ -1157,7 +1273,8 @@ gtk_selection_property_notify (GtkWidget *widget, gtk_selection_retrieval_report (info, type, format, (type == GDK_NONE) ? NULL : info->buffer, - (type == GDK_NONE) ? -1 : info->offset); + (type == GDK_NONE) ? -1 : info->offset, + info->notify_time); } else /* append on newly arrived data */ { @@ -1219,7 +1336,7 @@ gtk_selection_retrieval_timeout (GtkRetrievalInfo *info) { current_retrievals = g_list_remove_link (current_retrievals, tmp_list); g_list_free (tmp_list); - gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1); + gtk_selection_retrieval_report (info, GDK_NONE, 0, NULL, -1, GDK_CURRENT_TIME); } g_free (info->buffer); @@ -1242,13 +1359,15 @@ gtk_selection_retrieval_timeout (GtkRetrievalInfo *info) * arguments: * info: information about the retrieval that completed * buffer: buffer containing data (NULL => errror) + * time: timestamp for data in buffer * results: *************************************************************/ static void gtk_selection_retrieval_report (GtkRetrievalInfo *info, GdkAtom type, gint format, - guchar *buffer, gint length) + guchar *buffer, gint length, + guint32 time) { GtkSelectionData data; @@ -1261,7 +1380,8 @@ gtk_selection_retrieval_report (GtkRetrievalInfo *info, data.data = buffer; gtk_signal_emit_by_name (GTK_OBJECT(info->widget), - "selection_received", &data); + "selection_received", + &data, time); } /************************************************************* @@ -1273,6 +1393,7 @@ gtk_selection_retrieval_report (GtkRetrievalInfo *info, * arguments: * widget: selection owner * data: selection data [INOUT] + * time: time from requeset * * results: * Number of bytes written to buffer, -1 if error @@ -1280,44 +1401,26 @@ gtk_selection_retrieval_report (GtkRetrievalInfo *info, static void gtk_selection_invoke_handler (GtkWidget *widget, - GtkSelectionData *data) + GtkSelectionData *data, + guint time) { - GList *tmp_list; - GtkSelectionHandler *handler; + GtkTargetList *target_list; + guint info; + g_return_if_fail (widget != NULL); - - tmp_list = gtk_object_get_data (GTK_OBJECT (widget), - gtk_selection_handler_key); - - while (tmp_list) + + target_list = gtk_selection_target_list_get (widget, data->selection); + if (target_list && + gtk_target_list_find (target_list, data->target, &info)) { - handler = (GtkSelectionHandler *)tmp_list->data; - if ((handler->selection == data->selection) && - (handler->target == data->target)) - break; - tmp_list = tmp_list->next; + gtk_signal_emit_by_name (GTK_OBJECT (widget), + "selection_get", + data, + info, time); } - - if (tmp_list == NULL) - gtk_selection_default_handler (widget, data); else - { - if (handler->marshal) - { - GtkArg args[2]; - args[0].type = GTK_TYPE_BOXED; - args[0].name = NULL; - GTK_VALUE_BOXED(args[0]) = data; - args[1].type = GTK_TYPE_NONE; - args[1].name = NULL; - - handler->marshal (GTK_OBJECT(widget), handler->data, 1, args); - } - else - if (handler->function) - handler->function (widget, data, handler->data); - } + gtk_selection_default_handler (widget, data); } /************************************************************* @@ -1369,18 +1472,16 @@ gtk_selection_default_handler (GtkWidget *widget, GdkAtom *p; gint count; GList *tmp_list; - GtkSelectionHandler *handler; + GtkTargetList *target_list; + GtkTargetPair *pair; count = 3; - tmp_list = gtk_object_get_data (GTK_OBJECT(widget), - gtk_selection_handler_key); + target_list = gtk_selection_target_list_get (widget, + data->selection); + tmp_list = target_list->list; while (tmp_list) { - handler = (GtkSelectionHandler *)tmp_list->data; - - if (handler->selection == data->selection) - count++; - + count++; tmp_list = tmp_list->next; } @@ -1395,14 +1496,11 @@ gtk_selection_default_handler (GtkWidget *widget, *p++ = gtk_selection_atoms[TARGETS]; *p++ = gtk_selection_atoms[MULTIPLE]; - tmp_list = gtk_object_get_data (GTK_OBJECT(widget), - gtk_selection_handler_key); + tmp_list = target_list->list; while (tmp_list) { - handler = (GtkSelectionHandler *)tmp_list->data; - - if (handler->selection == data->selection) - *p++ = handler->target; + pair = (GtkTargetPair *)tmp_list->data; + *p++ = pair->target; tmp_list = tmp_list->next; } diff --git a/gtk/gtkselection.h b/gtk/gtkselection.h index 21f9ed2664..75fcd5aeb6 100644 --- a/gtk/gtkselection.h +++ b/gtk/gtkselection.h @@ -29,36 +29,63 @@ extern "C" { #endif /* __cplusplus */ typedef struct _GtkSelectionData GtkSelectioData; +typedef struct _GtkTargetList GtkTargetList; +typedef struct _GtkTargetEntry GtkTargetEntry; -/* a callback function that provides the selection. Arguments are: - widget: selection owner - offset: offset into selection - buffer: buffer into which to store selection - length: length of buffer - bytes_after: (sizeof(selection) - offset - length ) (return) - data: callback data */ +struct _GtkTargetEntry { + gchar *target; + guint flags; + guint info; +}; -typedef void (*GtkSelectionFunction) (GtkWidget *widget, - GtkSelectionData *selection_data, - gpointer data); +/* These structures not public, and are here only for the convenience of + * gtkdnd.c + */ + +typedef struct _GtkTargetPair GtkTargetPair; + +/* This structure is a list of destinations, and associated guint id's */ +struct _GtkTargetList { + GList *list; + guint ref_count; +}; + +struct _GtkTargetPair { + GdkAtom target; + guint flags; + guint info; +}; + +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 flags, + guint info); +void gtk_target_list_add_table (GtkTargetList *list, + GtkTargetEntry *targets, + guint ntargets); +void gtk_target_list_remove (GtkTargetList *list, + GdkAtom target); +gboolean gtk_target_list_find (GtkTargetList *list, + GdkAtom target, + guint *info); /* Public interface */ gint gtk_selection_owner_set (GtkWidget *widget, GdkAtom selection, guint32 time); -void gtk_selection_add_handler (GtkWidget *widget, +void gtk_selection_add_target (GtkWidget *widget, + GdkAtom selection, + GdkAtom target, + guint info); +void gtk_selection_add_targets (GtkWidget *widget, GdkAtom selection, - GdkAtom target, - GtkSelectionFunction function, - gpointer data); -void gtk_selection_add_handler_full (GtkWidget *widget, - GdkAtom selection, - GdkAtom target, - GtkSelectionFunction function, - GtkCallbackMarshal marshal, - gpointer data, - GtkDestroyNotify destroy); + GtkTargetEntry *targets, + guint ntargets); gint gtk_selection_convert (GtkWidget *widget, GdkAtom selection, GdkAtom target, diff --git a/gtk/gtksignal.c b/gtk/gtksignal.c index a35840f656..f9b3bec44c 100644 --- a/gtk/gtksignal.c +++ b/gtk/gtksignal.c @@ -290,7 +290,7 @@ gtk_signal_newv (const gchar *r_name, if (return_val != GTK_TYPE_NONE && (signal_flags & GTK_RUN_BOTH) == GTK_RUN_FIRST) { - g_warning ("gtk_signal_newv(): signal \"%s\" with return value `%s' excludes GTK_RUN_LAST", + g_warning ("gtk_signal_newv(): signal \"%s\" - return value `%s' incompatible with GTK_RUN_FIRST", name, gtk_type_name (return_val)); g_free (name); return 0; diff --git a/gtk/gtkstyle.h b/gtk/gtkstyle.h index 5d1a5fb12b..0def4b5c52 100644 --- a/gtk/gtkstyle.h +++ b/gtk/gtkstyle.h @@ -28,7 +28,6 @@ extern "C" { #endif /* __cplusplus */ - typedef struct _GtkStyle GtkStyle; typedef struct _GtkStyleClass GtkStyleClass; diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 6871d48a4f..218428abe9 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -72,16 +72,18 @@ enum { SELECTION_CLEAR_EVENT, SELECTION_REQUEST_EVENT, SELECTION_NOTIFY_EVENT, + SELECTION_GET, SELECTION_RECEIVED, PROXIMITY_IN_EVENT, PROXIMITY_OUT_EVENT, - DRAG_BEGIN_EVENT, - DRAG_REQUEST_EVENT, - DRAG_END_EVENT, - DROP_ENTER_EVENT, - DROP_LEAVE_EVENT, - DROP_DATA_AVAILABLE_EVENT, - OTHER_EVENT, + DRAG_BEGIN, + DRAG_END, + DRAG_DATA_DELETE, + DRAG_LEAVE, + DRAG_MOTION, + DRAG_DROP, + DRAG_DATA_GET, + DRAG_DATA_RECEIVED, CLIENT_EVENT, NO_EXPOSE_EVENT, VISIBILITY_NOTIFY_EVENT, @@ -548,9 +550,20 @@ gtk_widget_class_init (GtkWidgetClass *klass) GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (GtkWidgetClass, selection_received), - gtk_marshal_NONE__ENUM, - GTK_TYPE_NONE, 1, - GTK_TYPE_SELECTION_DATA); + gtk_marshal_NONE__POINTER_UINT, + GTK_TYPE_NONE, 2, + GTK_TYPE_SELECTION_DATA, + GTK_TYPE_UINT); + widget_signals[SELECTION_GET] = + gtk_signal_new ("selection_get", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkWidgetClass, selection_get), + gtk_marshal_NONE__POINTER_UINT_UINT, + GTK_TYPE_NONE, 2, + GTK_TYPE_SELECTION_DATA, + GTK_TYPE_UINT, + GTK_TYPE_UINT); widget_signals[PROXIMITY_IN_EVENT] = gtk_signal_new ("proximity_in_event", GTK_RUN_LAST, @@ -567,55 +580,83 @@ gtk_widget_class_init (GtkWidgetClass *klass) gtk_marshal_BOOL__POINTER, GTK_TYPE_BOOL, 1, GTK_TYPE_GDK_EVENT); - widget_signals[DRAG_BEGIN_EVENT] = - gtk_signal_new ("drag_begin_event", + widget_signals[DRAG_LEAVE] = + gtk_signal_new ("drag_leave", GTK_RUN_LAST, object_class->type, - GTK_SIGNAL_OFFSET (GtkWidgetClass, drag_begin_event), - gtk_marshal_BOOL__POINTER, - GTK_TYPE_BOOL, 1, - GTK_TYPE_GDK_EVENT); - widget_signals[DRAG_REQUEST_EVENT] = - gtk_signal_new ("drag_request_event", + GTK_SIGNAL_OFFSET (GtkWidgetClass, drag_leave), + gtk_marshal_NONE__POINTER_UINT, + GTK_TYPE_NONE, 2, + GTK_TYPE_GDK_DRAG_CONTEXT, + GTK_TYPE_UINT); + widget_signals[DRAG_BEGIN] = + gtk_signal_new ("drag_begin", GTK_RUN_LAST, object_class->type, - GTK_SIGNAL_OFFSET (GtkWidgetClass, drag_request_event), - gtk_marshal_BOOL__POINTER, - GTK_TYPE_BOOL, 1, - GTK_TYPE_GDK_EVENT); - widget_signals[DRAG_END_EVENT] = - gtk_signal_new ("drag_end_event", + GTK_SIGNAL_OFFSET (GtkWidgetClass, drag_begin), + gtk_marshal_NONE__POINTER, + GTK_TYPE_NONE, 1, + GTK_TYPE_GDK_DRAG_CONTEXT); + widget_signals[DRAG_END] = + gtk_signal_new ("drag_end", GTK_RUN_LAST, object_class->type, - GTK_SIGNAL_OFFSET (GtkWidgetClass, drag_end_event), - gtk_marshal_BOOL__POINTER, - GTK_TYPE_BOOL, 1, - GTK_TYPE_GDK_EVENT); - widget_signals[DROP_ENTER_EVENT] = - gtk_signal_new ("drop_enter_event", + GTK_SIGNAL_OFFSET (GtkWidgetClass, drag_end), + gtk_marshal_NONE__POINTER, + GTK_TYPE_NONE, 1, + GTK_TYPE_GDK_DRAG_CONTEXT); + widget_signals[DRAG_DATA_DELETE] = + gtk_signal_new ("drag_data_delete", GTK_RUN_LAST, object_class->type, - GTK_SIGNAL_OFFSET (GtkWidgetClass, drop_enter_event), - gtk_marshal_BOOL__POINTER, - GTK_TYPE_BOOL, 1, - GTK_TYPE_GDK_EVENT); - widget_signals[DROP_LEAVE_EVENT] = - gtk_signal_new ("drop_leave_event", + GTK_SIGNAL_OFFSET (GtkWidgetClass, drag_data_delete), + gtk_marshal_NONE__POINTER, + GTK_TYPE_NONE, 1, + GTK_TYPE_GDK_DRAG_CONTEXT); + widget_signals[DRAG_MOTION] = + gtk_signal_new ("drag_motion", GTK_RUN_LAST, object_class->type, - GTK_SIGNAL_OFFSET (GtkWidgetClass, drop_leave_event), - gtk_marshal_BOOL__POINTER, - GTK_TYPE_BOOL, 1, - GTK_TYPE_GDK_EVENT); - widget_signals[DROP_DATA_AVAILABLE_EVENT] = - gtk_signal_new ("drop_data_available_event", + GTK_SIGNAL_OFFSET (GtkWidgetClass, drag_motion), + gtk_marshal_BOOL__POINTER_INT_INT_UINT, + GTK_TYPE_BOOL, 4, + GTK_TYPE_GDK_DRAG_CONTEXT, + GTK_TYPE_INT, + GTK_TYPE_INT, + GTK_TYPE_UINT); + widget_signals[DRAG_DROP] = + gtk_signal_new ("drag_drop", GTK_RUN_LAST, object_class->type, - GTK_SIGNAL_OFFSET (GtkWidgetClass, - drop_data_available_event), - gtk_marshal_BOOL__POINTER, - GTK_TYPE_BOOL, 1, - GTK_TYPE_GDK_EVENT); + GTK_SIGNAL_OFFSET (GtkWidgetClass, drag_drop), + gtk_marshal_BOOL__POINTER_INT_INT_UINT, + GTK_TYPE_BOOL, 4, + GTK_TYPE_GDK_DRAG_CONTEXT, + GTK_TYPE_INT, + GTK_TYPE_INT, + GTK_TYPE_UINT); + widget_signals[DRAG_DATA_GET] = + gtk_signal_new ("drag_data_get", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkWidgetClass, drag_data_get), + gtk_marshal_NONE__POINTER_POINTER_UINT_UINT, + GTK_TYPE_NONE, 4, + GTK_TYPE_GDK_DRAG_CONTEXT, + GTK_TYPE_SELECTION_DATA, + GTK_TYPE_UINT, + GTK_TYPE_UINT); + widget_signals[DRAG_DATA_RECEIVED] = + gtk_signal_new ("drag_data_received", + GTK_RUN_LAST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkWidgetClass, drag_data_received), + gtk_marshal_NONE__POINTER_POINTER_UINT_UINT, + GTK_TYPE_NONE, 4, + GTK_TYPE_GDK_DRAG_CONTEXT, + GTK_TYPE_SELECTION_DATA, + GTK_TYPE_UINT, + GTK_TYPE_UINT); widget_signals[VISIBILITY_NOTIFY_EVENT] = gtk_signal_new ("visibility_notify_event", GTK_RUN_LAST, @@ -640,14 +681,6 @@ gtk_widget_class_init (GtkWidgetClass *klass) gtk_marshal_BOOL__POINTER, GTK_TYPE_BOOL, 1, GTK_TYPE_GDK_EVENT); - widget_signals[OTHER_EVENT] = - gtk_signal_new ("other_event", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET (GtkWidgetClass, other_event), - gtk_marshal_BOOL__POINTER, - GTK_TYPE_BOOL, 1, - GTK_TYPE_GDK_EVENT); widget_signals[DEBUG_MSG] = gtk_signal_new ("debug_msg", GTK_RUN_LAST | GTK_RUN_ACTION, @@ -706,13 +739,15 @@ gtk_widget_class_init (GtkWidgetClass *klass) klass->selection_received = NULL; klass->proximity_in_event = NULL; klass->proximity_out_event = NULL; - klass->drag_begin_event = NULL; - klass->drag_request_event = NULL; - klass->drop_enter_event = NULL; - klass->drop_leave_event = NULL; - klass->drop_data_available_event = NULL; + klass->drag_begin = NULL; + klass->drag_end = NULL; + klass->drag_data_delete = NULL; + klass->drag_leave = NULL; + klass->drag_motion = NULL; + klass->drag_drop = NULL; + klass->drag_data_received = NULL; + klass->no_expose_event = NULL; - klass->other_event = NULL; klass->debug_msg = gtk_widget_debug_msg; } @@ -2071,24 +2106,6 @@ gtk_widget_event (GtkWidget *widget, case GDK_PROXIMITY_OUT: signal_num = PROXIMITY_OUT_EVENT; break; - case GDK_DRAG_BEGIN: - signal_num = DRAG_BEGIN_EVENT; - break; - case GDK_DRAG_REQUEST: - signal_num = DRAG_REQUEST_EVENT; - break; - case GDK_DROP_ENTER: - signal_num = DROP_ENTER_EVENT; - break; - case GDK_DROP_LEAVE: - signal_num = DROP_LEAVE_EVENT; - break; - case GDK_DROP_DATA_AVAIL: - signal_num = DROP_DATA_AVAILABLE_EVENT; - break; - case GDK_OTHER_EVENT: - signal_num = OTHER_EVENT; - break; case GDK_NO_EXPOSE: signal_num = NO_EXPOSE_EVENT; break; @@ -3000,6 +3017,51 @@ gtk_widget_set_events (GtkWidget *widget, } /***************************************** + * gtk_widget_add_events: + * + * arguments: + * + * results: + *****************************************/ + +void +gtk_widget_add_events (GtkWidget *widget, + gint events) +{ + gint *eventp; + + g_return_if_fail (widget != NULL); + g_return_if_fail (!GTK_WIDGET_NO_WINDOW (widget)); + + eventp = gtk_object_get_data_by_id (GTK_OBJECT (widget), event_key_id); + + if (events) + { + if (!eventp) + { + eventp = g_new (gint, 1); + *eventp = 0; + } + + *eventp |= events; + if (!event_key_id) + event_key_id = g_quark_from_static_string (event_key); + gtk_object_set_data_by_id (GTK_OBJECT (widget), event_key_id, eventp); + } + else if (eventp) + { + g_free (eventp); + gtk_object_remove_data_by_id (GTK_OBJECT (widget), event_key_id); + } + + if (GTK_WIDGET_REALIZED (widget)) + { + gdk_window_set_events (widget->window, + gdk_window_get_events (widget->window) | events); + } +} + +/***************************************** * gtk_widget_set_extension_events: * * arguments: @@ -3949,96 +4011,6 @@ gtk_widget_shape_combine_mask (GtkWidget *widget, } } -/***************************************** - * gtk_widget_dnd_drag_add: - * when you get a DRAG_ENTER event, you can use this - * to tell Gtk ofother widgets that are to be dragged as well - * - * arguments: - * - * results: - *****************************************/ -void -gtk_widget_dnd_drag_add (GtkWidget *widget) -{ -} - -/***************************************** - * gtk_widget_dnd_drag_set: - * these two functions enable drag and/or drop on a - * widget and also let Gtk know what data types will be accepted - * use MIME type naming,plus tacking "URL:" on the front for link - * dragging - * - * - * arguments: - * - * results: - *****************************************/ -void -gtk_widget_dnd_drag_set (GtkWidget *widget, - guint8 drag_enable, - gchar **type_accept_list, - guint numtypes) -{ - g_return_if_fail(widget != NULL); - - if (!widget->window) - gtk_widget_realize (widget); - - g_return_if_fail (widget->window != NULL); - gdk_window_dnd_drag_set (widget->window, - drag_enable, - type_accept_list, - numtypes); -} - -/***************************************** - * gtk_widget_dnd_drop_set: - * - * arguments: - * - * results: - *****************************************/ -void -gtk_widget_dnd_drop_set (GtkWidget *widget, - guint8 drop_enable, - gchar **type_accept_list, - guint numtypes, - guint8 is_destructive_operation) -{ - g_return_if_fail(widget != NULL); - - if (!widget->window) - gtk_widget_realize (widget); - - g_return_if_fail (widget->window != NULL); - gdk_window_dnd_drop_set (widget->window, - drop_enable, - type_accept_list, - numtypes, - is_destructive_operation); -} - -/***************************************** - * gtk_widget_dnd_data_set: - * - * arguments: - * - * results: - *****************************************/ -void -gtk_widget_dnd_data_set (GtkWidget *widget, - GdkEvent *event, - gpointer data, - gulong data_numbytes) -{ - g_return_if_fail (widget != NULL); - g_return_if_fail (widget->window != NULL); - - gdk_window_dnd_data_set (widget->window, event, data, data_numbytes); -} - void gtk_widget_ref (GtkWidget *widget) { diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index de0d954f1b..505b701759 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -136,7 +136,7 @@ struct _GtkAllocation no fields should be modified directly, they should not be created directly, and pointers to them should not be stored beyond the duration of a callback. (If the last is changed, we'll need to add reference - counting) */ + counting.) The time field gives the timestamp at which the data was sent. */ struct _GtkSelectionData { @@ -314,31 +314,55 @@ struct _GtkWidgetClass GdkEventProximity *event); gint (* proximity_out_event) (GtkWidget *widget, GdkEventProximity *event); - gint (* drag_begin_event) (GtkWidget *widget, - GdkEventDragBegin *event); - gint (* drag_request_event) (GtkWidget *widget, - GdkEventDragRequest *event); - gint (* drag_end_event) (GtkWidget *widget, - GdkEvent *event); - gint (* drop_enter_event) (GtkWidget *widget, - GdkEventDropEnter *event); - gint (* drop_leave_event) (GtkWidget *widget, - GdkEventDropLeave *event); - gint (* drop_data_available_event)(GtkWidget *widget, - GdkEventDropDataAvailable *event); gint (* visibility_notify_event) (GtkWidget *widget, GdkEventVisibility *event); gint (* client_event) (GtkWidget *widget, GdkEventClient *event); gint (* no_expose_event) (GtkWidget *widget, GdkEventAny *event); - gint (* other_event) (GtkWidget *widget, - GdkEventOther *event); - + /* selection */ + void (* selection_get) (GtkWidget *widget, + GtkSelectionData *selection_data, + guint info, + guint time); void (* selection_received) (GtkWidget *widget, - GtkSelectionData *selection_data); - + GtkSelectionData *selection_data, + guint time); + + /* Source side drag signals */ + void (* drag_begin) (GtkWidget *widget, + GdkDragContext *context); + void (* drag_end) (GtkWidget *widget, + GdkDragContext *context); + void (* drag_data_get) (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint32 time); + void (* drag_data_delete) (GtkWidget *widget, + GdkDragContext *context); + + /* Target side drag signals */ + void (* drag_leave) (GtkWidget *widget, + GdkDragContext *context, + guint time); + gboolean (* drag_motion) (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time); + gboolean (* drag_drop) (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time); + void (* drag_data_received) (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint32 time); + /* action signals */ void (* debug_msg) (GtkWidget *widget, const gchar *string); @@ -460,6 +484,8 @@ void gtk_widget_set_usize (GtkWidget *widget, gint height); void gtk_widget_set_events (GtkWidget *widget, gint events); +void gtk_widget_add_events (GtkWidget *widget, + gint events); void gtk_widget_set_extension_events (GtkWidget *widget, GdkExtensionMode mode); @@ -536,33 +562,6 @@ void gtk_widget_class_path (GtkWidget *widget, gchar **path, gchar **path_reversed); -/* When you get a drag_enter event, you can use this to tell Gtk of other - * items that are to be dragged as well... - */ -void gtk_widget_dnd_drag_add (GtkWidget *widget); - -/* These two functions enable drag and/or drop on a widget, - * and also let Gtk know what data types will be accepted (use MIME - * type naming, plus tacking "URL:" on the front for link dragging) - */ -void gtk_widget_dnd_drag_set (GtkWidget *widget, - guint8 drag_enable, - gchar **type_accept_list, - guint numtypes); -void gtk_widget_dnd_drop_set (GtkWidget *widget, - guint8 drop_enable, - gchar **type_accept_list, - guint numtypes, - guint8 is_destructive_operation); - -/* Used to reply to a DRAG_REQUEST event - if you don't want to - * give the data then pass in NULL for it - */ -void gtk_widget_dnd_data_set (GtkWidget *widget, - GdkEvent *event, - gpointer data, - gulong data_numbytes); - #if defined (GTK_TRACE_OBJECTS) && defined (__GNUC__) # define gtk_widget_ref gtk_object_ref # define gtk_widget_unref gtk_object_unref diff --git a/gtk/testgtk.c b/gtk/testgtk.c index 914725b21f..68ff79724d 100644 --- a/gtk/testgtk.c +++ b/gtk/testgtk.c @@ -6039,6 +6039,7 @@ create_panes (void) * Drag -N- Drop */ +#if 0 gint dnd_drop_destroy_popup (GtkWidget *widget, GtkWindow **window) { @@ -6263,6 +6264,7 @@ create_dnd (void) else gtk_widget_destroy (window); } +#endif /* * Shaped Windows @@ -7908,7 +7910,7 @@ create_main_window (void) { "ctree", create_ctree }, { "cursors", create_cursors }, { "dialog", create_dialog }, - { "dnd", create_dnd }, + /* { "dnd", create_dnd }, */ { "entry", create_entry }, { "file selection", create_file_selection }, { "font selection", create_font_selection }, diff --git a/gtk/testselection.c b/gtk/testselection.c index 2c12b8dec4..bcb6c57bb4 100644 --- a/gtk/testselection.c +++ b/gtk/testselection.c @@ -48,50 +48,49 @@ typedef struct _Target { SelType type; GdkAtom target; gint format; - GtkSelectionFunction *handler; } Target; /* The following is a list of all the selection targets defined in the ICCCM */ static Target targets[] = { - { "ADOBE_PORTABLE_DOCUMENT_FORMAT", STRING, 0, 8, NULL }, - { "APPLE_PICT", APPLE_PICT, 0, 8, NULL }, - { "BACKGROUND", PIXEL, 0, 32, NULL }, - { "BITMAP", BITMAP, 0, 32, NULL }, - { "CHARACTER_POSITION", SPAN, 0, 32, NULL }, - { "CLASS", TEXT, 0, 8, NULL }, - { "CLIENT_WINDOW", WINDOW, 0, 32, NULL }, - { "COLORMAP", COLORMAP, 0, 32, NULL }, - { "COLUMN_NUMBER", SPAN, 0, 32, NULL }, - { "COMPOUND_TEXT", COMPOUND_TEXT, 0, 8, NULL }, - /* { "DELETE", "NULL", 0, ?, NULL }, */ - { "DRAWABLE", DRAWABLE, 0, 32, NULL }, - { "ENCAPSULATED_POSTSCRIPT", STRING, 0, 8, NULL }, - { "ENCAPSULATED_POSTSCRIPT_INTERCHANGE", STRING, 0, 8, NULL }, - { "FILE_NAME", TEXT, 0, 8, NULL }, - { "FOREGROUND", PIXEL, 0, 32, NULL }, - { "HOST_NAME", TEXT, 0, 8, NULL }, + { "ADOBE_PORTABLE_DOCUMENT_FORMAT", STRING, 0, 8 }, + { "APPLE_PICT", APPLE_PICT, 0, 8 }, + { "BACKGROUND", PIXEL, 0, 32 }, + { "BITMAP", BITMAP, 0, 32 }, + { "CHARACTER_POSITION", SPAN, 0, 32 }, + { "CLASS", TEXT, 0, 8 }, + { "CLIENT_WINDOW", WINDOW, 0, 32 }, + { "COLORMAP", COLORMAP, 0, 32 }, + { "COLUMN_NUMBER", SPAN, 0, 32 }, + { "COMPOUND_TEXT", COMPOUND_TEXT, 0, 8 }, + /* { "DELETE", "NULL", 0, ? }, */ + { "DRAWABLE", DRAWABLE, 0, 32 }, + { "ENCAPSULATED_POSTSCRIPT", STRING, 0, 8 }, + { "ENCAPSULATED_POSTSCRIPT_INTERCHANGE", STRING, 0, 8 }, + { "FILE_NAME", TEXT, 0, 8 }, + { "FOREGROUND", PIXEL, 0, 32 }, + { "HOST_NAME", TEXT, 0, 8 }, /* { "INSERT_PROPERTY", "NULL", 0, ? NULL }, */ /* { "INSERT_SELECTION", "NULL", 0, ? NULL }, */ - { "LENGTH", INTEGER, 0, 32, NULL }, - { "LINE_NUMBER", SPAN, 0, 32, NULL }, - { "LIST_LENGTH", INTEGER, 0, 32, NULL }, - { "MODULE", TEXT, 0, 8, NULL }, - /* { "MULTIPLE", "ATOM_PAIR", 0, 32, NULL }, */ - { "NAME", TEXT, 0, 8, NULL }, - { "ODIF", TEXT, 0, 8, NULL }, - { "OWNER_OS", TEXT, 0, 8, NULL }, - { "PIXMAP", PIXMAP, 0, 32, NULL }, - { "POSTSCRIPT", STRING, 0, 8, NULL }, - { "PROCEDURE", TEXT, 0, 8, NULL }, - { "PROCESS", INTEGER, 0, 32, NULL }, - { "STRING", STRING, 0, 8, NULL }, - { "TARGETS", ATOM, 0, 32, NULL }, - { "TASK", INTEGER, 0, 32, NULL }, - { "TEXT", TEXT, 0, 8 , NULL }, - { "TIMESTAMP", INTEGER, 0, 32, NULL }, - { "USER", TEXT, 0, 8, NULL }, + { "LENGTH", INTEGER, 0, 32 }, + { "LINE_NUMBER", SPAN, 0, 32 }, + { "LIST_LENGTH", INTEGER, 0, 32 }, + { "MODULE", TEXT, 0, 8 }, + /* { "MULTIPLE", "ATOM_PAIR", 0, 32 }, */ + { "NAME", TEXT, 0, 8 }, + { "ODIF", TEXT, 0, 8 }, + { "OWNER_OS", TEXT, 0, 8 }, + { "PIXMAP", PIXMAP, 0, 32 }, + { "POSTSCRIPT", STRING, 0, 8 }, + { "PROCEDURE", TEXT, 0, 8 }, + { "PROCESS", INTEGER, 0, 32 }, + { "STRING", STRING, 0, 8 }, + { "TARGETS", ATOM, 0, 32 }, + { "TASK", INTEGER, 0, 32 }, + { "TEXT", TEXT, 0, 8 }, + { "TIMESTAMP", INTEGER, 0, 32 }, + { "USER", TEXT, 0, 8 }, }; static int num_targets = sizeof(targets)/sizeof(Target); @@ -152,11 +151,15 @@ selection_toggled (GtkWidget *widget) } void -selection_handle (GtkWidget *widget, - GtkSelectionData *selection_data, gpointer data) +selection_get (GtkWidget *widget, + GtkSelectionData *selection_data, + guint info, + guint time, + gpointer data) { guchar *buffer; gint len; + GdkAtom type = GDK_NONE; if (!selection_string) { @@ -168,11 +171,17 @@ selection_handle (GtkWidget *widget, buffer = (guchar *)selection_string->str; len = selection_string->len; } + + switch (info) + { + case COMPOUND_TEXT: + case TEXT: + type = seltypes[COMPOUND_TEXT]; + case STRING: + type = seltypes[STRING]; + } - gtk_selection_data_set (selection_data, - selection_data->target == seltypes[COMPOUND_TEXT] ? - seltypes[COMPOUND_TEXT] : seltypes[STRING], - 8, buffer, len); + gtk_selection_data_set (selection_data, type, 8, buffer, len); } gint @@ -373,6 +382,13 @@ main (int argc, char *argv[]) GtkWidget *hscrollbar; GtkWidget *vscrollbar; GtkWidget *hbox; + + static GtkTargetEntry targetlist[] = { + { "STRING", STRING }, + { "TEXT", TEXT }, + { "COMPOUND_TEXT", COMPOUND_TEXT } + }; + static gint ntargets = sizeof(targetlist) / sizeof(targetlist[0]); gtk_init (&argc, &argv); @@ -408,15 +424,11 @@ main (int argc, char *argv[]) gtk_signal_connect (GTK_OBJECT(selection_button), "selection_received", GTK_SIGNAL_FUNC (selection_received), NULL); - gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY, - seltypes[STRING], selection_handle, NULL); - - gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY, - seltypes[TEXT], selection_handle, NULL); + gtk_selection_add_targets (selection_button, GDK_SELECTION_PRIMARY, + targetlist, ntargets); - gtk_selection_add_handler (selection_button, GDK_SELECTION_PRIMARY, - seltypes[COMPOUND_TEXT], - selection_handle, NULL); + gtk_signal_connect (GTK_OBJECT(selection_button), "selection_get", + GTK_SIGNAL_FUNC (selection_get), NULL); selection_text = gtk_text_new (NULL, NULL); gtk_table_attach_defaults (GTK_TABLE (table), selection_text, 0, 1, 1, 2); |