1121663Sharti/****************************************************************************
2121663Sharti * Copyright 2018-2020,2021 Thomas E. Dickey                                *
3121663Sharti * Copyright 1998-2016,2017 Free Software Foundation, Inc.                  *
4121663Sharti *                                                                          *
5121663Sharti * Permission is hereby granted, free of charge, to any person obtaining a  *
6208483Suqs * copy of this software and associated documentation files (the            *
7131823Sharti * "Software"), to deal in the Software without restriction, including      *
8121663Sharti * without limitation the rights to use, copy, modify, merge, publish,      *
9121663Sharti * distribute, distribute with modifications, sublicense, and/or sell       *
10121663Sharti * copies of the Software, and to permit persons to whom the Software is    *
11121663Sharti * furnished to do so, subject to the following conditions:                 *
12121663Sharti *                                                                          *
13121663Sharti * The above copyright notice and this permission notice shall be included  *
14121663Sharti * in all copies or substantial portions of the Software.                   *
15121663Sharti *                                                                          *
16121663Sharti * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17121663Sharti * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18121663Sharti * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19121663Sharti * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20121663Sharti * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21121663Sharti * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22121663Sharti * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23121663Sharti *                                                                          *
24121663Sharti * Except as contained in this notice, the name(s) of the above copyright   *
25121663Sharti * holders shall not be used in advertising or otherwise to promote the     *
26121663Sharti * sale, use or other dealings in this Software without prior written       *
27121663Sharti * authorization.                                                           *
28121663Sharti ****************************************************************************/
29131823Sharti
30121663Sharti/****************************************************************************
31121663Sharti *   Author:  Juergen Pfeifer, 1995,1997                                    *
32121663Sharti ****************************************************************************/
33121663Sharti
34121663Sharti#include "form.priv.h"
35121663Sharti
36121663ShartiMODULE_ID("$Id: frm_driver.c,v 1.135 2021/09/01 23:34:01 tom Exp $")
37121663Sharti
38121663Sharti/*----------------------------------------------------------------------------
39121663Sharti  This is the core module of the form library. It contains the majority
40121663Sharti  of the driver routines as well as the form_driver function.
41121663Sharti
42121663Sharti  Essentially this module is nearly the whole library. This is because
43121663Sharti  all the functions in this module depends on some others in the module,
44121663Sharti  so it makes no sense to split them into separate files because they
45121663Sharti  will always be linked together. The only acceptable concern is turnaround
46121663Sharti  time for this module, but now we have all Pentiums or RISCs, so what!
47121663Sharti
48121663Sharti  The driver routines are grouped into nine generic categories:
49121663Sharti
50121663Sharti   a)   Page Navigation            ( all functions prefixed by PN_ )
51121663Sharti        The current page of the form is left and some new page is
52121663Sharti        entered.
53121663Sharti   b)   Inter-Field Navigation     ( all functions prefixed by FN_ )
54121663Sharti        The current field of the form is left and some new field is
55121663Sharti        entered.
56121663Sharti   c)   Intra-Field Navigation     ( all functions prefixed by IFN_ )
57121663Sharti        The current position in the current field is changed.
58121663Sharti   d)   Vertical Scrolling         ( all functions prefixed by VSC_ )
59121663Sharti        Essentially this is a specialization of Intra-Field navigation.
60121663Sharti        It has to check for a multi-line field.
61121663Sharti   e)   Horizontal Scrolling       ( all functions prefixed by HSC_ )
62121663Sharti        Essentially this is a specialization of Intra-Field navigation.
63121663Sharti        It has to check for a single-line field.
64121663Sharti   f)   Field Editing              ( all functions prefixed by FE_ )
65121663Sharti        The content of the current field is changed
66121663Sharti   g)   Edit Mode requests         ( all functions prefixed by EM_ )
67121663Sharti        Switching between insert and overlay mode
68121663Sharti   h)   Field-Validation requests  ( all functions prefixed by FV_ )
69121663Sharti        Perform verifications of the field.
70121663Sharti   i)   Choice requests            ( all functions prefixed by CR_ )
71121663Sharti        Requests to enumerate possible field values
72121663Sharti  --------------------------------------------------------------------------*/
73121663Sharti
74121663Sharti/*----------------------------------------------------------------------------
75121663Sharti  Some remarks on the placements of assert() macros :
76121663Sharti  I use them only on "strategic" places, i.e. top level entries where
77121663Sharti  I want to make sure that things are set correctly. Throughout subordinate
78121663Sharti  routines I omit them mostly.
79121663Sharti  --------------------------------------------------------------------------*/
80121663Sharti
81121663Sharti/*
82121663ShartiSome options that may effect compatibility in behavior to SVr4 forms,
83121663Shartibut they are here to allow a more intuitive and user friendly behavior of
84121663Shartiour form implementation. This doesn't affect the API, so we feel it is
85121663Shartiuncritical.
86121663Sharti
87121663ShartiThe initial implementation tries to stay very close with the behavior
88121663Shartiof the original SVr4 implementation, although in some areas it is quite
89121663Sharticlear that this isn't the most appropriate way. As far as possible this
90121663Shartisources will allow you to build a forms lib that behaves quite similar
91121663Shartito SVr4, but now and in the future we will give you better options.
92121663ShartiPerhaps at some time we will make this configurable at runtime.
93121663Sharti*/
94121663Sharti
95121663Sharti/* Implement a more user-friendly previous/next word behavior */
96121663Sharti#define FRIENDLY_PREV_NEXT_WORD (1)
97121663Sharti/* Fix the wrong behavior for forms with all fields inactive */
98121663Sharti#define FIX_FORM_INACTIVE_BUG (1)
99121663Sharti/* Allow dynamic field growth also when navigating past the end */
100121663Sharti#define GROW_IF_NAVIGATE (1)
101121663Sharti
102121663Sharti#if USE_WIDEC_SUPPORT
103121663Sharti#define myADDNSTR(w, s, n) wide_waddnstr(w, s, n)
104121663Sharti#define myINSNSTR(w, s, n) wide_winsnstr(w, s, n)
105121663Sharti#define myINNSTR(w, s, n)  wide_winnstr(w, s, n)
106121663Sharti#define myWCWIDTH(w, y, x) cell_width(w, y, x)
107121663Sharti#else
108121663Sharti#define myADDNSTR(w, s, n) waddnstr(w, s, n)
109121663Sharti#define myINSNSTR(w, s, n) winsnstr(w, s, n)
110121663Sharti#define myINNSTR(w, s, n)  winnstr(w, s, n)
111121663Sharti#define myWCWIDTH(w, y, x) 1
112121663Sharti#endif
113121663Sharti
114121663Sharti/*----------------------------------------------------------------------------
115121663Sharti  Forward references to some internally used static functions
116121663Sharti  --------------------------------------------------------------------------*/
117121663Shartistatic int Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form);
118121663Shartistatic int FN_Next_Field(FORM *form);
119121663Shartistatic int FN_Previous_Field(FORM *form);
120121663Shartistatic int FE_New_Line(FORM *);
121121663Shartistatic int FE_Delete_Previous(FORM *);
122121663Sharti
123121663Sharti/*----------------------------------------------------------------------------
124121663Sharti  Macro Definitions.
125121663Sharti
126121663Sharti  Some Remarks on that: I use the convention to use UPPERCASE for constants
127121663Sharti  defined by Macros. If I provide a macro as a kind of inline routine to
128121663Sharti  provide some logic, I use my Upper_Lower case style.
129121663Sharti  --------------------------------------------------------------------------*/
130121663Sharti
131121663Sharti/* Calculate the position of a single row in a field buffer */
132121663Sharti#define Position_Of_Row_In_Buffer(field,row) ((row)*(field)->dcols)
133121663Sharti
134121663Sharti/* Calculate start address for the field's buffer# N */
135121663Sharti#define Address_Of_Nth_Buffer(field,N) \
136121663Sharti  ((field)->buf + (N)*(1+Buffer_Length(field)))
137121663Sharti
138121663Sharti/* Calculate the start address of the row in the field's specified buffer# N */
139121663Sharti#define Address_Of_Row_In_Nth_Buffer(field,N,row) \
140121663Sharti  (Address_Of_Nth_Buffer(field,N) + Position_Of_Row_In_Buffer(field,row))
141121663Sharti
142121663Sharti/* Calculate the start address of the row in the field's primary buffer */
143121663Sharti#define Address_Of_Row_In_Buffer(field,row) \
144121663Sharti  Address_Of_Row_In_Nth_Buffer(field,0,row)
145121663Sharti
146121663Sharti/* Calculate the start address of the row in the form's current field
147121663Sharti   buffer# N */
148121663Sharti#define Address_Of_Current_Row_In_Nth_Buffer(form,N) \
149121663Sharti   Address_Of_Row_In_Nth_Buffer((form)->current,N,(form)->currow)
150121663Sharti
151121663Sharti/* Calculate the start address of the row in the form's current field
152121663Sharti   primary buffer */
153121663Sharti#define Address_Of_Current_Row_In_Buffer(form) \
154121663Sharti   Address_Of_Current_Row_In_Nth_Buffer(form,0)
155121663Sharti
156121663Sharti/* Calculate the address of the cursor in the form's current field
157121663Sharti   primary buffer */
158121663Sharti#define Address_Of_Current_Position_In_Nth_Buffer(form,N) \
159121663Sharti   (Address_Of_Current_Row_In_Nth_Buffer(form,N) + (form)->curcol)
160121663Sharti
161121663Sharti/* Calculate the address of the cursor in the form's current field
162121663Sharti   buffer# N */
163121663Sharti#define Address_Of_Current_Position_In_Buffer(form) \
164121663Sharti  Address_Of_Current_Position_In_Nth_Buffer(form,0)
165121663Sharti
166121663Sharti/* Logic to decide whether or not a field is actually a field with
167121663Sharti   vertical or horizontal scrolling */
168121663Sharti#define Is_Scroll_Field(field)          \
169208483Suqs   (((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  --------------------------------------------------------------------------*/
213static FIELD_CELL myBLANK = BLANK;
214static FIELD_CELL myZEROS;
215
216#ifdef TRACE
217static void
218check_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 */
245static int
246wide_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 */
264static int
265wide_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 */
287static int
288wide_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 */
309static int
310cell_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 */
331static int
332cell_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 */
358static void
359delete_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+--------------------------------------------------------------------------*/
386NCURSES_INLINE static FIELD_CELL *
387Get_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+--------------------------------------------------------------------------*/
408NCURSES_INLINE static FIELD_CELL *
409After_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+--------------------------------------------------------------------------*/
428NCURSES_INLINE static FIELD_CELL *
429Get_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+--------------------------------------------------------------------------*/
450NCURSES_INLINE static FIELD_CELL *
451After_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+--------------------------------------------------------------------------*/
479NCURSES_INLINE static void
480Adjust_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+--------------------------------------------------------------------------*/
510static void
511Buffer_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+--------------------------------------------------------------------------*/
553FORM_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+--------------------------------------------------------------------------*/
608static void
609Window_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+--------------------------------------------------------------------------*/
625NCURSES_INLINE static void
626Synchronize_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+--------------------------------------------------------------------------*/
651static bool
652Field_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+--------------------------------------------------------------------------*/
801static int
802Field_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+--------------------------------------------------------------------------*/
830FORM_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+--------------------------------------------------------------------------*/
871static bool move_after_insert = TRUE;
872FORM_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+--------------------------------------------------------------------------*/
1005static void
1006Perform_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+--------------------------------------------------------------------------*/
1053static void
1054Undo_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+--------------------------------------------------------------------------*/
1090static bool
1091Check_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+--------------------------------------------------------------------------*/
1138static int
1139Display_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+--------------------------------------------------------------------------*/
1197static int
1198Synchronize_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+--------------------------------------------------------------------------*/
1241static int
1242Synchronize_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+--------------------------------------------------------------------------*/
1278FORM_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+--------------------------------------------------------------------------*/
1345FORM_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 */
1438void
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+--------------------------------------------------------------------------*/
1498FORM_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+--------------------------------------------------------------------------*/
1581static int
1582IFN_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+--------------------------------------------------------------------------*/
1623static int
1624IFN_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+--------------------------------------------------------------------------*/
1652static int
1653IFN_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+--------------------------------------------------------------------------*/
1680static int
1681IFN_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+--------------------------------------------------------------------------*/
1702static int
1703IFN_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+--------------------------------------------------------------------------*/
1744static int
1745IFN_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+--------------------------------------------------------------------------*/
1798static int
1799IFN_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+--------------------------------------------------------------------------*/
1821static int
1822IFN_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+--------------------------------------------------------------------------*/
1845static int
1846IFN_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+--------------------------------------------------------------------------*/
1869static int
1870IFN_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+--------------------------------------------------------------------------*/
1896static int
1897IFN_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+--------------------------------------------------------------------------*/
1921static int
1922IFN_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+--------------------------------------------------------------------------*/
1952static int
1953IFN_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+--------------------------------------------------------------------------*/
1974static int
1975IFN_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+--------------------------------------------------------------------------*/
2009static int
2010VSC_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+--------------------------------------------------------------------------*/
2062static int
2063Vertical_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+--------------------------------------------------------------------------*/
2085static int
2086VSC_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+--------------------------------------------------------------------------*/
2101static int
2102VSC_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+--------------------------------------------------------------------------*/
2117static int
2118VSC_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+--------------------------------------------------------------------------*/
2133static int
2134VSC_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+--------------------------------------------------------------------------*/
2149static int
2150VSC_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+--------------------------------------------------------------------------*/
2165static int
2166VSC_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+--------------------------------------------------------------------------*/
2189static int
2190HSC_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+--------------------------------------------------------------------------*/
2241static int
2242Horizontal_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+--------------------------------------------------------------------------*/
2259static int
2260HSC_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+--------------------------------------------------------------------------*/
2275static int
2276HSC_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+--------------------------------------------------------------------------*/
2291static int
2292HSC_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+--------------------------------------------------------------------------*/
2307static int
2308HSC_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+--------------------------------------------------------------------------*/
2323static int
2324HSC_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+--------------------------------------------------------------------------*/
2339static int
2340HSC_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+--------------------------------------------------------------------------*/
2364NCURSES_INLINE static bool
2365Is_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+--------------------------------------------------------------------------*/
2386NCURSES_INLINE static bool
2387Is_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+--------------------------------------------------------------------------*/
2421static int
2422Insert_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+--------------------------------------------------------------------------*/
2497static int
2498Wrapping_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+--------------------------------------------------------------------------*/
2575static int
2576Field_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+--------------------------------------------------------------------------*/
2632static int
2633FE_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+--------------------------------------------------------------------------*/
2716static int
2717FE_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+--------------------------------------------------------------------------*/
2752static int
2753FE_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+--------------------------------------------------------------------------*/
2789static int
2790FE_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+--------------------------------------------------------------------------*/
2810static int
2811FE_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+--------------------------------------------------------------------------*/
2880static int
2881FE_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+--------------------------------------------------------------------------*/
2898static int
2899FE_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+--------------------------------------------------------------------------*/
2938static int
2939FE_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+--------------------------------------------------------------------------*/
2955static int
2956FE_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+--------------------------------------------------------------------------*/
2972static int
2973FE_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+--------------------------------------------------------------------------*/
2996static int
2997EM_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+--------------------------------------------------------------------------*/
3012static int
3013EM_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+--------------------------------------------------------------------------*/
3041static bool
3042Next_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+--------------------------------------------------------------------------*/
3082static bool
3083Previous_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+--------------------------------------------------------------------------*/
3126static int
3127CR_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+--------------------------------------------------------------------------*/
3147static int
3148CR_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+--------------------------------------------------------------------------*/
3180static bool
3181Check_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+--------------------------------------------------------------------------*/
3233FORM_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+--------------------------------------------------------------------------*/
3269static int
3270FV_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+--------------------------------------------------------------------------*/
3297NCURSES_INLINE static FIELD *
3298Next_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+--------------------------------------------------------------------------*/
3327FORM_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+--------------------------------------------------------------------------*/
3380NCURSES_INLINE static FIELD *
3381Previous_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+--------------------------------------------------------------------------*/
3410NCURSES_INLINE static FIELD *
3411Sorted_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+--------------------------------------------------------------------------*/
3436NCURSES_INLINE static FIELD *
3437Sorted_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+--------------------------------------------------------------------------*/
3461NCURSES_INLINE static FIELD *
3462Left_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+--------------------------------------------------------------------------*/
3489NCURSES_INLINE static FIELD *
3490Right_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+--------------------------------------------------------------------------*/
3517static FIELD *
3518Upper_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 is 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+--------------------------------------------------------------------------*/
3567static FIELD *
3568Down_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+--------------------------------------------------------------------------*/
3623static int
3624Inter_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+--------------------------------------------------------------------------*/
3648static int
3649FN_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+--------------------------------------------------------------------------*/
3666static int
3667FN_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+--------------------------------------------------------------------------*/
3683static int
3684FN_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+--------------------------------------------------------------------------*/
3700static int
3701FN_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+--------------------------------------------------------------------------*/
3719static int
3720FN_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+--------------------------------------------------------------------------*/
3737static int
3738FN_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+--------------------------------------------------------------------------*/
3755static int
3756FN_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+--------------------------------------------------------------------------*/
3773static int
3774FN_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+--------------------------------------------------------------------------*/
3791static int
3792FN_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+--------------------------------------------------------------------------*/
3809static int
3810FN_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+--------------------------------------------------------------------------*/
3829static int
3830FN_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+--------------------------------------------------------------------------*/
3849static int
3850FN_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+--------------------------------------------------------------------------*/
3881FORM_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+--------------------------------------------------------------------------*/
3924NCURSES_INLINE static int
3925Next_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+--------------------------------------------------------------------------*/
3940NCURSES_INLINE static int
3941Previous_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+--------------------------------------------------------------------------*/
3966static int
3967Page_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+--------------------------------------------------------------------------*/
3993static int
3994PN_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+--------------------------------------------------------------------------*/
4009static int
4010PN_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+--------------------------------------------------------------------------*/
4025static int
4026PN_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+--------------------------------------------------------------------------*/
4041static int
4042PN_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+--------------------------------------------------------------------------*/
4068static int
4069Data_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+--------------------------------------------------------------------------*/
4155static int
4156Data_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*/
4244typedef 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}
4249Binding_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* */
4267static 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+--------------------------------------------------------------------------*/
4362FORM_EXPORT(int)
4363form_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+--------------------------------------------------------------------------*/
4568FORM_EXPORT(int)
4569form_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+--------------------------------------------------------------------------*/
4749FORM_EXPORT(int)
4750set_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+--------------------------------------------------------------------------*/
4866FORM_EXPORT(char *)
4867field_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+--------------------------------------------------------------------------*/
4934FORM_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 */
5009