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