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