Gnus -- Minor tweak define #'time-to-seconds
[packages] / xemacs-packages / edit-utils / scroll-in-place.el
1
2 ;;;; -*-Emacs-Lisp-*- Improved Vertical Scrolling Commands
3 ;;;; Written by Eric Eide, last modified on 1994/11/18 21:23:01.
4 ;;;; (C) Copyright 1993, 1994, Eric Eide and the University of Utah
5 ;;;;
6 ;;;; COPYRIGHT NOTICE
7 ;;;;
8 ;;;; This program is free software; you can redistribute it and/or modify it
9 ;;;; under the terms of the GNU General Public License as published by the Free
10 ;;;; Software Foundation; either version 2 of the License, or (at your option)
11 ;;;; any later version.
12 ;;;;
13 ;;;; This program is distributed in the hope that it will be useful, but
14 ;;;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 ;;;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 ;;;; for more details.
17 ;;;;
18 ;;;; You should have received a copy of the GNU General Public License along
19 ;;;; with GNU Emacs.  If you did not, write to the Free Software Foundation,
20 ;;;; Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21
22 ;;; Synched up with: Not in FSF.
23
24 ;;;; AUTHORS
25 ;;;;
26 ;;;; This package was written by Eric Eide (eeide@cs.utah.edu) and was based on
27 ;;;; a very similar package ("scroll-fix") by Joe Wells.  Almost all of the
28 ;;;; code in this file is original, but I owe a great debt to Mr. Wells for his
29 ;;;; ideas and his original implementation.
30 ;;;;
31 ;;;;   Eric Eide (eeide@cs.utah.edu)
32 ;;;;   University of Utah
33 ;;;;   3190 Merrill Engineering Building
34 ;;;;   Salt Lake City, Utah  84112
35 ;;;;
36 ;;;;   Joe Wells (jbw@cs.bu.edu)
37 ;;;;
38 ;;;; Joe Wells' "scroll-fix" package is Copyright (C) 1988, 1989, and 1991 by
39 ;;;; the Free Software Foundation.  It is distributed under the terms of the
40 ;;;; GNU General Public License.
41
42 ;;;; LISP CODE DIRECTORY INFORMATION
43 ;;;;
44 ;;;; LCD Archive Entry:
45 ;;;; scroll-in-place|Eric Eide|eeide@cs.utah.edu|
46 ;;;; Improved vertical scrolling commands|
47 ;;;; 1994/11/18 21:23:01|1.3|~/misc/scroll-in-place.el.Z|
48
49 ;;;; SUMMARY
50 ;;;;
51 ;;;; This package provides improved vertical scrolling commands for GNU Emacs.
52 ;;;; These new commands offer the following features:
53 ;;;;
54 ;;;; + When a scrolling command is executed, GNU Emacs tries to keep point as
55 ;;;;   close as possible to its original window position (window line and
56 ;;;;   column).  This is what "scroll in place" means: point stays "in place"
57 ;;;;   within the window.  (There are times when point must be moved from its
58 ;;;;   original window position in order to execute the scroll; see below.)
59 ;;;;
60 ;;;;   The variable `scroll-in-place', which is true by default, determines
61 ;;;;   whether or not the standard GNU Emacs scrolling commands (`scroll-down',
62 ;;;;   `scroll-up', `scroll-other-window-down', and `scroll-other-window') use
63 ;;;;   the "in place" features listed here.  When `scroll-in-place' is `nil'
64 ;;;;   the standard GNU Emacs scrolling commands essentially just call the
65 ;;;;   original versions of themselves.  (Note that even when `scroll-in-place'
66 ;;;;   is `nil' the new versions of `scroll-down' and `scroll-up' have slightly
67 ;;;;   different behavior when a minibuffer window is the selected window.  See
68 ;;;;   below.)
69 ;;;;
70 ;;;;   It is possible to turn off (or turn on) "in place" scrolling for certain
71 ;;;;   buffers by making buffer-local bindings of the variable `scroll-in-
72 ;;;;   place' for those buffers.  The variable `scroll-in-place' is not usually
73 ;;;;   buffer-local, but you can make it so if you desire.
74 ;;;;
75 ;;;; + Because the improved scrolling commands keep point at its original
76 ;;;;   window position, these scrolling commands are "reversible."  The
77 ;;;;   `scroll-up' command undoes the effect of the immediately previous
78 ;;;;   `scroll-down' command (if any) and vice versa.  In other words, if you
79 ;;;;   scroll up and then immediately scroll back down, the window config-
80 ;;;;   uration is restored to its exact original state.  This allows you to
81 ;;;;   browse through a buffer more easily, as you can always get back to the
82 ;;;;   original configuration.
83 ;;;;
84 ;;;;   Note, however, that the improved scrolling commands are guaranteed to be
85 ;;;;   reversible only if there are no intervening non-scrolling commands.
86 ;;;;   Also, if you give a prefix argument to a scrolling command (in order to
87 ;;;;   specify the number of lines to scroll by), previous scrolling commands
88 ;;;;   may no longer be reversible.  More specifically, if the new prefix
89 ;;;;   argument has a different magnitude than the previous scrolling distance,
90 ;;;;   then any previous scrolling commands are not reversible.  The new prefix
91 ;;;;   argument takes precedence.
92 ;;;;
93 ;;;;   You might find it useful to think of the scrolling commands as forming
94 ;;;;   "chains."  A scrolling command either starts or continues a chain.  By
95 ;;;;   issuing a non-scrolling command or by changing the number of lines to be
96 ;;;;   scrolled, you break the chain.  (Note that simply changing the scrolling
97 ;;;;   direction won't break the chain; changing the absolute number of lines
98 ;;;;   to be scrolled is what breaks the chain.)  Scrolling commands are
99 ;;;;   guaranteed to be reversible only within the current chain.  Hopefully
100 ;;;;   that's clear enough.
101 ;;;;
102 ;;;; + When a scrolling command is given a prefix argument (which specifies the
103 ;;;;   number of lines to scroll by), then that argument becomes the default
104 ;;;;   scrolling distance for all immediately subsequent scrolling commands.
105 ;;;;   This means that you can easily set the scrolling distance for a chain
106 ;;;;   of scrolling commands.  Note that a new prefix argument or any non-
107 ;;;;   scrolling command breaks the chain (as described above), and any further
108 ;;;;   scrolling commands will use the usual defaults (or the prefix argument
109 ;;;;   you specify at that time, of course).
110 ;;;;
111 ;;;;   However, there are cases in which one doesn't want the current scrolling
112 ;;;;   command to use the default scrolling distance that was set by the
113 ;;;;   previous scrolling command.  For example, suppose that you had special
114 ;;;;   commands that scrolled one line up and one line down.  When you invoke
115 ;;;;   one of these commands, the "in place" scrolling routines set the default
116 ;;;;   scrolling distance to be just one line.  Now suppose that you use one of
117 ;;;;   your special commands and then immediately invoke `scroll-up' (`C-v'),
118 ;;;;   expecting it to scroll by a near windowful of text.  You would be
119 ;;;;   disappointed --- because the previous command set the default scrolling
120 ;;;;   distance to be just one line, `scroll-up' just scrolls by one line.
121 ;;;;
122 ;;;;   To solve this problem, "scroll-in-place" allows you to divide scrolling
123 ;;;;   commands into separate "groups."  Commands in a group can only form
124 ;;;;   chains with (and therefore, inherit defaults from) commands in the same
125 ;;;;   group.  (Note that no command can be in more than one group.)  If you
126 ;;;;   invoke a scrolling command that is not in the same group as that of the
127 ;;;;   immediately previous scrolling command, then the previous chain is
128 ;;;;   broken and you start a new chain --- with a new set of defaults.
129 ;;;;
130 ;;;;   So to solve the problem described above, you could put your one-line
131 ;;;;   scrolling commands in their own group.  Once that is done, the standard
132 ;;;;   scrolling commands will not form chains with your one-line scrolling
133 ;;;;   commands, and therefore will not use the default scrolling distance set
134 ;;;;   by those commands.  Problem solved!
135 ;;;;
136 ;;;;   By default, all "in place" scrolling commands are in a single group.  If
137 ;;;;   you want to partition some commands into separate groups, you must do
138 ;;;;   that yourself *before* any "in place" commands are invoked.  For more
139 ;;;;   information about grouping commands, see the documentation for the
140 ;;;;   variables `scroll-command-groups' and `scroll-default-command-group'.
141 ;;;;
142 ;;;; + The improved scrolling commands will avoid displaying empty lines past
143 ;;;;   the end of the buffer when possible.  In other words, just as you can't
144 ;;;;   see "dead space" before the beginning of the buffer text, the new
145 ;;;;   scrolling commands try to avoid displaying "dead space" past the end of
146 ;;;;   the buffer text.  This behavior is somewhat configurable; see the
147 ;;;;   documentation for the variable `scroll-allow-blank-lines-past-eob'.
148 ;;;;
149 ;;;;   Dead space will be displayed if it is necessary in order to make a
150 ;;;;   previous scrolling action reversible, however.
151 ;;;;
152 ;;;; + If the scrolling commands cannot keep point at its initial window
153 ;;;;   position (because a buffer boundary is on screen and the window can't be
154 ;;;;   scrolled as far as necessary to keep point at the right place), point is
155 ;;;;   allowed to temporarily stray from its initial window position.  That is,
156 ;;;;   point moves the correct number of window lines, even if it means that it
157 ;;;;   has to stray from its desired window position.  This straying is undone
158 ;;;;   when (and if) the scrolling action is reversed.
159 ;;;;
160 ;;;; + If a scrolling command tries to move point past a buffer boundary, point
161 ;;;;   is instead moved to the boundary (the beginning or the end of the buffer
162 ;;;;   as appropriate) and an appropriate message is displayed.  This motion is
163 ;;;;   reversible, of course.
164 ;;;;
165 ;;;;   However, if point was already at the buffer boundary when the scrolling
166 ;;;;   command was invoked, the command signals an appropriate error instead.
167 ;;;;
168 ;;;; + When a minibuffer window is the selected window, the new versions of
169 ;;;;   `scroll-up' and `scroll-down' either scroll the window in the variable
170 ;;;;   `minibuffer-scroll-window' (which is usually the window of completions)
171 ;;;;   or the `next-window' if there is no `minibuffer-scroll-window'.  This is
172 ;;;;   usually much more useful than scrolling the minibuffer itself.  (Note
173 ;;;;   that this feature is available even when the variable `scroll-in-place'
174 ;;;;   is `nil'.)
175 ;;;;
176 ;;;; + When a scrolling command is scrolling a window other than the selected
177 ;;;;   window, it will signal an appropriate buffer boundary error if the
178 ;;;;   window cannot be scrolled (because the appropriate buffer boundary is
179 ;;;;   already visible).  This means that an error is signalled even in cases
180 ;;;;   that would be allowed (by "straying" point or by moving it to the buffer
181 ;;;;   boundary) if the window were selected.
182 ;;;;
183 ;;;;   (If an error were not signalled in these cases, then there would be many
184 ;;;;   cases in which the last scroll in a particular direction would appear to
185 ;;;;   do nothing because only the point position would change --- the
186 ;;;;   displayed text would stay the same!  To avoid these cases the scrolling
187 ;;;;   commands signal boundary errors "prematurely" when the window to be
188 ;;;;   scrolled is not selected.)
189 ;;;;
190 ;;;; So how is this package different than Joe Wells' "scroll-fix" package?
191 ;;;;
192 ;;;; + This package provides "in place" behavior for the standard GNU Emacs
193 ;;;;   commands by default; "scroll-fix" does not.
194 ;;;;
195 ;;;; + "scroll-fix" behaves differently when the window is near a buffer
196 ;;;;   boundary.  Instead of allowing point to stray, "scroll-fix" first does
197 ;;;;   an incomplete scroll (i.e., moves point less than the full distance in
198 ;;;;   order to keep point at the desired window position) and then pops point
199 ;;;;   to the buffer boundary.  I think that the behavior of this package is
200 ;;;;   somewhat move intuitive, especially for small scrolling distances.
201 ;;;;
202 ;;;; + The scrolling commands in this package will appropriately signal buffer
203 ;;;;   boundary errors; the commands in "scroll-fix" never signal boundary
204 ;;;;   errors.  This makes it difficult to allow "scroll-fix" to replace the
205 ;;;;   standard `scroll-down' and `scroll-up' commands because some other
206 ;;;;   packages (e.g., VM and GNUS) expect the scrolling commands to signal
207 ;;;;   these errors as necessary.
208 ;;;;
209 ;;;; + This package handles long lines correctly.  (But see PROBLEMS, below.)
210 ;;;;
211 ;;;; + "scroll-fix" handles prefix arguments differently.  In "scroll-fix", a
212 ;;;;   number-containing prefix argument always breaks any running chain of
213 ;;;;   scrolling commands.  The prefix argument `-' (the symbol minus,
214 ;;;;   generated by `C-u -') causes a temporary change in direction --- a
215 ;;;;   change for only the current command.  In this package, however, a
216 ;;;;   number-containing prefix argument only breaks a running chain if it has
217 ;;;;   a different magnitude than the default scrolling distance, and the
218 ;;;;   prefix argument `-' causes a permanent change in the sign of the default
219 ;;;;   scrolling distance --- a change visible to immediately subsequent
220 ;;;;   scrolling commands.
221 ;;;;
222 ;;;; + This package keeps track of the set of "in place" scrolling commands
223 ;;;;   dynamically, in order to detect "chains" of scrolling commands.
224 ;;;;   "scroll-fix" has a fixed list of scrolling commands, so "scroll-fix"
225 ;;;;   cannot keep track of some chains.  (Again, "scroll-fix" interacts badly
226 ;;;;   with VM and GNUS.)  And because "scroll-fix" keeps a static list of
227 ;;;;   scrolling commands, it is a bad idea to call its "in place" commands
228 ;;;;   from a program.  This package, because it maintains the information
229 ;;;;   dynamically, has no such problems.
230 ;;;;
231 ;;;; + This package allows one to divide the "in place" scrolling commands into
232 ;;;;   groups; a command in a group only forms chains with the members of its
233 ;;;;   group.  "scroll-fix" has no notion of command groups.
234 ;;;;
235 ;;;; + This package provides "in place" versions of the standard GNU Emacs
236 ;;;;   commands `scroll-other-window-down' and `scroll-other-window'.
237 ;;;;
238 ;;;; + This package will refuse to scroll non-selected windows (by signalling
239 ;;;;   an error) when the displayed text would not change, as described in the
240 ;;;;   feature list above.
241 ;;;;
242 ;;;; + When a minibuffer window is selected, this package always scrolls a
243 ;;;;   window other than the minibuffer.  "scroll-fix" will scroll another
244 ;;;;   window only if the entire minibuffer contents are visible.
245 ;;;;
246 ;;;; + "scroll-fix" provides a command to toggle the "in place" behavior of the
247 ;;;;   standard GNU Emacs commands.  This package doesn't; you'll have to set
248 ;;;;   the option manually with the command `set-variable'.
249 ;;;;
250 ;;;; + This package has gratuitous variable renaming (insert smile here!):
251 ;;;;
252 ;;;;   "scroll-fix" user variable            Equivalent in this package
253 ;;;;   -----------------------------------------------------------------------
254 ;;;;   scroll-in-place                       (none)
255 ;;;;   scroll-in-place-replace-original      scroll-in-place
256 ;;;;   scroll-in-place-eob-blank-allowed     scroll-allow-blank-lines-past-eob
257 ;;;;
258 ;;;; + This package allows programmers to specify the default scrolling
259 ;;;;   distance (i.e., the default distance used when starting a new chain of
260 ;;;;   scrolling commands) for custom scrolling commands.
261
262 ;;;; COMMANDS AND FUNCTIONS
263 ;;;;
264 ;;;; This package provides the following "in place" versions of GNU Emacs'
265 ;;;; standard vertical scrolling commands:
266 ;;;;
267 ;;;;   scroll-down-in-place
268 ;;;;   scroll-up-in-place
269 ;;;;   scroll-other-window-down-in-place
270 ;;;;   scroll-other-window-in-place
271 ;;;;
272 ;;;; The variable `scroll-in-place', which is true by default, determines
273 ;;;; whether or not the new versions of the standard GNU Emacs scrolling
274 ;;;; commands (`scroll-down', `scroll-up', `scroll-other-window-down', and
275 ;;;; `scroll-other-window') use the "in place" features listed above.  When
276 ;;;; `scroll-in-place' is `nil' the standard GNU Emacs scrolling commands
277 ;;;; essentially just call the original versions of themselves.  (Note that
278 ;;;; even when `scroll-in-place' is `nil' the new versions of `scroll-down' and
279 ;;;; `scroll-up' have slightly different behavior when a minibuffer window is
280 ;;;; the selected window.  See the feature list above.)
281 ;;;;
282 ;;;; NOTE that this package redefines the standard GNU Emacs commands `scroll-
283 ;;;; down', `scroll-up', `scroll-other-window-down', and `scroll-other-window'
284 ;;;; (in order to check the variable `scroll-in-place', as described above).
285 ;;;; The command `scroll-other-window-down' first appeared as a standard
286 ;;;; command in the FSF's GNU Emacs 19.26.
287 ;;;;
288 ;;;; This package also provides the following functions and variables which are
289 ;;;; of use to programmers:
290 ;;;;
291 ;;;;   scroll-window
292 ;;;;   scroll-window-in-place
293 ;;;;   scroll-window-in-place-continue-sequence
294 ;;;;   scroll-default-lines (variable)
295 ;;;;   scroll-command-groups (variable)
296 ;;;;
297 ;;;; The `scroll-window-in-place' function is the heart of the "in place"
298 ;;;; scrolling commands.  `scroll-window' is a function that checks the
299 ;;;; variable `scroll-in-place' and calls the appropriate scrolling function
300 ;;;; (either `scroll-window-in-place' or one of the original versions of
301 ;;;; `scroll-down' and `scroll-up').  The function `scroll-window-in-place-
302 ;;;; continue-sequence' is provided in order to preserve running "chains" of
303 ;;;; scrolling commands as described above.
304 ;;;;
305 ;;;; The variable `scroll-default-lines' determines the default scrolling
306 ;;;; distance when a new chain of "in place" scrolling commands begins.  If
307 ;;;; this variable is not a number, then the default distance is the height of
308 ;;;; the window to be scrolled minus `next-screen-context-lines'.  The variable
309 ;;;; `scroll-command-groups' contains the explicit groups of "in place"
310 ;;;; scrolling commands; for more information read the variable documentation.
311
312 ;;;; YOUR .EMACS FILE
313 ;;;;
314 ;;;; To use this package, you simply need to load it from within your ".emacs"
315 ;;;; file:
316 ;;;;
317 ;;;;   (require 'scroll-in-place)
318 ;;;;
319 ;;;; By default, this package provides for the standard GNU Emacs vertical
320 ;;;; scrolling commands (`scroll-down', `scroll-up', `scroll-other-window-
321 ;;;; down', and `scroll-other-window') to use the "in place" features.  If you
322 ;;;; would rather not have this, set the variable `scroll-in-place' to `nil':
323 ;;;;
324 ;;;;   (setq scroll-in-place nil)
325 ;;;;
326 ;;;; When `scroll-in-place' is `nil' you will have to bind keys in order to
327 ;;;; call the "in place" scrolling commands.  For example, you might want to do
328 ;;;; the following:
329 ;;;;
330 ;;;;   (global-set-key "\M-v" 'scroll-down-in-place)
331 ;;;;   (global-set-key "\C-v" 'scroll-up-in-place)
332 ;;;;
333 ;;;; Sun users should also read the PROBLEMS section, below.
334 ;;;;
335 ;;;; ADVANCED CUSTOMIZATION
336 ;;;;
337 ;;;; If you want to partition certain "in place" scrolling commands into
338 ;;;; separate groups, you should do something like the following:
339 ;;;;
340 ;;;;   ;; Make one group containing the commands `scroll-down-one-line' and
341 ;;;;   ;; `scroll-up-one-line'.  (These are not standard GNU Emacs commands.)
342 ;;;;   (setq scroll-command-groups
343 ;;;;         (list '(scroll-down-one-line scroll-up-one-line)))
344 ;;;;
345 ;;;; You could write the `scroll-down-one-line' command like this:
346 ;;;;
347 ;;;;   (defun scroll-down-one-line (arg)
348 ;;;;     "Scroll down one line, or number of lines specified by prefix arg."
349 ;;;;     (interactive "P")
350 ;;;;     (let ((scroll-default-lines 1))
351 ;;;;       (scroll-down-in-place arg)))
352 ;;;;
353 ;;;; If you want to disable "in place" scrolling for windows that display a
354 ;;;; particular buffer (while leaving it available in other windows), you can
355 ;;;; make `scroll-in-place' a buffer-local variable for that buffer and then
356 ;;;; bind that local copy of `scroll-in-place' to `nil'.  This is the kind of
357 ;;;; thing that one generally does in a major mode hook.  For example, you can
358 ;;;; disable "in place" scrolling of GNUS article windows with the following
359 ;;;; code:
360 ;;;;
361 ;;;;   (setq gnus-article-mode-hook
362 ;;;;         (function (lambda ()
363 ;;;;                     (make-local-variable 'scroll-in-place)
364 ;;;;                     (setq scroll-in-place nil))))
365 ;;;;   ;; Set the variable `gnus-Article-mode-hook' instead if you are using
366 ;;;;   ;; an old version of GNUS, say version 3.13 or 3.14.
367 ;;;;
368 ;;;; The variable `scroll-allow-blank-lines-past-eob' can also be made local to
369 ;;;; particular buffers, if you desire.  (But why would you want to do that?)
370
371 ;;;; PROBLEMS
372 ;;;;
373 ;;;; + It is sometimes difficult for one's eyes to follow an incomplete scroll
374 ;;;;   (i.e., a scroll in which the text doesn't move as far as one expected),
375 ;;;;   especially when the scrolled window is not selected (and therefore that
376 ;;;;   window's point is not highlighted).  One can lose one's place in the
377 ;;;;   text.
378 ;;;;
379 ;;;; + The names `scroll-down-in-place' and `scroll-up-in-place' conflict with
380 ;;;;   two commands in the GNU Emacs terminal-specific file "term/sun.el".
381 ;;;;   This means that in order to load this package correctly, Sunterm users
382 ;;;;   will have to use the hook `term-setup-hook'.  For example, you might put
383 ;;;;   the following form in your ".emacs" file:
384 ;;;;
385 ;;;;   (setq term-setup-hook (function (lambda () (require 'scroll-in-place))))
386 ;;;;
387 ;;;;   If this is confusing, get help from your local GNU Emacs guru.
388 ;;;;
389 ;;;; + `scroll-determine-goal-column' tries to honor the variable `track-eol'
390 ;;;;   if it is set.  But when lines are being wrapped we can't move point past
391 ;;;;   the wrap --- or else it is possible that scrolling won't work correctly.
392 ;;;;   In short, this package honors `track-eol' as best it can.
393 ;;;;
394 ;;;; + `scroll-window-in-place' can become confused when something changes the
395 ;;;;   window "out from under it."  By "confused" I mean that it is possible
396 ;;;;   for `scroll-window-in-place' to think that it should continue the
397 ;;;;   running sequence of "in place" scrolls when it should really probably
398 ;;;;   start a new sequence.  For example, if a process filter inserts text
399 ;;;;   into the buffer and moves point, `scroll-window-in-place' loses track of
400 ;;;;   where point should be and where the window should start.  Commands that
401 ;;;;   call a "scroll in place" function and then subsequently move point can
402 ;;;;   also confuse `scroll-window-in-place'.
403 ;;;;
404 ;;;;   To correct some of this confusion, `scroll-window-in-place' could keep
405 ;;;;   track of the final positions of `window-start' and `window-point',
406 ;;;;   possibly with both markers and character positions.  In my experience
407 ;;;;   the "in place" scrolling commands are almost never confused (except by
408 ;;;;   fancy packages that do their own fancy kinds of scrolling, as described
409 ;;;;   below), so the extra sanity checking isn't worth the effort.  If your
410 ;;;;   mileage varies let me know.
411 ;;;;
412 ;;;; + The "in place" scrolling commands can interact poorly with packages that
413 ;;;;   provide their own special scrolling commands.  For example, there are
414 ;;;;   varying degrees of conflict with Rmail, VM, and GNUS.
415 ;;;;
416 ;;;;   RMAIL
417 ;;;;
418 ;;;;   In the version of Rmail that is part of the FSF's GNU Emacs 19 (19.25
419 ;;;;   through 19.28 at least), the command `rmail-summary-scroll-msg-down' in
420 ;;;;   the file "rmailsum.el" fails to work properly when "in place" scrolling
421 ;;;;   is enabled for the Rmail message buffer.  (The source of the conflict:
422 ;;;;   the "in place" scrolling commands and Emacs' standard scrolling commands
423 ;;;;   interpret the argument '- in different ways.)  Fortunately it is easy to
424 ;;;;   patch Rmail.  Send me mail if you would like to receive a copy of these
425 ;;;;   patches.
426 ;;;;
427 ;;;;   I know of no conflicts between the "in place" scrolling commands and
428 ;;;;   older versions of Rmail (i.e., versions that came with GNU Emacs 18).
429 ;;;;
430 ;;;;   VM
431 ;;;;
432 ;;;;   `scroll-window-in-place' is *very* confused by VM 5's message scrolling
433 ;;;;   commands, especially because VM 5 rebuilds Emacs' window configuration
434 ;;;;   from scratch so often.  I have written an experimental set of patches
435 ;;;;   for VM 5.70 that allows VM 5 to use the "scroll-in-place" features; send
436 ;;;;   me mail if you would like to receive a copy of these patches.  I hope
437 ;;;;   that someday my patches will be incorporated into VM.
438 ;;;;
439 ;;;;   `scroll-window-in-place' is not confused by VM 4.41's message scrolling
440 ;;;;   commands, however.
441 ;;;;
442 ;;;;   GNUS
443 ;;;;
444 ;;;;   `scroll-window-in-place' can be *slightly* confused by GNUS' article
445 ;;;;   scrolling commands because they move point to the last line of the
446 ;;;;   article window and then scroll the text.  (This is the case for at least
447 ;;;;   GNUS versions 3.13 through 4.1, inclusive.)  The potential conflict is
448 ;;;;   so minor, however, that you'll probably never notice it.  I never do.
449 ;;;;
450 ;;;;   A severe conflict, however, exists between the "in place" scrolling
451 ;;;;   commands and the add-on "gnus-hide" package.  "gnus-hide" can elide
452 ;;;;   signatures at the ends of articles but it does so in a way that causes
453 ;;;;   `scroll-window-in-place', as invoked by the GNUS scrolling commands, not
454 ;;;;   to signal end-of-buffer conditions at the right times.  Someday I may
455 ;;;;   write new article scrolling commands for GNUS.
456 ;;;;
457 ;;;; + Process filters that call scrolling functions can cause confusion.  They
458 ;;;;   may break running chains of "in place" scrolling commands and they may
459 ;;;;   set up inappropriate defaults for future scrolling commands.  Maybe this
460 ;;;;   is a moot problem, as I am currently unaware of any process filters that
461 ;;;;   invoke scrolling commands (although many filters move point around,
462 ;;;;   which will also confuse `scroll-window-in-place').
463
464 ;; sb -- Added turn-on and turn-off hook functions to prepare for making this
465 ;;  a standardly dumped package with XEmacs.
466
467 ;; (provide 'scroll-in-place) at the end of this file.
468
469 \f
470 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
471 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
472 ;;;;
473 ;;;; Here are the variable declarations, both user options and internal
474 ;;;; variables.
475 ;;;;
476 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
477 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
478
479 (defvar scroll-in-place t
480   "*When this variable is true (i.e., non-`nil'), the standard GNU Emacs
481 vertical scrolling commands `scroll-down', `scroll-up', `scroll-other-window-
482 down', and `scroll-other-window' will attempt to keep point at its current
483 position in the window (window line and column).  In other words, point stays
484 \"in place\" within the window.
485
486 When this variable is `nil' the standard GNU Emacs vertical scrolling commands
487 behave as usual.  The \"in place\" equivalents, however, are still available as
488 separate commands.
489
490 This variable may be made buffer-local in order to disable (or enable) \"in
491 place\" scrolling in particular buffers."
492   ;; I have thought about dividing `scroll-in-place' into three variables: a
493   ;; list of commands that always scroll in place, a list of commands that
494   ;; never scroll in place, and a flag that determines the default behavior of
495   ;; other scrolling commands.  This could make it easier to make "in place"
496   ;; scrolling the default because one could single out certain ill-behaved
497   ;; commands.  But as of now I'm sure that the added complexity would really
498   ;; be worth it.
499   )
500
501 (defvar scroll-allow-blank-lines-past-eob nil
502   "*When this variable is `nil' the \"in place\" scrolling commands will avoid
503 displaying empty lines past the end of the buffer text.  In other words, just
504 as you can't see \"dead space\" before the beginning of the buffer text, the
505 \"in place\" scrolling commands try to avoid displaying \"dead space\" past the
506 end of the buffer text.  This helps make the most of window real estate.
507
508 Note that sometimes it is necessary to display \"dead space\" in order to make
509 a previous scrolling action reversible.
510
511 When this variable is non-`nil' the \"in place\" scrolling commands will always
512 allow blank lines to be shown past the end of the buffer.")
513
514 ;;;;
515 ;;;; The following variables are not user options, but are intended to be set
516 ;;;; by code outside this package.
517 ;;;;
518
519 (defvar scroll-default-lines nil
520   "The default number of lines to be scrolled by when a new sequence of \"in
521 place\" scrolling commands begins.  Of course, when an explicit number of lines
522 is specified, that explicit number takes precedence.  See the documentation for
523 the function `scroll-window-in-place' for more information.
524
525 If this variable is not bound to a number, then the default number of lines is
526 the height of the window to be scrolled minus `next-screen-context-lines'.
527
528 This variable should not be set globally!  Commands that want to specify a
529 default scrolling distance should just bind the variable `scroll-default-lines'
530 temporarily.")
531
532 (defvar scroll-command-groups '((scroll-up-one scroll-down-one)
533                                 (scrollbar-line-up scrollbar-line-down)
534                                 (scrollbar-page-up scrollbar-page-down))
535   "The explicitly specified \"groups\" of \"in place\" scrolling commands.
536 This variable should be set before or immediately after the \"in place\"
537 scrolling package is loaded, and then not changed after that.
538
539 Usually, \"in place\" scrolling commands share state (e.g., the number of lines
540 to scroll by) with any and all immediately previous \"in place\" scrolling
541 commands.  Sometimes, however, this is undesirable.  In these cases the \"in
542 place\" scrolling commands can be divided into groups.  A command in a group
543 only shares state with members of its group.
544
545 Each element of `scroll-command-groups' is a list that contains all of the
546 members of a unique command group.  For example, if there were only one
547 explicit group and that group contained the commands `scroll-down-one-line' and
548 `scroll-up-one-line', then `scroll-command-groups' would be set to:
549
550   ((scroll-down-one-line scroll-up-one-line))
551
552 Commands that are not in any explicitly specified group are added to a default
553 group.  That group is stored in the variable `scroll-default-command-group'.
554
555 The \"in place\" scrolling functions assume that all of the scrolling command
556 groups are nonintersecting (i.e., no command is in more than one group) and
557 only contain \"in place\" scrolling commands.")
558
559 ;;;;
560 ;;;; The variables below this point are internal to this package.
561 ;;;;
562
563 (defvar scroll-default-command-group nil
564   "The set of \"in place\" scrolling commands that are not members of any
565 explicitly defined group of commands.  This set of commands is an implicitly
566 defined group, constructed as \"in place\" commands are invoked, and members of
567 this group share state among themselves.  See the documentation for the
568 variable `scroll-command-groups' for more information.")
569
570 (defvar scroll-initially-displayed-lines 0
571   "The number of window lines that contained buffer text when the current
572 sequence of \"in place\" scrolling commands started.  Unless the variable
573 `scroll-in-place-allow-blank-lines-past-eob' is true, the \"in place\"
574 scrolling commands ensure that at least this many text lines are visible at all
575 times.")
576
577 (defvar scroll-previous-window nil
578   "The window that was most recently scrolled by an \"in place\" scrolling
579 command.")
580
581 (defvar scroll-previous-lines 0
582   "The number of window lines that the previous \"in place\" scrolling command
583 attempted to scroll.")
584
585 (defvar scroll-goal-column 0
586   "The desired horizontal window position for point, used by the \"in place\"
587 scrolling commands.")
588
589 (defvar scroll-boundary-previous-point nil
590   "The value of point before point was moved to a buffer boundary.")
591
592 (defvar scroll-boundary-previous-lines 0
593   "The number of lines that point moved when it moved to a buffer boundary.")
594
595 (defvar scroll-boundary-error-command nil
596   "The value of `this-command' when an \"in place\" scrolling command signalled
597 a buffer boundary error.  This is used to decide how subsequent scrolling
598 commands should recover from the error.")
599
600 (defvar scroll-boundary-error-point nil
601   "The value of point when an \"in place\" scrolling command signalled a buffer
602 boundary error.  This is used to decide how subsequent scrolling commands
603 should recover from the error."
604   ;; This variable is used as a flag, indicating whether or not the previous
605   ;; "in place" scrolling command signalled an error.
606   )
607
608 (defvar scroll-window-debt 0
609   "The difference between the number of lines an \"in place\" scrolling command
610 tried to scroll a window and the number of lines that the window actually
611 scrolled.  This difference is the \"debt\" in the window's starting position.
612 Subsequent \"in place\" scrolling commands try to make up this debt.")
613
614 (defconst scroll-pos-visible-bug-p
615   ;; On September 14, 1993, David Hughes <djh@Harston.CV.COM> told me that
616   ;; Lucid GNU Emacs 19.8 had inherited the bug from Epoch... sigh.
617   (let ((old-match-data (match-data)))
618     (unwind-protect
619         (or (and (boundp 'epoch::version)
620                  (if (string-match "\\`4\\." emacs-version) t nil)
621                  )
622             (and (string-match "Lucid" emacs-version)
623                  (if (string-match "\\`19\\.8\\." emacs-version) t nil)
624                  )
625             )
626       (store-match-data old-match-data)))
627   "A flag, set when this version of GNU Emacs has a buggy version of the
628 function `pos-visible-in-window-p' that returns `nil' when given `(point-max)'
629 and `(point-max)' is on the last line of the window.  Currently, this flag is
630 set for all versions of Epoch 4 and for Lucid GNU Emacs 19.8.")
631
632 \f
633 ;; Hook functions to make turning the mode on and off easier.
634 (defun turn-on-scroll-in-place ()
635   "Unconditionally turn on scroll-in-place mode."
636   (set (make-local-variable 'scroll-in-place) t))
637
638 (defun turn-off-scroll-in-place ()
639   "Unconditionally turn on scroll-in-place mode."
640   (set (make-local-variable 'scroll-in-place) nil))
641
642 \f
643 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
644 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
645 ;;;;
646 ;;;; Here are the window-choosing auxiliary functions used by the new scrolling
647 ;;;; commands.
648 ;;;;
649 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
650 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
651
652 (defun scroll-choose-window ()
653   "Choose the window to be scrolled by the commands `scroll-down', `scroll-up',
654 `scroll-down-in-place', and `scroll-up-in-place'.
655
656 The rules are simple.  If the selected window is not a minibuffer window, then
657 just choose the selected window.
658
659 However, when a minibuffer window is selected, look first for the `minibuffer-
660 scroll-window'.  The `minibuffer-scroll-window' is usually the window that
661 displays completions.  If it exists, choose it; otherwise choose the next
662 window after the selected window in the canonical ordering of windows.  The
663 next window is generally the one below the selected window, or the one at the
664 top of the screen if the selected window is at the bottom of the screen."
665   (let ((selected-window (selected-window)))
666     (if (window-minibuffer-p selected-window)
667         ;; A minibuffer window is selected --- scroll some other window.
668         (if (window-live-p minibuffer-scroll-window)
669             minibuffer-scroll-window
670           ;; We know that the (selected) minibuffer is active, so `next-window'
671           ;; will examine all of the frames that share this minibuffer.
672           ;; Should we consider `other-window-scroll-buffer' here?  I don't
673           ;; believe so.
674           (next-window selected-window))
675       selected-window)))
676
677 ;;;
678 ;;;
679 ;;;
680
681 (defun scroll-choose-other-window ()
682   "Choose the window to be scrolled by the commands `scroll-other-window-down',
683 `scroll-other-window', `scroll-other-window-down-in-place', and `scroll-other-
684 window-in-place'.
685
686 The rules are these.  If the selected window is not a minibuffer window, then
687 choose either:
688
689   + a window that displays the `other-window-scroll-buffer', if that buffer
690     exists.  Note, this function will display that buffer if necessary.
691
692   + the next window after the selected window in the canonical ordering of
693     windows.  The next window is generally the one below the selected window,
694     or the one at the top of the screen if the selected window is at the bottom
695     of the screen.
696
697 However, when a minibuffer window is selected, look first for the `minibuffer-
698 scroll-window'.  The `minibuffer-scroll-window' is usually the window that
699 displays completions.  If it exists, choose it; otherwise choose the window to
700 be scrolled as described above (`other-window-scroll-buffer' or next window).
701
702 This function is essentially a Lisp version of the function `other-window-for-
703 scrolling' which first appeared in the FSF's GNU Emacs 19.26."
704   (let* ((no-error nil)
705          (selected-window (selected-window))
706          (other-window nil))
707     (setq other-window
708           (cond ((and (window-minibuffer-p selected-window)
709                       (window-live-p minibuffer-scroll-window))
710                  ;; Don't signal an error when `minibuffer-scroll-window' is
711                  ;; the minibuffer itself --- which would be really weird, but
712                  ;; isn't necessarily erroneous.
713                  (setq no-error t)
714                  minibuffer-scroll-window)
715                 
716                 ((and ;; `other-window-scroll-buffer' is an Emacs 19 invention.
717                       (boundp 'other-window-scroll-buffer)
718                       (bufferp other-window-scroll-buffer)
719                       ;; `buffer-name' is `nil' if the buffer has been killed.
720                       (buffer-name other-window-scroll-buffer))
721                  ;; This is what FSF GNU Emacs 19.26 does, but it occurred to
722                  ;; me: what if one of these forms returns the selected window?
723                  ;; Signalling an error would be bad news, so I added a flag.
724                  (setq no-error t)
725                  (or (get-buffer-window other-window-scroll-buffer)
726                      (display-buffer other-window-scroll-buffer t)))
727                 
728                 ((let ((next-window (next-window selected-window)))
729                    (if (eq next-window selected-window)
730                        nil
731                      next-window)))
732                 
733                 (t
734                  ;; In Emacs 19 (FSF, Lucid, and XEmacs), look for a window on
735                  ;; another visible frame.  This could be written for
736                  ;; Epoch, too, I suppose...
737                  (condition-case nil
738                      (let ((this-window (next-window selected-window nil t)))
739                        (while (not (or (eq this-window selected-window)
740                                        (scroll-choose-window-frame-visible-p
741                                         this-window)))
742                          (setq this-window (next-window this-window nil t)))
743                        this-window)
744                    ;; In older versions of Emacs, `next-window' didn't accept
745                    ;; three arguments.  Catch this error and then return the
746                    ;; selected window --- which will cause another error to be
747                    ;; signalled later on.
748                    (wrong-number-of-arguments selected-window))
749                  )
750                 ))
751     
752     (if (and (not no-error)
753              (eq selected-window other-window))
754         (error "There is no other window."))
755     other-window))
756
757 ;;;
758 ;;;
759 ;;;
760
761 (defun scroll-choose-window-frame-visible-p (window)
762   "Return a true value if the frame of the given WINDOW is visible."
763   (cond ((fboundp 'window-frame)
764          (eq t (frame-visible-p (window-frame window))))
765         (t t)))
766
767 \f
768 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
769 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
770 ;;;;
771 ;;;; Here are the "in place" scrolling commands (interactive functions) and the
772 ;;;; replacements for the standard GNU Emacs vertical scrolling commands.
773 ;;;;
774 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
775 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
776
777 ;;;;
778 ;;;; Here are the new scroll "in place" commands.
779 ;;;;
780
781 (defun scroll-down-in-place (&optional lines)
782   "Scroll the text of the current window downward by LINES lines, leaving point
783 as close as possible to its current window position (window line and column).
784 In other words, point is left \"in place\" within the window.  As a special
785 case, when the current window is a minibuffer window, this command scrolls the
786 `minibuffer-scroll-window' (which is usually the list of completions) if it
787 exists, or otherwise the next window in the canonical ordering of windows.
788
789 If the optional argument LINES is `nil', scroll the window by the same amount
790 it was moved by the immediately previous \"in place\" scrolling command, or by
791 the value of the variable `scroll-default-lines' (usually almost a windowful)
792 if the previous command was not an \"in place\" scrolling command (or when that
793 previous command scrolled some other window, or when other circumstances
794 prevent the previous scrolling distance from being used).  If LINES is the
795 symbol `-', then the scrolling distance is determined as if LINES had been
796 `nil' and then that distance is multiplied by -1.
797
798 If the window cannot be scrolled by the full distance, point is allowed to
799 stray from its initial position so that it can move the full number of lines.
800 If point cannot move the full number of lines, point is moved to the buffer
801 boundary.  Any immediately subsequent \"in place\" scrolling commands will try
802 to restore point to its initial window position."
803   (interactive "P")
804   (scroll-window-in-place (scroll-choose-window) lines -1))
805
806 ;;;
807 ;;;
808 ;;;
809
810 (defun scroll-up-in-place (&optional lines)
811   "Scroll the text of the current window upward by LINES lines, leaving point
812 as close as possible to its current window position (window line and column).
813 In other words, point is left \"in place\" within the window.  As a special
814 case, when the current window is a minibuffer window, this command scrolls the
815 `minibuffer-scroll-window' (which is usually the list of completions) if it
816 exists, or otherwise the next window in the canonical ordering of windows.
817
818 If the optional argument LINES is `nil', scroll the window by the same amount
819 it was moved by the immediately previous \"in place\" scrolling command, or by
820 the value of the variable `scroll-default-lines' (usually almost a windowful)
821 if the previous command was not an \"in place\" scrolling command (or when that
822 previous command scrolled some other window, or when other circumstances
823 prevent the previous scrolling distance from being used).  If LINES is the
824 symbol `-', then the scrolling distance is determined as if LINES had been
825 `nil' and then that distance is multiplied by -1.
826
827 If the window cannot be scrolled by the full distance, point is allowed to
828 stray from its initial position so that it can move the full number of lines.
829 If point cannot move the full number of lines, point is moved to the buffer
830 boundary.  Any immediately subsequent \"in place\" scrolling commands will try
831 to restore point to its initial window position."
832   (interactive "P")
833   (scroll-window-in-place (scroll-choose-window) lines 1))
834
835 ;;;
836 ;;; The command `scroll-other-window-down' first appeared in FSF GNU Emacs
837 ;;; 19.26.
838 ;;;
839
840 (defun scroll-other-window-down-in-place (&optional lines)
841   "Scroll the text of the next window downward by LINES lines, leaving point in
842 that window as close as possible to its current window position (window line
843 and column).  In other words, point is left \"in place\" within the window.
844 The next window is generally the one below the current one, or the one at the
845 top of the screen if the current window is at the bottom of the screen.  In
846 special circumstances this command will scroll a window other than the next
847 window.  Read the documentation for the function `scroll-choose-other-window'
848 for details.
849
850 If the optional argument LINES is `nil', scroll the window by the same amount
851 it was moved by the immediately previous \"in place\" scrolling command, or by
852 the value of the variable `scroll-default-lines' (usually almost a windowful)
853 if the previous command was not an \"in place\" scrolling command (or when that
854 previous command scrolled some other window, or when other circumstances
855 prevent the previous scrolling distance from being used).  If LINES is the
856 symbol `-', then the scrolling distance is determined as if LINES had been
857 `nil' and then that distance is multiplied by -1.
858
859 If the window cannot be scrolled by the full distance, point is allowed to
860 stray from its initial position so that it can move the full number of lines.
861 If point cannot move the full number of lines, point is moved to the buffer
862 boundary.  Any immediately subsequent \"in place\" scrolling commands will try
863 to restore point to its initial window position.
864
865 If it is impossible to scroll the text of the window at all (because a buffer
866 boundary is already visible), this command signals a buffer boundary error.
867 The error is signalled even if point could otherwise move the full number of
868 lines."
869   (interactive "P")
870   (scroll-window-in-place (scroll-choose-other-window) lines -1))
871
872 ;;;
873 ;;;
874 ;;;
875
876 (defun scroll-other-window-in-place (&optional lines)
877   "Scroll the text of the next window upward by LINES lines, leaving point in
878 that window as close as possible to its current window position (window line
879 and column).  In other words, point is left \"in place\" within the window.
880 The next window is generally the one below the current one, or the one at the
881 top of the screen if the current window is at the bottom of the screen.  In
882 special circumstances this command will scroll a window other than the next
883 window.  Read the documentation for the function `scroll-choose-other-window'
884 for details.
885
886 If the optional argument LINES is `nil', scroll the window by the same amount
887 it was moved by the immediately previous \"in place\" scrolling command, or by
888 the value of the variable `scroll-default-lines' (usually almost a windowful)
889 if the previous command was not an \"in place\" scrolling command (or when that
890 previous command scrolled some other window, or when other circumstances
891 prevent the previous scrolling distance from being used).  If LINES is the
892 symbol `-', then the scrolling distance is determined as if LINES had been
893 `nil' and then that distance is multiplied by -1.
894
895 If the window cannot be scrolled by the full distance, point is allowed to
896 stray from its initial position so that it can move the full number of lines.
897 If point cannot move the full number of lines, point is moved to the buffer
898 boundary.  Any immediately subsequent \"in place\" scrolling commands will try
899 to restore point to its initial window position.
900
901 If it is impossible to scroll the text of the window at all (because a buffer
902 boundary is already visible), this command signals a buffer boundary error.
903 The error is signalled even if point could otherwise move the full number of
904 lines."
905   (interactive "P")
906   (scroll-window-in-place (scroll-choose-other-window) lines 1))
907
908 ;;;;
909 ;;;; Here are the replacements for GNU Emacs' standard vertical scrolling
910 ;;;; commands.
911 ;;;;
912
913 (or (fboundp 'original-scroll-down)
914     (fset 'original-scroll-down (symbol-function 'scroll-down)))
915 (or (fboundp 'original-scroll-up)
916     (fset 'original-scroll-up (symbol-function 'scroll-up)))
917 (or (fboundp 'original-scroll-other-window-down)
918     ;; `scroll-other-window-down' first appeared in FSF GNU Emacs 19.26.
919     (if (fboundp 'scroll-other-window-down)
920         (fset 'original-scroll-other-window-down
921               (symbol-function 'scroll-other-window-down))
922       ))
923 (or (fboundp 'original-scroll-other-window)
924     (fset 'original-scroll-other-window (symbol-function 'scroll-other-window))
925     )
926
927 ;;;
928 ;;;
929 ;;;
930
931 (defun scroll-down (&optional lines window)
932   "Scroll the text of WINDOW downward by LINES lines.
933 If WINDOW is nil, use the selected window -- but as a special case, when
934 the current window is a minibuffer window, this command scrolls the
935 `minibuffer-scroll-window' (which is usually the list of completions) if it
936 exists, or otherwise the next window in the canonical ordering of windows.
937
938 The argument LINES is optional.  Its meaning depends on the current value of
939 the variable `scroll-in-place'.
940
941 When the variable `scroll-in-place' is true, this command works just like the
942 command `scroll-down-in-place', scrolling the current window and leaving point
943 \"in place\" within the window.  See the documentation for the command
944 `scroll-down-in-place' for more information.
945
946 When the variable `scroll-in-place' is `nil' this command invokes the standard
947 GNU Emacs version of `scroll-down'.  In that case, when LINES is `nil' the
948 current window is scrolled by nearly a complete windowful of text.
949
950 Note that this command correctly handles cases in which `scroll-in-place' has a
951 buffer-local value in the window to be scrolled.  That value is honored."
952   (interactive "P")
953   (scroll-window (or window (scroll-choose-window)) lines -1))
954
955 ;;;
956 ;;;
957 ;;;
958
959 (defun scroll-up (&optional lines window)
960   "Scroll the text of WINDOW upward by LINES lines.
961 If WINDOW is nil, use the selected window -- but as a special case, when
962 the current window is a minibuffer window, this command scrolls the
963 `minibuffer-scroll-window' (which is usually the list of completions) if it
964 exists, or otherwise the next window in the canonical ordering of windows.
965
966 The argument LINES is optional.  Its meaning depends on the current value of
967 the variable `scroll-in-place'.
968
969 When the variable `scroll-in-place' is true, this command works just like the
970 command `scroll-up-in-place', scrolling the current window and leaving point
971 \"in place\" within the window.  See the documentation for the command
972 `scroll-up-in-place' for more information.
973
974 When the variable `scroll-in-place' is `nil' this command invokes the standard
975 GNU Emacs version of `scroll-up'.  In that case, when LINES is `nil' the
976 current window is scrolled by nearly a complete windowful of text.
977
978 Note that this command correctly handles cases in which `scroll-in-place' has a
979 buffer-local value in the window to be scrolled.  That value is honored."
980   (interactive "P")
981   (scroll-window (or window (scroll-choose-window)) lines 1))
982
983 ;;;
984 ;;; NOTE that in the FSF GNU Emacs 19.26 version of `scroll-other-window-down',
985 ;;; the `lines' argument is required.  I've left it optional in order to be
986 ;;; like `scroll-other-window'.
987 ;;;
988
989 (defun scroll-other-window-down (&optional lines)
990   "Scroll the text of the next window downward by LINES lines.  The next window
991 is generally the one below the current one, or the one at the top of the screen
992 if the current window is at the bottom of the screen.  In special circumstances
993 this command will scroll a window other than the next window.  Read the
994 documentation for the function `scroll-choose-other-window' for details.
995
996 The argument LINES is optional.  Its meaning depends on the current value of
997 the variable `scroll-in-place'.
998
999 When the variable `scroll-in-place' is true, this command works just like the
1000 command `scroll-other-window-down-in-place', scrolling the next window and
1001 leaving point \"in place\" within that window.  See the documentation for the
1002 command `scroll-other-window-down-in-place' for more information.
1003
1004 When the variable `scroll-in-place' is `nil' this command invokes the standard
1005 GNU Emacs version of `scroll-other-window-down'.  In that case, when LINES is
1006 `nil' the next window is scrolled by nearly a complete windowful of text.
1007 \(Note that `scroll-other-window-down' first appeared as a standard command in
1008 the FSF's GNU Emacs 19.26.  If the builtin version of that command is not
1009 available in the current Emacs system, an equivalent action is invoked
1010 instead.)
1011
1012 Note that this command correctly handles cases in which `scroll-in-place' has a
1013 buffer-local value in the window to be scrolled.  That value is honored."
1014   (interactive "P")
1015   ;; This code is similar to the body of `scroll-window', below.
1016   (let* ((other-window (scroll-choose-other-window))
1017          (other-window-buffer (window-buffer other-window)))
1018     (if ;; Allow `scroll-in-place' to be a buffer-local variable.
1019         (save-excursion (set-buffer other-window-buffer) scroll-in-place)
1020         (scroll-window-in-place other-window lines -1)
1021       
1022       ;; Paranoid, we forcibly break any running sequence of "in place"
1023       ;; scrolling commands.
1024       (setq scroll-previous-window nil)
1025       ;; For XEmacs and Lucid GNU Emacs, preserve the region's state.
1026       (if (boundp 'zmacs-region-stays)
1027           (setq zmacs-region-stays t))
1028       (if (fboundp 'original-scroll-other-window-down)
1029           (original-scroll-other-window-down lines)
1030         ;; `scroll-other-window-down' first appeared as a builtin in FSF GNU
1031         ;; Emacs 19.26, so it may not be available in the current Emacs system.
1032         ;; Do the equivalent thing.
1033         (original-scroll-other-window (cond
1034                                        ((null lines) '-)
1035                                        ((eq lines '-) nil)
1036                                        (t (- (prefix-numeric-value lines)))
1037                                        ))
1038         ))
1039     ))
1040
1041 ;;;
1042 ;;;
1043 ;;;
1044
1045 (defun scroll-other-window (&optional lines)
1046   "Scroll the text of the next window upward by LINES lines.  The next window
1047 is generally the one below the current one, or the one at the top of the screen
1048 if the current window is at the bottom of the screen.  In special circumstances
1049 this command will scroll a window other than the next window.  Read the
1050 documentation for the function `scroll-choose-other-window' for details.
1051
1052 The argument LINES is optional.  Its meaning depends on the current value of
1053 the variable `scroll-in-place'.
1054
1055 When the variable `scroll-in-place' is true, this command works just like the
1056 command `scroll-other-window-in-place', scrolling the next window and leaving
1057 point \"in place\" within that window.  See the documentation for the command
1058 `scroll-other-window-in-place' for more information.
1059
1060 When the variable `scroll-in-place' is `nil' this command invokes the standard
1061 GNU Emacs version of `scroll-other-window'.  In that case, when LINES is `nil'
1062 the next window is scrolled by nearly a complete windowful of text.
1063
1064 Note that this command correctly handles cases in which `scroll-in-place' has a
1065 buffer-local value in the window to be scrolled.  That value is honored."
1066   (interactive "P")
1067   ;; This code is similar to the body of `scroll-window', below.
1068   (let* ((other-window (scroll-choose-other-window))
1069          (other-window-buffer (window-buffer other-window)))
1070     (if ;; Allow `scroll-in-place' to be a buffer-local variable.
1071         (save-excursion (set-buffer other-window-buffer) scroll-in-place)
1072         (scroll-window-in-place other-window lines 1)
1073       
1074       ;; Paranoid, we forcibly break any running sequence of "in place"
1075       ;; scrolling commands.
1076       (setq scroll-previous-window nil)
1077       ;; For XEmacs and Lucid GNU Emacs, preserve the region's state.
1078       (if (boundp 'zmacs-region-stays)
1079           (setq zmacs-region-stays t))
1080       (original-scroll-other-window lines))
1081     ))
1082
1083 \f
1084 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1085 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1086 ;;;;
1087 ;;;; Here are the new functions `scroll-window-in-place', `scroll-window', and
1088 ;;;; `scroll-window-in-place-continue-sequence'.  These functions are intended
1089 ;;;; to be available to programs outside this package.
1090 ;;;;
1091 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1092 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1093
1094 (defun scroll-window-in-place (window lines direction)
1095   "Scroll WINDOW vertically by the given number of window LINES in the given
1096 DIRECTION, leaving the window's point as close as possible to its original
1097 window position (window line and column).  In other words, the window's point
1098 is left \"in place\" within the window.
1099
1100 Note that the window to be scrolled does not have to be the selected window,
1101 and that this function does not change which window is selected.
1102
1103 LINES specifies the number of window lines to scroll and is interpreted as if
1104 it were a raw prefix argument.  If LINES is `nil', the window is scrolled by
1105 the amount it was moved by the immediately previous \"in place\" scrolling
1106 command, or by the value of the variable `scroll-default-lines' (by default,
1107 almost a windowful) if the previous command was not an \"in place\" scrolling
1108 command (or when WINDOW is not the previously scrolled window, or when the
1109 value of `this-command' is not in the same group as the previous scrolling
1110 command (see the documentation for the variable `scroll-command-groups'), or
1111 when other circumstances prevent the previous scrolling distance from being
1112 used).  If LINES is the symbol `-', then the scrolling distance is determined
1113 as if LINES had been `nil' and then that distance is multiplied by -1.
1114
1115 DIRECTION determines the direction of the scrolling motion.  The values -1 and
1116 `down' indicate downward motion; the values 1 and `up' indicate upward motion.
1117 Any other value causes an error.
1118
1119 If the window cannot be scrolled by the full distance (because the window hits
1120 the boundary of its buffer), the window's point is allowed to stray from its
1121 initial position so that it can move the full number of lines.  If point cannot
1122 move the full number of lines, point is moved to the buffer boundary (unless it
1123 was already there, in which case a buffer boundary error is signalled instead).
1124 Any immediately subsequent \"in place\" scrolling commands will try to restore
1125 point to its initial window position.
1126
1127 Unless the variable `scroll-allow-blank-lines-past-eob' is true, this function
1128 avoids displaying blank lines past the end of the buffer except as necessary to
1129 make a previous \"in place\" scrolling action reversible.  Effectively, this
1130 means that this function will not display any more past-end-of-buffer blank
1131 lines than were visible when the current sequence of \"in place\" scrolling
1132 commands started.  When the variable `scroll-allow-blank-lines-past-eob' is
1133 true, this function will display as many blank lines as is necessary to keep
1134 point \"in place\" in the window.
1135
1136 Note that if WINDOW is not the selected window and it is impossible to scroll
1137 the text of WINDOW at all (because a buffer boundary is already visible), then
1138 this function signals a buffer boundary error.  The error is signalled even if
1139 point could otherwise move the full number of lines."
1140   (let* (;; Make sure that the user doesn't quit in the middle and leave us
1141          ;; with our variables out of sync.
1142          (inhibit-quit t)
1143          (original-window (selected-window))
1144          (original-buffer (current-buffer))
1145          (window-height (- (window-height window)
1146                            (if (window-minibuffer-p window)
1147                                0 1)))
1148          (this-command-group (scroll-get-command-group this-command))
1149          (continue-scroll-p
1150           (and ;; We're scrolling the previously scrolled window...
1151                (windowp scroll-previous-window)
1152                (eq window scroll-previous-window)
1153                ;; ...and the last command was an "in place" scrolling command
1154                ;; that can be continued by this command.
1155                (if (eq last-command t)
1156                    ;; If the previous command signalled an error, the value of
1157                    ;; `last-command' is `t'.  Try to see if we signalled the
1158                    ;; error and if point is where we left it.  (NOTE that FSF
1159                    ;; GNU Emacs 19.23+ no longer sets `last-command' to `t'
1160                    ;; when a command signals an error.  This is OK because the
1161                    ;; else part of this `if' does the appropriate thing.)
1162                    (and scroll-boundary-error-point
1163                         (eq (window-point window) scroll-boundary-error-point)
1164                         (memq scroll-boundary-error-command this-command-group)
1165                         )
1166                  ;; Otherwise...
1167                  (memq last-command this-command-group))
1168                ))
1169          (lines-value (prefix-numeric-value lines))
1170          )
1171     
1172     ;; For XEmacs and Lucid GNU Emacs, preserve the region's state.  Note that
1173     ;; these Emacsen will forcibly deactivate the region if we signal an error
1174     ;; later on.  Is this bad?
1175     (if (boundp 'zmacs-region-stays)
1176         (setq zmacs-region-stays t))
1177     ;; Parse the direction into a unit distance (1 or -1).
1178     (setq direction (scroll-parse-direction direction))
1179     
1180     (setq scroll-previous-window window
1181           ;; `(setq scroll-boundary-error-command nil)' is not necessary.
1182           scroll-boundary-error-point nil)
1183     (unwind-protect
1184         (progn
1185           ;; `select-window' does an implicit `set-buffer'.
1186           (select-window window)
1187           
1188           (if (or ;; The current command is not a continuation of a running
1189                   ;; sequence of "in place" scrolling commands...
1190                   (not continue-scroll-p)
1191                   ;; ...or we were given an explicit number of lines to scroll,
1192                   ;; and that number has a different magnitude than the last
1193                   ;; number of lines we scrolled...
1194                   (and (or (numberp lines) (consp lines))
1195                        (/= scroll-previous-lines lines-value)
1196                        (/= scroll-previous-lines (- lines-value)))
1197                   ;; ...or the last successful scrolling command moved to a
1198                   ;; buffer boundary, but the buffer is no longer in the state
1199                   ;; we left it.  (This can occur if, for example, we signal an
1200                   ;; end-of-buffer error and something catches it and moves
1201                   ;; point or renarrows.  VM, for example, does this.)
1202                   (and scroll-boundary-previous-point
1203                        (or (not (or (bobp) (eobp)))
1204                            (< scroll-boundary-previous-point (point-min))
1205                            (> scroll-boundary-previous-point (point-max))
1206                            (eq scroll-boundary-previous-point (point)))))
1207               
1208               ;; We're starting a new sequence of scrolling commands.
1209               (setq lines (if (or (numberp lines) (consp lines))
1210                               lines-value
1211                             ;; The default number of lines...
1212                             (* (if (eq lines '-) -1 1)
1213                                (if (numberp scroll-default-lines)
1214                                    scroll-default-lines
1215                                  (max (- window-height
1216                                          next-screen-context-lines)
1217                                       1))))
1218                     scroll-previous-lines lines
1219                     scroll-goal-column (scroll-determine-goal-column window)
1220                     scroll-boundary-previous-point nil
1221                     ;; `(setq scroll-boundary-previous-lines 0)' is not
1222                     ;; necessary.
1223                     scroll-window-debt 0
1224                     scroll-initially-displayed-lines
1225                     (if scroll-allow-blank-lines-past-eob
1226                         0
1227                       (save-excursion
1228                         (goto-char (window-start window))
1229                         (vertical-motion (1- window-height)))))
1230             
1231             ;; Otherwise we want to scroll by the same number of lines (but
1232             ;; possibly in a different direction) that we scrolled in previous
1233             ;; invocations of this function.
1234             (cond ((null lines)
1235                    (setq lines scroll-previous-lines))
1236                   ((eq lines '-)
1237                    (setq lines (- scroll-previous-lines)
1238                          scroll-previous-lines lines))
1239                   (t
1240                    (setq lines lines-value
1241                          scroll-previous-lines lines)))
1242             )
1243           
1244           (setq lines (* direction lines))
1245           
1246           ;; If point is not in the window, center window around point.  We try
1247           ;; to account for a bug in `pos-visible-in-window-p' in some versions
1248           ;; of Emacs (see `scroll-pos-visible-bug-p', above).
1249           (save-excursion
1250             (if (pos-visible-in-window-p (let ((point (point)))
1251                                            (if (and scroll-pos-visible-bug-p
1252                                                     (= point (point-max)))
1253                                                (max (1- point) (point-min))
1254                                              point))
1255                                          window)
1256                 nil
1257               (vertical-motion (/ (- window-height) 2))
1258               (set-window-start window (point))))
1259           
1260           (cond ((and scroll-boundary-previous-point
1261                       ;; `lines' is the same sign as the direction from point
1262                       ;; to the `scroll-boundary-previous-point'.
1263                       (cond ((> lines 0)
1264                              (> (- scroll-boundary-previous-point (point)) 0))
1265                             ((< lines 0)
1266                              (< (- scroll-boundary-previous-point (point)) 0))
1267                             (t nil)))
1268                  ;; We're moving away from the buffer boundary.
1269                  (goto-char scroll-boundary-previous-point)
1270                  ;; Always move here (i.e., don't reject cases in which the
1271                  ;; window doesn't move).
1272                  (scroll-set-window-start window
1273                                           (- scroll-boundary-previous-lines))
1274                  ;; (message "Back, window debt is %s." scroll-window-debt)
1275                  (setq scroll-boundary-previous-point nil))
1276
1277                 ((= lines 0)
1278                  ;; We're going nowhere, so save ourselves some work.
1279                  ;; (message "Scrolled zero lines.")
1280                  )
1281                 
1282                 (t
1283                  ;; Perform the scrolling motion.
1284                  (let ((initial-point (point))
1285                        (moved nil))
1286                    ;; First move point and see how far it goes.
1287                    (setq moved (vertical-motion lines))
1288                    (if (= moved lines)
1289                        (progn
1290                          ;; Point moved the full distance.  Move to the desired
1291                          ;; column and then try to move the window the full
1292                          ;; distance, too.
1293                          (move-to-column (+ (current-column)
1294                                             scroll-goal-column))
1295                          (or (scroll-set-window-start window moved
1296                                                       original-window)
1297                              (scroll-signal-boundary-error initial-point
1298                                                            lines))
1299                          ;; (message "Normal, window debt is %s."
1300                          ;;          scroll-window-debt)
1301                          )
1302                      ;; Point couldn't move all the way.  Move to the buffer
1303                      ;; boundary if we're not already there, or signal a buffer
1304                      ;; boundary error otherwise.
1305                      (let ((boundary-point (if (< lines 0)
1306                                                (point-min)
1307                                              (point-max)))
1308                            (boundary-symbol (if (< lines 0)
1309                                                 'beginning-of-buffer
1310                                               'end-of-buffer)))
1311                        (if (= initial-point boundary-point)
1312                            (scroll-signal-boundary-error initial-point lines)
1313                          ;; Scroll the window by as many lines as point could
1314                          ;; move.
1315                          (or (scroll-set-window-start window moved
1316                                                       original-window)
1317                              (scroll-signal-boundary-error initial-point
1318                                                            lines))
1319                          (message "%s" (get boundary-symbol 'error-message))
1320                          ;; (message "Boundary, window debt is %s."
1321                          ;;          scroll-window-debt)
1322                          (setq scroll-boundary-previous-lines moved)
1323                          (setq scroll-boundary-previous-point initial-point)
1324                          (goto-char boundary-point))
1325                        )))
1326                  )))
1327       
1328       ;; The unwind forms of the `unwind-protect', above.  Restore the
1329       ;; originally selected window and current buffer.
1330       (select-window original-window)
1331       (set-buffer original-buffer)))
1332   
1333   ;; The standard GNU Emacs scrolling commands return `nil' so we do, too.
1334   nil)
1335
1336 ;;;
1337 ;;;
1338 ;;;
1339
1340 (defun scroll-window (window lines direction)
1341   "Scroll WINDOW vertically by the given number of window LINES in the given
1342 DIRECTION.  Note that the window to be scrolled does not have to be the
1343 selected window, and that this function does not change which window is
1344 selected.
1345
1346 When the variable `scroll-in-place' is true, this function simply invokes the
1347 function `scroll-window-in-place' to scroll the window and leave point \"in
1348 place\" within that window.  See the documentation for `scroll-window-in-place'
1349 for more information.
1350
1351 When the variable `scroll-in-place' is `nil' this function invokes the original
1352 version of the standard GNU Emacs command `scroll-down' or `scroll-up', as
1353 determined by DIRECTION, to scroll the window.  If DIRECTION is -1 or `down',
1354 the original `scroll-down' is called; if DIRECTION is 1 or `up', the original
1355 `scroll-up' is called.  Any other DIRECTION is an error.  LINES is interpreted
1356 as if it were a raw prefix argument.  If LINES is `nil', the window is scrolled
1357 by almost a complete windowful.  If LINES is the symbol `-', the window is
1358 scrolled by almost a complete windowful in the opposite direction.
1359
1360 Note that this function correctly handles cases in which `scroll-in-place' has
1361 a buffer-local value in the WINDOW's buffer.  That value is honored."
1362   (let ((current-buffer (current-buffer))
1363         (selected-window (selected-window))
1364         (window-buffer (window-buffer window)))
1365     (if ;; Allow `scroll-in-place' to be a buffer-local variable.
1366         (if (eq current-buffer window-buffer)
1367             scroll-in-place
1368           (save-excursion (set-buffer window-buffer) scroll-in-place))
1369         (scroll-window-in-place window lines direction)
1370       
1371       (unwind-protect
1372           (progn
1373             ;; Paranoid, we forcibly break any running sequence of "in place"
1374             ;; scrolling commands.
1375             (setq scroll-previous-window nil)
1376             ;; For XEmacs and Lucid GNU Emacs, preserve the region's state.
1377             (if (boundp 'zmacs-region-stays)
1378                 (setq zmacs-region-stays t))
1379             (select-window window)
1380             (if (= (scroll-parse-direction direction) 1)
1381                 (original-scroll-up lines)
1382               (original-scroll-down lines)))
1383         (select-window selected-window)
1384         (set-buffer current-buffer))
1385       )))
1386
1387 ;;;
1388 ;;; The following function is sometimes useful.  For example, I call it from
1389 ;;; functions that are invoked by certain mouse button down events in order to
1390 ;;; preserve any running chain of "in place" scrolling commands.  This lets me
1391 ;;; continue the sequence from my mouse button up functions.
1392 ;;;
1393 ;;; I haven't yet needed a function to purposely break a running sequence of
1394 ;;; "in place" scrolling commands.  Such a function would be easy to write,
1395 ;;; however; just set the variable `scroll-previous-window' to `nil'.
1396 ;;;
1397
1398 (defun scroll-window-in-place-continue-sequence ()
1399   "If the previous command was a \"scroll in place\" command, set the variable
1400 `this-command' to the name of that previous command.  This ensures that any
1401 running sequence of \"in place\" scrolling commands will not be broken by the
1402 current command.  See the documentation for the commands `scroll-down-in-place'
1403 and `scroll-up-in-place' for more information about \"in place\" scrolling.
1404
1405 NOTE that you don't need to call this function if the current command scrolls
1406 in place!  You only need to call this function when the current command is not
1407 a \"scroll in place\" command but you still want to preserve any running
1408 sequence of \"in place\" commands.  Such situations are rare.
1409
1410 NOTE that this function sets `this-command' in order to trick the \"in place\"
1411 scrolling commands.  If something else subsequently sets `this-command', any
1412 running sequence of scrolling commands will probably be broken anyway."
1413   (if (if (eq last-command t)
1414           ;; If `last-command' is `t', then the previous command signalled an
1415           ;; error.  See if the last invocation of `scroll-window-in-place'
1416           ;; signalled an error.  (NOTE that FSF GNU Emacs 19.23+ no longer
1417           ;; sets `last-command' to `t' when a command signals an error.  This
1418           ;; is OK because the else part of this `if' does the appropriate
1419           ;; thing.)
1420           scroll-boundary-error-point
1421         ;; Otherwise, the value of `last-command' must belong to some group of
1422         ;; "in place" scrolling commands.
1423         (or (memq last-command scroll-default-command-group)
1424             (let ((groups scroll-command-groups)
1425                   (found nil))
1426               (while (and groups (not found))
1427                 (if (memq last-command (car groups))
1428                     (setq found t)
1429                   (setq groups (cdr groups)))
1430                 )
1431               found)))
1432       (setq this-command last-command)))
1433
1434 \f
1435 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1436 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1437 ;;;;
1438 ;;;; Here are the various auxiliary functions called by the function `scroll-
1439 ;;;; window-in-place'.  None of the functions are intended to be called from
1440 ;;;; outside this package.
1441 ;;;;
1442 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1443 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1444
1445 (defun scroll-get-command-group (command)
1446   "Return the group of \"in place\" scrolling commands that contains the given
1447 COMMAND.  This is the list of commands with which the given command may share
1448 state and form \"chains.\"
1449
1450 This function is an auxiliary for the function `scroll-window-in-place'.  Don't
1451 call this function from other code."
1452   ;; This function assumes that the given command is an "in place" scrolling
1453   ;; command.
1454   (let ((groups scroll-command-groups)
1455         (found nil))
1456     (while (and groups (not found))
1457       (if (memq command (car groups))
1458           (setq found t)
1459         (setq groups (cdr groups)))
1460       )
1461     (if groups
1462         (car groups)
1463       ;; Otherwise return the default command group.  If necessary, add the
1464       ;; given command to the default command group.
1465       (or (memq command scroll-default-command-group)
1466           (setq scroll-default-command-group
1467                 (cons command scroll-default-command-group)))
1468       scroll-default-command-group)
1469     ))
1470
1471 ;;;
1472 ;;;
1473 ;;;
1474
1475 (defun scroll-parse-direction (direction)
1476   "Return the signed unit distance for the given DIRECTION.  If DIRECTION is
1477 unacceptable, signal an error."
1478   (cond ((or (eq direction 1) (eq direction -1)) direction)
1479         ((eq direction 'up) 1)
1480         ((eq direction 'down) -1)
1481         (t (signal 'args-out-of-range (list 'direction direction)))
1482         ))
1483
1484 ;;;
1485 ;;;
1486 ;;;
1487
1488 (defun scroll-determine-goal-column (window)
1489   "Return the goal column for the \"in place\" vertical scrolling commands.
1490 This is the horizontal window position at which these commands try to keep
1491 point.
1492
1493 This function is an auxiliary for the function `scroll-window-in-place'.  Don't
1494 call this function from other code."
1495   ;; NOTE that `window' must be the selected window!  `scroll-window-in-place'
1496   ;; ensures that this is so.
1497   (cond ((or truncate-lines
1498              (and truncate-partial-width-windows
1499                   (< (window-width window) (frame-width)))
1500              (> (window-hscroll window) 0))
1501          ;; Lines in this window are being truncated.
1502          (if (and track-eol (eolp))
1503              9999
1504            (current-column)))
1505         ((and track-eol (eolp))
1506          ;; In some ways this isn't quite right, as point doesn't track the
1507          ;; ends of wrapped lines.  But if it did so, point would be on the
1508          ;; wrong window line.  This is the best we can do.
1509          (1- (window-width window)))
1510         (t (% (current-column) (1- (window-width window))))
1511         ))
1512
1513 ;;;
1514 ;;;
1515 ;;;
1516
1517 (defun scroll-set-window-start (window lines &optional original-window)
1518   "Move the `window-start' of the given window, which must be the selected
1519 window.  If the window was successfully scrolled, update the variable
1520 `scroll-window-debt' and return `t'.  Otherwise return `nil'.
1521
1522 This function is an auxiliary for the function `scroll-window-in-place'.  Don't
1523 call this function from other code."
1524   (save-excursion
1525     (goto-char (window-start window))
1526     ;; Try to move the window start by the specified number of lines.  In
1527     ;; addition, try to make up any existing debt in the window start's
1528     ;; position and make sure that we don't move too close to the end of the
1529     ;; buffer.
1530     (let ((moved (+ (vertical-motion (+ lines
1531                                         scroll-window-debt
1532                                         scroll-initially-displayed-lines))
1533                     (vertical-motion (- scroll-initially-displayed-lines)))))
1534       ;; If we're not scrolling the `original-window' (i.e., the originally
1535       ;; selected window), punt if we didn't move the window start at all.
1536       (if (and original-window
1537                (not (eq window original-window))
1538                (= moved 0))
1539           nil
1540         ;; Otherwise update the window start and keep track of the debt in our
1541         ;; position.  Return `t' to indicate success.
1542         (set-window-start window (point))
1543         (setq scroll-window-debt (- (+ lines scroll-window-debt) moved))
1544         t))
1545     ))
1546
1547 ;;;
1548 ;;;
1549 ;;;
1550
1551 (defun scroll-signal-boundary-error (initial-point lines)
1552   "Move point to its initial location and signal an appropriate buffer boundary
1553 error.
1554
1555 This function is an auxiliary for the function `scroll-window-in-place'.  Don't
1556 call this function from other code."
1557   (goto-char initial-point)
1558   ;; Remember what we were doing and where point was when we signalled the
1559   ;; error so that subsequent "in place" scrolling commands can decide how to
1560   ;; recover.
1561   (setq scroll-boundary-error-command this-command
1562         scroll-boundary-error-point initial-point)
1563   (when signal-error-on-buffer-boundary
1564     (signal (if (< lines 0) 'beginning-of-buffer 'end-of-buffer)
1565             nil)))
1566
1567 \f
1568 ;;; Some convenience redefinitions for modes that don't like scroll-in-place
1569 (add-hook 'vm-mode-hook 'turn-off-scroll-in-place)
1570 (add-hook 'vm-select-message-hook 'turn-off-scroll-in-place)
1571 (add-hook 'vm-summary-mode-hook 'turn-off-scroll-in-place)
1572
1573 (add-hook 'list-mode-hook 'turn-off-scroll-in-place)
1574
1575 ;; This doesn't work with Red Gnus
1576 ;; (add-hook 'gnus-article-mode-hook 'turn-off-scroll-in-place)
1577
1578 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1579 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1580 ;;;;
1581 ;;;; Finally, here is the `provide' statement.
1582 ;;;;
1583 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1584 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1585
1586 (provide 'scroll-in-place)
1587
1588 ;;; scroll-in-place.el ends here