changeset 4790:b110036d90b0

Version 2.3. Documentation: `forms-forms-scroll' and `forms-forms-jump' now default to nil. `forms-new-record-filter' and `forms-modified-record-filter' cannot be redefined as functions. Commands and keymaps are changed. Add function key defs. (forms-version): Docstring includes full RCS id. (forms-forms-scroll): Defaults to nil. (forms-forms-jump): Defaults to nil. (forms-mode-edit-map, forms-mode-ro-map): Additional keymaps for edit mode and read-only mode. (forms--new-record-filter, forms--modified-record-filter): Deleted. (forms-mode): Docstring now includes the key bindings, since both edit mode and read-only mode must be supported. Changed `forms-new-record-filter' and `forms-modified-record-filter' semantics: the variable must point to a function and may not be defined as a function anymore. Use three keymaps: `forms-mode-map' (C-c commands), `forms-mode-edit-map' (normal mode) and `forms-mode-ro-map' (read-only mode). The maps are not buffer local. Changed the text of error messages to be more descriptive, and onsistent with the documentation. Moved setting up write-file-hooks and revert-buffer-function to function `forms--change-commands'. (forms--process-format-list): Changed error messages to be more descriptive. (forms--set-keymaps): Setup the three keymaps. (forms--mode-commands): Use new command key bindings. (forms--mode-commands1): New helper function for `forms--mode-commands'. (forms--change-commands): Handle setup of local-write-file-hooks and revert-buffer-function. (forms--help): Show new command bindings. (forms--show-record): Replaced `forms--modified-record-filter' by `forms-modified-record-filter'. (forms-jump-record): Changed error message. (forms-toggle-read-only): New function, replaces `forms-view-mode' and `forms-edit-mode'. (forms-view-mode, forms-edit-mode): Deleted. (forms-insert-record): Replaced `forms--new-record-filter' by `forms-new-record-filter'. (forms-insert-record, forms-delete-record): Disallow in read-only mode. (forms-prev-field): New function.
author Richard M. Stallman <rms@gnu.org>
date Mon, 27 Sep 1993 02:19:46 +0000
parents 86318d7728f5
children 3544f3a41ae6
files lisp/forms.el
diffstat 1 files changed, 266 insertions(+), 172 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/forms.el	Mon Sep 27 01:36:27 1993 +0000
+++ b/lisp/forms.el	Mon Sep 27 02:19:46 1993 +0000
@@ -2,7 +2,7 @@
 ;;; Copyright (C) 1991, 1993 Free Software Foundation, Inc.
 
 ;; Author: Johan Vromans <jv@mh.nl>
-;; Version: 2.2
+;; Version: $Revision: 2.3 $
 
 ;; This file is part of GNU Emacs.
 
@@ -113,32 +113,28 @@
 ;;;			to newlines.  Upon storage they are translated
 ;;;			back to the separator character.
 ;;;
-;;;	forms-forms-scroll			[bool, default t]
+;;;	forms-forms-scroll			[bool, default nil]
 ;;;			Non-nil means: rebind locally the commands that
 ;;;			perform `scroll-up' or `scroll-down' to use
 ;;;			`forms-next-field' resp. `forms-prev-field'.
 ;;;
-;;;	forms-forms-jump			[bool, default t]
+;;;	forms-forms-jump			[bool, default nil]
 ;;;			Non-nil means: rebind locally the commands that
 ;;;			perform `beginning-of-buffer' or `end-of-buffer'
 ;;;			to perform `forms-first-field' resp. `forms-last-field'.
 ;;;
-;;;	forms-new-record-filter			[symbol, no default]
-;;;			If defined: this should be the name of a 
+;;;	forms-new-record-filter			[symbol, default nil]
+;;;			If not nil: this should be the name of a 
 ;;;			function that is called when a new
 ;;;			record is created.  It can be used to fill in
 ;;;			the new record with default fields, for example.
-;;;			Instead of the name of the function, it may
-;;;			be the function itself.
 ;;;
-;;;	forms-modified-record-filter		[symbol, no default]
-;;;			If defined: this should be the name of a 
+;;;	forms-modified-record-filter		[symbol, default nil]
+;;;			If not nil: this should be the name of a 
 ;;;			function that is called when a record has
 ;;;			been modified.  It is called after the fields
 ;;;			are parsed.  It can be used to register
 ;;;			modification dates, for example.
-;;;			Instead of the name of the function, it may
-;;;			be the function itself.
 ;;;
 ;;;	forms-use-text-properties		[bool, see text for default]
 ;;;			This variable controls if forms mode should use
@@ -206,39 +202,56 @@
 ;;; file (using forms-last-record) will adjust forms--total-records if
 ;;; needed.
 ;;;
-;;; Commands and keymaps:
-;;;
-;;; A local keymap `forms-mode-map' is used in the forms buffer.
-;;; If the forms is in view mode, this keymap is used so all forms mode
-;;; functions are accessible.
-;;; If the forms is in edit mode, this map can be accessed with C-c prefix.
-;;;
-;;; Default bindings:
+;;; The forms buffer can be in on eof two modes: edit mode or view
+;;; mode.  View mode is a read-only mode, you cannot modify the
+;;; contents of the buffer.
 ;;;
-;;;	\C-c	forms-mode-map
-;;;	TAB	forms-next-field
-;;;	SPC 	forms-next-record
-;;;	<	forms-first-record
-;;;	>	forms-last-record
-;;;	?	describe-mode
-;;;	d	forms-delete-record
-;;;	e	forms-edit-mode
-;;;	i	forms-insert-record
-;;;	j	forms-jump-record
-;;;	n	forms-next-record
-;;;	p	forms-prev-record
-;;;	q	forms-exit
-;;;	s	forms-search
-;;;	v	forms-view-mode
-;;;	x	forms-exit-no-save
-;;;	DEL	forms-prev-record
+;;; Edit mode commands:
+;;; 
+;;; TAB		 forms-next-field
+;;; \C-c TAB	 forms-next-field
+;;; \C-c <	 forms-first-record
+;;; \C-c >	 forms-last-record
+;;; \C-c ?	 describe-mode
+;;; \C-c \C-k	 forms-delete-record
+;;; \C-c \C-q	 forms-toggle-read-only
+;;; \C-c \C-o	 forms-insert-record
+;;; \C-c \C-l	 forms-jump-record
+;;; \C-c \C-n	 forms-next-record
+;;; \C-c \C-p	 forms-prev-record
+;;; \C-c \C-s	 forms-search
+;;; \C-c \C-x	 forms-exit
+;;; 
+;;; Read-only mode commands:
+;;; 
+;;; SPC 	 forms-next-record
+;;; DEL	 forms-prev-record
+;;; ?	 describe-mode
+;;; \C-q forms-toggle-read-only
+;;; l	 forms-jump-record
+;;; n	 forms-next-record
+;;; p	 forms-prev-record
+;;; s	 forms-search
+;;; x	 forms-exit
+;;; 
+;;; Of course, it is also possible to use the \C-c prefix to obtain the
+;;; same command keys as in edit mode.
+;;; 
+;;; The following bindings are available, independent of the mode: 
+;;; 
+;;; [next]	  forms-next-record
+;;; [prior]	  forms-prev-record
+;;; [begin]	  forms-first-record
+;;; [end]	  forms-last-record
+;;; [S-TAB]	  forms-prev-field
+;;; [backtab] forms-prev-field
 ;;;
 ;;; For convenience, TAB is always bound to `forms-next-field', so you
 ;;; don't need the C-c prefix for this command.
 ;;;
 ;;; As mentioned above (see `forms-forms-scroll' and `forms-forms-jump')
 ;;; the bindings of standard functions `scroll-up', `scroll-down',
-;;; `beginning-of-buffer' and `end-of-buffer' are locally replaced with
+;;; `beginning-of-buffer' and `end-of-buffer' can be locally replaced with
 ;;; forms mode functions next/prev record and first/last
 ;;; record.
 ;;;
@@ -253,8 +266,10 @@
 (provide 'forms)			;;; official
 (provide 'forms-mode)			;;; for compatibility
 
-(defconst forms-version "2.2"
-  "Version of forms-mode implementation.")
+(defconst forms-version (substring "$Revision: 2.3 $" 11 -2)
+  "The version number of forms-mode (as string).  The complete RCS id is:
+
+  $Id: forms.el,v 2.3 1993/09/26 14:07:12 jv Exp $")
 
 (defvar forms-mode-hooks nil
   "Hook functions to be run upon entering Forms mode.")
@@ -282,11 +297,11 @@
 (defvar forms-multi-line "\C-k"
   "If not nil: use this character to separate multi-line fields (default C-k).")
 
-(defvar forms-forms-scroll t
+(defvar forms-forms-scroll nil
   "*Non-nil means replace scroll-up/down commands in Forms mode.
 The replacement commands performs forms-next/prev-record.")
 
-(defvar forms-forms-jump t
+(defvar forms-forms-jump nil
   "*Non-nil means redefine beginning/end-of-buffer in Forms mode.
 The replacement commands performs forms-first/last-record.")
 
@@ -322,8 +337,12 @@
 (defvar forms--current-record 0
   "Number of the record currently on the screen.")
 
-(defvar forms-mode-map nil		; yes - this one is global
+(defvar forms-mode-map nil
    "Keymap for form buffer.")
+(defvar forms-mode-ro-map nil
+   "Keymap for form buffer in view mode.")
+(defvar forms-mode-edit-map nil
+   "Keymap for form buffer in edit mode.")
 
 (defvar forms--markers nil
   "Field markers in the screen.")
@@ -347,12 +366,6 @@
   "To keep track of forms-mode being set-up.")
 (make-variable-buffer-local 'forms--mode-setup)
 
-(defvar forms--new-record-filter nil
-  "Set if a new record filter has been defined.")
-
-(defvar forms--modified-record-filter nil
-  "Set if a modified record filter has been defined.")
-
 (defvar forms--dynamic-text nil
   "Array that holds dynamic texts to insert between fields.")
 
@@ -369,10 +382,22 @@
 (defun forms-mode (&optional primary)
   "Major mode to visit files in a field-structured manner using a form.
 
-Commands (prefix with C-c if not in read-only mode):
-\\{forms-mode-map}"
-
-  (interactive)				; no - 'primary' is not prefix arg
+Commands:                        Equivalent keys in read-only mode:
+ TAB            forms-next-field          TAB
+ \\C-c TAB       forms-next-field          
+ \\C-c <         forms-first-record         <
+ \\C-c >         forms-last-record          >
+ \\C-c ?         describe-mode              ?
+ \\C-c \\C-k      forms-delete-record
+ \\C-c \\C-q      forms-toggle-read-only     q
+ \\C-c \\C-o      forms-insert-record
+ \\C-c \\C-l      forms-jump-record          l
+ \\C-c \\C-n      forms-next-record          n
+ \\C-c \\C-p      forms-prev-record          p
+ \\C-c \\C-s      forms-search               s
+ \\C-c \\C-x      forms-exit                 x
+"
+  (interactive)
 
   ;; This is not a simple major mode, as usual.  Therefore, forms-mode
   ;; takes an optional argument `primary' which is used for the
@@ -403,12 +428,12 @@
 	(make-local-variable 'forms-forms-scroll)
 	(make-local-variable 'forms-forms-jump)
 	(make-local-variable 'forms-use-text-properties)
-	(make-local-variable 'forms--new-record-filter)
-	(make-local-variable 'forms--modified-record-filter)
+	(make-local-variable 'forms-new-record-filter)
+	(make-local-variable 'forms-modified-record-filter)
 
 	;; Make sure no filters exist.
-	(fmakunbound 'forms-new-record-filter)
-	(fmakunbound 'forms-modified-record-filter)
+	(setq forms-new-record-filter nil)
+	(setq forms-modified-record-filter nil)
 
 	;; If running Emacs 19 under X, setup faces to show read-only and 
 	;; read-write fields.
@@ -423,19 +448,26 @@
 
 	;; check if the mandatory variables make sense.
 	(or forms-file
-	    (error "'forms-file' has not been set"))
+	    (error (concat "Forms control file error: " 
+			   "'forms-file' has not been set")))
 	(or forms-number-of-fields
-	    (error "'forms-number-of-fields' has not been set"))
-	(or (> forms-number-of-fields 0)
-	    (error "'forms-number-of-fields' must be > 0")
-	(or (stringp forms-field-sep))
-	    (error "'forms-field-sep' is not a string"))
+	    (error (concat "Forms control file error: "
+			   "'forms-number-of-fields' has not been set")))
+	(or (and (numberp forms-number-of-fields)
+		 (> forms-number-of-fields 0))
+	    (error (concat "Forms control file error: "
+			   "'forms-number-of-fields' must be a number > 0")))
+	(or (stringp forms-field-sep)
+	    (error (concat "Forms control file error: "
+			   "'forms-field-sep' is not a string")))
 	(if forms-multi-line
 	    (if (and (stringp forms-multi-line)
 		     (eq (length forms-multi-line) 1))
 		(if (string= forms-multi-line forms-field-sep)
-		    (error "'forms-multi-line' is equal to 'forms-field-sep'"))
-	      (error "'forms-multi-line' must be nil or a one-character string")))
+		    (error (concat "Forms control file error: " 
+				   "'forms-multi-line' is equal to 'forms-field-sep'")))
+	      (error (concat "Forms control file error: "
+			     "'forms-multi-line' must be nil or a one-character string"))))
 	(or (fboundp 'set-text-properties)
 	    (setq forms-use-text-properties nil))
 	    
@@ -456,22 +488,15 @@
 	;;(message "forms: building parser... done.")
 
 	;; Check if record filters are defined.
-	(setq forms--new-record-filter 
-	      (cond
-	       ((fboundp 'forms-new-record-filter)
-		(symbol-function 'forms-new-record-filter))
-	       ((and (boundp 'forms-new-record-filter)
-		     (fboundp forms-new-record-filter))
-		forms-new-record-filter)))
-	(fmakunbound 'forms-new-record-filter)
-	(setq forms--modified-record-filter 
-	      (cond
-	       ((fboundp 'forms-modified-record-filter)
-		(symbol-function 'forms-modified-record-filter))
-	       ((and (boundp 'forms-modified-record-filter)
-		     (fboundp forms-modified-record-filter))
-		forms-modified-record-filter)))
-	(fmakunbound 'forms-modified-record-filter)
+	(if (and forms-new-record-filter
+		 (not (fboundp forms-new-record-filter)))
+	    (error (concat "Forms control file error: "
+			   "'forms-new-record-filter' is not a function")))
+
+	(if (and forms-modified-record-filter
+		 (not (fboundp forms-modified-record-filter)))
+	    (error (concat "Forms control file error: "
+			   "'forms-modified-record-filter' is not a function")))
 
 	;; The filters acces the contents of the forms using `forms-fields'.
 	(make-local-variable 'forms-fields)
@@ -509,15 +534,14 @@
   (make-local-variable 'forms--the-record-list)
   (make-local-variable 'forms--search-regexp)
 
-  ;; A bug in the current Emacs release prevents a keymap
-  ;; which is buffer-local from being used by 'describe-mode'.
-  ;; Hence we'll leave it global.
-  ;;(make-local-variable 'forms-mode-map)
+  ; The keymaps are global, so multiple forms mode buffers can share them.
+  ;(make-local-variable 'forms-mode-map)
+  ;(make-local-variable 'forms-mode-ro-map)
+  ;(make-local-variable 'forms-mode-edit-map)
   (if forms-mode-map			; already defined
       nil
     ;;(message "forms: building keymap...")
-    (setq forms-mode-map (make-keymap))
-    (forms--mode-commands forms-mode-map)
+    (forms--mode-commands)
     ;;(message "forms: building keymap... done.")
     )
 
@@ -549,17 +573,12 @@
   (forms--set-minor-mode)
   ;;(message "forms: proceeding setup (keymaps)...")
   (forms--set-keymaps)
-  (make-local-variable 'local-write-file-hooks)
   ;;(message "forms: proceeding setup (commands)...")
   (forms--change-commands)
 
   ;;(message "forms: proceeding setup (buffer)...")
   (set-buffer-modified-p nil)
 
-  ;; We have our own revert function - use it
-  (make-local-variable 'revert-buffer-function)
-  (setq revert-buffer-function 'forms-revert-buffer)
-
   ;; setup the first (or current) record to show
   (if (< forms--current-record 1)
       (setq forms--current-record 1))
@@ -590,10 +609,12 @@
 
   ;; Verify that `forms-format-list' is not nil.
   (or forms-format-list
-      (error "'forms-format-list' has not been set"))
+      (error (concat "Forms control file error: "
+		     "'forms-format-list' has not been set")))
   ;; It must be a list.
   (or (listp forms-format-list)
-      (error "'forms-format-list' is not a list"))
+      (error (concat "Forms control file error: "
+		     "'forms-format-list' is not a list")))
 
   ;; Assume every field is painted once.
   ;; `forms--elements' will grow if needed.
@@ -633,9 +654,9 @@
 	  ;; Validate range.
 	  (if (or (<= el 0)
 		  (> el forms-number-of-fields))
-	      (error
-	       "Forms error: field number %d out of range 1..%d"
-	       el forms-number-of-fields))
+	      (error (concat "Forms format error: "
+			     "field number %d out of range 1..%d")
+		     el forms-number-of-fields))
 
 	  ;; Store forms order.
 	  (if (> field-num (length forms--elements))
@@ -653,9 +674,9 @@
 
 	  ;; Validate.
 	  (or (fboundp (car-safe el))
-	      (error 
-	       "Forms error: not a function: %s"
-	       (prin1-to-string (car-safe el))))
+	      (error (concat "Forms format error: "
+			     "not a function "
+			     (prin1-to-string (car-safe el)))))
 
 	  ;; Shift.
 	  (if prev-item
@@ -665,8 +686,9 @@
 
 	 ;; else
 	 (t
-	  (error "Forms error: invalid element %s"
-		 (prin1-to-string el))))
+	  (error (concat "Forms format error: "
+			 "invalid element "
+			 (prin1-to-string el)))))
 
 	;; Advance to next element of the list.
 	(setq the-list rem)))
@@ -1058,36 +1080,62 @@
 (defun forms--set-keymaps ()
   "Set the keymaps used in this mode."
 
-  (if forms-read-only
-      (use-local-map forms-mode-map)
-    (use-local-map (make-sparse-keymap))
-    (define-key (current-local-map) "\C-c" forms-mode-map)
-    (define-key (current-local-map) "\t"   'forms-next-field)))
+  (use-local-map (if forms-read-only 
+		     forms-mode-ro-map 
+		   forms-mode-edit-map)))
+
+(defun forms--mode-commands ()
+  "Fill the Forms mode keymaps."
 
-(defun forms--mode-commands (map)
-  "Fill map with all Forms mode commands."
+  ;; `forms-mode-map' is always accessible via \C-c prefix.
+  (setq forms-mode-map (make-keymap))
+  (define-key forms-mode-map "\t" 'forms-next-field)
+  (define-key forms-mode-map "\C-k" 'forms-delete-record)
+  (define-key forms-mode-map "\C-q" 'forms-toggle-read-only)
+  (define-key forms-mode-map "\C-o" 'forms-insert-record)
+  (define-key forms-mode-map "\C-l" 'forms-jump-record)
+  (define-key forms-mode-map "\C-n" 'forms-next-record)
+  (define-key forms-mode-map "\C-p" 'forms-prev-record)
+  (define-key forms-mode-map "\C-s" 'forms-search)
+  (define-key forms-mode-map "\C-x" 'forms-exit)
+  (define-key forms-mode-map "<" 'forms-first-record)
+  (define-key forms-mode-map ">" 'forms-last-record)
+  (define-key forms-mode-map "?" 'describe-mode)
+  (define-key forms-mode-map "\C-?" 'forms-prev-record)
 
-  (define-key map "\t" 'forms-next-field)
-  (define-key map " " 'forms-next-record)
-  (define-key map "d" 'forms-delete-record)
-  (define-key map "e" 'forms-edit-mode)
-  (define-key map "i" 'forms-insert-record)
-  (define-key map "j" 'forms-jump-record)
-  (define-key map "n" 'forms-next-record)
-  (define-key map "p" 'forms-prev-record)
-  (define-key map "q" 'forms-exit)
-  (define-key map "s" 'forms-search)
-  (define-key map "v" 'forms-view-mode)
-  (define-key map "x" 'forms-exit-no-save)
-  (define-key map "<" 'forms-first-record)
-  (define-key map ">" 'forms-last-record)
-  (define-key map "?" 'describe-mode)
-  (define-key map "\177" 'forms-prev-record)
-  ;(define-key map "\C-c" map)
-  ;(define-key map "\e" 'ESC-prefix)
-  ;(define-key map "\C-x" ctl-x-map)
-  ;(define-key map "\C-u" 'universal-argument)
-  ;(define-key map "\C-h" help-map)
+  ;; `forms-mode-ro-map' replaces the local map when in read-only mode.
+  (setq forms-mode-ro-map (make-keymap))
+  (suppress-keymap forms-mode-ro-map)
+  (define-key forms-mode-ro-map "\C-c" forms-mode-map)
+  (define-key forms-mode-ro-map "\t" 'forms-next-field)
+  (define-key forms-mode-ro-map "q" 'forms-toggle-read-only)
+  (define-key forms-mode-ro-map "l" 'forms-jump-record)
+  (define-key forms-mode-ro-map "n" 'forms-next-record)
+  (define-key forms-mode-ro-map "p" 'forms-prev-record)
+  (define-key forms-mode-ro-map "s" 'forms-search)
+  (define-key forms-mode-ro-map "x" 'forms-exit)
+  (define-key forms-mode-ro-map "<" 'forms-first-record)
+  (define-key forms-mode-ro-map ">" 'forms-last-record)
+  (define-key forms-mode-ro-map "?" 'describe-mode)
+  (define-key forms-mode-ro-map " " 'forms-next-record)
+  (forms--mode-commands1 forms-mode-ro-map)
+
+  ;; This is the normal, local map.
+  (setq forms-mode-edit-map (make-keymap))
+  (define-key forms-mode-edit-map "\t"   'forms-next-field)
+  (define-key forms-mode-edit-map "\C-c" forms-mode-map)
+  (forms--mode-commands1 forms-mode-edit-map)
+  )
+
+(defun forms--mode-commands1 (map)
+  "Helper routine to define keys."
+  (define-key map [TAB] 'forms-next-field)
+  (define-key map [S-tab] 'forms-prev-field)
+  (define-key map [next] 'forms-next-record)
+  (define-key map [prior] 'forms-prev-record)
+  (define-key map [begin] 'forms-first-record)
+  (define-key map [last] 'forms-last-record)
+  (define-key map [backtab] 'forms-prev-field)
   )
 
 ;;; Changed functions
@@ -1118,6 +1166,7 @@
 				   (current-global-map))))
   ;;
   ;; save-buffer -> forms--save-buffer
+  (make-local-variable 'local-write-file-hooks)
   (add-hook 'local-write-file-hooks
 	    (function
 	     (lambda (nil)
@@ -1125,22 +1174,27 @@
 	       (save-excursion
 		 (set-buffer forms--file-buffer)
 		 (save-buffer))
-	       t))))
+	       t)))
+  ;; We have our own revert function - use it
+  (make-local-variable 'revert-buffer-function)
+  (setq revert-buffer-function 'forms-revert-buffer)
+
+  t)
 
 (defun forms--help ()
   "Initial help for Forms mode."
   ;; We should use
-  ;;(message (substitute-command-keys (concat
-  ;;"\\[forms-next-record]:next"
-  ;;"   \\[forms-prev-record]:prev"
-  ;;"   \\[forms-first-record]:first"
-  ;;"   \\[forms-last-record]:last"
-  ;;"   \\[describe-mode]:help"
-  ;;"   \\[forms-exit]:exit")))
-  ;; but it's too slow ....
-  (if forms-read-only
-      (message "SPC:next   DEL:prev   <:first   >:last   ?:help   q:exit")
-    (message "C-c n:next   C-c p:prev   C-c <:first   C-c >:last   C-c ?:help   C-c q:exit")))
+  (message (substitute-command-keys (concat
+  "\\[forms-next-record]:next"
+  "   \\[forms-prev-record]:prev"
+  "   \\[forms-first-record]:first"
+  "   \\[forms-last-record]:last"
+  "   \\[describe-mode]:help"))))
+  ; but it's too slow ....
+;  (if forms-read-only
+;      (message "SPC:next   DEL:prev   <:first   >:last   ?:help   q:exit")
+;    (message "C-c n:next   C-c p:prev   C-c <:first   C-c >:last   C-c ?:help   C-c q:exit"))
+;  )
 
 (defun forms--trans (subj arg rep)
   "Translate in SUBJ all chars ARG into char REP.  ARG and REP should
@@ -1217,7 +1271,7 @@
   (if (= (length forms--the-record-list) forms-number-of-fields)
       nil
     (beep)
-    (message "Record has %d fields instead of %d."
+    (message "Warning: this record has %d fields instead of %d"
 	     (length forms--the-record-list) forms-number-of-fields)
     (if (< (length forms--the-record-list) forms-number-of-fields)
 	(setq forms--the-record-list 
@@ -1256,11 +1310,11 @@
     (let ((forms--dynamic-text forms--dynamic-text))
       (funcall forms--parser))
 
-    (if forms--modified-record-filter
+    (if forms-modified-record-filter
 	;; As a service to the user, we add a zeroth element so she
 	;; can use the same indices as in the forms definition.
 	(let ((the-fields (vconcat [nil] forms--recordv)))
-	  (setq the-fields (funcall forms--modified-record-filter the-fields))
+	  (setq the-fields (funcall forms-modified-record-filter the-fields))
 	  (cdr (append the-fields nil)))
 
       ;; Transform to a list and return.
@@ -1392,7 +1446,7 @@
 	  (progn
 	    (setq forms--current-record cur)
 	    (beep)
-	    (message "Stuck at record %d." cur))))))
+	    (message "Stuck at record %d" cur))))))
 
 (defun forms-first-record ()
   "Jump to first record."
@@ -1412,34 +1466,43 @@
 	nil
       (beep)
       (setq forms--total-records numrec)
-      (message "Number of records reset to %d." forms--total-records)))
+      (message "Warning: number of records changed to %d" forms--total-records)))
   (forms-jump-record forms--total-records))
 
 ;;; Other commands
 
-(defun forms-view-mode ()
-  "Visit buffer read-only."
-  (interactive)
-  (if forms-read-only
-      nil
-    (forms--checkmod)			; sync
-    (setq forms-read-only t)
-    (forms-mode)))
+(defun forms-toggle-read-only (arg)
+  "Toggles read-only mode of a forms mode buffer.
+With an argument, enables read-only mode if the argument is positive.
+Otherwise enables edit mode if the visited file is writeable."
+
+  (interactive "P")
+
+  (if (if arg
+	  ;; Negative arg means switch it off.
+	  (<= (prefix-numeric-value arg) 0)
+	;; No arg means toggle.
+	forms-read-only)
 
-(defun forms-edit-mode ()
-  "Make form suitable for editing, if possible."
-  (interactive)
-  (let ((ro forms-read-only))
-    (if (save-excursion
-	  (set-buffer forms--file-buffer)
-	  buffer-read-only)
-	(progn
-	  (setq forms-read-only t)
-	  (message "No write access to \"%s\"" forms-file)
-	  (beep))
-      (setq forms-read-only nil))
-    (if (equal ro forms-read-only)
+      ;; Enable edit mode, if possible.
+      (let ((ro forms-read-only))
+	(if (save-excursion
+	      (set-buffer forms--file-buffer)
+	      buffer-read-only)
+	    (progn
+	      (setq forms-read-only t)
+	      (message "No write access to \"%s\"" forms-file)
+	      (beep))
+	  (setq forms-read-only nil))
+	(if (equal ro forms-read-only)
+	    nil
+	  (forms-mode)))
+
+    ;; Enable view mode.
+    (if forms-read-only
 	nil
+      (forms--checkmod)			; sync
+      (setq forms-read-only t)
       (forms-mode))))
 
 ;; Sample:
@@ -1453,22 +1516,23 @@
 (defun forms-insert-record (arg)
   "Create a new record before the current one.
 With ARG: store the record after the current one.
-If a function `forms-new-record-filter' is defined, or 
-`forms-new-record-filter' contains the name of a function, 
+If `forms-new-record-filter' contains the name of a function, 
 it is called to fill (some of) the fields with default values."
- ; The above doc is not true, but for documentary purposes only
 
   (interactive "P")
 
+  (if forms-read-only
+      (error ""))
+
   (let ((ln (if arg (1+ forms--current-record) forms--current-record))
         the-list the-record)
 
     (forms--checkmod)
-    (if forms--new-record-filter
+    (if forms-new-record-filter
 	;; As a service to the user, we add a zeroth element so she
 	;; can use the same indices as in the forms definition.
 	(let ((the-fields (make-vector (1+ forms-number-of-fields) "")))
-	  (setq the-fields (funcall forms--new-record-filter the-fields))
+	  (setq the-fields (funcall forms-new-record-filter the-fields))
 	  (setq the-list (cdr (append the-fields nil))))
       (setq the-list (make-list forms-number-of-fields "")))
 
@@ -1493,6 +1557,10 @@
 (defun forms-delete-record (arg)
   "Deletes a record.  With a prefix argument: don't ask."
   (interactive "P")
+
+  (if forms-read-only
+      (error ""))
+
   (forms--checkmod)
   (if (or arg
 	  (y-or-n-p "Really delete this record? "))
@@ -1577,6 +1645,31 @@
 	nil
       (goto-char (aref forms--markers 0)))))
 
+(defun forms-prev-field (arg)
+  "Jump to ARG-th previous field."
+  (interactive "p")
+
+  (let ((i (length forms--markers))
+	(here (point))
+	there
+	(cnt 0))
+
+    (if (zerop arg)
+	(setq cnt 1)
+      (setq cnt (+ cnt arg)))
+
+    (if (catch 'done
+	  (while (> i 0)
+	    (setq i ( 1- i))
+	    (if (or (null (setq there (aref forms--markers i)))
+		    (>= there here))
+		nil
+	      (if (<= (setq cnt (1- cnt)) 0)
+		  (progn
+		    (goto-char there)
+		    (throw 'done t))))))
+	nil
+      (goto-char (aref forms--markers (1- (length forms--markers)))))))
 ;;;
 ;;; Special service
 ;;;
@@ -1627,3 +1720,4 @@
 	  (insert ret)))))
 
 ;;; forms.el ends here.
+