]> code.delx.au - gnu-emacs/blobdiff - lisp/mouse-sel.el
(gnus-newsrc-file-version): Add defvar.
[gnu-emacs] / lisp / mouse-sel.el
index fdcd9a3e623e5c2955bb8a8351904ff5280c6c1a..ccd469200be410dcd98381733312d845dfd250e2 100644 (file)
@@ -1,8 +1,9 @@
-;;; mouse-sel.el --- Multi-click selection support for Emacs 19
+;;; mouse-sel.el --- multi-click selection support for Emacs 19
 
 
-;; Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
+;; Copyright (C) 1993, 1994, 1995, 2001, 2002, 2003, 2004,
+;;   2005 Free Software Foundation, Inc.
 
 
-;; Author: Mike Williams <mikew@gopher.dosli.govt.nz>
+;; Author: Mike Williams <mdub@bigfoot.com>
 ;; Keywords: mouse
 
 ;; This file is part of GNU Emacs.
 ;; Keywords: mouse
 
 ;; This file is part of GNU Emacs.
@@ -19,8 +20,8 @@
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
 
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
 
 ;;; Commentary:
 
 
 ;;; Commentary:
 
@@ -50,9 +51,9 @@
 ;;
 ;;   * Pressing mouse-2 while selecting or extending copies selection
 ;;     to the kill ring.  Pressing mouse-1 or mouse-3 kills it.
 ;;
 ;;   * Pressing mouse-2 while selecting or extending copies selection
 ;;     to the kill ring.  Pressing mouse-1 or mouse-3 kills it.
-;;     
+;;
 ;;   * Double-clicking mouse-3 also kills selection.
 ;;   * Double-clicking mouse-3 also kills selection.
-;;     
+;;
 ;;   * M-mouse-1, M-mouse-2 & M-mouse-3 work similarly to mouse-1, mouse-2
 ;;     & mouse-3, but operate on the X secondary selection rather than the
 ;;     primary selection and region.
 ;;   * M-mouse-1, M-mouse-2 & M-mouse-3 work similarly to mouse-1, mouse-2
 ;;     & mouse-3, but operate on the X secondary selection rather than the
 ;;     primary selection and region.
@@ -71,7 +72,7 @@
 ;;
 ;;      ;; But only in the selected window
 ;;      (setq highlight-nonselected-windows nil)
 ;;
 ;;      ;; But only in the selected window
 ;;      (setq highlight-nonselected-windows nil)
-;;      
+;;
 ;;      ;; Enable pending-delete
 ;;      (delete-selection-mode 1)
 ;;
 ;;      ;; Enable pending-delete
 ;;      (delete-selection-mode 1)
 ;;
@@ -79,7 +80,7 @@
 ;;   of mouse-sel-default-bindings before loading mouse-sel.
 ;;
 ;;   (a) If mouse-sel-default-bindings = t (the default)
 ;;   of mouse-sel-default-bindings before loading mouse-sel.
 ;;
 ;;   (a) If mouse-sel-default-bindings = t (the default)
-;;   
+;;
 ;;       Mouse sets and insert selection
 ;;        mouse-1              mouse-select
 ;;        mouse-2              mouse-insert-selection
 ;;       Mouse sets and insert selection
 ;;        mouse-1              mouse-select
 ;;        mouse-2              mouse-insert-selection
 ;;         interprogram-paste-function = nil
 ;;
 ;;   (b) If mouse-sel-default-bindings = 'interprogram-cut-paste
 ;;         interprogram-paste-function = nil
 ;;
 ;;   (b) If mouse-sel-default-bindings = 'interprogram-cut-paste
-;;   
+;;
 ;;       Mouse sets selection, and pastes from kill-ring
 ;;        mouse-1              mouse-select
 ;;       Mouse sets selection, and pastes from kill-ring
 ;;        mouse-1              mouse-select
-;;        mouse-2              mouse-yank-at-click
+;;        mouse-2              mouse-insert-selection
 ;;        mouse-3              mouse-extend
 ;;        mouse-3              mouse-extend
-;;  
+;;      In this mode, mouse-insert-selection just calls mouse-yank-at-click.
+;;
 ;;       Selection/kill-ring interaction is retained
 ;;         interprogram-cut-function   = x-select-text
 ;;         interprogram-paste-function = x-cut-buffer-or-selection-value
 ;;       Selection/kill-ring interaction is retained
 ;;         interprogram-cut-function   = x-select-text
 ;;         interprogram-paste-function = x-cut-buffer-or-selection-value
-;;         
+;;
 ;;       What you lose is the ability to select some text in
 ;;       delete-selection-mode and yank over the top of it.
 ;;       What you lose is the ability to select some text in
 ;;       delete-selection-mode and yank over the top of it.
-;;       
+;;
 ;;   (c) If mouse-sel-default-bindings = nil, no bindings are made.
 ;;
 ;; * By default, mouse-insert-selection (mouse-2) inserts the selection at
 ;;   (c) If mouse-sel-default-bindings = nil, no bindings are made.
 ;;
 ;; * By default, mouse-insert-selection (mouse-2) inserts the selection at
 
 ;;; Code:
 
 
 ;;; Code:
 
-(provide 'mouse-sel)
-
 (require 'mouse)
 (require 'thingatpt)
 
 (require 'mouse)
 (require 'thingatpt)
 
+(eval-when-compile
+  (require 'cl))
+
 ;;=== User Variables ======================================================
 
 ;;=== User Variables ======================================================
 
-(defvar mouse-sel-leave-point-near-mouse t
+(defgroup mouse-sel nil
+  "Mouse selection enhancement."
+  :group 'mouse)
+
+(defcustom mouse-sel-leave-point-near-mouse t
   "*Leave point near last mouse position.
 If non-nil, \\[mouse-select] and \\[mouse-extend] will leave point at the end
 of the region nearest to where the mouse last was.
   "*Leave point near last mouse position.
 If non-nil, \\[mouse-select] and \\[mouse-extend] will leave point at the end
 of the region nearest to where the mouse last was.
-If nil, point will always be placed at the beginning of the region.")
+If nil, point will always be placed at the beginning of the region."
+  :type 'boolean
+  :group 'mouse-sel)
+
+(defcustom mouse-sel-cycle-clicks t
+  "*If non-nil, \\[mouse-select] cycles the click-counts after 4 clicks."
+  :type 'boolean
+  :group 'mouse-sel)
+
+(defcustom mouse-sel-default-bindings t
+  "*Control mouse bindings."
+  :type '(choice (const :tag "none" nil)
+                (const :tag "cut and paste" interprogram-cut-paste)
+                (other :tag "default bindings" t))
+  :group 'mouse-sel)
+
+;;=== Key bindings ========================================================
 
 
-(defvar mouse-sel-cycle-clicks t
-  "*If non-nil, \\[mouse-select] cycles the click-counts after 4 clicks.")
+(defconst mouse-sel-bound-events
+  '(;; Primary selection bindings.
+    ;;
+    ;; Bind keys to `ignore' instead of unsetting them because modes may
+    ;; bind `down-mouse-1', for instance, without binding `mouse-1'.
+    ;; If we unset `mouse-1', this leads to a bitch_at_user when the
+    ;; mouse goes up because no matching binding is found for that.
+    ([mouse-1]         . ignore)
+    ([drag-mouse-1]    . ignore)
+    ([mouse-3]         . ignore)
+    ([down-mouse-1]    . mouse-select)
+    ([down-mouse-3]    . mouse-extend)
+    ([mouse-2]         . mouse-insert-selection)
+    ;; Secondary selection bindings.
+    ([M-mouse-1]       . ignore)
+    ([M-drag-mouse-1]  . ignore)
+    ([M-mouse-3]       . ignore)
+    ([M-down-mouse-1]  . mouse-select-secondary)
+    ([M-mouse-2]       . mouse-insert-secondary)
+    ([M-down-mouse-3]  . mouse-extend-secondary))
+  "An alist of events that `mouse-sel-mode' binds.")
+
+;;=== User Command ========================================================
+
+(defvar mouse-sel-has-been-enabled nil
+  "Non-nil if Mouse Sel mode has been enabled at least once.")
+
+(defvar mouse-sel-original-bindings nil)
+(defvar mouse-sel-original-interprogram-cut-function nil)
+(defvar mouse-sel-original-interprogram-paste-function nil)
+
+;;;###autoload
+(define-minor-mode mouse-sel-mode
+  "Toggle Mouse Sel mode.
+With prefix ARG, turn Mouse Sel mode on if and only if ARG is positive.
+Returns the new status of Mouse Sel mode (non-nil means on).
+
+When Mouse Sel mode is enabled, mouse selection is enhanced in various ways:
+
+- Clicking mouse-1 starts (cancels) selection, dragging extends it.
+
+- Clicking or dragging mouse-3 extends the selection as well.
+
+- Double-clicking on word constituents selects words.
+Double-clicking on symbol constituents selects symbols.
+Double-clicking on quotes or parentheses selects sexps.
+Double-clicking on whitespace selects whitespace.
+Triple-clicking selects lines.
+Quad-clicking selects paragraphs.
+
+- Selecting sets the region & X primary selection, but does NOT affect
+the `kill-ring', nor do the kill-ring functions change the X selection.
+Because the mouse handlers set the primary selection directly,
+mouse-sel sets the variables `interprogram-cut-function' and
+`interprogram-paste-function' to nil.
+
+- Clicking mouse-2 inserts the contents of the primary selection at
+the mouse position (or point, if `mouse-yank-at-point' is non-nil).
 
 
-(defvar mouse-sel-default-bindings t
-  "Set to nil before loading `mouse-sel' to prevent default mouse bindings.")
+- Pressing mouse-2 while selecting or extending copies selection
+to the kill ring.  Pressing mouse-1 or mouse-3 kills it.
+
+- Double-clicking mouse-3 also kills selection.
+
+- M-mouse-1, M-mouse-2 & M-mouse-3 work similarly to mouse-1, mouse-2
+& mouse-3, but operate on the X secondary selection rather than the
+primary selection and region."
+  :global t
+  :group 'mouse-sel
+  (if mouse-sel-mode
+      (progn
+       (add-hook 'x-lost-selection-functions 'mouse-sel-lost-selection-hook)
+       (when mouse-sel-default-bindings
+         ;; Save original bindings and replace them with new ones.
+         (setq mouse-sel-original-bindings
+               (mapcar (lambda (binding)
+                         (let ((event (car binding)))
+                           (prog1 (cons event (lookup-key global-map event))
+                             (global-set-key event (cdr binding)))))
+                       mouse-sel-bound-events))
+         ;; Update interprogram functions.
+         (setq mouse-sel-original-interprogram-cut-function
+               interprogram-cut-function
+               mouse-sel-original-interprogram-paste-function
+               interprogram-paste-function
+               mouse-sel-has-been-enabled t)
+         (unless (eq mouse-sel-default-bindings 'interprogram-cut-paste)
+           (setq interprogram-cut-function nil
+                 interprogram-paste-function nil))))
+
+    ;; Restore original bindings
+    (remove-hook 'x-lost-selection-functions 'mouse-sel-lost-selection-hook)
+    (dolist (binding mouse-sel-original-bindings)
+      (global-set-key (car binding) (cdr binding)))
+    ;; Restore the old values of these variables,
+    ;; only if they were actually saved previously.
+    (if mouse-sel-has-been-enabled
+       (setq interprogram-cut-function
+             mouse-sel-original-interprogram-cut-function
+             interprogram-paste-function
+             mouse-sel-original-interprogram-paste-function))))
 
 ;;=== Internal Variables/Constants ========================================
 
 
 ;;=== Internal Variables/Constants ========================================
 
-(defvar mouse-sel-primary-thing nil 
+(defvar mouse-sel-primary-thing nil
   "Type of PRIMARY selection in current buffer.")
 (make-variable-buffer-local 'mouse-sel-primary-thing)
 
   "Type of PRIMARY selection in current buffer.")
 (make-variable-buffer-local 'mouse-sel-primary-thing)
 
-(defvar mouse-sel-secondary-thing nil 
+(defvar mouse-sel-secondary-thing nil
   "Type of SECONDARY selection in current buffer.")
 (make-variable-buffer-local 'mouse-sel-secondary-thing)
 
 ;; Ensure that secondary overlay is defined
   "Type of SECONDARY selection in current buffer.")
 (make-variable-buffer-local 'mouse-sel-secondary-thing)
 
 ;; Ensure that secondary overlay is defined
-(if (overlayp mouse-secondary-overlay) nil
+(unless (overlayp mouse-secondary-overlay)
   (setq mouse-secondary-overlay (make-overlay 1 1))
   (overlay-put mouse-secondary-overlay 'face 'secondary-selection))
 
 (defconst mouse-sel-selection-alist
   '((PRIMARY mouse-drag-overlay mouse-sel-primary-thing)
     (SECONDARY mouse-secondary-overlay mouse-sel-secondary-thing))
   (setq mouse-secondary-overlay (make-overlay 1 1))
   (overlay-put mouse-secondary-overlay 'face 'secondary-selection))
 
 (defconst mouse-sel-selection-alist
   '((PRIMARY mouse-drag-overlay mouse-sel-primary-thing)
     (SECONDARY mouse-secondary-overlay mouse-sel-secondary-thing))
-  "Alist associating selections with variables.  Each element is of
-the form:
+  "Alist associating selections with variables.
+Each element is of the form:
 
    (SELECTION-NAME OVERLAY-SYMBOL SELECTION-THING-SYMBOL)
 
 
    (SELECTION-NAME OVERLAY-SYMBOL SELECTION-THING-SYMBOL)
 
@@ -183,26 +302,30 @@ where   SELECTION-NAME          = name of selection
         OVERLAY-SYMBOL          = name of variable containing overlay to use
        SELECTION-THING-SYMBOL  = name of variable where the current selection
                                  type for this selection should be stored.")
         OVERLAY-SYMBOL          = name of variable containing overlay to use
        SELECTION-THING-SYMBOL  = name of variable where the current selection
                                  type for this selection should be stored.")
-    
-(defvar mouse-sel-set-selection-function 
-  (function (lambda (selection value)
-             (if (eq selection 'PRIMARY)
-                 (x-select-text value)
-               (x-set-selection selection value))))
+
+(defvar mouse-sel-set-selection-function
+  (if (eq mouse-sel-default-bindings 'interprogram-cut-paste)
+      'x-set-selection
+    (lambda (selection value)
+      (if (eq selection 'PRIMARY)
+         (x-select-text value)
+       (x-set-selection selection value))))
   "Function to call to set selection.
 Called with two arguments:
 
   SELECTION, the name of the selection concerned, and
   VALUE, the text to store.
   "Function to call to set selection.
 Called with two arguments:
 
   SELECTION, the name of the selection concerned, and
   VALUE, the text to store.
-This sets the selection as well as the cut buffer for the older applications.
-Use (setq mouse-sel-set-selection-function 'x-set-selection) if you don't care
-for them.")
+
+This sets the selection as well as the cut buffer for the older applications,
+unless `mouse-sel-default-bindings' is `interprogram-cut-paste'.")
 
 (defvar mouse-sel-get-selection-function
 
 (defvar mouse-sel-get-selection-function
-  (function (lambda (selection)
-             (if (eq selection 'PRIMARY)
-                 (x-cut-buffer-or-selection-value)
-               (x-get-selection selection))))
+  (lambda (selection)
+    (if (eq selection 'PRIMARY)
+       (or (x-cut-buffer-or-selection-value)
+           (bound-and-true-p x-last-selected-text)
+           (bound-and-true-p x-last-selected-text-primary))
+      (x-get-selection selection)))
   "Function to call to get the selection.
 Called with one argument:
 
   "Function to call to get the selection.
 Called with one argument:
 
@@ -226,14 +349,14 @@ Feel free to re-define this function to support your own desired
 multi-click semantics."
   (let* ((next-char (char-after (point)))
         (char-syntax (if next-char (char-syntax next-char))))
 multi-click semantics."
   (let* ((next-char (char-after (point)))
         (char-syntax (if next-char (char-syntax next-char))))
-    (if mouse-sel-cycle-clicks 
+    (if mouse-sel-cycle-clicks
        (setq nclicks (1+ (% (1- nclicks) 4))))
     (cond
      ((= nclicks 1) nil)
      ((= nclicks 3) 'line)
      ((>= nclicks 4) 'paragraph)
      ((memq char-syntax '(?\( ?\) ?\" ?')) 'sexp)
        (setq nclicks (1+ (% (1- nclicks) 4))))
     (cond
      ((= nclicks 1) nil)
      ((= nclicks 3) 'line)
      ((>= nclicks 4) 'paragraph)
      ((memq char-syntax '(?\( ?\) ?\" ?')) 'sexp)
-     ((memq next-char '(? ?\t ?\n)) 'whitespace)
+     ((memq next-char '(?\s ?\t ?\n)) 'whitespace)
      ((eq char-syntax ?_) 'symbol)
      ((eq char-syntax ?w) 'word))))
 
      ((eq char-syntax ?_) 'symbol)
      ((eq char-syntax ?w) 'word))))
 
@@ -263,17 +386,17 @@ multi-click semantics."
 
 (defun mouse-sel-region-to-primary (orig-window)
   "Convert region to PRIMARY overlay and deactivate region.
 
 (defun mouse-sel-region-to-primary (orig-window)
   "Convert region to PRIMARY overlay and deactivate region.
-Argument ORIG-WINDOW specifies the window the cursor was in when the 
-originating command was issued, and is used to determine whether the 
+Argument ORIG-WINDOW specifies the window the cursor was in when the
+originating command was issued, and is used to determine whether the
 region was visible or not."
   (if transient-mark-mode
       (let ((overlay (mouse-sel-selection-overlay 'PRIMARY)))
        (cond
 region was visible or not."
   (if transient-mark-mode
       (let ((overlay (mouse-sel-selection-overlay 'PRIMARY)))
        (cond
-        ((and mark-active 
-              (or highlight-nonselected-windows 
+        ((and mark-active
+              (or highlight-nonselected-windows
                   (eq orig-window (selected-window))))
          ;; Region was visible, so convert region to overlay
                   (eq orig-window (selected-window))))
          ;; Region was visible, so convert region to overlay
-         (move-overlay overlay (region-beginning) (region-end) 
+         (move-overlay overlay (region-beginning) (region-end)
                        (current-buffer)))
         ((eq orig-window (selected-window))
          ;; Point was visible, so set overlay at point
                        (current-buffer)))
         ((eq orig-window (selected-window))
          ;; Point was visible, so set overlay at point
@@ -307,24 +430,22 @@ dragged right-to-left."
   "Evaluate forms at mouse position.
 Move to the end position of EVENT, execute FORMS, and restore original
 point and window."
   "Evaluate forms at mouse position.
 Move to the end position of EVENT, execute FORMS, and restore original
 point and window."
-    (` 
-     (let ((posn (event-end (, event))))
-       (if posn (mouse-minibuffer-check (, event)))
-       (if (and posn (not (windowp (posn-window posn))))
-          (error "Cursor not in text area of window"))
-       (let (orig-window orig-point-marker)
-        (setq orig-window (selected-window))
-        (if posn (select-window (posn-window posn)))
-        (setq orig-point-marker (point-marker))
-        (if (and posn (numberp (posn-point posn)))
-            (goto-char (posn-point posn)))
-        (unwind-protect
-            (progn
-              (,@ forms))
-          (goto-char (marker-position orig-point-marker))
-          (move-marker orig-point-marker nil)
-          (select-window orig-window)
-          )))))
+  `(let ((posn (event-end ,event)))
+    (if posn (mouse-minibuffer-check ,event))
+    (if (and posn (not (windowp (posn-window posn))))
+        (error "Cursor not in text area of window"))
+    (let (orig-window orig-point-marker)
+      (setq orig-window (selected-window))
+      (if posn (select-window (posn-window posn)))
+      (setq orig-point-marker (point-marker))
+      (if (and posn (numberp (posn-point posn)))
+          (goto-char (posn-point posn)))
+      (unwind-protect
+           (progn
+             ,@forms)
+        (goto-char (marker-position orig-point-marker))
+        (move-marker orig-point-marker nil)
+        (select-window orig-window)))))
 
 (put 'mouse-sel-eval-at-event-end 'lisp-indent-hook 1)
 
 
 (put 'mouse-sel-eval-at-event-end 'lisp-indent-hook 1)
 
@@ -336,7 +457,7 @@ point and window."
 Click sets point & mark to click position.
 Dragging extends region/selection.
 
 Click sets point & mark to click position.
 Dragging extends region/selection.
 
-Multi-clicking selects word/lines/paragraphs, as determined by 
+Multi-clicking selects word/lines/paragraphs, as determined by
 'mouse-sel-determine-selection-thing.
 
 Clicking mouse-2 while selecting copies selected text to the kill-ring.
 'mouse-sel-determine-selection-thing.
 
 Clicking mouse-2 while selecting copies selected text to the kill-ring.
@@ -344,29 +465,38 @@ Clicking mouse-1 or mouse-3 kills the selected text.
 
 This should be bound to a down-mouse event."
   (interactive "@e")
 
 This should be bound to a down-mouse event."
   (interactive "@e")
-  (let (direction)
+  (let (select)
     (unwind-protect
     (unwind-protect
-       (setq direction (mouse-select-internal 'PRIMARY event))
-      (mouse-sel-primary-to-region direction))))
+       (setq select (mouse-select-internal 'PRIMARY event))
+      (if (and select (listp select))
+         (push (cons 'mouse-2 (cdr event)) unread-command-events)
+       (mouse-sel-primary-to-region select)))))
 
 (defun mouse-select-secondary (event)
 
 (defun mouse-select-secondary (event)
-   "Set secondary selection using the mouse.
+  "Set secondary selection using the mouse.
 
 Click sets the start of the secondary selection to click position.
 Dragging extends the secondary selection.
 
 
 Click sets the start of the secondary selection to click position.
 Dragging extends the secondary selection.
 
-Multi-clicking selects word/lines/paragraphs, as determined by 
+Multi-clicking selects word/lines/paragraphs, as determined by
 'mouse-sel-determine-selection-thing.
 
 Clicking mouse-2 while selecting copies selected text to the kill-ring.
 Clicking mouse-1 or mouse-3 kills the selected text.
 
 This should be bound to a down-mouse event."
 'mouse-sel-determine-selection-thing.
 
 Clicking mouse-2 while selecting copies selected text to the kill-ring.
 Clicking mouse-1 or mouse-3 kills the selected text.
 
 This should be bound to a down-mouse event."
-   (interactive "e")
+  (interactive "e")
   (mouse-select-internal 'SECONDARY event))
 
 (defun mouse-select-internal (selection event)
   (mouse-select-internal 'SECONDARY event))
 
 (defun mouse-select-internal (selection event)
-  "Set SELECTION using the mouse."
+  "Set SELECTION using the mouse, with EVENT as the initial down-event.
+Normally, this returns the direction in which the selection was
+made: a value of 1 indicates that the mouse was dragged
+left-to-right, otherwise it was dragged right-to-left.
+
+However, if `mouse-1-click-follows-link' is non-nil and the
+subsequent mouse events specify following a link, this returns
+the final mouse-event.  In that case, the selection is not set."
   (mouse-sel-eval-at-event-end event
     (let ((thing-symbol (mouse-sel-selection-thing selection))
          (overlay (mouse-sel-selection-overlay selection)))
   (mouse-sel-eval-at-event-end event
     (let ((thing-symbol (mouse-sel-selection-thing selection))
          (overlay (mouse-sel-selection-overlay selection)))
@@ -380,7 +510,8 @@ This should be bound to a down-mouse event."
                            (car object-bounds) (cdr object-bounds)
                            (current-buffer)))
          (move-overlay overlay (point) (point) (current-buffer)))))
                            (car object-bounds) (cdr object-bounds)
                            (current-buffer)))
          (move-overlay overlay (point) (point) (current-buffer)))))
-    (mouse-extend-internal selection)))
+    (catch 'follow-link
+      (mouse-extend-internal selection event t))))
 
 ;;=== Extend ==============================================================
 
 
 ;;=== Extend ==============================================================
 
@@ -402,15 +533,16 @@ This should be bound to a down-mouse event."
   (save-window-excursion
     (mouse-extend-internal 'SECONDARY event)))
 
   (save-window-excursion
     (mouse-extend-internal 'SECONDARY event)))
 
-(defun mouse-extend-internal (selection &optional initial-event)
+(defun mouse-extend-internal (selection &optional initial-event no-process)
   "Extend specified SELECTION using the mouse.
 Track mouse-motion events, adjusting the SELECTION appropriately.
   "Extend specified SELECTION using the mouse.
 Track mouse-motion events, adjusting the SELECTION appropriately.
-Optional argument INITIAL-EVENT specifies an initial down-mouse event to 
-process. 
+Optional argument INITIAL-EVENT specifies an initial down-mouse event.
+Optional argument NO-PROCESS means not to process the initial
+event.
 
 See documentation for mouse-select-internal for more details."
   (mouse-sel-eval-at-event-end initial-event
 
 See documentation for mouse-select-internal for more details."
   (mouse-sel-eval-at-event-end initial-event
-    (let ((orig-cursor-type 
+    (let ((orig-cursor-type
           (cdr (assoc 'cursor-type (frame-parameters (selected-frame))))))
       (unwind-protect
 
           (cdr (assoc 'cursor-type (frame-parameters (selected-frame))))))
       (unwind-protect
 
@@ -433,29 +565,30 @@ See documentation for mouse-select-internal for more details."
              (setq min (point)
                    max min)
              (set thing-symbol nil))
              (setq min (point)
                    max min)
              (set thing-symbol nil))
-                   
+
 
            ;; Bar cursor
            (if (fboundp 'modify-frame-parameters)
                (modify-frame-parameters (selected-frame)
                                         '((cursor-type . bar))))
 
            ;; Bar cursor
            (if (fboundp 'modify-frame-parameters)
                (modify-frame-parameters (selected-frame)
                                         '((cursor-type . bar))))
-           
+
            ;; Handle dragging
            (track-mouse
            ;; Handle dragging
            (track-mouse
-           
-             (while (if initial-event  ; Use initial event
+
+             (while (if (and initial-event (not no-process))
+                        ;; Use initial event
                         (prog1
                             (setq event initial-event)
                           (setq initial-event nil))
                       (setq event (read-event))
                       (and (consp event)
                            (memq (car event) '(mouse-movement switch-frame))))
                         (prog1
                             (setq event initial-event)
                           (setq initial-event nil))
                       (setq event (read-event))
                       (and (consp event)
                            (memq (car event) '(mouse-movement switch-frame))))
-               
+
                (let ((selection-thing (symbol-value thing-symbol))
                      (end (event-end event)))
                (let ((selection-thing (symbol-value thing-symbol))
                      (end (event-end event)))
-               
+
                  (cond
                  (cond
-                    
+
                   ;; Ignore any movement outside the frame
                   ((eq (car-safe event) 'switch-frame) nil)
                   ((and (posn-window end)
                   ;; Ignore any movement outside the frame
                   ((eq (car-safe event) 'switch-frame) nil)
                   ((and (posn-window end)
@@ -464,7 +597,7 @@ See documentation for mouse-select-internal for more details."
                                        (window-frame posn-w)
                                      posn-w))
                                  (window-frame orig-window)))) nil)
                                        (window-frame posn-w)
                                      posn-w))
                                  (window-frame orig-window)))) nil)
-                    
+
                   ;; Different window, same frame
                   ((not (eq (posn-window end) orig-window))
                    (let ((end-row (cdr (cdr (mouse-position)))))
                   ;; Different window, same frame
                   ((not (eq (posn-window end) orig-window))
                    (let ((end-row (cdr (cdr (mouse-position)))))
@@ -476,16 +609,16 @@ See documentation for mouse-select-internal for more details."
                        (mouse-scroll-subr orig-window (1+ (- end-row bottom))
                                           overlay min))
                       )))
                        (mouse-scroll-subr orig-window (1+ (- end-row bottom))
                                           overlay min))
                       )))
-                  
+
                   ;; On the mode line
                   ((eq (posn-point end) 'mode-line)
                    (mouse-scroll-subr orig-window 1 overlay min))
                   ;; On the mode line
                   ((eq (posn-point end) 'mode-line)
                    (mouse-scroll-subr orig-window 1 overlay min))
-                  
+
                   ;; In original window
                   (t (goto-char (posn-point end)))
                   ;; In original window
                   (t (goto-char (posn-point end)))
-                  
+
                   )
                   )
-                 
+
                  ;; Determine direction of drag
                  (cond
                   ((and (not direction) (not (eq min max)))
                  ;; Determine direction of drag
                  (cond
                   ((and (not direction) (not (eq min max)))
@@ -494,12 +627,12 @@ See documentation for mouse-select-internal for more details."
                    (setq direction -1))
                   ((and (not (eq direction 1)) (>= (point) max))
                    (setq direction 1)))
                    (setq direction -1))
                   ((and (not (eq direction 1)) (>= (point) max))
                    (setq direction 1)))
-                 
+
                  (if (not selection-thing) nil
                  (if (not selection-thing) nil
-                   
+
                    ;; If dragging forward, goal is next character
                    (if (and (eq direction 1) (not (eobp))) (forward-char 1))
                    ;; If dragging forward, goal is next character
                    (if (and (eq direction 1) (not (eobp))) (forward-char 1))
-                   
+
                    ;; Move to start/end of selected thing
                    (let ((goal (point)))
                      (goto-char (if (eq 1 direction) min max))
                    ;; Move to start/end of selected thing
                    (let ((goal (point)))
                      (goto-char (if (eq 1 direction) min max))
@@ -513,25 +646,29 @@ See documentation for mouse-select-internal for more details."
                               (if (> (* direction (- goal (point))) 0)
                                   end (point)))))
                        (error))))
                               (if (> (* direction (- goal (point))) 0)
                                   end (point)))))
                        (error))))
-                 
+
                  ;; Move overlay
                  (move-overlay overlay
                                (if (eq 1 direction) min (point))
                                (if (eq -1 direction) max (point))
                                (current-buffer))
                  ;; Move overlay
                  (move-overlay overlay
                                (if (eq 1 direction) min (point))
                                (if (eq -1 direction) max (point))
                                (current-buffer))
-                 
+
                  )))                   ; end track-mouse
 
                  )))                   ; end track-mouse
 
+           ;; Detect follow-link events
+           (when (mouse-sel-follow-link-p initial-event event)
+             (throw 'follow-link event))
+
            ;; Finish up after dragging
            (let ((overlay-start (overlay-start overlay))
                  (overlay-end (overlay-end overlay)))
            ;; Finish up after dragging
            (let ((overlay-start (overlay-start overlay))
                  (overlay-end (overlay-end overlay)))
-             
+
              ;; Set selection
              (if (not (eq overlay-start overlay-end))
                  (mouse-sel-set-selection
                   selection
                   (buffer-substring overlay-start overlay-end)))
              ;; Set selection
              (if (not (eq overlay-start overlay-end))
                  (mouse-sel-set-selection
                   selection
                   (buffer-substring overlay-start overlay-end)))
-             
+
              ;; Handle copy/kill
              (let (this-command)
                (cond
              ;; Handle copy/kill
              (let (this-command)
                (cond
@@ -553,18 +690,39 @@ See documentation for mouse-select-internal for more details."
 
        ;; Restore cursor
        (if (fboundp 'modify-frame-parameters)
 
        ;; Restore cursor
        (if (fboundp 'modify-frame-parameters)
-           (modify-frame-parameters 
+           (modify-frame-parameters
             (selected-frame) (list (cons 'cursor-type orig-cursor-type))))
             (selected-frame) (list (cons 'cursor-type orig-cursor-type))))
-       
+
        ))))
 
        ))))
 
+(defun mouse-sel-follow-link-p (initial final)
+  "Return t if we should follow a link, given INITIAL and FINAL mouse events.
+See `mouse-1-click-follows-link' for details.  Currently, Mouse
+Sel mode does not support using a `double' value to follow links
+using double-clicks."
+  (and initial final mouse-1-click-follows-link
+       (eq (car initial) 'down-mouse-1)
+       (mouse-on-link-p        (posn-point (event-start initial)))
+       (= (posn-point (event-start initial))
+         (posn-point (event-end final)))
+       (= (event-click-count initial) 1)
+       (or (not (integerp mouse-1-click-follows-link))
+          (let ((t0 (posn-timestamp (event-start initial)))
+                (t1 (posn-timestamp (event-end final))))
+            (and (integerp t0) (integerp t1)
+                 (if (> mouse-1-click-follows-link 0)
+                     (<= (- t1 t0) mouse-1-click-follows-link)
+                   (< (- t0 t1) mouse-1-click-follows-link)))))))
+
 ;;=== Paste ===============================================================
 
 ;;=== Paste ===============================================================
 
-(defun mouse-insert-selection (event)
+(defun mouse-insert-selection (event arg)
   "Insert the contents of the PRIMARY selection at mouse click.
 If `mouse-yank-at-point' is non-nil, insert at point instead."
   "Insert the contents of the PRIMARY selection at mouse click.
 If `mouse-yank-at-point' is non-nil, insert at point instead."
-  (interactive "e")
-  (mouse-insert-selection-internal 'PRIMARY event))
+  (interactive "e\nP")
+  (if (eq mouse-sel-default-bindings 'interprogram-cut-paste)
+      (mouse-yank-at-click event arg)
+    (mouse-insert-selection-internal 'PRIMARY event)))
 
 (defun mouse-insert-secondary (event)
   "Insert the contents of the SECONDARY selection at mouse click.
 
 (defun mouse-insert-secondary (event)
   "Insert the contents of the SECONDARY selection at mouse click.
@@ -575,12 +733,11 @@ If `mouse-yank-at-point' is non-nil, insert at point instead."
 (defun mouse-insert-selection-internal (selection event)
   "Insert the contents of the named SELECTION at mouse click.
 If `mouse-yank-at-point' is non-nil, insert at point instead."
 (defun mouse-insert-selection-internal (selection event)
   "Insert the contents of the named SELECTION at mouse click.
 If `mouse-yank-at-point' is non-nil, insert at point instead."
-  (or mouse-yank-at-point 
-      (mouse-set-point event))
-  (if mouse-sel-get-selection-function
-      (progn
-       (push-mark (point) 'nomsg)
-       (insert (or (funcall mouse-sel-get-selection-function selection) "")))))
+  (unless mouse-yank-at-point
+    (mouse-set-point event))
+  (when mouse-sel-get-selection-function
+    (push-mark (point) 'nomsg)
+    (insert (or (funcall mouse-sel-get-selection-function selection) ""))))
 
 ;;=== Handle loss of selections ===========================================
 
 
 ;;=== Handle loss of selections ===========================================
 
@@ -589,58 +746,7 @@ If `mouse-yank-at-point' is non-nil, insert at point instead."
   (let ((overlay (mouse-sel-selection-overlay selection)))
     (delete-overlay overlay)))
 
   (let ((overlay (mouse-sel-selection-overlay selection)))
     (delete-overlay overlay)))
 
-(add-hook 'x-lost-selection-hooks 'mouse-sel-lost-selection-hook)
-
-;;=== Key bindings ========================================================
+(provide 'mouse-sel)
 
 
-(if (not mouse-sel-default-bindings) nil
-  
-  (global-unset-key [mouse-1])
-  (global-unset-key [drag-mouse-1])
-  (global-unset-key [mouse-3])
-  
-  (global-set-key [down-mouse-1]       'mouse-select)
-  (global-set-key [down-mouse-3]       'mouse-extend)
-
-  (global-unset-key [M-mouse-1])
-  (global-unset-key [M-drag-mouse-1])
-  (global-unset-key [M-mouse-3])
-  
-  (global-set-key [M-down-mouse-1]     'mouse-select-secondary)
-  (global-set-key [M-down-mouse-3]     'mouse-extend-secondary)
-
-  (if (eq mouse-sel-default-bindings 'interprogram-cut-paste) nil
-    
-    (global-set-key [mouse-2]          'mouse-insert-selection)
-
-    (setq interprogram-cut-function nil
-         interprogram-paste-function nil))
-  
-  (global-set-key [M-mouse-2]          'mouse-insert-secondary)
-    
-  )
-
-;;=== Bug reporting =======================================================
-
-(defconst mouse-sel-maintainer-address "mikew@gopher.dosli.govt.nz")
-
-(defun mouse-sel-submit-bug-report ()
-  "Submit a bug report on mouse-sel.el via mail."
-  (interactive)
-  (require 'reporter)
-  (reporter-submit-bug-report
-   mouse-sel-maintainer-address
-   (concat "mouse-sel.el "
-           (or (condition-case nil mouse-sel-version (error))
-               "(distributed with Emacs)"))
-   (list 'transient-mark-mode
-        'delete-selection-mode
-        'mouse-sel-default-bindings
-        'mouse-sel-leave-point-near-mouse
-        'mouse-sel-cycle-clicks
-        'mouse-sel-selection-alist
-        'mouse-sel-set-selection-function
-        'mouse-sel-get-selection-function
-        'mouse-yank-at-point)))
-
-;; mouse-sel.el ends here.
+;; arch-tag: 86e6c73f-deaa-48d3-a24e-c565fda1f7d7
+;;; mouse-sel.el ends here