/****************************************************************************1* Copyright (c) 1998 Free Software Foundation, Inc. *2* *3* Permission is hereby granted, free of charge, to any person obtaining a *4* copy of this software and associated documentation files (the *5* "Software"), to deal in the Software without restriction, including *6* without limitation the rights to use, copy, modify, merge, publish, *7* distribute, distribute with modifications, sublicense, and/or sell *8* copies of the Software, and to permit persons to whom the Software is *9* furnished to do so, subject to the following conditions: *10* *11* The above copyright notice and this permission notice shall be included *12* in all copies or substantial portions of the Software. *13* *14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *15* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *16* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *17* IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *18* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *19* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *20* THE USE OR OTHER DEALINGS IN THE SOFTWARE. *21* *22* Except as contained in this notice, the name(s) of the above copyright *23* holders shall not be used in advertising or otherwise to promote the *24* sale, use or other dealings in this Software without prior written *25* authorization. *26****************************************************************************/2728/****************************************************************************29* Author: Juergen Pfeifer <[email protected]> 1995,1997 *30****************************************************************************/31#include "form.priv.h"3233/* AIX seems to define this */34#undef lines35#undef columns3637MODULE_ID("$Id$")3839/* These declarations are missing from curses.h on some platforms. */40extern int winnstr(WINDOW *, char *, int);41#if defined(__DECCXX_VER) || (defined(__GNUC__) && defined(__osf__))42extern int waddnstr(WINDOW *,const char *const,int);43extern void wbkgdset(WINDOW *,chtype);44#ifndef untouchwin45extern int untouchwin(WINDOW *);46#endif47extern void wcursyncup(WINDOW *);48extern int copywin(const WINDOW*,WINDOW*,int,int,int,int,int,int,int);49extern bool is_linetouched(WINDOW *,int);50extern void wsyncup(WINDOW *);51extern WINDOW *derwin(WINDOW *,int,int,int,int);52extern int winsnstr(WINDOW *, const char *,int);53extern int winsdelln(WINDOW *,int);54#endif5556/*----------------------------------------------------------------------------57This is the core module of the form library. It contains the majority58of the driver routines as well as the form_driver function.5960Essentially this module is nearly the whole library. This is because61all the functions in this module depends on some others in the module,62so it makes no sense to split them into separate files because they63will always be linked together. The only acceptable concern is turnaround64time for this module, but now we have all Pentiums or Riscs, so what!6566The driver routines are grouped into nine generic categories:6768a) Page Navigation ( all functions prefixed by PN_ )69The current page of the form is left and some new page is70entered.71b) Inter-Field Navigation ( all functions prefixed by FN_ )72The current field of the form is left and some new field is73entered.74c) Intra-Field Navigation ( all functions prefixed by IFN_ )75The current position in the current field is changed.76d) Vertical Scrolling ( all functions prefixed by VSC_ )77Esseantially this is a specialization of Intra-Field navigation.78It has to check for a multi-line field.79e) Horizontal Scrolling ( all functions prefixed by HSC_ )80Esseantially this is a specialization of Intra-Field navigation.81It has to check for a single-line field.82f) Field Editing ( all functions prefixed by FE_ )83The content of the current field is changed84g) Edit Mode requests ( all functions prefixed by EM_ )85Switching between insert and overlay mode86h) Field-Validation requests ( all functions prefixed by FV_ )87Perform verifications of the field.88i) Choice requests ( all functions prefixed by CR_ )89Requests to enumerate possible field values90--------------------------------------------------------------------------*/9192/*----------------------------------------------------------------------------93Some remarks on the placements of assert() macros :94I use them only on "strategic" places, i.e. top level entries where95I want to make sure that things are set correctly. Throughout subordinate96routines I omit them mostly.97--------------------------------------------------------------------------*/9899/*100Some options that may effect compatibility in behavior to SVr4 forms,101but they are here to allow a more intuitive and user friendly behaviour of102our form implementation. This doesn't affect the API, so we feel it is103uncritical.104105The initial implementation tries to stay very close with the behaviour106of the original SVr4 implementation, although in some areas it is quite107clear that this isn't the most appropriate way. As far as possible this108sources will allow you to build a forms lib that behaves quite similar109to SVr4, but now and in the future we will give you better options.110Perhaps at some time we will make this configurable at runtime.111*/112113/* Implement a more user-friendly previous/next word behaviour */114#define FRIENDLY_PREV_NEXT_WORD (1)115/* Fix the wrong behaviour for forms with all fields inactive */116#define FIX_FORM_INACTIVE_BUG (1)117/* Allow dynamic field growth also when navigating past the end */118#define GROW_IF_NAVIGATE (1)119120/*----------------------------------------------------------------------------121Forward references to some internally used static functions122--------------------------------------------------------------------------*/123static int Inter_Field_Navigation ( int (* const fct) (FORM *), FORM * form );124static int FN_Next_Field (FORM * form);125static int FN_Previous_Field (FORM * form);126static int FE_New_Line(FORM *);127static int FE_Delete_Previous(FORM *);128129/*----------------------------------------------------------------------------130Macro Definitions.131132Some Remarks on that: I use the convention to use UPPERCASE for constants133defined by Macros. If I provide a macro as a kind of inline routine to134provide some logic, I use my Upper_Lower case style.135--------------------------------------------------------------------------*/136137/* Calculate the position of a single row in a field buffer */138#define Position_Of_Row_In_Buffer(field,row) ((row)*(field)->dcols)139140/* Calculate start address for the fields buffer# N */141#define Address_Of_Nth_Buffer(field,N) \142((field)->buf + (N)*(1+Buffer_Length(field)))143144/* Calculate the start address of the row in the fields specified buffer# N */145#define Address_Of_Row_In_Nth_Buffer(field,N,row) \146(Address_Of_Nth_Buffer(field,N) + Position_Of_Row_In_Buffer(field,row))147148/* Calculate the start address of the row in the fields primary buffer */149#define Address_Of_Row_In_Buffer(field,row) \150Address_Of_Row_In_Nth_Buffer(field,0,row)151152/* Calculate the start address of the row in the forms current field153buffer# N */154#define Address_Of_Current_Row_In_Nth_Buffer(form,N) \155Address_Of_Row_In_Nth_Buffer((form)->current,N,(form)->currow)156157/* Calculate the start address of the row in the forms current field158primary buffer */159#define Address_Of_Current_Row_In_Buffer(form) \160Address_Of_Current_Row_In_Nth_Buffer(form,0)161162/* Calculate the address of the cursor in the forms current field163primary buffer */164#define Address_Of_Current_Position_In_Nth_Buffer(form,N) \165(Address_Of_Current_Row_In_Nth_Buffer(form,N) + (form)->curcol)166167/* Calculate the address of the cursor in the forms current field168buffer# N */169#define Address_Of_Current_Position_In_Buffer(form) \170Address_Of_Current_Position_In_Nth_Buffer(form,0)171172/* Logic to decide whether or not a field is actually a field with173vertical or horizontal scrolling */174#define Is_Scroll_Field(field) \175(((field)->drows > (field)->rows) || \176((field)->dcols > (field)->cols))177178/* Logic to decide whether or not a field needs to have an individual window179instead of a derived window because it contains invisible parts.180This is true for non-public fields and for scrollable fields. */181#define Has_Invisible_Parts(field) \182(!((field)->opts & O_PUBLIC) || \183Is_Scroll_Field(field))184185/* Logic to decide whether or not a field needs justification */186#define Justification_Allowed(field) \187(((field)->just != NO_JUSTIFICATION) && \188(Single_Line_Field(field)) && \189(((field)->dcols == (field)->cols) && \190((field)->opts & O_STATIC)) )191192/* Logic to determine whether or not a dynamic field may still grow */193#define Growable(field) ((field)->status & _MAY_GROW)194195/* Macro to set the attributes for a fields window */196#define Set_Field_Window_Attributes(field,win) \197( wbkgdset((win),(chtype)((field)->pad | (field)->back)), \198wattrset((win),(field)->fore) )199200/* Logic to decide whether or not a field really appears on the form */201#define Field_Really_Appears(field) \202((field->form) &&\203(field->form->status & _POSTED) &&\204(field->opts & O_VISIBLE) &&\205(field->page == field->form->curpage))206207/* Logic to determine whether or not we are on the first position in the208current field */209#define First_Position_In_Current_Field(form) \210(((form)->currow==0) && ((form)->curcol==0))211212213#define Minimum(a,b) (((a)<=(b)) ? (a) : (b))214#define Maximum(a,b) (((a)>=(b)) ? (a) : (b))215216/*---------------------------------------------------------------------------217| Facility : libnform218| Function : static char *Get_Start_Of_Data(char * buf, int blen)219|220| Description : Return pointer to first non-blank position in buffer.221| If buffer is empty return pointer to buffer itself.222|223| Return Values : Pointer to first non-blank position in buffer224+--------------------------------------------------------------------------*/225INLINE static char *Get_Start_Of_Data(char * buf, int blen)226{227char *p = buf;228char *end = &buf[blen];229230assert(buf && blen>=0);231while( (p < end) && is_blank(*p) )232p++;233return( (p==end) ? buf : p );234}235236/*---------------------------------------------------------------------------237| Facility : libnform238| Function : static char *After_End_Of_Data(char * buf, int blen)239|240| Description : Return pointer after last non-blank position in buffer.241| If buffer is empty, return pointer to buffer itself.242|243| Return Values : Pointer to position after last non-blank position in244| buffer.245+--------------------------------------------------------------------------*/246INLINE static char *After_End_Of_Data(char * buf,int blen)247{248char *p = &buf[blen];249250assert(buf && blen>=0);251while( (p>buf) && is_blank(p[-1]) )252p--;253return( p );254}255256/*---------------------------------------------------------------------------257| Facility : libnform258| Function : static char *Get_First_Whitespace_Character(259| char * buf, int blen)260|261| Description : Position to the first whitespace character.262|263| Return Values : Pointer to first whitespace character in buffer.264+--------------------------------------------------------------------------*/265INLINE static char *Get_First_Whitespace_Character(char * buf, int blen)266{267char *p = buf;268char *end = &p[blen];269270assert(buf && blen>=0);271while( (p < end) && !is_blank(*p))272p++;273return( (p==end) ? buf : p );274}275276/*---------------------------------------------------------------------------277| Facility : libnform278| Function : static char *After_Last_Whitespace_Character(279| char * buf, int blen)280|281| Description : Get the position after the last whitespace character.282|283| Return Values : Pointer to position after last whitespace character in284| buffer.285+--------------------------------------------------------------------------*/286INLINE static char *After_Last_Whitespace_Character(char * buf, int blen)287{288char *p = &buf[blen];289290assert(buf && blen>=0);291while( (p>buf) && !is_blank(p[-1]) )292p--;293return( p );294}295296/* Set this to 1 to use the div_t version. This is a good idea if your297compiler has an intrinsic div() support. Unfortunately GNU-C has it298not yet.299N.B.: This only works if form->curcol follows immediately form->currow300and both are of type int.301*/302#define USE_DIV_T (0)303304/*---------------------------------------------------------------------------305| Facility : libnform306| Function : static void Adjust_Cursor_Position(307| FORM * form, const char * pos)308|309| Description : Set current row and column of the form to values310| corresponding to the buffer position.311|312| Return Values : -313+--------------------------------------------------------------------------*/314INLINE static void Adjust_Cursor_Position(FORM * form, const char * pos)315{316FIELD *field;317int idx;318319field = form->current;320assert( pos >= field->buf && field->dcols > 0);321idx = (int)( pos - field->buf );322#if USE_DIV_T323*((div_t *)&(form->currow)) = div(idx,field->dcols);324#else325form->currow = idx / field->dcols;326form->curcol = idx - field->cols * form->currow;327#endif328if ( field->drows < form->currow )329form->currow = 0;330}331332/*---------------------------------------------------------------------------333| Facility : libnform334| Function : static void Buffer_To_Window(335| const FIELD * field,336| WINDOW * win)337|338| Description : Copy the buffer to the window. If its a multiline339| field, the buffer is split to the lines of the340| window without any editing.341|342| Return Values : -343+--------------------------------------------------------------------------*/344static void Buffer_To_Window(const FIELD * field, WINDOW * win)345{346int width, height;347int len;348int row;349char *pBuffer;350351assert(win && field);352353getmaxyx(win, height, width);354355for(row=0, pBuffer=field->buf;356row < height;357row++, pBuffer += width )358{359if ((len = (int)( After_End_Of_Data( pBuffer, width ) - pBuffer )) > 0)360{361wmove( win, row, 0 );362waddnstr( win, pBuffer, len );363}364}365}366367/*---------------------------------------------------------------------------368| Facility : libnform369| Function : static void Window_To_Buffer(370| WINDOW * win,371| FIELD * field)372|373| Description : Copy the content of the window into the buffer.374| The multiple lines of a window are simply375| concatenated into the buffer. Pad characters in376| the window will be replaced by blanks in the buffer.377|378| Return Values : -379+--------------------------------------------------------------------------*/380static void Window_To_Buffer(WINDOW * win, FIELD * field)381{382int pad;383int len = 0;384char *p;385int row, height, width;386387assert(win && field && field->buf );388389pad = field->pad;390p = field->buf;391getmaxyx(win, height, width);392393for(row=0; (row < height) && (row < field->drows); row++ )394{395wmove( win, row, 0 );396len += winnstr( win, p+len, field->dcols );397}398p[len] = '\0';399400/* replace visual padding character by blanks in buffer */401if (pad != C_BLANK)402{403int i;404for(i=0; i<len; i++, p++)405{406if (*p==pad)407*p = C_BLANK;408}409}410}411412/*---------------------------------------------------------------------------413| Facility : libnform414| Function : static void Synchronize_Buffer(FORM * form)415|416| Description : If there was a change, copy the content of the417| window into the buffer, so the buffer is synchronized418| with the windows content. We have to indicate that the419| buffer needs validation due to the change.420|421| Return Values : -422+--------------------------------------------------------------------------*/423INLINE static void Synchronize_Buffer(FORM * form)424{425if (form->status & _WINDOW_MODIFIED)426{427form->status &= ~_WINDOW_MODIFIED;428form->status |= _FCHECK_REQUIRED;429Window_To_Buffer(form->w,form->current);430wmove(form->w,form->currow,form->curcol);431}432}433434/*---------------------------------------------------------------------------435| Facility : libnform436| Function : static bool Field_Grown( FIELD *field, int amount)437|438| Description : This function is called for growable dynamic fields439| only. It has to increase the buffers and to allocate440| a new window for this field.441| This function has the side effect to set a new442| field-buffer pointer, the dcols and drows values443| as well as a new current Window for the field.444|445| Return Values : TRUE - field successfully increased446| FALSE - there was some error447+--------------------------------------------------------------------------*/448static bool Field_Grown(FIELD * field, int amount)449{450bool result = FALSE;451452if (field && Growable(field))453{454bool single_line_field = Single_Line_Field(field);455int old_buflen = Buffer_Length(field);456int new_buflen;457int old_dcols = field->dcols;458int old_drows = field->drows;459char *oldbuf = field->buf;460char *newbuf;461462int growth;463FORM *form = field->form;464bool need_visual_update = ((form != (FORM *)0) &&465(form->status & _POSTED) &&466(form->current==field));467468if (need_visual_update)469Synchronize_Buffer(form);470471if (single_line_field)472{473growth = field->cols * amount;474if (field->maxgrow)475growth = Minimum(field->maxgrow - field->dcols,growth);476field->dcols += growth;477if (field->dcols == field->maxgrow)478field->status &= ~_MAY_GROW;479}480else481{482growth = (field->rows + field->nrow) * amount;483if (field->maxgrow)484growth = Minimum(field->maxgrow - field->drows,growth);485field->drows += growth;486if (field->drows == field->maxgrow)487field->status &= ~_MAY_GROW;488}489/* drows, dcols changed, so we get really the new buffer length */490new_buflen = Buffer_Length(field);491newbuf=(char *)malloc((size_t)Total_Buffer_Size(field));492if (!newbuf)493{ /* restore to previous state */494field->dcols = old_dcols;495field->drows = old_drows;496if (( single_line_field && (field->dcols!=field->maxgrow)) ||497(!single_line_field && (field->drows!=field->maxgrow)))498field->status |= _MAY_GROW;499return FALSE;500}501else502{ /* Copy all the buffers. This is the reason why we can't503just use realloc().504*/505int i;506char *old_bp;507char *new_bp;508509field->buf = newbuf;510for(i=0;i<=field->nbuf;i++)511{512new_bp = Address_Of_Nth_Buffer(field,i);513old_bp = oldbuf + i*(1+old_buflen);514memcpy(new_bp,old_bp,(size_t)old_buflen);515if (new_buflen > old_buflen)516memset(new_bp + old_buflen,C_BLANK,517(size_t)(new_buflen - old_buflen));518*(new_bp + new_buflen) = '\0';519}520521if (need_visual_update)522{523WINDOW *new_window = newpad(field->drows,field->dcols);524if (!new_window)525{ /* restore old state */526field->dcols = old_dcols;527field->drows = old_drows;528field->buf = oldbuf;529if (( single_line_field &&530(field->dcols!=field->maxgrow)) ||531(!single_line_field &&532(field->drows!=field->maxgrow)))533field->status |= _MAY_GROW;534free( newbuf );535return FALSE;536}537assert(form!=(FORM *)0);538delwin(form->w);539form->w = new_window;540Set_Field_Window_Attributes(field,form->w);541werase(form->w);542Buffer_To_Window(field,form->w);543untouchwin(form->w);544wmove(form->w,form->currow,form->curcol);545}546547free(oldbuf);548/* reflect changes in linked fields */549if (field != field->link)550{551FIELD *linked_field;552for(linked_field = field->link;553linked_field!= field;554linked_field = linked_field->link)555{556linked_field->buf = field->buf;557linked_field->drows = field->drows;558linked_field->dcols = field->dcols;559}560}561result = TRUE;562}563}564return(result);565}566567/*---------------------------------------------------------------------------568| Facility : libnform569| Function : int _nc_Position_Form_Cursor(FORM * form)570|571| Description : Position the cursor in the window for the current572| field to be in sync. with the currow and curcol573| values.574|575| Return Values : E_OK - success576| E_BAD_ARGUMENT - invalid form pointer577| E_SYSTEM_ERROR - form has no current field or578| field-window579+--------------------------------------------------------------------------*/580int581_nc_Position_Form_Cursor(FORM * form)582{583FIELD *field;584WINDOW *formwin;585586if (!form)587return(E_BAD_ARGUMENT);588589if (!form->w || !form->current)590return(E_SYSTEM_ERROR);591592field = form->current;593formwin = Get_Form_Window(form);594595wmove( form->w, form->currow, form->curcol );596if ( Has_Invisible_Parts(field) )597{598/* in this case fieldwin isn't derived from formwin, so we have599to move the cursor in formwin by hand... */600wmove(formwin,601field->frow + form->currow - form->toprow,602field->fcol + form->curcol - form->begincol);603wcursyncup(formwin);604}605else606wcursyncup(form->w);607return(E_OK);608}609610/*---------------------------------------------------------------------------611| Facility : libnform612| Function : int _nc_Refresh_Current_Field(FORM * form)613|614| Description : Propagate the changes in the fields window to the615| window of the form.616|617| Return Values : E_OK - on success618| E_BAD_ARGUMENT - invalid form pointer619| E_SYSTEM_ERROR - general error620+--------------------------------------------------------------------------*/621int622_nc_Refresh_Current_Field(FORM * form)623{624WINDOW *formwin;625FIELD *field;626627if (!form)628RETURN(E_BAD_ARGUMENT);629630if (!form->w || !form->current)631RETURN(E_SYSTEM_ERROR);632633field = form->current;634formwin = Get_Form_Window(form);635636if (field->opts & O_PUBLIC)637{638if (Is_Scroll_Field(field))639{640/* Again, in this case the fieldwin isn't derived from formwin,641so we have to perform a copy operation. */642if (Single_Line_Field(field))643{ /* horizontal scrolling */644if (form->curcol < form->begincol)645form->begincol = form->curcol;646else647{648if (form->curcol >= (form->begincol + field->cols))649form->begincol = form->curcol - field->cols + 1;650}651copywin(form->w,652formwin,6530,654form->begincol,655field->frow,656field->fcol,657field->frow,658field->cols + field->fcol - 1,6590);660}661else662{ /* A multiline, i.e. vertical scrolling field */663int row_after_bottom,first_modified_row,first_unmodified_row;664665if (field->drows > field->rows)666{667row_after_bottom = form->toprow + field->rows;668if (form->currow < form->toprow)669{670form->toprow = form->currow;671field->status |= _NEWTOP;672}673if (form->currow >= row_after_bottom)674{675form->toprow = form->currow - field->rows + 1;676field->status |= _NEWTOP;677}678if (field->status & _NEWTOP)679{ /* means we have to copy whole range */680first_modified_row = form->toprow;681first_unmodified_row = first_modified_row + field->rows;682field->status &= ~_NEWTOP;683}684else685{ /* we try to optimize : finding the range of touched686lines */687first_modified_row = form->toprow;688while(first_modified_row < row_after_bottom)689{690if (is_linetouched(form->w,first_modified_row))691break;692first_modified_row++;693}694first_unmodified_row = first_modified_row;695while(first_unmodified_row < row_after_bottom)696{697if (!is_linetouched(form->w,first_unmodified_row))698break;699first_unmodified_row++;700}701}702}703else704{705first_modified_row = form->toprow;706first_unmodified_row = first_modified_row + field->rows;707}708if (first_unmodified_row != first_modified_row)709copywin(form->w,710formwin,711first_modified_row,7120,713field->frow + first_modified_row - form->toprow,714field->fcol,715field->frow + first_unmodified_row - form->toprow - 1,716field->cols + field->fcol - 1,7170);718}719wsyncup(formwin);720}721else722{ /* if the field-window is simply a derived window, i.e. contains723no invisible parts, the whole thing is trivial724*/725wsyncup(form->w);726}727}728untouchwin(form->w);729return _nc_Position_Form_Cursor(form);730}731732/*---------------------------------------------------------------------------733| Facility : libnform734| Function : static void Perform_Justification(735| FIELD * field,736| WINDOW * win)737|738| Description : Output field with requested justification739|740| Return Values : -741+--------------------------------------------------------------------------*/742static void Perform_Justification(FIELD * field, WINDOW * win)743{744char *bp;745int len;746int col = 0;747748bp = Get_Start_Of_Data(field->buf,Buffer_Length(field));749len = (int)(After_End_Of_Data(field->buf,Buffer_Length(field)) - bp);750751if (len>0)752{753assert(win && (field->drows == 1) && (field->dcols == field->cols));754755switch(field->just)756{757case JUSTIFY_LEFT:758break;759case JUSTIFY_CENTER:760col = (field->cols - len)/2;761break;762case JUSTIFY_RIGHT:763col = field->cols - len;764break;765default:766break;767}768769wmove(win,0,col);770waddnstr(win,bp,len);771}772}773774/*---------------------------------------------------------------------------775| Facility : libnform776| Function : static void Undo_Justification(777| FIELD * field,778| WINDOW * win)779|780| Description : Display field without any justification, i.e.781| left justified782|783| Return Values : -784+--------------------------------------------------------------------------*/785static void Undo_Justification(FIELD * field, WINDOW * win)786{787char *bp;788int len;789790bp = Get_Start_Of_Data(field->buf,Buffer_Length(field));791len = (int)(After_End_Of_Data(field->buf,Buffer_Length(field))-bp);792793if (len>0)794{795assert(win != 0);796wmove(win,0,0);797waddnstr(win,bp,len);798}799}800801/*---------------------------------------------------------------------------802| Facility : libnform803| Function : static bool Check_Char(804| FIELDTYPE * typ,805| int ch,806| TypeArgument *argp)807|808| Description : Perform a single character check for character ch809| according to the fieldtype instance.810|811| Return Values : TRUE - Character is valid812| FALSE - Character is invalid813+--------------------------------------------------------------------------*/814static bool Check_Char(FIELDTYPE * typ, int ch, TypeArgument *argp)815{816if (typ)817{818if (typ->status & _LINKED_TYPE)819{820assert(argp != 0);821return(822Check_Char(typ->left ,ch,argp->left ) ||823Check_Char(typ->right,ch,argp->right) );824}825else826{827if (typ->ccheck)828return typ->ccheck(ch,(void *)argp);829}830}831return (isprint((unsigned char)ch) ? TRUE : FALSE);832}833834/*---------------------------------------------------------------------------835| Facility : libnform836| Function : static int Display_Or_Erase_Field(837| FIELD * field,838| bool bEraseFlag)839|840| Description : Create a subwindow for the field and display the841| buffer contents (apply justification if required)842| or simply erase the field.843|844| Return Values : E_OK - on success845| E_SYSTEM_ERROR - some error (typical no memory)846+--------------------------------------------------------------------------*/847static int Display_Or_Erase_Field(FIELD * field, bool bEraseFlag)848{849WINDOW *win;850WINDOW *fwin;851852if (!field)853return E_SYSTEM_ERROR;854855fwin = Get_Form_Window(field->form);856win = derwin(fwin,857field->rows,field->cols,field->frow,field->fcol);858859if (!win)860return E_SYSTEM_ERROR;861else862{863if (field->opts & O_VISIBLE)864Set_Field_Window_Attributes(field,win);865else866{867#if defined(__LSB_VERSION__)868/* getattrs() would be handy, but it is not part of LSB 4.0 */869attr_t fwinAttrs;870short fwinPair;871wattr_get(fwin, &fwinAttrs, &fwinPair, 0);872wattr_set(win, fwinAttrs, fwinPair, 0);873#else874wattrset(win,getattrs(fwin));875#endif876}877werase(win);878}879880if (!bEraseFlag)881{882if (field->opts & O_PUBLIC)883{884if (Justification_Allowed(field))885Perform_Justification(field,win);886else887Buffer_To_Window(field,win);888}889field->status &= ~_NEWTOP;890}891wsyncup(win);892delwin(win);893return E_OK;894}895896/* Macros to preset the bEraseFlag */897#define Display_Field(field) Display_Or_Erase_Field(field,FALSE)898#define Erase_Field(field) Display_Or_Erase_Field(field,TRUE)899900/*---------------------------------------------------------------------------901| Facility : libnform902| Function : static int Synchronize_Field(FIELD * field)903|904| Description : Synchronize the windows content with the value in905| the buffer.906|907| Return Values : E_OK - success908| E_BAD_ARGUMENT - invalid field pointer909| E_SYSTEM_ERROR - some severe basic error910+--------------------------------------------------------------------------*/911static int Synchronize_Field(FIELD * field)912{913FORM *form;914int res = E_OK;915916if (!field)917return(E_BAD_ARGUMENT);918919if (((form=field->form) != (FORM *)0)920&& Field_Really_Appears(field))921{922if (field == form->current)923{924form->currow = form->curcol = form->toprow = form->begincol = 0;925werase(form->w);926927if ( (field->opts & O_PUBLIC) && Justification_Allowed(field) )928Undo_Justification( field, form->w );929else930Buffer_To_Window( field, form->w );931932field->status |= _NEWTOP;933res = _nc_Refresh_Current_Field( form );934}935else936res = Display_Field( field );937}938field->status |= _CHANGED;939return(res);940}941942/*---------------------------------------------------------------------------943| Facility : libnform944| Function : static int Synchronize_Linked_Fields(FIELD * field)945|946| Description : Propagate the Synchronize_Field function to all linked947| fields. The first error that occurs in the sequence948| of updates is the returnvalue.949|950| Return Values : E_OK - success951| E_BAD_ARGUMENT - invalid field pointer952| E_SYSTEM_ERROR - some severe basic error953+--------------------------------------------------------------------------*/954static int Synchronize_Linked_Fields(FIELD * field)955{956FIELD *linked_field;957int res = E_OK;958int syncres;959960if (!field)961return(E_BAD_ARGUMENT);962963if (!field->link)964return(E_SYSTEM_ERROR);965966for(linked_field = field->link;967linked_field!= field;968linked_field = linked_field->link )969{970if (((syncres=Synchronize_Field(linked_field)) != E_OK) &&971(res==E_OK))972res = syncres;973}974return(res);975}976977/*---------------------------------------------------------------------------978| Facility : libnform979| Function : int _nc_Synchronize_Attributes(FIELD * field)980|981| Description : If a fields visual attributes have changed, this982| routine is called to propagate those changes to the983| screen.984|985| Return Values : E_OK - success986| E_BAD_ARGUMENT - invalid field pointer987| E_SYSTEM_ERROR - some severe basic error988+--------------------------------------------------------------------------*/989int _nc_Synchronize_Attributes(FIELD * field)990{991FORM *form;992int res = E_OK;993WINDOW *formwin;994995if (!field)996return(E_BAD_ARGUMENT);997998if (((form=field->form) != (FORM *)0)999&& Field_Really_Appears(field))1000{1001if (form->current==field)1002{1003Synchronize_Buffer(form);1004Set_Field_Window_Attributes(field,form->w);1005werase(form->w);1006if (field->opts & O_PUBLIC)1007{1008if (Justification_Allowed(field))1009Undo_Justification(field,form->w);1010else1011Buffer_To_Window(field,form->w);1012}1013else1014{1015formwin = Get_Form_Window(form);1016copywin(form->w,formwin,10170,0,1018field->frow,field->fcol,1019field->rows-1,field->cols-1,0);1020wsyncup(formwin);1021Buffer_To_Window(field,form->w);1022field->status |= _NEWTOP; /* fake refresh to paint all */1023_nc_Refresh_Current_Field(form);1024}1025}1026else1027{1028res = Display_Field(field);1029}1030}1031return(res);1032}10331034/*---------------------------------------------------------------------------1035| Facility : libnform1036| Function : int _nc_Synchronize_Options(FIELD * field,1037| Field_Options newopts)1038|1039| Description : If a fields options have changed, this routine is1040| called to propagate these changes to the screen and1041| to really change the behaviour of the field.1042|1043| Return Values : E_OK - success1044| E_BAD_ARGUMENT - invalid field pointer1045| E_SYSTEM_ERROR - some severe basic error1046+--------------------------------------------------------------------------*/1047int1048_nc_Synchronize_Options(FIELD *field, Field_Options newopts)1049{1050Field_Options oldopts;1051Field_Options changed_opts;1052FORM *form;1053int res = E_OK;10541055if (!field)1056return(E_BAD_ARGUMENT);10571058oldopts = field->opts;1059changed_opts = oldopts ^ newopts;1060field->opts = newopts;1061form = field->form;10621063if (form)1064{1065if (form->current == field)1066{1067field->opts = oldopts;1068return(E_CURRENT);1069}10701071if (form->status & _POSTED)1072{1073if (form->curpage == field->page)1074{1075if (changed_opts & O_VISIBLE)1076{1077if (newopts & O_VISIBLE)1078res = Display_Field(field);1079else1080res = Erase_Field(field);1081}1082else1083{1084if ((changed_opts & O_PUBLIC) &&1085(newopts & O_VISIBLE))1086res = Display_Field(field);1087}1088}1089}1090}10911092if (changed_opts & O_STATIC)1093{1094bool single_line_field = Single_Line_Field(field);1095int res2 = E_OK;10961097if (newopts & O_STATIC)1098{ /* the field becomes now static */1099field->status &= ~_MAY_GROW;1100/* if actually we have no hidden columns, justification may1101occur again */1102if (single_line_field &&1103(field->cols == field->dcols) &&1104(field->just != NO_JUSTIFICATION) &&1105Field_Really_Appears(field))1106{1107res2 = Display_Field(field);1108}1109}1110else1111{ /* field is no longer static */1112if ((field->maxgrow==0) ||1113( single_line_field && (field->dcols < field->maxgrow)) ||1114(!single_line_field && (field->drows < field->maxgrow)))1115{1116field->status |= _MAY_GROW;1117/* a field with justification now changes its behaviour,1118so we must redisplay it */1119if (single_line_field &&1120(field->just != NO_JUSTIFICATION) &&1121Field_Really_Appears(field))1122{1123res2 = Display_Field(field);1124}1125}1126}1127if (res2 != E_OK)1128res = res2;1129}11301131return(res);1132}11331134/*---------------------------------------------------------------------------1135| Facility : libnform1136| Function : int _nc_Set_Current_Field(FORM * form,1137| FIELD * newfield)1138|1139| Description : Make the newfield the new current field.1140|1141| Return Values : E_OK - success1142| E_BAD_ARGUMENT - invalid form or field pointer1143| E_SYSTEM_ERROR - some severe basic error1144+--------------------------------------------------------------------------*/1145int1146_nc_Set_Current_Field(FORM *form, FIELD *newfield)1147{1148FIELD *field;1149WINDOW *new_window;11501151if (!form || !newfield || !form->current || (newfield->form!=form))1152return(E_BAD_ARGUMENT);11531154if ( (form->status & _IN_DRIVER) )1155return(E_BAD_STATE);11561157if (!(form->field))1158return(E_NOT_CONNECTED);11591160field = form->current;11611162if ((field!=newfield) ||1163!(form->status & _POSTED))1164{1165if ((form->w) &&1166(field->opts & O_VISIBLE) &&1167(field->form->curpage == field->page))1168{1169_nc_Refresh_Current_Field(form);1170if (field->opts & O_PUBLIC)1171{1172if (field->drows > field->rows)1173{1174if (form->toprow==0)1175field->status &= ~_NEWTOP;1176else1177field->status |= _NEWTOP;1178}1179else1180{1181if (Justification_Allowed(field))1182{1183Window_To_Buffer(form->w,field);1184werase(form->w);1185Perform_Justification(field,form->w);1186wsyncup(form->w);1187}1188}1189}1190delwin(form->w);1191}11921193field = newfield;11941195if (Has_Invisible_Parts(field))1196new_window = newpad(field->drows,field->dcols);1197else1198new_window = derwin(Get_Form_Window(form),1199field->rows,field->cols,field->frow,field->fcol);12001201if (!new_window)1202return(E_SYSTEM_ERROR);12031204form->current = field;1205form->w = new_window;1206form->status &= ~_WINDOW_MODIFIED;1207Set_Field_Window_Attributes(field,form->w);12081209if (Has_Invisible_Parts(field))1210{1211werase(form->w);1212Buffer_To_Window(field,form->w);1213}1214else1215{1216if (Justification_Allowed(field))1217{1218werase(form->w);1219Undo_Justification(field,form->w);1220wsyncup(form->w);1221}1222}12231224untouchwin(form->w);1225}12261227form->currow = form->curcol = form->toprow = form->begincol = 0;1228return(E_OK);1229}12301231/*----------------------------------------------------------------------------1232Intra-Field Navigation routines1233--------------------------------------------------------------------------*/12341235/*---------------------------------------------------------------------------1236| Facility : libnform1237| Function : static int IFN_Next_Character(FORM * form)1238|1239| Description : Move to the next character in the field. In a multiline1240| field this wraps at the end of the line.1241|1242| Return Values : E_OK - success1243| E_REQUEST_DENIED - at the rightmost position1244+--------------------------------------------------------------------------*/1245static int IFN_Next_Character(FORM * form)1246{1247FIELD *field = form->current;12481249if ((++(form->curcol))==field->dcols)1250{1251if ((++(form->currow))==field->drows)1252{1253#if GROW_IF_NAVIGATE1254if (!Single_Line_Field(field) && Field_Grown(field,1)) {1255form->curcol = 0;1256return(E_OK);1257}1258#endif1259form->currow--;1260#if GROW_IF_NAVIGATE1261if (Single_Line_Field(field) && Field_Grown(field,1))1262return(E_OK);1263#endif1264form->curcol--;1265return(E_REQUEST_DENIED);1266}1267form->curcol = 0;1268}1269return(E_OK);1270}12711272/*---------------------------------------------------------------------------1273| Facility : libnform1274| Function : static int IFN_Previous_Character(FORM * form)1275|1276| Description : Move to the previous character in the field. In a1277| multiline field this wraps and the beginning of the1278| line.1279|1280| Return Values : E_OK - success1281| E_REQUEST_DENIED - at the leftmost position1282+--------------------------------------------------------------------------*/1283static int IFN_Previous_Character(FORM * form)1284{1285if ((--(form->curcol))<0)1286{1287if ((--(form->currow))<0)1288{1289form->currow++;1290form->curcol++;1291return(E_REQUEST_DENIED);1292}1293form->curcol = form->current->dcols - 1;1294}1295return(E_OK);1296}12971298/*---------------------------------------------------------------------------1299| Facility : libnform1300| Function : static int IFN_Next_Line(FORM * form)1301|1302| Description : Move to the beginning of the next line in the field1303|1304| Return Values : E_OK - success1305| E_REQUEST_DENIED - at the last line1306+--------------------------------------------------------------------------*/1307static int IFN_Next_Line(FORM * form)1308{1309FIELD *field = form->current;13101311if ((++(form->currow))==field->drows)1312{1313#if GROW_IF_NAVIGATE1314if (!Single_Line_Field(field) && Field_Grown(field,1))1315return(E_OK);1316#endif1317form->currow--;1318return(E_REQUEST_DENIED);1319}1320form->curcol = 0;1321return(E_OK);1322}13231324/*---------------------------------------------------------------------------1325| Facility : libnform1326| Function : static int IFN_Previous_Line(FORM * form)1327|1328| Description : Move to the beginning of the previous line in the field1329|1330| Return Values : E_OK - success1331| E_REQUEST_DENIED - at the first line1332+--------------------------------------------------------------------------*/1333static int IFN_Previous_Line(FORM * form)1334{1335if ( (--(form->currow)) < 0 )1336{1337form->currow++;1338return(E_REQUEST_DENIED);1339}1340form->curcol = 0;1341return(E_OK);1342}13431344/*---------------------------------------------------------------------------1345| Facility : libnform1346| Function : static int IFN_Next_Word(FORM * form)1347|1348| Description : Move to the beginning of the next word in the field.1349|1350| Return Values : E_OK - success1351| E_REQUEST_DENIED - there is no next word1352+--------------------------------------------------------------------------*/1353static int IFN_Next_Word(FORM * form)1354{1355FIELD *field = form->current;1356char *bp = Address_Of_Current_Position_In_Buffer(form);1357char *s;1358char *t;13591360/* We really need access to the data, so we have to synchronize */1361Synchronize_Buffer(form);13621363/* Go to the first whitespace after the current position (including1364current position). This is then the startpoint to look for the1365next non-blank data */1366s = Get_First_Whitespace_Character(bp,Buffer_Length(field) -1367(int)(bp - field->buf));13681369/* Find the start of the next word */1370t = Get_Start_Of_Data(s,Buffer_Length(field) -1371(int)(s - field->buf));1372#if !FRIENDLY_PREV_NEXT_WORD1373if (s==t)1374return(E_REQUEST_DENIED);1375else1376#endif1377{1378Adjust_Cursor_Position(form,t);1379return(E_OK);1380}1381}13821383/*---------------------------------------------------------------------------1384| Facility : libnform1385| Function : static int IFN_Previous_Word(FORM * form)1386|1387| Description : Move to the beginning of the previous word in the field.1388|1389| Return Values : E_OK - success1390| E_REQUEST_DENIED - there is no previous word1391+--------------------------------------------------------------------------*/1392static int IFN_Previous_Word(FORM * form)1393{1394FIELD *field = form->current;1395char *bp = Address_Of_Current_Position_In_Buffer(form);1396char *s;1397char *t;1398bool again = FALSE;13991400/* We really need access to the data, so we have to synchronize */1401Synchronize_Buffer(form);14021403s = After_End_Of_Data(field->buf,(int)(bp-field->buf));1404/* s points now right after the last non-blank in the buffer before bp.1405If bp was in a word, s equals bp. In this case we must find the last1406whitespace in the buffer before bp and repeat the game to really find1407the previous word! */1408if (s==bp)1409again = TRUE;14101411/* And next call now goes backward to look for the last whitespace1412before that, pointing right after this, so it points to the begin1413of the previous word.1414*/1415t = After_Last_Whitespace_Character(field->buf,(int)(s - field->buf));1416#if !FRIENDLY_PREV_NEXT_WORD1417if (s==t)1418return(E_REQUEST_DENIED);1419#endif1420if (again)1421{ /* and do it again, replacing bp by t */1422s = After_End_Of_Data(field->buf,(int)(t - field->buf));1423t = After_Last_Whitespace_Character(field->buf,(int)(s - field->buf));1424#if !FRIENDLY_PREV_NEXT_WORD1425if (s==t)1426return(E_REQUEST_DENIED);1427#endif1428}1429Adjust_Cursor_Position(form,t);1430return(E_OK);1431}14321433/*---------------------------------------------------------------------------1434| Facility : libnform1435| Function : static int IFN_Beginning_Of_Field(FORM * form)1436|1437| Description : Place the cursor at the first non-pad character in1438| the field.1439|1440| Return Values : E_OK - success1441+--------------------------------------------------------------------------*/1442static int IFN_Beginning_Of_Field(FORM * form)1443{1444FIELD *field = form->current;14451446Synchronize_Buffer(form);1447Adjust_Cursor_Position(form,1448Get_Start_Of_Data(field->buf,Buffer_Length(field)));1449return(E_OK);1450}14511452/*---------------------------------------------------------------------------1453| Facility : libnform1454| Function : static int IFN_End_Of_Field(FORM * form)1455|1456| Description : Place the cursor after the last non-pad character in1457| the field. If the field occupies the last position in1458| the buffer, the cursor is positioned on the last1459| character.1460|1461| Return Values : E_OK - success1462+--------------------------------------------------------------------------*/1463static int IFN_End_Of_Field(FORM * form)1464{1465FIELD *field = form->current;1466char *pos;14671468Synchronize_Buffer(form);1469pos = After_End_Of_Data(field->buf,Buffer_Length(field));1470if (pos==(field->buf + Buffer_Length(field)))1471pos--;1472Adjust_Cursor_Position(form,pos);1473return(E_OK);1474}14751476/*---------------------------------------------------------------------------1477| Facility : libnform1478| Function : static int IFN_Beginning_Of_Line(FORM * form)1479|1480| Description : Place the cursor on the first non-pad character in1481| the current line of the field.1482|1483| Return Values : E_OK - success1484+--------------------------------------------------------------------------*/1485static int IFN_Beginning_Of_Line(FORM * form)1486{1487FIELD *field = form->current;14881489Synchronize_Buffer(form);1490Adjust_Cursor_Position(form,1491Get_Start_Of_Data(Address_Of_Current_Row_In_Buffer(form),1492field->dcols));1493return(E_OK);1494}14951496/*---------------------------------------------------------------------------1497| Facility : libnform1498| Function : static int IFN_End_Of_Line(FORM * form)1499|1500| Description : Place the cursor after the last non-pad character in the1501| current line of the field. If the field occupies the1502| last column in the line, the cursor is positioned on the1503| last character of the line.1504|1505| Return Values : E_OK - success1506+--------------------------------------------------------------------------*/1507static int IFN_End_Of_Line(FORM * form)1508{1509FIELD *field = form->current;1510char *pos;1511char *bp;15121513Synchronize_Buffer(form);1514bp = Address_Of_Current_Row_In_Buffer(form);1515pos = After_End_Of_Data(bp,field->dcols);1516if (pos == (bp + field->dcols))1517pos--;1518Adjust_Cursor_Position(form,pos);1519return(E_OK);1520}15211522/*---------------------------------------------------------------------------1523| Facility : libnform1524| Function : static int IFN_Left_Character(FORM * form)1525|1526| Description : Move one character to the left in the current line.1527| This doesn't cycle.1528|1529| Return Values : E_OK - success1530| E_REQUEST_DENIED - already in first column1531+--------------------------------------------------------------------------*/1532static int IFN_Left_Character(FORM * form)1533{1534if ( (--(form->curcol)) < 0 )1535{1536form->curcol++;1537return(E_REQUEST_DENIED);1538}1539return(E_OK);1540}15411542/*---------------------------------------------------------------------------1543| Facility : libnform1544| Function : static int IFN_Right_Character(FORM * form)1545|1546| Description : Move one character to the right in the current line.1547| This doesn't cycle.1548|1549| Return Values : E_OK - success1550| E_REQUEST_DENIED - already in last column1551+--------------------------------------------------------------------------*/1552static int IFN_Right_Character(FORM * form)1553{1554if ( (++(form->curcol)) == form->current->dcols )1555{1556#if GROW_IF_NAVIGATE1557FIELD *field = form->current;1558if (Single_Line_Field(field) && Field_Grown(field,1))1559return(E_OK);1560#endif1561--(form->curcol);1562return(E_REQUEST_DENIED);1563}1564return(E_OK);1565}15661567/*---------------------------------------------------------------------------1568| Facility : libnform1569| Function : static int IFN_Up_Character(FORM * form)1570|1571| Description : Move one line up. This doesn't cycle through the lines1572| of the field.1573|1574| Return Values : E_OK - success1575| E_REQUEST_DENIED - already in last column1576+--------------------------------------------------------------------------*/1577static int IFN_Up_Character(FORM * form)1578{1579if ( (--(form->currow)) < 0 )1580{1581form->currow++;1582return(E_REQUEST_DENIED);1583}1584return(E_OK);1585}15861587/*---------------------------------------------------------------------------1588| Facility : libnform1589| Function : static int IFN_Down_Character(FORM * form)1590|1591| Description : Move one line down. This doesn't cycle through the1592| lines of the field.1593|1594| Return Values : E_OK - success1595| E_REQUEST_DENIED - already in last column1596+--------------------------------------------------------------------------*/1597static int IFN_Down_Character(FORM * form)1598{1599FIELD *field = form->current;16001601if ( (++(form->currow)) == field->drows )1602{1603#if GROW_IF_NAVIGATE1604if (!Single_Line_Field(field) && Field_Grown(field,1))1605return(E_OK);1606#endif1607--(form->currow);1608return(E_REQUEST_DENIED);1609}1610return(E_OK);1611}1612/*----------------------------------------------------------------------------1613END of Intra-Field Navigation routines1614--------------------------------------------------------------------------*/16151616/*----------------------------------------------------------------------------1617Vertical scrolling helper routines1618--------------------------------------------------------------------------*/16191620/*---------------------------------------------------------------------------1621| Facility : libnform1622| Function : static int VSC_Generic(FORM *form, int lines)1623|1624| Description : Scroll multi-line field forward (lines>0) or1625| backward (lines<0) this many lines.1626|1627| Return Values : E_OK - success1628| E_REQUEST_DENIED - can't scroll1629+--------------------------------------------------------------------------*/1630static int VSC_Generic(FORM *form, int lines)1631{1632FIELD *field = form->current;1633int res = E_REQUEST_DENIED;1634int rows_to_go = (lines > 0 ? lines : -lines);16351636if (lines > 0)1637{1638if ( (rows_to_go + form->toprow) > (field->drows - field->rows) )1639rows_to_go = (field->drows - field->rows - form->toprow);16401641if (rows_to_go > 0)1642{1643form->currow += rows_to_go;1644form->toprow += rows_to_go;1645res = E_OK;1646}1647}1648else1649{1650if (rows_to_go > form->toprow)1651rows_to_go = form->toprow;16521653if (rows_to_go > 0)1654{1655form->currow -= rows_to_go;1656form->toprow -= rows_to_go;1657res = E_OK;1658}1659}1660return(res);1661}1662/*----------------------------------------------------------------------------1663End of Vertical scrolling helper routines1664--------------------------------------------------------------------------*/16651666/*----------------------------------------------------------------------------1667Vertical scrolling routines1668--------------------------------------------------------------------------*/16691670/*---------------------------------------------------------------------------1671| Facility : libnform1672| Function : static int Vertical_Scrolling(1673| int (* const fct) (FORM *),1674| FORM * form)1675|1676| Description : Performs the generic vertical scrolling routines.1677| This has to check for a multi-line field and to set1678| the _NEWTOP flag if scrolling really occurred.1679|1680| Return Values : Propagated error code from low-level driver calls1681+--------------------------------------------------------------------------*/1682static int Vertical_Scrolling(int (* const fct) (FORM *), FORM * form)1683{1684int res = E_REQUEST_DENIED;16851686if (!Single_Line_Field(form->current))1687{1688res = fct(form);1689if (res == E_OK)1690form->current->status |= _NEWTOP;1691}1692return(res);1693}16941695/*---------------------------------------------------------------------------1696| Facility : libnform1697| Function : static int VSC_Scroll_Line_Forward(FORM * form)1698|1699| Description : Scroll multi-line field forward a line1700|1701| Return Values : E_OK - success1702| E_REQUEST_DENIED - no data ahead1703+--------------------------------------------------------------------------*/1704static int VSC_Scroll_Line_Forward(FORM * form)1705{1706return VSC_Generic(form,1);1707}17081709/*---------------------------------------------------------------------------1710| Facility : libnform1711| Function : static int VSC_Scroll_Line_Backward(FORM * form)1712|1713| Description : Scroll multi-line field backward a line1714|1715| Return Values : E_OK - success1716| E_REQUEST_DENIED - no data behind1717+--------------------------------------------------------------------------*/1718static int VSC_Scroll_Line_Backward(FORM * form)1719{1720return VSC_Generic(form,-1);1721}17221723/*---------------------------------------------------------------------------1724| Facility : libnform1725| Function : static int VSC_Scroll_Page_Forward(FORM * form)1726|1727| Description : Scroll a multi-line field forward a page1728|1729| Return Values : E_OK - success1730| E_REQUEST_DENIED - no data ahead1731+--------------------------------------------------------------------------*/1732static int VSC_Scroll_Page_Forward(FORM * form)1733{1734return VSC_Generic(form,form->current->rows);1735}17361737/*---------------------------------------------------------------------------1738| Facility : libnform1739| Function : static int VSC_Scroll_Half_Page_Forward(FORM * form)1740|1741| Description : Scroll a multi-line field forward half a page1742|1743| Return Values : E_OK - success1744| E_REQUEST_DENIED - no data ahead1745+--------------------------------------------------------------------------*/1746static int VSC_Scroll_Half_Page_Forward(FORM * form)1747{1748return VSC_Generic(form,(form->current->rows + 1)/2);1749}17501751/*---------------------------------------------------------------------------1752| Facility : libnform1753| Function : static int VSC_Scroll_Page_Backward(FORM * form)1754|1755| Description : Scroll a multi-line field backward a page1756|1757| Return Values : E_OK - success1758| E_REQUEST_DENIED - no data behind1759+--------------------------------------------------------------------------*/1760static int VSC_Scroll_Page_Backward(FORM * form)1761{1762return VSC_Generic(form, -(form->current->rows));1763}17641765/*---------------------------------------------------------------------------1766| Facility : libnform1767| Function : static int VSC_Scroll_Half_Page_Backward(FORM * form)1768|1769| Description : Scroll a multi-line field backward half a page1770|1771| Return Values : E_OK - success1772| E_REQUEST_DENIED - no data behind1773+--------------------------------------------------------------------------*/1774static int VSC_Scroll_Half_Page_Backward(FORM * form)1775{1776return VSC_Generic(form, -((form->current->rows + 1)/2));1777}1778/*----------------------------------------------------------------------------1779End of Vertical scrolling routines1780--------------------------------------------------------------------------*/17811782/*----------------------------------------------------------------------------1783Horizontal scrolling helper routines1784--------------------------------------------------------------------------*/17851786/*---------------------------------------------------------------------------1787| Facility : libnform1788| Function : static int HSC_Generic(FORM *form, int columns)1789|1790| Description : Scroll single-line field forward (columns>0) or1791| backward (columns<0) this many columns.1792|1793| Return Values : E_OK - success1794| E_REQUEST_DENIED - can't scroll1795+--------------------------------------------------------------------------*/1796static int HSC_Generic(FORM *form, int columns)1797{1798FIELD *field = form->current;1799int res = E_REQUEST_DENIED;1800int cols_to_go = (columns > 0 ? columns : -columns);18011802if (columns > 0)1803{1804if ((cols_to_go + form->begincol) > (field->dcols - field->cols))1805cols_to_go = field->dcols - field->cols - form->begincol;18061807if (cols_to_go > 0)1808{1809form->curcol += cols_to_go;1810form->begincol += cols_to_go;1811res = E_OK;1812}1813}1814else1815{1816if ( cols_to_go > form->begincol )1817cols_to_go = form->begincol;18181819if (cols_to_go > 0)1820{1821form->curcol -= cols_to_go;1822form->begincol -= cols_to_go;1823res = E_OK;1824}1825}1826return(res);1827}1828/*----------------------------------------------------------------------------1829End of Horizontal scrolling helper routines1830--------------------------------------------------------------------------*/18311832/*----------------------------------------------------------------------------1833Horizontal scrolling routines1834--------------------------------------------------------------------------*/18351836/*---------------------------------------------------------------------------1837| Facility : libnform1838| Function : static int Horizontal_Scrolling(1839| int (* const fct) (FORM *),1840| FORM * form)1841|1842| Description : Performs the generic horizontal scrolling routines.1843| This has to check for a single-line field.1844|1845| Return Values : Propagated error code from low-level driver calls1846+--------------------------------------------------------------------------*/1847static int Horizontal_Scrolling(int (* const fct) (FORM *), FORM * form)1848{1849if (Single_Line_Field(form->current))1850return fct(form);1851else1852return(E_REQUEST_DENIED);1853}18541855/*---------------------------------------------------------------------------1856| Facility : libnform1857| Function : static int HSC_Scroll_Char_Forward(FORM * form)1858|1859| Description : Scroll single-line field forward a character1860|1861| Return Values : E_OK - success1862| E_REQUEST_DENIED - no data ahead1863+--------------------------------------------------------------------------*/1864static int HSC_Scroll_Char_Forward(FORM *form)1865{1866return HSC_Generic(form,1);1867}18681869/*---------------------------------------------------------------------------1870| Facility : libnform1871| Function : static int HSC_Scroll_Char_Backward(FORM * form)1872|1873| Description : Scroll single-line field backward a character1874|1875| Return Values : E_OK - success1876| E_REQUEST_DENIED - no data behind1877+--------------------------------------------------------------------------*/1878static int HSC_Scroll_Char_Backward(FORM *form)1879{1880return HSC_Generic(form,-1);1881}18821883/*---------------------------------------------------------------------------1884| Facility : libnform1885| Function : static int HSC_Horizontal_Line_Forward(FORM* form)1886|1887| Description : Scroll single-line field forward a line1888|1889| Return Values : E_OK - success1890| E_REQUEST_DENIED - no data ahead1891+--------------------------------------------------------------------------*/1892static int HSC_Horizontal_Line_Forward(FORM * form)1893{1894return HSC_Generic(form,form->current->cols);1895}18961897/*---------------------------------------------------------------------------1898| Facility : libnform1899| Function : static int HSC_Horizontal_Half_Line_Forward(FORM* form)1900|1901| Description : Scroll single-line field forward half a line1902|1903| Return Values : E_OK - success1904| E_REQUEST_DENIED - no data ahead1905+--------------------------------------------------------------------------*/1906static int HSC_Horizontal_Half_Line_Forward(FORM * form)1907{1908return HSC_Generic(form,(form->current->cols + 1)/2);1909}19101911/*---------------------------------------------------------------------------1912| Facility : libnform1913| Function : static int HSC_Horizontal_Line_Backward(FORM* form)1914|1915| Description : Scroll single-line field backward a line1916|1917| Return Values : E_OK - success1918| E_REQUEST_DENIED - no data behind1919+--------------------------------------------------------------------------*/1920static int HSC_Horizontal_Line_Backward(FORM * form)1921{1922return HSC_Generic(form,-(form->current->cols));1923}19241925/*---------------------------------------------------------------------------1926| Facility : libnform1927| Function : static int HSC_Horizontal_Half_Line_Backward(FORM* form)1928|1929| Description : Scroll single-line field backward half a line1930|1931| Return Values : E_OK - success1932| E_REQUEST_DENIED - no data behind1933+--------------------------------------------------------------------------*/1934static int HSC_Horizontal_Half_Line_Backward(FORM * form)1935{1936return HSC_Generic(form,-((form->current->cols + 1)/2));1937}19381939/*----------------------------------------------------------------------------1940End of Horizontal scrolling routines1941--------------------------------------------------------------------------*/19421943/*----------------------------------------------------------------------------1944Helper routines for Field Editing1945--------------------------------------------------------------------------*/19461947/*---------------------------------------------------------------------------1948| Facility : libnform1949| Function : static bool Is_There_Room_For_A_Line(FORM * form)1950|1951| Description : Check whether or not there is enough room in the1952| buffer to enter a whole line.1953|1954| Return Values : TRUE - there is enough space1955| FALSE - there is not enough space1956+--------------------------------------------------------------------------*/1957INLINE static bool Is_There_Room_For_A_Line(FORM * form)1958{1959FIELD *field = form->current;1960char *begin_of_last_line, *s;19611962Synchronize_Buffer(form);1963begin_of_last_line = Address_Of_Row_In_Buffer(field,(field->drows-1));1964s = After_End_Of_Data(begin_of_last_line,field->dcols);1965return ((s==begin_of_last_line) ? TRUE : FALSE);1966}19671968/*---------------------------------------------------------------------------1969| Facility : libnform1970| Function : static bool Is_There_Room_For_A_Char_In_Line(FORM * form)1971|1972| Description : Checks whether or not there is room for a new character1973| in the current line.1974|1975| Return Values : TRUE - there is room1976| FALSE - there is not enough room (line full)1977+--------------------------------------------------------------------------*/1978INLINE static bool Is_There_Room_For_A_Char_In_Line(FORM * form)1979{1980int last_char_in_line;19811982wmove(form->w,form->currow,form->current->dcols-1);1983last_char_in_line = (int)(winch(form->w) & A_CHARTEXT);1984wmove(form->w,form->currow,form->curcol);1985return (((last_char_in_line == form->current->pad) ||1986is_blank(last_char_in_line)) ? TRUE : FALSE);1987}19881989#define There_Is_No_Room_For_A_Char_In_Line(f) \1990!Is_There_Room_For_A_Char_In_Line(f)19911992/*---------------------------------------------------------------------------1993| Facility : libnform1994| Function : static int Insert_String(1995| FORM * form,1996| int row,1997| char *txt,1998| int len )1999|2000| Description : Insert the 'len' characters beginning at pointer 'txt'2001| into the 'row' of the 'form'. The insertion occurs2002| on the beginning of the row, all other characters are2003| moved to the right. After the text a pad character will2004| be inserted to separate the text from the rest. If2005| necessary the insertion moves characters on the next2006| line to make place for the requested insertion string.2007|2008| Return Values : E_OK - success2009| E_REQUEST_DENIED -2010| E_SYSTEM_ERROR - system error2011+--------------------------------------------------------------------------*/2012static int Insert_String(FORM *form, int row, char *txt, int len)2013{2014FIELD *field = form->current;2015char *bp = Address_Of_Row_In_Buffer(field,row);2016int datalen = (int)(After_End_Of_Data(bp,field->dcols) - bp);2017int freelen = field->dcols - datalen;2018int requiredlen = len+1;2019char *split;2020int result = E_REQUEST_DENIED;2021char *Space;20222023Space = (char*)malloc(2*sizeof(char));2024strcpy(Space, " ");20252026if (freelen >= requiredlen)2027{2028wmove(form->w,row,0);2029winsnstr(form->w,txt,len);2030wmove(form->w,row,len);2031winsnstr(form->w,Space,1);2032free(Space);2033return E_OK;2034}2035else2036{ /* we have to move characters on the next line. If we are on the2037last line this may work, if the field is growable */2038if ((row == (field->drows - 1)) && Growable(field))2039{2040if (!Field_Grown(field,1))2041{2042free(Space);2043return(E_SYSTEM_ERROR);2044}2045/* !!!Side-Effect : might be changed due to growth!!! */2046bp = Address_Of_Row_In_Buffer(field,row);2047}20482049if (row < (field->drows - 1))2050{2051split = After_Last_Whitespace_Character(bp,2052(int)(Get_Start_Of_Data(bp + field->dcols - requiredlen ,2053requiredlen) - bp));2054/* split points now to the first character of the portion of the2055line that must be moved to the next line */2056datalen = (int)(split-bp); /* + freelen has to stay on this line */2057freelen = field->dcols - (datalen + freelen); /* for the next line */20582059if ((result=Insert_String(form,row+1,split,freelen))==E_OK)2060{2061wmove(form->w,row,datalen);2062wclrtoeol(form->w);2063wmove(form->w,row,0);2064winsnstr(form->w,txt,len);2065wmove(form->w,row,len);2066winsnstr(form->w,Space,1);2067free(Space);2068return E_OK;2069}2070}2071free(Space);2072return(result);2073}2074}20752076/*---------------------------------------------------------------------------2077| Facility : libnform2078| Function : static int Wrapping_Not_Necessary_Or_Wrapping_Ok(2079| FORM * form)2080|2081| Description : If a character has been entered into a field, it may2082| be that wrapping has to occur. This routine checks2083| whether or not wrapping is required and if so, performs2084| the wrapping.2085|2086| Return Values : E_OK - no wrapping required or wrapping2087| was successful2088| E_REQUEST_DENIED -2089| E_SYSTEM_ERROR - some system error2090+--------------------------------------------------------------------------*/2091static int Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM * form)2092{2093FIELD *field = form->current;2094int result = E_REQUEST_DENIED;2095bool Last_Row = ((field->drows - 1) == form->currow);20962097if ( (field->opts & O_WRAP) && /* wrapping wanted */2098(!Single_Line_Field(field)) && /* must be multi-line */2099(There_Is_No_Room_For_A_Char_In_Line(form)) && /* line is full */2100(!Last_Row || Growable(field)) ) /* there are more lines*/2101{2102char *bp;2103char *split;2104int chars_to_be_wrapped;2105int chars_to_remain_on_line;2106if (Last_Row)2107{ /* the above logic already ensures, that in this case the field2108is growable */2109if (!Field_Grown(field,1))2110return E_SYSTEM_ERROR;2111}2112bp = Address_Of_Current_Row_In_Buffer(form);2113Window_To_Buffer(form->w,field);2114split = After_Last_Whitespace_Character(bp,field->dcols);2115/* split points to the first character of the sequence to be brought2116on the next line */2117chars_to_remain_on_line = (int)(split - bp);2118chars_to_be_wrapped = field->dcols - chars_to_remain_on_line;2119if (chars_to_remain_on_line > 0)2120{2121if ((result=Insert_String(form,form->currow+1,split,2122chars_to_be_wrapped)) == E_OK)2123{2124wmove(form->w,form->currow,chars_to_remain_on_line);2125wclrtoeol(form->w);2126if (form->curcol >= chars_to_remain_on_line)2127{2128form->currow++;2129form->curcol -= chars_to_remain_on_line;2130}2131return E_OK;2132}2133}2134else2135return E_OK;2136if (result!=E_OK)2137{2138wmove(form->w,form->currow,form->curcol);2139wdelch(form->w);2140Window_To_Buffer(form->w,field);2141result = E_REQUEST_DENIED;2142}2143}2144else2145result = E_OK; /* wrapping was not necessary */2146return(result);2147}21482149/*----------------------------------------------------------------------------2150Field Editing routines2151--------------------------------------------------------------------------*/21522153/*---------------------------------------------------------------------------2154| Facility : libnform2155| Function : static int Field_Editing(2156| int (* const fct) (FORM *),2157| FORM * form)2158|2159| Description : Generic routine for field editing requests. The driver2160| routines are only called for editable fields, the2161| _WINDOW_MODIFIED flag is set if editing occurred.2162| This is somewhat special due to the overload semantics2163| of the NEW_LINE and DEL_PREV requests.2164|2165| Return Values : Error code from low level drivers.2166+--------------------------------------------------------------------------*/2167static int Field_Editing(int (* const fct) (FORM *), FORM * form)2168{2169int res = E_REQUEST_DENIED;21702171/* We have to deal here with the specific case of the overloaded2172behaviour of New_Line and Delete_Previous requests.2173They may end up in navigational requests if we are on the first2174character in a field. But navigation is also allowed on non-2175editable fields.2176*/2177if ((fct==FE_Delete_Previous) &&2178(form->opts & O_BS_OVERLOAD) &&2179First_Position_In_Current_Field(form) )2180{2181res = Inter_Field_Navigation(FN_Previous_Field,form);2182}2183else2184{2185if (fct==FE_New_Line)2186{2187if ((form->opts & O_NL_OVERLOAD) &&2188First_Position_In_Current_Field(form))2189{2190res = Inter_Field_Navigation(FN_Next_Field,form);2191}2192else2193/* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */2194res = fct(form);2195}2196else2197{2198/* From now on, everything must be editable */2199if (form->current->opts & O_EDIT)2200{2201res = fct(form);2202if (res==E_OK)2203form->status |= _WINDOW_MODIFIED;2204}2205}2206}2207return res;2208}22092210/*---------------------------------------------------------------------------2211| Facility : libnform2212| Function : static int FE_New_Line(FORM * form)2213|2214| Description : Perform a new line request. This is rather complex2215| compared to other routines in this code due to the2216| rather difficult to understand description in the2217| manuals.2218|2219| Return Values : E_OK - success2220| E_REQUEST_DENIED - new line not allowed2221| E_SYSTEM_ERROR - system error2222+--------------------------------------------------------------------------*/2223static int FE_New_Line(FORM * form)2224{2225FIELD *field = form->current;2226char *bp, *t;2227bool Last_Row = ((field->drows - 1)==form->currow);22282229if (form->status & _OVLMODE)2230{2231if (Last_Row &&2232(!(Growable(field) && !Single_Line_Field(field))))2233{2234if (!(form->opts & O_NL_OVERLOAD))2235return(E_REQUEST_DENIED);2236wclrtoeol(form->w);2237/* we have to set this here, although it is also2238handled in the generic routine. The reason is,2239that FN_Next_Field may fail, but the form is2240definitively changed */2241form->status |= _WINDOW_MODIFIED;2242return Inter_Field_Navigation(FN_Next_Field,form);2243}2244else2245{2246if (Last_Row && !Field_Grown(field,1))2247{ /* N.B.: due to the logic in the 'if', LastRow==TRUE2248means here that the field is growable and not2249a single-line field */2250return(E_SYSTEM_ERROR);2251}2252wclrtoeol(form->w);2253form->currow++;2254form->curcol = 0;2255form->status |= _WINDOW_MODIFIED;2256return(E_OK);2257}2258}2259else2260{ /* Insert Mode */2261if (Last_Row &&2262!(Growable(field) && !Single_Line_Field(field)))2263{2264if (!(form->opts & O_NL_OVERLOAD))2265return(E_REQUEST_DENIED);2266return Inter_Field_Navigation(FN_Next_Field,form);2267}2268else2269{2270bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);22712272if (!(May_Do_It || Growable(field)))2273return(E_REQUEST_DENIED);2274if (!May_Do_It && !Field_Grown(field,1))2275return(E_SYSTEM_ERROR);22762277bp= Address_Of_Current_Position_In_Buffer(form);2278t = After_End_Of_Data(bp,field->dcols - form->curcol);2279wclrtoeol(form->w);2280form->currow++;2281form->curcol=0;2282wmove(form->w,form->currow,form->curcol);2283winsertln(form->w);2284waddnstr(form->w,bp,(int)(t-bp));2285form->status |= _WINDOW_MODIFIED;2286return E_OK;2287}2288}2289}22902291/*---------------------------------------------------------------------------2292| Facility : libnform2293| Function : static int FE_Insert_Character(FORM * form)2294|2295| Description : Insert blank character at the cursor position2296|2297| Return Values : E_OK2298| E_REQUEST_DENIED2299+--------------------------------------------------------------------------*/2300static int FE_Insert_Character(FORM * form)2301{2302FIELD *field = form->current;2303int result = E_REQUEST_DENIED;23042305if (Check_Char(field->type,(int)C_BLANK,(TypeArgument *)(field->arg)))2306{2307bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);23082309if (There_Is_Room ||2310((Single_Line_Field(field) && Growable(field))))2311{2312if (!There_Is_Room && !Field_Grown(field,1))2313result = E_SYSTEM_ERROR;2314else2315{2316winsch(form->w,(chtype)C_BLANK);2317result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);2318}2319}2320}2321return result;2322}23232324/*---------------------------------------------------------------------------2325| Facility : libnform2326| Function : static int FE_Insert_Line(FORM * form)2327|2328| Description : Insert a blank line at the cursor position2329|2330| Return Values : E_OK - success2331| E_REQUEST_DENIED - line can not be inserted2332+--------------------------------------------------------------------------*/2333static int FE_Insert_Line(FORM * form)2334{2335FIELD *field = form->current;2336int result = E_REQUEST_DENIED;23372338if (Check_Char(field->type,(int)C_BLANK,(TypeArgument *)(field->arg)))2339{2340bool Maybe_Done = (form->currow!=(field->drows-1)) &&2341Is_There_Room_For_A_Line(form);23422343if (!Single_Line_Field(field) &&2344(Maybe_Done || Growable(field)))2345{2346if (!Maybe_Done && !Field_Grown(field,1))2347result = E_SYSTEM_ERROR;2348else2349{2350form->curcol = 0;2351winsertln(form->w);2352result = E_OK;2353}2354}2355}2356return result;2357}23582359/*---------------------------------------------------------------------------2360| Facility : libnform2361| Function : static int FE_Delete_Character(FORM * form)2362|2363| Description : Delete character at the cursor position2364|2365| Return Values : E_OK - success2366+--------------------------------------------------------------------------*/2367static int FE_Delete_Character(FORM * form)2368{2369wdelch(form->w);2370return E_OK;2371}23722373/*---------------------------------------------------------------------------2374| Facility : libnform2375| Function : static int FE_Delete_Previous(FORM * form)2376|2377| Description : Delete character before cursor. Again this is a rather2378| difficult piece compared to others due to the overloading2379| semantics of backspace.2380| N.B.: The case of overloaded BS on first field position2381| is already handled in the generic routine.2382|2383| Return Values : E_OK - success2384| E_REQUEST_DENIED - Character can't be deleted2385+--------------------------------------------------------------------------*/2386static int FE_Delete_Previous(FORM * form)2387{2388FIELD *field = form->current;23892390if (First_Position_In_Current_Field(form))2391return E_REQUEST_DENIED;23922393if ( (--(form->curcol))<0 )2394{2395char *this_line, *prev_line, *prev_end, *this_end;23962397form->curcol++;2398if (form->status & _OVLMODE)2399return E_REQUEST_DENIED;24002401prev_line = Address_Of_Row_In_Buffer(field,(form->currow-1));2402this_line = Address_Of_Row_In_Buffer(field,(form->currow));2403Synchronize_Buffer(form);2404prev_end = After_End_Of_Data(prev_line,field->dcols);2405this_end = After_End_Of_Data(this_line,field->dcols);2406if ((int)(this_end-this_line) >2407(field->cols-(int)(prev_end-prev_line)))2408return E_REQUEST_DENIED;2409wdeleteln(form->w);2410Adjust_Cursor_Position(form,prev_end);2411wmove(form->w,form->currow,form->curcol);2412waddnstr(form->w,this_line,(int)(this_end-this_line));2413}2414else2415{2416wmove(form->w,form->currow,form->curcol);2417wdelch(form->w);2418}2419return E_OK;2420}24212422/*---------------------------------------------------------------------------2423| Facility : libnform2424| Function : static int FE_Delete_Line(FORM * form)2425|2426| Description : Delete line at cursor position.2427|2428| Return Values : E_OK - success2429+--------------------------------------------------------------------------*/2430static int FE_Delete_Line(FORM * form)2431{2432form->curcol = 0;2433wdeleteln(form->w);2434return E_OK;2435}24362437/*---------------------------------------------------------------------------2438| Facility : libnform2439| Function : static int FE_Delete_Word(FORM * form)2440|2441| Description : Delete word at cursor position2442|2443| Return Values : E_OK - success2444| E_REQUEST_DENIED - failure2445+--------------------------------------------------------------------------*/2446static int FE_Delete_Word(FORM * form)2447{2448FIELD *field = form->current;2449char *bp = Address_Of_Current_Row_In_Buffer(form);2450char *ep = bp + field->dcols;2451char *cp = bp + form->curcol;2452char *s;24532454Synchronize_Buffer(form);2455if (is_blank(*cp))2456return E_REQUEST_DENIED; /* not in word */24572458/* move cursor to begin of word and erase to end of screen-line */2459Adjust_Cursor_Position(form,2460After_Last_Whitespace_Character(bp,form->curcol));2461wmove(form->w,form->currow,form->curcol);2462wclrtoeol(form->w);24632464/* skip over word in buffer */2465s = Get_First_Whitespace_Character(cp,(int)(ep-cp));2466/* to begin of next word */2467s = Get_Start_Of_Data(s,(int)(ep - s));2468if ( (s!=cp) && !is_blank(*s))2469{2470/* copy remaining line to window */2471waddnstr(form->w,s,(int)(s - After_End_Of_Data(s,(int)(ep - s))));2472}2473return E_OK;2474}24752476/*---------------------------------------------------------------------------2477| Facility : libnform2478| Function : static int FE_Clear_To_End_Of_Line(FORM * form)2479|2480| Description : Clear to end of current line.2481|2482| Return Values : E_OK - success2483+--------------------------------------------------------------------------*/2484static int FE_Clear_To_End_Of_Line(FORM * form)2485{2486wclrtoeol(form->w);2487return E_OK;2488}24892490/*---------------------------------------------------------------------------2491| Facility : libnform2492| Function : static int FE_Clear_To_End_Of_Form(FORM * form)2493|2494| Description : Clear to end of form.2495|2496| Return Values : E_OK - success2497+--------------------------------------------------------------------------*/2498static int FE_Clear_To_End_Of_Form(FORM * form)2499{2500wclrtobot(form->w);2501return E_OK;2502}25032504/*---------------------------------------------------------------------------2505| Facility : libnform2506| Function : static int FE_Clear_Field(FORM * form)2507|2508| Description : Clear entire field.2509|2510| Return Values : E_OK - success2511+--------------------------------------------------------------------------*/2512static int FE_Clear_Field(FORM * form)2513{2514form->currow = form->curcol = 0;2515werase(form->w);2516return E_OK;2517}2518/*----------------------------------------------------------------------------2519END of Field Editing routines2520--------------------------------------------------------------------------*/25212522/*----------------------------------------------------------------------------2523Edit Mode routines2524--------------------------------------------------------------------------*/25252526/*---------------------------------------------------------------------------2527| Facility : libnform2528| Function : static int EM_Overlay_Mode(FORM * form)2529|2530| Description : Switch to overlay mode.2531|2532| Return Values : E_OK - success2533+--------------------------------------------------------------------------*/2534static int EM_Overlay_Mode(FORM * form)2535{2536form->status |= _OVLMODE;2537return E_OK;2538}25392540/*---------------------------------------------------------------------------2541| Facility : libnform2542| Function : static int EM_Insert_Mode(FORM * form)2543|2544| Description : Switch to insert mode2545|2546| Return Values : E_OK - success2547+--------------------------------------------------------------------------*/2548static int EM_Insert_Mode(FORM * form)2549{2550form->status &= ~_OVLMODE;2551return E_OK;2552}25532554/*----------------------------------------------------------------------------2555END of Edit Mode routines2556--------------------------------------------------------------------------*/25572558/*----------------------------------------------------------------------------2559Helper routines for Choice Requests2560--------------------------------------------------------------------------*/25612562/*---------------------------------------------------------------------------2563| Facility : libnform2564| Function : static bool Next_Choice(2565| FIELDTYPE * typ,2566| FIELD * field,2567| TypeArgument *argp)2568|2569| Description : Get the next field choice. For linked types this is2570| done recursively.2571|2572| Return Values : TRUE - next choice successfully retrieved2573| FALSE - couldn't retrieve next choice2574+--------------------------------------------------------------------------*/2575static bool Next_Choice(FIELDTYPE * typ, FIELD *field, TypeArgument *argp)2576{2577if (!typ || !(typ->status & _HAS_CHOICE))2578return FALSE;25792580if (typ->status & _LINKED_TYPE)2581{2582assert(argp != 0);2583return(2584Next_Choice(typ->left ,field,argp->left) ||2585Next_Choice(typ->right,field,argp->right) );2586}2587else2588{2589assert(typ->next != 0);2590return typ->next(field,(void *)argp);2591}2592}25932594/*---------------------------------------------------------------------------2595| Facility : libnform2596| Function : static bool Previous_Choice(2597| FIELDTYPE * typ,2598| FIELD * field,2599| TypeArgument *argp)2600|2601| Description : Get the previous field choice. For linked types this2602| is done recursively.2603|2604| Return Values : TRUE - previous choice successfully retrieved2605| FALSE - couldn't retrieve previous choice2606+--------------------------------------------------------------------------*/2607static bool Previous_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)2608{2609if (!typ || !(typ->status & _HAS_CHOICE))2610return FALSE;26112612if (typ->status & _LINKED_TYPE)2613{2614assert(argp != 0);2615return(2616Previous_Choice(typ->left ,field,argp->left) ||2617Previous_Choice(typ->right,field,argp->right));2618}2619else2620{2621assert(typ->prev != 0);2622return typ->prev(field,(void *)argp);2623}2624}2625/*----------------------------------------------------------------------------2626End of Helper routines for Choice Requests2627--------------------------------------------------------------------------*/26282629/*----------------------------------------------------------------------------2630Routines for Choice Requests2631--------------------------------------------------------------------------*/26322633/*---------------------------------------------------------------------------2634| Facility : libnform2635| Function : static int CR_Next_Choice(FORM * form)2636|2637| Description : Get the next field choice.2638|2639| Return Values : E_OK - success2640| E_REQUEST_DENIED - next choice couldn't be retrieved2641+--------------------------------------------------------------------------*/2642static int CR_Next_Choice(FORM * form)2643{2644FIELD *field = form->current;2645Synchronize_Buffer(form);2646return ((Next_Choice(field->type,field,(TypeArgument *)(field->arg))) ?2647E_OK : E_REQUEST_DENIED);2648}26492650/*---------------------------------------------------------------------------2651| Facility : libnform2652| Function : static int CR_Previous_Choice(FORM * form)2653|2654| Description : Get the previous field choice.2655|2656| Return Values : E_OK - success2657| E_REQUEST_DENIED - prev. choice couldn't be retrieved2658+--------------------------------------------------------------------------*/2659static int CR_Previous_Choice(FORM * form)2660{2661FIELD *field = form->current;2662Synchronize_Buffer(form);2663return ((Previous_Choice(field->type,field,(TypeArgument *)(field->arg))) ?2664E_OK : E_REQUEST_DENIED);2665}2666/*----------------------------------------------------------------------------2667End of Routines for Choice Requests2668--------------------------------------------------------------------------*/26692670/*----------------------------------------------------------------------------2671Helper routines for Field Validations.2672--------------------------------------------------------------------------*/26732674/*---------------------------------------------------------------------------2675| Facility : libnform2676| Function : static bool Check_Field(2677| FIELDTYPE * typ,2678| FIELD * field,2679| TypeArgument * argp)2680|2681| Description : Check the field according to its fieldtype and its2682| actual arguments. For linked fieldtypes this is done2683| recursively.2684|2685| Return Values : TRUE - field is valid2686| FALSE - field is invalid.2687+--------------------------------------------------------------------------*/2688static bool Check_Field(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)2689{2690if (typ)2691{2692if (field->opts & O_NULLOK)2693{2694char *bp = field->buf;2695assert(bp != 0);2696while(is_blank(*bp))2697{ bp++; }2698if (*bp == '\0')2699return TRUE;2700}27012702if (typ->status & _LINKED_TYPE)2703{2704assert(argp != 0);2705return(2706Check_Field(typ->left ,field,argp->left ) ||2707Check_Field(typ->right,field,argp->right) );2708}2709else2710{2711if (typ->fcheck)2712return typ->fcheck(field,(void *)argp);2713}2714}2715return TRUE;2716}27172718/*---------------------------------------------------------------------------2719| Facility : libnform2720| Function : bool _nc_Internal_Validation(FORM * form )2721|2722| Description : Validate the current field of the form.2723|2724| Return Values : TRUE - field is valid2725| FALSE - field is invalid2726+--------------------------------------------------------------------------*/2727bool2728_nc_Internal_Validation(FORM *form)2729{2730FIELD *field;27312732field = form->current;27332734Synchronize_Buffer(form);2735if ((form->status & _FCHECK_REQUIRED) ||2736(!(field->opts & O_PASSOK)))2737{2738if (!Check_Field(field->type,field,(TypeArgument *)(field->arg)))2739return FALSE;2740form->status &= ~_FCHECK_REQUIRED;2741field->status |= _CHANGED;2742Synchronize_Linked_Fields(field);2743}2744return TRUE;2745}2746/*----------------------------------------------------------------------------2747End of Helper routines for Field Validations.2748--------------------------------------------------------------------------*/27492750/*----------------------------------------------------------------------------2751Routines for Field Validation.2752--------------------------------------------------------------------------*/27532754/*---------------------------------------------------------------------------2755| Facility : libnform2756| Function : static int FV_Validation(FORM * form)2757|2758| Description : Validate the current field of the form.2759|2760| Return Values : E_OK - field valid2761| E_INVALID_FIELD - field not valid2762+--------------------------------------------------------------------------*/2763static int FV_Validation(FORM * form)2764{2765if (_nc_Internal_Validation(form))2766return E_OK;2767else2768return E_INVALID_FIELD;2769}2770/*----------------------------------------------------------------------------2771End of routines for Field Validation.2772--------------------------------------------------------------------------*/27732774/*----------------------------------------------------------------------------2775Helper routines for Inter-Field Navigation2776--------------------------------------------------------------------------*/27772778/*---------------------------------------------------------------------------2779| Facility : libnform2780| Function : static FIELD *Next_Field_On_Page(FIELD * field)2781|2782| Description : Get the next field after the given field on the current2783| page. The order of fields is the one defined by the2784| fields array. Only visible and active fields are2785| counted.2786|2787| Return Values : Pointer to the next field.2788+--------------------------------------------------------------------------*/2789INLINE static FIELD *Next_Field_On_Page(FIELD * field)2790{2791FORM *form = field->form;2792FIELD **field_on_page = &form->field[field->index];2793FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];2794FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];27952796do2797{2798field_on_page =2799(field_on_page==last_on_page) ? first_on_page : field_on_page + 1;2800if (Field_Is_Selectable(*field_on_page))2801break;2802} while(field!=(*field_on_page));2803return(*field_on_page);2804}28052806/*---------------------------------------------------------------------------2807| Facility : libnform2808| Function : FIELD* _nc_First_Active_Field(FORM * form)2809|2810| Description : Get the first active field on the current page,2811| if there are such. If there are none, get the first2812| visible field on the page. If there are also none,2813| we return the first field on page and hope the best.2814|2815| Return Values : Pointer to calculated field.2816+--------------------------------------------------------------------------*/2817FIELD*2818_nc_First_Active_Field(FORM * form)2819{2820FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];2821FIELD *proposed = Next_Field_On_Page(*last_on_page);28222823if (proposed == *last_on_page)2824{ /* there might be the special situation, where there is no2825active and visible field on the current page. We then select2826the first visible field on this readonly page2827*/2828if (Field_Is_Not_Selectable(proposed))2829{2830FIELD **field = &form->field[proposed->index];2831FIELD **first = &form->field[form->page[form->curpage].pmin];28322833do2834{2835field = (field==last_on_page) ? first : field + 1;2836if (((*field)->opts & O_VISIBLE))2837break;2838} while(proposed!=(*field));28392840proposed = *field;28412842if ((proposed == *last_on_page) && !(proposed->opts&O_VISIBLE))2843{ /* This means, there is also no visible field on the page.2844So we propose the first one and hope the very best...2845Some very clever user has designed a readonly and invisible2846page on this form.2847*/2848proposed = *first;2849}2850}2851}2852return(proposed);2853}28542855/*---------------------------------------------------------------------------2856| Facility : libnform2857| Function : static FIELD *Previous_Field_On_Page(FIELD * field)2858|2859| Description : Get the previous field before the given field on the2860| current page. The order of fields is the one defined by2861| the fields array. Only visible and active fields are2862| counted.2863|2864| Return Values : Pointer to the previous field.2865+--------------------------------------------------------------------------*/2866INLINE static FIELD *Previous_Field_On_Page(FIELD * field)2867{2868FORM *form = field->form;2869FIELD **field_on_page = &form->field[field->index];2870FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];2871FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];28722873do2874{2875field_on_page =2876(field_on_page==first_on_page) ? last_on_page : field_on_page - 1;2877if (Field_Is_Selectable(*field_on_page))2878break;2879} while(field!=(*field_on_page));28802881return (*field_on_page);2882}28832884/*---------------------------------------------------------------------------2885| Facility : libnform2886| Function : static FIELD *Sorted_Next_Field(FIELD * field)2887|2888| Description : Get the next field after the given field on the current2889| page. The order of fields is the one defined by the2890| (row,column) geometry, rows are major.2891|2892| Return Values : Pointer to the next field.2893+--------------------------------------------------------------------------*/2894INLINE static FIELD *Sorted_Next_Field(FIELD * field)2895{2896FIELD *field_on_page = field;28972898do2899{2900field_on_page = field_on_page->snext;2901if (Field_Is_Selectable(field_on_page))2902break;2903} while(field_on_page!=field);29042905return (field_on_page);2906}29072908/*---------------------------------------------------------------------------2909| Facility : libnform2910| Function : static FIELD *Sorted_Previous_Field(FIELD * field)2911|2912| Description : Get the previous field before the given field on the2913| current page. The order of fields is the one defined2914| by the (row,column) geometry, rows are major.2915|2916| Return Values : Pointer to the previous field.2917+--------------------------------------------------------------------------*/2918INLINE static FIELD *Sorted_Previous_Field(FIELD * field)2919{2920FIELD *field_on_page = field;29212922do2923{2924field_on_page = field_on_page->sprev;2925if (Field_Is_Selectable(field_on_page))2926break;2927} while(field_on_page!=field);29282929return (field_on_page);2930}29312932/*---------------------------------------------------------------------------2933| Facility : libnform2934| Function : static FIELD *Left_Neighbour_Field(FIELD * field)2935|2936| Description : Get the left neighbour of the field on the same line2937| and the same page. Cycles through the line.2938|2939| Return Values : Pointer to left neighbour field.2940+--------------------------------------------------------------------------*/2941INLINE static FIELD *Left_Neighbour_Field(FIELD * field)2942{2943FIELD *field_on_page = field;29442945/* For a field that has really a left neighbour, the while clause2946immediately fails and the loop is left, positioned at the right2947neighbour. Otherwise we cycle backwards through the sorted fieldlist2948until we enter the same line (from the right end).2949*/2950do2951{2952field_on_page = Sorted_Previous_Field(field_on_page);2953} while(field_on_page->frow != field->frow);29542955return (field_on_page);2956}29572958/*---------------------------------------------------------------------------2959| Facility : libnform2960| Function : static FIELD *Right_Neighbour_Field(FIELD * field)2961|2962| Description : Get the right neighbour of the field on the same line2963| and the same page.2964|2965| Return Values : Pointer to right neighbour field.2966+--------------------------------------------------------------------------*/2967INLINE static FIELD *Right_Neighbour_Field(FIELD * field)2968{2969FIELD *field_on_page = field;29702971/* See the comments on Left_Neighbour_Field to understand how it works */2972do2973{2974field_on_page = Sorted_Next_Field(field_on_page);2975} while(field_on_page->frow != field->frow);29762977return (field_on_page);2978}29792980/*---------------------------------------------------------------------------2981| Facility : libnform2982| Function : static FIELD *Upper_Neighbour_Field(FIELD * field)2983|2984| Description : Because of the row-major nature of sorting the fields,2985| its more difficult to define what's the upper neighbour2986| field really means. We define that it must be on a2987| 'previous' line (cyclic order!) and is the rightmost2988| field laying on the left side of the given field. If2989| this set is empty, we take the first field on the line.2990|2991| Return Values : Pointer to the upper neighbour field.2992+--------------------------------------------------------------------------*/2993static FIELD *Upper_Neighbour_Field(FIELD * field)2994{2995FIELD *field_on_page = field;2996int frow = field->frow;2997int fcol = field->fcol;29982999/* Walk back to the 'previous' line. The second term in the while clause3000just guarantees that we stop if we cycled through the line because3001there might be no 'previous' line if the page has just one line.3002*/3003do3004{3005field_on_page = Sorted_Previous_Field(field_on_page);3006} while(field_on_page->frow==frow && field_on_page->fcol!=fcol);30073008if (field_on_page->frow!=frow)3009{ /* We really found a 'previous' line. We are positioned at the3010rightmost field on this line */3011frow = field_on_page->frow;30123013/* We walk to the left as long as we are really right of the3014field. */3015while(field_on_page->frow==frow && field_on_page->fcol>fcol)3016field_on_page = Sorted_Previous_Field(field_on_page);30173018/* If we wrapped, just go to the right which is the first field on3019the row */3020if (field_on_page->frow!=frow)3021field_on_page = Sorted_Next_Field(field_on_page);3022}30233024return (field_on_page);3025}30263027/*---------------------------------------------------------------------------3028| Facility : libnform3029| Function : static FIELD *Down_Neighbour_Field(FIELD * field)3030|3031| Description : Because of the row-major nature of sorting the fields,3032| its more difficult to define what's the down neighbour3033| field really means. We define that it must be on a3034| 'next' line (cyclic order!) and is the leftmost3035| field laying on the right side of the given field. If3036| this set is empty, we take the last field on the line.3037|3038| Return Values : Pointer to the upper neighbour field.3039+--------------------------------------------------------------------------*/3040static FIELD *Down_Neighbour_Field(FIELD * field)3041{3042FIELD *field_on_page = field;3043int frow = field->frow;3044int fcol = field->fcol;30453046/* Walk forward to the 'next' line. The second term in the while clause3047just guarantees that we stop if we cycled through the line because3048there might be no 'next' line if the page has just one line.3049*/3050do3051{3052field_on_page = Sorted_Next_Field(field_on_page);3053} while(field_on_page->frow==frow && field_on_page->fcol!=fcol);30543055if (field_on_page->frow!=frow)3056{ /* We really found a 'next' line. We are positioned at the rightmost3057field on this line */3058frow = field_on_page->frow;30593060/* We walk to the right as long as we are really left of the3061field. */3062while(field_on_page->frow==frow && field_on_page->fcol<fcol)3063field_on_page = Sorted_Next_Field(field_on_page);30643065/* If we wrapped, just go to the left which is the last field on3066the row */3067if (field_on_page->frow!=frow)3068field_on_page = Sorted_Previous_Field(field_on_page);3069}30703071return(field_on_page);3072}30733074/*----------------------------------------------------------------------------3075Inter-Field Navigation routines3076--------------------------------------------------------------------------*/30773078/*---------------------------------------------------------------------------3079| Facility : libnform3080| Function : static int Inter_Field_Navigation(3081| int (* const fct) (FORM *),3082| FORM * form)3083|3084| Description : Generic behaviour for changing the current field, the3085| field is left and a new field is entered. So the field3086| must be validated and the field init/term hooks must3087| be called.3088|3089| Return Values : E_OK - success3090| E_INVALID_FIELD - field is invalid3091| some other - error from subordinate call3092+--------------------------------------------------------------------------*/3093static int Inter_Field_Navigation(int (* const fct) (FORM *),FORM *form)3094{3095int res;30963097if (!_nc_Internal_Validation(form))3098res = E_INVALID_FIELD;3099else3100{3101Call_Hook(form,fieldterm);3102res = fct(form);3103Call_Hook(form,fieldinit);3104}3105return res;3106}31073108/*---------------------------------------------------------------------------3109| Facility : libnform3110| Function : static int FN_Next_Field(FORM * form)3111|3112| Description : Move to the next field on the current page of the form3113|3114| Return Values : E_OK - success3115| != E_OK - error from subordinate call3116+--------------------------------------------------------------------------*/3117static int FN_Next_Field(FORM * form)3118{3119return _nc_Set_Current_Field(form,3120Next_Field_On_Page(form->current));3121}31223123/*---------------------------------------------------------------------------3124| Facility : libnform3125| Function : static int FN_Previous_Field(FORM * form)3126|3127| Description : Move to the previous field on the current page of the3128| form3129|3130| Return Values : E_OK - success3131| != E_OK - error from subordinate call3132+--------------------------------------------------------------------------*/3133static int FN_Previous_Field(FORM * form)3134{3135return _nc_Set_Current_Field(form,3136Previous_Field_On_Page(form->current));3137}31383139/*---------------------------------------------------------------------------3140| Facility : libnform3141| Function : static int FN_First_Field(FORM * form)3142|3143| Description : Move to the first field on the current page of the form3144|3145| Return Values : E_OK - success3146| != E_OK - error from subordinate call3147+--------------------------------------------------------------------------*/3148static int FN_First_Field(FORM * form)3149{3150return _nc_Set_Current_Field(form,3151Next_Field_On_Page(form->field[form->page[form->curpage].pmax]));3152}31533154/*---------------------------------------------------------------------------3155| Facility : libnform3156| Function : static int FN_Last_Field(FORM * form)3157|3158| Description : Move to the last field on the current page of the form3159|3160| Return Values : E_OK - success3161| != E_OK - error from subordinate call3162+--------------------------------------------------------------------------*/3163static int FN_Last_Field(FORM * form)3164{3165return3166_nc_Set_Current_Field(form,3167Previous_Field_On_Page(form->field[form->page[form->curpage].pmin]));3168}31693170/*---------------------------------------------------------------------------3171| Facility : libnform3172| Function : static int FN_Sorted_Next_Field(FORM * form)3173|3174| Description : Move to the sorted next field on the current page3175| of the form.3176|3177| Return Values : E_OK - success3178| != E_OK - error from subordinate call3179+--------------------------------------------------------------------------*/3180static int FN_Sorted_Next_Field(FORM * form)3181{3182return _nc_Set_Current_Field(form,3183Sorted_Next_Field(form->current));3184}31853186/*---------------------------------------------------------------------------3187| Facility : libnform3188| Function : static int FN_Sorted_Previous_Field(FORM * form)3189|3190| Description : Move to the sorted previous field on the current page3191| of the form.3192|3193| Return Values : E_OK - success3194| != E_OK - error from subordinate call3195+--------------------------------------------------------------------------*/3196static int FN_Sorted_Previous_Field(FORM * form)3197{3198return _nc_Set_Current_Field(form,3199Sorted_Previous_Field(form->current));3200}32013202/*---------------------------------------------------------------------------3203| Facility : libnform3204| Function : static int FN_Sorted_First_Field(FORM * form)3205|3206| Description : Move to the sorted first field on the current page3207| of the form.3208|3209| Return Values : E_OK - success3210| != E_OK - error from subordinate call3211+--------------------------------------------------------------------------*/3212static int FN_Sorted_First_Field(FORM * form)3213{3214return _nc_Set_Current_Field(form,3215Sorted_Next_Field(form->field[form->page[form->curpage].smax]));3216}32173218/*---------------------------------------------------------------------------3219| Facility : libnform3220| Function : static int FN_Sorted_Last_Field(FORM * form)3221|3222| Description : Move to the sorted last field on the current page3223| of the form.3224|3225| Return Values : E_OK - success3226| != E_OK - error from subordinate call3227+--------------------------------------------------------------------------*/3228static int FN_Sorted_Last_Field(FORM * form)3229{3230return _nc_Set_Current_Field(form,3231Sorted_Previous_Field(form->field[form->page[form->curpage].smin]));3232}32333234/*---------------------------------------------------------------------------3235| Facility : libnform3236| Function : static int FN_Left_Field(FORM * form)3237|3238| Description : Get the field on the left of the current field on the3239| same line and the same page. Cycles through the line.3240|3241| Return Values : E_OK - success3242| != E_OK - error from subordinate call3243+--------------------------------------------------------------------------*/3244static int FN_Left_Field(FORM * form)3245{3246return _nc_Set_Current_Field(form,3247Left_Neighbour_Field(form->current));3248}32493250/*---------------------------------------------------------------------------3251| Facility : libnform3252| Function : static int FN_Right_Field(FORM * form)3253|3254| Description : Get the field on the right of the current field on the3255| same line and the same page. Cycles through the line.3256|3257| Return Values : E_OK - success3258| != E_OK - error from subordinate call3259+--------------------------------------------------------------------------*/3260static int FN_Right_Field(FORM * form)3261{3262return _nc_Set_Current_Field(form,3263Right_Neighbour_Field(form->current));3264}32653266/*---------------------------------------------------------------------------3267| Facility : libnform3268| Function : static int FN_Up_Field(FORM * form)3269|3270| Description : Get the upper neighbour of the current field. This3271| cycles through the page. See the comments of the3272| Upper_Neighbour_Field function to understand how3273| 'upper' is defined.3274|3275| Return Values : E_OK - success3276| != E_OK - error from subordinate call3277+--------------------------------------------------------------------------*/3278static int FN_Up_Field(FORM * form)3279{3280return _nc_Set_Current_Field(form,3281Upper_Neighbour_Field(form->current));3282}32833284/*---------------------------------------------------------------------------3285| Facility : libnform3286| Function : static int FN_Down_Field(FORM * form)3287|3288| Description : Get the down neighbour of the current field. This3289| cycles through the page. See the comments of the3290| Down_Neighbour_Field function to understand how3291| 'down' is defined.3292|3293| Return Values : E_OK - success3294| != E_OK - error from subordinate call3295+--------------------------------------------------------------------------*/3296static int FN_Down_Field(FORM * form)3297{3298return _nc_Set_Current_Field(form,3299Down_Neighbour_Field(form->current));3300}3301/*----------------------------------------------------------------------------3302END of Field Navigation routines3303--------------------------------------------------------------------------*/33043305/*----------------------------------------------------------------------------3306Helper routines for Page Navigation3307--------------------------------------------------------------------------*/33083309/*---------------------------------------------------------------------------3310| Facility : libnform3311| Function : int _nc_Set_Form_Page(FORM * form,3312| int page,3313| FIELD * field)3314|3315| Description : Make the given page nr. the current page and make3316| the given field the current field on the page. If3317| for the field NULL is given, make the first field on3318| the page the current field. The routine acts only3319| if the requested page is not the current page.3320|3321| Return Values : E_OK - success3322| != E_OK - error from subordinate call3323+--------------------------------------------------------------------------*/3324int3325_nc_Set_Form_Page(FORM * form, int page, FIELD * field)3326{3327int res = E_OK;33283329if ((form->curpage!=page))3330{3331FIELD *last_field, *field_on_page;33323333werase(Get_Form_Window(form));3334form->curpage = page;3335last_field = field_on_page = form->field[form->page[page].smin];3336do3337{3338if (field_on_page->opts & O_VISIBLE)3339if ((res=Display_Field(field_on_page))!=E_OK)3340return(res);3341field_on_page = field_on_page->snext;3342} while(field_on_page != last_field);33433344if (field)3345res = _nc_Set_Current_Field(form,field);3346else3347/* N.B.: we don't encapsulate this by Inter_Field_Navigation(),3348because this is already executed in a page navigation3349context that contains field navigation3350*/3351res = FN_First_Field(form);3352}3353return(res);3354}33553356/*---------------------------------------------------------------------------3357| Facility : libnform3358| Function : static int Next_Page_Number(const FORM * form)3359|3360| Description : Calculate the page number following the current page3361| number. This cycles if the highest page number is3362| reached.3363|3364| Return Values : The next page number3365+--------------------------------------------------------------------------*/3366INLINE static int Next_Page_Number(const FORM * form)3367{3368return (form->curpage + 1) % form->maxpage;3369}33703371/*---------------------------------------------------------------------------3372| Facility : libnform3373| Function : static int Previous_Page_Number(const FORM * form)3374|3375| Description : Calculate the page number before the current page3376| number. This cycles if the first page number is3377| reached.3378|3379| Return Values : The previous page number3380+--------------------------------------------------------------------------*/3381INLINE static int Previous_Page_Number(const FORM * form)3382{3383return (form->curpage!=0 ? form->curpage - 1 : form->maxpage - 1);3384}33853386/*----------------------------------------------------------------------------3387Page Navigation routines3388--------------------------------------------------------------------------*/33893390/*---------------------------------------------------------------------------3391| Facility : libnform3392| Function : static int Page_Navigation(3393| int (* const fct) (FORM *),3394| FORM * form)3395|3396| Description : Generic behaviour for changing a page. This means3397| that the field is left and a new field is entered.3398| So the field must be validated and the field init/term3399| hooks must be called. Because also the page is changed,3400| the forms init/term hooks must be called also.3401|3402| Return Values : E_OK - success3403| E_INVALID_FIELD - field is invalid3404| some other - error from subordinate call3405+--------------------------------------------------------------------------*/3406static int Page_Navigation(int (* const fct) (FORM *), FORM * form)3407{3408int res;34093410if (!_nc_Internal_Validation(form))3411res = E_INVALID_FIELD;3412else3413{3414Call_Hook(form,fieldterm);3415Call_Hook(form,formterm);3416res = fct(form);3417Call_Hook(form,forminit);3418Call_Hook(form,fieldinit);3419}3420return res;3421}34223423/*---------------------------------------------------------------------------3424| Facility : libnform3425| Function : static int PN_Next_Page(FORM * form)3426|3427| Description : Move to the next page of the form3428|3429| Return Values : E_OK - success3430| != E_OK - error from subordinate call3431+--------------------------------------------------------------------------*/3432static int PN_Next_Page(FORM * form)3433{3434return _nc_Set_Form_Page(form,Next_Page_Number(form),(FIELD *)0);3435}34363437/*---------------------------------------------------------------------------3438| Facility : libnform3439| Function : static int PN_Previous_Page(FORM * form)3440|3441| Description : Move to the previous page of the form3442|3443| Return Values : E_OK - success3444| != E_OK - error from subordinate call3445+--------------------------------------------------------------------------*/3446static int PN_Previous_Page(FORM * form)3447{3448return _nc_Set_Form_Page(form,Previous_Page_Number(form),(FIELD *)0);3449}34503451/*---------------------------------------------------------------------------3452| Facility : libnform3453| Function : static int PN_First_Page(FORM * form)3454|3455| Description : Move to the first page of the form3456|3457| Return Values : E_OK - success3458| != E_OK - error from subordinate call3459+--------------------------------------------------------------------------*/3460static int PN_First_Page(FORM * form)3461{3462return _nc_Set_Form_Page(form,0,(FIELD *)0);3463}34643465/*---------------------------------------------------------------------------3466| Facility : libnform3467| Function : static int PN_Last_Page(FORM * form)3468|3469| Description : Move to the last page of the form3470|3471| Return Values : E_OK - success3472| != E_OK - error from subordinate call3473+--------------------------------------------------------------------------*/3474static int PN_Last_Page(FORM * form)3475{3476return _nc_Set_Form_Page(form,form->maxpage-1,(FIELD *)0);3477}3478/*----------------------------------------------------------------------------3479END of Field Navigation routines3480--------------------------------------------------------------------------*/34813482/*----------------------------------------------------------------------------3483Helper routines for the core form driver.3484--------------------------------------------------------------------------*/34853486/*---------------------------------------------------------------------------3487| Facility : libnform3488| Function : static int Data_Entry(FORM * form,int c)3489|3490| Description : Enter character c into at the current position of the3491| current field of the form.3492|3493| Return Values : E_OK -3494| E_REQUEST_DENIED -3495| E_SYSTEM_ERROR -3496+--------------------------------------------------------------------------*/3497static int Data_Entry(FORM * form, int c)3498{3499FIELD *field = form->current;3500int result = E_REQUEST_DENIED;35013502if ( (field->opts & O_EDIT)3503#if FIX_FORM_INACTIVE_BUG3504&& (field->opts & O_ACTIVE)3505#endif3506)3507{3508if ( (field->opts & O_BLANK) &&3509First_Position_In_Current_Field(form) &&3510!(form->status & _FCHECK_REQUIRED) &&3511!(form->status & _WINDOW_MODIFIED) )3512werase(form->w);35133514if (form->status & _OVLMODE)3515{3516waddch(form->w,(chtype)c);3517}3518else /* no _OVLMODE */3519{3520bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);35213522if (!(There_Is_Room ||3523((Single_Line_Field(field) && Growable(field)))))3524return E_REQUEST_DENIED;35253526if (!There_Is_Room && !Field_Grown(field,1))3527return E_SYSTEM_ERROR;35283529winsch(form->w,(chtype)c);3530}35313532if ((result=Wrapping_Not_Necessary_Or_Wrapping_Ok(form))==E_OK)3533{3534bool End_Of_Field= (((field->drows-1)==form->currow) &&3535((field->dcols-1)==form->curcol));3536form->status |= _WINDOW_MODIFIED;3537if (End_Of_Field && !Growable(field) && (field->opts & O_AUTOSKIP))3538result = Inter_Field_Navigation(FN_Next_Field,form);3539else3540{3541if (End_Of_Field && Growable(field) && !Field_Grown(field,1))3542result = E_SYSTEM_ERROR;3543else3544{3545IFN_Next_Character(form);3546result = E_OK;3547}3548}3549}3550}3551return result;3552}35533554/* Structure to describe the binding of a request code to a function.3555The member keycode codes the request value as well as the generic3556routine to use for the request. The code for the generic routine3557is coded in the upper 16 Bits while the request code is coded in3558the lower 16 bits.35593560In terms of C++ you might think of a request as a class with a3561virtual method "perform". The different types of request are3562derived from this base class and overload (or not) the base class3563implementation of perform.3564*/3565typedef struct {3566int keycode; /* must be at least 32 bit: hi:mode, lo: key */3567int (*cmd)(FORM *); /* low level driver routine for this key */3568} Binding_Info;35693570/* You may see this is the class-id of the request type class */3571#define ID_PN (0x00000000) /* Page navigation */3572#define ID_FN (0x00010000) /* Inter-Field navigation */3573#define ID_IFN (0x00020000) /* Intra-Field navigation */3574#define ID_VSC (0x00030000) /* Vertical Scrolling */3575#define ID_HSC (0x00040000) /* Horizontal Scrolling */3576#define ID_FE (0x00050000) /* Field Editing */3577#define ID_EM (0x00060000) /* Edit Mode */3578#define ID_FV (0x00070000) /* Field Validation */3579#define ID_CH (0x00080000) /* Choice */3580#define ID_Mask (0xffff0000)3581#define Key_Mask (0x0000ffff)3582#define ID_Shft (16)35833584/* This array holds all the Binding Infos */3585static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =3586{3587{ REQ_NEXT_PAGE |ID_PN ,PN_Next_Page},3588{ REQ_PREV_PAGE |ID_PN ,PN_Previous_Page},3589{ REQ_FIRST_PAGE |ID_PN ,PN_First_Page},3590{ REQ_LAST_PAGE |ID_PN ,PN_Last_Page},35913592{ REQ_NEXT_FIELD |ID_FN ,FN_Next_Field},3593{ REQ_PREV_FIELD |ID_FN ,FN_Previous_Field},3594{ REQ_FIRST_FIELD |ID_FN ,FN_First_Field},3595{ REQ_LAST_FIELD |ID_FN ,FN_Last_Field},3596{ REQ_SNEXT_FIELD |ID_FN ,FN_Sorted_Next_Field},3597{ REQ_SPREV_FIELD |ID_FN ,FN_Sorted_Previous_Field},3598{ REQ_SFIRST_FIELD |ID_FN ,FN_Sorted_First_Field},3599{ REQ_SLAST_FIELD |ID_FN ,FN_Sorted_Last_Field},3600{ REQ_LEFT_FIELD |ID_FN ,FN_Left_Field},3601{ REQ_RIGHT_FIELD |ID_FN ,FN_Right_Field},3602{ REQ_UP_FIELD |ID_FN ,FN_Up_Field},3603{ REQ_DOWN_FIELD |ID_FN ,FN_Down_Field},36043605{ REQ_NEXT_CHAR |ID_IFN ,IFN_Next_Character},3606{ REQ_PREV_CHAR |ID_IFN ,IFN_Previous_Character},3607{ REQ_NEXT_LINE |ID_IFN ,IFN_Next_Line},3608{ REQ_PREV_LINE |ID_IFN ,IFN_Previous_Line},3609{ REQ_NEXT_WORD |ID_IFN ,IFN_Next_Word},3610{ REQ_PREV_WORD |ID_IFN ,IFN_Previous_Word},3611{ REQ_BEG_FIELD |ID_IFN ,IFN_Beginning_Of_Field},3612{ REQ_END_FIELD |ID_IFN ,IFN_End_Of_Field},3613{ REQ_BEG_LINE |ID_IFN ,IFN_Beginning_Of_Line},3614{ REQ_END_LINE |ID_IFN ,IFN_End_Of_Line},3615{ REQ_LEFT_CHAR |ID_IFN ,IFN_Left_Character},3616{ REQ_RIGHT_CHAR |ID_IFN ,IFN_Right_Character},3617{ REQ_UP_CHAR |ID_IFN ,IFN_Up_Character},3618{ REQ_DOWN_CHAR |ID_IFN ,IFN_Down_Character},36193620{ REQ_NEW_LINE |ID_FE ,FE_New_Line},3621{ REQ_INS_CHAR |ID_FE ,FE_Insert_Character},3622{ REQ_INS_LINE |ID_FE ,FE_Insert_Line},3623{ REQ_DEL_CHAR |ID_FE ,FE_Delete_Character},3624{ REQ_DEL_PREV |ID_FE ,FE_Delete_Previous},3625{ REQ_DEL_LINE |ID_FE ,FE_Delete_Line},3626{ REQ_DEL_WORD |ID_FE ,FE_Delete_Word},3627{ REQ_CLR_EOL |ID_FE ,FE_Clear_To_End_Of_Line},3628{ REQ_CLR_EOF |ID_FE ,FE_Clear_To_End_Of_Form},3629{ REQ_CLR_FIELD |ID_FE ,FE_Clear_Field},36303631{ REQ_OVL_MODE |ID_EM ,EM_Overlay_Mode},3632{ REQ_INS_MODE |ID_EM ,EM_Insert_Mode},36333634{ REQ_SCR_FLINE |ID_VSC ,VSC_Scroll_Line_Forward},3635{ REQ_SCR_BLINE |ID_VSC ,VSC_Scroll_Line_Backward},3636{ REQ_SCR_FPAGE |ID_VSC ,VSC_Scroll_Page_Forward},3637{ REQ_SCR_BPAGE |ID_VSC ,VSC_Scroll_Page_Backward},3638{ REQ_SCR_FHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Forward},3639{ REQ_SCR_BHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Backward},36403641{ REQ_SCR_FCHAR |ID_HSC ,HSC_Scroll_Char_Forward},3642{ REQ_SCR_BCHAR |ID_HSC ,HSC_Scroll_Char_Backward},3643{ REQ_SCR_HFLINE |ID_HSC ,HSC_Horizontal_Line_Forward},3644{ REQ_SCR_HBLINE |ID_HSC ,HSC_Horizontal_Line_Backward},3645{ REQ_SCR_HFHALF |ID_HSC ,HSC_Horizontal_Half_Line_Forward},3646{ REQ_SCR_HBHALF |ID_HSC ,HSC_Horizontal_Half_Line_Backward},36473648{ REQ_VALIDATION |ID_FV ,FV_Validation},36493650{ REQ_NEXT_CHOICE |ID_CH ,CR_Next_Choice},3651{ REQ_PREV_CHOICE |ID_CH ,CR_Previous_Choice}3652};36533654/*---------------------------------------------------------------------------3655| Facility : libnform3656| Function : int form_driver(FORM * form,int c)3657|3658| Description : This is the workhorse of the forms system. It checks3659| to determine whether the character c is a request or3660| data. If it is a request, the form driver executes3661| the request and returns the result. If it is data3662| (printable character), it enters the data into the3663| current position in the current field. If it is not3664| recognized, the form driver assumes it is an application3665| defined command and returns E_UNKNOWN_COMMAND.3666| Application defined command should be defined relative3667| to MAX_FORM_COMMAND, the maximum value of a request.3668|3669| Return Values : E_OK - success3670| E_SYSTEM_ERROR - system error3671| E_BAD_ARGUMENT - an argument is incorrect3672| E_NOT_POSTED - form is not posted3673| E_INVALID_FIELD - field contents are invalid3674| E_BAD_STATE - called from inside a hook routine3675| E_REQUEST_DENIED - request failed3676| E_UNKNOWN_COMMAND - command not known3677+--------------------------------------------------------------------------*/3678int form_driver(FORM * form, int c)3679{3680const Binding_Info* BI = (Binding_Info *)0;3681int res = E_UNKNOWN_COMMAND;36823683if (!form)3684RETURN(E_BAD_ARGUMENT);36853686if (!(form->field))3687RETURN(E_NOT_CONNECTED);36883689assert(form->page != 0);36903691if (c==FIRST_ACTIVE_MAGIC)3692{3693form->current = _nc_First_Active_Field(form);3694return E_OK;3695}36963697assert(form->current &&3698form->current->buf &&3699(form->current->form == form)3700);37013702if ( form->status & _IN_DRIVER )3703RETURN(E_BAD_STATE);37043705if ( !( form->status & _POSTED ) )3706RETURN(E_NOT_POSTED);37073708if ((c>=MIN_FORM_COMMAND && c<=MAX_FORM_COMMAND) &&3709((bindings[c-MIN_FORM_COMMAND].keycode & Key_Mask) == c))3710BI = &(bindings[c-MIN_FORM_COMMAND]);37113712if (BI)3713{3714typedef int (*Generic_Method)(int (* const)(FORM *),FORM *);3715static const Generic_Method Generic_Methods[] =3716{3717Page_Navigation, /* overloaded to call field&form hooks */3718Inter_Field_Navigation, /* overloaded to call field hooks */3719NULL, /* Intra-Field is generic */3720Vertical_Scrolling, /* Overloaded to check multi-line */3721Horizontal_Scrolling, /* Overloaded to check single-line */3722Field_Editing, /* Overloaded to mark modification */3723NULL, /* Edit Mode is generic */3724NULL, /* Field Validation is generic */3725NULL /* Choice Request is generic */3726};3727size_t nMethods = (sizeof(Generic_Methods)/sizeof(Generic_Methods[0]));3728size_t method = ((BI->keycode & ID_Mask) >> ID_Shft) & 0xffff;37293730if ( (method >= nMethods) || !(BI->cmd) )3731res = E_SYSTEM_ERROR;3732else3733{3734Generic_Method fct = Generic_Methods[method];3735if (fct)3736res = fct(BI->cmd,form);3737else3738res = (BI->cmd)(form);3739}3740}3741else3742{3743if (!(c & (~(int)MAX_REGULAR_CHARACTER)) &&3744isprint((unsigned char)c) &&3745Check_Char(form->current->type,c,3746(TypeArgument *)(form->current->arg)))3747res = Data_Entry(form,c);3748}3749_nc_Refresh_Current_Field(form);3750RETURN(res);3751}37523753/*----------------------------------------------------------------------------3754Field-Buffer manipulation routines.3755The effects of setting a buffer is tightly coupled to the core of the form3756driver logic. This is especially true in the case of growable fields.3757So I don't separate this into an own module.3758--------------------------------------------------------------------------*/37593760/*---------------------------------------------------------------------------3761| Facility : libnform3762| Function : int set_field_buffer(FIELD *field,3763| int buffer, char *value)3764|3765| Description : Set the given buffer of the field to the given value.3766| Buffer 0 stores the displayed content of the field.3767| For dynamic fields this may grow the fieldbuffers if3768| the length of the value exceeds the current buffer3769| length. For buffer 0 only printable values are allowed.3770| For static fields, the value needs not to be zero ter-3771| minated. It is copied up to the length of the buffer.3772|3773| Return Values : E_OK - success3774| E_BAD_ARGUMENT - invalid argument3775| E_SYSTEM_ERROR - system error3776+--------------------------------------------------------------------------*/3777int set_field_buffer(FIELD * field, int buffer, const char * value)3778{3779char *s, *p;3780int res = E_OK;3781unsigned int len;37823783if ( !field || !value || ((buffer < 0)||(buffer > field->nbuf)) )3784RETURN(E_BAD_ARGUMENT);37853786len = Buffer_Length(field);37873788if (buffer==0)3789{3790const char *v;3791unsigned int i = 0;37923793for(v=value; *v && (i<len); v++,i++)3794{3795if (!isprint((unsigned char)*v))3796RETURN(E_BAD_ARGUMENT);3797}3798}37993800if (Growable(field))3801{3802/* for a growable field we must assume zero terminated strings, because3803somehow we have to detect the length of what should be copied.3804*/3805unsigned int vlen = strlen(value);3806if (vlen > len)3807{3808if (!Field_Grown(field,3809(int)(1 + (vlen-len)/((field->rows+field->nrow)*field->cols))))3810RETURN(E_SYSTEM_ERROR);38113812/* in this case we also have to check, whether or not the remaining3813characters in value are also printable for buffer 0. */3814if (buffer==0)3815{3816unsigned int i;38173818for(i=len; i<vlen; i++)3819if (!isprint((int)(value[i])))3820RETURN(E_BAD_ARGUMENT);3821}3822len = vlen;3823}3824}38253826p = Address_Of_Nth_Buffer(field,buffer);38273828#if HAVE_MEMCCPY3829s = memccpy(p,value,0,len);3830#else3831for(s=(char *)value; *s && (s < (value+len)); s++)3832p[s-value] = *s;3833if (s < (value+len))3834{3835int off = s-value;3836p[off] = *s++;3837s = p + (s-value);3838}3839else3840s=(char *)0;3841#endif38423843if (s)3844{ /* this means, value was null terminated and not greater than the3845buffer. We have to pad with blanks. Please note that due to memccpy3846logic s points after the terminating null. */3847s--; /* now we point to the terminator. */3848assert(len >= (unsigned int)(s-p));3849if (len > (unsigned int)(s-p))3850memset(s,C_BLANK,len-(unsigned int)(s-p));3851}38523853if (buffer==0)3854{3855int syncres;3856if (((syncres=Synchronize_Field( field ))!=E_OK) &&3857(res==E_OK))3858res = syncres;3859if (((syncres=Synchronize_Linked_Fields(field ))!=E_OK) &&3860(res==E_OK))3861res = syncres;3862}3863RETURN(res);3864}38653866/*---------------------------------------------------------------------------3867| Facility : libnform3868| Function : char *field_buffer(const FIELD *field,int buffer)3869|3870| Description : Return the address of the buffer for the field.3871|3872| Return Values : Pointer to buffer or NULL if arguments were invalid.3873+--------------------------------------------------------------------------*/3874char *field_buffer(const FIELD * field, int buffer)3875{3876if (field && (buffer >= 0) && (buffer <= field->nbuf))3877return Address_Of_Nth_Buffer(field,buffer);3878else3879return (char *)0;3880}38813882/* frm_driver.c ends here */388338843885