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