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