aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPo Lu2022-03-18 13:17:19 +0800
committerPo Lu2022-03-18 13:17:19 +0800
commit45609c347e7810b20c54bedc1ce5355182f240e5 (patch)
treedacf08d61bf7c25006caa9647ec12f6728e627b9
parente781cbb2d3ec3b4cfd35cd29ccba8e1c265fad4a (diff)
downloademacs-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/NEWS5
-rw-r--r--lisp/dired.el55
-rw-r--r--lisp/select.el38
-rw-r--r--src/xselect.c16
4 files changed, 104 insertions, 10 deletions
diff --git a/etc/NEWS b/etc/NEWS
index f4d8756950b..e2546bb3ca5 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -932,6 +932,11 @@ the thumbnail file.
932 932
933** Dired 933** Dired
934 934
935*** New user option 'dired-mouse-drag-files'.
936If non-nil, dragging filenames with the mouse in a Dired buffer will
937initiate a drag-and-drop session allowing them to be opened in other
938programs.
939
935*** New user option 'dired-free-space'. 940*** New user option 'dired-free-space'.
936Dired will now, by default, include the free space in the first line 941Dired will now, by default, include the free space in the first line
937instead of having it on a separate line. To get the previous behavior 942instead 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.
253Dragging the mouse and then releasing it over the window of
254another program will result in that program opening the file, or
255creating a copy of it .
256
257If the value is `link', then a symbolic link will be created to
258the 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.
1693If 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
1743mouse-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.
660VALUE 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.
2692These functions are called to convert the selection, with three args: 2695These functions are called to convert the selection, with three args:
2693the name of the selection (typically `PRIMARY', `SECONDARY', or `CLIPBOARD'); 2696the name of the selection (typically `PRIMARY', `SECONDARY', or
2694a desired type to which the selection should be converted; 2697`CLIPBOARD'); a desired type to which the selection should be
2695and the local selection value (whatever was given to 2698converted; and the local selection value (whatever was given to
2696`x-own-selection-internal'). 2699`x-own-selection-internal').
2697 2700
2701On X Windows, the function can also be a cons of (PREDICATE
2702. FUNCTION), where PREDICATE determines whether or not the selection
2703type will appear in the list of selection types available to other
2704programs, and FUNCTION is the function which is actually called.
2705PREDICATE is called with the same arguments as FUNCTION, and should
2706return a non-nil value if the data type is to appear in that list.
2707
2698The function should return the value to send to the X server 2708The 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
2700means that the conversion could not be done. 2710means that the conversion could not be done.