1 /****************************************************************************
2 * Copyright 2018-2020,2021 Thomas E. Dickey *
3 * Copyright 1998-2016,2017 Free Software Foundation, Inc. *
5 * Permission is hereby granted, free of charge, to any person obtaining a *
6 * copy of this software and associated documentation files (the *
7 * "Software"), to deal in the Software without restriction, including *
8 * without limitation the rights to use, copy, modify, merge, publish, *
9 * distribute, distribute with modifications, sublicense, and/or sell *
10 * copies of the Software, and to permit persons to whom the Software is *
11 * furnished to do so, subject to the following conditions: *
13 * The above copyright notice and this permission notice shall be included *
14 * in all copies or substantial portions of the Software. *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
24 * Except as contained in this notice, the name(s) of the above copyright *
25 * holders shall not be used in advertising or otherwise to promote the *
26 * sale, use or other dealings in this Software without prior written *
28 ****************************************************************************/
30 /****************************************************************************
31 * Author: Juergen Pfeifer, 1995,1997 *
32 ****************************************************************************/
34 #include "form.priv.h"
36 MODULE_ID("$Id: frm_driver.c,v 1.135 2021/09/01 23:34:01 tom Exp $")
38 /*----------------------------------------------------------------------------
39 This is the core module of the form library. It contains the majority
40 of the driver routines as well as the form_driver function.
42 Essentially this module is nearly the whole library. This is because
43 all the functions in this module depends on some others in the module,
44 so it makes no sense to split them into separate files because they
45 will always be linked together. The only acceptable concern is turnaround
46 time for this module, but now we have all Pentiums or RISCs, so what!
48 The driver routines are grouped into nine generic categories:
50 a) Page Navigation ( all functions prefixed by PN_ )
51 The current page of the form is left and some new page is
53 b) Inter-Field Navigation ( all functions prefixed by FN_ )
54 The current field of the form is left and some new field is
56 c) Intra-Field Navigation ( all functions prefixed by IFN_ )
57 The current position in the current field is changed.
58 d) Vertical Scrolling ( all functions prefixed by VSC_ )
59 Essentially this is a specialization of Intra-Field navigation.
60 It has to check for a multi-line field.
61 e) Horizontal Scrolling ( all functions prefixed by HSC_ )
62 Essentially this is a specialization of Intra-Field navigation.
63 It has to check for a single-line field.
64 f) Field Editing ( all functions prefixed by FE_ )
65 The content of the current field is changed
66 g) Edit Mode requests ( all functions prefixed by EM_ )
67 Switching between insert and overlay mode
68 h) Field-Validation requests ( all functions prefixed by FV_ )
69 Perform verifications of the field.
70 i) Choice requests ( all functions prefixed by CR_ )
71 Requests to enumerate possible field values
72 --------------------------------------------------------------------------*/
74 /*----------------------------------------------------------------------------
75 Some remarks on the placements of assert() macros :
76 I use them only on "strategic" places, i.e. top level entries where
77 I want to make sure that things are set correctly. Throughout subordinate
78 routines I omit them mostly.
79 --------------------------------------------------------------------------*/
82 Some options that may effect compatibility in behavior to SVr4 forms,
83 but they are here to allow a more intuitive and user friendly behavior of
84 our form implementation. This doesn't affect the API, so we feel it is
87 The initial implementation tries to stay very close with the behavior
88 of the original SVr4 implementation, although in some areas it is quite
89 clear that this isn't the most appropriate way. As far as possible this
90 sources will allow you to build a forms lib that behaves quite similar
91 to SVr4, but now and in the future we will give you better options.
92 Perhaps at some time we will make this configurable at runtime.
95 /* Implement a more user-friendly previous/next word behavior */
96 #define FRIENDLY_PREV_NEXT_WORD (1)
97 /* Fix the wrong behavior for forms with all fields inactive */
98 #define FIX_FORM_INACTIVE_BUG (1)
99 /* Allow dynamic field growth also when navigating past the end */
100 #define GROW_IF_NAVIGATE (1)
102 #if USE_WIDEC_SUPPORT
103 #define myADDNSTR(w, s, n) wide_waddnstr(w, s, n)
104 #define myINSNSTR(w, s, n) wide_winsnstr(w, s, n)
105 #define myINNSTR(w, s, n) wide_winnstr(w, s, n)
106 #define myWCWIDTH(w, y, x) cell_width(w, y, x)
108 #define myADDNSTR(w, s, n) waddnstr(w, s, n)
109 #define myINSNSTR(w, s, n) winsnstr(w, s, n)
110 #define myINNSTR(w, s, n) winnstr(w, s, n)
111 #define myWCWIDTH(w, y, x) 1
114 /*----------------------------------------------------------------------------
115 Forward references to some internally used static functions
116 --------------------------------------------------------------------------*/
117 static int Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form);
118 static int FN_Next_Field(FORM *form);
119 static int FN_Previous_Field(FORM *form);
120 static int FE_New_Line(FORM *);
121 static int FE_Delete_Previous(FORM *);
123 /*----------------------------------------------------------------------------
126 Some Remarks on that: I use the convention to use UPPERCASE for constants
127 defined by Macros. If I provide a macro as a kind of inline routine to
128 provide some logic, I use my Upper_Lower case style.
129 --------------------------------------------------------------------------*/
131 /* Calculate the position of a single row in a field buffer */
132 #define Position_Of_Row_In_Buffer(field,row) ((row)*(field)->dcols)
134 /* Calculate start address for the field's buffer# N */
135 #define Address_Of_Nth_Buffer(field,N) \
136 ((field)->buf + (N)*(1+Buffer_Length(field)))
138 /* Calculate the start address of the row in the field's specified buffer# N */
139 #define Address_Of_Row_In_Nth_Buffer(field,N,row) \
140 (Address_Of_Nth_Buffer(field,N) + Position_Of_Row_In_Buffer(field,row))
142 /* Calculate the start address of the row in the field's primary buffer */
143 #define Address_Of_Row_In_Buffer(field,row) \
144 Address_Of_Row_In_Nth_Buffer(field,0,row)
146 /* Calculate the start address of the row in the form's current field
148 #define Address_Of_Current_Row_In_Nth_Buffer(form,N) \
149 Address_Of_Row_In_Nth_Buffer((form)->current,N,(form)->currow)
151 /* Calculate the start address of the row in the form's current field
153 #define Address_Of_Current_Row_In_Buffer(form) \
154 Address_Of_Current_Row_In_Nth_Buffer(form,0)
156 /* Calculate the address of the cursor in the form's current field
158 #define Address_Of_Current_Position_In_Nth_Buffer(form,N) \
159 (Address_Of_Current_Row_In_Nth_Buffer(form,N) + (form)->curcol)
161 /* Calculate the address of the cursor in the form's current field
163 #define Address_Of_Current_Position_In_Buffer(form) \
164 Address_Of_Current_Position_In_Nth_Buffer(form,0)
166 /* Logic to decide whether or not a field is actually a field with
167 vertical or horizontal scrolling */
168 #define Is_Scroll_Field(field) \
169 (((field)->drows > (field)->rows) || \
170 ((field)->dcols > (field)->cols))
172 /* Logic to decide whether or not a field needs to have an individual window
173 instead of a derived window because it contains invisible parts.
174 This is true for non-public fields and for scrollable fields. */
175 #define Has_Invisible_Parts(field) \
176 (!(Field_Has_Option(field, O_PUBLIC)) || \
177 Is_Scroll_Field(field))
179 /* Logic to decide whether or not a field needs justification */
180 #define Justification_Allowed(field) \
181 (((field)->just != NO_JUSTIFICATION) && \
182 (Single_Line_Field(field)) && \
183 ((Field_Has_Option(field, O_STATIC) && \
184 ((field)->dcols == (field)->cols)) || \
185 Field_Has_Option(field, O_DYNAMIC_JUSTIFY)))
187 /* Logic to determine whether or not a dynamic field may still grow */
188 #define Growable(field) ((field)->status & _MAY_GROW)
190 /* Macro to set the attributes for a field's window */
191 #define Set_Field_Window_Attributes(field,win) \
192 ( wbkgdset((win),(chtype)((chtype)((field)->pad) | (field)->back)), \
193 (void) wattrset((win), (int)(field)->fore) )
195 /* Logic to decide whether or not a field really appears on the form */
196 #define Field_Really_Appears(field) \
198 (field->form->status & _POSTED) &&\
199 (Field_Has_Option(field, O_VISIBLE)) &&\
200 (field->page == field->form->curpage))
202 /* Logic to determine whether or not we are on the first position in the
204 #define First_Position_In_Current_Field(form) \
205 (((form)->currow==0) && ((form)->curcol==0))
207 #define Minimum(a,b) (((a)<=(b)) ? (a) : (b))
208 #define Maximum(a,b) (((a)>=(b)) ? (a) : (b))
210 /*----------------------------------------------------------------------------
212 --------------------------------------------------------------------------*/
213 static FIELD_CELL myBLANK = BLANK;
214 static FIELD_CELL myZEROS;
218 check_pos(FORM *form, int lineno)
224 getyx(form->w, y, x);
225 if (y != form->currow || x != form->curcol)
227 T(("CHECKPOS %s@%d have position %d,%d vs want %d,%d",
230 form->currow, form->curcol));
234 #define CHECKPOS(form) check_pos(form, __LINE__)
236 #define CHECKPOS(form) /* nothing */
239 /*----------------------------------------------------------------------------
240 Wide-character special functions
241 --------------------------------------------------------------------------*/
242 #if USE_WIDEC_SUPPORT
243 /* add like waddnstr, but using cchar_t* rather than char*
246 wide_waddnstr(WINDOW *w, const cchar_t *s, int n)
252 if ((rc = wadd_wch(w, s)) != OK)
259 /* insert like winsnstr, but using cchar_t* rather than char*
261 * X/Open Curses has no close equivalent; inserts are done only with wchar_t
265 wide_winsnstr(WINDOW *w, const cchar_t *s, int n)
274 if ((code = wins_wch(w, s++)) != OK)
276 if ((code = wmove(w, y, x + 1)) != OK)
282 /* retrieve like winnstr, but using cchar_t*, rather than char*.
284 * X/Open Curses' closest equivalent, win_wchnstr(), is inconsistent with
285 * winnstr(), since it returns OK rather than the number of items transferred.
288 wide_winnstr(WINDOW *w, cchar_t *s, int n)
292 win_wchnstr(w, s, n);
294 * This function is used to extract the text only from the window.
295 * Strip attributes and color from the string so they will not be added
296 * back when copying the string to the window.
298 for (x = 0; x < n; ++x)
300 RemAttr(s[x], A_ATTRIBUTES);
307 * Returns the column of the base of the given cell.
310 cell_base(WINDOW *win, int y, int x)
314 while (LEGALYX(win, y, x))
316 cchar_t *data = &(win->_line[y].text[x]);
318 if (isWidecBase(CHDEREF(data)) || !isWidecExt(CHDEREF(data)))
329 * Returns the number of columns needed for the given cell in a window.
332 cell_width(WINDOW *win, int y, int x)
336 if (LEGALYX(win, y, x))
338 cchar_t *data = &(win->_line[y].text[x]);
340 if (isWidecExt(CHDEREF(data)))
342 /* recur, providing the number of columns to the next character */
343 result = cell_width(win, y, x - 1);
347 result = wcwidth(CharOf(CHDEREF(data)));
354 * There is no wide-character function such as wdel_wch(), so we must find
355 * all of the cells that comprise a multi-column character and delete them
359 delete_char(FORM *form)
361 int cells = cell_width(form->w, form->currow, form->curcol);
363 form->curcol = cell_base(form->w, form->currow, form->curcol);
364 wmove(form->w, form->currow, form->curcol);
370 #define DeleteChar(form) delete_char(form)
372 #define DeleteChar(form) \
373 wmove((form)->w, (form)->currow, (form)->curcol), \
377 /*---------------------------------------------------------------------------
378 | Facility : libnform
379 | Function : static char *Get_Start_Of_Data(char * buf, int blen)
381 | Description : Return pointer to first non-blank position in buffer.
382 | If buffer is empty return pointer to buffer itself.
384 | Return Values : Pointer to first non-blank position in buffer
385 +--------------------------------------------------------------------------*/
386 NCURSES_INLINE static FIELD_CELL *
387 Get_Start_Of_Data(FIELD_CELL *buf, int blen)
390 FIELD_CELL *end = &buf[blen];
392 assert(buf && blen >= 0);
393 while ((p < end) && ISBLANK(*p))
395 return ((p == end) ? buf : p);
398 /*---------------------------------------------------------------------------
399 | Facility : libnform
400 | Function : static char *After_End_Of_Data(char * buf, int blen)
402 | Description : Return pointer after last non-blank position in buffer.
403 | If buffer is empty, return pointer to buffer itself.
405 | Return Values : Pointer to position after last non-blank position in
407 +--------------------------------------------------------------------------*/
408 NCURSES_INLINE static FIELD_CELL *
409 After_End_Of_Data(FIELD_CELL *buf, int blen)
411 FIELD_CELL *p = &buf[blen];
413 assert(buf && blen >= 0);
414 while ((p > buf) && ISBLANK(p[-1]))
419 /*---------------------------------------------------------------------------
420 | Facility : libnform
421 | Function : static char *Get_First_Whitespace_Character(
422 | char * buf, int blen)
424 | Description : Position to the first whitespace character.
426 | Return Values : Pointer to first whitespace character in buffer.
427 +--------------------------------------------------------------------------*/
428 NCURSES_INLINE static FIELD_CELL *
429 Get_First_Whitespace_Character(FIELD_CELL *buf, int blen)
432 FIELD_CELL *end = &p[blen];
434 assert(buf && blen >= 0);
435 while ((p < end) && !ISBLANK(*p))
437 return ((p == end) ? buf : p);
440 /*---------------------------------------------------------------------------
441 | Facility : libnform
442 | Function : static char *After_Last_Whitespace_Character(
443 | char * buf, int blen)
445 | Description : Get the position after the last whitespace character.
447 | Return Values : Pointer to position after last whitespace character in
449 +--------------------------------------------------------------------------*/
450 NCURSES_INLINE static FIELD_CELL *
451 After_Last_Whitespace_Character(FIELD_CELL *buf, int blen)
453 FIELD_CELL *p = &buf[blen];
455 assert(buf && blen >= 0);
456 while ((p > buf) && !ISBLANK(p[-1]))
461 /* Set this to 1 to use the div_t version. This is a good idea if your
462 compiler has an intrinsic div() support. Unfortunately GNU-C has it
464 N.B.: This only works if form->curcol follows immediately form->currow
465 and both are of type int.
467 #define USE_DIV_T (0)
469 /*---------------------------------------------------------------------------
470 | Facility : libnform
471 | Function : static void Adjust_Cursor_Position(
472 | FORM * form, const char * pos)
474 | Description : Set current row and column of the form to values
475 | corresponding to the buffer position.
478 +--------------------------------------------------------------------------*/
479 NCURSES_INLINE static void
480 Adjust_Cursor_Position(FORM *form, const FIELD_CELL *pos)
485 field = form->current;
486 assert(pos >= field->buf && field->dcols > 0);
487 idx = (int)(pos - field->buf);
489 *((div_t *) & (form->currow)) = div(idx, field->dcols);
491 form->currow = idx / field->dcols;
492 form->curcol = idx - field->cols * form->currow;
494 if (field->drows < form->currow)
498 /*---------------------------------------------------------------------------
499 | Facility : libnform
500 | Function : static void Buffer_To_Window(
501 | const FIELD * field,
504 | Description : Copy the buffer to the window. If it is a multi-line
505 | field, the buffer is split to the lines of the
506 | window without any editing.
509 +--------------------------------------------------------------------------*/
511 Buffer_To_Window(const FIELD *field, WINDOW *win)
518 assert(win && field);
521 width = getmaxx(win);
522 height = getmaxy(win);
524 for (row = 0, pBuffer = field->buf;
526 row++, pBuffer += width)
530 if ((len = (int)(After_End_Of_Data(pBuffer, width) - pBuffer)) > 0)
533 myADDNSTR(win, pBuffer, len);
539 /*---------------------------------------------------------------------------
540 | Facility : libnform
541 | Function : void _nc_get_fieldbuffer(
546 | Description : Copy the content of the window into the buffer.
547 | The multiple lines of a window are simply
548 | concatenated into the buffer. Pad characters in
549 | the window will be replaced by blanks in the buffer.
552 +--------------------------------------------------------------------------*/
554 _nc_get_fieldbuffer(FORM *form, FIELD *field, FIELD_CELL *buf)
562 assert(form && field && buf);
569 height = getmaxy(win);
571 for (row = 0; (row < height) && (row < field->drows); row++)
574 len += myINNSTR(win, p + len, field->dcols);
578 /* replace visual padding character by blanks in buffer */
583 for (i = 0; i < len; i++, p++)
585 if ((unsigned long)CharOf(*p) == ChCharOf(pad)
586 #if USE_WIDEC_SUPPORT
595 /*---------------------------------------------------------------------------
596 | Facility : libnform
597 | Function : static void Window_To_Buffer(
601 | Description : Copy the content of the window into the buffer.
602 | The multiple lines of a window are simply
603 | concatenated into the buffer. Pad characters in
604 | the window will be replaced by blanks in the buffer.
607 +--------------------------------------------------------------------------*/
609 Window_To_Buffer(FORM *form, FIELD *field)
611 _nc_get_fieldbuffer(form, field, field->buf);
614 /*---------------------------------------------------------------------------
615 | Facility : libnform
616 | Function : static void Synchronize_Buffer(FORM * form)
618 | Description : If there was a change, copy the content of the
619 | window into the buffer, so the buffer is synchronized
620 | with the windows content. We have to indicate that the
621 | buffer needs validation due to the change.
624 +--------------------------------------------------------------------------*/
625 NCURSES_INLINE static void
626 Synchronize_Buffer(FORM *form)
628 if (form->status & _WINDOW_MODIFIED)
630 ClrStatus(form, _WINDOW_MODIFIED);
631 SetStatus(form, _FCHECK_REQUIRED);
632 Window_To_Buffer(form, form->current);
633 wmove(form->w, form->currow, form->curcol);
637 /*---------------------------------------------------------------------------
638 | Facility : libnform
639 | Function : static bool Field_Grown( FIELD *field, int amount)
641 | Description : This function is called for growable dynamic fields
642 | only. It has to increase the buffers and to allocate
643 | a new window for this field.
644 | This function has the side effect to set a new
645 | field-buffer pointer, the dcols and drows values
646 | as well as a new current Window for the field.
648 | Return Values : TRUE - field successfully increased
649 | FALSE - there was some error
650 +--------------------------------------------------------------------------*/
652 Field_Grown(FIELD *field, int amount)
656 if (field && Growable(field))
658 bool single_line_field = Single_Line_Field(field);
659 int old_buflen = Buffer_Length(field);
661 int old_dcols = field->dcols;
662 int old_drows = field->drows;
663 FIELD_CELL *oldbuf = field->buf;
667 FORM *form = field->form;
668 bool need_visual_update = ((form != (FORM *)0) &&
669 (form->status & _POSTED) &&
670 (form->current == field));
672 if (need_visual_update)
673 Synchronize_Buffer(form);
675 if (single_line_field)
677 growth = field->cols * amount;
679 growth = Minimum(field->maxgrow - field->dcols, growth);
680 field->dcols += growth;
681 if (field->dcols == field->maxgrow)
682 ClrStatus(field, _MAY_GROW);
686 growth = (field->rows + field->nrow) * amount;
688 growth = Minimum(field->maxgrow - field->drows, growth);
689 field->drows += growth;
690 if (field->drows == field->maxgrow)
691 ClrStatus(field, _MAY_GROW);
693 /* drows, dcols changed, so we get really the new buffer length */
694 new_buflen = Buffer_Length(field);
695 newbuf = (FIELD_CELL *)malloc(Total_Buffer_Size(field));
698 /* restore to previous state */
699 field->dcols = old_dcols;
700 field->drows = old_drows;
701 if ((single_line_field && (field->dcols != field->maxgrow)) ||
702 (!single_line_field && (field->drows != field->maxgrow)))
703 SetStatus(field, _MAY_GROW);
707 /* Copy all the buffers. This is the reason why we can't just use
712 result = TRUE; /* allow sharing of recovery on failure */
714 T((T_CREATE("fieldcell %p"), (void *)newbuf));
716 for (i = 0; i <= field->nbuf; i++)
718 FIELD_CELL *new_bp = Address_Of_Nth_Buffer(field, i);
719 FIELD_CELL *old_bp = oldbuf + i * (1 + old_buflen);
721 for (j = 0; j < old_buflen; ++j)
722 new_bp[j] = old_bp[j];
723 while (j < new_buflen)
724 new_bp[j++] = myBLANK;
725 new_bp[new_buflen] = myZEROS;
728 #if USE_WIDEC_SUPPORT && NCURSES_EXT_FUNCS
729 if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR)
733 if (need_visual_update && result)
735 WINDOW *new_window = newpad(field->drows, field->dcols);
739 assert(form != (FORM *)0);
742 form->w = new_window;
743 Set_Field_Window_Attributes(field, form->w);
745 Buffer_To_Window(field, form->w);
747 wmove(form->w, form->currow, form->curcol);
756 /* reflect changes in linked fields */
757 if (field != field->link)
761 for (linked_field = field->link;
762 linked_field != field;
763 linked_field = linked_field->link)
765 linked_field->buf = field->buf;
766 linked_field->drows = field->drows;
767 linked_field->dcols = field->dcols;
773 /* restore old state */
774 field->dcols = old_dcols;
775 field->drows = old_drows;
777 if ((single_line_field &&
778 (field->dcols != field->maxgrow)) ||
779 (!single_line_field &&
780 (field->drows != field->maxgrow)))
781 SetStatus(field, _MAY_GROW);
789 #ifdef NCURSES_MOUSE_VERSION
790 /*---------------------------------------------------------------------------
791 | Facility : libnform
792 | Function : int Field_encloses(FIELD *field, int ry, int rx)
794 | Description : Check if the given coordinates lie within the given field.
796 | Return Values : E_OK - success
797 | E_BAD_ARGUMENT - invalid form pointer
798 | E_SYSTEM_ERROR - form has no current field or
800 +--------------------------------------------------------------------------*/
802 Field_encloses(FIELD *field, int ry, int rx)
804 T((T_CALLED("Field_encloses(%p)"), (void *)field));
807 && (field->frow + field->rows) > ry
809 && (field->fcol + field->cols) > rx)
813 RETURN(E_INVALID_FIELD);
817 /*---------------------------------------------------------------------------
818 | Facility : libnform
819 | Function : int _nc_Position_Form_Cursor(FORM * form)
821 | Description : Position the cursor in the window for the current
822 | field to be in sync. with the currow and curcol
825 | Return Values : E_OK - success
826 | E_BAD_ARGUMENT - invalid form pointer
827 | E_SYSTEM_ERROR - form has no current field or
829 +--------------------------------------------------------------------------*/
831 _nc_Position_Form_Cursor(FORM *form)
837 return (E_BAD_ARGUMENT);
839 if (!form->w || !form->current)
840 return (E_SYSTEM_ERROR);
842 field = form->current;
843 formwin = Get_Form_Window(form);
845 wmove(form->w, form->currow, form->curcol);
846 if (Has_Invisible_Parts(field))
848 /* in this case fieldwin isn't derived from formwin, so we have
849 to move the cursor in formwin by hand... */
851 field->frow + form->currow - form->toprow,
852 field->fcol + form->curcol - form->begincol);
860 /*---------------------------------------------------------------------------
861 | Facility : libnform
862 | Function : int _nc_Refresh_Current_Field(FORM * form)
864 | Description : Propagate the changes in the field's window to the
865 | window of the form.
867 | Return Values : E_OK - on success
868 | E_BAD_ARGUMENT - invalid form pointer
869 | E_SYSTEM_ERROR - general error
870 +--------------------------------------------------------------------------*/
871 static bool move_after_insert = TRUE;
873 _nc_Refresh_Current_Field(FORM *form)
879 T((T_CALLED("_nc_Refresh_Current_Field(%p)"), (void *)form));
882 RETURN(E_BAD_ARGUMENT);
884 if (!form->w || !form->current)
885 RETURN(E_SYSTEM_ERROR);
887 field = form->current;
888 formwin = Get_Form_Window(form);
890 is_public = Field_Has_Option(field, O_PUBLIC);
892 if (Is_Scroll_Field(field))
894 /* Again, in this case the fieldwin isn't derived from formwin,
895 so we have to perform a copy operation. */
896 if (Single_Line_Field(field))
898 /* horizontal scrolling */
899 if (form->curcol < form->begincol)
900 form->begincol = form->curcol;
903 if (form->curcol >= (form->begincol + field->cols))
904 form->begincol = form->curcol - field->cols
905 + (move_after_insert ? 1 : 0);
915 field->cols + field->fcol - 1,
920 /* A multi-line, i.e. vertical scrolling field */
921 int first_modified_row, first_unmodified_row;
923 if (field->drows > field->rows)
925 int row_after_bottom = form->toprow + field->rows;
927 if (form->currow < form->toprow)
929 form->toprow = form->currow;
930 SetStatus(field, _NEWTOP);
932 if (form->currow >= row_after_bottom)
934 form->toprow = form->currow - field->rows + 1;
935 SetStatus(field, _NEWTOP);
937 if (field->status & _NEWTOP)
939 /* means we have to copy whole range */
940 first_modified_row = form->toprow;
941 first_unmodified_row = first_modified_row + field->rows;
942 ClrStatus(field, _NEWTOP);
946 /* we try to optimize : finding the range of touched
948 first_modified_row = form->toprow;
949 while (first_modified_row < row_after_bottom)
951 if (is_linetouched(form->w, first_modified_row))
953 first_modified_row++;
955 first_unmodified_row = first_modified_row;
956 while (first_unmodified_row < row_after_bottom)
958 if (!is_linetouched(form->w, first_unmodified_row))
960 first_unmodified_row++;
966 first_modified_row = form->toprow;
967 first_unmodified_row = first_modified_row + field->rows;
969 if (first_unmodified_row != first_modified_row && is_public)
974 field->frow + first_modified_row - form->toprow,
976 field->frow + first_unmodified_row - form->toprow - 1,
977 field->cols + field->fcol - 1,
985 /* if the field-window is simply a derived window, i.e. contains no
986 * invisible parts, the whole thing is trivial
992 returnCode(_nc_Position_Form_Cursor(form));
995 /*---------------------------------------------------------------------------
996 | Facility : libnform
997 | Function : static void Perform_Justification(
1001 | Description : Output field with requested justification
1004 +--------------------------------------------------------------------------*/
1006 Perform_Justification(FIELD *field, WINDOW *win)
1011 bp = (Field_Has_Option(field, O_NO_LEFT_STRIP)
1013 : Get_Start_Of_Data(field->buf, Buffer_Length(field)));
1014 len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
1020 assert(win && (field->drows == 1));
1022 if (field->cols - len >= 0)
1023 switch (field->just)
1027 case JUSTIFY_CENTER:
1028 col = (field->cols - len) / 2;
1031 col = field->cols - len;
1038 myADDNSTR(win, bp, len);
1042 /*---------------------------------------------------------------------------
1043 | Facility : libnform
1044 | Function : static void Undo_Justification(
1048 | Description : Display field without any justification, i.e.
1052 +--------------------------------------------------------------------------*/
1054 Undo_Justification(FIELD *field, WINDOW *win)
1062 bp = (Field_Has_Option(field, O_NO_LEFT_STRIP)
1064 : Get_Start_Of_Data(field->buf, Buffer_Length(field)));
1065 len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
1071 myADDNSTR(win, bp, len);
1076 /*---------------------------------------------------------------------------
1077 | Facility : libnform
1078 | Function : static bool Check_Char(FORM *form,
1082 | TypeArgument *argp)
1084 | Description : Perform a single character check for character ch
1085 | according to the fieldtype instance.
1087 | Return Values : TRUE - Character is valid
1088 | FALSE - Character is invalid
1089 +--------------------------------------------------------------------------*/
1091 Check_Char(FORM *form,
1099 if (typ->status & _LINKED_TYPE)
1103 Check_Char(form, field, typ->left, ch, argp->left) ||
1104 Check_Char(form, field, typ->right, ch, argp->right));
1108 #if NCURSES_INTEROP_FUNCS
1109 if (typ->charcheck.occheck)
1111 if (typ->status & _GENERIC)
1112 return typ->charcheck.gccheck(ch, form, field, (void *)argp);
1114 return typ->charcheck.occheck(ch, (void *)argp);
1118 return typ->ccheck(ch, (void *)argp);
1122 return (!iscntrl(UChar(ch)) ? TRUE : FALSE);
1125 /*---------------------------------------------------------------------------
1126 | Facility : libnform
1127 | Function : static int Display_Or_Erase_Field(
1131 | Description : Create a subwindow for the field and display the
1132 | buffer contents (apply justification if required)
1133 | or simply erase the field.
1135 | Return Values : E_OK - on success
1136 | E_SYSTEM_ERROR - some error (typical no memory)
1137 +--------------------------------------------------------------------------*/
1139 Display_Or_Erase_Field(FIELD *field, bool bEraseFlag)
1145 return E_SYSTEM_ERROR;
1147 fwin = Get_Form_Window(field->form);
1149 field->rows, field->cols, field->frow, field->fcol);
1152 return E_SYSTEM_ERROR;
1155 if (Field_Has_Option(field, O_VISIBLE))
1157 Set_Field_Window_Attributes(field, win);
1161 (void)wattrset(win, (int)WINDOW_ATTRS(fwin));
1168 if (Field_Has_Option(field, O_PUBLIC))
1170 if (Justification_Allowed(field))
1171 Perform_Justification(field, win);
1173 Buffer_To_Window(field, win);
1175 ClrStatus(field, _NEWTOP);
1182 /* Macros to preset the bEraseFlag */
1183 #define Display_Field(field) Display_Or_Erase_Field(field,FALSE)
1184 #define Erase_Field(field) Display_Or_Erase_Field(field,TRUE)
1186 /*---------------------------------------------------------------------------
1187 | Facility : libnform
1188 | Function : static int Synchronize_Field(FIELD * field)
1190 | Description : Synchronize the windows content with the value in
1193 | Return Values : E_OK - success
1194 | E_BAD_ARGUMENT - invalid field pointer
1195 | E_SYSTEM_ERROR - some severe basic error
1196 +--------------------------------------------------------------------------*/
1198 Synchronize_Field(FIELD *field)
1204 return (E_BAD_ARGUMENT);
1206 if (((form = field->form) != (FORM *)0)
1207 && Field_Really_Appears(field))
1209 if (field == form->current)
1211 form->currow = form->curcol = form->toprow = form->begincol = 0;
1214 if ((Field_Has_Option(field, O_PUBLIC)) && Justification_Allowed(field))
1215 Undo_Justification(field, form->w);
1217 Buffer_To_Window(field, form->w);
1219 SetStatus(field, _NEWTOP);
1220 res = _nc_Refresh_Current_Field(form);
1223 res = Display_Field(field);
1225 SetStatus(field, _CHANGED);
1229 /*---------------------------------------------------------------------------
1230 | Facility : libnform
1231 | Function : static int Synchronize_Linked_Fields(FIELD * field)
1233 | Description : Propagate the Synchronize_Field function to all linked
1234 | fields. The first error that occurs in the sequence
1235 | of updates is the return value.
1237 | Return Values : E_OK - success
1238 | E_BAD_ARGUMENT - invalid field pointer
1239 | E_SYSTEM_ERROR - some severe basic error
1240 +--------------------------------------------------------------------------*/
1242 Synchronize_Linked_Fields(FIELD *field)
1244 FIELD *linked_field;
1248 return (E_BAD_ARGUMENT);
1251 return (E_SYSTEM_ERROR);
1253 for (linked_field = field->link;
1254 (linked_field != field) && (linked_field != 0);
1255 linked_field = linked_field->link)
1259 if (((syncres = Synchronize_Field(linked_field)) != E_OK) &&
1266 /*---------------------------------------------------------------------------
1267 | Facility : libnform
1268 | Function : int _nc_Synchronize_Attributes(FIELD * field)
1270 | Description : If a field's visual attributes have changed, this
1271 | routine is called to propagate those changes to the
1274 | Return Values : E_OK - success
1275 | E_BAD_ARGUMENT - invalid field pointer
1276 | E_SYSTEM_ERROR - some severe basic error
1277 +--------------------------------------------------------------------------*/
1279 _nc_Synchronize_Attributes(FIELD *field)
1284 T((T_CALLED("_nc_Synchronize_Attributes(%p)"), (void *)field));
1287 returnCode(E_BAD_ARGUMENT);
1289 CHECKPOS(field->form);
1290 if (((form = field->form) != (FORM *)0)
1291 && Field_Really_Appears(field))
1293 if (form->current == field)
1295 Synchronize_Buffer(form);
1296 Set_Field_Window_Attributes(field, form->w);
1298 wmove(form->w, form->currow, form->curcol);
1300 if (Field_Has_Option(field, O_PUBLIC))
1302 if (Justification_Allowed(field))
1303 Undo_Justification(field, form->w);
1305 Buffer_To_Window(field, form->w);
1309 WINDOW *formwin = Get_Form_Window(form);
1311 copywin(form->w, formwin,
1313 field->frow, field->fcol,
1314 field->frow + field->rows - 1,
1315 field->fcol + field->cols - 1, 0);
1317 Buffer_To_Window(field, form->w);
1318 SetStatus(field, _NEWTOP); /* fake refresh to paint all */
1319 _nc_Refresh_Current_Field(form);
1324 res = Display_Field(field);
1331 /*---------------------------------------------------------------------------
1332 | Facility : libnform
1333 | Function : int _nc_Synchronize_Options(FIELD * field,
1334 | Field_Options newopts)
1336 | Description : If a field's options have changed, this routine is
1337 | called to propagate these changes to the screen and
1338 | to really change the behavior of the field.
1340 | Return Values : E_OK - success
1341 | E_BAD_ARGUMENT - invalid field pointer
1342 | E_CURRENT - field is the current one
1343 | E_SYSTEM_ERROR - some severe basic error
1344 +--------------------------------------------------------------------------*/
1346 _nc_Synchronize_Options(FIELD *field, Field_Options newopts)
1348 Field_Options oldopts;
1349 Field_Options changed_opts;
1353 T((T_CALLED("_nc_Synchronize_Options(%p,%#x)"), (void *)field, newopts));
1356 returnCode(E_BAD_ARGUMENT);
1358 oldopts = field->opts;
1359 changed_opts = oldopts ^ newopts;
1360 field->opts = newopts;
1365 if (form->status & _POSTED)
1367 if (form->current == field)
1369 field->opts = oldopts;
1370 returnCode(E_CURRENT);
1372 if (form->curpage == field->page)
1374 if ((unsigned)changed_opts & O_VISIBLE)
1376 if ((unsigned)newopts & O_VISIBLE)
1377 res = Display_Field(field);
1379 res = Erase_Field(field);
1383 if (((unsigned)changed_opts & O_PUBLIC) &&
1384 ((unsigned)newopts & O_VISIBLE))
1385 res = Display_Field(field);
1391 if ((unsigned)changed_opts & O_STATIC)
1393 bool single_line_field = Single_Line_Field(field);
1396 if ((unsigned)newopts & O_STATIC)
1398 /* the field becomes now static */
1399 ClrStatus(field, _MAY_GROW);
1400 /* if actually we have no hidden columns, justification may
1402 if (single_line_field &&
1403 (field->cols == field->dcols) &&
1404 (field->just != NO_JUSTIFICATION) &&
1405 Field_Really_Appears(field))
1407 res2 = Display_Field(field);
1412 /* field is no longer static */
1413 if ((field->maxgrow == 0) ||
1414 (single_line_field && (field->dcols < field->maxgrow)) ||
1415 (!single_line_field && (field->drows < field->maxgrow)))
1417 SetStatus(field, _MAY_GROW);
1418 /* a field with justification now changes its behavior,
1419 so we must redisplay it */
1420 if (single_line_field &&
1421 (field->just != NO_JUSTIFICATION) &&
1422 Field_Really_Appears(field))
1424 res2 = Display_Field(field);
1436 * Removes the focus from the current field of the form.
1439 _nc_Unset_Current_Field(FORM *form)
1441 FIELD *field = form->current;
1443 _nc_Refresh_Current_Field(form);
1444 if (Field_Has_Option(field, O_PUBLIC))
1446 if (field->drows > field->rows)
1448 if (form->toprow == 0)
1449 ClrStatus(field, _NEWTOP);
1451 SetStatus(field, _NEWTOP);
1455 if (Justification_Allowed(field))
1457 Window_To_Buffer(form, field);
1459 Perform_Justification(field, form->w);
1460 if (Field_Has_Option(field, O_DYNAMIC_JUSTIFY) &&
1461 (form->w->_parent == 0))
1464 Get_Form_Window(form),
1470 field->cols + field->fcol - 1,
1472 wsyncup(Get_Form_Window(form));
1482 form->w = (WINDOW *)0;
1486 /*---------------------------------------------------------------------------
1487 | Facility : libnform
1488 | Function : int _nc_Set_Current_Field(FORM * form,
1491 | Description : Make the newfield the new current field.
1493 | Return Values : E_OK - success
1494 | E_BAD_ARGUMENT - invalid form or field pointer
1495 | E_SYSTEM_ERROR - some severe basic error
1496 | E_NOT_CONNECTED - no fields are connected to the form
1497 +--------------------------------------------------------------------------*/
1499 _nc_Set_Current_Field(FORM *form, FIELD *newfield)
1504 T((T_CALLED("_nc_Set_Current_Field(%p,%p)"), (void *)form, (void *)newfield));
1506 if (!form || !newfield || (newfield->form != form))
1507 returnCode(E_BAD_ARGUMENT);
1509 if ((form->status & _IN_DRIVER))
1510 returnCode(E_BAD_STATE);
1513 returnCode(E_NOT_CONNECTED);
1515 field = form->current;
1517 if ((field != newfield) ||
1518 !(form->status & _POSTED))
1520 if (field && (form->w) &&
1521 (Field_Has_Option(field, O_VISIBLE)) &&
1522 (field->form->curpage == field->page))
1523 _nc_Unset_Current_Field(form);
1527 if (Has_Invisible_Parts(field))
1528 new_window = newpad(field->drows, field->dcols);
1530 new_window = derwin(Get_Form_Window(form),
1531 field->rows, field->cols, field->frow, field->fcol);
1534 returnCode(E_SYSTEM_ERROR);
1536 form->current = field;
1540 form->w = new_window;
1542 ClrStatus(form, _WINDOW_MODIFIED);
1543 Set_Field_Window_Attributes(field, form->w);
1545 if (Has_Invisible_Parts(field))
1548 Buffer_To_Window(field, form->w);
1552 if (Justification_Allowed(field))
1555 Undo_Justification(field, form->w);
1560 untouchwin(form->w);
1563 form->currow = form->curcol = form->toprow = form->begincol = 0;
1567 /*----------------------------------------------------------------------------
1568 Intra-Field Navigation routines
1569 --------------------------------------------------------------------------*/
1571 /*---------------------------------------------------------------------------
1572 | Facility : libnform
1573 | Function : static int IFN_Next_Character(FORM * form)
1575 | Description : Move to the next character in the field. In a multi-line
1576 | field this wraps at the end of the line.
1578 | Return Values : E_OK - success
1579 | E_REQUEST_DENIED - at the rightmost position
1580 +--------------------------------------------------------------------------*/
1582 IFN_Next_Character(FORM *form)
1584 FIELD *field = form->current;
1585 int step = myWCWIDTH(form->w, form->currow, form->curcol);
1587 T((T_CALLED("IFN_Next_Character(%p)"), (void *)form));
1588 if ((form->curcol += step) == field->dcols)
1590 if ((++(form->currow)) == field->drows)
1592 #if GROW_IF_NAVIGATE
1593 if (!Single_Line_Field(field) && Field_Grown(field, 1))
1600 #if GROW_IF_NAVIGATE
1601 if (Single_Line_Field(field) && Field_Grown(field, 1))
1604 form->curcol -= step;
1605 returnCode(E_REQUEST_DENIED);
1612 /*---------------------------------------------------------------------------
1613 | Facility : libnform
1614 | Function : static int IFN_Previous_Character(FORM * form)
1616 | Description : Move to the previous character in the field. In a
1617 | multi-line field this wraps and the beginning of the
1620 | Return Values : E_OK - success
1621 | E_REQUEST_DENIED - at the leftmost position
1622 +--------------------------------------------------------------------------*/
1624 IFN_Previous_Character(FORM *form)
1626 int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1627 int oldcol = form->curcol;
1629 T((T_CALLED("IFN_Previous_Character(%p)"), (void *)form));
1630 if ((form->curcol -= amount) < 0)
1632 if ((--(form->currow)) < 0)
1635 form->curcol = oldcol;
1636 returnCode(E_REQUEST_DENIED);
1638 form->curcol = form->current->dcols - 1;
1643 /*---------------------------------------------------------------------------
1644 | Facility : libnform
1645 | Function : static int IFN_Next_Line(FORM * form)
1647 | Description : Move to the beginning of the next line in the field
1649 | Return Values : E_OK - success
1650 | E_REQUEST_DENIED - at the last line
1651 +--------------------------------------------------------------------------*/
1653 IFN_Next_Line(FORM *form)
1655 FIELD *field = form->current;
1657 T((T_CALLED("IFN_Next_Line(%p)"), (void *)form));
1658 if ((++(form->currow)) == field->drows)
1660 #if GROW_IF_NAVIGATE
1661 if (!Single_Line_Field(field) && Field_Grown(field, 1))
1665 returnCode(E_REQUEST_DENIED);
1671 /*---------------------------------------------------------------------------
1672 | Facility : libnform
1673 | Function : static int IFN_Previous_Line(FORM * form)
1675 | Description : Move to the beginning of the previous line in the field
1677 | Return Values : E_OK - success
1678 | E_REQUEST_DENIED - at the first line
1679 +--------------------------------------------------------------------------*/
1681 IFN_Previous_Line(FORM *form)
1683 T((T_CALLED("IFN_Previous_Line(%p)"), (void *)form));
1684 if ((--(form->currow)) < 0)
1687 returnCode(E_REQUEST_DENIED);
1693 /*---------------------------------------------------------------------------
1694 | Facility : libnform
1695 | Function : static int IFN_Next_Word(FORM * form)
1697 | Description : Move to the beginning of the next word in the field.
1699 | Return Values : E_OK - success
1700 | E_REQUEST_DENIED - there is no next word
1701 +--------------------------------------------------------------------------*/
1703 IFN_Next_Word(FORM *form)
1705 FIELD *field = form->current;
1706 FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1710 T((T_CALLED("IFN_Next_Word(%p)"), (void *)form));
1712 /* We really need access to the data, so we have to synchronize */
1713 Synchronize_Buffer(form);
1715 /* Go to the first whitespace after the current position (including
1716 current position). This is then the starting point to look for the
1717 next non-blank data */
1718 s = Get_First_Whitespace_Character(bp, Buffer_Length(field) -
1719 (int)(bp - field->buf));
1721 /* Find the start of the next word */
1722 t = Get_Start_Of_Data(s, Buffer_Length(field) -
1723 (int)(s - field->buf));
1724 #if !FRIENDLY_PREV_NEXT_WORD
1726 returnCode(E_REQUEST_DENIED);
1730 Adjust_Cursor_Position(form, t);
1735 /*---------------------------------------------------------------------------
1736 | Facility : libnform
1737 | Function : static int IFN_Previous_Word(FORM * form)
1739 | Description : Move to the beginning of the previous word in the field.
1741 | Return Values : E_OK - success
1742 | E_REQUEST_DENIED - there is no previous word
1743 +--------------------------------------------------------------------------*/
1745 IFN_Previous_Word(FORM *form)
1747 FIELD *field = form->current;
1748 FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1753 T((T_CALLED("IFN_Previous_Word(%p)"), (void *)form));
1755 /* We really need access to the data, so we have to synchronize */
1756 Synchronize_Buffer(form);
1758 s = After_End_Of_Data(field->buf, (int)(bp - field->buf));
1759 /* s points now right after the last non-blank in the buffer before bp.
1760 If bp was in a word, s equals bp. In this case we must find the last
1761 whitespace in the buffer before bp and repeat the game to really find
1762 the previous word! */
1766 /* And next call now goes backward to look for the last whitespace
1767 before that, pointing right after this, so it points to the begin
1768 of the previous word.
1770 t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1771 #if !FRIENDLY_PREV_NEXT_WORD
1773 returnCode(E_REQUEST_DENIED);
1777 /* and do it again, replacing bp by t */
1778 s = After_End_Of_Data(field->buf, (int)(t - field->buf));
1779 t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1780 #if !FRIENDLY_PREV_NEXT_WORD
1782 returnCode(E_REQUEST_DENIED);
1785 Adjust_Cursor_Position(form, t);
1789 /*---------------------------------------------------------------------------
1790 | Facility : libnform
1791 | Function : static int IFN_Beginning_Of_Field(FORM * form)
1793 | Description : Place the cursor at the first non-pad character in
1796 | Return Values : E_OK - success
1797 +--------------------------------------------------------------------------*/
1799 IFN_Beginning_Of_Field(FORM *form)
1801 FIELD *field = form->current;
1803 T((T_CALLED("IFN_Beginning_Of_Field(%p)"), (void *)form));
1804 Synchronize_Buffer(form);
1805 Adjust_Cursor_Position(form,
1806 Get_Start_Of_Data(field->buf, Buffer_Length(field)));
1810 /*---------------------------------------------------------------------------
1811 | Facility : libnform
1812 | Function : static int IFN_End_Of_Field(FORM * form)
1814 | Description : Place the cursor after the last non-pad character in
1815 | the field. If the field occupies the last position in
1816 | the buffer, the cursor is positioned on the last
1819 | Return Values : E_OK - success
1820 +--------------------------------------------------------------------------*/
1822 IFN_End_Of_Field(FORM *form)
1824 FIELD *field = form->current;
1827 T((T_CALLED("IFN_End_Of_Field(%p)"), (void *)form));
1828 Synchronize_Buffer(form);
1829 pos = After_End_Of_Data(field->buf, Buffer_Length(field));
1830 if (pos == (field->buf + Buffer_Length(field)))
1832 Adjust_Cursor_Position(form, pos);
1836 /*---------------------------------------------------------------------------
1837 | Facility : libnform
1838 | Function : static int IFN_Beginning_Of_Line(FORM * form)
1840 | Description : Place the cursor on the first non-pad character in
1841 | the current line of the field.
1843 | Return Values : E_OK - success
1844 +--------------------------------------------------------------------------*/
1846 IFN_Beginning_Of_Line(FORM *form)
1848 FIELD *field = form->current;
1850 T((T_CALLED("IFN_Beginning_Of_Line(%p)"), (void *)form));
1851 Synchronize_Buffer(form);
1852 Adjust_Cursor_Position(form,
1853 Get_Start_Of_Data(Address_Of_Current_Row_In_Buffer(form),
1858 /*---------------------------------------------------------------------------
1859 | Facility : libnform
1860 | Function : static int IFN_End_Of_Line(FORM * form)
1862 | Description : Place the cursor after the last non-pad character in the
1863 | current line of the field. If the field occupies the
1864 | last column in the line, the cursor is positioned on the
1865 | last character of the line.
1867 | Return Values : E_OK - success
1868 +--------------------------------------------------------------------------*/
1870 IFN_End_Of_Line(FORM *form)
1872 FIELD *field = form->current;
1876 T((T_CALLED("IFN_End_Of_Line(%p)"), (void *)form));
1877 Synchronize_Buffer(form);
1878 bp = Address_Of_Current_Row_In_Buffer(form);
1879 pos = After_End_Of_Data(bp, field->dcols);
1880 if (pos == (bp + field->dcols))
1882 Adjust_Cursor_Position(form, pos);
1886 /*---------------------------------------------------------------------------
1887 | Facility : libnform
1888 | Function : static int IFN_Left_Character(FORM * form)
1890 | Description : Move one character to the left in the current line.
1891 | This doesn't cycle.
1893 | Return Values : E_OK - success
1894 | E_REQUEST_DENIED - already in first column
1895 +--------------------------------------------------------------------------*/
1897 IFN_Left_Character(FORM *form)
1899 int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1900 int oldcol = form->curcol;
1902 T((T_CALLED("IFN_Left_Character(%p)"), (void *)form));
1903 if ((form->curcol -= amount) < 0)
1905 form->curcol = oldcol;
1906 returnCode(E_REQUEST_DENIED);
1911 /*---------------------------------------------------------------------------
1912 | Facility : libnform
1913 | Function : static int IFN_Right_Character(FORM * form)
1915 | Description : Move one character to the right in the current line.
1916 | This doesn't cycle.
1918 | Return Values : E_OK - success
1919 | E_REQUEST_DENIED - already in last column
1920 +--------------------------------------------------------------------------*/
1922 IFN_Right_Character(FORM *form)
1924 int amount = myWCWIDTH(form->w, form->currow, form->curcol);
1925 int oldcol = form->curcol;
1927 T((T_CALLED("IFN_Right_Character(%p)"), (void *)form));
1928 if ((form->curcol += amount) >= form->current->dcols)
1930 #if GROW_IF_NAVIGATE
1931 FIELD *field = form->current;
1933 if (Single_Line_Field(field) && Field_Grown(field, 1))
1936 form->curcol = oldcol;
1937 returnCode(E_REQUEST_DENIED);
1942 /*---------------------------------------------------------------------------
1943 | Facility : libnform
1944 | Function : static int IFN_Up_Character(FORM * form)
1946 | Description : Move one line up. This doesn't cycle through the lines
1949 | Return Values : E_OK - success
1950 | E_REQUEST_DENIED - already in last column
1951 +--------------------------------------------------------------------------*/
1953 IFN_Up_Character(FORM *form)
1955 T((T_CALLED("IFN_Up_Character(%p)"), (void *)form));
1956 if ((--(form->currow)) < 0)
1959 returnCode(E_REQUEST_DENIED);
1964 /*---------------------------------------------------------------------------
1965 | Facility : libnform
1966 | Function : static int IFN_Down_Character(FORM * form)
1968 | Description : Move one line down. This doesn't cycle through the
1969 | lines of the field.
1971 | Return Values : E_OK - success
1972 | E_REQUEST_DENIED - already in last column
1973 +--------------------------------------------------------------------------*/
1975 IFN_Down_Character(FORM *form)
1977 FIELD *field = form->current;
1979 T((T_CALLED("IFN_Down_Character(%p)"), (void *)form));
1980 if ((++(form->currow)) == field->drows)
1982 #if GROW_IF_NAVIGATE
1983 if (!Single_Line_Field(field) && Field_Grown(field, 1))
1987 returnCode(E_REQUEST_DENIED);
1991 /*----------------------------------------------------------------------------
1992 END of Intra-Field Navigation routines
1993 --------------------------------------------------------------------------*/
1995 /*----------------------------------------------------------------------------
1996 Vertical scrolling helper routines
1997 --------------------------------------------------------------------------*/
1999 /*---------------------------------------------------------------------------
2000 | Facility : libnform
2001 | Function : static int VSC_Generic(FORM *form, int nlines)
2003 | Description : Scroll multi-line field forward (nlines>0) or
2004 | backward (nlines<0) this many lines.
2006 | Return Values : E_OK - success
2007 | E_REQUEST_DENIED - can't scroll
2008 +--------------------------------------------------------------------------*/
2010 VSC_Generic(FORM *form, int nlines)
2012 FIELD *field = form->current;
2013 int res = E_REQUEST_DENIED;
2014 int rows_to_go = (nlines > 0 ? nlines : -nlines);
2018 if ((rows_to_go + form->toprow) > (field->drows - field->rows))
2019 rows_to_go = (field->drows - field->rows - form->toprow);
2023 form->currow += rows_to_go;
2024 form->toprow += rows_to_go;
2030 if (rows_to_go > form->toprow)
2031 rows_to_go = form->toprow;
2035 form->currow -= rows_to_go;
2036 form->toprow -= rows_to_go;
2042 /*----------------------------------------------------------------------------
2043 End of Vertical scrolling helper routines
2044 --------------------------------------------------------------------------*/
2046 /*----------------------------------------------------------------------------
2047 Vertical scrolling routines
2048 --------------------------------------------------------------------------*/
2050 /*---------------------------------------------------------------------------
2051 | Facility : libnform
2052 | Function : static int Vertical_Scrolling(
2053 | int (* const fct) (FORM *),
2056 | Description : Performs the generic vertical scrolling routines.
2057 | This has to check for a multi-line field and to set
2058 | the _NEWTOP flag if scrolling really occurred.
2060 | Return Values : Propagated error code from low-level driver calls
2061 +--------------------------------------------------------------------------*/
2063 Vertical_Scrolling(int (*const fct) (FORM *), FORM *form)
2065 int res = E_REQUEST_DENIED;
2067 if (!Single_Line_Field(form->current))
2071 SetStatus(form->current, _NEWTOP);
2076 /*---------------------------------------------------------------------------
2077 | Facility : libnform
2078 | Function : static int VSC_Scroll_Line_Forward(FORM * form)
2080 | Description : Scroll multi-line field forward a line
2082 | Return Values : E_OK - success
2083 | E_REQUEST_DENIED - no data ahead
2084 +--------------------------------------------------------------------------*/
2086 VSC_Scroll_Line_Forward(FORM *form)
2088 T((T_CALLED("VSC_Scroll_Line_Forward(%p)"), (void *)form));
2089 returnCode(VSC_Generic(form, 1));
2092 /*---------------------------------------------------------------------------
2093 | Facility : libnform
2094 | Function : static int VSC_Scroll_Line_Backward(FORM * form)
2096 | Description : Scroll multi-line field backward a line
2098 | Return Values : E_OK - success
2099 | E_REQUEST_DENIED - no data behind
2100 +--------------------------------------------------------------------------*/
2102 VSC_Scroll_Line_Backward(FORM *form)
2104 T((T_CALLED("VSC_Scroll_Line_Backward(%p)"), (void *)form));
2105 returnCode(VSC_Generic(form, -1));
2108 /*---------------------------------------------------------------------------
2109 | Facility : libnform
2110 | Function : static int VSC_Scroll_Page_Forward(FORM * form)
2112 | Description : Scroll a multi-line field forward a page
2114 | Return Values : E_OK - success
2115 | E_REQUEST_DENIED - no data ahead
2116 +--------------------------------------------------------------------------*/
2118 VSC_Scroll_Page_Forward(FORM *form)
2120 T((T_CALLED("VSC_Scroll_Page_Forward(%p)"), (void *)form));
2121 returnCode(VSC_Generic(form, form->current->rows));
2124 /*---------------------------------------------------------------------------
2125 | Facility : libnform
2126 | Function : static int VSC_Scroll_Half_Page_Forward(FORM * form)
2128 | Description : Scroll a multi-line field forward half a page
2130 | Return Values : E_OK - success
2131 | E_REQUEST_DENIED - no data ahead
2132 +--------------------------------------------------------------------------*/
2134 VSC_Scroll_Half_Page_Forward(FORM *form)
2136 T((T_CALLED("VSC_Scroll_Half_Page_Forward(%p)"), (void *)form));
2137 returnCode(VSC_Generic(form, (form->current->rows + 1) / 2));
2140 /*---------------------------------------------------------------------------
2141 | Facility : libnform
2142 | Function : static int VSC_Scroll_Page_Backward(FORM * form)
2144 | Description : Scroll a multi-line field backward a page
2146 | Return Values : E_OK - success
2147 | E_REQUEST_DENIED - no data behind
2148 +--------------------------------------------------------------------------*/
2150 VSC_Scroll_Page_Backward(FORM *form)
2152 T((T_CALLED("VSC_Scroll_Page_Backward(%p)"), (void *)form));
2153 returnCode(VSC_Generic(form, -(form->current->rows)));
2156 /*---------------------------------------------------------------------------
2157 | Facility : libnform
2158 | Function : static int VSC_Scroll_Half_Page_Backward(FORM * form)
2160 | Description : Scroll a multi-line field backward half a page
2162 | Return Values : E_OK - success
2163 | E_REQUEST_DENIED - no data behind
2164 +--------------------------------------------------------------------------*/
2166 VSC_Scroll_Half_Page_Backward(FORM *form)
2168 T((T_CALLED("VSC_Scroll_Half_Page_Backward(%p)"), (void *)form));
2169 returnCode(VSC_Generic(form, -((form->current->rows + 1) / 2)));
2171 /*----------------------------------------------------------------------------
2172 End of Vertical scrolling routines
2173 --------------------------------------------------------------------------*/
2175 /*----------------------------------------------------------------------------
2176 Horizontal scrolling helper routines
2177 --------------------------------------------------------------------------*/
2179 /*---------------------------------------------------------------------------
2180 | Facility : libnform
2181 | Function : static int HSC_Generic(FORM *form, int ncolumns)
2183 | Description : Scroll single-line field forward (ncolumns>0) or
2184 | backward (ncolumns<0) this many columns.
2186 | Return Values : E_OK - success
2187 | E_REQUEST_DENIED - can't scroll
2188 +--------------------------------------------------------------------------*/
2190 HSC_Generic(FORM *form, int ncolumns)
2192 FIELD *field = form->current;
2193 int res = E_REQUEST_DENIED;
2194 int cols_to_go = (ncolumns > 0 ? ncolumns : -ncolumns);
2198 if ((cols_to_go + form->begincol) > (field->dcols - field->cols))
2199 cols_to_go = field->dcols - field->cols - form->begincol;
2203 form->curcol += cols_to_go;
2204 form->begincol += cols_to_go;
2210 if (cols_to_go > form->begincol)
2211 cols_to_go = form->begincol;
2215 form->curcol -= cols_to_go;
2216 form->begincol -= cols_to_go;
2222 /*----------------------------------------------------------------------------
2223 End of Horizontal scrolling helper routines
2224 --------------------------------------------------------------------------*/
2226 /*----------------------------------------------------------------------------
2227 Horizontal scrolling routines
2228 --------------------------------------------------------------------------*/
2230 /*---------------------------------------------------------------------------
2231 | Facility : libnform
2232 | Function : static int Horizontal_Scrolling(
2233 | int (* const fct) (FORM *),
2236 | Description : Performs the generic horizontal scrolling routines.
2237 | This has to check for a single-line field.
2239 | Return Values : Propagated error code from low-level driver calls
2240 +--------------------------------------------------------------------------*/
2242 Horizontal_Scrolling(int (*const fct) (FORM *), FORM *form)
2244 if (Single_Line_Field(form->current))
2247 return (E_REQUEST_DENIED);
2250 /*---------------------------------------------------------------------------
2251 | Facility : libnform
2252 | Function : static int HSC_Scroll_Char_Forward(FORM * form)
2254 | Description : Scroll single-line field forward a character
2256 | Return Values : E_OK - success
2257 | E_REQUEST_DENIED - no data ahead
2258 +--------------------------------------------------------------------------*/
2260 HSC_Scroll_Char_Forward(FORM *form)
2262 T((T_CALLED("HSC_Scroll_Char_Forward(%p)"), (void *)form));
2263 returnCode(HSC_Generic(form, 1));
2266 /*---------------------------------------------------------------------------
2267 | Facility : libnform
2268 | Function : static int HSC_Scroll_Char_Backward(FORM * form)
2270 | Description : Scroll single-line field backward a character
2272 | Return Values : E_OK - success
2273 | E_REQUEST_DENIED - no data behind
2274 +--------------------------------------------------------------------------*/
2276 HSC_Scroll_Char_Backward(FORM *form)
2278 T((T_CALLED("HSC_Scroll_Char_Backward(%p)"), (void *)form));
2279 returnCode(HSC_Generic(form, -1));
2282 /*---------------------------------------------------------------------------
2283 | Facility : libnform
2284 | Function : static int HSC_Horizontal_Line_Forward(FORM* form)
2286 | Description : Scroll single-line field forward a line
2288 | Return Values : E_OK - success
2289 | E_REQUEST_DENIED - no data ahead
2290 +--------------------------------------------------------------------------*/
2292 HSC_Horizontal_Line_Forward(FORM *form)
2294 T((T_CALLED("HSC_Horizontal_Line_Forward(%p)"), (void *)form));
2295 returnCode(HSC_Generic(form, form->current->cols));
2298 /*---------------------------------------------------------------------------
2299 | Facility : libnform
2300 | Function : static int HSC_Horizontal_Half_Line_Forward(FORM* form)
2302 | Description : Scroll single-line field forward half a line
2304 | Return Values : E_OK - success
2305 | E_REQUEST_DENIED - no data ahead
2306 +--------------------------------------------------------------------------*/
2308 HSC_Horizontal_Half_Line_Forward(FORM *form)
2310 T((T_CALLED("HSC_Horizontal_Half_Line_Forward(%p)"), (void *)form));
2311 returnCode(HSC_Generic(form, (form->current->cols + 1) / 2));
2314 /*---------------------------------------------------------------------------
2315 | Facility : libnform
2316 | Function : static int HSC_Horizontal_Line_Backward(FORM* form)
2318 | Description : Scroll single-line field backward a line
2320 | Return Values : E_OK - success
2321 | E_REQUEST_DENIED - no data behind
2322 +--------------------------------------------------------------------------*/
2324 HSC_Horizontal_Line_Backward(FORM *form)
2326 T((T_CALLED("HSC_Horizontal_Line_Backward(%p)"), (void *)form));
2327 returnCode(HSC_Generic(form, -(form->current->cols)));
2330 /*---------------------------------------------------------------------------
2331 | Facility : libnform
2332 | Function : static int HSC_Horizontal_Half_Line_Backward(FORM* form)
2334 | Description : Scroll single-line field backward half a line
2336 | Return Values : E_OK - success
2337 | E_REQUEST_DENIED - no data behind
2338 +--------------------------------------------------------------------------*/
2340 HSC_Horizontal_Half_Line_Backward(FORM *form)
2342 T((T_CALLED("HSC_Horizontal_Half_Line_Backward(%p)"), (void *)form));
2343 returnCode(HSC_Generic(form, -((form->current->cols + 1) / 2)));
2346 /*----------------------------------------------------------------------------
2347 End of Horizontal scrolling routines
2348 --------------------------------------------------------------------------*/
2350 /*----------------------------------------------------------------------------
2351 Helper routines for Field Editing
2352 --------------------------------------------------------------------------*/
2354 /*---------------------------------------------------------------------------
2355 | Facility : libnform
2356 | Function : static bool Is_There_Room_For_A_Line(FORM * form)
2358 | Description : Check whether or not there is enough room in the
2359 | buffer to enter a whole line.
2361 | Return Values : TRUE - there is enough space
2362 | FALSE - there is not enough space
2363 +--------------------------------------------------------------------------*/
2364 NCURSES_INLINE static bool
2365 Is_There_Room_For_A_Line(FORM *form)
2367 FIELD *field = form->current;
2368 FIELD_CELL *begin_of_last_line, *s;
2370 Synchronize_Buffer(form);
2371 begin_of_last_line = Address_Of_Row_In_Buffer(field, (field->drows - 1));
2372 s = After_End_Of_Data(begin_of_last_line, field->dcols);
2373 return ((s == begin_of_last_line) ? TRUE : FALSE);
2376 /*---------------------------------------------------------------------------
2377 | Facility : libnform
2378 | Function : static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
2380 | Description : Checks whether or not there is room for a new character
2381 | in the current line.
2383 | Return Values : TRUE - there is room
2384 | FALSE - there is not enough room (line full)
2385 +--------------------------------------------------------------------------*/
2386 NCURSES_INLINE static bool
2387 Is_There_Room_For_A_Char_In_Line(FORM *form)
2389 int last_char_in_line;
2391 wmove(form->w, form->currow, form->current->dcols - 1);
2392 last_char_in_line = (int)(winch(form->w) & A_CHARTEXT);
2393 wmove(form->w, form->currow, form->curcol);
2394 return (((last_char_in_line == form->current->pad) ||
2395 is_blank(last_char_in_line)) ? TRUE : FALSE);
2398 #define There_Is_No_Room_For_A_Char_In_Line(f) \
2399 !Is_There_Room_For_A_Char_In_Line(f)
2401 /*---------------------------------------------------------------------------
2402 | Facility : libnform
2403 | Function : static int Insert_String(
2409 | Description : Insert the 'len' characters beginning at pointer 'txt'
2410 | into the 'row' of the 'form'. The insertion occurs
2411 | on the beginning of the row, all other characters are
2412 | moved to the right. After the text a pad character will
2413 | be inserted to separate the text from the rest. If
2414 | necessary the insertion moves characters on the next
2415 | line to make place for the requested insertion string.
2417 | Return Values : E_OK - success
2418 | E_REQUEST_DENIED -
2419 | E_SYSTEM_ERROR - system error
2420 +--------------------------------------------------------------------------*/
2422 Insert_String(FORM *form, int row, FIELD_CELL *txt, int len)
2424 FIELD *field = form->current;
2425 FIELD_CELL *bp = Address_Of_Row_In_Buffer(field, row);
2426 int datalen = (int)(After_End_Of_Data(bp, field->dcols) - bp);
2427 int freelen = field->dcols - datalen;
2428 int requiredlen = len + 1;
2429 int result = E_REQUEST_DENIED;
2431 if (freelen >= requiredlen)
2433 wmove(form->w, row, 0);
2434 myINSNSTR(form->w, txt, len);
2435 wmove(form->w, row, len);
2436 myINSNSTR(form->w, &myBLANK, 1);
2441 /* we have to move characters on the next line. If we are on the
2442 last line this may work, if the field is growable */
2443 if ((row == (field->drows - 1)) && Growable(field))
2445 if (!Field_Grown(field, 1))
2446 return (E_SYSTEM_ERROR);
2447 /* !!!Side-Effect : might be changed due to growth!!! */
2448 bp = Address_Of_Row_In_Buffer(field, row);
2451 if (row < (field->drows - 1))
2456 After_Last_Whitespace_Character(bp,
2457 (int)(Get_Start_Of_Data(bp
2462 /* split points now to the first character of the portion of the
2463 line that must be moved to the next line */
2464 datalen = (int)(split - bp); /* + freelen has to stay on this line */
2465 freelen = field->dcols - (datalen + freelen); /* for the next line */
2467 if ((result = Insert_String(form, row + 1, split, freelen)) == E_OK)
2469 wmove(form->w, row, datalen);
2471 wmove(form->w, row, 0);
2472 myINSNSTR(form->w, txt, len);
2473 wmove(form->w, row, len);
2474 myINSNSTR(form->w, &myBLANK, 1);
2482 /*---------------------------------------------------------------------------
2483 | Facility : libnform
2484 | Function : static int Wrapping_Not_Necessary_Or_Wrapping_Ok(
2487 | Description : If a character has been entered into a field, it may
2488 | be that wrapping has to occur. This routine checks
2489 | whether or not wrapping is required and if so, performs
2492 | Return Values : E_OK - no wrapping required or wrapping
2494 | E_REQUEST_DENIED -
2495 | E_SYSTEM_ERROR - some system error
2496 +--------------------------------------------------------------------------*/
2498 Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM *form)
2500 FIELD *field = form->current;
2501 int result = E_REQUEST_DENIED;
2502 bool Last_Row = ((field->drows - 1) == form->currow);
2504 if ((Field_Has_Option(field, O_WRAP)) && /* wrapping wanted */
2505 (!Single_Line_Field(field)) && /* must be multi-line */
2506 (There_Is_No_Room_For_A_Char_In_Line(form)) && /* line is full */
2507 (!Last_Row || Growable(field))) /* there are more lines */
2511 int chars_to_be_wrapped;
2512 int chars_to_remain_on_line;
2516 /* the above logic already ensures, that in this case the field
2518 if (!Field_Grown(field, 1))
2519 return E_SYSTEM_ERROR;
2521 bp = Address_Of_Current_Row_In_Buffer(form);
2522 Window_To_Buffer(form, field);
2523 split = After_Last_Whitespace_Character(bp, field->dcols);
2524 /* split points to the first character of the sequence to be brought
2526 chars_to_remain_on_line = (int)(split - bp);
2527 chars_to_be_wrapped = field->dcols - chars_to_remain_on_line;
2528 if (chars_to_remain_on_line > 0)
2530 if ((result = Insert_String(form, form->currow + 1, split,
2531 chars_to_be_wrapped)) == E_OK)
2533 wmove(form->w, form->currow, chars_to_remain_on_line);
2535 if (form->curcol >= chars_to_remain_on_line)
2538 form->curcol -= chars_to_remain_on_line;
2548 Window_To_Buffer(form, field);
2549 result = E_REQUEST_DENIED;
2553 result = E_OK; /* wrapping was not necessary */
2557 /*----------------------------------------------------------------------------
2558 Field Editing routines
2559 --------------------------------------------------------------------------*/
2561 /*---------------------------------------------------------------------------
2562 | Facility : libnform
2563 | Function : static int Field_Editing(
2564 | int (* const fct) (FORM *),
2567 | Description : Generic routine for field editing requests. The driver
2568 | routines are only called for editable fields, the
2569 | _WINDOW_MODIFIED flag is set if editing occurred.
2570 | This is somewhat special due to the overload semantics
2571 | of the NEW_LINE and DEL_PREV requests.
2573 | Return Values : Error code from low level drivers.
2574 +--------------------------------------------------------------------------*/
2576 Field_Editing(int (*const fct) (FORM *), FORM *form)
2578 int res = E_REQUEST_DENIED;
2580 /* We have to deal here with the specific case of the overloaded
2581 behavior of New_Line and Delete_Previous requests.
2582 They may end up in navigational requests if we are on the first
2583 character in a field. But navigation is also allowed on non-
2586 if ((fct == FE_Delete_Previous) &&
2587 ((unsigned)form->opts & O_BS_OVERLOAD) &&
2588 First_Position_In_Current_Field(form))
2590 res = Inter_Field_Navigation(FN_Previous_Field, form);
2594 if (fct == FE_New_Line)
2596 if (((unsigned)form->opts & O_NL_OVERLOAD) &&
2597 First_Position_In_Current_Field(form))
2599 res = Inter_Field_Navigation(FN_Next_Field, form);
2602 /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */
2607 /* From now on, everything must be editable */
2608 if ((unsigned)form->current->opts & O_EDIT)
2612 SetStatus(form, _WINDOW_MODIFIED);
2619 /*---------------------------------------------------------------------------
2620 | Facility : libnform
2621 | Function : static int FE_New_Line(FORM * form)
2623 | Description : Perform a new line request. This is rather complex
2624 | compared to other routines in this code due to the
2625 | rather difficult to understand description in the
2628 | Return Values : E_OK - success
2629 | E_REQUEST_DENIED - new line not allowed
2630 | E_SYSTEM_ERROR - system error
2631 +--------------------------------------------------------------------------*/
2633 FE_New_Line(FORM *form)
2635 FIELD *field = form->current;
2637 bool Last_Row = ((field->drows - 1) == form->currow);
2639 T((T_CALLED("FE_New_Line(%p)"), (void *)form));
2640 if (form->status & _OVLMODE)
2643 (!(Growable(field) && !Single_Line_Field(field))))
2645 if (!((unsigned)form->opts & O_NL_OVERLOAD))
2646 returnCode(E_REQUEST_DENIED);
2647 wmove(form->w, form->currow, form->curcol);
2649 /* we have to set this here, although it is also
2650 handled in the generic routine. The reason is,
2651 that FN_Next_Field may fail, but the form is
2652 definitively changed */
2653 SetStatus(form, _WINDOW_MODIFIED);
2654 returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2658 if (Last_Row && !Field_Grown(field, 1))
2660 /* N.B.: due to the logic in the 'if', LastRow==TRUE
2661 means here that the field is growable and not
2662 a single-line field */
2663 returnCode(E_SYSTEM_ERROR);
2665 wmove(form->w, form->currow, form->curcol);
2669 SetStatus(form, _WINDOW_MODIFIED);
2677 !(Growable(field) && !Single_Line_Field(field)))
2679 if (!((unsigned)form->opts & O_NL_OVERLOAD))
2680 returnCode(E_REQUEST_DENIED);
2681 returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2685 bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);
2687 if (!(May_Do_It || Growable(field)))
2688 returnCode(E_REQUEST_DENIED);
2689 if (!May_Do_It && !Field_Grown(field, 1))
2690 returnCode(E_SYSTEM_ERROR);
2692 bp = Address_Of_Current_Position_In_Buffer(form);
2693 t = After_End_Of_Data(bp, field->dcols - form->curcol);
2694 wmove(form->w, form->currow, form->curcol);
2698 wmove(form->w, form->currow, form->curcol);
2700 myADDNSTR(form->w, bp, (int)(t - bp));
2701 SetStatus(form, _WINDOW_MODIFIED);
2707 /*---------------------------------------------------------------------------
2708 | Facility : libnform
2709 | Function : static int FE_Insert_Character(FORM * form)
2711 | Description : Insert blank character at the cursor position
2713 | Return Values : E_OK
2715 +--------------------------------------------------------------------------*/
2717 FE_Insert_Character(FORM *form)
2719 FIELD *field = form->current;
2720 int result = E_REQUEST_DENIED;
2722 T((T_CALLED("FE_Insert_Character(%p)"), (void *)form));
2723 if (Check_Char(form, field, field->type, (int)C_BLANK,
2724 (TypeArgument *)(field->arg)))
2726 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
2728 if (There_Is_Room ||
2729 ((Single_Line_Field(field) && Growable(field))))
2731 if (!There_Is_Room && !Field_Grown(field, 1))
2732 result = E_SYSTEM_ERROR;
2735 winsch(form->w, (chtype)C_BLANK);
2736 result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);
2743 /*---------------------------------------------------------------------------
2744 | Facility : libnform
2745 | Function : static int FE_Insert_Line(FORM * form)
2747 | Description : Insert a blank line at the cursor position
2749 | Return Values : E_OK - success
2750 | E_REQUEST_DENIED - line can not be inserted
2751 +--------------------------------------------------------------------------*/
2753 FE_Insert_Line(FORM *form)
2755 FIELD *field = form->current;
2756 int result = E_REQUEST_DENIED;
2758 T((T_CALLED("FE_Insert_Line(%p)"), (void *)form));
2759 if (Check_Char(form, field,
2760 field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2762 bool Maybe_Done = (form->currow != (field->drows - 1)) &&
2763 Is_There_Room_For_A_Line(form);
2765 if (!Single_Line_Field(field) &&
2766 (Maybe_Done || Growable(field)))
2768 if (!Maybe_Done && !Field_Grown(field, 1))
2769 result = E_SYSTEM_ERROR;
2781 /*---------------------------------------------------------------------------
2782 | Facility : libnform
2783 | Function : static int FE_Delete_Character(FORM * form)
2785 | Description : Delete character at the cursor position
2787 | Return Values : E_OK - success
2788 +--------------------------------------------------------------------------*/
2790 FE_Delete_Character(FORM *form)
2792 T((T_CALLED("FE_Delete_Character(%p)"), (void *)form));
2797 /*---------------------------------------------------------------------------
2798 | Facility : libnform
2799 | Function : static int FE_Delete_Previous(FORM * form)
2801 | Description : Delete character before cursor. Again this is a rather
2802 | difficult piece compared to others due to the overloading
2803 | semantics of backspace.
2804 | N.B.: The case of overloaded BS on first field position
2805 | is already handled in the generic routine.
2807 | Return Values : E_OK - success
2808 | E_REQUEST_DENIED - Character can't be deleted
2809 +--------------------------------------------------------------------------*/
2811 FE_Delete_Previous(FORM *form)
2813 FIELD *field = form->current;
2815 T((T_CALLED("FE_Delete_Previous(%p)"), (void *)form));
2816 if (First_Position_In_Current_Field(form))
2817 returnCode(E_REQUEST_DENIED);
2819 if ((--(form->curcol)) < 0)
2821 FIELD_CELL *this_line, *prev_line, *prev_end, *this_end;
2822 int this_row = form->currow;
2825 if (form->status & _OVLMODE)
2826 returnCode(E_REQUEST_DENIED);
2828 prev_line = Address_Of_Row_In_Buffer(field, (form->currow - 1));
2829 this_line = Address_Of_Row_In_Buffer(field, (form->currow));
2830 Synchronize_Buffer(form);
2831 prev_end = After_End_Of_Data(prev_line, field->dcols);
2832 this_end = After_End_Of_Data(this_line, field->dcols);
2833 if ((int)(this_end - this_line) >
2834 (field->cols - (int)(prev_end - prev_line)))
2835 returnCode(E_REQUEST_DENIED);
2836 wmove(form->w, form->currow, form->curcol);
2838 Adjust_Cursor_Position(form, prev_end);
2840 * If we did not really move to the previous line, help the user a
2841 * little. It is however a little inconsistent. Normally, when
2842 * backspacing around the point where text wraps to a new line in a
2843 * multi-line form, we absorb one keystroke for the wrapping point. That
2844 * is consistent with SVr4 forms. However, SVr4 does not allow typing
2845 * into the last column of the field, and requires the user to enter a
2846 * newline to move to the next line. Therefore it can consistently eat
2847 * that keystroke. Since ncurses allows the last column, it wraps
2848 * automatically (given the proper options). But we cannot eat the
2849 * keystroke to back over the wrapping point, since that would put the
2850 * cursor past the end of the form field. In this case, just delete the
2851 * character at the end of the field.
2853 if (form->currow == this_row && this_row > 0)
2856 form->curcol = field->dcols - 1;
2861 wmove(form->w, form->currow, form->curcol);
2862 myADDNSTR(form->w, this_line, (int)(this_end - this_line));
2872 /*---------------------------------------------------------------------------
2873 | Facility : libnform
2874 | Function : static int FE_Delete_Line(FORM * form)
2876 | Description : Delete line at cursor position.
2878 | Return Values : E_OK - success
2879 +--------------------------------------------------------------------------*/
2881 FE_Delete_Line(FORM *form)
2883 T((T_CALLED("FE_Delete_Line(%p)"), (void *)form));
2889 /*---------------------------------------------------------------------------
2890 | Facility : libnform
2891 | Function : static int FE_Delete_Word(FORM * form)
2893 | Description : Delete word at cursor position
2895 | Return Values : E_OK - success
2896 | E_REQUEST_DENIED - failure
2897 +--------------------------------------------------------------------------*/
2899 FE_Delete_Word(FORM *form)
2901 FIELD *field = form->current;
2902 FIELD_CELL *bp = Address_Of_Current_Row_In_Buffer(form);
2903 FIELD_CELL *ep = bp + field->dcols;
2904 FIELD_CELL *cp = bp + form->curcol;
2907 T((T_CALLED("FE_Delete_Word(%p)"), (void *)form));
2908 Synchronize_Buffer(form);
2910 returnCode(E_REQUEST_DENIED); /* not in word */
2912 /* move cursor to begin of word and erase to end of screen-line */
2913 Adjust_Cursor_Position(form,
2914 After_Last_Whitespace_Character(bp, form->curcol));
2915 wmove(form->w, form->currow, form->curcol);
2918 /* skip over word in buffer */
2919 s = Get_First_Whitespace_Character(cp, (int)(ep - cp));
2920 /* to begin of next word */
2921 s = Get_Start_Of_Data(s, (int)(ep - s));
2922 if ((s != cp) && !ISBLANK(*s))
2924 /* copy remaining line to window */
2925 myADDNSTR(form->w, s, (int)(s - After_End_Of_Data(s, (int)(ep - s))));
2930 /*---------------------------------------------------------------------------
2931 | Facility : libnform
2932 | Function : static int FE_Clear_To_End_Of_Line(FORM * form)
2934 | Description : Clear to end of current line.
2936 | Return Values : E_OK - success
2937 +--------------------------------------------------------------------------*/
2939 FE_Clear_To_End_Of_Line(FORM *form)
2941 T((T_CALLED("FE_Clear_To_End_Of_Line(%p)"), (void *)form));
2942 wmove(form->w, form->currow, form->curcol);
2947 /*---------------------------------------------------------------------------
2948 | Facility : libnform
2949 | Function : static int FE_Clear_To_End_Of_Field(FORM * form)
2951 | Description : Clear to end of field.
2953 | Return Values : E_OK - success
2954 +--------------------------------------------------------------------------*/
2956 FE_Clear_To_End_Of_Field(FORM *form)
2958 T((T_CALLED("FE_Clear_To_End_Of_Field(%p)"), (void *)form));
2959 wmove(form->w, form->currow, form->curcol);
2964 /*---------------------------------------------------------------------------
2965 | Facility : libnform
2966 | Function : static int FE_Clear_Field(FORM * form)
2968 | Description : Clear entire field.
2970 | Return Values : E_OK - success
2971 +--------------------------------------------------------------------------*/
2973 FE_Clear_Field(FORM *form)
2975 T((T_CALLED("FE_Clear_Field(%p)"), (void *)form));
2976 form->currow = form->curcol = 0;
2980 /*----------------------------------------------------------------------------
2981 END of Field Editing routines
2982 --------------------------------------------------------------------------*/
2984 /*----------------------------------------------------------------------------
2986 --------------------------------------------------------------------------*/
2988 /*---------------------------------------------------------------------------
2989 | Facility : libnform
2990 | Function : static int EM_Overlay_Mode(FORM * form)
2992 | Description : Switch to overlay mode.
2994 | Return Values : E_OK - success
2995 +--------------------------------------------------------------------------*/
2997 EM_Overlay_Mode(FORM *form)
2999 T((T_CALLED("EM_Overlay_Mode(%p)"), (void *)form));
3000 SetStatus(form, _OVLMODE);
3004 /*---------------------------------------------------------------------------
3005 | Facility : libnform
3006 | Function : static int EM_Insert_Mode(FORM * form)
3008 | Description : Switch to insert mode
3010 | Return Values : E_OK - success
3011 +--------------------------------------------------------------------------*/
3013 EM_Insert_Mode(FORM *form)
3015 T((T_CALLED("EM_Insert_Mode(%p)"), (void *)form));
3016 ClrStatus(form, _OVLMODE);
3020 /*----------------------------------------------------------------------------
3021 END of Edit Mode routines
3022 --------------------------------------------------------------------------*/
3024 /*----------------------------------------------------------------------------
3025 Helper routines for Choice Requests
3026 --------------------------------------------------------------------------*/
3028 /*---------------------------------------------------------------------------
3029 | Facility : libnform
3030 | Function : static bool Next_Choice(FORM * form,
3033 | TypeArgument *argp)
3035 | Description : Get the next field choice. For linked types this is
3038 | Return Values : TRUE - next choice successfully retrieved
3039 | FALSE - couldn't retrieve next choice
3040 +--------------------------------------------------------------------------*/
3042 Next_Choice(FORM *form, FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3044 if (!typ || !(typ->status & _HAS_CHOICE))
3047 if (typ->status & _LINKED_TYPE)
3051 Next_Choice(form, typ->left, field, argp->left) ||
3052 Next_Choice(form, typ->right, field, argp->right));
3056 #if NCURSES_INTEROP_FUNCS
3057 assert(typ->enum_next.onext);
3058 if (typ->status & _GENERIC)
3059 return typ->enum_next.gnext(form, field, (void *)argp);
3061 return typ->enum_next.onext(field, (void *)argp);
3064 return typ->next(field, (void *)argp);
3069 /*---------------------------------------------------------------------------
3070 | Facility : libnform
3071 | Function : static bool Previous_Choice(FORM * form,
3074 | TypeArgument *argp)
3076 | Description : Get the previous field choice. For linked types this
3077 | is done recursively.
3079 | Return Values : TRUE - previous choice successfully retrieved
3080 | FALSE - couldn't retrieve previous choice
3081 +--------------------------------------------------------------------------*/
3083 Previous_Choice(FORM *form, FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3085 if (!typ || !(typ->status & _HAS_CHOICE))
3088 if (typ->status & _LINKED_TYPE)
3092 Previous_Choice(form, typ->left, field, argp->left) ||
3093 Previous_Choice(form, typ->right, field, argp->right));
3097 #if NCURSES_INTEROP_FUNCS
3098 assert(typ->enum_prev.oprev);
3099 if (typ->status & _GENERIC)
3100 return typ->enum_prev.gprev(form, field, (void *)argp);
3102 return typ->enum_prev.oprev(field, (void *)argp);
3105 return typ->prev(field, (void *)argp);
3109 /*----------------------------------------------------------------------------
3110 End of Helper routines for Choice Requests
3111 --------------------------------------------------------------------------*/
3113 /*----------------------------------------------------------------------------
3114 Routines for Choice Requests
3115 --------------------------------------------------------------------------*/
3117 /*---------------------------------------------------------------------------
3118 | Facility : libnform
3119 | Function : static int CR_Next_Choice(FORM * form)
3121 | Description : Get the next field choice.
3123 | Return Values : E_OK - success
3124 | E_REQUEST_DENIED - next choice couldn't be retrieved
3125 +--------------------------------------------------------------------------*/
3127 CR_Next_Choice(FORM *form)
3129 FIELD *field = form->current;
3131 T((T_CALLED("CR_Next_Choice(%p)"), (void *)form));
3132 Synchronize_Buffer(form);
3133 returnCode((Next_Choice(form, field->type, field, (TypeArgument *)(field->arg)))
3135 : E_REQUEST_DENIED);
3138 /*---------------------------------------------------------------------------
3139 | Facility : libnform
3140 | Function : static int CR_Previous_Choice(FORM * form)
3142 | Description : Get the previous field choice.
3144 | Return Values : E_OK - success
3145 | E_REQUEST_DENIED - prev. choice couldn't be retrieved
3146 +--------------------------------------------------------------------------*/
3148 CR_Previous_Choice(FORM *form)
3150 FIELD *field = form->current;
3152 T((T_CALLED("CR_Previous_Choice(%p)"), (void *)form));
3153 Synchronize_Buffer(form);
3154 returnCode((Previous_Choice(form, field->type, field, (TypeArgument *)(field->arg)))
3156 : E_REQUEST_DENIED);
3158 /*----------------------------------------------------------------------------
3159 End of Routines for Choice Requests
3160 --------------------------------------------------------------------------*/
3162 /*----------------------------------------------------------------------------
3163 Helper routines for Field Validations.
3164 --------------------------------------------------------------------------*/
3166 /*---------------------------------------------------------------------------
3167 | Facility : libnform
3168 | Function : static bool Check_Field(FORM* form,
3171 | TypeArgument * argp)
3173 | Description : Check the field according to its fieldtype and its
3174 | actual arguments. For linked fieldtypes this is done
3177 | Return Values : TRUE - field is valid
3178 | FALSE - field is invalid.
3179 +--------------------------------------------------------------------------*/
3181 Check_Field(FORM *form, FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3185 if (Field_Has_Option(field, O_NULLOK))
3187 FIELD_CELL *bp = field->buf;
3190 while (ISBLANK(*bp))
3194 if (CharOf(*bp) == 0)
3198 if (typ->status & _LINKED_TYPE)
3202 Check_Field(form, typ->left, field, argp->left) ||
3203 Check_Field(form, typ->right, field, argp->right));
3207 #if NCURSES_INTEROP_FUNCS
3208 if (typ->fieldcheck.ofcheck)
3210 if (typ->status & _GENERIC)
3211 return typ->fieldcheck.gfcheck(form, field, (void *)argp);
3213 return typ->fieldcheck.ofcheck(field, (void *)argp);
3217 return typ->fcheck(field, (void *)argp);
3224 /*---------------------------------------------------------------------------
3225 | Facility : libnform
3226 | Function : bool _nc_Internal_Validation(FORM * form )
3228 | Description : Validate the current field of the form.
3230 | Return Values : TRUE - field is valid
3231 | FALSE - field is invalid
3232 +--------------------------------------------------------------------------*/
3234 _nc_Internal_Validation(FORM *form)
3238 field = form->current;
3240 Synchronize_Buffer(form);
3241 if ((form->status & _FCHECK_REQUIRED) ||
3242 (!(Field_Has_Option(field, O_PASSOK))))
3244 if (!Check_Field(form, field->type, field, (TypeArgument *)(field->arg)))
3246 ClrStatus(form, _FCHECK_REQUIRED);
3247 SetStatus(field, _CHANGED);
3248 Synchronize_Linked_Fields(field);
3252 /*----------------------------------------------------------------------------
3253 End of Helper routines for Field Validations.
3254 --------------------------------------------------------------------------*/
3256 /*----------------------------------------------------------------------------
3257 Routines for Field Validation.
3258 --------------------------------------------------------------------------*/
3260 /*---------------------------------------------------------------------------
3261 | Facility : libnform
3262 | Function : static int FV_Validation(FORM * form)
3264 | Description : Validate the current field of the form.
3266 | Return Values : E_OK - field valid
3267 | E_INVALID_FIELD - field not valid
3268 +--------------------------------------------------------------------------*/
3270 FV_Validation(FORM *form)
3272 T((T_CALLED("FV_Validation(%p)"), (void *)form));
3273 if (_nc_Internal_Validation(form))
3276 returnCode(E_INVALID_FIELD);
3278 /*----------------------------------------------------------------------------
3279 End of routines for Field Validation.
3280 --------------------------------------------------------------------------*/
3282 /*----------------------------------------------------------------------------
3283 Helper routines for Inter-Field Navigation
3284 --------------------------------------------------------------------------*/
3286 /*---------------------------------------------------------------------------
3287 | Facility : libnform
3288 | Function : static FIELD *Next_Field_On_Page(FIELD * field)
3290 | Description : Get the next field after the given field on the current
3291 | page. The order of fields is the one defined by the
3292 | field's array. Only visible and active fields are
3295 | Return Values : Pointer to the next field.
3296 +--------------------------------------------------------------------------*/
3297 NCURSES_INLINE static FIELD *
3298 Next_Field_On_Page(FIELD *field)
3300 FORM *form = field->form;
3301 FIELD **field_on_page = &form->field[field->index];
3302 FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3303 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3308 (field_on_page == last_on_page) ? first_on_page : field_on_page + 1;
3309 if (Field_Is_Selectable(*field_on_page))
3312 while (field != (*field_on_page));
3313 return (*field_on_page);
3316 /*---------------------------------------------------------------------------
3317 | Facility : libnform
3318 | Function : FIELD* _nc_First_Active_Field(FORM * form)
3320 | Description : Get the first active field on the current page,
3321 | if there are such. If there are none, get the first
3322 | visible field on the page. If there are also none,
3323 | we return the first field on page and hope the best.
3325 | Return Values : Pointer to calculated field.
3326 +--------------------------------------------------------------------------*/
3327 FORM_EXPORT(FIELD *)
3328 _nc_First_Active_Field(FORM *form)
3330 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3331 FIELD *proposed = Next_Field_On_Page(*last_on_page);
3333 if (proposed == *last_on_page)
3335 /* there might be the special situation, where there is no
3336 active and visible field on the current page. We then select
3337 the first visible field on this readonly page
3339 if (Field_Is_Not_Selectable(proposed))
3341 FIELD **field = &form->field[proposed->index];
3342 FIELD **first = &form->field[form->page[form->curpage].pmin];
3346 field = (field == last_on_page) ? first : field + 1;
3347 if (Field_Has_Option(*field, O_VISIBLE))
3350 while (proposed != (*field));
3354 if ((proposed == *last_on_page) &&
3355 !((unsigned)proposed->opts & O_VISIBLE))
3357 /* This means, there is also no visible field on the page.
3358 So we propose the first one and hope the very best...
3359 Some very clever user has designed a readonly and invisible
3369 /*---------------------------------------------------------------------------
3370 | Facility : libnform
3371 | Function : static FIELD *Previous_Field_On_Page(FIELD * field)
3373 | Description : Get the previous field before the given field on the
3374 | current page. The order of fields is the one defined by
3375 | the field's array. Only visible and active fields are
3378 | Return Values : Pointer to the previous field.
3379 +--------------------------------------------------------------------------*/
3380 NCURSES_INLINE static FIELD *
3381 Previous_Field_On_Page(FIELD *field)
3383 FORM *form = field->form;
3384 FIELD **field_on_page = &form->field[field->index];
3385 FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3386 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3391 (field_on_page == first_on_page) ? last_on_page : field_on_page - 1;
3392 if (Field_Is_Selectable(*field_on_page))
3395 while (field != (*field_on_page));
3397 return (*field_on_page);
3400 /*---------------------------------------------------------------------------
3401 | Facility : libnform
3402 | Function : static FIELD *Sorted_Next_Field(FIELD * field)
3404 | Description : Get the next field after the given field on the current
3405 | page. The order of fields is the one defined by the
3406 | (row,column) geometry, rows are major.
3408 | Return Values : Pointer to the next field.
3409 +--------------------------------------------------------------------------*/
3410 NCURSES_INLINE static FIELD *
3411 Sorted_Next_Field(FIELD *field)
3413 FIELD *field_on_page = field;
3417 field_on_page = field_on_page->snext;
3418 if (Field_Is_Selectable(field_on_page))
3421 while (field_on_page != field);
3423 return (field_on_page);
3426 /*---------------------------------------------------------------------------
3427 | Facility : libnform
3428 | Function : static FIELD *Sorted_Previous_Field(FIELD * field)
3430 | Description : Get the previous field before the given field on the
3431 | current page. The order of fields is the one defined
3432 | by the (row,column) geometry, rows are major.
3434 | Return Values : Pointer to the previous field.
3435 +--------------------------------------------------------------------------*/
3436 NCURSES_INLINE static FIELD *
3437 Sorted_Previous_Field(FIELD *field)
3439 FIELD *field_on_page = field;
3443 field_on_page = field_on_page->sprev;
3444 if (Field_Is_Selectable(field_on_page))
3447 while (field_on_page != field);
3449 return (field_on_page);
3452 /*---------------------------------------------------------------------------
3453 | Facility : libnform
3454 | Function : static FIELD *Left_Neighbor_Field(FIELD * field)
3456 | Description : Get the left neighbor of the field on the same line
3457 | and the same page. Cycles through the line.
3459 | Return Values : Pointer to left neighbor field.
3460 +--------------------------------------------------------------------------*/
3461 NCURSES_INLINE static FIELD *
3462 Left_Neighbor_Field(FIELD *field)
3464 FIELD *field_on_page = field;
3466 /* For a field that has really a left neighbor, the while clause
3467 immediately fails and the loop is left, positioned at the right
3468 neighbor. Otherwise we cycle backwards through the sorted field list
3469 until we enter the same line (from the right end).
3473 field_on_page = Sorted_Previous_Field(field_on_page);
3475 while (field_on_page->frow != field->frow);
3477 return (field_on_page);
3480 /*---------------------------------------------------------------------------
3481 | Facility : libnform
3482 | Function : static FIELD *Right_Neighbor_Field(FIELD * field)
3484 | Description : Get the right neighbor of the field on the same line
3485 | and the same page.
3487 | Return Values : Pointer to right neighbor field.
3488 +--------------------------------------------------------------------------*/
3489 NCURSES_INLINE static FIELD *
3490 Right_Neighbor_Field(FIELD *field)
3492 FIELD *field_on_page = field;
3494 /* See the comments on Left_Neighbor_Field to understand how it works */
3497 field_on_page = Sorted_Next_Field(field_on_page);
3499 while (field_on_page->frow != field->frow);
3501 return (field_on_page);
3504 /*---------------------------------------------------------------------------
3505 | Facility : libnform
3506 | Function : static FIELD *Upper_Neighbor_Field(FIELD * field)
3508 | Description : Because of the row-major nature of sorting the fields,
3509 | it is more difficult to define what the upper neighbor
3510 | field really means. We define that it must be on a
3511 | 'previous' line (cyclic order!) and is the rightmost
3512 | field lying on the left side of the given field. If
3513 | this set is empty, we take the first field on the line.
3515 | Return Values : Pointer to the upper neighbor field.
3516 +--------------------------------------------------------------------------*/
3518 Upper_Neighbor_Field(FIELD *field)
3520 FIELD *field_on_page = field;
3521 int frow = field->frow;
3522 int fcol = field->fcol;
3524 /* Walk back to the 'previous' line. The second term in the while clause
3525 just guarantees that we stop if we cycled through the line because
3526 there might be no 'previous' line if the page has just one line.
3530 field_on_page = Sorted_Previous_Field(field_on_page);
3532 while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3534 if (field_on_page->frow != frow)
3536 /* We really found a 'previous' line. We are positioned at the
3537 rightmost field on this line */
3538 frow = field_on_page->frow;
3540 /* We walk to the left as long as we are really right of the
3542 while (field_on_page->frow == frow && field_on_page->fcol > fcol)
3543 field_on_page = Sorted_Previous_Field(field_on_page);
3545 /* If we wrapped, just go to the right which is the first field on
3547 if (field_on_page->frow != frow)
3548 field_on_page = Sorted_Next_Field(field_on_page);
3551 return (field_on_page);
3554 /*---------------------------------------------------------------------------
3555 | Facility : libnform
3556 | Function : static FIELD *Down_Neighbor_Field(FIELD * field)
3558 | Description : Because of the row-major nature of sorting the fields,
3559 | it is more difficult to define what the down neighbor
3560 | field really means. We define that it must be on a
3561 | 'next' line (cyclic order!) and is the leftmost
3562 | field laying on the right side of the given field. If
3563 | this set is empty, we take the last field on the line.
3565 | Return Values : Pointer to the upper neighbor field.
3566 +--------------------------------------------------------------------------*/
3568 Down_Neighbor_Field(FIELD *field)
3570 FIELD *field_on_page = field;
3571 int frow = field->frow;
3572 int fcol = field->fcol;
3574 /* Walk forward to the 'next' line. The second term in the while clause
3575 just guarantees that we stop if we cycled through the line because
3576 there might be no 'next' line if the page has just one line.
3580 field_on_page = Sorted_Next_Field(field_on_page);
3582 while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3584 if (field_on_page->frow != frow)
3586 /* We really found a 'next' line. We are positioned at the rightmost
3587 field on this line */
3588 frow = field_on_page->frow;
3590 /* We walk to the right as long as we are really left of the
3592 while (field_on_page->frow == frow && field_on_page->fcol < fcol)
3593 field_on_page = Sorted_Next_Field(field_on_page);
3595 /* If we wrapped, just go to the left which is the last field on
3597 if (field_on_page->frow != frow)
3598 field_on_page = Sorted_Previous_Field(field_on_page);
3601 return (field_on_page);
3604 /*----------------------------------------------------------------------------
3605 Inter-Field Navigation routines
3606 --------------------------------------------------------------------------*/
3608 /*---------------------------------------------------------------------------
3609 | Facility : libnform
3610 | Function : static int Inter_Field_Navigation(
3611 | int (* const fct) (FORM *),
3614 | Description : Generic behavior for changing the current field, the
3615 | field is left and a new field is entered. So the field
3616 | must be validated and the field init/term hooks must
3619 | Return Values : E_OK - success
3620 | E_INVALID_FIELD - field is invalid
3621 | some other - error from subordinate call
3622 +--------------------------------------------------------------------------*/
3624 Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form)
3628 if (!_nc_Internal_Validation(form))
3629 res = E_INVALID_FIELD;
3632 Call_Hook(form, fieldterm);
3634 Call_Hook(form, fieldinit);
3639 /*---------------------------------------------------------------------------
3640 | Facility : libnform
3641 | Function : static int FN_Next_Field(FORM * form)
3643 | Description : Move to the next field on the current page of the form
3645 | Return Values : E_OK - success
3646 | != E_OK - error from subordinate call
3647 +--------------------------------------------------------------------------*/
3649 FN_Next_Field(FORM *form)
3651 T((T_CALLED("FN_Next_Field(%p)"), (void *)form));
3652 returnCode(_nc_Set_Current_Field(form,
3653 Next_Field_On_Page(form->current)));
3656 /*---------------------------------------------------------------------------
3657 | Facility : libnform
3658 | Function : static int FN_Previous_Field(FORM * form)
3660 | Description : Move to the previous field on the current page of the
3663 | Return Values : E_OK - success
3664 | != E_OK - error from subordinate call
3665 +--------------------------------------------------------------------------*/
3667 FN_Previous_Field(FORM *form)
3669 T((T_CALLED("FN_Previous_Field(%p)"), (void *)form));
3670 returnCode(_nc_Set_Current_Field(form,
3671 Previous_Field_On_Page(form->current)));
3674 /*---------------------------------------------------------------------------
3675 | Facility : libnform
3676 | Function : static int FN_First_Field(FORM * form)
3678 | Description : Move to the first field on the current page of the form
3680 | Return Values : E_OK - success
3681 | != E_OK - error from subordinate call
3682 +--------------------------------------------------------------------------*/
3684 FN_First_Field(FORM *form)
3686 T((T_CALLED("FN_First_Field(%p)"), (void *)form));
3687 returnCode(_nc_Set_Current_Field(form,
3688 Next_Field_On_Page(form->field[form->page[form->curpage].pmax])));
3691 /*---------------------------------------------------------------------------
3692 | Facility : libnform
3693 | Function : static int FN_Last_Field(FORM * form)
3695 | Description : Move to the last field on the current page of the form
3697 | Return Values : E_OK - success
3698 | != E_OK - error from subordinate call
3699 +--------------------------------------------------------------------------*/
3701 FN_Last_Field(FORM *form)
3703 T((T_CALLED("FN_Last_Field(%p)"), (void *)form));
3705 _nc_Set_Current_Field(form,
3706 Previous_Field_On_Page(form->field[form->page[form->curpage].pmin])));
3709 /*---------------------------------------------------------------------------
3710 | Facility : libnform
3711 | Function : static int FN_Sorted_Next_Field(FORM * form)
3713 | Description : Move to the sorted next field on the current page
3716 | Return Values : E_OK - success
3717 | != E_OK - error from subordinate call
3718 +--------------------------------------------------------------------------*/
3720 FN_Sorted_Next_Field(FORM *form)
3722 T((T_CALLED("FN_Sorted_Next_Field(%p)"), (void *)form));
3723 returnCode(_nc_Set_Current_Field(form,
3724 Sorted_Next_Field(form->current)));
3727 /*---------------------------------------------------------------------------
3728 | Facility : libnform
3729 | Function : static int FN_Sorted_Previous_Field(FORM * form)
3731 | Description : Move to the sorted previous field on the current page
3734 | Return Values : E_OK - success
3735 | != E_OK - error from subordinate call
3736 +--------------------------------------------------------------------------*/
3738 FN_Sorted_Previous_Field(FORM *form)
3740 T((T_CALLED("FN_Sorted_Previous_Field(%p)"), (void *)form));
3741 returnCode(_nc_Set_Current_Field(form,
3742 Sorted_Previous_Field(form->current)));
3745 /*---------------------------------------------------------------------------
3746 | Facility : libnform
3747 | Function : static int FN_Sorted_First_Field(FORM * form)
3749 | Description : Move to the sorted first field on the current page
3752 | Return Values : E_OK - success
3753 | != E_OK - error from subordinate call
3754 +--------------------------------------------------------------------------*/
3756 FN_Sorted_First_Field(FORM *form)
3758 T((T_CALLED("FN_Sorted_First_Field(%p)"), (void *)form));
3759 returnCode(_nc_Set_Current_Field(form,
3760 Sorted_Next_Field(form->field[form->page[form->curpage].smax])));
3763 /*---------------------------------------------------------------------------
3764 | Facility : libnform
3765 | Function : static int FN_Sorted_Last_Field(FORM * form)
3767 | Description : Move to the sorted last field on the current page
3770 | Return Values : E_OK - success
3771 | != E_OK - error from subordinate call
3772 +--------------------------------------------------------------------------*/
3774 FN_Sorted_Last_Field(FORM *form)
3776 T((T_CALLED("FN_Sorted_Last_Field(%p)"), (void *)form));
3777 returnCode(_nc_Set_Current_Field(form,
3778 Sorted_Previous_Field(form->field[form->page[form->curpage].smin])));
3781 /*---------------------------------------------------------------------------
3782 | Facility : libnform
3783 | Function : static int FN_Left_Field(FORM * form)
3785 | Description : Get the field on the left of the current field on the
3786 | same line and the same page. Cycles through the line.
3788 | Return Values : E_OK - success
3789 | != E_OK - error from subordinate call
3790 +--------------------------------------------------------------------------*/
3792 FN_Left_Field(FORM *form)
3794 T((T_CALLED("FN_Left_Field(%p)"), (void *)form));
3795 returnCode(_nc_Set_Current_Field(form,
3796 Left_Neighbor_Field(form->current)));
3799 /*---------------------------------------------------------------------------
3800 | Facility : libnform
3801 | Function : static int FN_Right_Field(FORM * form)
3803 | Description : Get the field on the right of the current field on the
3804 | same line and the same page. Cycles through the line.
3806 | Return Values : E_OK - success
3807 | != E_OK - error from subordinate call
3808 +--------------------------------------------------------------------------*/
3810 FN_Right_Field(FORM *form)
3812 T((T_CALLED("FN_Right_Field(%p)"), (void *)form));
3813 returnCode(_nc_Set_Current_Field(form,
3814 Right_Neighbor_Field(form->current)));
3817 /*---------------------------------------------------------------------------
3818 | Facility : libnform
3819 | Function : static int FN_Up_Field(FORM * form)
3821 | Description : Get the upper neighbor of the current field. This
3822 | cycles through the page. See the comments of the
3823 | Upper_Neighbor_Field function to understand how
3824 | 'upper' is defined.
3826 | Return Values : E_OK - success
3827 | != E_OK - error from subordinate call
3828 +--------------------------------------------------------------------------*/
3830 FN_Up_Field(FORM *form)
3832 T((T_CALLED("FN_Up_Field(%p)"), (void *)form));
3833 returnCode(_nc_Set_Current_Field(form,
3834 Upper_Neighbor_Field(form->current)));
3837 /*---------------------------------------------------------------------------
3838 | Facility : libnform
3839 | Function : static int FN_Down_Field(FORM * form)
3841 | Description : Get the down neighbor of the current field. This
3842 | cycles through the page. See the comments of the
3843 | Down_Neighbor_Field function to understand how
3844 | 'down' is defined.
3846 | Return Values : E_OK - success
3847 | != E_OK - error from subordinate call
3848 +--------------------------------------------------------------------------*/
3850 FN_Down_Field(FORM *form)
3852 T((T_CALLED("FN_Down_Field(%p)"), (void *)form));
3853 returnCode(_nc_Set_Current_Field(form,
3854 Down_Neighbor_Field(form->current)));
3856 /*----------------------------------------------------------------------------
3857 END of Field Navigation routines
3858 --------------------------------------------------------------------------*/
3860 /*----------------------------------------------------------------------------
3861 Helper routines for Page Navigation
3862 --------------------------------------------------------------------------*/
3864 /*---------------------------------------------------------------------------
3865 | Facility : libnform
3866 | Function : int _nc_Set_Form_Page(FORM * form,
3870 | Description : Make the given page number the current page and make
3871 | the given field the current field on the page. If
3872 | for the field NULL is given, make the first field on
3873 | the page the current field. The routine acts only
3874 | if the requested page is not the current page.
3876 | Return Values : E_OK - success
3877 | != E_OK - error from subordinate call
3878 | E_BAD_ARGUMENT - invalid field pointer
3879 | E_SYSTEM_ERROR - some severe basic error
3880 +--------------------------------------------------------------------------*/
3882 _nc_Set_Form_Page(FORM *form, int page, FIELD *field)
3886 if ((form->curpage != page))
3888 FIELD *last_field, *field_on_page;
3890 werase(Get_Form_Window(form));
3891 form->curpage = (short)page;
3892 last_field = field_on_page = form->field[form->page[page].smin];
3895 if ((unsigned)field_on_page->opts & O_VISIBLE)
3896 if ((res = Display_Field(field_on_page)) != E_OK)
3898 field_on_page = field_on_page->snext;
3900 while (field_on_page != last_field);
3903 res = _nc_Set_Current_Field(form, field);
3905 /* N.B.: we don't encapsulate this by Inter_Field_Navigation(),
3906 because this is already executed in a page navigation
3907 context that contains field navigation
3909 res = FN_First_Field(form);
3914 /*---------------------------------------------------------------------------
3915 | Facility : libnform
3916 | Function : static int Next_Page_Number(const FORM * form)
3918 | Description : Calculate the page number following the current page
3919 | number. This cycles if the highest page number is
3922 | Return Values : The next page number
3923 +--------------------------------------------------------------------------*/
3924 NCURSES_INLINE static int
3925 Next_Page_Number(const FORM *form)
3927 return (form->curpage + 1) % form->maxpage;
3930 /*---------------------------------------------------------------------------
3931 | Facility : libnform
3932 | Function : static int Previous_Page_Number(const FORM * form)
3934 | Description : Calculate the page number before the current page
3935 | number. This cycles if the first page number is
3938 | Return Values : The previous page number
3939 +--------------------------------------------------------------------------*/
3940 NCURSES_INLINE static int
3941 Previous_Page_Number(const FORM *form)
3943 return (form->curpage != 0 ? form->curpage - 1 : form->maxpage - 1);
3946 /*----------------------------------------------------------------------------
3947 Page Navigation routines
3948 --------------------------------------------------------------------------*/
3950 /*---------------------------------------------------------------------------
3951 | Facility : libnform
3952 | Function : static int Page_Navigation(
3953 | int (* const fct) (FORM *),
3956 | Description : Generic behavior for changing a page. This means
3957 | that the field is left and a new field is entered.
3958 | So the field must be validated and the field init/term
3959 | hooks must be called. Because also the page is changed,
3960 | the form's init/term hooks must be called also.
3962 | Return Values : E_OK - success
3963 | E_INVALID_FIELD - field is invalid
3964 | some other - error from subordinate call
3965 +--------------------------------------------------------------------------*/
3967 Page_Navigation(int (*const fct) (FORM *), FORM *form)
3971 if (!_nc_Internal_Validation(form))
3972 res = E_INVALID_FIELD;
3975 Call_Hook(form, fieldterm);
3976 Call_Hook(form, formterm);
3978 Call_Hook(form, forminit);
3979 Call_Hook(form, fieldinit);
3984 /*---------------------------------------------------------------------------
3985 | Facility : libnform
3986 | Function : static int PN_Next_Page(FORM * form)
3988 | Description : Move to the next page of the form
3990 | Return Values : E_OK - success
3991 | != E_OK - error from subordinate call
3992 +--------------------------------------------------------------------------*/
3994 PN_Next_Page(FORM *form)
3996 T((T_CALLED("PN_Next_Page(%p)"), (void *)form));
3997 returnCode(_nc_Set_Form_Page(form, Next_Page_Number(form), (FIELD *)0));
4000 /*---------------------------------------------------------------------------
4001 | Facility : libnform
4002 | Function : static int PN_Previous_Page(FORM * form)
4004 | Description : Move to the previous page of the form
4006 | Return Values : E_OK - success
4007 | != E_OK - error from subordinate call
4008 +--------------------------------------------------------------------------*/
4010 PN_Previous_Page(FORM *form)
4012 T((T_CALLED("PN_Previous_Page(%p)"), (void *)form));
4013 returnCode(_nc_Set_Form_Page(form, Previous_Page_Number(form), (FIELD *)0));
4016 /*---------------------------------------------------------------------------
4017 | Facility : libnform
4018 | Function : static int PN_First_Page(FORM * form)
4020 | Description : Move to the first page of the form
4022 | Return Values : E_OK - success
4023 | != E_OK - error from subordinate call
4024 +--------------------------------------------------------------------------*/
4026 PN_First_Page(FORM *form)
4028 T((T_CALLED("PN_First_Page(%p)"), (void *)form));
4029 returnCode(_nc_Set_Form_Page(form, 0, (FIELD *)0));
4032 /*---------------------------------------------------------------------------
4033 | Facility : libnform
4034 | Function : static int PN_Last_Page(FORM * form)
4036 | Description : Move to the last page of the form
4038 | Return Values : E_OK - success
4039 | != E_OK - error from subordinate call
4040 +--------------------------------------------------------------------------*/
4042 PN_Last_Page(FORM *form)
4044 T((T_CALLED("PN_Last_Page(%p)"), (void *)form));
4045 returnCode(_nc_Set_Form_Page(form, form->maxpage - 1, (FIELD *)0));
4048 /*----------------------------------------------------------------------------
4049 END of Field Navigation routines
4050 --------------------------------------------------------------------------*/
4052 /*----------------------------------------------------------------------------
4053 Helper routines for the core form driver.
4054 --------------------------------------------------------------------------*/
4056 # if USE_WIDEC_SUPPORT
4057 /*---------------------------------------------------------------------------
4058 | Facility : libnform
4059 | Function : static int Data_Entry_w(FORM * form, wchar_t c)
4061 | Description : Enter the wide character c into at the current
4062 | position of the current field of the form.
4064 | Return Values : E_OK - success
4065 | E_REQUEST_DENIED - driver could not process the request
4067 +--------------------------------------------------------------------------*/
4069 Data_Entry_w(FORM *form, wchar_t c)
4071 FIELD *field = form->current;
4072 int result = E_REQUEST_DENIED;
4074 T((T_CALLED("Data_Entry(%p,%s)"), (void *)form, _tracechtype((chtype)c)));
4075 if ((Field_Has_Option(field, O_EDIT))
4076 #if FIX_FORM_INACTIVE_BUG
4077 && (Field_Has_Option(field, O_ACTIVE))
4086 setcchar(&temp_ch, given, 0, 0, (void *)0);
4087 if ((Field_Has_Option(field, O_BLANK)) &&
4088 First_Position_In_Current_Field(form) &&
4089 !(form->status & _FCHECK_REQUIRED) &&
4090 !(form->status & _WINDOW_MODIFIED))
4093 if (form->status & _OVLMODE)
4095 wadd_wch(form->w, &temp_ch);
4100 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
4102 if (!(There_Is_Room ||
4103 ((Single_Line_Field(field) && Growable(field)))))
4104 RETURN(E_REQUEST_DENIED);
4106 if (!There_Is_Room && !Field_Grown(field, 1))
4107 RETURN(E_SYSTEM_ERROR);
4109 wins_wch(form->w, &temp_ch);
4112 if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
4114 bool End_Of_Field = (((field->drows - 1) == form->currow) &&
4115 ((field->dcols - 1) == form->curcol));
4117 form->status |= _WINDOW_MODIFIED;
4118 if (End_Of_Field && !Growable(field) && (Field_Has_Option(field, O_AUTOSKIP)))
4119 result = Inter_Field_Navigation(FN_Next_Field, form);
4122 if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
4123 result = E_SYSTEM_ERROR;
4127 * We have just added a byte to the form field. It may have
4128 * been part of a multibyte character. If it was, the
4129 * addch_used field is nonzero and we should not try to move
4132 if (WINDOW_EXT(form->w, addch_used) == 0)
4133 IFN_Next_Character(form);
4144 /*---------------------------------------------------------------------------
4145 | Facility : libnform
4146 | Function : static int Data_Entry(FORM * form,int c)
4148 | Description : Enter character c into at the current position of the
4149 | current field of the form.
4151 | Return Values : E_OK - success
4152 | E_REQUEST_DENIED - driver could not process the request
4154 +--------------------------------------------------------------------------*/
4156 Data_Entry(FORM *form, int c)
4158 FIELD *field = form->current;
4159 int result = E_REQUEST_DENIED;
4161 T((T_CALLED("Data_Entry(%p,%s)"), (void *)form, _tracechtype((chtype)c)));
4162 if ((Field_Has_Option(field, O_EDIT))
4163 #if FIX_FORM_INACTIVE_BUG
4164 && (Field_Has_Option(field, O_ACTIVE))
4168 if ((Field_Has_Option(field, O_BLANK)) &&
4169 First_Position_In_Current_Field(form) &&
4170 !(form->status & _FCHECK_REQUIRED) &&
4171 !(form->status & _WINDOW_MODIFIED))
4174 if (form->status & _OVLMODE)
4176 waddch(form->w, (chtype)c);
4181 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
4183 if (!(There_Is_Room ||
4184 ((Single_Line_Field(field) && Growable(field)))))
4185 RETURN(E_REQUEST_DENIED);
4187 if (!There_Is_Room && !Field_Grown(field, 1))
4188 RETURN(E_SYSTEM_ERROR);
4190 winsch(form->w, (chtype)c);
4193 if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
4195 bool End_Of_Field = (((field->drows - 1) == form->currow) &&
4196 ((field->dcols - 1) == form->curcol));
4198 if (Field_Has_Option(field, O_EDGE_INSERT_STAY))
4199 move_after_insert = !!(form->curcol
4204 SetStatus(form, _WINDOW_MODIFIED);
4205 if (End_Of_Field && !Growable(field) && (Field_Has_Option(field, O_AUTOSKIP)))
4206 result = Inter_Field_Navigation(FN_Next_Field, form);
4209 if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
4210 result = E_SYSTEM_ERROR;
4213 #if USE_WIDEC_SUPPORT
4215 * We have just added a byte to the form field. It may have
4216 * been part of a multibyte character. If it was, the
4217 * addch_used field is nonzero and we should not try to move
4220 if (WINDOW_EXT(form->w, addch_used) == 0)
4221 IFN_Next_Character(form);
4223 IFN_Next_Character(form);
4233 /* Structure to describe the binding of a request code to a function.
4234 The member keycode codes the request value as well as the generic
4235 routine to use for the request. The code for the generic routine
4236 is coded in the upper 16 Bits while the request code is coded in
4239 In terms of C++ you might think of a request as a class with a
4240 virtual method "perform". The different types of request are
4241 derived from this base class and overload (or not) the base class
4242 implementation of perform.
4246 int keycode; /* must be at least 32 bit: hi:mode, lo: key */
4247 int (*cmd) (FORM *); /* low level driver routine for this key */
4251 /* You may see this is the class-id of the request type class */
4252 #define ID_PN (0x00000000) /* Page navigation */
4253 #define ID_FN (0x00010000) /* Inter-Field navigation */
4254 #define ID_IFN (0x00020000) /* Intra-Field navigation */
4255 #define ID_VSC (0x00030000) /* Vertical Scrolling */
4256 #define ID_HSC (0x00040000) /* Horizontal Scrolling */
4257 #define ID_FE (0x00050000) /* Field Editing */
4258 #define ID_EM (0x00060000) /* Edit Mode */
4259 #define ID_FV (0x00070000) /* Field Validation */
4260 #define ID_CH (0x00080000) /* Choice */
4261 #define ID_Mask (0xffff0000)
4262 #define Key_Mask (0x0000ffff)
4263 #define ID_Shft (16)
4265 /* This array holds all the Binding Infos */
4267 static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =
4269 { REQ_NEXT_PAGE |ID_PN ,PN_Next_Page},
4270 { REQ_PREV_PAGE |ID_PN ,PN_Previous_Page},
4271 { REQ_FIRST_PAGE |ID_PN ,PN_First_Page},
4272 { REQ_LAST_PAGE |ID_PN ,PN_Last_Page},
4274 { REQ_NEXT_FIELD |ID_FN ,FN_Next_Field},
4275 { REQ_PREV_FIELD |ID_FN ,FN_Previous_Field},
4276 { REQ_FIRST_FIELD |ID_FN ,FN_First_Field},
4277 { REQ_LAST_FIELD |ID_FN ,FN_Last_Field},
4278 { REQ_SNEXT_FIELD |ID_FN ,FN_Sorted_Next_Field},
4279 { REQ_SPREV_FIELD |ID_FN ,FN_Sorted_Previous_Field},
4280 { REQ_SFIRST_FIELD |ID_FN ,FN_Sorted_First_Field},
4281 { REQ_SLAST_FIELD |ID_FN ,FN_Sorted_Last_Field},
4282 { REQ_LEFT_FIELD |ID_FN ,FN_Left_Field},
4283 { REQ_RIGHT_FIELD |ID_FN ,FN_Right_Field},
4284 { REQ_UP_FIELD |ID_FN ,FN_Up_Field},
4285 { REQ_DOWN_FIELD |ID_FN ,FN_Down_Field},
4287 { REQ_NEXT_CHAR |ID_IFN ,IFN_Next_Character},
4288 { REQ_PREV_CHAR |ID_IFN ,IFN_Previous_Character},
4289 { REQ_NEXT_LINE |ID_IFN ,IFN_Next_Line},
4290 { REQ_PREV_LINE |ID_IFN ,IFN_Previous_Line},
4291 { REQ_NEXT_WORD |ID_IFN ,IFN_Next_Word},
4292 { REQ_PREV_WORD |ID_IFN ,IFN_Previous_Word},
4293 { REQ_BEG_FIELD |ID_IFN ,IFN_Beginning_Of_Field},
4294 { REQ_END_FIELD |ID_IFN ,IFN_End_Of_Field},
4295 { REQ_BEG_LINE |ID_IFN ,IFN_Beginning_Of_Line},
4296 { REQ_END_LINE |ID_IFN ,IFN_End_Of_Line},
4297 { REQ_LEFT_CHAR |ID_IFN ,IFN_Left_Character},
4298 { REQ_RIGHT_CHAR |ID_IFN ,IFN_Right_Character},
4299 { REQ_UP_CHAR |ID_IFN ,IFN_Up_Character},
4300 { REQ_DOWN_CHAR |ID_IFN ,IFN_Down_Character},
4302 { REQ_NEW_LINE |ID_FE ,FE_New_Line},
4303 { REQ_INS_CHAR |ID_FE ,FE_Insert_Character},
4304 { REQ_INS_LINE |ID_FE ,FE_Insert_Line},
4305 { REQ_DEL_CHAR |ID_FE ,FE_Delete_Character},
4306 { REQ_DEL_PREV |ID_FE ,FE_Delete_Previous},
4307 { REQ_DEL_LINE |ID_FE ,FE_Delete_Line},
4308 { REQ_DEL_WORD |ID_FE ,FE_Delete_Word},
4309 { REQ_CLR_EOL |ID_FE ,FE_Clear_To_End_Of_Line},
4310 { REQ_CLR_EOF |ID_FE ,FE_Clear_To_End_Of_Field},
4311 { REQ_CLR_FIELD |ID_FE ,FE_Clear_Field},
4313 { REQ_OVL_MODE |ID_EM ,EM_Overlay_Mode},
4314 { REQ_INS_MODE |ID_EM ,EM_Insert_Mode},
4316 { REQ_SCR_FLINE |ID_VSC ,VSC_Scroll_Line_Forward},
4317 { REQ_SCR_BLINE |ID_VSC ,VSC_Scroll_Line_Backward},
4318 { REQ_SCR_FPAGE |ID_VSC ,VSC_Scroll_Page_Forward},
4319 { REQ_SCR_BPAGE |ID_VSC ,VSC_Scroll_Page_Backward},
4320 { REQ_SCR_FHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Forward},
4321 { REQ_SCR_BHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Backward},
4323 { REQ_SCR_FCHAR |ID_HSC ,HSC_Scroll_Char_Forward},
4324 { REQ_SCR_BCHAR |ID_HSC ,HSC_Scroll_Char_Backward},
4325 { REQ_SCR_HFLINE |ID_HSC ,HSC_Horizontal_Line_Forward},
4326 { REQ_SCR_HBLINE |ID_HSC ,HSC_Horizontal_Line_Backward},
4327 { REQ_SCR_HFHALF |ID_HSC ,HSC_Horizontal_Half_Line_Forward},
4328 { REQ_SCR_HBHALF |ID_HSC ,HSC_Horizontal_Half_Line_Backward},
4330 { REQ_VALIDATION |ID_FV ,FV_Validation},
4332 { REQ_NEXT_CHOICE |ID_CH ,CR_Next_Choice},
4333 { REQ_PREV_CHOICE |ID_CH ,CR_Previous_Choice}
4337 /*---------------------------------------------------------------------------
4338 | Facility : libnform
4339 | Function : int form_driver(FORM * form,int c)
4341 | Description : This is the workhorse of the forms system. It checks
4342 | to determine whether the character c is a request or
4343 | data. If it is a request, the form driver executes
4344 | the request and returns the result. If it is data
4345 | (printable character), it enters the data into the
4346 | current position in the current field. If it is not
4347 | recognized, the form driver assumes it is an application
4348 | defined command and returns E_UNKNOWN_COMMAND.
4349 | Application defined command should be defined relative
4350 | to MAX_FORM_COMMAND, the maximum value of a request.
4352 | Return Values : E_OK - success
4353 | E_SYSTEM_ERROR - system error
4354 | E_BAD_ARGUMENT - an argument is incorrect
4355 | E_NOT_POSTED - form is not posted
4356 | E_INVALID_FIELD - field contents are invalid
4357 | E_BAD_STATE - called from inside a hook routine
4358 | E_REQUEST_DENIED - request failed
4359 | E_NOT_CONNECTED - no fields are connected to the form
4360 | E_UNKNOWN_COMMAND - command not known
4361 +--------------------------------------------------------------------------*/
4363 form_driver(FORM *form, int c)
4365 const Binding_Info *BI = (Binding_Info *)0;
4366 int res = E_UNKNOWN_COMMAND;
4368 move_after_insert = TRUE;
4370 T((T_CALLED("form_driver(%p,%d)"), (void *)form, c));
4373 RETURN(E_BAD_ARGUMENT);
4375 if (!(form->field) || !(form->current))
4376 RETURN(E_NOT_CONNECTED);
4380 if (c == FIRST_ACTIVE_MAGIC)
4382 form->current = _nc_First_Active_Field(form);
4386 assert(form->current &&
4387 form->current->buf &&
4388 (form->current->form == form)
4391 if (form->status & _IN_DRIVER)
4392 RETURN(E_BAD_STATE);
4394 if (!(form->status & _POSTED))
4395 RETURN(E_NOT_POSTED);
4397 if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4398 ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4400 TR(TRACE_CALLS, ("form_request %s", form_request_name(c)));
4401 BI = &(bindings[c - MIN_FORM_COMMAND]);
4406 typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4407 static const Generic_Method Generic_Methods[] =
4409 Page_Navigation, /* overloaded to call field&form hooks */
4410 Inter_Field_Navigation, /* overloaded to call field hooks */
4411 NULL, /* Intra-Field is generic */
4412 Vertical_Scrolling, /* Overloaded to check multi-line */
4413 Horizontal_Scrolling, /* Overloaded to check single-line */
4414 Field_Editing, /* Overloaded to mark modification */
4415 NULL, /* Edit Mode is generic */
4416 NULL, /* Field Validation is generic */
4417 NULL /* Choice Request is generic */
4419 size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
4420 size_t method = (size_t)((BI->keycode >> ID_Shft) & 0xffff); /* see ID_Mask */
4422 if ((method >= nMethods) || !(BI->cmd))
4423 res = E_SYSTEM_ERROR;
4426 Generic_Method fct = Generic_Methods[method];
4430 res = fct(BI->cmd, form);
4434 res = (BI->cmd) (form);
4438 #ifdef NCURSES_MOUSE_VERSION
4439 else if (KEY_MOUSE == c)
4442 WINDOW *win = form->win ? form->win : StdScreen(Get_Form_Screen(form));
4443 WINDOW *sub = form->sub ? form->sub : win;
4446 if ((event.bstate & (BUTTON1_CLICKED |
4447 BUTTON1_DOUBLE_CLICKED |
4448 BUTTON1_TRIPLE_CLICKED))
4449 && wenclose(win, event.y, event.x))
4450 { /* we react only if the click was in the userwin, that means
4451 * inside the form display area or at the decoration window.
4453 int ry = event.y, rx = event.x; /* screen coordinates */
4455 res = E_REQUEST_DENIED;
4456 if (mouse_trafo(&ry, &rx, FALSE))
4457 { /* rx, ry are now "curses" coordinates */
4458 if (ry < sub->_begy)
4459 { /* we clicked above the display region; this is
4460 * interpreted as "scroll up" request
4462 if (event.bstate & BUTTON1_CLICKED)
4463 res = form_driver(form, REQ_PREV_FIELD);
4464 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4465 res = form_driver(form, REQ_PREV_PAGE);
4466 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4467 res = form_driver(form, REQ_FIRST_FIELD);
4469 else if (ry > sub->_begy + sub->_maxy)
4470 { /* we clicked below the display region; this is
4471 * interpreted as "scroll down" request
4473 if (event.bstate & BUTTON1_CLICKED)
4474 res = form_driver(form, REQ_NEXT_FIELD);
4475 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4476 res = form_driver(form, REQ_NEXT_PAGE);
4477 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4478 res = form_driver(form, REQ_LAST_FIELD);
4480 else if (wenclose(sub, event.y, event.x))
4481 { /* Inside the area we try to find the hit item */
4484 if (wmouse_trafo(sub, &ry, &rx, FALSE))
4486 int min_field = form->page[form->curpage].pmin;
4487 int max_field = form->page[form->curpage].pmax;
4490 for (i = min_field; i <= max_field; ++i)
4492 FIELD *field = form->field[i];
4494 if (Field_Is_Selectable(field)
4495 && Field_encloses(field, ry, rx) == E_OK)
4497 res = _nc_Set_Current_Field(form, field);
4499 res = _nc_Position_Form_Cursor(form);
4501 && (event.bstate & BUTTON1_DOUBLE_CLICKED))
4502 res = E_UNKNOWN_COMMAND;
4511 res = E_REQUEST_DENIED;
4513 #endif /* NCURSES_MOUSE_VERSION */
4514 else if (!(c & (~(int)MAX_REGULAR_CHARACTER)))
4517 * If we're using 8-bit characters, iscntrl+isprint cover the whole set.
4518 * But with multibyte characters, there is a third possibility, i.e.,
4519 * parts of characters that build up into printable characters which are
4520 * not considered printable.
4522 * FIXME: the wide-character branch should also use Check_Char().
4524 #if USE_WIDEC_SUPPORT
4525 if (!iscntrl(UChar(c)))
4527 if (isprint(UChar(c)) &&
4528 Check_Char(form, form->current, form->current->type, c,
4529 (TypeArgument *)(form->current->arg)))
4531 res = Data_Entry(form, c);
4533 _nc_Refresh_Current_Field(form);
4537 # if USE_WIDEC_SUPPORT
4538 /*---------------------------------------------------------------------------
4539 | Facility : libnform
4540 | Function : int form_driver_w(FORM * form,int type,wchar_t c)
4542 | Description : This is the workhorse of the forms system.
4544 | Input is either a key code (request) or a wide char
4545 | returned by e.g. get_wch (). The type must be passed
4546 | as well,so that we are able to determine whether the char
4547 | is a multibyte char or a request.
4549 | If it is a request, the form driver executes
4550 | the request and returns the result. If it is data
4551 | (printable character), it enters the data into the
4552 | current position in the current field. If it is not
4553 | recognized, the form driver assumes it is an application
4554 | defined command and returns E_UNKNOWN_COMMAND.
4555 | Application defined command should be defined relative
4556 | to MAX_FORM_COMMAND, the maximum value of a request.
4558 | Return Values : E_OK - success
4559 | E_SYSTEM_ERROR - system error
4560 | E_BAD_ARGUMENT - an argument is incorrect
4561 | E_NOT_POSTED - form is not posted
4562 | E_INVALID_FIELD - field contents are invalid
4563 | E_BAD_STATE - called from inside a hook routine
4564 | E_REQUEST_DENIED - request failed
4565 | E_NOT_CONNECTED - no fields are connected to the form
4566 | E_UNKNOWN_COMMAND - command not known
4567 +--------------------------------------------------------------------------*/
4569 form_driver_w(FORM *form, int type, wchar_t c)
4571 const Binding_Info *BI = (Binding_Info *)0;
4572 int res = E_UNKNOWN_COMMAND;
4574 T((T_CALLED("form_driver(%p,%d)"), (void *)form, (int)c));
4577 RETURN(E_BAD_ARGUMENT);
4580 RETURN(E_NOT_CONNECTED);
4584 if (c == (wchar_t)FIRST_ACTIVE_MAGIC)
4586 form->current = _nc_First_Active_Field(form);
4590 assert(form->current &&
4591 form->current->buf &&
4592 (form->current->form == form)
4595 if (form->status & _IN_DRIVER)
4596 RETURN(E_BAD_STATE);
4598 if (!(form->status & _POSTED))
4599 RETURN(E_NOT_POSTED);
4601 /* check if this is a keycode or a (wide) char */
4602 if (type == KEY_CODE_YES)
4604 if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4605 ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4606 BI = &(bindings[c - MIN_FORM_COMMAND]);
4611 typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4612 static const Generic_Method Generic_Methods[] =
4614 Page_Navigation, /* overloaded to call field&form hooks */
4615 Inter_Field_Navigation, /* overloaded to call field hooks */
4616 NULL, /* Intra-Field is generic */
4617 Vertical_Scrolling, /* Overloaded to check multi-line */
4618 Horizontal_Scrolling, /* Overloaded to check single-line */
4619 Field_Editing, /* Overloaded to mark modification */
4620 NULL, /* Edit Mode is generic */
4621 NULL, /* Field Validation is generic */
4622 NULL /* Choice Request is generic */
4624 size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
4625 size_t method = (size_t)(BI->keycode >> ID_Shft) & 0xffff; /* see ID_Mask */
4627 if ((method >= nMethods) || !(BI->cmd))
4628 res = E_SYSTEM_ERROR;
4631 Generic_Method fct = Generic_Methods[method];
4634 res = fct(BI->cmd, form);
4636 res = (BI->cmd) (form);
4639 #ifdef NCURSES_MOUSE_VERSION
4640 else if (KEY_MOUSE == c)
4643 WINDOW *win = form->win ? form->win : StdScreen(Get_Form_Screen(form));
4644 WINDOW *sub = form->sub ? form->sub : win;
4647 if ((event.bstate & (BUTTON1_CLICKED |
4648 BUTTON1_DOUBLE_CLICKED |
4649 BUTTON1_TRIPLE_CLICKED))
4650 && wenclose(win, event.y, event.x))
4651 { /* we react only if the click was in the userwin, that means
4652 * inside the form display area or at the decoration window.
4654 int ry = event.y, rx = event.x; /* screen coordinates */
4656 res = E_REQUEST_DENIED;
4657 if (mouse_trafo(&ry, &rx, FALSE))
4658 { /* rx, ry are now "curses" coordinates */
4659 if (ry < sub->_begy)
4660 { /* we clicked above the display region; this is
4661 * interpreted as "scroll up" request
4663 if (event.bstate & BUTTON1_CLICKED)
4664 res = form_driver(form, REQ_PREV_FIELD);
4665 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4666 res = form_driver(form, REQ_PREV_PAGE);
4667 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4668 res = form_driver(form, REQ_FIRST_FIELD);
4670 else if (ry > sub->_begy + sub->_maxy)
4671 { /* we clicked below the display region; this is
4672 * interpreted as "scroll down" request
4674 if (event.bstate & BUTTON1_CLICKED)
4675 res = form_driver(form, REQ_NEXT_FIELD);
4676 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4677 res = form_driver(form, REQ_NEXT_PAGE);
4678 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4679 res = form_driver(form, REQ_LAST_FIELD);
4681 else if (wenclose(sub, event.y, event.x))
4682 { /* Inside the area we try to find the hit item */
4685 if (wmouse_trafo(sub, &ry, &rx, FALSE))
4687 int min_field = form->page[form->curpage].pmin;
4688 int max_field = form->page[form->curpage].pmax;
4691 for (i = min_field; i <= max_field; ++i)
4693 FIELD *field = form->field[i];
4695 if (Field_Is_Selectable(field)
4696 && Field_encloses(field, ry, rx) == E_OK)
4698 res = _nc_Set_Current_Field(form, field);
4700 res = _nc_Position_Form_Cursor(form);
4702 && (event.bstate & BUTTON1_DOUBLE_CLICKED))
4703 res = E_UNKNOWN_COMMAND;
4712 res = E_REQUEST_DENIED;
4714 #endif /* NCURSES_MOUSE_VERSION */
4715 else if (type == OK)
4717 res = Data_Entry_w(form, c);
4720 _nc_Refresh_Current_Field(form);
4723 # endif /* USE_WIDEC_SUPPORT */
4725 /*----------------------------------------------------------------------------
4726 Field-Buffer manipulation routines.
4727 The effects of setting a buffer are tightly coupled to the core of the form
4728 driver logic. This is especially true in the case of growable fields.
4729 So I don't separate this into a separate module.
4730 --------------------------------------------------------------------------*/
4732 /*---------------------------------------------------------------------------
4733 | Facility : libnform
4734 | Function : int set_field_buffer(FIELD *field,
4735 | int buffer, char *value)
4737 | Description : Set the given buffer of the field to the given value.
4738 | Buffer 0 stores the displayed content of the field.
4739 | For dynamic fields this may grow the fieldbuffers if
4740 | the length of the value exceeds the current buffer
4741 | length. For buffer 0 only printable values are allowed.
4742 | For static fields, the value must not be zero terminated.
4743 | It is copied up to the length of the buffer.
4745 | Return Values : E_OK - success
4746 | E_BAD_ARGUMENT - invalid argument
4747 | E_SYSTEM_ERROR - system error
4748 +--------------------------------------------------------------------------*/
4750 set_field_buffer(FIELD *field, int buffer, const char *value)
4757 #if USE_WIDEC_SUPPORT
4758 FIELD_CELL *widevalue = 0;
4761 T((T_CALLED("set_field_buffer(%p,%d,%s)"), (void *)field, buffer, _nc_visbuf(value)));
4763 if (!field || !value || ((buffer < 0) || (buffer > field->nbuf)))
4764 RETURN(E_BAD_ARGUMENT);
4766 len = Buffer_Length(field);
4768 if (Growable(field))
4770 /* for a growable field we must assume zero terminated strings, because
4771 somehow we have to detect the length of what should be copied.
4773 int vlen = (int)strlen(value);
4777 if (!Field_Grown(field,
4778 (int)(1 + (vlen - len) / ((field->rows + field->nrow)
4780 RETURN(E_SYSTEM_ERROR);
4782 #if !USE_WIDEC_SUPPORT
4788 p = Address_Of_Nth_Buffer(field, buffer);
4790 #if USE_WIDEC_SUPPORT
4792 * Use addstr's logic for converting a string to an array of cchar_t's.
4793 * There should be a better way, but this handles nonspacing characters
4794 * and other special cases that we really do not want to handle here.
4796 #if NCURSES_EXT_FUNCS
4797 if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR)
4800 delwin(field->working);
4801 field->working = newpad(1, Buffer_Length(field) + 1);
4803 len = Buffer_Length(field);
4804 wclear(field->working);
4805 (void)mvwaddstr(field->working, 0, 0, value);
4807 if ((widevalue = typeCalloc(FIELD_CELL, len + 1)) == 0)
4809 RETURN(E_SYSTEM_ERROR);
4813 for (i = 0; i < field->drows; ++i)
4815 (void)mvwin_wchnstr(field->working, 0, (int)i * field->dcols,
4816 widevalue + ((int)i * field->dcols),
4819 for (i = 0; i < len; ++i)
4821 if (CharEq(myZEROS, widevalue[i]))
4827 p[i] = widevalue[i];
4832 for (i = 0; i < len; ++i)
4834 if (value[i] == '\0')
4848 if (((syncres = Synchronize_Field(field)) != E_OK) &&
4851 if (((syncres = Synchronize_Linked_Fields(field)) != E_OK) &&
4858 /*---------------------------------------------------------------------------
4859 | Facility : libnform
4860 | Function : char *field_buffer(const FIELD *field,int buffer)
4862 | Description : Return the address of the buffer for the field.
4864 | Return Values : Pointer to buffer or NULL if arguments were invalid.
4865 +--------------------------------------------------------------------------*/
4867 field_buffer(const FIELD *field, int buffer)
4871 T((T_CALLED("field_buffer(%p,%d)"), (const void *)field, buffer));
4873 if (field && (buffer >= 0) && (buffer <= field->nbuf))
4875 #if USE_WIDEC_SUPPORT
4876 FIELD_CELL *data = Address_Of_Nth_Buffer(field, buffer);
4878 int size = Buffer_Length(field);
4881 /* determine the number of bytes needed to store the expanded string */
4882 for (n = 0; n < size; ++n)
4884 if (!isWidecExt(data[n]) && data[n].chars[0] != L'\0')
4890 next = _nc_wcrtomb(0, data[n].chars[0], &state);
4896 /* allocate a place to store the expanded string */
4897 if (field->expanded[buffer] != 0)
4898 free(field->expanded[buffer]);
4899 field->expanded[buffer] = typeMalloc(char, need + 1);
4902 * Expand the multibyte data.
4904 * It may also be multi-column data. In that case, the data for a row
4905 * may be null-padded to align to the dcols/drows layout (or it may
4906 * contain embedded wide-character extensions). Change the null-padding
4907 * to blanks as needed.
4909 if ((result = field->expanded[buffer]) != 0)
4911 wclear(field->working);
4912 wmove(field->working, 0, 0);
4913 for (n = 0; n < size; ++n)
4915 if (!isWidecExt(data[n]) && data[n].chars[0] != L'\0')
4916 wadd_wch(field->working, &data[n]);
4918 wmove(field->working, 0, 0);
4919 winnstr(field->working, result, (int)need);
4922 result = Address_Of_Nth_Buffer(field, buffer);
4928 #if USE_WIDEC_SUPPORT
4930 /*---------------------------------------------------------------------------
4931 | Convert a multibyte string to a wide-character string. The result must be
4932 | freed by the caller.
4933 +--------------------------------------------------------------------------*/
4934 FORM_EXPORT(wchar_t *)
4935 _nc_Widen_String(char *source, int *lengthp)
4937 wchar_t *result = 0;
4939 size_t given = strlen(source);
4944 #ifndef state_unused
4948 for (pass = 0; pass < 2; ++pass)
4953 while (passed < given)
4957 for (tries = 1, status = 0; tries <= (given - passed); ++tries)
4959 int save = source[passed + tries];
4961 source[passed + tries] = 0;
4962 reset_mbytes(state);
4963 status = check_mbytes(wch, source + passed, tries, state);
4964 source[passed + tries] = (char)save;
4978 passed += (size_t)status;
4985 result[need] = (wchar_t)source[passed];
4996 result = typeCalloc(wchar_t, need);
4998 *lengthp = (int)need;
5008 /* frm_driver.c ends here */