/*===========================================================================
  Copyright (C) 1989-2009 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++
.NAME		tfield.c
.TYPE           Module
.LANGUAGE       C
.KEYWORDS	Forms
.ENVIRONMENT    TermWindows
.AUTHOR		Alan Richmond, Francois Ochsenbein 
.COMMENTS
	This module contains the basic operations on Fields:
	creation, destruction, retrieval, remove.
\begin{TeX}
	The field types can be:
	\begin{itemize}
        \item {\tt i$n$}	Integer ($n=$1, 2 or 4)
        \item {\tt d$n$}	Floating-point number
        \item {\tt c$n$}	Character, case sensitive
        \item {\tt C$n$}	Character, case insensitive
        \item {\tt $*$}		Action field
	\end{itemize}
	{\bf Modifiers} can be
	\begin{itemize}
	\item {\tt t}	Time (origin 1-Jan-1980)
	\item {\tt T}	Time (origin 1-Jan-1970)
        \item {\tt D}	Display-only field
        \item {\tt E}	No-echo field (typically for Passwords)
        \item {\tt M}	Field CAN BE modifed by other fields.
        \item {\tt R}	Raw Field (Display Control Characters)
        \end{itemize}

	The field types may be:
	integer float string action
\end{TeX}

.VERSION 1.0 	07-Jun-1989: Extracted from Proteus.

 090902         last modif
-----------------*/

#define	PM_LEVEL	LEVEL_TF

#define  PASCAL_DEF	0	/* Don't include Pascalisation 	*/

#include <stdlib.h>	/* Standard definitions	*/
#include <midas_def.h>	/* Standard definitions	*/

#include <stesodef.h>	/* Standard definitions	*/
#include <tform.h>	/* for form values	*/
#include <str.h>	/* String utilities	*/
#include <tra.h>	/* Conversion utilities	*/


extern int mm_free();

extern int tx_display();

extern int ta_ret(), ta_error();

extern int tv_dim(), tv_supply(), tv_where(), tv_bell();

extern int tw_fill(), tw_attr(), tw_uattr(), tw_r(), tw_goto(), tw_rule();
extern int tw_st(), tw_uflag(), tw_chars(), tw_copw(), tw_flags();
extern int tw_uw(), tw_write(), tw_il(), tw_clear(), tw_dc();
extern int tw_nl(), tw_upw(), tw_uc(), tw_mvc(), tw_copy();
extern int tw_mrule(), tw_cus(), tw_gc2(), tw_close(), tw_cur();
extern int tw_home(), tw_tra(), tw_wa(), tw_agoto(), tw_mods();
extern int tw_cline(), tw_stopin(), tw_ms2(), tw_wf(), tw_awhere();
extern int tw_getc(), tw_helps(), tw_where(), tw_tr();

extern int pm_enter(), pm_iexit(), pm_tr2(), pm_ed_i();
extern int pm_trace(), pm_ed_trace();

extern int eh_put1(), eh_clear(), eh_ed_as();

extern int tff_goto(), tff_convert(), tff_edit(), tff_display();
extern int tff_putnote(), tff_move(), tff_oshow();
extern int tff_new(), tff_dset(), tff_oshow();


#define FINISH			goto FIN

/*=====================================================================*/ 
int tff_anew(form, name, type, edit, text, attr, action)
/*++++++++ 
.PURPOSE Install a new Field as
	name_of_Field (blank) Field.
	Items are aligned.
.RETURNS OK / NOK (Doesn't fit)
.REMARKS 
	The size of the field is defined by text or edit, depending on type.
-------------*/
	TFORM	*form;		/* IN: The Form	concerned */
	char	*name;		/* IN: field name	*/
	char	*type;		/* IN: field type	*/
	char	*edit;		/* IN: field picture OR action	*/
	char	*text;		/* IN: Text in the Field	*/
	int	attr;		/* IN: Field Attribute (video)	*/
	TF_FCT	action;		/* IN: Function (TFORM) action 	*/
{
	int	ln, len, nb, total_len, i, j, status;
	WINDOW	*w;
	static	char blanks[9] = "        ";
	short	where[2];
	
  ENTER("tff_anew");
  nb = ln = 0;

  status = NOK;
  w = form->window;
  i = w->pos/w->dim[1];	j = w->pos%w->dim[1];	/* Current Position */

	/* Determine the length of the Field */
  
  if (*type == '*')	   		/* Action Field */
  	len = strlen(text);
  else 	if (toupper(*type) == 'C')	/* Char Field  */
  	len = atoi(type+1);
  else	len = strlen(edit);

	/* See if it fits on the Line */

  if (*type != '*')	   		/* Write Field Name */
  {
  	ln = strlen(name);	
  	nb = 7 - ((j+ln)&7);		/* Padding Blanks	*/
  	total_len = ln+nb+len+1;
  	if ((j+total_len) > w->dim[1]) /* Name doesn't fit 	*/
  		i++, j = 0, w->pos = w->dim[1]*i, 
  		nb = 7 - (ln&7);
  	total_len = ln+nb+len+1;
  }
  else	total_len = len;

  if ((w->pos + total_len) > w->marker[1])
  	FINISH;

  if (*type != '*')	   		/* Write Field Name */
  	Write(w, blanks, nb),
  	Write(w, name, ln),
  	Write(w, blanks, 1);		

  j = SetAttr(w, attr);		/* Save Old Attribute	*/
  
  GetCursor(w, where);

  nb = len;			/* Blanks to fill field	*/
  
  if (text)			/* Write Out the Text	*/
  {
	ln = strlen(text);	ln = MIN(ln, len);
	Write(w, text, ln);
	nb -= ln;		/* Remaining blanks	*/
  }
  
  for(; nb > 0; nb -= 8)	/* Write Blanks		*/
  	Write(w, blanks, MIN(nb, 8));	

  SetAttr(w, j);		/* Reset Old Attribute	*/

  w->pos += 1;			/* For Next field	*/
  
	/* Install New Field	*/

  status = tff_new(form, name, type, edit, 
  		where[0], where[1], 1, len, action) ;
  if (status > 0)	status = OK;

  FIN:
  EXIT(status);
}

/*=====================================================================*/ 
int tff_new(form, name, type, edit, line, col, lines, cols, action) 
/*++++++++ 
.PURPOSE Load a form field with no default, and format picture. 
.RETURNS Maximum length of field (0 if field cannot fit on Window)
.REMARKS 
	The field size is (lines * cols). When lines>1, a multiline field is
	created; note that the number of columns cols may be larger than
	the window width. 
	Values of zero for lines and cols lead to defaults computed from
	other parameters.
	The attribute and the contents of the field are taken directly
	from the Window. 

	Scaling factors may be specified in the type, to be applied on values
	before display. In this implementation we only treat integer divisors. 
	
	Strings supplied for name, type, picture are NOT saved (only 
	addresses are kept). 
	
	The current field in the form is the new one.
-------------*/
	TFORM	*form;		/* IN: The Form	concerned */
	char	*name;		/* IN: field name	*/
	char	*type;		/* IN: field type	*/
	char	*edit;		/* IN: field picture 	*/
	int	line;		/* IN: screen home row (from 0)	*/
	int	col;		/* IN: screen home col	(from 0)*/
	int	lines;		/* IN: Number of Lines or 0	*/
	int	cols;		/* IN: Number of Columns  or 0	*/
	TF_FCT	action;		/* IN: Function (TFORM) action 	*/
{
	int	fn, i;		/* field number		*/
	TFIELD	*field;
	int	flen;		/* max. length		*/
	char	*p;
	WINDOW	*w;
	int	old_pos;

  ENTER("+tff_new");

  if ( (lines == 0) && cols)	lines = 1;
  flen = lines * cols;

  fn = (form->nfields)++;
  if (fn >= form->mfields)
  {
  	ERR_ED_STRING(" Form capacity exceeded for ", form->name);
  	flen = 0;
  	FINISH;
  }

  form->ifield = fn;
  field = form->fields + fn;
  w = form->window;
  old_pos = w->pos;
  CursorTo (w, line, col);
	
  p = (type ? type : ""); 

	/* --- Insert the New Field --- */
	
  field->name		= name;   	/* field name	*/
  field->pos[0]		= line;    	/* row 		*/
  field->pos[1]		= col;     	/* col 		*/
					/* Get and Reset Video */
  field->attr     	= SetAttr(w, SetAttr(w,_ATTRIBUTE_)); 
  field->type		= _DTYPE_STR;	/* 	 default type	*/

  if (flen)
  	field->string_size	= ++flen,	/* value string maxlen  */
	field->string     	= MEM_GET(char, flen),	/* value string */
	*(field->string)	= EOS;
  else	field->string_size	= 0,		/* value string maxlen  */
  	field->string     	= NULL_PTR(char);

  i = strlen(p); i = MIN(i, 255);

  field->value.action = action;	/* Action */
  
  field->picture	= edit;  	/* picture		*/
  field->factor 	= 1;		/* Default Factor	*/
  field->options	= _FIELD_TOSCREEN | _FIELD_TOFILE |_FIELD_MODIFIED;
  field->parm_size	= i;		/* strlen(p)		*/
  field->parm		= p;		/* e.g. i4		*/
  field->note		= NULL_PTR(char);	/* Optional Note*/



	/* Derive the type of Data as _DTYPE_INT, double, string */

  tff_dset(field, field->parm);		/* Routine in TFPIC	*/

  			/* non-string fields can't lay over several lines */
  if_not(ischarField(field))	lines = 1;

	/* Reformat the Parameter String (replace spaces with zeros) 
	  ... but take case of possible text within quotes !
	*/

  p	+= strscan(p, _SPACE_);

  while (*p)
  {
	if (isspace(*p))	*(p++) = EOS; 
	else if (*p == '=')
	{	if (*++p == '"')
		{	++p, p += strloc(p, '"');
			if (*p)	*(p++) = EOS;
		}
		p += strscan(p, _SPACE_);
	}
	else	p += strloc(p, '=');
  }


	/* Open Subwindow if more than 1 line	*/

  if (lines > 1)
  {	
	field->value.window = OpenSubWindow(w, name, line, col, 
		lines, cols, field->attr, 0, 0);
	field->attr = _NORMAL_;
	w = field->value.window;
	EnableArrows(w);
  }

  	/* Determine now the bin type / factor
  	 */

  field->bintype	= field->type;	/* Bin.  default type	*/
  field->binfactor 	= field->factor;/* Default Bin Factor	*/
  if (field->bintype == _DTYPE_INT)	/* Convert to Float	*/
  {
	p = edit + strloc(edit, '.');
	if (*p)
	{
  		field->bintype = _DTYPE_DOUBLE;	/* Convert to Float	*/
		while (*++p == '9')	field->binfactor *= 10.e0;
	}
  }

  	/* Populate the Field with what exists on the Window:
  	 * and convert if necessary
  	 */

  if (flen)	TranslateWindow (w, field->string, --flen),
  		field->string[flen] = EOS,
  		tff_convert(field, field->string);

  SetPosition(w, old_pos);

  FIN:
  EXIT(flen);
}

/*=====================================================================*/ 
char *tff_parm(form, parm)
/*++++++++++++
.PURPOSE Retrieve a parameter (form name=) in the current field
.RETURNS Pointer to the text follwing the = sign / NULL if not found
.REMARKS Parameter is of the form parm=value (NO BLANKS)
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
	char	*parm;	/* IN: The parameter to look for, e.g. `O='	*/
{
	TFIELD	*field;
	char	*p, *pe;
	int	len;

  ENTER("*tff_parm");

  len = strlen(parm);

  field = form->fields + form->ifield;
  p = field->parm, pe = p + field->parm_size;

  while (p < pe)
  {	
	if (oscomp(parm, p, len) == 0)	break;
	p += 1 + strlen(p);
  }

  if (p < pe)
  {	p += len;
  	if (*p == '"')	p++;
  }
  else	p = NULL_PTR(char);

  EXITp(p);
}

/*=====================================================================*/ 
int tff_find(form, name)
/*++++++++++++
.PURPOSE Retrieve a named field.
.RETURNS The number of the named field / -1 if not found.
.REMARKS Comparison is case sensitive.
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
	char	*name;	/* IN: The field to retrieve 	*/
{
	TFIELD	*field;
	int	fn;

  ENTER("+tff_find");

  for(field = form->fields, fn = form->nfields; --fn >= 0; field++)
  	if (strcomp(name, field->name) == 0)	break;
  
  if (fn >= 0)	fn = field - form->fields;

  EXIT(fn);
}

/*=====================================================================*/ 
TFIELD *tff_ptr(form, name)
/*++++++++++++
.PURPOSE Retrieve a named field pointer.
.RETURNS The pointer to the found Field / NULL when not found
.REMARKS Comparison is case sensitive.
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
	char	*name;	/* IN: The field to retrieve 	*/
{
	TFIELD	*field;
	int	fn;

  ENTER("*tff_ptr");

  for(field = form->fields, fn = form->nfields; --fn >= 0; field++)
  	if (strcomp(name, field->name) == 0)	break;
  
  if (fn < 0)	field = NULL_PTR(TFIELD);

  EXIT_PTR(TFIELD, field);
}

/*=====================================================================*/ 
TF_FCT tff_aset(form, name, action)
/*++++++++++++
.PURPOSE Associate an action (routine) to action field.
.RETURNS The previous associated action / NULL if field not found or 
		not action field.
.REMARKS 
---------------*/
	TFORM	*form;	/* IN: The form concerned			*/
	char	*name;	/* IN: The field to associate with action	*/
	TF_FCT	action;	/* IN: Function called as action(form) 		*/
{
	TFIELD	*field;
	int	fn;
	TF_FCT	old_action;

  ENTER("*tff_aset");

  old_action = (TF_FCT)0;

  fn = tff_find(form, name);	/* Find Field	*/
  if (fn >= 0)			/* Field Found	*/
  {
  	field = form->fields + fn;
  	if (isactionField(field))	old_action = field->value.action,
  					field->value.action = action;
	else	ERR_ED_STRING("Field is NOT an action: ", name);
  }
  else	ERR_ED_STRING("Field not found: ", name);

  EXIT_CAST(TF_FCT, old_action);
}

/*=====================================================================*/ 
int tff_goto(form, fieldno)
/*++++++++++++
.PURPOSE Move to a specified field.
.RETURNS The old field number
.REMARKS The attribute of the field is NOT set.
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
	int	fieldno;/* IN: The field to go to 	*/
{
	TFIELD	*field;
	int	old_field;

  ENTER("+tff_goto");

  old_field = form->ifield;
  
  if ( (fieldno < 0) || (fieldno >= form->nfields))
  	form->ifield = form->nfields - 1;
  else	form->ifield = fieldno;

  field = form->fields + form->ifield;
  
  CursorTo(form->window, field->pos[0], field->pos[1]);
  
	/* Put in place the address of the Current Field */
  switch(field->type & 0xf)
  {
    case _DTYPE_INT:
    	form->avalue = (int *)&(field->value.integer);	break;

    case _DTYPE_DOUBLE: case _DTYPE_FLOAT:
    	form->avalue = (int *)&(field->value.real);	break;

    default:		/* Assume String */
    	form->avalue = (int *)field->string;		break;
  }

  EXIT(old_field);
}

/*=====================================================================*/ 
int tff_move(form, direction)
/*++++++++++++
.PURPOSE Move to an adjacent field.
.RETURNS The old field number
.REMARKS The attribute of the field is NOT set.
---------------*/
	TFORM	*form;		/* IN: The form concerned	*/
	int	direction;	/* IN: _UP_ _DOWN_ etc. OR'ed with 16 \
				   if DISPLAY-ONLY must be taken into account*/
{
	TFIELD	*pf;
	int	i, imax, the_dir;
	short	pos[2];

  ENTER("+tff_move");

  GetCursor(form->window, pos);
  
  i = form->ifield;	pf = form->fields + i;
  imax = form->nfields - 1;
  the_dir = direction;

  while(1)			/* For Ever ... */
  {
  	pf = form->fields + i;

  	switch (the_dir&0xf)
  	{
    	  case _LEFT_	:	i--;			break;
	  	default :	
    	  case _RIGHT_  :	i++;			break;

    	  case _UP_	:	/* Go first to previous line ... */
		while ((i > 0)    && (pf->pos[0] == pos[0]))	pf--, i--;
		if (i <= 0)	break;
		pos[0] = pf->pos[0];
		while ((i > 0)    && (pf->pos[0] == pos[0]))	pf--, i--;
		if (pos[0] != pf->pos[0]) pf++, i++;
		goto case_vertical;
    	  case _DOWN_	:	/* Go first to next line ...	*/
		while ((i < imax) && (pf->pos[0] == pos[0]))	pf++, i++;
		if (i >= imax)	break;
	  case_vertical:	/* Move to the right until a field is found */
		pos[0] = pf->pos[0];	/* Line */
		while ((pf->pos[0] == pos[0]) && 
			((pf->pos[1] + pf->string_size) < pos[1]))
			i++, pf++;
		if (pf->pos[0] != pos[0])	pf--, i--;
		break;

	  case _HOME_	:	i = 0;
	  	break;
  	}

  	if (i < 0)	i += form->nfields;
	if (i > imax)	i = 0;
  	i %= form->nfields;
	pf = form->fields + i;

  	if_not(the_dir&0x10) 			break;
  	if_not(pf->options & _FIELD_DISPLAY) 	break;
  		/* Field cannot be modified by user... */
	if(the_dir & 2)	continue;	/* Horizontal displacement */
	if (i == 0)	the_dir = (the_dir & ~0xf) |_RIGHT_;
	if (i == imax)	the_dir = (the_dir & ~0xf) |_LEFT_;
  }
	
  EXIT(tff_goto(form, i));
}

/*=====================================================================*/ 
int tff_cur(form)
/*++++++++++++
.PURPOSE Retrieve the current field.
.RETURNS The current field number (-1 if none)
.REMARKS 
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
{
  return(form->ifield);
}

/*=====================================================================*/ 
TFIELD *tff_field(form, fieldno)
/*++++++++++++
.PURPOSE Retrieve the specified field pointer.
.RETURNS The current field pointer / NULL if Bad
.REMARKS 
---------------*/
	TFORM	*form;		/* IN: The form concerned	*/
	int	fieldno;	/* IN: Number of Field		*/
{
  	TFIELD	*field;

  field = NULL_PTR(TFIELD);
  if ((fieldno >= 0) && (fieldno < form->nfields))
	field = form->fields + fieldno;

  return(field);
}

/*=====================================================================*/ 
char *tff_name(form)
/*++++++++++++
.PURPOSE Retrieve the current field name
.RETURNS The current field name pointer.
.REMARKS 
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
{
	TFIELD 	*field;
	
  field = form->fields + form->ifield;
  return(field->name);
}

/*=====================================================================*/ 
char *tff_string(form)
/*++++++++++++
.PURPOSE Retrieve the current field contents (string).
.RETURNS The current field pointer.
.REMARKS 
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
{
	TFIELD 	*field;
	
  field = form->fields + form->ifield;
  return(field->string);
}

/*=====================================================================*/ 
long tff_ival(form)
/*++++++++++++
.PURPOSE Retrieve the current field contents (integer).
.RETURNS The integer value in the current field.
.REMARKS Non-numeric data retun the NULL.
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
{
	TFIELD 	*field;
	long	value;
	
  field = form->fields + form->ifield;
  switch(field->type & 7)
  { 
    case _DTYPE_INT:	value = field->value.integer;	break;
    case _DTYPE_DOUBLE:	value = field->value.real;	break;
    default:		value = 0;			break;
  }
  
  return(value);
}

/*=====================================================================*/ 
double tff_fval(form)
/*++++++++++++
.PURPOSE Retrieve the current field contents (double).
.RETURNS The floating-point value in the current field.
.REMARKS Non-numeric data retun the NULL.
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
{
	TFIELD 	*field;
	double	value;
	
  field = form->fields + form->ifield;
  switch(field->type & 7)
  { 
    case _DTYPE_INT:	value = field->value.integer / field->binfactor;
    			break;
    case _DTYPE_DOUBLE:	value = field->value.real / field->binfactor;
    			break;
    default:		value = 0;			break;
  }
  
  return(value);
}

/*=====================================================================*/ 
int tff_isnull(form)
/*++++++++++++
.PURPOSE Tells if the current field has NULL value.
.RETURNS 0 (not null) / 1 (null)
.REMARKS 
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
{
	TFIELD 	*field;
	int	value;
	
  field = form->fields + form->ifield;
  switch(field->type & 7)
  { 
    case _DTYPE_INT:	
    	value = (field->value.intnull[0] == field->value.intnull[1]);
		    	break;
    default:
    	value = 0;	break;
  }
  
  return(value);
}

/*=====================================================================*/ 
char *tff_pic(form)
/*++++++++++++
.PURPOSE Find the picture of the current field
.RETURNS Pointer to current picture field.
.REMARKS 
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
{
	TFIELD 	*field;
	
  field = form->fields + form->ifield;
  return(field->picture);
}

/*=====================================================================*/ 
char *tff_dtype(form)
/*++++++++++++
.PURPOSE Find the type of the current field
.RETURNS Pointer to current type field.
.REMARKS 
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
{
	TFIELD 	*field;
	
  field = form->fields + form->ifield;
  return(field->parm);
}

/*=====================================================================*/ 
char *tff_note(form)
/*++++++++++++
.PURPOSE Find the note associated to the current field
.RETURNS Pointer to current type field.
.REMARKS 
---------------*/
	TFORM	*form;	/* IN: The form concerned	*/
{
	TFIELD 	*field;
	
  field = form->fields + form->ifield;
  return(field->note);
}

/*=====================================================================*/
int tff_oclear(form, mask)
/*++++++++++++
.PURPOSE Clear option in current field
.RETURNS The status as it was before clearing.
------------------------------------------------------------------------*/
	TFORM	*form;	/* IN: The form concerned	*/
	int	mask;	/* IN: The mask	*/
{
	int	i;
	TFIELD	*field;

  field = form->fields + form->ifield;
  i = field->options & mask;

  field->options &= ~mask;

  return (i);
}

/*=====================================================================*/
int tff_oset(form, mask)
/*++++++++++++
.PURPOSE Set options in form
.RETURNS The status as it was before setting.
------------------------------------------------------------------------*/
	TFORM	*form;	/* IN: The form concerned	*/
	int	mask;	/* IN: The mask	*/
{
	int	i;
	TFIELD	*field;

  field = form->fields + form->ifield;
  i = field->options & mask;

  field->options |= mask;

  return (i);
}

/*=====================================================================*/
int tff_oget(form, mask)
/*++++++++++++
.PURPOSE Get options in current field of form.
.RETURNS 0 if option not present / not-zero if option present
------------------------------------------------------------------------*/
	TFORM	*form;	/* IN: The form concerned	*/
	int	mask;	/* IN: The mask	*/
{
	TFIELD	*field;

  field = form->fields + form->ifield;

  return(field->options & mask);
}

/*=====================================================================*/
int tff_putnote (form, note)
/*+++++++++++
.PURPOSE Insert or delete a note in the current field.
.RETURNS OK / NOK.
--------------*/
	TFORM	*form;		/* IN: The field concerned	*/
	char	*note;		/* IN: Note to insert (NULL to delete)	*/
{
	int	len;
	TFIELD	*field;

  ENTER("tff_note");

  if (note)	TRACE_ED_STRING("Note: ", note);
  if (note)	if (*note == EOS)	note = NULL_PTR(char);

  field = form->fields + form->ifield;

  if (note)
  {	len = strlen(note) + 1;
	field->note = MEM_EXP(char, field->note, len);
	strcopy(field->note, note);
  }
  else
  {	MEM_FREE(field->note);
	field->note = NULL_PTR(char);
  }

  EXIT(OK);
}
