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