bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Improve X Selection?


From: Tom Horsley
Subject: [PATCH] Improve X Selection?
Date: Mon, 15 Oct 2007 20:15:29 -0400

To liven up the "Improve X Selection" thread, here is a patch against
emacs-22.1 that implements (to the best of my ability) the original scheme I
suggested at the start of the thread (my email address has changed, since I
am doing this work at home, not at ccur.com). The (current-kill) function
(apparently the only caller of interprogram-paste-function) has been
modified to accept either a simple string or a list of strings. To go with
that, the (x-cut-buffer-or-selection-value) function has been modified to
return a list of selection strings if multiple selections have changed since
the last call, the order of the selections and which selections it uses are
determined by the new variable x-paste-selection-names.

I freely admit that some of the hackery I've done here was somewhat blind,
so it is entirely possible that I have screwed up some subtle details
somewhere (particularly all the coding system stuff going on in the cut
buffer code I extracted into a helper function from the old code), but it
seems to work for me in my testing so far.

Anyway this is something more concrete to play with and see how folks like
it. I note that it doesn't preclude implementing different paste functions
in different contexts as has been suggested in the original thread (menu,
keyboard, mouse, etc), you can always do a 'let' to modify the
x-paste-selection-names temporarily then call the common code, but I have to
say I prefer not having to think about which selection I'm going to get -
just lump 'em all in the kill-ring, and if I see the wrong text, I can just
do a yank-pop to get the other string.


*** emacs-22.1/lisp/ChangeLog   Sat Jun  2 00:30:23 2007
--- emacs-22.1lisp/ChangeLog    Mon Oct 15 19:50:53 2007
***************
*** 1,3 ****
--- 1,15 ----
+ 2007-10-15  Tom Horsley  <tom.horsley@att.net>
+ 
+       * simple.el (current-kill): Accept list of strings as well
+       as single string from `interprogram-paste-function'.
+ 
+       * term/x-win.el (x-cut-buffer-or-selection-value): Modify
+       to return list of all changed selections ordered by the
+       selection named mentioned in the new variable
+       `x-paste-selection-names'.
+       (x-cut-buffer-selection): New function used to make
+       X cut buffer act somewhat like a selection.
+ 
  2007-06-02  Chong Yidong  <cyd@stupidchicken.com>
  
        * Version 22.1 released.
*** emacs-22.1/lisp/simple.el   Sun May 27 10:35:51 2007
--- emacs-22.1lisp/simple.el    Mon Oct 15 17:53:29 2007
***************
*** 2409,2414 ****
--- 2409,2419 ----
  string, then the caller of the function \(usually `current-kill')
  should put this string in the kill ring as the latest kill.
  
+ This function may also return a list of strings if the window
+ system supports multiple selections. The first string will be
+ used as the pasted text, but the other will be placed in the
+ kill ring for easy access via `yank-pop'.
+ 
  Note that the function should return a string only if a program other
  than Emacs has provided a string for pasting; if Emacs provided the
  most recent string, the function should return nil.  If it is
***************
*** 2490,2500 ****
  
  (defun current-kill (n &optional do-not-move)
    "Rotate the yanking point by N places, and then return that kill.
! If N is zero, `interprogram-paste-function' is set, and calling it
! returns a string, then that string is added to the front of the
! kill ring and returned as the latest kill.
! If optional arg DO-NOT-MOVE is non-nil, then don't actually move the
! yanking point; just return the Nth kill forward."
    (let ((interprogram-paste (and (= n 0)
                                 interprogram-paste-function
                                 (funcall interprogram-paste-function))))
--- 2495,2505 ----
  
  (defun current-kill (n &optional do-not-move)
    "Rotate the yanking point by N places, and then return that kill.
! If N is zero, `interprogram-paste-function' is set, and calling it returns a
! string or list of strings, then that string (or list) is added to the front
! of the kill ring and the string (or first string in the list) is returned as
! the latest kill.  If optional arg DO-NOT-MOVE is non-nil, then don't
! actually move the yanking point; just return the Nth kill forward."
    (let ((interprogram-paste (and (= n 0)
                                 interprogram-paste-function
                                 (funcall interprogram-paste-function))))
***************
*** 2504,2511 ****
          ;; text to the kill ring, so Emacs doesn't try to own the
          ;; selection, with identical text.
          (let ((interprogram-cut-function nil))
!           (kill-new interprogram-paste))
!         interprogram-paste)
        (or kill-ring (error "Kill ring is empty"))
        (let ((ARGth-kill-element
             (nthcdr (mod (- n (length kill-ring-yank-pointer))
--- 2509,2518 ----
          ;; text to the kill ring, so Emacs doesn't try to own the
          ;; selection, with identical text.
          (let ((interprogram-cut-function nil))
!           (if (listp interprogram-paste)
!             (mapc 'kill-new (nreverse interprogram-paste))
!             (kill-new interprogram-paste)))
!         (car kill-ring))
        (or kill-ring (error "Kill ring is empty"))
        (let ((ARGth-kill-element
             (nthcdr (mod (- n (length kill-ring-yank-pointer))
*** emacs-22.1/lisp/term/x-win.el       Tue Mar  6 00:50:48 2007
--- emacs-22.1lisp/term/x-win.el        Mon Oct 15 19:46:30 2007
***************
*** 2126,2137 ****
  ;; seperately in case another X application only sets one of them
  ;; (say the cut buffer) we aren't fooled by the PRIMARY or
  ;; CLIPBOARD selection staying the same.
- (defvar x-last-selected-text-clipboard nil
-   "The value of the CLIPBOARD X selection last time we selected or
- pasted text.")
- (defvar x-last-selected-text-primary nil
-   "The value of the PRIMARY X selection last time we selected or
- pasted text.")
  (defvar x-last-selected-text-cut nil
    "The value of the X cut buffer last time we selected or pasted text.
  The actual text stored in the X cut buffer is what encoded from this value.")
--- 2126,2131 ----
***************
*** 2151,2156 ****
--- 2145,2161 ----
    :type 'boolean
    :group 'killing)
  
+ ;; In addition to representing the prefered order of selections
+ ;; for paste operations, the symbols in the x-paste-selection-names
+ ;; list also use the 'lastval symbol property to remember the
+ ;; previous selection value so that only selections which have
+ ;; changed since the last paste operation will be returned.
+ (defvar x-paste-selection-names
+   '( CLIPBOARD PRIMARY SECONDARY x-cut-buffer-selection )
+   "The list of selection names in priority order. If the name is
+ bound to a function, that function is called to obtain the selection
+ string, otherwise the named X selection is fetched.")
+ 
  (defun x-select-text (text &optional push)
    "Make TEXT, a string, the primary X selection.
  Also, set the value of X cut buffer 0, for backward compatibility
***************
*** 2289,2340 ****
        (remove-text-properties 0 (length text) '(foreign-selection nil) text))
      text))
  
! ;; Return the value of the current X selection.
! ;; Consult the selection, and the cut buffer.  Treat empty strings
! ;; as if they were unset.
! ;; If this function is called twice and finds the same text,
! ;; it returns nil the second time.  This is so that a single
! ;; selection won't be added to the kill ring over and over.
! (defun x-cut-buffer-or-selection-value ()
!   (let (clip-text primary-text cut-text)
!     (when x-select-enable-clipboard
!       (setq clip-text (x-selection-value 'CLIPBOARD))
!       (if (string= clip-text "") (setq clip-text nil))
! 
!       ;; Check the CLIPBOARD selection for 'newness', is it different
!       ;; from what we remebered them to be last time we did a
!       ;; cut/paste operation.
!       (setq clip-text
!           (cond;; check clipboard
!            ((or (not clip-text) (string= clip-text ""))
!             (setq x-last-selected-text-clipboard nil))
!            ((eq      clip-text x-last-selected-text-clipboard) nil)
!            ((string= clip-text x-last-selected-text-clipboard)
!             ;; Record the newer string,
!             ;; so subsequent calls can use the `eq' test.
!             (setq x-last-selected-text-clipboard clip-text)
!             nil)
!            (t
!             (setq x-last-selected-text-clipboard clip-text))))
!       )
! 
!     (setq primary-text (x-selection-value 'PRIMARY))
!     ;; Check the PRIMARY selection for 'newness', is it different
!     ;; from what we remebered them to be last time we did a
!     ;; cut/paste operation.
!     (setq primary-text
!         (cond;; check primary selection
!          ((or (not primary-text) (string= primary-text ""))
!           (setq x-last-selected-text-primary nil))
!          ((eq      primary-text x-last-selected-text-primary) nil)
!          ((string= primary-text x-last-selected-text-primary)
!           ;; Record the newer string,
!           ;; so subsequent calls can use the `eq' test.
!           (setq x-last-selected-text-primary primary-text)
!           nil)
!          (t
!           (setq x-last-selected-text-primary primary-text))))
! 
      (setq cut-text (x-get-cut-buffer 0))
  
      ;; Check the x cut buffer for 'newness', is it different
--- 2294,2303 ----
        (remove-text-properties 0 (length text) '(foreign-selection nil) text))
      text))
  
! (defun x-cut-buffer-selection ()
!   "Helper function used to make fetching the X cut
! buffer act much like fetching an X selection."
!   (let (cut-text)
      (setq cut-text (x-get-cut-buffer 0))
  
      ;; Check the x cut buffer for 'newness', is it different
***************
*** 2366,2394 ****
  
      ;; As we have done one selection, clear this now.
      (setq next-selection-coding-system nil)
  
!     ;; At this point we have recorded the current values for the
!     ;; selection from clipboard (if we are supposed to) primary,
!     ;; and cut buffer.  So return the first one that has changed
!     ;; (which is the first non-null one).
!     ;;
!     ;; NOTE: There will be cases where more than one of these has
!     ;; changed and the new values differ.  This indicates that
!     ;; 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 or two of them (say
!     ;; just the cut-buffer).  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.
!     ;; This code is still a big improvement because now the user can
!     ;; futz with the current selection and get emacs to pay attention
!     ;; to the cut buffer again (previously as soon as clipboard or
!     ;; primary had been set the cut buffer would essentially never be
!     ;; checked again).
!     (or clip-text primary-text cut-text)
!     ))
  
  
  ;; Do the actual X Windows setup here; the above code just defines
--- 2329,2383 ----
  
      ;; As we have done one selection, clear this now.
      (setq next-selection-coding-system nil)
+     cut-text
+   ))
  
! ;; Return only the changed values of all the X selections in the list
! ;; x-paste-selection-names. The changed selection strings will be ordered
! ;; in the returned list according to the x-paste-selection-names order.
! (defun x-cut-buffer-or-selection-value ()
!    (let ((selection-names x-paste-selection-names)
!          sel-name
!          sel-string
!          changed-list)
!       (while selection-names
!          (setq sel-name (car selection-names))
!          (setq selection-names (cdr selection-names))
!          (if (fboundp sel-name)
!             (setq sel-string (funcall sel-name))
!             (setq sel-string (x-selection-value sel-name))
!          )
!          (if (string= sel-string "") (setq sel-string nil))
!          (setq sel-string
!                (cond
!                 ((not sel-string)
!                  (put sel-name 'lastval nil))
!                 ((eq      sel-string (get sel-name 'lastval)) nil)
!                 ((string= sel-string (get sel-name 'lastval))
!                  ;; Record the newer string,
!                  ;; so subsequent calls can use the `eq' test.
!                  (put sel-name 'lastval sel-string)
!                  nil)
!                 (t
!                  (put sel-name 'lastval sel-string))))
!          ;; emacs is one app that often sets multiple selections to the
!          ;; same string. Other apps may also do this, so don't add the
!          ;; selection to the list if the same string is already in the
!          ;; list via a previous selection.
!          (if (and sel-string (not (member sel-string changed-list)))
!             (setq changed-list (cons sel-string changed-list)))
!       )
!       ;; At this point, the changed-list holds the strings from all the
!       ;; different selections which have changed. Typically, the caller of
!       ;; this routine will push them all on the kill-ring, so if you get the
!       ;; incorrect text pasted, you can do something like yank-pop to fetch
!       ;; the one you really want.  If we only have one selection, just
!       ;; return that string, not the list. If we have multiple selections,
!       ;; reverse the list to get them in the proper priority order.
!       (if (= (length changed-list) 1)
!          (car changed-list)
!          (nreverse changed-list)
!       )))
  
  
  ;; Do the actual X Windows setup here; the above code just defines




reply via email to

[Prev in Thread] Current Thread [Next in Thread]