(auto-mode-alist): Add .am as makefile-mode.
[bpt/emacs.git] / lisp / scroll-bar.el
CommitLineData
aae56ea7 1;;; scroll-bar.el --- window system-independent scroll bar support.
6d62a90e 2
b578f267 3;; Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
6d62a90e
JB
4
5;; Maintainer: FSF
6;; Keywords: hardware
7
b578f267 8;; This file is part of GNU Emacs.
6d62a90e 9
b578f267
EN
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
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
6d62a90e 14
b578f267
EN
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.
6d62a90e 19
b578f267
EN
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs; see the file COPYING. If not, write to the
22;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23;; Boston, MA 02111-1307, USA.
76550a57 24
d9ecc911
ER
25;;; Commentary:
26
27;; Window-system-independent bindings of mouse clicks on the scroll bar.
28;; Presently emulates the scroll-bar behavior of xterm.
b578f267 29
d9ecc911
ER
30;;; Code:
31
dbc4e1c1
JB
32(require 'mouse)
33
6d62a90e
JB
34\f
35;;;; Utilities.
36
86828678
RS
37(defun scroll-bar-event-ratio (event)
38 "Given a scroll bar event EVENT, return the scroll bar position as a ratio.
39The value is a cons cell (PORTION . WHOLE) containing two integers
40whose ratio gives the event's vertical position in the scroll bar, with 0
41referring to the top and 1 to the bottom."
42 (nth 2 event))
43
bf3c8a70 44(defun scroll-bar-scale (num-denom whole)
6d62a90e 45 "Given a pair (NUM . DENOM) and WHOLE, return (/ (* NUM WHOLE) DENOM).
bf3c8a70
JB
46This is handy for scaling a position on a scroll bar into real units,
47like buffer positions. If SCROLL-BAR-POS is the (PORTION . WHOLE) pair
48from a scroll bar event, then (scroll-bar-scale SCROLL-BAR-POS
6d62a90e 49\(buffer-size)) is the position in the current buffer corresponding to
bf3c8a70 50that scroll bar position."
6d62a90e
JB
51 ;; We multiply before we divide to maintain precision.
52 ;; We use floating point because the product of a large buffer size
bf3c8a70 53 ;; with a large scroll bar portion can easily overflow a lisp int.
6d62a90e
JB
54 (truncate (/ (* (float (car num-denom)) whole) (cdr num-denom))))
55
56\f
fe48f821 57;;;; Helpful functions for enabling and disabling scroll bars.
fe48f821 58
1064fe38
RS
59(defvar scroll-bar-mode)
60
2124851f
RS
61(defvar scroll-bar-mode-explicit nil
62 "Non-nil means `set-scroll-bar-mode' should really do something.
63This is nil while loading `scroll-bar.el', and t afterward.")
64
e4b201bb
KH
65(defun set-scroll-bar-mode-1 (ignore value)
66 (set-scroll-bar-mode value))
67
68(defun set-scroll-bar-mode (value)
04c34f2e
RS
69 "Set `scroll-bar-mode' to VALUE and put the new value into effect."
70 (setq scroll-bar-mode value)
71
2124851f
RS
72 (when scroll-bar-mode-explicit
73 ;; Apply it to default-frame-alist.
74 (let ((parameter (assq 'vertical-scroll-bars default-frame-alist)))
75 (if (consp parameter)
76 (setcdr parameter scroll-bar-mode)
77 (setq default-frame-alist
78 (cons (cons 'vertical-scroll-bars scroll-bar-mode)
79 default-frame-alist))))
80
81 ;; Apply it to existing frames.
82 (let ((frames (frame-list)))
83 (while frames
84 (modify-frame-parameters
85 (car frames)
86 (list (cons 'vertical-scroll-bars scroll-bar-mode)))
87 (setq frames (cdr frames))))))
04c34f2e
RS
88
89(defcustom scroll-bar-mode 'left
90 "*Specify whether to have vertical scroll bars, and on which side.
91Possible values are nil (no scroll bars), `left' (scroll bars on left)
92and `right' (scroll bars on right).
93When you set the variable in a Lisp program, it takes effect for new frames,
94and for existing frames when `toggle-scroll-bar' is used.
95When you set this with the customization buffer,
96it takes effect immediately for all frames."
97 :type '(choice (const :tag "none (nil)")
98 (const left)
99 (const right))
100 :group 'frames
e4b201bb 101 :set 'set-scroll-bar-mode-1)
04c34f2e 102
2124851f
RS
103;; We just set scroll-bar-mode, but that was the default.
104;; If it is set again, that is for real.
105(setq scroll-bar-mode-explicit t)
106
fe48f821 107(defun scroll-bar-mode (flag)
04c34f2e 108 "Toggle display of vertical scroll bars on all frames.
fe48f821
JB
109This command applies to all frames that exist and frames to be
110created in the future.
111With a numeric argument, if the argument is negative,
112turn off scroll bars; otherwise, turn on scroll bars."
113 (interactive "P")
6ee9f953 114 (if flag (setq flag (prefix-numeric-value flag)))
ada464a7 115
04c34f2e 116 ;; Tweedle the variable according to the argument.
e4b201bb 117 (set-scroll-bar-mode (if (null flag) (not scroll-bar-mode)
04c34f2e
RS
118 (and (or (not (numberp flag)) (>= flag 0))
119 'left))))
120
121(defun toggle-scroll-bar (arg)
122 "Toggle whether or not the selected frame has vertical scroll bars.
123With arg, turn vertical scroll bars on if and only if arg is positive.
124The variable `scroll-bar-mode' controls which side the scroll bars are on
125when they are turned on; if it is nil, they go on the left."
126 (interactive "P")
127 (if (null arg)
128 (setq arg
129 (if (cdr (assq 'vertical-scroll-bars
130 (frame-parameters (selected-frame))))
19c6feac
KH
131 -1 1))
132 (setq arg (prefix-numeric-value arg)))
04c34f2e
RS
133 (modify-frame-parameters (selected-frame)
134 (list (cons 'vertical-scroll-bars
135 (if (> arg 0)
136 (or scroll-bar-mode 'left))))))
137
138(defun toggle-horizontal-scroll-bar (arg)
139 "Toggle whether or not the selected frame has horizontal scroll bars.
140With arg, turn horizontal scroll bars on if and only if arg is positive.
141Horizontal scroll bars aren't implemented yet."
142 (interactive "P")
143 (error "Horizontal scroll bars aren't implemented yet"))
fe48f821 144\f
bf3c8a70 145;;;; Buffer navigation using the scroll bar.
6d62a90e 146
e532b016 147;;; This was used for up-events on button 2, but no longer.
bf3c8a70
JB
148(defun scroll-bar-set-window-start (event)
149 "Set the window start according to where the scroll bar is dragged.
150EVENT should be a scroll bar click or drag event."
6d62a90e 151 (interactive "e")
dbc4e1c1 152 (let* ((end-position (event-end event))
6d62a90e
JB
153 (window (nth 0 end-position))
154 (portion-whole (nth 2 end-position)))
155 (save-excursion
156 (set-buffer (window-buffer window))
157 (save-excursion
4cad38d5
JB
158 (goto-char (+ (point-min)
159 (scroll-bar-scale portion-whole
160 (- (point-max) (point-min)))))
6d62a90e
JB
161 (beginning-of-line)
162 (set-window-start window (point))))))
163
aeee66be
RS
164(defun scroll-bar-drag-position (portion-whole)
165 "Calculate new window start for drag event."
166 (save-excursion
167 (goto-char (+ (point-min)
168 (scroll-bar-scale portion-whole
169 (- (point-max) (point-min)))))
170 (beginning-of-line)
171 (point)))
172
173(defun scroll-bar-maybe-set-window-start (event)
174 "Set the window start according to where the scroll bar is dragged.
175Only change window start if the new start is substantially different.
176EVENT should be a scroll bar click or drag event."
177 (interactive "e")
178 (let* ((end-position (event-end event))
179 (window (nth 0 end-position))
180 (portion-whole (nth 2 end-position))
181 (next-portion-whole (cons (1+ (car portion-whole))
182 (cdr portion-whole)))
183 portion-start
184 next-portion-start
185 (current-start (window-start window)))
186 (save-excursion
187 (set-buffer (window-buffer window))
188 (setq portion-start (scroll-bar-drag-position portion-whole))
189 (setq next-portion-start (max
190 (scroll-bar-drag-position next-portion-whole)
191 (1+ portion-start)))
15655694 192 (if (or (>= current-start next-portion-start)
aeee66be 193 (< current-start portion-start))
9c005c65
KH
194 (set-window-start window portion-start)
195 ;; Always set window start, to ensure scroll bar position is updated.
196 (set-window-start window current-start)))))
aeee66be 197
e532b016
RS
198;; Scroll the window to the proper position for EVENT.
199(defun scroll-bar-drag-1 (event)
200 (let* ((start-position (event-start event))
201 (window (nth 0 start-position))
202 (portion-whole (nth 2 start-position)))
203 (save-excursion
204 (set-buffer (window-buffer window))
d2ae6f7e
RS
205 ;; Calculate position relative to the accessible part of the buffer.
206 (goto-char (+ (point-min)
207 (scroll-bar-scale portion-whole
208 (- (point-max) (point-min)))))
e532b016
RS
209 (beginning-of-line)
210 (set-window-start window (point)))))
211
212(defun scroll-bar-drag (event)
213 "Scroll the window by dragging the scroll bar slider.
214If you click outside the slider, the window scrolls to bring the slider there."
215 (interactive "e")
c782bea5
RS
216 (let* (done
217 (echo-keystrokes 0))
8f37c4f3
RS
218 (or point-before-scroll
219 (setq point-before-scroll (point)))
6538766a 220 ;; Our scrolling can move point; don't let that clear point-before-scroll.
8f37c4f3 221 (let (point-before-scroll)
6538766a 222 (scroll-bar-drag-1 event)
8f37c4f3
RS
223 (track-mouse
224 (while (not done)
225 (setq event (read-event))
226 (if (eq (car-safe event) 'mouse-movement)
227 (setq event (read-event)))
228 (cond ((eq (car-safe event) 'scroll-bar-movement)
229 (scroll-bar-drag-1 event))
230 (t
231 ;; Exit when we get the drag event; ignore that event.
6538766a
KH
232 (setq done t)))))
233 (sit-for 0))))
e532b016 234
bf3c8a70
JB
235(defun scroll-bar-scroll-down (event)
236 "Scroll the window's top line down to the location of the scroll bar click.
237EVENT should be a scroll bar click."
6d62a90e
JB
238 (interactive "e")
239 (let ((old-selected-window (selected-window)))
240 (unwind-protect
241 (progn
dbc4e1c1 242 (let* ((end-position (event-end event))
6d62a90e
JB
243 (window (nth 0 end-position))
244 (portion-whole (nth 2 end-position)))
8f37c4f3
RS
245 (let (point-before-scroll)
246 (select-window window))
247 (or point-before-scroll
248 (setq point-before-scroll (point)))
249 (let (point-before-scroll)
250 (scroll-down
251 (scroll-bar-scale portion-whole (1- (window-height)))))))
6d62a90e
JB
252 (select-window old-selected-window))))
253
bf3c8a70
JB
254(defun scroll-bar-scroll-up (event)
255 "Scroll the line next to the scroll bar click to the top of the window.
256EVENT should be a scroll bar click."
6d62a90e
JB
257 (interactive "e")
258 (let ((old-selected-window (selected-window)))
259 (unwind-protect
260 (progn
dbc4e1c1 261 (let* ((end-position (event-end event))
6d62a90e
JB
262 (window (nth 0 end-position))
263 (portion-whole (nth 2 end-position)))
8f37c4f3
RS
264 (let (point-before-scroll)
265 (select-window window))
266 (or point-before-scroll
267 (setq point-before-scroll (point)))
268 (let (point-before-scroll)
269 (scroll-up
270 (scroll-bar-scale portion-whole (1- (window-height)))))))
6d62a90e
JB
271 (select-window old-selected-window))))
272
273\f
274;;;; Bindings.
275
276;;; For now, we'll set things up to work like xterm.
bf3c8a70
JB
277(global-set-key [vertical-scroll-bar mouse-1] 'scroll-bar-scroll-up)
278(global-set-key [vertical-scroll-bar drag-mouse-1] 'scroll-bar-scroll-up)
6d62a90e 279
e532b016
RS
280(global-set-key [vertical-scroll-bar down-mouse-2] 'scroll-bar-drag)
281
bf3c8a70
JB
282(global-set-key [vertical-scroll-bar mouse-3] 'scroll-bar-scroll-down)
283(global-set-key [vertical-scroll-bar drag-mouse-3] 'scroll-bar-scroll-down)
6d62a90e
JB
284
285\f
dc14eed2 286(provide 'scroll-bar)
6d62a90e 287
bf3c8a70 288;;; scroll-bar.el ends here