summaryrefslogtreecommitdiff
path: root/lisp/select.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/select.el')
-rw-r--r--lisp/select.el481
1 files changed, 385 insertions, 96 deletions
diff --git a/lisp/select.el b/lisp/select.el
index d9efe811a07..2d501f207f1 100644
--- a/lisp/select.el
+++ b/lisp/select.el
@@ -25,9 +25,10 @@
;; Based partially on earlier release by Lucid.
;; The functionality here is divided in two parts:
-;; - Low-level: gui-get-selection, gui-set-selection, gui-selection-owner-p,
-;; gui-selection-exists-p are the backend-dependent functions meant to access
-;; various kinds of selections (CLIPBOARD, PRIMARY, SECONDARY).
+;; - Low-level: gui-backend-get-selection, gui-backend-set-selection,
+;; gui-backend-selection-owner-p, gui-backend-selection-exists-p are
+;; the backend-dependent functions meant to access various kinds of
+;; selections (CLIPBOARD, PRIMARY, SECONDARY).
;; - Higher-level: gui-select-text and gui-selection-value go together to
;; access the general notion of "GUI selection" for interoperation with other
;; applications. This can use either the clipboard or the primary selection,
@@ -108,56 +109,117 @@ E.g. it doesn't exist under MS-Windows."
:group 'killing
:version "25.1")
-;; We keep track of the last text selected here, so we can check the
-;; current selection against it, and avoid passing back our own text
-;; from gui-selection-value. We track both
-;; separately in case another X application only sets one of them
-;; we aren't fooled by the PRIMARY or CLIPBOARD selection staying the same.
+;; We keep track of the last selection here, so we can check the
+;; current selection against it, and avoid passing back with
+;; gui-selection-value the same text we previously killed or
+;; yanked. We track both separately in case another X application only
+;; sets one of them we aren't fooled by the PRIMARY or CLIPBOARD
+;; selection staying the same.
(defvar gui--last-selected-text-clipboard nil
"The value of the CLIPBOARD selection last seen.")
+
(defvar gui--last-selected-text-primary nil
"The value of the PRIMARY selection last seen.")
+(defvar gui--last-selection-timestamp-clipboard nil
+ "The timestamp of the CLIPBOARD selection last seen.")
+
+(defvar gui--last-selection-timestamp-primary nil
+ "The timestamp of the PRIMARY selection last seen.")
+
+(defvar gui-last-cut-in-clipboard nil
+ "Whether or not the last call to `interprogram-cut-function' owned CLIPBOARD.")
+
+(defvar gui-last-cut-in-primary nil
+ "Whether or not the last call to `interprogram-cut-function' owned PRIMARY.")
+
+(defun gui--set-last-clipboard-selection (text)
+ "Save last clipboard selection.
+Save the selected text, passed as argument, and for window
+systems that support it, save the selection timestamp too."
+ (setq gui--last-selected-text-clipboard text)
+ (when (eq window-system 'x)
+ (setq gui--last-selection-timestamp-clipboard
+ (gui-backend-get-selection 'CLIPBOARD 'TIMESTAMP))))
+
+(defun gui--set-last-primary-selection (text)
+ "Save last primary selection.
+Save the selected text, passed as argument, and for window
+systems that support it, save the selection timestamp too."
+ (setq gui--last-selected-text-primary text)
+ (when (eq window-system 'x)
+ (setq gui--last-selection-timestamp-primary
+ (gui-backend-get-selection 'PRIMARY 'TIMESTAMP))))
+
+(defun gui--clipboard-selection-unchanged-p (text)
+ "Check whether the clipboard selection has changed.
+Compare the selection text, passed as argument, with the text
+from the last saved selection. For window systems that support
+it, compare the selection timestamp too."
+ (and
+ (equal text gui--last-selected-text-clipboard)
+ (or (not (eq window-system 'x))
+ (eq gui--last-selection-timestamp-clipboard
+ (gui-backend-get-selection 'CLIPBOARD 'TIMESTAMP)))))
+
+(defun gui--primary-selection-unchanged-p (text)
+ "Check whether the primary selection has changed.
+Compare the selection text, passed as argument, with the text
+from the last saved selection. For window systems that support
+it, compare the selection timestamp too."
+ (and
+ (equal text gui--last-selected-text-primary)
+ (or (not (eq window-system 'x))
+ (eq gui--last-selection-timestamp-primary
+ (gui-backend-get-selection 'PRIMARY 'TIMESTAMP)))))
+
+
(defun gui-select-text (text)
"Select TEXT, a string, according to the window system.
-if `select-enable-clipboard' is non-nil, copy TEXT to the system's clipboard.
+If `select-enable-clipboard' is non-nil, copy TEXT to the system's clipboard.
If `select-enable-primary' is non-nil, put TEXT in the primary selection.
MS-Windows does not have a \"primary\" selection."
(when select-enable-primary
(gui-set-selection 'PRIMARY text)
- (setq gui--last-selected-text-primary text))
+ (gui--set-last-primary-selection text))
(when select-enable-clipboard
;; When cutting, the selection is cleared and PRIMARY
;; set to the empty string. Prevent that, PRIMARY
;; should not be reset by cut (Bug#16382).
(setq saved-region-selection text)
(gui-set-selection 'CLIPBOARD text)
- (setq gui--last-selected-text-clipboard text)))
+ (gui--set-last-clipboard-selection text))
+ ;; Record which selections we now have ownership over.
+ (setq gui-last-cut-in-clipboard select-enable-clipboard
+ gui-last-cut-in-primary select-enable-primary))
(define-obsolete-function-alias 'x-select-text 'gui-select-text "25.1")
(defcustom x-select-request-type nil
"Data type request for X selection.
The value is one of the following data types, a list of them, or nil:
- `COMPOUND_TEXT', `UTF8_STRING', `STRING', `TEXT'
+ `COMPOUND_TEXT', `UTF8_STRING', `STRING', `TEXT', `text/plain\\;charset=utf-8'
If the value is one of the above symbols, try only the specified type.
If the value is a list of them, try each of them in the specified
order until succeed.
-The value nil is the same as the list (UTF8_STRING COMPOUND_TEXT STRING)."
+The value nil is the same as the list (UTF8_STRING COMPOUND_TEXT STRING
+text/plain\\;charset=utf-8)."
:type '(choice (const :tag "Default" nil)
(const COMPOUND_TEXT)
(const UTF8_STRING)
(const STRING)
(const TEXT)
+ (const text/plain\;charset=utf-8)
(set :tag "List of values"
(const COMPOUND_TEXT)
(const UTF8_STRING)
(const STRING)
- (const TEXT)))
+ (const TEXT)
+ (const text/plain\;charset=utf-8)))
:group 'killing)
(defun gui--selection-value-internal (type)
@@ -165,20 +227,29 @@ The value nil is the same as the list (UTF8_STRING COMPOUND_TEXT STRING)."
Call `gui-get-selection' with an appropriate DATA-TYPE argument
decided by `x-select-request-type'. The return value is already
decoded. If `gui-get-selection' signals an error, return nil."
- (let ((request-type (if (eq window-system 'x)
- (or x-select-request-type
- '(UTF8_STRING COMPOUND_TEXT STRING))
- 'STRING))
- text)
- (with-demoted-errors "gui-get-selection: %S"
- (if (consp request-type)
- (while (and request-type (not text))
- (setq text (gui-get-selection type (car request-type)))
- (setq request-type (cdr request-type)))
- (setq text (gui-get-selection type request-type))))
- (if text
- (remove-text-properties 0 (length text) '(foreign-selection nil) text))
- text))
+ ;; The doc string of `interprogram-paste-function' says to return
+ ;; nil if no other program has provided text to paste.
+ (unless (and gui-last-cut-in-clipboard
+ ;; `gui-backend-selection-owner-p' might be unreliable on
+ ;; some other window systems.
+ (memq window-system '(x haiku))
+ (eq type 'CLIPBOARD)
+ ;; Should we unify this with gui--clipboard-selection-unchanged-p?
+ (gui-backend-selection-owner-p type))
+ (let ((request-type (if (memq window-system '(x pgtk haiku))
+ (or x-select-request-type
+ '(UTF8_STRING COMPOUND_TEXT STRING text/plain\;charset=utf-8))
+ 'STRING))
+ text)
+ (with-demoted-errors "gui-get-selection: %S"
+ (if (consp request-type)
+ (while (and request-type (not text))
+ (setq text (gui-get-selection type (car request-type)))
+ (setq request-type (cdr request-type)))
+ (setq text (gui-get-selection type request-type))))
+ (if text
+ (remove-text-properties 0 (length text) '(foreign-selection nil) text))
+ text)))
(defun gui-selection-value ()
(let ((clip-text
@@ -186,19 +257,25 @@ decoded. If `gui-get-selection' signals an error, return nil."
(let ((text (gui--selection-value-internal 'CLIPBOARD)))
(when (string= text "")
(setq text nil))
- ;; When `select-enable-clipboard' is non-nil,
- ;; killing/copying text (with, say, `C-w') will push the
- ;; text to the clipboard (and store it in
- ;; `gui--last-selected-text-clipboard'). We check
- ;; whether the text on the clipboard is identical to this
- ;; text, and if so, we report that the clipboard is
- ;; empty. See (bug#27442) for further discussion about
- ;; this DWIM action, and possible ways to make this check
- ;; less fragile, if so desired.
- (prog1
- (unless (equal text gui--last-selected-text-clipboard)
- text)
- (setq gui--last-selected-text-clipboard text)))))
+ ;; Check the CLIPBOARD selection for 'newness', i.e.,
+ ;; whether it is different from the last time we did a
+ ;; yank operation or whether it was set by Emacs itself
+ ;; with a kill operation, since in both cases the text
+ ;; will already be in the kill ring. See (bug#27442) and
+ ;; (bug#53894) for further discussion about this DWIM
+ ;; action, and possible ways to make this check less
+ ;; fragile, if so desired.
+
+ ;; Don't check the "newness" of CLIPBOARD if the last
+ ;; call to `gui-select-text' didn't cause us to become
+ ;; its owner. This lets the user yank text killed by
+ ;; `clipboard-kill-region' with `clipboard-yank' without
+ ;; interference from text killed by other means when
+ ;; `select-enable-clipboard' is nil.
+ (unless (and gui-last-cut-in-clipboard
+ (gui--clipboard-selection-unchanged-p text))
+ (gui--set-last-clipboard-selection text)
+ text))))
(primary-text
(when select-enable-primary
(let ((text (gui--selection-value-internal 'PRIMARY)))
@@ -206,10 +283,10 @@ decoded. If `gui-get-selection' signals an error, return nil."
;; Check the PRIMARY selection for 'newness', is it different
;; from what we remembered them to be last time we did a
;; cut/paste operation.
- (prog1
- (unless (equal text gui--last-selected-text-primary)
- text)
- (setq gui--last-selected-text-primary text))))))
+ (unless (and gui-last-cut-in-primary
+ (gui--primary-selection-unchanged-p text))
+ (gui--set-last-primary-selection text)
+ text)))))
;; As we have done one selection, clear this now.
(setq next-selection-coding-system nil)
@@ -224,11 +301,11 @@ decoded. If `gui-get-selection' signals an error, return nil."
;; something like the following has happened since the last time
;; we looked at the selections: Application X set all the
;; selections, then Application Y set only one of them.
- ;; In this case since we don't have
- ;; timestamps there is no way to know what the 'correct' value to
- ;; return is. The nice thing to do would be to tell the user we
- ;; saw multiple possible selections and ask the user which was the
- ;; one they wanted.
+ ;; In this case, for systems that support selection timestamps, we
+ ;; could return the newer. For systems that don't, there is no
+ ;; way to know what the 'correct' value to return is. The nice
+ ;; thing to do would be to tell the user we saw multiple possible
+ ;; selections and ask the user which was the one they wanted.
(or clip-text primary-text)
))
@@ -304,22 +381,33 @@ the formats available in the clipboard if TYPE is `CLIPBOARD'."
(let ((data (gui-backend-get-selection (or type 'PRIMARY)
(or data-type 'STRING))))
(when (and (stringp data)
- (setq data-type (get-text-property 0 'foreign-selection data)))
+ ;; If this text property is set, then the data needs to
+ ;; be decoded -- otherwise it has already been decoded
+ ;; by the lower level functions.
+ (get-text-property 0 'foreign-selection data))
(let ((coding (or next-selection-coding-system
selection-coding-system
(pcase data-type
('UTF8_STRING 'utf-8)
+ ('text/plain\;charset=utf-8 'utf-8)
('COMPOUND_TEXT 'compound-text-with-extensions)
('C_STRING nil)
- ('STRING 'iso-8859-1)
- (_ (error "Unknown selection data type: %S"
- type))))))
- (setq data (if coding (decode-coding-string data coding)
- ;; This is for C_STRING case.
+ ('STRING 'iso-8859-1)))))
+ (setq data
+ (cond (coding (decode-coding-string data coding))
;; We want to convert each non-ASCII byte to the
;; corresponding eight-bit character, which has
;; a codepoint >= #x3FFF00.
- (string-to-multibyte data))))
+ ((eq data-type 'C_STRING)
+ (string-to-multibyte data))
+ ;; Guess at the charset for types like text/html
+ ;; -- it can be anything, and different
+ ;; applications use different encodings.
+ ((string-match-p "\\`text/" (symbol-name data-type))
+ (decode-coding-string
+ data (car (detect-coding-string data))))
+ ;; Do nothing.
+ (t data))))
(setq next-selection-coding-system nil)
(put-text-property 0 (length data) 'foreign-selection data-type data))
data))
@@ -328,16 +416,21 @@ the formats available in the clipboard if TYPE is `CLIPBOARD'."
(defun gui-set-selection (type data)
"Make an X selection of type TYPE and value DATA.
The argument TYPE (nil means `PRIMARY') says which selection, and
-DATA specifies the contents. TYPE must be a symbol. \(It can also
-be a string, which stands for the symbol with that name, but this
-is considered obsolete.) DATA may be a string, a symbol, an
-integer (or a cons of two integers or list of two integers).
-
-The selection may also be a cons of two markers pointing to the same buffer,
-or an overlay. In these cases, the selection is considered to be the text
-between the markers *at whatever time the selection is examined*.
-Thus, editing done in the buffer after you specify the selection
-can alter the effective value of the selection.
+DATA specifies the contents. TYPE must be a symbol. \(It can
+also be a string, which stands for the symbol with that name, but
+this is considered obsolete.) DATA may be a string, a symbol, or
+an integer.
+
+The selection may also be a cons of two markers pointing to the
+same buffer, or an overlay. In these cases, the selection is
+considered to be the text between the markers *at whatever time
+the selection is examined*. Thus, editing done in the buffer
+after you specify the selection can alter the effective value of
+the selection. If DATA is a string, then its text properties can
+specify alternative values for different data types. For
+example, the value of any property named `text/uri-list' will be
+used instead of DATA itself when another program converts TYPE to
+the target `text/uri-list'.
The data may also be a vector of valid non-vector selection values.
@@ -382,6 +475,73 @@ are not available to other programs."
(symbolp data)
(integerp data)))
+
+;; Minor mode to make losing ownership of PRIMARY behave more like
+;; other X programs.
+
+(defvar lost-selection-last-region-buffer nil
+ "The last buffer from which the region was selected.")
+
+(defun lost-selection-post-select-region-function (_text)
+ "Handle the region being selected into PRIMARY.
+If the current buffer is different from the last buffer,
+deactivate the mark in every other buffer.
+TEXT is ignored."
+ (when (not (eq lost-selection-last-region-buffer
+ (current-buffer)))
+ (dolist (buffer (buffer-list))
+ (unless (or (string-match-p "^ "
+ (buffer-name buffer))
+ (eq buffer (current-buffer)))
+ (with-current-buffer buffer
+ (deactivate-mark t))))
+ (setq lost-selection-last-region-buffer (current-buffer))))
+
+(defun lost-selection-function (selection)
+ "Handle losing of ownership of SELECTION.
+If SELECTION is `PRIMARY', deactivate the mark in every
+non-temporary buffer."
+ (let ((select-active-regions nil))
+ (when (eq selection 'PRIMARY)
+ (dolist (buffer (buffer-list))
+ (unless (string-match-p "^ "
+ (buffer-name buffer))
+ (with-current-buffer buffer
+ (deactivate-mark t)))))))
+
+(define-minor-mode lost-selection-mode
+ "Toggle `lost-selection-mode'.
+
+When this is enabled, selecting some text in another program will
+cause the mark to be deactivated in all buffers, mimicking the
+behavior of most X Windows programs.
+
+Selecting text in a buffer that ends up changing the primary
+selection will also cause the mark to be deactivated in all other
+buffers."
+ :global t
+ :group 'x
+ (if lost-selection-mode
+ (progn
+ (cond ((featurep 'x) (add-hook 'x-lost-selection-functions
+ #'lost-selection-function))
+ ((featurep 'pgtk) (add-hook 'pgtk-lost-selection-functions
+ #'lost-selection-function))
+ ((featurep 'haiku) (add-hook 'haiku-lost-selection-functions
+ #'lost-selection-function)))
+ (add-hook 'post-select-region-hook
+ #'lost-selection-post-select-region-function))
+ (cond ((featurep 'x) (remove-hook 'x-lost-selection-functions
+ #'lost-selection-function))
+ ((featurep 'pgtk) (remove-hook 'pgtk-lost-selection-functions
+ #'lost-selection-function))
+ ((featurep 'haiku) (remove-hook 'haiku-lost-selection-functions
+ #'lost-selection-function)))
+ (remove-hook 'post-select-region-hook
+ #'lost-selection-post-select-region-function)
+ (setq lost-selection-last-region-buffer nil)))
+
+
;; Functions to convert the selection into various other selection types.
;; Every selection type that Emacs handles is implemented this way, except
;; for TIMESTAMP, which is a special case.
@@ -413,7 +573,8 @@ two markers or an overlay. Otherwise, it is nil."
(defun xselect--int-to-cons (n)
(cons (ash n -16) (logand n 65535)))
-(defun xselect--encode-string (type str &optional can-modify)
+(defun xselect--encode-string (type str &optional can-modify
+ prefer-string-to-c-string)
(when str
;; If TYPE is nil, this is a local request; return STR as-is.
(if (null type)
@@ -440,13 +601,13 @@ two markers or an overlay. Otherwise, it is nil."
(setq type 'C_STRING))
(t
(let (non-latin-1 non-unicode eight-bit)
- (mapc #'(lambda (x)
- (if (>= x #x100)
- (if (< x #x110000)
- (setq non-latin-1 t)
- (if (< x #x3FFF80)
- (setq non-unicode t)
- (setq eight-bit t)))))
+ (mapc (lambda (x)
+ (if (>= x #x100)
+ (if (< x #x110000)
+ (setq non-latin-1 t)
+ (if (< x #x3FFF80)
+ (setq non-unicode t)
+ (setq eight-bit t)))))
str)
(setq type (if (or non-unicode
(and
@@ -463,7 +624,8 @@ two markers or an overlay. Otherwise, it is nil."
(if eight-bit 'C_STRING
'STRING))))))))
(cond
- ((eq type 'UTF8_STRING)
+ ((or (eq type 'UTF8_STRING)
+ (eq type 'text/plain\;charset=utf-8))
(if (or (not coding)
(not (eq (coding-system-type coding) 'utf-8)))
(setq coding 'utf-8))
@@ -475,6 +637,12 @@ two markers or an overlay. Otherwise, it is nil."
(setq coding 'iso-8859-1))
(setq str (encode-coding-string str coding)))
+ ((eq type 'text/plain)
+ (if (or (not coding)
+ (not (eq (coding-system-type coding) 'charset)))
+ (setq coding 'ascii))
+ (setq str (encode-coding-string str coding)))
+
((eq type 'COMPOUND_TEXT)
(if (or (not coding)
(not (eq (coding-system-type coding) 'iso-2022)))
@@ -499,7 +667,10 @@ two markers or an overlay. Otherwise, it is nil."
(setq str (string-replace "\0" "\\0" str))
(setq next-selection-coding-system nil)
- (cons type str))))
+ (cons (if (and prefer-string-to-c-string
+ (eq type 'C_STRING))
+ 'STRING type)
+ str))))
(defun xselect-convert-to-string (_selection type value)
(let ((str (cond ((stringp value) value)
@@ -517,31 +688,61 @@ two markers or an overlay. Otherwise, it is nil."
(if len
(xselect--int-to-cons len))))
-(defun xselect-convert-to-targets (_selection _type _value)
- ;; return a vector of atoms, but remove duplicates first.
- (let* ((all (cons 'TIMESTAMP
- (cons 'MULTIPLE
- (mapcar 'car selection-converter-alist))))
- (rest all))
- (while rest
- (cond ((memq (car rest) (cdr rest))
- (setcdr rest (delq (car rest) (cdr rest))))
- ((eq (car (cdr rest)) '_EMACS_INTERNAL) ; shh, it's a secret
- (setcdr rest (cdr (cdr rest))))
- (t
- (setq rest (cdr rest)))))
- (apply 'vector all)))
+(defvar x-dnd-targets-list)
+
+(defun xselect-convert-to-targets (selection _type value)
+ ;; Return a vector of atoms, but remove duplicates first.
+ (if (eq selection 'XdndSelection)
+ ;; This isn't required by the XDND protocol, and sure enough no
+ ;; clients seem to dependent on it, but Emacs implements the
+ ;; receiver side of the Motif drop protocol by looking at the
+ ;; initiator selection's TARGETS target (which Motif provides)
+ ;; instead of the target table on the drag window, so it seems
+ ;; plausible for other clients to rely on that as well.
+ (apply #'vector (mapcar #'intern x-dnd-targets-list))
+ (apply #'vector
+ (delete-dups
+ `( TIMESTAMP MULTIPLE
+ . ,(delq '_EMACS_INTERNAL
+ (mapcar (lambda (conv)
+ (if (or (not (consp (cdr conv)))
+ (funcall (cadr conv) selection
+ (car conv) value))
+ (car conv)
+ '_EMACS_INTERNAL))
+ selection-converter-alist)))))))
(defun xselect-convert-to-delete (selection _type _value)
- (gui-backend-set-selection selection nil)
+ ;; This should be handled by the caller of `x-begin-drag'.
+ (unless (eq selection 'XdndSelection)
+ (gui-backend-set-selection selection nil))
;; A return value of nil means that we do not know how to do this conversion,
;; and replies with an "error". A return value of NULL means that we have
;; done the conversion (and any side-effects) but have no value to return.
'NULL)
-(defun xselect-convert-to-filename (_selection _type value)
- (when (setq value (xselect--selection-bounds value))
- (xselect--encode-string 'TEXT (buffer-file-name (nth 2 value)))))
+(defun xselect-convert-to-filename (selection _type value)
+ (if (not (eq selection 'XdndSelection))
+ (when (setq value (xselect--selection-bounds value))
+ (xselect--encode-string 'TEXT (buffer-file-name (nth 2 value))))
+ (if (and (stringp value)
+ (file-exists-p value))
+ ;; Motif expects this to be STRING, but it treats the data as
+ ;; a sequence of bytes instead of a Latin-1 string.
+ (cons 'STRING (encode-coding-string (expand-file-name value)
+ (or file-name-coding-system
+ default-file-name-coding-system)))
+ (when (vectorp value)
+ (with-temp-buffer
+ (cl-loop for file across value
+ do (insert (expand-file-name file) "\0"))
+ ;; Get rid of the last NULL byte.
+ (when (> (point) 1)
+ (delete-char -1))
+ ;; Motif wants STRING.
+ (cons 'STRING (encode-coding-string (buffer-string)
+ (or file-name-coding-system
+ default-file-name-coding-system))))))))
(defun xselect-convert-to-charpos (_selection _type value)
(when (setq value (xselect--selection-bounds value))
@@ -603,11 +804,95 @@ This function returns the string \"emacs\"."
(when (eq selection 'CLIPBOARD)
'NULL))
+(defun xselect-convert-to-username (_selection _type _value)
+ (user-real-login-name))
+
+(defun xselect-convert-to-text-uri-list (_selection _type value)
+ (let ((string
+ (if (stringp value)
+ (xselect--encode-string 'TEXT
+ (concat (url-encode-url value) "\n"))
+ (when (vectorp value)
+ (with-temp-buffer
+ (cl-loop for tem across value
+ do (progn
+ (insert (url-encode-url tem))
+ (insert "\n")))
+ (xselect--encode-string 'TEXT (buffer-string)))))))
+ (cons 'text/uri-list (cdr string))))
+
+(defun xselect-convert-to-xm-file (selection _type value)
+ (when (and (stringp value)
+ (file-exists-p value)
+ (eq selection 'XdndSelection))
+ (xselect--encode-string 'C_STRING
+ (concat value [0]))))
+
+(defun xselect-uri-list-available-p (selection _type value)
+ "Return whether or not `text/uri-list' is a valid target for SELECTION.
+VALUE is the local selection value of SELECTION."
+ (and (eq selection 'XdndSelection)
+ (or (stringp value)
+ (vectorp value))))
+
+(defun xselect-convert-xm-special (_selection _type _value)
+ "")
+
+(defun xselect-dt-netfile-available-p (selection _type value)
+ "Return whether or not `_DT_NETFILE' is a valid target for SELECTION.
+VALUE is SELECTION's local selection value."
+ (and (eq selection 'XdndSelection)
+ (stringp value)
+ (file-exists-p value)
+ (not (file-remote-p value))))
+
+(defun xselect-tt-net-file (file)
+ "Get the canonical ToolTalk filename for FILE.
+FILE must be a local file, or otherwise the conversion will fail.
+The string returned has three components: the hostname of the
+machine where the file is, the real path, and the local path.
+They are encoded into a string of the form
+\"HOST=0-X,RPATH=X-Y,LPATH=Y-Z:DATA\", where X, Y, and Z are the
+positions of the hostname, rpath and lpath inside DATA."
+ (let ((hostname (system-name))
+ (rpath file)
+ (lpath file))
+ (format "HOST=0-%d,RPATH=%d-%d,LPATH=%d-%d:%s%s%s"
+ (1- (length hostname)) (length hostname)
+ (1- (+ (length hostname) (length rpath)))
+ (+ (length hostname) (length rpath))
+ (1- (+ (length hostname) (length rpath)
+ (length lpath)))
+ hostname rpath lpath)))
+
+(defun xselect-convert-to-dt-netfile (selection _type value)
+ "Convert SELECTION to a ToolTalk filename.
+VALUE should be SELECTION's local value."
+ (when (and (eq selection 'XdndSelection)
+ (stringp value)
+ (file-exists-p value)
+ (not (file-remote-p value)))
+ (let ((name (encode-coding-string value
+ (or file-name-coding-system
+ default-file-name-coding-system))))
+ (cons 'STRING
+ (encode-coding-string (xselect-tt-net-file name)
+ (or file-name-coding-system
+ default-file-name-coding-system)
+ t)))))
+
(setq selection-converter-alist
'((TEXT . xselect-convert-to-string)
(COMPOUND_TEXT . xselect-convert-to-string)
(STRING . xselect-convert-to-string)
(UTF8_STRING . xselect-convert-to-string)
+ (text/plain . xselect-convert-to-string)
+ (text/plain\;charset=utf-8 . xselect-convert-to-string)
+ (text/uri-list . (xselect-uri-list-available-p
+ . xselect-convert-to-text-uri-list))
+ (text/x-xdnd-username . xselect-convert-to-username)
+ (FILE . (xselect-uri-list-available-p
+ . xselect-convert-to-xm-file))
(TARGETS . xselect-convert-to-targets)
(LENGTH . xselect-convert-to-length)
(DELETE . xselect-convert-to-delete)
@@ -623,7 +908,11 @@ This function returns the string \"emacs\"."
(ATOM . xselect-convert-to-atom)
(INTEGER . xselect-convert-to-integer)
(SAVE_TARGETS . xselect-convert-to-save-targets)
- (_EMACS_INTERNAL . xselect-convert-to-identity)))
+ (_EMACS_INTERNAL . xselect-convert-to-identity)
+ (XmTRANSFER_SUCCESS . xselect-convert-xm-special)
+ (XmTRANSFER_FAILURE . xselect-convert-xm-special)
+ (_DT_NETFILE . (xselect-dt-netfile-available-p
+ . xselect-convert-to-dt-netfile))))
(provide 'select)