;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;;  Copyright (c) 1988 Microelectronics and Computer Technology Corporation
;;;
;;; File: 	field.el
;;; Created: 	Sun Jan 10 10:16:17 1988
;;; Author: 	Frank Halasz (halasz@babyhalasz)
;;;
;;; Description: Implementation of fields which are markers that span regions.
;;; 
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;  A field is like a marker but it defines a region rather than a
;;;  point.  Like a marker, a field is aasocated with a buffer.
;;;  The field mechanism uses the marker mechanism in the
;;;  sense that its start and end points are maintained as markers
;;;  updated in the usual way as the buffer changes.
;;;  A field can be protected or unprotected.  If it is protected,
;;;  no modifications can be made that affect the field in its buffer 
;;;  (see insdel.c and field.c).  Finally, an arbitrary data object
;;;  can be associated with a field.

;;;  Fields are currently implemented using Lisp vectors.  This is to
;;;  avoid having to make a new lisp type and deal with all the
;;;  garbage collection issues, etc.  At some later point, it may make
;;;  sense to make them into a lisp data type of their own.

(defvar buffer-fields nil)

(defun add-field (start end &optional buffer protected data)
  "Add a new field to BUFFER that starts at START (inclusive)and ends at
END (exclusive).
START and END can be character numbers or markers.
If PROTECTED is non-nil, then the field
will be protected from insertion and deletion.
ALIST (optional) is the initial value for the field's alist.
Returns the field object (which is actually a vector)."

  ;; nil buffer means current buffer
  (if (null buffer)
      (setq buffer (current-buffer)))

  ;; transform marker args (if any) in character positions
  (if (markerp start)
      (setq start (marker-position start)))
  (if (markerp end)
      (setq end (marker-position end)))

  ;; Reverse start and end if necessary
  (if (> start end)
      (let (temp)
	(setq temp start
	      start end
	      end temp)))
   
  ;; Make the field, fill in the slots, hook the field into buffer's
  ;; field chain.
  (let ((field (vector 'field buffer
		       ;; markers stick to the preceeding character!!!!!
		       (set-marker (make-marker) (1+ start) buffer)
		       (set-marker (make-marker) (+ 0 end) buffer)
		       protected
		       data)))
    (save-excursion
      (set-buffer buffer)
      (setq buffer-fields (cons field buffer-fields)))
  
    field))

(defun region-field ()
  (add-field (region-beginning) (region-end)))

(defun protected-region-field ()
  (add-field (region-beginning) (region-end) (current-buffer) t nil))

(defmacro field-buffer (field)
  "Return the buffer that FIELD is associated with."
  (list 'aref field 1))
  
(defmacro field-start (field)
  "Return the character number of the current starting point of FIELD"
  ;; markers stick to the preceeding character
  (list '1- (list 'aref field 2)))

(defmacro field-end (field)
  "Return the character number of the current end point of FIELD"
  (list 'aref field 3))

(defmacro field-protected (field)
  "Return t is FIELD is protected, nil otherwise."
  (list 'aref field 4))

(defmacro field-alist (field)
  "Return the data associated with FIELD."
  (list 'aref field 5))

(defmacro set-field-protected (field protected)
  "Set the protection status of FIELD to PROTECTED."
  (list 'aset field 4 protected))
  
(defmacro set-field-alist (field alist)
  "Set the data associate with FIELD to be ALIST."
  (list 'aset field 5 alist))

(defun delete-field (field)
  "Delete field FIELD, in whichever buffer it belongs to."
  (save-excursion
    (set-buffer (field-buffer field))
    (setq buffer-field-list
	  (delq field buffer-field-list))))