From: Po Lu Date: Thu, 24 Mar 2022 01:42:47 +0000 (+0800) Subject: Allow dragging and dropping multiple actions X-Git-Tag: emacs-29.0.90~1931^2~952 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=17393c0db0fbd4ba9b7ddcdc668974ef8a65107d;p=emacs.git 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. --- diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index 9717fa29786..a031b25e474 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -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 diff --git a/lisp/term/haiku-win.el b/lisp/term/haiku-win.el index 8ec959a7584..1433620875b 100644 --- a/lisp/term/haiku-win.el +++ b/lisp/term/haiku-win.el @@ -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)))) diff --git a/src/xfns.c b/src/xfns.c index eae409eed24..c4b924e007f 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -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; } diff --git a/src/xterm.c b/src/xterm.c index e4c17644e42..0543f152eda 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -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); diff --git a/src/xterm.h b/src/xterm.h index 2a11f87e167..4a71968b047 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -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