xref: /freebsd/contrib/ncurses/form/frm_driver.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
1 /****************************************************************************
2  * Copyright (c) 1998 Free Software Foundation, Inc.                        *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 
29 /****************************************************************************
30  *   Author: Juergen Pfeifer <juergen.pfeifer@gmx.net> 1995,1997            *
31  ****************************************************************************/
32 #include "form.priv.h"
33 
34 MODULE_ID("$Id: frm_driver.c,v 1.35 1999/05/16 17:20:52 juergen Exp $")
35 
36 /*----------------------------------------------------------------------------
37   This is the core module of the form library. It contains the majority
38   of the driver routines as well as the form_driver function.
39 
40   Essentially this module is nearly the whole library. This is because
41   all the functions in this module depends on some others in the module,
42   so it makes no sense to split them into separate files because they
43   will always be linked together. The only acceptable concern is turnaround
44   time for this module, but now we have all Pentiums or Riscs, so what!
45 
46   The driver routines are grouped into nine generic categories:
47 
48    a)   Page Navigation            ( all functions prefixed by PN_ )
49         The current page of the form is left and some new page is
50         entered.
51    b)   Inter-Field Navigation     ( all functions prefixed by FN_ )
52         The current field of the form is left and some new field is
53         entered.
54    c)   Intra-Field Navigation     ( all functions prefixed by IFN_ )
55         The current position in the current field is changed.
56    d)   Vertical Scrolling         ( all functions prefixed by VSC_ )
57         Esseantially this is a specialization of Intra-Field navigation.
58         It has to check for a multi-line field.
59    e)   Horizontal Scrolling       ( all functions prefixed by HSC_ )
60         Esseantially this is a specialization of Intra-Field navigation.
61         It has to check for a single-line field.
62    f)   Field Editing              ( all functions prefixed by FE_ )
63         The content of the current field is changed
64    g)   Edit Mode requests         ( all functions prefixed by EM_ )
65         Switching between insert and overlay mode
66    h)   Field-Validation requests  ( all functions prefixed by FV_ )
67         Perform verifications of the field.
68    i)   Choice requests            ( all functions prefixed by CR_ )
69         Requests to enumerate possible field values
70   --------------------------------------------------------------------------*/
71 
72 /*----------------------------------------------------------------------------
73   Some remarks on the placements of assert() macros :
74   I use them only on "strategic" places, i.e. top level entries where
75   I want to make sure that things are set correctly. Throughout subordinate
76   routines I omit them mostly.
77   --------------------------------------------------------------------------*/
78 
79 /*
80 Some options that may effect compatibility in behavior to SVr4 forms,
81 but they are here to allow a more intuitive and user friendly behaviour of
82 our form implementation. This doesn't affect the API, so we feel it is
83 uncritical.
84 
85 The initial implementation tries to stay very close with the behaviour
86 of the original SVr4 implementation, although in some areas it is quite
87 clear that this isn't the most appropriate way. As far as possible this
88 sources will allow you to build a forms lib that behaves quite similar
89 to SVr4, but now and in the future we will give you better options.
90 Perhaps at some time we will make this configurable at runtime.
91 */
92 
93 /* Implement a more user-friendly previous/next word behaviour */
94 #define FRIENDLY_PREV_NEXT_WORD (1)
95 /* Fix the wrong behaviour for forms with all fields inactive */
96 #define FIX_FORM_INACTIVE_BUG (1)
97 /* Allow dynamic field growth also when navigating past the end */
98 #define GROW_IF_NAVIGATE (1)
99 
100 /*----------------------------------------------------------------------------
101   Forward references to some internally used static functions
102   --------------------------------------------------------------------------*/
103 static int Inter_Field_Navigation ( int (* const fct) (FORM *), FORM * form );
104 static int FN_Next_Field (FORM * form);
105 static int FN_Previous_Field (FORM * form);
106 static int FE_New_Line(FORM *);
107 static int FE_Delete_Previous(FORM *);
108 
109 /*----------------------------------------------------------------------------
110   Macro Definitions.
111 
112   Some Remarks on that: I use the convention to use UPPERCASE for constants
113   defined by Macros. If I provide a macro as a kind of inline routine to
114   provide some logic, I use my Upper_Lower case style.
115   --------------------------------------------------------------------------*/
116 
117 /* Calculate the position of a single row in a field buffer */
118 #define Position_Of_Row_In_Buffer(field,row) ((row)*(field)->dcols)
119 
120 /* Calculate start address for the fields buffer# N */
121 #define Address_Of_Nth_Buffer(field,N) \
122   ((field)->buf + (N)*(1+Buffer_Length(field)))
123 
124 /* Calculate the start address of the row in the fields specified buffer# N */
125 #define Address_Of_Row_In_Nth_Buffer(field,N,row) \
126   (Address_Of_Nth_Buffer(field,N) + Position_Of_Row_In_Buffer(field,row))
127 
128 /* Calculate the start address of the row in the fields primary buffer */
129 #define Address_Of_Row_In_Buffer(field,row) \
130   Address_Of_Row_In_Nth_Buffer(field,0,row)
131 
132 /* Calculate the start address of the row in the forms current field
133    buffer# N */
134 #define Address_Of_Current_Row_In_Nth_Buffer(form,N) \
135    Address_Of_Row_In_Nth_Buffer((form)->current,N,(form)->currow)
136 
137 /* Calculate the start address of the row in the forms current field
138    primary buffer */
139 #define Address_Of_Current_Row_In_Buffer(form) \
140    Address_Of_Current_Row_In_Nth_Buffer(form,0)
141 
142 /* Calculate the address of the cursor in the forms current field
143    primary buffer */
144 #define Address_Of_Current_Position_In_Nth_Buffer(form,N) \
145    (Address_Of_Current_Row_In_Nth_Buffer(form,N) + (form)->curcol)
146 
147 /* Calculate the address of the cursor in the forms current field
148    buffer# N */
149 #define Address_Of_Current_Position_In_Buffer(form) \
150   Address_Of_Current_Position_In_Nth_Buffer(form,0)
151 
152 /* Logic to decide wether or not a field is actually a field with
153    vertical or horizontal scrolling */
154 #define Is_Scroll_Field(field)          \
155    (((field)->drows > (field)->rows) || \
156     ((field)->dcols > (field)->cols))
157 
158 /* Logic to decide whether or not a field needs to have an individual window
159    instead of a derived window because it contains invisible parts.
160    This is true for non-public fields and for scrollable fields. */
161 #define Has_Invisible_Parts(field)     \
162   (!((field)->opts & O_PUBLIC)      || \
163    Is_Scroll_Field(field))
164 
165 /* Logic to decide whether or not a field needs justification */
166 #define Justification_Allowed(field)        \
167    (((field)->just != NO_JUSTIFICATION)  && \
168     (Single_Line_Field(field))           && \
169     (((field)->dcols == (field)->cols)   && \
170     ((field)->opts & O_STATIC))             )
171 
172 /* Logic to determine whether or not a dynamic field may still grow */
173 #define Growable(field) ((field)->status & _MAY_GROW)
174 
175 /* Macro to set the attributes for a fields window */
176 #define Set_Field_Window_Attributes(field,win) \
177 (  wbkgdset((win),(chtype)((field)->pad | (field)->back)), \
178    wattrset((win),(field)->fore) )
179 
180 /* Logic to decide whether or not a field really appears on the form */
181 #define Field_Really_Appears(field)         \
182   ((field->form)                          &&\
183    (field->form->status & _POSTED)        &&\
184    (field->opts & O_VISIBLE)              &&\
185    (field->page == field->form->curpage))
186 
187 /* Logic to determine whether or not we are on the first position in the
188    current field */
189 #define First_Position_In_Current_Field(form) \
190   (((form)->currow==0) && ((form)->curcol==0))
191 
192 
193 #define Minimum(a,b) (((a)<=(b)) ? (a) : (b))
194 #define Maximum(a,b) (((a)>=(b)) ? (a) : (b))
195 
196 /*---------------------------------------------------------------------------
197 |   Facility      :  libnform
198 |   Function      :  static char *Get_Start_Of_Data(char * buf, int blen)
199 |
200 |   Description   :  Return pointer to first non-blank position in buffer.
201 |                    If buffer is empty return pointer to buffer itself.
202 |
203 |   Return Values :  Pointer to first non-blank position in buffer
204 +--------------------------------------------------------------------------*/
205 INLINE static char *Get_Start_Of_Data(char * buf, int blen)
206 {
207   char *p   = buf;
208   char *end = &buf[blen];
209 
210   assert(buf && blen>=0);
211   while( (p < end) && is_blank(*p) )
212     p++;
213   return( (p==end) ? buf : p );
214 }
215 
216 /*---------------------------------------------------------------------------
217 |   Facility      :  libnform
218 |   Function      :  static char *After_End_Of_Data(char * buf, int blen)
219 |
220 |   Description   :  Return pointer after last non-blank position in buffer.
221 |                    If buffer is empty, return pointer to buffer itself.
222 |
223 |   Return Values :  Pointer to position after last non-blank position in
224 |                    buffer.
225 +--------------------------------------------------------------------------*/
226 INLINE static char *After_End_Of_Data(char * buf,int blen)
227 {
228   char *p   = &buf[blen];
229 
230   assert(buf && blen>=0);
231   while( (p>buf) && is_blank(p[-1]) )
232     p--;
233   return( p );
234 }
235 
236 /*---------------------------------------------------------------------------
237 |   Facility      :  libnform
238 |   Function      :  static char *Get_First_Whitespace_Character(
239 |                                     char * buf, int   blen)
240 |
241 |   Description   :  Position to the first whitespace character.
242 |
243 |   Return Values :  Pointer to first whitespace character in buffer.
244 +--------------------------------------------------------------------------*/
245 INLINE static char *Get_First_Whitespace_Character(char * buf, int blen)
246 {
247   char *p   = buf;
248   char *end = &p[blen];
249 
250   assert(buf && blen>=0);
251   while( (p < end) && !is_blank(*p))
252     p++;
253   return( (p==end) ? buf : p );
254 }
255 
256 /*---------------------------------------------------------------------------
257 |   Facility      :  libnform
258 |   Function      :  static char *After_Last_Whitespace_Character(
259 |                                     char * buf, int blen)
260 |
261 |   Description   :  Get the position after the last whitespace character.
262 |
263 |   Return Values :  Pointer to position after last whitespace character in
264 |                    buffer.
265 +--------------------------------------------------------------------------*/
266 INLINE static char *After_Last_Whitespace_Character(char * buf, int blen)
267 {
268   char *p   = &buf[blen];
269 
270   assert(buf && blen>=0);
271   while( (p>buf) && !is_blank(p[-1]) )
272     p--;
273   return( p );
274 }
275 
276 /* Set this to 1 to use the div_t version. This is a good idea if your
277    compiler has an intrinsic div() support. Unfortunately GNU-C has it
278    not yet.
279    N.B.: This only works if form->curcol follows immediately form->currow
280          and both are of type int.
281 */
282 #define USE_DIV_T (0)
283 
284 /*---------------------------------------------------------------------------
285 |   Facility      :  libnform
286 |   Function      :  static void Adjust_Cursor_Position(
287 |                                       FORM * form, const char * pos)
288 |
289 |   Description   :  Set current row and column of the form to values
290 |                    corresponding to the buffer position.
291 |
292 |   Return Values :  -
293 +--------------------------------------------------------------------------*/
294 INLINE static void Adjust_Cursor_Position(FORM * form, const char * pos)
295 {
296   FIELD *field;
297   int idx;
298 
299   field = form->current;
300   assert( pos >= field->buf && field->dcols > 0);
301   idx = (int)( pos - field->buf );
302 #if USE_DIV_T
303   *((div_t *)&(form->currow)) = div(idx,field->dcols);
304 #else
305   form->currow = idx / field->dcols;
306   form->curcol = idx - field->cols * form->currow;
307 #endif
308   if ( field->drows < form->currow )
309     form->currow = 0;
310 }
311 
312 /*---------------------------------------------------------------------------
313 |   Facility      :  libnform
314 |   Function      :  static void Buffer_To_Window(
315 |                                      const FIELD  * field,
316 |                                      WINDOW * win)
317 |
318 |   Description   :  Copy the buffer to the window. If its a multiline
319 |                    field, the buffer is split to the lines of the
320 |                    window without any editing.
321 |
322 |   Return Values :  -
323 +--------------------------------------------------------------------------*/
324 static void Buffer_To_Window(const FIELD  * field, WINDOW * win)
325 {
326   int width, height;
327   int len;
328   int row;
329   char *pBuffer;
330 
331   assert(win && field);
332 
333   width  = getmaxx(win);
334   height = getmaxy(win);
335 
336   for(row=0, pBuffer=field->buf;
337       row < height;
338       row++, pBuffer += width )
339     {
340       if ((len = (int)( After_End_Of_Data( pBuffer, width ) - pBuffer )) > 0)
341 	{
342 	  wmove( win, row, 0 );
343 	  waddnstr( win, pBuffer, len );
344 	}
345     }
346 }
347 
348 /*---------------------------------------------------------------------------
349 |   Facility      :  libnform
350 |   Function      :  static void Window_To_Buffer(
351 |                                          WINDOW * win,
352 |                                          FIELD  * field)
353 |
354 |   Description   :  Copy the content of the window into the buffer.
355 |                    The multiple lines of a window are simply
356 |                    concatenated into the buffer. Pad characters in
357 |                    the window will be replaced by blanks in the buffer.
358 |
359 |   Return Values :  -
360 +--------------------------------------------------------------------------*/
361 static void Window_To_Buffer(WINDOW * win, FIELD  * field)
362 {
363   int pad;
364   int len = 0;
365   char *p;
366   int row, height;
367 
368   assert(win && field && field->buf );
369 
370   pad = field->pad;
371   p = field->buf;
372   height = getmaxy(win);
373 
374   for(row=0; (row < height) && (row < field->drows); row++ )
375     {
376       wmove( win, row, 0 );
377       len += winnstr( win, p+len, field->dcols );
378     }
379   p[len] = '\0';
380 
381   /* replace visual padding character by blanks in buffer */
382   if (pad != C_BLANK)
383     {
384       int i;
385       for(i=0; i<len; i++, p++)
386 	{
387 	  if (*p==pad)
388 	    *p = C_BLANK;
389 	}
390     }
391 }
392 
393 /*---------------------------------------------------------------------------
394 |   Facility      :  libnform
395 |   Function      :  static void Synchronize_Buffer(FORM * form)
396 |
397 |   Description   :  If there was a change, copy the content of the
398 |                    window into the buffer, so the buffer is synchronized
399 |                    with the windows content. We have to indicate that the
400 |                    buffer needs validation due to the change.
401 |
402 |   Return Values :  -
403 +--------------------------------------------------------------------------*/
404 INLINE static void Synchronize_Buffer(FORM * form)
405 {
406   if (form->status & _WINDOW_MODIFIED)
407     {
408       form->status &= ~_WINDOW_MODIFIED;
409       form->status |=  _FCHECK_REQUIRED;
410       Window_To_Buffer(form->w,form->current);
411       wmove(form->w,form->currow,form->curcol);
412     }
413 }
414 
415 /*---------------------------------------------------------------------------
416 |   Facility      :  libnform
417 |   Function      :  static bool Field_Grown( FIELD *field, int amount)
418 |
419 |   Description   :  This function is called for growable dynamic fields
420 |                    only. It has to increase the buffers and to allocate
421 |                    a new window for this field.
422 |                    This function has the side effect to set a new
423 |                    field-buffer pointer, the dcols and drows values
424 |                    as well as a new current Window for the field.
425 |
426 |   Return Values :  TRUE     - field successfully increased
427 |                    FALSE    - there was some error
428 +--------------------------------------------------------------------------*/
429 static bool Field_Grown(FIELD * field, int amount)
430 {
431   bool result = FALSE;
432 
433   if (field && Growable(field))
434     {
435       bool single_line_field = Single_Line_Field(field);
436       int old_buflen = Buffer_Length(field);
437       int new_buflen;
438       int old_dcols = field->dcols;
439       int old_drows = field->drows;
440       char *oldbuf  = field->buf;
441       char *newbuf;
442 
443       int growth;
444       FORM *form = field->form;
445       bool need_visual_update = ((form != (FORM *)0)      &&
446 				 (form->status & _POSTED) &&
447 				 (form->current==field));
448 
449       if (need_visual_update)
450 	Synchronize_Buffer(form);
451 
452       if (single_line_field)
453 	{
454 	  growth = field->cols * amount;
455 	  if (field->maxgrow)
456 	    growth = Minimum(field->maxgrow - field->dcols,growth);
457 	  field->dcols += growth;
458 	  if (field->dcols == field->maxgrow)
459 	    field->status &= ~_MAY_GROW;
460 	}
461       else
462 	{
463 	  growth = (field->rows + field->nrow) * amount;
464 	  if (field->maxgrow)
465 	    growth = Minimum(field->maxgrow - field->drows,growth);
466 	  field->drows += growth;
467 	  if (field->drows == field->maxgrow)
468 	    field->status &= ~_MAY_GROW;
469 	}
470       /* drows, dcols changed, so we get really the new buffer length */
471       new_buflen = Buffer_Length(field);
472       newbuf=(char *)malloc((size_t)Total_Buffer_Size(field));
473       if (!newbuf)
474 	{ /* restore to previous state */
475 	  field->dcols = old_dcols;
476 	  field->drows = old_drows;
477 	  if (( single_line_field && (field->dcols!=field->maxgrow)) ||
478 	      (!single_line_field && (field->drows!=field->maxgrow)))
479 	    field->status |= _MAY_GROW;
480 	  return FALSE;
481 	}
482       else
483 	{ /* Copy all the buffers. This is the reason why we can't
484 	     just use realloc().
485 	     */
486 	  int i;
487 	  char *old_bp;
488 	  char *new_bp;
489 
490 	  field->buf = newbuf;
491 	  for(i=0;i<=field->nbuf;i++)
492 	    {
493 	      new_bp = Address_Of_Nth_Buffer(field,i);
494 	      old_bp = oldbuf + i*(1+old_buflen);
495 	      memcpy(new_bp,old_bp,(size_t)old_buflen);
496 	      if (new_buflen > old_buflen)
497 		memset(new_bp + old_buflen,C_BLANK,
498 		       (size_t)(new_buflen - old_buflen));
499 	      *(new_bp + new_buflen) = '\0';
500 	    }
501 
502 	  if (need_visual_update)
503 	    {
504 	      WINDOW *new_window = newpad(field->drows,field->dcols);
505 	      if (!new_window)
506 		{ /* restore old state */
507 		  field->dcols = old_dcols;
508 		  field->drows = old_drows;
509 		  field->buf   = oldbuf;
510 		  if (( single_line_field              &&
511 			(field->dcols!=field->maxgrow)) ||
512 		      (!single_line_field              &&
513 		       (field->drows!=field->maxgrow)))
514 		    field->status |= _MAY_GROW;
515 		  free( newbuf );
516 		  return FALSE;
517 		}
518 	      assert(form!=(FORM *)0);
519 	      delwin(form->w);
520 	      form->w = new_window;
521 	      Set_Field_Window_Attributes(field,form->w);
522 	      werase(form->w);
523 	      Buffer_To_Window(field,form->w);
524 	      untouchwin(form->w);
525 	      wmove(form->w,form->currow,form->curcol);
526 	    }
527 
528 	  free(oldbuf);
529 	  /* reflect changes in linked fields */
530 	  if (field != field->link)
531 	    {
532 	      FIELD *linked_field;
533 	      for(linked_field = field->link;
534 		  linked_field!= field;
535 		  linked_field = linked_field->link)
536 		{
537 		  linked_field->buf   = field->buf;
538 		  linked_field->drows = field->drows;
539 		  linked_field->dcols = field->dcols;
540 		}
541 	    }
542 	  result = TRUE;
543 	}
544     }
545   return(result);
546 }
547 
548 /*---------------------------------------------------------------------------
549 |   Facility      :  libnform
550 |   Function      :  int _nc_Position_Form_Cursor(FORM * form)
551 |
552 |   Description   :  Position the cursor in the window for the current
553 |                    field to be in sync. with the currow and curcol
554 |                    values.
555 |
556 |   Return Values :  E_OK              - success
557 |                    E_BAD_ARGUMENT    - invalid form pointer
558 |                    E_SYSTEM_ERROR    - form has no current field or
559 |                                        field-window
560 +--------------------------------------------------------------------------*/
561 int
562 _nc_Position_Form_Cursor(FORM * form)
563 {
564   FIELD  *field;
565   WINDOW *formwin;
566 
567   if (!form)
568     return(E_BAD_ARGUMENT);
569 
570   if (!form->w || !form->current)
571     return(E_SYSTEM_ERROR);
572 
573   field    = form->current;
574   formwin  = Get_Form_Window(form);
575 
576   wmove( form->w, form->currow, form->curcol );
577   if ( Has_Invisible_Parts(field) )
578     {
579       /* in this case fieldwin isn't derived from formwin, so we have
580 	 to move the cursor in formwin by hand... */
581       wmove(formwin,
582 	    field->frow + form->currow - form->toprow,
583 	    field->fcol + form->curcol - form->begincol);
584       wcursyncup(formwin);
585     }
586   else
587     wcursyncup(form->w);
588   return(E_OK);
589 }
590 
591 /*---------------------------------------------------------------------------
592 |   Facility      :  libnform
593 |   Function      :  int _nc_Refresh_Current_Field(FORM * form)
594 |
595 |   Description   :  Propagate the changes in the fields window to the
596 |                    window of the form.
597 |
598 |   Return Values :  E_OK              - on success
599 |                    E_BAD_ARGUMENT    - invalid form pointer
600 |                    E_SYSTEM_ERROR    - general error
601 +--------------------------------------------------------------------------*/
602 int
603 _nc_Refresh_Current_Field(FORM * form)
604 {
605   WINDOW *formwin;
606   FIELD  *field;
607 
608   if (!form)
609     RETURN(E_BAD_ARGUMENT);
610 
611   if (!form->w || !form->current)
612     RETURN(E_SYSTEM_ERROR);
613 
614   field    = form->current;
615   formwin  = Get_Form_Window(form);
616 
617   if (field->opts & O_PUBLIC)
618     {
619       if (Is_Scroll_Field(field))
620 	{
621 	  /* Again, in this case the fieldwin isn't derived from formwin,
622 	     so we have to perform a copy operation. */
623 	  if (Single_Line_Field(field))
624 	    { /* horizontal scrolling */
625 	      if (form->curcol < form->begincol)
626 		  form->begincol = form->curcol;
627 	      else
628 		{
629 		  if (form->curcol >= (form->begincol + field->cols))
630 		      form->begincol = form->curcol - field->cols + 1;
631 		}
632 	      copywin(form->w,
633 		      formwin,
634 		      0,
635 		      form->begincol,
636 		      field->frow,
637 		      field->fcol,
638 		      field->frow,
639 		      field->cols + field->fcol - 1,
640 		      0);
641 	    }
642 	  else
643 	    { /* A multiline, i.e. vertical scrolling field */
644 	      int row_after_bottom,first_modified_row,first_unmodified_row;
645 
646 	      if (field->drows > field->rows)
647 		{
648 		  row_after_bottom = form->toprow + field->rows;
649 		  if (form->currow < form->toprow)
650 		    {
651 		      form->toprow = form->currow;
652 		      field->status |= _NEWTOP;
653 		    }
654 		  if (form->currow >= row_after_bottom)
655 		    {
656 		      form->toprow = form->currow - field->rows + 1;
657 		      field->status |= _NEWTOP;
658 		    }
659 		  if (field->status & _NEWTOP)
660 		    { /* means we have to copy whole range */
661 		      first_modified_row = form->toprow;
662 		      first_unmodified_row = first_modified_row + field->rows;
663 		      field->status &= ~_NEWTOP;
664 		    }
665 		  else
666 		    { /* we try to optimize : finding the range of touched
667                          lines */
668 		      first_modified_row = form->toprow;
669 		      while(first_modified_row < row_after_bottom)
670 			{
671 			  if (is_linetouched(form->w,first_modified_row))
672 			    break;
673 			  first_modified_row++;
674 			}
675 		      first_unmodified_row = first_modified_row;
676 		      while(first_unmodified_row < row_after_bottom)
677 			{
678 			  if (!is_linetouched(form->w,first_unmodified_row))
679 			    break;
680 			  first_unmodified_row++;
681 			}
682 		    }
683 		}
684 	      else
685 		{
686 		  first_modified_row   = form->toprow;
687 		  first_unmodified_row = first_modified_row + field->rows;
688 		}
689 	      if (first_unmodified_row != first_modified_row)
690 		copywin(form->w,
691 			formwin,
692 			first_modified_row,
693 			0,
694 			field->frow + first_modified_row - form->toprow,
695 			field->fcol,
696 			field->frow + first_unmodified_row - form->toprow - 1,
697 			field->cols + field->fcol - 1,
698 			0);
699 	    }
700 	  wsyncup(formwin);
701 	}
702       else
703 	{ /* if the field-window is simply a derived window, i.e. contains
704 	     no invisible parts, the whole thing is trivial
705 	  */
706 	  wsyncup(form->w);
707 	}
708     }
709   untouchwin(form->w);
710   return _nc_Position_Form_Cursor(form);
711 }
712 
713 /*---------------------------------------------------------------------------
714 |   Facility      :  libnform
715 |   Function      :  static void Perform_Justification(
716 |                                        FIELD  * field,
717 |                                        WINDOW * win)
718 |
719 |   Description   :  Output field with requested justification
720 |
721 |   Return Values :  -
722 +--------------------------------------------------------------------------*/
723 static void Perform_Justification(FIELD  * field, WINDOW * win)
724 {
725   char *bp;
726   int len;
727   int col  = 0;
728 
729   bp  = Get_Start_Of_Data(field->buf,Buffer_Length(field));
730   len = (int)(After_End_Of_Data(field->buf,Buffer_Length(field)) - bp);
731 
732   if (len>0)
733     {
734       assert(win && (field->drows == 1) && (field->dcols == field->cols));
735 
736       switch(field->just)
737 	{
738 	case JUSTIFY_LEFT:
739 	  break;
740 	case JUSTIFY_CENTER:
741 	  col = (field->cols - len)/2;
742 	  break;
743 	case JUSTIFY_RIGHT:
744 	  col = field->cols - len;
745 	  break;
746 	default:
747 	  break;
748 	}
749 
750       wmove(win,0,col);
751       waddnstr(win,bp,len);
752     }
753 }
754 
755 /*---------------------------------------------------------------------------
756 |   Facility      :  libnform
757 |   Function      :  static void Undo_Justification(
758 |                                     FIELD  * field,
759 |                                     WINDOW * win)
760 |
761 |   Description   :  Display field without any justification, i.e.
762 |                    left justified
763 |
764 |   Return Values :  -
765 +--------------------------------------------------------------------------*/
766 static void Undo_Justification(FIELD  * field, WINDOW * win)
767 {
768   char *bp;
769   int len;
770 
771   bp  = Get_Start_Of_Data(field->buf,Buffer_Length(field));
772   len = (int)(After_End_Of_Data(field->buf,Buffer_Length(field))-bp);
773 
774   if (len>0)
775     {
776       assert(win);
777       wmove(win,0,0);
778       waddnstr(win,bp,len);
779     }
780 }
781 
782 /*---------------------------------------------------------------------------
783 |   Facility      :  libnform
784 |   Function      :  static bool Check_Char(
785 |                                           FIELDTYPE * typ,
786 |                                           int ch,
787 |                                           TypeArgument *argp)
788 |
789 |   Description   :  Perform a single character check for character ch
790 |                    according to the fieldtype instance.
791 |
792 |   Return Values :  TRUE             - Character is valid
793 |                    FALSE            - Character is invalid
794 +--------------------------------------------------------------------------*/
795 static bool Check_Char(FIELDTYPE * typ, int ch, TypeArgument *argp)
796 {
797   if (typ)
798     {
799       if (typ->status & _LINKED_TYPE)
800 	{
801 	  assert(argp);
802 	  return(
803 	    Check_Char(typ->left ,ch,argp->left ) ||
804 	    Check_Char(typ->right,ch,argp->right) );
805 	}
806       else
807 	{
808 	  if (typ->ccheck)
809 	    return typ->ccheck(ch,(void *)argp);
810 	}
811     }
812   return (isprint((unsigned char)ch) ? TRUE : FALSE);
813 }
814 
815 /*---------------------------------------------------------------------------
816 |   Facility      :  libnform
817 |   Function      :  static int Display_Or_Erase_Field(
818 |                                           FIELD * field,
819 |                                           bool bEraseFlag)
820 |
821 |   Description   :  Create a subwindow for the field and display the
822 |                    buffer contents (apply justification if required)
823 |                    or simply erase the field.
824 |
825 |   Return Values :  E_OK           - on success
826 |                    E_SYSTEM_ERROR - some error (typical no memory)
827 +--------------------------------------------------------------------------*/
828 static int Display_Or_Erase_Field(FIELD * field, bool bEraseFlag)
829 {
830   WINDOW *win;
831   WINDOW *fwin;
832 
833   if (!field)
834     return E_SYSTEM_ERROR;
835 
836   fwin = Get_Form_Window(field->form);
837   win  = derwin(fwin,
838 		field->rows,field->cols,field->frow,field->fcol);
839 
840   if (!win)
841     return E_SYSTEM_ERROR;
842   else
843     {
844       if (field->opts & O_VISIBLE)
845 	Set_Field_Window_Attributes(field,win);
846       else
847 	wattrset(win,getattrs(fwin));
848       werase(win);
849     }
850 
851   if (!bEraseFlag)
852     {
853       if (field->opts & O_PUBLIC)
854 	{
855 	  if (Justification_Allowed(field))
856 	    Perform_Justification(field,win);
857 	  else
858 	    Buffer_To_Window(field,win);
859 	}
860       field->status &= ~_NEWTOP;
861     }
862   wsyncup(win);
863   delwin(win);
864   return E_OK;
865 }
866 
867 /* Macros to preset the bEraseFlag */
868 #define Display_Field(field) Display_Or_Erase_Field(field,FALSE)
869 #define Erase_Field(field)   Display_Or_Erase_Field(field,TRUE)
870 
871 /*---------------------------------------------------------------------------
872 |   Facility      :  libnform
873 |   Function      :  static int Synchronize_Field(FIELD * field)
874 |
875 |   Description   :  Synchronize the windows content with the value in
876 |                    the buffer.
877 |
878 |   Return Values :  E_OK                - success
879 |                    E_BAD_ARGUMENT      - invalid field pointer
880 |                    E_SYSTEM_ERROR      - some severe basic error
881 +--------------------------------------------------------------------------*/
882 static int Synchronize_Field(FIELD * field)
883 {
884   FORM *form;
885   int res = E_OK;
886 
887   if (!field)
888     return(E_BAD_ARGUMENT);
889 
890   if (((form=field->form) != (FORM *)0)
891       && Field_Really_Appears(field))
892     {
893       if (field == form->current)
894 	{
895 	  form->currow  = form->curcol = form->toprow = form->begincol = 0;
896 	  werase(form->w);
897 
898 	  if ( (field->opts & O_PUBLIC) && Justification_Allowed(field) )
899 	    Undo_Justification( field, form->w );
900 	  else
901 	    Buffer_To_Window( field, form->w );
902 
903 	  field->status |= _NEWTOP;
904 	  res = _nc_Refresh_Current_Field( form );
905 	}
906       else
907 	res = Display_Field( field );
908     }
909   field->status |= _CHANGED;
910   return(res);
911 }
912 
913 /*---------------------------------------------------------------------------
914 |   Facility      :  libnform
915 |   Function      :  static int Synchronize_Linked_Fields(FIELD * field)
916 |
917 |   Description   :  Propagate the Synchronize_Field function to all linked
918 |                    fields. The first error that occurs in the sequence
919 |                    of updates is the returnvalue.
920 |
921 |   Return Values :  E_OK                - success
922 |                    E_BAD_ARGUMENT      - invalid field pointer
923 |                    E_SYSTEM_ERROR      - some severe basic error
924 +--------------------------------------------------------------------------*/
925 static int Synchronize_Linked_Fields(FIELD * field)
926 {
927   FIELD *linked_field;
928   int res = E_OK;
929   int syncres;
930 
931   if (!field)
932     return(E_BAD_ARGUMENT);
933 
934   if (!field->link)
935     return(E_SYSTEM_ERROR);
936 
937   for(linked_field = field->link;
938       linked_field!= field;
939       linked_field = linked_field->link )
940     {
941       if (((syncres=Synchronize_Field(linked_field)) != E_OK) &&
942 	  (res==E_OK))
943 	res = syncres;
944     }
945   return(res);
946 }
947 
948 /*---------------------------------------------------------------------------
949 |   Facility      :  libnform
950 |   Function      :  int _nc_Synchronize_Attributes(FIELD * field)
951 |
952 |   Description   :  If a fields visual attributes have changed, this
953 |                    routine is called to propagate those changes to the
954 |                    screen.
955 |
956 |   Return Values :  E_OK             - success
957 |                    E_BAD_ARGUMENT   - invalid field pointer
958 |                    E_SYSTEM_ERROR   - some severe basic error
959 +--------------------------------------------------------------------------*/
960 int _nc_Synchronize_Attributes(FIELD * field)
961 {
962   FORM *form;
963   int res = E_OK;
964   WINDOW *formwin;
965 
966   if (!field)
967     return(E_BAD_ARGUMENT);
968 
969   if (((form=field->form) != (FORM *)0)
970       && Field_Really_Appears(field))
971     {
972       if (form->current==field)
973 	{
974 	  Synchronize_Buffer(form);
975 	  Set_Field_Window_Attributes(field,form->w);
976 	  werase(form->w);
977 	  if (field->opts & O_PUBLIC)
978 	    {
979 	      if (Justification_Allowed(field))
980 		Undo_Justification(field,form->w);
981 	      else
982 		Buffer_To_Window(field,form->w);
983 	    }
984 	  else
985 	    {
986 	      formwin = Get_Form_Window(form);
987 	      copywin(form->w,formwin,
988 		      0,0,
989 		      field->frow,field->fcol,
990 		      field->rows-1,field->cols-1,0);
991 	      wsyncup(formwin);
992 	      Buffer_To_Window(field,form->w);
993 	      field->status |= _NEWTOP; /* fake refresh to paint all */
994 	      _nc_Refresh_Current_Field(form);
995 	    }
996 	}
997       else
998 	{
999 	  res = Display_Field(field);
1000 	}
1001     }
1002   return(res);
1003 }
1004 
1005 /*---------------------------------------------------------------------------
1006 |   Facility      :  libnform
1007 |   Function      :  int _nc_Synchronize_Options(FIELD * field,
1008 |                                                Field_Options newopts)
1009 |
1010 |   Description   :  If a fields options have changed, this routine is
1011 |                    called to propagate these changes to the screen and
1012 |                    to really change the behaviour of the field.
1013 |
1014 |   Return Values :  E_OK                - success
1015 |                    E_BAD_ARGUMENT      - invalid field pointer
1016 |                    E_SYSTEM_ERROR      - some severe basic error
1017 +--------------------------------------------------------------------------*/
1018 int
1019 _nc_Synchronize_Options(FIELD *field, Field_Options newopts)
1020 {
1021   Field_Options oldopts;
1022   Field_Options changed_opts;
1023   FORM *form;
1024   int res = E_OK;
1025 
1026   if (!field)
1027     return(E_BAD_ARGUMENT);
1028 
1029   oldopts      = field->opts;
1030   changed_opts = oldopts ^ newopts;
1031   field->opts  = newopts;
1032   form         = field->form;
1033 
1034   if (form)
1035     {
1036       if (form->current == field)
1037 	{
1038 	  field->opts = oldopts;
1039 	  return(E_CURRENT);
1040 	}
1041 
1042       if (form->status & _POSTED)
1043 	{
1044 	  if ((form->curpage == field->page))
1045 	    {
1046 	      if (changed_opts & O_VISIBLE)
1047 		{
1048 		  if (newopts & O_VISIBLE)
1049 		    res = Display_Field(field);
1050 		  else
1051 		    res = Erase_Field(field);
1052 		}
1053 	      else
1054 		{
1055 		  if ((changed_opts & O_PUBLIC) &&
1056 		      (newopts & O_VISIBLE))
1057 		    res = Display_Field(field);
1058 		}
1059 	    }
1060 	}
1061     }
1062 
1063   if (changed_opts & O_STATIC)
1064     {
1065       bool single_line_field = Single_Line_Field(field);
1066       int res2 = E_OK;
1067 
1068       if (newopts & O_STATIC)
1069 	{ /* the field becomes now static */
1070 	  field->status &= ~_MAY_GROW;
1071 	  /* if actually we have no hidden columns, justification may
1072 	     occur again */
1073 	  if (single_line_field                 &&
1074 	      (field->cols == field->dcols)     &&
1075 	      (field->just != NO_JUSTIFICATION) &&
1076 	      Field_Really_Appears(field))
1077 	    {
1078 	      res2 = Display_Field(field);
1079 	    }
1080 	}
1081       else
1082 	{ /* field is no longer static */
1083 	  if ((field->maxgrow==0) ||
1084 	      ( single_line_field && (field->dcols < field->maxgrow)) ||
1085 	      (!single_line_field && (field->drows < field->maxgrow)))
1086 	    {
1087 	      field->status |= _MAY_GROW;
1088 	      /* a field with justification now changes its behaviour,
1089 		 so we must redisplay it */
1090 	      if (single_line_field                 &&
1091 		  (field->just != NO_JUSTIFICATION) &&
1092 		  Field_Really_Appears(field))
1093 		{
1094 		  res2 = Display_Field(field);
1095 		}
1096 	    }
1097 	}
1098       if (res2 != E_OK)
1099 	res = res2;
1100     }
1101 
1102   return(res);
1103 }
1104 
1105 /*---------------------------------------------------------------------------
1106 |   Facility      :  libnform
1107 |   Function      :  int _nc_Set_Current_Field(FORM  * form,
1108 |                                              FIELD * newfield)
1109 |
1110 |   Description   :  Make the newfield the new current field.
1111 |
1112 |   Return Values :  E_OK                - success
1113 |                    E_BAD_ARGUMENT      - invalid form or field pointer
1114 |                    E_SYSTEM_ERROR      - some severe basic error
1115 +--------------------------------------------------------------------------*/
1116 int
1117 _nc_Set_Current_Field(FORM  *form, FIELD *newfield)
1118 {
1119   FIELD  *field;
1120   WINDOW *new_window;
1121 
1122   if (!form || !newfield || !form->current || (newfield->form!=form))
1123     return(E_BAD_ARGUMENT);
1124 
1125   if ( (form->status & _IN_DRIVER) )
1126     return(E_BAD_STATE);
1127 
1128   if (!(form->field))
1129     return(E_NOT_CONNECTED);
1130 
1131   field = form->current;
1132 
1133   if ((field!=newfield) ||
1134       !(form->status & _POSTED))
1135     {
1136       if ((form->w) &&
1137 	  (field->opts & O_VISIBLE) &&
1138 	  (field->form->curpage == field->page))
1139 	{
1140 	  _nc_Refresh_Current_Field(form);
1141 	  if (field->opts & O_PUBLIC)
1142 	    {
1143 	      if (field->drows > field->rows)
1144 		{
1145 		  if (form->toprow==0)
1146 		    field->status &= ~_NEWTOP;
1147 		  else
1148 		    field->status |= _NEWTOP;
1149 		}
1150 	      else
1151 		{
1152 		  if (Justification_Allowed(field))
1153 		    {
1154 		      Window_To_Buffer(form->w,field);
1155 		      werase(form->w);
1156 		      Perform_Justification(field,form->w);
1157 		      wsyncup(form->w);
1158 		    }
1159 		}
1160 	    }
1161 	  delwin(form->w);
1162 	}
1163 
1164       field = newfield;
1165 
1166       if (Has_Invisible_Parts(field))
1167 	new_window = newpad(field->drows,field->dcols);
1168       else
1169 	new_window = derwin(Get_Form_Window(form),
1170 			    field->rows,field->cols,field->frow,field->fcol);
1171 
1172       if (!new_window)
1173 	return(E_SYSTEM_ERROR);
1174 
1175       form->current = field;
1176       form->w       = new_window;
1177       form->status &= ~_WINDOW_MODIFIED;
1178       Set_Field_Window_Attributes(field,form->w);
1179 
1180       if (Has_Invisible_Parts(field))
1181 	{
1182 	  werase(form->w);
1183 	  Buffer_To_Window(field,form->w);
1184 	}
1185       else
1186 	{
1187 	  if (Justification_Allowed(field))
1188 	    {
1189 	      werase(form->w);
1190 	      Undo_Justification(field,form->w);
1191 	      wsyncup(form->w);
1192 	    }
1193 	}
1194 
1195       untouchwin(form->w);
1196     }
1197 
1198   form->currow = form->curcol = form->toprow = form->begincol = 0;
1199   return(E_OK);
1200 }
1201 
1202 /*----------------------------------------------------------------------------
1203   Intra-Field Navigation routines
1204   --------------------------------------------------------------------------*/
1205 
1206 /*---------------------------------------------------------------------------
1207 |   Facility      :  libnform
1208 |   Function      :  static int IFN_Next_Character(FORM * form)
1209 |
1210 |   Description   :  Move to the next character in the field. In a multiline
1211 |                    field this wraps at the end of the line.
1212 |
1213 |   Return Values :  E_OK                - success
1214 |                    E_REQUEST_DENIED    - at the rightmost position
1215 +--------------------------------------------------------------------------*/
1216 static int IFN_Next_Character(FORM * form)
1217 {
1218   FIELD *field = form->current;
1219 
1220   if ((++(form->curcol))==field->dcols)
1221     {
1222       if ((++(form->currow))==field->drows)
1223 	{
1224 #if GROW_IF_NAVIGATE
1225 	  if (!Single_Line_Field(field) && Field_Grown(field,1)) {
1226 	    form->curcol = 0;
1227 	    return(E_OK);
1228 	  }
1229 #endif
1230 	  form->currow--;
1231 #if GROW_IF_NAVIGATE
1232 	  if (Single_Line_Field(field) && Field_Grown(field,1))
1233 	    return(E_OK);
1234 #endif
1235 	  form->curcol--;
1236 	  return(E_REQUEST_DENIED);
1237 	}
1238       form->curcol = 0;
1239     }
1240   return(E_OK);
1241 }
1242 
1243 /*---------------------------------------------------------------------------
1244 |   Facility      :  libnform
1245 |   Function      :  static int IFN_Previous_Character(FORM * form)
1246 |
1247 |   Description   :  Move to the previous character in the field. In a
1248 |                    multiline field this wraps and the beginning of the
1249 |                    line.
1250 |
1251 |   Return Values :  E_OK                - success
1252 |                    E_REQUEST_DENIED    - at the leftmost position
1253 +--------------------------------------------------------------------------*/
1254 static int IFN_Previous_Character(FORM * form)
1255 {
1256   if ((--(form->curcol))<0)
1257     {
1258       if ((--(form->currow))<0)
1259 	{
1260 	  form->currow++;
1261 	  form->curcol++;
1262 	  return(E_REQUEST_DENIED);
1263 	}
1264       form->curcol = form->current->dcols - 1;
1265     }
1266   return(E_OK);
1267 }
1268 
1269 /*---------------------------------------------------------------------------
1270 |   Facility      :  libnform
1271 |   Function      :  static int IFN_Next_Line(FORM * form)
1272 |
1273 |   Description   :  Move to the beginning of the next line in the field
1274 |
1275 |   Return Values :  E_OK                - success
1276 |                    E_REQUEST_DENIED    - at the last line
1277 +--------------------------------------------------------------------------*/
1278 static int IFN_Next_Line(FORM * form)
1279 {
1280   FIELD *field = form->current;
1281 
1282   if ((++(form->currow))==field->drows)
1283     {
1284 #if GROW_IF_NAVIGATE
1285       if (!Single_Line_Field(field) && Field_Grown(field,1))
1286 	return(E_OK);
1287 #endif
1288       form->currow--;
1289       return(E_REQUEST_DENIED);
1290     }
1291   form->curcol = 0;
1292   return(E_OK);
1293 }
1294 
1295 /*---------------------------------------------------------------------------
1296 |   Facility      :  libnform
1297 |   Function      :  static int IFN_Previous_Line(FORM * form)
1298 |
1299 |   Description   :  Move to the beginning of the previous line in the field
1300 |
1301 |   Return Values :  E_OK                - success
1302 |                    E_REQUEST_DENIED    - at the first line
1303 +--------------------------------------------------------------------------*/
1304 static int IFN_Previous_Line(FORM * form)
1305 {
1306   if ( (--(form->currow)) < 0 )
1307     {
1308       form->currow++;
1309       return(E_REQUEST_DENIED);
1310     }
1311   form->curcol = 0;
1312   return(E_OK);
1313 }
1314 
1315 /*---------------------------------------------------------------------------
1316 |   Facility      :  libnform
1317 |   Function      :  static int IFN_Next_Word(FORM * form)
1318 |
1319 |   Description   :  Move to the beginning of the next word in the field.
1320 |
1321 |   Return Values :  E_OK             - success
1322 |                    E_REQUEST_DENIED - there is no next word
1323 +--------------------------------------------------------------------------*/
1324 static int IFN_Next_Word(FORM * form)
1325 {
1326   FIELD *field = form->current;
1327   char  *bp    = Address_Of_Current_Position_In_Buffer(form);
1328   char  *s;
1329   char  *t;
1330 
1331   /* We really need access to the data, so we have to synchronize */
1332   Synchronize_Buffer(form);
1333 
1334   /* Go to the first whitespace after the current position (including
1335      current position). This is then the startpoint to look for the
1336     next non-blank data */
1337   s = Get_First_Whitespace_Character(bp,Buffer_Length(field) -
1338 				     (int)(bp - field->buf));
1339 
1340   /* Find the start of the next word */
1341   t = Get_Start_Of_Data(s,Buffer_Length(field) -
1342 			(int)(s - field->buf));
1343 #if !FRIENDLY_PREV_NEXT_WORD
1344   if (s==t)
1345     return(E_REQUEST_DENIED);
1346   else
1347 #endif
1348     {
1349       Adjust_Cursor_Position(form,t);
1350       return(E_OK);
1351     }
1352 }
1353 
1354 /*---------------------------------------------------------------------------
1355 |   Facility      :  libnform
1356 |   Function      :  static int IFN_Previous_Word(FORM * form)
1357 |
1358 |   Description   :  Move to the beginning of the previous word in the field.
1359 |
1360 |   Return Values :  E_OK             - success
1361 |                    E_REQUEST_DENIED - there is no previous word
1362 +--------------------------------------------------------------------------*/
1363 static int IFN_Previous_Word(FORM * form)
1364 {
1365   FIELD *field = form->current;
1366   char  *bp    = Address_Of_Current_Position_In_Buffer(form);
1367   char  *s;
1368   char  *t;
1369   bool  again = FALSE;
1370 
1371   /* We really need access to the data, so we have to synchronize */
1372   Synchronize_Buffer(form);
1373 
1374   s = After_End_Of_Data(field->buf,(int)(bp-field->buf));
1375   /* s points now right after the last non-blank in the buffer before bp.
1376      If bp was in a word, s equals bp. In this case we must find the last
1377      whitespace in the buffer before bp and repeat the game to really find
1378      the previous word! */
1379   if (s==bp)
1380     again = TRUE;
1381 
1382   /* And next call now goes backward to look for the last whitespace
1383      before that, pointing right after this, so it points to the begin
1384      of the previous word.
1385   */
1386   t = After_Last_Whitespace_Character(field->buf,(int)(s - field->buf));
1387 #if !FRIENDLY_PREV_NEXT_WORD
1388   if (s==t)
1389     return(E_REQUEST_DENIED);
1390 #endif
1391   if (again)
1392     { /* and do it again, replacing bp by t */
1393       s = After_End_Of_Data(field->buf,(int)(t - field->buf));
1394       t = After_Last_Whitespace_Character(field->buf,(int)(s - field->buf));
1395 #if !FRIENDLY_PREV_NEXT_WORD
1396       if (s==t)
1397 	return(E_REQUEST_DENIED);
1398 #endif
1399     }
1400   Adjust_Cursor_Position(form,t);
1401   return(E_OK);
1402 }
1403 
1404 /*---------------------------------------------------------------------------
1405 |   Facility      :  libnform
1406 |   Function      :  static int IFN_Beginning_Of_Field(FORM * form)
1407 |
1408 |   Description   :  Place the cursor at the first non-pad character in
1409 |                    the field.
1410 |
1411 |   Return Values :  E_OK             - success
1412 +--------------------------------------------------------------------------*/
1413 static int IFN_Beginning_Of_Field(FORM * form)
1414 {
1415   FIELD *field = form->current;
1416 
1417   Synchronize_Buffer(form);
1418   Adjust_Cursor_Position(form,
1419 		 Get_Start_Of_Data(field->buf,Buffer_Length(field)));
1420   return(E_OK);
1421 }
1422 
1423 /*---------------------------------------------------------------------------
1424 |   Facility      :  libnform
1425 |   Function      :  static int IFN_End_Of_Field(FORM * form)
1426 |
1427 |   Description   :  Place the cursor after the last non-pad character in
1428 |                    the field. If the field occupies the last position in
1429 |                    the buffer, the cursos is positioned on the last
1430 |                    character.
1431 |
1432 |   Return Values :  E_OK              - success
1433 +--------------------------------------------------------------------------*/
1434 static int IFN_End_Of_Field(FORM * form)
1435 {
1436   FIELD *field = form->current;
1437   char *pos;
1438 
1439   Synchronize_Buffer(form);
1440   pos = After_End_Of_Data(field->buf,Buffer_Length(field));
1441   if (pos==(field->buf + Buffer_Length(field)))
1442     pos--;
1443   Adjust_Cursor_Position(form,pos);
1444   return(E_OK);
1445 }
1446 
1447 /*---------------------------------------------------------------------------
1448 |   Facility      :  libnform
1449 |   Function      :  static int IFN_Beginning_Of_Line(FORM * form)
1450 |
1451 |   Description   :  Place the cursor on the first non-pad character in
1452 |                    the current line of the field.
1453 |
1454 |   Return Values :  E_OK         - success
1455 +--------------------------------------------------------------------------*/
1456 static int IFN_Beginning_Of_Line(FORM * form)
1457 {
1458   FIELD *field = form->current;
1459 
1460   Synchronize_Buffer(form);
1461   Adjust_Cursor_Position(form,
1462 		 Get_Start_Of_Data(Address_Of_Current_Row_In_Buffer(form),
1463 				   field->dcols));
1464   return(E_OK);
1465 }
1466 
1467 /*---------------------------------------------------------------------------
1468 |   Facility      :  libnform
1469 |   Function      :  static int IFN_End_Of_Line(FORM * form)
1470 |
1471 |   Description   :  Place the cursor after the last non-pad character in the
1472 |                    current line of the field. If the field occupies the
1473 |                    last column in the line, the cursor is positioned on the
1474 |                    last character of the line.
1475 |
1476 |   Return Values :  E_OK        - success
1477 +--------------------------------------------------------------------------*/
1478 static int IFN_End_Of_Line(FORM * form)
1479 {
1480   FIELD *field = form->current;
1481   char *pos;
1482   char *bp;
1483 
1484   Synchronize_Buffer(form);
1485   bp  = Address_Of_Current_Row_In_Buffer(form);
1486   pos = After_End_Of_Data(bp,field->dcols);
1487   if (pos == (bp + field->dcols))
1488     pos--;
1489   Adjust_Cursor_Position(form,pos);
1490   return(E_OK);
1491 }
1492 
1493 /*---------------------------------------------------------------------------
1494 |   Facility      :  libnform
1495 |   Function      :  static int IFN_Left_Character(FORM * form)
1496 |
1497 |   Description   :  Move one character to the left in the current line.
1498 |                    This doesn't cycle.
1499 |
1500 |   Return Values :  E_OK             - success
1501 |                    E_REQUEST_DENIED - already in first column
1502 +--------------------------------------------------------------------------*/
1503 static int IFN_Left_Character(FORM * form)
1504 {
1505   if ( (--(form->curcol)) < 0 )
1506     {
1507       form->curcol++;
1508       return(E_REQUEST_DENIED);
1509     }
1510   return(E_OK);
1511 }
1512 
1513 /*---------------------------------------------------------------------------
1514 |   Facility      :  libnform
1515 |   Function      :  static int IFN_Right_Character(FORM * form)
1516 |
1517 |   Description   :  Move one character to the right in the current line.
1518 |                    This doesn't cycle.
1519 |
1520 |   Return Values :  E_OK              - success
1521 |                    E_REQUEST_DENIED  - already in last column
1522 +--------------------------------------------------------------------------*/
1523 static int IFN_Right_Character(FORM * form)
1524 {
1525   if ( (++(form->curcol)) == form->current->dcols )
1526     {
1527 #if GROW_IF_NAVIGATE
1528       FIELD *field = form->current;
1529       if (Single_Line_Field(field) && Field_Grown(field,1))
1530 	return(E_OK);
1531 #endif
1532       --(form->curcol);
1533       return(E_REQUEST_DENIED);
1534     }
1535   return(E_OK);
1536 }
1537 
1538 /*---------------------------------------------------------------------------
1539 |   Facility      :  libnform
1540 |   Function      :  static int IFN_Up_Character(FORM * form)
1541 |
1542 |   Description   :  Move one line up. This doesn't cycle through the lines
1543 |                    of the field.
1544 |
1545 |   Return Values :  E_OK              - success
1546 |                    E_REQUEST_DENIED  - already in last column
1547 +--------------------------------------------------------------------------*/
1548 static int IFN_Up_Character(FORM * form)
1549 {
1550   if ( (--(form->currow)) < 0 )
1551     {
1552       form->currow++;
1553       return(E_REQUEST_DENIED);
1554     }
1555   return(E_OK);
1556 }
1557 
1558 /*---------------------------------------------------------------------------
1559 |   Facility      :  libnform
1560 |   Function      :  static int IFN_Down_Character(FORM * form)
1561 |
1562 |   Description   :  Move one line down. This doesn't cycle through the
1563 |                    lines of the field.
1564 |
1565 |   Return Values :  E_OK              - success
1566 |                    E_REQUEST_DENIED  - already in last column
1567 +--------------------------------------------------------------------------*/
1568 static int IFN_Down_Character(FORM * form)
1569 {
1570   FIELD *field = form->current;
1571 
1572   if ( (++(form->currow)) == field->drows )
1573     {
1574 #if GROW_IF_NAVIGATE
1575       if (!Single_Line_Field(field) && Field_Grown(field,1))
1576 	return(E_OK);
1577 #endif
1578       --(form->currow);
1579       return(E_REQUEST_DENIED);
1580     }
1581   return(E_OK);
1582 }
1583 /*----------------------------------------------------------------------------
1584   END of Intra-Field Navigation routines
1585   --------------------------------------------------------------------------*/
1586 
1587 /*----------------------------------------------------------------------------
1588   Vertical scrolling helper routines
1589   --------------------------------------------------------------------------*/
1590 
1591 /*---------------------------------------------------------------------------
1592 |   Facility      :  libnform
1593 |   Function      :  static int VSC_Generic(FORM *form, int lines)
1594 |
1595 |   Description   :  Scroll multi-line field forward (lines>0) or
1596 |                    backward (lines<0) this many lines.
1597 |
1598 |   Return Values :  E_OK              - success
1599 |                    E_REQUEST_DENIED  - can't scroll
1600 +--------------------------------------------------------------------------*/
1601 static int VSC_Generic(FORM *form, int lines)
1602 {
1603   FIELD *field = form->current;
1604   int res = E_REQUEST_DENIED;
1605   int rows_to_go = (lines > 0 ? lines : -lines);
1606 
1607   if (lines > 0)
1608     {
1609       if ( (rows_to_go + form->toprow) > (field->drows - field->rows) )
1610 	rows_to_go = (field->drows - field->rows - form->toprow);
1611 
1612       if (rows_to_go > 0)
1613 	{
1614 	  form->currow += rows_to_go;
1615 	  form->toprow += rows_to_go;
1616 	  res = E_OK;
1617 	}
1618     }
1619   else
1620     {
1621       if (rows_to_go > form->toprow)
1622 	rows_to_go = form->toprow;
1623 
1624       if (rows_to_go > 0)
1625 	{
1626 	  form->currow -= rows_to_go;
1627 	  form->toprow -= rows_to_go;
1628 	  res = E_OK;
1629 	}
1630     }
1631   return(res);
1632 }
1633 /*----------------------------------------------------------------------------
1634   End of Vertical scrolling helper routines
1635   --------------------------------------------------------------------------*/
1636 
1637 /*----------------------------------------------------------------------------
1638   Vertical scrolling routines
1639   --------------------------------------------------------------------------*/
1640 
1641 /*---------------------------------------------------------------------------
1642 |   Facility      :  libnform
1643 |   Function      :  static int Vertical_Scrolling(
1644 |                                           int (* const fct) (FORM *),
1645 |                                           FORM * form)
1646 |
1647 |   Description   :  Performs the generic vertical scrolling routines.
1648 |                    This has to check for a multi-line field and to set
1649 |                    the _NEWTOP flag if scrolling really occured.
1650 |
1651 |   Return Values :  Propagated error code from low-level driver calls
1652 +--------------------------------------------------------------------------*/
1653 static int Vertical_Scrolling(int (* const fct) (FORM *), FORM * form)
1654 {
1655   int res = E_REQUEST_DENIED;
1656 
1657   if (!Single_Line_Field(form->current))
1658     {
1659       res = fct(form);
1660       if (res == E_OK)
1661 	form->current->status |= _NEWTOP;
1662     }
1663   return(res);
1664 }
1665 
1666 /*---------------------------------------------------------------------------
1667 |   Facility      :  libnform
1668 |   Function      :  static int VSC_Scroll_Line_Forward(FORM * form)
1669 |
1670 |   Description   :  Scroll multi-line field forward a line
1671 |
1672 |   Return Values :  E_OK                - success
1673 |                    E_REQUEST_DENIED    - no data ahead
1674 +--------------------------------------------------------------------------*/
1675 static int VSC_Scroll_Line_Forward(FORM * form)
1676 {
1677   return VSC_Generic(form,1);
1678 }
1679 
1680 /*---------------------------------------------------------------------------
1681 |   Facility      :  libnform
1682 |   Function      :  static int VSC_Scroll_Line_Backward(FORM * form)
1683 |
1684 |   Description   :  Scroll multi-line field backward a line
1685 |
1686 |   Return Values :  E_OK                - success
1687 |                    E_REQUEST_DENIED    - no data behind
1688 +--------------------------------------------------------------------------*/
1689 static int VSC_Scroll_Line_Backward(FORM * form)
1690 {
1691   return VSC_Generic(form,-1);
1692 }
1693 
1694 /*---------------------------------------------------------------------------
1695 |   Facility      :  libnform
1696 |   Function      :  static int VSC_Scroll_Page_Forward(FORM * form)
1697 |
1698 |   Description   :  Scroll a multi-line field forward a page
1699 |
1700 |   Return Values :  E_OK              - success
1701 |                    E_REQUEST_DENIED  - no data ahead
1702 +--------------------------------------------------------------------------*/
1703 static int VSC_Scroll_Page_Forward(FORM * form)
1704 {
1705   return VSC_Generic(form,form->current->rows);
1706 }
1707 
1708 /*---------------------------------------------------------------------------
1709 |   Facility      :  libnform
1710 |   Function      :  static int VSC_Scroll_Half_Page_Forward(FORM * form)
1711 |
1712 |   Description   :  Scroll a multi-line field forward half a page
1713 |
1714 |   Return Values :  E_OK              - success
1715 |                    E_REQUEST_DENIED  - no data ahead
1716 +--------------------------------------------------------------------------*/
1717 static int VSC_Scroll_Half_Page_Forward(FORM * form)
1718 {
1719   return VSC_Generic(form,(form->current->rows + 1)/2);
1720 }
1721 
1722 /*---------------------------------------------------------------------------
1723 |   Facility      :  libnform
1724 |   Function      :  static int VSC_Scroll_Page_Backward(FORM * form)
1725 |
1726 |   Description   :  Scroll a multi-line field backward a page
1727 |
1728 |   Return Values :  E_OK              - success
1729 |                    E_REQUEST_DENIED  - no data behind
1730 +--------------------------------------------------------------------------*/
1731 static int VSC_Scroll_Page_Backward(FORM * form)
1732 {
1733   return VSC_Generic(form, -(form->current->rows));
1734 }
1735 
1736 /*---------------------------------------------------------------------------
1737 |   Facility      :  libnform
1738 |   Function      :  static int VSC_Scroll_Half_Page_Backward(FORM * form)
1739 |
1740 |   Description   :  Scroll a multi-line field backward half a page
1741 |
1742 |   Return Values :  E_OK              - success
1743 |                    E_REQUEST_DENIED  - no data behind
1744 +--------------------------------------------------------------------------*/
1745 static int VSC_Scroll_Half_Page_Backward(FORM * form)
1746 {
1747   return VSC_Generic(form, -((form->current->rows + 1)/2));
1748 }
1749 /*----------------------------------------------------------------------------
1750   End of Vertical scrolling routines
1751   --------------------------------------------------------------------------*/
1752 
1753 /*----------------------------------------------------------------------------
1754   Horizontal scrolling helper routines
1755   --------------------------------------------------------------------------*/
1756 
1757 /*---------------------------------------------------------------------------
1758 |   Facility      :  libnform
1759 |   Function      :  static int HSC_Generic(FORM *form, int columns)
1760 |
1761 |   Description   :  Scroll single-line field forward (columns>0) or
1762 |                    backward (columns<0) this many columns.
1763 |
1764 |   Return Values :  E_OK              - success
1765 |                    E_REQUEST_DENIED  - can't scroll
1766 +--------------------------------------------------------------------------*/
1767 static int HSC_Generic(FORM *form, int columns)
1768 {
1769   FIELD *field = form->current;
1770   int res = E_REQUEST_DENIED;
1771   int cols_to_go = (columns > 0 ? columns : -columns);
1772 
1773   if (columns > 0)
1774     {
1775       if ((cols_to_go + form->begincol) > (field->dcols - field->cols))
1776 	cols_to_go = field->dcols - field->cols - form->begincol;
1777 
1778       if (cols_to_go > 0)
1779 	{
1780 	  form->curcol   += cols_to_go;
1781 	  form->begincol += cols_to_go;
1782 	  res = E_OK;
1783 	}
1784     }
1785   else
1786     {
1787       if ( cols_to_go > form->begincol )
1788 	cols_to_go = form->begincol;
1789 
1790       if (cols_to_go > 0)
1791 	{
1792 	  form->curcol   -= cols_to_go;
1793 	  form->begincol -= cols_to_go;
1794 	  res = E_OK;
1795 	}
1796     }
1797   return(res);
1798 }
1799 /*----------------------------------------------------------------------------
1800   End of Horizontal scrolling helper routines
1801   --------------------------------------------------------------------------*/
1802 
1803 /*----------------------------------------------------------------------------
1804   Horizontal scrolling routines
1805   --------------------------------------------------------------------------*/
1806 
1807 /*---------------------------------------------------------------------------
1808 |   Facility      :  libnform
1809 |   Function      :  static int Horizontal_Scrolling(
1810 |                                          int (* const fct) (FORM *),
1811 |                                          FORM * form)
1812 |
1813 |   Description   :  Performs the generic horizontal scrolling routines.
1814 |                    This has to check for a single-line field.
1815 |
1816 |   Return Values :  Propagated error code from low-level driver calls
1817 +--------------------------------------------------------------------------*/
1818 static int Horizontal_Scrolling(int (* const fct) (FORM *), FORM * form)
1819 {
1820   if (Single_Line_Field(form->current))
1821     return fct(form);
1822   else
1823     return(E_REQUEST_DENIED);
1824 }
1825 
1826 /*---------------------------------------------------------------------------
1827 |   Facility      :  libnform
1828 |   Function      :  static int HSC_Scroll_Char_Forward(FORM * form)
1829 |
1830 |   Description   :  Scroll single-line field forward a character
1831 |
1832 |   Return Values :  E_OK                - success
1833 |                    E_REQUEST_DENIED    - no data ahead
1834 +--------------------------------------------------------------------------*/
1835 static int HSC_Scroll_Char_Forward(FORM *form)
1836 {
1837   return HSC_Generic(form,1);
1838 }
1839 
1840 /*---------------------------------------------------------------------------
1841 |   Facility      :  libnform
1842 |   Function      :  static int HSC_Scroll_Char_Backward(FORM * form)
1843 |
1844 |   Description   :  Scroll single-line field backward a character
1845 |
1846 |   Return Values :  E_OK                - success
1847 |                    E_REQUEST_DENIED    - no data behind
1848 +--------------------------------------------------------------------------*/
1849 static int HSC_Scroll_Char_Backward(FORM *form)
1850 {
1851   return HSC_Generic(form,-1);
1852 }
1853 
1854 /*---------------------------------------------------------------------------
1855 |   Facility      :  libnform
1856 |   Function      :  static int HSC_Horizontal_Line_Forward(FORM* form)
1857 |
1858 |   Description   :  Scroll single-line field forward a line
1859 |
1860 |   Return Values :  E_OK                - success
1861 |                    E_REQUEST_DENIED    - no data ahead
1862 +--------------------------------------------------------------------------*/
1863 static int HSC_Horizontal_Line_Forward(FORM * form)
1864 {
1865   return HSC_Generic(form,form->current->cols);
1866 }
1867 
1868 /*---------------------------------------------------------------------------
1869 |   Facility      :  libnform
1870 |   Function      :  static int HSC_Horizontal_Half_Line_Forward(FORM* form)
1871 |
1872 |   Description   :  Scroll single-line field forward half a line
1873 |
1874 |   Return Values :  E_OK               - success
1875 |                    E_REQUEST_DENIED   - no data ahead
1876 +--------------------------------------------------------------------------*/
1877 static int HSC_Horizontal_Half_Line_Forward(FORM * form)
1878 {
1879   return HSC_Generic(form,(form->current->cols + 1)/2);
1880 }
1881 
1882 /*---------------------------------------------------------------------------
1883 |   Facility      :  libnform
1884 |   Function      :  static int HSC_Horizontal_Line_Backward(FORM* form)
1885 |
1886 |   Description   :  Scroll single-line field backward a line
1887 |
1888 |   Return Values :  E_OK                - success
1889 |                    E_REQUEST_DENIED    - no data behind
1890 +--------------------------------------------------------------------------*/
1891 static int HSC_Horizontal_Line_Backward(FORM * form)
1892 {
1893   return HSC_Generic(form,-(form->current->cols));
1894 }
1895 
1896 /*---------------------------------------------------------------------------
1897 |   Facility      :  libnform
1898 |   Function      :  static int HSC_Horizontal_Half_Line_Backward(FORM* form)
1899 |
1900 |   Description   :  Scroll single-line field backward half a line
1901 |
1902 |   Return Values :  E_OK                - success
1903 |                    E_REQUEST_DENIED    - no data behind
1904 +--------------------------------------------------------------------------*/
1905 static int HSC_Horizontal_Half_Line_Backward(FORM * form)
1906 {
1907   return HSC_Generic(form,-((form->current->cols + 1)/2));
1908 }
1909 
1910 /*----------------------------------------------------------------------------
1911   End of Horizontal scrolling routines
1912   --------------------------------------------------------------------------*/
1913 
1914 /*----------------------------------------------------------------------------
1915   Helper routines for Field Editing
1916   --------------------------------------------------------------------------*/
1917 
1918 /*---------------------------------------------------------------------------
1919 |   Facility      :  libnform
1920 |   Function      :  static bool Is_There_Room_For_A_Line(FORM * form)
1921 |
1922 |   Description   :  Check whether or not there is enough room in the
1923 |                    buffer to enter a whole line.
1924 |
1925 |   Return Values :  TRUE   - there is enough space
1926 |                    FALSE  - there is not enough space
1927 +--------------------------------------------------------------------------*/
1928 INLINE static bool Is_There_Room_For_A_Line(FORM * form)
1929 {
1930   FIELD *field = form->current;
1931   char *begin_of_last_line, *s;
1932 
1933   Synchronize_Buffer(form);
1934   begin_of_last_line = Address_Of_Row_In_Buffer(field,(field->drows-1));
1935   s  = After_End_Of_Data(begin_of_last_line,field->dcols);
1936   return ((s==begin_of_last_line) ? TRUE : FALSE);
1937 }
1938 
1939 /*---------------------------------------------------------------------------
1940 |   Facility      :  libnform
1941 |   Function      :  static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
1942 |
1943 |   Description   :  Checks whether or not there is room for a new character
1944 |                    in the current line.
1945 |
1946 |   Return Values :  TRUE    - there is room
1947 |                    FALSE   - there is not enough room (line full)
1948 +--------------------------------------------------------------------------*/
1949 INLINE static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
1950 {
1951   int last_char_in_line;
1952 
1953   wmove(form->w,form->currow,form->current->dcols-1);
1954   last_char_in_line  = (int)(winch(form->w) & A_CHARTEXT);
1955   wmove(form->w,form->currow,form->curcol);
1956   return (((last_char_in_line == form->current->pad) ||
1957 	   is_blank(last_char_in_line)) ? TRUE : FALSE);
1958 }
1959 
1960 #define There_Is_No_Room_For_A_Char_In_Line(f) \
1961   !Is_There_Room_For_A_Char_In_Line(f)
1962 
1963 /*---------------------------------------------------------------------------
1964 |   Facility      :  libnform
1965 |   Function      :  static int Insert_String(
1966 |                                             FORM * form,
1967 |                                             int row,
1968 |                                             char *txt,
1969 |                                             int  len )
1970 |
1971 |   Description   :  Insert the 'len' characters beginning at pointer 'txt'
1972 |                    into the 'row' of the 'form'. The insertion occurs
1973 |                    on the beginning of the row, all other characters are
1974 |                    moved to the right. After the text a pad character will
1975 |                    be inserted to separate the text from the rest. If
1976 |                    necessary the insertion moves characters on the next
1977 |                    line to make place for the requested insertion string.
1978 |
1979 |   Return Values :  E_OK              - success
1980 |                    E_REQUEST_DENIED  -
1981 |                    E_SYSTEM_ERROR    - system error
1982 +--------------------------------------------------------------------------*/
1983 static int Insert_String(FORM *form, int row, char *txt, int len)
1984 {
1985   FIELD  *field    = form->current;
1986   char *bp         = Address_Of_Row_In_Buffer(field,row);
1987   int datalen      = (int)(After_End_Of_Data(bp,field->dcols) - bp);
1988   int freelen      = field->dcols - datalen;
1989   int requiredlen  = len+1;
1990   char *split;
1991   int result = E_REQUEST_DENIED;
1992   const char *Space = " ";
1993 
1994   if (freelen >= requiredlen)
1995     {
1996       wmove(form->w,row,0);
1997       winsnstr(form->w,txt,len);
1998       wmove(form->w,row,len);
1999       winsnstr(form->w,Space,1);
2000       return E_OK;
2001     }
2002   else
2003     { /* we have to move characters on the next line. If we are on the
2004 	 last line this may work, if the field is growable */
2005       if ((row == (field->drows - 1)) && Growable(field))
2006 	{
2007 	  if (!Field_Grown(field,1))
2008 	    return(E_SYSTEM_ERROR);
2009 	  /* !!!Side-Effect : might be changed due to growth!!! */
2010 	  bp = Address_Of_Row_In_Buffer(field,row);
2011 	}
2012 
2013       if (row < (field->drows - 1))
2014 	{
2015 	  split = After_Last_Whitespace_Character(bp,
2016 		    (int)(Get_Start_Of_Data(bp + field->dcols - requiredlen ,
2017 					    requiredlen) - bp));
2018 	  /* split points now to the first character of the portion of the
2019 	     line that must be moved to the next line */
2020 	  datalen = (int)(split-bp); /* + freelen has to stay on this line   */
2021 	  freelen = field->dcols - (datalen + freelen); /* for the next line */
2022 
2023 	  if ((result=Insert_String(form,row+1,split,freelen))==E_OK)
2024 	    {
2025 	      wmove(form->w,row,datalen);
2026 	      wclrtoeol(form->w);
2027 	      wmove(form->w,row,0);
2028 	      winsnstr(form->w,txt,len);
2029 	      wmove(form->w,row,len);
2030 	      winsnstr(form->w,Space,1);
2031 	      return E_OK;
2032 	    }
2033 	}
2034       return(result);
2035     }
2036 }
2037 
2038 /*---------------------------------------------------------------------------
2039 |   Facility      :  libnform
2040 |   Function      :  static int Wrapping_Not_Necessary_Or_Wrapping_Ok(
2041 |                                             FORM * form)
2042 |
2043 |   Description   :  If a character has been entered into a field, it may
2044 |                    be that wrapping has to occur. This routine checks
2045 |                    whether or not wrapping is required and if so, performs
2046 |                    the wrapping.
2047 |
2048 |   Return Values :  E_OK              - no wrapping required or wrapping
2049 |                                        was successfull
2050 |                    E_REQUEST_DENIED  -
2051 |                    E_SYSTEM_ERROR    - some system error
2052 +--------------------------------------------------------------------------*/
2053 static int Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM * form)
2054 {
2055   FIELD  *field = form->current;
2056   int result = E_REQUEST_DENIED;
2057   bool Last_Row = ((field->drows - 1) == form->currow);
2058 
2059   if ( (field->opts & O_WRAP)                     &&  /* wrapping wanted     */
2060       (!Single_Line_Field(field))                 &&  /* must be multi-line  */
2061       (There_Is_No_Room_For_A_Char_In_Line(form)) &&  /* line is full        */
2062       (!Last_Row || Growable(field))               )  /* there are more lines*/
2063     {
2064       char *bp;
2065       char *split;
2066       int chars_to_be_wrapped;
2067       int chars_to_remain_on_line;
2068       if (Last_Row)
2069 	{ /* the above logic already ensures, that in this case the field
2070 	     is growable */
2071 	  if (!Field_Grown(field,1))
2072 	    return E_SYSTEM_ERROR;
2073 	}
2074       bp = Address_Of_Current_Row_In_Buffer(form);
2075       Window_To_Buffer(form->w,field);
2076       split = After_Last_Whitespace_Character(bp,field->dcols);
2077       /* split points to the first character of the sequence to be brought
2078          on the next line */
2079       chars_to_remain_on_line = (int)(split - bp);
2080       chars_to_be_wrapped     = field->dcols - chars_to_remain_on_line;
2081       if (chars_to_remain_on_line > 0)
2082 	{
2083 	  if ((result=Insert_String(form,form->currow+1,split,
2084 				    chars_to_be_wrapped)) == E_OK)
2085 	    {
2086 	      wmove(form->w,form->currow,chars_to_remain_on_line);
2087 	      wclrtoeol(form->w);
2088 	      if (form->curcol >= chars_to_remain_on_line)
2089 		{
2090 		  form->currow++;
2091 		  form->curcol -= chars_to_remain_on_line;
2092 		}
2093 	      return E_OK;
2094 	    }
2095 	}
2096       else
2097 	return E_OK;
2098       if (result!=E_OK)
2099 	{
2100 	  wmove(form->w,form->currow,form->curcol);
2101 	  wdelch(form->w);
2102 	  Window_To_Buffer(form->w,field);
2103 	  result = E_REQUEST_DENIED;
2104 	}
2105     }
2106   else
2107     result = E_OK; /* wrapping was not necessary */
2108   return(result);
2109 }
2110 
2111 /*----------------------------------------------------------------------------
2112   Field Editing routines
2113   --------------------------------------------------------------------------*/
2114 
2115 /*---------------------------------------------------------------------------
2116 |   Facility      :  libnform
2117 |   Function      :  static int Field_Editing(
2118 |                                    int (* const fct) (FORM *),
2119 |                                    FORM * form)
2120 |
2121 |   Description   :  Generic routine for field editing requests. The driver
2122 |                    routines are only called for editable fields, the
2123 |                    _WINDOW_MODIFIED flag is set if editing occured.
2124 |                    This is somewhat special due to the overload semantics
2125 |                    of the NEW_LINE and DEL_PREV requests.
2126 |
2127 |   Return Values :  Error code from low level drivers.
2128 +--------------------------------------------------------------------------*/
2129 static int Field_Editing(int (* const fct) (FORM *), FORM * form)
2130 {
2131   int res = E_REQUEST_DENIED;
2132 
2133   /* We have to deal here with the specific case of the overloaded
2134      behaviour of New_Line and Delete_Previous requests.
2135      They may end up in navigational requests if we are on the first
2136      character in a field. But navigation is also allowed on non-
2137      editable fields.
2138   */
2139   if ((fct==FE_Delete_Previous)            &&
2140       (form->opts & O_BS_OVERLOAD)         &&
2141       First_Position_In_Current_Field(form) )
2142     {
2143       res = Inter_Field_Navigation(FN_Previous_Field,form);
2144     }
2145   else
2146     {
2147       if (fct==FE_New_Line)
2148 	{
2149 	  if ((form->opts & O_NL_OVERLOAD)         &&
2150 	      First_Position_In_Current_Field(form))
2151 	    {
2152 	      res = Inter_Field_Navigation(FN_Next_Field,form);
2153 	    }
2154 	  else
2155 	    /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */
2156 	    res = fct(form);
2157 	}
2158       else
2159 	{
2160 	  /* From now on, everything must be editable */
2161 	  if (form->current->opts & O_EDIT)
2162 	    {
2163 	      res = fct(form);
2164 	      if (res==E_OK)
2165 		form->status |= _WINDOW_MODIFIED;
2166 	    }
2167 	}
2168     }
2169   return res;
2170 }
2171 
2172 /*---------------------------------------------------------------------------
2173 |   Facility      :  libnform
2174 |   Function      :  static int FE_New_Line(FORM * form)
2175 |
2176 |   Description   :  Perform a new line request. This is rather complex
2177 |                    compared to other routines in this code due to the
2178 |                    rather difficult to understand description in the
2179 |                    manuals.
2180 |
2181 |   Return Values :  E_OK               - success
2182 |                    E_REQUEST_DENIED   - new line not allowed
2183 |                    E_SYSTEM_ERROR     - system error
2184 +--------------------------------------------------------------------------*/
2185 static int FE_New_Line(FORM * form)
2186 {
2187   FIELD  *field = form->current;
2188   char *bp, *t;
2189   bool Last_Row = ((field->drows - 1)==form->currow);
2190 
2191   if (form->status & _OVLMODE)
2192     {
2193       if (Last_Row &&
2194 	  (!(Growable(field) && !Single_Line_Field(field))))
2195 	{
2196 	  if (!(form->opts & O_NL_OVERLOAD))
2197 	    return(E_REQUEST_DENIED);
2198 	  wclrtoeol(form->w);
2199 	  /* we have to set this here, although it is also
2200 	     handled in the generic routine. The reason is,
2201 	     that FN_Next_Field may fail, but the form is
2202 	     definitively changed */
2203 	  form->status |= _WINDOW_MODIFIED;
2204 	  return Inter_Field_Navigation(FN_Next_Field,form);
2205 	}
2206       else
2207 	{
2208 	  if (Last_Row && !Field_Grown(field,1))
2209 	    { /* N.B.: due to the logic in the 'if', LastRow==TRUE
2210 		 means here that the field is growable and not
2211 		 a single-line field */
2212 	      return(E_SYSTEM_ERROR);
2213 	    }
2214 	  wclrtoeol(form->w);
2215 	  form->currow++;
2216 	  form->curcol = 0;
2217 	  form->status |= _WINDOW_MODIFIED;
2218 	  return(E_OK);
2219 	}
2220     }
2221   else
2222     { /* Insert Mode */
2223       if (Last_Row &&
2224 	  !(Growable(field) && !Single_Line_Field(field)))
2225 	{
2226 	  if (!(form->opts & O_NL_OVERLOAD))
2227 	    return(E_REQUEST_DENIED);
2228 	  return Inter_Field_Navigation(FN_Next_Field,form);
2229 	}
2230       else
2231 	{
2232 	  bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);
2233 
2234 	  if (!(May_Do_It || Growable(field)))
2235 	    return(E_REQUEST_DENIED);
2236 	  if (!May_Do_It && !Field_Grown(field,1))
2237 	    return(E_SYSTEM_ERROR);
2238 
2239 	  bp= Address_Of_Current_Position_In_Buffer(form);
2240 	  t = After_End_Of_Data(bp,field->dcols - form->curcol);
2241 	  wclrtoeol(form->w);
2242 	  form->currow++;
2243 	  form->curcol=0;
2244 	  wmove(form->w,form->currow,form->curcol);
2245 	  winsertln(form->w);
2246 	  waddnstr(form->w,bp,(int)(t-bp));
2247 	  form->status |= _WINDOW_MODIFIED;
2248 	  return E_OK;
2249 	}
2250     }
2251 }
2252 
2253 /*---------------------------------------------------------------------------
2254 |   Facility      :  libnform
2255 |   Function      :  static int FE_Insert_Character(FORM * form)
2256 |
2257 |   Description   :  Insert blank character at the cursor position
2258 |
2259 |   Return Values :  E_OK
2260 |                    E_REQUEST_DENIED
2261 +--------------------------------------------------------------------------*/
2262 static int FE_Insert_Character(FORM * form)
2263 {
2264   FIELD *field = form->current;
2265   int result = E_REQUEST_DENIED;
2266 
2267   if (Check_Char(field->type,(int)C_BLANK,(TypeArgument *)(field->arg)))
2268     {
2269       bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
2270 
2271       if (There_Is_Room ||
2272 	  ((Single_Line_Field(field) && Growable(field))))
2273 	{
2274 	  if (!There_Is_Room && !Field_Grown(field,1))
2275 	    result =  E_SYSTEM_ERROR;
2276 	  else
2277 	    {
2278 	      winsch(form->w,(chtype)C_BLANK);
2279 	      result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);
2280 	    }
2281 	}
2282     }
2283   return result;
2284 }
2285 
2286 /*---------------------------------------------------------------------------
2287 |   Facility      :  libnform
2288 |   Function      :  static int FE_Insert_Line(FORM * form)
2289 |
2290 |   Description   :  Insert a blank line at the cursor position
2291 |
2292 |   Return Values :  E_OK               - success
2293 |                    E_REQUEST_DENIED   - line can not be inserted
2294 +--------------------------------------------------------------------------*/
2295 static int FE_Insert_Line(FORM * form)
2296 {
2297   FIELD *field = form->current;
2298   int result = E_REQUEST_DENIED;
2299 
2300   if (Check_Char(field->type,(int)C_BLANK,(TypeArgument *)(field->arg)))
2301     {
2302       bool Maybe_Done = (form->currow!=(field->drows-1)) &&
2303 	                Is_There_Room_For_A_Line(form);
2304 
2305       if (!Single_Line_Field(field) &&
2306 	  (Maybe_Done || Growable(field)))
2307 	{
2308 	  if (!Maybe_Done && !Field_Grown(field,1))
2309 	    result = E_SYSTEM_ERROR;
2310 	  else
2311 	    {
2312 	      form->curcol = 0;
2313 	      winsertln(form->w);
2314 	      result = E_OK;
2315 	    }
2316 	}
2317     }
2318   return result;
2319 }
2320 
2321 /*---------------------------------------------------------------------------
2322 |   Facility      :  libnform
2323 |   Function      :  static int FE_Delete_Character(FORM * form)
2324 |
2325 |   Description   :  Delete character at the cursor position
2326 |
2327 |   Return Values :  E_OK    - success
2328 +--------------------------------------------------------------------------*/
2329 static int FE_Delete_Character(FORM * form)
2330 {
2331   wdelch(form->w);
2332   return E_OK;
2333 }
2334 
2335 /*---------------------------------------------------------------------------
2336 |   Facility      :  libnform
2337 |   Function      :  static int FE_Delete_Previous(FORM * form)
2338 |
2339 |   Description   :  Delete character before cursor. Again this is a rather
2340 |                    difficult piece compared to others due to the overloading
2341 |                    semantics of backspace.
2342 |                    N.B.: The case of overloaded BS on first field position
2343 |                          is already handled in the generic routine.
2344 |
2345 |   Return Values :  E_OK                - success
2346 |                    E_REQUEST_DENIED    - Character can't be deleted
2347 +--------------------------------------------------------------------------*/
2348 static int FE_Delete_Previous(FORM * form)
2349 {
2350   FIELD  *field = form->current;
2351 
2352   if (First_Position_In_Current_Field(form))
2353     return E_REQUEST_DENIED;
2354 
2355   if ( (--(form->curcol))<0 )
2356     {
2357       char *this_line, *prev_line, *prev_end, *this_end;
2358 
2359       form->curcol++;
2360       if (form->status & _OVLMODE)
2361 	return E_REQUEST_DENIED;
2362 
2363       prev_line = Address_Of_Row_In_Buffer(field,(form->currow-1));
2364       this_line = Address_Of_Row_In_Buffer(field,(form->currow));
2365       Synchronize_Buffer(form);
2366       prev_end = After_End_Of_Data(prev_line,field->dcols);
2367       this_end = After_End_Of_Data(this_line,field->dcols);
2368       if ((int)(this_end-this_line) >
2369 	  (field->cols-(int)(prev_end-prev_line)))
2370 	return E_REQUEST_DENIED;
2371       wdeleteln(form->w);
2372       Adjust_Cursor_Position(form,prev_end);
2373       wmove(form->w,form->currow,form->curcol);
2374       waddnstr(form->w,this_line,(int)(this_end-this_line));
2375     }
2376   else
2377     {
2378       wmove(form->w,form->currow,form->curcol);
2379       wdelch(form->w);
2380     }
2381   return E_OK;
2382 }
2383 
2384 /*---------------------------------------------------------------------------
2385 |   Facility      :  libnform
2386 |   Function      :  static int FE_Delete_Line(FORM * form)
2387 |
2388 |   Description   :  Delete line at cursor position.
2389 |
2390 |   Return Values :  E_OK  - success
2391 +--------------------------------------------------------------------------*/
2392 static int FE_Delete_Line(FORM * form)
2393 {
2394   form->curcol = 0;
2395   wdeleteln(form->w);
2396   return E_OK;
2397 }
2398 
2399 /*---------------------------------------------------------------------------
2400 |   Facility      :  libnform
2401 |   Function      :  static int FE_Delete_Word(FORM * form)
2402 |
2403 |   Description   :  Delete word at cursor position
2404 |
2405 |   Return Values :  E_OK               - success
2406 |                    E_REQUEST_DENIED   - failure
2407 +--------------------------------------------------------------------------*/
2408 static int FE_Delete_Word(FORM * form)
2409 {
2410   FIELD  *field = form->current;
2411   char   *bp = Address_Of_Current_Row_In_Buffer(form);
2412   char   *ep = bp + field->dcols;
2413   char   *cp = bp + form->curcol;
2414   char *s;
2415 
2416   Synchronize_Buffer(form);
2417   if (is_blank(*cp))
2418     return E_REQUEST_DENIED; /* not in word */
2419 
2420   /* move cursor to begin of word and erase to end of screen-line */
2421   Adjust_Cursor_Position(form,
2422 			 After_Last_Whitespace_Character(bp,form->curcol));
2423   wmove(form->w,form->currow,form->curcol);
2424   wclrtoeol(form->w);
2425 
2426   /* skip over word in buffer */
2427   s = Get_First_Whitespace_Character(cp,(int)(ep-cp));
2428   /* to begin of next word    */
2429   s = Get_Start_Of_Data(s,(int)(ep - s));
2430   if ( (s!=cp) && !is_blank(*s))
2431     {
2432       /* copy remaining line to window */
2433       waddnstr(form->w,s,(int)(s - After_End_Of_Data(s,(int)(ep - s))));
2434     }
2435   return E_OK;
2436 }
2437 
2438 /*---------------------------------------------------------------------------
2439 |   Facility      :  libnform
2440 |   Function      :  static int FE_Clear_To_End_Of_Line(FORM * form)
2441 |
2442 |   Description   :  Clear to end of current line.
2443 |
2444 |   Return Values :  E_OK   - success
2445 +--------------------------------------------------------------------------*/
2446 static int FE_Clear_To_End_Of_Line(FORM * form)
2447 {
2448   wclrtoeol(form->w);
2449   return E_OK;
2450 }
2451 
2452 /*---------------------------------------------------------------------------
2453 |   Facility      :  libnform
2454 |   Function      :  static int FE_Clear_To_End_Of_Form(FORM * form)
2455 |
2456 |   Description   :  Clear to end of form.
2457 |
2458 |   Return Values :  E_OK   - success
2459 +--------------------------------------------------------------------------*/
2460 static int FE_Clear_To_End_Of_Form(FORM * form)
2461 {
2462   wclrtobot(form->w);
2463   return E_OK;
2464 }
2465 
2466 /*---------------------------------------------------------------------------
2467 |   Facility      :  libnform
2468 |   Function      :  static int FE_Clear_Field(FORM * form)
2469 |
2470 |   Description   :  Clear entire field.
2471 |
2472 |   Return Values :  E_OK   - success
2473 +--------------------------------------------------------------------------*/
2474 static int FE_Clear_Field(FORM * form)
2475 {
2476   form->currow = form->curcol = 0;
2477   werase(form->w);
2478   return E_OK;
2479 }
2480 /*----------------------------------------------------------------------------
2481   END of Field Editing routines
2482   --------------------------------------------------------------------------*/
2483 
2484 /*----------------------------------------------------------------------------
2485   Edit Mode routines
2486   --------------------------------------------------------------------------*/
2487 
2488 /*---------------------------------------------------------------------------
2489 |   Facility      :  libnform
2490 |   Function      :  static int EM_Overlay_Mode(FORM * form)
2491 |
2492 |   Description   :  Switch to overlay mode.
2493 |
2494 |   Return Values :  E_OK   - success
2495 +--------------------------------------------------------------------------*/
2496 static int EM_Overlay_Mode(FORM * form)
2497 {
2498   form->status |= _OVLMODE;
2499   return E_OK;
2500 }
2501 
2502 /*---------------------------------------------------------------------------
2503 |   Facility      :  libnform
2504 |   Function      :  static int EM_Insert_Mode(FORM * form)
2505 |
2506 |   Description   :  Switch to insert mode
2507 |
2508 |   Return Values :  E_OK   - success
2509 +--------------------------------------------------------------------------*/
2510 static int EM_Insert_Mode(FORM * form)
2511 {
2512   form->status &= ~_OVLMODE;
2513   return E_OK;
2514 }
2515 
2516 /*----------------------------------------------------------------------------
2517   END of Edit Mode routines
2518   --------------------------------------------------------------------------*/
2519 
2520 /*----------------------------------------------------------------------------
2521   Helper routines for Choice Requests
2522   --------------------------------------------------------------------------*/
2523 
2524 /*---------------------------------------------------------------------------
2525 |   Facility      :  libnform
2526 |   Function      :  static bool Next_Choice(
2527 |                                            FIELDTYPE * typ,
2528 |                                            FIELD * field,
2529 |                                            TypeArgument *argp)
2530 |
2531 |   Description   :  Get the next field choice. For linked types this is
2532 |                    done recursively.
2533 |
2534 |   Return Values :  TRUE    - next choice successfully retrieved
2535 |                    FALSE   - couldn't retrieve next choice
2536 +--------------------------------------------------------------------------*/
2537 static bool Next_Choice(FIELDTYPE * typ, FIELD *field, TypeArgument *argp)
2538 {
2539   if (!typ || !(typ->status & _HAS_CHOICE))
2540     return FALSE;
2541 
2542   if (typ->status & _LINKED_TYPE)
2543     {
2544       assert(argp);
2545       return(
2546 	     Next_Choice(typ->left ,field,argp->left) ||
2547 	     Next_Choice(typ->right,field,argp->right) );
2548     }
2549   else
2550     {
2551       assert(typ->next);
2552       return typ->next(field,(void *)argp);
2553     }
2554 }
2555 
2556 /*---------------------------------------------------------------------------
2557 |   Facility      :  libnform
2558 |   Function      :  static bool Previous_Choice(
2559 |                                                FIELDTYPE * typ,
2560 |                                                FIELD * field,
2561 |                                                TypeArgument *argp)
2562 |
2563 |   Description   :  Get the previous field choice. For linked types this
2564 |                    is done recursively.
2565 |
2566 |   Return Values :  TRUE    - previous choice successfully retrieved
2567 |                    FALSE   - couldn't retrieve previous choice
2568 +--------------------------------------------------------------------------*/
2569 static bool Previous_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2570 {
2571   if (!typ || !(typ->status & _HAS_CHOICE))
2572     return FALSE;
2573 
2574   if (typ->status & _LINKED_TYPE)
2575     {
2576       assert(argp);
2577       return(
2578 	     Previous_Choice(typ->left ,field,argp->left) ||
2579 	     Previous_Choice(typ->right,field,argp->right));
2580     }
2581   else
2582     {
2583       assert(typ->prev);
2584       return typ->prev(field,(void *)argp);
2585     }
2586 }
2587 /*----------------------------------------------------------------------------
2588   End of Helper routines for Choice Requests
2589   --------------------------------------------------------------------------*/
2590 
2591 /*----------------------------------------------------------------------------
2592   Routines for Choice Requests
2593   --------------------------------------------------------------------------*/
2594 
2595 /*---------------------------------------------------------------------------
2596 |   Facility      :  libnform
2597 |   Function      :  static int CR_Next_Choice(FORM * form)
2598 |
2599 |   Description   :  Get the next field choice.
2600 |
2601 |   Return Values :  E_OK              - success
2602 |                    E_REQUEST_DENIED  - next choice couldn't be retrieved
2603 +--------------------------------------------------------------------------*/
2604 static int CR_Next_Choice(FORM * form)
2605 {
2606   FIELD *field = form->current;
2607   Synchronize_Buffer(form);
2608   return ((Next_Choice(field->type,field,(TypeArgument *)(field->arg))) ?
2609 	  E_OK : E_REQUEST_DENIED);
2610 }
2611 
2612 /*---------------------------------------------------------------------------
2613 |   Facility      :  libnform
2614 |   Function      :  static int CR_Previous_Choice(FORM * form)
2615 |
2616 |   Description   :  Get the previous field choice.
2617 |
2618 |   Return Values :  E_OK              - success
2619 |                    E_REQUEST_DENIED  - prev. choice couldn't be retrieved
2620 +--------------------------------------------------------------------------*/
2621 static int CR_Previous_Choice(FORM * form)
2622 {
2623   FIELD *field = form->current;
2624   Synchronize_Buffer(form);
2625   return ((Previous_Choice(field->type,field,(TypeArgument *)(field->arg))) ?
2626 	  E_OK : E_REQUEST_DENIED);
2627 }
2628 /*----------------------------------------------------------------------------
2629   End of Routines for Choice Requests
2630   --------------------------------------------------------------------------*/
2631 
2632 /*----------------------------------------------------------------------------
2633   Helper routines for Field Validations.
2634   --------------------------------------------------------------------------*/
2635 
2636 /*---------------------------------------------------------------------------
2637 |   Facility      :  libnform
2638 |   Function      :  static bool Check_Field(
2639 |                                            FIELDTYPE * typ,
2640 |                                            FIELD * field,
2641 |                                            TypeArgument * argp)
2642 |
2643 |   Description   :  Check the field according to its fieldtype and its
2644 |                    actual arguments. For linked fieldtypes this is done
2645 |                    recursively.
2646 |
2647 |   Return Values :  TRUE       - field is valid
2648 |                    FALSE      - field is invalid.
2649 +--------------------------------------------------------------------------*/
2650 static bool Check_Field(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2651 {
2652   if (typ)
2653     {
2654       if (field->opts & O_NULLOK)
2655 	{
2656 	  char *bp = field->buf;
2657 	  assert(bp);
2658 	  while(is_blank(*bp))
2659 	    { bp++; }
2660 	  if (*bp == '\0')
2661 	    return TRUE;
2662 	}
2663 
2664       if (typ->status & _LINKED_TYPE)
2665 	{
2666 	  assert(argp);
2667 	  return(
2668 		 Check_Field(typ->left ,field,argp->left ) ||
2669 		 Check_Field(typ->right,field,argp->right) );
2670 	}
2671       else
2672 	{
2673 	  if (typ->fcheck)
2674 	    return typ->fcheck(field,(void *)argp);
2675 	}
2676     }
2677   return TRUE;
2678 }
2679 
2680 /*---------------------------------------------------------------------------
2681 |   Facility      :  libnform
2682 |   Function      :  bool _nc_Internal_Validation(FORM * form )
2683 |
2684 |   Description   :  Validate the current field of the form.
2685 |
2686 |   Return Values :  TRUE  - field is valid
2687 |                    FALSE - field is invalid
2688 +--------------------------------------------------------------------------*/
2689 bool
2690 _nc_Internal_Validation(FORM *form)
2691 {
2692   FIELD *field;
2693 
2694   field = form->current;
2695 
2696   Synchronize_Buffer(form);
2697   if ((form->status & _FCHECK_REQUIRED) ||
2698       (!(field->opts & O_PASSOK)))
2699     {
2700       if (!Check_Field(field->type,field,(TypeArgument *)(field->arg)))
2701 	return FALSE;
2702       form->status  &= ~_FCHECK_REQUIRED;
2703       field->status |= _CHANGED;
2704       Synchronize_Linked_Fields(field);
2705     }
2706   return TRUE;
2707 }
2708 /*----------------------------------------------------------------------------
2709   End of Helper routines for Field Validations.
2710   --------------------------------------------------------------------------*/
2711 
2712 /*----------------------------------------------------------------------------
2713   Routines for Field Validation.
2714   --------------------------------------------------------------------------*/
2715 
2716 /*---------------------------------------------------------------------------
2717 |   Facility      :  libnform
2718 |   Function      :  static int FV_Validation(FORM * form)
2719 |
2720 |   Description   :  Validate the current field of the form.
2721 |
2722 |   Return Values :  E_OK             - field valid
2723 |                    E_INVALID_FIELD  - field not valid
2724 +--------------------------------------------------------------------------*/
2725 static int FV_Validation(FORM * form)
2726 {
2727   if (_nc_Internal_Validation(form))
2728     return E_OK;
2729   else
2730     return E_INVALID_FIELD;
2731 }
2732 /*----------------------------------------------------------------------------
2733   End of routines for Field Validation.
2734   --------------------------------------------------------------------------*/
2735 
2736 /*----------------------------------------------------------------------------
2737   Helper routines for Inter-Field Navigation
2738   --------------------------------------------------------------------------*/
2739 
2740 /*---------------------------------------------------------------------------
2741 |   Facility      :  libnform
2742 |   Function      :  static FIELD *Next_Field_On_Page(FIELD * field)
2743 |
2744 |   Description   :  Get the next field after the given field on the current
2745 |                    page. The order of fields is the one defined by the
2746 |                    fields array. Only visible and active fields are
2747 |                    counted.
2748 |
2749 |   Return Values :  Pointer to the next field.
2750 +--------------------------------------------------------------------------*/
2751 INLINE static FIELD *Next_Field_On_Page(FIELD * field)
2752 {
2753   FORM  *form = field->form;
2754   FIELD **field_on_page = &form->field[field->index];
2755   FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
2756   FIELD **last_on_page  = &form->field[form->page[form->curpage].pmax];
2757 
2758   do
2759     {
2760       field_on_page =
2761 	(field_on_page==last_on_page) ? first_on_page : field_on_page + 1;
2762       if (Field_Is_Selectable(*field_on_page))
2763 	break;
2764     } while(field!=(*field_on_page));
2765   return(*field_on_page);
2766 }
2767 
2768 /*---------------------------------------------------------------------------
2769 |   Facility      :  libnform
2770 |   Function      :  FIELD* _nc_First_Active_Field(FORM * form)
2771 |
2772 |   Description   :  Get the first active field on the current page,
2773 |                    if there are such. If there are none, get the first
2774 |                    visible field on the page. If there are also none,
2775 |                    we return the first field on page and hope the best.
2776 |
2777 |   Return Values :  Pointer to calculated field.
2778 +--------------------------------------------------------------------------*/
2779 FIELD*
2780 _nc_First_Active_Field(FORM * form)
2781 {
2782   FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
2783   FIELD *proposed = Next_Field_On_Page(*last_on_page);
2784 
2785   if (proposed == *last_on_page)
2786     { /* there might be the special situation, where there is no
2787 	 active and visible field on the current page. We then select
2788 	 the first visible field on this readonly page
2789       */
2790       if (Field_Is_Not_Selectable(proposed))
2791 	{
2792 	  FIELD **field = &form->field[proposed->index];
2793 	  FIELD **first = &form->field[form->page[form->curpage].pmin];
2794 
2795 	  do
2796 	    {
2797 	      field = (field==last_on_page) ? first : field + 1;
2798 	      if (((*field)->opts & O_VISIBLE))
2799 		break;
2800 	    } while(proposed!=(*field));
2801 
2802 	  proposed = *field;
2803 
2804 	  if ((proposed == *last_on_page) && !(proposed->opts&O_VISIBLE))
2805 	    { /* This means, there is also no visible field on the page.
2806 		 So we propose the first one and hope the very best...
2807 		 Some very clever user has designed a readonly and invisible
2808 		 page on this form.
2809 	       */
2810 	      proposed = *first;
2811 	    }
2812 	}
2813     }
2814   return(proposed);
2815 }
2816 
2817 /*---------------------------------------------------------------------------
2818 |   Facility      :  libnform
2819 |   Function      :  static FIELD *Previous_Field_On_Page(FIELD * field)
2820 |
2821 |   Description   :  Get the previous field before the given field on the
2822 |                    current page. The order of fields is the one defined by
2823 |                    the fields array. Only visible and active fields are
2824 |                    counted.
2825 |
2826 |   Return Values :  Pointer to the previous field.
2827 +--------------------------------------------------------------------------*/
2828 INLINE static FIELD *Previous_Field_On_Page(FIELD * field)
2829 {
2830   FORM  *form   = field->form;
2831   FIELD **field_on_page = &form->field[field->index];
2832   FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
2833   FIELD **last_on_page  = &form->field[form->page[form->curpage].pmax];
2834 
2835   do
2836     {
2837       field_on_page =
2838 	(field_on_page==first_on_page) ? last_on_page : field_on_page - 1;
2839       if (Field_Is_Selectable(*field_on_page))
2840 	break;
2841     } while(field!=(*field_on_page));
2842 
2843   return (*field_on_page);
2844 }
2845 
2846 /*---------------------------------------------------------------------------
2847 |   Facility      :  libnform
2848 |   Function      :  static FIELD *Sorted_Next_Field(FIELD * field)
2849 |
2850 |   Description   :  Get the next field after the given field on the current
2851 |                    page. The order of fields is the one defined by the
2852 |                    (row,column) geometry, rows are major.
2853 |
2854 |   Return Values :  Pointer to the next field.
2855 +--------------------------------------------------------------------------*/
2856 INLINE static FIELD *Sorted_Next_Field(FIELD * field)
2857 {
2858   FIELD *field_on_page = field;
2859 
2860   do
2861     {
2862       field_on_page = field_on_page->snext;
2863       if (Field_Is_Selectable(field_on_page))
2864 	break;
2865     } while(field_on_page!=field);
2866 
2867   return (field_on_page);
2868 }
2869 
2870 /*---------------------------------------------------------------------------
2871 |   Facility      :  libnform
2872 |   Function      :  static FIELD *Sorted_Previous_Field(FIELD * field)
2873 |
2874 |   Description   :  Get the previous field before the given field on the
2875 |                    current page. The order of fields is the one defined
2876 |                    by the (row,column) geometry, rows are major.
2877 |
2878 |   Return Values :  Pointer to the previous field.
2879 +--------------------------------------------------------------------------*/
2880 INLINE static FIELD *Sorted_Previous_Field(FIELD * field)
2881 {
2882   FIELD *field_on_page = field;
2883 
2884   do
2885     {
2886       field_on_page = field_on_page->sprev;
2887       if (Field_Is_Selectable(field_on_page))
2888 	break;
2889     } while(field_on_page!=field);
2890 
2891   return (field_on_page);
2892 }
2893 
2894 /*---------------------------------------------------------------------------
2895 |   Facility      :  libnform
2896 |   Function      :  static FIELD *Left_Neighbour_Field(FIELD * field)
2897 |
2898 |   Description   :  Get the left neighbour of the field on the same line
2899 |                    and the same page. Cycles through the line.
2900 |
2901 |   Return Values :  Pointer to left neighbour field.
2902 +--------------------------------------------------------------------------*/
2903 INLINE static FIELD *Left_Neighbour_Field(FIELD * field)
2904 {
2905   FIELD *field_on_page = field;
2906 
2907   /* For a field that has really a left neighbour, the while clause
2908      immediately fails and the loop is left, positioned at the right
2909      neighbour. Otherwise we cycle backwards through the sorted fieldlist
2910      until we enter the same line (from the right end).
2911   */
2912   do
2913     {
2914       field_on_page = Sorted_Previous_Field(field_on_page);
2915     } while(field_on_page->frow != field->frow);
2916 
2917   return (field_on_page);
2918 }
2919 
2920 /*---------------------------------------------------------------------------
2921 |   Facility      :  libnform
2922 |   Function      :  static FIELD *Right_Neighbour_Field(FIELD * field)
2923 |
2924 |   Description   :  Get the right neighbour of the field on the same line
2925 |                    and the same page.
2926 |
2927 |   Return Values :  Pointer to right neighbour field.
2928 +--------------------------------------------------------------------------*/
2929 INLINE static FIELD *Right_Neighbour_Field(FIELD * field)
2930 {
2931   FIELD *field_on_page = field;
2932 
2933   /* See the comments on Left_Neighbour_Field to understand how it works */
2934   do
2935     {
2936       field_on_page = Sorted_Next_Field(field_on_page);
2937     } while(field_on_page->frow != field->frow);
2938 
2939   return (field_on_page);
2940 }
2941 
2942 /*---------------------------------------------------------------------------
2943 |   Facility      :  libnform
2944 |   Function      :  static FIELD *Upper_Neighbour_Field(FIELD * field)
2945 |
2946 |   Description   :  Because of the row-major nature of sorting the fields,
2947 |                    its more difficult to define whats the upper neighbour
2948 |                    field really means. We define that it must be on a
2949 |                    'previous' line (cyclic order!) and is the rightmost
2950 |                    field laying on the left side of the given field. If
2951 |                    this set is empty, we take the first field on the line.
2952 |
2953 |   Return Values :  Pointer to the upper neighbour field.
2954 +--------------------------------------------------------------------------*/
2955 static FIELD *Upper_Neighbour_Field(FIELD * field)
2956 {
2957   FIELD *field_on_page = field;
2958   int frow = field->frow;
2959   int fcol = field->fcol;
2960 
2961   /* Walk back to the 'previous' line. The second term in the while clause
2962      just guarantees that we stop if we cycled through the line because
2963      there might be no 'previous' line if the page has just one line.
2964   */
2965   do
2966     {
2967       field_on_page = Sorted_Previous_Field(field_on_page);
2968     } while(field_on_page->frow==frow && field_on_page->fcol!=fcol);
2969 
2970   if (field_on_page->frow!=frow)
2971     { /* We really found a 'previous' line. We are positioned at the
2972          rightmost field on this line */
2973       frow = field_on_page->frow;
2974 
2975       /* We walk to the left as long as we are really right of the
2976 	 field. */
2977       while(field_on_page->frow==frow && field_on_page->fcol>fcol)
2978 	field_on_page = Sorted_Previous_Field(field_on_page);
2979 
2980       /* If we wrapped, just go to the right which is the first field on
2981 	 the row */
2982       if (field_on_page->frow!=frow)
2983 	field_on_page = Sorted_Next_Field(field_on_page);
2984     }
2985 
2986   return (field_on_page);
2987 }
2988 
2989 /*---------------------------------------------------------------------------
2990 |   Facility      :  libnform
2991 |   Function      :  static FIELD *Down_Neighbour_Field(FIELD * field)
2992 |
2993 |   Description   :  Because of the row-major nature of sorting the fields,
2994 |                    its more difficult to define whats the down neighbour
2995 |                    field really means. We define that it must be on a
2996 |                    'next' line (cyclic order!) and is the leftmost
2997 |                    field laying on the right side of the given field. If
2998 |                    this set is empty, we take the last field on the line.
2999 |
3000 |   Return Values :  Pointer to the upper neighbour field.
3001 +--------------------------------------------------------------------------*/
3002 static FIELD *Down_Neighbour_Field(FIELD * field)
3003 {
3004   FIELD *field_on_page = field;
3005   int frow = field->frow;
3006   int fcol = field->fcol;
3007 
3008   /* Walk forward to the 'next' line. The second term in the while clause
3009      just guarantees that we stop if we cycled through the line because
3010      there might be no 'next' line if the page has just one line.
3011   */
3012   do
3013     {
3014       field_on_page = Sorted_Next_Field(field_on_page);
3015     } while(field_on_page->frow==frow && field_on_page->fcol!=fcol);
3016 
3017   if (field_on_page->frow!=frow)
3018     { /* We really found a 'next' line. We are positioned at the rightmost
3019          field on this line */
3020       frow = field_on_page->frow;
3021 
3022       /* We walk to the right as long as we are really left of the
3023 	 field. */
3024       while(field_on_page->frow==frow && field_on_page->fcol<fcol)
3025 	field_on_page = Sorted_Next_Field(field_on_page);
3026 
3027       /* If we wrapped, just go to the left which is the last field on
3028 	 the row */
3029       if (field_on_page->frow!=frow)
3030 	field_on_page = Sorted_Previous_Field(field_on_page);
3031     }
3032 
3033   return(field_on_page);
3034 }
3035 
3036 /*----------------------------------------------------------------------------
3037   Inter-Field Navigation routines
3038   --------------------------------------------------------------------------*/
3039 
3040 /*---------------------------------------------------------------------------
3041 |   Facility      :  libnform
3042 |   Function      :  static int Inter_Field_Navigation(
3043 |                                           int (* const fct) (FORM *),
3044 |                                           FORM * form)
3045 |
3046 |   Description   :  Generic behaviour for changing the current field, the
3047 |                    field is left and a new field is entered. So the field
3048 |                    must be validated and the field init/term hooks must
3049 |                    be called.
3050 |
3051 |   Return Values :  E_OK                - success
3052 |                    E_INVALID_FIELD     - field is invalid
3053 |                    some other          - error from subordinate call
3054 +--------------------------------------------------------------------------*/
3055 static int Inter_Field_Navigation(int (* const fct) (FORM *),FORM *form)
3056 {
3057   int res;
3058 
3059   if (!_nc_Internal_Validation(form))
3060     res = E_INVALID_FIELD;
3061   else
3062     {
3063       Call_Hook(form,fieldterm);
3064       res = fct(form);
3065       Call_Hook(form,fieldinit);
3066     }
3067   return res;
3068 }
3069 
3070 /*---------------------------------------------------------------------------
3071 |   Facility      :  libnform
3072 |   Function      :  static int FN_Next_Field(FORM * form)
3073 |
3074 |   Description   :  Move to the next field on the current page of the form
3075 |
3076 |   Return Values :  E_OK                 - success
3077 |                    != E_OK              - error from subordinate call
3078 +--------------------------------------------------------------------------*/
3079 static int FN_Next_Field(FORM * form)
3080 {
3081   return _nc_Set_Current_Field(form,
3082 			       Next_Field_On_Page(form->current));
3083 }
3084 
3085 /*---------------------------------------------------------------------------
3086 |   Facility      :  libnform
3087 |   Function      :  static int FN_Previous_Field(FORM * form)
3088 |
3089 |   Description   :  Move to the previous field on the current page of the
3090 |                    form
3091 |
3092 |   Return Values :  E_OK                 - success
3093 |                    != E_OK              - error from subordinate call
3094 +--------------------------------------------------------------------------*/
3095 static int FN_Previous_Field(FORM * form)
3096 {
3097   return _nc_Set_Current_Field(form,
3098 			       Previous_Field_On_Page(form->current));
3099 }
3100 
3101 /*---------------------------------------------------------------------------
3102 |   Facility      :  libnform
3103 |   Function      :  static int FN_First_Field(FORM * form)
3104 |
3105 |   Description   :  Move to the first field on the current page of the form
3106 |
3107 |   Return Values :  E_OK                 - success
3108 |                    != E_OK              - error from subordinate call
3109 +--------------------------------------------------------------------------*/
3110 static int FN_First_Field(FORM * form)
3111 {
3112   return _nc_Set_Current_Field(form,
3113       Next_Field_On_Page(form->field[form->page[form->curpage].pmax]));
3114 }
3115 
3116 /*---------------------------------------------------------------------------
3117 |   Facility      :  libnform
3118 |   Function      :  static int FN_Last_Field(FORM * form)
3119 |
3120 |   Description   :  Move to the last field on the current page of the form
3121 |
3122 |   Return Values :  E_OK                 - success
3123 |                    != E_OK              - error from subordinate call
3124 +--------------------------------------------------------------------------*/
3125 static int FN_Last_Field(FORM * form)
3126 {
3127   return
3128     _nc_Set_Current_Field(form,
3129        Previous_Field_On_Page(form->field[form->page[form->curpage].pmin]));
3130 }
3131 
3132 /*---------------------------------------------------------------------------
3133 |   Facility      :  libnform
3134 |   Function      :  static int FN_Sorted_Next_Field(FORM * form)
3135 |
3136 |   Description   :  Move to the sorted next field on the current page
3137 |                    of the form.
3138 |
3139 |   Return Values :  E_OK            - success
3140 |                    != E_OK         - error from subordinate call
3141 +--------------------------------------------------------------------------*/
3142 static int FN_Sorted_Next_Field(FORM * form)
3143 {
3144   return _nc_Set_Current_Field(form,
3145 			       Sorted_Next_Field(form->current));
3146 }
3147 
3148 /*---------------------------------------------------------------------------
3149 |   Facility      :  libnform
3150 |   Function      :  static int FN_Sorted_Previous_Field(FORM * form)
3151 |
3152 |   Description   :  Move to the sorted previous field on the current page
3153 |                    of the form.
3154 |
3155 |   Return Values :  E_OK            - success
3156 |                    != E_OK         - error from subordinate call
3157 +--------------------------------------------------------------------------*/
3158 static int FN_Sorted_Previous_Field(FORM * form)
3159 {
3160   return _nc_Set_Current_Field(form,
3161 			       Sorted_Previous_Field(form->current));
3162 }
3163 
3164 /*---------------------------------------------------------------------------
3165 |   Facility      :  libnform
3166 |   Function      :  static int FN_Sorted_First_Field(FORM * form)
3167 |
3168 |   Description   :  Move to the sorted first field on the current page
3169 |                    of the form.
3170 |
3171 |   Return Values :  E_OK            - success
3172 |                    != E_OK         - error from subordinate call
3173 +--------------------------------------------------------------------------*/
3174 static int FN_Sorted_First_Field(FORM * form)
3175 {
3176   return _nc_Set_Current_Field(form,
3177 	      Sorted_Next_Field(form->field[form->page[form->curpage].smax]));
3178 }
3179 
3180 /*---------------------------------------------------------------------------
3181 |   Facility      :  libnform
3182 |   Function      :  static int FN_Sorted_Last_Field(FORM * form)
3183 |
3184 |   Description   :  Move to the sorted last field on the current page
3185 |                    of the form.
3186 |
3187 |   Return Values :  E_OK            - success
3188 |                    != E_OK         - error from subordinate call
3189 +--------------------------------------------------------------------------*/
3190 static int FN_Sorted_Last_Field(FORM * form)
3191 {
3192   return _nc_Set_Current_Field(form,
3193 	   Sorted_Previous_Field(form->field[form->page[form->curpage].smin]));
3194 }
3195 
3196 /*---------------------------------------------------------------------------
3197 |   Facility      :  libnform
3198 |   Function      :  static int FN_Left_Field(FORM * form)
3199 |
3200 |   Description   :  Get the field on the left of the current field on the
3201 |                    same line and the same page. Cycles through the line.
3202 |
3203 |   Return Values :  E_OK            - success
3204 |                    != E_OK         - error from subordinate call
3205 +--------------------------------------------------------------------------*/
3206 static int FN_Left_Field(FORM * form)
3207 {
3208   return _nc_Set_Current_Field(form,
3209 			       Left_Neighbour_Field(form->current));
3210 }
3211 
3212 /*---------------------------------------------------------------------------
3213 |   Facility      :  libnform
3214 |   Function      :  static int FN_Right_Field(FORM * form)
3215 |
3216 |   Description   :  Get the field on the right of the current field on the
3217 |                    same line and the same page. Cycles through the line.
3218 |
3219 |   Return Values :  E_OK            - success
3220 |                    != E_OK         - error from subordinate call
3221 +--------------------------------------------------------------------------*/
3222 static int FN_Right_Field(FORM * form)
3223 {
3224   return _nc_Set_Current_Field(form,
3225 			       Right_Neighbour_Field(form->current));
3226 }
3227 
3228 /*---------------------------------------------------------------------------
3229 |   Facility      :  libnform
3230 |   Function      :  static int FN_Up_Field(FORM * form)
3231 |
3232 |   Description   :  Get the upper neighbour of the current field. This
3233 |                    cycles through the page. See the comments of the
3234 |                    Upper_Neighbour_Field function to understand how
3235 |                    'upper' is defined.
3236 |
3237 |   Return Values :  E_OK            - success
3238 |                    != E_OK         - error from subordinate call
3239 +--------------------------------------------------------------------------*/
3240 static int FN_Up_Field(FORM * form)
3241 {
3242   return _nc_Set_Current_Field(form,
3243 			       Upper_Neighbour_Field(form->current));
3244 }
3245 
3246 /*---------------------------------------------------------------------------
3247 |   Facility      :  libnform
3248 |   Function      :  static int FN_Down_Field(FORM * form)
3249 |
3250 |   Description   :  Get the down neighbour of the current field. This
3251 |                    cycles through the page. See the comments of the
3252 |                    Down_Neighbour_Field function to understand how
3253 |                    'down' is defined.
3254 |
3255 |   Return Values :  E_OK            - success
3256 |                    != E_OK         - error from subordinate call
3257 +--------------------------------------------------------------------------*/
3258 static int FN_Down_Field(FORM * form)
3259 {
3260   return _nc_Set_Current_Field(form,
3261 			       Down_Neighbour_Field(form->current));
3262 }
3263 /*----------------------------------------------------------------------------
3264   END of Field Navigation routines
3265   --------------------------------------------------------------------------*/
3266 
3267 /*----------------------------------------------------------------------------
3268   Helper routines for Page Navigation
3269   --------------------------------------------------------------------------*/
3270 
3271 /*---------------------------------------------------------------------------
3272 |   Facility      :  libnform
3273 |   Function      :  int _nc_Set_Form_Page(FORM * form,
3274 |                                          int page,
3275 |                                          FIELD * field)
3276 |
3277 |   Description   :  Make the given page nr. the current page and make
3278 |                    the given field the current field on the page. If
3279 |                    for the field NULL is given, make the first field on
3280 |                    the page the current field. The routine acts only
3281 |                    if the requested page is not the current page.
3282 |
3283 |   Return Values :  E_OK                - success
3284 |                    != E_OK             - error from subordinate call
3285 +--------------------------------------------------------------------------*/
3286 int
3287 _nc_Set_Form_Page(FORM * form, int page, FIELD * field)
3288 {
3289   int res = E_OK;
3290 
3291   if ((form->curpage!=page))
3292     {
3293       FIELD *last_field, *field_on_page;
3294 
3295       werase(Get_Form_Window(form));
3296       form->curpage = page;
3297       last_field = field_on_page = form->field[form->page[page].smin];
3298       do
3299 	{
3300 	  if (field_on_page->opts & O_VISIBLE)
3301 	    if ((res=Display_Field(field_on_page))!=E_OK)
3302 	      return(res);
3303 	  field_on_page = field_on_page->snext;
3304 	} while(field_on_page != last_field);
3305 
3306       if (field)
3307 	res = _nc_Set_Current_Field(form,field);
3308       else
3309 	/* N.B.: we don't encapsulate this by Inter_Field_Navigation(),
3310 	   because this is already executed in a page navigation
3311 	   context that contains field navigation
3312 	 */
3313 	res = FN_First_Field(form);
3314     }
3315   return(res);
3316 }
3317 
3318 /*---------------------------------------------------------------------------
3319 |   Facility      :  libnform
3320 |   Function      :  static int Next_Page_Number(const FORM * form)
3321 |
3322 |   Description   :  Calculate the page number following the current page
3323 |                    number. This cycles if the highest page number is
3324 |                    reached.
3325 |
3326 |   Return Values :  The next page number
3327 +--------------------------------------------------------------------------*/
3328 INLINE static int Next_Page_Number(const FORM * form)
3329 {
3330   return (form->curpage + 1) % form->maxpage;
3331 }
3332 
3333 /*---------------------------------------------------------------------------
3334 |   Facility      :  libnform
3335 |   Function      :  static int Previous_Page_Number(const FORM * form)
3336 |
3337 |   Description   :  Calculate the page number before the current page
3338 |                    number. This cycles if the first page number is
3339 |                    reached.
3340 |
3341 |   Return Values :  The previous page number
3342 +--------------------------------------------------------------------------*/
3343 INLINE static int Previous_Page_Number(const FORM * form)
3344 {
3345   return (form->curpage!=0 ? form->curpage - 1 : form->maxpage - 1);
3346 }
3347 
3348 /*----------------------------------------------------------------------------
3349   Page Navigation routines
3350   --------------------------------------------------------------------------*/
3351 
3352 /*---------------------------------------------------------------------------
3353 |   Facility      :  libnform
3354 |   Function      :  static int Page_Navigation(
3355 |                                               int (* const fct) (FORM *),
3356 |                                               FORM * form)
3357 |
3358 |   Description   :  Generic behaviour for changing a page. This means
3359 |                    that the field is left and a new field is entered.
3360 |                    So the field must be validated and the field init/term
3361 |                    hooks must be called. Because also the page is changed,
3362 |                    the forms init/term hooks must be called also.
3363 |
3364 |   Return Values :  E_OK                - success
3365 |                    E_INVALID_FIELD     - field is invalid
3366 |                    some other          - error from subordinate call
3367 +--------------------------------------------------------------------------*/
3368 static int Page_Navigation(int (* const fct) (FORM *), FORM * form)
3369 {
3370   int res;
3371 
3372   if (!_nc_Internal_Validation(form))
3373     res = E_INVALID_FIELD;
3374   else
3375     {
3376       Call_Hook(form,fieldterm);
3377       Call_Hook(form,formterm);
3378       res = fct(form);
3379       Call_Hook(form,forminit);
3380       Call_Hook(form,fieldinit);
3381     }
3382   return res;
3383 }
3384 
3385 /*---------------------------------------------------------------------------
3386 |   Facility      :  libnform
3387 |   Function      :  static int PN_Next_Page(FORM * form)
3388 |
3389 |   Description   :  Move to the next page of the form
3390 |
3391 |   Return Values :  E_OK                - success
3392 |                    != E_OK             - error from subordinate call
3393 +--------------------------------------------------------------------------*/
3394 static int PN_Next_Page(FORM * form)
3395 {
3396   return _nc_Set_Form_Page(form,Next_Page_Number(form),(FIELD *)0);
3397 }
3398 
3399 /*---------------------------------------------------------------------------
3400 |   Facility      :  libnform
3401 |   Function      :  static int PN_Previous_Page(FORM * form)
3402 |
3403 |   Description   :  Move to the previous page of the form
3404 |
3405 |   Return Values :  E_OK              - success
3406 |                    != E_OK           - error from subordinate call
3407 +--------------------------------------------------------------------------*/
3408 static int PN_Previous_Page(FORM * form)
3409 {
3410   return _nc_Set_Form_Page(form,Previous_Page_Number(form),(FIELD *)0);
3411 }
3412 
3413 /*---------------------------------------------------------------------------
3414 |   Facility      :  libnform
3415 |   Function      :  static int PN_First_Page(FORM * form)
3416 |
3417 |   Description   :  Move to the first page of the form
3418 |
3419 |   Return Values :  E_OK              - success
3420 |                    != E_OK           - error from subordinate call
3421 +--------------------------------------------------------------------------*/
3422 static int PN_First_Page(FORM * form)
3423 {
3424   return _nc_Set_Form_Page(form,0,(FIELD *)0);
3425 }
3426 
3427 /*---------------------------------------------------------------------------
3428 |   Facility      :  libnform
3429 |   Function      :  static int PN_Last_Page(FORM * form)
3430 |
3431 |   Description   :  Move to the last page of the form
3432 |
3433 |   Return Values :  E_OK              - success
3434 |                    != E_OK           - error from subordinate call
3435 +--------------------------------------------------------------------------*/
3436 static int PN_Last_Page(FORM * form)
3437 {
3438   return _nc_Set_Form_Page(form,form->maxpage-1,(FIELD *)0);
3439 }
3440 /*----------------------------------------------------------------------------
3441   END of Field Navigation routines
3442   --------------------------------------------------------------------------*/
3443 
3444 /*----------------------------------------------------------------------------
3445   Helper routines for the core form driver.
3446   --------------------------------------------------------------------------*/
3447 
3448 /*---------------------------------------------------------------------------
3449 |   Facility      :  libnform
3450 |   Function      :  static int Data_Entry(FORM * form,int c)
3451 |
3452 |   Description   :  Enter character c into at the current position of the
3453 |                    current field of the form.
3454 |
3455 |   Return Values :  E_OK              -
3456 |                    E_REQUEST_DENIED  -
3457 |                    E_SYSTEM_ERROR    -
3458 +--------------------------------------------------------------------------*/
3459 static int Data_Entry(FORM * form, int c)
3460 {
3461   FIELD  *field = form->current;
3462   int result = E_REQUEST_DENIED;
3463 
3464   if ( (field->opts & O_EDIT)
3465 #if FIX_FORM_INACTIVE_BUG
3466        && (field->opts & O_ACTIVE)
3467 #endif
3468        )
3469     {
3470       if ( (field->opts & O_BLANK) &&
3471 	   First_Position_In_Current_Field(form) &&
3472 	   !(form->status & _FCHECK_REQUIRED) &&
3473 	   !(form->status & _WINDOW_MODIFIED) )
3474 	werase(form->w);
3475 
3476       if (form->status & _OVLMODE)
3477 	{
3478 	  waddch(form->w,(chtype)c);
3479 	}
3480       else /* no _OVLMODE */
3481 	{
3482 	  bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
3483 
3484 	  if (!(There_Is_Room ||
3485 		((Single_Line_Field(field) && Growable(field)))))
3486 	      return E_REQUEST_DENIED;
3487 
3488 	  if (!There_Is_Room && !Field_Grown(field,1))
3489 	    return E_SYSTEM_ERROR;
3490 
3491 	  winsch(form->w,(chtype)c);
3492 	}
3493 
3494       if ((result=Wrapping_Not_Necessary_Or_Wrapping_Ok(form))==E_OK)
3495 	{
3496 	  bool End_Of_Field= (((field->drows-1)==form->currow) &&
3497 			      ((field->dcols-1)==form->curcol));
3498 	  form->status |= _WINDOW_MODIFIED;
3499 	  if (End_Of_Field && !Growable(field) && (field->opts & O_AUTOSKIP))
3500 	    result = Inter_Field_Navigation(FN_Next_Field,form);
3501 	  else
3502 	    {
3503 	      if (End_Of_Field && Growable(field) && !Field_Grown(field,1))
3504 		result = E_SYSTEM_ERROR;
3505 	      else
3506 		{
3507 		  IFN_Next_Character(form);
3508 		  result = E_OK;
3509 		}
3510 	    }
3511 	}
3512     }
3513   return result;
3514 }
3515 
3516 /* Structure to describe the binding of a request code to a function.
3517    The member keycode codes the request value as well as the generic
3518    routine to use for the request. The code for the generic routine
3519    is coded in the upper 16 Bits while the request code is coded in
3520    the lower 16 bits.
3521 
3522    In terms of C++ you might think of a request as a class with a
3523    virtual method "perform". The different types of request are
3524    derived from this base class and overload (or not) the base class
3525    implementation of perform.
3526 */
3527 typedef struct {
3528   int keycode;           /* must be at least 32 bit: hi:mode, lo: key */
3529   int (*cmd)(FORM *);    /* low level driver routine for this key     */
3530 } Binding_Info;
3531 
3532 /* You may see this is the class-id of the request type class */
3533 #define ID_PN    (0x00000000)    /* Page navigation           */
3534 #define ID_FN    (0x00010000)    /* Inter-Field navigation    */
3535 #define ID_IFN   (0x00020000)    /* Intra-Field navigation    */
3536 #define ID_VSC   (0x00030000)    /* Vertical Scrolling        */
3537 #define ID_HSC   (0x00040000)    /* Horizontal Scrolling      */
3538 #define ID_FE    (0x00050000)    /* Field Editing             */
3539 #define ID_EM    (0x00060000)    /* Edit Mode                 */
3540 #define ID_FV    (0x00070000)    /* Field Validation          */
3541 #define ID_CH    (0x00080000)    /* Choice                    */
3542 #define ID_Mask  (0xffff0000)
3543 #define Key_Mask (0x0000ffff)
3544 #define ID_Shft  (16)
3545 
3546 /* This array holds all the Binding Infos */
3547 static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =
3548 {
3549   { REQ_NEXT_PAGE    |ID_PN  ,PN_Next_Page},
3550   { REQ_PREV_PAGE    |ID_PN  ,PN_Previous_Page},
3551   { REQ_FIRST_PAGE   |ID_PN  ,PN_First_Page},
3552   { REQ_LAST_PAGE    |ID_PN  ,PN_Last_Page},
3553 
3554   { REQ_NEXT_FIELD   |ID_FN  ,FN_Next_Field},
3555   { REQ_PREV_FIELD   |ID_FN  ,FN_Previous_Field},
3556   { REQ_FIRST_FIELD  |ID_FN  ,FN_First_Field},
3557   { REQ_LAST_FIELD   |ID_FN  ,FN_Last_Field},
3558   { REQ_SNEXT_FIELD  |ID_FN  ,FN_Sorted_Next_Field},
3559   { REQ_SPREV_FIELD  |ID_FN  ,FN_Sorted_Previous_Field},
3560   { REQ_SFIRST_FIELD |ID_FN  ,FN_Sorted_First_Field},
3561   { REQ_SLAST_FIELD  |ID_FN  ,FN_Sorted_Last_Field},
3562   { REQ_LEFT_FIELD   |ID_FN  ,FN_Left_Field},
3563   { REQ_RIGHT_FIELD  |ID_FN  ,FN_Right_Field},
3564   { REQ_UP_FIELD     |ID_FN  ,FN_Up_Field},
3565   { REQ_DOWN_FIELD   |ID_FN  ,FN_Down_Field},
3566 
3567   { REQ_NEXT_CHAR    |ID_IFN ,IFN_Next_Character},
3568   { REQ_PREV_CHAR    |ID_IFN ,IFN_Previous_Character},
3569   { REQ_NEXT_LINE    |ID_IFN ,IFN_Next_Line},
3570   { REQ_PREV_LINE    |ID_IFN ,IFN_Previous_Line},
3571   { REQ_NEXT_WORD    |ID_IFN ,IFN_Next_Word},
3572   { REQ_PREV_WORD    |ID_IFN ,IFN_Previous_Word},
3573   { REQ_BEG_FIELD    |ID_IFN ,IFN_Beginning_Of_Field},
3574   { REQ_END_FIELD    |ID_IFN ,IFN_End_Of_Field},
3575   { REQ_BEG_LINE     |ID_IFN ,IFN_Beginning_Of_Line},
3576   { REQ_END_LINE     |ID_IFN ,IFN_End_Of_Line},
3577   { REQ_LEFT_CHAR    |ID_IFN ,IFN_Left_Character},
3578   { REQ_RIGHT_CHAR   |ID_IFN ,IFN_Right_Character},
3579   { REQ_UP_CHAR      |ID_IFN ,IFN_Up_Character},
3580   { REQ_DOWN_CHAR    |ID_IFN ,IFN_Down_Character},
3581 
3582   { REQ_NEW_LINE     |ID_FE  ,FE_New_Line},
3583   { REQ_INS_CHAR     |ID_FE  ,FE_Insert_Character},
3584   { REQ_INS_LINE     |ID_FE  ,FE_Insert_Line},
3585   { REQ_DEL_CHAR     |ID_FE  ,FE_Delete_Character},
3586   { REQ_DEL_PREV     |ID_FE  ,FE_Delete_Previous},
3587   { REQ_DEL_LINE     |ID_FE  ,FE_Delete_Line},
3588   { REQ_DEL_WORD     |ID_FE  ,FE_Delete_Word},
3589   { REQ_CLR_EOL      |ID_FE  ,FE_Clear_To_End_Of_Line},
3590   { REQ_CLR_EOF      |ID_FE  ,FE_Clear_To_End_Of_Form},
3591   { REQ_CLR_FIELD    |ID_FE  ,FE_Clear_Field},
3592 
3593   { REQ_OVL_MODE     |ID_EM  ,EM_Overlay_Mode},
3594   { REQ_INS_MODE     |ID_EM  ,EM_Insert_Mode},
3595 
3596   { REQ_SCR_FLINE    |ID_VSC ,VSC_Scroll_Line_Forward},
3597   { REQ_SCR_BLINE    |ID_VSC ,VSC_Scroll_Line_Backward},
3598   { REQ_SCR_FPAGE    |ID_VSC ,VSC_Scroll_Page_Forward},
3599   { REQ_SCR_BPAGE    |ID_VSC ,VSC_Scroll_Page_Backward},
3600   { REQ_SCR_FHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Forward},
3601   { REQ_SCR_BHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Backward},
3602 
3603   { REQ_SCR_FCHAR    |ID_HSC ,HSC_Scroll_Char_Forward},
3604   { REQ_SCR_BCHAR    |ID_HSC ,HSC_Scroll_Char_Backward},
3605   { REQ_SCR_HFLINE   |ID_HSC ,HSC_Horizontal_Line_Forward},
3606   { REQ_SCR_HBLINE   |ID_HSC ,HSC_Horizontal_Line_Backward},
3607   { REQ_SCR_HFHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Forward},
3608   { REQ_SCR_HBHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Backward},
3609 
3610   { REQ_VALIDATION   |ID_FV  ,FV_Validation},
3611 
3612   { REQ_NEXT_CHOICE  |ID_CH  ,CR_Next_Choice},
3613   { REQ_PREV_CHOICE  |ID_CH  ,CR_Previous_Choice}
3614 };
3615 
3616 /*---------------------------------------------------------------------------
3617 |   Facility      :  libnform
3618 |   Function      :  int form_driver(FORM * form,int  c)
3619 |
3620 |   Description   :  This is the workhorse of the forms system. It checks
3621 |                    to determine whether the character c is a request or
3622 |                    data. If it is a request, the form driver executes
3623 |                    the request and returns the result. If it is data
3624 |                    (printable character), it enters the data into the
3625 |                    current position in the current field. If it is not
3626 |                    recognized, the form driver assumes it is an application
3627 |                    defined command and returns E_UNKNOWN_COMMAND.
3628 |                    Application defined command should be defined relative
3629 |                    to MAX_FORM_COMMAND, the maximum value of a request.
3630 |
3631 |   Return Values :  E_OK              - success
3632 |                    E_SYSTEM_ERROR    - system error
3633 |                    E_BAD_ARGUMENT    - an argument is incorrect
3634 |                    E_NOT_POSTED      - form is not posted
3635 |                    E_INVALID_FIELD   - field contents are invalid
3636 |                    E_BAD_STATE       - called from inside a hook routine
3637 |                    E_REQUEST_DENIED  - request failed
3638 |                    E_UNKNOWN_COMMAND - command not known
3639 +--------------------------------------------------------------------------*/
3640 int form_driver(FORM * form, int  c)
3641 {
3642   const Binding_Info* BI = (Binding_Info *)0;
3643   int res = E_UNKNOWN_COMMAND;
3644 
3645   if (!form)
3646     RETURN(E_BAD_ARGUMENT);
3647 
3648   if (!(form->field))
3649     RETURN(E_NOT_CONNECTED);
3650 
3651   assert(form->page);
3652 
3653   if (c==FIRST_ACTIVE_MAGIC)
3654     {
3655       form->current = _nc_First_Active_Field(form);
3656       return E_OK;
3657     }
3658 
3659   assert(form->current &&
3660 	 form->current->buf &&
3661 	 (form->current->form == form)
3662 	);
3663 
3664   if ( form->status & _IN_DRIVER )
3665     RETURN(E_BAD_STATE);
3666 
3667   if ( !( form->status & _POSTED ) )
3668     RETURN(E_NOT_POSTED);
3669 
3670   if ((c>=MIN_FORM_COMMAND && c<=MAX_FORM_COMMAND) &&
3671       ((bindings[c-MIN_FORM_COMMAND].keycode & Key_Mask) == c))
3672     BI = &(bindings[c-MIN_FORM_COMMAND]);
3673 
3674   if (BI)
3675     {
3676       typedef int (*Generic_Method)(int (* const)(FORM *),FORM *);
3677       static const Generic_Method Generic_Methods[] =
3678 	{
3679 	  Page_Navigation,         /* overloaded to call field&form hooks */
3680 	  Inter_Field_Navigation,  /* overloaded to call field hooks      */
3681 	  NULL,                    /* Intra-Field is generic              */
3682 	  Vertical_Scrolling,      /* Overloaded to check multi-line      */
3683 	  Horizontal_Scrolling,    /* Overloaded to check single-line     */
3684 	  Field_Editing,           /* Overloaded to mark modification     */
3685 	  NULL,                    /* Edit Mode is generic                */
3686 	  NULL,                    /* Field Validation is generic         */
3687 	  NULL                     /* Choice Request is generic           */
3688 	};
3689       size_t nMethods = (sizeof(Generic_Methods)/sizeof(Generic_Methods[0]));
3690       size_t method   = ((BI->keycode & ID_Mask) >> ID_Shft) & 0xffff;
3691 
3692       if ( (method >= nMethods) || !(BI->cmd) )
3693 	res = E_SYSTEM_ERROR;
3694       else
3695 	{
3696 	  Generic_Method fct = Generic_Methods[method];
3697 	  if (fct)
3698 	    res = fct(BI->cmd,form);
3699 	  else
3700 	    res = (BI->cmd)(form);
3701 	}
3702     }
3703   else
3704     {
3705       if (!(c & (~(int)MAX_REGULAR_CHARACTER)) &&
3706 	  isprint((unsigned char)c) &&
3707 	  Check_Char(form->current->type,c,
3708 		     (TypeArgument *)(form->current->arg)))
3709 	res = Data_Entry(form,c);
3710     }
3711   _nc_Refresh_Current_Field(form);
3712   RETURN(res);
3713 }
3714 
3715 /*----------------------------------------------------------------------------
3716   Field-Buffer manipulation routines.
3717   The effects of setting a buffer is tightly coupled to the core of the form
3718   driver logic. This is especially true in the case of growable fields.
3719   So I don't separate this into an own module.
3720   --------------------------------------------------------------------------*/
3721 
3722 /*---------------------------------------------------------------------------
3723 |   Facility      :  libnform
3724 |   Function      :  int set_field_buffer(FIELD *field,
3725 |                                         int buffer, char *value)
3726 |
3727 |   Description   :  Set the given buffer of the field to the given value.
3728 |                    Buffer 0 stores the displayed content of the field.
3729 |                    For dynamic fields this may grow the fieldbuffers if
3730 |                    the length of the value exceeds the current buffer
3731 |                    length. For buffer 0 only printable values are allowed.
3732 |                    For static fields, the value needs not to be zero ter-
3733 |                    minated. It is copied up to the length of the buffer.
3734 |
3735 |   Return Values :  E_OK            - success
3736 |                    E_BAD_ARGUMENT  - invalid argument
3737 |                    E_SYSTEM_ERROR  - system error
3738 +--------------------------------------------------------------------------*/
3739 int set_field_buffer(FIELD * field, int buffer, const char * value)
3740 {
3741   char *s, *p;
3742   int res = E_OK;
3743   unsigned int len;
3744 
3745   if ( !field || !value || ((buffer < 0)||(buffer > field->nbuf)) )
3746     RETURN(E_BAD_ARGUMENT);
3747 
3748   len  = Buffer_Length(field);
3749 
3750   if (buffer==0)
3751     {
3752       const char *v;
3753       unsigned int i = 0;
3754 
3755       for(v=value; *v && (i<len); v++,i++)
3756 	{
3757 	  if (!isprint((unsigned char)*v))
3758 	    RETURN(E_BAD_ARGUMENT);
3759 	}
3760     }
3761 
3762   if (Growable(field))
3763     {
3764       /* for a growable field we must assume zero terminated strings, because
3765 	 somehow we have to detect the length of what should be copied.
3766       */
3767       unsigned int vlen = strlen(value);
3768       if (vlen > len)
3769 	{
3770 	  if (!Field_Grown(field,
3771 			   (int)(1 + (vlen-len)/((field->rows+field->nrow)*field->cols))))
3772 	    RETURN(E_SYSTEM_ERROR);
3773 
3774 	  /* in this case we also have to check, wether or not the remaining
3775 	     characters in value are also printable for buffer 0. */
3776 	  if (buffer==0)
3777 	    {
3778 	      unsigned int i;
3779 
3780 	      for(i=len; i<vlen; i++)
3781 		if (!isprint(value[i]))
3782 		  RETURN(E_BAD_ARGUMENT);
3783 	    }
3784 	  len = vlen;
3785 	}
3786     }
3787 
3788   p   = Address_Of_Nth_Buffer(field,buffer);
3789 
3790 #if HAVE_MEMCCPY
3791   s = memccpy(p,value,0,len);
3792 #else
3793   for(s=(char *)value; *s && (s < (value+len)); s++)
3794     p[s-value] = *s;
3795   if (s < (value+len))
3796     {
3797       p[s-value] = *s++;
3798       s = p + (s-value);
3799     }
3800   else
3801     s=(char *)0;
3802 #endif
3803 
3804   if (s)
3805     { /* this means, value was null terminated and not greater than the
3806 	 buffer. We have to pad with blanks. Please note that due to memccpy
3807 	 logic s points after the terminating null. */
3808       s--; /* now we point to the terminator. */
3809       assert(len >= (unsigned int)(s-p));
3810       if (len > (unsigned int)(s-p))
3811 	memset(s,C_BLANK,len-(unsigned int)(s-p));
3812     }
3813 
3814   if (buffer==0)
3815     {
3816       int syncres;
3817       if (((syncres=Synchronize_Field( field ))!=E_OK) &&
3818 	  (res==E_OK))
3819 	res = syncres;
3820       if (((syncres=Synchronize_Linked_Fields(field ))!=E_OK) &&
3821 	  (res==E_OK))
3822 	res = syncres;
3823     }
3824   RETURN(res);
3825 }
3826 
3827 /*---------------------------------------------------------------------------
3828 |   Facility      :  libnform
3829 |   Function      :  char *field_buffer(const FIELD *field,int buffer)
3830 |
3831 |   Description   :  Return the address of the buffer for the field.
3832 |
3833 |   Return Values :  Pointer to buffer or NULL if arguments were invalid.
3834 +--------------------------------------------------------------------------*/
3835 char *field_buffer(const FIELD * field, int  buffer)
3836 {
3837   if (field && (buffer >= 0) && (buffer <= field->nbuf))
3838     return Address_Of_Nth_Buffer(field,buffer);
3839   else
3840     return (char *)0;
3841 }
3842 
3843 /* frm_driver.c ends here */
3844