Main Page | Class Hierarchy | Class List | Directories | File List | Class Members | File Members

frm_driver.c

Go to the documentation of this file.
00001 /****************************************************************************
00002  * Copyright (c) 1998-2004,2005 Free Software Foundation, Inc.              *
00003  *                                                                          *
00004  * Permission is hereby granted, free of charge, to any person obtaining a  *
00005  * copy of this software and associated documentation files (the            *
00006  * "Software"), to deal in the Software without restriction, including      *
00007  * without limitation the rights to use, copy, modify, merge, publish,      *
00008  * distribute, distribute with modifications, sublicense, and/or sell       *
00009  * copies of the Software, and to permit persons to whom the Software is    *
00010  * furnished to do so, subject to the following conditions:                 *
00011  *                                                                          *
00012  * The above copyright notice and this permission notice shall be included  *
00013  * in all copies or substantial portions of the Software.                   *
00014  *                                                                          *
00015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
00016  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
00017  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
00018  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
00019  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
00020  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
00021  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
00022  *                                                                          *
00023  * Except as contained in this notice, the name(s) of the above copyright   *
00024  * holders shall not be used in advertising or otherwise to promote the     *
00025  * sale, use or other dealings in this Software without prior written       *
00026  * authorization.                                                           *
00027  ****************************************************************************/
00028 
00029 /****************************************************************************
00030  *   Author:  Juergen Pfeifer, 1995,1997                                    *
00031  ****************************************************************************/
00032 
00033 #include "form.priv.h"
00034 
00035 MODULE_ID("$Id: frm_driver.c,v 1.71 2005/10/01 19:42:40 tom Exp $")
00036 
00037 /*----------------------------------------------------------------------------
00038   This is the core module of the form library. It contains the majority
00039   of the driver routines as well as the form_driver function.
00040 
00041   Essentially this module is nearly the whole library. This is because
00042   all the functions in this module depends on some others in the module,
00043   so it makes no sense to split them into separate files because they
00044   will always be linked together. The only acceptable concern is turnaround
00045   time for this module, but now we have all Pentiums or RISCs, so what!
00046 
00047   The driver routines are grouped into nine generic categories:
00048 
00049    a)   Page Navigation            ( all functions prefixed by PN_ )
00050         The current page of the form is left and some new page is
00051         entered.
00052    b)   Inter-Field Navigation     ( all functions prefixed by FN_ )
00053         The current field of the form is left and some new field is
00054         entered.
00055    c)   Intra-Field Navigation     ( all functions prefixed by IFN_ )
00056         The current position in the current field is changed.
00057    d)   Vertical Scrolling         ( all functions prefixed by VSC_ )
00058         Essentially this is a specialization of Intra-Field navigation.
00059         It has to check for a multi-line field.
00060    e)   Horizontal Scrolling       ( all functions prefixed by HSC_ )
00061         Essentially this is a specialization of Intra-Field navigation.
00062         It has to check for a single-line field.
00063    f)   Field Editing              ( all functions prefixed by FE_ )
00064         The content of the current field is changed
00065    g)   Edit Mode requests         ( all functions prefixed by EM_ )
00066         Switching between insert and overlay mode
00067    h)   Field-Validation requests  ( all functions prefixed by FV_ )
00068         Perform verifications of the field.
00069    i)   Choice requests            ( all functions prefixed by CR_ )
00070         Requests to enumerate possible field values
00071   --------------------------------------------------------------------------*/
00072 
00073 /*----------------------------------------------------------------------------
00074   Some remarks on the placements of assert() macros :
00075   I use them only on "strategic" places, i.e. top level entries where
00076   I want to make sure that things are set correctly. Throughout subordinate
00077   routines I omit them mostly.
00078   --------------------------------------------------------------------------*/
00079 
00080 /*
00081 Some options that may effect compatibility in behavior to SVr4 forms,
00082 but they are here to allow a more intuitive and user friendly behavior of
00083 our form implementation. This doesn't affect the API, so we feel it is
00084 uncritical.
00085 
00086 The initial implementation tries to stay very close with the behavior
00087 of the original SVr4 implementation, although in some areas it is quite
00088 clear that this isn't the most appropriate way. As far as possible this
00089 sources will allow you to build a forms lib that behaves quite similar
00090 to SVr4, but now and in the future we will give you better options.
00091 Perhaps at some time we will make this configurable at runtime.
00092 */
00093 
00094 /* Implement a more user-friendly previous/next word behavior */
00095 #define FRIENDLY_PREV_NEXT_WORD (1)
00096 /* Fix the wrong behavior for forms with all fields inactive */
00097 #define FIX_FORM_INACTIVE_BUG (1)
00098 /* Allow dynamic field growth also when navigating past the end */
00099 #define GROW_IF_NAVIGATE (1)
00100 
00101 #if USE_WIDEC_SUPPORT
00102 #define myADDNSTR(w, s, n) wadd_wchnstr(w, s, n)
00103 #define myINSNSTR(w, s, n) wins_wchnstr(w, s, n)
00104 #define myINNSTR(w, s, n)  fix_wchnstr(w, s, n)
00105 #define myWCWIDTH(w, y, x) cell_width(w, y, x)
00106 #else
00107 #define myADDNSTR(w, s, n) waddnstr(w, s, n)
00108 #define myINSNSTR(w, s, n) winsnstr(w, s, n)
00109 #define myINNSTR(w, s, n)  winnstr(w, s, n)
00110 #define myWCWIDTH(w, y, x) 1
00111 #endif
00112 
00113 /*----------------------------------------------------------------------------
00114   Forward references to some internally used static functions
00115   --------------------------------------------------------------------------*/
00116 static int Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form);
00117 static int FN_Next_Field(FORM *form);
00118 static int FN_Previous_Field(FORM *form);
00119 static int FE_New_Line(FORM *);
00120 static int FE_Delete_Previous(FORM *);
00121 
00122 /*----------------------------------------------------------------------------
00123   Macro Definitions.
00124 
00125   Some Remarks on that: I use the convention to use UPPERCASE for constants
00126   defined by Macros. If I provide a macro as a kind of inline routine to
00127   provide some logic, I use my Upper_Lower case style.
00128   --------------------------------------------------------------------------*/
00129 
00130 /* Calculate the position of a single row in a field buffer */
00131 #define Position_Of_Row_In_Buffer(field,row) ((row)*(field)->dcols)
00132 
00133 /* Calculate start address for the fields buffer# N */
00134 #define Address_Of_Nth_Buffer(field,N) \
00135   ((field)->buf + (N)*(1+Buffer_Length(field)))
00136 
00137 /* Calculate the start address of the row in the fields specified buffer# N */
00138 #define Address_Of_Row_In_Nth_Buffer(field,N,row) \
00139   (Address_Of_Nth_Buffer(field,N) + Position_Of_Row_In_Buffer(field,row))
00140 
00141 /* Calculate the start address of the row in the fields primary buffer */
00142 #define Address_Of_Row_In_Buffer(field,row) \
00143   Address_Of_Row_In_Nth_Buffer(field,0,row)
00144 
00145 /* Calculate the start address of the row in the forms current field
00146    buffer# N */
00147 #define Address_Of_Current_Row_In_Nth_Buffer(form,N) \
00148    Address_Of_Row_In_Nth_Buffer((form)->current,N,(form)->currow)
00149 
00150 /* Calculate the start address of the row in the forms current field
00151    primary buffer */
00152 #define Address_Of_Current_Row_In_Buffer(form) \
00153    Address_Of_Current_Row_In_Nth_Buffer(form,0)
00154 
00155 /* Calculate the address of the cursor in the forms current field
00156    primary buffer */
00157 #define Address_Of_Current_Position_In_Nth_Buffer(form,N) \
00158    (Address_Of_Current_Row_In_Nth_Buffer(form,N) + (form)->curcol)
00159 
00160 /* Calculate the address of the cursor in the forms current field
00161    buffer# N */
00162 #define Address_Of_Current_Position_In_Buffer(form) \
00163   Address_Of_Current_Position_In_Nth_Buffer(form,0)
00164 
00165 /* Logic to decide whether or not a field is actually a field with
00166    vertical or horizontal scrolling */
00167 #define Is_Scroll_Field(field)          \
00168    (((field)->drows > (field)->rows) || \
00169     ((field)->dcols > (field)->cols))
00170 
00171 /* Logic to decide whether or not a field needs to have an individual window
00172    instead of a derived window because it contains invisible parts.
00173    This is true for non-public fields and for scrollable fields. */
00174 #define Has_Invisible_Parts(field)     \
00175   (!((field)->opts & O_PUBLIC)      || \
00176    Is_Scroll_Field(field))
00177 
00178 /* Logic to decide whether or not a field needs justification */
00179 #define Justification_Allowed(field)        \
00180    (((field)->just != NO_JUSTIFICATION)  && \
00181     (Single_Line_Field(field))           && \
00182     (((field)->dcols == (field)->cols)   && \
00183     ((field)->opts & O_STATIC))             )
00184 
00185 /* Logic to determine whether or not a dynamic field may still grow */
00186 #define Growable(field) ((field)->status & _MAY_GROW)
00187 
00188 /* Macro to set the attributes for a fields window */
00189 #define Set_Field_Window_Attributes(field,win) \
00190 (  wbkgdset((win),(chtype)((field)->pad | (field)->back)), \
00191    wattrset((win),(field)->fore) )
00192 
00193 /* Logic to decide whether or not a field really appears on the form */
00194 #define Field_Really_Appears(field)         \
00195   ((field->form)                          &&\
00196    (field->form->status & _POSTED)        &&\
00197    (field->opts & O_VISIBLE)              &&\
00198    (field->page == field->form->curpage))
00199 
00200 /* Logic to determine whether or not we are on the first position in the
00201    current field */
00202 #define First_Position_In_Current_Field(form) \
00203   (((form)->currow==0) && ((form)->curcol==0))
00204 
00205 #define Minimum(a,b) (((a)<=(b)) ? (a) : (b))
00206 #define Maximum(a,b) (((a)>=(b)) ? (a) : (b))
00207 
00208 /*----------------------------------------------------------------------------
00209   Useful constants
00210   --------------------------------------------------------------------------*/
00211 static FIELD_CELL myBLANK = BLANK;
00212 static FIELD_CELL myZEROS;
00213 
00214 #ifdef TRACE
00215 static void
00216 check_pos(FORM *form, int lineno)
00217 {
00218   int y, x;
00219 
00220   if (form && form->w)
00221     {
00222       getyx(form->w, y, x);
00223       if (y != form->currow || x != form->curcol)
00224         {
00225           T(("CHECKPOS %s@%d have position %d,%d vs want %d,%d",
00226              __FILE__, lineno,
00227              y, x,
00228              form->currow, form->curcol));
00229         }
00230     }
00231 }
00232 #define CHECKPOS(form) check_pos(form, __LINE__)
00233 #else
00234 #define CHECKPOS(form)          /* nothing */
00235 #endif
00236 
00237 /*----------------------------------------------------------------------------
00238   Wide-character special functions
00239   --------------------------------------------------------------------------*/
00240 #if USE_WIDEC_SUPPORT
00241 /* like winsnstr */
00242 static int
00243 wins_wchnstr(WINDOW *w, cchar_t *s, int n)
00244 {
00245   int code = ERR;
00246   int y, x;
00247 
00248   while (n-- > 0)
00249     {
00250       getyx(w, y, x);
00251       if ((code = wins_wch(w, s++)) != OK)
00252         break;
00253       if ((code = wmove(w, y, x + 1)) != OK)
00254         break;
00255     }
00256   return code;
00257 }
00258 
00259 /* win_wchnstr is inconsistent with winnstr, since it returns OK rather than
00260  * the number of items transferred.
00261  */
00262 static int
00263 fix_wchnstr(WINDOW *w, cchar_t *s, int n)
00264 {
00265   win_wchnstr(w, s, n);
00266   return n;
00267 }
00268 
00269 /*
00270  * Returns the column of the base of the given cell.
00271  */
00272 static int
00273 cell_base(WINDOW *win, int y, int x)
00274 {
00275   int result = x;
00276 
00277   while (LEGALYX(win, y, x))
00278     {
00279       cchar_t *data = &(win->_line[y].text[x]);
00280 
00281       if (isWidecBase(CHDEREF(data)) || !isWidecExt(CHDEREF(data)))
00282         {
00283           result = x;
00284           break;
00285         }
00286       --x;
00287     }
00288   return result;
00289 }
00290 
00291 /*
00292  * Returns the number of columns needed for the given cell in a window.
00293  */
00294 static int
00295 cell_width(WINDOW *win, int y, int x)
00296 {
00297   int result = 1;
00298 
00299   if (LEGALYX(win, y, x))
00300     {
00301       cchar_t *data = &(win->_line[y].text[x]);
00302 
00303       if (isWidecExt(CHDEREF(data)))
00304         {
00305           /* recur, providing the number of columns to the next character */
00306           result = cell_width(win, y, x - 1);
00307         }
00308       else
00309         {
00310           result = wcwidth(CharOf(CHDEREF(data)));
00311         }
00312     }
00313   return result;
00314 }
00315 
00316 /*
00317  * There is no wide-character function such as wdel_wch(), so we must find
00318  * all of the cells that comprise a multi-column character and delete them
00319  * one-by-one.
00320  */
00321 static void
00322 delete_char(FORM *form)
00323 {
00324   int cells = cell_width(form->w, form->currow, form->curcol);
00325 
00326   form->curcol = cell_base(form->w, form->currow, form->curcol);
00327   wmove(form->w, form->currow, form->curcol);
00328   while (cells-- > 0)
00329     {
00330       wdelch(form->w);
00331     }
00332 }
00333 #define DeleteChar(form) delete_char(form)
00334 #else
00335 #define DeleteChar(form) \
00336           wmove((form)->w, (form)->currow, (form)->curcol), \
00337           wdelch((form)->w)
00338 #endif
00339 
00340 /*---------------------------------------------------------------------------
00341 |   Facility      :  libnform
00342 |   Function      :  static char *Get_Start_Of_Data(char * buf, int blen)
00343 |
00344 |   Description   :  Return pointer to first non-blank position in buffer.
00345 |                    If buffer is empty return pointer to buffer itself.
00346 |
00347 |   Return Values :  Pointer to first non-blank position in buffer
00348 +--------------------------------------------------------------------------*/
00349 INLINE static FIELD_CELL *
00350 Get_Start_Of_Data(FIELD_CELL *buf, int blen)
00351 {
00352   FIELD_CELL *p = buf;
00353   FIELD_CELL *end = &buf[blen];
00354 
00355   assert(buf && blen >= 0);
00356   while ((p < end) && ISBLANK(*p))
00357     p++;
00358   return ((p == end) ? buf : p);
00359 }
00360 
00361 /*---------------------------------------------------------------------------
00362 |   Facility      :  libnform
00363 |   Function      :  static char *After_End_Of_Data(char * buf, int blen)
00364 |
00365 |   Description   :  Return pointer after last non-blank position in buffer.
00366 |                    If buffer is empty, return pointer to buffer itself.
00367 |
00368 |   Return Values :  Pointer to position after last non-blank position in
00369 |                    buffer.
00370 +--------------------------------------------------------------------------*/
00371 INLINE static FIELD_CELL *
00372 After_End_Of_Data(FIELD_CELL *buf, int blen)
00373 {
00374   FIELD_CELL *p = &buf[blen];
00375 
00376   assert(buf && blen >= 0);
00377   while ((p > buf) && ISBLANK(p[-1]))
00378     p--;
00379   return (p);
00380 }
00381 
00382 /*---------------------------------------------------------------------------
00383 |   Facility      :  libnform
00384 |   Function      :  static char *Get_First_Whitespace_Character(
00385 |                                     char * buf, int   blen)
00386 |
00387 |   Description   :  Position to the first whitespace character.
00388 |
00389 |   Return Values :  Pointer to first whitespace character in buffer.
00390 +--------------------------------------------------------------------------*/
00391 INLINE static FIELD_CELL *
00392 Get_First_Whitespace_Character(FIELD_CELL *buf, int blen)
00393 {
00394   FIELD_CELL *p = buf;
00395   FIELD_CELL *end = &p[blen];
00396 
00397   assert(buf && blen >= 0);
00398   while ((p < end) && !ISBLANK(*p))
00399     p++;
00400   return ((p == end) ? buf : p);
00401 }
00402 
00403 /*---------------------------------------------------------------------------
00404 |   Facility      :  libnform
00405 |   Function      :  static char *After_Last_Whitespace_Character(
00406 |                                     char * buf, int blen)
00407 |
00408 |   Description   :  Get the position after the last whitespace character.
00409 |
00410 |   Return Values :  Pointer to position after last whitespace character in
00411 |                    buffer.
00412 +--------------------------------------------------------------------------*/
00413 INLINE static FIELD_CELL *
00414 After_Last_Whitespace_Character(FIELD_CELL *buf, int blen)
00415 {
00416   FIELD_CELL *p = &buf[blen];
00417 
00418   assert(buf && blen >= 0);
00419   while ((p > buf) && !ISBLANK(p[-1]))
00420     p--;
00421   return (p);
00422 }
00423 
00424 /* Set this to 1 to use the div_t version. This is a good idea if your
00425    compiler has an intrinsic div() support. Unfortunately GNU-C has it
00426    not yet.
00427    N.B.: This only works if form->curcol follows immediately form->currow
00428          and both are of type int.
00429 */
00430 #define USE_DIV_T (0)
00431 
00432 /*---------------------------------------------------------------------------
00433 |   Facility      :  libnform
00434 |   Function      :  static void Adjust_Cursor_Position(
00435 |                                       FORM * form, const char * pos)
00436 |
00437 |   Description   :  Set current row and column of the form to values
00438 |                    corresponding to the buffer position.
00439 |
00440 |   Return Values :  -
00441 +--------------------------------------------------------------------------*/
00442 INLINE static void
00443 Adjust_Cursor_Position(FORM *form, const FIELD_CELL *pos)
00444 {
00445   FIELD *field;
00446   int idx;
00447 
00448   field = form->current;
00449   assert(pos >= field->buf && field->dcols > 0);
00450   idx = (int)(pos - field->buf);
00451 #if USE_DIV_T
00452   *((div_t *) & (form->currow)) = div(idx, field->dcols);
00453 #else
00454   form->currow = idx / field->dcols;
00455   form->curcol = idx - field->cols * form->currow;
00456 #endif
00457   if (field->drows < form->currow)
00458     form->currow = 0;
00459 }
00460 
00461 /*---------------------------------------------------------------------------
00462 |   Facility      :  libnform
00463 |   Function      :  static void Buffer_To_Window(
00464 |                                      const FIELD  * field,
00465 |                                      WINDOW * win)
00466 |
00467 |   Description   :  Copy the buffer to the window. If it is a multi-line
00468 |                    field, the buffer is split to the lines of the
00469 |                    window without any editing.
00470 |
00471 |   Return Values :  -
00472 +--------------------------------------------------------------------------*/
00473 static void
00474 Buffer_To_Window(const FIELD *field, WINDOW *win)
00475 {
00476   int width, height;
00477   int y, x;
00478   int len;
00479   int row;
00480   FIELD_CELL *pBuffer;
00481 
00482   assert(win && field);
00483 
00484   getyx(win, y, x);
00485   width = getmaxx(win);
00486   height = getmaxy(win);
00487 
00488   for (row = 0, pBuffer = field->buf;
00489        row < height;
00490        row++, pBuffer += width)
00491     {
00492       if ((len = (int)(After_End_Of_Data(pBuffer, width) - pBuffer)) > 0)
00493         {
00494           wmove(win, row, 0);
00495           myADDNSTR(win, pBuffer, len);
00496         }
00497     }
00498   wmove(win, y, x);
00499 }
00500 
00501 /*---------------------------------------------------------------------------
00502 |   Facility      :  libnform
00503 |   Function      :  static void Window_To_Buffer(
00504 |                                          WINDOW * win,
00505 |                                          FIELD  * field)
00506 |
00507 |   Description   :  Copy the content of the window into the buffer.
00508 |                    The multiple lines of a window are simply
00509 |                    concatenated into the buffer. Pad characters in
00510 |                    the window will be replaced by blanks in the buffer.
00511 |
00512 |   Return Values :  -
00513 +--------------------------------------------------------------------------*/
00514 static void
00515 Window_To_Buffer(WINDOW *win, FIELD *field)
00516 {
00517   int pad;
00518   int len = 0;
00519   FIELD_CELL *p;
00520   int row, height;
00521 
00522   assert(win && field && field->buf);
00523 
00524   pad = field->pad;
00525   p = field->buf;
00526   height = getmaxy(win);
00527 
00528   for (row = 0; (row < height) && (row < field->drows); row++)
00529     {
00530       wmove(win, row, 0);
00531       len += myINNSTR(win, p + len, field->dcols);
00532     }
00533   p[len] = myZEROS;
00534 
00535   /* replace visual padding character by blanks in buffer */
00536   if (pad != C_BLANK)
00537     {
00538       int i;
00539 
00540       for (i = 0; i < len; i++, p++)
00541         {
00542           if ((unsigned long)CharOf(*p) == ChCharOf(pad)
00543 #if USE_WIDEC_SUPPORT
00544               && p->chars[1] == 0
00545 #endif
00546               && AttrOf(*p) == ChAttrOf(pad))
00547             *p = myBLANK;
00548         }
00549     }
00550 }
00551 
00552 /*---------------------------------------------------------------------------
00553 |   Facility      :  libnform
00554 |   Function      :  static void Synchronize_Buffer(FORM * form)
00555 |
00556 |   Description   :  If there was a change, copy the content of the
00557 |                    window into the buffer, so the buffer is synchronized
00558 |                    with the windows content. We have to indicate that the
00559 |                    buffer needs validation due to the change.
00560 |
00561 |   Return Values :  -
00562 +--------------------------------------------------------------------------*/
00563 INLINE static void
00564 Synchronize_Buffer(FORM *form)
00565 {
00566   if (form->status & _WINDOW_MODIFIED)
00567     {
00568       form->status &= ~_WINDOW_MODIFIED;
00569       form->status |= _FCHECK_REQUIRED;
00570       Window_To_Buffer(form->w, form->current);
00571       wmove(form->w, form->currow, form->curcol);
00572     }
00573 }
00574 
00575 /*---------------------------------------------------------------------------
00576 |   Facility      :  libnform
00577 |   Function      :  static bool Field_Grown( FIELD *field, int amount)
00578 |
00579 |   Description   :  This function is called for growable dynamic fields
00580 |                    only. It has to increase the buffers and to allocate
00581 |                    a new window for this field.
00582 |                    This function has the side effect to set a new
00583 |                    field-buffer pointer, the dcols and drows values
00584 |                    as well as a new current Window for the field.
00585 |
00586 |   Return Values :  TRUE     - field successfully increased
00587 |                    FALSE    - there was some error
00588 +--------------------------------------------------------------------------*/
00589 static bool
00590 Field_Grown(FIELD *field, int amount)
00591 {
00592   bool result = FALSE;
00593 
00594   if (field && Growable(field))
00595     {
00596       bool single_line_field = Single_Line_Field(field);
00597       int old_buflen = Buffer_Length(field);
00598       int new_buflen;
00599       int old_dcols = field->dcols;
00600       int old_drows = field->drows;
00601       FIELD_CELL *oldbuf = field->buf;
00602       FIELD_CELL *newbuf;
00603 
00604       int growth;
00605       FORM *form = field->form;
00606       bool need_visual_update = ((form != (FORM *)0) &&
00607                                  (form->status & _POSTED) &&
00608                                  (form->current == field));
00609 
00610       if (need_visual_update)
00611         Synchronize_Buffer(form);
00612 
00613       if (single_line_field)
00614         {
00615           growth = field->cols * amount;
00616           if (field->maxgrow)
00617             growth = Minimum(field->maxgrow - field->dcols, growth);
00618           field->dcols += growth;
00619           if (field->dcols == field->maxgrow)
00620             field->status &= ~_MAY_GROW;
00621         }
00622       else
00623         {
00624           growth = (field->rows + field->nrow) * amount;
00625           if (field->maxgrow)
00626             growth = Minimum(field->maxgrow - field->drows, growth);
00627           field->drows += growth;
00628           if (field->drows == field->maxgrow)
00629             field->status &= ~_MAY_GROW;
00630         }
00631       /* drows, dcols changed, so we get really the new buffer length */
00632       new_buflen = Buffer_Length(field);
00633       newbuf = (FIELD_CELL *)malloc(Total_Buffer_Size(field));
00634       if (!newbuf)
00635         {
00636           /* restore to previous state */
00637           field->dcols = old_dcols;
00638           field->drows = old_drows;
00639           if ((single_line_field && (field->dcols != field->maxgrow)) ||
00640               (!single_line_field && (field->drows != field->maxgrow)))
00641             field->status |= _MAY_GROW;
00642         }
00643       else
00644         {
00645           /* Copy all the buffers.  This is the reason why we can't just use
00646            * realloc().
00647            */
00648           int i, j;
00649           FIELD_CELL *old_bp;
00650           FIELD_CELL *new_bp;
00651 
00652           result = TRUE;        /* allow sharing of recovery on failure */
00653 
00654           field->buf = newbuf;
00655           for (i = 0; i <= field->nbuf; i++)
00656             {
00657               new_bp = Address_Of_Nth_Buffer(field, i);
00658               old_bp = oldbuf + i * (1 + old_buflen);
00659               for (j = 0; j < old_buflen; ++j)
00660                 new_bp[j] = old_bp[j];
00661               while (j < new_buflen)
00662                 new_bp[j++] = myBLANK;
00663               new_bp[new_buflen] = myZEROS;
00664             }
00665 
00666 #if USE_WIDEC_SUPPORT
00667           if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR)
00668             result = FALSE;
00669 #endif
00670 
00671           if (need_visual_update && result)
00672             {
00673               WINDOW *new_window = newpad(field->drows, field->dcols);
00674 
00675               if (new_window != 0)
00676                 {
00677                   assert(form != (FORM *)0);
00678                   if (form->w)
00679                     delwin(form->w);
00680                   form->w = new_window;
00681                   Set_Field_Window_Attributes(field, form->w);
00682                   werase(form->w);
00683                   Buffer_To_Window(field, form->w);
00684                   untouchwin(form->w);
00685                   wmove(form->w, form->currow, form->curcol);
00686                 }
00687               else
00688                 result = FALSE;
00689             }
00690 
00691           if (result)
00692             {
00693               free(oldbuf);
00694               /* reflect changes in linked fields */
00695               if (field != field->link)
00696                 {
00697                   FIELD *linked_field;
00698 
00699                   for (linked_field = field->link;
00700                        linked_field != field;
00701                        linked_field = linked_field->link)
00702                     {
00703                       linked_field->buf = field->buf;
00704                       linked_field->drows = field->drows;
00705                       linked_field->dcols = field->dcols;
00706                     }
00707                 }
00708             }
00709           else
00710             {
00711               /* restore old state */
00712               field->dcols = old_dcols;
00713               field->drows = old_drows;
00714               field->buf = oldbuf;
00715               if ((single_line_field &&
00716                    (field->dcols != field->maxgrow)) ||
00717                   (!single_line_field &&
00718                    (field->drows != field->maxgrow)))
00719                 field->status |= _MAY_GROW;
00720               free(newbuf);
00721             }
00722         }
00723     }
00724   return (result);
00725 }
00726 
00727 /*---------------------------------------------------------------------------
00728 |   Facility      :  libnform
00729 |   Function      :  int _nc_Position_Form_Cursor(FORM * form)
00730 |
00731 |   Description   :  Position the cursor in the window for the current
00732 |                    field to be in sync. with the currow and curcol
00733 |                    values.
00734 |
00735 |   Return Values :  E_OK              - success
00736 |                    E_BAD_ARGUMENT    - invalid form pointer
00737 |                    E_SYSTEM_ERROR    - form has no current field or
00738 |                                        field-window
00739 +--------------------------------------------------------------------------*/
00740 NCURSES_EXPORT(int)
00741 _nc_Position_Form_Cursor(FORM *form)
00742 {
00743   FIELD *field;
00744   WINDOW *formwin;
00745 
00746   if (!form)
00747     return (E_BAD_ARGUMENT);
00748 
00749   if (!form->w || !form->current)
00750     return (E_SYSTEM_ERROR);
00751 
00752   field = form->current;
00753   formwin = Get_Form_Window(form);
00754 
00755   wmove(form->w, form->currow, form->curcol);
00756   if (Has_Invisible_Parts(field))
00757     {
00758       /* in this case fieldwin isn't derived from formwin, so we have
00759          to move the cursor in formwin by hand... */
00760       wmove(formwin,
00761             field->frow + form->currow - form->toprow,
00762             field->fcol + form->curcol - form->begincol);
00763       wcursyncup(formwin);
00764     }
00765   else
00766     wcursyncup(form->w);
00767   return (E_OK);
00768 }
00769 
00770 /*---------------------------------------------------------------------------
00771 |   Facility      :  libnform
00772 |   Function      :  int _nc_Refresh_Current_Field(FORM * form)
00773 |
00774 |   Description   :  Propagate the changes in the fields window to the
00775 |                    window of the form.
00776 |
00777 |   Return Values :  E_OK              - on success
00778 |                    E_BAD_ARGUMENT    - invalid form pointer
00779 |                    E_SYSTEM_ERROR    - general error
00780 +--------------------------------------------------------------------------*/
00781 NCURSES_EXPORT(int)
00782 _nc_Refresh_Current_Field(FORM *form)
00783 {
00784   WINDOW *formwin;
00785   FIELD *field;
00786 
00787   T((T_CALLED("_nc_Refresh_Current_Field(%p)"), form));
00788 
00789   if (!form)
00790     RETURN(E_BAD_ARGUMENT);
00791 
00792   if (!form->w || !form->current)
00793     RETURN(E_SYSTEM_ERROR);
00794 
00795   field = form->current;
00796   formwin = Get_Form_Window(form);
00797 
00798   if (field->opts & O_PUBLIC)
00799     {
00800       if (Is_Scroll_Field(field))
00801         {
00802           /* Again, in this case the fieldwin isn't derived from formwin,
00803              so we have to perform a copy operation. */
00804           if (Single_Line_Field(field))
00805             {
00806               /* horizontal scrolling */
00807               if (form->curcol < form->begincol)
00808                 form->begincol = form->curcol;
00809               else
00810                 {
00811                   if (form->curcol >= (form->begincol + field->cols))
00812                     form->begincol = form->curcol - field->cols + 1;
00813                 }
00814               copywin(form->w,
00815                       formwin,
00816                       0,
00817                       form->begincol,
00818                       field->frow,
00819                       field->fcol,
00820                       field->frow,
00821                       field->cols + field->fcol - 1,
00822                       0);
00823             }
00824           else
00825             {
00826               /* A multi-line, i.e. vertical scrolling field */
00827               int row_after_bottom, first_modified_row, first_unmodified_row;
00828 
00829               if (field->drows > field->rows)
00830                 {
00831                   row_after_bottom = form->toprow + field->rows;
00832                   if (form->currow < form->toprow)
00833                     {
00834                       form->toprow = form->currow;
00835                       field->status |= _NEWTOP;
00836                     }
00837                   if (form->currow >= row_after_bottom)
00838                     {
00839                       form->toprow = form->currow - field->rows + 1;
00840                       field->status |= _NEWTOP;
00841                     }
00842                   if (field->status & _NEWTOP)
00843                     {
00844                       /* means we have to copy whole range */
00845                       first_modified_row = form->toprow;
00846                       first_unmodified_row = first_modified_row + field->rows;
00847                       field->status &= ~_NEWTOP;
00848                     }
00849                   else
00850                     {
00851                       /* we try to optimize : finding the range of touched
00852                          lines */
00853                       first_modified_row = form->toprow;
00854                       while (first_modified_row < row_after_bottom)
00855                         {
00856                           if (is_linetouched(form->w, first_modified_row))
00857                             break;
00858                           first_modified_row++;
00859                         }
00860                       first_unmodified_row = first_modified_row;
00861                       while (first_unmodified_row < row_after_bottom)
00862                         {
00863                           if (!is_linetouched(form->w, first_unmodified_row))
00864                             break;
00865                           first_unmodified_row++;
00866                         }
00867                     }
00868                 }
00869               else
00870                 {
00871                   first_modified_row = form->toprow;
00872                   first_unmodified_row = first_modified_row + field->rows;
00873                 }
00874               if (first_unmodified_row != first_modified_row)
00875                 copywin(form->w,
00876                         formwin,
00877                         first_modified_row,
00878                         0,
00879                         field->frow + first_modified_row - form->toprow,
00880                         field->fcol,
00881                         field->frow + first_unmodified_row - form->toprow - 1,
00882                         field->cols + field->fcol - 1,
00883                         0);
00884             }
00885           wsyncup(formwin);
00886         }
00887       else
00888         {
00889           /* if the field-window is simply a derived window, i.e. contains no
00890            * invisible parts, the whole thing is trivial
00891            */
00892           wsyncup(form->w);
00893         }
00894     }
00895   untouchwin(form->w);
00896   returnCode(_nc_Position_Form_Cursor(form));
00897 }
00898 
00899 /*---------------------------------------------------------------------------
00900 |   Facility      :  libnform
00901 |   Function      :  static void Perform_Justification(
00902 |                                        FIELD  * field,
00903 |                                        WINDOW * win)
00904 |
00905 |   Description   :  Output field with requested justification
00906 |
00907 |   Return Values :  -
00908 +--------------------------------------------------------------------------*/
00909 static void
00910 Perform_Justification(FIELD *field, WINDOW *win)
00911 {
00912   FIELD_CELL *bp;
00913   int len;
00914   int col = 0;
00915 
00916   bp = Get_Start_Of_Data(field->buf, Buffer_Length(field));
00917   len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
00918 
00919   if (len > 0)
00920     {
00921       assert(win && (field->drows == 1) && (field->dcols == field->cols));
00922 
00923       switch (field->just)
00924         {
00925         case JUSTIFY_LEFT:
00926           break;
00927         case JUSTIFY_CENTER:
00928           col = (field->cols - len) / 2;
00929           break;
00930         case JUSTIFY_RIGHT:
00931           col = field->cols - len;
00932           break;
00933         default:
00934           break;
00935         }
00936 
00937       wmove(win, 0, col);
00938       myADDNSTR(win, bp, len);
00939     }
00940 }
00941 
00942 /*---------------------------------------------------------------------------
00943 |   Facility      :  libnform
00944 |   Function      :  static void Undo_Justification(
00945 |                                     FIELD  * field,
00946 |                                     WINDOW * win)
00947 |
00948 |   Description   :  Display field without any justification, i.e.
00949 |                    left justified
00950 |
00951 |   Return Values :  -
00952 +--------------------------------------------------------------------------*/
00953 static void
00954 Undo_Justification(FIELD *field, WINDOW *win)
00955 {
00956   FIELD_CELL *bp;
00957   int len;
00958 
00959   bp = Get_Start_Of_Data(field->buf, Buffer_Length(field));
00960   len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
00961 
00962   if (len > 0)
00963     {
00964       assert(win);
00965       wmove(win, 0, 0);
00966       myADDNSTR(win, bp, len);
00967     }
00968 }
00969 
00970 /*---------------------------------------------------------------------------
00971 |   Facility      :  libnform
00972 |   Function      :  static bool Check_Char(
00973 |                                           FIELDTYPE * typ,
00974 |                                           int ch,
00975 |                                           TypeArgument *argp)
00976 |
00977 |   Description   :  Perform a single character check for character ch
00978 |                    according to the fieldtype instance.
00979 |
00980 |   Return Values :  TRUE             - Character is valid
00981 |                    FALSE            - Character is invalid
00982 +--------------------------------------------------------------------------*/
00983 static bool
00984 Check_Char(FIELDTYPE *typ, int ch, TypeArgument *argp)
00985 {
00986   if (typ)
00987     {
00988       if (typ->status & _LINKED_TYPE)
00989         {
00990           assert(argp);
00991           return (
00992                    Check_Char(typ->left, ch, argp->left) ||
00993                    Check_Char(typ->right, ch, argp->right));
00994         }
00995       else
00996         {
00997           if (typ->ccheck)
00998             return typ->ccheck(ch, (void *)argp);
00999         }
01000     }
01001   return (!iscntrl(UChar(ch)) ? TRUE : FALSE);
01002 }
01003 
01004 /*---------------------------------------------------------------------------
01005 |   Facility      :  libnform
01006 |   Function      :  static int Display_Or_Erase_Field(
01007 |                                           FIELD * field,
01008 |                                           bool bEraseFlag)
01009 |
01010 |   Description   :  Create a subwindow for the field and display the
01011 |                    buffer contents (apply justification if required)
01012 |                    or simply erase the field.
01013 |
01014 |   Return Values :  E_OK           - on success
01015 |                    E_SYSTEM_ERROR - some error (typical no memory)
01016 +--------------------------------------------------------------------------*/
01017 static int
01018 Display_Or_Erase_Field(FIELD *field, bool bEraseFlag)
01019 {
01020   WINDOW *win;
01021   WINDOW *fwin;
01022 
01023   if (!field)
01024     return E_SYSTEM_ERROR;
01025 
01026   fwin = Get_Form_Window(field->form);
01027   win = derwin(fwin,
01028                field->rows, field->cols, field->frow, field->fcol);
01029 
01030   if (!win)
01031     return E_SYSTEM_ERROR;
01032   else
01033     {
01034       if (field->opts & O_VISIBLE)
01035         Set_Field_Window_Attributes(field, win);
01036       else
01037         wattrset(win, getattrs(fwin));
01038       werase(win);
01039     }
01040 
01041   if (!bEraseFlag)
01042     {
01043       if (field->opts & O_PUBLIC)
01044         {
01045           if (Justification_Allowed(field))
01046             Perform_Justification(field, win);
01047           else
01048             Buffer_To_Window(field, win);
01049         }
01050       field->status &= ~_NEWTOP;
01051     }
01052   wsyncup(win);
01053   delwin(win);
01054   return E_OK;
01055 }
01056 
01057 /* Macros to preset the bEraseFlag */
01058 #define Display_Field(field) Display_Or_Erase_Field(field,FALSE)
01059 #define Erase_Field(field)   Display_Or_Erase_Field(field,TRUE)
01060 
01061 /*---------------------------------------------------------------------------
01062 |   Facility      :  libnform
01063 |   Function      :  static int Synchronize_Field(FIELD * field)
01064 |
01065 |   Description   :  Synchronize the windows content with the value in
01066 |                    the buffer.
01067 |
01068 |   Return Values :  E_OK                - success
01069 |                    E_BAD_ARGUMENT      - invalid field pointer
01070 |                    E_SYSTEM_ERROR      - some severe basic error
01071 +--------------------------------------------------------------------------*/
01072 static int
01073 Synchronize_Field(FIELD *field)
01074 {
01075   FORM *form;
01076   int res = E_OK;
01077 
01078   if (!field)
01079     return (E_BAD_ARGUMENT);
01080 
01081   if (((form = field->form) != (FORM *)0)
01082       && Field_Really_Appears(field))
01083     {
01084       if (field == form->current)
01085         {
01086           form->currow = form->curcol = form->toprow = form->begincol = 0;
01087           werase(form->w);
01088 
01089           if ((field->opts & O_PUBLIC) && Justification_Allowed(field))
01090             Undo_Justification(field, form->w);
01091           else
01092             Buffer_To_Window(field, form->w);
01093 
01094           field->status |= _NEWTOP;
01095           res = _nc_Refresh_Current_Field(form);
01096         }
01097       else
01098         res = Display_Field(field);
01099     }
01100   field->status |= _CHANGED;
01101   return (res);
01102 }
01103 
01104 /*---------------------------------------------------------------------------
01105 |   Facility      :  libnform
01106 |   Function      :  static int Synchronize_Linked_Fields(FIELD * field)
01107 |
01108 |   Description   :  Propagate the Synchronize_Field function to all linked
01109 |                    fields. The first error that occurs in the sequence
01110 |                    of updates is the return value.
01111 |
01112 |   Return Values :  E_OK                - success
01113 |                    E_BAD_ARGUMENT      - invalid field pointer
01114 |                    E_SYSTEM_ERROR      - some severe basic error
01115 +--------------------------------------------------------------------------*/
01116 static int
01117 Synchronize_Linked_Fields(FIELD *field)
01118 {
01119   FIELD *linked_field;
01120   int res = E_OK;
01121   int syncres;
01122 
01123   if (!field)
01124     return (E_BAD_ARGUMENT);
01125 
01126   if (!field->link)
01127     return (E_SYSTEM_ERROR);
01128 
01129   for (linked_field = field->link;
01130        linked_field != field;
01131        linked_field = linked_field->link)
01132     {
01133       if (((syncres = Synchronize_Field(linked_field)) != E_OK) &&
01134           (res == E_OK))
01135         res = syncres;
01136     }
01137   return (res);
01138 }
01139 
01140 /*---------------------------------------------------------------------------
01141 |   Facility      :  libnform
01142 |   Function      :  int _nc_Synchronize_Attributes(FIELD * field)
01143 |
01144 |   Description   :  If a fields visual attributes have changed, this
01145 |                    routine is called to propagate those changes to the
01146 |                    screen.
01147 |
01148 |   Return Values :  E_OK             - success
01149 |                    E_BAD_ARGUMENT   - invalid field pointer
01150 |                    E_SYSTEM_ERROR   - some severe basic error
01151 +--------------------------------------------------------------------------*/
01152 NCURSES_EXPORT(int)
01153 _nc_Synchronize_Attributes(FIELD *field)
01154 {
01155   FORM *form;
01156   int res = E_OK;
01157   WINDOW *formwin;
01158 
01159   T((T_CALLED("_nc_Synchronize_Attributes(%p)"), field));
01160 
01161   if (!field)
01162     returnCode(E_BAD_ARGUMENT);
01163 
01164   CHECKPOS(field->form);
01165   if (((form = field->form) != (FORM *)0)
01166       && Field_Really_Appears(field))
01167     {
01168       if (form->current == field)
01169         {
01170           Synchronize_Buffer(form);
01171           Set_Field_Window_Attributes(field, form->w);
01172           werase(form->w);
01173           wmove(form->w, form->currow, form->curcol);
01174 
01175           if (field->opts & O_PUBLIC)
01176             {
01177               if (Justification_Allowed(field))
01178                 Undo_Justification(field, form->w);
01179               else
01180                 Buffer_To_Window(field, form->w);
01181             }
01182           else
01183             {
01184               formwin = Get_Form_Window(form);
01185               copywin(form->w, formwin,
01186                       0, 0,
01187                       field->frow, field->fcol,
01188                       field->rows - 1, field->cols - 1, 0);
01189               wsyncup(formwin);
01190               Buffer_To_Window(field, form->w);
01191               field->status |= _NEWTOP;         /* fake refresh to paint all */
01192               _nc_Refresh_Current_Field(form);
01193             }
01194         }
01195       else
01196         {
01197           res = Display_Field(field);
01198         }
01199     }
01200   CHECKPOS(form);
01201   returnCode(res);
01202 }
01203 
01204 /*---------------------------------------------------------------------------
01205 |   Facility      :  libnform
01206 |   Function      :  int _nc_Synchronize_Options(FIELD * field,
01207 |                                                Field_Options newopts)
01208 |
01209 |   Description   :  If a fields options have changed, this routine is
01210 |                    called to propagate these changes to the screen and
01211 |                    to really change the behavior of the field.
01212 |
01213 |   Return Values :  E_OK                - success
01214 |                    E_BAD_ARGUMENT      - invalid field pointer
01215 |                    E_SYSTEM_ERROR      - some severe basic error
01216 +--------------------------------------------------------------------------*/
01217 NCURSES_EXPORT(int)
01218 _nc_Synchronize_Options(FIELD *field, Field_Options newopts)
01219 {
01220   Field_Options oldopts;
01221   Field_Options changed_opts;
01222   FORM *form;
01223   int res = E_OK;
01224 
01225   T((T_CALLED("_nc_Synchronize_Options(%p,%#x)"), field, newopts));
01226 
01227   if (!field)
01228     returnCode(E_BAD_ARGUMENT);
01229 
01230   oldopts = field->opts;
01231   changed_opts = oldopts ^ newopts;
01232   field->opts = newopts;
01233   form = field->form;
01234 
01235   if (form)
01236     {
01237       if (form->current == field)
01238         {
01239           field->opts = oldopts;
01240           returnCode(E_CURRENT);
01241         }
01242 
01243       if (form->status & _POSTED)
01244         {
01245           if ((form->curpage == field->page))
01246             {
01247               if (changed_opts & O_VISIBLE)
01248                 {
01249                   if (newopts & O_VISIBLE)
01250                     res = Display_Field(field);
01251                   else
01252                     res = Erase_Field(field);
01253                 }
01254               else
01255                 {
01256                   if ((changed_opts & O_PUBLIC) &&
01257                       (newopts & O_VISIBLE))
01258                     res = Display_Field(field);
01259                 }
01260             }
01261         }
01262     }
01263 
01264   if (changed_opts & O_STATIC)
01265     {
01266       bool single_line_field = Single_Line_Field(field);
01267       int res2 = E_OK;
01268 
01269       if (newopts & O_STATIC)
01270         {
01271           /* the field becomes now static */
01272           fie