* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
-#include <config.h>
+#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "gdk/gdkkeysyms.h"
+#ifdef GDK_WINDOWING_X11
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include "gdk/x11/gdkx.h"
+#endif
+
#include "gtkdnd.h"
#include "gtkiconfactory.h"
#include "gtkicontheme.h"
}
+#ifdef GDK_WINDOWING_X11
+
+/*
+ * We want to handle a handful of keys during DND, e.g. Escape to abort.
+ * Grabbing the keyboard has the unfortunate side-effect of preventing
+ * useful things such as using Alt-Tab to cycle between windows or
+ * switching workspaces. Therefore, we just grab the few keys we are
+ * interested in. Note that we need to put the grabs on the root window
+ * in order for them to still work when the focus is moved to another
+ * app/workspace.
+ *
+ * GDK needs a little help to successfully deliver root key events...
+ */
+
+static GdkFilterReturn
+root_key_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *ev = (XEvent *)xevent;
+
+ if ((ev->type == KeyPress || ev->type == KeyRelease) &&
+ ev->xkey.root == ev->xkey.window)
+ ev->xkey.window = (Window)data;
+
+ return GDK_FILTER_CONTINUE;
+}
+
+typedef struct {
+ gint keysym;
+ gint modifiers;
+} GrabKey;
+
+static GrabKey grab_keys[] = {
+ { XK_Escape, 0 },
+ { XK_space, 0 },
+ { XK_KP_Space, 0 },
+ { XK_Return, 0 },
+ { XK_KP_Enter, 0 },
+ { XK_Up, 0 },
+ { XK_Up, Mod1Mask },
+ { XK_Down, 0 },
+ { XK_Down, Mod1Mask },
+ { XK_Left, 0 },
+ { XK_Left, Mod1Mask },
+ { XK_Right, 0 },
+ { XK_Right, Mod1Mask },
+ { XK_KP_Up, 0 },
+ { XK_KP_Up, Mod1Mask },
+ { XK_KP_Down, 0 },
+ { XK_KP_Down, Mod1Mask },
+ { XK_KP_Left, 0 },
+ { XK_KP_Left, Mod1Mask },
+ { XK_KP_Right, 0 },
+ { XK_KP_Right, Mod1Mask }
+};
+
+static void
+grab_dnd_keys (GtkWidget *widget,
+ guint32 time)
+{
+ guint i;
+ GdkWindow *window, *root;
+ gint keycode;
+
+ window = widget->window;
+ root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
+
+ gdk_error_trap_push ();
+
+ for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
+ {
+ keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
+ XGrabKey (GDK_WINDOW_XDISPLAY (window),
+ keycode, grab_keys[i].modifiers,
+ GDK_WINDOW_XID (root),
+ FALSE,
+ GrabModeAsync,
+ GrabModeAsync);
+ }
+
+ gdk_flush ();
+ gdk_error_trap_pop ();
+
+ gdk_window_add_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
+}
+
+static void
+ungrab_dnd_keys (GtkWidget *widget,
+ guint32 time)
+{
+ guint i;
+ GdkWindow *window, *root;
+ gint keycode;
+
+ window = widget->window;
+ root = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
+
+ gdk_window_remove_filter (NULL, root_key_filter, (gpointer) GDK_WINDOW_XID (window));
+
+ gdk_error_trap_push ();
+
+ for (i = 0; i < G_N_ELEMENTS (grab_keys); ++i)
+ {
+ keycode = XKeysymToKeycode (GDK_WINDOW_XDISPLAY (window), grab_keys[i].keysym);
+ XUngrabKey (GDK_WINDOW_XDISPLAY (window),
+ keycode, grab_keys[i].modifiers,
+ GDK_WINDOW_XID (root));
+ }
+
+ gdk_flush ();
+ gdk_error_trap_pop ();
+}
+
+#else
+
+static void
+grab_dnd_keys (GtkWidget *widget,
+ guint32 time)
+{
+ gdk_keyboard_grab (widget->window, FALSE, time);
+}
+
+static void
+ungrab_dnd_keys (GtkWidget *widget,
+ guint32 time)
+{
+ gdk_display_keyboard_ungrab (gtk_widget_get_display (widget), time);
+}
+
+#endif
+
+
/***************************************************************
* gtk_drag_release_ipc_widget:
* Releases widget retrieved with gtk_drag_get_ipc_widget ()
GdkScreen *screen = gtk_widget_get_screen (widget);
GSList *drag_widgets = g_object_get_data (G_OBJECT (screen),
"gtk-dnd-ipc-widgets");
+ ungrab_dnd_keys (widget, GDK_CURRENT_TIME);
if (window->group)
gtk_window_group_remove_window (window->group, window);
drag_widgets = g_slist_prepend (drag_widgets, widget);
g_object_ref (context);
g_object_ref (widget);
- g_signal_connect (selection_widget, "selection_received",
+ g_signal_connect (selection_widget, "selection-received",
G_CALLBACK (gtk_drag_selection_received), widget);
g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
g_object_ref (context);
g_object_set_data (G_OBJECT (selection_widget), I_("drag-context"), context);
- g_signal_connect (selection_widget, "selection_received",
+ g_signal_connect (selection_widget, "selection-received",
G_CALLBACK (gtk_drag_selection_received),
NULL);
gtk_paint_shadow (widget->style, widget->window,
GTK_STATE_NORMAL, GTK_SHADOW_OUT,
- NULL, widget, "dnd",
+ &event->area, widget, "dnd",
x, y, width, height);
cr = gdk_cairo_create (widget->window);
{
g_return_if_fail (GTK_IS_WIDGET (widget));
- g_signal_connect_after (widget, "expose_event",
+ g_signal_connect_after (widget, "expose-event",
G_CALLBACK (gtk_drag_highlight_expose),
NULL);
g_signal_connect (widget, "realize",
G_CALLBACK (gtk_drag_dest_realized), site);
- g_signal_connect (widget, "hierarchy_changed",
+ g_signal_connect (widget, "hierarchy-changed",
G_CALLBACK (gtk_drag_dest_hierarchy_changed), site);
g_object_set_data_full (G_OBJECT (widget), I_("gtk-drag-dest"),
site, gtk_drag_dest_site_destroy);
}
-
-/*************************************************************
+/**
* 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,
- const GtkTargetEntry *targets,
- gint n_targets,
- GdkDragAction actions)
+ * @widget: a #GtkWidget
+ * @flags: which types of default drag behavior to use
+ * @targets: a pointer to an array of #GtkTargetEntry<!-- -->s indicating
+ * the drop types that this @widget will accept. Later you can access the list
+ * with gtk_drag_dest_get_target_list() and gtk_drag_dest_find_target().
+ * @n_targets: the number of entries in @targets.
+ * @actions: a bitmask of possible actions for a drop onto this @widget.
+ *
+ * Sets a widget as a potential drop destination, and adds default behaviors.
+ *
+ * The default behaviors listed in @flags have an effect similar
+ * to installing default handlers for the widget's drag-and-drop signals
+ * (#GtkWidget:drag-motion, #GtkWidget:drag-drop, ...). They all exist
+ * for convenience. When passing #GTK_DEST_DEFAULT_ALL for instance it is
+ * sufficient to connect to the widget's #GtkWidget::drag-data-received
+ * signal to get primitive, but consistent drag-and-drop support.
+ *
+ * Things become more complicated when you try to preview the dragged data,
+ * as described in the documentation for #GtkWidget:drag-motion. The default
+ * behaviors described by @flags make some assumptions, that can conflict
+ * with your own signal handlers. For instance #GTK_DEST_DEFAULT_DROP causes
+ * invokations of gdk_drag_status() in the context of #GtkWidget:drag-motion,
+ * and invokations of gtk_drag_finish() in #GtkWidget:drag-data-received.
+ * Especially the later is dramatic, when your own #GtkWidget:drag-motion
+ * handler calls gtk_drag_get_data() to inspect the dragged data.
+ */
+void
+gtk_drag_dest_set (GtkWidget *widget,
+ GtkDestDefaults flags,
+ const GtkTargetEntry *targets,
+ gint n_targets,
+ GdkDragAction actions)
{
GtkDragDestSite *site;
if (tmp_source->data == GUINT_TO_POINTER (pair->target))
{
if ((!(pair->flags & GTK_TARGET_SAME_APP) || source_widget) &&
- (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)))
+ (!(pair->flags & GTK_TARGET_SAME_WIDGET) || (source_widget == widget)) &&
+ (!(pair->flags & GTK_TARGET_OTHER_APP) || !source_widget) &&
+ (!(pair->flags & GTK_TARGET_OTHER_WIDGET) || (source_widget != widget)))
return pair->target;
else
break;
if (!(site->flags & GTK_DEST_DEFAULT_DROP) ||
selection_data->length >= 0)
g_signal_emit_by_name (drop_widget,
- "drag_data_received",
+ "drag-data-received",
context, info->drop_x, info->drop_y,
selection_data,
target_info, time);
else
{
g_signal_emit_by_name (drop_widget,
- "drag_data_received",
+ "drag-data-received",
context, info->drop_x, info->drop_y,
selection_data,
0, time);
new_data.toplevel = FALSE;
/* need to reference children temporarily in case the
- * ::drag_motion/::drag_drop callbacks change the widget heirarchy.
+ * ::drag-motion/::drag-drop callbacks change the widget hierarchy.
*/
gtk_container_forall (GTK_CONTAINER (widget), prepend_and_ref_widget, &children);
for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
}
/* If not, and this widget is registered as a drop site, check to
- * emit "drag_motion" to check if we are actually in
+ * emit "drag-motion" to check if we are actually in
* a drop site.
*/
if (!data->found &&
data->x - x_offset - allocation_to_window_x,
data->y - y_offset - allocation_to_window_y,
data->time);
- /* If so, send a "drag_leave" to the last widget */
+ /* If so, send a "drag-leave" to the last widget */
if (data->found)
{
if (data->info->widget && data->info->widget != widget)
source_info = gtk_drag_get_source_info (context, TRUE);
source_info->ipc_widget = ipc_widget;
- source_info->widget = gtk_widget_ref (widget);
+ source_info->widget = g_object_ref (widget);
source_info->target_list = gtk_target_list_new (NULL, 0);
tmp_list = dest_info->context->targets;
source_info->proxy_dest = dest_info;
g_signal_connect (ipc_widget,
- "selection_get",
+ "selection-get",
G_CALLBACK (gtk_drag_selection_get),
source_info);
if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
site->track_motion)
- g_signal_emit_by_name (widget, "drag_leave", context, time);
+ g_signal_emit_by_name (widget, "drag-leave", context, time);
site->have_drag = FALSE;
}
}
}
- g_signal_emit_by_name (widget, "drag_motion",
+ g_signal_emit_by_name (widget, "drag-motion",
context, x, y, time, &retval);
return (site->flags & GTK_DEST_DEFAULT_MOTION) ? TRUE : retval;
gtk_drag_get_data (widget, context, target, time);
}
- g_signal_emit_by_name (widget, "drag_drop",
+ g_signal_emit_by_name (widget, "drag-drop",
context, x, y, time, &retval);
return (site->flags & GTK_DEST_DEFAULT_DROP) ? TRUE : retval;
return NULL;
}
- gdk_keyboard_grab (ipc_widget->window, FALSE, time);
-
+ grab_dnd_keys (ipc_widget, time);
+
/* We use a GTK grab here to override any grabs that the widget
* we are dragging from might have held
*/
info->ipc_widget = ipc_widget;
g_object_set_data (G_OBJECT (info->ipc_widget), I_("gtk-info"), info);
- info->widget = gtk_widget_ref (widget);
+ info->widget = g_object_ref (widget);
info->button = button;
info->cursor = cursor;
info->icon_window = NULL;
info->destroy_icon = FALSE;
- /* Set cur_x, cur_y here so if the "drag_begin" signal shows
+ /* Set cur_x, cur_y here so if the "drag-begin" signal shows
* the drag icon, it will be in the right place
*/
if (event && event->type == GDK_MOTION_NOTIFY)
&info->cur_screen, &info->cur_x, &info->cur_y, NULL);
}
- g_signal_emit_by_name (widget, "drag_begin", info->context);
+ g_signal_emit_by_name (widget, "drag-begin", info->context);
/* Ensure that we have an icon before we start the drag; the
* application may have set one in ::drag_begin, or it may
info->start_x = info->cur_x;
info->start_y = info->cur_y;
- g_signal_connect (info->ipc_widget, "grab_broken_event",
+ g_signal_connect (info->ipc_widget, "grab-broken-event",
G_CALLBACK (gtk_drag_grab_broken_event_cb), info);
- g_signal_connect (info->ipc_widget, "grab_notify",
+ g_signal_connect (info->ipc_widget, "grab-notify",
G_CALLBACK (gtk_drag_grab_notify_cb), info);
- g_signal_connect (info->ipc_widget, "button_release_event",
+ g_signal_connect (info->ipc_widget, "button-release-event",
G_CALLBACK (gtk_drag_button_release_cb), info);
- g_signal_connect (info->ipc_widget, "motion_notify_event",
+ g_signal_connect (info->ipc_widget, "motion-notify-event",
G_CALLBACK (gtk_drag_motion_cb), info);
- g_signal_connect (info->ipc_widget, "key_press_event",
+ g_signal_connect (info->ipc_widget, "key-press-event",
G_CALLBACK (gtk_drag_key_cb), info);
- g_signal_connect (info->ipc_widget, "key_release_event",
+ g_signal_connect (info->ipc_widget, "key-release-event",
G_CALLBACK (gtk_drag_key_cb), info);
- g_signal_connect (info->ipc_widget, "selection_get",
+ g_signal_connect (info->ipc_widget, "selection-get",
G_CALLBACK (gtk_drag_selection_get), info);
info->have_grab = TRUE;
site->icon_type = GTK_IMAGE_EMPTY;
- g_signal_connect (widget, "button_press_event",
+ g_signal_connect (widget, "button-press-event",
G_CALLBACK (gtk_drag_source_event_cb),
site);
- g_signal_connect (widget, "button_release_event",
+ g_signal_connect (widget, "button-release-event",
G_CALLBACK (gtk_drag_source_event_cb),
site);
- g_signal_connect (widget, "motion_notify_event",
+ g_signal_connect (widget, "motion-notify-event",
G_CALLBACK (gtk_drag_source_event_cb),
site);
gtk_drag_remove_icon (info);
if (widget)
- gtk_widget_ref (widget);
+ g_object_ref (widget);
info->icon_window = widget;
info->hot_x = hot_x;
*
* Changes the icon for a widget to a given widget. GTK+
* will not destroy the icon, so if you don't want
- * it to persist, you should connect to the "drag_end"
+ * it to persist, you should connect to the "drag-end"
* signal and destroy it yourself.
**/
void
* with a context for the source side of a drag)
* @colormap: the colormap of the icon
* @pixmap: the image data for the icon
- * @mask: the transparency mask for the icon
+ * @mask: the transparency mask for the icon or %NULL for none.
* @hot_x: the X offset within @pixmap of the hotspot.
* @hot_y: the Y offset within @pixmap of the hotspot.
*
*
* Changes the default drag icon. GTK+ retains references for the
* arguments, and will release them when they are no longer needed.
- * This function is obsolete. The default icon should now be changed
- * via the stock system by changing the stock pixbuf for #GTK_STOCK_DND.
+ *
+ * Deprecated: Change the default drag icon via the stock system by
+ * changing the stock pixbuf for #GTK_STOCK_DND instead.
**/
void
gtk_drag_set_default_icon (GdkColormap *colormap,
else
{
if (!success)
- g_signal_emit_by_name (info->widget, "drag_failed",
+ g_signal_emit_by_name (info->widget, "drag-failed",
info->context, result, &success);
if (success)
selection_data.data = NULL;
selection_data.length = -1;
- g_signal_emit_by_name (info->widget, "drag_data_get",
+ g_signal_emit_by_name (info->widget, "drag-data-get",
info->context, &selection_data,
pair->info,
time);
{
case TARGET_DELETE:
g_signal_emit_by_name (info->widget,
- "drag_data_delete",
+ "drag-data-delete",
info->context);
gtk_selection_data_set (selection_data, null_atom, 8, NULL, 0);
break;
selection_data->target,
&target_info))
{
- g_signal_emit_by_name (info->widget, "drag_data_get",
+ g_signal_emit_by_name (info->widget, "drag-data-get",
info->context,
selection_data,
target_info,
info);
if (!info->proxy_dest)
- g_signal_emit_by_name (info->widget, "drag_end",
- info->context);
+ g_signal_emit_by_name (info->widget, "drag-end", info->context);
if (info->widget)
g_object_unref (info->widget);
if (info->drop_timeout)
g_source_remove (info->drop_timeout);
+ if (info->update_idle)
+ g_source_remove (info->update_idle);
+
g_free (info);
}
static void
gtk_drag_add_update_idle (GtkDragSourceInfo *info)
{
- /* We use an idle lowerthan GDK_PRIORITY_REDRAW so that exposes
+ /* We use an idle lower than GDK_PRIORITY_REDRAW so that exposes
* from the last move can catch up before we move again.
*/
if (!info->update_idle)
info);
gdk_display_pointer_ungrab (display, time);
- gdk_display_keyboard_ungrab (display, time);
+ ungrab_dnd_keys (info->ipc_widget, time);
gtk_grab_remove (info->ipc_widget);
/* Send on a release pair to the original
/*************************************************************
* gtk_drag_motion_cb:
- * "motion_notify_event" callback during drag.
+ * "motion-notify-event" callback during drag.
* arguments:
*
* results:
/*************************************************************
* gtk_drag_key_cb:
- * "key_press/release_event" callback during drag.
+ * "key-press/release-event" callback during drag.
* arguments:
*
* results:
case GDK_space:
case GDK_Return:
+ case GDK_ISO_Enter:
case GDK_KP_Enter:
case GDK_KP_Space:
gtk_drag_end (info, event->time);
/*************************************************************
* gtk_drag_button_release_cb:
- * "button_release_event" callback during drag.
+ * "button-release-event" callback during drag.
* arguments:
*
* results: