(toggle-scroll-bar): Call prefix-numeric-value.
[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
04c34f2e
RS
65(defun set-scroll-bar-mode (ignore value)
66 "Set `scroll-bar-mode' to VALUE and put the new value into effect."
67 (setq scroll-bar-mode value)
68
2124851f
RS
69 (when scroll-bar-mode-explicit
70 ;; Apply it to default-frame-alist.
71 (let ((parameter (assq 'vertical-scroll-bars default-frame-alist)))
72 (if (consp parameter)
73 (setcdr parameter scroll-bar-mode)
74 (setq default-frame-alist
75 (cons (cons 'vertical-scroll-bars scroll-bar-mode)
76 default-frame-alist))))
77
78 ;; Apply it to existing frames.
79 (let ((frames (frame-list)))
80 (while frames
81 (modify-frame-parameters
82 (car frames)
83 (list (cons 'vertical-scroll-bars scroll-bar-mode)))
84 (setq frames (cdr frames))))))
04c34f2e
RS
85
86(defcustom scroll-bar-mode 'left
87 "*Specify whether to have vertical scroll bars, and on which side.
88Possible values are nil (no scroll bars), `left' (scroll bars on left)
89and `right' (scroll bars on right).
90When you set the variable in a Lisp program, it takes effect for new frames,
91and for existing frames when `toggle-scroll-bar' is used.
92When you set this with the customization buffer,
93it takes effect immediately for all frames."
94 :type '(choice (const :tag "none (nil)")
95 (const left)
96 (const right))
97 :group 'frames
98 :set 'set-scroll-bar-mode)
99
2124851f
RS
100;; We just set scroll-bar-mode, but that was the default.
101;; If it is set again, that is for real.
102(setq scroll-bar-mode-explicit t)
103
fe48f821 104(defun scroll-bar-mode (flag)
04c34f2e 105 "Toggle display of vertical scroll bars on all frames.
fe48f821
JB
106This command applies to all frames that exist and frames to be
107created in the future.
108With a numeric argument, if the argument is negative,
109turn off scroll bars; otherwise, turn on scroll bars."
110 (interactive "P")
6ee9f953 111 (if flag (setq flag (prefix-numeric-value flag)))
ada464a7 112
04c34f2e
RS
113 ;; Tweedle the variable according to the argument.
114 (set-scroll-bar-mode nil
115 (if (null flag) (not scroll-bar-mode)
116 (and (or (not (numberp flag)) (>= flag 0))
117 'left))))
118
119(defun toggle-scroll-bar (arg)
120 "Toggle whether or not the selected frame has vertical scroll bars.
121With arg, turn vertical scroll bars on if and only if arg is positive.
122The variable `scroll-bar-mode' controls which side the scroll bars are on
123when they are turned on; if it is nil, they go on the left."
124 (interactive "P")
125 (if (null arg)
126 (setq arg
127 (if (cdr (assq 'vertical-scroll-bars
128 (frame-parameters (selected-frame))))
19c6feac
KH
129 -1 1))
130 (setq arg (prefix-numeric-value arg)))
04c34f2e
RS
131 (modify-frame-parameters (selected-frame)
132 (list (cons 'vertical-scroll-bars
133 (if (> arg 0)
134 (or scroll-bar-mode 'left))))))
135
136(defun toggle-horizontal-scroll-bar (arg)
137 "Toggle whether or not the selected frame has horizontal scroll bars.
138With arg, turn horizontal scroll bars on if and only if arg is positive.
139Horizontal scroll bars aren't implemented yet."
140 (interactive "P")
141 (error "Horizontal scroll bars aren't implemented yet"))
fe48f821 142\f
bf3c8a70 143;;;; Buffer navigation using the scroll bar.
6d62a90e 144
e532b016 145;;; This was used for up-events on button 2, but no longer.
bf3c8a70
JB
146(defun scroll-bar-set-window-start (event)
147 "Set the window start according to where the scroll bar is dragged.
148EVENT should be a scroll bar click or drag event."
6d62a90e 149 (interactive "e")
dbc4e1c1 150 (let* ((end-position (event-end event))
6d62a90e
JB
151 (window (nth 0 end-position))
152 (portion-whole (nth 2 end-position)))
153 (save-excursion
154 (set-buffer (window-buffer window))
155 (save-excursion
4cad38d5
JB
156 (goto-char (+ (point-min)
157 (scroll-bar-scale portion-whole
158 (- (point-max) (point-min)))))
6d62a90e
JB
159 (beginning-of-line)
160 (set-window-start window (point))))))
161
aeee66be
RS
162(defun scroll-bar-drag-position (portion-whole)
163 "Calculate new window start for drag event."
164 (save-excursion
165 (goto-char (+ (point-min)
166 (scroll-bar-scale portion-whole
167 (- (point-max) (point-min)))))
168 (beginning-of-line)
169 (point)))
170
171(defun scroll-bar-maybe-set-window-start (event)
172 "Set the window start according to where the scroll bar is dragged.
173Only change window start if the new start is substantially different.
174EVENT should be a scroll bar click or drag event."
175 (interactive "e")
176 (let* ((end-position (event-end event))
177 (window (nth 0 end-position))
178 (portion-whole (nth 2 end-position))
179 (next-portion-whole (cons (1+ (car portion-whole))
180 (cdr portion-whole)))
181 portion-start
182 next-portion-start
183 (current-start (window-start window)))
184 (save-excursion
185 (set-buffer (window-buffer window))
186 (setq portion-start (scroll-bar-drag-position portion-whole))
187 (setq next-portion-start (max
188 (scroll-bar-drag-position next-portion-whole)
189 (1+ portion-start)))
15655694 190 (if (or (>= current-start next-portion-start)
aeee66be 191 (< current-start portion-start))
9c005c65
KH
192 (set-window-start window portion-start)
193 ;; Always set window start, to ensure scroll bar position is updated.
194 (set-window-start window current-start)))))
aeee66be 195
e532b016
RS
196;; Scroll the window to the proper position for EVENT.
197(defun scroll-bar-drag-1 (event)
198 (let* ((start-position (event-start event))
199 (window (nth 0 start-position))
200 (portion-whole (nth 2 start-position)))
201 (save-excursion
202 (set-buffer (window-buffer window))
d2ae6f7e
RS
203 ;; Calculate position relative to the accessible part of the buffer.
204 (goto-char (+ (point-min)
205 (scroll-bar-scale portion-whole
206 (- (point-max) (point-min)))))
e532b016
RS
207 (beginning-of-line)
208 (set-window-start window (point)))))
209
210(defun scroll-bar-drag (event)
211 "Scroll the window by dragging the scroll bar slider.
212If you click outside the slider, the window scrolls to bring the slider there."
213 (interactive "e")
c782bea5
RS
214 (let* (done
215 (echo-keystrokes 0))
8f37c4f3
RS
216 (or point-before-scroll
217 (setq point-before-scroll (point)))
6538766a 218 ;; Our scrolling can move point; don't let that clear point-before-scroll.
8f37c4f3 219 (let (point-before-scroll)
6538766a 220 (scroll-bar-drag-1 event)
8f37c4f3
RS
221 (track-mouse
222 (while (not done)
223 (setq event (read-event))
224 (if (eq (car-safe event) 'mouse-movement)
225 (setq event (read-event)))
226 (cond ((eq (car-safe event) 'scroll-bar-movement)
227 (scroll-bar-drag-1 event))
228 (t
229 ;; Exit when we get the drag event; ignore that event.
6538766a
KH
230 (setq done t)))))
231 (sit-for 0))))
e532b016 232
bf3c8a70
JB
233(defun scroll-bar-scroll-down (event)
234 "Scroll the window's top line down to the location of the scroll bar click.
235EVENT should be a scroll bar click."
6d62a90e
JB
236 (interactive "e")
237 (let ((old-selected-window (selected-window)))
238 (unwind-protect
239 (progn
dbc4e1c1 240 (let* ((end-position (event-end event))
6d62a90e
JB
241 (window (nth 0 end-position))
242 (portion-whole (nth 2 end-position)))
8f37c4f3
RS
243 (let (point-before-scroll)
244 (select-window window))
245 (or point-before-scroll
246 (setq point-before-scroll (point)))
247 (let (point-before-scroll)
248 (scroll-down
249 (scroll-bar-scale portion-whole (1- (window-height)))))))
6d62a90e
JB
250 (select-window old-selected-window))))
251
bf3c8a70
JB
252(defun scroll-bar-scroll-up (event)
253 "Scroll the line next to the scroll bar click to the top of the window.
254EVENT should be a scroll bar click."
6d62a90e
JB
255 (interactive "e")
256 (let ((old-selected-window (selected-window)))
257 (unwind-protect
258 (progn
dbc4e1c1 259 (let* ((end-position (event-end event))
6d62a90e
JB
260 (window (nth 0 end-position))
261 (portion-whole (nth 2 end-position)))
8f37c4f3
RS
262 (let (point-before-scroll)
263 (select-window window))
264 (or point-before-scroll
265 (setq point-before-scroll (point)))
266 (let (point-before-scroll)
267 (scroll-up
268 (scroll-bar-scale portion-whole (1- (window-height)))))))
6d62a90e
JB
269 (select-window old-selected-window))))
270
271\f
272;;;; Bindings.
273
274;;; For now, we'll set things up to work like xterm.
bf3c8a70
JB
275(global-set-key [vertical-scroll-bar mouse-1] 'scroll-bar-scroll-up)
276(global-set-key [vertical-scroll-bar drag-mouse-1] 'scroll-bar-scroll-up)
6d62a90e 277
e532b016
RS
278(global-set-key [vertical-scroll-bar down-mouse-2] 'scroll-bar-drag)
279
bf3c8a70
JB
280(global-set-key [vertical-scroll-bar mouse-3] 'scroll-bar-scroll-down)
281(global-set-key [vertical-scroll-bar drag-mouse-3] 'scroll-bar-scroll-down)
6d62a90e
JB
282
283\f
dc14eed2 284(provide 'scroll-bar)
6d62a90e 285
bf3c8a70 286;;; scroll-bar.el ends here