Initial Commit
[packages] / xemacs-packages / tpu / tpu-extras.el
1 ;;; tpu-extras.el --- Scroll margins and free cursor mode for TPU-edt
2
3 ;; Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
4
5 ;; Author: Rob Riepel <riepel@networking.stanford.edu>
6 ;; Maintainer: Rob Riepel <riepel@networking.stanford.edu>
7 ;; Keywords: emulations
8
9 ;; This file is part of XEmacs.
10 ;; XEmacs modifications by Kevin Oberman <oberman@es.net>
11
12 ;; XEmacs is free software; you can redistribute it and/or modify it
13 ;; under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
16
17 ;; XEmacs is distributed in the hope that it will be useful, but
18 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with XEmacs; see the file COPYING.  If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
26
27 ;;; Synced up with FSF 19.34 and XEmacs 21.0
28
29 ;;; Commentary:
30
31 ;;  Use the functions defined here to customize TPU-edt to your tastes by
32 ;;  setting scroll margins and/or turning on free cursor mode.  Here's an
33 ;;  example for your .emacs file.
34
35 ;;     (tpu-set-cursor-free)                   ; Set cursor free.
36 ;;     (tpu-set-scroll-margins "10%" "15%")    ; Set scroll margins.
37
38 ;;  Scroll margins and cursor binding can be changed from within emacs using
39 ;;  the following commands:
40
41 ;;     tpu-set-scroll-margins  or   set scroll margins
42 ;;     tpu-set-cursor-bound    or   set cursor bound
43 ;;     tpu-set-cursor-free     or   set cursor free
44
45 ;;  Additionally, Gold-F toggles between bound and free cursor modes.
46
47 ;;  Note that switching out of free cursor mode or exiting TPU-edt while in
48 ;;  free cursor mode strips trailing whitespace from every line in the file.
49
50
51 ;;; Details:
52
53 ;;  The functions contained in this file implement scroll margins and free
54 ;;  cursor mode.  The following keys and commands are affected.
55
56 ;;       key/command   function                        scroll   cursor
57
58 ;;       Up-Arrow      previous line                     x        x
59 ;;       Down-Arrow    next line                         x        x
60 ;;       Right-Arrow   next character                             x
61 ;;       Left-Arrow    previous character                         x
62 ;;       KP0           next or previous line             x
63 ;;       KP7           next or previous page             x
64 ;;       KP8           next or previous screen           x
65 ;;       KP2           next or previous end-of-line      x        x
66 ;;       Control-e     current end-of-line                        x
67 ;;       Control-h     previous beginning-of-line        x
68 ;;       Next Scr      next screen                       x
69 ;;       Prev Scr      previous screen                   x
70 ;;       Search        find a string                     x
71 ;;       Replace       find and replace a string         x
72 ;;       Newline       insert a newline                  x
73 ;;       Paragraph     next or previous paragraph        x
74 ;;       Auto-Fill     break lines on spaces             x
75
76 ;;  These functions are not part of the base TPU-edt for the following
77 ;;  reasons:
78
79 ;;  Free cursor mode is implemented with the emacs picture-mode functions.
80 ;;  These functions support moving the cursor all over the screen, however,
81 ;;  when the cursor is moved past the end of a line, spaces or tabs are
82 ;;  appended to the line - even if no text is entered in that area.  In
83 ;;  order for a free cursor mode to work exactly like TPU/edt, this trailing
84 ;;  whitespace needs to be dealt with in every function that might encounter
85 ;;  it.  Such global changes are impractical, however, free cursor mode is
86 ;;  too valuable to abandon completely, so it has been implemented in those
87 ;;  functions where it serves best.
88
89 ;;  The implementation of scroll margins adds overhead to previously
90 ;;  simple and often used commands.  These commands are now responsible
91 ;;  for their normal operation and part of the display function.  There
92 ;;  is a possibility that this display overhead could adversely affect the
93 ;;  performance of TPU-edt on slower computers.  In order to support the
94 ;;  widest range of computers, scroll margin support is optional.
95
96 ;;  It's actually not known whether the overhead associated with scroll
97 ;;  margin support is significant.  If you find that it is, please send
98 ;;  a note describing the extent of the performance degradation.  Be sure
99 ;;  to include a description of the platform where you're running TPU-edt.
100 ;;  Send your note to the address provided by Gold-V.
101
102 ;;  Even with these differences and limitations, these functions implement
103 ;;  important aspects of the real TPU/edt.  Those who miss free cursor mode
104 ;;  and/or scroll margins will appreciate these implementations.
105
106 ;;; Code:
107
108
109 ;;;  Gotta have tpu-edt
110
111 (require 'tpu-edt)
112
113
114 ;;;  Customization variables
115
116 (defcustom tpu-top-scroll-margin 0
117   "*Scroll margin at the top of the screen.
118 Interpreted as a percent of the current window size."
119   :type 'integer
120   :group 'tpu)
121 (defcustom tpu-bottom-scroll-margin 0
122   "*Scroll margin at the bottom of the screen.
123 Interpreted as a percent of the current window size."
124   :type 'integer
125   :group 'tpu)
126
127 (defcustom tpu-backward-char-like-tpu t
128   "*If non-nil, in free cursor mode backward-char (left-arrow) works
129 just like TPU/edt.  Otherwise, backward-char will move to the end of
130 the previous line when starting from a line beginning."
131   :type 'boolean
132   :group 'tpu)
133
134
135 ;;;  Global variables
136
137 (defvar tpu-cursor-free nil
138   "If non-nil, let the cursor roam free.")
139
140
141 ;;;  Hooks  --  Set cursor free in picture mode.
142 ;;;             Clean up when writing a file from cursor free mode.
143
144 (add-hook 'picture-mode-hook 'tpu-set-cursor-free)
145
146 (defun tpu-write-file-hook nil
147   "Eliminate whitespace at ends of lines, if the cursor is free."
148   (if (and (buffer-modified-p) tpu-cursor-free) (picture-clean)))
149
150 (or (memq 'tpu-write-file-hook write-file-hooks)
151     (setq write-file-hooks
152           (cons 'tpu-write-file-hook write-file-hooks)))
153
154
155 ;;;  Utility routines for implementing scroll margins
156
157 (defun tpu-top-check (beg lines)
158   "Enforce scroll margin at the top of screen."
159   (let ((margin  (/ (* (window-height) tpu-top-scroll-margin) 100)))
160     (cond ((< beg margin) (recenter beg))
161           ((< (- beg lines) margin) (recenter margin)))))
162
163 (defun tpu-bottom-check (beg lines)
164   "Enforce scroll margin at the bottom of screen."
165   (let* ((height (window-height))
166          (margin (+ 1 (/ (* height tpu-bottom-scroll-margin) 100)))
167          ;; subtract 1 from height because it includes mode line
168          (difference (- height margin 1)))
169     (cond ((> beg difference) (recenter beg))
170           ((> (+ beg lines) difference) (recenter (- margin))))))
171
172
173 ;;;  Movement by character
174
175 (defun tpu-forward-char (num)
176   "Move right ARG characters (left if ARG is negative)."
177   (interactive "p")
178   (setq zmacs-region-stays t)
179   (if tpu-cursor-free (picture-forward-column num) (forward-char num)))
180
181 (defun tpu-backward-char (num)
182   "Move left ARG characters (right if ARG is negative)."
183   (interactive "p")
184   (setq zmacs-region-stays t)
185   (cond ((not tpu-cursor-free)
186          (backward-char num))
187         (tpu-backward-char-like-tpu
188          (picture-backward-column num))
189         ((bolp)
190          (backward-char 1)
191          (picture-end-of-line)
192          (picture-backward-column (1- num)))
193         (t
194          (picture-backward-column num))))
195
196
197 ;;;  Movement by line
198
199 (defun tpu-next-line (num)
200   "Move to next line.
201 Prefix argument serves as a repeat count."
202   (interactive "p")
203   (setq zmacs-region-stays t)
204   (let ((beg (tpu-current-line)))
205     (if tpu-cursor-free (or (eobp) (picture-move-down num))
206       (next-line-internal num))
207     (tpu-bottom-check beg num)
208     (setq this-command 'next-line)))
209
210 (defun tpu-previous-line (num)
211   "Move to previous line.
212 Prefix argument serves as a repeat count."
213   (interactive "p")
214   (setq zmacs-region-stays t)
215   (let ((beg (tpu-current-line)))
216     (if tpu-cursor-free (picture-move-up num) (next-line-internal (- num)))
217     (tpu-top-check beg num)
218     (setq this-command 'previous-line)))
219
220 (defun tpu-next-beginning-of-line (num)
221   "Move to beginning of line; if at beginning, move to beginning of next line.
222 Accepts a prefix argument for the number of lines to move."
223   (interactive "p")
224   (setq zmacs-region-stays t)
225   (let ((beg (tpu-current-line)))
226     (backward-char 1)
227     (forward-line (- 1 num))
228     (tpu-top-check beg num)))
229
230 (defun tpu-next-end-of-line (num)
231   "Move to end of line; if at end, move to end of next line.
232 Accepts a prefix argument for the number of lines to move."
233   (interactive "p")
234   (setq zmacs-region-stays t)
235   (let ((beg (tpu-current-line)))
236     (cond (tpu-cursor-free
237            (let ((beg (point)))
238              (if (< 1 num) (forward-line num))
239              (picture-end-of-line)
240              (if (<= (point) beg) (progn (forward-line) (picture-end-of-line)))))
241           (t
242            (forward-char)
243            (end-of-line num)))
244     (tpu-bottom-check beg num)))
245
246 (defun tpu-previous-end-of-line (num)
247   "Move EOL upward.
248 Accepts a prefix argument for the number of lines to move."
249   (interactive "p")
250   (setq zmacs-region-stays t)
251   (let ((beg (tpu-current-line)))
252     (cond (tpu-cursor-free
253            (picture-end-of-line (- 1 num)))
254           (t
255            (end-of-line (- 1 num))))
256     (tpu-top-check beg num)))
257
258 (defun tpu-current-end-of-line nil
259   "Move point to end of current line."
260   (interactive)
261   (setq zmacs-region-stays t)
262   (let ((beg (point)))
263     (if tpu-cursor-free (picture-end-of-line) (end-of-line))
264     (if (= beg (point)) (message "You are already at the end of a line."))))
265
266 (defun tpu-forward-line (num)
267   "Move to beginning of next line.
268 Prefix argument serves as a repeat count."
269   (interactive "p")
270   (let ((beg (tpu-current-line)))
271     (next-line-internal num)
272     (tpu-bottom-check beg num)
273     (beginning-of-line)))
274
275 (defun tpu-backward-line (num)
276   "Move to beginning of previous line.
277 Prefix argument serves as repeat count."
278   (interactive "p")
279   (setq zmacs-region-stays t)
280   (let ((beg (tpu-current-line)))
281     (or (bolp) (>= 0 num) (setq num (- num 1)))
282     (next-line-internal (- num))
283     (tpu-top-check beg num)
284     (beginning-of-line)))
285
286
287 ;;;  Movement by paragraph
288
289 (defun tpu-paragraph (num)
290   "Move to the next paragraph in the current direction.
291 A repeat count means move that many paragraphs."
292   (interactive "p")
293   (setq zmacs-region-stays t)
294   (let* ((left nil)
295          (beg (tpu-current-line))
296          (height (window-height))
297          (top-percent
298           (if (= 0 tpu-top-scroll-margin) 10 tpu-top-scroll-margin))
299          (bottom-percent
300           (if (= 0 tpu-bottom-scroll-margin) 15 tpu-bottom-scroll-margin))
301          (top-margin (/ (* height top-percent) 100))
302          (bottom-up-margin (+ 1 (/ (* height bottom-percent) 100)))
303          (bottom-margin (max beg (- height bottom-up-margin 1)))
304          (top (save-excursion (move-to-window-line top-margin) (point)))
305          (bottom (save-excursion (move-to-window-line bottom-margin) (point)))
306          (far (save-excursion
307                 (goto-char bottom) (forward-line (- height 2)) (point))))
308     (cond (tpu-advance
309            (tpu-next-paragraph num)
310            (cond((> (point) far)
311                  (setq left (save-excursion (forward-line height)))
312                  (if (= 0 left) (recenter top-margin)
313                    (recenter (- left bottom-up-margin))))
314                 (t
315                  (and (> (point) bottom) (recenter bottom-margin)))))
316           (t
317            (tpu-previous-paragraph num)
318            (and (< (point) top) (recenter (min beg top-margin)))))))
319
320
321 ;;;  Movement by page
322
323 (defun tpu-page (num)
324   "Move to the next page in the current direction.
325 A repeat count means move that many pages."
326   (interactive "p")
327   (setq zmacs-region-stays t)
328   (let* ((left nil)
329          (beg (tpu-current-line))
330          (height (window-height))
331          (top-percent
332           (if (= 0 tpu-top-scroll-margin) 10 tpu-top-scroll-margin))
333          (bottom-percent
334           (if (= 0 tpu-bottom-scroll-margin) 15 tpu-bottom-scroll-margin))
335          (top-margin (/ (* height top-percent) 100))
336          (bottom-up-margin (+ 1 (/ (* height bottom-percent) 100)))
337          (bottom-margin (max beg (- height bottom-up-margin 1)))
338          (top (save-excursion (move-to-window-line top-margin) (point)))
339          (bottom (save-excursion (move-to-window-line bottom-margin) (point)))
340          (far (save-excursion
341                 (goto-char bottom) (forward-line (- height 2)) (point))))
342     (cond (tpu-advance
343            (forward-page num)
344            (cond((> (point) far)
345                  (setq left (save-excursion (forward-line height)))
346                  (if (= 0 left) (recenter top-margin)
347                    (recenter (- left bottom-up-margin))))
348                 (t
349                  (and (> (point) bottom) (recenter bottom-margin)))))
350           (t
351            (backward-page num)
352            (and (< (point) top) (recenter (min beg top-margin)))))))
353
354
355 ;;;  Scrolling
356
357 (defun tpu-scroll-window-down (num)
358   "Scroll the display down to the next section.
359 A repeat count means scroll that many sections."
360   (interactive "p")
361   (setq zmacs-region-stays t)
362   (let* ((beg (tpu-current-line))
363          (height (1- (window-height)))
364          (lines (* num (/ (* height tpu-percent-scroll) 100))))
365     (next-line-internal (- lines))
366     (tpu-top-check beg lines)))
367
368 (defun tpu-scroll-window-up (num)
369   "Scroll the display up to the next section.
370 A repeat count means scroll that many sections."
371   (interactive "p")
372   (setq zmacs-region-stays t)
373   (let* ((beg (tpu-current-line))
374          (height (1- (window-height)))
375          (lines (* num (/ (* height tpu-percent-scroll) 100))))
376     (next-line-internal lines)
377     (tpu-bottom-check beg lines)))
378
379
380 ;;;  Replace the TPU-edt internal search function
381
382 (defun tpu-search-internal (pat &optional quiet)
383   "Search for a string or regular expression."
384   (let* ((left nil)
385          (beg (tpu-current-line))
386          (height (window-height))
387          (top-percent
388           (if (= 0 tpu-top-scroll-margin) 10 tpu-top-scroll-margin))
389          (bottom-percent
390           (if (= 0 tpu-bottom-scroll-margin) 15 tpu-bottom-scroll-margin))
391          (top-margin (/ (* height top-percent) 100))
392          (bottom-up-margin (+ 1 (/ (* height bottom-percent) 100)))
393          (bottom-margin (max beg (- height bottom-up-margin 1)))
394          (top (save-excursion (move-to-window-line top-margin) (point)))
395          (bottom (save-excursion (move-to-window-line bottom-margin) (point)))
396          (far (save-excursion
397                 (goto-char bottom) (forward-line (- height 2)) (point))))
398     (tpu-search-internal-core pat quiet)
399     (if tpu-searching-forward
400         (cond((> (point) far)
401               (setq left (save-excursion (forward-line height)))
402               (if (= 0 left) (recenter top-margin)
403                 (recenter (- left bottom-up-margin))))
404              (t
405               (and (> (point) bottom) (recenter bottom-margin))))
406       (and (< (point) top) (recenter (min beg top-margin))))))
407
408
409
410 ;;;  Replace the newline, newline-and-indent, and do-auto-fill functions
411
412 (or (fboundp 'tpu-old-newline)
413     (fset 'tpu-old-newline (symbol-function 'newline)))
414 (or (fboundp 'tpu-old-do-auto-fill)
415     (fset 'tpu-old-do-auto-fill (symbol-function 'do-auto-fill)))
416 (or (fboundp 'tpu-old-newline-and-indent)
417     (fset 'tpu-old-newline-and-indent (symbol-function 'newline-and-indent)))
418
419 (defun newline (&optional num)
420   "Insert a newline.  With arg, insert that many newlines.
421 In Auto Fill mode, can break the preceding line if no numeric arg.
422 This is the TPU-edt version that respects the bottom scroll margin."
423   (interactive "p")
424   (setq zmacs-region-stays t)
425   (let ((beg (tpu-current-line)))
426     (or num (setq num 1))
427     (tpu-old-newline num)
428     (tpu-bottom-check beg num)))
429
430 (defun newline-and-indent nil
431   "Insert a newline, then indent according to major mode.
432 Indentation is done using the current indent-line-function.
433 In programming language modes, this is the same as TAB.
434 In some text modes, where TAB inserts a tab, this indents
435 to the specified left-margin column.  This is the TPU-edt
436 version that respects the bottom scroll margin."
437   (interactive)
438   (setq zmacs-region-stays t)
439   (let ((beg (tpu-current-line)))
440     (tpu-old-newline-and-indent)
441     (tpu-bottom-check beg 1)))
442
443 (defun do-auto-fill nil
444   "TPU-edt version that respects the bottom scroll margin."
445   (let ((beg (tpu-current-line)))
446     (tpu-old-do-auto-fill)
447     (tpu-bottom-check beg 1)))
448
449
450 ;;;  Function to set scroll margins
451
452 ;;;###autoload
453 (defun tpu-set-scroll-margins (top bottom)
454   "Set scroll margins."
455   (interactive
456    "sEnter top scroll margin (N lines or N%% or RETURN for current value): \
457 \nsEnter bottom scroll margin (N lines or N%% or RETURN for current value): ")
458   (setq zmacs-region-stays t)
459   ;; set top scroll margin
460   (or (string= top "")
461       (if (string= "%" (substring top -1))
462           (setq tpu-top-scroll-margin (string-to-int top))
463         (setq tpu-top-scroll-margin
464               (/ (1- (+ (* (string-to-int top) 100) (window-height)))
465                  (window-height)))))
466   ;; set bottom scroll margin
467   (or (string= bottom "")
468       (if (string= "%" (substring bottom -1))
469           (setq tpu-bottom-scroll-margin (string-to-int bottom))
470         (setq tpu-bottom-scroll-margin
471               (/ (1- (+ (* (string-to-int bottom) 100) (window-height)))
472                  (window-height)))))
473   ;; report scroll margin settings if running interactively
474   (and (interactive-p)
475        (message "Scroll margins set.  Top = %s%%, Bottom = %s%%"
476                 tpu-top-scroll-margin tpu-bottom-scroll-margin)))
477
478
479 ;;;  Functions to set cursor bound or free
480
481 ;;;###autoload
482 (defun tpu-set-cursor-free nil
483   "Allow the cursor to move freely about the screen."
484   (interactive)
485   (setq zmacs-region-stays t)
486   (setq tpu-cursor-free t)
487   (substitute-key-definition 'tpu-set-cursor-free
488                              'tpu-set-cursor-bound
489                              GOLD-map)
490   (message "The cursor will now move freely about the screen."))
491
492 ;;;###autoload
493 (defun tpu-set-cursor-bound nil
494   "Constrain the cursor to the flow of the text."
495   (interactive)
496   (setq zmacs-region-stays t)
497   (picture-clean)
498   (setq tpu-cursor-free nil)
499   (substitute-key-definition 'tpu-set-cursor-bound
500                              'tpu-set-cursor-free
501                              GOLD-map)
502   (message "The cursor is now bound to the flow of your text."))
503
504 ;;; tpu-extras.el ends here