Allow dragging and dropping multiple actions

* doc/lispref/frames.texi (Drag and Drop): Document new meaning
of `action'.
* lisp/term/haiku-win.el (x-begin-drag): Correct for new meaning
of `action'.
* src/xfns.c (Fx_begin_drag): Handle new alist meaning of
`action'.
* src/xterm.c (x_dnd_begin_drag_and_drop): New parameters
`ask_action_list', `ask_action_names' and `n_ask_actions'.
* src/xterm.h: Update prototypes.
This commit is contained in:
Po Lu 2022-03-24 09:42:47 +08:00
parent ac3bb7e754
commit 17393c0db0
5 changed files with 107 additions and 9 deletions

View file

@ -4061,6 +4061,11 @@ the drop target; or @code{XdndActionMove}, which means copy as with
@code{XdndActionCopy}, and in addition the caller should delete
whatever was stored in that selection after copying it.
@var{action} may also be an alist which associates between symbols
describing the available actions, and strings that the drop target is
expected to present to the user to choose between the available
actions.
If @var{return-frame} is non-nil and the mouse moves over an Emacs
frame after first moving out of @var{frame}, then the frame to which
the mouse moves will be returned immediately. This is useful when you

View file

@ -224,7 +224,9 @@ take effect on menu items until the menu bar is updated again."
(push (cadr selection-result)
(cdr (alist-get (car selection-result) message
nil nil #'equal))))))))
(prog1 (or action 'XdndActionCopy)
(prog1 (or (and (symbolp action)
action)
'XdndActionCopy)
(haiku-drag-message (or frame (selected-frame))
message))))

View file

@ -6614,17 +6614,28 @@ If RETURN-FRAME is non-nil, this function will return the frame if the
mouse pointer moves onto an Emacs frame, after first moving out of
FRAME.
If ACTION is a list and not nil, its elements are assumed to be a cons
of (ITEM . STRING), where ITEM is the name of an action, and STRING is
a string describing ITEM to the user. The drop target is expected to
prompt the user to choose between any of the actions in the list.
If ACTION is not specified or nil, `XdndActionCopy' is used
instead. */)
(Lisp_Object targets, Lisp_Object action, Lisp_Object frame,
Lisp_Object return_frame)
{
struct frame *f = decode_window_system_frame (frame);
int ntargets = 0;
int ntargets = 0, nnames = 0;
ptrdiff_t len;
char *target_names[2048];
Atom *target_atoms;
Lisp_Object lval, original;
Lisp_Object lval, original, tem, t1, t2;
Atom xaction;
Atom action_list[2048];
char *name_list[2048];
char *scratch;
USE_SAFE_ALLOCA;
CHECK_LIST (targets);
original = targets;
@ -6650,10 +6661,48 @@ instead. */)
xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionMove;
else if (EQ (action, QXdndActionLink))
xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionLink;
else if (EQ (action, QXdndActionAsk))
xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionAsk;
else if (EQ (action, QXdndActionPrivate))
xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionPrivate;
else if (CONSP (action))
{
xaction = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionAsk;
original = action;
CHECK_LIST (action);
for (; CONSP (action); action = XCDR (action))
{
tem = XCAR (action);
CHECK_CONS (tem);
t1 = XCAR (tem);
t2 = XCDR (tem);
CHECK_SYMBOL (t1);
CHECK_STRING (t2);
if (nnames < 2048)
{
if (EQ (t1, QXdndActionCopy))
action_list[nnames] = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionCopy;
else if (EQ (t1, QXdndActionMove))
action_list[nnames] = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionMove;
else if (EQ (t1, QXdndActionLink))
action_list[nnames] = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionLink;
else if (EQ (t1, QXdndActionPrivate))
action_list[nnames] = FRAME_DISPLAY_INFO (f)->Xatom_XdndActionPrivate;
else
signal_error ("Invalid drag-and-drop action", tem);
scratch = SSDATA (ENCODE_UTF_8 (t2));
len = strlen (scratch);
name_list[nnames] = SAFE_ALLOCA (len + 1);
strncpy (name_list[nnames], scratch, len + 1);
nnames++;
}
else
error ("Too many actions");
}
CHECK_LIST_END (action, original);
}
else
signal_error ("Invalid drag-and-drop action", action);
@ -6666,8 +6715,10 @@ instead. */)
x_set_dnd_targets (target_atoms, ntargets);
lval = x_dnd_begin_drag_and_drop (f, FRAME_DISPLAY_INFO (f)->last_user_time,
xaction, !NILP (return_frame));
xaction, !NILP (return_frame), action_list,
(const char **) &name_list, nnames);
SAFE_FREE ();
return lval;
}

View file

@ -6942,7 +6942,9 @@ x_top_window_to_frame (struct x_display_info *dpyinfo, int wdesc)
Lisp_Object
x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
bool return_frame_p)
bool return_frame_p, Atom *ask_action_list,
const char **ask_action_names,
size_t n_ask_actions)
{
#ifndef USE_GTK
XEvent next_event;
@ -6951,9 +6953,11 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
XWindowAttributes root_window_attrs;
struct input_event hold_quit;
struct frame *any;
char *atom_name;
char *atom_name, *ask_actions;
Lisp_Object action, ltimestamp;
specpdl_ref ref;
ptrdiff_t i, end, fill;
XTextProperty prop;
if (!FRAME_VISIBLE_P (f))
error ("Frame is invisible");
@ -6972,6 +6976,41 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
else
x_dnd_selection_timestamp = XFIXNUM (ltimestamp);
if (n_ask_actions)
{
ask_actions = NULL;
end = 0;
for (i = 0; i < n_ask_actions; ++i)
{
fill = end;
end += strlen (ask_action_names[i]) + 1;
if (ask_actions)
ask_actions = xrealloc (ask_actions, end);
else
ask_actions = xmalloc (end);
strncpy (ask_actions + fill,
ask_action_names[i],
end - fill);
}
prop.value = (unsigned char *) ask_actions;
prop.encoding = XA_STRING;
prop.format = 8;
prop.nitems = end;
XSetTextProperty (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
&prop, FRAME_DISPLAY_INFO (f)->Xatom_XdndActionDescription);
xfree (ask_actions);
XChangeProperty (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
FRAME_DISPLAY_INFO (f)->Xatom_XdndActionList, XA_ATOM, 32,
PropModeReplace, (unsigned char *) ask_action_list,
n_ask_actions);
}
x_dnd_in_progress = true;
x_dnd_frame = f;
x_dnd_last_seen_window = FRAME_X_WINDOW (f);

View file

@ -1373,7 +1373,8 @@ extern void x_scroll_bar_configure (GdkEvent *);
#endif
extern Lisp_Object x_dnd_begin_drag_and_drop (struct frame *, Time, Atom,
bool);
bool, Atom *, const char **,
size_t);
extern void x_set_dnd_targets (Atom *, int);
INLINE int