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