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