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