Improve the documentation of the XDS support

* doc/lispref/frames.texi (Drag and Drop): Rephrase and rearrange
the documentation of XDS support.  Add indexing.  Document
'x-dnd-save-direct' and 'x-dnd-save-direct-immediately'.  Original
patch from Po Lu <luangruo@yahoo.com>.

* lisp/x-dnd.el (x-dnd-types-alist, x-dnd-test-function)
(x-dnd-default-test-function, x-dnd-direct-save-function): Doc
fixes.
(x-dnd-save-direct, x-dnd-save-direct-immediately): Rename the
second argument to FILENAME.  Doc fix.
This commit is contained in:
Eli Zaretskii 2023-04-08 18:36:23 +03:00
parent 14d1c00e80
commit 08cda286c3
2 changed files with 131 additions and 73 deletions

View file

@ -4112,7 +4112,7 @@ has the same meaning as the @var{action} argument to
Emacs implements receiving text and URLs individually for each Emacs implements receiving text and URLs individually for each
window system, and does not by default support receiving other kinds window system, and does not by default support receiving other kinds
of data as drops. To support receiving other kinds of data, use the of data as drops. To support receiving other kinds of data, use the
X-specific interface described below: X-specific interface described below.
@vindex x-dnd-test-function @vindex x-dnd-test-function
@vindex x-dnd-known-types @vindex x-dnd-known-types
@ -4141,29 +4141,71 @@ depending on the specific drag-and-drop protocol being used. For
example, the data type used for plain text may be either example, the data type used for plain text may be either
@code{"STRING"} or @code{"text/plain"}. @code{"STRING"} or @code{"text/plain"}.
@cindex XDS
@cindex direct save protocol
@vindex x-dnd-direct-save-function @vindex x-dnd-direct-save-function
@c FIXME: This description is overly-complicated and confusing. In When Emacs runs on X window system, it supports the X Direct Save
@c particular, the two calls to the function basically sound (@acronym{XDS}) protocol, which allows users to save a file by
@c identical, so it is unclear how should the function distinguish dragging and dropping it onto an Emacs window, such as a Dired window.
@c between the first and the second one. The description of who asks To comply with the unique requirements of @acronym{XDS}, these
@c whom to do what is also very hard to understand. Needs rewording, drag-and-drop requests are processed specially: instead of being
@c and needs shorter sentences. Perhaps examples could help. handled according to @code{x-dnd-types-alist}, they are handled by the
However, @code{x-dnd-types-alist} does not handle a special kind of @dfn{direct-save function} that is the value of the variable
drop sent by a program that wants Emacs to tell it where to save a @code{x-dnd-direct-save-function}. The value should be a function of
file in a specific location determined by the user. These drops are two arguments, @var{need-name} and @var{filename}. The @acronym{XDS}
instead handled by a function that is the value of the variable protocol uses a two-step procedure for dragging files:
@code{x-dnd-direct-save-function}. This function should accept two arguments.
If the first argument is non-@code{nil}, then the second argument is a @enumerate 1
file name to save (with leading directories) that the other @item
program recommends, and the The application from which the file is dragged asks Emacs to provide
function should return the full file name under which it should be the full file name under which to save the file. For this purpose,
saved. After the function completes, Emacs will ask the other program the direct-save function is called with its first argument
to save the file under the name that was returned, and if the file was @var{need-name} non-@code{nil}, and the second argument @var{filename}
successfully saved, call the function again with the first argument set to the basename of the file to be saved. It should return the
set to a non-@code{nil} value and the second argument set to the file fully-expanded absolute file name under which to save the file. For
name that was returned. The function should then perform whatever example, if a file is dragged to a Dired window, the natural directory
action is appropriate (i.e., opening the file or refreshing a for the file is the directory of the file shown at location of the
directory listing.) drop. If saving the file is not possible for some reason, the
function should return @code{nil}, which will cancel the drag-and-drop
operation.
@item
The application from which the file is dragged saves the file under
the name returned by the first call to the direct-save function. If
it succeeds in saving the file, the direct-save function is called
again, this time with the first argument @var{need-name} set to
@code{nil} and the second argument @var{filename} set to the full
absolute name of the saved file. The function is then expected to do
whatever is needed given the fact that file was saved. For example,
Dired should update the directory on display by showing the new file
there.
@end enumerate
The default value of @code{x-dnd-direct-save-function} is
@code{x-dnd-save-direct}.
@defun x-dnd-save-direct need-name filename
When called with the @var{need-name} argument non-@code{nil}, this
function prompts the user for the absolute file name under which it
should be saved. If the specified file already exists, it
additionally asks the user whether to overwrite it, and returns the
absolute file name only if the user confirms the overwriting.
When called with the @var{need-name} argument @code{nil}, it reverts
the Dired listing if the current buffer is in Dired mode or one of its
descendants, and otherwise visits the file by calling @code{find-file}
(@pxref{Visiting Functions}).
@end defun
@defun x-dnd-save-direct-immediately need-name filename
This function works like @code{x-dnd-save-direct}, but when called
with its @var{need-name} argument non-@code{nil}, it doesn't prompt
the user for the full name of the file to be saved; instead, it
returns its argument @var{filename} expanded against the current
buffer's default directory (@pxref{File Name Expansion}). (It still
asks for confirmation if a file by that name already exists in the
default directory.)
@end defun
@cindex initiating drag-and-drop @cindex initiating drag-and-drop
On capable window systems, Emacs also supports dragging contents On capable window systems, Emacs also supports dragging contents

View file

@ -34,20 +34,20 @@
;;; Customizable variables ;;; Customizable variables
(defcustom x-dnd-test-function #'x-dnd-default-test-function (defcustom x-dnd-test-function #'x-dnd-default-test-function
"The function drag and drop uses to determine if to accept or reject a drop. "Function to be used by drag-and-drop to determine whether to accept a drop.
The function takes three arguments, WINDOW, ACTION and TYPES. The function takes three arguments: WINDOW, ACTION, and TYPES.
WINDOW is where the mouse is when the function is called. WINDOW WINDOW is where the window under the mouse is when the function is called.
may be a frame if the mouse isn't over a real window (i.e. menu WINDOW may be a frame if the mouse isn't over a real window (e.g., menu
bar, tool bar or scroll bar). ACTION is the suggested action bar, tool bar, scroll bar, etc.).
from the drag and drop source, one of the symbols move, copy, ACTION is the suggested action from the drag and drop source, one of the
link or ask. TYPES is a vector of available types for the drop. symbols `move', `copy', `link' or `ask'.
TYPES is a vector of available types for the drop.
Each element of TYPE should either be a string (containing the Each element of TYPES should either be a string (containing the
name of the type's X atom), or a symbol, whose name will be used. name of the type's X atom), or a symbol, whose name will be used.
The function shall return nil to reject the drop or a cons with The function shall return nil to reject the drop or a cons with
two values, the wanted action as car and the wanted type as cdr. two values, the wanted action as `car' and the wanted type as `cdr'.
The wanted action can be copy, move, link, ask or private. The wanted action can be `copy', `move', `link', `ask' or `private'.
The default value for this variable is `x-dnd-default-test-function'." The default value for this variable is `x-dnd-default-test-function'."
:version "22.1" :version "22.1"
@ -70,14 +70,18 @@ The default value for this variable is `x-dnd-default-test-function'."
(,(purecopy "DndTypeFile") . x-dnd-handle-offix-file) (,(purecopy "DndTypeFile") . x-dnd-handle-offix-file)
(,(purecopy "DndTypeFiles") . x-dnd-handle-offix-files) (,(purecopy "DndTypeFiles") . x-dnd-handle-offix-files)
(,(purecopy "DndTypeText") . dnd-insert-text)) (,(purecopy "DndTypeText") . dnd-insert-text))
"Which function to call to handle a drop of that type. "Functions to call to handle drag-and-drop of known types.
If the type for the drop is not present, or the function is nil, If the type of the drop is not present in the alist, or the
the drop is rejected. The function takes three arguments, WINDOW, ACTION function corresponding to the type is nil, the drop of that
and DATA. WINDOW is where the drop occurred, ACTION is the action for type will be rejected.
this drop (copy, move, link, private or ask) as determined by a previous
call to `x-dnd-test-function'. DATA is the drop data. Each function takes three arguments: WINDOW, ACTION, and DATA.
The function shall return the action used (copy, move, link or private) WINDOW is the window where the drop occurred.
if drop is successful, nil if not." ACTION is the action for this drop (`copy', `move', `link', `private'
or `ask'), as determined by a previous call to `x-dnd-test-function'.
DATA is the drop data.
The function shall return the action it used (one of the above,
excluding `ask') if drop is successful, nil if not."
:version "22.1" :version "22.1"
:type 'alist :type 'alist
:group 'x) :group 'x)
@ -122,22 +126,27 @@ like xterm) for text."
:group 'x) :group 'x)
(defcustom x-dnd-direct-save-function #'x-dnd-save-direct (defcustom x-dnd-direct-save-function #'x-dnd-save-direct
"Function called when a file is dropped that Emacs must save. "Function called when a file is dropped via XDS protocol.
It is called with two arguments: the first is either nil or t, The value should be a function of two arguments that supports
and the second is a string. the X Direct Save (XDS) protocol. The function will be called
twice during the protocol execution.
If the first argument is t, the second argument is the name the When the function is called with the first argument non-nil,
dropped file should be saved under. The function should return a it should return an absolute file name whose base name is
complete file name describing where the file should be saved. the value of the second argument, a string. The return value
is the file name for the dragged file to be saved. The function
can also return nil if saving the file should be refused for some
reason; in that case the drop will be canceled.
It can also return nil, which means to cancel the drop. When the function is called with the first argument nil, the
second argument specifies the file name where the file was saved;
If the first argument is nil, the second is the name of the file the function should then do whatever is appropriate when such a
that was dropped." file is saved, like show the file in the Dired buffer or visit
the file."
:version "29.1" :version "29.1"
:type '(choice (const :tag "Prompt for name before saving" :type '(choice (const :tag "Prompt for file name to save"
x-dnd-save-direct) x-dnd-save-direct)
(const :tag "Save and open immediately without prompting" (const :tag "Save in `default-directory' without prompting"
x-dnd-save-direct-immediately) x-dnd-save-direct-immediately)
(function :tag "Other function")) (function :tag "Other function"))
:group 'x) :group 'x)
@ -222,14 +231,14 @@ any protocol specific data.")
(cdr (x-dnd-get-state-cons-for-frame frame-or-window))) (cdr (x-dnd-get-state-cons-for-frame frame-or-window)))
(defun x-dnd-default-test-function (_window _action types) (defun x-dnd-default-test-function (_window _action types)
"The default test function for drag and drop. "The default test function for drag-and-drop.
WINDOW is where the mouse is when this function is called. It WINDOW is where the mouse is when this function is called. It
may be a frame if the mouse is over the menu bar, scroll bar or may be a frame if the mouse is over the menu bar, scroll bar or
tool bar. ACTION is the suggested action from the source, and tool bar. ACTION is the suggested action from the source, and
TYPES are the types the drop data can have. This function only TYPES are the types the drop data can have. This function only
accepts drops with types in `x-dnd-known-types'. It always accepts drops with types in `x-dnd-known-types'. It always
returns the action `private', unless `types' contains a value returns the action `private', unless `types' contains a value
inside `x-dnd-copy-types'." inside `x-dnd-copy-types', in which case it may return `copy'."
(let ((type (x-dnd-choose-type types))) (let ((type (x-dnd-choose-type types)))
(when type (let ((list x-dnd-copy-types)) (when type (let ((list x-dnd-copy-types))
(catch 'out (catch 'out
@ -1564,17 +1573,24 @@ was taken, or the direct save failed."
(when (not (equal file-name original-file-name)) (when (not (equal file-name original-file-name))
(delete-file file-name))))) (delete-file file-name)))))
(defun x-dnd-save-direct (need-name name) (defun x-dnd-save-direct (need-name filename)
"Handle dropping a file that should be saved immediately. "Handle dropping a file FILENAME that should be saved first, asking the user.
NEED-NAME tells whether or not the file was not yet saved. NAME NEED-NAME non-nil means the caller requests the full absolute
is either the name of the file, or the name the drop source wants file name of FILENAME under which to save it; FILENAME is just
us to save under. the base name in that case. The function then prompts the user
for where to save to file and returns the result to the caller.
Prompt the user for a file name, then open it." NEED-NAME nil means the file was saved as FILENAME (which should
be the full absolute file name in that case). The function then
refreshes the Dired display, if the current buffer is in Dired
mode, or visits the file otherwise.
This function is intended to be the value of `x-dnd-direct-save-function',
which see."
(if need-name (if need-name
(let ((file-name (read-file-name "Write file: " (let ((file-name (read-file-name "Write file: "
default-directory default-directory
nil nil name))) nil nil filename)))
(when (file-exists-p file-name) (when (file-exists-p file-name)
(unless (y-or-n-p (format-message (unless (y-or-n-p (format-message
"File `%s' exists; overwrite? " file-name)) "File `%s' exists; overwrite? " file-name))
@ -1584,18 +1600,18 @@ Prompt the user for a file name, then open it."
;; interface can be found. ;; interface can be found.
(if (derived-mode-p 'dired-mode) (if (derived-mode-p 'dired-mode)
(revert-buffer) (revert-buffer)
(find-file name)))) (find-file filename))))
(defun x-dnd-save-direct-immediately (need-name name) (defun x-dnd-save-direct-immediately (need-name filename)
"Save and open a dropped file, like `x-dnd-save-direct'. "Handle dropping a file FILENAME that should be saved first.
NEED-NAME tells whether or not the file was not yet saved. NAME Like `x-dnd-save-direct', but do not prompt for the file name;
is either the name of the file, or the name the drop source wants instead, return its absolute file name for saving in the current
us to save under. directory.
Unlike `x-dnd-save-direct', do not prompt for the name by which This function is intended to be the value of `x-dnd-direct-save-function',
to save the file. Simply save it in the current directory." which see."
(if need-name (if need-name
(let ((file-name (expand-file-name name))) (let ((file-name (expand-file-name filename)))
(when (file-exists-p file-name) (when (file-exists-p file-name)
(unless (y-or-n-p (format-message (unless (y-or-n-p (format-message
"File `%s' exists; overwrite? " file-name)) "File `%s' exists; overwrite? " file-name))
@ -1605,7 +1621,7 @@ to save the file. Simply save it in the current directory."
;; interface can be found. ;; interface can be found.
(if (derived-mode-p 'dired-mode) (if (derived-mode-p 'dired-mode)
(revert-buffer) (revert-buffer)
(find-file name)))) (find-file filename))))
(defun x-dnd-handle-octet-stream-for-drop (save-to) (defun x-dnd-handle-octet-stream-for-drop (save-to)
"Save the contents of the XDS selection to SAVE-TO. "Save the contents of the XDS selection to SAVE-TO.