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