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