return Qnil;
}
-DEFUN ("x-begin-drag", Fx_begin_drag, Sx_begin_drag, 1, 5, 0,
+DEFUN ("x-begin-drag", Fx_begin_drag, Sx_begin_drag, 1, 6, 0,
doc: /* Begin dragging contents on FRAME, with targets TARGETS.
TARGETS is a list of strings, which defines the X selection targets
that will be available to the drop target. Block until the mouse
is allowed to be FRAME. Otherwise, no action will be taken if the
mouse buttons are released on top of FRAME.
+If FOLLOW-TOOLTIP is non-nil, any tooltip currently being displayed
+will be moved to follow the mouse pointer while the drag is in
+progress.
+
This function will sometimes return immediately if no mouse buttons
are currently held down. It should only be called when it is known
that mouse buttons are being held down, such as immediately after a
`down-mouse-1' (or similar) event. */)
(Lisp_Object targets, Lisp_Object action, Lisp_Object frame,
- Lisp_Object return_frame, Lisp_Object allow_current_frame)
+ Lisp_Object return_frame, Lisp_Object allow_current_frame,
+ Lisp_Object follow_tooltip)
{
struct frame *f = decode_window_system_frame (frame);
int ntargets = 0, nnames = 0;
xaction, return_frame, action_list,
(const char **) &name_list, nnames,
!NILP (allow_current_frame), target_atoms,
- ntargets, original);
+ ntargets, original, !NILP (follow_tooltip));
SAFE_FREE ();
return lval;
Lisp_Object, int, int, int *, int *);
/* The frame of the currently visible tooltip, or nil if none. */
-static Lisp_Object tip_frame;
+Lisp_Object tip_frame;
/* The window-system window corresponding to the frame of the
currently visible tooltip. */
Window tip_window;
+/* The X and Y deltas of the last call to `x-show-tip'. */
+Lisp_Object tip_dx, tip_dy;
+
/* A timer that hides or deletes the currently visible tooltip when it
fires. */
static Lisp_Object tip_timer;
else
CHECK_FIXNUM (dy);
+ tip_dx = dx;
+ tip_dy = dy;
+
#ifdef USE_GTK
if (use_system_tooltips)
{
staticpro (&tip_last_string);
tip_last_parms = Qnil;
staticpro (&tip_last_parms);
+ tip_dx = Qnil;
+ staticpro (&tip_dx);
+ tip_dy = Qnil;
+ staticpro (&tip_dy);
defsubr (&Sx_uses_old_gtk_dialog);
#if defined (USE_MOTIF) || defined (USE_GTK)
important information. */
bool x_dnd_waiting_for_finish;
+/* Whether or not to move the tooltip along with the mouse pointer
+ during drag-and-drop. */
+static bool x_dnd_update_tooltip;
+
+/* Monitor attribute list used for updating the tooltip position. */
+static Lisp_Object x_dnd_monitors;
+
/* The display the drop target that is supposed to send information is
on. */
static Display *x_dnd_finish_display;
x_dnd_n_targets = 0;
}
+static void
+x_clear_dnd_monitors (void)
+{
+ x_dnd_monitors = Qnil;
+}
+
static void
x_free_dnd_toplevels (void)
{
Lisp_Object return_frame, Atom *ask_action_list,
const char **ask_action_names, size_t n_ask_actions,
bool allow_current_frame, Atom *target_atoms,
- int ntargets, Lisp_Object selection_target_list)
+ int ntargets, Lisp_Object selection_target_list,
+ bool follow_tooltip)
{
#ifndef USE_GTK
XEvent next_event;
unblock_input ();
}
+ if (follow_tooltip)
+ {
+ x_dnd_monitors
+ = Fx_display_monitor_attributes_list (frame);
+ record_unwind_protect_void (x_clear_dnd_monitors);
+ }
+
+ x_dnd_update_tooltip = follow_tooltip;
+
/* This shouldn't happen. */
if (x_dnd_toplevels)
x_dnd_free_toplevels (true);
}
}
+static void
+x_dnd_compute_tip_xy (int *root_x, int *root_y, Lisp_Object attributes)
+{
+ Lisp_Object monitor, geometry;
+ int min_x, min_y, max_x, max_y;
+ int width, height;
+
+ width = FRAME_PIXEL_WIDTH (XFRAME (tip_frame));
+ height = FRAME_PIXEL_HEIGHT (XFRAME (tip_frame));
+
+ max_y = -1;
+
+ /* Try to determine the monitor where the mouse pointer is and
+ its geometry. See bug#22549. */
+ while (CONSP (attributes))
+ {
+ monitor = XCAR (attributes);
+ geometry = assq_no_quit (Qgeometry, monitor);
+
+ if (CONSP (geometry))
+ {
+ min_x = XFIXNUM (Fnth (make_fixnum (1), geometry));
+ min_y = XFIXNUM (Fnth (make_fixnum (2), geometry));
+ max_x = min_x + XFIXNUM (Fnth (make_fixnum (3), geometry));
+ max_y = min_y + XFIXNUM (Fnth (make_fixnum (4), geometry));
+
+ if (min_x <= *root_x && *root_x < max_x
+ && min_y <= *root_y && *root_y < max_y)
+ break;
+
+ max_y = -1;
+ }
+
+ attributes = XCDR (attributes);
+ }
+
+ /* It was not possible to determine the monitor's geometry, so we
+ assign some sane defaults here: */
+ if (max_y < 0)
+ {
+ min_x = 0;
+ min_y = 0;
+ max_x = x_display_pixel_width (FRAME_DISPLAY_INFO (x_dnd_frame));
+ max_y = x_display_pixel_height (FRAME_DISPLAY_INFO (x_dnd_frame));
+ }
+
+ if (*root_y + XFIXNUM (tip_dy) <= min_y)
+ *root_y = min_y; /* Can happen for negative dy */
+ else if (*root_y + XFIXNUM (tip_dy) + height <= max_y)
+ /* It fits below the pointer */
+ *root_y += XFIXNUM (tip_dy);
+ else if (height + XFIXNUM (tip_dy) + min_y <= *root_y)
+ /* It fits above the pointer. */
+ *root_y -= height + XFIXNUM (tip_dy);
+ else
+ /* Put it on the top. */
+ *root_y = min_y;
+
+ if (*root_x + XFIXNUM (tip_dx) <= min_x)
+ *root_x = 0; /* Can happen for negative dx */
+ else if (*root_x + XFIXNUM (tip_dx) + width <= max_x)
+ /* It fits to the right of the pointer. */
+ *root_x += XFIXNUM (tip_dx);
+ else if (width + XFIXNUM (tip_dx) + min_x <= *root_x)
+ /* It fits to the left of the pointer. */
+ *root_x -= width + XFIXNUM (tip_dx);
+ else
+ /* Put it left justified on the screen -- it ought to fit that way. */
+ *root_x = min_x;
+}
+
+static void
+x_dnd_update_tooltip_position (int root_x, int root_y)
+{
+ struct frame *tip_f;
+
+ if (!x_dnd_in_progress || !x_dnd_update_tooltip)
+ return;
+
+ if (!FRAMEP (tip_frame))
+ return;
+
+ tip_f = XFRAME (tip_frame);
+
+ if (!FRAME_LIVE_P (tip_f)
+ || (FRAME_X_DISPLAY (tip_f)
+ != FRAME_X_DISPLAY (x_dnd_frame)))
+ return;
+
+ if (tip_window != None
+ && FIXNUMP (tip_dx) && FIXNUMP (tip_dy))
+ {
+ x_dnd_compute_tip_xy (&root_x, &root_y,
+ x_dnd_monitors);
+
+ XMoveWindow (FRAME_X_DISPLAY (x_dnd_frame),
+ tip_window, root_x, root_y);
+ }
+}
+
/* Get the window underneath the pointer, see if it moved, and update
the DND state accordingly. */
static void
xm_send_drag_motion_message (dpyinfo, FRAME_X_WINDOW (x_dnd_frame),
target, &dmsg);
}
+
+ x_dnd_update_tooltip_position (root_x, root_y);
}
/* The pointer moved out of the screen. */
else if (x_dnd_last_protocol_version != -1)
target, &dmsg);
}
+ x_dnd_update_tooltip_position (event->xmotion.x_root,
+ event->xmotion.y_root);
+
goto OTHER;
}
{
x_dnd_end_window = x_dnd_last_seen_window;
x_dnd_in_progress = false;
+
+ if (x_dnd_update_tooltip
+ && FRAMEP (tip_frame)
+ && FRAME_LIVE_P (XFRAME (tip_frame))
+ && (FRAME_X_DISPLAY (XFRAME (tip_frame))
+ == FRAME_X_DISPLAY (x_dnd_frame)))
+ Fx_hide_tip ();
+
x_dnd_finish_frame = x_dnd_frame;
if (x_dnd_last_seen_window != None
target, &dmsg);
}
+ x_dnd_update_tooltip_position (xev->root_x, xev->root_y);
+
goto XI_OTHER;
}
x_dnd_end_window = x_dnd_last_seen_window;
x_dnd_in_progress = false;
+ /* If a tooltip that we're following is
+ displayed, hide it now. */
+
+ if (x_dnd_update_tooltip
+ && FRAMEP (tip_frame)
+ && FRAME_LIVE_P (XFRAME (tip_frame))
+ && (FRAME_X_DISPLAY (XFRAME (tip_frame))
+ == FRAME_X_DISPLAY (x_dnd_frame)))
+ Fx_hide_tip ();
+
/* This doesn't have to be marked since it
is only accessed if
x_dnd_waiting_for_finish is true, which
x_error_message = NULL;
PDUMPER_IGNORE (x_error_message);
+ x_dnd_monitors = Qnil;
+ staticpro (&x_dnd_monitors);
+
DEFSYM (Qvendor_specific_keysyms, "vendor-specific-keysyms");
DEFSYM (Qlatin_1, "latin-1");
DEFSYM (Qnow, "now");