#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);
{
gint x, y, width, height;
- if (GTK_WIDGET_DRAWABLE (widget))
+ if (gtk_widget_is_drawable (widget))
{
cairo_t *cr;
- if (GTK_WIDGET_NO_WINDOW (widget))
+ if (!gtk_widget_get_has_window (widget))
{
x = widget->allocation.x;
y = widget->allocation.y;
* 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.
+ *
+ * There's no way to set a default action here, you can use the
+ * #GtkWidget:drag-motion callback for that. Here's an example which selects
+ * the action to use depending on whether the control key is pressed or not:
+ * |[
+ * static void
+ * drag_motion (GtkWidget *widget,
+ * GdkDragContext *context,
+ * gint x,
+ * gint y,
+ * guint time)
+ * {
+ * GdkModifierType mask;
+ *
+ * gdk_window_get_pointer (gtk_widget_get_window (widget),
+ * NULL, NULL, &mask);
+ * if (mask & GDK_CONTROL_MASK)
+ * gdk_drag_status (context, GDK_ACTION_COPY, time);
+ * else
+ * gdk_drag_status (context, GDK_ACTION_MOVE, time);
+ * }
+ * ]|
*/
void
gtk_drag_dest_set (GtkWidget *widget,
void
gtk_drag_dest_unset (GtkWidget *widget)
{
+ GtkDragDestSite *old_site;
+
g_return_if_fail (GTK_IS_WIDGET (widget));
+ old_site = g_object_get_data (G_OBJECT (widget),
+ "gtk-drag-dest");
+ if (old_site)
+ {
+ g_signal_handlers_disconnect_by_func (widget,
+ gtk_drag_dest_realized,
+ old_site);
+ g_signal_handlers_disconnect_by_func (widget,
+ gtk_drag_dest_hierarchy_changed,
+ old_site);
+ }
+
g_object_set_data (G_OBJECT (widget), I_("gtk-drag-dest"), NULL);
}
/**
* gtk_drag_dest_set_target_list:
* @widget: a #GtkWidget that's a drag destination
- * @target_list: list of droppable targets, or %NULL for none
+ * @target_list: (allow-none): list of droppable targets, or %NULL for none
*
* Sets the target types that this widget can accept from drag-and-drop.
* The widget must first be made into a drag destination with
* gtk_drag_dest_find_target:
* @widget: drag destination widget
* @context: drag context
- * @target_list: list of droppable targets, or %NULL to use
+ * @target_list: (allow-none): list of droppable targets, or %NULL to use
* gtk_drag_dest_get_target_list (@widget).
*
* Looks for a match between @context->targets and the
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);
gint x_offset = 0;
gint y_offset = 0;
- if (data->found || !GTK_WIDGET_MAPPED (widget) || !GTK_WIDGET_SENSITIVE (widget))
+ if (data->found || !GTK_WIDGET_MAPPED (widget) || !gtk_widget_get_sensitive (widget))
return;
/* Note that in the following code, we only count the
allocation_to_window_x = widget->allocation.x;
allocation_to_window_y = widget->allocation.y;
- if (!GTK_WIDGET_NO_WINDOW (widget))
+ if (gtk_widget_get_has_window (widget))
{
/* The allocation is relative to the parent window for
* window widgets, not to widget->window.
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 (!new_data.found && GTK_WIDGET_DRAWABLE (tmp_list->data))
+ if (!new_data.found && gtk_widget_is_drawable (tmp_list->data))
gtk_drag_find_widget (tmp_list->data, &new_data);
g_object_unref (tmp_list->data);
}
}
/* 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)
{
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
- if (GTK_WIDGET_TOPLEVEL (toplevel))
+ if (gtk_widget_is_toplevel (toplevel))
gdk_window_register_dnd (toplevel->window);
}
{
GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
- if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_WIDGET_REALIZED (toplevel))
+ if (gtk_widget_is_toplevel (toplevel) && GTK_WIDGET_REALIZED (toplevel))
gdk_window_register_dnd (toplevel->window);
}
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->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
/**
* gtk_drag_source_set_target_list:
* @widget: a #GtkWidget that's a drag source
- * @target_list: list of draggable targets, or %NULL for none
+ * @target_list: (allow-none): list of draggable targets, or %NULL for none
*
* Changes the target types that this widget offers for drag-and-drop.
* The widget must first be made into a drag source with
* @widget: a #GtkWidget
* @colormap: the colormap of the icon
* @pixmap: the image data for the icon
- * @mask: the transparency mask for an image.
- *
+ * @mask: (allow-none): the transparency mask for an image.
+ *
* Sets the icon that will be used for drags from a particular widget
- * from a pixmap/mask. GTK+ retains references for the arguments, and
+ * from a pixmap/mask. GTK+ retains references for the arguments, and
* will release them when they are no longer needed.
* Use gtk_drag_source_set_icon_pixbuf() instead.
**/
info->cur_x - hot_x,
info->cur_y - hot_y);
- if (GTK_WIDGET_VISIBLE (icon_window))
+ if (gtk_widget_get_visible (icon_window))
gdk_window_raise (icon_window->window);
else
gtk_widget_show (icon_window);
*
* 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 or %NULL for none.
+ * @mask: (allow-none): 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);
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:
/*************************************************************
* gtk_drag_button_release_cb:
- * "button_release_event" callback during drag.
+ * "button-release-event" callback during drag.
* arguments:
*
* results: