]> code.delx.au - gnu-emacs/blob - lisp/forms.el
Withdraw mouse-major-mode-map modifications.
[gnu-emacs] / lisp / forms.el
1 ;;; forms.el -- Forms mode: edit a file as a form to fill in.
2 ;;; Copyright (C) 1991, 1994, 1995 Free Software Foundation, Inc.
3
4 ;; Author: Johan Vromans <jv@nl.net>
5
6 ;; This file is part of GNU Emacs.
7
8 ;; GNU Emacs is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 2, or (at your option)
11 ;; any later version.
12
13 ;; GNU Emacs is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
17
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with GNU Emacs; see the file COPYING. If not, write to
20 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 ;;; Commentary:
23
24 ;;; Visit a file using a form.
25 ;;;
26 ;;; === Naming conventions
27 ;;;
28 ;;; The names of all variables and functions start with 'forms-'.
29 ;;; Names which start with 'forms--' are intended for internal use, and
30 ;;; should *NOT* be used from the outside.
31 ;;;
32 ;;; All variables are buffer-local, to enable multiple forms visits
33 ;;; simultaneously.
34 ;;; Variable `forms--mode-setup' is local to *ALL* buffers, for it
35 ;;; controls if forms-mode has been enabled in a buffer.
36 ;;;
37 ;;; === How it works ===
38 ;;;
39 ;;; Forms mode means visiting a data file which is supposed to consist
40 ;;; of records each containing a number of fields. The records are
41 ;;; separated by a newline, the fields are separated by a user-defined
42 ;;; field separater (default: TAB).
43 ;;; When shown, a record is transferred to an Emacs buffer and
44 ;;; presented using a user-defined form. One record is shown at a
45 ;;; time.
46 ;;;
47 ;;; Forms mode is a composite mode. It involves two files, and two
48 ;;; buffers.
49 ;;; The first file, called the control file, defines the name of the
50 ;;; data file and the forms format. This file buffer will be used to
51 ;;; present the forms.
52 ;;; The second file holds the actual data. The buffer of this file
53 ;;; will be buried, for it is never accessed directly.
54 ;;;
55 ;;; Forms mode is invoked using M-x forms-find-file control-file .
56 ;;; Alternativily `forms-find-file-other-window' can be used.
57 ;;;
58 ;;; You may also visit the control file, and switch to forms mode by hand
59 ;;; with M-x forms-mode .
60 ;;;
61 ;;; Automatic mode switching is supported if you specify
62 ;;; "-*- forms -*-" in the first line of the control file.
63 ;;;
64 ;;; The control file is visited, evaluated using `eval-current-buffer',
65 ;;; and should set at least the following variables:
66 ;;;
67 ;;; forms-file [string]
68 ;;; The name of the data file.
69 ;;;
70 ;;; forms-number-of-fields [integer]
71 ;;; The number of fields in each record.
72 ;;;
73 ;;; forms-format-list [list]
74 ;;; Formatting instructions.
75 ;;;
76 ;;; `forms-format-list' should be a list, each element containing
77 ;;;
78 ;;; - a string, e.g. "hello". The string is inserted in the forms
79 ;;; "as is".
80 ;;;
81 ;;; - an integer, denoting a field number.
82 ;;; The contents of this field are inserted at this point.
83 ;;; Fields are numbered starting with number one.
84 ;;;
85 ;;; - a function call, e.g. (insert "text").
86 ;;; This function call is dynamically evaluated and should return a
87 ;;; string. It should *NOT* have side-effects on the forms being
88 ;;; constructed. The current fields are available to the function
89 ;;; in the variable `forms-fields', they should *NOT* be modified.
90 ;;;
91 ;;; - a lisp symbol, that must evaluate to one of the above.
92 ;;;
93 ;;; Optional variables which may be set in the control file:
94 ;;;
95 ;;; forms-field-sep [string, default TAB]
96 ;;; The field separator used to separate the
97 ;;; fields in the data file. It may be a string.
98 ;;;
99 ;;; forms-read-only [bool, default nil]
100 ;;; Non-nil means that the data file is visited
101 ;;; read-only (view mode) as opposed to edit mode.
102 ;;; If no write access to the data file is
103 ;;; possible, view mode is enforced.
104 ;;;
105 ;;; forms-multi-line [string, default "^K"]
106 ;;; If non-null the records of the data file may
107 ;;; contain fields that can span multiple lines in
108 ;;; the form.
109 ;;; This variable denotes the separator character
110 ;;; to be used for this purpose. Upon display, all
111 ;;; occurrencies of this character are translated
112 ;;; to newlines. Upon storage they are translated
113 ;;; back to the separator character.
114 ;;;
115 ;;; forms-forms-scroll [bool, default nil]
116 ;;; Non-nil means: rebind locally the commands that
117 ;;; perform `scroll-up' or `scroll-down' to use
118 ;;; `forms-next-field' resp. `forms-prev-field'.
119 ;;;
120 ;;; forms-forms-jump [bool, default nil]
121 ;;; Non-nil means: rebind locally the commands that
122 ;;; perform `beginning-of-buffer' or `end-of-buffer'
123 ;;; to perform `forms-first-field' resp. `forms-last-field'.
124 ;;;
125 ;;; forms-read-file-filter [symbol, default nil]
126 ;;; If not nil: this should be the name of a
127 ;;; function that is called after the forms data file
128 ;;; has been read. It can be used to transform
129 ;;; the contents of the file into a format more suitable
130 ;;; for forms-mode processing.
131 ;;;
132 ;;; forms-write-file-filter [symbol, default nil]
133 ;;; If not nil: this should be the name of a
134 ;;; function that is called before the forms data file
135 ;;; is written (saved) to disk. It can be used to undo
136 ;;; the effects of `forms-read-file-filter', if any.
137 ;;;
138 ;;; forms-new-record-filter [symbol, default nil]
139 ;;; If not nil: this should be the name of a
140 ;;; function that is called when a new
141 ;;; record is created. It can be used to fill in
142 ;;; the new record with default fields, for example.
143 ;;;
144 ;;; forms-modified-record-filter [symbol, default nil]
145 ;;; If not nil: this should be the name of a
146 ;;; function that is called when a record has
147 ;;; been modified. It is called after the fields
148 ;;; are parsed. It can be used to register
149 ;;; modification dates, for example.
150 ;;;
151 ;;; forms-use-text-properties [bool, see text for default]
152 ;;; This variable controls if forms mode should use
153 ;;; text properties to protect the form text from being
154 ;;; modified (using text-property `read-only').
155 ;;; Also, the read-write fields are shown using a
156 ;;; distinct face, if possible.
157 ;;; As of emacs 19.29, the `intangible' text property
158 ;;; is used to prevent moving into read-only fields.
159 ;;; This variable defaults to t if running Emacs 19
160 ;;; with text properties.
161 ;;; The default face to show read-write fields is
162 ;;; copied from face `region'.
163 ;;;
164 ;;; forms-ro-face [symbol, default 'default]
165 ;;; This is the face that is used to show
166 ;;; read-only text on the screen.If used, this
167 ;;; variable should be set to a symbol that is a
168 ;;; valid face.
169 ;;; E.g.
170 ;;; (make-face 'my-face)
171 ;;; (setq forms-ro-face 'my-face)
172 ;;;
173 ;;; forms-rw-face [symbol, default 'region]
174 ;;; This is the face that is used to show
175 ;;; read-write text on the screen.
176 ;;;
177 ;;; After evaluating the control file, its buffer is cleared and used
178 ;;; for further processing.
179 ;;; The data file (as designated by `forms-file') is visited in a buffer
180 ;;; `forms--file-buffer' which will not normally be shown.
181 ;;; Great malfunctioning may be expected if this file/buffer is modified
182 ;;; outside of this package while it is being visited!
183 ;;;
184 ;;; Normal operation is to transfer one line (record) from the data file,
185 ;;; split it into fields (into `forms--the-record-list'), and display it
186 ;;; using the specs in `forms-format-list'.
187 ;;; A format routine `forms--format' is built upon startup to format
188 ;;; the records according to `forms-format-list'.
189 ;;;
190 ;;; When a form is changed the record is updated as soon as this form
191 ;;; is left. The contents of the form are parsed using information
192 ;;; obtained from `forms-format-list', and the fields which are
193 ;;; deduced from the form are modified. Fields not shown on the forms
194 ;;; retain their origional values. The newly formed record then
195 ;;; replaces the contents of the old record in `forms--file-buffer'.
196 ;;; A parse routine `forms--parser' is built upon startup to parse
197 ;;; the records.
198 ;;;
199 ;;; Two exit functions exist: `forms-exit' and `forms-exit-no-save'.
200 ;;; `forms-exit' saves the data to the file, if modified.
201 ;;; `forms-exit-no-save` does not. However, if `forms-exit-no-save'
202 ;;; is executed and the file buffer has been modified, Emacs will ask
203 ;;; questions anyway.
204 ;;;
205 ;;; Other functions provided by forms mode are:
206 ;;;
207 ;;; paging (forward, backward) by record
208 ;;; jumping (first, last, random number)
209 ;;; searching
210 ;;; creating and deleting records
211 ;;; reverting the form (NOT the file buffer)
212 ;;; switching edit <-> view mode v.v.
213 ;;; jumping from field to field
214 ;;;
215 ;;; As an documented side-effect: jumping to the last record in the
216 ;;; file (using forms-last-record) will adjust forms--total-records if
217 ;;; needed.
218 ;;;
219 ;;; The forms buffer can be in on eof two modes: edit mode or view
220 ;;; mode. View mode is a read-only mode, you cannot modify the
221 ;;; contents of the buffer.
222 ;;;
223 ;;; Edit mode commands:
224 ;;;
225 ;;; TAB forms-next-field
226 ;;; \C-c TAB forms-next-field
227 ;;; \C-c < forms-first-record
228 ;;; \C-c > forms-last-record
229 ;;; \C-c ? describe-mode
230 ;;; \C-c \C-k forms-delete-record
231 ;;; \C-c \C-q forms-toggle-read-only
232 ;;; \C-c \C-o forms-insert-record
233 ;;; \C-c \C-l forms-jump-record
234 ;;; \C-c \C-n forms-next-record
235 ;;; \C-c \C-p forms-prev-record
236 ;;; \C-c \C-r forms-search-backward
237 ;;; \C-c \C-s forms-search-forward
238 ;;; \C-c \C-x forms-exit
239 ;;;
240 ;;; Read-only mode commands:
241 ;;;
242 ;;; SPC forms-next-record
243 ;;; DEL forms-prev-record
244 ;;; ? describe-mode
245 ;;; \C-q forms-toggle-read-only
246 ;;; l forms-jump-record
247 ;;; n forms-next-record
248 ;;; p forms-prev-record
249 ;;; r forms-search-backward
250 ;;; s forms-search-forward
251 ;;; x forms-exit
252 ;;;
253 ;;; Of course, it is also possible to use the \C-c prefix to obtain the
254 ;;; same command keys as in edit mode.
255 ;;;
256 ;;; The following bindings are available, independent of the mode:
257 ;;;
258 ;;; [next] forms-next-record
259 ;;; [prior] forms-prev-record
260 ;;; [begin] forms-first-record
261 ;;; [end] forms-last-record
262 ;;; [S-TAB] forms-prev-field
263 ;;; [backtab] forms-prev-field
264 ;;;
265 ;;; For convenience, TAB is always bound to `forms-next-field', so you
266 ;;; don't need the C-c prefix for this command.
267 ;;;
268 ;;; As mentioned above (see `forms-forms-scroll' and `forms-forms-jump')
269 ;;; the bindings of standard functions `scroll-up', `scroll-down',
270 ;;; `beginning-of-buffer' and `end-of-buffer' can be locally replaced with
271 ;;; forms mode functions next/prev record and first/last
272 ;;; record.
273 ;;;
274 ;;; `local-write-file hook' is defined to save the actual data file
275 ;;; instead of the buffer data, `revert-file-hook' is defined to
276 ;;; revert a forms to original.
277 \f
278 ;;; Code:
279
280 ;;; Global variables and constants:
281
282 (provide 'forms) ;;; official
283 (provide 'forms-mode) ;;; for compatibility
284
285 (defconst forms-version (substring "$Revision: 2.17 $" 11 -2)
286 "The version number of forms-mode (as string). The complete RCS id is:
287
288 $Id: forms.el,v 2.17 1995/06/17 13:00:22 rms Exp jvromans $")
289
290 (defvar forms-mode-hooks nil
291 "Hook functions to be run upon entering Forms mode.")
292 \f
293 ;;; Mandatory variables - must be set by evaluating the control file.
294
295 (defvar forms-file nil
296 "Name of the file holding the data.")
297
298 (defvar forms-format-list nil
299 "List of formatting specifications.")
300
301 (defvar forms-number-of-fields nil
302 "Number of fields per record.")
303 \f
304 ;;; Optional variables with default values.
305
306 (defvar forms-field-sep "\t"
307 "Field separator character (default TAB).")
308
309 (defvar forms-read-only nil
310 "Non-nil means: visit the file in view (read-only) mode.
311 \(Defaults to the write access on the data file).")
312
313 (defvar forms-multi-line "\C-k"
314 "If not nil: use this character to separate multi-line fields (default C-k).")
315
316 (defvar forms-forms-scroll nil
317 "*Non-nil means replace scroll-up/down commands in Forms mode.
318 The replacement commands performs forms-next/prev-record.")
319
320 (defvar forms-forms-jump nil
321 "*Non-nil means redefine beginning/end-of-buffer in Forms mode.
322 The replacement commands performs forms-first/last-record.")
323
324 (defvar forms-read-file-filter nil
325 "The name of a function that is called after reading the data file.
326 This can be used to change the contents of the file to something more
327 suitable for forms processing.")
328
329 (defvar forms-write-file-filter nil
330 "The name of a function that is called before writing the data file.
331 This can be used to undo the effects of form-read-file-hook.")
332
333 (defvar forms-new-record-filter nil
334 "The name of a function that is called when a new record is created.")
335
336 (defvar forms-modified-record-filter nil
337 "The name of a function that is called when a record has been modified.")
338
339 (defvar forms-fields nil
340 "List with fields of the current forms. First field has number 1.
341 This variable is for use by the filter routines only.
342 The contents may NOT be modified.")
343
344 (defvar forms-use-text-properties (fboundp 'set-text-properties)
345 "*Non-nil means: use emacs-19 text properties.
346 Defaults to t if this emacs is capable of handling text properties.")
347
348 (defvar forms-ro-face 'default
349 "The face (a symbol) that is used to display read-only text on the screen.")
350
351 (defvar forms-rw-face 'region
352 "The face (a symbol) that is used to display read-write text on the screen.")
353 \f
354 ;;; Internal variables.
355
356 (defvar forms--file-buffer nil
357 "Buffer which holds the file data")
358
359 (defvar forms--total-records 0
360 "Total number of records in the data file.")
361
362 (defvar forms--current-record 0
363 "Number of the record currently on the screen.")
364
365 (defvar forms-mode-map nil
366 "Keymap for form buffer.")
367 (defvar forms-mode-ro-map nil
368 "Keymap for form buffer in view mode.")
369 (defvar forms-mode-edit-map nil
370 "Keymap for form buffer in edit mode.")
371
372 (defvar forms--markers nil
373 "Field markers in the screen.")
374
375 (defvar forms--dyntexts nil
376 "Dynamic texts (resulting from function calls) on the screen.")
377
378 (defvar forms--the-record-list nil
379 "List of strings of the current record, as parsed from the file.")
380
381 (defvar forms--search-regexp nil
382 "Last regexp used by forms-search functions.")
383
384 (defvar forms--format nil
385 "Formatting routine.")
386
387 (defvar forms--parser nil
388 "Forms parser routine.")
389
390 (defvar forms--mode-setup nil
391 "To keep track of forms-mode being set-up.")
392 (make-variable-buffer-local 'forms--mode-setup)
393
394 (defvar forms--dynamic-text nil
395 "Array that holds dynamic texts to insert between fields.")
396
397 (defvar forms--elements nil
398 "Array with the order in which the fields are displayed.")
399
400 (defvar forms--ro-face nil
401 "Face used to represent read-only data on the screen.")
402
403 (defvar forms--rw-face nil
404 "Face used to represent read-write data on the screen.")
405 \f
406 ;;;###autoload
407 (defun forms-mode (&optional primary)
408 "Major mode to visit files in a field-structured manner using a form.
409
410 Commands: Equivalent keys in read-only mode:
411 TAB forms-next-field TAB
412 \\C-c TAB forms-next-field
413 \\C-c < forms-first-record <
414 \\C-c > forms-last-record >
415 \\C-c ? describe-mode ?
416 \\C-c \\C-k forms-delete-record
417 \\C-c \\C-q forms-toggle-read-only q
418 \\C-c \\C-o forms-insert-record
419 \\C-c \\C-l forms-jump-record l
420 \\C-c \\C-n forms-next-record n
421 \\C-c \\C-p forms-prev-record p
422 \\C-c \\C-r forms-search-reverse r
423 \\C-c \\C-s forms-search-forward s
424 \\C-c \\C-x forms-exit x
425 "
426 (interactive)
427
428 ;; This is not a simple major mode, as usual. Therefore, forms-mode
429 ;; takes an optional argument `primary' which is used for the
430 ;; initial set-up. Normal use would leave `primary' to nil.
431 ;; A global buffer-local variable `forms--mode-setup' has the same
432 ;; effect but makes it possible to auto-invoke forms-mode using
433 ;; `find-file'.
434 ;; Note: although it seems logical to have `make-local-variable'
435 ;; executed where the variable is first needed, I have deliberately
436 ;; placed all calls in this function.
437
438 ;; Primary set-up: evaluate buffer and check if the mandatory
439 ;; variables have been set.
440 (if (or primary (not forms--mode-setup))
441 (progn
442 ;;(message "forms: setting up...")
443 (kill-all-local-variables)
444
445 ;; Make mandatory variables.
446 (make-local-variable 'forms-file)
447 (make-local-variable 'forms-number-of-fields)
448 (make-local-variable 'forms-format-list)
449
450 ;; Make optional variables.
451 (make-local-variable 'forms-field-sep)
452 (make-local-variable 'forms-read-only)
453 (make-local-variable 'forms-multi-line)
454 (make-local-variable 'forms-forms-scroll)
455 (make-local-variable 'forms-forms-jump)
456 (make-local-variable 'forms-use-text-properties)
457
458 ;; Filter functions.
459 (make-local-variable 'forms-read-file-filter)
460 (make-local-variable 'forms-write-file-filter)
461 (make-local-variable 'forms-new-record-filter)
462 (make-local-variable 'forms-modified-record-filter)
463
464 ;; Make sure no filters exist.
465 (setq forms-read-file-filter nil)
466 (setq forms-write-file-filter nil)
467 (setq forms-new-record-filter nil)
468 (setq forms-modified-record-filter nil)
469
470 ;; If running Emacs 19 under X, setup faces to show read-only and
471 ;; read-write fields.
472 (if (fboundp 'make-face)
473 (progn
474 (make-local-variable 'forms-ro-face)
475 (make-local-variable 'forms-rw-face)))
476
477 ;; eval the buffer, should set variables
478 ;;(message "forms: processing control file...")
479 ;; If enable-local-eval is not set to t the user is asked first.
480 (if (or (eq enable-local-eval t)
481 (yes-or-no-p
482 (concat "Evaluate lisp code in buffer "
483 (buffer-name) " to display forms ")))
484 (eval-current-buffer)
485 (error "`enable-local-eval' inhibits buffer evaluation"))
486
487 ;; Check if the mandatory variables make sense.
488 (or forms-file
489 (error (concat "Forms control file error: "
490 "'forms-file' has not been set")))
491
492 ;; Check forms-field-sep first, since it can be needed to
493 ;; construct a default format list.
494 (or (stringp forms-field-sep)
495 (error (concat "Forms control file error: "
496 "'forms-field-sep' is not a string")))
497
498 (if forms-number-of-fields
499 (or (and (numberp forms-number-of-fields)
500 (> forms-number-of-fields 0))
501 (error (concat "Forms control file error: "
502 "'forms-number-of-fields' must be a number > 0")))
503 (or (null forms-format-list)
504 (error (concat "Forms control file error: "
505 "'forms-number-of-fields' has not been set"))))
506
507 (or forms-format-list
508 (forms--intuit-from-file))
509
510 (if forms-multi-line
511 (if (and (stringp forms-multi-line)
512 (eq (length forms-multi-line) 1))
513 (if (string= forms-multi-line forms-field-sep)
514 (error (concat "Forms control file error: "
515 "'forms-multi-line' is equal to 'forms-field-sep'")))
516 (error (concat "Forms control file error: "
517 "'forms-multi-line' must be nil or a one-character string"))))
518 (or (fboundp 'set-text-properties)
519 (setq forms-use-text-properties nil))
520
521 ;; Validate and process forms-format-list.
522 ;;(message "forms: pre-processing format list...")
523 (forms--process-format-list)
524
525 ;; Build the formatter and parser.
526 ;;(message "forms: building formatter...")
527 (make-local-variable 'forms--format)
528 (make-local-variable 'forms--markers)
529 (make-local-variable 'forms--dyntexts)
530 (make-local-variable 'forms--elements)
531 ;;(message "forms: building parser...")
532 (forms--make-format)
533 (make-local-variable 'forms--parser)
534 (forms--make-parser)
535 ;;(message "forms: building parser... done.")
536
537 ;; Check if record filters are defined.
538 (if (and forms-new-record-filter
539 (not (fboundp forms-new-record-filter)))
540 (error (concat "Forms control file error: "
541 "'forms-new-record-filter' is not a function")))
542
543 (if (and forms-modified-record-filter
544 (not (fboundp forms-modified-record-filter)))
545 (error (concat "Forms control file error: "
546 "'forms-modified-record-filter' is not a function")))
547
548 ;; The filters acces the contents of the forms using `forms-fields'.
549 (make-local-variable 'forms-fields)
550
551 ;; Dynamic text support.
552 (make-local-variable 'forms--dynamic-text)
553
554 ;; Prevent accidental overwrite of the control file and autosave.
555 (set-visited-file-name nil)
556
557 ;; Prepare this buffer for further processing.
558 (setq buffer-read-only nil)
559 (erase-buffer)
560
561 ;;(message "forms: setting up... done.")
562 ))
563
564 ;; initialization done
565 (setq forms--mode-setup t)
566
567 ;; Copy desired faces to the actual variables used by the forms formatter.
568 (if (fboundp 'make-face)
569 (progn
570 (make-local-variable 'forms--ro-face)
571 (make-local-variable 'forms--rw-face)
572 (if forms-read-only
573 (progn
574 (setq forms--ro-face forms-ro-face)
575 (setq forms--rw-face forms-ro-face))
576 (setq forms--ro-face forms-ro-face)
577 (setq forms--rw-face forms-rw-face))))
578
579 ;; Make more local variables.
580 (make-local-variable 'forms--file-buffer)
581 (make-local-variable 'forms--total-records)
582 (make-local-variable 'forms--current-record)
583 (make-local-variable 'forms--the-record-list)
584 (make-local-variable 'forms--search-regexp)
585
586 ; The keymaps are global, so multiple forms mode buffers can share them.
587 ;(make-local-variable 'forms-mode-map)
588 ;(make-local-variable 'forms-mode-ro-map)
589 ;(make-local-variable 'forms-mode-edit-map)
590 (if forms-mode-map ; already defined
591 nil
592 ;;(message "forms: building keymap...")
593 (forms--mode-commands)
594 ;;(message "forms: building keymap... done.")
595 )
596
597 ;; set the major mode indicator
598 (setq major-mode 'forms-mode)
599 (setq mode-name "Forms")
600
601 ;; find the data file
602 (setq forms--file-buffer (find-file-noselect forms-file))
603
604 ;; Pre-transform.
605 (let ((read-file-filter forms-read-file-filter)
606 (write-file-filter forms-write-file-filter))
607 (if read-file-filter
608 (save-excursion
609 (set-buffer forms--file-buffer)
610 (let ((inhibit-read-only t)
611 (file-modified (buffer-modified-p)))
612 (run-hooks 'read-file-filter)
613 (if (not file-modified) (set-buffer-modified-p nil)))
614 (if write-file-filter
615 (progn
616 (make-variable-buffer-local 'local-write-file-hooks)
617 (setq local-write-file-hooks (list write-file-filter)))))
618 (if write-file-filter
619 (save-excursion
620 (set-buffer forms--file-buffer)
621 (make-variable-buffer-local 'local-write-file-hooks)
622 (setq local-write-file-hooks write-file-filter)))))
623
624 ;; count the number of records, and set see if it may be modified
625 (let (ro)
626 (setq forms--total-records
627 (save-excursion
628 (prog1
629 (progn
630 ;;(message "forms: counting records...")
631 (set-buffer forms--file-buffer)
632 (bury-buffer (current-buffer))
633 (setq ro buffer-read-only)
634 (count-lines (point-min) (point-max)))
635 ;;(message "forms: counting records... done.")
636 )))
637 (if ro
638 (setq forms-read-only t)))
639
640 ;;(message "forms: proceeding setup...")
641
642 ;; Since we aren't really implementing a minor mode, we hack the modeline
643 ;; directly to get the text " View " into forms-read-only form buffers. For
644 ;; that reason, this variable must be buffer only.
645 (make-local-variable 'minor-mode-alist)
646 (setq minor-mode-alist (list (list 'forms-read-only " View")))
647
648 ;;(message "forms: proceeding setup (keymaps)...")
649 (forms--set-keymaps)
650 ;;(message "forms: proceeding setup (commands)...")
651 (forms--change-commands)
652
653 ;;(message "forms: proceeding setup (buffer)...")
654 (set-buffer-modified-p nil)
655
656 (if (= forms--total-records 0)
657 ;;(message "forms: proceeding setup (new file)...")
658 (progn
659 (insert
660 "GNU Emacs Forms Mode version " forms-version "\n\n"
661 (if (file-exists-p forms-file)
662 (concat "No records available in file \"" forms-file "\".\n\n")
663 (format "Creating new file \"%s\"\nwith %d field%s per record.\n\n"
664 forms-file forms-number-of-fields
665 (if (= 1 forms-number-of-fields) "" "s")))
666 "Use " (substitute-command-keys "\\[forms-insert-record]")
667 " to create new records.\n")
668 (setq forms--current-record 1)
669 (setq buffer-read-only t)
670 (set-buffer-modified-p nil))
671
672 ;; setup the first (or current) record to show
673 (if (< forms--current-record 1)
674 (setq forms--current-record 1))
675 (forms-jump-record forms--current-record)
676 )
677
678 ;; user customising
679 ;;(message "forms: proceeding setup (user hooks)...")
680 (run-hooks 'forms-mode-hooks)
681 ;;(message "forms: setting up... done.")
682
683 ;; be helpful
684 (forms--help)
685 )
686 \f
687 (defun forms--process-format-list ()
688 ;; Validate `forms-format-list' and set some global variables.
689 ;; Symbols in the list are evaluated, and consecutive strings are
690 ;; concatenated.
691 ;; Array `forms--elements' is constructed that contains the order
692 ;; of the fields on the display. This array is used by
693 ;; `forms--parser-using-text-properties' to extract the fields data
694 ;; from the form on the screen.
695 ;; Upon completion, `forms-format-list' is garanteed correct, so
696 ;; `forms--make-format' and `forms--make-parser' do not need to perform
697 ;; any checks.
698
699 ;; Verify that `forms-format-list' is not nil.
700 (or forms-format-list
701 (error (concat "Forms control file error: "
702 "'forms-format-list' has not been set")))
703 ;; It must be a list.
704 (or (listp forms-format-list)
705 (error (concat "Forms control file error: "
706 "'forms-format-list' is not a list")))
707
708 ;; Assume every field is painted once.
709 ;; `forms--elements' will grow if needed.
710 (setq forms--elements (make-vector forms-number-of-fields nil))
711
712 (let ((the-list forms-format-list) ; the list of format elements
713 (this-item 0) ; element in list
714 (prev-item nil)
715 (field-num 0)) ; highest field number
716
717 (setq forms-format-list nil) ; gonna rebuild
718
719 (while the-list
720
721 (let ((el (car-safe the-list))
722 (rem (cdr-safe the-list)))
723
724 ;; If it is a symbol, eval it first.
725 (if (and (symbolp el)
726 (boundp el))
727 (setq el (eval el)))
728
729 (cond
730
731 ;; Try string ...
732 ((stringp el)
733 (if (stringp prev-item) ; try to concatenate strings
734 (setq prev-item (concat prev-item el))
735 (if prev-item
736 (setq forms-format-list
737 (append forms-format-list (list prev-item) nil)))
738 (setq prev-item el)))
739
740 ;; Try numeric ...
741 ((numberp el)
742
743 ;; Validate range.
744 (if (or (<= el 0)
745 (> el forms-number-of-fields))
746 (error (concat "Forms format error: "
747 "field number %d out of range 1..%d")
748 el forms-number-of-fields))
749
750 ;; Store forms order.
751 (if (> field-num (length forms--elements))
752 (setq forms--elements (vconcat forms--elements (1- el)))
753 (aset forms--elements field-num (1- el)))
754 (setq field-num (1+ field-num))
755
756 (if prev-item
757 (setq forms-format-list
758 (append forms-format-list (list prev-item) nil)))
759 (setq prev-item el))
760
761 ;; Try function ...
762 ((listp el)
763
764 ;; Validate.
765 (or (fboundp (car-safe el))
766 (error (concat "Forms format error: "
767 "not a function "
768 (prin1-to-string (car-safe el)))))
769
770 ;; Shift.
771 (if prev-item
772 (setq forms-format-list
773 (append forms-format-list (list prev-item) nil)))
774 (setq prev-item el))
775
776 ;; else
777 (t
778 (error (concat "Forms format error: "
779 "invalid element "
780 (prin1-to-string el)))))
781
782 ;; Advance to next element of the list.
783 (setq the-list rem)))
784
785 ;; Append last item.
786 (if prev-item
787 (progn
788 (setq forms-format-list
789 (append forms-format-list (list prev-item) nil))
790 ;; Append a newline if the last item is a field.
791 ;; This prevents parsing problems.
792 ;; Also it makes it possible to insert an empty last field.
793 (if (numberp prev-item)
794 (setq forms-format-list
795 (append forms-format-list (list "\n") nil))))))
796
797 (forms--debug 'forms-format-list
798 'forms--elements))
799 \f
800 ;; Special treatment for read-only segments.
801 ;;
802 ;; If text is inserted between two read-only segments, it inherits the
803 ;; read-only properties. This is not what we want.
804 ;; To solve this, read-only segments get the `insert-in-front-hooks'
805 ;; property set with a function that temporarily switches the properties
806 ;; of the first character of the segment to read-write, so the new
807 ;; text gets the right properties.
808 ;; The `post-command-hook' is used to restore the original properties.
809
810 (defvar forms--iif-start nil
811 "Record start of modification command.")
812 (defvar forms--iif-properties nil
813 "Original properties of the character being overridden.")
814
815 (defun forms--iif-hook (begin end)
816 "`insert-in-front-hooks' function for read-only segments."
817
818 ;; Note start location. By making it a marker that points one
819 ;; character beyond the actual location, it is guaranteed to move
820 ;; correctly if text is inserted.
821 (or forms--iif-start
822 (setq forms--iif-start (copy-marker (1+ (point)))))
823
824 ;; Check if there is special treatment required.
825 (if (or (<= forms--iif-start 2)
826 (get-text-property (- forms--iif-start 2)
827 'read-only))
828 (progn
829 ;; Fetch current properties.
830 (setq forms--iif-properties
831 (text-properties-at (1- forms--iif-start)))
832
833 ;; Replace them.
834 (let ((inhibit-read-only t))
835 (set-text-properties
836 (1- forms--iif-start) forms--iif-start
837 (list 'face forms--rw-face 'front-sticky '(face))))
838
839 ;; Enable `post-command-hook' to restore the properties.
840 (setq post-command-hook
841 (append (list 'forms--iif-post-command-hook) post-command-hook)))
842
843 ;; No action needed. Clear marker.
844 (setq forms--iif-start nil)))
845
846 (defun forms--iif-post-command-hook ()
847 "`post-command-hook' function for read-only segments."
848
849 ;; Disable `post-command-hook'.
850 (setq post-command-hook
851 (delq 'forms--iif-hook-post-command-hook post-command-hook))
852
853 ;; Restore properties.
854 (if forms--iif-start
855 (let ((inhibit-read-only t))
856 (set-text-properties
857 (1- forms--iif-start) forms--iif-start
858 forms--iif-properties)))
859
860 ;; Cleanup.
861 (setq forms--iif-start nil))
862 \f
863 (defvar forms--marker)
864 (defvar forms--dyntext)
865
866 (defun forms--make-format ()
867 "Generate `forms--format' using the information in `forms-format-list'."
868
869 ;; The real work is done using a mapcar of `forms--make-format-elt' on
870 ;; `forms-format-list'.
871 ;; This function sets up the necessary environment, and decides
872 ;; which function to mapcar.
873
874 (let ((forms--marker 0)
875 (forms--dyntext 0))
876 (setq
877 forms--format
878 (if forms-use-text-properties
879 (` (lambda (arg)
880 (let ((inhibit-read-only t))
881 (,@ (apply 'append
882 (mapcar 'forms--make-format-elt-using-text-properties
883 forms-format-list)))
884 ;; Prevent insertion before the first text.
885 (,@ (if (numberp (car forms-format-list))
886 nil
887 '((add-text-properties (point-min) (1+ (point-min))
888 '(front-sticky (read-only intangible))))))
889 ;; Prevent insertion after the last text.
890 (remove-text-properties (1- (point)) (point)
891 '(rear-nonsticky)))
892 (setq forms--iif-start nil)))
893 (` (lambda (arg)
894 (,@ (apply 'append
895 (mapcar 'forms--make-format-elt forms-format-list)))))))
896
897 ;; We have tallied the number of markers and dynamic texts,
898 ;; so we can allocate the arrays now.
899 (setq forms--markers (make-vector forms--marker nil))
900 (setq forms--dyntexts (make-vector forms--dyntext nil)))
901 (forms--debug 'forms--format))
902
903 (defun forms--make-format-elt-using-text-properties (el)
904 "Helper routine to generate format function."
905
906 ;; The format routine `forms--format' will look like
907 ;;
908 ;; ;; preamble
909 ;; (lambda (arg)
910 ;; (let ((inhibit-read-only t))
911 ;;
912 ;; ;; A string, e.g. "text: ".
913 ;; (set-text-properties
914 ;; (point)
915 ;; (progn (insert "text: ") (point))
916 ;; (list 'face forms--ro-face
917 ;; 'read-only 1
918 ;; 'insert-in-front-hooks 'forms--iif-hook
919 ;; 'rear-nonsticky '(read-only face insert-in-front-hooks)))
920 ;;
921 ;; ;; A field, e.g. 6.
922 ;; (let ((here (point)))
923 ;; (aset forms--markers 0 (point-marker))
924 ;; (insert (elt arg 5))
925 ;; (or (= (point) here)
926 ;; (set-text-properties
927 ;; here (point)
928 ;; (list 'face forms--rw-face
929 ;; 'front-sticky '(face))))
930 ;;
931 ;; ;; Another string, e.g. "\nmore text: ".
932 ;; (set-text-properties
933 ;; (point)
934 ;; (progn (insert "\nmore text: ") (point))
935 ;; (list 'face forms--ro-face
936 ;; 'read-only 2
937 ;; 'insert-in-front-hooks 'forms--iif-hook
938 ;; 'rear-nonsticky '(read-only face insert-in-front-hooks)))
939 ;;
940 ;; ;; A function, e.g. (tocol 40).
941 ;; (set-text-properties
942 ;; (point)
943 ;; (progn
944 ;; (insert (aset forms--dyntexts 0 (tocol 40)))
945 ;; (point))
946 ;; (list 'face forms--ro-face
947 ;; 'read-only 2
948 ;; 'insert-in-front-hooks 'forms--iif-hook
949 ;; 'rear-nonsticky '(read-only face insert-in-front-hooks)))
950 ;;
951 ;; ;; Prevent insertion before the first text.
952 ;; (add-text-properties (point-min) (1+ (point-min))
953 ;; '(front-sticky (read-only))))))
954 ;; ;; Prevent insertion after the last text.
955 ;; (remove-text-properties (1- (point)) (point)
956 ;; '(rear-nonsticky)))
957 ;;
958 ;; ;; wrap up
959 ;; (setq forms--iif-start nil)
960 ;; ))
961
962 (cond
963 ((stringp el)
964
965 (` ((set-text-properties
966 (point) ; start at point
967 (progn ; until after insertion
968 (insert (, el))
969 (point))
970 (list 'face forms--ro-face ; read-only appearance
971 'read-only (,@ (list (1+ forms--marker)))
972 'intangible t
973 'insert-in-front-hooks '(forms--iif-hook)
974 'rear-nonsticky '(face read-only insert-in-front-hooks
975 intangible))))))
976
977 ((numberp el)
978 (` ((let ((here (point)))
979 (aset forms--markers
980 (, (prog1 forms--marker
981 (setq forms--marker (1+ forms--marker))))
982 (point-marker))
983 (insert (elt arg (, (1- el))))
984 (or (= (point) here)
985 (set-text-properties
986 here (point)
987 (list 'face forms--rw-face
988 'front-sticky '(face))))))))
989
990 ((listp el)
991 (` ((set-text-properties
992 (point)
993 (progn
994 (insert (aset forms--dyntexts
995 (, (prog1 forms--dyntext
996 (setq forms--dyntext (1+ forms--dyntext))))
997 (, el)))
998 (point))
999 (list 'face forms--ro-face
1000 'read-only (,@ (list (1+ forms--marker)))
1001 'intangible t
1002 'insert-in-front-hooks '(forms--iif-hook)
1003 'rear-nonsticky '(read-only face insert-in-front-hooks
1004 intangible))))))
1005
1006 ;; end of cond
1007 ))
1008
1009 (defun forms--make-format-elt (el)
1010 "Helper routine to generate format function."
1011
1012 ;; If we're not using text properties, the format routine
1013 ;; `forms--format' will look like
1014 ;;
1015 ;; (lambda (arg)
1016 ;; ;; a string, e.g. "text: "
1017 ;; (insert "text: ")
1018 ;; ;; a field, e.g. 6
1019 ;; (aset forms--markers 0 (point-marker))
1020 ;; (insert (elt arg 5))
1021 ;; ;; another string, e.g. "\nmore text: "
1022 ;; (insert "\nmore text: ")
1023 ;; ;; a function, e.g. (tocol 40)
1024 ;; (insert (aset forms--dyntexts 0 (tocol 40)))
1025 ;; ... )
1026
1027 (cond
1028 ((stringp el)
1029 (` ((insert (, el)))))
1030 ((numberp el)
1031 (prog1
1032 (` ((aset forms--markers (, forms--marker) (point-marker))
1033 (insert (elt arg (, (1- el))))))
1034 (setq forms--marker (1+ forms--marker))))
1035 ((listp el)
1036 (prog1
1037 (` ((insert (aset forms--dyntexts (, forms--dyntext) (, el)))))
1038 (setq forms--dyntext (1+ forms--dyntext))))))
1039 \f
1040 (defvar forms--field)
1041 (defvar forms--recordv)
1042 (defvar forms--seen-text)
1043
1044 (defun forms--make-parser ()
1045 "Generate `forms--parser' from the information in `forms-format-list'."
1046
1047 ;; If we can use text properties, we simply set it to
1048 ;; `forms--parser-using-text-properties'.
1049 ;; Otherwise, the function is constructed using a mapcar of
1050 ;; `forms--make-parser-elt on `forms-format-list'.
1051
1052 (setq
1053 forms--parser
1054 (if forms-use-text-properties
1055 (function forms--parser-using-text-properties)
1056 (let ((forms--field nil)
1057 (forms--seen-text nil)
1058 (forms--dyntext 0))
1059
1060 ;; Note: we add a nil element to the list passed to `mapcar',
1061 ;; see `forms--make-parser-elt' for details.
1062 (` (lambda nil
1063 (let (here)
1064 (goto-char (point-min))
1065 (,@ (apply 'append
1066 (mapcar
1067 'forms--make-parser-elt
1068 (append forms-format-list (list nil)))))))))))
1069
1070 (forms--debug 'forms--parser))
1071
1072 (defun forms--parser-using-text-properties ()
1073 "Extract field info from forms when using text properties."
1074
1075 ;; Using text properties, we can simply jump to the markers, and
1076 ;; extract the information up to the following read-only segment.
1077
1078 (let ((i 0)
1079 here there)
1080 (while (< i (length forms--markers))
1081 (goto-char (setq here (aref forms--markers i)))
1082 (if (get-text-property here 'read-only)
1083 (aset forms--recordv (aref forms--elements i) nil)
1084 (if (setq there
1085 (next-single-property-change here 'read-only))
1086 (aset forms--recordv (aref forms--elements i)
1087 (buffer-substring here there))
1088 (aset forms--recordv (aref forms--elements i)
1089 (buffer-substring here (point-max)))))
1090 (setq i (1+ i)))))
1091
1092 (defun forms--make-parser-elt (el)
1093 "Helper routine to generate forms parser function."
1094
1095 ;; The parse routine will look like:
1096 ;;
1097 ;; (lambda nil
1098 ;; (let (here)
1099 ;; (goto-char (point-min))
1100 ;;
1101 ;; ;; "text: "
1102 ;; (if (not (looking-at "text: "))
1103 ;; (error "Parse error: cannot find \"text: \""))
1104 ;; (forward-char 6) ; past "text: "
1105 ;;
1106 ;; ;; 6
1107 ;; ;; "\nmore text: "
1108 ;; (setq here (point))
1109 ;; (if (not (search-forward "\nmore text: " nil t nil))
1110 ;; (error "Parse error: cannot find \"\\nmore text: \""))
1111 ;; (aset forms--recordv 5 (buffer-substring here (- (point) 12)))
1112 ;;
1113 ;; ;; (tocol 40)
1114 ;; (let ((forms--dyntext (car-safe forms--dynamic-text)))
1115 ;; (if (not (looking-at (regexp-quote forms--dyntext)))
1116 ;; (error "Parse error: not looking at \"%s\"" forms--dyntext))
1117 ;; (forward-char (length forms--dyntext))
1118 ;; (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))
1119 ;; ...
1120 ;; ;; final flush (due to terminator sentinel, see below)
1121 ;; (aset forms--recordv 7 (buffer-substring (point) (point-max)))
1122
1123 (cond
1124 ((stringp el)
1125 (prog1
1126 (if forms--field
1127 (` ((setq here (point))
1128 (if (not (search-forward (, el) nil t nil))
1129 (error "Parse error: cannot find \"%s\"" (, el)))
1130 (aset forms--recordv (, (1- forms--field))
1131 (buffer-substring here
1132 (- (point) (, (length el)))))))
1133 (` ((if (not (looking-at (, (regexp-quote el))))
1134 (error "Parse error: not looking at \"%s\"" (, el)))
1135 (forward-char (, (length el))))))
1136 (setq forms--seen-text t)
1137 (setq forms--field nil)))
1138 ((numberp el)
1139 (if forms--field
1140 (error "Cannot parse adjacent fields %d and %d"
1141 forms--field el)
1142 (setq forms--field el)
1143 nil))
1144 ((null el)
1145 (if forms--field
1146 (` ((aset forms--recordv (, (1- forms--field))
1147 (buffer-substring (point) (point-max)))))))
1148 ((listp el)
1149 (prog1
1150 (if forms--field
1151 (` ((let ((here (point))
1152 (forms--dyntext (aref forms--dyntexts (, forms--dyntext))))
1153 (if (not (search-forward forms--dyntext nil t nil))
1154 (error "Parse error: cannot find \"%s\"" forms--dyntext))
1155 (aset forms--recordv (, (1- forms--field))
1156 (buffer-substring here
1157 (- (point) (length forms--dyntext)))))))
1158 (` ((let ((forms--dyntext (aref forms--dyntexts (, forms--dyntext))))
1159 (if (not (looking-at (regexp-quote forms--dyntext)))
1160 (error "Parse error: not looking at \"%s\"" forms--dyntext))
1161 (forward-char (length forms--dyntext))))))
1162 (setq forms--dyntext (1+ forms--dyntext))
1163 (setq forms--seen-text t)
1164 (setq forms--field nil)))
1165 ))
1166 \f
1167 (defun forms--intuit-from-file ()
1168 "Get number of fields and a default form using the data file."
1169
1170 ;; If `forms-number-of-fields' is not set, get it from the data file.
1171 (if (null forms-number-of-fields)
1172
1173 ;; Need a file to do this.
1174 (if (not (file-exists-p forms-file))
1175 (error "Need existing file or explicit 'forms-number-of-records'.")
1176
1177 ;; Visit the file and extract the first record.
1178 (setq forms--file-buffer (find-file-noselect forms-file))
1179 (let ((read-file-filter forms-read-file-filter)
1180 (the-record))
1181 (setq the-record
1182 (save-excursion
1183 (set-buffer forms--file-buffer)
1184 (let ((inhibit-read-only t))
1185 (run-hooks 'read-file-filter))
1186 (goto-char (point-min))
1187 (forms--get-record)))
1188
1189 ;; This may be overkill, but try to avoid interference with
1190 ;; the normal processing.
1191 (kill-buffer forms--file-buffer)
1192
1193 ;; Count the number of fields in `the-record'.
1194 (let (the-result
1195 (start-pos 0)
1196 found-pos
1197 (field-sep-length (length forms-field-sep)))
1198 (setq forms-number-of-fields 1)
1199 (while (setq found-pos
1200 (string-match forms-field-sep the-record start-pos))
1201 (progn
1202 (setq forms-number-of-fields (1+ forms-number-of-fields))
1203 (setq start-pos (+ field-sep-length found-pos))))))))
1204
1205 ;; Construct default format list.
1206 (setq forms-format-list (list "Forms file \"" forms-file "\".\n\n"))
1207 (let ((i 0))
1208 (while (<= (setq i (1+ i)) forms-number-of-fields)
1209 (setq forms-format-list
1210 (append forms-format-list
1211 (list (format "%4d: " i) i "\n"))))))
1212 \f
1213 (defun forms--set-keymaps ()
1214 "Set the keymaps used in this mode."
1215
1216 (use-local-map (if forms-read-only
1217 forms-mode-ro-map
1218 forms-mode-edit-map)))
1219
1220 (defun forms--mode-commands ()
1221 "Fill the Forms mode keymaps."
1222
1223 ;; `forms-mode-map' is always accessible via \C-c prefix.
1224 (setq forms-mode-map (make-keymap))
1225 (define-key forms-mode-map "\t" 'forms-next-field)
1226 (define-key forms-mode-map "\C-k" 'forms-delete-record)
1227 (define-key forms-mode-map "\C-q" 'forms-toggle-read-only)
1228 (define-key forms-mode-map "\C-o" 'forms-insert-record)
1229 (define-key forms-mode-map "\C-l" 'forms-jump-record)
1230 (define-key forms-mode-map "\C-n" 'forms-next-record)
1231 (define-key forms-mode-map "\C-p" 'forms-prev-record)
1232 (define-key forms-mode-map "\C-r" 'forms-search-backward)
1233 (define-key forms-mode-map "\C-s" 'forms-search-forward)
1234 (define-key forms-mode-map "\C-x" 'forms-exit)
1235 (define-key forms-mode-map "<" 'forms-first-record)
1236 (define-key forms-mode-map ">" 'forms-last-record)
1237 (define-key forms-mode-map "\C-?" 'forms-prev-record)
1238
1239 ;; `forms-mode-ro-map' replaces the local map when in read-only mode.
1240 (setq forms-mode-ro-map (make-keymap))
1241 (suppress-keymap forms-mode-ro-map)
1242 (define-key forms-mode-ro-map "\C-c" forms-mode-map)
1243 (define-key forms-mode-ro-map "\t" 'forms-next-field)
1244 (define-key forms-mode-ro-map "q" 'forms-toggle-read-only)
1245 (define-key forms-mode-ro-map "l" 'forms-jump-record)
1246 (define-key forms-mode-ro-map "n" 'forms-next-record)
1247 (define-key forms-mode-ro-map "p" 'forms-prev-record)
1248 (define-key forms-mode-ro-map "r" 'forms-search-backward)
1249 (define-key forms-mode-ro-map "s" 'forms-search-forward)
1250 (define-key forms-mode-ro-map "x" 'forms-exit)
1251 (define-key forms-mode-ro-map "<" 'forms-first-record)
1252 (define-key forms-mode-ro-map ">" 'forms-last-record)
1253 (define-key forms-mode-ro-map "?" 'describe-mode)
1254 (define-key forms-mode-ro-map " " 'forms-next-record)
1255 (forms--mode-commands1 forms-mode-ro-map)
1256 (forms--mode-menu-ro forms-mode-ro-map)
1257
1258 ;; This is the normal, local map.
1259 (setq forms-mode-edit-map (make-keymap))
1260 (define-key forms-mode-edit-map "\t" 'forms-next-field)
1261 (define-key forms-mode-edit-map "\C-c" forms-mode-map)
1262 (forms--mode-commands1 forms-mode-edit-map)
1263 (forms--mode-menu-edit forms-mode-edit-map)
1264 )
1265
1266 (defun forms--mode-menu-ro (map)
1267 ;;; Menu initialisation
1268 ; (define-key map [menu-bar] (make-sparse-keymap))
1269 (define-key map [menu-bar forms]
1270 (cons "Forms" (make-sparse-keymap "Forms")))
1271 (define-key map [menu-bar forms menu-forms-exit]
1272 '("Exit" . forms-exit))
1273 (define-key map [menu-bar forms menu-forms-sep1]
1274 '("----"))
1275 (define-key map [menu-bar forms menu-forms-save]
1276 '("Save Data" . forms-save-buffer))
1277 (define-key map [menu-bar forms menu-forms-print]
1278 '("Print Data" . forms-print))
1279 (define-key map [menu-bar forms menu-forms-describe]
1280 '("Describe Mode" . describe-mode))
1281 (define-key map [menu-bar forms menu-forms-toggle-ro]
1282 '("Toggle View/Edit" . forms-toggle-read-only))
1283 (define-key map [menu-bar forms menu-forms-jump-record]
1284 '("Jump" . forms-jump-record))
1285 (define-key map [menu-bar forms menu-forms-search-backward]
1286 '("Search Backward" . forms-search-backward))
1287 (define-key map [menu-bar forms menu-forms-search-forward]
1288 '("Search Forward" . forms-search-forward))
1289 (define-key map [menu-bar forms menu-forms-delete-record]
1290 '("Delete" . forms-delete-record))
1291 (define-key map [menu-bar forms menu-forms-insert-record]
1292 '("Insert" . forms-insert-record))
1293 (define-key map [menu-bar forms menu-forms-sep2]
1294 '("----"))
1295 (define-key map [menu-bar forms menu-forms-last-record]
1296 '("Last Record" . forms-last-record))
1297 (define-key map [menu-bar forms menu-forms-first-record]
1298 '("First Record" . forms-first-record))
1299 (define-key map [menu-bar forms menu-forms-prev-record]
1300 '("Previous Record" . forms-prev-record))
1301 (define-key map [menu-bar forms menu-forms-next-record]
1302 '("Next Record" . forms-next-record))
1303 (define-key map [menu-bar forms menu-forms-sep3]
1304 '("----"))
1305 (define-key map [menu-bar forms menu-forms-prev-field]
1306 '("Previous Field" . forms-prev-field))
1307 (define-key map [menu-bar forms menu-forms-next-field]
1308 '("Next Field" . forms-next-field))
1309 (put 'forms-insert-record 'menu-enable '(not forms-read-only))
1310 (put 'forms-delete-record 'menu-enable '(not forms-read-only))
1311 )
1312 (defun forms--mode-menu-edit (map)
1313 ;;; Menu initialisation
1314 ; (define-key map [menu-bar] (make-sparse-keymap))
1315 (define-key map [menu-bar forms]
1316 (cons "Forms" (make-sparse-keymap "Forms")))
1317 (define-key map [menu-bar forms menu-forms-edit--exit]
1318 '("Exit" . forms-exit))
1319 (define-key map [menu-bar forms menu-forms-edit-sep1]
1320 '("----"))
1321 (define-key map [menu-bar forms menu-forms-edit-save]
1322 '("Save Data" . forms-save-buffer))
1323 (define-key map [menu-bar forms menu-forms-edit-print]
1324 '("Print Data" . forms-print))
1325 (define-key map [menu-bar forms menu-forms-edit-describe]
1326 '("Describe Mode" . describe-mode))
1327 (define-key map [menu-bar forms menu-forms-edit-toggle-ro]
1328 '("Toggle View/Edit" . forms-toggle-read-only))
1329 (define-key map [menu-bar forms menu-forms-edit-jump-record]
1330 '("Jump" . forms-jump-record))
1331 (define-key map [menu-bar forms menu-forms-edit-search-backward]
1332 '("Search Backward" . forms-search-backward))
1333 (define-key map [menu-bar forms menu-forms-edit-search-forward]
1334 '("Search Forward" . forms-search-forward))
1335 (define-key map [menu-bar forms menu-forms-edit-delete-record]
1336 '("Delete" . forms-delete-record))
1337 (define-key map [menu-bar forms menu-forms-edit-insert-record]
1338 '("Insert" . forms-insert-record))
1339 (define-key map [menu-bar forms menu-forms-edit-sep2]
1340 '("----"))
1341 (define-key map [menu-bar forms menu-forms-edit-last-record]
1342 '("Last Record" . forms-last-record))
1343 (define-key map [menu-bar forms menu-forms-edit-first-record]
1344 '("First Record" . forms-first-record))
1345 (define-key map [menu-bar forms menu-forms-edit-prev-record]
1346 '("Previous Record" . forms-prev-record))
1347 (define-key map [menu-bar forms menu-forms-edit-next-record]
1348 '("Next Record" . forms-next-record))
1349 (define-key map [menu-bar forms menu-forms-edit-sep3]
1350 '("----"))
1351 (define-key map [menu-bar forms menu-forms-edit-prev-field]
1352 '("Previous Field" . forms-prev-field))
1353 (define-key map [menu-bar forms menu-forms-edit-next-field]
1354 '("Next Field" . forms-next-field))
1355 (put 'forms-insert-record 'menu-enable '(not forms-read-only))
1356 (put 'forms-delete-record 'menu-enable '(not forms-read-only))
1357 )
1358
1359 (defun forms--mode-commands1 (map)
1360 "Helper routine to define keys."
1361 (define-key map [TAB] 'forms-next-field)
1362 (define-key map [S-tab] 'forms-prev-field)
1363 (define-key map [next] 'forms-next-record)
1364 (define-key map [prior] 'forms-prev-record)
1365 (define-key map [begin] 'forms-first-record)
1366 (define-key map [last] 'forms-last-record)
1367 (define-key map [backtab] 'forms-prev-field)
1368 )
1369 \f
1370 ;;; Changed functions
1371
1372 (defun forms--change-commands ()
1373 "Localize some commands for Forms mode."
1374
1375 ;; scroll-down -> forms-prev-record
1376 ;; scroll-up -> forms-next-record
1377 (if forms-forms-scroll
1378 (progn
1379 (substitute-key-definition 'scroll-up 'forms-next-record
1380 (current-local-map)
1381 (current-global-map))
1382 (substitute-key-definition 'scroll-down 'forms-prev-record
1383 (current-local-map)
1384 (current-global-map))))
1385 ;;
1386 ;; beginning-of-buffer -> forms-first-record
1387 ;; end-of-buffer -> forms-end-record
1388 (if forms-forms-jump
1389 (progn
1390 (substitute-key-definition 'beginning-of-buffer 'forms-first-record
1391 (current-local-map)
1392 (current-global-map))
1393 (substitute-key-definition 'end-of-buffer 'forms-last-record
1394 (current-local-map)
1395 (current-global-map))))
1396 ;;
1397 ;; Save buffer
1398 (local-set-key "\C-x\C-s" 'forms-save-buffer)
1399 ;;
1400 ;; We have our own revert function - use it.
1401 (make-local-variable 'revert-buffer-function)
1402 (setq revert-buffer-function 'forms--revert-buffer)
1403
1404 t)
1405
1406 (defun forms--help ()
1407 "Initial help for Forms mode."
1408 (message (substitute-command-keys (concat
1409 "\\[forms-next-record]:next"
1410 " \\[forms-prev-record]:prev"
1411 " \\[forms-first-record]:first"
1412 " \\[forms-last-record]:last"
1413 " \\[describe-mode]:help"))))
1414
1415 (defun forms--trans (subj arg rep)
1416 "Translate in SUBJ all chars ARG into char REP. ARG and REP should
1417 be single-char strings."
1418 (let ((i 0)
1419 (x (length subj))
1420 (re (regexp-quote arg))
1421 (k (string-to-char rep)))
1422 (while (setq i (string-match re subj i))
1423 (aset subj i k)
1424 (setq i (1+ i)))))
1425
1426 (defun forms--exit (query &optional save)
1427 "Internal exit from forms mode function."
1428
1429 (let ((buf (buffer-name forms--file-buffer)))
1430 (forms--checkmod)
1431 (if (and save
1432 (buffer-modified-p forms--file-buffer))
1433 (forms-save-buffer))
1434 (save-excursion
1435 (set-buffer forms--file-buffer)
1436 (delete-auto-save-file-if-necessary)
1437 (kill-buffer (current-buffer)))
1438 (if (get-buffer buf) ; not killed???
1439 (if save
1440 (progn
1441 (beep)
1442 (message "Problem saving buffers?")))
1443 (delete-auto-save-file-if-necessary)
1444 (kill-buffer (current-buffer)))))
1445
1446 (defun forms--get-record ()
1447 "Fetch the current record from the file buffer."
1448
1449 ;; This function is executed in the context of the `forms--file-buffer'.
1450
1451 (or (bolp)
1452 (beginning-of-line nil))
1453 (let ((here (point)))
1454 (prog2
1455 (end-of-line)
1456 (buffer-substring here (point))
1457 (goto-char here))))
1458
1459 (defun forms--show-record (the-record)
1460 "Format THE-RECORD and display it in the current buffer."
1461
1462 ;; Split the-record.
1463 (let (the-result
1464 (start-pos 0)
1465 found-pos
1466 (field-sep-length (length forms-field-sep)))
1467 (if forms-multi-line
1468 (forms--trans the-record forms-multi-line "\n"))
1469 ;; Add an extra separator (makes splitting easy).
1470 (setq the-record (concat the-record forms-field-sep))
1471 (while (setq found-pos (string-match forms-field-sep the-record start-pos))
1472 (let ((ent (substring the-record start-pos found-pos)))
1473 (setq the-result
1474 (append the-result (list ent)))
1475 (setq start-pos (+ field-sep-length found-pos))))
1476 (setq forms--the-record-list the-result))
1477
1478 (setq buffer-read-only nil)
1479 (if forms-use-text-properties
1480 (let ((inhibit-read-only t))
1481 (set-text-properties (point-min) (point-max) nil)))
1482 (erase-buffer)
1483
1484 ;; Verify the number of fields, extend forms--the-record-list if needed.
1485 (if (= (length forms--the-record-list) forms-number-of-fields)
1486 nil
1487 (beep)
1488 (message "Warning: this record has %d fields instead of %d"
1489 (length forms--the-record-list) forms-number-of-fields)
1490 (if (< (length forms--the-record-list) forms-number-of-fields)
1491 (setq forms--the-record-list
1492 (append forms--the-record-list
1493 (make-list
1494 (- forms-number-of-fields
1495 (length forms--the-record-list))
1496 "")))))
1497
1498 ;; Call the formatter function.
1499 (setq forms-fields (append (list nil) forms--the-record-list nil))
1500 (funcall forms--format forms--the-record-list)
1501
1502 ;; Prepare.
1503 (goto-char (point-min))
1504 (set-buffer-modified-p nil)
1505 (setq buffer-read-only forms-read-only)
1506 (setq mode-line-process
1507 (concat " " forms--current-record "/" forms--total-records)))
1508
1509 (defun forms--parse-form ()
1510 "Parse contents of form into list of strings."
1511 ;; The contents of the form are parsed, and a new list of strings
1512 ;; is constructed.
1513 ;; A vector with the strings from the original record is
1514 ;; constructed, which is updated with the new contents. Therefore
1515 ;; fields which were not in the form are not modified.
1516 ;; Finally, the vector is transformed into a list for further processing.
1517
1518 (let (forms--recordv)
1519
1520 ;; Build the vector.
1521 (setq forms--recordv (vconcat forms--the-record-list))
1522
1523 ;; Parse the form and update the vector.
1524 (let ((forms--dynamic-text forms--dynamic-text))
1525 (funcall forms--parser))
1526
1527 (if forms-modified-record-filter
1528 ;; As a service to the user, we add a zeroth element so she
1529 ;; can use the same indices as in the forms definition.
1530 (let ((the-fields (vconcat [nil] forms--recordv)))
1531 (setq the-fields (funcall forms-modified-record-filter the-fields))
1532 (cdr (append the-fields nil)))
1533
1534 ;; Transform to a list and return.
1535 (append forms--recordv nil))))
1536
1537 (defun forms--update ()
1538 "Update current record with contents of form.
1539 As a side effect: sets `forms--the-record-list'."
1540
1541 (if forms-read-only
1542 (progn
1543 (message "Read-only buffer!")
1544 (beep))
1545
1546 (let (the-record)
1547 ;; Build new record.
1548 (setq forms--the-record-list (forms--parse-form))
1549 (setq the-record
1550 (mapconcat 'identity forms--the-record-list forms-field-sep))
1551
1552 (if (string-match (regexp-quote forms-field-sep)
1553 (mapconcat 'identity forms--the-record-list ""))
1554 (error "Field separator occurs in record - update refused!"))
1555
1556 ;; Handle multi-line fields, if allowed.
1557 (if forms-multi-line
1558 (forms--trans the-record "\n" forms-multi-line))
1559
1560 ;; A final sanity check before updating.
1561 (if (string-match "\n" the-record)
1562 (progn
1563 (message "Multi-line fields in this record - update refused!")
1564 (beep))
1565
1566 (save-excursion
1567 (set-buffer forms--file-buffer)
1568 ;; Use delete-region instead of kill-region, to avoid
1569 ;; adding junk to the kill-ring.
1570 (delete-region (save-excursion (beginning-of-line) (point))
1571 (save-excursion (end-of-line) (point)))
1572 (insert the-record)
1573 (beginning-of-line))))))
1574
1575 (defun forms--checkmod ()
1576 "Check if this form has been modified, and call forms--update if so."
1577 (if (buffer-modified-p nil)
1578 (let ((here (point)))
1579 (forms--update)
1580 (set-buffer-modified-p nil)
1581 (goto-char here))))
1582 \f
1583 ;;; Start and exit
1584
1585 ;;;###autoload
1586 (defun forms-find-file (fn)
1587 "Visit a file in Forms mode."
1588 (interactive "fForms file: ")
1589 (let ((enable-local-eval t)
1590 (enable-local-variables t))
1591 (find-file-read-only fn)
1592 (or forms--mode-setup (forms-mode t))))
1593
1594 ;;;###autoload
1595 (defun forms-find-file-other-window (fn)
1596 "Visit a file in Forms mode in other window."
1597 (interactive "fFbrowse file in other window: ")
1598 (let ((enable-local-eval t)
1599 (enable-local-variables t))
1600 (find-file-other-window fn)
1601 (or forms--mode-setup (forms-mode t))))
1602
1603 (defun forms-exit (query)
1604 "Normal exit from Forms mode. Modified buffers are saved."
1605 (interactive "P")
1606 (forms--exit query t))
1607
1608 (defun forms-exit-no-save (query)
1609 "Exit from Forms mode without saving buffers."
1610 (interactive "P")
1611 (forms--exit query nil))
1612 \f
1613 ;;; Navigating commands
1614
1615 (defun forms-next-record (arg)
1616 "Advance to the ARGth following record."
1617 (interactive "P")
1618 (forms-jump-record (+ forms--current-record (prefix-numeric-value arg)) t))
1619
1620 (defun forms-prev-record (arg)
1621 "Advance to the ARGth previous record."
1622 (interactive "P")
1623 (forms-jump-record (- forms--current-record (prefix-numeric-value arg)) t))
1624
1625 (defun forms-jump-record (arg &optional relative)
1626 "Jump to a random record."
1627 (interactive "NRecord number: ")
1628
1629 ;; Verify that the record number is within range.
1630 (if (or (> arg forms--total-records)
1631 (<= arg 0))
1632 (progn
1633 (beep)
1634 ;; Don't give the message if just paging.
1635 (if (not relative)
1636 (message "Record number %d out of range 1..%d"
1637 arg forms--total-records))
1638 )
1639
1640 ;; Flush.
1641 (forms--checkmod)
1642
1643 ;; Calculate displacement.
1644 (let ((disp (- arg forms--current-record))
1645 (cur forms--current-record))
1646
1647 ;; `forms--show-record' needs it now.
1648 (setq forms--current-record arg)
1649
1650 ;; Get the record and show it.
1651 (forms--show-record
1652 (save-excursion
1653 (set-buffer forms--file-buffer)
1654 (beginning-of-line)
1655
1656 ;; Move, and adjust the amount if needed (shouldn't happen).
1657 (if relative
1658 (if (zerop disp)
1659 nil
1660 (setq cur (+ cur disp (- (forward-line disp)))))
1661 (setq cur (+ cur disp (- (goto-line arg)))))
1662
1663 (forms--get-record)))
1664
1665 ;; This shouldn't happen.
1666 (if (/= forms--current-record cur)
1667 (progn
1668 (setq forms--current-record cur)
1669 (beep)
1670 (message "Stuck at record %d" cur))))))
1671
1672 (defun forms-first-record ()
1673 "Jump to first record."
1674 (interactive)
1675 (forms-jump-record 1))
1676
1677 (defun forms-last-record ()
1678 "Jump to last record.
1679 As a side effect: re-calculates the number of records in the data file."
1680 (interactive)
1681 (let
1682 ((numrec
1683 (save-excursion
1684 (set-buffer forms--file-buffer)
1685 (count-lines (point-min) (point-max)))))
1686 (if (= numrec forms--total-records)
1687 nil
1688 (beep)
1689 (setq forms--total-records numrec)
1690 (message "Warning: number of records changed to %d" forms--total-records)))
1691 (forms-jump-record forms--total-records))
1692 \f
1693 ;;; Other commands
1694
1695 (defun forms-toggle-read-only (arg)
1696 "Toggles read-only mode of a forms mode buffer.
1697 With an argument, enables read-only mode if the argument is positive.
1698 Otherwise enables edit mode if the visited file is writeable."
1699
1700 (interactive "P")
1701
1702 (if (if arg
1703 ;; Negative arg means switch it off.
1704 (<= (prefix-numeric-value arg) 0)
1705 ;; No arg means toggle.
1706 forms-read-only)
1707
1708 ;; Enable edit mode, if possible.
1709 (let ((ro forms-read-only))
1710 (if (save-excursion
1711 (set-buffer forms--file-buffer)
1712 buffer-read-only)
1713 (progn
1714 (setq forms-read-only t)
1715 (message "No write access to \"%s\"" forms-file)
1716 (beep))
1717 (setq forms-read-only nil))
1718 (if (equal ro forms-read-only)
1719 nil
1720 (forms-mode)))
1721
1722 ;; Enable view mode.
1723 (if forms-read-only
1724 nil
1725 (forms--checkmod) ; sync
1726 (setq forms-read-only t)
1727 (forms-mode))))
1728
1729 ;; Sample:
1730 ;; (defun my-new-record-filter (the-fields)
1731 ;; ;; numbers are relative to 1
1732 ;; (aset the-fields 4 (current-time-string))
1733 ;; (aset the-fields 6 (user-login-name))
1734 ;; the-list)
1735 ;; (setq forms-new-record-filter 'my-new-record-filter)
1736
1737 (defun forms-insert-record (arg)
1738 "Create a new record before the current one.
1739 With ARG: store the record after the current one.
1740 If `forms-new-record-filter' contains the name of a function,
1741 it is called to fill (some of) the fields with default values."
1742
1743 (interactive "P")
1744
1745 (if forms-read-only
1746 (error ""))
1747
1748 (let ((ln (if arg (1+ forms--current-record) forms--current-record))
1749 the-list the-record)
1750
1751 (forms--checkmod)
1752 (if forms-new-record-filter
1753 ;; As a service to the user, we add a zeroth element so she
1754 ;; can use the same indices as in the forms definition.
1755 (let ((the-fields (make-vector (1+ forms-number-of-fields) "")))
1756 (setq the-fields (funcall forms-new-record-filter the-fields))
1757 (setq the-list (cdr (append the-fields nil))))
1758 (setq the-list (make-list forms-number-of-fields "")))
1759
1760 (setq the-record
1761 (mapconcat
1762 'identity
1763 the-list
1764 forms-field-sep))
1765
1766 (save-excursion
1767 (set-buffer forms--file-buffer)
1768 (goto-line ln)
1769 (open-line 1)
1770 (insert the-record)
1771 (beginning-of-line))
1772
1773 (setq forms--current-record ln))
1774
1775 (setq forms--total-records (1+ forms--total-records))
1776 (forms-jump-record forms--current-record))
1777
1778 (defun forms-delete-record (arg)
1779 "Deletes a record. With a prefix argument: don't ask."
1780 (interactive "P")
1781
1782 (if forms-read-only
1783 (error ""))
1784
1785 (forms--checkmod)
1786 (if (or arg
1787 (y-or-n-p "Really delete this record? "))
1788 (let ((ln forms--current-record))
1789 (save-excursion
1790 (set-buffer forms--file-buffer)
1791 (goto-line ln)
1792 ;; Use delete-region instead of kill-region, to avoid
1793 ;; adding junk to the kill-ring.
1794 (delete-region (progn (beginning-of-line) (point))
1795 (progn (beginning-of-line 2) (point))))
1796 (setq forms--total-records (1- forms--total-records))
1797 (if (> forms--current-record forms--total-records)
1798 (setq forms--current-record forms--total-records))
1799 (forms-jump-record forms--current-record)))
1800 (message ""))
1801
1802 (defun forms-search-forward (regexp)
1803 "Search forward for record containing REGEXP."
1804 (interactive
1805 (list (read-string (concat "Search forward for"
1806 (if forms--search-regexp
1807 (concat " ("
1808 forms--search-regexp
1809 ")"))
1810 ": "))))
1811 (if (equal "" regexp)
1812 (setq regexp forms--search-regexp))
1813 (forms--checkmod)
1814
1815 (let (the-line the-record here
1816 (fld-sep forms-field-sep))
1817 (if (save-excursion
1818 (set-buffer forms--file-buffer)
1819 (setq here (point))
1820 (end-of-line)
1821 (if (null (re-search-forward regexp nil t))
1822 (progn
1823 (goto-char here)
1824 (message (concat "\"" regexp "\" not found."))
1825 nil)
1826 (setq the-record (forms--get-record))
1827 (setq the-line (1+ (count-lines (point-min) (point))))))
1828 (progn
1829 (setq forms--current-record the-line)
1830 (forms--show-record the-record)
1831 (re-search-forward regexp nil t))))
1832 (setq forms--search-regexp regexp))
1833
1834 (defun forms-search-backward (regexp)
1835 "Search backward for record containing REGEXP."
1836 (interactive
1837 (list (read-string (concat "Search backward for"
1838 (if forms--search-regexp
1839 (concat " ("
1840 forms--search-regexp
1841 ")"))
1842 ": "))))
1843 (if (equal "" regexp)
1844 (setq regexp forms--search-regexp))
1845 (forms--checkmod)
1846
1847 (let (the-line the-record here
1848 (fld-sep forms-field-sep))
1849 (if (save-excursion
1850 (set-buffer forms--file-buffer)
1851 (setq here (point))
1852 (beginning-of-line)
1853 (if (null (re-search-backward regexp nil t))
1854 (progn
1855 (goto-char here)
1856 (message (concat "\"" regexp "\" not found."))
1857 nil)
1858 (setq the-record (forms--get-record))
1859 (setq the-line (1+ (count-lines (point-min) (point))))))
1860 (progn
1861 (setq forms--current-record the-line)
1862 (forms--show-record the-record)
1863 (re-search-forward regexp nil t))))
1864 (setq forms--search-regexp regexp))
1865
1866 (defun forms-save-buffer (&optional args)
1867 "Forms mode replacement for save-buffer.
1868 It saves the data buffer instead of the forms buffer.
1869 Calls `forms-write-file-filter' before writing out the data."
1870 (interactive "p")
1871 (forms--checkmod)
1872 (let ((read-file-filter forms-read-file-filter))
1873 (save-excursion
1874 (set-buffer forms--file-buffer)
1875 (let ((inhibit-read-only t))
1876 (save-buffer args)
1877 (if read-file-filter
1878 (run-hooks 'read-file-filter))
1879 (set-buffer-modified-p nil))))
1880 t)
1881
1882 (defun forms--revert-buffer (&optional arg noconfirm)
1883 "Reverts current form to un-modified."
1884 (interactive "P")
1885 (if (or noconfirm
1886 (yes-or-no-p "Revert form to unmodified? "))
1887 (progn
1888 (set-buffer-modified-p nil)
1889 (forms-jump-record forms--current-record))))
1890
1891 (defun forms-next-field (arg)
1892 "Jump to ARG-th next field."
1893 (interactive "p")
1894
1895 (let ((i 0)
1896 (here (point))
1897 there
1898 (cnt 0)
1899 (inhibit-point-motion-hooks t))
1900
1901 (if (zerop arg)
1902 (setq cnt 1)
1903 (setq cnt (+ cnt arg)))
1904
1905 (if (catch 'done
1906 (while (< i (length forms--markers))
1907 (if (or (null (setq there (aref forms--markers i)))
1908 (<= there here))
1909 nil
1910 (if (<= (setq cnt (1- cnt)) 0)
1911 (progn
1912 (goto-char there)
1913 (throw 'done t))))
1914 (setq i (1+ i))))
1915 nil
1916 (goto-char (aref forms--markers 0)))))
1917
1918 (defun forms-prev-field (arg)
1919 "Jump to ARG-th previous field."
1920 (interactive "p")
1921
1922 (let ((i (length forms--markers))
1923 (here (point))
1924 there
1925 (cnt 0)
1926 (inhibit-point-motion-hooks t))
1927
1928 (if (zerop arg)
1929 (setq cnt 1)
1930 (setq cnt (+ cnt arg)))
1931
1932 (if (catch 'done
1933 (while (> i 0)
1934 (setq i ( 1- i))
1935 (if (or (null (setq there (aref forms--markers i)))
1936 (>= there here))
1937 nil
1938 (if (<= (setq cnt (1- cnt)) 0)
1939 (progn
1940 (goto-char there)
1941 (throw 'done t))))))
1942 nil
1943 (goto-char (aref forms--markers (1- (length forms--markers)))))))
1944
1945 (defun forms-print ()
1946 "Send the records to the printer with 'print-buffer', one record per page."
1947 (interactive)
1948 (let ((inhibit-read-only t)
1949 (save-record forms--current-record)
1950 (nb-record 1)
1951 (record nil))
1952 (while (<= nb-record forms--total-records)
1953 (forms-jump-record nb-record)
1954 (setq record (buffer-string))
1955 (save-excursion
1956 (set-buffer (get-buffer-create "*forms-print*"))
1957 (goto-char (buffer-end 1))
1958 (insert record)
1959 (setq buffer-read-only nil)
1960 (if (< nb-record forms--total-records)
1961 (insert "\n\f\n")))
1962 (setq nb-record (1+ nb-record)))
1963 (save-excursion
1964 (set-buffer "*forms-print*")
1965 (print-buffer)
1966 (set-buffer-modified-p nil)
1967 (kill-buffer (current-buffer)))
1968 (forms-jump-record save-record)))
1969
1970 ;;;
1971 ;;; Special service
1972 ;;;
1973 (defun forms-enumerate (the-fields)
1974 "Take a quoted list of symbols, and set their values to sequential numbers.
1975 The first symbol gets number 1, the second 2 and so on.
1976 It returns the higest number.
1977
1978 Usage: (setq forms-number-of-fields
1979 (forms-enumerate
1980 '(field1 field2 field2 ...)))"
1981
1982 (let ((the-index 0))
1983 (while the-fields
1984 (setq the-index (1+ the-index))
1985 (let ((el (car-safe the-fields)))
1986 (setq the-fields (cdr-safe the-fields))
1987 (set el the-index)))
1988 the-index))
1989 \f
1990 ;;; Debugging
1991
1992 (defvar forms--debug nil
1993 "*Enables forms-mode debugging if not nil.")
1994
1995 (defun forms--debug (&rest args)
1996 "Internal debugging routine."
1997 (if forms--debug
1998 (let ((ret nil))
1999 (while args
2000 (let ((el (car-safe args)))
2001 (setq args (cdr-safe args))
2002 (if (stringp el)
2003 (setq ret (concat ret el))
2004 (setq ret (concat ret (prin1-to-string el) " = "))
2005 (if (boundp el)
2006 (let ((vel (eval el)))
2007 (setq ret (concat ret (prin1-to-string vel) "\n")))
2008 (setq ret (concat ret "<unbound>" "\n")))
2009 (if (fboundp el)
2010 (setq ret (concat ret (prin1-to-string (symbol-function el))
2011 "\n"))))))
2012 (save-excursion
2013 (set-buffer (get-buffer-create "*forms-mode debug*"))
2014 (if (zerop (buffer-size))
2015 (emacs-lisp-mode))
2016 (goto-char (point-max))
2017 (insert ret)))))
2018
2019 ;;; forms.el ends here.