(define-minor-mode): Only preserve messages output during execution of the body.
[bpt/emacs.git] / lisp / mwheel.el
CommitLineData
d09696f7 1;;; mwheel.el --- Wheel mouse support
a32b7419 2
0d30b337 3;; Copyright (C) 1998, 2000, 2001, 2002, 2002, 2004,
aaef169d 4;; 2005, 2006 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
8ed8f294
GM
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
a32b7419
WP
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
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
8ed8f294 21;; along with GNU Emacs; see the file COPYING. If not, write to the
086add15
LK
22;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23;; Boston, MA 02110-1301, USA.
a32b7419 24
a32b7419
WP
25;;; Commentary:
26
27;; This code will enable the use of the infamous 'wheel' on the new
28;; crop of mice. Under XFree86 and the XSuSE X Servers, the wheel
29;; events are sent as button4/button5 events.
30
31;; I for one would prefer some way of converting the button4/button5
32;; events into different event types, like 'mwheel-up' or
33;; 'mwheel-down', but I cannot find a way to do this very easily (or
34;; portably), so for now I just live with it.
35
36;; To enable this code, simply put this at the top of your .emacs
37;; file:
38;;
abd01646 39;; (mouse-wheel-mode 1)
a32b7419
WP
40
41;;; Code:
42
43(require 'custom)
d09696f7 44(require 'timer)
a32b7419 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)
bb5d43fe
SM
51 (let ((active mouse-wheel-mode))
52 ;; Deactivate before changing the setting.
53 (when active (mouse-wheel-mode -1))
54 (set-default var button)
55 (when active (mouse-wheel-mode 1))))
bf85004b 56
e5ab6ade
JB
57(defvar mouse-wheel-down-button 4)
58(make-obsolete-variable 'mouse-wheel-down-button
59 'mouse-wheel-down-event)
bb5d43fe
SM
60(defcustom mouse-wheel-down-event
61 ;; In the latest versions of XEmacs, we could just use mouse-%s as well.
bf03bdf3 62 (if (memq window-system '(w32 mac))
f4e62260
JR
63 'wheel-up
64 (intern (format (if (featurep 'xemacs) "button%s" "mouse-%s")
65 mouse-wheel-down-button)))
bb5d43fe 66 "Event used for scrolling down."
bf85004b 67 :group 'mouse
bb5d43fe 68 :type 'symbol
bf85004b
GM
69 :set 'mouse-wheel-change-button)
70
e5ab6ade
JB
71(defvar mouse-wheel-up-button 5)
72(make-obsolete-variable 'mouse-wheel-up-button
73 'mouse-wheel-up-event)
bb5d43fe
SM
74(defcustom mouse-wheel-up-event
75 ;; In the latest versions of XEmacs, we could just use mouse-%s as well.
bf03bdf3 76 (if (memq window-system '(w32 mac))
f4e62260
JR
77 'wheel-down
78 (intern (format (if (featurep 'xemacs) "button%s" "mouse-%s")
79 mouse-wheel-up-button)))
a348f5ba 80 "Event used for scrolling up."
bf85004b 81 :group 'mouse
bb5d43fe 82 :type 'symbol
bf85004b
GM
83 :set 'mouse-wheel-change-button)
84
e5ab6ade
JB
85(defvar mouse-wheel-click-button 2)
86(make-obsolete-variable 'mouse-wheel-click-button
87 'mouse-wheel-click-event)
d09696f7
KS
88(defcustom mouse-wheel-click-event
89 ;; In the latest versions of XEmacs, we could just use mouse-%s as well.
90 (intern (format (if (featurep 'xemacs) "button%s" "mouse-%s")
91 mouse-wheel-click-button))
92 "Event that should be temporarily inhibited after mouse scrolling.
93The mouse wheel is typically on the mouse-2 button, so it may easily
a6d48e09 94happen that text is accidentally yanked into the buffer when
d09696f7
KS
95scrolling with the mouse wheel. To prevent that, this variable can be
96set to the event sent when clicking on the mouse wheel button."
97 :group 'mouse
98 :type 'symbol
99 :set 'mouse-wheel-change-button)
100
101(defcustom mouse-wheel-inhibit-click-time 0.35
102 "Time in seconds to inhibit clicking on mouse wheel button after scroll."
103 :group 'mouse
68f2d641 104 :type 'number)
d09696f7 105
141ddc20 106(defcustom mouse-wheel-scroll-amount '(5 ((shift) . 1) ((control) . nil))
a32b7419 107 "Amount to scroll windows by when spinning the mouse wheel.
eb4504e0
SM
108This is an alist mapping the modifier key to the amount to scroll when
109the wheel is moved with the modifier key depressed.
110Elements of the list have the form (MODIFIERS . AMOUNT) or just AMOUNT if
111MODIFIERS is nil.
112
41bbbdce 113AMOUNT should be the number of lines to scroll, or nil for near full
eb4504e0
SM
114screen. It can also be a floating point number, specifying the fraction of
115a full screen to scroll. A near full screen is `next-screen-context-lines'
116less than a full screen."
a32b7419 117 :group 'mouse
141ddc20
SM
118 :type '(cons
119 (choice :tag "Normal"
a32b7419 120 (const :tag "Full screen" :value nil)
141ddc20 121 (integer :tag "Specific # of lines")
055016a4 122 (float :tag "Fraction of window")
eb4504e0
SM
123 (cons
124 (repeat (choice :tag "modifier"
125 (const alt) (const control) (const hyper)
126 (const meta) (const shift) (const super)))
127 (choice :tag "scroll amount"
128 (const :tag "Full screen" :value nil)
129 (integer :tag "Specific # of lines")
055016a4 130 (float :tag "Fraction of window"))))
141ddc20
SM
131 (repeat
132 (cons
eb4504e0
SM
133 (repeat (choice :tag "modifier"
134 (const alt) (const control) (const hyper)
141ddc20
SM
135 (const meta) (const shift) (const super)))
136 (choice :tag "scroll amount"
137 (const :tag "Full screen" :value nil)
138 (integer :tag "Specific # of lines")
055016a4 139 (float :tag "Fraction of window"))))))
141ddc20 140
d1ff89d9 141(defcustom mouse-wheel-progressive-speed t
141ddc20
SM
142 "If non-nil, the faster the user moves the wheel, the faster the scrolling.
143Note that this has no effect when `mouse-wheel-scroll-amount' specifies
eb4504e0
SM
144a \"near full screen\" scroll or when the mouse wheel sends key instead
145of button events."
141ddc20
SM
146 :group 'mouse
147 :type 'boolean)
a32b7419 148
bb5d43fe 149(defcustom mouse-wheel-follow-mouse t
a32b7419 150 "Whether the mouse wheel should scroll the window that the mouse is over.
eb4504e0 151This can be slightly disconcerting, but some people prefer it."
a32b7419
WP
152 :group 'mouse
153 :type 'boolean)
154
141ddc20
SM
155(if (not (fboundp 'event-button))
156 (defun mwheel-event-button (event)
bb5d43fe 157 (let ((x (event-basic-type event)))
141ddc20 158 ;; Map mouse-wheel events to appropriate buttons
bb5d43fe 159 (if (eq 'mouse-wheel x)
141ddc20
SM
160 (let ((amount (car (cdr (cdr (cdr event))))))
161 (if (< amount 0)
bb5d43fe
SM
162 mouse-wheel-up-event
163 mouse-wheel-down-event))
164 x)))
94394914 165 (fset 'mwheel-event-button 'event-button))
141ddc20
SM
166
167(if (not (fboundp 'event-window))
168 (defun mwheel-event-window (event)
169 (posn-window (event-start event)))
170 (fset 'mwheel-event-window 'event-window))
171
d09696f7
KS
172(defvar mwheel-inhibit-click-event-timer nil
173 "Timer running while mouse wheel click event is inhibited.")
174
175(defun mwheel-inhibit-click-timeout ()
176 "Handler for `mwheel-inhibit-click-event-timer'."
177 (setq mwheel-inhibit-click-event-timer nil)
178 (remove-hook 'pre-command-hook 'mwheel-filter-click-events))
179
180(defun mwheel-filter-click-events ()
181 "Discard `mouse-wheel-click-event' while scrolling the mouse."
182 (if (eq (event-basic-type last-input-event) mouse-wheel-click-event)
183 (setq this-command 'ignore)))
184
141ddc20
SM
185(defun mwheel-scroll (event)
186 "Scroll up or down according to the EVENT.
187This should only be bound to mouse buttons 4 and 5."
bb5d43fe 188 (interactive (list last-input-event))
141ddc20
SM
189 (let* ((curwin (if mouse-wheel-follow-mouse
190 (prog1
191 (selected-window)
192 (select-window (mwheel-event-window event)))))
193 (mods
194 (delq 'click (delq 'double (delq 'triple (event-modifiers event)))))
eb4504e0
SM
195 (amt (assoc mods mouse-wheel-scroll-amount)))
196 ;; Extract the actual amount or find the element that has no modifiers.
197 (if amt (setq amt (cdr amt))
198 (let ((list-elt mouse-wheel-scroll-amount))
199 (while (consp (setq amt (pop list-elt))))))
141ddc20 200 (if (floatp amt) (setq amt (1+ (truncate (* amt (window-height))))))
d1ff89d9 201 (when (and mouse-wheel-progressive-speed (numberp amt))
141ddc20 202 ;; When the double-mouse-N comes in, a mouse-N has been executed already,
bb5d43fe 203 ;; So by adding things up we get a squaring up (1, 3, 6, 10, 15, ...).
141ddc20 204 (setq amt (* amt (event-click-count event))))
a32b7419 205 (unwind-protect
141ddc20 206 (let ((button (mwheel-event-button event)))
bb5d43fe
SM
207 (cond ((eq button mouse-wheel-down-event) (scroll-down amt))
208 ((eq button mouse-wheel-up-event) (scroll-up amt))
141ddc20 209 (t (error "Bad binding in mwheel-scroll"))))
d09696f7
KS
210 (if curwin (select-window curwin))))
211 (when (and mouse-wheel-click-event mouse-wheel-inhibit-click-time)
212 (if mwheel-inhibit-click-event-timer
213 (cancel-timer mwheel-inhibit-click-event-timer)
214 (add-hook 'pre-command-hook 'mwheel-filter-click-events))
f1180544 215 (setq mwheel-inhibit-click-event-timer
d09696f7
KS
216 (run-with-timer mouse-wheel-inhibit-click-time nil
217 'mwheel-inhibit-click-timeout))))
f4cbc7a0 218
a32b7419 219;;;###autoload
f4cbc7a0
MB
220(define-minor-mode mouse-wheel-mode
221 "Toggle mouse wheel support.
222With prefix argument ARG, turn on if positive, otherwise off.
a6d48e09 223Return non-nil if the new state is enabled."
f4cbc7a0
MB
224 :global t
225 :group 'mouse
bb5d43fe
SM
226 (let* ((dn mouse-wheel-down-event)
227 (up mouse-wheel-up-event)
141ddc20 228 (keys
eb4504e0
SM
229 (nconc (mapcar (lambda (amt) `[(,@(if (consp amt) (car amt)) ,up)])
230 mouse-wheel-scroll-amount)
231 (mapcar (lambda (amt) `[(,@(if (consp amt) (car amt)) ,dn)])
232 mouse-wheel-scroll-amount))))
141ddc20
SM
233 ;; This condition-case is here because Emacs 19 will throw an error
234 ;; if you try to define a key that it does not know about. I for one
235 ;; prefer to just unconditionally do a mwheel-install in my .emacs, so
236 ;; that if the wheeled-mouse is there, it just works, and this way it
237 ;; doesn't yell at me if I'm on my laptop or another machine, etc.
238 (condition-case ()
239 (dolist (key keys)
240 (cond (mouse-wheel-mode
241 (global-set-key key 'mwheel-scroll))
242 ((eq (lookup-key (current-global-map) key) 'mwheel-scroll)
243 (global-unset-key key))))
244 (error nil))))
f4cbc7a0
MB
245
246;;; Compatibility entry point
247;;;###autoload
248(defun mwheel-install (&optional uninstall)
249 "Enable mouse wheel support."
bb5d43fe 250 (mouse-wheel-mode (if uninstall -1 1)))
f4cbc7a0 251
a32b7419
WP
252(provide 'mwheel)
253
d1ff89d9 254;; arch-tag: 50ed00e7-3686-4b7a-8037-fb31aa5c237f
a32b7419 255;;; mwheel.el ends here