123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- ;;; mwheel.el --- Wheel mouse support
- ;; Copyright (C) 1998, 2000-2017 Free Software Foundation, Inc.
- ;; Maintainer: William M. Perry <wmperry@gnu.org>
- ;; Keywords: mouse
- ;; Package: emacs
- ;; This file is part of GNU Emacs.
- ;; GNU Emacs is free software: you can redistribute it and/or modify
- ;; it under the terms of the GNU General Public License as published by
- ;; the Free Software Foundation, either version 3 of the License, or
- ;; (at your option) any later version.
- ;; GNU Emacs is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;; GNU General Public License for more details.
- ;; You should have received a copy of the GNU General Public License
- ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
- ;;; Commentary:
- ;; This code will enable the use of the infamous 'wheel' on the new
- ;; crop of mice. Under XFree86 and the XSuSE X Servers, the wheel
- ;; events are sent as button4/button5 events.
- ;; I for one would prefer some way of converting the button4/button5
- ;; events into different event types, like 'mwheel-up' or
- ;; 'mwheel-down', but I cannot find a way to do this very easily (or
- ;; portably), so for now I just live with it.
- ;; To enable this code, simply put this at the top of your .emacs
- ;; file:
- ;;
- ;; (mouse-wheel-mode 1)
- ;;; Code:
- (require 'custom)
- (require 'timer)
- (defvar mouse-wheel-mode)
- ;; Setter function for mouse-button user-options. Switch Mouse Wheel
- ;; mode off and on again so that the old button is unbound and
- ;; new button is bound to mwheel-scroll.
- (defun mouse-wheel-change-button (var button)
- (set-default var button)
- ;; Sync the bindings.
- (when (bound-and-true-p mouse-wheel-mode) (mouse-wheel-mode 1)))
- (defvar mouse-wheel-down-button 4)
- (make-obsolete-variable 'mouse-wheel-down-button
- 'mouse-wheel-down-event
- "22.1")
- (defcustom mouse-wheel-down-event
- (if (or (featurep 'w32-win) (featurep 'ns-win))
- 'wheel-up
- (intern (format "mouse-%s" mouse-wheel-down-button)))
- "Event used for scrolling down."
- :group 'mouse
- :type 'symbol
- :set 'mouse-wheel-change-button)
- (defvar mouse-wheel-up-button 5)
- (make-obsolete-variable 'mouse-wheel-up-button
- 'mouse-wheel-up-event
- "22.1")
- (defcustom mouse-wheel-up-event
- (if (or (featurep 'w32-win) (featurep 'ns-win))
- 'wheel-down
- (intern (format "mouse-%s" mouse-wheel-up-button)))
- "Event used for scrolling up."
- :group 'mouse
- :type 'symbol
- :set 'mouse-wheel-change-button)
- (defvar mouse-wheel-click-button 2)
- (make-obsolete-variable 'mouse-wheel-click-button
- 'mouse-wheel-click-event
- "22.1")
- (defcustom mouse-wheel-click-event
- (intern (format "mouse-%s" mouse-wheel-click-button))
- "Event that should be temporarily inhibited after mouse scrolling.
- The mouse wheel is typically on the mouse-2 button, so it may easily
- happen that text is accidentally yanked into the buffer when
- scrolling with the mouse wheel. To prevent that, this variable can be
- set to the event sent when clicking on the mouse wheel button."
- :group 'mouse
- :type 'symbol
- :set 'mouse-wheel-change-button)
- (defcustom mouse-wheel-inhibit-click-time 0.35
- "Time in seconds to inhibit clicking on mouse wheel button after scroll."
- :group 'mouse
- :type 'number)
- (defcustom mouse-wheel-scroll-amount '(5 ((shift) . 1) ((control) . nil))
- "Amount to scroll windows by when spinning the mouse wheel.
- This is an alist mapping the modifier key to the amount to scroll when
- the wheel is moved with the modifier key depressed.
- Elements of the list have the form (MODIFIERS . AMOUNT) or just AMOUNT if
- MODIFIERS is nil.
- AMOUNT should be the number of lines to scroll, or nil for near full
- screen. It can also be a floating point number, specifying the fraction of
- a full screen to scroll. A near full screen is `next-screen-context-lines'
- less than a full screen."
- :group 'mouse
- :type '(cons
- (choice :tag "Normal"
- (const :tag "Full screen" :value nil)
- (integer :tag "Specific # of lines")
- (float :tag "Fraction of window")
- (cons
- (repeat (choice :tag "modifier"
- (const alt) (const control) (const hyper)
- (const meta) (const shift) (const super)))
- (choice :tag "scroll amount"
- (const :tag "Full screen" :value nil)
- (integer :tag "Specific # of lines")
- (float :tag "Fraction of window"))))
- (repeat
- (cons
- (repeat (choice :tag "modifier"
- (const alt) (const control) (const hyper)
- (const meta) (const shift) (const super)))
- (choice :tag "scroll amount"
- (const :tag "Full screen" :value nil)
- (integer :tag "Specific # of lines")
- (float :tag "Fraction of window")))))
- :set 'mouse-wheel-change-button)
- (defcustom mouse-wheel-progressive-speed t
- "If non-nil, the faster the user moves the wheel, the faster the scrolling.
- Note that this has no effect when `mouse-wheel-scroll-amount' specifies
- a \"near full screen\" scroll or when the mouse wheel sends key instead
- of button events."
- :group 'mouse
- :type 'boolean)
- (defcustom mouse-wheel-follow-mouse t
- "Whether the mouse wheel should scroll the window that the mouse is over.
- This can be slightly disconcerting, but some people prefer it."
- :group 'mouse
- :type 'boolean)
- (eval-and-compile
- (if (fboundp 'event-button)
- (fset 'mwheel-event-button 'event-button)
- (defun mwheel-event-button (event)
- (let ((x (event-basic-type event)))
- ;; Map mouse-wheel events to appropriate buttons
- (if (eq 'mouse-wheel x)
- (let ((amount (car (cdr (cdr (cdr event))))))
- (if (< amount 0)
- mouse-wheel-up-event
- mouse-wheel-down-event))
- x))))
- (if (fboundp 'event-window)
- (fset 'mwheel-event-window 'event-window)
- (defun mwheel-event-window (event)
- (posn-window (event-start event)))))
- (defvar mwheel-inhibit-click-event-timer nil
- "Timer running while mouse wheel click event is inhibited.")
- (defun mwheel-inhibit-click-timeout ()
- "Handler for `mwheel-inhibit-click-event-timer'."
- (setq mwheel-inhibit-click-event-timer nil)
- (remove-hook 'pre-command-hook 'mwheel-filter-click-events))
- (defun mwheel-filter-click-events ()
- "Discard `mouse-wheel-click-event' while scrolling the mouse."
- (if (eq (event-basic-type last-input-event) mouse-wheel-click-event)
- (setq this-command 'ignore)))
- (defvar mwheel-scroll-up-function 'scroll-up
- "Function that does the job of scrolling upward.")
- (defvar mwheel-scroll-down-function 'scroll-down
- "Function that does the job of scrolling downward.")
- (defun mwheel-scroll (event)
- "Scroll up or down according to the EVENT.
- This should be bound only to mouse buttons 4, 5, 6, and 7 on
- non-Windows systems."
- (interactive (list last-input-event))
- (let* ((selected-window (selected-window))
- (scroll-window
- (or (catch 'found
- (let* ((window (if mouse-wheel-follow-mouse
- (mwheel-event-window event)
- (selected-window)))
- (frame (when (window-live-p window)
- (frame-parameter
- (window-frame window) 'mouse-wheel-frame))))
- (when (frame-live-p frame)
- (let* ((pos (mouse-absolute-pixel-position))
- (pos-x (car pos))
- (pos-y (cdr pos)))
- (walk-window-tree
- (lambda (window-1)
- (let ((edges (window-edges window-1 nil t t)))
- (when (and (<= (nth 0 edges) pos-x)
- (<= pos-x (nth 2 edges))
- (<= (nth 1 edges) pos-y)
- (<= pos-y (nth 3 edges)))
- (throw 'found window-1))))
- frame nil t)))))
- (mwheel-event-window event)))
- (old-point
- (and (eq scroll-window selected-window)
- (eq (car-safe transient-mark-mode) 'only)
- (window-point)))
- (mods
- (delq 'click (delq 'double (delq 'triple (event-modifiers event)))))
- (amt (assoc mods mouse-wheel-scroll-amount)))
- (unless (eq scroll-window selected-window)
- ;; Mark window to be scrolled for redisplay.
- (select-window scroll-window 'mark-for-redisplay))
- ;; Extract the actual amount or find the element that has no modifiers.
- (if amt (setq amt (cdr amt))
- (let ((list-elt mouse-wheel-scroll-amount))
- (while (consp (setq amt (pop list-elt))))))
- (if (floatp amt) (setq amt (1+ (truncate (* amt (window-height))))))
- (when (and mouse-wheel-progressive-speed (numberp amt))
- ;; When the double-mouse-N comes in, a mouse-N has been executed already,
- ;; So by adding things up we get a squaring up (1, 3, 6, 10, 15, ...).
- (setq amt (* amt (event-click-count event))))
- (unwind-protect
- (let ((button (mwheel-event-button event)))
- (cond ((eq button mouse-wheel-down-event)
- (condition-case nil (funcall mwheel-scroll-down-function amt)
- ;; Make sure we do indeed scroll to the beginning of
- ;; the buffer.
- (beginning-of-buffer
- (unwind-protect
- (funcall mwheel-scroll-down-function)
- ;; If the first scroll succeeded, then some scrolling
- ;; is possible: keep scrolling til the beginning but
- ;; do not signal an error. For some reason, we have
- ;; to do it even if the first scroll signaled an
- ;; error, because otherwise the window is recentered
- ;; for a reason that escapes me. This problem seems
- ;; to only affect scroll-down. --Stef
- (set-window-start (selected-window) (point-min))))))
- ((eq button mouse-wheel-up-event)
- (condition-case nil (funcall mwheel-scroll-up-function amt)
- ;; Make sure we do indeed scroll to the end of the buffer.
- (end-of-buffer (while t (funcall mwheel-scroll-up-function)))))
- ((eq button mouse-wheel-left-event) ; for tilt scroll
- (when mwheel-tilt-scroll-p
- (funcall (if mwheel-flip-direction
- mwheel-scroll-right-function
- mwheel-scroll-left-function) amt)))
- ((eq button mouse-wheel-right-event) ; for tilt scroll
- (when mwheel-tilt-scroll-p
- (funcall (if mwheel-flip-direction
- mwheel-scroll-left-function
- mwheel-scroll-right-function) amt)))
- (t (error "Bad binding in mwheel-scroll"))))
- (if (eq scroll-window selected-window)
- ;; If there is a temporarily active region, deactivate it if
- ;; scrolling moved point.
- (when (and old-point (/= old-point (window-point)))
- ;; Call `deactivate-mark' at the original position, so that
- ;; the original region is saved to the X selection.
- (let ((new-point (window-point)))
- (goto-char old-point)
- (deactivate-mark)
- (goto-char new-point)))
- (select-window selected-window t))))
- (when (and mouse-wheel-click-event mouse-wheel-inhibit-click-time)
- (if mwheel-inhibit-click-event-timer
- (cancel-timer mwheel-inhibit-click-event-timer)
- (add-hook 'pre-command-hook 'mwheel-filter-click-events))
- (setq mwheel-inhibit-click-event-timer
- (run-with-timer mouse-wheel-inhibit-click-time nil
- 'mwheel-inhibit-click-timeout))))
- (put 'mwheel-scroll 'scroll-command t)
- (defvar mwheel-installed-bindings nil)
- (define-minor-mode mouse-wheel-mode
- "Toggle mouse wheel support (Mouse Wheel mode).
- With a prefix argument ARG, enable Mouse Wheel mode if ARG is
- positive, and disable it otherwise. If called from Lisp, enable
- the mode if ARG is omitted or nil."
- :init-value t
- ;; We'd like to use custom-initialize-set here so the setup is done
- ;; before dumping, but at the point where the defcustom is evaluated,
- ;; the corresponding function isn't defined yet, so
- ;; custom-initialize-set signals an error.
- :initialize 'custom-initialize-delay
- :global t
- :group 'mouse
- ;; Remove previous bindings, if any.
- (while mwheel-installed-bindings
- (let ((key (pop mwheel-installed-bindings)))
- (when (eq (lookup-key (current-global-map) key) 'mwheel-scroll)
- (global-unset-key key))))
- ;; Setup bindings as needed.
- (when mouse-wheel-mode
- (dolist (event (list mouse-wheel-down-event mouse-wheel-up-event mouse-wheel-right-event mouse-wheel-left-event))
- (dolist (key (mapcar (lambda (amt) `[(,@(if (consp amt) (car amt)) ,event)])
- mouse-wheel-scroll-amount))
- (global-set-key key 'mwheel-scroll)
- (push key mwheel-installed-bindings)))))
- ;;; Compatibility entry point
- ;; preloaded ;;;###autoload
- (defun mwheel-install (&optional uninstall)
- "Enable mouse wheel support."
- (mouse-wheel-mode (if uninstall -1 1)))
- ;;; For tilt-scroll
- ;;;
- (defcustom mwheel-tilt-scroll-p nil
- "Enable scroll using tilting mouse wheel."
- :group 'mouse
- :type 'boolean
- :version "26.1")
- (defcustom mwheel-flip-direction nil
- "Swap direction of 'wheel-right and 'wheel-left."
- :group 'mouse
- :type 'boolean
- :version "26.1")
- (defcustom mwheel-scroll-left-function 'scroll-left
- "Function that does the job of scrolling left."
- :group 'mouse
- :type 'function
- :version "26.1")
- (defcustom mwheel-scroll-right-function 'scroll-right
- "Function that does the job of scrolling right."
- :group 'mouse
- :type 'function
- :version "26.1")
- (defvar mouse-wheel-left-event
- (if (or (featurep 'w32-win) (featurep 'ns-win))
- 'wheel-left
- (intern "mouse-6"))
- "Event used for scrolling left.")
- (defvar mouse-wheel-right-event
- (if (or (featurep 'w32-win) (featurep 'ns-win))
- 'wheel-right
- (intern "mouse-7"))
- "Event used for scrolling right.")
- (provide 'mwheel)
- ;;; mwheel.el ends here
|