summaryrefslogtreecommitdiff
path: root/gdk/gdkdnd.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdk/gdkdnd.c')
-rw-r--r--gdk/gdkdnd.c230
1 files changed, 173 insertions, 57 deletions
diff --git a/gdk/gdkdnd.c b/gdk/gdkdnd.c
index 7c951e9869..a4244bd35c 100644
--- a/gdk/gdkdnd.c
+++ b/gdk/gdkdnd.c
@@ -56,11 +56,13 @@ struct _GdkDragContextPrivate {
guint16 last_x; /* Coordinates from last event */
guint16 last_y;
- GdkDragAction old_action; /* The last action we sent to the source */
+ GdkDragAction old_action; /* The last action we sent to the source */
+ GdkDragAction old_actions; /* The last actions we sent to the source */
+ GdkDragAction xdnd_actions; /* What is currently set in XdndActionList */
Window dest_xid;
guint xdnd_targets_set : 1; /* Whether we've already set XdndTypeList */
- guint xdnd_actions_set : 1; /* Whether we've already set XdndActionsList */
+ guint xdnd_actions_set : 1; /* Whether we've already set XdndActionList */
guint xdnd_have_actions : 1; /* Whether an XdndActionList was provided */
guint motif_targets_set : 1; /* Whether we've already set motif initiator info */
guint drag_status : 4; /* current status of drag */
@@ -103,6 +105,10 @@ static GdkFilterReturn xdnd_drop_filter (GdkXEvent *xev,
GdkEvent *event,
gpointer data);
+static void xdnd_manage_source_filter (GdkDragContext *context,
+ GdkWindow *window,
+ gboolean add_filter);
+
/* Drag Contexts */
static GList *contexts;
@@ -145,7 +151,13 @@ gdk_drag_context_unref (GdkDragContext *context)
g_list_free (context->targets);
if (context->source_window)
- gdk_window_unref (context->source_window);
+ {
+ if ((context->protocol == GDK_DRAG_PROTO_XDND) &&
+ !context->is_source)
+ xdnd_manage_source_filter (context, context->source_window, FALSE);
+
+ gdk_window_unref (context->source_window);
+ }
if (context->dest_window)
gdk_window_unref (context->dest_window);
@@ -1361,11 +1373,12 @@ motif_send_motion (GdkDragContext *context,
MOTIF_XCLIENT_SHORT (&xev, 1) = motif_dnd_get_flags (context);
MOTIF_XCLIENT_LONG (&xev, 1) = time;
- if (context->suggested_action != private->old_action)
+ if ((context->suggested_action != private->old_action) ||
+ (context->actions != private->old_actions))
{
MOTIF_XCLIENT_BYTE (&xev, 0) = XmOPERATION_CHANGED;
- private->drag_status = GDK_DRAG_STATUS_ACTION_WAIT;
+ /* private->drag_status = GDK_DRAG_STATUS_ACTION_WAIT; */
retval = TRUE;
}
else
@@ -1724,7 +1737,8 @@ motif_drag_status (GdkEvent *event,
if (context)
{
GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
- if (private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
+ if ((private->drag_status == GDK_DRAG_STATUS_MOTION_WAIT) ||
+ (private->drag_status == GDK_DRAG_STATUS_ACTION_WAIT))
private->drag_status = GDK_DRAG_STATUS_DRAG;
event->dnd.type = GDK_DRAG_STATUS;
@@ -2009,7 +2023,7 @@ xdnd_set_actions (GdkDragContext *context)
if (!xdnd_actions_initialized)
xdnd_initialize_actions();
-
+
actions = context->actions;
n_atoms = 0;
for (i=0; i<xdnd_n_actions; i++)
@@ -2042,6 +2056,7 @@ xdnd_set_actions (GdkDragContext *context)
(guchar *)atomlist, n_atoms);
private->xdnd_actions_set = 1;
+ private->xdnd_actions = context->actions;
}
static void
@@ -2082,9 +2097,6 @@ xdnd_send_enter (GdkDragContext *context)
}
}
- if (!private->xdnd_actions_set)
- xdnd_set_actions (context);
-
if (!gdk_send_xevent (GDK_WINDOW_XWINDOW (context->dest_window),
FALSE, 0, &xev))
{
@@ -2256,6 +2268,133 @@ xdnd_check_dest (Window win)
/* Target side */
+static void
+xdnd_read_actions (GdkDragContext *context)
+{
+ Atom type;
+ int format;
+ gulong nitems, after;
+ Atom *data;
+
+ gint i;
+
+ gint old_warnings = gdk_error_warnings;
+
+ gdk_error_code = 0;
+ gdk_error_warnings = 0;
+
+ /* Get the XdndActionList, if set */
+
+ XGetWindowProperty (GDK_WINDOW_XDISPLAY (context->source_window),
+ GDK_WINDOW_XWINDOW (context->source_window),
+ gdk_atom_intern ("XdndActionList", FALSE), 0, 65536,
+ False, XA_ATOM, &type, &format, &nitems,
+ &after, (guchar **)&data);
+
+ if (!gdk_error_code && (format == 32) && (type == XA_ATOM))
+ {
+ context->actions = 0;
+
+ for (i=0; i<nitems; i++)
+ context->actions |= xdnd_action_from_atom (data[i]);
+
+ ((GdkDragContextPrivate *)context)->xdnd_have_actions = TRUE;
+
+#ifdef G_ENABLE_DEBUG
+ if (gdk_debug_flags & GDK_DEBUG_DND)
+ {
+ GString *action_str = g_string_new (NULL);
+ if (context->actions & GDK_ACTION_MOVE)
+ g_string_append(action_str, "MOVE ");
+ if (context->actions & GDK_ACTION_COPY)
+ g_string_append(action_str, "COPY ");
+ if (context->actions & GDK_ACTION_LINK)
+ g_string_append(action_str, "LINK ");
+ if (context->actions & GDK_ACTION_ASK)
+ g_string_append(action_str, "ASK ");
+
+ g_message("Xdnd actions = %s", action_str->str);
+ g_string_free (action_str, TRUE);
+ }
+#endif /* G_ENABLE_DEBUG */
+
+ XFree(data);
+ }
+
+ gdk_error_warnings = old_warnings;
+ gdk_error_code = 0;
+}
+
+/* We have to make sure that the XdndActionList we keep internally
+ * is up to date with the XdndActionList on the source window
+ * because we get no notification, because Xdnd wasn't meant
+ * to continually send actions. So we select on PropertyChangeMask
+ * and add this filter.
+ */
+static GdkFilterReturn
+xdnd_source_window_filter (GdkXEvent *xev,
+ GdkEvent *event,
+ gpointer cb_data)
+{
+ XEvent *xevent = (XEvent *)xev;
+ GdkDragContext *context = cb_data;
+
+ if ((xevent->xany.type == PropertyNotify) &&
+ (xevent->xproperty.atom == gdk_atom_intern ("XdndActionList", FALSE)))
+ {
+ xdnd_read_actions (context);
+
+ return GDK_FILTER_REMOVE;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static void
+xdnd_manage_source_filter (GdkDragContext *context,
+ GdkWindow *window,
+ gboolean add_filter)
+{
+ gint old_warnings = 0; /* quiet gcc */
+ GdkWindowPrivate *private = (GdkWindowPrivate *)window;
+
+ gboolean is_foreign = (private->window_type == GDK_WINDOW_FOREIGN);
+
+ if (is_foreign)
+ {
+ old_warnings = gdk_error_warnings;
+ gdk_error_warnings = 0;
+ }
+
+ if (!private->destroyed)
+ {
+ if (add_filter)
+ {
+ gdk_window_set_events (window,
+ gdk_window_get_events (window) |
+ GDK_PROPERTY_CHANGE_MASK);
+ gdk_window_add_filter (window, xdnd_source_window_filter, context);
+
+ }
+ else
+ {
+ gdk_window_remove_filter (window,
+ xdnd_source_window_filter,
+ context);
+ /* Should we remove the GDK_PROPERTY_NOTIFY mask?
+ * but we might want it for other reasons. (Like
+ * INCR selection transactions).
+ */
+ }
+ }
+
+ if (is_foreign)
+ {
+ gdk_flush();
+ gdk_error_warnings = old_warnings;
+ }
+}
+
static GdkFilterReturn
xdnd_enter_filter (GdkXEvent *xev,
GdkEvent *event,
@@ -2339,49 +2478,14 @@ xdnd_enter_filter (GdkXEvent *xev,
GUINT_TO_POINTER (xevent->xclient.data.l[2+i]));
}
- /* Get the XdndActionList, if set */
-
- XGetWindowProperty (GDK_WINDOW_XDISPLAY (event->any.window),
- source_window,
- gdk_atom_intern ("XdndActionList", FALSE), 0, 65536,
- False, XA_ATOM, &type, &format, &nitems,
- &after, (guchar **)&data);
-
- if ((format == 32) && (type == XA_ATOM))
- {
- new_context->actions = 0;
-
- for (i=0; i<nitems; i++)
- new_context->actions |= xdnd_action_from_atom (data[i]);
-
- ((GdkDragContextPrivate *)new_context)->xdnd_have_actions = TRUE;
-
-#ifdef G_ENABLE_DEBUG
- if (gdk_debug_flags & GDK_DEBUG_DND)
- {
- GString *action_str = g_string_new (NULL);
- if (new_context->actions & GDK_ACTION_MOVE)
- g_string_append(action_str, "MOVE ");
- if (new_context->actions & GDK_ACTION_COPY)
- g_string_append(action_str, "COPY ");
- if (new_context->actions & GDK_ACTION_LINK)
- g_string_append(action_str, "LINK ");
- if (new_context->actions & GDK_ACTION_ASK)
- g_string_append(action_str, "ASK ");
-
- g_message("\tactions = %s", action_str->str);
- g_string_free (action_str, TRUE);
- }
-#endif /* G_ENABLE_DEBUG */
-
- XFree(data);
- }
-
#ifdef G_ENABLE_DEBUG
if (gdk_debug_flags & GDK_DEBUG_DND)
print_target_list (new_context->targets);
#endif /* G_ENABLE_DEBUG */
+ xdnd_manage_source_filter (new_context, new_context->source_window, TRUE);
+ xdnd_read_actions (new_context);
+
event->dnd.type = GDK_DRAG_ENTER;
event->dnd.context = new_context;
gdk_drag_context_ref (new_context);
@@ -2558,8 +2662,7 @@ gdk_drag_do_leave (GdkDragContext *context, guint32 time)
GdkDragContext *
gdk_drag_begin (GdkWindow *window,
- GList *targets,
- GdkDragAction actions)
+ GList *targets)
{
GList *tmp_list;
GdkDragContext *new_context;
@@ -2580,7 +2683,7 @@ gdk_drag_begin (GdkWindow *window,
tmp_list = tmp_list->prev;
}
- new_context->actions = actions;
+ new_context->actions = 0;
return new_context;
}
@@ -2717,13 +2820,25 @@ gdk_drag_motion (GdkDragContext *context,
GdkDragProtocol protocol,
gint x_root,
gint y_root,
- GdkDragAction action,
+ GdkDragAction suggested_action,
+ GdkDragAction possible_actions,
guint32 time)
{
GdkDragContextPrivate *private = (GdkDragContextPrivate *)context;
g_return_val_if_fail (context != NULL, FALSE);
+ /* When we have a Xdnd target, make sure our XdndActionList
+ * matches the current actions;
+ */
+ private->old_actions = context->actions;
+ context->actions = possible_actions;
+
+ if ((protocol == GDK_DRAG_PROTO_XDND) &&
+ (!private->xdnd_actions_set ||
+ private->xdnd_actions != possible_actions))
+ xdnd_set_actions (context);
+
if (context->dest_window != dest_window)
{
GdkEvent temp_event;
@@ -2754,8 +2869,9 @@ gdk_drag_motion (GdkDragContext *context,
case GDK_DRAG_PROTO_NONE:
break;
}
- private->old_action = action;
- context->suggested_action = action;
+ private->old_action = suggested_action;
+ context->suggested_action = suggested_action;
+ private->old_actions = possible_actions;
}
else
{
@@ -2782,7 +2898,7 @@ gdk_drag_motion (GdkDragContext *context,
else
{
private->old_action = context->suggested_action;
- context->suggested_action = action;
+ context->suggested_action = suggested_action;
}
/* Send a drag-motion event */
@@ -2797,11 +2913,11 @@ gdk_drag_motion (GdkDragContext *context,
switch (context->protocol)
{
case GDK_DRAG_PROTO_MOTIF:
- motif_send_motion (context, x_root, y_root, action, time);
+ motif_send_motion (context, x_root, y_root, suggested_action, time);
break;
case GDK_DRAG_PROTO_XDND:
- xdnd_send_motion (context, x_root, y_root, action, time);
+ xdnd_send_motion (context, x_root, y_root, suggested_action, time);
break;
case GDK_DRAG_PROTO_ROOTWIN: