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