dynwind fixes
[bpt/emacs.git] / lisp / mwheel.el
CommitLineData
d09696f7 1;;; mwheel.el --- Wheel mouse support
a32b7419 2
ba318903 3;; Copyright (C) 1998, 2000-2014 Free Software Foundation, Inc.
a32b7419
WP
4;; Maintainer: William M. Perry <wmperry@gnu.org>
5;; Keywords: mouse
bd78fa1d 6;; Package: emacs
a32b7419 7
8ed8f294 8;; This file is part of GNU Emacs.
a32b7419 9
eb3fa2cf 10;; GNU Emacs is free software: you can redistribute it and/or modify
8ed8f294 11;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
a32b7419 14
8ed8f294
GM
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
a32b7419
WP
19
20;; You should have received a copy of the GNU General Public License
eb3fa2cf 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
a32b7419 22
a32b7419
WP
23;;; Commentary:
24
25;; This code will enable the use of the infamous 'wheel' on the new
26;; crop of mice. Under XFree86 and the XSuSE X Servers, the wheel
27;; events are sent as button4/button5 events.
28
29;; I for one would prefer some way of converting the button4/button5
30;; events into different event types, like 'mwheel-up' or
31;; 'mwheel-down', but I cannot find a way to do this very easily (or
32;; portably), so for now I just live with it.
33
34;; To enable this code, simply put this at the top of your .emacs
35;; file:
36;;
abd01646 37;; (mouse-wheel-mode 1)
a32b7419
WP
38
39;;; Code:
40
41(require 'custom)
d09696f7 42(require 'timer)
a32b7419 43
0a4afea9 44(defvar mouse-wheel-mode)
8f825ee6 45
bf85004b
GM
46;; Setter function for mouse-button user-options. Switch Mouse Wheel
47;; mode off and on again so that the old button is unbound and
48;; new button is bound to mwheel-scroll.
49
50(defun mouse-wheel-change-button (var button)
ab5c0fcd
SM
51 (set-default var button)
52 ;; Sync the bindings.
0a4afea9 53 (when (bound-and-true-p mouse-wheel-mode) (mouse-wheel-mode 1)))
bf85004b 54
e5ab6ade
JB
55(defvar mouse-wheel-down-button 4)
56(make-obsolete-variable 'mouse-wheel-down-button
89ee0163
JB
57 'mouse-wheel-down-event
58 "22.1")
bb5d43fe 59(defcustom mouse-wheel-down-event
19261da4 60 (if (or (featurep 'w32-win) (featurep 'ns-win))
f4e62260 61 'wheel-up
19261da4 62 (intern (format "mouse-%s" mouse-wheel-down-button)))
bb5d43fe 63 "Event used for scrolling down."
bf85004b 64 :group 'mouse
bb5d43fe 65 :type 'symbol
bf85004b
GM
66 :set 'mouse-wheel-change-button)
67
e5ab6ade
JB
68(defvar mouse-wheel-up-button 5)
69(make-obsolete-variable 'mouse-wheel-up-button
89ee0163
JB
70 'mouse-wheel-up-event
71 "22.1")
bb5d43fe 72(defcustom mouse-wheel-up-event
19261da4 73 (if (or (featurep 'w32-win) (featurep 'ns-win))
f4e62260 74 'wheel-down
19261da4 75 (intern (format "mouse-%s" mouse-wheel-up-button)))
a348f5ba 76 "Event used for scrolling up."
bf85004b 77 :group 'mouse
bb5d43fe 78 :type 'symbol
bf85004b
GM
79 :set 'mouse-wheel-change-button)
80
e5ab6ade
JB
81(defvar mouse-wheel-click-button 2)
82(make-obsolete-variable 'mouse-wheel-click-button
89ee0163
JB
83 'mouse-wheel-click-event
84 "22.1")
d09696f7 85(defcustom mouse-wheel-click-event
19261da4 86 (intern (format "mouse-%s" mouse-wheel-click-button))
d09696f7
KS
87 "Event that should be temporarily inhibited after mouse scrolling.
88The mouse wheel is typically on the mouse-2 button, so it may easily
a6d48e09 89happen that text is accidentally yanked into the buffer when
d09696f7
KS
90scrolling with the mouse wheel. To prevent that, this variable can be
91set to the event sent when clicking on the mouse wheel button."
92 :group 'mouse
93 :type 'symbol
94 :set 'mouse-wheel-change-button)
95
96(defcustom mouse-wheel-inhibit-click-time 0.35
97 "Time in seconds to inhibit clicking on mouse wheel button after scroll."
98 :group 'mouse
68f2d641 99 :type 'number)
d09696f7 100
141ddc20 101(defcustom mouse-wheel-scroll-amount '(5 ((shift) . 1) ((control) . nil))
a32b7419 102 "Amount to scroll windows by when spinning the mouse wheel.
eb4504e0
SM
103This is an alist mapping the modifier key to the amount to scroll when
104the wheel is moved with the modifier key depressed.
105Elements of the list have the form (MODIFIERS . AMOUNT) or just AMOUNT if
106MODIFIERS is nil.
107
41bbbdce 108AMOUNT should be the number of lines to scroll, or nil for near full
eb4504e0
SM
109screen. It can also be a floating point number, specifying the fraction of
110a full screen to scroll. A near full screen is `next-screen-context-lines'
111less than a full screen."
a32b7419 112 :group 'mouse
141ddc20
SM
113 :type '(cons
114 (choice :tag "Normal"
a32b7419 115 (const :tag "Full screen" :value nil)
141ddc20 116 (integer :tag "Specific # of lines")
055016a4 117 (float :tag "Fraction of window")
eb4504e0
SM
118 (cons
119 (repeat (choice :tag "modifier"
120 (const alt) (const control) (const hyper)
121 (const meta) (const shift) (const super)))
122 (choice :tag "scroll amount"
123 (const :tag "Full screen" :value nil)
124 (integer :tag "Specific # of lines")
055016a4 125 (float :tag "Fraction of window"))))
141ddc20
SM
126 (repeat
127 (cons
eb4504e0
SM
128 (repeat (choice :tag "modifier"
129 (const alt) (const control) (const hyper)
141ddc20
SM
130 (const meta) (const shift) (const super)))
131 (choice :tag "scroll amount"
132 (const :tag "Full screen" :value nil)
133 (integer :tag "Specific # of lines")
0a4afea9
SM
134 (float :tag "Fraction of window")))))
135 :set 'mouse-wheel-change-button)
141ddc20 136
d1ff89d9 137(defcustom mouse-wheel-progressive-speed t
141ddc20
SM
138 "If non-nil, the faster the user moves the wheel, the faster the scrolling.
139Note that this has no effect when `mouse-wheel-scroll-amount' specifies
eb4504e0
SM
140a \"near full screen\" scroll or when the mouse wheel sends key instead
141of button events."
141ddc20
SM
142 :group 'mouse
143 :type 'boolean)
a32b7419 144
bb5d43fe 145(defcustom mouse-wheel-follow-mouse t
a32b7419 146 "Whether the mouse wheel should scroll the window that the mouse is over.
eb4504e0 147This can be slightly disconcerting, but some people prefer it."
a32b7419
WP
148 :group 'mouse
149 :type 'boolean)
150
fc926716
GM
151(eval-and-compile
152 (if (fboundp 'event-button)
153 (fset 'mwheel-event-button 'event-button)
141ddc20 154 (defun mwheel-event-button (event)
bb5d43fe 155 (let ((x (event-basic-type event)))
141ddc20 156 ;; Map mouse-wheel events to appropriate buttons
bb5d43fe 157 (if (eq 'mouse-wheel x)
141ddc20
SM
158 (let ((amount (car (cdr (cdr (cdr event))))))
159 (if (< amount 0)
bb5d43fe
SM
160 mouse-wheel-up-event
161 mouse-wheel-down-event))
fc926716 162 x))))
141ddc20 163
fc926716
GM
164 (if (fboundp 'event-window)
165 (fset 'mwheel-event-window 'event-window)
141ddc20 166 (defun mwheel-event-window (event)
fc926716 167 (posn-window (event-start event)))))
141ddc20 168
d09696f7
KS
169(defvar mwheel-inhibit-click-event-timer nil
170 "Timer running while mouse wheel click event is inhibited.")
171
172(defun mwheel-inhibit-click-timeout ()
173 "Handler for `mwheel-inhibit-click-event-timer'."
174 (setq mwheel-inhibit-click-event-timer nil)
175 (remove-hook 'pre-command-hook 'mwheel-filter-click-events))
176
177(defun mwheel-filter-click-events ()
178 "Discard `mouse-wheel-click-event' while scrolling the mouse."
179 (if (eq (event-basic-type last-input-event) mouse-wheel-click-event)
180 (setq this-command 'ignore)))
181
7d6b4d3c
JL
182(defvar mwheel-scroll-up-function 'scroll-up
183 "Function that does the job of scrolling upward.")
184
185(defvar mwheel-scroll-down-function 'scroll-down
186 "Function that does the job of scrolling downward.")
187
141ddc20
SM
188(defun mwheel-scroll (event)
189 "Scroll up or down according to the EVENT.
190This should only be bound to mouse buttons 4 and 5."
bb5d43fe 191 (interactive (list last-input-event))
141ddc20
SM
192 (let* ((curwin (if mouse-wheel-follow-mouse
193 (prog1
194 (selected-window)
195 (select-window (mwheel-event-window event)))))
05786f2d
CY
196 (buffer (window-buffer curwin))
197 (opoint (with-current-buffer buffer
198 (when (eq (car-safe transient-mark-mode) 'only)
199 (point))))
141ddc20
SM
200 (mods
201 (delq 'click (delq 'double (delq 'triple (event-modifiers event)))))
eb4504e0
SM
202 (amt (assoc mods mouse-wheel-scroll-amount)))
203 ;; Extract the actual amount or find the element that has no modifiers.
204 (if amt (setq amt (cdr amt))
205 (let ((list-elt mouse-wheel-scroll-amount))
206 (while (consp (setq amt (pop list-elt))))))
141ddc20 207 (if (floatp amt) (setq amt (1+ (truncate (* amt (window-height))))))
d1ff89d9 208 (when (and mouse-wheel-progressive-speed (numberp amt))
141ddc20 209 ;; When the double-mouse-N comes in, a mouse-N has been executed already,
bb5d43fe 210 ;; So by adding things up we get a squaring up (1, 3, 6, 10, 15, ...).
141ddc20 211 (setq amt (* amt (event-click-count event))))
a32b7419 212 (unwind-protect
141ddc20 213 (let ((button (mwheel-event-button event)))
44a50ffd 214 (cond ((eq button mouse-wheel-down-event)
7d6b4d3c 215 (condition-case nil (funcall mwheel-scroll-down-function amt)
44a50ffd
SM
216 ;; Make sure we do indeed scroll to the beginning of
217 ;; the buffer.
218 (beginning-of-buffer
219 (unwind-protect
7d6b4d3c 220 (funcall mwheel-scroll-down-function)
44a50ffd
SM
221 ;; If the first scroll succeeded, then some scrolling
222 ;; is possible: keep scrolling til the beginning but
223 ;; do not signal an error. For some reason, we have
04bf5b65 224 ;; to do it even if the first scroll signaled an
44a50ffd
SM
225 ;; error, because otherwise the window is recentered
226 ;; for a reason that escapes me. This problem seems
227 ;; to only affect scroll-down. --Stef
228 (set-window-start (selected-window) (point-min))))))
229 ((eq button mouse-wheel-up-event)
7d6b4d3c 230 (condition-case nil (funcall mwheel-scroll-up-function amt)
44a50ffd 231 ;; Make sure we do indeed scroll to the end of the buffer.
7d6b4d3c 232 (end-of-buffer (while t (funcall mwheel-scroll-up-function)))))
141ddc20 233 (t (error "Bad binding in mwheel-scroll"))))
05786f2d 234 (if curwin (select-window curwin)))
f06e2758 235 ;; If there is a temporarily active region, deactivate it if
05786f2d
CY
236 ;; scrolling moves point.
237 (when opoint
238 (with-current-buffer buffer
239 (when (/= opoint (point))
f06e2758
CY
240 ;; Call `deactivate-mark' at the original position, so that
241 ;; the original region is saved to the X selection.
242 (let ((newpoint (point)))
243 (goto-char opoint)
244 (deactivate-mark)
245 (goto-char newpoint))))))
d09696f7
KS
246 (when (and mouse-wheel-click-event mouse-wheel-inhibit-click-time)
247 (if mwheel-inhibit-click-event-timer
248 (cancel-timer mwheel-inhibit-click-event-timer)
249 (add-hook 'pre-command-hook 'mwheel-filter-click-events))
f1180544 250 (setq mwheel-inhibit-click-event-timer
d09696f7
KS
251 (run-with-timer mouse-wheel-inhibit-click-time nil
252 'mwheel-inhibit-click-timeout))))
f4cbc7a0 253
a4b000fb 254(put 'mwheel-scroll 'scroll-command t)
9013a7f8 255
ab5c0fcd
SM
256(defvar mwheel-installed-bindings nil)
257
f4cbc7a0 258(define-minor-mode mouse-wheel-mode
06e21633
CY
259 "Toggle mouse wheel support (Mouse Wheel mode).
260With a prefix argument ARG, enable Mouse Wheel mode if ARG is
261positive, and disable it otherwise. If called from Lisp, enable
262the mode if ARG is omitted or nil."
ab5c0fcd
SM
263 :init-value t
264 ;; We'd like to use custom-initialize-set here so the setup is done
265 ;; before dumping, but at the point where the defcustom is evaluated,
266 ;; the corresponding function isn't defined yet, so
267 ;; custom-initialize-set signals an error.
268 :initialize 'custom-initialize-delay
f4cbc7a0
MB
269 :global t
270 :group 'mouse
ab5c0fcd
SM
271 ;; Remove previous bindings, if any.
272 (while mwheel-installed-bindings
273 (let ((key (pop mwheel-installed-bindings)))
274 (when (eq (lookup-key (current-global-map) key) 'mwheel-scroll)
275 (global-unset-key key))))
276 ;; Setup bindings as needed.
277 (when mouse-wheel-mode
278 (dolist (event (list mouse-wheel-down-event mouse-wheel-up-event))
279 (dolist (key (mapcar (lambda (amt) `[(,@(if (consp amt) (car amt)) ,event)])
280 mouse-wheel-scroll-amount))
281 (global-set-key key 'mwheel-scroll)
282 (push key mwheel-installed-bindings)))))
f4cbc7a0
MB
283
284;;; Compatibility entry point
0a4afea9 285;; preloaded ;;;###autoload
f4cbc7a0
MB
286(defun mwheel-install (&optional uninstall)
287 "Enable mouse wheel support."
bb5d43fe 288 (mouse-wheel-mode (if uninstall -1 1)))
f4cbc7a0 289
a32b7419
WP
290(provide 'mwheel)
291
292;;; mwheel.el ends here