changeset 14578:55d804f97ffc

Initial revision
author Karl Heuer <kwzh@gnu.org>
date Fri, 16 Feb 1996 01:01:16 +0000
parents 39a431eafb34
children a81dd4e9603a
files lisp/follow.el
diffstat 1 files changed, 2349 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/follow.el	Fri Feb 16 01:01:16 1996 +0000
@@ -0,0 +1,2349 @@
+;;; follow.el --- Minor mode, Synchronize windows showing the same buffer.
+
+;; Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+
+;; Author: Anders Lindgren <andersl@csd.uu.se>
+;; Maintainer: Anders Lindgren <andersl@csd.uu.se>
+;; Created: 25 May 1995
+;; Version: 1.5
+;; Keywords: display, window, minor-mode
+;; Date: 22 Jan 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
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; This program 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 this program; see the file COPYING.  If not, write to
+;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Commentary:
+
+;;{{{ Documentation
+
+;; `Follow mode' is a minor mode for Emacs 19 and XEmacs which
+;; combines windows into one tall virtual window.
+;;
+;; The feeling of a "virtual window" has been accomplished by the use
+;; of two major techniques:
+;;
+;; * The windows always displays adjacent sections of the buffer. 
+;;   This means that whenever one window is moved, all the
+;;   others will follow.  (Hence the name Follow Mode.)
+;;
+;; * Should the point (cursor) end up outside a window, another 
+;;   window displaying that point is selected, if possible.  This 
+;;   makes it possible to walk between windows using normal cursor 
+;;   movement commands.
+;;
+;; Follow mode comes to its prime when used on a large screen and two
+;; side-by-side window are used. The user can, with the help of Follow
+;; mode, use two full-height windows as though they would have been
+;; one. Imagine yourself editing a large function, or section of text,
+;; and beeing able to use 144 lines instead of the normal 72... (your
+;; mileage may vary).
+
+;; The latest version, and a demonstration, are avaiable at:
+;;
+;;	ftp://ftp.csd.uu.se/pub/users/andersl/emacs/follow.el
+;;	http://www.csd.uu.se/~andersl/follow.shtml
+
+;; `Follow mode' can be used together with Emacs 19 and XEmacs.
+;; It has been tested together with Emacs 19.27, 19.28, 19.29,
+;; 19.30, XEmacs 19.12, and 19.13.
+
+
+;; To test this package, make sure `follow' is loaded, or will be
+;; autoloaded when activated (see below). Then do the following:
+;;
+;; * Find your favorite file (preferably a long one.)
+;;
+;; * Resize Emacs so that it will be wide enough for two full sized
+;;   columns.  Delete the other windows and split with the commands
+;;   `C-x 1 C-x 3'.
+;;
+;; * Give the command:
+;;	M-x follow-mode <RETURN>
+;;
+;; * Now the display should look something like (assuming the text "71"
+;;   is on line 71):
+;;
+;;		    +----------+----------+
+;;		    |1         |73        |
+;;		    |2         |74        |
+;;		    |3         |75        |
+;;		         ...        ...   
+;;		    |71        |143       |
+;;		    |72        |144       |
+;;		    +----------+----------+
+;;
+;;   As you can see, the right-hand window starts at line 73, the line
+;;   immediately below the end of the left-hand window. As long as
+;;   `follow-mode' is active, the two windows will follow eachother!
+;;
+;; * Play around and enjoy! Scroll one window and watch the other.
+;;   Jump to the beginning or end. Press `Cursor down' at the last
+;;   line of the left-hand window. Enter new lines into the
+;;   text. Enter long lines spanning several lines, or several
+;;   windows.
+;;
+;; * Should you find `Follow' mode annoying, just type
+;;	M-x follow-mode <RETURN> 
+;;   to turn it off.
+
+
+;; Installation:
+;;
+;; To fully install this, add this file to your Emacs Lisp directory and
+;; compile it with M-x byte-compile-file.  Then add the following to the
+;; appropriate init file (normally your `~/.emacs' file):
+;;
+;; (autoload 'follow-mode "follow"
+;;   "Synchronize windows showing the same buffer, minor mode." t)
+
+
+;; The command `follow-delete-other-windows-and-split' maximises the
+;; visible area of the current buffer.
+;;
+;; I recommend adding it, and `follow-mode', to hotkeys in the global
+;; key map.  To do so, add the following lines (replacing `[f7]' and
+;; `[f8]' with your favorite keys) to the init file:
+;;
+;; (autoload 'follow-mode "follow"
+;;   "Synchronize windows showing the same buffer, minor mode." t)
+;; (global-set-key [f8] 'follow-mode)
+;;
+;; (autoload 'follow-delete-other-windows-and-split "follow"
+;;   "Delete other windows, split the frame in two, and enter Follow Mode." t)
+;; (global-set-key [f7] 'follow-delete-other-windows-and-split)
+
+
+;; 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.
+;;
+;; To make sure lines are never truncated, please place the following
+;; lines in your init file:
+;;
+;; (setq truncate-lines nil)
+;; (setq truncate-partial-width-windows nil)
+
+
+;; 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
+;; a hook.
+;;
+;; When `Follow' mode is activated, functions stored in the hook
+;; `follow-mode-hook' are called.  When it is deactivated
+;; `follow-mode-off-hook' is runed.
+;;
+;; The keymap `follow-key-map' contains key bindings activated by
+;; `follow-mode'.
+;;
+;; Example:
+;; (add-hook 'follow-mode-hook 'my-follow-mode-hook)
+;;
+;; (defun my-follow-mode-hook ()
+;;    (define-key follow-mode-map "\C-ca" 'your-favorite-function)
+;;    (define-key follow-mode-map "\C-cb" 'another-function))
+
+
+;; Usage:
+;;
+;; To activate give the command:  M-x follow-mode
+;; and press return. To deactivate, do it again.
+;;
+;; Some special commands have been developed to make life even easier:
+;;	follow-scroll-up			 C-c . C-v
+;;		Scroll text in a Follow Mode window chain up.
+;;
+;;	follow-scroll-down			 C-c . v
+;;		Like `follow-scroll-up', but in the other direction.
+;;
+;;	follow-delete-other-windows-and-split	 C-c . 1
+;;		Maximise the visible area of the current buffer,
+;;		and enter Follow Mode. 	This is a very convenient
+;;		way to start Follow Mode, hence it is recomended
+;;		that this command is added to the global keymap.
+;;
+;;	follow-recenter				 C-c . C-l
+;;		Place the point in the center of the middle window,
+;;		or a specified number of lines from either top or bottom.
+;;
+;;	follow-switch-to-buffer			 C-c . b
+;;		Switch buffer in all windows displaying the current buffer
+;;		in this frame.
+;;
+;;	follow-switch-to-buffer-all		 C-c . C-b
+;;		Switch buffer in all windows in the active frame.
+;;
+;;	follow-switch-to-current-buffer-all
+;;		Show the current buffer in all windows on the current
+;;		frame and turn on `follow-mode'.
+;;
+;;	follow-first-window			 C-c . <
+;;		Select the first window in the frame showing the same buffer.
+;;
+;;	follow-last-window			 C-c . >
+;;		Select the last window in the frame showing the same buffer.
+;;
+;;	follow-next-window			 C-c . n
+;;		Select the next window in the frame showing the same buffer.
+;;
+;;	follow-previous-window			 C-c . p
+;;		Select the previous window showing the same buffer.
+
+
+;; Well, it seems ok, but what if I really want to look at two different
+;; positions in the text? Here are two simple methods to use:
+;;
+;; 1) Use multiple frames; `follow' mode only affects windows displayed
+;;    in the same frame. (My apoligies to you who can't use frames.)
+;;
+;; 2) Bind `follow-mode' to key so you can turn it off whenever
+;;    you want to view two locations. Of course, `follow' mode can
+;;    be reactivated by hitting the same key again.
+;;
+;;    Example from my ~/.emacs:
+;;	(global-set-key [f8] 'follow-mode)
+
+
+;; Implementation: 
+;;
+;; In an ideal world, follow mode would have been implemented in the
+;; kernal of the display routines, making sure that the windows (in
+;; follow mode) ALWAYS are aligned. On planet earth, however, we must
+;; accept a solution where we ALMOST ALWAYS can make sure that the
+;; windows are aligned.
+;;
+;; Follow mode does this in three places:
+;; 1) After each user command.
+;; 2) After a process output has been perfomed.
+;; 3) When a scrollbar has been moved.
+;;
+;; This will cover most situations. (Let me know if there are other
+;; situations which should be covered.)
+;;
+;; However, only the selected window is checked, for the reason of
+;; efficiency and code complexity. (i.e. it is possible to make a
+;; non-selected windows unaligned. It will, however, pop right back
+;; when it is selected.)
+
+;;}}}
+;;{{{ Change Log
+
+;;; Change log:
+;;	25-May-95 andersl  * File created.
+;;	26-May-95 andersl  * It works!
+;;	27-May-95 andersl  * Avoids hitting the head in the roof.
+;;		           * follow-scroll-up, -scroll-down, and -recenter.
+;;		           * V0.1 Sent to Ohio.
+;;	28-May-95 andersl  * Scroll-bar support added.
+;;	30-May-95 andersl  * Code adopted to standard style.
+;;			   * Minor mode keymap.
+;;	 2-Jun-95 andersl  * Processor output.
+;;	 3-Jun-95 andersl  * V0.4
+;;	 5-Jun-95 andersl  * V0.5. Copyright notice corrected.
+;;			     (The old one stated that I had copyright, but
+;;			     that Emacs could be freely distributed ;-)  )
+;;	 6-Jun-95 andersl  * Lucid support added. (no longer valid.)
+;;	 7-Jun-95 andersl  * Menu bar added.
+;;			   * Bug fix, (at-window 0 0) => (frame-first-window)
+;;	15-Jun-95 andersl  * 0.8 Major rework. looong lines and outline mode.
+;;	18-Jun-95 andersl  * 0.9 Allow a tail window to be selected, but pick
+;;			     a better one when edited.
+;;	26-Jun-95 andersl  * Inlineing.
+;;      02-Jul-95 andersl  * compute-motion imitated with a ugly workaround,
+;;			     Works with XEmacs again!
+;;	15-Jul-95 andersl  * find-file hook.
+;;                         * submit-feedback.
+;;                         * Survives major mode changes.
+;;                         * Region spanning multiple windows looks 
+;;			       resonabely good.
+;;	19-Jul-95 andersl  * New process-filter handling.
+;;	 1-Aug-95 andersl  * XEmacs scrollbar support.
+;;			   * Emacs 19 `window-size-change' support.
+;;			   * `save-window-excursion' removed, it triggered
+;;			     a redraw!
+;;	 5-Aug-95 andersl  * `follow-switch-to-current-buffer-all' added.
+;;	16-Nov-95 andersl  * V1.0 released!
+;;	17-Nov-95 andersl  * Byte compiler silencer for XEmacs broken.
+;;			   * fkey-end-of-buffer treated the same way 
+;;			     end-of-buffer is.
+;;			   * follow-mode-off-hook added. 
+;;			     (Suggested by David Hughes, thanks!)
+;;	20-Nov-95 andersl  * Bug in menu code corrected.
+;;			     (Reported by Robert E. Brown, thanks!)
+;;	 5-Dec-95 andersl  * `follow-avoid-tail-recenter' added to the
+;;			     post-command-idle-hook to avoid recentering
+;;			     caused by `paren' et. al.
+;;	 7-Dec-95 andersl  * `follow-avoid-tail-recenter' called by
+;;			     `window-scroll-functions'.
+;;	18-Dec-95 andersl  * All processes intercepted.
+;;	20-Dec-95 andersl  * `follow-recenter' accepts arguments.
+;;                         * `move-overlay' advices, drag-region works.
+;;	 2-Jan-96 andersl  * XEmacs: isearch fixed.
+;;                         * `follow-calc-win-end' created.
+;;       8-Jan-96 andersl  * XEmacs: `window-end' with `guarantee'
+;;                           argument used in `follow-calc-win-end'.
+;;       9-Jan-96 andersl  * `follow-end-of-buffer' added. 
+;;                           Code in post hook removed.
+;;                         * XEmacs: Post hook is always executed
+;;			     after a mouse button event.
+;;      22-Jan-95 andersl  * 1.5 released.
+;;
+
+;;}}}
+;;{{{ LCD Entry
+
+;;; 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|
+
+;;}}}
+
+;;; Code:
+
+;;{{{ Preliminaries
+
+;; Make the compiler shut up!
+;; There are two strategies:
+;; 1) Shut warnings off completely.
+;; 2) Handle each warning separately.
+;;
+;; Since I would like to see real errors, I've selected the latter
+;; method.
+;;
+;; The problem with undefined variables and functions has been solved
+;; by using `set', `symbol-value' and `symbol-function' rather than
+;; `setq' and direct references to variables and functions.
+;;
+;; For example:
+;;	(if (boundp 'foo)   ... (symbol-value 'foo) )
+;;	(set 'foo ...)   <-- XEmacs doesn't fall for this one.
+;;	(funcall (symbol-function 'set) 'bar ...)
+;;
+;; Note: When this file is interpreted, `eval-when-compile' is
+;; evaluted (really smart...)  Since it doesn't hurt to evaluate it,
+;; but it is a bit annoying, we test if the byte-compiler has been
+;; loaded.  This can, of course, lead to some occasional unintended
+;; evaluation...
+;;
+;; Should someone come up with a better solution, please let me
+;; know.
+
+(eval-when-compile
+  (if (or (featurep 'bytecomp)
+	  (featurep 'byte-compile))
+      (cond ((string-match "XEmacs" emacs-version)
+	     ;; Make XEmacs shut up!  I'm using standard Emacs
+	     ;; functions, they are NOT obsolete!
+	     (if (eq (get 'force-mode-line-update 'byte-compile)
+		     'byte-compile-obsolete)
+		 (put 'force-mode-line-update 'byte-compile 'nil))
+	     (if (eq (get 'frame-first-window 'byte-compile)
+		     'byte-compile-obsolete)
+		 (put 'frame-first-window 'byte-compile 'nil))))))
+
+;;}}}
+;;{{{ Variables
+
+(defvar follow-mode nil
+  "Variable indicating if Follow mode is active.")
+
+(defvar follow-mode-hook nil
+  "*Hooks to run when follow-mode is turned on.")
+
+(defvar follow-mode-off-hook nil
+  "*Hooks to run when follow-mode is turned off.")
+
+(defvar follow-mode-version "follow.el (Release 1.5)"
+  "The current version of Follow mode.")
+
+(defvar follow-mode-map nil
+  "Minor mode keymap for Follow mode.")
+
+(defvar follow-mode-line-text " Follow"
+  "*Text shown in the mode line when Follow mode is active.  
+Defaults to \" Follow\".  Examples of other values
+are \" Fw\", or simply \"\".")
+
+(defvar follow-auto nil
+  "*Non-nil activates Follow mode whenever a file is loaded.")
+
+(defvar follow-mode-prefix "\C-c."
+  "*Prefix key to use for follow commands in Follow mode.
+The value of this variable is checked as part of loading Follow mode.
+After that, changing the prefix key requires manipulating keymaps.")
+
+(defvar follow-intercept-processes t
+  "*When non-nil, Follow Mode will monitor process output.")
+
+(defvar follow-emacs-version-xemacs-p 
+  (string-match "XEmacs" emacs-version)
+  "Non-nil when running under XEmacs.")
+
+(defvar follow-avoid-tail-recenter-p 
+  (not follow-emacs-version-xemacs-p)
+  "*When non-nil, patch emacs so that tail windows won't be recentered.
+
+A \"tail window\" is a window which displays only the end of 
+the buffer.  Normally it is practical for the user that empty
+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.
+
+Must be set before Follow Mode is loaded.
+
+Please note that it is not possible to fully prevent Emacs from
+recentering empty windows.  Please report if you find a repeatable
+situation in which Emacs recenters empty windows.
+
+XEmacs, as of 19.12, does not recenter windows, good!")
+
+(defvar follow-debug nil
+  "*Non-nil when debugging Follow mode.")
+
+
+;;; Internal variables
+
+(defvar follow-internal-force-redisplay nil
+  "True when Follow mode should redisplay the windows.")
+
+(defvar follow-process-filter-alist '()
+  "The original filters for processes intercepted by Follow mode.")
+
+(defvar follow-active-menu nil
+  "The menu visible when Follow mode is active.")
+
+(defvar follow-deactive-menu nil
+  "The menu visible when Follow mode is deactivated.")
+
+(defvar follow-inside-post-command-hook nil
+  "Non-nil when inside Follow modes `post-command-hook'. 
+Used by `follow-window-size-change'.")
+
+;;}}}
+;;{{{ Bug report
+
+(eval-when-compile (require 'reporter))
+
+(defun follow-submit-feedback ()
+  "Sumbit feedback on Follow mode to the author: andersl@csd.uu.se"
+  (interactive)
+  (require 'reporter)
+  (and (y-or-n-p "Do you really want to submit a report on Follow mode? ")
+       (reporter-submit-bug-report
+	"Anders Lindgren <andersl@csd.uu.se>"
+	follow-mode-version
+	'(post-command-hook 
+	  post-command-idle-hook
+	  pre-command-hook
+	  window-size-change-functions
+	  window-scroll-functions
+	  follow-mode-hook
+	  follow-mode-off-hook
+	  follow-auto 
+	  follow-intercept-processes
+	  follow-avoid-tail-recenter-p
+	  follow-process-filter-alist)
+	nil
+	nil
+	(concat 
+	 "Hi Anders!\n\n"
+	 "(I have read the section on how to report bugs in the "
+	 "Emacs manual.)\n\n"
+	 "Even though I know you are busy, I thought you might "
+	 "want to know...\n\n"))))
+
+;;}}}
+;;{{{ Debug messages
+
+;; This inline function must be as small as possible!
+;; Maybe we should define a macro which expands to nil if
+;; the varible is not set.
+
+(defsubst follow-debug-message (&rest args)
+  "Like message, but only active when `follow-debug' is non-nil."
+  (if (and (boundp 'follow-debug) follow-debug)
+      (apply 'message args)))
+
+;;}}}
+
+;;{{{ Keymap/Menu
+
+;;; Define keys for the follow-mode minor mode map and replace some
+;;; functions in the global map.  All `follow' mode special functions
+;;; can be found on (the somewhat cumbersome) "C-c . <key>"
+;;; (Control-C dot <key>). (As of Emacs 19.29 the keys
+;;; C-c <punctuation character> are reserved for minor modes.)
+;;;
+;;; To change the prefix, redefine `follow-mode-prefix' before
+;;; `follow' is loaded, or see the section on `follow-mode-hook'
+;;; above for an example of how to bind the keys the way you like.
+;;;
+;;; Please note that the keymap is defined the first time this file is
+;;; loaded.  Also note that the only legal way to manipulate the
+;;; keymap is to use `define-key'.  Don't change it using `setq' or
+;;; similar!
+
+
+(if follow-mode-map
+    nil
+  (setq follow-mode-map (make-sparse-keymap))
+  (let ((map (make-sparse-keymap)))
+    (define-key map "\C-v"	'follow-scroll-up)
+    (define-key map "\M-v"	'follow-scroll-down)
+    (define-key map "v"		'follow-scroll-down)
+    (define-key map "1"		'follow-delete-other-windows-and-split)
+    (define-key map "b"		'follow-switch-to-buffer)
+    (define-key map "\C-b"	'follow-switch-to-buffer-all)
+    (define-key map "\C-l"	'follow-recenter)
+    (define-key map "<"		'follow-first-window)
+    (define-key map ">"		'follow-last-window)
+    (define-key map "n"		'follow-next-window)
+    (define-key map "p"		'follow-previous-window)
+
+    (define-key follow-mode-map follow-mode-prefix map)
+
+    ;; Replace the standard `end-of-buffer', when in Follow Mode.  (I
+    ;; don't see the point in trying to replace every function which
+    ;; could be enhanced in Follow mode.  End-of-buffer is a special
+    ;; case since it is very simple to define and it greatly enhances
+    ;; the look and feel of Follow mode.)
+    ;;
+    ;; (The function `substitute-key-definition' does not work
+    ;; in all versions of Emacs.)
+    (mapcar
+     (function 
+      (lambda (pair)
+	(let ((old (car pair))
+	      (new (cdr pair)))
+	  (mapcar (function (lambda (key) 
+			      (define-key follow-mode-map key new)))
+		  (where-is-internal old global-map)))))
+     '((end-of-buffer      . follow-end-of-buffer)
+       (fkey-end-of-buffer . follow-end-of-buffer)))
+
+    ;;;
+    ;;; The menu.
+    ;;;
+
+    (if (not follow-emacs-version-xemacs-p)
+
+	;;
+	;; Emacs 19
+	;;
+	(let ((menumap (funcall (symbol-function 'make-sparse-keymap)	
+			"Follow"))
+	      (count 0)
+	      id)
+	  (mapcar 
+	   (function 
+	    (lambda (item)
+	      (setq id 
+		    (or (cdr item)
+			(progn
+			  (setq count (+ count 1))
+			  (intern (format "separator-%d" count)))))
+	      (define-key menumap (vector id) item)
+	      (or (eq id 'follow-mode)
+		  (put id 'menu-enable 'follow-mode))))
+	   ;; In reverse order:
+	   '(("Toggle Follow mode" . follow-mode)
+	     ("--")
+	     ("Recenter"           . follow-recenter)
+	     ("--")
+	     ("Previous Window"    . follow-previous-window)
+	     ("Next Windows"       . follow-next-window)
+	     ("Last Window"        . follow-last-window)
+	     ("First Window"       . follow-first-window)
+	     ("--")
+	     ("Switch To Buffer (all windows)"
+	                           . follow-switch-to-buffer-all)
+	     ("Switch To Buffer"   . follow-switch-to-buffer)
+	     ("--")
+	     ("Delete Other Windows and Split"
+	                           . follow-delete-other-windows-and-split)
+	     ("--")
+	     ("Scroll Down"        . follow-scroll-down)
+	     ("Scroll Up"          . follow-scroll-up)))
+	  
+	  ;; If there is a `tools' meny, we use it. However, we can't add a
+	  ;; minor-mode specific item to it (it's broken), so we make the
+	  ;; contents ghosted when not in use, and add ourselves to the
+	  ;; global map.  If no `tools' menu is present, just make a
+	  ;; top-level menu visible when the mode is activated.
+	  
+	  (let ((tools-map (lookup-key (current-global-map) [menu-bar tools]))
+		(last nil))
+	    (if (sequencep tools-map)
+		(progn
+		  ;; Find the last entry in the menu and store it in `last'.
+		  (mapcar (function
+			   (lambda (x) 
+			     (setq last (or (cdr-safe 
+					     (cdr-safe 
+					      (cdr-safe x))) 
+					    last))))
+			  tools-map)
+		  (if last
+		      (progn
+			(funcall (symbol-function 'define-key-after)
+				  tools-map [separator-follow] '("--") last)
+			(funcall (symbol-function 'define-key-after)
+				 tools-map [follow] (cons "Follow" menumap)
+				 'separator-follow))
+		    ;; Didn't find the last item, Adding to the top of
+		    ;; tools.  (This will probably never happend...)
+		    (define-key (current-global-map) [menu-bar tools follow]
+		      (cons "Follow" menumap))))
+	      ;; No tools menu, add "Follow" to the menubar.
+	      (define-key follow-mode-map [menu-bar follow]
+		(cons "Follow" menumap)))))
+
+      ;;
+      ;; XEmacs.
+      ;;
+
+      ;; place the menu in the `Tools' menu.
+      (let ((menu '("Follow"
+		    :filter follow-menu-filter
+		    ["Scroll Up" follow-scroll-up t]
+		    ["Scroll Down" follow-scroll-down t]
+		    ["Delete Other Windows and Split"
+		     follow-delete-other-windows-and-split t]
+		    ["Switch To Buffer" follow-switch-to-buffer t]
+		    ["Switch To Buffer (all windows)" 
+		     follow-switch-to-buffer-all t]
+		    ["First Window" follow-first-window t]
+		    ["Last Window" follow-last-window t]
+		    ["Next Windows" follow-next-window t]
+		    ["Previous Window" follow-previous-window t]
+		    ["Recenter" follow-recenter t]
+		    ["Deactivate" follow-mode t])))
+
+	;; Why not just `(set-buffer-menubar current-menubar)'?  The
+	;; question is a very good question.  The reason is that under
+	;; Emacs 19, neither `set-buffer-menubar' nor
+	;; `current-menubar' is defined, hence the byte-compiler will
+	;; warn.
+	(funcall (symbol-function 'set-buffer-menubar)
+		 (symbol-value 'current-menubar))
+	(funcall (symbol-function 'add-submenu) '("Tools") menu))
+
+      ;; When the mode is not activated, only one item is visible:
+      ;; "Activate".
+      (defun follow-menu-filter (menu)
+	(if follow-mode
+	    menu
+	  '(["Activate          " follow-mode t]))))))
+
+
+;;; Register the follow mode keymap.
+(or (assq 'follow-mode minor-mode-map-alist)
+    (setq minor-mode-map-alist
+	  (cons (cons 'follow-mode follow-mode-map) minor-mode-map-alist)))
+
+;;}}}
+
+;;{{{ The mode
+
+;;;###autoload
+(defun turn-on-follow-mode ()
+  "Turn on Follow mode. Please see the function `follow-mode'."
+  (interactive)
+  (follow-mode 1))
+
+
+;;;###autoload
+(defun turn-off-follow-mode ()
+  "Turn off Follow mode. Please see the function `follow-mode'."
+  (interactive)
+  (follow-mode -1))
+
+
+;;;###autoload
+(defun follow-mode (arg) 
+  "Minor mode which combines windows into one tall virtual window.
+
+The feeling of a \"virtual window\" has been accomplished by the use
+of two major techniques:
+
+* The windows always displays adjacent sections of the buffer. 
+  This means that whenever one window is moved, all the
+  others will follow.  (Hence the name Follow Mode.)
+
+* Should the point (cursor) end up outside a window, another 
+  window displaying that point is selected, if possible.  This 
+  makes it possible to walk between windows using normal cursor 
+  movement commands.
+
+Follow mode comes to its prime when used on a large screen and two
+side-by-side window are used. The user can, with the help of Follow
+mode, use two full-height windows as though they would have been
+one. Imagine yourself editing a large function, or section of text,
+and beeing able to use 144 lines instead of the normal 72... (your
+mileage may vary).
+
+To split one large window into two side-by-side windows, the commands
+`\\[split-window-horizontally]' or \
+`M-x follow-delete-other-windows-and-split' can be used.
+
+Only windows displayed in the same frame follow each-other.
+
+If the variable `follow-intercept-processes' is non-nil, Follow mode
+will listen to the output of processes and redisplay accordingly.
+\(This is the default.)
+
+When Follow mode is switched on, the hook `follow-mode-hook'
+is called.  When turned off, `follow-mode-off-hook' is called.
+
+Keys specific to Follow mode:
+\\{follow-mode-map}"
+  (interactive "P")
+  (make-local-variable 'follow-mode)
+  (put 'follow-mode 'permanent-local t)
+  (let ((follow-mode-orig follow-mode))
+    (setq follow-mode
+	  (if (null arg)
+	      (not follow-mode)
+	    (> (prefix-numeric-value arg) 0)))
+    (if (and follow-mode follow-intercept-processes)
+	(follow-intercept-process-output))
+    (cond ((and follow-mode (not follow-mode-orig)) ; On
+	   ;; XEmacs: If this is non-nil, the window will scroll before
+	   ;; the point will have a chance to get into the next window.
+	   (if (boundp 'scroll-on-clipped-lines)
+	       (set 'scroll-on-clipped-lines nil))
+	   (force-mode-line-update)
+	   (add-hook 'post-command-hook 'follow-post-command-hook t)
+	   (if (boundp 'post-command-idle-hook)
+	       (add-hook 'post-command-idle-hook 
+			 'follow-avoid-tail-recenter t))
+	   (run-hooks 'follow-mode-hook))
+
+	  ((and (not follow-mode) follow-mode-orig) ; Off
+	   (force-mode-line-update)
+	   (run-hooks 'follow-mode-off-hook)))))
+
+
+;; 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)))
+
+;;}}}
+;;{{{ Find file hook
+
+;; This will start follow-mode whenever a new file is loaded, if
+;; the variable `follow-auto' is non-nil.
+
+(add-hook 'find-file-hooks 'follow-find-file-hook t)
+
+(defun follow-find-file-hook ()
+  "Find-file hook for Follow Mode.  See the variable `follow-auto'."
+  (if follow-auto (follow-mode t)))
+
+;;}}}
+
+;;{{{ User functions
+
+;;;
+;;; User functions usable when in Follow mode.
+;;;
+
+;;{{{ Scroll
+
+;; `scroll-up' and `-down', but for windows in Follow Mode.
+;;
+;; Almost like the real thing, excpet when the cursor ends up outside
+;; the top or bottom...  In our case however, we end up outside the
+;; window and hence we are recenterd.  Should we let `recenter' handle
+;; the point position we would never leave the selected window.  To do
+;; it ourselves we would need to do our own redisplay, which is easier
+;; said than done.  (Why didn't I do a real display abstraction from
+;; the beginning?)
+;;
+;; We must sometimes set `follow-internal-force-redisplay', otherwise
+;; our post-command-hook will move our windows back into the old
+;; position...  (This would also be corrected if we would have had a
+;; good redisplay abstraction.)
+
+(defun follow-scroll-up (&optional arg)
+  "Scroll text in a Follow Mode window chain up.
+
+If called with no ARG, the `next-screen-context-lines' last lines of
+the bottom window in the chain will be visible in the top window.
+
+If called with an argument, scroll ARG lines up.
+Negative ARG means scroll downward.
+
+Works like `scroll-up' when not in Follow Mode."
+  (interactive "P")
+  (cond ((not (and (boundp 'follow-mode) follow-mode))
+	 (scroll-up arg))
+	(arg
+	 (save-excursion (scroll-up arg))
+	 (setq follow-internal-force-redisplay t))
+	(t
+	 (let* ((windows (follow-all-followers))
+		(end (window-end (car (reverse windows)))))
+	   (if (eq end (point-max))
+	       (signal 'end-of-buffer nil)
+	     (select-window (car windows))
+	     (goto-char end)
+	     (vertical-motion (- next-screen-context-lines))
+	     (set-window-start (car windows) (point)))))))
+
+
+(defun follow-scroll-down (&optional arg)
+  "Scroll text in a Follow Mode window chain down.
+
+If called with no ARG, the `next-screen-context-lines' top lines of
+the top window in the chain will be visible in the bottom window.
+
+If called with an argument, scroll ARG lines down.
+Negative ARG means scroll upward.
+
+Works like `scroll-up' when not in Follow Mode."
+  (interactive "P")
+  (cond ((not (and (boundp 'follow-mode) follow-mode))
+	 (scroll-up arg))
+	(arg
+	 (save-excursion (scroll-down arg)))
+	(t
+	 (let* ((windows (follow-all-followers))
+		(win (car (reverse windows)))
+		(start (window-start (car windows))))
+	   (if (eq start (point-min))
+	       (signal 'beginning-of-buffer nil)
+	     (select-window win)
+	     (goto-char start)
+	     (vertical-motion (- (- (window-height win) 
+				    1 
+				    next-screen-context-lines)))
+	     (set-window-start win (point))
+	     (goto-char start)
+	     (vertical-motion (- next-screen-context-lines 1))
+	     (setq follow-internal-force-redisplay t))))))
+
+;;}}}
+;;{{{ Buffer
+
+;;;###autoload
+(defun follow-delete-other-windows-and-split (&optional arg)
+  "Create two side by side windows and enter Follow Mode.
+
+Execute this command to display as much as possible of the text
+in the selected window.  All other windows, in the current 
+frame, are deleted and the selected window is split in two
+side-by-side windows. Follow Mode is activated, hence the 
+two windows always will display two successive pages.
+\(If one window is moved, the other one will follow.)
+
+If ARG is positive, the leftmost window is selected.  If it negative,
+the rightmost is selected.  If ARG is nil, the leftmost window is
+selected if the original window is the first one in the frame.
+
+To bind this command to a hotkey, place the following line
+in your `~/.emacs' file, replacing [f7] by your favourite key:
+    (global-set-key [f7] 'follow-delete-other-windows-and-split)"
+  (interactive "P")
+  (let ((other (or (and (null arg) 
+			(not (eq (selected-window)
+				 (frame-first-window (selected-frame)))))
+		   (and arg
+			(< (prefix-numeric-value arg) 0))))
+	(start (window-start)))
+    (delete-other-windows)
+    (split-window-horizontally)
+    (if other 
+	(progn
+	  (other-window 1)
+	  (set-window-start (selected-window) start)
+	  (setq follow-internal-force-redisplay t)))
+    (follow-mode 1)))
+
+(defun follow-switch-to-buffer (buffer)
+  "Show BUFFER in all windows in the current Follow Mode window chain."
+  (interactive "BSwitch to Buffer: ")
+  (let ((orig-window (selected-window))
+	(windows (follow-all-followers)))
+    (while windows
+      (select-window (car windows))
+      (switch-to-buffer buffer)
+      (setq windows (cdr windows)))
+    (select-window orig-window)))
+
+
+(defun follow-switch-to-buffer-all (&optional buffer)
+  "Show BUFFER in all windows on this frame.
+Defaults to current buffer."
+  (interactive (list (read-buffer "Switch to Buffer: " 
+				  (current-buffer))))
+  (or buffer (setq buffer (current-buffer)))
+  (let ((orig-window (selected-window)))
+    (walk-windows 
+     (function 
+      (lambda (win)
+	(select-window win)
+	(switch-to-buffer buffer))))
+    (select-window orig-window)
+    (follow-redisplay)))
+
+
+(defun follow-switch-to-current-buffer-all ()
+  "Show current buffer in all windows on this frame, and enter Follow Mode.
+
+To bind this command to a hotkey place the following line 
+in your `~/.emacs' file:
+	(global-set-key [f7] 'follow-switch-to-current-buffer-all)"
+  (interactive)
+  (or (and (boundp 'follow-mode) follow-mode)
+      (follow-mode 1))
+  (follow-switch-to-buffer-all))
+
+;;}}}
+;;{{{ Movement
+
+;; Note, these functions are not very useful, atleast not unless you
+;; rebind the rather cumbersome key sequence `C-c . p'.
+
+(defun follow-next-window ()
+  "Select the next window showing the same buffer."
+  (interactive)
+  (let ((succ (cdr (follow-split-followers (follow-all-followers)))))
+    (if succ
+	(select-window (car succ))
+      (error "%s" "No more windows"))))
+
+
+(defun follow-previous-window ()
+  "Select the previous window showing the same buffer."
+  (interactive)
+  (let ((pred (car (follow-split-followers (follow-all-followers)))))
+    (if pred
+	(select-window (car pred))
+      (error "%s" "No more windows"))))
+
+
+(defun follow-first-window ()
+  "Select the first window in the frame showing the same buffer."
+  (interactive)
+  (select-window (car (follow-all-followers))))
+
+
+(defun follow-last-window ()
+  "Select the last window in the frame showing the same buffer."
+  (interactive)
+  (select-window (car (reverse (follow-all-followers)))))
+
+;;}}}
+;;{{{ Redraw
+
+(defun follow-recenter (&optional arg)
+  "Recenter the middle window around the point,
+and rearrange all other windows around the middle window.
+
+With a positive argument, place the current line ARG lines
+from the top.  With a negative, place it -ARG lines from the
+bottom."
+  (interactive "P")
+  (if arg
+      (let ((p (point))
+	    (arg (prefix-numeric-value arg)))
+	(if (>= arg 0)
+	    ;; Recenter relative to the top.
+	    (progn
+	      (follow-first-window)
+	      (goto-char p)
+	      (recenter arg))
+	  ;; Recenter relative to the bottom.
+	  (follow-last-window)
+	  (goto-char p)
+	  (recenter arg)
+	  ;; Otherwise, our post-command-hook will move the window
+	  ;; right back.
+	  (setq follow-internal-force-redisplay t)))
+    ;; Recenter in the middle.
+    (let* ((dest (point))
+	   (windows (follow-all-followers))
+	   (win (nth (/ (- (length windows) 1) 2) windows)))
+      (select-window win)
+      (goto-char dest)
+      (recenter)
+      ;;(setq follow-internal-force-redisplay t)
+      )))
+
+
+(defun follow-redraw ()
+  "Arrange windows displaying the same buffer in successor order.
+This function can be called even if the buffer is not in Follow mode.
+
+Hopefully, there should be no reason to call this function when in
+Follow mode since the windows should always be aligned."
+  (interactive)
+  (sit-for 0)
+  (follow-redisplay))
+
+;;}}}
+;;{{{ End of buffer
+
+(defun follow-end-of-buffer (&optional arg)
+  "Move point to the end of the buffer. Follow Mode style.
+
+If the end is not visible, it will be displayed in the last possible
+window in the Follow Mode window chain.
+
+The mark is left at the previous position. With arg N, put point N/10
+of the way from the true end."
+  (interactive "P")
+  (let ((followers (follow-all-followers))
+	(pos (point)))
+    (cond (arg
+	   (select-window (car (reverse followers))))
+	  ((follow-select-if-end-visible 
+	    (follow-windows-start-end followers)))
+	  (t
+	   (select-window (car (reverse followers)))))
+    (goto-char pos)
+    (end-of-buffer arg)))
+
+;;}}}
+
+;;}}}
+
+;;{{{ Display
+
+;;;; The display routines
+
+;;{{{ Information gathering functions
+
+(defun follow-all-followers (&optional testwin)
+  "Return all windows displaying the same buffer as the TESTWIN.
+The list contains only windows displayed in the same frame as TESTWIN.
+If TESTWIN is nil the selected window is used."
+  (or (and testwin (window-live-p testwin))
+      (setq testwin (selected-window)))
+  (let* ((top (frame-first-window (window-frame testwin)))
+	 (win top)
+	 (done nil)
+	 (windows '())
+	 (buffer (window-buffer testwin)))
+    (while (and (not done) win)
+      (if (eq (window-buffer win) buffer)
+	  (setq windows (cons win windows)))
+      (setq win (next-window win 'not))
+      (if (eq win top)
+	  (setq done t)))
+    (nreverse windows)))
+
+
+(defun follow-split-followers (windows &optional win)
+  "Split the WINDOWS into the sets: predecessors and successors.
+Return `(PRED . SUCC)' where `PRED' and `SUCC' are ordered starting 
+from the selected window."
+  (or win 
+      (setq win (selected-window)))
+  (let ((pred '()))
+    (while (not (eq (car windows) win))
+      (setq pred (cons (car windows) pred))
+      (setq windows (cdr windows)))
+    (cons pred (cdr windows))))
+
+
+;; Try to optimize this function for speed!
+
+(defun follow-calc-win-end (&optional win)
+  "Calculate the presumed window end for WIN.
+
+Actually, the position returned is the start of the next
+window, normally is the end plus one.
+
+If WIN is nil, the selected window is used.
+
+Returns (end-pos end-of-buffer-p)"
+  (if follow-emacs-version-xemacs-p
+      ;; XEmacs can calculate the end of the window by using
+      ;; the 'guarantee options. GOOD!
+      (let ((end (window-end win t)))
+	(if (= end (funcall (symbol-function 'point-max)
+			    (window-buffer win)))
+	    (list end t)
+	  (list (+ end 1) nil))) 
+    ;; Emacs 19: We have to calculate the end by ourselves.
+    ;; This code works on both XEmacs and Emacs 19, but now
+    ;; that XEmacs has got custom-written code, this could
+    ;; be optimized for Emacs 19.
+    (let ((orig-win (and win (selected-window)))
+	  height
+	  buffer-end-p)
+      (if win (select-window win))
+      (prog1
+	  (save-excursion
+	    (goto-char (window-start))
+	    (setq height (- (window-height) 1))
+	    (setq buffer-end-p
+		  (if (bolp)
+		      (not (= height (vertical-motion height)))
+		    (save-restriction
+		      ;; Fix a mis-feature in `vertical-motion':
+		      ;; The start of the window is assumed to
+		      ;; coinside with the start of a line.
+		      (narrow-to-region (point) (point-max))
+		      (not (= height (vertical-motion height))))))
+	    (list (point) buffer-end-p))
+	(if orig-win
+	    (select-window orig-win))))))
+
+
+;; Can't use `save-window-excursion' since it triggers a redraw.
+(defun follow-calc-win-start (windows pos win)
+  "Calculate where WIN will start if the first in WINDOWS start at POS.
+
+If WIN is nil the point below all windows is returned."
+  (let (start)
+    (while (and windows (not (eq (car windows) win)))
+      (setq start (window-start (car windows)))
+      (set-window-start (car windows) pos 'noforce)
+      (setq pos (car (inline (follow-calc-win-end (car windows)))))
+      (set-window-start (car windows) start 'noforce)
+      (setq windows (cdr windows)))
+    pos))
+
+
+;; 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:
+;; ((Win Start End End-of-buffer-visible-p) ...)
+
+;; Used to have a `save-window-excursion', but it obviously triggered
+;; redraws of the display. Check if I used it for anything.
+
+
+(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)
+  "Non-nil when POS is visible in WIN."
+  (let ((wstart-wend-bend (cdr (assq win win-start-end))))
+    (and (>= pos (car wstart-wend-bend))
+	 (or (< pos (car (cdr wstart-wend-bend)))
+	     (nth 2 wstart-wend-bend)))))
+
+
+;; By `aligned' we mean that for all adjecent windows, the end of the
+;; 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)
+  "Non-nil if the follower WINDOWS are alinged."
+  (let ((res t)) 
+    (save-excursion
+       (goto-char (window-start (car (car win-start-end))))
+       (if (bolp)
+	   nil
+	 (vertical-motion 0 (car (car win-start-end)))
+	 (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 win-start-end (cdr win-start-end)))
+    res))
+
+
+;; Check if the point is visible in all windows. (So that
+;; no one will be recentered.)
+
+(defun follow-point-visible-all-windows-p (win-start-end)
+  "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 win-start-end (cdr win-start-end)))
+    res))
+
+
+;; Make sure WIN always starts at the beginning of an whole screen
+;; line. If WIN is not aligned the start is updated which probably
+;; will lead to a redisplay of the screen later on.
+;;
+;; This is used with the first window in a follow chain.  The reason
+;; is that we want to detect that the point is outside the window.
+;; (Without the update, the start of the window will move as the
+;; user presses BackSpace, and the other window redisplay routines
+;; will move the start of the window in the wrong direction.)
+
+(defun follow-update-window-start (win)
+  "Make sure that the start of WIN starts at a full screen line."
+  (save-excursion
+    (goto-char (window-start win))
+    (if (bolp)
+	nil
+      (vertical-motion 0 win)
+      (if (eq (point) (window-start win))
+	  nil
+	(vertical-motion 1 win)
+	(set-window-start win (point) 'noforce)))))
+
+;;}}}
+;;{{{ Selection functions
+
+;; Make a window in WINDOWS selected if it currently
+;; is displaying the position DEST.
+;;
+;; We don't select a window if it just has been moved.
+
+(defun follow-select-if-visible (dest win-start-end)
+  "Select and return a window, if DEST is visible in it.
+Return the selected window."
+  (let ((win nil))
+    (while (and (not win) win-start-end)
+      ;; Don't select a window which was just moved. This makes it
+      ;; possible to later select the last window after a `end-of-buffer'
+      ;; command.
+      (if (follow-pos-visible dest (car (car win-start-end)) win-start-end)
+	  (progn
+	    (setq win (car (car win-start-end)))
+	    (select-window win)))
+      (setq win-start-end (cdr win-start-end)))
+    win))
+
+
+;; Lets select a window showing the end. Make sure we only select it if it
+;; it wasn't just moved here. (i.e. M-> shall not unconditionally place
+;; the point in the selected window.)
+;;
+;; (Compability cludge: in Emacs 19 `window-end' is equal to `point-max';
+;; in XEmacs, it is equal to `point-max + 1'. Should I really bother
+;; checking `window-end' now when I check `end-of-buffer' explicitylt?)
+
+(defun follow-select-if-end-visible (win-start-end)
+  "Select and return a window, if end is visible in it."
+  (let ((win nil))
+    (while (and (not win) win-start-end)
+      ;; Don't select a window which was just moved. This makes it
+      ;; possible to later select the last window after a `end-of-buffer'
+      ;; command.
+      (if (and (eq (point-max) (nth 2 (car win-start-end)))
+	       (nth 3 (car win-start-end))
+	       (eq (point-max) (min (point-max)
+				    (window-end (car (car win-start-end))))))
+	  (progn
+	    (setq win (car (car win-start-end)))
+	    (select-window win)))
+      (setq win-start-end (cdr win-start-end)))
+    win))
+
+
+;; Select a window which will display the point if the windows would
+;; be redisplayed with the first window fixed. This is useful for
+;; example when the user has pressed return at the bottom of a window
+;; as the point is not visible in any window.
+
+(defun follow-select-if-visible-from-first (dest windows)
+  "Select and return a window with DEST, if WINDOWS are redrawn from top."
+  (let ((win nil)
+	end-pos-end-p)
+    (save-excursion
+      (goto-char (window-start (car windows)))
+      ;; Make sure the line start in the beginning of a real screen
+      ;; line.
+      (vertical-motion 0 (car windows))    
+      (if (< dest (point))
+	  ;; Above the start, not visible.
+	  nil
+	;; At or below the start. Check the windows.
+	(save-window-excursion
+	  (while (and (not win) windows)
+	    (set-window-start (car windows) (point) 'noforce)
+	    (setq end-pos-end-p (follow-calc-win-end (car windows)))
+	    (goto-char (car end-pos-end-p))
+	    ;; Visible, if dest above end, or if eob is visible inside
+	    ;; the window.
+	    (if (or (car (cdr end-pos-end-p))
+		    (< dest (point)))
+		  (setq win (car windows))
+		(setq windows (cdr windows)))))))
+    (if win 
+	(select-window win))
+    win))
+
+
+;;}}}
+;;{{{ Redisplay 
+
+;; Redraw all the windows on the screen, starting with the top window.
+;; The window used as as marker is WIN, or the selcted window if WIN
+;; is nil.
+
+(defun follow-redisplay (&optional windows win)
+  "Reposition the WINDOWS around WIN.
+Should the point be too close to the roof we redisplay everything
+from the top. WINDOWS should contain a list of windows to
+redisplay, it is assumed that WIN is a member of the list. 
+Should WINDOWS be nil, the windows displaying the
+same buffer as WIN, in the current frame, are used.
+Should WIN be nil, the selected window is used." 
+  (or win
+      (setq win (selected-window)))
+  (or windows 
+      (setq windows (follow-all-followers win)))
+  (follow-downward windows (follow-calculate-first-window-start windows win)))
+
+
+;; Redisplay a chain of windows. Start every window directly after the
+;; end of the previous window, to make sure long lines are displayed
+;; correctly.
+
+(defun follow-downward (windows pos)
+  "Redisplay all WINDOWS starting at POS."
+  (while windows
+    (set-window-start (car windows) pos)
+    (setq pos (car (follow-calc-win-end (car windows))))
+    (setq windows (cdr windows))))
+
+
+;;(defun follow-downward (windows pos)
+;;  "Redisplay all WINDOWS starting at POS."
+;;  (let (p)
+;;    (while windows
+;;      (setq p (window-point (car windows)))
+;;      (set-window-start (car windows) pos)
+;;      (set-window-point (car windows) (max p pos))
+;;      (setq pos (car (follow-calc-win-end (car windows))))
+;;      (setq windows (cdr windows)))))
+
+
+;; Return the start of the first window.
+;;
+;; First, estimate the position. It the value is not perfect (i.e. we
+;; have somewhere splited a line between windows) we try to enhance
+;; the value.
+;;
+;; The guess is always perfect if no long lines is split between
+;; windows.
+;;
+;; The worst case peformace of probably very bad, but it is very
+;; unlikely that we ever will miss the correct start by more than one
+;; or two lines.
+
+(defun follow-calculate-first-window-start (windows &optional win start)
+  "Calculate the start of the first window.
+
+WINDOWS is a chain of windows to work with.  WIN is the window
+to recenter around.  It is assumed that WIN starts at position 
+START."
+  (or win 
+      (setq win (selected-window)))
+  (or start 
+      (setq start (window-start win)))
+  (let ((guess (follow-estimate-first-window-start windows win start)))
+    (if (car guess)
+	(cdr guess)
+      ;; The guess wasn't exact, try to enhance it.
+      (let ((win-start (follow-calc-win-start windows (cdr guess) win)))
+	(cond ((= win-start start) 
+	       (follow-debug-message "exact")
+	       (cdr guess))
+	      ((< win-start start)
+	       (follow-debug-message "above")
+	       (follow-calculate-first-window-start-from-above 
+		windows (cdr guess) win start))
+	      (t
+	       (follow-debug-message "below")
+	       (follow-calculate-first-window-start-from-below 
+		windows (cdr guess) win start)))))))
+
+
+;; `exact' is disabled due to XEmacs and fonts of variable
+;; height.
+(defun follow-estimate-first-window-start (windows win start)
+  "Estimate the position of the first window.
+
+Returns (EXACT . POS). If EXACT is non-nil, POS is the starting
+position of the first window. Otherwise it is a good guess."
+  (let ((pred (car (follow-split-followers windows win)))
+	(exact nil))
+    (save-excursion
+      (goto-char start)
+      ;(setq exact (bolp))
+      (vertical-motion 0 win)
+      (while pred
+	(vertical-motion (- 1 (window-height (car pred))) (car pred))
+	(if (not (bolp))
+	  (setq exact nil))
+	(setq pred (cdr pred)))
+      (cons exact (point)))))
+
+
+;; Find the starting point, start at GUESS and search downward.
+;; The returned point is always a point below GUESS.
+
+(defun follow-calculate-first-window-start-from-above 
+       (windows guess win start)
+  (save-excursion
+    (let ((done nil)
+	  win-start
+	  res)
+      (goto-char guess)
+      (while (not done)
+	(if (not (= (vertical-motion 1 (car windows)) 1))
+	    ;; Hit bottom! (Can we really do this?)
+	    ;; We'll keep it, since it ensures termination.
+	    (progn
+	      (setq done t)
+	      (setq res (point-max)))
+	  (setq win-start (follow-calc-win-start windows (point) win))
+	  (if (>= win-start start)
+	      (progn 
+		(setq done t)
+		(setq res (point))))))
+      res)))
+
+
+;; Find the starting point, start at GUESS and search upward.  Return
+;; a point on the same line as GUESS, or above.
+;;
+;; (Is this ever used? I must make sure it works just in case it is
+;; ever called.)
+
+(defun follow-calculate-first-window-start-from-below
+       (windows guess &optional win start)
+  (setq win (or win (selected-window)))
+  (setq start (or start (window-start win)))
+  (save-excursion
+    (let ((done nil)
+	  win-start
+	  res)
+      ;; Always calculate what happend when no line is displayed in the first
+      ;; window. (The `previous' res is needed below!)
+      (goto-char guess)
+      (vertical-motion 0 (car windows))
+      (setq res (point))
+      (while (not done)
+	(if (not (= (vertical-motion -1 (car windows)) -1))
+	    ;; Hit roof!
+	    (progn
+	      (setq done t)
+	      (setq res (point-min)))
+	  (setq win-start (follow-calc-win-start windows (point) win))
+	  (cond ((= win-start start)	; Perfect match, use this value
+		 (setq done t)
+		 (setq res (point)))
+		((< win-start start)	; Walked to far, use preious result
+		 (setq done t))
+		(t			; Store result for next iteration
+		 (setq res (point))))))
+      res)))
+
+;;}}}
+;;{{{ Avoid tail recenter
+
+;; This sets the window internal flag `force_start'. The effect is that
+;; windows only displaying the tail isn't recentered.
+;; Has to be called before every redisplay... (Great isn't it?)
+;;
+;; XEmacs doesn't recenter the tail, GOOD!
+;;
+;; A window displaying only the tail, is a windows whose
+;; window-start position is equal to (point-max) of the buffer it
+;; displays.
+;;
+;; This function is also added to `post-command-idle-hook', introduced
+;; in Emacs 19.30.  This is needed since the vaccine injected by the
+;; call from `post-command-hook' only works until the next redisplay.
+;; It is possible that the functions in the `post-command-idle-hook'
+;; can cause a redisplay, and hence a new vaccine is needed.
+;;
+;; Sometimes, calling this function could actually cause a redisplay,
+;; especially if it is placed in the debug filter section.  I must
+;; investigate this further...
+
+(defun follow-avoid-tail-recenter (&rest rest)
+  "Make sure windows displaying the end of a buffer aren't recentered.
+
+This is done by reading and rewriting the start positon of 
+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))))
+
+;;}}}
+
+;;}}}
+;;{{{ Post Command Hook
+
+;;; The magic little box. This function is called after every command.
+
+;; This is not as complicated as it seems. It is simply a list of common
+;; display situations and the actions to take, plus commands for redrawing
+;; the screen if it should be unaligned.
+;;
+;; We divide the check into two parts; whether we are at the end or not.
+;; This is due to the fact that the end can actaually be visible
+;; in several window even though they are aligned.
+
+(defun follow-post-command-hook ()
+  "Ensure that the windows in Follow mode are adjecent after each command."
+  (setq follow-inside-post-command-hook t)
+  (if (or (not (input-pending-p))
+	  ;; Sometimes, in XEmacs, mouse events are not handled
+	  ;; properly by `input-pending-p'.  A typical example is
+	  ;; when clicking on a node in `info'.
+	  (and (boundp 'current-mouse-event)
+	       (symbol-value 'current-mouse-event)
+	       (fboundp 'button-event-p)
+	       (funcall (symbol-function 'button-event-p)
+			(symbol-value 'current-mouse-event))))
+      ;; Work in the selected window, not in the current buffer.
+      (let ((orig-buffer (current-buffer))
+	    (win (selected-window)))
+	(set-buffer (window-buffer win))
+	(if (and (boundp 'follow-mode) follow-mode
+		 (not (window-minibuffer-p win)))
+	    ;; The buffer shown in the selected window is in follow
+	    ;; mode, lets find the current state of the display and
+	    ;; cache the result for speed (i.e. `aligned' and `visible'.)
+	    (let* ((windows (inline (follow-all-followers win)))
+		   (dest (point))
+		   (win-start-end (progn
+				    (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)
+	      ;; Select a window to display the point.
+	      (or follow-internal-force-redisplay
+		  (progn
+		    (if (eq dest (point-max))
+			;; We're at the end, we have be be careful since
+			;; the display can be aligned while `dest' can
+			;; be visible in several windows.
+			(cond
+			 ;; Select the current window, but only when
+			 ;; the display is correct. (When inserting
+			 ;; character in a tail window, the display is
+			 ;; not correct, as they are shown twice.)
+			 ;;
+			 ;; Never stick to the current window after a
+			 ;; deletion.  The reason is cosmetic, when
+			 ;; typing `DEL' in a window showing only the
+			 ;; end of the file, character are removed
+			 ;; from the window above, which is very
+			 ;; unintuitive.
+			 ((and visible
+			       aligned
+			       (not (memq this-command 
+					  '(backward-delete-char
+					    delete-backward-char
+					    backward-delete-char-untabify
+					    kill-region))))
+			  (follow-debug-message "Max: same"))
+			 ;; If the end is visible, and the window
+			 ;; doesn't seems like it just has been moved,
+			 ;; select it.
+			 ((follow-select-if-end-visible win-start-end)
+			  (follow-debug-message "Max: end visible")
+			  (setq visible t)
+			  (setq aligned nil)
+			  (goto-char dest))
+			 ;; Just show the end...
+			 (t
+			  (follow-debug-message "Max: default")
+			  (select-window (car (reverse windows)))
+			  (goto-char dest)
+			  (setq visible nil)
+			  (setq aligned nil)))
+		      
+		      ;; We're not at the end, here life is much simpler.
+		      (cond
+		       ;; This is the normal case! 
+		       ;; It should be optimized for speed.
+		       ((and visible aligned)
+			(follow-debug-message "same"))
+		       ;; Pick a position in any window.  If the
+		       ;; display is ok, this will pick the `correct'
+		       ;; window.  If the display is wierd do this
+		       ;; anyway, this will be the case after a delete
+		       ;; at the beginning of the window.
+		       ((follow-select-if-visible dest win-start-end)
+			(follow-debug-message "visible")
+			(setq visible t)
+			(goto-char dest))
+		       ;; Not visible anywhere else, lets pick this one.
+		       ;; (Is this case used?)
+		       (visible
+			(follow-debug-message "visible in selected."))
+		       ;; Far out!
+		       ((eq dest (point-min))
+			(follow-debug-message "min")
+			(select-window (car windows))
+			(goto-char dest)
+			(set-window-start (selected-window) (point-min))
+			(setq win-start-end (follow-windows-start-end windows))
+			(setq visible t)
+			(setq aligned nil))
+		       ;; If we can position the cursor without moving the first
+		       ;; window, do it. This is the case which catches `RET'
+		       ;; at the bottom of a window.
+		       ((follow-select-if-visible-from-first dest windows)
+			(follow-debug-message "Below first")
+			(setq visible t)		  
+			(setq aligned t)
+			(follow-redisplay windows (car windows))
+			(goto-char dest))
+		       ;; None of the above. For simplicity, we stick to the
+		       ;; selected window.
+		       (t
+			(follow-debug-message "None")
+			(setq visible nil)
+			(setq aligned nil))))
+		    ;; If a new window has been selected, make sure that the
+		    ;; old is not scrolled when the point is outside the
+		    ;; window.
+		    (or (eq win (selected-window))
+			(let ((p (window-point win)))
+			  (set-window-start win (window-start win) nil)
+			  (set-window-point win p)))))
+	      ;; Make sure the point is visible in the selected window.
+	      ;; (This could lead to a scroll.)
+	      (if (or visible
+		      (follow-pos-visible dest win win-start-end))
+		  nil
+		(sit-for 0)
+		(follow-avoid-tail-recenter)
+		(setq win-start-end (follow-windows-start-end windows))
+		(setq aligned nil))
+	      ;; Redraw the windows whenever needed.
+	      (if (or follow-internal-force-redisplay
+		      (not (or aligned
+			       (follow-windows-aligned-p win-start-end)))
+		      (not (inline (follow-point-visible-all-windows-p 
+				    win-start-end))))
+		  (progn
+		    (setq follow-internal-force-redisplay nil)
+		    (follow-redisplay windows (selected-window))
+		    (setq win-start-end (follow-windows-start-end windows))
+		    ;; 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.
+		    ;; It can also, in rare situations happend when
+		    ;; long lines are used and there is a big
+		    ;; difference between the width of the windows.
+		    ;; (When scrolling one line in a wide window which
+		    ;; will cause a move larger that an entire small
+		    ;; window.)
+		    (if (follow-pos-visible dest win win-start-end)
+			nil
+		      (follow-select-if-visible dest win-start-end)
+		      (goto-char dest))))
+	      
+	      ;; If the region is visible, make it look good when spanning
+	      ;; multiple windows.
+	      (if (or (and (boundp 'mark-active) (symbol-value 'mark-active))
+		      (and (fboundp 'region-active-p) 
+			   (funcall (symbol-function 'region-active-p))))
+		  (follow-maximize-region 
+		   (selected-window) windows win-start-end))
+
+	      (follow-avoid-tail-recenter)	      
+	      ;; DEBUG
+	      ;;(if (not (follow-windows-aligned-p 
+	      ;;           (follow-windows-start-end windows)))
+	      ;;    (message "follow-mode: windows still unaligend!"))
+	      ;; END OF DEBUG
+	      )				; Matches (let*
+	  ;; Buffer not in follow mode:
+	  ;; We still must update the windows displaying the tail so that
+	  ;; Emacs won't recenter them.
+	  (follow-avoid-tail-recenter))
+	(set-buffer orig-buffer)))
+  (setq follow-inside-post-command-hook nil))
+
+;;}}}
+;;{{{ The region
+
+;; Tries to make the highlighted area representing the region look
+;; good when spanning several windows. 
+;;
+;; Not perfect, as the point can't be placed at window end, only at
+;; end-1. Whis will highlight a little bit in windows above
+;; the current.
+
+(defun follow-maximize-region (win windows win-start-end)
+  "Make a highlighted region stretching multiple windows look good
+when in Follow mode."
+  (let* ((all (follow-split-followers windows win))
+	 (pred (car all))
+	 (succ (cdr all))
+	 data)
+    (while pred
+      (setq data (assq (car pred) win-start-end))
+      (set-window-point (car pred) (max (nth 1 data) (- (nth 2 data) 1)))
+      (setq pred (cdr pred)))
+    (while succ
+      (set-window-point (car succ) (nth 1 (assq (car succ) win-start-end)))
+      (setq succ (cdr succ)))))
+
+;;}}}
+;;{{{ Scroll bar
+
+;;;; Scroll-bar support code.
+
+;;; Why is it needed? Well, if the selected window is in follow mode,
+;;; all its follower stick to it blindly. If one of them is scrolled,
+;;; it immediately returns to the original position when the mouse is
+;;; released. If the selected window is not a follower of the dragged
+;;; window the windows will be unaligned.
+
+;;; The advices doesn't get compiled. Aestetically, this might be a
+;;; problem but in practical life it isn't.
+
+;;; Discussion: Now when the other windows in the chain follow the
+;;; dragged, should we really select it?
+
+(cond ((fboundp 'scroll-bar-drag)
+       ;;;
+       ;;; Emacs 19 style scrollbars.
+       ;;;
+
+       ;; Select the dragged window if it is a follower of the
+       ;; selected window.
+       ;;
+       ;; Generate advices of the form:
+       ;; (defadvice scroll-bar-drag (after follow-scroll-bar-drag activate)
+       ;;   "Adviced by `follow-mode'."
+       ;;   (follow-redraw-after-event (ad-get-arg 0)))
+       (let ((cmds '(scroll-bar-drag
+		     scroll-bar-drag-1	; Executed at every move.
+		     scroll-bar-scroll-down
+		     scroll-bar-scroll-up
+		     scroll-bar-set-window-start)))
+	 (while cmds
+	   (eval
+	    (` (defadvice (, (intern (symbol-name (car cmds))))
+		 (after 
+		  (, (intern (concat "follow-" (symbol-name (car cmds))))) 
+		  activate)
+		 "Adviced by Follow Mode."
+		 (follow-redraw-after-event (ad-get-arg 0)))))
+	   (setq cmds (cdr cmds))))
+    
+    
+       (defun follow-redraw-after-event (event)
+	 "Adviced by Follow mode."
+	 (condition-case nil
+	     (let* ((orig-win (selected-window))
+		    (win (nth 0 (funcall 
+				 (symbol-function 'event-start) event)))
+		    (fmode (assq 'follow-mode 
+				 (buffer-local-variables 
+				  (window-buffer win)))))
+	       (if (and fmode (cdr fmode))
+		   ;; The selected window is in follow-mode
+		   (progn
+		     ;; Recenter around the dragged window.
+		     (select-window win)
+		     (follow-redisplay)
+		     (select-window orig-win))))
+	   (error nil))))
+
+
+      ((fboundp 'scrollbar-vertical-drag)
+       ;;;
+       ;;; XEmacs style scrollbars.
+       ;;;
+
+       ;; Advice all scrollbar functions on the form:
+       ;;
+       ;; (defadvice scrollbar-line-down 
+       ;;	(after follow-scrollbar-line-down activate)
+       ;;   (follow-xemacs-scrollbar-support (ad-get-arg 0)))
+
+      (let ((cmds '(scrollbar-line-down	; Window
+		    scrollbar-line-up
+		    scrollbar-page-down	; Object
+		    scrollbar-page-up
+		    scrollbar-to-bottom	; Window
+		    scrollbar-to-top
+		    scrollbar-vertical-drag ; Object
+		    )))
+	
+	(while cmds
+	  (eval
+	   (` (defadvice (, (intern (symbol-name (car cmds))))
+		(after 
+		 (, (intern (concat "follow-" (symbol-name (car cmds))))) 
+		 activate)
+		"Adviced by `follow-mode'."
+		(follow-xemacs-scrollbar-support (ad-get-arg 0)))))
+	  (setq cmds (cdr cmds))))
+
+
+      (defun follow-xemacs-scrollbar-support (window)
+	"Redraw windows showing the same buffer as shown in WINDOW.
+WINDOW is either the dragged window, or a cons containing the
+window as its first element. This is called while the user drags
+the scrollbar.
+
+WINDOW can be an object or a window."
+	(condition-case nil
+	    (progn
+	      (if (consp window)
+		  (setq window (car window)))
+	      (let ((fmode (assq 'follow-mode 
+				 (buffer-local-variables 
+				  (window-buffer window))))
+		    (orig-win (selected-window)))
+		(if (and fmode (cdr fmode))
+		    (progn
+		      ;; Recenter around the dragged window.
+		      (select-window window)
+		      (follow-redisplay)
+		      (select-window orig-win)))))
+	  (error nil)))))
+
+;;}}}
+;;{{{ Process output
+
+;;; The following sections installs a spy which listens to process
+;;; output and tries to reposition the windows whose buffers are in
+;;; Follow mode.  We play safe as much as possible...
+;;;
+;;; When follow-mode is activated all active processes are
+;;; intercepted.  All new processes which change their filter function
+;;; using `set-process-filter' are also intercepted.  The reason is
+;;; that a process can cause a redisplay recentering "tail" windows.
+;;; Note that it doesn't hurt to spy on more processes than needed.
+;;;
+;;; Technically, we set the process filter to `follow-generic-filter'.
+;;; The original filter is stored in `follow-process-filter-alist'.
+;;; Our generic filter calls the original filter, or inserts the
+;;; output into the buffer, if the buffer originally didn't have an
+;;; output filter.  It also makes sure that the windows connected to
+;;; the buffer are aligned.
+;;;
+;;; Discussion: How to we find processes which doesn't call
+;;; `set-process-filter'?  (How often are processes created in a
+;;; buffer after Follow mode are activated?)
+;;;
+;;; Discussion: Should we also advice `process-filter' to make our
+;;; filter invisible to others?
+
+;;{{{ Advice for `set-process-filter'
+
+;; Do not call this with 'follow-generic-filter as the name of the
+;; filter...
+
+(defadvice set-process-filter (before follow-set-process-filter activate)
+  "Follow Mode listens to calls to this function to make 
+sure process output will be displayed correctly in buffers
+in which the mode is activated.
+
+Follow Mode inserts it's own process filter to do it's 
+magic stuff before the real process filter is called."
+  (if follow-intercept-processes
+      (progn
+	(setq follow-process-filter-alist
+	      (delq (assq (ad-get-arg 0) follow-process-filter-alist)
+		    follow-process-filter-alist))
+	(follow-tidy-process-filter-alist)
+	(cond ((eq (ad-get-arg 1) t))
+	      ((eq (ad-get-arg 1) nil)
+	       (ad-set-arg 1 'follow-generic-filter))
+	      (t
+	       (setq follow-process-filter-alist 
+		     (cons (cons (ad-get-arg 0) (ad-get-arg 1)) 
+			   follow-process-filter-alist))
+	       (ad-set-arg 1 'follow-generic-filter))))))
+
+
+(defun follow-call-set-process-filter (proc filter)
+  "Call original `set-process-filter' without the Follow mode advice."
+  (ad-disable-advice 'set-process-filter 'before
+		     'follow-set-process-filter)
+  (ad-activate 'set-process-filter)
+  (prog1 
+      (set-process-filter proc filter)
+    (ad-enable-advice 'set-process-filter 'before
+		      'follow-set-process-filter)
+    (ad-activate 'set-process-filter)))
+
+
+(defadvice process-filter (after follow-process-filter activate)
+  "Normally when Follow mode is activated all processes has the
+process filter set to `follow-generic-filter'.  With this advice,
+the original process filter is returned."
+  (cond ((eq ad-return-value 'follow-generic-filter)
+	 (setq ad-return-value
+	       (cdr-safe (assq (ad-get-arg 0) 
+			       follow-process-filter-alist))))))
+
+
+(defun follow-call-process-filter (proc)
+  "Call original `process-filter' without the Follow mode advice."
+  (ad-disable-advice 'process-filter 'after
+		     'follow-process-filter)
+  (ad-activate 'process-filter)
+  (prog1 
+      (process-filter proc)
+    (ad-enable-advice 'process-filter 'after 
+		      'follow-process-filter)
+    (ad-activate 'process-filter)))
+  
+
+(defun follow-tidy-process-filter-alist ()
+  "Remove old processes from `follow-process-filter-alist'."
+  (let ((alist follow-process-filter-alist)
+	(ps (process-list))
+	(new ()))
+    (while alist
+      (if (and (not (memq (process-status (car (car alist))) 
+			  '(exit signal closed nil)))
+	       (memq (car (car alist)) ps))
+	  (setq new (cons (car alist) new)))
+      (setq alist (cdr alist)))
+    (setq follow-process-filter-alist new)))
+
+;;}}}
+;;{{{ Start/stop interception of processes.
+
+;; Normally, all new processed are intercepted by our `set-process-filter'.
+;; This is needed to intercept old processed which were started before we were
+;; loaded, and processes we have forgotten by calling
+;; `follow-stop-intercept-process-output'.
+
+(defun follow-intercept-process-output ()
+  "Intercept all active processes.
+
+This is needed so that Follow Mode can track all display events in the
+system.  (See `follow-mode')"
+  (interactive)
+  (let ((list (process-list)))
+    (while list
+      (if (eq (process-filter (car list)) 'follow-generic-filter)
+	  nil
+	;; The custom `set-process-filter' defined above.
+	(set-process-filter (car list) (process-filter (car list))))
+      (setq list (cdr list))))
+  (setq follow-intercept-processes t))
+
+
+(defun follow-stop-intercept-process-output ()
+  "Stop Follow Mode from spying on processes.
+
+All current spypoints are removed and no new will be added.
+
+The effect is that Follow mode won't be able to handle buffers 
+connected to processes.  
+
+The only reason to call this function is if the Follow mode spy filter
+would interfere with some other package.  If this happens, please
+report this using the `follow-submit-feedback' function."
+  (interactive)
+  (follow-tidy-process-filter-alist)
+  (let ((list (process-list)))
+    (while list
+      (if (eq (process-filter (car list)) 'follow-generic-filter)
+	  (progn
+	    (follow-call-set-process-filter 
+	     (car list) 
+	     (cdr-safe (assq (car list) follow-process-filter-alist)))
+	    (setq follow-process-filter-alist 
+		  (delq (assq (car list) follow-process-filter-alist)
+			follow-process-filter-alist))))
+      (setq list (cdr list))))
+  (setq follow-intercept-processes nil))
+
+;;}}}
+;;{{{ The filter
+
+;;; The following section is a naive method to make buffers with
+;;; process output to work with Follow mode. Whenever the start of the
+;;; window displaying the buffer is moved, we moves it back to it's
+;;; original position and try to select a new window.  (If we fail,
+;;; the normal redisplay functions of Emacs will scroll it right
+;;; back!)
+
+(defun follow-generic-filter (proc output)
+  "Process output filter for process connected to buffers in Follow mode."
+  (let* ((old-buffer (current-buffer))
+	 (orig-win (selected-window))
+	 (buf (process-buffer proc))
+	 (win (and buf (if (eq buf (window-buffer orig-win))
+			   orig-win
+			 (get-buffer-window buf t))))
+	 (return-to-orig-win (and win (not (eq win orig-win))))
+	 (orig-window-start (and win (window-start win))))
+
+    ;; If input is pending, the `sit-for' below won't redraw the
+    ;; display. In that case, calling `follow-avoid-tail-recenter' may
+    ;; provoke the process hadnling code to sceduling a redisplay.
+    ;(or (input-pending-p)
+    ; (follow-avoid-tail-recenter))
+
+    ;; Output the `output'.
+    (let ((filter (cdr-safe (assq proc follow-process-filter-alist))))
+      (cond 
+       ;; Call the original filter function
+       (filter
+	(funcall filter proc output))
+
+       ;; No filter, but we've got a buffer. Just output into it.
+       (buf
+	(set-buffer buf)
+	(if (not (marker-buffer (process-mark proc)))
+	    (set-marker (process-mark proc) (point-max)))
+	(let ((moving (= (point) (process-mark proc)))
+	      (odeactivate (and (boundp 'deactivate-mark)
+				(symbol-value 'deactivate-mark)))
+	      (old-buffer-read-only buffer-read-only))
+	  (setq buffer-read-only nil)
+	  (save-excursion
+	    (goto-char (process-mark proc))
+	    ;; `insert-before-markers' just in case the users next
+	    ;; command is M-y.
+	    (insert-before-markers output)
+	    (set-marker (process-mark proc) (point)))
+	  (if moving (goto-char (process-mark proc)))
+	  (if (boundp 'deactivate-mark)
+	      ;; This could really be
+	      ;;    (setq deactivate-mark odeactivate)
+	      ;; but this raises an error when compiling on XEmacs.
+	      (funcall (symbol-function 'set)
+		       'deactivate-mark odeactivate))
+	  (setq buffer-read-only old-buffer-read-only)))))
+
+    ;; If we're in follow mode, do our stuff.  Select a new window and
+    ;; redisplay.  (Actually, it is redundant to check `buf', but I
+    ;; feel it's more correct.)
+    (if (and buf win (window-live-p win))
+	(progn
+	  (set-buffer buf)
+	  (if (and (boundp 'follow-mode) follow-mode)
+	      (progn 
+		(select-window win)
+		(let* ((windows (follow-all-followers win))
+		       (win-start-end (follow-windows-start-end windows))
+		       (new-window-start (window-start win))
+		       (new-window-point (window-point win)))
+		  (cond
+		   ;; The window was moved. Move it back and
+		   ;; select a new.  If no better could be found,
+		   ;; we stick the the new start position.  This
+		   ;; is used when the original process filter
+		   ;; tries to position the cursor at the bottom
+		   ;; of the window.  Example: `lyskom'.
+		   ((not (eq orig-window-start new-window-start))
+		    (follow-debug-message "filter: Moved")
+		    (set-window-start win orig-window-start)
+		    (follow-redisplay windows win)
+		    (setq win-start-end (follow-windows-start-end windows))
+		    (follow-select-if-visible new-window-point 
+					      win-start-end)
+		    (goto-char new-window-point)
+		    (if (eq win (selected-window))
+			(set-window-start win new-window-start))
+		    (setq win-start-end (follow-windows-start-end windows)))
+		   ;; Stick to this window, if point is visible in it.
+		   ((pos-visible-in-window-p new-window-point)
+		    (follow-debug-message "filter: Visible in window"))
+		   ;; Avoid redisplaying the first window. If the
+		   ;; point is visible at a window below,
+		   ;; redisplay and select it.
+		   ((follow-select-if-visible-from-first 
+		     new-window-point windows)
+		    (follow-debug-message "filter: Seen from first")
+		    (follow-redisplay windows (car windows))
+		    (goto-char new-window-point)
+		    (setq win-start-end
+			  (follow-windows-start-end windows)))
+		   ;; None of the above. We stick to the current window.
+		   (t 
+		    (follow-debug-message "filter: nothing")))
+
+		  ;; Here we have slected a window. Make sure the
+		  ;; windows are aligned and the point is visible
+		  ;; in the selected window.
+		  (if (and (not (follow-pos-visible 
+				 (point) (selected-window) win-start-end))
+			   (not return-to-orig-win))
+		      (progn
+			(sit-for 0)
+			(setq win-start-end 
+			      (follow-windows-start-end windows))))
+
+		  (if (or follow-internal-force-redisplay
+			  (not (follow-windows-aligned-p win-start-end)))
+		      (follow-redisplay windows)))))))
+
+    ;; return to the original window.
+    (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)))
+
+;;}}}
+
+;;}}}
+;;{{{ Window size change
+
+;; In Emacs 19.29, the functions in `window-size-change-functions' are
+;; called every time a window in a frame changes size. Most notably, it
+;; is called after the frame has been resized.
+;;
+;; We basically call our post-command-hook for every buffer which is
+;; visible in any window in the resized frame, which is in follow-mode.
+;;
+;; Since this function can be called indirectly from
+;; `follow-post-command-hook' we have a potential infinite loop.  We
+;; handle this problem by simply not doing anything at all in this
+;; situation.  The variable `follow-inside-post-command-hook' contains
+;; information about whether the execution actually is inside the
+;; post-command-hook or not.
+
+(if (boundp 'window-size-change-functions)
+    (add-hook 'window-size-change-functions 'follow-window-size-change))
+
+
+(defun follow-window-size-change (frame)
+  "Redraw all windows in FRAME, when in Follow mode."
+  ;; Below, we call `post-command-hook'. This makes sure that we
+  ;; doesn't start a mutally recursive endless loop.
+  (if follow-inside-post-command-hook
+      nil
+    (let ((buffers '())
+	  (orig-window (selected-window))
+	  (orig-buffer (current-buffer))
+	  (orig-frame (selected-frame))
+	  windows
+	  buf)
+      (select-frame frame)
+      (unwind-protect
+	  (walk-windows 
+	   (function 
+	    (lambda (win)
+	      (setq buf (window-buffer win))
+	      (if (memq buf buffers)
+		  nil
+		(set-buffer buf)
+		(if (and (boundp 'follow-mode)
+			 follow-mode)
+		    (progn
+		      (setq windows (follow-all-followers win))
+		      (if (memq orig-window windows)
+			  (progn
+			    ;; Make sure we're redrawing around the
+			    ;; selected window.
+			    ;;
+			    ;; We must be really careful not to do this
+			    ;; when we are (indirectly) called by
+			    ;; `post-command-hook'.
+			    (select-window orig-window)
+			    (follow-post-command-hook)
+			    (setq orig-window (selected-window)))
+			(follow-redisplay windows win))
+		      (setq buffers (cons buf buffers))))))))
+	(select-frame orig-frame)
+	(set-buffer orig-buffer)
+	(select-window orig-window)))))
+
+;;}}}
+
+;;{{{ XEmacs isearch
+
+;; In XEmacs, isearch often finds matches in other windows than the
+;; currently selected.  However, when exiting the old window
+;; configuration is restored, with the exception of the beginning of
+;; the start of the window for the selected window.  This is not much
+;; help for us.
+;;
+;; We overwrite the stored window configuration with the current,
+;; unless we are in `slow-search-mode', i.e. only a few lines
+;; of text is visible.
+
+(if follow-emacs-version-xemacs-p
+    (defadvice isearch-done (before follow-isearch-done activate)
+      (if (and (boundp 'follow-mode)
+	       follow-mode
+	       (boundp 'isearch-window-configuration)
+	       isearch-window-configuration
+	       (boundp 'isearch-slow-terminal-mode)
+	       (not isearch-slow-terminal-mode))
+	  (let ((buf (current-buffer)))
+	    (setq isearch-window-configuration 
+		  (current-window-configuration))
+	    (set-buffer buf)))))
+
+;;}}}
+;;{{{ Tail window handling
+
+;;; In Emacs (not XEmacs) windows showing nothing are sometimes 
+;;; recentered.  When in Follow Mode, this is not desireable for
+;;; non-first windows in the window chain.  This section tries to
+;;; make the windows stay where they should be.
+;;;
+;;; If the display is updated, all windows starting at (point-max) are
+;;; going to be recentered at the next redisplay, unless we do a
+;;; read-and-write cycle to update the `force' flag inside the windows.
+;;;
+;;; In 19.30, a new varible `window-scroll-functions' is called every
+;;; time a window is recentered.  It is not perfect for our situation,
+;;; since when it is called for a tail window, it is to late.  However,
+;;; if it is called for another window, we can try to update our
+;;; windows.
+;;;
+;;; By patching `sit-for' we can make sure that to catch all explicit
+;;; updates initiated by lisp programs.  Internal calls, on the other
+;;; hand, are not handled.
+;;;
+;;; Please note that the function `follow-avoid-tail-recenter' is also
+;;; called from other places, e.g. `post-command-hook' and
+;;; `post-command-idle-hook'.
+
+;; If this function is called it is to late for this window, but
+;; we might save other windows from beeing recentered.
+
+(if (and follow-avoid-tail-recenter-p (boundp 'window-scroll-functions))
+    (add-hook 'window-scroll-functions 'follow-avoid-tail-recenter t))
+
+
+;;  This prevents all packages which calls `sit-for' directly
+;;  to recenter tail windows.
+
+(if follow-avoid-tail-recenter-p
+    (defadvice sit-for (before follow-sit-for activate)
+      "Adviced by Follow Mode.
+
+Avoid to recenter windows displaying only the end of a file as when
+displaying a short file in two windows, using Follow Mode."
+      (follow-avoid-tail-recenter)))
+
+
+;;  Without this advice, `mouse-drag-region' would start to recenter
+;;  tail windows.
+
+(if (and follow-avoid-tail-recenter-p
+	 (fboundp 'move-overlay))
+    (defadvice move-overlay (before follow-move-overlay activate)
+      "Adviced by Follow Mode.  Don't recenter windows showing only 
+the end of a buffer.  This prevents `mouse-drag-region' from
+messing things up."
+      (follow-avoid-tail-recenter)))
+
+;;}}}
+;;{{{ profile support
+
+;; The following (non-evaluated) section can be used to
+;; profile this package using `elp'.
+;;
+;; Invalid indentation on purpose!
+
+(cond (nil
+(setq elp-function-list
+      '(window-end 
+	vertical-motion 
+	; sit-for  ;; elp can't handle advices...
+	follow-mode
+	follow-all-followers
+	follow-split-followers 
+	follow-redisplay
+	follow-downward
+	follow-calculate-first-window-start
+	follow-estimate-first-window-start
+	follow-calculate-first-window-start-from-above
+	follow-calculate-first-window-start-from-below
+	follow-calc-win-end
+	follow-calc-win-start
+	follow-pos-visible
+	follow-windows-start-end
+	follow-select-if-visible
+	follow-select-if-visible-from-first
+	follow-windows-aligned-p
+	follow-point-visible-all-windows-p
+	follow-avoid-tail-recenter
+	follow-update-window-start
+	follow-post-command-hook
+	))))
+
+;;}}}
+
+;;{{{ The end
+
+;;;
+;;; We're done!
+;;;
+
+(provide 'follow)
+
+;;}}}
+
+;; /------------------------------------------------------------------------\
+;; | "I [..] am rarely happier then when spending an entire day programming |
+;; | my computer to perform automatically a task that it would otherwise    |
+;; | take me a good ten seconds to do by hand. Ten seconds, I tell myself,  |
+;; | is ten seconds. Time is valuable and ten seconds' worth of it is well  |
+;; | worth the investment of a day's happy activity working out a way to    |
+;; | save it".             -- Douglas Adams, "Last Chance to See"           |
+;; \------------------------------------------------------------------------/
+
+;;; follow.el ends here