summaryrefslogtreecommitdiff
path: root/lisp/dnd.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/dnd.el')
-rw-r--r--lisp/dnd.el131
1 files changed, 119 insertions, 12 deletions
diff --git a/lisp/dnd.el b/lisp/dnd.el
index 89652d32abf..1fc1ab45b84 100644
--- a/lisp/dnd.el
+++ b/lisp/dnd.el
@@ -42,23 +42,25 @@
;;;###autoload
(defcustom dnd-protocol-alist
- `((,(purecopy "^file:///") . dnd-open-local-file) ; XDND format.
- (,(purecopy "^file://") . dnd-open-file) ; URL with host
- (,(purecopy "^file:") . dnd-open-local-file) ; Old KDE, Motif, Sun
- (,(purecopy "^\\(https?\\|ftp\\|file\\|nfs\\)://") . dnd-open-file))
-
+ `((,(purecopy "^file:///") . dnd-open-local-file) ; XDND format.
+ (,(purecopy "^file://[^/]") . dnd-open-file) ; URL with host
+ (,(purecopy "^file:/[^/]") . dnd-open-local-file) ; Old KDE, Motif, Sun
+ (,(purecopy "^file:[^/]") . dnd-open-local-file) ; MS-Windows
+ (,(purecopy "^\\(https?\\|ftp\\|nfs\\)://") . dnd-open-file))
"The functions to call for different protocols when a drop is made.
-This variable is used by `dnd-handle-one-url' and `dnd-handle-file-name'.
+This variable is used by `dnd-handle-multiple-urls'.
The list contains of (REGEXP . FUNCTION) pairs.
The functions shall take two arguments, URL, which is the URL dropped and
ACTION which is the action to be performed for the drop (move, copy, link,
private or ask).
+If a function's `dnd-multiple-handler' property is set, it is provided
+a list of each URI dropped instead.
If no match is found here, and the value of `browse-url-browser-function'
is a pair of (REGEXP . FUNCTION), those regexps are tried for a match.
If no match is found, the URL is inserted as text by calling `dnd-insert-text'.
The function shall return the action done (move, copy, link or private)
if some action was made, or nil if the URL is ignored."
- :version "22.1"
+ :version "30.1"
:type '(repeat (cons (regexp) (function)))
:group 'dnd)
@@ -159,7 +161,10 @@ If no match is found here, `browse-url-handlers' and
`browse-url-default-handlers' are searched for a match.
If no match is found, just call `dnd-insert-text'. WINDOW is
where the drop happened, ACTION is the action for the drop, URL
-is what has been dropped. Returns ACTION."
+is what has been dropped. Returns ACTION.
+
+This function has been obsolete since Emacs 30.1; it has been
+supplanted by `dnd-handle-multiple-urls'."
(let (ret)
(or
(catch 'done
@@ -180,6 +185,91 @@ is what has been dropped. Returns ACTION."
(setq ret 'private)))
ret))
+(make-obsolete 'dnd-handle-one-url 'dnd-handle-multiple-urls "30.1")
+
+(defun dnd-handle-multiple-urls (window urls action)
+ "Select a handler for, then open, each element of URLS.
+The argument ACTION is the action which must be taken, much as
+that to `dnd-begin-file-drag'.
+
+Assign and give each URL to one of the \"DND handler\" functions
+listed in the variable `dnd-protocol-alist'. When multiple
+handlers matching the same subset of URLs exist, give precedence
+to the handler assigned the greatest number of URLs.
+
+If a handler is a symbol with the property
+`dnd-multiple-handler', call it with ACTION and a list of every
+URL it is assigned. Otherwise, call it once for each URL
+assigned with ACTION and the URL in question.
+
+Subsequently open URLs that don't match any handlers opened with
+any handler selected by `browse-url-select-handler', and failing
+even that, insert them with `dnd-insert-text'.
+
+Return a symbol designating the actions taken by each DND handler
+called. If all DND handlers called return the same symbol,
+return that symbol; otherwise, or if no DND handlers are called,
+return `private'.
+
+Do not rely on the contents of URLS after calling this function,
+for it will be modified."
+ (let ((list nil) (return-value nil))
+ (with-selected-window window
+ (dolist (handler dnd-protocol-alist)
+ (let ((pattern (car handler))
+ (handler (cdr handler)))
+ (dolist (uri urls)
+ (when (string-match pattern uri)
+ (let ((cell (or (cdr (assq handler list))
+ (let ((cell (cons handler nil)))
+ (push cell list)
+ cell))))
+ (unless (memq uri cell)
+ (setcdr cell (cons uri (cdr cell)))))))))
+ (setq list (nreverse list))
+ ;; While unassessed handlers still exist...
+ (while list
+ ;; Sort list by the number of URLs assigned to each handler.
+ (setq list (sort list (lambda (first second)
+ (> (length (cdr first))
+ (length (cdr second))))))
+ ;; Call the handler in its car before removing each URL from
+ ;; URLs.
+ (let ((handler (caar list))
+ (entry-urls (cdar list)))
+ (setq list (cdr list))
+ (when entry-urls
+ (if (and (symbolp handler)
+ (get handler 'dnd-multiple-handler))
+ (progn
+ (let ((value (funcall handler entry-urls action)))
+ (if (or (not return-value)
+ (eq return-value value))
+ (setq return-value value)
+ (setq return-value 'private)))
+ (dolist (url entry-urls)
+ (setq urls (delq url urls))
+ ;; And each handler-URL list after this.
+ (dolist (item list)
+ (setcdr item (delq url (cdr item))))))
+ (dolist (url entry-urls)
+ (let ((value (funcall handler url action)))
+ (if (or (not return-value) (eq return-value value))
+ (setq return-value value)
+ (setq return-value 'private)))
+ (setq urls (delq url urls))
+ ;; And each handler-URL list after this.
+ (dolist (item list)
+ (setcdr item (delq url (cdr item)))))))))
+ ;; URLS should now incorporate only those which haven't been
+ ;; assigned their own handlers.
+ (dolist (leftover urls)
+ (setq return-value 'private)
+ (if-let ((handler (browse-url-select-handler leftover
+ 'internal)))
+ (funcall handler leftover action)
+ (dnd-insert-text window action leftover)))
+ (or return-value 'private))))
(defun dnd-get-local-file-uri (uri)
"Return an uri converted to file:/// syntax if uri is a local file.
@@ -201,6 +291,11 @@ Return nil if URI is not a local file."
(string-equal sysname-no-dot hostname)))
(concat "file://" (substring uri (+ 7 (length hostname))))))))
+(defvar dnd-unescape-file-uris t
+ "Whether to unescape file: URIs before they are opened.
+Bind this to nil when providing `dnd-get-local-file-name' with a
+file name that may incorporate URI escape sequences.")
+
(defun dnd--unescape-uri (uri)
;; Merge with corresponding code in URL library.
(replace-regexp-in-string
@@ -226,7 +321,10 @@ Return nil if URI is not a local file."
'utf-8
(or file-name-coding-system
default-file-name-coding-system))))
- (and f (setq f (decode-coding-string (dnd--unescape-uri f) coding)))
+ (and f (setq f (decode-coding-string
+ (if dnd-unescape-file-uris
+ (dnd--unescape-uri f) f)
+ coding)))
(when (and f must-exist (not (file-readable-p f)))
(setq f nil))
f))
@@ -355,7 +453,10 @@ on FRAME itself.
This function might return immediately if no mouse buttons are
currently being held down. It should only be called upon a
-`down-mouse-1' (or similar) event."
+`down-mouse-1' (or similar) event.
+
+This function is only supported on X Windows, macOS/GNUstep, and Haiku;
+on all other platforms it will signal an error."
(unless (fboundp 'x-begin-drag)
(error "Dragging text from Emacs is not supported by this window system"))
(gui-set-selection 'XdndSelection text)
@@ -415,7 +516,10 @@ nil, any drops on FRAME itself will be ignored.
This function might return immediately if no mouse buttons are
currently being held down. It should only be called upon a
-`down-mouse-1' (or similar) event."
+`down-mouse-1' (or similar) event.
+
+This function is only supported on X Windows, macOS/GNUstep, and Haiku;
+on all other platforms it will signal an error."
(unless (fboundp 'x-begin-drag)
(error "Dragging files from Emacs is not supported by this window system"))
(dnd-remove-last-dragged-remote-file)
@@ -482,7 +586,10 @@ FRAME, ACTION and ALLOW-SAME-FRAME mean the same as in
FILES is a list of files that will be dragged. If the drop
target doesn't support dropping multiple files, the first file in
-FILES will be dragged."
+FILES will be dragged.
+
+This function is only supported on X Windows, macOS/GNUstep, and Haiku;
+on all other platforms it will signal an error."
(unless (fboundp 'x-begin-drag)
(error "Dragging files from Emacs is not supported by this window system"))
(dnd-remove-last-dragged-remote-file)