(mouse-major-mode-menu, mouse-popup-menubar, mouse-popup-menubar-stuff):
[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,
409cc4a3 4;; 2005, 2006, 2007, 2008 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
b4aa6026 12;; the Free Software Foundation; either version 3, or (at your option)
a32b7419
WP
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)))))
05786f2d
CY
193 (buffer (window-buffer curwin))
194 (opoint (with-current-buffer buffer
195 (when (eq (car-safe transient-mark-mode) 'only)
196 (point))))
141ddc20
SM
197 (mods
198 (delq 'click (delq 'double (delq 'triple (event-modifiers event)))))
eb4504e0
SM
199 (amt (assoc mods mouse-wheel-scroll-amount)))
200 ;; Extract the actual amount or find the element that has no modifiers.
201 (if amt (setq amt (cdr amt))
202 (let ((list-elt mouse-wheel-scroll-amount))
203 (while (consp (setq amt (pop list-elt))))))
141ddc20 204 (if (floatp amt) (setq amt (1+ (truncate (* amt (window-height))))))
d1ff89d9 205 (when (and mouse-wheel-progressive-speed (numberp amt))
141ddc20 206 ;; When the double-mouse-N comes in, a mouse-N has been executed already,
bb5d43fe 207 ;; So by adding things up we get a squaring up (1, 3, 6, 10, 15, ...).
141ddc20 208 (setq amt (* amt (event-click-count event))))
a32b7419 209 (unwind-protect
141ddc20 210 (let ((button (mwheel-event-button event)))
44a50ffd
SM
211 (cond ((eq button mouse-wheel-down-event)
212 (condition-case nil (scroll-down amt)
213 ;; Make sure we do indeed scroll to the beginning of
214 ;; the buffer.
215 (beginning-of-buffer
216 (unwind-protect
217 (scroll-down)
218 ;; If the first scroll succeeded, then some scrolling
219 ;; is possible: keep scrolling til the beginning but
220 ;; do not signal an error. For some reason, we have
221 ;; to do it even if the first scroll signalled an
222 ;; error, because otherwise the window is recentered
223 ;; for a reason that escapes me. This problem seems
224 ;; to only affect scroll-down. --Stef
225 (set-window-start (selected-window) (point-min))))))
226 ((eq button mouse-wheel-up-event)
227 (condition-case nil (scroll-up amt)
228 ;; Make sure we do indeed scroll to the end of the buffer.
229 (end-of-buffer (while t (scroll-up)))))
141ddc20 230 (t (error "Bad binding in mwheel-scroll"))))
05786f2d
CY
231 (if curwin (select-window curwin)))
232 ;; If there is a temporarily active region, deactivate it iff
233 ;; scrolling moves point.
234 (when opoint
235 (with-current-buffer buffer
236 (when (/= opoint (point))
237 (deactivate-mark)))))
d09696f7
KS
238 (when (and mouse-wheel-click-event mouse-wheel-inhibit-click-time)
239 (if mwheel-inhibit-click-event-timer
240 (cancel-timer mwheel-inhibit-click-event-timer)
241 (add-hook 'pre-command-hook 'mwheel-filter-click-events))
f1180544 242 (setq mwheel-inhibit-click-event-timer
d09696f7
KS
243 (run-with-timer mouse-wheel-inhibit-click-time nil
244 'mwheel-inhibit-click-timeout))))
f4cbc7a0 245
a32b7419 246;;;###autoload
f4cbc7a0
MB
247(define-minor-mode mouse-wheel-mode
248 "Toggle mouse wheel support.
249With prefix argument ARG, turn on if positive, otherwise off.
a6d48e09 250Return non-nil if the new state is enabled."
f4cbc7a0
MB
251 :global t
252 :group 'mouse
bb5d43fe
SM
253 (let* ((dn mouse-wheel-down-event)
254 (up mouse-wheel-up-event)
141ddc20 255 (keys
eb4504e0
SM
256 (nconc (mapcar (lambda (amt) `[(,@(if (consp amt) (car amt)) ,up)])
257 mouse-wheel-scroll-amount)
258 (mapcar (lambda (amt) `[(,@(if (consp amt) (car amt)) ,dn)])
259 mouse-wheel-scroll-amount))))
141ddc20
SM
260 ;; This condition-case is here because Emacs 19 will throw an error
261 ;; if you try to define a key that it does not know about. I for one
262 ;; prefer to just unconditionally do a mwheel-install in my .emacs, so
263 ;; that if the wheeled-mouse is there, it just works, and this way it
264 ;; doesn't yell at me if I'm on my laptop or another machine, etc.
265 (condition-case ()
266 (dolist (key keys)
267 (cond (mouse-wheel-mode
268 (global-set-key key 'mwheel-scroll))
269 ((eq (lookup-key (current-global-map) key) 'mwheel-scroll)
270 (global-unset-key key))))
271 (error nil))))
f4cbc7a0
MB
272
273;;; Compatibility entry point
274;;;###autoload
275(defun mwheel-install (&optional uninstall)
276 "Enable mouse wheel support."
bb5d43fe 277 (mouse-wheel-mode (if uninstall -1 1)))
f4cbc7a0 278
a32b7419
WP
279(provide 'mwheel)
280
d1ff89d9 281;; arch-tag: 50ed00e7-3686-4b7a-8037-fb31aa5c237f
a32b7419 282;;; mwheel.el ends here