In Fset_window_configuration install revison of reverted fix.
[bpt/emacs.git] / lisp / mouse.el
index 6339561..4ea8428 100644 (file)
@@ -1,6 +1,6 @@
-;;; mouse.el --- window system-independent mouse support
+;;; mouse.el --- window system-independent mouse support  -*- lexical-binding: t -*-
 
-;; Copyright (C) 1993-1995, 1999-201 Free Software Foundation, Inc.
+;; Copyright (C) 1993-1995, 1999-2012 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: hardware, mouse
@@ -62,7 +62,7 @@ typically sets point where you click the mouse).
 If value is an integer, the time elapsed between pressing and
 releasing the mouse button determines whether to follow the link
 or perform the normal Mouse-1 action (typically set point).
-The absolute numeric value specifices the maximum duration of a
+The absolute numeric value specifies the maximum duration of a
 \"short click\" in milliseconds.  A positive value means that a
 short click follows the link, and a longer click performs the
 normal action.  A negative value gives the opposite behavior.
@@ -101,8 +101,8 @@ point at the click position."
   "Popup the given menu and call the selected option.
 MENU can be a keymap, an easymenu-style menu or a list of keymaps as for
 `x-popup-menu'.
-POSITION can be a click event or ((XOFFSET YOFFSET) WINDOW) and defaults to
-  the current mouse position.
+The menu is shown at the place where POSITION specifies. About
+the form of POSITION, see `popup-menu-normalize-position'.
 PREFIX is the prefix argument (if any) to pass to the command."
   (let* ((map (cond
               ((keymapp menu) menu)
@@ -111,10 +111,8 @@ PREFIX is the prefix argument (if any) to pass to the command."
                         (filter (when (symbolp map)
                                   (plist-get (get map 'menu-prop) :filter))))
                    (if filter (funcall filter (symbol-function map)) map)))))
-        event cmd)
-    (unless position
-      (let ((mp (mouse-pixel-position)))
-       (setq position (list (list (cadr mp) (cddr mp)) (car mp)))))
+        event cmd
+        (position (popup-menu-normalize-position position)))
     ;; The looping behavior was taken from lmenu's popup-menu-popup
     (while (and map (setq event
                          ;; map could be a prefix key, in which case
@@ -132,7 +130,7 @@ PREFIX is the prefix argument (if any) to pass to the command."
                      binding)
                  (while (and map (null binding))
                    (setq binding (lookup-key (car map) mouse-click))
-                   (if (numberp binding) ; `too long'
+                   (if (numberp binding)       ; `too long'
                        (setq binding nil))
                    (setq map (cdr map)))
                  binding)
@@ -152,6 +150,26 @@ PREFIX is the prefix argument (if any) to pass to the command."
       ;; mouse-major-mode-menu was using `command-execute' instead.
       (call-interactively cmd))))
 
+(defun popup-menu-normalize-position (position)
+  "Convert the POSITION to the form which `popup-menu' expects internally.
+POSITION can an event, a posn- value, a value having
+form ((XOFFSET YOFFSET) WINDOW), or nil.
+If nil, the current mouse position is used."
+  (pcase position
+    ;; nil -> mouse cursor position
+    (`nil
+     (let ((mp (mouse-pixel-position)))
+       (list (list (cadr mp) (cddr mp)) (car mp))))
+    ;; Value returned from `event-end' or `posn-at-point'.
+    ((pred posnp)
+     (let ((xy (posn-x-y position)))
+       (list (list (car xy) (cdr xy))
+            (posn-window position))))
+    ;; Event.
+    ((pred eventp)
+     (popup-menu-normalize-position (event-end position)))
+    (t position)))
+
 (defun minor-mode-menu-from-indicator (indicator)
   "Show menu for minor mode specified by INDICATOR.
 Interactively, INDICATOR is read using completion.
@@ -194,8 +212,7 @@ items `Turn Off' and `Help'."
         (newmap (if ancestor
                     (make-sparse-keymap (concat (format-mode-line mode-name)
                                                  " Mode"))
-                  menu-bar-edit-menu))
-        uniq)
+                  menu-bar-edit-menu)))
     (if ancestor
        (set-keymap-parent newmap ancestor))
     newmap))
@@ -299,7 +316,7 @@ Use the former if the menu bar is showing, otherwise the latter."
   (let ((w (posn-window (event-start event))))
     (and (window-minibuffer-p w)
         (not (minibuffer-window-active-p w))
-        (error "Minibuffer window is not active")))
+        (user-error "Minibuffer window is not active")))
   ;; Give temporary modes such as isearch a chance to turn off.
   (run-hooks 'mouse-leave-buffer-hook))
 
@@ -372,298 +389,151 @@ This command must be bound to a mouse click."
        (split-window-horizontally
         (min (max new-width first-col) last-col))))))
 
-(defun mouse-drag-window-above (window)
-  "Return the (or a) window directly above WINDOW.
-That means one whose bottom edge is at the same height as WINDOW's top edge."
-  (let ((start-top   (nth 1 (window-edges window)))
-        (start-left  (nth 0 (window-edges window)))
-        (start-right (nth 2 (window-edges window)))
-       (start-window window)
-       above-window)
-    (setq window (previous-window window 0))
-    (while (and (not above-window) (not (eq window start-window)))
-      (let ((left  (nth 0 (window-edges window)))
-            (right (nth 2 (window-edges window))))
-        (when (and (= (+ (window-height window) (nth 1 (window-edges window)))
-                      start-top)
-                   (or (and (<= left start-left)  (<= start-right right))
-                       (and (<= start-left left)  (<= left start-right))
-                       (and (<= start-left right) (<= right start-right))))
-          (setq above-window window)))
-      (setq window (previous-window window)))
-    above-window))
-
-(defun mouse-drag-move-window-bottom (window growth)
-  "Move the bottom of WINDOW up or down by GROWTH lines.
-Move it down if GROWTH is positive, or up if GROWTH is negative.
-If this would make WINDOW too short,
-shrink the window or windows above it to make room."
-  (condition-case nil
-      (adjust-window-trailing-edge window growth nil)
-    (error nil)))
-
-(defsubst mouse-drag-move-window-top (window growth)
-  "Move the top of WINDOW up or down by GROWTH lines.
-Move it down if GROWTH is positive, or up if GROWTH is negative.
-If this would make WINDOW too short, shrink the window or windows
-above it to make room."
-  ;; Moving the top of WINDOW is actually moving the bottom of the
-  ;; window above.
-  (let ((window-above (mouse-drag-window-above window)))
-    (and window-above
-        (mouse-drag-move-window-bottom window-above (- growth)))))
-
-(defun mouse-drag-mode-line-1 (start-event mode-line-p)
-  "Change the height of a window by dragging on the mode or header line.
-START-EVENT is the starting mouse-event of the drag action.
-MODE-LINE-P non-nil means dragging a mode line; nil means a header line."
+;; `mouse-drag-line' is now the common routine for handling all line
+;; dragging events combining the earlier `mouse-drag-mode-line-1' and
+;; `mouse-drag-vertical-line'.  It should improve the behavior of line
+;; dragging wrt Emacs 23 as follows:
+
+;; (1) Gratuitous error messages and restrictions have been (hopefully)
+;; removed.  (The help-echo that dragging the mode-line can resize a
+;; one-window-frame's window will still show through via bindings.el.)
+
+;; (2) No gratuitous selection of other windows should happen.  (This
+;; has not been completely fixed for mouse-autoselected windows yet.)
+
+;; (3) Mouse clicks below a scroll-bar should pass through via unread
+;; command events.
+
+;; Note that `window-in-direction' replaces `mouse-drag-window-above'
+;; and `mouse-drag-vertical-line-rightward-window' with Emacs 24.1.
+
+(defun mouse-drag-line (start-event line)
+  "Drag a mode line, header line, or vertical line with the mouse.
+START-EVENT is the starting mouse-event of the drag action.  LINE
+must be one of the symbols `header', `mode', or `vertical'."
   ;; Give temporary modes such as isearch a chance to turn off.
   (run-hooks 'mouse-leave-buffer-hook)
-  (let* ((done nil)
-        (echo-keystrokes 0)
+  (let* ((echo-keystrokes 0)
         (start (event-start start-event))
-        (start-event-window (posn-window start))
-        (start-event-frame (window-frame start-event-window))
-        (start-nwindows (count-windows t))
+        (window (posn-window start))
+        (frame (window-frame window))
+        (minibuffer-window (minibuffer-window frame))
          (on-link (and mouse-1-click-follows-link
-                      (or mouse-1-click-in-non-selected-windows
-                          (eq (posn-window start) (selected-window)))
-                       (mouse-on-link-p start)))
-        (minibuffer (frame-parameter nil 'minibuffer))
-        should-enlarge-minibuffer event mouse y top bot edges wconfig growth)
+                      (mouse-on-link-p start)))
+        (side (and (eq line 'vertical)
+                   (or (cdr (assq 'vertical-scroll-bars
+                                  (frame-parameters frame)))
+                       'right)))
+        (draggable t)
+        event position growth dragged)
+    (cond
+     ((eq line 'header)
+      ;; Check whether header-line can be dragged at all.
+      (if (window-at-side-p window 'top)
+         (setq draggable nil)
+       (setq window (window-in-direction 'above window t))))
+     ((eq line 'mode)
+      ;; Check whether mode-line can be dragged at all.
+      (and (window-at-side-p window 'bottom)
+          ;; Allow resizing the minibuffer window if it's on the same
+          ;; frame as and immediately below the clicked window, and
+          ;; it's active or `resize-mini-windows' is nil.
+          (not (and (eq (window-frame minibuffer-window) frame)
+                    (= (nth 1 (window-edges minibuffer-window))
+                       (nth 3 (window-edges window)))
+                    (or (not resize-mini-windows)
+                        (eq minibuffer-window
+                            (active-minibuffer-window)))))
+          (setq draggable nil)))
+     ((eq line 'vertical)
+      ;; Get the window to adjust for the vertical case.  If the
+      ;; scroll bar is on the window's right or there's no scroll bar
+      ;; at all, adjust the window where the start-event occurred.  If
+      ;; the scroll bar is on the start-event window's left, adjust
+      ;; the window on the left of it.
+      (unless (eq side 'right)
+       (setq window (window-in-direction 'left window t)))))
+
+    ;; Start tracking.
     (track-mouse
-      (progn
-       ;; if this is the bottommost ordinary window, then to
-       ;; move its modeline the minibuffer must be enlarged.
-       (setq should-enlarge-minibuffer
-             (and minibuffer
-                  mode-line-p
-                  (not (one-window-p t))
-                  (= (nth 1 (window-edges minibuffer))
-                     (nth 3 (window-edges start-event-window)))))
-
-       ;; loop reading events and sampling the position of
-       ;; the mouse.
-       (while (not done)
-         (setq event (read-event)
-               mouse (mouse-position))
-
-         ;; do nothing if
-         ;;   - there is a switch-frame event.
-         ;;   - the mouse isn't in the frame that we started in
-         ;;   - the mouse isn't in any Emacs frame
-         ;; drag if
-         ;;   - there is a mouse-movement event
-         ;;   - there is a scroll-bar-movement event
-         ;;     (same as mouse movement for our purposes)
-         ;; quit if
-         ;;   - there is a keyboard event or some other unknown event.
-         (cond ((not (consp event))
-                (setq done t))
-
-               ((memq (car event) '(switch-frame select-window))
-                nil)
-
-               ((not (memq (car event) '(mouse-movement scroll-bar-movement)))
-                (when (consp event)
-                  ;; Do not unread a drag-mouse-1 event since it will cause the
-                  ;; selection of the window above when dragging the modeline
-                  ;; above the selected window.
-                  (unless (eq (car event) 'drag-mouse-1)
-                    (push event unread-command-events)))
-                (setq done t))
-
-               ((not (eq (car mouse) start-event-frame))
-                nil)
-
-               ((null (car (cdr mouse)))
-                nil)
-
-               (t
-                (setq y (cdr (cdr mouse))
-                      edges (window-edges start-event-window)
-                      top (nth 1 edges)
-                      bot (nth 3 edges))
-
-                ;; compute size change needed
-                (cond (mode-line-p
-                       (setq growth (- y bot -1)))
-                      (t       ; header line
-                       (when (< (- bot y) window-min-height)
-                         (setq y (- bot window-min-height)))
-                       ;; The window's top includes the header line!
-                       (setq growth (- top y))))
-                (setq wconfig (current-window-configuration))
-
-                ;; Check for an error case.
-                (when (and (/= growth 0)
-                           (not minibuffer)
-                           (one-window-p t))
-                  (error "Attempt to resize sole window"))
-
-                 ;; If we ever move, make sure we don't mistakenly treat
-                 ;; some unexpected `mouse-1' final event as a sign that
-                 ;; this whole drag was nothing more than a click.
-                 (if (/= growth 0) (setq on-link nil))
-
-                ;; grow/shrink minibuffer?
-                (if should-enlarge-minibuffer
-                    (unless resize-mini-windows
-                      (mouse-drag-move-window-bottom start-event-window growth))
-                  ;; no.  grow/shrink the selected window
-                  ;(message "growth = %d" growth)
-                  (if mode-line-p
-                      (mouse-drag-move-window-bottom start-event-window growth)
-                    (mouse-drag-move-window-top start-event-window growth)))
-
-                ;; if this window's growth caused another
-                ;; window to be deleted because it was too
-                ;; short, rescind the change.
-                ;;
-                ;; if size change caused space to be stolen
-                ;; from a window above this one, rescind the
-                ;; change, but only if we didn't grow/shrink
-                ;; the minibuffer.  minibuffer size changes
-                ;; can cause all windows to shrink... no way
-                ;; around it.
-                (when (or (/= start-nwindows (count-windows t))
-                          (and (not should-enlarge-minibuffer)
-                               (> growth 0)
-                               mode-line-p
-                               (/= top
-                                   (nth 1 (window-edges
-                                           ;; Choose right window.
-                                           start-event-window)))))
-                  (set-window-configuration wconfig)))))
-
-        ;; Presumably if this was just a click, the last event should
-        ;; be `mouse-1', whereas if this did move the mouse, it should be
-        ;; a `drag-mouse-1'.  In any case `on-link' would have been nulled
-        ;; above if there had been any significant mouse movement.
-        (when (and on-link (eq 'mouse-1 (car-safe event)))
-         ;; If mouse-2 has never been done by the user, it doesn't
-         ;; have the necessary property to be interpreted correctly.
-         (put 'mouse-2 'event-kind 'mouse-click)
-          (push (cons 'mouse-2 (cdr event)) unread-command-events))))))
+      ;; Loop reading events and sampling the position of the mouse.
+      (while draggable
+       (setq event (read-event))
+       (setq position (mouse-position))
+       ;; Do nothing if
+       ;;   - there is a switch-frame event.
+       ;;   - the mouse isn't in the frame that we started in
+       ;;   - the mouse isn't in any Emacs frame
+       ;; Drag if
+       ;;   - there is a mouse-movement event
+       ;;   - there is a scroll-bar-movement event (Why? -- cyd)
+       ;;     (same as mouse movement for our purposes)
+       ;; Quit if
+       ;;   - there is a keyboard event or some other unknown event.
+       (cond
+        ((not (consp event))
+         (setq draggable nil))
+        ((memq (car event) '(switch-frame select-window))
+         nil)
+        ((not (memq (car event) '(mouse-movement scroll-bar-movement)))
+         (when (consp event)
+           ;; Do not unread a drag-mouse-1 event to avoid selecting
+           ;; some other window.  For vertical line dragging do not
+           ;; unread mouse-1 events either (but only if we dragged at
+           ;; least once to allow mouse-1 clicks get through.
+           (unless (and dragged
+                        (if (eq line 'vertical)
+                            (memq (car event) '(drag-mouse-1 mouse-1))
+                          (eq (car event) 'drag-mouse-1)))
+             (push event unread-command-events)))
+         (setq draggable nil))
+        ((or (not (eq (car position) frame))
+             (null (car (cdr position))))
+         nil)
+        ((eq line 'vertical)
+         ;; Drag vertical divider.
+         (setq growth (- (cadr position)
+                         (if (eq side 'right) 0 2)
+                         (nth 2 (window-edges window))
+                         -1))
+         (unless (zerop growth)
+           (setq dragged t))
+         (adjust-window-trailing-edge window growth t))
+        (draggable
+         ;; Drag horizontal divider.
+         (setq growth
+               (if (eq line 'mode)
+                   (- (cddr position) (nth 3 (window-edges window)) -1)
+                 ;; The window's top includes the header line!
+                 (- (nth 3 (window-edges window)) (cddr position))))
+         (unless (zerop growth)
+           (setq dragged t))
+         (adjust-window-trailing-edge window (if (eq line 'mode)
+                                                 growth
+                                               (- growth)))))))
+    ;; Process the terminating event.
+    (when (and (mouse-event-p event) on-link (not dragged)
+              (mouse--remap-link-click-p start-event event))
+      ;; If mouse-2 has never been done by the user, it doesn't have
+      ;; the necessary property to be interpreted correctly.
+      (put 'mouse-2 'event-kind 'mouse-click)
+      (setcar event 'mouse-2))
+    (push event unread-command-events)))
 
 (defun mouse-drag-mode-line (start-event)
   "Change the height of a window by dragging on the mode line."
   (interactive "e")
-  (mouse-drag-mode-line-1 start-event t))
+  (mouse-drag-line start-event 'mode))
 
 (defun mouse-drag-header-line (start-event)
-  "Change the height of a window by dragging on the header line.
-Windows whose header-lines are at the top of the frame cannot be
-resized by dragging their header-line."
+  "Change the height of a window by dragging on the header line."
   (interactive "e")
-  ;; Changing the window's size by dragging its header-line when the
-  ;; header-line is at the top of the frame is somewhat strange,
-  ;; because the header-line doesn't move, so don't do it.
-  (let* ((start (event-start start-event))
-        (window (posn-window start))
-        (frame (window-frame window))
-        (first-window (frame-first-window frame)))
-    (unless (or (eq window first-window)
-               (= (nth 1 (window-edges window))
-                  (nth 1 (window-edges first-window))))
-      (mouse-drag-mode-line-1 start-event nil))))
-
-\f
-(defun mouse-drag-vertical-line-rightward-window (window)
-  "Return a window that is immediately to the right of WINDOW, or nil."
-  (let ((bottom (nth 3 (window-inside-edges window)))
-       (left (nth 0 (window-inside-edges window)))
-       best best-right
-       (try (previous-window window)))
-    (while (not (eq try window))
-      (let ((try-top (nth 1 (window-inside-edges try)))
-           (try-bottom (nth 3 (window-inside-edges try)))
-           (try-right (nth 2 (window-inside-edges try))))
-       (if (and (< try-top bottom)
-                (>= try-bottom bottom)
-                (< try-right left)
-                (or (null best-right) (> try-right best-right)))
-           (setq best-right try-right best try)))
-      (setq try (previous-window try)))
-    best))
+  (mouse-drag-line start-event 'header))
 
 (defun mouse-drag-vertical-line (start-event)
   "Change the width of a window by dragging on the vertical line."
   (interactive "e")
-  ;; Give temporary modes such as isearch a chance to turn off.
-  (run-hooks 'mouse-leave-buffer-hook)
-  (let* ((done nil)
-        (echo-keystrokes 0)
-        (start-event-frame (window-frame (car (car (cdr start-event)))))
-        (start-event-window (car (car (cdr start-event))))
-        event mouse x left right edges growth
-        (which-side
-         (or (cdr (assq 'vertical-scroll-bars (frame-parameters start-event-frame)))
-             'right)))
-    (cond
-     ((one-window-p t)
-      (error "Attempt to resize sole ordinary window"))
-     ((and (eq which-side 'right)
-          (>= (nth 2 (window-inside-edges start-event-window))
-              (frame-width start-event-frame)))
-      (error "Attempt to drag rightmost scrollbar"))
-     ((and (eq which-side 'left)
-          (= (nth 0 (window-inside-edges start-event-window)) 0))
-      (error "Attempt to drag leftmost scrollbar")))
-    (track-mouse
-      (progn
-       ;; loop reading events and sampling the position of
-       ;; the mouse.
-       (while (not done)
-         (setq event (read-event)
-               mouse (mouse-position))
-         ;; do nothing if
-         ;;   - there is a switch-frame event.
-         ;;   - the mouse isn't in the frame that we started in
-         ;;   - the mouse isn't in any Emacs frame
-         ;; drag if
-         ;;   - there is a mouse-movement event
-         ;;   - there is a scroll-bar-movement event
-         ;;     (same as mouse movement for our purposes)
-         ;; quit if
-         ;;   - there is a keyboard event or some other unknown event
-         ;;     unknown event.
-         (cond ((integerp event)
-                (setq done t))
-               ((memq (car event) '(switch-frame select-window))
-                nil)
-               ((not (memq (car event)
-                           '(mouse-movement scroll-bar-movement)))
-                (if (consp event)
-                    (setq unread-command-events
-                          (cons event unread-command-events)))
-                (setq done t))
-               ((not (eq (car mouse) start-event-frame))
-                nil)
-               ((null (car (cdr mouse)))
-                nil)
-               (t
-                (let ((window
-                       ;; If the scroll bar is on the window's left,
-                       ;; adjust the window on the left.
-                       (if (eq which-side 'right)
-                           start-event-window
-                         (mouse-drag-vertical-line-rightward-window
-                          start-event-window))))
-                  (setq x (- (car (cdr mouse))
-                             (if (eq which-side 'right) 0 2))
-                        edges (window-edges window)
-                        left (nth 0 edges)
-                        right (nth 2 edges))
-                  ;; scale back a move that would make the
-                  ;; window too thin.
-                  (if (< (- x left -1) window-min-width)
-                      (setq x (+ left window-min-width -1)))
-                  ;; compute size change needed
-                  (setq growth (- x right -1))
-                  (condition-case nil
-                      (adjust-window-trailing-edge window growth t)
-                    (error nil))))))))))
+  (mouse-drag-line start-event 'vertical))
 \f
 (defun mouse-set-point (event)
   "Move point to the position clicked on with the mouse.
@@ -904,7 +774,8 @@ DO-MOUSE-DRAG-REGION-POST-PROCESS should only be used by
   (mouse-minibuffer-check start-event)
   (setq mouse-selection-click-count-buffer (current-buffer))
   (deactivate-mark)
-  (let* ((original-window (selected-window))
+  (let* ((scroll-margin 0) ; Avoid margin scrolling (Bug#9541).
+        (original-window (selected-window))
          ;; We've recorded what we needed from the current buffer and
          ;; window, now let's jump to the place of the event, where things
          ;; are happening.
@@ -923,10 +794,8 @@ DO-MOUSE-DRAG-REGION-POST-PROCESS should only be used by
                   ;; Don't count the mode line.
                   (1- (nth 3 bounds))))
         (on-link (and mouse-1-click-follows-link
-                      (or mouse-1-click-in-non-selected-windows
-                          (eq start-window original-window))
                        ;; Use start-point before the intangibility
-                       ;; treatment, in case we click on a link inside an
+                       ;; treatment, in case we click on a link inside
                        ;; intangible text.
                        (mouse-on-link-p start-posn)))
         (click-count (1- (event-click-count start-event)))
@@ -935,9 +804,9 @@ DO-MOUSE-DRAG-REGION-POST-PROCESS should only be used by
                                  (= click-count 1)))
         ;; Suppress automatic hscrolling, because that is a nuisance
         ;; when setting point near the right fringe (but see below).
-        (automatic-hscrolling-saved automatic-hscrolling)
-        (automatic-hscrolling nil)
-        event end end-point)
+        (auto-hscroll-mode-saved auto-hscroll-mode)
+        (auto-hscroll-mode nil)
+        moved-off-start event end end-point)
 
     (setq mouse-selection-click-count click-count)
     ;; In case the down click is in the middle of some intangible text,
@@ -968,10 +837,13 @@ DO-MOUSE-DRAG-REGION-POST-PROCESS should only be used by
          ;; Automatic hscrolling did not occur during the call to
          ;; `read-event'; but if the user subsequently drags the
          ;; mouse, go ahead and hscroll.
-         (let ((automatic-hscrolling automatic-hscrolling-saved))
+         (let ((auto-hscroll-mode auto-hscroll-mode-saved))
            (redisplay))
          (setq end (event-end event)
                end-point (posn-point end))
+         ;; Note whether the mouse has left the starting position.
+         (unless (eq end-point start-point)
+           (setq moved-off-start t))
          (if (and (eq (posn-window end) start-window)
                   (integer-or-marker-p end-point))
              (mouse--drag-set-mark-and-point start-point
@@ -1012,11 +884,13 @@ DO-MOUSE-DRAG-REGION-POST-PROCESS should only be used by
                   (let (deactivate-mark)
                     (copy-region-as-kill (mark) (point)))))
 
-         ;; If point hasn't moved, run the binding of the
-         ;; terminating up-event.
+         ;; Otherwise, run binding of terminating up-event.
          (if do-multi-click
              (goto-char start-point)
-           (deactivate-mark))
+           (deactivate-mark)
+           (unless moved-off-start
+             (pop-mark)))
+
          (when (and (functionp fun)
                     (= start-hscroll (window-hscroll start-window))
                     ;; Don't run the up-event handler if the window
@@ -1263,8 +1137,8 @@ regardless of where you click."
 
 (defun mouse-yank-primary (click)
   "Insert the primary selection at the position clicked on.
-Move point to the end of the inserted text.
-If `mouse-yank-at-point' is non-nil, insert at point
+Move point to the end of the inserted text, and set mark at
+beginning.  If `mouse-yank-at-point' is non-nil, insert at point
 regardless of where you click."
   (interactive "e")
   ;; Give temporary modes such as isearch a chance to turn off.
@@ -1292,9 +1166,10 @@ regardless of where you click."
          ;; FIXME: What about xterm-mouse-mode etc.?
          (t
           (x-get-selection 'PRIMARY)))))
-    (if primary
-        (insert primary)
-      (error "No selection is available"))))
+    (unless primary
+      (error "No selection is available"))
+    (push-mark (point))
+    (insert primary)))
 
 (defun mouse-kill-ring-save (click)
   "Copy the region between point and the mouse click in the kill ring.
@@ -2076,12 +1951,14 @@ choose a font."
              (choice
               ;; Either choice == 'x-select-font, or choice is a
               ;; symbol whose name is a font.
-              (buffer-face-mode-invoke (font-face-attributes
-                                        (if (eq choice 'x-select-font)
-                                            (x-select-font)
-                                          (symbol-name choice)))
-                                       t
-                                       (called-interactively-p 'interactive))))))))
+              (let ((font (if (eq choice 'x-select-font)
+                              (x-select-font)
+                            (symbol-name choice))))
+                (buffer-face-mode-invoke
+                 (if (fontp font 'font-spec)
+                     (list :font font)
+                   (font-face-attributes font))
+                 t (called-interactively-p 'interactive)))))))))
 
 \f
 ;;; Bindings for mouse commands.