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