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