]> ncurses.scripts.mit.edu Git - ncurses.git/blob - form/frm_driver.c
ncurses 5.6
[ncurses.git] / form / frm_driver.c
1 /****************************************************************************
2  * Copyright (c) 1998-2005,2006 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.76 2006/11/04 18:45:35 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 NCURSES_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 NCURSES_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 NCURSES_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 NCURSES_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 NCURSES_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             )
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 NCURSES_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, WINDOW_ATTRS(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_CURRENT           - field is the current one
1216 |                    E_SYSTEM_ERROR      - some severe basic error
1217 +--------------------------------------------------------------------------*/
1218 NCURSES_EXPORT(int)
1219 _nc_Synchronize_Options(FIELD *field, Field_Options newopts)
1220 {
1221   Field_Options oldopts;
1222   Field_Options changed_opts;
1223   FORM *form;
1224   int res = E_OK;
1225
1226   T((T_CALLED("_nc_Synchronize_Options(%p,%#x)"), field, newopts));
1227
1228   if (!field)
1229     returnCode(E_BAD_ARGUMENT);
1230
1231   oldopts = field->opts;
1232   changed_opts = oldopts ^ newopts;
1233   field->opts = newopts;
1234   form = field->form;
1235
1236   if (form)
1237     {
1238       if (form->current == field)
1239         {
1240           field->opts = oldopts;
1241           returnCode(E_CURRENT);
1242         }
1243
1244       if (form->status & _POSTED)
1245         {
1246           if ((form->curpage == field->page))
1247             {
1248               if (changed_opts & O_VISIBLE)
1249                 {
1250                   if (newopts & O_VISIBLE)
1251                     res = Display_Field(field);
1252                   else
1253                     res = Erase_Field(field);
1254                 }
1255               else
1256                 {
1257                   if ((changed_opts & O_PUBLIC) &&
1258                       (newopts & O_VISIBLE))
1259                     res = Display_Field(field);
1260                 }
1261             }
1262         }
1263     }
1264
1265   if (changed_opts & O_STATIC)
1266     {
1267       bool single_line_field = Single_Line_Field(field);
1268       int res2 = E_OK;
1269
1270       if (newopts & O_STATIC)
1271         {
1272           /* the field becomes now static */
1273           field->status &= ~_MAY_GROW;
1274           /* if actually we have no hidden columns, justification may
1275              occur again */
1276           if (single_line_field &&
1277               (field->cols == field->dcols) &&
1278               (field->just != NO_JUSTIFICATION) &&
1279               Field_Really_Appears(field))
1280             {
1281               res2 = Display_Field(field);
1282             }
1283         }
1284       else
1285         {
1286           /* field is no longer static */
1287           if ((field->maxgrow == 0) ||
1288               (single_line_field && (field->dcols < field->maxgrow)) ||
1289               (!single_line_field && (field->drows < field->maxgrow)))
1290             {
1291               field->status |= _MAY_GROW;
1292               /* a field with justification now changes its behavior,
1293                  so we must redisplay it */
1294               if (single_line_field &&
1295                   (field->just != NO_JUSTIFICATION) &&
1296                   Field_Really_Appears(field))
1297                 {
1298                   res2 = Display_Field(field);
1299                 }
1300             }
1301         }
1302       if (res2 != E_OK)
1303         res = res2;
1304     }
1305
1306   returnCode(res);
1307 }
1308
1309 /*---------------------------------------------------------------------------
1310 |   Facility      :  libnform
1311 |   Function      :  int _nc_Set_Current_Field(FORM  * form,
1312 |                                              FIELD * newfield)
1313 |
1314 |   Description   :  Make the newfield the new current field.
1315 |
1316 |   Return Values :  E_OK              - success
1317 |                    E_BAD_ARGUMENT    - invalid form or field pointer
1318 |                    E_SYSTEM_ERROR    - some severe basic error
1319 |                    E_NOT_CONNECTED   - no fields are connected to the form
1320 +--------------------------------------------------------------------------*/
1321 NCURSES_EXPORT(int)
1322 _nc_Set_Current_Field(FORM *form, FIELD *newfield)
1323 {
1324   FIELD *field;
1325   WINDOW *new_window;
1326
1327   T((T_CALLED("_nc_Set_Current_Field(%p,%p)"), form, newfield));
1328
1329   if (!form || !newfield || !form->current || (newfield->form != form))
1330     returnCode(E_BAD_ARGUMENT);
1331
1332   if ((form->status & _IN_DRIVER))
1333     returnCode(E_BAD_STATE);
1334
1335   if (!(form->field))
1336     returnCode(E_NOT_CONNECTED);
1337
1338   field = form->current;
1339
1340   if ((field != newfield) ||
1341       !(form->status & _POSTED))
1342     {
1343       if ((form->w) &&
1344           (field->opts & O_VISIBLE) &&
1345           (field->form->curpage == field->page))
1346         {
1347           _nc_Refresh_Current_Field(form);
1348           if (field->opts & O_PUBLIC)
1349             {
1350               if (field->drows > field->rows)
1351                 {
1352                   if (form->toprow == 0)
1353                     field->status &= ~_NEWTOP;
1354                   else
1355                     field->status |= _NEWTOP;
1356                 }
1357               else
1358                 {
1359                   if (Justification_Allowed(field))
1360                     {
1361                       Window_To_Buffer(form->w, field);
1362                       werase(form->w);
1363                       Perform_Justification(field, form->w);
1364                       wsyncup(form->w);
1365                     }
1366                 }
1367             }
1368           delwin(form->w);
1369           form->w = (WINDOW *)0;
1370         }
1371
1372       field = newfield;
1373
1374       if (Has_Invisible_Parts(field))
1375         new_window = newpad(field->drows, field->dcols);
1376       else
1377         new_window = derwin(Get_Form_Window(form),
1378                             field->rows, field->cols, field->frow, field->fcol);
1379
1380       if (!new_window)
1381         returnCode(E_SYSTEM_ERROR);
1382
1383       form->current = field;
1384
1385       if (form->w)
1386         delwin(form->w);
1387       form->w = new_window;
1388
1389       form->status &= ~_WINDOW_MODIFIED;
1390       Set_Field_Window_Attributes(field, form->w);
1391
1392       if (Has_Invisible_Parts(field))
1393         {
1394           werase(form->w);
1395           Buffer_To_Window(field, form->w);
1396         }
1397       else
1398         {
1399           if (Justification_Allowed(field))
1400             {
1401               werase(form->w);
1402               Undo_Justification(field, form->w);
1403               wsyncup(form->w);
1404             }
1405         }
1406
1407       untouchwin(form->w);
1408     }
1409
1410   form->currow = form->curcol = form->toprow = form->begincol = 0;
1411   returnCode(E_OK);
1412 }
1413
1414 /*----------------------------------------------------------------------------
1415   Intra-Field Navigation routines
1416   --------------------------------------------------------------------------*/
1417
1418 /*---------------------------------------------------------------------------
1419 |   Facility      :  libnform
1420 |   Function      :  static int IFN_Next_Character(FORM * form)
1421 |
1422 |   Description   :  Move to the next character in the field. In a multi-line
1423 |                    field this wraps at the end of the line.
1424 |
1425 |   Return Values :  E_OK                - success
1426 |                    E_REQUEST_DENIED    - at the rightmost position
1427 +--------------------------------------------------------------------------*/
1428 static int
1429 IFN_Next_Character(FORM *form)
1430 {
1431   FIELD *field = form->current;
1432   int step = myWCWIDTH(form->w, form->currow, form->curcol);
1433
1434   T((T_CALLED("IFN_Next_Character(%p)"), form));
1435   if ((form->curcol += step) == field->dcols)
1436     {
1437       if ((++(form->currow)) == field->drows)
1438         {
1439 #if GROW_IF_NAVIGATE
1440           if (!Single_Line_Field(field) && Field_Grown(field, 1))
1441             {
1442               form->curcol = 0;
1443               returnCode(E_OK);
1444             }
1445 #endif
1446           form->currow--;
1447 #if GROW_IF_NAVIGATE
1448           if (Single_Line_Field(field) && Field_Grown(field, 1))
1449             returnCode(E_OK);
1450 #endif
1451           form->curcol -= step;
1452           returnCode(E_REQUEST_DENIED);
1453         }
1454       form->curcol = 0;
1455     }
1456   returnCode(E_OK);
1457 }
1458
1459 /*---------------------------------------------------------------------------
1460 |   Facility      :  libnform
1461 |   Function      :  static int IFN_Previous_Character(FORM * form)
1462 |
1463 |   Description   :  Move to the previous character in the field. In a
1464 |                    multi-line field this wraps and the beginning of the
1465 |                    line.
1466 |
1467 |   Return Values :  E_OK                - success
1468 |                    E_REQUEST_DENIED    - at the leftmost position
1469 +--------------------------------------------------------------------------*/
1470 static int
1471 IFN_Previous_Character(FORM *form)
1472 {
1473   int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1474   int oldcol = form->curcol;
1475
1476   T((T_CALLED("IFN_Previous_Character(%p)"), form));
1477   if ((form->curcol -= amount) < 0)
1478     {
1479       if ((--(form->currow)) < 0)
1480         {
1481           form->currow++;
1482           form->curcol = oldcol;
1483           returnCode(E_REQUEST_DENIED);
1484         }
1485       form->curcol = form->current->dcols - 1;
1486     }
1487   returnCode(E_OK);
1488 }
1489
1490 /*---------------------------------------------------------------------------
1491 |   Facility      :  libnform
1492 |   Function      :  static int IFN_Next_Line(FORM * form)
1493 |
1494 |   Description   :  Move to the beginning of the next line in the field
1495 |
1496 |   Return Values :  E_OK                - success
1497 |                    E_REQUEST_DENIED    - at the last line
1498 +--------------------------------------------------------------------------*/
1499 static int
1500 IFN_Next_Line(FORM *form)
1501 {
1502   FIELD *field = form->current;
1503
1504   T((T_CALLED("IFN_Next_Line(%p)"), form));
1505   if ((++(form->currow)) == field->drows)
1506     {
1507 #if GROW_IF_NAVIGATE
1508       if (!Single_Line_Field(field) && Field_Grown(field, 1))
1509         returnCode(E_OK);
1510 #endif
1511       form->currow--;
1512       returnCode(E_REQUEST_DENIED);
1513     }
1514   form->curcol = 0;
1515   returnCode(E_OK);
1516 }
1517
1518 /*---------------------------------------------------------------------------
1519 |   Facility      :  libnform
1520 |   Function      :  static int IFN_Previous_Line(FORM * form)
1521 |
1522 |   Description   :  Move to the beginning of the previous line in the field
1523 |
1524 |   Return Values :  E_OK                - success
1525 |                    E_REQUEST_DENIED    - at the first line
1526 +--------------------------------------------------------------------------*/
1527 static int
1528 IFN_Previous_Line(FORM *form)
1529 {
1530   T((T_CALLED("IFN_Previous_Line(%p)"), form));
1531   if ((--(form->currow)) < 0)
1532     {
1533       form->currow++;
1534       returnCode(E_REQUEST_DENIED);
1535     }
1536   form->curcol = 0;
1537   returnCode(E_OK);
1538 }
1539
1540 /*---------------------------------------------------------------------------
1541 |   Facility      :  libnform
1542 |   Function      :  static int IFN_Next_Word(FORM * form)
1543 |
1544 |   Description   :  Move to the beginning of the next word in the field.
1545 |
1546 |   Return Values :  E_OK             - success
1547 |                    E_REQUEST_DENIED - there is no next word
1548 +--------------------------------------------------------------------------*/
1549 static int
1550 IFN_Next_Word(FORM *form)
1551 {
1552   FIELD *field = form->current;
1553   FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1554   FIELD_CELL *s;
1555   FIELD_CELL *t;
1556
1557   T((T_CALLED("IFN_Next_Word(%p)"), form));
1558
1559   /* We really need access to the data, so we have to synchronize */
1560   Synchronize_Buffer(form);
1561
1562   /* Go to the first whitespace after the current position (including
1563      current position). This is then the starting point to look for the
1564      next non-blank data */
1565   s = Get_First_Whitespace_Character(bp, Buffer_Length(field) -
1566                                      (int)(bp - field->buf));
1567
1568   /* Find the start of the next word */
1569   t = Get_Start_Of_Data(s, Buffer_Length(field) -
1570                         (int)(s - field->buf));
1571 #if !FRIENDLY_PREV_NEXT_WORD
1572   if (s == t)
1573     returnCode(E_REQUEST_DENIED);
1574   else
1575 #endif
1576     {
1577       Adjust_Cursor_Position(form, t);
1578       returnCode(E_OK);
1579     }
1580 }
1581
1582 /*---------------------------------------------------------------------------
1583 |   Facility      :  libnform
1584 |   Function      :  static int IFN_Previous_Word(FORM * form)
1585 |
1586 |   Description   :  Move to the beginning of the previous word in the field.
1587 |
1588 |   Return Values :  E_OK             - success
1589 |                    E_REQUEST_DENIED - there is no previous word
1590 +--------------------------------------------------------------------------*/
1591 static int
1592 IFN_Previous_Word(FORM *form)
1593 {
1594   FIELD *field = form->current;
1595   FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1596   FIELD_CELL *s;
1597   FIELD_CELL *t;
1598   bool again = FALSE;
1599
1600   T((T_CALLED("IFN_Previous_Word(%p)"), form));
1601
1602   /* We really need access to the data, so we have to synchronize */
1603   Synchronize_Buffer(form);
1604
1605   s = After_End_Of_Data(field->buf, (int)(bp - field->buf));
1606   /* s points now right after the last non-blank in the buffer before bp.
1607      If bp was in a word, s equals bp. In this case we must find the last
1608      whitespace in the buffer before bp and repeat the game to really find
1609      the previous word! */
1610   if (s == bp)
1611     again = TRUE;
1612
1613   /* And next call now goes backward to look for the last whitespace
1614      before that, pointing right after this, so it points to the begin
1615      of the previous word.
1616    */
1617   t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1618 #if !FRIENDLY_PREV_NEXT_WORD
1619   if (s == t)
1620     returnCode(E_REQUEST_DENIED);
1621 #endif
1622   if (again)
1623     {
1624       /* and do it again, replacing bp by t */
1625       s = After_End_Of_Data(field->buf, (int)(t - field->buf));
1626       t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1627 #if !FRIENDLY_PREV_NEXT_WORD
1628       if (s == t)
1629         returnCode(E_REQUEST_DENIED);
1630 #endif
1631     }
1632   Adjust_Cursor_Position(form, t);
1633   returnCode(E_OK);
1634 }
1635
1636 /*---------------------------------------------------------------------------
1637 |   Facility      :  libnform
1638 |   Function      :  static int IFN_Beginning_Of_Field(FORM * form)
1639 |
1640 |   Description   :  Place the cursor at the first non-pad character in
1641 |                    the field.
1642 |
1643 |   Return Values :  E_OK             - success
1644 +--------------------------------------------------------------------------*/
1645 static int
1646 IFN_Beginning_Of_Field(FORM *form)
1647 {
1648   FIELD *field = form->current;
1649
1650   T((T_CALLED("IFN_Beginning_Of_Field(%p)"), form));
1651   Synchronize_Buffer(form);
1652   Adjust_Cursor_Position(form,
1653                          Get_Start_Of_Data(field->buf, Buffer_Length(field)));
1654   returnCode(E_OK);
1655 }
1656
1657 /*---------------------------------------------------------------------------
1658 |   Facility      :  libnform
1659 |   Function      :  static int IFN_End_Of_Field(FORM * form)
1660 |
1661 |   Description   :  Place the cursor after the last non-pad character in
1662 |                    the field. If the field occupies the last position in
1663 |                    the buffer, the cursor is positioned on the last
1664 |                    character.
1665 |
1666 |   Return Values :  E_OK              - success
1667 +--------------------------------------------------------------------------*/
1668 static int
1669 IFN_End_Of_Field(FORM *form)
1670 {
1671   FIELD *field = form->current;
1672   FIELD_CELL *pos;
1673
1674   T((T_CALLED("IFN_End_Of_Field(%p)"), form));
1675   Synchronize_Buffer(form);
1676   pos = After_End_Of_Data(field->buf, Buffer_Length(field));
1677   if (pos == (field->buf + Buffer_Length(field)))
1678     pos--;
1679   Adjust_Cursor_Position(form, pos);
1680   returnCode(E_OK);
1681 }
1682
1683 /*---------------------------------------------------------------------------
1684 |   Facility      :  libnform
1685 |   Function      :  static int IFN_Beginning_Of_Line(FORM * form)
1686 |
1687 |   Description   :  Place the cursor on the first non-pad character in
1688 |                    the current line of the field.
1689 |
1690 |   Return Values :  E_OK         - success
1691 +--------------------------------------------------------------------------*/
1692 static int
1693 IFN_Beginning_Of_Line(FORM *form)
1694 {
1695   FIELD *field = form->current;
1696
1697   T((T_CALLED("IFN_Beginning_Of_Line(%p)"), form));
1698   Synchronize_Buffer(form);
1699   Adjust_Cursor_Position(form,
1700                          Get_Start_Of_Data(Address_Of_Current_Row_In_Buffer(form),
1701                                            field->dcols));
1702   returnCode(E_OK);
1703 }
1704
1705 /*---------------------------------------------------------------------------
1706 |   Facility      :  libnform
1707 |   Function      :  static int IFN_End_Of_Line(FORM * form)
1708 |
1709 |   Description   :  Place the cursor after the last non-pad character in the
1710 |                    current line of the field. If the field occupies the
1711 |                    last column in the line, the cursor is positioned on the
1712 |                    last character of the line.
1713 |
1714 |   Return Values :  E_OK        - success
1715 +--------------------------------------------------------------------------*/
1716 static int
1717 IFN_End_Of_Line(FORM *form)
1718 {
1719   FIELD *field = form->current;
1720   FIELD_CELL *pos;
1721   FIELD_CELL *bp;
1722
1723   T((T_CALLED("IFN_End_Of_Line(%p)"), form));
1724   Synchronize_Buffer(form);
1725   bp = Address_Of_Current_Row_In_Buffer(form);
1726   pos = After_End_Of_Data(bp, field->dcols);
1727   if (pos == (bp + field->dcols))
1728     pos--;
1729   Adjust_Cursor_Position(form, pos);
1730   returnCode(E_OK);
1731 }
1732
1733 /*---------------------------------------------------------------------------
1734 |   Facility      :  libnform
1735 |   Function      :  static int IFN_Left_Character(FORM * form)
1736 |
1737 |   Description   :  Move one character to the left in the current line.
1738 |                    This doesn't cycle.
1739 |
1740 |   Return Values :  E_OK             - success
1741 |                    E_REQUEST_DENIED - already in first column
1742 +--------------------------------------------------------------------------*/
1743 static int
1744 IFN_Left_Character(FORM *form)
1745 {
1746   int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1747   int oldcol = form->curcol;
1748
1749   T((T_CALLED("IFN_Left_Character(%p)"), form));
1750   if ((form->curcol -= amount) < 0)
1751     {
1752       form->curcol = oldcol;
1753       returnCode(E_REQUEST_DENIED);
1754     }
1755   returnCode(E_OK);
1756 }
1757
1758 /*---------------------------------------------------------------------------
1759 |   Facility      :  libnform
1760 |   Function      :  static int IFN_Right_Character(FORM * form)
1761 |
1762 |   Description   :  Move one character to the right in the current line.
1763 |                    This doesn't cycle.
1764 |
1765 |   Return Values :  E_OK              - success
1766 |                    E_REQUEST_DENIED  - already in last column
1767 +--------------------------------------------------------------------------*/
1768 static int
1769 IFN_Right_Character(FORM *form)
1770 {
1771   int amount = myWCWIDTH(form->w, form->currow, form->curcol);
1772   int oldcol = form->curcol;
1773
1774   T((T_CALLED("IFN_Right_Character(%p)"), form));
1775   if ((form->curcol += amount) >= form->current->dcols)
1776     {
1777 #if GROW_IF_NAVIGATE
1778       FIELD *field = form->current;
1779
1780       if (Single_Line_Field(field) && Field_Grown(field, 1))
1781         returnCode(E_OK);
1782 #endif
1783       form->curcol = oldcol;
1784       returnCode(E_REQUEST_DENIED);
1785     }
1786   returnCode(E_OK);
1787 }
1788
1789 /*---------------------------------------------------------------------------
1790 |   Facility      :  libnform
1791 |   Function      :  static int IFN_Up_Character(FORM * form)
1792 |
1793 |   Description   :  Move one line up. This doesn't cycle through the lines
1794 |                    of the field.
1795 |
1796 |   Return Values :  E_OK              - success
1797 |                    E_REQUEST_DENIED  - already in last column
1798 +--------------------------------------------------------------------------*/
1799 static int
1800 IFN_Up_Character(FORM *form)
1801 {
1802   T((T_CALLED("IFN_Up_Character(%p)"), form));
1803   if ((--(form->currow)) < 0)
1804     {
1805       form->currow++;
1806       returnCode(E_REQUEST_DENIED);
1807     }
1808   returnCode(E_OK);
1809 }
1810
1811 /*---------------------------------------------------------------------------
1812 |   Facility      :  libnform
1813 |   Function      :  static int IFN_Down_Character(FORM * form)
1814 |
1815 |   Description   :  Move one line down. This doesn't cycle through the
1816 |                    lines of the field.
1817 |
1818 |   Return Values :  E_OK              - success
1819 |                    E_REQUEST_DENIED  - already in last column
1820 +--------------------------------------------------------------------------*/
1821 static int
1822 IFN_Down_Character(FORM *form)
1823 {
1824   FIELD *field = form->current;
1825
1826   T((T_CALLED("IFN_Down_Character(%p)"), form));
1827   if ((++(form->currow)) == field->drows)
1828     {
1829 #if GROW_IF_NAVIGATE
1830       if (!Single_Line_Field(field) && Field_Grown(field, 1))
1831         returnCode(E_OK);
1832 #endif
1833       --(form->currow);
1834       returnCode(E_REQUEST_DENIED);
1835     }
1836   returnCode(E_OK);
1837 }
1838 /*----------------------------------------------------------------------------
1839   END of Intra-Field Navigation routines
1840   --------------------------------------------------------------------------*/
1841
1842 /*----------------------------------------------------------------------------
1843   Vertical scrolling helper routines
1844   --------------------------------------------------------------------------*/
1845
1846 /*---------------------------------------------------------------------------
1847 |   Facility      :  libnform
1848 |   Function      :  static int VSC_Generic(FORM *form, int nlines)
1849 |
1850 |   Description   :  Scroll multi-line field forward (nlines>0) or
1851 |                    backward (nlines<0) this many lines.
1852 |
1853 |   Return Values :  E_OK              - success
1854 |                    E_REQUEST_DENIED  - can't scroll
1855 +--------------------------------------------------------------------------*/
1856 static int
1857 VSC_Generic(FORM *form, int nlines)
1858 {
1859   FIELD *field = form->current;
1860   int res = E_REQUEST_DENIED;
1861   int rows_to_go = (nlines > 0 ? nlines : -nlines);
1862
1863   if (nlines > 0)
1864     {
1865       if ((rows_to_go + form->toprow) > (field->drows - field->rows))
1866         rows_to_go = (field->drows - field->rows - form->toprow);
1867
1868       if (rows_to_go > 0)
1869         {
1870           form->currow += rows_to_go;
1871           form->toprow += rows_to_go;
1872           res = E_OK;
1873         }
1874     }
1875   else
1876     {
1877       if (rows_to_go > form->toprow)
1878         rows_to_go = form->toprow;
1879
1880       if (rows_to_go > 0)
1881         {
1882           form->currow -= rows_to_go;
1883           form->toprow -= rows_to_go;
1884           res = E_OK;
1885         }
1886     }
1887   return (res);
1888 }
1889 /*----------------------------------------------------------------------------
1890   End of Vertical scrolling helper routines
1891   --------------------------------------------------------------------------*/
1892
1893 /*----------------------------------------------------------------------------
1894   Vertical scrolling routines
1895   --------------------------------------------------------------------------*/
1896
1897 /*---------------------------------------------------------------------------
1898 |   Facility      :  libnform
1899 |   Function      :  static int Vertical_Scrolling(
1900 |                                           int (* const fct) (FORM *),
1901 |                                           FORM * form)
1902 |
1903 |   Description   :  Performs the generic vertical scrolling routines.
1904 |                    This has to check for a multi-line field and to set
1905 |                    the _NEWTOP flag if scrolling really occurred.
1906 |
1907 |   Return Values :  Propagated error code from low-level driver calls
1908 +--------------------------------------------------------------------------*/
1909 static int
1910 Vertical_Scrolling(int (*const fct) (FORM *), FORM *form)
1911 {
1912   int res = E_REQUEST_DENIED;
1913
1914   if (!Single_Line_Field(form->current))
1915     {
1916       res = fct(form);
1917       if (res == E_OK)
1918         form->current->status |= _NEWTOP;
1919     }
1920   return (res);
1921 }
1922
1923 /*---------------------------------------------------------------------------
1924 |   Facility      :  libnform
1925 |   Function      :  static int VSC_Scroll_Line_Forward(FORM * form)
1926 |
1927 |   Description   :  Scroll multi-line field forward a line
1928 |
1929 |   Return Values :  E_OK                - success
1930 |                    E_REQUEST_DENIED    - no data ahead
1931 +--------------------------------------------------------------------------*/
1932 static int
1933 VSC_Scroll_Line_Forward(FORM *form)
1934 {
1935   T((T_CALLED("VSC_Scroll_Line_Forward(%p)"), form));
1936   returnCode(VSC_Generic(form, 1));
1937 }
1938
1939 /*---------------------------------------------------------------------------
1940 |   Facility      :  libnform
1941 |   Function      :  static int VSC_Scroll_Line_Backward(FORM * form)
1942 |
1943 |   Description   :  Scroll multi-line field backward a line
1944 |
1945 |   Return Values :  E_OK                - success
1946 |                    E_REQUEST_DENIED    - no data behind
1947 +--------------------------------------------------------------------------*/
1948 static int
1949 VSC_Scroll_Line_Backward(FORM *form)
1950 {
1951   T((T_CALLED("VSC_Scroll_Line_Backward(%p)"), form));
1952   returnCode(VSC_Generic(form, -1));
1953 }
1954
1955 /*---------------------------------------------------------------------------
1956 |   Facility      :  libnform
1957 |   Function      :  static int VSC_Scroll_Page_Forward(FORM * form)
1958 |
1959 |   Description   :  Scroll a multi-line field forward a page
1960 |
1961 |   Return Values :  E_OK              - success
1962 |                    E_REQUEST_DENIED  - no data ahead
1963 +--------------------------------------------------------------------------*/
1964 static int
1965 VSC_Scroll_Page_Forward(FORM *form)
1966 {
1967   T((T_CALLED("VSC_Scroll_Page_Forward(%p)"), form));
1968   returnCode(VSC_Generic(form, form->current->rows));
1969 }
1970
1971 /*---------------------------------------------------------------------------
1972 |   Facility      :  libnform
1973 |   Function      :  static int VSC_Scroll_Half_Page_Forward(FORM * form)
1974 |
1975 |   Description   :  Scroll a multi-line field forward half a page
1976 |
1977 |   Return Values :  E_OK              - success
1978 |                    E_REQUEST_DENIED  - no data ahead
1979 +--------------------------------------------------------------------------*/
1980 static int
1981 VSC_Scroll_Half_Page_Forward(FORM *form)
1982 {
1983   T((T_CALLED("VSC_Scroll_Half_Page_Forward(%p)"), form));
1984   returnCode(VSC_Generic(form, (form->current->rows + 1) / 2));
1985 }
1986
1987 /*---------------------------------------------------------------------------
1988 |   Facility      :  libnform
1989 |   Function      :  static int VSC_Scroll_Page_Backward(FORM * form)
1990 |
1991 |   Description   :  Scroll a multi-line field backward a page
1992 |
1993 |   Return Values :  E_OK              - success
1994 |                    E_REQUEST_DENIED  - no data behind
1995 +--------------------------------------------------------------------------*/
1996 static int
1997 VSC_Scroll_Page_Backward(FORM *form)
1998 {
1999   T((T_CALLED("VSC_Scroll_Page_Backward(%p)"), form));
2000   returnCode(VSC_Generic(form, -(form->current->rows)));
2001 }
2002
2003 /*---------------------------------------------------------------------------
2004 |   Facility      :  libnform
2005 |   Function      :  static int VSC_Scroll_Half_Page_Backward(FORM * form)
2006 |
2007 |   Description   :  Scroll a multi-line field backward half a page
2008 |
2009 |   Return Values :  E_OK              - success
2010 |                    E_REQUEST_DENIED  - no data behind
2011 +--------------------------------------------------------------------------*/
2012 static int
2013 VSC_Scroll_Half_Page_Backward(FORM *form)
2014 {
2015   T((T_CALLED("VSC_Scroll_Half_Page_Backward(%p)"), form));
2016   returnCode(VSC_Generic(form, -((form->current->rows + 1) / 2)));
2017 }
2018 /*----------------------------------------------------------------------------
2019   End of Vertical scrolling routines
2020   --------------------------------------------------------------------------*/
2021
2022 /*----------------------------------------------------------------------------
2023   Horizontal scrolling helper routines
2024   --------------------------------------------------------------------------*/
2025
2026 /*---------------------------------------------------------------------------
2027 |   Facility      :  libnform
2028 |   Function      :  static int HSC_Generic(FORM *form, int ncolumns)
2029 |
2030 |   Description   :  Scroll single-line field forward (ncolumns>0) or
2031 |                    backward (ncolumns<0) this many columns.
2032 |
2033 |   Return Values :  E_OK              - success
2034 |                    E_REQUEST_DENIED  - can't scroll
2035 +--------------------------------------------------------------------------*/
2036 static int
2037 HSC_Generic(FORM *form, int ncolumns)
2038 {
2039   FIELD *field = form->current;
2040   int res = E_REQUEST_DENIED;
2041   int cols_to_go = (ncolumns > 0 ? ncolumns : -ncolumns);
2042
2043   if (ncolumns > 0)
2044     {
2045       if ((cols_to_go + form->begincol) > (field->dcols - field->cols))
2046         cols_to_go = field->dcols - field->cols - form->begincol;
2047
2048       if (cols_to_go > 0)
2049         {
2050           form->curcol += cols_to_go;
2051           form->begincol += cols_to_go;
2052           res = E_OK;
2053         }
2054     }
2055   else
2056     {
2057       if (cols_to_go > form->begincol)
2058         cols_to_go = form->begincol;
2059
2060       if (cols_to_go > 0)
2061         {
2062           form->curcol -= cols_to_go;
2063           form->begincol -= cols_to_go;
2064           res = E_OK;
2065         }
2066     }
2067   return (res);
2068 }
2069 /*----------------------------------------------------------------------------
2070   End of Horizontal scrolling helper routines
2071   --------------------------------------------------------------------------*/
2072
2073 /*----------------------------------------------------------------------------
2074   Horizontal scrolling routines
2075   --------------------------------------------------------------------------*/
2076
2077 /*---------------------------------------------------------------------------
2078 |   Facility      :  libnform
2079 |   Function      :  static int Horizontal_Scrolling(
2080 |                                          int (* const fct) (FORM *),
2081 |                                          FORM * form)
2082 |
2083 |   Description   :  Performs the generic horizontal scrolling routines.
2084 |                    This has to check for a single-line field.
2085 |
2086 |   Return Values :  Propagated error code from low-level driver calls
2087 +--------------------------------------------------------------------------*/
2088 static int
2089 Horizontal_Scrolling(int (*const fct) (FORM *), FORM *form)
2090 {
2091   if (Single_Line_Field(form->current))
2092     return fct(form);
2093   else
2094     return (E_REQUEST_DENIED);
2095 }
2096
2097 /*---------------------------------------------------------------------------
2098 |   Facility      :  libnform
2099 |   Function      :  static int HSC_Scroll_Char_Forward(FORM * form)
2100 |
2101 |   Description   :  Scroll single-line field forward a character
2102 |
2103 |   Return Values :  E_OK                - success
2104 |                    E_REQUEST_DENIED    - no data ahead
2105 +--------------------------------------------------------------------------*/
2106 static int
2107 HSC_Scroll_Char_Forward(FORM *form)
2108 {
2109   T((T_CALLED("HSC_Scroll_Char_Forward(%p)"), form));
2110   returnCode(HSC_Generic(form, 1));
2111 }
2112
2113 /*---------------------------------------------------------------------------
2114 |   Facility      :  libnform
2115 |   Function      :  static int HSC_Scroll_Char_Backward(FORM * form)
2116 |
2117 |   Description   :  Scroll single-line field backward a character
2118 |
2119 |   Return Values :  E_OK                - success
2120 |                    E_REQUEST_DENIED    - no data behind
2121 +--------------------------------------------------------------------------*/
2122 static int
2123 HSC_Scroll_Char_Backward(FORM *form)
2124 {
2125   T((T_CALLED("HSC_Scroll_Char_Backward(%p)"), form));
2126   returnCode(HSC_Generic(form, -1));
2127 }
2128
2129 /*---------------------------------------------------------------------------
2130 |   Facility      :  libnform
2131 |   Function      :  static int HSC_Horizontal_Line_Forward(FORM* form)
2132 |
2133 |   Description   :  Scroll single-line field forward a line
2134 |
2135 |   Return Values :  E_OK                - success
2136 |                    E_REQUEST_DENIED    - no data ahead
2137 +--------------------------------------------------------------------------*/
2138 static int
2139 HSC_Horizontal_Line_Forward(FORM *form)
2140 {
2141   T((T_CALLED("HSC_Horizontal_Line_Forward(%p)"), form));
2142   returnCode(HSC_Generic(form, form->current->cols));
2143 }
2144
2145 /*---------------------------------------------------------------------------
2146 |   Facility      :  libnform
2147 |   Function      :  static int HSC_Horizontal_Half_Line_Forward(FORM* form)
2148 |
2149 |   Description   :  Scroll single-line field forward half a line
2150 |
2151 |   Return Values :  E_OK               - success
2152 |                    E_REQUEST_DENIED   - no data ahead
2153 +--------------------------------------------------------------------------*/
2154 static int
2155 HSC_Horizontal_Half_Line_Forward(FORM *form)
2156 {
2157   T((T_CALLED("HSC_Horizontal_Half_Line_Forward(%p)"), form));
2158   returnCode(HSC_Generic(form, (form->current->cols + 1) / 2));
2159 }
2160
2161 /*---------------------------------------------------------------------------
2162 |   Facility      :  libnform
2163 |   Function      :  static int HSC_Horizontal_Line_Backward(FORM* form)
2164 |
2165 |   Description   :  Scroll single-line field backward a line
2166 |
2167 |   Return Values :  E_OK                - success
2168 |                    E_REQUEST_DENIED    - no data behind
2169 +--------------------------------------------------------------------------*/
2170 static int
2171 HSC_Horizontal_Line_Backward(FORM *form)
2172 {
2173   T((T_CALLED("HSC_Horizontal_Line_Backward(%p)"), form));
2174   returnCode(HSC_Generic(form, -(form->current->cols)));
2175 }
2176
2177 /*---------------------------------------------------------------------------
2178 |   Facility      :  libnform
2179 |   Function      :  static int HSC_Horizontal_Half_Line_Backward(FORM* form)
2180 |
2181 |   Description   :  Scroll single-line field backward half a line
2182 |
2183 |   Return Values :  E_OK                - success
2184 |                    E_REQUEST_DENIED    - no data behind
2185 +--------------------------------------------------------------------------*/
2186 static int
2187 HSC_Horizontal_Half_Line_Backward(FORM *form)
2188 {
2189   T((T_CALLED("HSC_Horizontal_Half_Line_Backward(%p)"), form));
2190   returnCode(HSC_Generic(form, -((form->current->cols + 1) / 2)));
2191 }
2192
2193 /*----------------------------------------------------------------------------
2194   End of Horizontal scrolling routines
2195   --------------------------------------------------------------------------*/
2196
2197 /*----------------------------------------------------------------------------
2198   Helper routines for Field Editing
2199   --------------------------------------------------------------------------*/
2200
2201 /*---------------------------------------------------------------------------
2202 |   Facility      :  libnform
2203 |   Function      :  static bool Is_There_Room_For_A_Line(FORM * form)
2204 |
2205 |   Description   :  Check whether or not there is enough room in the
2206 |                    buffer to enter a whole line.
2207 |
2208 |   Return Values :  TRUE   - there is enough space
2209 |                    FALSE  - there is not enough space
2210 +--------------------------------------------------------------------------*/
2211 NCURSES_INLINE static bool
2212 Is_There_Room_For_A_Line(FORM *form)
2213 {
2214   FIELD *field = form->current;
2215   FIELD_CELL *begin_of_last_line, *s;
2216
2217   Synchronize_Buffer(form);
2218   begin_of_last_line = Address_Of_Row_In_Buffer(field, (field->drows - 1));
2219   s = After_End_Of_Data(begin_of_last_line, field->dcols);
2220   return ((s == begin_of_last_line) ? TRUE : FALSE);
2221 }
2222
2223 /*---------------------------------------------------------------------------
2224 |   Facility      :  libnform
2225 |   Function      :  static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
2226 |
2227 |   Description   :  Checks whether or not there is room for a new character
2228 |                    in the current line.
2229 |
2230 |   Return Values :  TRUE    - there is room
2231 |                    FALSE   - there is not enough room (line full)
2232 +--------------------------------------------------------------------------*/
2233 NCURSES_INLINE static bool
2234 Is_There_Room_For_A_Char_In_Line(FORM *form)
2235 {
2236   int last_char_in_line;
2237
2238   wmove(form->w, form->currow, form->current->dcols - 1);
2239   last_char_in_line = (int)(winch(form->w) & A_CHARTEXT);
2240   wmove(form->w, form->currow, form->curcol);
2241   return (((last_char_in_line == form->current->pad) ||
2242            is_blank(last_char_in_line)) ? TRUE : FALSE);
2243 }
2244
2245 #define There_Is_No_Room_For_A_Char_In_Line(f) \
2246   !Is_There_Room_For_A_Char_In_Line(f)
2247
2248 /*---------------------------------------------------------------------------
2249 |   Facility      :  libnform
2250 |   Function      :  static int Insert_String(
2251 |                                             FORM * form,
2252 |                                             int row,
2253 |                                             char *txt,
2254 |                                             int  len )
2255 |
2256 |   Description   :  Insert the 'len' characters beginning at pointer 'txt'
2257 |                    into the 'row' of the 'form'. The insertion occurs
2258 |                    on the beginning of the row, all other characters are
2259 |                    moved to the right. After the text a pad character will
2260 |                    be inserted to separate the text from the rest. If
2261 |                    necessary the insertion moves characters on the next
2262 |                    line to make place for the requested insertion string.
2263 |
2264 |   Return Values :  E_OK              - success
2265 |                    E_REQUEST_DENIED  -
2266 |                    E_SYSTEM_ERROR    - system error
2267 +--------------------------------------------------------------------------*/
2268 static int
2269 Insert_String(FORM *form, int row, FIELD_CELL *txt, int len)
2270 {
2271   FIELD *field = form->current;
2272   FIELD_CELL *bp = Address_Of_Row_In_Buffer(field, row);
2273   int datalen = (int)(After_End_Of_Data(bp, field->dcols) - bp);
2274   int freelen = field->dcols - datalen;
2275   int requiredlen = len + 1;
2276   FIELD_CELL *split;
2277   int result = E_REQUEST_DENIED;
2278
2279   if (freelen >= requiredlen)
2280     {
2281       wmove(form->w, row, 0);
2282       myINSNSTR(form->w, txt, len);
2283       wmove(form->w, row, len);
2284       myINSNSTR(form->w, &myBLANK, 1);
2285       return E_OK;
2286     }
2287   else
2288     {
2289       /* we have to move characters on the next line. If we are on the
2290          last line this may work, if the field is growable */
2291       if ((row == (field->drows - 1)) && Growable(field))
2292         {
2293           if (!Field_Grown(field, 1))
2294             return (E_SYSTEM_ERROR);
2295           /* !!!Side-Effect : might be changed due to growth!!! */
2296           bp = Address_Of_Row_In_Buffer(field, row);
2297         }
2298
2299       if (row < (field->drows - 1))
2300         {
2301           split =
2302             After_Last_Whitespace_Character(bp,
2303                                             (int)(Get_Start_Of_Data(bp
2304                                                                     + field->dcols
2305                                                                     - requiredlen,
2306                                                                     requiredlen)
2307                                                   - bp));
2308           /* split points now to the first character of the portion of the
2309              line that must be moved to the next line */
2310           datalen = (int)(split - bp);  /* + freelen has to stay on this line   */
2311           freelen = field->dcols - (datalen + freelen);         /* for the next line */
2312
2313           if ((result = Insert_String(form, row + 1, split, freelen)) == E_OK)
2314             {
2315               wmove(form->w, row, datalen);
2316               wclrtoeol(form->w);
2317               wmove(form->w, row, 0);
2318               myINSNSTR(form->w, txt, len);
2319               wmove(form->w, row, len);
2320               myINSNSTR(form->w, &myBLANK, 1);
2321               return E_OK;
2322             }
2323         }
2324       return (result);
2325     }
2326 }
2327
2328 /*---------------------------------------------------------------------------
2329 |   Facility      :  libnform
2330 |   Function      :  static int Wrapping_Not_Necessary_Or_Wrapping_Ok(
2331 |                                             FORM * form)
2332 |
2333 |   Description   :  If a character has been entered into a field, it may
2334 |                    be that wrapping has to occur. This routine checks
2335 |                    whether or not wrapping is required and if so, performs
2336 |                    the wrapping.
2337 |
2338 |   Return Values :  E_OK              - no wrapping required or wrapping
2339 |                                        was successful
2340 |                    E_REQUEST_DENIED  -
2341 |                    E_SYSTEM_ERROR    - some system error
2342 +--------------------------------------------------------------------------*/
2343 static int
2344 Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM *form)
2345 {
2346   FIELD *field = form->current;
2347   int result = E_REQUEST_DENIED;
2348   bool Last_Row = ((field->drows - 1) == form->currow);
2349
2350   if ((field->opts & O_WRAP) && /* wrapping wanted     */
2351       (!Single_Line_Field(field)) &&    /* must be multi-line  */
2352       (There_Is_No_Room_For_A_Char_In_Line(form)) &&    /* line is full        */
2353       (!Last_Row || Growable(field)))   /* there are more lines */
2354     {
2355       FIELD_CELL *bp;
2356       FIELD_CELL *split;
2357       int chars_to_be_wrapped;
2358       int chars_to_remain_on_line;
2359
2360       if (Last_Row)
2361         {
2362           /* the above logic already ensures, that in this case the field
2363              is growable */
2364           if (!Field_Grown(field, 1))
2365             return E_SYSTEM_ERROR;
2366         }
2367       bp = Address_Of_Current_Row_In_Buffer(form);
2368       Window_To_Buffer(form->w, field);
2369       split = After_Last_Whitespace_Character(bp, field->dcols);
2370       /* split points to the first character of the sequence to be brought
2371          on the next line */
2372       chars_to_remain_on_line = (int)(split - bp);
2373       chars_to_be_wrapped = field->dcols - chars_to_remain_on_line;
2374       if (chars_to_remain_on_line > 0)
2375         {
2376           if ((result = Insert_String(form, form->currow + 1, split,
2377                                       chars_to_be_wrapped)) == E_OK)
2378             {
2379               wmove(form->w, form->currow, chars_to_remain_on_line);
2380               wclrtoeol(form->w);
2381               if (form->curcol >= chars_to_remain_on_line)
2382                 {
2383                   form->currow++;
2384                   form->curcol -= chars_to_remain_on_line;
2385                 }
2386               return E_OK;
2387             }
2388         }
2389       else
2390         return E_OK;
2391       if (result != E_OK)
2392         {
2393           DeleteChar(form);
2394           Window_To_Buffer(form->w, field);
2395           result = E_REQUEST_DENIED;
2396         }
2397     }
2398   else
2399     result = E_OK;              /* wrapping was not necessary */
2400   return (result);
2401 }
2402
2403 /*----------------------------------------------------------------------------
2404   Field Editing routines
2405   --------------------------------------------------------------------------*/
2406
2407 /*---------------------------------------------------------------------------
2408 |   Facility      :  libnform
2409 |   Function      :  static int Field_Editing(
2410 |                                    int (* const fct) (FORM *),
2411 |                                    FORM * form)
2412 |
2413 |   Description   :  Generic routine for field editing requests. The driver
2414 |                    routines are only called for editable fields, the
2415 |                    _WINDOW_MODIFIED flag is set if editing occurred.
2416 |                    This is somewhat special due to the overload semantics
2417 |                    of the NEW_LINE and DEL_PREV requests.
2418 |
2419 |   Return Values :  Error code from low level drivers.
2420 +--------------------------------------------------------------------------*/
2421 static int
2422 Field_Editing(int (*const fct) (FORM *), FORM *form)
2423 {
2424   int res = E_REQUEST_DENIED;
2425
2426   /* We have to deal here with the specific case of the overloaded
2427      behavior of New_Line and Delete_Previous requests.
2428      They may end up in navigational requests if we are on the first
2429      character in a field. But navigation is also allowed on non-
2430      editable fields.
2431    */
2432   if ((fct == FE_Delete_Previous) &&
2433       (form->opts & O_BS_OVERLOAD) &&
2434       First_Position_In_Current_Field(form))
2435     {
2436       res = Inter_Field_Navigation(FN_Previous_Field, form);
2437     }
2438   else
2439     {
2440       if (fct == FE_New_Line)
2441         {
2442           if ((form->opts & O_NL_OVERLOAD) &&
2443               First_Position_In_Current_Field(form))
2444             {
2445               res = Inter_Field_Navigation(FN_Next_Field, form);
2446             }
2447           else
2448             /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */
2449             res = fct(form);
2450         }
2451       else
2452         {
2453           /* From now on, everything must be editable */
2454           if (form->current->opts & O_EDIT)
2455             {
2456               res = fct(form);
2457               if (res == E_OK)
2458                 form->status |= _WINDOW_MODIFIED;
2459             }
2460         }
2461     }
2462   return res;
2463 }
2464
2465 /*---------------------------------------------------------------------------
2466 |   Facility      :  libnform
2467 |   Function      :  static int FE_New_Line(FORM * form)
2468 |
2469 |   Description   :  Perform a new line request. This is rather complex
2470 |                    compared to other routines in this code due to the
2471 |                    rather difficult to understand description in the
2472 |                    manuals.
2473 |
2474 |   Return Values :  E_OK               - success
2475 |                    E_REQUEST_DENIED   - new line not allowed
2476 |                    E_SYSTEM_ERROR     - system error
2477 +--------------------------------------------------------------------------*/
2478 static int
2479 FE_New_Line(FORM *form)
2480 {
2481   FIELD *field = form->current;
2482   FIELD_CELL *bp, *t;
2483   bool Last_Row = ((field->drows - 1) == form->currow);
2484
2485   T((T_CALLED("FE_New_Line(%p)"), form));
2486   if (form->status & _OVLMODE)
2487     {
2488       if (Last_Row &&
2489           (!(Growable(field) && !Single_Line_Field(field))))
2490         {
2491           if (!(form->opts & O_NL_OVERLOAD))
2492             returnCode(E_REQUEST_DENIED);
2493           wmove(form->w, form->currow, form->curcol);
2494           wclrtoeol(form->w);
2495           /* we have to set this here, although it is also
2496              handled in the generic routine. The reason is,
2497              that FN_Next_Field may fail, but the form is
2498              definitively changed */
2499           form->status |= _WINDOW_MODIFIED;
2500           returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2501         }
2502       else
2503         {
2504           if (Last_Row && !Field_Grown(field, 1))
2505             {
2506               /* N.B.: due to the logic in the 'if', LastRow==TRUE
2507                  means here that the field is growable and not
2508                  a single-line field */
2509               returnCode(E_SYSTEM_ERROR);
2510             }
2511           wmove(form->w, form->currow, form->curcol);
2512           wclrtoeol(form->w);
2513           form->currow++;
2514           form->curcol = 0;
2515           form->status |= _WINDOW_MODIFIED;
2516           returnCode(E_OK);
2517         }
2518     }
2519   else
2520     {
2521       /* Insert Mode */
2522       if (Last_Row &&
2523           !(Growable(field) && !Single_Line_Field(field)))
2524         {
2525           if (!(form->opts & O_NL_OVERLOAD))
2526             returnCode(E_REQUEST_DENIED);
2527           returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2528         }
2529       else
2530         {
2531           bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);
2532
2533           if (!(May_Do_It || Growable(field)))
2534             returnCode(E_REQUEST_DENIED);
2535           if (!May_Do_It && !Field_Grown(field, 1))
2536             returnCode(E_SYSTEM_ERROR);
2537
2538           bp = Address_Of_Current_Position_In_Buffer(form);
2539           t = After_End_Of_Data(bp, field->dcols - form->curcol);
2540           wmove(form->w, form->currow, form->curcol);
2541           wclrtoeol(form->w);
2542           form->currow++;
2543           form->curcol = 0;
2544           wmove(form->w, form->currow, form->curcol);
2545           winsertln(form->w);
2546           myADDNSTR(form->w, bp, (int)(t - bp));
2547           form->status |= _WINDOW_MODIFIED;
2548           returnCode(E_OK);
2549         }
2550     }
2551 }
2552
2553 /*---------------------------------------------------------------------------
2554 |   Facility      :  libnform
2555 |   Function      :  static int FE_Insert_Character(FORM * form)
2556 |
2557 |   Description   :  Insert blank character at the cursor position
2558 |
2559 |   Return Values :  E_OK
2560 |                    E_REQUEST_DENIED
2561 +--------------------------------------------------------------------------*/
2562 static int
2563 FE_Insert_Character(FORM *form)
2564 {
2565   FIELD *field = form->current;
2566   int result = E_REQUEST_DENIED;
2567
2568   T((T_CALLED("FE_Insert_Character(%p)"), form));
2569   if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2570     {
2571       bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
2572
2573       if (There_Is_Room ||
2574           ((Single_Line_Field(field) && Growable(field))))
2575         {
2576           if (!There_Is_Room && !Field_Grown(field, 1))
2577             result = E_SYSTEM_ERROR;
2578           else
2579             {
2580               winsch(form->w, (chtype)C_BLANK);
2581               result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);
2582             }
2583         }
2584     }
2585   returnCode(result);
2586 }
2587
2588 /*---------------------------------------------------------------------------
2589 |   Facility      :  libnform
2590 |   Function      :  static int FE_Insert_Line(FORM * form)
2591 |
2592 |   Description   :  Insert a blank line at the cursor position
2593 |
2594 |   Return Values :  E_OK               - success
2595 |                    E_REQUEST_DENIED   - line can not be inserted
2596 +--------------------------------------------------------------------------*/
2597 static int
2598 FE_Insert_Line(FORM *form)
2599 {
2600   FIELD *field = form->current;
2601   int result = E_REQUEST_DENIED;
2602
2603   T((T_CALLED("FE_Insert_Line(%p)"), form));
2604   if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2605     {
2606       bool Maybe_Done = (form->currow != (field->drows - 1)) &&
2607       Is_There_Room_For_A_Line(form);
2608
2609       if (!Single_Line_Field(field) &&
2610           (Maybe_Done || Growable(field)))
2611         {
2612           if (!Maybe_Done && !Field_Grown(field, 1))
2613             result = E_SYSTEM_ERROR;
2614           else
2615             {
2616               form->curcol = 0;
2617               winsertln(form->w);
2618               result = E_OK;
2619             }
2620         }
2621     }
2622   returnCode(result);
2623 }
2624
2625 /*---------------------------------------------------------------------------
2626 |   Facility      :  libnform
2627 |   Function      :  static int FE_Delete_Character(FORM * form)
2628 |
2629 |   Description   :  Delete character at the cursor position
2630 |
2631 |   Return Values :  E_OK    - success
2632 +--------------------------------------------------------------------------*/
2633 static int
2634 FE_Delete_Character(FORM *form)
2635 {
2636   T((T_CALLED("FE_Delete_Character(%p)"), form));
2637   DeleteChar(form);
2638   returnCode(E_OK);
2639 }
2640
2641 /*---------------------------------------------------------------------------
2642 |   Facility      :  libnform
2643 |   Function      :  static int FE_Delete_Previous(FORM * form)
2644 |
2645 |   Description   :  Delete character before cursor. Again this is a rather
2646 |                    difficult piece compared to others due to the overloading
2647 |                    semantics of backspace.
2648 |                    N.B.: The case of overloaded BS on first field position
2649 |                          is already handled in the generic routine.
2650 |
2651 |   Return Values :  E_OK                - success
2652 |                    E_REQUEST_DENIED    - Character can't be deleted
2653 +--------------------------------------------------------------------------*/
2654 static int
2655 FE_Delete_Previous(FORM *form)
2656 {
2657   FIELD *field = form->current;
2658
2659   T((T_CALLED("FE_Delete_Previous(%p)"), form));
2660   if (First_Position_In_Current_Field(form))
2661     returnCode(E_REQUEST_DENIED);
2662
2663   if ((--(form->curcol)) < 0)
2664     {
2665       FIELD_CELL *this_line, *prev_line, *prev_end, *this_end;
2666       int this_row = form->currow;
2667
2668       form->curcol++;
2669       if (form->status & _OVLMODE)
2670         returnCode(E_REQUEST_DENIED);
2671
2672       prev_line = Address_Of_Row_In_Buffer(field, (form->currow - 1));
2673       this_line = Address_Of_Row_In_Buffer(field, (form->currow));
2674       Synchronize_Buffer(form);
2675       prev_end = After_End_Of_Data(prev_line, field->dcols);
2676       this_end = After_End_Of_Data(this_line, field->dcols);
2677       if ((int)(this_end - this_line) >
2678           (field->cols - (int)(prev_end - prev_line)))
2679         returnCode(E_REQUEST_DENIED);
2680       wmove(form->w, form->currow, form->curcol);
2681       wdeleteln(form->w);
2682       Adjust_Cursor_Position(form, prev_end);
2683       /*
2684        * If we did not really move to the previous line, help the user a
2685        * little.  It is however a little inconsistent.  Normally, when
2686        * backspacing around the point where text wraps to a new line in a
2687        * multi-line form, we absorb one keystroke for the wrapping point.  That
2688        * is consistent with SVr4 forms.  However, SVr4 does not allow typing
2689        * into the last column of the field, and requires the user to enter a
2690        * newline to move to the next line.  Therefore it can consistently eat
2691        * that keystroke.  Since ncurses allows the last column, it wraps
2692        * automatically (given the proper options).  But we cannot eat the
2693        * keystroke to back over the wrapping point, since that would put the
2694        * cursor past the end of the form field.  In this case, just delete the
2695        * character at the end of the field.
2696        */
2697       if (form->currow == this_row && this_row > 0)
2698         {
2699           form->currow -= 1;
2700           form->curcol = field->dcols - 1;
2701           DeleteChar(form);
2702         }
2703       else
2704         {
2705           wmove(form->w, form->currow, form->curcol);
2706           myADDNSTR(form->w, this_line, (int)(this_end - this_line));
2707         }
2708     }
2709   else
2710     {
2711       DeleteChar(form);
2712     }
2713   returnCode(E_OK);
2714 }
2715
2716 /*---------------------------------------------------------------------------
2717 |   Facility      :  libnform
2718 |   Function      :  static int FE_Delete_Line(FORM * form)
2719 |
2720 |   Description   :  Delete line at cursor position.
2721 |
2722 |   Return Values :  E_OK  - success
2723 +--------------------------------------------------------------------------*/
2724 static int
2725 FE_Delete_Line(FORM *form)
2726 {
2727   T((T_CALLED("FE_Delete_Line(%p)"), form));
2728   form->curcol = 0;
2729   wdeleteln(form->w);
2730   returnCode(E_OK);
2731 }
2732
2733 /*---------------------------------------------------------------------------
2734 |   Facility      :  libnform
2735 |   Function      :  static int FE_Delete_Word(FORM * form)
2736 |
2737 |   Description   :  Delete word at cursor position
2738 |
2739 |   Return Values :  E_OK               - success
2740 |                    E_REQUEST_DENIED   - failure
2741 +--------------------------------------------------------------------------*/
2742 static int
2743 FE_Delete_Word(FORM *form)
2744 {
2745   FIELD *field = form->current;
2746   FIELD_CELL *bp = Address_Of_Current_Row_In_Buffer(form);
2747   FIELD_CELL *ep = bp + field->dcols;
2748   FIELD_CELL *cp = bp + form->curcol;
2749   FIELD_CELL *s;
2750
2751   T((T_CALLED("FE_Delete_Word(%p)"), form));
2752   Synchronize_Buffer(form);
2753   if (ISBLANK(*cp))
2754     returnCode(E_REQUEST_DENIED);       /* not in word */
2755
2756   /* move cursor to begin of word and erase to end of screen-line */
2757   Adjust_Cursor_Position(form,
2758                          After_Last_Whitespace_Character(bp, form->curcol));
2759   wmove(form->w, form->currow, form->curcol);
2760   wclrtoeol(form->w);
2761
2762   /* skip over word in buffer */
2763   s = Get_First_Whitespace_Character(cp, (int)(ep - cp));
2764   /* to begin of next word    */
2765   s = Get_Start_Of_Data(s, (int)(ep - s));
2766   if ((s != cp) && !ISBLANK(*s))
2767     {
2768       /* copy remaining line to window */
2769       myADDNSTR(form->w, s, (int)(s - After_End_Of_Data(s, (int)(ep - s))));
2770     }
2771   returnCode(E_OK);
2772 }
2773
2774 /*---------------------------------------------------------------------------
2775 |   Facility      :  libnform
2776 |   Function      :  static int FE_Clear_To_End_Of_Line(FORM * form)
2777 |
2778 |   Description   :  Clear to end of current line.
2779 |
2780 |   Return Values :  E_OK   - success
2781 +--------------------------------------------------------------------------*/
2782 static int
2783 FE_Clear_To_End_Of_Line(FORM *form)
2784 {
2785   T((T_CALLED("FE_Clear_To_End_Of_Line(%p)"), form));
2786   wmove(form->w, form->currow, form->curcol);
2787   wclrtoeol(form->w);
2788   returnCode(E_OK);
2789 }
2790
2791 /*---------------------------------------------------------------------------
2792 |   Facility      :  libnform
2793 |   Function      :  static int FE_Clear_To_End_Of_Field(FORM * form)
2794 |
2795 |   Description   :  Clear to end of field.
2796 |
2797 |   Return Values :  E_OK   - success
2798 +--------------------------------------------------------------------------*/
2799 static int
2800 FE_Clear_To_End_Of_Field(FORM *form)
2801 {
2802   T((T_CALLED("FE_Clear_To_End_Of_Field(%p)"), form));
2803   wmove(form->w, form->currow, form->curcol);
2804   wclrtobot(form->w);
2805   returnCode(E_OK);
2806 }
2807
2808 /*---------------------------------------------------------------------------
2809 |   Facility      :  libnform
2810 |   Function      :  static int FE_Clear_Field(FORM * form)
2811 |
2812 |   Description   :  Clear entire field.
2813 |
2814 |   Return Values :  E_OK   - success
2815 +--------------------------------------------------------------------------*/
2816 static int
2817 FE_Clear_Field(FORM *form)
2818 {
2819   T((T_CALLED("FE_Clear_Field(%p)"), form));
2820   form->currow = form->curcol = 0;
2821   werase(form->w);
2822   returnCode(E_OK);
2823 }
2824 /*----------------------------------------------------------------------------
2825   END of Field Editing routines
2826   --------------------------------------------------------------------------*/
2827
2828 /*----------------------------------------------------------------------------
2829   Edit Mode routines
2830   --------------------------------------------------------------------------*/
2831
2832 /*---------------------------------------------------------------------------
2833 |   Facility      :  libnform
2834 |   Function      :  static int EM_Overlay_Mode(FORM * form)
2835 |
2836 |   Description   :  Switch to overlay mode.
2837 |
2838 |   Return Values :  E_OK   - success
2839 +--------------------------------------------------------------------------*/
2840 static int
2841 EM_Overlay_Mode(FORM *form)
2842 {
2843   T((T_CALLED("EM_Overlay_Mode(%p)"), form));
2844   form->status |= _OVLMODE;
2845   returnCode(E_OK);
2846 }
2847
2848 /*---------------------------------------------------------------------------
2849 |   Facility      :  libnform
2850 |   Function      :  static int EM_Insert_Mode(FORM * form)
2851 |
2852 |   Description   :  Switch to insert mode
2853 |
2854 |   Return Values :  E_OK   - success
2855 +--------------------------------------------------------------------------*/
2856 static int
2857 EM_Insert_Mode(FORM *form)
2858 {
2859   T((T_CALLED("EM_Insert_Mode(%p)"), form));
2860   form->status &= ~_OVLMODE;
2861   returnCode(E_OK);
2862 }
2863
2864 /*----------------------------------------------------------------------------
2865   END of Edit Mode routines
2866   --------------------------------------------------------------------------*/
2867
2868 /*----------------------------------------------------------------------------
2869   Helper routines for Choice Requests
2870   --------------------------------------------------------------------------*/
2871
2872 /*---------------------------------------------------------------------------
2873 |   Facility      :  libnform
2874 |   Function      :  static bool Next_Choice(
2875 |                                            FIELDTYPE * typ,
2876 |                                            FIELD * field,
2877 |                                            TypeArgument *argp)
2878 |
2879 |   Description   :  Get the next field choice. For linked types this is
2880 |                    done recursively.
2881 |
2882 |   Return Values :  TRUE    - next choice successfully retrieved
2883 |                    FALSE   - couldn't retrieve next choice
2884 +--------------------------------------------------------------------------*/
2885 static bool
2886 Next_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2887 {
2888   if (!typ || !(typ->status & _HAS_CHOICE))
2889     return FALSE;
2890
2891   if (typ->status & _LINKED_TYPE)
2892     {
2893       assert(argp);
2894       return (
2895                Next_Choice(typ->left, field, argp->left) ||
2896                Next_Choice(typ->right, field, argp->right));
2897     }
2898   else
2899     {
2900       assert(typ->next);
2901       return typ->next(field, (void *)argp);
2902     }
2903 }
2904
2905 /*---------------------------------------------------------------------------
2906 |   Facility      :  libnform
2907 |   Function      :  static bool Previous_Choice(
2908 |                                                FIELDTYPE * typ,
2909 |                                                FIELD * field,
2910 |                                                TypeArgument *argp)
2911 |
2912 |   Description   :  Get the previous field choice. For linked types this
2913 |                    is done recursively.
2914 |
2915 |   Return Values :  TRUE    - previous choice successfully retrieved
2916 |                    FALSE   - couldn't retrieve previous choice
2917 +--------------------------------------------------------------------------*/
2918 static bool
2919 Previous_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2920 {
2921   if (!typ || !(typ->status & _HAS_CHOICE))
2922     return FALSE;
2923
2924   if (typ->status & _LINKED_TYPE)
2925     {
2926       assert(argp);
2927       return (
2928                Previous_Choice(typ->left, field, argp->left) ||
2929                Previous_Choice(typ->right, field, argp->right));
2930     }
2931   else
2932     {
2933       assert(typ->prev);
2934       return typ->prev(field, (void *)argp);
2935     }
2936 }
2937 /*----------------------------------------------------------------------------
2938   End of Helper routines for Choice Requests
2939   --------------------------------------------------------------------------*/
2940
2941 /*----------------------------------------------------------------------------
2942   Routines for Choice Requests
2943   --------------------------------------------------------------------------*/
2944
2945 /*---------------------------------------------------------------------------
2946 |   Facility      :  libnform
2947 |   Function      :  static int CR_Next_Choice(FORM * form)
2948 |
2949 |   Description   :  Get the next field choice.
2950 |
2951 |   Return Values :  E_OK              - success
2952 |                    E_REQUEST_DENIED  - next choice couldn't be retrieved
2953 +--------------------------------------------------------------------------*/
2954 static int
2955 CR_Next_Choice(FORM *form)
2956 {
2957   FIELD *field = form->current;
2958
2959   T((T_CALLED("CR_Next_Choice(%p)"), form));
2960   Synchronize_Buffer(form);
2961   returnCode((Next_Choice(field->type, field, (TypeArgument *)(field->arg)))
2962              ? E_OK
2963              : E_REQUEST_DENIED);
2964 }
2965
2966 /*---------------------------------------------------------------------------
2967 |   Facility      :  libnform
2968 |   Function      :  static int CR_Previous_Choice(FORM * form)
2969 |
2970 |   Description   :  Get the previous field choice.
2971 |
2972 |   Return Values :  E_OK              - success
2973 |                    E_REQUEST_DENIED  - prev. choice couldn't be retrieved
2974 +--------------------------------------------------------------------------*/
2975 static int
2976 CR_Previous_Choice(FORM *form)
2977 {
2978   FIELD *field = form->current;
2979
2980   T((T_CALLED("CR_Previous_Choice(%p)"), form));
2981   Synchronize_Buffer(form);
2982   returnCode((Previous_Choice(field->type, field, (TypeArgument *)(field->arg)))
2983              ? E_OK
2984              : E_REQUEST_DENIED);
2985 }
2986 /*----------------------------------------------------------------------------
2987   End of Routines for Choice Requests
2988   --------------------------------------------------------------------------*/
2989
2990 /*----------------------------------------------------------------------------
2991   Helper routines for Field Validations.
2992   --------------------------------------------------------------------------*/
2993
2994 /*---------------------------------------------------------------------------
2995 |   Facility      :  libnform
2996 |   Function      :  static bool Check_Field(
2997 |                                            FIELDTYPE * typ,
2998 |                                            FIELD * field,
2999 |                                            TypeArgument * argp)
3000 |
3001 |   Description   :  Check the field according to its fieldtype and its
3002 |                    actual arguments. For linked fieldtypes this is done
3003 |                    recursively.
3004 |
3005 |   Return Values :  TRUE       - field is valid
3006 |                    FALSE      - field is invalid.
3007 +--------------------------------------------------------------------------*/
3008 static bool
3009 Check_Field(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3010 {
3011   if (typ)
3012     {
3013       if (field->opts & O_NULLOK)
3014         {
3015           FIELD_CELL *bp = field->buf;
3016
3017           assert(bp);
3018           while (ISBLANK(*bp))
3019             {
3020               bp++;
3021             }
3022           if (CharOf(*bp) == 0)
3023             return TRUE;
3024         }
3025
3026       if (typ->status & _LINKED_TYPE)
3027         {
3028           assert(argp);
3029           return (
3030                    Check_Field(typ->left, field, argp->left) ||
3031                    Check_Field(typ->right, field, argp->right));
3032         }
3033       else
3034         {
3035           if (typ->fcheck)
3036             return typ->fcheck(field, (void *)argp);
3037         }
3038     }
3039   return TRUE;
3040 }
3041
3042 /*---------------------------------------------------------------------------
3043 |   Facility      :  libnform
3044 |   Function      :  bool _nc_Internal_Validation(FORM * form )
3045 |
3046 |   Description   :  Validate the current field of the form.
3047 |
3048 |   Return Values :  TRUE  - field is valid
3049 |                    FALSE - field is invalid
3050 +--------------------------------------------------------------------------*/
3051 NCURSES_EXPORT(bool)
3052 _nc_Internal_Validation(FORM *form)
3053 {
3054   FIELD *field;
3055
3056   field = form->current;
3057
3058   Synchronize_Buffer(form);
3059   if ((form->status & _FCHECK_REQUIRED) ||
3060       (!(field->opts & O_PASSOK)))
3061     {
3062       if (!Check_Field(field->type, field, (TypeArgument *)(field->arg)))
3063         return FALSE;
3064       form->status &= ~_FCHECK_REQUIRED;
3065       field->status |= _CHANGED;
3066       Synchronize_Linked_Fields(field);
3067     }
3068   return TRUE;
3069 }
3070 /*----------------------------------------------------------------------------
3071   End of Helper routines for Field Validations.
3072   --------------------------------------------------------------------------*/
3073
3074 /*----------------------------------------------------------------------------
3075   Routines for Field Validation.
3076   --------------------------------------------------------------------------*/
3077
3078 /*---------------------------------------------------------------------------
3079 |   Facility      :  libnform
3080 |   Function      :  static int FV_Validation(FORM * form)
3081 |
3082 |   Description   :  Validate the current field of the form.
3083 |
3084 |   Return Values :  E_OK             - field valid
3085 |                    E_INVALID_FIELD  - field not valid
3086 +--------------------------------------------------------------------------*/
3087 static int
3088 FV_Validation(FORM *form)
3089 {
3090   T((T_CALLED("FV_Validation(%p)"), form));
3091   if (_nc_Internal_Validation(form))
3092     returnCode(E_OK);
3093   else
3094     returnCode(E_INVALID_FIELD);
3095 }
3096 /*----------------------------------------------------------------------------
3097   End of routines for Field Validation.
3098   --------------------------------------------------------------------------*/
3099
3100 /*----------------------------------------------------------------------------
3101   Helper routines for Inter-Field Navigation
3102   --------------------------------------------------------------------------*/
3103
3104 /*---------------------------------------------------------------------------
3105 |   Facility      :  libnform
3106 |   Function      :  static FIELD *Next_Field_On_Page(FIELD * field)
3107 |
3108 |   Description   :  Get the next field after the given field on the current
3109 |                    page. The order of fields is the one defined by the
3110 |                    fields array. Only visible and active fields are
3111 |                    counted.
3112 |
3113 |   Return Values :  Pointer to the next field.
3114 +--------------------------------------------------------------------------*/
3115 NCURSES_INLINE static FIELD *
3116 Next_Field_On_Page(FIELD *field)
3117 {
3118   FORM *form = field->form;
3119   FIELD **field_on_page = &form->field[field->index];
3120   FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3121   FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3122
3123   do
3124     {
3125       field_on_page =
3126         (field_on_page == last_on_page) ? first_on_page : field_on_page + 1;
3127       if (Field_Is_Selectable(*field_on_page))
3128         break;
3129     }
3130   while (field != (*field_on_page));
3131   return (*field_on_page);
3132 }
3133
3134 /*---------------------------------------------------------------------------
3135 |   Facility      :  libnform
3136 |   Function      :  FIELD* _nc_First_Active_Field(FORM * form)
3137 |
3138 |   Description   :  Get the first active field on the current page,
3139 |                    if there are such. If there are none, get the first
3140 |                    visible field on the page. If there are also none,
3141 |                    we return the first field on page and hope the best.
3142 |
3143 |   Return Values :  Pointer to calculated field.
3144 +--------------------------------------------------------------------------*/
3145 NCURSES_EXPORT(FIELD *)
3146 _nc_First_Active_Field(FORM *form)
3147 {
3148   FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3149   FIELD *proposed = Next_Field_On_Page(*last_on_page);
3150
3151   if (proposed == *last_on_page)
3152     {
3153       /* there might be the special situation, where there is no
3154          active and visible field on the current page. We then select
3155          the first visible field on this readonly page
3156        */
3157       if (Field_Is_Not_Selectable(proposed))
3158         {
3159           FIELD **field = &form->field[proposed->index];
3160           FIELD **first = &form->field[form->page[form->curpage].pmin];
3161
3162           do
3163             {
3164               field = (field == last_on_page) ? first : field + 1;
3165               if (((*field)->opts & O_VISIBLE))
3166                 break;
3167             }
3168           while (proposed != (*field));
3169
3170           proposed = *field;
3171
3172           if ((proposed == *last_on_page) && !(proposed->opts & O_VISIBLE))
3173             {
3174               /* This means, there is also no visible field on the page.
3175                  So we propose the first one and hope the very best...
3176                  Some very clever user has designed a readonly and invisible
3177                  page on this form.
3178                */
3179               proposed = *first;
3180             }
3181         }
3182     }
3183   return (proposed);
3184 }
3185
3186 /*---------------------------------------------------------------------------
3187 |   Facility      :  libnform
3188 |   Function      :  static FIELD *Previous_Field_On_Page(FIELD * field)
3189 |
3190 |   Description   :  Get the previous field before the given field on the
3191 |                    current page. The order of fields is the one defined by
3192 |                    the fields array. Only visible and active fields are
3193 |                    counted.
3194 |
3195 |   Return Values :  Pointer to the previous field.
3196 +--------------------------------------------------------------------------*/
3197 NCURSES_INLINE static FIELD *
3198 Previous_Field_On_Page(FIELD *field)
3199 {
3200   FORM *form = field->form;
3201   FIELD **field_on_page = &form->field[field->index];
3202   FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3203   FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3204
3205   do
3206     {
3207       field_on_page =
3208         (field_on_page == first_on_page) ? last_on_page : field_on_page - 1;
3209       if (Field_Is_Selectable(*field_on_page))
3210         break;
3211     }
3212   while (field != (*field_on_page));
3213
3214   return (*field_on_page);
3215 }
3216
3217 /*---------------------------------------------------------------------------
3218 |   Facility      :  libnform
3219 |   Function      :  static FIELD *Sorted_Next_Field(FIELD * field)
3220 |
3221 |   Description   :  Get the next field after the given field on the current
3222 |                    page. The order of fields is the one defined by the
3223 |                    (row,column) geometry, rows are major.
3224 |
3225 |   Return Values :  Pointer to the next field.
3226 +--------------------------------------------------------------------------*/
3227 NCURSES_INLINE static FIELD *
3228 Sorted_Next_Field(FIELD *field)
3229 {
3230   FIELD *field_on_page = field;
3231
3232   do
3233     {
3234       field_on_page = field_on_page->snext;
3235       if (Field_Is_Selectable(field_on_page))
3236         break;
3237     }
3238   while (field_on_page != field);
3239
3240   return (field_on_page);
3241 }
3242
3243 /*---------------------------------------------------------------------------
3244 |   Facility      :  libnform
3245 |   Function      :  static FIELD *Sorted_Previous_Field(FIELD * field)
3246 |
3247 |   Description   :  Get the previous field before the given field on the
3248 |                    current page. The order of fields is the one defined
3249 |                    by the (row,column) geometry, rows are major.
3250 |
3251 |   Return Values :  Pointer to the previous field.
3252 +--------------------------------------------------------------------------*/
3253 NCURSES_INLINE static FIELD *
3254 Sorted_Previous_Field(FIELD *field)
3255 {
3256   FIELD *field_on_page = field;
3257
3258   do
3259     {
3260       field_on_page = field_on_page->sprev;
3261       if (Field_Is_Selectable(field_on_page))
3262         break;
3263     }
3264   while (field_on_page != field);
3265
3266   return (field_on_page);
3267 }
3268
3269 /*---------------------------------------------------------------------------
3270 |   Facility      :  libnform
3271 |   Function      :  static FIELD *Left_Neighbor_Field(FIELD * field)
3272 |
3273 |   Description   :  Get the left neighbor of the field on the same line
3274 |                    and the same page. Cycles through the line.
3275 |
3276 |   Return Values :  Pointer to left neighbor field.
3277 +--------------------------------------------------------------------------*/
3278 NCURSES_INLINE static FIELD *
3279 Left_Neighbor_Field(FIELD *field)
3280 {
3281   FIELD *field_on_page = field;
3282
3283   /* For a field that has really a left neighbor, the while clause
3284      immediately fails and the loop is left, positioned at the right
3285      neighbor. Otherwise we cycle backwards through the sorted field list
3286      until we enter the same line (from the right end).
3287    */
3288   do
3289     {
3290       field_on_page = Sorted_Previous_Field(field_on_page);
3291     }
3292   while (field_on_page->frow != field->frow);
3293
3294   return (field_on_page);
3295 }
3296
3297 /*---------------------------------------------------------------------------
3298 |   Facility      :  libnform
3299 |   Function      :  static FIELD *Right_Neighbor_Field(FIELD * field)
3300 |
3301 |   Description   :  Get the right neighbor of the field on the same line
3302 |                    and the same page.
3303 |
3304 |   Return Values :  Pointer to right neighbor field.
3305 +--------------------------------------------------------------------------*/
3306 NCURSES_INLINE static FIELD *
3307 Right_Neighbor_Field(FIELD *field)
3308 {
3309   FIELD *field_on_page = field;
3310
3311   /* See the comments on Left_Neighbor_Field to understand how it works */
3312   do
3313     {
3314       field_on_page = Sorted_Next_Field(field_on_page);
3315     }
3316   while (field_on_page->frow != field->frow);
3317
3318   return (field_on_page);
3319 }
3320
3321 /*---------------------------------------------------------------------------
3322 |   Facility      :  libnform
3323 |   Function      :  static FIELD *Upper_Neighbor_Field(FIELD * field)
3324 |
3325 |   Description   :  Because of the row-major nature of sorting the fields,
3326 |                    it is more difficult to define whats the upper neighbor
3327 |                    field really means. We define that it must be on a
3328 |                    'previous' line (cyclic order!) and is the rightmost
3329 |                    field laying on the left side of the given field. If
3330 |                    this set is empty, we take the first field on the line.
3331 |
3332 |   Return Values :  Pointer to the upper neighbor field.
3333 +--------------------------------------------------------------------------*/
3334 static FIELD *
3335 Upper_Neighbor_Field(FIELD *field)
3336 {
3337   FIELD *field_on_page = field;
3338   int frow = field->frow;
3339   int fcol = field->fcol;
3340
3341   /* Walk back to the 'previous' line. The second term in the while clause
3342      just guarantees that we stop if we cycled through the line because
3343      there might be no 'previous' line if the page has just one line.
3344    */
3345   do
3346     {
3347       field_on_page = Sorted_Previous_Field(field_on_page);
3348     }
3349   while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3350
3351   if (field_on_page->frow != frow)
3352     {
3353       /* We really found a 'previous' line. We are positioned at the
3354          rightmost field on this line */
3355       frow = field_on_page->frow;
3356
3357       /* We walk to the left as long as we are really right of the
3358          field. */
3359       while (field_on_page->frow == frow && field_on_page->fcol > fcol)
3360         field_on_page = Sorted_Previous_Field(field_on_page);
3361
3362       /* If we wrapped, just go to the right which is the first field on
3363          the row */
3364       if (field_on_page->frow != frow)
3365         field_on_page = Sorted_Next_Field(field_on_page);
3366     }
3367
3368   return (field_on_page);
3369 }
3370
3371 /*---------------------------------------------------------------------------
3372 |   Facility      :  libnform
3373 |   Function      :  static FIELD *Down_Neighbor_Field(FIELD * field)
3374 |
3375 |   Description   :  Because of the row-major nature of sorting the fields,
3376 |                    its more difficult to define whats the down neighbor
3377 |                    field really means. We define that it must be on a
3378 |                    'next' line (cyclic order!) and is the leftmost
3379 |                    field laying on the right side of the given field. If
3380 |                    this set is empty, we take the last field on the line.
3381 |
3382 |   Return Values :  Pointer to the upper neighbor field.
3383 +--------------------------------------------------------------------------*/
3384 static FIELD *
3385 Down_Neighbor_Field(FIELD *field)
3386 {
3387   FIELD *field_on_page = field;
3388   int frow = field->frow;
3389   int fcol = field->fcol;
3390
3391   /* Walk forward to the 'next' line. The second term in the while clause
3392      just guarantees that we stop if we cycled through the line because
3393      there might be no 'next' line if the page has just one line.
3394    */
3395   do
3396     {
3397       field_on_page = Sorted_Next_Field(field_on_page);
3398     }
3399   while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3400
3401   if (field_on_page->frow != frow)
3402     {
3403       /* We really found a 'next' line. We are positioned at the rightmost
3404          field on this line */
3405       frow = field_on_page->frow;
3406
3407       /* We walk to the right as long as we are really left of the
3408          field. */
3409       while (field_on_page->frow == frow && field_on_page->fcol < fcol)
3410         field_on_page = Sorted_Next_Field(field_on_page);
3411
3412       /* If we wrapped, just go to the left which is the last field on
3413          the row */
3414       if (field_on_page->frow != frow)
3415         field_on_page = Sorted_Previous_Field(field_on_page);
3416     }
3417
3418   return (field_on_page);
3419 }
3420
3421 /*----------------------------------------------------------------------------
3422   Inter-Field Navigation routines
3423   --------------------------------------------------------------------------*/
3424
3425 /*---------------------------------------------------------------------------
3426 |   Facility      :  libnform
3427 |   Function      :  static int Inter_Field_Navigation(
3428 |                                           int (* const fct) (FORM *),
3429 |                                           FORM * form)
3430 |
3431 |   Description   :  Generic behavior for changing the current field, the
3432 |                    field is left and a new field is entered. So the field
3433 |                    must be validated and the field init/term hooks must
3434 |                    be called.
3435 |
3436 |   Return Values :  E_OK                - success
3437 |                    E_INVALID_FIELD     - field is invalid
3438 |                    some other          - error from subordinate call
3439 +--------------------------------------------------------------------------*/
3440 static int
3441 Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form)
3442 {
3443   int res;
3444
3445   if (!_nc_Internal_Validation(form))
3446     res = E_INVALID_FIELD;
3447   else
3448     {
3449       Call_Hook(form, fieldterm);
3450       res = fct(form);
3451       Call_Hook(form, fieldinit);
3452     }
3453   return res;
3454 }
3455
3456 /*---------------------------------------------------------------------------
3457 |   Facility      :  libnform
3458 |   Function      :  static int FN_Next_Field(FORM * form)
3459 |
3460 |   Description   :  Move to the next field on the current page of the form
3461 |
3462 |   Return Values :  E_OK                 - success
3463 |                    != E_OK              - error from subordinate call
3464 +--------------------------------------------------------------------------*/
3465 static int
3466 FN_Next_Field(FORM *form)
3467 {
3468   T((T_CALLED("FN_Next_Field(%p)"), form));
3469   returnCode(_nc_Set_Current_Field(form,
3470                                    Next_Field_On_Page(form->current)));
3471 }
3472
3473 /*---------------------------------------------------------------------------
3474 |   Facility      :  libnform
3475 |   Function      :  static int FN_Previous_Field(FORM * form)
3476 |
3477 |   Description   :  Move to the previous field on the current page of the
3478 |                    form
3479 |
3480 |   Return Values :  E_OK                 - success
3481 |                    != E_OK              - error from subordinate call
3482 +--------------------------------------------------------------------------*/
3483 static int
3484 FN_Previous_Field(FORM *form)
3485 {
3486   T((T_CALLED("FN_Previous_Field(%p)"), form));
3487   returnCode(_nc_Set_Current_Field(form,
3488                                    Previous_Field_On_Page(form->current)));
3489 }
3490
3491 /*---------------------------------------------------------------------------
3492 |   Facility      :  libnform
3493 |   Function      :  static int FN_First_Field(FORM * form)
3494 |
3495 |   Description   :  Move to the first field on the current page of the form
3496 |
3497 |   Return Values :  E_OK                 - success
3498 |                    != E_OK              - error from subordinate call
3499 +--------------------------------------------------------------------------*/
3500 static int
3501 FN_First_Field(FORM *form)
3502 {
3503   T((T_CALLED("FN_First_Field(%p)"), form));
3504   returnCode(_nc_Set_Current_Field(form,
3505                                    Next_Field_On_Page(form->field[form->page[form->curpage].pmax])));
3506 }
3507
3508 /*---------------------------------------------------------------------------
3509 |   Facility      :  libnform
3510 |   Function      :  static int FN_Last_Field(FORM * form)
3511 |
3512 |   Description   :  Move to the last field on the current page of the form
3513 |
3514 |   Return Values :  E_OK                 - success
3515 |                    != E_OK              - error from subordinate call
3516 +--------------------------------------------------------------------------*/
3517 static int
3518 FN_Last_Field(FORM *form)
3519 {
3520   T((T_CALLED("FN_Last_Field(%p)"), form));
3521   returnCode(
3522               _nc_Set_Current_Field(form,
3523                                     Previous_Field_On_Page(form->field[form->page[form->curpage].pmin])));
3524 }
3525
3526 /*---------------------------------------------------------------------------
3527 |   Facility      :  libnform
3528 |   Function      :  static int FN_Sorted_Next_Field(FORM * form)
3529 |
3530 |   Description   :  Move to the sorted next field on the current page
3531 |                    of the form.
3532 |
3533 |   Return Values :  E_OK            - success
3534 |                    != E_OK         - error from subordinate call
3535 +--------------------------------------------------------------------------*/
3536 static int
3537 FN_Sorted_Next_Field(FORM *form)
3538 {
3539   T((T_CALLED("FN_Sorted_Next_Field(%p)"), form));
3540   returnCode(_nc_Set_Current_Field(form,
3541                                    Sorted_Next_Field(form->current)));
3542 }
3543
3544 /*---------------------------------------------------------------------------
3545 |   Facility      :  libnform
3546 |   Function      :  static int FN_Sorted_Previous_Field(FORM * form)
3547 |
3548 |   Description   :  Move to the sorted previous field on the current page
3549 |                    of the form.
3550 |
3551 |   Return Values :  E_OK            - success
3552 |                    != E_OK         - error from subordinate call
3553 +--------------------------------------------------------------------------*/
3554 static int
3555 FN_Sorted_Previous_Field(FORM *form)
3556 {
3557   T((T_CALLED("FN_Sorted_Previous_Field(%p)"), form));
3558   returnCode(_nc_Set_Current_Field(form,
3559                                    Sorted_Previous_Field(form->current)));
3560 }
3561
3562 /*---------------------------------------------------------------------------
3563 |   Facility      :  libnform
3564 |   Function      :  static int FN_Sorted_First_Field(FORM * form)
3565 |
3566 |   Description   :  Move to the sorted first field on the current page
3567 |                    of the form.
3568 |
3569 |   Return Values :  E_OK            - success
3570 |                    != E_OK         - error from subordinate call
3571 +--------------------------------------------------------------------------*/
3572 static int
3573 FN_Sorted_First_Field(FORM *form)
3574 {
3575   T((T_CALLED("FN_Sorted_First_Field(%p)"), form));
3576   returnCode(_nc_Set_Current_Field(form,
3577                                    Sorted_Next_Field(form->field[form->page[form->curpage].smax])));
3578 }
3579
3580 /*---------------------------------------------------------------------------
3581 |   Facility      :  libnform
3582 |   Function      :  static int FN_Sorted_Last_Field(FORM * form)
3583 |
3584 |   Description   :  Move to the sorted last field on the current page
3585 |                    of the form.
3586 |
3587 |   Return Values :  E_OK            - success
3588 |                    != E_OK         - error from subordinate call
3589 +--------------------------------------------------------------------------*/
3590 static int
3591 FN_Sorted_Last_Field(FORM *form)
3592 {
3593   T((T_CALLED("FN_Sorted_Last_Field(%p)"), form));
3594   returnCode(_nc_Set_Current_Field(form,
3595                                    Sorted_Previous_Field(form->field[form->page[form->curpage].smin])));
3596 }
3597
3598 /*---------------------------------------------------------------------------
3599 |   Facility      :  libnform
3600 |   Function      :  static int FN_Left_Field(FORM * form)
3601 |
3602 |   Description   :  Get the field on the left of the current field on the
3603 |                    same line and the same page. Cycles through the line.
3604 |
3605 |   Return Values :  E_OK            - success
3606 |                    != E_OK         - error from subordinate call
3607 +--------------------------------------------------------------------------*/
3608 static int
3609 FN_Left_Field(FORM *form)
3610 {
3611   T((T_CALLED("FN_Left_Field(%p)"), form));
3612   returnCode(_nc_Set_Current_Field(form,
3613                                    Left_Neighbor_Field(form->current)));
3614 }
3615
3616 /*---------------------------------------------------------------------------
3617 |   Facility      :  libnform
3618 |   Function      :  static int FN_Right_Field(FORM * form)
3619 |
3620 |   Description   :  Get the field on the right of the current field on the
3621 |                    same line and the same page. Cycles through the line.
3622 |
3623 |   Return Values :  E_OK            - success
3624 |                    != E_OK         - error from subordinate call
3625 +--------------------------------------------------------------------------*/
3626 static int
3627 FN_Right_Field(FORM *form)
3628 {
3629   T((T_CALLED("FN_Right_Field(%p)"), form));
3630   returnCode(_nc_Set_Current_Field(form,
3631                                    Right_Neighbor_Field(form->current)));
3632 }
3633
3634 /*---------------------------------------------------------------------------
3635 |   Facility      :  libnform
3636 |   Function      :  static int FN_Up_Field(FORM * form)
3637 |
3638 |   Description   :  Get the upper neighbor of the current field. This
3639 |                    cycles through the page. See the comments of the
3640 |                    Upper_Neighbor_Field function to understand how
3641 |                    'upper' is defined.
3642 |
3643 |   Return Values :  E_OK            - success
3644 |                    != E_OK         - error from subordinate call
3645 +--------------------------------------------------------------------------*/
3646 static int
3647 FN_Up_Field(FORM *form)
3648 {
3649   T((T_CALLED("FN_Up_Field(%p)"), form));
3650   returnCode(_nc_Set_Current_Field(form,
3651                                    Upper_Neighbor_Field(form->current)));
3652 }
3653
3654 /*---------------------------------------------------------------------------
3655 |   Facility      :  libnform
3656 |   Function      :  static int FN_Down_Field(FORM * form)
3657 |
3658 |   Description   :  Get the down neighbor of the current field. This
3659 |                    cycles through the page. See the comments of the
3660 |                    Down_Neighbor_Field function to understand how
3661 |                    'down' is defined.
3662 |
3663 |   Return Values :  E_OK            - success
3664 |                    != E_OK         - error from subordinate call
3665 +--------------------------------------------------------------------------*/
3666 static int
3667 FN_Down_Field(FORM *form)
3668 {
3669   T((T_CALLED("FN_Down_Field(%p)"), form));
3670   returnCode(_nc_Set_Current_Field(form,
3671                                    Down_Neighbor_Field(form->current)));
3672 }
3673 /*----------------------------------------------------------------------------
3674   END of Field Navigation routines
3675   --------------------------------------------------------------------------*/
3676
3677 /*----------------------------------------------------------------------------
3678   Helper routines for Page Navigation
3679   --------------------------------------------------------------------------*/
3680
3681 /*---------------------------------------------------------------------------
3682 |   Facility      :  libnform
3683 |   Function      :  int _nc_Set_Form_Page(FORM * form,
3684 |                                          int page,
3685 |                                          FIELD * field)
3686 |
3687 |   Description   :  Make the given page number the current page and make
3688 |                    the given field the current field on the page. If
3689 |                    for the field NULL is given, make the first field on
3690 |                    the page the current field. The routine acts only
3691 |                    if the requested page is not the current page.
3692 |
3693 |   Return Values :  E_OK                - success
3694 |                    != E_OK             - error from subordinate call
3695 |                    E_BAD_ARGUMENT      - invalid field pointer
3696 |                    E_SYSTEM_ERROR      - some severe basic error
3697 +--------------------------------------------------------------------------*/
3698 NCURSES_EXPORT(int)
3699 _nc_Set_Form_Page(FORM *form, int page, FIELD *field)
3700 {
3701   int res = E_OK;
3702
3703   if ((form->curpage != page))
3704     {
3705       FIELD *last_field, *field_on_page;
3706
3707       werase(Get_Form_Window(form));
3708       form->curpage = page;
3709       last_field = field_on_page = form->field[form->page[page].smin];
3710       do
3711         {
3712           if (field_on_page->opts & O_VISIBLE)
3713             if ((res = Display_Field(field_on_page)) != E_OK)
3714               return (res);
3715           field_on_page = field_on_page->snext;
3716         }
3717       while (field_on_page != last_field);
3718
3719       if (field)
3720         res = _nc_Set_Current_Field(form, field);
3721       else
3722         /* N.B.: we don't encapsulate this by Inter_Field_Navigation(),
3723            because this is already executed in a page navigation
3724            context that contains field navigation
3725          */
3726         res = FN_First_Field(form);
3727     }
3728   return (res);
3729 }
3730
3731 /*---------------------------------------------------------------------------
3732 |   Facility      :  libnform
3733 |   Function      :  static int Next_Page_Number(const FORM * form)
3734 |
3735 |   Description   :  Calculate the page number following the current page
3736 |                    number. This cycles if the highest page number is
3737 |                    reached.
3738 |
3739 |   Return Values :  The next page number
3740 +--------------------------------------------------------------------------*/
3741 NCURSES_INLINE static int
3742 Next_Page_Number(const FORM *form)
3743 {
3744   return (form->curpage + 1) % form->maxpage;
3745 }
3746
3747 /*---------------------------------------------------------------------------
3748 |   Facility      :  libnform
3749 |   Function      :  static int Previous_Page_Number(const FORM * form)
3750 |
3751 |   Description   :  Calculate the page number before the current page
3752 |                    number. This cycles if the first page number is
3753 |                    reached.
3754 |
3755 |   Return Values :  The previous page number
3756 +--------------------------------------------------------------------------*/
3757 NCURSES_INLINE static int
3758 Previous_Page_Number(const FORM *form)
3759 {
3760   return (form->curpage != 0 ? form->curpage - 1 : form->maxpage - 1);
3761 }
3762
3763 /*----------------------------------------------------------------------------
3764   Page Navigation routines
3765   --------------------------------------------------------------------------*/
3766
3767 /*---------------------------------------------------------------------------
3768 |   Facility      :  libnform
3769 |   Function      :  static int Page_Navigation(
3770 |                                               int (* const fct) (FORM *),
3771 |                                               FORM * form)
3772 |
3773 |   Description   :  Generic behavior for changing a page. This means
3774 |                    that the field is left and a new field is entered.
3775 |                    So the field must be validated and the field init/term
3776 |                    hooks must be called. Because also the page is changed,
3777 |                    the forms init/term hooks must be called also.
3778 |
3779 |   Return Values :  E_OK                - success
3780 |                    E_INVALID_FIELD     - field is invalid
3781 |                    some other          - error from subordinate call
3782 +--------------------------------------------------------------------------*/
3783 static int
3784 Page_Navigation(int (*const fct) (FORM *), FORM *form)
3785 {
3786   int res;
3787
3788   if (!_nc_Internal_Validation(form))
3789     res = E_INVALID_FIELD;
3790   else
3791     {
3792       Call_Hook(form, fieldterm);
3793       Call_Hook(form, formterm);
3794       res = fct(form);
3795       Call_Hook(form, forminit);
3796       Call_Hook(form, fieldinit);
3797     }
3798   return res;
3799 }
3800
3801 /*---------------------------------------------------------------------------
3802 |   Facility      :  libnform
3803 |   Function      :  static int PN_Next_Page(FORM * form)
3804 |
3805 |   Description   :  Move to the next page of the form
3806 |
3807 |   Return Values :  E_OK                - success
3808 |                    != E_OK             - error from subordinate call
3809 +--------------------------------------------------------------------------*/
3810 static int
3811 PN_Next_Page(FORM *form)
3812 {
3813   T((T_CALLED("PN_Next_Page(%p)"), form));
3814   returnCode(_nc_Set_Form_Page(form, Next_Page_Number(form), (FIELD *)0));
3815 }
3816
3817 /*---------------------------------------------------------------------------
3818 |   Facility      :  libnform
3819 |   Function      :  static int PN_Previous_Page(FORM * form)
3820 |
3821 |   Description   :  Move to the previous page of the form
3822 |
3823 |   Return Values :  E_OK              - success
3824 |                    != E_OK           - error from subordinate call
3825 +--------------------------------------------------------------------------*/
3826 static int
3827 PN_Previous_Page(FORM *form)
3828 {
3829   T((T_CALLED("PN_Previous_Page(%p)"), form));
3830   returnCode(_nc_Set_Form_Page(form, Previous_Page_Number(form), (FIELD *)0));
3831 }
3832
3833 /*---------------------------------------------------------------------------
3834 |   Facility      :  libnform
3835 |   Function      :  static int PN_First_Page(FORM * form)
3836 |
3837 |   Description   :  Move to the first page of the form
3838 |
3839 |   Return Values :  E_OK              - success
3840 |                    != E_OK           - error from subordinate call
3841 +--------------------------------------------------------------------------*/
3842 static int
3843 PN_First_Page(FORM *form)
3844 {
3845   T((T_CALLED("PN_First_Page(%p)"), form));
3846   returnCode(_nc_Set_Form_Page(form, 0, (FIELD *)0));
3847 }
3848
3849 /*---------------------------------------------------------------------------
3850 |   Facility      :  libnform
3851 |   Function      :  static int PN_Last_Page(FORM * form)
3852 |
3853 |   Description   :  Move to the last page of the form
3854 |
3855 |   Return Values :  E_OK              - success
3856 |                    != E_OK           - error from subordinate call
3857 +--------------------------------------------------------------------------*/
3858 static int
3859 PN_Last_Page(FORM *form)
3860 {
3861   T((T_CALLED("PN_Last_Page(%p)"), form));
3862   returnCode(_nc_Set_Form_Page(form, form->maxpage - 1, (FIELD *)0));
3863 }
3864
3865 /*----------------------------------------------------------------------------
3866   END of Field Navigation routines
3867   --------------------------------------------------------------------------*/
3868
3869 /*----------------------------------------------------------------------------
3870   Helper routines for the core form driver.
3871   --------------------------------------------------------------------------*/
3872
3873 /*---------------------------------------------------------------------------
3874 |   Facility      :  libnform
3875 |   Function      :  static int Data_Entry(FORM * form,int c)
3876 |
3877 |   Description   :  Enter character c into at the current position of the
3878 |                    current field of the form.
3879 |
3880 |   Return Values :  E_OK              - success
3881 |                    E_REQUEST_DENIED  - driver could not process the request
3882 |                    E_SYSTEM_ERROR    -
3883 +--------------------------------------------------------------------------*/
3884 static int
3885 Data_Entry(FORM *form, int c)
3886 {
3887   FIELD *field = form->current;
3888   int result = E_REQUEST_DENIED;
3889
3890   T((T_CALLED("Data_Entry(%p,%s)"), form, _tracechtype((chtype)c)));
3891   if ((field->opts & O_EDIT)
3892 #if FIX_FORM_INACTIVE_BUG
3893       && (field->opts & O_ACTIVE)
3894 #endif
3895     )
3896     {
3897       if ((field->opts & O_BLANK) &&
3898           First_Position_In_Current_Field(form) &&
3899           !(form->status & _FCHECK_REQUIRED) &&
3900           !(form->status & _WINDOW_MODIFIED))
3901         werase(form->w);
3902
3903       if (form->status & _OVLMODE)
3904         {
3905           waddch(form->w, (chtype)c);
3906         }
3907       else
3908         /* no _OVLMODE */
3909         {
3910           bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
3911
3912           if (!(There_Is_Room ||
3913                 ((Single_Line_Field(field) && Growable(field)))))
3914             RETURN(E_REQUEST_DENIED);
3915
3916           if (!There_Is_Room && !Field_Grown(field, 1))
3917             RETURN(E_SYSTEM_ERROR);
3918
3919           winsch(form->w, (chtype)c);
3920         }
3921
3922       if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
3923         {
3924           bool End_Of_Field = (((field->drows - 1) == form->currow) &&
3925                                ((field->dcols - 1) == form->curcol));
3926
3927           form->status |= _WINDOW_MODIFIED;
3928           if (End_Of_Field && !Growable(field) && (field->opts & O_AUTOSKIP))
3929             result = Inter_Field_Navigation(FN_Next_Field, form);
3930           else
3931             {
3932               if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
3933                 result = E_SYSTEM_ERROR;
3934               else
3935                 {
3936 #if USE_WIDEC_SUPPORT
3937                   /*
3938                    * We have just added a byte to the form field.  It may have
3939                    * been part of a multibyte character.  If it was, the
3940                    * addch_used field is nonzero and we should not try to move
3941                    * to a new column.
3942                    */
3943                   if (WINDOW_EXT(form->w, addch_used) == 0)
3944                     IFN_Next_Character(form);
3945 #else
3946                   IFN_Next_Character(form);
3947 #endif
3948                   result = E_OK;
3949                 }
3950             }
3951         }
3952     }
3953   RETURN(result);
3954 }
3955
3956 /* Structure to describe the binding of a request code to a function.
3957    The member keycode codes the request value as well as the generic
3958    routine to use for the request. The code for the generic routine
3959    is coded in the upper 16 Bits while the request code is coded in
3960    the lower 16 bits.
3961
3962    In terms of C++ you might think of a request as a class with a
3963    virtual method "perform". The different types of request are
3964    derived from this base class and overload (or not) the base class
3965    implementation of perform.
3966 */
3967 typedef struct
3968 {
3969   int keycode;                  /* must be at least 32 bit: hi:mode, lo: key */
3970   int (*cmd) (FORM *);          /* low level driver routine for this key     */
3971 }
3972 Binding_Info;
3973
3974 /* You may see this is the class-id of the request type class */
3975 #define ID_PN    (0x00000000)   /* Page navigation           */
3976 #define ID_FN    (0x00010000)   /* Inter-Field navigation    */
3977 #define ID_IFN   (0x00020000)   /* Intra-Field navigation    */
3978 #define ID_VSC   (0x00030000)   /* Vertical Scrolling        */
3979 #define ID_HSC   (0x00040000)   /* Horizontal Scrolling      */
3980 #define ID_FE    (0x00050000)   /* Field Editing             */
3981 #define ID_EM    (0x00060000)   /* Edit Mode                 */
3982 #define ID_FV    (0x00070000)   /* Field Validation          */
3983 #define ID_CH    (0x00080000)   /* Choice                    */
3984 #define ID_Mask  (0xffff0000)
3985 #define Key_Mask (0x0000ffff)
3986 #define ID_Shft  (16)
3987
3988 /* This array holds all the Binding Infos */
3989 /* *INDENT-OFF* */
3990 static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =
3991 {
3992   { REQ_NEXT_PAGE    |ID_PN  ,PN_Next_Page},
3993   { REQ_PREV_PAGE    |ID_PN  ,PN_Previous_Page},
3994   { REQ_FIRST_PAGE   |ID_PN  ,PN_First_Page},
3995   { REQ_LAST_PAGE    |ID_PN  ,PN_Last_Page},
3996
3997   { REQ_NEXT_FIELD   |ID_FN  ,FN_Next_Field},
3998   { REQ_PREV_FIELD   |ID_FN  ,FN_Previous_Field},
3999   { REQ_FIRST_FIELD  |ID_FN  ,FN_First_Field},
4000   { REQ_LAST_FIELD   |ID_FN  ,FN_Last_Field},
4001   { REQ_SNEXT_FIELD  |ID_FN  ,FN_Sorted_Next_Field},
4002   { REQ_SPREV_FIELD  |ID_FN  ,FN_Sorted_Previous_Field},
4003   { REQ_SFIRST_FIELD |ID_FN  ,FN_Sorted_First_Field},
4004   { REQ_SLAST_FIELD  |ID_FN  ,FN_Sorted_Last_Field},
4005   { REQ_LEFT_FIELD   |ID_FN  ,FN_Left_Field},
4006   { REQ_RIGHT_FIELD  |ID_FN  ,FN_Right_Field},
4007   { REQ_UP_FIELD     |ID_FN  ,FN_Up_Field},
4008   { REQ_DOWN_FIELD   |ID_FN  ,FN_Down_Field},
4009
4010   { REQ_NEXT_CHAR    |ID_IFN ,IFN_Next_Character},
4011   { REQ_PREV_CHAR    |ID_IFN ,IFN_Previous_Character},
4012   { REQ_NEXT_LINE    |ID_IFN ,IFN_Next_Line},
4013   { REQ_PREV_LINE    |ID_IFN ,IFN_Previous_Line},
4014   { REQ_NEXT_WORD    |ID_IFN ,IFN_Next_Word},
4015   { REQ_PREV_WORD    |ID_IFN ,IFN_Previous_Word},
4016   { REQ_BEG_FIELD    |ID_IFN ,IFN_Beginning_Of_Field},
4017   { REQ_END_FIELD    |ID_IFN ,IFN_End_Of_Field},
4018   { REQ_BEG_LINE     |ID_IFN ,IFN_Beginning_Of_Line},
4019   { REQ_END_LINE     |ID_IFN ,IFN_End_Of_Line},
4020   { REQ_LEFT_CHAR    |ID_IFN ,IFN_Left_Character},
4021   { REQ_RIGHT_CHAR   |ID_IFN ,IFN_Right_Character},
4022   { REQ_UP_CHAR      |ID_IFN ,IFN_Up_Character},
4023   { REQ_DOWN_CHAR    |ID_IFN ,IFN_Down_Character},
4024
4025   { REQ_NEW_LINE     |ID_FE  ,FE_New_Line},
4026   { REQ_INS_CHAR     |ID_FE  ,FE_Insert_Character},
4027   { REQ_INS_LINE     |ID_FE  ,FE_Insert_Line},
4028   { REQ_DEL_CHAR     |ID_FE  ,FE_Delete_Character},
4029   { REQ_DEL_PREV     |ID_FE  ,FE_Delete_Previous},
4030   { REQ_DEL_LINE     |ID_FE  ,FE_Delete_Line},
4031   { REQ_DEL_WORD     |ID_FE  ,FE_Delete_Word},
4032   { REQ_CLR_EOL      |ID_FE  ,FE_Clear_To_End_Of_Line},
4033   { REQ_CLR_EOF      |ID_FE  ,FE_Clear_To_End_Of_Field},
4034   { REQ_CLR_FIELD    |ID_FE  ,FE_Clear_Field},
4035
4036   { REQ_OVL_MODE     |ID_EM  ,EM_Overlay_Mode},
4037   { REQ_INS_MODE     |ID_EM  ,EM_Insert_Mode},
4038
4039   { REQ_SCR_FLINE    |ID_VSC ,VSC_Scroll_Line_Forward},
4040   { REQ_SCR_BLINE    |ID_VSC ,VSC_Scroll_Line_Backward},
4041   { REQ_SCR_FPAGE    |ID_VSC ,VSC_Scroll_Page_Forward},
4042   { REQ_SCR_BPAGE    |ID_VSC ,VSC_Scroll_Page_Backward},
4043   { REQ_SCR_FHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Forward},
4044   { REQ_SCR_BHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Backward},
4045
4046   { REQ_SCR_FCHAR    |ID_HSC ,HSC_Scroll_Char_Forward},
4047   { REQ_SCR_BCHAR    |ID_HSC ,HSC_Scroll_Char_Backward},
4048   { REQ_SCR_HFLINE   |ID_HSC ,HSC_Horizontal_Line_Forward},
4049   { REQ_SCR_HBLINE   |ID_HSC ,HSC_Horizontal_Line_Backward},
4050   { REQ_SCR_HFHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Forward},
4051   { REQ_SCR_HBHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Backward},
4052
4053   { REQ_VALIDATION   |ID_FV  ,FV_Validation},
4054
4055   { REQ_NEXT_CHOICE  |ID_CH  ,CR_Next_Choice},
4056   { REQ_PREV_CHOICE  |ID_CH  ,CR_Previous_Choice}
4057 };
4058 /* *INDENT-ON* */
4059
4060 /*---------------------------------------------------------------------------
4061 |   Facility      :  libnform
4062 |   Function      :  int form_driver(FORM * form,int  c)
4063 |
4064 |   Description   :  This is the workhorse of the forms system. It checks
4065 |                    to determine whether the character c is a request or
4066 |                    data. If it is a request, the form driver executes
4067 |                    the request and returns the result. If it is data
4068 |                    (printable character), it enters the data into the
4069 |                    current position in the current field. If it is not
4070 |                    recognized, the form driver assumes it is an application
4071 |                    defined command and returns E_UNKNOWN_COMMAND.
4072 |                    Application defined command should be defined relative
4073 |                    to MAX_FORM_COMMAND, the maximum value of a request.
4074 |
4075 |   Return Values :  E_OK              - success
4076 |                    E_SYSTEM_ERROR    - system error
4077 |                    E_BAD_ARGUMENT    - an argument is incorrect
4078 |                    E_NOT_POSTED      - form is not posted
4079 |                    E_INVALID_FIELD   - field contents are invalid
4080 |                    E_BAD_STATE       - called from inside a hook routine
4081 |                    E_REQUEST_DENIED  - request failed
4082 |                    E_NOT_CONNECTED   - no fields are connected to the form
4083 |                    E_UNKNOWN_COMMAND - command not known
4084 +--------------------------------------------------------------------------*/
4085 NCURSES_EXPORT(int)
4086 form_driver(FORM *form, int c)
4087 {
4088   const Binding_Info *BI = (Binding_Info *) 0;
4089   int res = E_UNKNOWN_COMMAND;
4090
4091   T((T_CALLED("form_driver(%p,%d)"), form, c));
4092
4093   if (!form)
4094     RETURN(E_BAD_ARGUMENT);
4095
4096   if (!(form->field))
4097     RETURN(E_NOT_CONNECTED);
4098
4099   assert(form->page);
4100
4101   if (c == FIRST_ACTIVE_MAGIC)
4102     {
4103       form->current = _nc_First_Active_Field(form);
4104       RETURN(E_OK);
4105     }
4106
4107   assert(form->current &&
4108          form->current->buf &&
4109          (form->current->form == form)
4110     );
4111
4112   if (form->status & _IN_DRIVER)
4113     RETURN(E_BAD_STATE);
4114
4115   if (!(form->status & _POSTED))
4116     RETURN(E_NOT_POSTED);
4117
4118   if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4119       ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4120     BI = &(bindings[c - MIN_FORM_COMMAND]);
4121
4122   if (BI)
4123     {
4124       typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4125       static const Generic_Method Generic_Methods[] =
4126       {
4127         Page_Navigation,        /* overloaded to call field&form hooks */
4128         Inter_Field_Navigation, /* overloaded to call field hooks      */
4129         NULL,                   /* Intra-Field is generic              */
4130         Vertical_Scrolling,     /* Overloaded to check multi-line      */
4131         Horizontal_Scrolling,   /* Overloaded to check single-line     */
4132         Field_Editing,          /* Overloaded to mark modification     */
4133         NULL,                   /* Edit Mode is generic                */
4134         NULL,                   /* Field Validation is generic         */
4135         NULL                    /* Choice Request is generic           */
4136       };
4137       size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
4138       size_t method = ((BI->keycode & ID_Mask) >> ID_Shft) & 0xffff;
4139
4140       if ((method >= nMethods) || !(BI->cmd))
4141         res = E_SYSTEM_ERROR;
4142       else
4143         {
4144           Generic_Method fct = Generic_Methods[method];
4145
4146           if (fct)
4147             res = fct(BI->cmd, form);
4148           else
4149             res = (BI->cmd) (form);
4150         }
4151     }
4152   else if (!(c & (~(int)MAX_REGULAR_CHARACTER)))
4153     {
4154       /*
4155        * If we're using 8-bit characters, iscntrl+isprint cover the whole set.
4156        * But with multibyte characters, there is a third possibility, i.e.,
4157        * parts of characters that build up into printable characters which are
4158        * not considered printable.
4159        *
4160        * FIXME: the wide-character branch should also use Check_Char().
4161        */
4162 #if USE_WIDEC_SUPPORT
4163       if (!iscntrl(UChar(c)))
4164 #else
4165       if (isprint(UChar(c)) &&
4166           Check_Char(form->current->type, c,
4167                      (TypeArgument *)(form->current->arg)))
4168 #endif
4169         res = Data_Entry(form, c);
4170     }
4171   _nc_Refresh_Current_Field(form);
4172   RETURN(res);
4173 }
4174
4175 /*----------------------------------------------------------------------------
4176   Field-Buffer manipulation routines.
4177   The effects of setting a buffer are tightly coupled to the core of the form
4178   driver logic. This is especially true in the case of growable fields.
4179   So I don't separate this into a separate module.
4180   --------------------------------------------------------------------------*/
4181
4182 /*---------------------------------------------------------------------------
4183 |   Facility      :  libnform
4184 |   Function      :  int set_field_buffer(FIELD *field,
4185 |                                         int buffer, char *value)
4186 |
4187 |   Description   :  Set the given buffer of the field to the given value.
4188 |                    Buffer 0 stores the displayed content of the field.
4189 |                    For dynamic fields this may grow the fieldbuffers if
4190 |                    the length of the value exceeds the current buffer
4191 |                    length. For buffer 0 only printable values are allowed.
4192 |                    For static fields, the value needs not to be zero ter-
4193 |                    minated. It is copied up to the length of the buffer.
4194 |
4195 |   Return Values :  E_OK            - success
4196 |                    E_BAD_ARGUMENT  - invalid argument
4197 |                    E_SYSTEM_ERROR  - system error
4198 +--------------------------------------------------------------------------*/
4199 NCURSES_EXPORT(int)
4200 set_field_buffer(FIELD *field, int buffer, const char *value)
4201 {
4202   FIELD_CELL *p;
4203   int res = E_OK;
4204   unsigned int i;
4205   unsigned int len;
4206
4207 #if USE_WIDEC_SUPPORT
4208   FIELD_CELL *widevalue = 0;
4209 #endif
4210
4211   T((T_CALLED("set_field_buffer(%p,%d,%s)"), field, buffer, _nc_visbuf(value)));
4212
4213   if (!field || !value || ((buffer < 0) || (buffer > field->nbuf)))
4214     RETURN(E_BAD_ARGUMENT);
4215
4216   len = Buffer_Length(field);
4217
4218   if (buffer == 0)
4219     {
4220       for (i = 0; (value[i] != '\0') && (i < len); ++i)
4221         {
4222           if (iscntrl(UChar(value[i])))
4223             RETURN(E_BAD_ARGUMENT);
4224         }
4225     }
4226
4227   if (Growable(field))
4228     {
4229       /* for a growable field we must assume zero terminated strings, because
4230          somehow we have to detect the length of what should be copied.
4231        */
4232       unsigned int vlen = strlen(value);
4233
4234       if (vlen > len)
4235         {
4236           if (!Field_Grown(field,
4237                            (int)(1 + (vlen - len) / ((field->rows + field->nrow)
4238                                                      * field->cols))))
4239             RETURN(E_SYSTEM_ERROR);
4240
4241           /* in this case we also have to check, whether or not the remaining
4242              characters in value are also printable for buffer 0. */
4243           if (buffer == 0)
4244             {
4245               for (i = len; i < vlen; i++)
4246                 if (iscntrl(UChar(value[i])))
4247                   RETURN(E_BAD_ARGUMENT);
4248             }
4249           len = vlen;
4250         }
4251     }
4252
4253   p = Address_Of_Nth_Buffer(field, buffer);
4254
4255 #if USE_WIDEC_SUPPORT
4256   /*
4257    * Use addstr's logic for converting a string to an array of cchar_t's.
4258    * There should be a better way, but this handles nonspacing characters
4259    * and other special cases that we really do not want to handle here.
4260    */
4261   wclear(field->working);
4262   mvwaddstr(field->working, 0, 0, value);
4263
4264   if ((widevalue = (FIELD_CELL *)calloc(len, sizeof(FIELD_CELL))) == 0)
4265     {
4266       RETURN(E_SYSTEM_ERROR);
4267     }
4268   else
4269     {
4270       mvwin_wchnstr(field->working, 0, 0, widevalue, (int)len);
4271       for (i = 0; i < len; ++i)
4272         {
4273           if (CharEq(myZEROS, widevalue[i]))
4274             {
4275               while (i < len)
4276                 p[i++] = myBLANK;
4277               break;
4278             }
4279           p[i] = widevalue[i];
4280         }
4281       free(widevalue);
4282     }
4283 #else
4284   for (i = 0; i < len; ++i)
4285     {
4286       if (value[i] == '\0')
4287         {
4288           while (i < len)
4289             p[i++] = myBLANK;
4290           break;
4291         }
4292       p[i] = value[i];
4293     }
4294 #endif
4295
4296   if (buffer == 0)
4297     {
4298       int syncres;
4299
4300       if (((syncres = Synchronize_Field(field)) != E_OK) &&
4301           (res == E_OK))
4302         res = syncres;
4303       if (((syncres = Synchronize_Linked_Fields(field)) != E_OK) &&
4304           (res == E_OK))
4305         res = syncres;
4306     }
4307   RETURN(res);
4308 }
4309
4310 /*---------------------------------------------------------------------------
4311 |   Facility      :  libnform
4312 |   Function      :  char *field_buffer(const FIELD *field,int buffer)
4313 |
4314 |   Description   :  Return the address of the buffer for the field.
4315 |
4316 |   Return Values :  Pointer to buffer or NULL if arguments were invalid.
4317 +--------------------------------------------------------------------------*/
4318 NCURSES_EXPORT(char *)
4319 field_buffer(const FIELD *field, int buffer)
4320 {
4321   char *result = 0;
4322
4323   T((T_CALLED("field_buffer(%p,%d)"), field, buffer));
4324
4325   if (field && (buffer >= 0) && (buffer <= field->nbuf))
4326     {
4327 #if USE_WIDEC_SUPPORT
4328       FIELD_CELL *data = Address_Of_Nth_Buffer(field, buffer);
4329       unsigned need = 0;
4330       int size = Buffer_Length(field);
4331       int n;
4332
4333       /* determine the number of bytes needed to store the expanded string */
4334       for (n = 0; n < size; ++n)
4335         {
4336           if (!isWidecExt(data[n]))
4337             {
4338               mbstate_t state;
4339               size_t next;
4340
4341               init_mb(state);
4342               next = _nc_wcrtomb(0, data[n].chars[0], &state);
4343               if (!isEILSEQ(next))
4344                 {
4345                   if (next != 0)
4346                     need += next;
4347                 }
4348             }
4349         }
4350
4351       /* allocate a place to store the expanded string */
4352       if (field->expanded[buffer] != 0)
4353         free(field->expanded[buffer]);
4354       field->expanded[buffer] = typeMalloc(char, need + 1);
4355
4356       /* expand the multibyte data */
4357       if ((result = field->expanded[buffer]) != 0)
4358         {
4359           wclear(field->working);
4360           mvwadd_wchnstr(field->working, 0, 0, data, size);
4361           mvwinnstr(field->working, 0, 0, result, (int)need + 1);
4362         }
4363 #else
4364       result = Address_Of_Nth_Buffer(field, buffer);
4365 #endif
4366     }
4367   returnPtr(result);
4368 }
4369
4370 #if USE_WIDEC_SUPPORT
4371
4372 /* FIXME: see lib_get_wch.c */
4373 #if HAVE_MBTOWC && HAVE_MBLEN
4374 #define reset_mbytes(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
4375 #define count_mbytes(buffer,length,state) mblen(buffer,length)
4376 #define trans_mbytes(wch,buffer,length,state) \
4377         (int) mbtowc(&wch, buffer, length)
4378 #elif HAVE_MBRTOWC && HAVE_MBRLEN
4379 #define NEED_STATE
4380 #define reset_mbytes(state) init_mb(state)
4381 #define count_mbytes(buffer,length,state) mbrlen(buffer,length,&state)
4382 #define trans_mbytes(wch,buffer,length,state) \
4383         (int) mbrtowc(&wch, buffer, length, &state)
4384 #else
4385 make an error
4386 #endif
4387
4388 /*---------------------------------------------------------------------------
4389 | Convert a multibyte string to a wide-character string.  The result must be
4390 | freed by the caller.
4391 +--------------------------------------------------------------------------*/
4392 NCURSES_EXPORT(wchar_t *)
4393 _nc_Widen_String(char *source, int *lengthp)
4394 {
4395   wchar_t *result = 0;
4396   wchar_t wch;
4397   size_t given = strlen(source);
4398   size_t tries;
4399   int pass;
4400   int status;
4401
4402 #ifdef NEED_STATE
4403   mbstate_t state;
4404 #endif
4405
4406   for (pass = 0; pass < 2; ++pass)
4407     {
4408       unsigned need = 0;
4409       size_t passed = 0;
4410
4411       while (passed < given)
4412         {
4413           bool found = FALSE;
4414
4415           for (tries = 1, status = 0; tries <= (given - passed); ++tries)
4416             {
4417               int save = source[passed + tries];
4418
4419               source[passed + tries] = 0;
4420               reset_mbytes(state);
4421               status = trans_mbytes(wch, source + passed, tries, state);
4422               source[passed + tries] = save;
4423
4424               if (status > 0)
4425                 {
4426                   found = TRUE;
4427                   break;
4428                 }
4429             }
4430           if (found)
4431             {
4432               if (pass)
4433                 {
4434                   result[need] = wch;
4435                 }
4436               passed += status;
4437               ++need;
4438             }
4439           else
4440             {
4441               if (pass)
4442                 {
4443                   result[need] = source[passed];
4444                 }
4445               ++need;
4446               ++passed;
4447             }
4448         }
4449
4450       if (!pass)
4451         {
4452           if (!need)
4453             break;
4454           result = typeCalloc(wchar_t, need);
4455
4456           *lengthp = need;
4457           if (result == 0)
4458             break;
4459         }
4460     }
4461
4462   return result;
4463 }
4464 #endif
4465
4466 /* frm_driver.c ends here */