;;; scroll-bar.el --- window system-independent scroll bar support
-;; Copyright (C) 1993-1995, 1999-2015 Free Software Foundation, Inc.
+;; Copyright (C) 1993-1995, 1999-2016 Free Software Foundation, Inc.
;; Maintainer: emacs-devel@gnu.org
;; Keywords: hardware
(frame-char-width)))
(0))))
+(defun scroll-bar-lines ()
+ "Return the height, measured in lines, of the horizontal scrollbar."
+ (let* ((wsb (window-scroll-bars))
+ (htype (nth 5 wsb))
+ (lines (nth 4 wsb)))
+ (cond
+ (htype lines)
+ ((frame-parameter nil 'horizontal-scroll-bars)
+ ;; nil means it's a non-toolkit scroll bar (which is currently
+ ;; impossible), and its height in lines is 14 pixels rounded up.
+ (ceiling (or (frame-parameter nil 'scroll-bar-height) 14)
+ (frame-char-width)))
+ (0))))
+
\f
;;;; Helpful functions for enabling and disabling scroll bars.
(defvar scroll-bar-mode)
+(defvar horizontal-scroll-bar-mode)
(defvar previous-scroll-bar-mode nil)
(defvar scroll-bar-mode-explicit nil
created in the future."
:variable ((get-scroll-bar-mode)
. (lambda (v) (set-scroll-bar-mode
- (if v (or previous-scroll-bar-mode
- default-frame-scroll-bars))))))
+ (if v (or previous-scroll-bar-mode
+ default-frame-scroll-bars))))))
+
+(defun horizontal-scroll-bars-available-p ()
+ "Return non-nil when horizontal scroll bars are available on this system."
+ (and (display-graphic-p)
+ (boundp 'x-toolkit-scroll-bars)
+ x-toolkit-scroll-bars
+ (not (eq (window-system) 'ns))))
+
+(define-minor-mode horizontal-scroll-bar-mode
+ "Toggle horizontal scroll bars on all frames (Horizontal Scroll Bar mode).
+With a prefix argument ARG, enable Horizontal Scroll Bar mode if
+ARG is positive, and disable it otherwise. If called from Lisp,
+enable the mode if ARG is omitted or nil.
+
+This command applies to all frames that exist and frames to be
+created in the future."
+ :init-value nil
+ :global t
+ :group 'frames
+ (if (and horizontal-scroll-bar-mode
+ (not (horizontal-scroll-bars-available-p)))
+ (progn
+ (setq horizontal-scroll-bar-mode nil)
+ (message "Horizontal scroll bars are not implemented on this system"))
+ (dolist (frame (frame-list))
+ (set-frame-parameter
+ frame 'horizontal-scroll-bars horizontal-scroll-bar-mode))
+ ;; Handle `default-frame-alist' entry.
+ (setq default-frame-alist
+ (cons (cons 'horizontal-scroll-bars horizontal-scroll-bar-mode)
+ (assq-delete-all 'horizontal-scroll-bars
+ default-frame-alist)))))
(defun toggle-scroll-bar (arg)
"Toggle whether or not the selected frame has vertical scroll bars.
-With arg, turn vertical scroll bars on if and only if arg is positive.
+With ARG, turn vertical scroll bars on if and only if ARG is positive.
The variable `scroll-bar-mode' controls which side the scroll bars are on
when they are turned on; if it is nil, they go on the left."
(interactive "P")
(if (> arg 0)
(or scroll-bar-mode default-frame-scroll-bars))))))
-(defun toggle-horizontal-scroll-bar (_arg)
+(defun toggle-horizontal-scroll-bar (arg)
"Toggle whether or not the selected frame has horizontal scroll bars.
-With arg, turn horizontal scroll bars on if and only if arg is positive.
-Horizontal scroll bars aren't implemented yet."
+With ARG, turn vertical scroll bars on if and only if ARG is positive."
(interactive "P")
- (error "Horizontal scroll bars aren't implemented yet"))
+ (if (null arg)
+ (setq arg
+ (if (cdr (assq 'horizontal-scroll-bars
+ (frame-parameters (selected-frame))))
+ -1 1))
+ (setq arg (prefix-numeric-value arg)))
+ (modify-frame-parameters
+ (selected-frame)
+ (list (cons 'horizontal-scroll-bars
+ (when (> arg 0) 'bottom)))))
\f
;;;; Buffer navigation using the scroll bar.
(with-current-buffer (window-buffer window)
(setq point-before-scroll before-scroll))))
+;; Scroll the window to the proper position for EVENT.
+(defun scroll-bar-horizontal-drag-1 (event)
+ (let* ((start-position (event-start event))
+ (window (nth 0 start-position))
+ (portion-whole (nth 2 start-position))
+ (unit (frame-char-width (window-frame window))))
+ (if (eq (current-bidi-paragraph-direction (window-buffer window))
+ 'left-to-right)
+ (set-window-hscroll
+ window (/ (+ (car portion-whole) (1- unit)) unit))
+ (set-window-hscroll
+ window (/ (+ (- (cdr portion-whole) (car portion-whole))
+ (1- unit))
+ unit)))))
+
+(defun scroll-bar-horizontal-drag (event)
+ "Scroll the window horizontally by dragging the scroll bar slider.
+If you click outside the slider, the window scrolls to bring the slider there."
+ (interactive "e")
+ (let* (done
+ (echo-keystrokes 0)
+ (end-position (event-end event))
+ (window (nth 0 end-position))
+ (before-scroll))
+ (with-current-buffer (window-buffer window)
+ (setq before-scroll point-before-scroll))
+ (save-selected-window
+ (select-window window)
+ (setq before-scroll
+ (or before-scroll (point))))
+ (scroll-bar-horizontal-drag-1 event)
+ (track-mouse
+ (while (not done)
+ (setq event (read-event))
+ (if (eq (car-safe event) 'mouse-movement)
+ (setq event (read-event)))
+ (cond ((eq (car-safe event) 'scroll-bar-movement)
+ (scroll-bar-horizontal-drag-1 event))
+ (t
+ ;; Exit when we get the drag event; ignore that event.
+ (setq done t)))))
+ (sit-for 0)
+ (with-current-buffer (window-buffer window)
+ (setq point-before-scroll before-scroll))))
+
(defun scroll-bar-scroll-down (event)
"Scroll the window's top line down to the location of the scroll bar click.
EVENT should be a scroll bar click."
;;; Tookit scroll bars.
(defun scroll-bar-toolkit-scroll (event)
+ "Handle event EVENT on vertical scroll bar."
(interactive "e")
(let* ((end-position (event-end event))
(window (nth 0 end-position))
(part (nth 4 end-position))
before-scroll)
- (cond ((eq part 'end-scroll))
- (t
- (with-current-buffer (window-buffer window)
- (setq before-scroll point-before-scroll))
- (save-selected-window
- (select-window window)
- (setq before-scroll (or before-scroll (point)))
- (cond ((eq part 'above-handle)
- (scroll-up '-))
- ((eq part 'below-handle)
- (scroll-up nil))
- ((eq part 'ratio)
- (let* ((portion-whole (nth 2 end-position))
- (lines (scroll-bar-scale portion-whole
- (1- (window-height)))))
- (scroll-up (cond ((not (zerop lines)) lines)
- ((< (car portion-whole) 0) -1)
- (t 1)))))
- ((eq part 'up)
- (scroll-up -1))
- ((eq part 'down)
- (scroll-up 1))
- ((eq part 'top)
- (set-window-start window (point-min)))
- ((eq part 'bottom)
- (goto-char (point-max))
- (recenter))
- ((eq part 'handle)
- (scroll-bar-drag-1 event))))
- (sit-for 0)
- (with-current-buffer (window-buffer window)
- (setq point-before-scroll before-scroll))))))
-
+ (cond
+ ((eq part 'end-scroll))
+ (t
+ (with-current-buffer (window-buffer window)
+ (setq before-scroll point-before-scroll))
+ (save-selected-window
+ (select-window window)
+ (setq before-scroll (or before-scroll (point)))
+ (cond
+ ((eq part 'above-handle)
+ (scroll-up '-))
+ ((eq part 'below-handle)
+ (scroll-up nil))
+ ((eq part 'ratio)
+ (let* ((portion-whole (nth 2 end-position))
+ (lines (scroll-bar-scale portion-whole
+ (1- (window-height)))))
+ (scroll-up (cond ((not (zerop lines)) lines)
+ ((< (car portion-whole) 0) -1)
+ (t 1)))))
+ ((eq part 'up)
+ (scroll-up -1))
+ ((eq part 'down)
+ (scroll-up 1))
+ ((eq part 'top)
+ (set-window-start window (point-min)))
+ ((eq part 'bottom)
+ (goto-char (point-max))
+ (recenter))
+ ((eq part 'handle)
+ (scroll-bar-drag-1 event))))
+ (sit-for 0)
+ (with-current-buffer (window-buffer window)
+ (setq point-before-scroll before-scroll))))))
+(defun scroll-bar-toolkit-horizontal-scroll (event)
+ "Handle event EVENT on horizontal scroll bar."
+ (interactive "e")
+ (let* ((end-position (event-end event))
+ (window (nth 0 end-position))
+ (part (nth 4 end-position))
+ (bidi-factor
+ (if (eq (current-bidi-paragraph-direction (window-buffer window))
+ 'left-to-right)
+ 1
+ -1))
+ before-scroll)
+ (cond
+ ((eq part 'end-scroll))
+ (t
+ (with-current-buffer (window-buffer window)
+ (setq before-scroll point-before-scroll))
+ (save-selected-window
+ (select-window window)
+ (setq before-scroll (or before-scroll (point)))
+ (cond
+ ((eq part 'before-handle)
+ (scroll-right (* bidi-factor 4)))
+ ((eq part 'after-handle)
+ (scroll-left (* bidi-factor 4)))
+ ((eq part 'ratio)
+ (let* ((portion-whole (nth 2 end-position))
+ (columns (scroll-bar-scale portion-whole
+ (1- (window-width)))))
+ (scroll-right
+ (* (cond
+ ((not (zerop columns))
+ columns)
+ ((< (car portion-whole) 0) -1)
+ (t 1))
+ bidi-factor))))
+ ((eq part 'left)
+ (scroll-right (* bidi-factor 1)))
+ ((eq part 'right)
+ (scroll-left (* bidi-factor 1)))
+ ((eq part 'leftmost)
+ (goto-char (if (eq bidi-factor 1)
+ (line-beginning-position)
+ (line-end-position))))
+ ((eq part 'rightmost)
+ (goto-char (if (eq bidi-factor 1)
+ (line-end-position)
+ (line-beginning-position))))
+ ((eq part 'horizontal-handle)
+ (scroll-bar-horizontal-drag-1 event))))
+ (sit-for 0)
+ (with-current-buffer (window-buffer window)
+ (setq point-before-scroll before-scroll))))))
\f
;;;; Bindings.
;; For now, we'll set things up to work like xterm.
(cond ((and (boundp 'x-toolkit-scroll-bars) x-toolkit-scroll-bars)
(global-set-key [vertical-scroll-bar mouse-1]
- 'scroll-bar-toolkit-scroll))
+ 'scroll-bar-toolkit-scroll)
+ (global-set-key [horizontal-scroll-bar mouse-1]
+ 'scroll-bar-toolkit-horizontal-scroll))
(t
(global-set-key [vertical-scroll-bar mouse-1]
'scroll-bar-scroll-up)