]> code.delx.au - gnu-emacs/blobdiff - lisp/scroll-bar.el
Prefer 'frame-parameter' where it is expected to be a bit faster
[gnu-emacs] / lisp / scroll-bar.el
index 0d693c52c8165ab1510781a083680b9dbf65662c..e5fe31675da3c199c037a80e614f4ae8b1de7214 100644 (file)
@@ -1,8 +1,8 @@
 ;;; scroll-bar.el --- window system-independent scroll bar support
 
-;; Copyright (C) 1993-1995, 1999-2012 Free Software Foundation, Inc.
+;; Copyright (C) 1993-1995, 1999-2016 Free Software Foundation, Inc.
 
-;; Maintainer: FSF
+;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: hardware
 ;; Package: emacs
 
@@ -70,10 +70,25 @@ SIDE must be the symbol `left' or `right'."
                (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
@@ -126,20 +141,49 @@ This command applies to all frames that exist and frames to be
 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))
+
+(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 (null arg)
       (setq arg
-           (if (cdr (assq 'vertical-scroll-bars
-                          (frame-parameters (selected-frame))))
-               -1 1))
+           (if (frame-parameter nil 'vertical-scroll-bars) -1 1))
     (setq arg (prefix-numeric-value arg)))
   (modify-frame-parameters
    (selected-frame)
@@ -147,12 +191,18 @@ when they are turned on; if it is nil, they go on the left."
               (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 (frame-parameter nil 'horizontal-scroll-bars) -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.
 
@@ -249,6 +299,51 @@ If you click outside the slider, the window scrolls to bring the slider there."
     (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."
@@ -295,52 +390,109 @@ 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)