;; Author: Anders Lindgren <andersl@csd.uu.se>
;; Maintainer: Anders Lindgren <andersl@csd.uu.se>
;; Created: 25 May 1995
-;; Version: 1.5
+;; Version: 1.6
;; Keywords: display, window, minor-mode
-;; Date: 22 Jan 1996
+;; Date: 20 Feb 1996
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; There exists two system variables which controls the appearence of
;; lines which are wider than the window containing them. The default
-;; is to truncate long lines if a window isn't as wide as the frame.
+;; is to truncate long lines whenever a window isn't as wide as the
+;; frame.
;;
;; To make sure lines are never truncated, please place the following
;; lines in your init file:
;; (setq truncate-partial-width-windows nil)
+;; Since the display of XEmacs is pixel-oriented, a line could be
+;; clipped in half at the bottom of the window.
+;;
+;; To make XEmacs avoid clipping (normal) lines, please place the
+;; following line in your init-file:
+;;
+;; (setq pixel-vertical-clip-threshold 30)
+
+
;; The correct way to cofigurate Follow mode, or any other mode for
;; that matter, is to create one (or more) function which does
;; whatever you would like to do. The function is then added to
;; Code in post hook removed.
;; * XEmacs: Post hook is always executed
;; after a mouse button event.
-;; 22-Jan-95 andersl * 1.5 released.
+;; 22-Jan-96 andersl * 1.5 released.
;;
;;}}}
;;; LCD Archive Entry:
;; follow|Anders Lindgren|andersl@csd.uu.se|
;; Combines windows into tall virtual window, minor mode.
-;; 22-Jan-1996|1.5|~/modes/follow.el.Z|
+;; 20-Feb-1996|1.6|~/modes/follow.el.Z|
;;}}}
(defvar follow-mode-off-hook nil
"*Hooks to run when follow-mode is turned off.")
-(defvar follow-mode-version "follow.el (Release 1.5)"
+(defvar follow-mode-version "follow.el (Release 1.6)"
"The current version of Follow mode.")
(defvar follow-mode-map nil
- "Minor mode keymap for Follow mode.")
+ "*Minor mode keymap for Follow mode.")
(defvar follow-mode-line-text " Follow"
"*Text shown in the mode line when Follow mode is active.
windows are recentered automatically. However, when using
Follow Mode it breaks the display when the end is displayed
in a window \"above\" the last window. This is for
-example the case when displaying short files.
+example the case when displaying a short page in info.
Must be set before Follow Mode is loaded.
XEmacs, as of 19.12, does not recenter windows, good!")
+(defvar follow-cache-command-list
+ '(next-line previous-line forward-char backward-char)
+ "List of commands which don't require recalculation.
+
+In order to be able to use the cache, a command should not change the
+contents of the buffer, nor should it change selected window or current
+buffer.
+
+The commands in this list are checked at load time.
+
+To mark other commands as suitable for caching, set the symbol
+property `follow-mode-use-cache' to non-nil.")
+
(defvar follow-debug nil
"*Non-nil when debugging Follow mode.")
-;;; Internal variables
+;; Internal variables:
(defvar follow-internal-force-redisplay nil
"True when Follow mode should redisplay the windows.")
"Non-nil when inside Follow modes `post-command-hook'.
Used by `follow-window-size-change'.")
+(defvar follow-windows-start-end-cache nil
+ "Cache used by `follow-window-start-end'.")
+
;;}}}
;;{{{ Bug report
(setq minor-mode-map-alist
(cons (cons 'follow-mode follow-mode-map) minor-mode-map-alist)))
+;;}}}
+;;{{{ Cache
+
+(let ((cmds follow-cache-command-list))
+ (while cmds
+ (put (car cmds) 'follow-mode-use-cache t)
+ (setq cmds (cdr cmds))))
+
;;}}}
;;{{{ The mode
;; Register follow-mode as a minor mode.
-(or (assq 'follow-mode minor-mode-alist)
- (setq minor-mode-alist
- (cons '(follow-mode follow-mode-line-text) minor-mode-alist)))
+(if (fboundp 'add-minor-mode)
+ ;; XEmacs
+ (funcall (symbol-function 'add-minor-mode)
+ 'follow-mode 'follow-mode-line-text)
+ (or (assq 'follow-mode minor-mode-alist)
+ (setq minor-mode-alist
+ (cons '(follow-mode follow-mode-line-text) minor-mode-alist))))
;;}}}
;;{{{ Find file hook
pos))
+;; The result from `follow-windows-start-end' is cached when using
+;; a handful simple commands, like cursor movement commands.
+
+(defsubst follow-cache-valid-p (windows)
+ "Test if the cached value of `follow-windows-start-end' can be used.
+Note that this handles the case when the cache has been set to nil."
+ (let ((res t)
+ (cache follow-windows-start-end-cache))
+ (while (and res windows cache)
+ (setq res (and (eq (car windows)
+ (car (car cache)))
+ (eq (window-start (car windows))
+ (car (cdr (car cache))))))
+ (setq windows (cdr windows))
+ (setq cache (cdr cache)))
+ (and res (null windows) (null cache))))
+
+
+(defsubst follow-invalidate-cache ()
+ "Force `follow-windows-start-end' to recalculate the end of the window."
+ (setq follow-windows-start-end-cache nil))
+
+
;; Build a list of windows and their start and end positions.
;; Useful to avoid calculating start/end position whenever they are needed.
;; The list has the format:
(defun follow-windows-start-end (windows)
"Builds a list of (WIN START END BUFFER-END-P) for every window in WINDOWS."
- (let ((win-start-end '())
- (orig-win (selected-window)))
- (while windows
- (select-window (car windows))
- (setq win-start-end
- (cons (cons (car windows)
- (cons (window-start)
- (follow-calc-win-end)))
- win-start-end))
- (setq windows (cdr windows)))
- (select-window orig-win)
- (nreverse win-start-end)))
-
-
-(defun follow-pos-visible (pos win win-start-end)
+ (if (follow-cache-valid-p windows)
+ follow-windows-start-end-cache
+ (let ((win-start-end '())
+ (orig-win (selected-window)))
+ (while windows
+ (select-window (car windows))
+ (setq win-start-end
+ (cons (cons (car windows)
+ (cons (window-start)
+ (follow-calc-win-end)))
+ win-start-end))
+ (setq windows (cdr windows)))
+ (select-window orig-win)
+ (setq follow-windows-start-end-cache (nreverse win-start-end))
+ follow-windows-start-end-cache)))
+
+
+(defsubst follow-pos-visible (pos win win-start-end)
"Non-nil when POS is visible in WIN."
(let ((wstart-wend-bend (cdr (assq win win-start-end))))
(and (>= pos (car wstart-wend-bend))
;; first is equal with the start of the successor. The first window
;; should start at a full screen line.
-(defun follow-windows-aligned-p (win-start-end)
+(defsubst follow-windows-aligned-p (win-start-end)
"Non-nil if the follower WINDOWS are alinged."
(let ((res t))
(save-excursion
(setq res (eq (point) (window-start (car (car win-start-end)))))))
(while (and res (cdr win-start-end))
;; At least two followers left
- (setq res (eq (nth 2 (car win-start-end))
- (nth 1 (car (cdr win-start-end)))))
+ (setq res (eq (car (cdr (cdr (car win-start-end))))
+ (car (cdr (car (cdr win-start-end))))))
(setq win-start-end (cdr win-start-end)))
res))
"Non-nil when the window-point is visible in all windows."
(let ((res t))
(while (and res win-start-end)
- (setq res (inline
- (follow-pos-visible (window-point (car (car win-start-end)))
- (car (car win-start-end))
- win-start-end)))
+ (setq res (follow-pos-visible (window-point (car (car win-start-end)))
+ (car (car win-start-end))
+ win-start-end))
(setq win-start-end (cdr win-start-end)))
res))
non-first windows in Follow Mode."
(if follow-avoid-tail-recenter-p
(let* ((orig-buffer (current-buffer))
- (top (frame-first-window (selected-frame)))
- (win top)
- (who '()) ; list of (buffer . frame)
- start
- pair) ; (buffer . frame)
- (while ;; look, no body!
- (progn
- (setq start (window-start win))
- (set-buffer (window-buffer win))
- (setq pair (cons (window-buffer win) (window-frame win)))
- (if (member pair who)
- (if (and (boundp 'follow-mode) follow-mode
- (eq (point-max) start))
- ;; Write the same window start back, but don't
- ;; set the NOFORCE flag.
- (set-window-start win start))
- (setq who (cons pair who)))
- (setq win (next-window win 'not t))
- (not (eq win top)))) ;; Loop while this is true.
- (set-buffer orig-buffer))))
+ (top (frame-first-window (selected-frame)))
+ (win top)
+ (who '()) ; list of (buffer . frame)
+ start
+ pair) ; (buffer . frame)
+ ;; If the only window in the frame is a minibuffer
+ ;; window, `next-window' will never find it again...
+ (if (window-minibuffer-p top)
+ nil
+ (while ;; look, no body!
+ (progn
+ (setq start (window-start win))
+ (set-buffer (window-buffer win))
+ (setq pair (cons (window-buffer win) (window-frame win)))
+ (if (member pair who)
+ (if (and (boundp 'follow-mode) follow-mode
+ (eq (point-max) start))
+ ;; Write the same window start back, but don't
+ ;; set the NOFORCE flag.
+ (set-window-start win start))
+ (setq who (cons pair who)))
+ (setq win (next-window win 'not t))
+ (not (eq win top)))) ;; Loop while this is true.
+ (set-buffer orig-buffer)))))
;;}}}
(let ((orig-buffer (current-buffer))
(win (selected-window)))
(set-buffer (window-buffer win))
+ (or (and (symbolp this-command)
+ (get this-command 'follow-mode-use-cache))
+ (follow-invalidate-cache))
(if (and (boundp 'follow-mode) follow-mode
(not (window-minibuffer-p win)))
;; The buffer shown in the selected window is in follow
;; cache the result for speed (i.e. `aligned' and `visible'.)
(let* ((windows (inline (follow-all-followers win)))
(dest (point))
- (win-start-end (progn
+ (win-start-end (inline
(follow-update-window-start (car windows))
(follow-windows-start-end windows)))
(aligned (follow-windows-aligned-p win-start-end))
(visible (follow-pos-visible dest win win-start-end)))
- (follow-avoid-tail-recenter)
+ (if (not (and aligned visible))
+ (follow-invalidate-cache))
+ (inline (follow-avoid-tail-recenter))
;; Select a window to display the point.
(or follow-internal-force-redisplay
(progn
(goto-char dest)
(set-window-start (selected-window) (point-min))
(setq win-start-end (follow-windows-start-end windows))
+ (follow-invalidate-cache)
(setq visible t)
(setq aligned nil))
;; If we can position the cursor without moving the first
(sit-for 0)
(follow-avoid-tail-recenter)
(setq win-start-end (follow-windows-start-end windows))
+ (follow-invalidate-cache)
(setq aligned nil))
;; Redraw the windows whenever needed.
(if (or follow-internal-force-redisplay
(setq follow-internal-force-redisplay nil)
(follow-redisplay windows (selected-window))
(setq win-start-end (follow-windows-start-end windows))
+ (follow-invalidate-cache)
;; When the point ends up in another window. This
;; happends when dest is in the beginning of the
;; file and the selected window is not the first.
(follow-maximize-region
(selected-window) windows win-start-end))
- (follow-avoid-tail-recenter)
+ (inline (follow-avoid-tail-recenter))
;; DEBUG
;;(if (not (follow-windows-aligned-p
;; (follow-windows-start-end windows)))
(if return-to-orig-win
(select-window orig-win))
(set-buffer old-buffer))
-
- ;; Normally, if the display has been changed, it is redrawn. All
- ;; windows showing only the end of a buffer is unconditionally
- ;; recentered, we can't prevent it by calling
- ;; `follow-avoid-tail-recenter'.
- ;;
- ;; By performing a redisplay on our own, Emacs need not perform
- ;; the above described redisplay. (However, bu performing it when
- ;; there are input available just seems to make things worse.)
- (if (and follow-avoid-tail-recenter-p
- (not (input-pending-p)))
- (sit-for 0)))
+
+ (follow-invalidate-cache)
+
+ ;; Normally, if the display has been changed, it is redrawn. All
+ ;; windows showing only the end of a buffer is unconditionally
+ ;; recentered, we can't prevent it by calling
+ ;; `follow-avoid-tail-recenter'.
+ ;;
+ ;; By performing a redisplay on our own, Emacs need not perform
+ ;; the above described redisplay. (However, bu performing it when
+ ;; there are input available just seems to make things worse.)
+ (if (and follow-avoid-tail-recenter-p
+ (not (input-pending-p)))
+ (sit-for 0)))
;;}}}
follow-calc-win-start
follow-pos-visible
follow-windows-start-end
+ follow-cache-valid-p
follow-select-if-visible
follow-select-if-visible-from-first
follow-windows-aligned-p