Provide additional default values (directories at other Dired
[bpt/emacs.git] / lisp / mwheel.el
CommitLineData
d09696f7 1;;; mwheel.el --- Wheel mouse support
a32b7419 2
fc926716 3;; Copyright (C) 1998, 2000, 2001, 2002, 2002, 2004, 2005, 2006, 2007,
ae940284 4;; 2008, 2009 Free Software Foundation, Inc.
a32b7419
WP
5;; Maintainer: William M. Perry <wmperry@gnu.org>
6;; Keywords: mouse
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
141ddc20
SM
182(defun mwheel-scroll (event)
183 "Scroll up or down according to the EVENT.
184This should only be bound to mouse buttons 4 and 5."
bb5d43fe 185 (interactive (list last-input-event))
141ddc20
SM
186 (let* ((curwin (if mouse-wheel-follow-mouse
187 (prog1
188 (selected-window)
189 (select-window (mwheel-event-window event)))))
05786f2d
CY
190 (buffer (window-buffer curwin))
191 (opoint (with-current-buffer buffer
192 (when (eq (car-safe transient-mark-mode) 'only)
193 (point))))
141ddc20
SM
194 (mods
195 (delq 'click (delq 'double (delq 'triple (event-modifiers event)))))
eb4504e0
SM
196 (amt (assoc mods mouse-wheel-scroll-amount)))
197 ;; Extract the actual amount or find the element that has no modifiers.
198 (if amt (setq amt (cdr amt))
199 (let ((list-elt mouse-wheel-scroll-amount))
200 (while (consp (setq amt (pop list-elt))))))
141ddc20 201 (if (floatp amt) (setq amt (1+ (truncate (* amt (window-height))))))
d1ff89d9 202 (when (and mouse-wheel-progressive-speed (numberp amt))
141ddc20 203 ;; When the double-mouse-N comes in, a mouse-N has been executed already,
bb5d43fe 204 ;; So by adding things up we get a squaring up (1, 3, 6, 10, 15, ...).
141ddc20 205 (setq amt (* amt (event-click-count event))))
a32b7419 206 (unwind-protect
141ddc20 207 (let ((button (mwheel-event-button event)))
44a50ffd
SM
208 (cond ((eq button mouse-wheel-down-event)
209 (condition-case nil (scroll-down amt)
210 ;; Make sure we do indeed scroll to the beginning of
211 ;; the buffer.
212 (beginning-of-buffer
213 (unwind-protect
214 (scroll-down)
215 ;; If the first scroll succeeded, then some scrolling
216 ;; is possible: keep scrolling til the beginning but
217 ;; do not signal an error. For some reason, we have
04bf5b65 218 ;; to do it even if the first scroll signaled an
44a50ffd
SM
219 ;; error, because otherwise the window is recentered
220 ;; for a reason that escapes me. This problem seems
221 ;; to only affect scroll-down. --Stef
222 (set-window-start (selected-window) (point-min))))))
223 ((eq button mouse-wheel-up-event)
224 (condition-case nil (scroll-up amt)
225 ;; Make sure we do indeed scroll to the end of the buffer.
226 (end-of-buffer (while t (scroll-up)))))
141ddc20 227 (t (error "Bad binding in mwheel-scroll"))))
05786f2d
CY
228 (if curwin (select-window curwin)))
229 ;; If there is a temporarily active region, deactivate it iff
230 ;; scrolling moves point.
231 (when opoint
232 (with-current-buffer buffer
233 (when (/= opoint (point))
234 (deactivate-mark)))))
d09696f7
KS
235 (when (and mouse-wheel-click-event mouse-wheel-inhibit-click-time)
236 (if mwheel-inhibit-click-event-timer
237 (cancel-timer mwheel-inhibit-click-event-timer)
238 (add-hook 'pre-command-hook 'mwheel-filter-click-events))
f1180544 239 (setq mwheel-inhibit-click-event-timer
d09696f7
KS
240 (run-with-timer mouse-wheel-inhibit-click-time nil
241 'mwheel-inhibit-click-timeout))))
f4cbc7a0 242
ab5c0fcd
SM
243(defvar mwheel-installed-bindings nil)
244
0a4afea9 245;; preloaded ;;;###autoload
f4cbc7a0
MB
246(define-minor-mode mouse-wheel-mode
247 "Toggle mouse wheel support.
248With prefix argument ARG, turn on if positive, otherwise off.
a6d48e09 249Return non-nil if the new state is enabled."
ab5c0fcd
SM
250 :init-value t
251 ;; We'd like to use custom-initialize-set here so the setup is done
252 ;; before dumping, but at the point where the defcustom is evaluated,
253 ;; the corresponding function isn't defined yet, so
254 ;; custom-initialize-set signals an error.
255 :initialize 'custom-initialize-delay
f4cbc7a0
MB
256 :global t
257 :group 'mouse
ab5c0fcd
SM
258 ;; Remove previous bindings, if any.
259 (while mwheel-installed-bindings
260 (let ((key (pop mwheel-installed-bindings)))
261 (when (eq (lookup-key (current-global-map) key) 'mwheel-scroll)
262 (global-unset-key key))))
263 ;; Setup bindings as needed.
264 (when mouse-wheel-mode
265 (dolist (event (list mouse-wheel-down-event mouse-wheel-up-event))
266 (dolist (key (mapcar (lambda (amt) `[(,@(if (consp amt) (car amt)) ,event)])
267 mouse-wheel-scroll-amount))
268 (global-set-key key 'mwheel-scroll)
269 (push key mwheel-installed-bindings)))))
f4cbc7a0
MB
270
271;;; Compatibility entry point
0a4afea9 272;; preloaded ;;;###autoload
f4cbc7a0
MB
273(defun mwheel-install (&optional uninstall)
274 "Enable mouse wheel support."
bb5d43fe 275 (mouse-wheel-mode (if uninstall -1 1)))
f4cbc7a0 276
a32b7419
WP
277(provide 'mwheel)
278
d1ff89d9 279;; arch-tag: 50ed00e7-3686-4b7a-8037-fb31aa5c237f
a32b7419 280;;; mwheel.el ends here