diff options
| author | Po Lu | 2022-03-18 13:17:19 +0800 |
|---|---|---|
| committer | Po Lu | 2022-03-18 13:17:19 +0800 |
| commit | 45609c347e7810b20c54bedc1ce5355182f240e5 (patch) | |
| tree | dacf08d61bf7c25006caa9647ec12f6728e627b9 | |
| parent | e781cbb2d3ec3b4cfd35cd29ccba8e1c265fad4a (diff) | |
| download | emacs-45609c347e7810b20c54bedc1ce5355182f240e5.tar.gz emacs-45609c347e7810b20c54bedc1ce5355182f240e5.zip | |
Allow dragging files from Dired to other programs
* etc/NEWS: Announce new user option `dired-mouse-drag-files'.
* lisp/dired.el (dired-mouse-drag-files): New user option.
(dired-mouse-drag): New command.
(dired-mouse-drag-files-map): New variable.
(dired-insert-set-properties): Add additional keymap if mouse
dragging is enabled.
* lisp/select.el (xselect-convert-to-targets): Handle new
form of selection converters.
(xselect-convert-to-username):
(xselect-convert-to-text-uri-list):
(xselect-uri-list-available-p): New functions.
(selection-converter-alist): Add them as selection converters.
* src/xselect.c (x_get_local_selection): Handle new form of
selection converters.
(syms_of_xselect): Update doc strings.
| -rw-r--r-- | etc/NEWS | 5 | ||||
| -rw-r--r-- | lisp/dired.el | 55 | ||||
| -rw-r--r-- | lisp/select.el | 38 | ||||
| -rw-r--r-- | src/xselect.c | 16 |
4 files changed, 104 insertions, 10 deletions
| @@ -932,6 +932,11 @@ the thumbnail file. | |||
| 932 | 932 | ||
| 933 | ** Dired | 933 | ** Dired |
| 934 | 934 | ||
| 935 | *** New user option 'dired-mouse-drag-files'. | ||
| 936 | If non-nil, dragging filenames with the mouse in a Dired buffer will | ||
| 937 | initiate a drag-and-drop session allowing them to be opened in other | ||
| 938 | programs. | ||
| 939 | |||
| 935 | *** New user option 'dired-free-space'. | 940 | *** New user option 'dired-free-space'. |
| 936 | Dired will now, by default, include the free space in the first line | 941 | Dired will now, by default, include the free space in the first line |
| 937 | instead of having it on a separate line. To get the previous behavior | 942 | instead of having it on a separate line. To get the previous behavior |
diff --git a/lisp/dired.el b/lisp/dired.el index bca30189230..da3c3c80cc1 100644 --- a/lisp/dired.el +++ b/lisp/dired.el | |||
| @@ -248,6 +248,18 @@ The target is used in the prompt for file copy, rename etc." | |||
| 248 | (other :tag "Try to guess" t)) | 248 | (other :tag "Try to guess" t)) |
| 249 | :group 'dired) | 249 | :group 'dired) |
| 250 | 250 | ||
| 251 | (defcustom dired-mouse-drag-files nil | ||
| 252 | "If non-nil, allow the mouse to drag files from inside a Dired buffer. | ||
| 253 | Dragging the mouse and then releasing it over the window of | ||
| 254 | another program will result in that program opening the file, or | ||
| 255 | creating a copy of it . | ||
| 256 | |||
| 257 | If the value is `link', then a symbolic link will be created to | ||
| 258 | the file instead by the other program (usually a file manager)." | ||
| 259 | :type '(choice (const :tag "Don't allow dragging" nil) | ||
| 260 | (const :tag "Copy file to other window" tx) | ||
| 261 | (const :tag "Create symbolic link to file" link))) | ||
| 262 | |||
| 251 | (defcustom dired-copy-preserve-time t | 263 | (defcustom dired-copy-preserve-time t |
| 252 | "If non-nil, Dired preserves the last-modified time in a file copy. | 264 | "If non-nil, Dired preserves the last-modified time in a file copy. |
| 253 | \(This works on only some systems.)" | 265 | \(This works on only some systems.)" |
| @@ -1674,6 +1686,36 @@ see `dired-use-ls-dired' for more details.") | |||
| 1674 | beg)) | 1686 | beg)) |
| 1675 | beg)))) | 1687 | beg)))) |
| 1676 | 1688 | ||
| 1689 | (declare-function x-begin-drag "xfns.cx") | ||
| 1690 | |||
| 1691 | (defun dired-mouse-drag (event) | ||
| 1692 | "Begin a drag-and-drop operation for the file at EVENT. | ||
| 1693 | If we get a mouse motion event right " | ||
| 1694 | (interactive "e") | ||
| 1695 | (save-excursion | ||
| 1696 | (goto-char (posn-point (event-end event))) | ||
| 1697 | (track-mouse | ||
| 1698 | (let ((new-event (read-event))) | ||
| 1699 | (if (not (eq (event-basic-type new-event) 'mouse-movement)) | ||
| 1700 | (push new-event unread-command-events) | ||
| 1701 | ;; We can get an error if there's by some chance no file | ||
| 1702 | ;; name at point. | ||
| 1703 | (condition-case nil | ||
| 1704 | (progn | ||
| 1705 | (gui-backend-set-selection 'XdndSelection | ||
| 1706 | (dired-file-name-at-point)) | ||
| 1707 | (x-begin-drag '("text/uri-list" | ||
| 1708 | "text/x-dnd-username") | ||
| 1709 | (if (eq 'dired-mouse-drag-files 'link) | ||
| 1710 | 'XdndActionLink | ||
| 1711 | 'XdndActionCopy))) | ||
| 1712 | (error (push new-event unread-command-events)))))))) | ||
| 1713 | |||
| 1714 | (defvar dired-mouse-drag-files-map (let ((keymap (make-sparse-keymap))) | ||
| 1715 | (define-key keymap [down-mouse-1] #'dired-mouse-drag) | ||
| 1716 | keymap) | ||
| 1717 | "Keymap applied to file names when `dired-mouse-drag-files' is enabled.") | ||
| 1718 | |||
| 1677 | (defun dired-insert-set-properties (beg end) | 1719 | (defun dired-insert-set-properties (beg end) |
| 1678 | "Add various text properties to the lines in the region, from BEG to END." | 1720 | "Add various text properties to the lines in the region, from BEG to END." |
| 1679 | (save-excursion | 1721 | (save-excursion |
| @@ -1693,10 +1735,15 @@ see `dired-use-ls-dired' for more details.") | |||
| 1693 | (progn | 1735 | (progn |
| 1694 | (dired-move-to-end-of-filename) | 1736 | (dired-move-to-end-of-filename) |
| 1695 | (point)) | 1737 | (point)) |
| 1696 | '(mouse-face | 1738 | (append `(mouse-face |
| 1697 | highlight | 1739 | highlight |
| 1698 | dired-filename t | 1740 | dired-filename t |
| 1699 | help-echo "mouse-2: visit this file in other window")) | 1741 | help-echo ,(if dired-mouse-drag-files |
| 1742 | "down-mouse-1: drag this file to another program | ||
| 1743 | mouse-2: visit this file in other window" | ||
| 1744 | "mouse-2: visit this file in other window")) | ||
| 1745 | (when dired-mouse-drag-files | ||
| 1746 | `(keymap ,dired-mouse-drag-files-map)))) | ||
| 1700 | (when (< (+ (point) 4) (line-end-position)) | 1747 | (when (< (+ (point) 4) (line-end-position)) |
| 1701 | (put-text-property (+ (point) 4) (line-end-position) | 1748 | (put-text-property (+ (point) 4) (line-end-position) |
| 1702 | 'invisible 'dired-hide-details-link)))) | 1749 | 'invisible 'dired-hide-details-link)))) |
diff --git a/lisp/select.el b/lisp/select.el index e9bc5451171..36452776e9a 100644 --- a/lisp/select.el +++ b/lisp/select.el | |||
| @@ -546,16 +546,22 @@ two markers or an overlay. Otherwise, it is nil." | |||
| 546 | (if len | 546 | (if len |
| 547 | (xselect--int-to-cons len)))) | 547 | (xselect--int-to-cons len)))) |
| 548 | 548 | ||
| 549 | (defun xselect-convert-to-targets (_selection _type _value) | 549 | (defun xselect-convert-to-targets (selection _type value) |
| 550 | ;; return a vector of atoms, but remove duplicates first. | 550 | ;; return a vector of atoms, but remove duplicates first. |
| 551 | (let* ((all (cons 'TIMESTAMP | 551 | (let* ((all (cons 'TIMESTAMP |
| 552 | (cons 'MULTIPLE | 552 | (cons 'MULTIPLE |
| 553 | (mapcar 'car selection-converter-alist)))) | 553 | (mapcar (lambda (conv) |
| 554 | (if (or (not (consp (cdr conv))) | ||
| 555 | (funcall (cadr conv) selection | ||
| 556 | (car conv) value)) | ||
| 557 | (car conv) | ||
| 558 | '_EMACS_INTERNAL)) | ||
| 559 | selection-converter-alist)))) | ||
| 554 | (rest all)) | 560 | (rest all)) |
| 555 | (while rest | 561 | (while rest |
| 556 | (cond ((memq (car rest) (cdr rest)) | 562 | (cond ((memq (car rest) (cdr rest)) |
| 557 | (setcdr rest (delq (car rest) (cdr rest)))) | 563 | (setcdr rest (delq (car rest) (cdr rest)))) |
| 558 | ((eq (car (cdr rest)) '_EMACS_INTERNAL) ; shh, it's a secret | 564 | ((eq (car (cdr rest)) '_EMACS_INTERNAL) |
| 559 | (setcdr rest (cdr (cdr rest)))) | 565 | (setcdr rest (cdr (cdr rest)))) |
| 560 | (t | 566 | (t |
| 561 | (setq rest (cdr rest))))) | 567 | (setq rest (cdr rest))))) |
| @@ -632,6 +638,30 @@ This function returns the string \"emacs\"." | |||
| 632 | (when (eq selection 'CLIPBOARD) | 638 | (when (eq selection 'CLIPBOARD) |
| 633 | 'NULL)) | 639 | 'NULL)) |
| 634 | 640 | ||
| 641 | (defun xselect-convert-to-username (_selection _type _value) | ||
| 642 | (user-real-login-name)) | ||
| 643 | |||
| 644 | (defun xselect-convert-to-text-uri-list (_selection _type value) | ||
| 645 | (when (and (stringp value) | ||
| 646 | (file-exists-p value)) | ||
| 647 | (concat (url-encode-url | ||
| 648 | ;; Uncomment the following code code in a better world where | ||
| 649 | ;; people write correct code that adds the hostname to the URI. | ||
| 650 | ;; Since most programs don't implement this properly, we omit the | ||
| 651 | ;; hostname so that copying files actually works. Most properly | ||
| 652 | ;; written programs will look at WM_CLIENT_MACHINE to determine | ||
| 653 | ;; the hostname anyway. (format "file://%s%s\n" (system-name) | ||
| 654 | ;; (expand-file-name value)) | ||
| 655 | (concat "file://" (expand-file-name value))) | ||
| 656 | "\n"))) | ||
| 657 | |||
| 658 | (defun xselect-uri-list-available-p (selection _type value) | ||
| 659 | "Return whether or not `text/uri-list' is a valid target for SELECTION. | ||
| 660 | VALUE is the local selection value of SELECTION." | ||
| 661 | (and (eq selection 'XdndSelection) | ||
| 662 | (stringp value) | ||
| 663 | (file-exists-p value))) | ||
| 664 | |||
| 635 | (setq selection-converter-alist | 665 | (setq selection-converter-alist |
| 636 | '((TEXT . xselect-convert-to-string) | 666 | '((TEXT . xselect-convert-to-string) |
| 637 | (COMPOUND_TEXT . xselect-convert-to-string) | 667 | (COMPOUND_TEXT . xselect-convert-to-string) |
| @@ -639,6 +669,8 @@ This function returns the string \"emacs\"." | |||
| 639 | (UTF8_STRING . xselect-convert-to-string) | 669 | (UTF8_STRING . xselect-convert-to-string) |
| 640 | (text/plain . xselect-convert-to-string) | 670 | (text/plain . xselect-convert-to-string) |
| 641 | (text/plain\;charset=utf-8 . xselect-convert-to-string) | 671 | (text/plain\;charset=utf-8 . xselect-convert-to-string) |
| 672 | (text/uri-list . (xselect-uri-list-available-p . xselect-convert-to-text-uri-list)) | ||
| 673 | (text/x-xdnd-username . xselect-convert-to-username) | ||
| 642 | (TARGETS . xselect-convert-to-targets) | 674 | (TARGETS . xselect-convert-to-targets) |
| 643 | (LENGTH . xselect-convert-to-length) | 675 | (LENGTH . xselect-convert-to-length) |
| 644 | (DELETE . xselect-convert-to-delete) | 676 | (DELETE . xselect-convert-to-delete) |
diff --git a/src/xselect.c b/src/xselect.c index cdc70d3e247..76a2f9f5075 100644 --- a/src/xselect.c +++ b/src/xselect.c | |||
| @@ -386,6 +386,9 @@ x_get_local_selection (Lisp_Object selection_symbol, Lisp_Object target_type, | |||
| 386 | CHECK_SYMBOL (target_type); | 386 | CHECK_SYMBOL (target_type); |
| 387 | handler_fn = Fcdr (Fassq (target_type, Vselection_converter_alist)); | 387 | handler_fn = Fcdr (Fassq (target_type, Vselection_converter_alist)); |
| 388 | 388 | ||
| 389 | if (CONSP (handler_fn)) | ||
| 390 | handler_fn = XCDR (handler_fn); | ||
| 391 | |||
| 389 | if (!NILP (handler_fn)) | 392 | if (!NILP (handler_fn)) |
| 390 | value = call3 (handler_fn, | 393 | value = call3 (handler_fn, |
| 391 | selection_symbol, (local_request ? Qnil : target_type), | 394 | selection_symbol, (local_request ? Qnil : target_type), |
| @@ -2690,11 +2693,18 @@ syms_of_xselect (void) | |||
| 2690 | DEFVAR_LISP ("selection-converter-alist", Vselection_converter_alist, | 2693 | DEFVAR_LISP ("selection-converter-alist", Vselection_converter_alist, |
| 2691 | doc: /* An alist associating X Windows selection-types with functions. | 2694 | doc: /* An alist associating X Windows selection-types with functions. |
| 2692 | These functions are called to convert the selection, with three args: | 2695 | These functions are called to convert the selection, with three args: |
| 2693 | the name of the selection (typically `PRIMARY', `SECONDARY', or `CLIPBOARD'); | 2696 | the name of the selection (typically `PRIMARY', `SECONDARY', or |
| 2694 | a desired type to which the selection should be converted; | 2697 | `CLIPBOARD'); a desired type to which the selection should be |
| 2695 | and the local selection value (whatever was given to | 2698 | converted; and the local selection value (whatever was given to |
| 2696 | `x-own-selection-internal'). | 2699 | `x-own-selection-internal'). |
| 2697 | 2700 | ||
| 2701 | On X Windows, the function can also be a cons of (PREDICATE | ||
| 2702 | . FUNCTION), where PREDICATE determines whether or not the selection | ||
| 2703 | type will appear in the list of selection types available to other | ||
| 2704 | programs, and FUNCTION is the function which is actually called. | ||
| 2705 | PREDICATE is called with the same arguments as FUNCTION, and should | ||
| 2706 | return a non-nil value if the data type is to appear in that list. | ||
| 2707 | |||
| 2698 | The function should return the value to send to the X server | 2708 | The function should return the value to send to the X server |
| 2699 | \(typically a string). A return value of nil | 2709 | \(typically a string). A return value of nil |
| 2700 | means that the conversion could not be done. | 2710 | means that the conversion could not be done. |