blob: 75656d69ea2a44b00136754cb5cbcf183c4599a8 [file] [log] [blame]
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301/****************************************************************************
micky3879b9f5e72025-07-08 18:04:53 -04002 * Copyright 2018-2020,2021 Thomas E. Dickey *
3 * Copyright 1998-2016,2017 Free Software Foundation, Inc. *
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304 * *
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: *
12 * *
13 * The above copyright notice and this permission notice shall be included *
14 * in all copies or substantial portions of the Software. *
15 * *
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. *
23 * *
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 *
27 * authorization. *
28 ****************************************************************************/
29
30/****************************************************************************
31 * Author: Juergen Pfeifer, 1995,1997 *
32 ****************************************************************************/
33
34#include "form.priv.h"
35
micky3879b9f5e72025-07-08 18:04:53 -040036MODULE_ID("$Id: frm_driver.c,v 1.135 2021/09/01 23:34:01 tom Exp $")
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +053037
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.
41
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!
47
48 The driver routines are grouped into nine generic categories:
49
50 a) Page Navigation ( all functions prefixed by PN_ )
51 The current page of the form is left and some new page is
52 entered.
53 b) Inter-Field Navigation ( all functions prefixed by FN_ )
54 The current field of the form is left and some new field is
55 entered.
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 --------------------------------------------------------------------------*/
73
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 --------------------------------------------------------------------------*/
80
81/*
82Some options that may effect compatibility in behavior to SVr4 forms,
83but they are here to allow a more intuitive and user friendly behavior of
84our form implementation. This doesn't affect the API, so we feel it is
85uncritical.
86
87The initial implementation tries to stay very close with the behavior
88of the original SVr4 implementation, although in some areas it is quite
89clear that this isn't the most appropriate way. As far as possible this
90sources will allow you to build a forms lib that behaves quite similar
91to SVr4, but now and in the future we will give you better options.
92Perhaps at some time we will make this configurable at runtime.
93*/
94
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)
101
102#if USE_WIDEC_SUPPORT
micky3879b9f5e72025-07-08 18:04:53 -0400103#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)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530106#define myWCWIDTH(w, y, x) cell_width(w, y, x)
107#else
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
112#endif
113
114/*----------------------------------------------------------------------------
115 Forward references to some internally used static functions
116 --------------------------------------------------------------------------*/
117static int Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form);
118static int FN_Next_Field(FORM *form);
119static int FN_Previous_Field(FORM *form);
120static int FE_New_Line(FORM *);
121static int FE_Delete_Previous(FORM *);
122
123/*----------------------------------------------------------------------------
124 Macro Definitions.
125
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 --------------------------------------------------------------------------*/
130
131/* Calculate the position of a single row in a field buffer */
132#define Position_Of_Row_In_Buffer(field,row) ((row)*(field)->dcols)
133
micky3879b9f5e72025-07-08 18:04:53 -0400134/* Calculate start address for the field's buffer# N */
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530135#define Address_Of_Nth_Buffer(field,N) \
136 ((field)->buf + (N)*(1+Buffer_Length(field)))
137
micky3879b9f5e72025-07-08 18:04:53 -0400138/* Calculate the start address of the row in the field's specified buffer# N */
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530139#define Address_Of_Row_In_Nth_Buffer(field,N,row) \
140 (Address_Of_Nth_Buffer(field,N) + Position_Of_Row_In_Buffer(field,row))
141
micky3879b9f5e72025-07-08 18:04:53 -0400142/* Calculate the start address of the row in the field's primary buffer */
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530143#define Address_Of_Row_In_Buffer(field,row) \
144 Address_Of_Row_In_Nth_Buffer(field,0,row)
145
micky3879b9f5e72025-07-08 18:04:53 -0400146/* Calculate the start address of the row in the form's current field
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530147 buffer# N */
148#define Address_Of_Current_Row_In_Nth_Buffer(form,N) \
149 Address_Of_Row_In_Nth_Buffer((form)->current,N,(form)->currow)
150
micky3879b9f5e72025-07-08 18:04:53 -0400151/* Calculate the start address of the row in the form's current field
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530152 primary buffer */
153#define Address_Of_Current_Row_In_Buffer(form) \
154 Address_Of_Current_Row_In_Nth_Buffer(form,0)
155
micky3879b9f5e72025-07-08 18:04:53 -0400156/* Calculate the address of the cursor in the form's current field
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530157 primary buffer */
158#define Address_Of_Current_Position_In_Nth_Buffer(form,N) \
159 (Address_Of_Current_Row_In_Nth_Buffer(form,N) + (form)->curcol)
160
micky3879b9f5e72025-07-08 18:04:53 -0400161/* Calculate the address of the cursor in the form's current field
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530162 buffer# N */
163#define Address_Of_Current_Position_In_Buffer(form) \
164 Address_Of_Current_Position_In_Nth_Buffer(form,0)
165
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))
171
172/* Logic to decide whether or not a field needs to have an individual window
173 instead of a derived window because it contains invisible parts.
174 This is true for non-public fields and for scrollable fields. */
175#define Has_Invisible_Parts(field) \
Steve Kondikae271bc2015-11-15 02:50:53 +0100176 (!(Field_Has_Option(field, O_PUBLIC)) || \
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530177 Is_Scroll_Field(field))
178
179/* Logic to decide whether or not a field needs justification */
180#define Justification_Allowed(field) \
181 (((field)->just != NO_JUSTIFICATION) && \
182 (Single_Line_Field(field)) && \
Steve Kondikae271bc2015-11-15 02:50:53 +0100183 ((Field_Has_Option(field, O_STATIC) && \
184 ((field)->dcols == (field)->cols)) || \
185 Field_Has_Option(field, O_DYNAMIC_JUSTIFY)))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530186
187/* Logic to determine whether or not a dynamic field may still grow */
188#define Growable(field) ((field)->status & _MAY_GROW)
189
micky3879b9f5e72025-07-08 18:04:53 -0400190/* Macro to set the attributes for a field's window */
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530191#define Set_Field_Window_Attributes(field,win) \
Steve Kondikae271bc2015-11-15 02:50:53 +0100192( wbkgdset((win),(chtype)((chtype)((field)->pad) | (field)->back)), \
193 (void) wattrset((win), (int)(field)->fore) )
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530194
195/* Logic to decide whether or not a field really appears on the form */
196#define Field_Really_Appears(field) \
197 ((field->form) &&\
198 (field->form->status & _POSTED) &&\
Steve Kondikae271bc2015-11-15 02:50:53 +0100199 (Field_Has_Option(field, O_VISIBLE)) &&\
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530200 (field->page == field->form->curpage))
201
202/* Logic to determine whether or not we are on the first position in the
203 current field */
204#define First_Position_In_Current_Field(form) \
205 (((form)->currow==0) && ((form)->curcol==0))
206
207#define Minimum(a,b) (((a)<=(b)) ? (a) : (b))
208#define Maximum(a,b) (((a)>=(b)) ? (a) : (b))
209
210/*----------------------------------------------------------------------------
211 Useful constants
212 --------------------------------------------------------------------------*/
213static FIELD_CELL myBLANK = BLANK;
214static FIELD_CELL myZEROS;
215
216#ifdef TRACE
217static void
218check_pos(FORM *form, int lineno)
219{
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530220 if (form && form->w)
221 {
micky3879b9f5e72025-07-08 18:04:53 -0400222 int y, x;
223
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530224 getyx(form->w, y, x);
225 if (y != form->currow || x != form->curcol)
226 {
227 T(("CHECKPOS %s@%d have position %d,%d vs want %d,%d",
228 __FILE__, lineno,
229 y, x,
230 form->currow, form->curcol));
231 }
232 }
233}
234#define CHECKPOS(form) check_pos(form, __LINE__)
235#else
236#define CHECKPOS(form) /* nothing */
237#endif
238
239/*----------------------------------------------------------------------------
240 Wide-character special functions
241 --------------------------------------------------------------------------*/
242#if USE_WIDEC_SUPPORT
micky3879b9f5e72025-07-08 18:04:53 -0400243/* add like waddnstr, but using cchar_t* rather than char*
244 */
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530245static int
micky3879b9f5e72025-07-08 18:04:53 -0400246wide_waddnstr(WINDOW *w, const cchar_t *s, int n)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530247{
micky3879b9f5e72025-07-08 18:04:53 -0400248 int rc = OK;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530249
250 while (n-- > 0)
251 {
micky3879b9f5e72025-07-08 18:04:53 -0400252 if ((rc = wadd_wch(w, s)) != OK)
253 break;
254 ++s;
255 }
256 return rc;
257}
258
259/* insert like winsnstr, but using cchar_t* rather than char*
260 *
261 * X/Open Curses has no close equivalent; inserts are done only with wchar_t
262 * strings.
263 */
264static int
265wide_winsnstr(WINDOW *w, const cchar_t *s, int n)
266{
267 int code = ERR;
268
269 while (n-- > 0)
270 {
271 int y, x;
272
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530273 getyx(w, y, x);
274 if ((code = wins_wch(w, s++)) != OK)
275 break;
276 if ((code = wmove(w, y, x + 1)) != OK)
277 break;
278 }
279 return code;
280}
281
micky3879b9f5e72025-07-08 18:04:53 -0400282/* retrieve like winnstr, but using cchar_t*, rather than char*.
283 *
284 * X/Open Curses' closest equivalent, win_wchnstr(), is inconsistent with
285 * winnstr(), since it returns OK rather than the number of items transferred.
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530286 */
287static int
micky3879b9f5e72025-07-08 18:04:53 -0400288wide_winnstr(WINDOW *w, cchar_t *s, int n)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530289{
290 int x;
291
292 win_wchnstr(w, s, n);
293 /*
294 * This function is used to extract the text only from the window.
295 * Strip attributes and color from the string so they will not be added
296 * back when copying the string to the window.
297 */
298 for (x = 0; x < n; ++x)
299 {
300 RemAttr(s[x], A_ATTRIBUTES);
301 SetPair(s[x], 0);
302 }
303 return n;
304}
305
306/*
307 * Returns the column of the base of the given cell.
308 */
309static int
310cell_base(WINDOW *win, int y, int x)
311{
312 int result = x;
313
314 while (LEGALYX(win, y, x))
315 {
316 cchar_t *data = &(win->_line[y].text[x]);
317
318 if (isWidecBase(CHDEREF(data)) || !isWidecExt(CHDEREF(data)))
319 {
320 result = x;
321 break;
322 }
323 --x;
324 }
325 return result;
326}
327
328/*
329 * Returns the number of columns needed for the given cell in a window.
330 */
331static int
332cell_width(WINDOW *win, int y, int x)
333{
334 int result = 1;
335
336 if (LEGALYX(win, y, x))
337 {
338 cchar_t *data = &(win->_line[y].text[x]);
339
340 if (isWidecExt(CHDEREF(data)))
341 {
342 /* recur, providing the number of columns to the next character */
343 result = cell_width(win, y, x - 1);
344 }
345 else
346 {
347 result = wcwidth(CharOf(CHDEREF(data)));
348 }
349 }
350 return result;
351}
352
353/*
354 * There is no wide-character function such as wdel_wch(), so we must find
355 * all of the cells that comprise a multi-column character and delete them
356 * one-by-one.
357 */
358static void
359delete_char(FORM *form)
360{
361 int cells = cell_width(form->w, form->currow, form->curcol);
362
363 form->curcol = cell_base(form->w, form->currow, form->curcol);
364 wmove(form->w, form->currow, form->curcol);
365 while (cells-- > 0)
366 {
367 wdelch(form->w);
368 }
369}
370#define DeleteChar(form) delete_char(form)
371#else
372#define DeleteChar(form) \
373 wmove((form)->w, (form)->currow, (form)->curcol), \
374 wdelch((form)->w)
375#endif
376
377/*---------------------------------------------------------------------------
378| Facility : libnform
379| Function : static char *Get_Start_Of_Data(char * buf, int blen)
380|
381| Description : Return pointer to first non-blank position in buffer.
382| If buffer is empty return pointer to buffer itself.
383|
384| Return Values : Pointer to first non-blank position in buffer
385+--------------------------------------------------------------------------*/
386NCURSES_INLINE static FIELD_CELL *
387Get_Start_Of_Data(FIELD_CELL *buf, int blen)
388{
389 FIELD_CELL *p = buf;
390 FIELD_CELL *end = &buf[blen];
391
392 assert(buf && blen >= 0);
393 while ((p < end) && ISBLANK(*p))
394 p++;
395 return ((p == end) ? buf : p);
396}
397
398/*---------------------------------------------------------------------------
399| Facility : libnform
400| Function : static char *After_End_Of_Data(char * buf, int blen)
401|
402| Description : Return pointer after last non-blank position in buffer.
403| If buffer is empty, return pointer to buffer itself.
404|
405| Return Values : Pointer to position after last non-blank position in
406| buffer.
407+--------------------------------------------------------------------------*/
408NCURSES_INLINE static FIELD_CELL *
409After_End_Of_Data(FIELD_CELL *buf, int blen)
410{
411 FIELD_CELL *p = &buf[blen];
412
413 assert(buf && blen >= 0);
414 while ((p > buf) && ISBLANK(p[-1]))
415 p--;
416 return (p);
417}
418
419/*---------------------------------------------------------------------------
420| Facility : libnform
421| Function : static char *Get_First_Whitespace_Character(
422| char * buf, int blen)
423|
424| Description : Position to the first whitespace character.
425|
426| Return Values : Pointer to first whitespace character in buffer.
427+--------------------------------------------------------------------------*/
428NCURSES_INLINE static FIELD_CELL *
429Get_First_Whitespace_Character(FIELD_CELL *buf, int blen)
430{
431 FIELD_CELL *p = buf;
432 FIELD_CELL *end = &p[blen];
433
434 assert(buf && blen >= 0);
435 while ((p < end) && !ISBLANK(*p))
436 p++;
437 return ((p == end) ? buf : p);
438}
439
440/*---------------------------------------------------------------------------
441| Facility : libnform
442| Function : static char *After_Last_Whitespace_Character(
443| char * buf, int blen)
444|
445| Description : Get the position after the last whitespace character.
446|
447| Return Values : Pointer to position after last whitespace character in
448| buffer.
449+--------------------------------------------------------------------------*/
450NCURSES_INLINE static FIELD_CELL *
451After_Last_Whitespace_Character(FIELD_CELL *buf, int blen)
452{
453 FIELD_CELL *p = &buf[blen];
454
455 assert(buf && blen >= 0);
456 while ((p > buf) && !ISBLANK(p[-1]))
457 p--;
458 return (p);
459}
460
461/* Set this to 1 to use the div_t version. This is a good idea if your
462 compiler has an intrinsic div() support. Unfortunately GNU-C has it
463 not yet.
464 N.B.: This only works if form->curcol follows immediately form->currow
465 and both are of type int.
466*/
467#define USE_DIV_T (0)
468
469/*---------------------------------------------------------------------------
470| Facility : libnform
471| Function : static void Adjust_Cursor_Position(
472| FORM * form, const char * pos)
473|
474| Description : Set current row and column of the form to values
475| corresponding to the buffer position.
476|
477| Return Values : -
478+--------------------------------------------------------------------------*/
479NCURSES_INLINE static void
480Adjust_Cursor_Position(FORM *form, const FIELD_CELL *pos)
481{
482 FIELD *field;
483 int idx;
484
485 field = form->current;
486 assert(pos >= field->buf && field->dcols > 0);
487 idx = (int)(pos - field->buf);
488#if USE_DIV_T
489 *((div_t *) & (form->currow)) = div(idx, field->dcols);
490#else
491 form->currow = idx / field->dcols;
492 form->curcol = idx - field->cols * form->currow;
493#endif
494 if (field->drows < form->currow)
495 form->currow = 0;
496}
497
498/*---------------------------------------------------------------------------
499| Facility : libnform
500| Function : static void Buffer_To_Window(
501| const FIELD * field,
502| WINDOW * win)
503|
504| Description : Copy the buffer to the window. If it is a multi-line
505| field, the buffer is split to the lines of the
506| window without any editing.
507|
508| Return Values : -
509+--------------------------------------------------------------------------*/
510static void
511Buffer_To_Window(const FIELD *field, WINDOW *win)
512{
513 int width, height;
514 int y, x;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530515 int row;
516 FIELD_CELL *pBuffer;
517
518 assert(win && field);
519
520 getyx(win, y, x);
521 width = getmaxx(win);
522 height = getmaxy(win);
523
524 for (row = 0, pBuffer = field->buf;
525 row < height;
526 row++, pBuffer += width)
527 {
micky3879b9f5e72025-07-08 18:04:53 -0400528 int len;
529
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530530 if ((len = (int)(After_End_Of_Data(pBuffer, width) - pBuffer)) > 0)
531 {
532 wmove(win, row, 0);
533 myADDNSTR(win, pBuffer, len);
534 }
535 }
536 wmove(win, y, x);
537}
538
539/*---------------------------------------------------------------------------
540| Facility : libnform
Steve Kondikae271bc2015-11-15 02:50:53 +0100541| Function : void _nc_get_fieldbuffer(
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530542| WINDOW * win,
Steve Kondikae271bc2015-11-15 02:50:53 +0100543| FIELD * field,
544| FIELD_CELL * buf)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530545|
546| Description : Copy the content of the window into the buffer.
547| The multiple lines of a window are simply
548| concatenated into the buffer. Pad characters in
549| the window will be replaced by blanks in the buffer.
550|
551| Return Values : -
552+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -0400553FORM_EXPORT(void)
Steve Kondikae271bc2015-11-15 02:50:53 +0100554_nc_get_fieldbuffer(FORM *form, FIELD *field, FIELD_CELL *buf)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530555{
556 int pad;
557 int len = 0;
558 FIELD_CELL *p;
559 int row, height;
Steve Kondikae271bc2015-11-15 02:50:53 +0100560 WINDOW *win;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530561
Steve Kondikae271bc2015-11-15 02:50:53 +0100562 assert(form && field && buf);
563
564 win = form->w;
565 assert(win);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530566
567 pad = field->pad;
Steve Kondikae271bc2015-11-15 02:50:53 +0100568 p = buf;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530569 height = getmaxy(win);
570
571 for (row = 0; (row < height) && (row < field->drows); row++)
572 {
573 wmove(win, row, 0);
574 len += myINNSTR(win, p + len, field->dcols);
575 }
576 p[len] = myZEROS;
577
578 /* replace visual padding character by blanks in buffer */
579 if (pad != C_BLANK)
580 {
581 int i;
582
583 for (i = 0; i < len; i++, p++)
584 {
585 if ((unsigned long)CharOf(*p) == ChCharOf(pad)
586#if USE_WIDEC_SUPPORT
587 && p->chars[1] == 0
588#endif
589 )
590 *p = myBLANK;
591 }
592 }
593}
594
595/*---------------------------------------------------------------------------
596| Facility : libnform
Steve Kondikae271bc2015-11-15 02:50:53 +0100597| Function : static void Window_To_Buffer(
598| FORM * form,
599| FIELD * field)
600|
601| Description : Copy the content of the window into the buffer.
602| The multiple lines of a window are simply
603| concatenated into the buffer. Pad characters in
604| the window will be replaced by blanks in the buffer.
605|
606| Return Values : -
607+--------------------------------------------------------------------------*/
608static void
609Window_To_Buffer(FORM *form, FIELD *field)
610{
611 _nc_get_fieldbuffer(form, field, field->buf);
612}
613
614/*---------------------------------------------------------------------------
615| Facility : libnform
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530616| Function : static void Synchronize_Buffer(FORM * form)
617|
618| Description : If there was a change, copy the content of the
619| window into the buffer, so the buffer is synchronized
620| with the windows content. We have to indicate that the
621| buffer needs validation due to the change.
622|
623| Return Values : -
624+--------------------------------------------------------------------------*/
625NCURSES_INLINE static void
626Synchronize_Buffer(FORM *form)
627{
628 if (form->status & _WINDOW_MODIFIED)
629 {
Steve Kondikae271bc2015-11-15 02:50:53 +0100630 ClrStatus(form, _WINDOW_MODIFIED);
631 SetStatus(form, _FCHECK_REQUIRED);
632 Window_To_Buffer(form, form->current);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530633 wmove(form->w, form->currow, form->curcol);
634 }
635}
636
637/*---------------------------------------------------------------------------
638| Facility : libnform
639| Function : static bool Field_Grown( FIELD *field, int amount)
640|
641| Description : This function is called for growable dynamic fields
642| only. It has to increase the buffers and to allocate
643| a new window for this field.
644| This function has the side effect to set a new
645| field-buffer pointer, the dcols and drows values
646| as well as a new current Window for the field.
647|
648| Return Values : TRUE - field successfully increased
649| FALSE - there was some error
650+--------------------------------------------------------------------------*/
651static bool
652Field_Grown(FIELD *field, int amount)
653{
654 bool result = FALSE;
655
656 if (field && Growable(field))
657 {
658 bool single_line_field = Single_Line_Field(field);
659 int old_buflen = Buffer_Length(field);
660 int new_buflen;
661 int old_dcols = field->dcols;
662 int old_drows = field->drows;
663 FIELD_CELL *oldbuf = field->buf;
664 FIELD_CELL *newbuf;
665
666 int growth;
667 FORM *form = field->form;
668 bool need_visual_update = ((form != (FORM *)0) &&
669 (form->status & _POSTED) &&
670 (form->current == field));
671
672 if (need_visual_update)
673 Synchronize_Buffer(form);
674
675 if (single_line_field)
676 {
677 growth = field->cols * amount;
678 if (field->maxgrow)
679 growth = Minimum(field->maxgrow - field->dcols, growth);
680 field->dcols += growth;
681 if (field->dcols == field->maxgrow)
Steve Kondikae271bc2015-11-15 02:50:53 +0100682 ClrStatus(field, _MAY_GROW);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530683 }
684 else
685 {
686 growth = (field->rows + field->nrow) * amount;
687 if (field->maxgrow)
688 growth = Minimum(field->maxgrow - field->drows, growth);
689 field->drows += growth;
690 if (field->drows == field->maxgrow)
Steve Kondikae271bc2015-11-15 02:50:53 +0100691 ClrStatus(field, _MAY_GROW);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530692 }
693 /* drows, dcols changed, so we get really the new buffer length */
694 new_buflen = Buffer_Length(field);
695 newbuf = (FIELD_CELL *)malloc(Total_Buffer_Size(field));
696 if (!newbuf)
697 {
698 /* restore to previous state */
699 field->dcols = old_dcols;
700 field->drows = old_drows;
701 if ((single_line_field && (field->dcols != field->maxgrow)) ||
702 (!single_line_field && (field->drows != field->maxgrow)))
Steve Kondikae271bc2015-11-15 02:50:53 +0100703 SetStatus(field, _MAY_GROW);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530704 }
705 else
706 {
707 /* Copy all the buffers. This is the reason why we can't just use
708 * realloc().
709 */
710 int i, j;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530711
712 result = TRUE; /* allow sharing of recovery on failure */
713
Steve Kondikae271bc2015-11-15 02:50:53 +0100714 T((T_CREATE("fieldcell %p"), (void *)newbuf));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530715 field->buf = newbuf;
716 for (i = 0; i <= field->nbuf; i++)
717 {
micky3879b9f5e72025-07-08 18:04:53 -0400718 FIELD_CELL *new_bp = Address_Of_Nth_Buffer(field, i);
719 FIELD_CELL *old_bp = oldbuf + i * (1 + old_buflen);
720
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530721 for (j = 0; j < old_buflen; ++j)
722 new_bp[j] = old_bp[j];
723 while (j < new_buflen)
724 new_bp[j++] = myBLANK;
725 new_bp[new_buflen] = myZEROS;
726 }
727
728#if USE_WIDEC_SUPPORT && NCURSES_EXT_FUNCS
729 if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR)
730 result = FALSE;
731#endif
732
733 if (need_visual_update && result)
734 {
735 WINDOW *new_window = newpad(field->drows, field->dcols);
736
737 if (new_window != 0)
738 {
739 assert(form != (FORM *)0);
740 if (form->w)
741 delwin(form->w);
742 form->w = new_window;
743 Set_Field_Window_Attributes(field, form->w);
744 werase(form->w);
745 Buffer_To_Window(field, form->w);
746 untouchwin(form->w);
747 wmove(form->w, form->currow, form->curcol);
748 }
749 else
750 result = FALSE;
751 }
752
753 if (result)
754 {
755 free(oldbuf);
756 /* reflect changes in linked fields */
757 if (field != field->link)
758 {
759 FIELD *linked_field;
760
761 for (linked_field = field->link;
762 linked_field != field;
763 linked_field = linked_field->link)
764 {
765 linked_field->buf = field->buf;
766 linked_field->drows = field->drows;
767 linked_field->dcols = field->dcols;
768 }
769 }
770 }
771 else
772 {
773 /* restore old state */
774 field->dcols = old_dcols;
775 field->drows = old_drows;
776 field->buf = oldbuf;
777 if ((single_line_field &&
778 (field->dcols != field->maxgrow)) ||
779 (!single_line_field &&
780 (field->drows != field->maxgrow)))
Steve Kondikae271bc2015-11-15 02:50:53 +0100781 SetStatus(field, _MAY_GROW);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530782 free(newbuf);
783 }
784 }
785 }
786 return (result);
787}
788
789#ifdef NCURSES_MOUSE_VERSION
790/*---------------------------------------------------------------------------
791| Facility : libnform
792| Function : int Field_encloses(FIELD *field, int ry, int rx)
793|
794| Description : Check if the given coordinates lie within the given field.
795|
796| Return Values : E_OK - success
797| E_BAD_ARGUMENT - invalid form pointer
798| E_SYSTEM_ERROR - form has no current field or
799| field-window
800+--------------------------------------------------------------------------*/
801static int
802Field_encloses(FIELD *field, int ry, int rx)
803{
Steve Kondikae271bc2015-11-15 02:50:53 +0100804 T((T_CALLED("Field_encloses(%p)"), (void *)field));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530805 if (field != 0
806 && field->frow <= ry
807 && (field->frow + field->rows) > ry
808 && field->fcol <= rx
809 && (field->fcol + field->cols) > rx)
810 {
811 RETURN(E_OK);
812 }
813 RETURN(E_INVALID_FIELD);
814}
815#endif
816
817/*---------------------------------------------------------------------------
818| Facility : libnform
819| Function : int _nc_Position_Form_Cursor(FORM * form)
820|
821| Description : Position the cursor in the window for the current
822| field to be in sync. with the currow and curcol
823| values.
824|
825| Return Values : E_OK - success
826| E_BAD_ARGUMENT - invalid form pointer
827| E_SYSTEM_ERROR - form has no current field or
828| field-window
829+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -0400830FORM_EXPORT(int)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530831_nc_Position_Form_Cursor(FORM *form)
832{
833 FIELD *field;
834 WINDOW *formwin;
835
836 if (!form)
837 return (E_BAD_ARGUMENT);
838
839 if (!form->w || !form->current)
840 return (E_SYSTEM_ERROR);
841
842 field = form->current;
843 formwin = Get_Form_Window(form);
844
845 wmove(form->w, form->currow, form->curcol);
846 if (Has_Invisible_Parts(field))
847 {
848 /* in this case fieldwin isn't derived from formwin, so we have
849 to move the cursor in formwin by hand... */
850 wmove(formwin,
851 field->frow + form->currow - form->toprow,
852 field->fcol + form->curcol - form->begincol);
853 wcursyncup(formwin);
854 }
855 else
856 wcursyncup(form->w);
857 return (E_OK);
858}
859
860/*---------------------------------------------------------------------------
861| Facility : libnform
862| Function : int _nc_Refresh_Current_Field(FORM * form)
863|
micky3879b9f5e72025-07-08 18:04:53 -0400864| Description : Propagate the changes in the field's window to the
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530865| window of the form.
866|
867| Return Values : E_OK - on success
868| E_BAD_ARGUMENT - invalid form pointer
869| E_SYSTEM_ERROR - general error
870+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -0400871static bool move_after_insert = TRUE;
872FORM_EXPORT(int)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530873_nc_Refresh_Current_Field(FORM *form)
874{
875 WINDOW *formwin;
876 FIELD *field;
micky3879b9f5e72025-07-08 18:04:53 -0400877 bool is_public;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530878
Steve Kondikae271bc2015-11-15 02:50:53 +0100879 T((T_CALLED("_nc_Refresh_Current_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530880
881 if (!form)
882 RETURN(E_BAD_ARGUMENT);
883
884 if (!form->w || !form->current)
885 RETURN(E_SYSTEM_ERROR);
886
887 field = form->current;
888 formwin = Get_Form_Window(form);
889
micky3879b9f5e72025-07-08 18:04:53 -0400890 is_public = Field_Has_Option(field, O_PUBLIC);
891
892 if (Is_Scroll_Field(field))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530893 {
micky3879b9f5e72025-07-08 18:04:53 -0400894 /* 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))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530897 {
micky3879b9f5e72025-07-08 18:04:53 -0400898 /* horizontal scrolling */
899 if (form->curcol < form->begincol)
900 form->begincol = form->curcol;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530901 else
902 {
micky3879b9f5e72025-07-08 18:04:53 -0400903 if (form->curcol >= (form->begincol + field->cols))
904 form->begincol = form->curcol - field->cols
905 + (move_after_insert ? 1 : 0);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530906 }
micky3879b9f5e72025-07-08 18:04:53 -0400907 if (is_public)
908 copywin(form->w,
909 formwin,
910 0,
911 form->begincol,
912 field->frow,
913 field->fcol,
914 field->frow,
915 field->cols + field->fcol - 1,
916 0);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530917 }
918 else
919 {
micky3879b9f5e72025-07-08 18:04:53 -0400920 /* A multi-line, i.e. vertical scrolling field */
921 int first_modified_row, first_unmodified_row;
922
923 if (field->drows > field->rows)
924 {
925 int row_after_bottom = form->toprow + field->rows;
926
927 if (form->currow < form->toprow)
928 {
929 form->toprow = form->currow;
930 SetStatus(field, _NEWTOP);
931 }
932 if (form->currow >= row_after_bottom)
933 {
934 form->toprow = form->currow - field->rows + 1;
935 SetStatus(field, _NEWTOP);
936 }
937 if (field->status & _NEWTOP)
938 {
939 /* means we have to copy whole range */
940 first_modified_row = form->toprow;
941 first_unmodified_row = first_modified_row + field->rows;
942 ClrStatus(field, _NEWTOP);
943 }
944 else
945 {
946 /* we try to optimize : finding the range of touched
947 lines */
948 first_modified_row = form->toprow;
949 while (first_modified_row < row_after_bottom)
950 {
951 if (is_linetouched(form->w, first_modified_row))
952 break;
953 first_modified_row++;
954 }
955 first_unmodified_row = first_modified_row;
956 while (first_unmodified_row < row_after_bottom)
957 {
958 if (!is_linetouched(form->w, first_unmodified_row))
959 break;
960 first_unmodified_row++;
961 }
962 }
963 }
964 else
965 {
966 first_modified_row = form->toprow;
967 first_unmodified_row = first_modified_row + field->rows;
968 }
969 if (first_unmodified_row != first_modified_row && is_public)
970 copywin(form->w,
971 formwin,
972 first_modified_row,
973 0,
974 field->frow + first_modified_row - form->toprow,
975 field->fcol,
976 field->frow + first_unmodified_row - form->toprow - 1,
977 field->cols + field->fcol - 1,
978 0);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530979 }
micky3879b9f5e72025-07-08 18:04:53 -0400980 if (is_public)
981 wsyncup(formwin);
982 }
983 else
984 {
985 /* if the field-window is simply a derived window, i.e. contains no
986 * invisible parts, the whole thing is trivial
987 */
988 if (is_public)
989 wsyncup(form->w);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530990 }
991 untouchwin(form->w);
992 returnCode(_nc_Position_Form_Cursor(form));
993}
994
995/*---------------------------------------------------------------------------
996| Facility : libnform
997| Function : static void Perform_Justification(
998| FIELD * field,
999| WINDOW * win)
1000|
1001| Description : Output field with requested justification
1002|
1003| Return Values : -
1004+--------------------------------------------------------------------------*/
1005static void
1006Perform_Justification(FIELD *field, WINDOW *win)
1007{
1008 FIELD_CELL *bp;
1009 int len;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301010
micky3879b9f5e72025-07-08 18:04:53 -04001011 bp = (Field_Has_Option(field, O_NO_LEFT_STRIP)
1012 ? field->buf
1013 : Get_Start_Of_Data(field->buf, Buffer_Length(field)));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301014 len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
1015
1016 if (len > 0)
1017 {
micky3879b9f5e72025-07-08 18:04:53 -04001018 int col = 0;
1019
Steve Kondikae271bc2015-11-15 02:50:53 +01001020 assert(win && (field->drows == 1));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301021
Steve Kondikae271bc2015-11-15 02:50:53 +01001022 if (field->cols - len >= 0)
1023 switch (field->just)
1024 {
1025 case JUSTIFY_LEFT:
1026 break;
1027 case JUSTIFY_CENTER:
1028 col = (field->cols - len) / 2;
1029 break;
1030 case JUSTIFY_RIGHT:
1031 col = field->cols - len;
1032 break;
1033 default:
1034 break;
1035 }
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301036
1037 wmove(win, 0, col);
1038 myADDNSTR(win, bp, len);
1039 }
1040}
1041
1042/*---------------------------------------------------------------------------
1043| Facility : libnform
1044| Function : static void Undo_Justification(
1045| FIELD * field,
1046| WINDOW * win)
1047|
1048| Description : Display field without any justification, i.e.
1049| left justified
1050|
1051| Return Values : -
1052+--------------------------------------------------------------------------*/
1053static void
1054Undo_Justification(FIELD *field, WINDOW *win)
1055{
1056 FIELD_CELL *bp;
micky3879b9f5e72025-07-08 18:04:53 -04001057 int y, x;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301058 int len;
1059
micky3879b9f5e72025-07-08 18:04:53 -04001060 getyx(win, y, x);
1061
1062 bp = (Field_Has_Option(field, O_NO_LEFT_STRIP)
1063 ? field->buf
1064 : Get_Start_Of_Data(field->buf, Buffer_Length(field)));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301065 len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
1066
1067 if (len > 0)
1068 {
1069 assert(win);
1070 wmove(win, 0, 0);
1071 myADDNSTR(win, bp, len);
1072 }
micky3879b9f5e72025-07-08 18:04:53 -04001073 wmove(win, y, x);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301074}
1075
1076/*---------------------------------------------------------------------------
1077| Facility : libnform
Steve Kondikae271bc2015-11-15 02:50:53 +01001078| Function : static bool Check_Char(FORM *form,
1079| FIELD *field,
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301080| FIELDTYPE * typ,
1081| int ch,
1082| TypeArgument *argp)
1083|
1084| Description : Perform a single character check for character ch
1085| according to the fieldtype instance.
1086|
1087| Return Values : TRUE - Character is valid
1088| FALSE - Character is invalid
1089+--------------------------------------------------------------------------*/
1090static bool
Steve Kondikae271bc2015-11-15 02:50:53 +01001091Check_Char(FORM *form,
1092 FIELD *field,
1093 FIELDTYPE *typ,
1094 int ch,
1095 TypeArgument *argp)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301096{
1097 if (typ)
1098 {
1099 if (typ->status & _LINKED_TYPE)
1100 {
1101 assert(argp);
1102 return (
Steve Kondikae271bc2015-11-15 02:50:53 +01001103 Check_Char(form, field, typ->left, ch, argp->left) ||
1104 Check_Char(form, field, typ->right, ch, argp->right));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301105 }
1106 else
1107 {
Steve Kondikae271bc2015-11-15 02:50:53 +01001108#if NCURSES_INTEROP_FUNCS
1109 if (typ->charcheck.occheck)
1110 {
1111 if (typ->status & _GENERIC)
1112 return typ->charcheck.gccheck(ch, form, field, (void *)argp);
1113 else
1114 return typ->charcheck.occheck(ch, (void *)argp);
1115 }
1116#else
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301117 if (typ->ccheck)
1118 return typ->ccheck(ch, (void *)argp);
Steve Kondikae271bc2015-11-15 02:50:53 +01001119#endif
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301120 }
1121 }
1122 return (!iscntrl(UChar(ch)) ? TRUE : FALSE);
1123}
1124
1125/*---------------------------------------------------------------------------
1126| Facility : libnform
1127| Function : static int Display_Or_Erase_Field(
1128| FIELD * field,
1129| bool bEraseFlag)
1130|
1131| Description : Create a subwindow for the field and display the
1132| buffer contents (apply justification if required)
1133| or simply erase the field.
1134|
1135| Return Values : E_OK - on success
1136| E_SYSTEM_ERROR - some error (typical no memory)
1137+--------------------------------------------------------------------------*/
1138static int
1139Display_Or_Erase_Field(FIELD *field, bool bEraseFlag)
1140{
1141 WINDOW *win;
1142 WINDOW *fwin;
1143
1144 if (!field)
1145 return E_SYSTEM_ERROR;
1146
1147 fwin = Get_Form_Window(field->form);
1148 win = derwin(fwin,
1149 field->rows, field->cols, field->frow, field->fcol);
1150
1151 if (!win)
1152 return E_SYSTEM_ERROR;
1153 else
1154 {
Steve Kondikae271bc2015-11-15 02:50:53 +01001155 if (Field_Has_Option(field, O_VISIBLE))
1156 {
1157 Set_Field_Window_Attributes(field, win);
1158 }
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301159 else
Steve Kondikae271bc2015-11-15 02:50:53 +01001160 {
1161 (void)wattrset(win, (int)WINDOW_ATTRS(fwin));
1162 }
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301163 werase(win);
1164 }
1165
1166 if (!bEraseFlag)
1167 {
Steve Kondikae271bc2015-11-15 02:50:53 +01001168 if (Field_Has_Option(field, O_PUBLIC))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301169 {
1170 if (Justification_Allowed(field))
1171 Perform_Justification(field, win);
1172 else
1173 Buffer_To_Window(field, win);
1174 }
Steve Kondikae271bc2015-11-15 02:50:53 +01001175 ClrStatus(field, _NEWTOP);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301176 }
1177 wsyncup(win);
1178 delwin(win);
1179 return E_OK;
1180}
1181
1182/* Macros to preset the bEraseFlag */
1183#define Display_Field(field) Display_Or_Erase_Field(field,FALSE)
1184#define Erase_Field(field) Display_Or_Erase_Field(field,TRUE)
1185
1186/*---------------------------------------------------------------------------
1187| Facility : libnform
1188| Function : static int Synchronize_Field(FIELD * field)
1189|
1190| Description : Synchronize the windows content with the value in
1191| the buffer.
1192|
1193| Return Values : E_OK - success
1194| E_BAD_ARGUMENT - invalid field pointer
1195| E_SYSTEM_ERROR - some severe basic error
1196+--------------------------------------------------------------------------*/
1197static int
1198Synchronize_Field(FIELD *field)
1199{
1200 FORM *form;
1201 int res = E_OK;
1202
1203 if (!field)
1204 return (E_BAD_ARGUMENT);
1205
1206 if (((form = field->form) != (FORM *)0)
1207 && Field_Really_Appears(field))
1208 {
1209 if (field == form->current)
1210 {
1211 form->currow = form->curcol = form->toprow = form->begincol = 0;
1212 werase(form->w);
1213
Steve Kondikae271bc2015-11-15 02:50:53 +01001214 if ((Field_Has_Option(field, O_PUBLIC)) && Justification_Allowed(field))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301215 Undo_Justification(field, form->w);
1216 else
1217 Buffer_To_Window(field, form->w);
1218
Steve Kondikae271bc2015-11-15 02:50:53 +01001219 SetStatus(field, _NEWTOP);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301220 res = _nc_Refresh_Current_Field(form);
1221 }
1222 else
1223 res = Display_Field(field);
1224 }
Steve Kondikae271bc2015-11-15 02:50:53 +01001225 SetStatus(field, _CHANGED);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301226 return (res);
1227}
1228
1229/*---------------------------------------------------------------------------
1230| Facility : libnform
1231| Function : static int Synchronize_Linked_Fields(FIELD * field)
1232|
1233| Description : Propagate the Synchronize_Field function to all linked
1234| fields. The first error that occurs in the sequence
1235| of updates is the return value.
1236|
1237| Return Values : E_OK - success
1238| E_BAD_ARGUMENT - invalid field pointer
1239| E_SYSTEM_ERROR - some severe basic error
1240+--------------------------------------------------------------------------*/
1241static int
1242Synchronize_Linked_Fields(FIELD *field)
1243{
1244 FIELD *linked_field;
1245 int res = E_OK;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301246
1247 if (!field)
1248 return (E_BAD_ARGUMENT);
1249
1250 if (!field->link)
1251 return (E_SYSTEM_ERROR);
1252
1253 for (linked_field = field->link;
Steve Kondikae271bc2015-11-15 02:50:53 +01001254 (linked_field != field) && (linked_field != 0);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301255 linked_field = linked_field->link)
1256 {
micky3879b9f5e72025-07-08 18:04:53 -04001257 int syncres;
1258
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301259 if (((syncres = Synchronize_Field(linked_field)) != E_OK) &&
1260 (res == E_OK))
1261 res = syncres;
1262 }
1263 return (res);
1264}
1265
1266/*---------------------------------------------------------------------------
1267| Facility : libnform
1268| Function : int _nc_Synchronize_Attributes(FIELD * field)
1269|
micky3879b9f5e72025-07-08 18:04:53 -04001270| Description : If a field's visual attributes have changed, this
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301271| routine is called to propagate those changes to the
1272| screen.
1273|
1274| Return Values : E_OK - success
1275| E_BAD_ARGUMENT - invalid field pointer
1276| E_SYSTEM_ERROR - some severe basic error
1277+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -04001278FORM_EXPORT(int)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301279_nc_Synchronize_Attributes(FIELD *field)
1280{
1281 FORM *form;
1282 int res = E_OK;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301283
Steve Kondikae271bc2015-11-15 02:50:53 +01001284 T((T_CALLED("_nc_Synchronize_Attributes(%p)"), (void *)field));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301285
1286 if (!field)
1287 returnCode(E_BAD_ARGUMENT);
1288
1289 CHECKPOS(field->form);
1290 if (((form = field->form) != (FORM *)0)
1291 && Field_Really_Appears(field))
1292 {
1293 if (form->current == field)
1294 {
1295 Synchronize_Buffer(form);
1296 Set_Field_Window_Attributes(field, form->w);
1297 werase(form->w);
1298 wmove(form->w, form->currow, form->curcol);
1299
Steve Kondikae271bc2015-11-15 02:50:53 +01001300 if (Field_Has_Option(field, O_PUBLIC))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301301 {
1302 if (Justification_Allowed(field))
1303 Undo_Justification(field, form->w);
1304 else
1305 Buffer_To_Window(field, form->w);
1306 }
1307 else
1308 {
micky3879b9f5e72025-07-08 18:04:53 -04001309 WINDOW *formwin = Get_Form_Window(form);
1310
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301311 copywin(form->w, formwin,
1312 0, 0,
1313 field->frow, field->fcol,
micky3879b9f5e72025-07-08 18:04:53 -04001314 field->frow + field->rows - 1,
1315 field->fcol + field->cols - 1, 0);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301316 wsyncup(formwin);
1317 Buffer_To_Window(field, form->w);
Steve Kondikae271bc2015-11-15 02:50:53 +01001318 SetStatus(field, _NEWTOP); /* fake refresh to paint all */
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301319 _nc_Refresh_Current_Field(form);
1320 }
1321 }
1322 else
1323 {
1324 res = Display_Field(field);
1325 }
1326 }
1327 CHECKPOS(form);
1328 returnCode(res);
1329}
1330
1331/*---------------------------------------------------------------------------
1332| Facility : libnform
1333| Function : int _nc_Synchronize_Options(FIELD * field,
1334| Field_Options newopts)
1335|
micky3879b9f5e72025-07-08 18:04:53 -04001336| Description : If a field's options have changed, this routine is
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301337| called to propagate these changes to the screen and
1338| to really change the behavior of the field.
1339|
1340| Return Values : E_OK - success
1341| E_BAD_ARGUMENT - invalid field pointer
1342| E_CURRENT - field is the current one
1343| E_SYSTEM_ERROR - some severe basic error
1344+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -04001345FORM_EXPORT(int)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301346_nc_Synchronize_Options(FIELD *field, Field_Options newopts)
1347{
1348 Field_Options oldopts;
1349 Field_Options changed_opts;
1350 FORM *form;
1351 int res = E_OK;
1352
Steve Kondikae271bc2015-11-15 02:50:53 +01001353 T((T_CALLED("_nc_Synchronize_Options(%p,%#x)"), (void *)field, newopts));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301354
1355 if (!field)
1356 returnCode(E_BAD_ARGUMENT);
1357
1358 oldopts = field->opts;
1359 changed_opts = oldopts ^ newopts;
1360 field->opts = newopts;
1361 form = field->form;
1362
1363 if (form)
1364 {
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301365 if (form->status & _POSTED)
1366 {
Steve Kondikae271bc2015-11-15 02:50:53 +01001367 if (form->current == field)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301368 {
Steve Kondikae271bc2015-11-15 02:50:53 +01001369 field->opts = oldopts;
1370 returnCode(E_CURRENT);
1371 }
1372 if (form->curpage == field->page)
1373 {
1374 if ((unsigned)changed_opts & O_VISIBLE)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301375 {
Steve Kondikae271bc2015-11-15 02:50:53 +01001376 if ((unsigned)newopts & O_VISIBLE)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301377 res = Display_Field(field);
1378 else
1379 res = Erase_Field(field);
1380 }
1381 else
1382 {
Steve Kondikae271bc2015-11-15 02:50:53 +01001383 if (((unsigned)changed_opts & O_PUBLIC) &&
1384 ((unsigned)newopts & O_VISIBLE))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301385 res = Display_Field(field);
1386 }
1387 }
1388 }
1389 }
1390
Steve Kondikae271bc2015-11-15 02:50:53 +01001391 if ((unsigned)changed_opts & O_STATIC)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301392 {
1393 bool single_line_field = Single_Line_Field(field);
1394 int res2 = E_OK;
1395
Steve Kondikae271bc2015-11-15 02:50:53 +01001396 if ((unsigned)newopts & O_STATIC)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301397 {
1398 /* the field becomes now static */
Steve Kondikae271bc2015-11-15 02:50:53 +01001399 ClrStatus(field, _MAY_GROW);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301400 /* if actually we have no hidden columns, justification may
1401 occur again */
1402 if (single_line_field &&
1403 (field->cols == field->dcols) &&
1404 (field->just != NO_JUSTIFICATION) &&
1405 Field_Really_Appears(field))
1406 {
1407 res2 = Display_Field(field);
1408 }
1409 }
1410 else
1411 {
1412 /* field is no longer static */
1413 if ((field->maxgrow == 0) ||
1414 (single_line_field && (field->dcols < field->maxgrow)) ||
1415 (!single_line_field && (field->drows < field->maxgrow)))
1416 {
Steve Kondikae271bc2015-11-15 02:50:53 +01001417 SetStatus(field, _MAY_GROW);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301418 /* a field with justification now changes its behavior,
1419 so we must redisplay it */
1420 if (single_line_field &&
1421 (field->just != NO_JUSTIFICATION) &&
1422 Field_Really_Appears(field))
1423 {
1424 res2 = Display_Field(field);
1425 }
1426 }
1427 }
1428 if (res2 != E_OK)
1429 res = res2;
1430 }
1431
1432 returnCode(res);
1433}
1434
micky3879b9f5e72025-07-08 18:04:53 -04001435/*
1436 * Removes the focus from the current field of the form.
1437 */
1438void
1439_nc_Unset_Current_Field(FORM *form)
1440{
1441 FIELD *field = form->current;
1442
1443 _nc_Refresh_Current_Field(form);
1444 if (Field_Has_Option(field, O_PUBLIC))
1445 {
1446 if (field->drows > field->rows)
1447 {
1448 if (form->toprow == 0)
1449 ClrStatus(field, _NEWTOP);
1450 else
1451 SetStatus(field, _NEWTOP);
1452 }
1453 else
1454 {
1455 if (Justification_Allowed(field))
1456 {
1457 Window_To_Buffer(form, field);
1458 werase(form->w);
1459 Perform_Justification(field, form->w);
1460 if (Field_Has_Option(field, O_DYNAMIC_JUSTIFY) &&
1461 (form->w->_parent == 0))
1462 {
1463 copywin(form->w,
1464 Get_Form_Window(form),
1465 0,
1466 0,
1467 field->frow,
1468 field->fcol,
1469 field->frow,
1470 field->cols + field->fcol - 1,
1471 0);
1472 wsyncup(Get_Form_Window(form));
1473 }
1474 else
1475 {
1476 wsyncup(form->w);
1477 }
1478 }
1479 }
1480 }
1481 delwin(form->w);
1482 form->w = (WINDOW *)0;
1483 form->current = 0;
1484}
1485
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301486/*---------------------------------------------------------------------------
1487| Facility : libnform
1488| Function : int _nc_Set_Current_Field(FORM * form,
1489| FIELD * newfield)
1490|
1491| Description : Make the newfield the new current field.
1492|
1493| Return Values : E_OK - success
1494| E_BAD_ARGUMENT - invalid form or field pointer
1495| E_SYSTEM_ERROR - some severe basic error
1496| E_NOT_CONNECTED - no fields are connected to the form
1497+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -04001498FORM_EXPORT(int)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301499_nc_Set_Current_Field(FORM *form, FIELD *newfield)
1500{
1501 FIELD *field;
1502 WINDOW *new_window;
1503
Steve Kondikae271bc2015-11-15 02:50:53 +01001504 T((T_CALLED("_nc_Set_Current_Field(%p,%p)"), (void *)form, (void *)newfield));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301505
micky3879b9f5e72025-07-08 18:04:53 -04001506 if (!form || !newfield || (newfield->form != form))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301507 returnCode(E_BAD_ARGUMENT);
1508
1509 if ((form->status & _IN_DRIVER))
1510 returnCode(E_BAD_STATE);
1511
1512 if (!(form->field))
1513 returnCode(E_NOT_CONNECTED);
1514
1515 field = form->current;
1516
1517 if ((field != newfield) ||
1518 !(form->status & _POSTED))
1519 {
micky3879b9f5e72025-07-08 18:04:53 -04001520 if (field && (form->w) &&
Steve Kondikae271bc2015-11-15 02:50:53 +01001521 (Field_Has_Option(field, O_VISIBLE)) &&
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301522 (field->form->curpage == field->page))
micky3879b9f5e72025-07-08 18:04:53 -04001523 _nc_Unset_Current_Field(form);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301524
1525 field = newfield;
1526
1527 if (Has_Invisible_Parts(field))
1528 new_window = newpad(field->drows, field->dcols);
1529 else
1530 new_window = derwin(Get_Form_Window(form),
1531 field->rows, field->cols, field->frow, field->fcol);
1532
1533 if (!new_window)
1534 returnCode(E_SYSTEM_ERROR);
1535
1536 form->current = field;
1537
1538 if (form->w)
1539 delwin(form->w);
1540 form->w = new_window;
1541
Steve Kondikae271bc2015-11-15 02:50:53 +01001542 ClrStatus(form, _WINDOW_MODIFIED);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301543 Set_Field_Window_Attributes(field, form->w);
1544
1545 if (Has_Invisible_Parts(field))
1546 {
1547 werase(form->w);
1548 Buffer_To_Window(field, form->w);
1549 }
1550 else
1551 {
1552 if (Justification_Allowed(field))
1553 {
1554 werase(form->w);
1555 Undo_Justification(field, form->w);
1556 wsyncup(form->w);
1557 }
1558 }
1559
1560 untouchwin(form->w);
1561 }
1562
1563 form->currow = form->curcol = form->toprow = form->begincol = 0;
1564 returnCode(E_OK);
1565}
1566
1567/*----------------------------------------------------------------------------
1568 Intra-Field Navigation routines
1569 --------------------------------------------------------------------------*/
1570
1571/*---------------------------------------------------------------------------
1572| Facility : libnform
1573| Function : static int IFN_Next_Character(FORM * form)
1574|
1575| Description : Move to the next character in the field. In a multi-line
1576| field this wraps at the end of the line.
1577|
1578| Return Values : E_OK - success
1579| E_REQUEST_DENIED - at the rightmost position
1580+--------------------------------------------------------------------------*/
1581static int
1582IFN_Next_Character(FORM *form)
1583{
1584 FIELD *field = form->current;
1585 int step = myWCWIDTH(form->w, form->currow, form->curcol);
1586
Steve Kondikae271bc2015-11-15 02:50:53 +01001587 T((T_CALLED("IFN_Next_Character(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301588 if ((form->curcol += step) == field->dcols)
1589 {
1590 if ((++(form->currow)) == field->drows)
1591 {
1592#if GROW_IF_NAVIGATE
1593 if (!Single_Line_Field(field) && Field_Grown(field, 1))
1594 {
1595 form->curcol = 0;
1596 returnCode(E_OK);
1597 }
1598#endif
1599 form->currow--;
1600#if GROW_IF_NAVIGATE
1601 if (Single_Line_Field(field) && Field_Grown(field, 1))
1602 returnCode(E_OK);
1603#endif
1604 form->curcol -= step;
1605 returnCode(E_REQUEST_DENIED);
1606 }
1607 form->curcol = 0;
1608 }
1609 returnCode(E_OK);
1610}
1611
1612/*---------------------------------------------------------------------------
1613| Facility : libnform
1614| Function : static int IFN_Previous_Character(FORM * form)
1615|
1616| Description : Move to the previous character in the field. In a
1617| multi-line field this wraps and the beginning of the
1618| line.
1619|
1620| Return Values : E_OK - success
1621| E_REQUEST_DENIED - at the leftmost position
1622+--------------------------------------------------------------------------*/
1623static int
1624IFN_Previous_Character(FORM *form)
1625{
1626 int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1627 int oldcol = form->curcol;
1628
Steve Kondikae271bc2015-11-15 02:50:53 +01001629 T((T_CALLED("IFN_Previous_Character(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301630 if ((form->curcol -= amount) < 0)
1631 {
1632 if ((--(form->currow)) < 0)
1633 {
1634 form->currow++;
1635 form->curcol = oldcol;
1636 returnCode(E_REQUEST_DENIED);
1637 }
1638 form->curcol = form->current->dcols - 1;
1639 }
1640 returnCode(E_OK);
1641}
1642
1643/*---------------------------------------------------------------------------
1644| Facility : libnform
1645| Function : static int IFN_Next_Line(FORM * form)
1646|
1647| Description : Move to the beginning of the next line in the field
1648|
1649| Return Values : E_OK - success
1650| E_REQUEST_DENIED - at the last line
1651+--------------------------------------------------------------------------*/
1652static int
1653IFN_Next_Line(FORM *form)
1654{
1655 FIELD *field = form->current;
1656
Steve Kondikae271bc2015-11-15 02:50:53 +01001657 T((T_CALLED("IFN_Next_Line(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301658 if ((++(form->currow)) == field->drows)
1659 {
1660#if GROW_IF_NAVIGATE
1661 if (!Single_Line_Field(field) && Field_Grown(field, 1))
1662 returnCode(E_OK);
1663#endif
1664 form->currow--;
1665 returnCode(E_REQUEST_DENIED);
1666 }
1667 form->curcol = 0;
1668 returnCode(E_OK);
1669}
1670
1671/*---------------------------------------------------------------------------
1672| Facility : libnform
1673| Function : static int IFN_Previous_Line(FORM * form)
1674|
1675| Description : Move to the beginning of the previous line in the field
1676|
1677| Return Values : E_OK - success
1678| E_REQUEST_DENIED - at the first line
1679+--------------------------------------------------------------------------*/
1680static int
1681IFN_Previous_Line(FORM *form)
1682{
Steve Kondikae271bc2015-11-15 02:50:53 +01001683 T((T_CALLED("IFN_Previous_Line(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301684 if ((--(form->currow)) < 0)
1685 {
1686 form->currow++;
1687 returnCode(E_REQUEST_DENIED);
1688 }
1689 form->curcol = 0;
1690 returnCode(E_OK);
1691}
1692
1693/*---------------------------------------------------------------------------
1694| Facility : libnform
1695| Function : static int IFN_Next_Word(FORM * form)
1696|
1697| Description : Move to the beginning of the next word in the field.
1698|
1699| Return Values : E_OK - success
1700| E_REQUEST_DENIED - there is no next word
1701+--------------------------------------------------------------------------*/
1702static int
1703IFN_Next_Word(FORM *form)
1704{
1705 FIELD *field = form->current;
1706 FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1707 FIELD_CELL *s;
1708 FIELD_CELL *t;
1709
Steve Kondikae271bc2015-11-15 02:50:53 +01001710 T((T_CALLED("IFN_Next_Word(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301711
1712 /* We really need access to the data, so we have to synchronize */
1713 Synchronize_Buffer(form);
1714
1715 /* Go to the first whitespace after the current position (including
1716 current position). This is then the starting point to look for the
1717 next non-blank data */
1718 s = Get_First_Whitespace_Character(bp, Buffer_Length(field) -
1719 (int)(bp - field->buf));
1720
1721 /* Find the start of the next word */
1722 t = Get_Start_Of_Data(s, Buffer_Length(field) -
1723 (int)(s - field->buf));
1724#if !FRIENDLY_PREV_NEXT_WORD
1725 if (s == t)
1726 returnCode(E_REQUEST_DENIED);
1727 else
1728#endif
1729 {
1730 Adjust_Cursor_Position(form, t);
1731 returnCode(E_OK);
1732 }
1733}
1734
1735/*---------------------------------------------------------------------------
1736| Facility : libnform
1737| Function : static int IFN_Previous_Word(FORM * form)
1738|
1739| Description : Move to the beginning of the previous word in the field.
1740|
1741| Return Values : E_OK - success
1742| E_REQUEST_DENIED - there is no previous word
1743+--------------------------------------------------------------------------*/
1744static int
1745IFN_Previous_Word(FORM *form)
1746{
1747 FIELD *field = form->current;
1748 FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1749 FIELD_CELL *s;
1750 FIELD_CELL *t;
1751 bool again = FALSE;
1752
Steve Kondikae271bc2015-11-15 02:50:53 +01001753 T((T_CALLED("IFN_Previous_Word(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301754
1755 /* We really need access to the data, so we have to synchronize */
1756 Synchronize_Buffer(form);
1757
1758 s = After_End_Of_Data(field->buf, (int)(bp - field->buf));
1759 /* s points now right after the last non-blank in the buffer before bp.
1760 If bp was in a word, s equals bp. In this case we must find the last
1761 whitespace in the buffer before bp and repeat the game to really find
1762 the previous word! */
1763 if (s == bp)
1764 again = TRUE;
1765
1766 /* And next call now goes backward to look for the last whitespace
1767 before that, pointing right after this, so it points to the begin
1768 of the previous word.
1769 */
1770 t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1771#if !FRIENDLY_PREV_NEXT_WORD
1772 if (s == t)
1773 returnCode(E_REQUEST_DENIED);
1774#endif
1775 if (again)
1776 {
1777 /* and do it again, replacing bp by t */
1778 s = After_End_Of_Data(field->buf, (int)(t - field->buf));
1779 t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1780#if !FRIENDLY_PREV_NEXT_WORD
1781 if (s == t)
1782 returnCode(E_REQUEST_DENIED);
1783#endif
1784 }
1785 Adjust_Cursor_Position(form, t);
1786 returnCode(E_OK);
1787}
1788
1789/*---------------------------------------------------------------------------
1790| Facility : libnform
1791| Function : static int IFN_Beginning_Of_Field(FORM * form)
1792|
1793| Description : Place the cursor at the first non-pad character in
1794| the field.
1795|
1796| Return Values : E_OK - success
1797+--------------------------------------------------------------------------*/
1798static int
1799IFN_Beginning_Of_Field(FORM *form)
1800{
1801 FIELD *field = form->current;
1802
Steve Kondikae271bc2015-11-15 02:50:53 +01001803 T((T_CALLED("IFN_Beginning_Of_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301804 Synchronize_Buffer(form);
1805 Adjust_Cursor_Position(form,
1806 Get_Start_Of_Data(field->buf, Buffer_Length(field)));
1807 returnCode(E_OK);
1808}
1809
1810/*---------------------------------------------------------------------------
1811| Facility : libnform
1812| Function : static int IFN_End_Of_Field(FORM * form)
1813|
1814| Description : Place the cursor after the last non-pad character in
1815| the field. If the field occupies the last position in
1816| the buffer, the cursor is positioned on the last
1817| character.
1818|
1819| Return Values : E_OK - success
1820+--------------------------------------------------------------------------*/
1821static int
1822IFN_End_Of_Field(FORM *form)
1823{
1824 FIELD *field = form->current;
1825 FIELD_CELL *pos;
1826
Steve Kondikae271bc2015-11-15 02:50:53 +01001827 T((T_CALLED("IFN_End_Of_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301828 Synchronize_Buffer(form);
1829 pos = After_End_Of_Data(field->buf, Buffer_Length(field));
1830 if (pos == (field->buf + Buffer_Length(field)))
1831 pos--;
1832 Adjust_Cursor_Position(form, pos);
1833 returnCode(E_OK);
1834}
1835
1836/*---------------------------------------------------------------------------
1837| Facility : libnform
1838| Function : static int IFN_Beginning_Of_Line(FORM * form)
1839|
1840| Description : Place the cursor on the first non-pad character in
1841| the current line of the field.
1842|
1843| Return Values : E_OK - success
1844+--------------------------------------------------------------------------*/
1845static int
1846IFN_Beginning_Of_Line(FORM *form)
1847{
1848 FIELD *field = form->current;
1849
Steve Kondikae271bc2015-11-15 02:50:53 +01001850 T((T_CALLED("IFN_Beginning_Of_Line(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301851 Synchronize_Buffer(form);
1852 Adjust_Cursor_Position(form,
1853 Get_Start_Of_Data(Address_Of_Current_Row_In_Buffer(form),
1854 field->dcols));
1855 returnCode(E_OK);
1856}
1857
1858/*---------------------------------------------------------------------------
1859| Facility : libnform
1860| Function : static int IFN_End_Of_Line(FORM * form)
1861|
1862| Description : Place the cursor after the last non-pad character in the
1863| current line of the field. If the field occupies the
1864| last column in the line, the cursor is positioned on the
1865| last character of the line.
1866|
1867| Return Values : E_OK - success
1868+--------------------------------------------------------------------------*/
1869static int
1870IFN_End_Of_Line(FORM *form)
1871{
1872 FIELD *field = form->current;
1873 FIELD_CELL *pos;
1874 FIELD_CELL *bp;
1875
Steve Kondikae271bc2015-11-15 02:50:53 +01001876 T((T_CALLED("IFN_End_Of_Line(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301877 Synchronize_Buffer(form);
1878 bp = Address_Of_Current_Row_In_Buffer(form);
1879 pos = After_End_Of_Data(bp, field->dcols);
1880 if (pos == (bp + field->dcols))
1881 pos--;
1882 Adjust_Cursor_Position(form, pos);
1883 returnCode(E_OK);
1884}
1885
1886/*---------------------------------------------------------------------------
1887| Facility : libnform
1888| Function : static int IFN_Left_Character(FORM * form)
1889|
1890| Description : Move one character to the left in the current line.
1891| This doesn't cycle.
1892|
1893| Return Values : E_OK - success
1894| E_REQUEST_DENIED - already in first column
1895+--------------------------------------------------------------------------*/
1896static int
1897IFN_Left_Character(FORM *form)
1898{
1899 int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1900 int oldcol = form->curcol;
1901
Steve Kondikae271bc2015-11-15 02:50:53 +01001902 T((T_CALLED("IFN_Left_Character(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301903 if ((form->curcol -= amount) < 0)
1904 {
1905 form->curcol = oldcol;
1906 returnCode(E_REQUEST_DENIED);
1907 }
1908 returnCode(E_OK);
1909}
1910
1911/*---------------------------------------------------------------------------
1912| Facility : libnform
1913| Function : static int IFN_Right_Character(FORM * form)
1914|
1915| Description : Move one character to the right in the current line.
1916| This doesn't cycle.
1917|
1918| Return Values : E_OK - success
1919| E_REQUEST_DENIED - already in last column
1920+--------------------------------------------------------------------------*/
1921static int
1922IFN_Right_Character(FORM *form)
1923{
1924 int amount = myWCWIDTH(form->w, form->currow, form->curcol);
1925 int oldcol = form->curcol;
1926
Steve Kondikae271bc2015-11-15 02:50:53 +01001927 T((T_CALLED("IFN_Right_Character(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301928 if ((form->curcol += amount) >= form->current->dcols)
1929 {
1930#if GROW_IF_NAVIGATE
1931 FIELD *field = form->current;
1932
1933 if (Single_Line_Field(field) && Field_Grown(field, 1))
1934 returnCode(E_OK);
1935#endif
1936 form->curcol = oldcol;
1937 returnCode(E_REQUEST_DENIED);
1938 }
1939 returnCode(E_OK);
1940}
1941
1942/*---------------------------------------------------------------------------
1943| Facility : libnform
1944| Function : static int IFN_Up_Character(FORM * form)
1945|
1946| Description : Move one line up. This doesn't cycle through the lines
1947| of the field.
1948|
1949| Return Values : E_OK - success
1950| E_REQUEST_DENIED - already in last column
1951+--------------------------------------------------------------------------*/
1952static int
1953IFN_Up_Character(FORM *form)
1954{
Steve Kondikae271bc2015-11-15 02:50:53 +01001955 T((T_CALLED("IFN_Up_Character(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301956 if ((--(form->currow)) < 0)
1957 {
1958 form->currow++;
1959 returnCode(E_REQUEST_DENIED);
1960 }
1961 returnCode(E_OK);
1962}
1963
1964/*---------------------------------------------------------------------------
1965| Facility : libnform
1966| Function : static int IFN_Down_Character(FORM * form)
1967|
1968| Description : Move one line down. This doesn't cycle through the
1969| lines of the field.
1970|
1971| Return Values : E_OK - success
1972| E_REQUEST_DENIED - already in last column
1973+--------------------------------------------------------------------------*/
1974static int
1975IFN_Down_Character(FORM *form)
1976{
1977 FIELD *field = form->current;
1978
Steve Kondikae271bc2015-11-15 02:50:53 +01001979 T((T_CALLED("IFN_Down_Character(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301980 if ((++(form->currow)) == field->drows)
1981 {
1982#if GROW_IF_NAVIGATE
1983 if (!Single_Line_Field(field) && Field_Grown(field, 1))
1984 returnCode(E_OK);
1985#endif
1986 --(form->currow);
1987 returnCode(E_REQUEST_DENIED);
1988 }
1989 returnCode(E_OK);
1990}
1991/*----------------------------------------------------------------------------
1992 END of Intra-Field Navigation routines
1993 --------------------------------------------------------------------------*/
1994
1995/*----------------------------------------------------------------------------
1996 Vertical scrolling helper routines
1997 --------------------------------------------------------------------------*/
1998
1999/*---------------------------------------------------------------------------
2000| Facility : libnform
2001| Function : static int VSC_Generic(FORM *form, int nlines)
2002|
2003| Description : Scroll multi-line field forward (nlines>0) or
2004| backward (nlines<0) this many lines.
2005|
2006| Return Values : E_OK - success
2007| E_REQUEST_DENIED - can't scroll
2008+--------------------------------------------------------------------------*/
2009static int
2010VSC_Generic(FORM *form, int nlines)
2011{
2012 FIELD *field = form->current;
2013 int res = E_REQUEST_DENIED;
2014 int rows_to_go = (nlines > 0 ? nlines : -nlines);
2015
2016 if (nlines > 0)
2017 {
2018 if ((rows_to_go + form->toprow) > (field->drows - field->rows))
2019 rows_to_go = (field->drows - field->rows - form->toprow);
2020
2021 if (rows_to_go > 0)
2022 {
2023 form->currow += rows_to_go;
2024 form->toprow += rows_to_go;
2025 res = E_OK;
2026 }
2027 }
2028 else
2029 {
2030 if (rows_to_go > form->toprow)
2031 rows_to_go = form->toprow;
2032
2033 if (rows_to_go > 0)
2034 {
2035 form->currow -= rows_to_go;
2036 form->toprow -= rows_to_go;
2037 res = E_OK;
2038 }
2039 }
2040 return (res);
2041}
2042/*----------------------------------------------------------------------------
2043 End of Vertical scrolling helper routines
2044 --------------------------------------------------------------------------*/
2045
2046/*----------------------------------------------------------------------------
2047 Vertical scrolling routines
2048 --------------------------------------------------------------------------*/
2049
2050/*---------------------------------------------------------------------------
2051| Facility : libnform
2052| Function : static int Vertical_Scrolling(
2053| int (* const fct) (FORM *),
2054| FORM * form)
2055|
2056| Description : Performs the generic vertical scrolling routines.
2057| This has to check for a multi-line field and to set
2058| the _NEWTOP flag if scrolling really occurred.
2059|
2060| Return Values : Propagated error code from low-level driver calls
2061+--------------------------------------------------------------------------*/
2062static int
2063Vertical_Scrolling(int (*const fct) (FORM *), FORM *form)
2064{
2065 int res = E_REQUEST_DENIED;
2066
2067 if (!Single_Line_Field(form->current))
2068 {
2069 res = fct(form);
2070 if (res == E_OK)
Steve Kondikae271bc2015-11-15 02:50:53 +01002071 SetStatus(form->current, _NEWTOP);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302072 }
2073 return (res);
2074}
2075
2076/*---------------------------------------------------------------------------
2077| Facility : libnform
2078| Function : static int VSC_Scroll_Line_Forward(FORM * form)
2079|
2080| Description : Scroll multi-line field forward a line
2081|
2082| Return Values : E_OK - success
2083| E_REQUEST_DENIED - no data ahead
2084+--------------------------------------------------------------------------*/
2085static int
2086VSC_Scroll_Line_Forward(FORM *form)
2087{
Steve Kondikae271bc2015-11-15 02:50:53 +01002088 T((T_CALLED("VSC_Scroll_Line_Forward(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302089 returnCode(VSC_Generic(form, 1));
2090}
2091
2092/*---------------------------------------------------------------------------
2093| Facility : libnform
2094| Function : static int VSC_Scroll_Line_Backward(FORM * form)
2095|
2096| Description : Scroll multi-line field backward a line
2097|
2098| Return Values : E_OK - success
2099| E_REQUEST_DENIED - no data behind
2100+--------------------------------------------------------------------------*/
2101static int
2102VSC_Scroll_Line_Backward(FORM *form)
2103{
Steve Kondikae271bc2015-11-15 02:50:53 +01002104 T((T_CALLED("VSC_Scroll_Line_Backward(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302105 returnCode(VSC_Generic(form, -1));
2106}
2107
2108/*---------------------------------------------------------------------------
2109| Facility : libnform
2110| Function : static int VSC_Scroll_Page_Forward(FORM * form)
2111|
2112| Description : Scroll a multi-line field forward a page
2113|
2114| Return Values : E_OK - success
2115| E_REQUEST_DENIED - no data ahead
2116+--------------------------------------------------------------------------*/
2117static int
2118VSC_Scroll_Page_Forward(FORM *form)
2119{
Steve Kondikae271bc2015-11-15 02:50:53 +01002120 T((T_CALLED("VSC_Scroll_Page_Forward(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302121 returnCode(VSC_Generic(form, form->current->rows));
2122}
2123
2124/*---------------------------------------------------------------------------
2125| Facility : libnform
2126| Function : static int VSC_Scroll_Half_Page_Forward(FORM * form)
2127|
2128| Description : Scroll a multi-line field forward half a page
2129|
2130| Return Values : E_OK - success
2131| E_REQUEST_DENIED - no data ahead
2132+--------------------------------------------------------------------------*/
2133static int
2134VSC_Scroll_Half_Page_Forward(FORM *form)
2135{
Steve Kondikae271bc2015-11-15 02:50:53 +01002136 T((T_CALLED("VSC_Scroll_Half_Page_Forward(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302137 returnCode(VSC_Generic(form, (form->current->rows + 1) / 2));
2138}
2139
2140/*---------------------------------------------------------------------------
2141| Facility : libnform
2142| Function : static int VSC_Scroll_Page_Backward(FORM * form)
2143|
2144| Description : Scroll a multi-line field backward a page
2145|
2146| Return Values : E_OK - success
2147| E_REQUEST_DENIED - no data behind
2148+--------------------------------------------------------------------------*/
2149static int
2150VSC_Scroll_Page_Backward(FORM *form)
2151{
Steve Kondikae271bc2015-11-15 02:50:53 +01002152 T((T_CALLED("VSC_Scroll_Page_Backward(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302153 returnCode(VSC_Generic(form, -(form->current->rows)));
2154}
2155
2156/*---------------------------------------------------------------------------
2157| Facility : libnform
2158| Function : static int VSC_Scroll_Half_Page_Backward(FORM * form)
2159|
2160| Description : Scroll a multi-line field backward half a page
2161|
2162| Return Values : E_OK - success
2163| E_REQUEST_DENIED - no data behind
2164+--------------------------------------------------------------------------*/
2165static int
2166VSC_Scroll_Half_Page_Backward(FORM *form)
2167{
Steve Kondikae271bc2015-11-15 02:50:53 +01002168 T((T_CALLED("VSC_Scroll_Half_Page_Backward(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302169 returnCode(VSC_Generic(form, -((form->current->rows + 1) / 2)));
2170}
2171/*----------------------------------------------------------------------------
2172 End of Vertical scrolling routines
2173 --------------------------------------------------------------------------*/
2174
2175/*----------------------------------------------------------------------------
2176 Horizontal scrolling helper routines
2177 --------------------------------------------------------------------------*/
2178
2179/*---------------------------------------------------------------------------
2180| Facility : libnform
2181| Function : static int HSC_Generic(FORM *form, int ncolumns)
2182|
2183| Description : Scroll single-line field forward (ncolumns>0) or
2184| backward (ncolumns<0) this many columns.
2185|
2186| Return Values : E_OK - success
2187| E_REQUEST_DENIED - can't scroll
2188+--------------------------------------------------------------------------*/
2189static int
2190HSC_Generic(FORM *form, int ncolumns)
2191{
2192 FIELD *field = form->current;
2193 int res = E_REQUEST_DENIED;
2194 int cols_to_go = (ncolumns > 0 ? ncolumns : -ncolumns);
2195
2196 if (ncolumns > 0)
2197 {
2198 if ((cols_to_go + form->begincol) > (field->dcols - field->cols))
2199 cols_to_go = field->dcols - field->cols - form->begincol;
2200
2201 if (cols_to_go > 0)
2202 {
2203 form->curcol += cols_to_go;
2204 form->begincol += cols_to_go;
2205 res = E_OK;
2206 }
2207 }
2208 else
2209 {
2210 if (cols_to_go > form->begincol)
2211 cols_to_go = form->begincol;
2212
2213 if (cols_to_go > 0)
2214 {
2215 form->curcol -= cols_to_go;
2216 form->begincol -= cols_to_go;
2217 res = E_OK;
2218 }
2219 }
2220 return (res);
2221}
2222/*----------------------------------------------------------------------------
2223 End of Horizontal scrolling helper routines
2224 --------------------------------------------------------------------------*/
2225
2226/*----------------------------------------------------------------------------
2227 Horizontal scrolling routines
2228 --------------------------------------------------------------------------*/
2229
2230/*---------------------------------------------------------------------------
2231| Facility : libnform
2232| Function : static int Horizontal_Scrolling(
2233| int (* const fct) (FORM *),
2234| FORM * form)
2235|
2236| Description : Performs the generic horizontal scrolling routines.
2237| This has to check for a single-line field.
2238|
2239| Return Values : Propagated error code from low-level driver calls
2240+--------------------------------------------------------------------------*/
2241static int
2242Horizontal_Scrolling(int (*const fct) (FORM *), FORM *form)
2243{
2244 if (Single_Line_Field(form->current))
2245 return fct(form);
2246 else
2247 return (E_REQUEST_DENIED);
2248}
2249
2250/*---------------------------------------------------------------------------
2251| Facility : libnform
2252| Function : static int HSC_Scroll_Char_Forward(FORM * form)
2253|
2254| Description : Scroll single-line field forward a character
2255|
2256| Return Values : E_OK - success
2257| E_REQUEST_DENIED - no data ahead
2258+--------------------------------------------------------------------------*/
2259static int
2260HSC_Scroll_Char_Forward(FORM *form)
2261{
Steve Kondikae271bc2015-11-15 02:50:53 +01002262 T((T_CALLED("HSC_Scroll_Char_Forward(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302263 returnCode(HSC_Generic(form, 1));
2264}
2265
2266/*---------------------------------------------------------------------------
2267| Facility : libnform
2268| Function : static int HSC_Scroll_Char_Backward(FORM * form)
2269|
2270| Description : Scroll single-line field backward a character
2271|
2272| Return Values : E_OK - success
2273| E_REQUEST_DENIED - no data behind
2274+--------------------------------------------------------------------------*/
2275static int
2276HSC_Scroll_Char_Backward(FORM *form)
2277{
Steve Kondikae271bc2015-11-15 02:50:53 +01002278 T((T_CALLED("HSC_Scroll_Char_Backward(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302279 returnCode(HSC_Generic(form, -1));
2280}
2281
2282/*---------------------------------------------------------------------------
2283| Facility : libnform
2284| Function : static int HSC_Horizontal_Line_Forward(FORM* form)
2285|
2286| Description : Scroll single-line field forward a line
2287|
2288| Return Values : E_OK - success
2289| E_REQUEST_DENIED - no data ahead
2290+--------------------------------------------------------------------------*/
2291static int
2292HSC_Horizontal_Line_Forward(FORM *form)
2293{
Steve Kondikae271bc2015-11-15 02:50:53 +01002294 T((T_CALLED("HSC_Horizontal_Line_Forward(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302295 returnCode(HSC_Generic(form, form->current->cols));
2296}
2297
2298/*---------------------------------------------------------------------------
2299| Facility : libnform
2300| Function : static int HSC_Horizontal_Half_Line_Forward(FORM* form)
2301|
2302| Description : Scroll single-line field forward half a line
2303|
2304| Return Values : E_OK - success
2305| E_REQUEST_DENIED - no data ahead
2306+--------------------------------------------------------------------------*/
2307static int
2308HSC_Horizontal_Half_Line_Forward(FORM *form)
2309{
Steve Kondikae271bc2015-11-15 02:50:53 +01002310 T((T_CALLED("HSC_Horizontal_Half_Line_Forward(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302311 returnCode(HSC_Generic(form, (form->current->cols + 1) / 2));
2312}
2313
2314/*---------------------------------------------------------------------------
2315| Facility : libnform
2316| Function : static int HSC_Horizontal_Line_Backward(FORM* form)
2317|
2318| Description : Scroll single-line field backward a line
2319|
2320| Return Values : E_OK - success
2321| E_REQUEST_DENIED - no data behind
2322+--------------------------------------------------------------------------*/
2323static int
2324HSC_Horizontal_Line_Backward(FORM *form)
2325{
Steve Kondikae271bc2015-11-15 02:50:53 +01002326 T((T_CALLED("HSC_Horizontal_Line_Backward(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302327 returnCode(HSC_Generic(form, -(form->current->cols)));
2328}
2329
2330/*---------------------------------------------------------------------------
2331| Facility : libnform
2332| Function : static int HSC_Horizontal_Half_Line_Backward(FORM* form)
2333|
2334| Description : Scroll single-line field backward half a line
2335|
2336| Return Values : E_OK - success
2337| E_REQUEST_DENIED - no data behind
2338+--------------------------------------------------------------------------*/
2339static int
2340HSC_Horizontal_Half_Line_Backward(FORM *form)
2341{
Steve Kondikae271bc2015-11-15 02:50:53 +01002342 T((T_CALLED("HSC_Horizontal_Half_Line_Backward(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302343 returnCode(HSC_Generic(form, -((form->current->cols + 1) / 2)));
2344}
2345
2346/*----------------------------------------------------------------------------
2347 End of Horizontal scrolling routines
2348 --------------------------------------------------------------------------*/
2349
2350/*----------------------------------------------------------------------------
2351 Helper routines for Field Editing
2352 --------------------------------------------------------------------------*/
2353
2354/*---------------------------------------------------------------------------
2355| Facility : libnform
2356| Function : static bool Is_There_Room_For_A_Line(FORM * form)
2357|
2358| Description : Check whether or not there is enough room in the
2359| buffer to enter a whole line.
2360|
2361| Return Values : TRUE - there is enough space
2362| FALSE - there is not enough space
2363+--------------------------------------------------------------------------*/
2364NCURSES_INLINE static bool
2365Is_There_Room_For_A_Line(FORM *form)
2366{
2367 FIELD *field = form->current;
2368 FIELD_CELL *begin_of_last_line, *s;
2369
2370 Synchronize_Buffer(form);
2371 begin_of_last_line = Address_Of_Row_In_Buffer(field, (field->drows - 1));
2372 s = After_End_Of_Data(begin_of_last_line, field->dcols);
2373 return ((s == begin_of_last_line) ? TRUE : FALSE);
2374}
2375
2376/*---------------------------------------------------------------------------
2377| Facility : libnform
2378| Function : static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
2379|
2380| Description : Checks whether or not there is room for a new character
2381| in the current line.
2382|
2383| Return Values : TRUE - there is room
2384| FALSE - there is not enough room (line full)
2385+--------------------------------------------------------------------------*/
2386NCURSES_INLINE static bool
2387Is_There_Room_For_A_Char_In_Line(FORM *form)
2388{
2389 int last_char_in_line;
2390
2391 wmove(form->w, form->currow, form->current->dcols - 1);
2392 last_char_in_line = (int)(winch(form->w) & A_CHARTEXT);
2393 wmove(form->w, form->currow, form->curcol);
2394 return (((last_char_in_line == form->current->pad) ||
2395 is_blank(last_char_in_line)) ? TRUE : FALSE);
2396}
2397
2398#define There_Is_No_Room_For_A_Char_In_Line(f) \
2399 !Is_There_Room_For_A_Char_In_Line(f)
2400
2401/*---------------------------------------------------------------------------
2402| Facility : libnform
2403| Function : static int Insert_String(
2404| FORM * form,
2405| int row,
2406| char *txt,
2407| int len )
2408|
2409| Description : Insert the 'len' characters beginning at pointer 'txt'
2410| into the 'row' of the 'form'. The insertion occurs
2411| on the beginning of the row, all other characters are
2412| moved to the right. After the text a pad character will
2413| be inserted to separate the text from the rest. If
2414| necessary the insertion moves characters on the next
2415| line to make place for the requested insertion string.
2416|
2417| Return Values : E_OK - success
2418| E_REQUEST_DENIED -
2419| E_SYSTEM_ERROR - system error
2420+--------------------------------------------------------------------------*/
2421static int
2422Insert_String(FORM *form, int row, FIELD_CELL *txt, int len)
2423{
2424 FIELD *field = form->current;
2425 FIELD_CELL *bp = Address_Of_Row_In_Buffer(field, row);
2426 int datalen = (int)(After_End_Of_Data(bp, field->dcols) - bp);
2427 int freelen = field->dcols - datalen;
2428 int requiredlen = len + 1;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302429 int result = E_REQUEST_DENIED;
2430
2431 if (freelen >= requiredlen)
2432 {
2433 wmove(form->w, row, 0);
2434 myINSNSTR(form->w, txt, len);
2435 wmove(form->w, row, len);
2436 myINSNSTR(form->w, &myBLANK, 1);
micky3879b9f5e72025-07-08 18:04:53 -04002437 result = E_OK;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302438 }
2439 else
2440 {
2441 /* we have to move characters on the next line. If we are on the
2442 last line this may work, if the field is growable */
2443 if ((row == (field->drows - 1)) && Growable(field))
2444 {
2445 if (!Field_Grown(field, 1))
2446 return (E_SYSTEM_ERROR);
2447 /* !!!Side-Effect : might be changed due to growth!!! */
2448 bp = Address_Of_Row_In_Buffer(field, row);
2449 }
2450
2451 if (row < (field->drows - 1))
2452 {
micky3879b9f5e72025-07-08 18:04:53 -04002453 FIELD_CELL *split;
2454
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302455 split =
2456 After_Last_Whitespace_Character(bp,
2457 (int)(Get_Start_Of_Data(bp
2458 + field->dcols
2459 - requiredlen,
2460 requiredlen)
2461 - bp));
2462 /* split points now to the first character of the portion of the
2463 line that must be moved to the next line */
2464 datalen = (int)(split - bp); /* + freelen has to stay on this line */
2465 freelen = field->dcols - (datalen + freelen); /* for the next line */
2466
2467 if ((result = Insert_String(form, row + 1, split, freelen)) == E_OK)
2468 {
2469 wmove(form->w, row, datalen);
2470 wclrtoeol(form->w);
2471 wmove(form->w, row, 0);
2472 myINSNSTR(form->w, txt, len);
2473 wmove(form->w, row, len);
2474 myINSNSTR(form->w, &myBLANK, 1);
2475 return E_OK;
2476 }
2477 }
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302478 }
micky3879b9f5e72025-07-08 18:04:53 -04002479 return (result);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302480}
2481
2482/*---------------------------------------------------------------------------
2483| Facility : libnform
2484| Function : static int Wrapping_Not_Necessary_Or_Wrapping_Ok(
2485| FORM * form)
2486|
2487| Description : If a character has been entered into a field, it may
2488| be that wrapping has to occur. This routine checks
2489| whether or not wrapping is required and if so, performs
2490| the wrapping.
2491|
2492| Return Values : E_OK - no wrapping required or wrapping
2493| was successful
2494| E_REQUEST_DENIED -
2495| E_SYSTEM_ERROR - some system error
2496+--------------------------------------------------------------------------*/
2497static int
2498Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM *form)
2499{
2500 FIELD *field = form->current;
2501 int result = E_REQUEST_DENIED;
2502 bool Last_Row = ((field->drows - 1) == form->currow);
2503
Steve Kondikae271bc2015-11-15 02:50:53 +01002504 if ((Field_Has_Option(field, O_WRAP)) && /* wrapping wanted */
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302505 (!Single_Line_Field(field)) && /* must be multi-line */
2506 (There_Is_No_Room_For_A_Char_In_Line(form)) && /* line is full */
2507 (!Last_Row || Growable(field))) /* there are more lines */
2508 {
2509 FIELD_CELL *bp;
2510 FIELD_CELL *split;
2511 int chars_to_be_wrapped;
2512 int chars_to_remain_on_line;
2513
2514 if (Last_Row)
2515 {
2516 /* the above logic already ensures, that in this case the field
2517 is growable */
2518 if (!Field_Grown(field, 1))
2519 return E_SYSTEM_ERROR;
2520 }
2521 bp = Address_Of_Current_Row_In_Buffer(form);
Steve Kondikae271bc2015-11-15 02:50:53 +01002522 Window_To_Buffer(form, field);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302523 split = After_Last_Whitespace_Character(bp, field->dcols);
2524 /* split points to the first character of the sequence to be brought
2525 on the next line */
2526 chars_to_remain_on_line = (int)(split - bp);
2527 chars_to_be_wrapped = field->dcols - chars_to_remain_on_line;
2528 if (chars_to_remain_on_line > 0)
2529 {
2530 if ((result = Insert_String(form, form->currow + 1, split,
2531 chars_to_be_wrapped)) == E_OK)
2532 {
2533 wmove(form->w, form->currow, chars_to_remain_on_line);
2534 wclrtoeol(form->w);
2535 if (form->curcol >= chars_to_remain_on_line)
2536 {
2537 form->currow++;
2538 form->curcol -= chars_to_remain_on_line;
2539 }
2540 return E_OK;
2541 }
2542 }
2543 else
2544 return E_OK;
2545 if (result != E_OK)
2546 {
2547 DeleteChar(form);
Steve Kondikae271bc2015-11-15 02:50:53 +01002548 Window_To_Buffer(form, field);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302549 result = E_REQUEST_DENIED;
2550 }
2551 }
2552 else
2553 result = E_OK; /* wrapping was not necessary */
2554 return (result);
2555}
2556
2557/*----------------------------------------------------------------------------
2558 Field Editing routines
2559 --------------------------------------------------------------------------*/
2560
2561/*---------------------------------------------------------------------------
2562| Facility : libnform
2563| Function : static int Field_Editing(
2564| int (* const fct) (FORM *),
2565| FORM * form)
2566|
2567| Description : Generic routine for field editing requests. The driver
2568| routines are only called for editable fields, the
2569| _WINDOW_MODIFIED flag is set if editing occurred.
2570| This is somewhat special due to the overload semantics
2571| of the NEW_LINE and DEL_PREV requests.
2572|
2573| Return Values : Error code from low level drivers.
2574+--------------------------------------------------------------------------*/
2575static int
2576Field_Editing(int (*const fct) (FORM *), FORM *form)
2577{
2578 int res = E_REQUEST_DENIED;
2579
2580 /* We have to deal here with the specific case of the overloaded
2581 behavior of New_Line and Delete_Previous requests.
2582 They may end up in navigational requests if we are on the first
2583 character in a field. But navigation is also allowed on non-
2584 editable fields.
2585 */
2586 if ((fct == FE_Delete_Previous) &&
Steve Kondikae271bc2015-11-15 02:50:53 +01002587 ((unsigned)form->opts & O_BS_OVERLOAD) &&
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302588 First_Position_In_Current_Field(form))
2589 {
2590 res = Inter_Field_Navigation(FN_Previous_Field, form);
2591 }
2592 else
2593 {
2594 if (fct == FE_New_Line)
2595 {
Steve Kondikae271bc2015-11-15 02:50:53 +01002596 if (((unsigned)form->opts & O_NL_OVERLOAD) &&
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302597 First_Position_In_Current_Field(form))
2598 {
2599 res = Inter_Field_Navigation(FN_Next_Field, form);
2600 }
2601 else
2602 /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */
2603 res = fct(form);
2604 }
2605 else
2606 {
2607 /* From now on, everything must be editable */
Steve Kondikae271bc2015-11-15 02:50:53 +01002608 if ((unsigned)form->current->opts & O_EDIT)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302609 {
2610 res = fct(form);
2611 if (res == E_OK)
Steve Kondikae271bc2015-11-15 02:50:53 +01002612 SetStatus(form, _WINDOW_MODIFIED);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302613 }
2614 }
2615 }
2616 return res;
2617}
2618
2619/*---------------------------------------------------------------------------
2620| Facility : libnform
2621| Function : static int FE_New_Line(FORM * form)
2622|
2623| Description : Perform a new line request. This is rather complex
2624| compared to other routines in this code due to the
2625| rather difficult to understand description in the
2626| manuals.
2627|
2628| Return Values : E_OK - success
2629| E_REQUEST_DENIED - new line not allowed
2630| E_SYSTEM_ERROR - system error
2631+--------------------------------------------------------------------------*/
2632static int
2633FE_New_Line(FORM *form)
2634{
2635 FIELD *field = form->current;
2636 FIELD_CELL *bp, *t;
2637 bool Last_Row = ((field->drows - 1) == form->currow);
2638
Steve Kondikae271bc2015-11-15 02:50:53 +01002639 T((T_CALLED("FE_New_Line(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302640 if (form->status & _OVLMODE)
2641 {
2642 if (Last_Row &&
2643 (!(Growable(field) && !Single_Line_Field(field))))
2644 {
Steve Kondikae271bc2015-11-15 02:50:53 +01002645 if (!((unsigned)form->opts & O_NL_OVERLOAD))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302646 returnCode(E_REQUEST_DENIED);
2647 wmove(form->w, form->currow, form->curcol);
2648 wclrtoeol(form->w);
2649 /* we have to set this here, although it is also
2650 handled in the generic routine. The reason is,
2651 that FN_Next_Field may fail, but the form is
2652 definitively changed */
Steve Kondikae271bc2015-11-15 02:50:53 +01002653 SetStatus(form, _WINDOW_MODIFIED);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302654 returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2655 }
2656 else
2657 {
2658 if (Last_Row && !Field_Grown(field, 1))
2659 {
2660 /* N.B.: due to the logic in the 'if', LastRow==TRUE
2661 means here that the field is growable and not
2662 a single-line field */
2663 returnCode(E_SYSTEM_ERROR);
2664 }
2665 wmove(form->w, form->currow, form->curcol);
2666 wclrtoeol(form->w);
2667 form->currow++;
2668 form->curcol = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +01002669 SetStatus(form, _WINDOW_MODIFIED);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302670 returnCode(E_OK);
2671 }
2672 }
2673 else
2674 {
2675 /* Insert Mode */
2676 if (Last_Row &&
2677 !(Growable(field) && !Single_Line_Field(field)))
2678 {
Steve Kondikae271bc2015-11-15 02:50:53 +01002679 if (!((unsigned)form->opts & O_NL_OVERLOAD))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302680 returnCode(E_REQUEST_DENIED);
2681 returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2682 }
2683 else
2684 {
2685 bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);
2686
2687 if (!(May_Do_It || Growable(field)))
2688 returnCode(E_REQUEST_DENIED);
2689 if (!May_Do_It && !Field_Grown(field, 1))
2690 returnCode(E_SYSTEM_ERROR);
2691
2692 bp = Address_Of_Current_Position_In_Buffer(form);
2693 t = After_End_Of_Data(bp, field->dcols - form->curcol);
2694 wmove(form->w, form->currow, form->curcol);
2695 wclrtoeol(form->w);
2696 form->currow++;
2697 form->curcol = 0;
2698 wmove(form->w, form->currow, form->curcol);
2699 winsertln(form->w);
2700 myADDNSTR(form->w, bp, (int)(t - bp));
Steve Kondikae271bc2015-11-15 02:50:53 +01002701 SetStatus(form, _WINDOW_MODIFIED);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302702 returnCode(E_OK);
2703 }
2704 }
2705}
2706
2707/*---------------------------------------------------------------------------
2708| Facility : libnform
2709| Function : static int FE_Insert_Character(FORM * form)
2710|
2711| Description : Insert blank character at the cursor position
2712|
2713| Return Values : E_OK
2714| E_REQUEST_DENIED
2715+--------------------------------------------------------------------------*/
2716static int
2717FE_Insert_Character(FORM *form)
2718{
2719 FIELD *field = form->current;
2720 int result = E_REQUEST_DENIED;
2721
Steve Kondikae271bc2015-11-15 02:50:53 +01002722 T((T_CALLED("FE_Insert_Character(%p)"), (void *)form));
2723 if (Check_Char(form, field, field->type, (int)C_BLANK,
2724 (TypeArgument *)(field->arg)))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302725 {
2726 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
2727
2728 if (There_Is_Room ||
2729 ((Single_Line_Field(field) && Growable(field))))
2730 {
2731 if (!There_Is_Room && !Field_Grown(field, 1))
2732 result = E_SYSTEM_ERROR;
2733 else
2734 {
2735 winsch(form->w, (chtype)C_BLANK);
2736 result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);
2737 }
2738 }
2739 }
2740 returnCode(result);
2741}
2742
2743/*---------------------------------------------------------------------------
2744| Facility : libnform
2745| Function : static int FE_Insert_Line(FORM * form)
2746|
2747| Description : Insert a blank line at the cursor position
2748|
2749| Return Values : E_OK - success
2750| E_REQUEST_DENIED - line can not be inserted
2751+--------------------------------------------------------------------------*/
2752static int
2753FE_Insert_Line(FORM *form)
2754{
2755 FIELD *field = form->current;
2756 int result = E_REQUEST_DENIED;
2757
Steve Kondikae271bc2015-11-15 02:50:53 +01002758 T((T_CALLED("FE_Insert_Line(%p)"), (void *)form));
2759 if (Check_Char(form, field,
2760 field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302761 {
2762 bool Maybe_Done = (form->currow != (field->drows - 1)) &&
2763 Is_There_Room_For_A_Line(form);
2764
2765 if (!Single_Line_Field(field) &&
2766 (Maybe_Done || Growable(field)))
2767 {
2768 if (!Maybe_Done && !Field_Grown(field, 1))
2769 result = E_SYSTEM_ERROR;
2770 else
2771 {
2772 form->curcol = 0;
2773 winsertln(form->w);
2774 result = E_OK;
2775 }
2776 }
2777 }
2778 returnCode(result);
2779}
2780
2781/*---------------------------------------------------------------------------
2782| Facility : libnform
2783| Function : static int FE_Delete_Character(FORM * form)
2784|
2785| Description : Delete character at the cursor position
2786|
2787| Return Values : E_OK - success
2788+--------------------------------------------------------------------------*/
2789static int
2790FE_Delete_Character(FORM *form)
2791{
Steve Kondikae271bc2015-11-15 02:50:53 +01002792 T((T_CALLED("FE_Delete_Character(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302793 DeleteChar(form);
2794 returnCode(E_OK);
2795}
2796
2797/*---------------------------------------------------------------------------
2798| Facility : libnform
2799| Function : static int FE_Delete_Previous(FORM * form)
2800|
2801| Description : Delete character before cursor. Again this is a rather
2802| difficult piece compared to others due to the overloading
2803| semantics of backspace.
2804| N.B.: The case of overloaded BS on first field position
2805| is already handled in the generic routine.
2806|
2807| Return Values : E_OK - success
2808| E_REQUEST_DENIED - Character can't be deleted
2809+--------------------------------------------------------------------------*/
2810static int
2811FE_Delete_Previous(FORM *form)
2812{
2813 FIELD *field = form->current;
2814
Steve Kondikae271bc2015-11-15 02:50:53 +01002815 T((T_CALLED("FE_Delete_Previous(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302816 if (First_Position_In_Current_Field(form))
2817 returnCode(E_REQUEST_DENIED);
2818
2819 if ((--(form->curcol)) < 0)
2820 {
2821 FIELD_CELL *this_line, *prev_line, *prev_end, *this_end;
2822 int this_row = form->currow;
2823
2824 form->curcol++;
2825 if (form->status & _OVLMODE)
2826 returnCode(E_REQUEST_DENIED);
2827
2828 prev_line = Address_Of_Row_In_Buffer(field, (form->currow - 1));
2829 this_line = Address_Of_Row_In_Buffer(field, (form->currow));
2830 Synchronize_Buffer(form);
2831 prev_end = After_End_Of_Data(prev_line, field->dcols);
2832 this_end = After_End_Of_Data(this_line, field->dcols);
2833 if ((int)(this_end - this_line) >
2834 (field->cols - (int)(prev_end - prev_line)))
2835 returnCode(E_REQUEST_DENIED);
2836 wmove(form->w, form->currow, form->curcol);
2837 wdeleteln(form->w);
2838 Adjust_Cursor_Position(form, prev_end);
2839 /*
2840 * If we did not really move to the previous line, help the user a
2841 * little. It is however a little inconsistent. Normally, when
2842 * backspacing around the point where text wraps to a new line in a
2843 * multi-line form, we absorb one keystroke for the wrapping point. That
2844 * is consistent with SVr4 forms. However, SVr4 does not allow typing
2845 * into the last column of the field, and requires the user to enter a
2846 * newline to move to the next line. Therefore it can consistently eat
2847 * that keystroke. Since ncurses allows the last column, it wraps
2848 * automatically (given the proper options). But we cannot eat the
2849 * keystroke to back over the wrapping point, since that would put the
2850 * cursor past the end of the form field. In this case, just delete the
2851 * character at the end of the field.
2852 */
2853 if (form->currow == this_row && this_row > 0)
2854 {
2855 form->currow -= 1;
2856 form->curcol = field->dcols - 1;
2857 DeleteChar(form);
2858 }
2859 else
2860 {
2861 wmove(form->w, form->currow, form->curcol);
2862 myADDNSTR(form->w, this_line, (int)(this_end - this_line));
2863 }
2864 }
2865 else
2866 {
2867 DeleteChar(form);
2868 }
2869 returnCode(E_OK);
2870}
2871
2872/*---------------------------------------------------------------------------
2873| Facility : libnform
2874| Function : static int FE_Delete_Line(FORM * form)
2875|
2876| Description : Delete line at cursor position.
2877|
2878| Return Values : E_OK - success
2879+--------------------------------------------------------------------------*/
2880static int
2881FE_Delete_Line(FORM *form)
2882{
Steve Kondikae271bc2015-11-15 02:50:53 +01002883 T((T_CALLED("FE_Delete_Line(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302884 form->curcol = 0;
2885 wdeleteln(form->w);
2886 returnCode(E_OK);
2887}
2888
2889/*---------------------------------------------------------------------------
2890| Facility : libnform
2891| Function : static int FE_Delete_Word(FORM * form)
2892|
2893| Description : Delete word at cursor position
2894|
2895| Return Values : E_OK - success
2896| E_REQUEST_DENIED - failure
2897+--------------------------------------------------------------------------*/
2898static int
2899FE_Delete_Word(FORM *form)
2900{
2901 FIELD *field = form->current;
2902 FIELD_CELL *bp = Address_Of_Current_Row_In_Buffer(form);
2903 FIELD_CELL *ep = bp + field->dcols;
2904 FIELD_CELL *cp = bp + form->curcol;
2905 FIELD_CELL *s;
2906
Steve Kondikae271bc2015-11-15 02:50:53 +01002907 T((T_CALLED("FE_Delete_Word(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302908 Synchronize_Buffer(form);
2909 if (ISBLANK(*cp))
2910 returnCode(E_REQUEST_DENIED); /* not in word */
2911
2912 /* move cursor to begin of word and erase to end of screen-line */
2913 Adjust_Cursor_Position(form,
2914 After_Last_Whitespace_Character(bp, form->curcol));
2915 wmove(form->w, form->currow, form->curcol);
2916 wclrtoeol(form->w);
2917
2918 /* skip over word in buffer */
2919 s = Get_First_Whitespace_Character(cp, (int)(ep - cp));
2920 /* to begin of next word */
2921 s = Get_Start_Of_Data(s, (int)(ep - s));
2922 if ((s != cp) && !ISBLANK(*s))
2923 {
2924 /* copy remaining line to window */
2925 myADDNSTR(form->w, s, (int)(s - After_End_Of_Data(s, (int)(ep - s))));
2926 }
2927 returnCode(E_OK);
2928}
2929
2930/*---------------------------------------------------------------------------
2931| Facility : libnform
2932| Function : static int FE_Clear_To_End_Of_Line(FORM * form)
2933|
2934| Description : Clear to end of current line.
2935|
2936| Return Values : E_OK - success
2937+--------------------------------------------------------------------------*/
2938static int
2939FE_Clear_To_End_Of_Line(FORM *form)
2940{
Steve Kondikae271bc2015-11-15 02:50:53 +01002941 T((T_CALLED("FE_Clear_To_End_Of_Line(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302942 wmove(form->w, form->currow, form->curcol);
2943 wclrtoeol(form->w);
2944 returnCode(E_OK);
2945}
2946
2947/*---------------------------------------------------------------------------
2948| Facility : libnform
2949| Function : static int FE_Clear_To_End_Of_Field(FORM * form)
2950|
2951| Description : Clear to end of field.
2952|
2953| Return Values : E_OK - success
2954+--------------------------------------------------------------------------*/
2955static int
2956FE_Clear_To_End_Of_Field(FORM *form)
2957{
Steve Kondikae271bc2015-11-15 02:50:53 +01002958 T((T_CALLED("FE_Clear_To_End_Of_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302959 wmove(form->w, form->currow, form->curcol);
2960 wclrtobot(form->w);
2961 returnCode(E_OK);
2962}
2963
2964/*---------------------------------------------------------------------------
2965| Facility : libnform
2966| Function : static int FE_Clear_Field(FORM * form)
2967|
2968| Description : Clear entire field.
2969|
2970| Return Values : E_OK - success
2971+--------------------------------------------------------------------------*/
2972static int
2973FE_Clear_Field(FORM *form)
2974{
Steve Kondikae271bc2015-11-15 02:50:53 +01002975 T((T_CALLED("FE_Clear_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05302976 form->currow = form->curcol = 0;
2977 werase(form->w);
2978 returnCode(E_OK);
2979}
2980/*----------------------------------------------------------------------------
2981 END of Field Editing routines
2982 --------------------------------------------------------------------------*/
2983
2984/*----------------------------------------------------------------------------
2985 Edit Mode routines
2986 --------------------------------------------------------------------------*/
2987
2988/*---------------------------------------------------------------------------
2989| Facility : libnform
2990| Function : static int EM_Overlay_Mode(FORM * form)
2991|
2992| Description : Switch to overlay mode.
2993|
2994| Return Values : E_OK - success
2995+--------------------------------------------------------------------------*/
2996static int
2997EM_Overlay_Mode(FORM *form)
2998{
Steve Kondikae271bc2015-11-15 02:50:53 +01002999 T((T_CALLED("EM_Overlay_Mode(%p)"), (void *)form));
3000 SetStatus(form, _OVLMODE);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303001 returnCode(E_OK);
3002}
3003
3004/*---------------------------------------------------------------------------
3005| Facility : libnform
3006| Function : static int EM_Insert_Mode(FORM * form)
3007|
3008| Description : Switch to insert mode
3009|
3010| Return Values : E_OK - success
3011+--------------------------------------------------------------------------*/
3012static int
3013EM_Insert_Mode(FORM *form)
3014{
Steve Kondikae271bc2015-11-15 02:50:53 +01003015 T((T_CALLED("EM_Insert_Mode(%p)"), (void *)form));
3016 ClrStatus(form, _OVLMODE);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303017 returnCode(E_OK);
3018}
3019
3020/*----------------------------------------------------------------------------
3021 END of Edit Mode routines
3022 --------------------------------------------------------------------------*/
3023
3024/*----------------------------------------------------------------------------
3025 Helper routines for Choice Requests
3026 --------------------------------------------------------------------------*/
3027
3028/*---------------------------------------------------------------------------
3029| Facility : libnform
Steve Kondikae271bc2015-11-15 02:50:53 +01003030| Function : static bool Next_Choice(FORM * form,
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303031| FIELDTYPE * typ,
3032| FIELD * field,
3033| TypeArgument *argp)
3034|
3035| Description : Get the next field choice. For linked types this is
3036| done recursively.
3037|
3038| Return Values : TRUE - next choice successfully retrieved
3039| FALSE - couldn't retrieve next choice
3040+--------------------------------------------------------------------------*/
3041static bool
Steve Kondikae271bc2015-11-15 02:50:53 +01003042Next_Choice(FORM *form, FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303043{
3044 if (!typ || !(typ->status & _HAS_CHOICE))
3045 return FALSE;
3046
3047 if (typ->status & _LINKED_TYPE)
3048 {
3049 assert(argp);
3050 return (
Steve Kondikae271bc2015-11-15 02:50:53 +01003051 Next_Choice(form, typ->left, field, argp->left) ||
3052 Next_Choice(form, typ->right, field, argp->right));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303053 }
3054 else
3055 {
Steve Kondikae271bc2015-11-15 02:50:53 +01003056#if NCURSES_INTEROP_FUNCS
3057 assert(typ->enum_next.onext);
3058 if (typ->status & _GENERIC)
3059 return typ->enum_next.gnext(form, field, (void *)argp);
3060 else
3061 return typ->enum_next.onext(field, (void *)argp);
3062#else
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303063 assert(typ->next);
3064 return typ->next(field, (void *)argp);
Steve Kondikae271bc2015-11-15 02:50:53 +01003065#endif
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303066 }
3067}
3068
3069/*---------------------------------------------------------------------------
3070| Facility : libnform
Steve Kondikae271bc2015-11-15 02:50:53 +01003071| Function : static bool Previous_Choice(FORM * form,
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303072| FIELDTYPE * typ,
3073| FIELD * field,
3074| TypeArgument *argp)
3075|
3076| Description : Get the previous field choice. For linked types this
3077| is done recursively.
3078|
3079| Return Values : TRUE - previous choice successfully retrieved
3080| FALSE - couldn't retrieve previous choice
3081+--------------------------------------------------------------------------*/
3082static bool
Steve Kondikae271bc2015-11-15 02:50:53 +01003083Previous_Choice(FORM *form, FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303084{
3085 if (!typ || !(typ->status & _HAS_CHOICE))
3086 return FALSE;
3087
3088 if (typ->status & _LINKED_TYPE)
3089 {
3090 assert(argp);
3091 return (
Steve Kondikae271bc2015-11-15 02:50:53 +01003092 Previous_Choice(form, typ->left, field, argp->left) ||
3093 Previous_Choice(form, typ->right, field, argp->right));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303094 }
3095 else
3096 {
Steve Kondikae271bc2015-11-15 02:50:53 +01003097#if NCURSES_INTEROP_FUNCS
3098 assert(typ->enum_prev.oprev);
3099 if (typ->status & _GENERIC)
3100 return typ->enum_prev.gprev(form, field, (void *)argp);
3101 else
3102 return typ->enum_prev.oprev(field, (void *)argp);
3103#else
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303104 assert(typ->prev);
3105 return typ->prev(field, (void *)argp);
Steve Kondikae271bc2015-11-15 02:50:53 +01003106#endif
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303107 }
3108}
3109/*----------------------------------------------------------------------------
3110 End of Helper routines for Choice Requests
3111 --------------------------------------------------------------------------*/
3112
3113/*----------------------------------------------------------------------------
3114 Routines for Choice Requests
3115 --------------------------------------------------------------------------*/
3116
3117/*---------------------------------------------------------------------------
3118| Facility : libnform
3119| Function : static int CR_Next_Choice(FORM * form)
3120|
3121| Description : Get the next field choice.
3122|
3123| Return Values : E_OK - success
3124| E_REQUEST_DENIED - next choice couldn't be retrieved
3125+--------------------------------------------------------------------------*/
3126static int
3127CR_Next_Choice(FORM *form)
3128{
3129 FIELD *field = form->current;
3130
Steve Kondikae271bc2015-11-15 02:50:53 +01003131 T((T_CALLED("CR_Next_Choice(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303132 Synchronize_Buffer(form);
Steve Kondikae271bc2015-11-15 02:50:53 +01003133 returnCode((Next_Choice(form, field->type, field, (TypeArgument *)(field->arg)))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303134 ? E_OK
3135 : E_REQUEST_DENIED);
3136}
3137
3138/*---------------------------------------------------------------------------
3139| Facility : libnform
3140| Function : static int CR_Previous_Choice(FORM * form)
3141|
3142| Description : Get the previous field choice.
3143|
3144| Return Values : E_OK - success
3145| E_REQUEST_DENIED - prev. choice couldn't be retrieved
3146+--------------------------------------------------------------------------*/
3147static int
3148CR_Previous_Choice(FORM *form)
3149{
3150 FIELD *field = form->current;
3151
Steve Kondikae271bc2015-11-15 02:50:53 +01003152 T((T_CALLED("CR_Previous_Choice(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303153 Synchronize_Buffer(form);
Steve Kondikae271bc2015-11-15 02:50:53 +01003154 returnCode((Previous_Choice(form, field->type, field, (TypeArgument *)(field->arg)))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303155 ? E_OK
3156 : E_REQUEST_DENIED);
3157}
3158/*----------------------------------------------------------------------------
3159 End of Routines for Choice Requests
3160 --------------------------------------------------------------------------*/
3161
3162/*----------------------------------------------------------------------------
3163 Helper routines for Field Validations.
3164 --------------------------------------------------------------------------*/
3165
3166/*---------------------------------------------------------------------------
3167| Facility : libnform
Steve Kondikae271bc2015-11-15 02:50:53 +01003168| Function : static bool Check_Field(FORM* form,
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303169| FIELDTYPE * typ,
3170| FIELD * field,
3171| TypeArgument * argp)
3172|
3173| Description : Check the field according to its fieldtype and its
3174| actual arguments. For linked fieldtypes this is done
3175| recursively.
3176|
3177| Return Values : TRUE - field is valid
3178| FALSE - field is invalid.
3179+--------------------------------------------------------------------------*/
3180static bool
Steve Kondikae271bc2015-11-15 02:50:53 +01003181Check_Field(FORM *form, FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303182{
3183 if (typ)
3184 {
Steve Kondikae271bc2015-11-15 02:50:53 +01003185 if (Field_Has_Option(field, O_NULLOK))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303186 {
3187 FIELD_CELL *bp = field->buf;
3188
3189 assert(bp);
3190 while (ISBLANK(*bp))
3191 {
3192 bp++;
3193 }
3194 if (CharOf(*bp) == 0)
3195 return TRUE;
3196 }
3197
3198 if (typ->status & _LINKED_TYPE)
3199 {
3200 assert(argp);
3201 return (
Steve Kondikae271bc2015-11-15 02:50:53 +01003202 Check_Field(form, typ->left, field, argp->left) ||
3203 Check_Field(form, typ->right, field, argp->right));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303204 }
3205 else
3206 {
Steve Kondikae271bc2015-11-15 02:50:53 +01003207#if NCURSES_INTEROP_FUNCS
3208 if (typ->fieldcheck.ofcheck)
3209 {
3210 if (typ->status & _GENERIC)
3211 return typ->fieldcheck.gfcheck(form, field, (void *)argp);
3212 else
3213 return typ->fieldcheck.ofcheck(field, (void *)argp);
3214 }
3215#else
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303216 if (typ->fcheck)
3217 return typ->fcheck(field, (void *)argp);
Steve Kondikae271bc2015-11-15 02:50:53 +01003218#endif
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303219 }
3220 }
3221 return TRUE;
3222}
3223
3224/*---------------------------------------------------------------------------
3225| Facility : libnform
3226| Function : bool _nc_Internal_Validation(FORM * form )
3227|
3228| Description : Validate the current field of the form.
3229|
3230| Return Values : TRUE - field is valid
3231| FALSE - field is invalid
3232+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -04003233FORM_EXPORT(bool)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303234_nc_Internal_Validation(FORM *form)
3235{
3236 FIELD *field;
3237
3238 field = form->current;
3239
3240 Synchronize_Buffer(form);
3241 if ((form->status & _FCHECK_REQUIRED) ||
Steve Kondikae271bc2015-11-15 02:50:53 +01003242 (!(Field_Has_Option(field, O_PASSOK))))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303243 {
Steve Kondikae271bc2015-11-15 02:50:53 +01003244 if (!Check_Field(form, field->type, field, (TypeArgument *)(field->arg)))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303245 return FALSE;
Steve Kondikae271bc2015-11-15 02:50:53 +01003246 ClrStatus(form, _FCHECK_REQUIRED);
3247 SetStatus(field, _CHANGED);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303248 Synchronize_Linked_Fields(field);
3249 }
3250 return TRUE;
3251}
3252/*----------------------------------------------------------------------------
3253 End of Helper routines for Field Validations.
3254 --------------------------------------------------------------------------*/
3255
3256/*----------------------------------------------------------------------------
3257 Routines for Field Validation.
3258 --------------------------------------------------------------------------*/
3259
3260/*---------------------------------------------------------------------------
3261| Facility : libnform
3262| Function : static int FV_Validation(FORM * form)
3263|
3264| Description : Validate the current field of the form.
3265|
3266| Return Values : E_OK - field valid
3267| E_INVALID_FIELD - field not valid
3268+--------------------------------------------------------------------------*/
3269static int
3270FV_Validation(FORM *form)
3271{
Steve Kondikae271bc2015-11-15 02:50:53 +01003272 T((T_CALLED("FV_Validation(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303273 if (_nc_Internal_Validation(form))
3274 returnCode(E_OK);
3275 else
3276 returnCode(E_INVALID_FIELD);
3277}
3278/*----------------------------------------------------------------------------
3279 End of routines for Field Validation.
3280 --------------------------------------------------------------------------*/
3281
3282/*----------------------------------------------------------------------------
3283 Helper routines for Inter-Field Navigation
3284 --------------------------------------------------------------------------*/
3285
3286/*---------------------------------------------------------------------------
3287| Facility : libnform
3288| Function : static FIELD *Next_Field_On_Page(FIELD * field)
3289|
3290| Description : Get the next field after the given field on the current
3291| page. The order of fields is the one defined by the
micky3879b9f5e72025-07-08 18:04:53 -04003292| field's array. Only visible and active fields are
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303293| counted.
3294|
3295| Return Values : Pointer to the next field.
3296+--------------------------------------------------------------------------*/
3297NCURSES_INLINE static FIELD *
3298Next_Field_On_Page(FIELD *field)
3299{
3300 FORM *form = field->form;
3301 FIELD **field_on_page = &form->field[field->index];
3302 FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3303 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3304
3305 do
3306 {
3307 field_on_page =
3308 (field_on_page == last_on_page) ? first_on_page : field_on_page + 1;
3309 if (Field_Is_Selectable(*field_on_page))
3310 break;
3311 }
3312 while (field != (*field_on_page));
3313 return (*field_on_page);
3314}
3315
3316/*---------------------------------------------------------------------------
3317| Facility : libnform
3318| Function : FIELD* _nc_First_Active_Field(FORM * form)
3319|
3320| Description : Get the first active field on the current page,
3321| if there are such. If there are none, get the first
3322| visible field on the page. If there are also none,
3323| we return the first field on page and hope the best.
3324|
3325| Return Values : Pointer to calculated field.
3326+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -04003327FORM_EXPORT(FIELD *)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303328_nc_First_Active_Field(FORM *form)
3329{
3330 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3331 FIELD *proposed = Next_Field_On_Page(*last_on_page);
3332
3333 if (proposed == *last_on_page)
3334 {
3335 /* there might be the special situation, where there is no
3336 active and visible field on the current page. We then select
3337 the first visible field on this readonly page
3338 */
3339 if (Field_Is_Not_Selectable(proposed))
3340 {
3341 FIELD **field = &form->field[proposed->index];
3342 FIELD **first = &form->field[form->page[form->curpage].pmin];
3343
3344 do
3345 {
3346 field = (field == last_on_page) ? first : field + 1;
Steve Kondikae271bc2015-11-15 02:50:53 +01003347 if (Field_Has_Option(*field, O_VISIBLE))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303348 break;
3349 }
3350 while (proposed != (*field));
3351
3352 proposed = *field;
3353
Steve Kondikae271bc2015-11-15 02:50:53 +01003354 if ((proposed == *last_on_page) &&
3355 !((unsigned)proposed->opts & O_VISIBLE))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303356 {
3357 /* This means, there is also no visible field on the page.
3358 So we propose the first one and hope the very best...
3359 Some very clever user has designed a readonly and invisible
3360 page on this form.
3361 */
3362 proposed = *first;
3363 }
3364 }
3365 }
3366 return (proposed);
3367}
3368
3369/*---------------------------------------------------------------------------
3370| Facility : libnform
3371| Function : static FIELD *Previous_Field_On_Page(FIELD * field)
3372|
3373| Description : Get the previous field before the given field on the
3374| current page. The order of fields is the one defined by
micky3879b9f5e72025-07-08 18:04:53 -04003375| the field's array. Only visible and active fields are
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303376| counted.
3377|
3378| Return Values : Pointer to the previous field.
3379+--------------------------------------------------------------------------*/
3380NCURSES_INLINE static FIELD *
3381Previous_Field_On_Page(FIELD *field)
3382{
3383 FORM *form = field->form;
3384 FIELD **field_on_page = &form->field[field->index];
3385 FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3386 FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3387
3388 do
3389 {
3390 field_on_page =
3391 (field_on_page == first_on_page) ? last_on_page : field_on_page - 1;
3392 if (Field_Is_Selectable(*field_on_page))
3393 break;
3394 }
3395 while (field != (*field_on_page));
3396
3397 return (*field_on_page);
3398}
3399
3400/*---------------------------------------------------------------------------
3401| Facility : libnform
3402| Function : static FIELD *Sorted_Next_Field(FIELD * field)
3403|
3404| Description : Get the next field after the given field on the current
3405| page. The order of fields is the one defined by the
3406| (row,column) geometry, rows are major.
3407|
3408| Return Values : Pointer to the next field.
3409+--------------------------------------------------------------------------*/
3410NCURSES_INLINE static FIELD *
3411Sorted_Next_Field(FIELD *field)
3412{
3413 FIELD *field_on_page = field;
3414
3415 do
3416 {
3417 field_on_page = field_on_page->snext;
3418 if (Field_Is_Selectable(field_on_page))
3419 break;
3420 }
3421 while (field_on_page != field);
3422
3423 return (field_on_page);
3424}
3425
3426/*---------------------------------------------------------------------------
3427| Facility : libnform
3428| Function : static FIELD *Sorted_Previous_Field(FIELD * field)
3429|
3430| Description : Get the previous field before the given field on the
3431| current page. The order of fields is the one defined
3432| by the (row,column) geometry, rows are major.
3433|
3434| Return Values : Pointer to the previous field.
3435+--------------------------------------------------------------------------*/
3436NCURSES_INLINE static FIELD *
3437Sorted_Previous_Field(FIELD *field)
3438{
3439 FIELD *field_on_page = field;
3440
3441 do
3442 {
3443 field_on_page = field_on_page->sprev;
3444 if (Field_Is_Selectable(field_on_page))
3445 break;
3446 }
3447 while (field_on_page != field);
3448
3449 return (field_on_page);
3450}
3451
3452/*---------------------------------------------------------------------------
3453| Facility : libnform
3454| Function : static FIELD *Left_Neighbor_Field(FIELD * field)
3455|
3456| Description : Get the left neighbor of the field on the same line
3457| and the same page. Cycles through the line.
3458|
3459| Return Values : Pointer to left neighbor field.
3460+--------------------------------------------------------------------------*/
3461NCURSES_INLINE static FIELD *
3462Left_Neighbor_Field(FIELD *field)
3463{
3464 FIELD *field_on_page = field;
3465
3466 /* For a field that has really a left neighbor, the while clause
3467 immediately fails and the loop is left, positioned at the right
3468 neighbor. Otherwise we cycle backwards through the sorted field list
3469 until we enter the same line (from the right end).
3470 */
3471 do
3472 {
3473 field_on_page = Sorted_Previous_Field(field_on_page);
3474 }
3475 while (field_on_page->frow != field->frow);
3476
3477 return (field_on_page);
3478}
3479
3480/*---------------------------------------------------------------------------
3481| Facility : libnform
3482| Function : static FIELD *Right_Neighbor_Field(FIELD * field)
3483|
3484| Description : Get the right neighbor of the field on the same line
3485| and the same page.
3486|
3487| Return Values : Pointer to right neighbor field.
3488+--------------------------------------------------------------------------*/
3489NCURSES_INLINE static FIELD *
3490Right_Neighbor_Field(FIELD *field)
3491{
3492 FIELD *field_on_page = field;
3493
3494 /* See the comments on Left_Neighbor_Field to understand how it works */
3495 do
3496 {
3497 field_on_page = Sorted_Next_Field(field_on_page);
3498 }
3499 while (field_on_page->frow != field->frow);
3500
3501 return (field_on_page);
3502}
3503
3504/*---------------------------------------------------------------------------
3505| Facility : libnform
3506| Function : static FIELD *Upper_Neighbor_Field(FIELD * field)
3507|
3508| Description : Because of the row-major nature of sorting the fields,
micky3879b9f5e72025-07-08 18:04:53 -04003509| it is more difficult to define what the upper neighbor
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303510| field really means. We define that it must be on a
3511| 'previous' line (cyclic order!) and is the rightmost
micky3879b9f5e72025-07-08 18:04:53 -04003512| field lying on the left side of the given field. If
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303513| this set is empty, we take the first field on the line.
3514|
3515| Return Values : Pointer to the upper neighbor field.
3516+--------------------------------------------------------------------------*/
3517static FIELD *
3518Upper_Neighbor_Field(FIELD *field)
3519{
3520 FIELD *field_on_page = field;
3521 int frow = field->frow;
3522 int fcol = field->fcol;
3523
3524 /* Walk back to the 'previous' line. The second term in the while clause
3525 just guarantees that we stop if we cycled through the line because
3526 there might be no 'previous' line if the page has just one line.
3527 */
3528 do
3529 {
3530 field_on_page = Sorted_Previous_Field(field_on_page);
3531 }
3532 while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3533
3534 if (field_on_page->frow != frow)
3535 {
3536 /* We really found a 'previous' line. We are positioned at the
3537 rightmost field on this line */
3538 frow = field_on_page->frow;
3539
3540 /* We walk to the left as long as we are really right of the
3541 field. */
3542 while (field_on_page->frow == frow && field_on_page->fcol > fcol)
3543 field_on_page = Sorted_Previous_Field(field_on_page);
3544
3545 /* If we wrapped, just go to the right which is the first field on
3546 the row */
3547 if (field_on_page->frow != frow)
3548 field_on_page = Sorted_Next_Field(field_on_page);
3549 }
3550
3551 return (field_on_page);
3552}
3553
3554/*---------------------------------------------------------------------------
3555| Facility : libnform
3556| Function : static FIELD *Down_Neighbor_Field(FIELD * field)
3557|
3558| Description : Because of the row-major nature of sorting the fields,
micky3879b9f5e72025-07-08 18:04:53 -04003559| it is more difficult to define what the down neighbor
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303560| field really means. We define that it must be on a
3561| 'next' line (cyclic order!) and is the leftmost
3562| field laying on the right side of the given field. If
3563| this set is empty, we take the last field on the line.
3564|
3565| Return Values : Pointer to the upper neighbor field.
3566+--------------------------------------------------------------------------*/
3567static FIELD *
3568Down_Neighbor_Field(FIELD *field)
3569{
3570 FIELD *field_on_page = field;
3571 int frow = field->frow;
3572 int fcol = field->fcol;
3573
3574 /* Walk forward to the 'next' line. The second term in the while clause
3575 just guarantees that we stop if we cycled through the line because
3576 there might be no 'next' line if the page has just one line.
3577 */
3578 do
3579 {
3580 field_on_page = Sorted_Next_Field(field_on_page);
3581 }
3582 while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3583
3584 if (field_on_page->frow != frow)
3585 {
3586 /* We really found a 'next' line. We are positioned at the rightmost
3587 field on this line */
3588 frow = field_on_page->frow;
3589
3590 /* We walk to the right as long as we are really left of the
3591 field. */
3592 while (field_on_page->frow == frow && field_on_page->fcol < fcol)
3593 field_on_page = Sorted_Next_Field(field_on_page);
3594
3595 /* If we wrapped, just go to the left which is the last field on
3596 the row */
3597 if (field_on_page->frow != frow)
3598 field_on_page = Sorted_Previous_Field(field_on_page);
3599 }
3600
3601 return (field_on_page);
3602}
3603
3604/*----------------------------------------------------------------------------
3605 Inter-Field Navigation routines
3606 --------------------------------------------------------------------------*/
3607
3608/*---------------------------------------------------------------------------
3609| Facility : libnform
3610| Function : static int Inter_Field_Navigation(
3611| int (* const fct) (FORM *),
3612| FORM * form)
3613|
3614| Description : Generic behavior for changing the current field, the
3615| field is left and a new field is entered. So the field
3616| must be validated and the field init/term hooks must
3617| be called.
3618|
3619| Return Values : E_OK - success
3620| E_INVALID_FIELD - field is invalid
3621| some other - error from subordinate call
3622+--------------------------------------------------------------------------*/
3623static int
3624Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form)
3625{
3626 int res;
3627
3628 if (!_nc_Internal_Validation(form))
3629 res = E_INVALID_FIELD;
3630 else
3631 {
3632 Call_Hook(form, fieldterm);
3633 res = fct(form);
3634 Call_Hook(form, fieldinit);
3635 }
3636 return res;
3637}
3638
3639/*---------------------------------------------------------------------------
3640| Facility : libnform
3641| Function : static int FN_Next_Field(FORM * form)
3642|
3643| Description : Move to the next field on the current page of the form
3644|
3645| Return Values : E_OK - success
3646| != E_OK - error from subordinate call
3647+--------------------------------------------------------------------------*/
3648static int
3649FN_Next_Field(FORM *form)
3650{
Steve Kondikae271bc2015-11-15 02:50:53 +01003651 T((T_CALLED("FN_Next_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303652 returnCode(_nc_Set_Current_Field(form,
3653 Next_Field_On_Page(form->current)));
3654}
3655
3656/*---------------------------------------------------------------------------
3657| Facility : libnform
3658| Function : static int FN_Previous_Field(FORM * form)
3659|
3660| Description : Move to the previous field on the current page of the
3661| form
3662|
3663| Return Values : E_OK - success
3664| != E_OK - error from subordinate call
3665+--------------------------------------------------------------------------*/
3666static int
3667FN_Previous_Field(FORM *form)
3668{
Steve Kondikae271bc2015-11-15 02:50:53 +01003669 T((T_CALLED("FN_Previous_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303670 returnCode(_nc_Set_Current_Field(form,
3671 Previous_Field_On_Page(form->current)));
3672}
3673
3674/*---------------------------------------------------------------------------
3675| Facility : libnform
3676| Function : static int FN_First_Field(FORM * form)
3677|
3678| Description : Move to the first field on the current page of the form
3679|
3680| Return Values : E_OK - success
3681| != E_OK - error from subordinate call
3682+--------------------------------------------------------------------------*/
3683static int
3684FN_First_Field(FORM *form)
3685{
Steve Kondikae271bc2015-11-15 02:50:53 +01003686 T((T_CALLED("FN_First_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303687 returnCode(_nc_Set_Current_Field(form,
3688 Next_Field_On_Page(form->field[form->page[form->curpage].pmax])));
3689}
3690
3691/*---------------------------------------------------------------------------
3692| Facility : libnform
3693| Function : static int FN_Last_Field(FORM * form)
3694|
3695| Description : Move to the last field on the current page of the form
3696|
3697| Return Values : E_OK - success
3698| != E_OK - error from subordinate call
3699+--------------------------------------------------------------------------*/
3700static int
3701FN_Last_Field(FORM *form)
3702{
Steve Kondikae271bc2015-11-15 02:50:53 +01003703 T((T_CALLED("FN_Last_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303704 returnCode(
3705 _nc_Set_Current_Field(form,
3706 Previous_Field_On_Page(form->field[form->page[form->curpage].pmin])));
3707}
3708
3709/*---------------------------------------------------------------------------
3710| Facility : libnform
3711| Function : static int FN_Sorted_Next_Field(FORM * form)
3712|
3713| Description : Move to the sorted next field on the current page
3714| of the form.
3715|
3716| Return Values : E_OK - success
3717| != E_OK - error from subordinate call
3718+--------------------------------------------------------------------------*/
3719static int
3720FN_Sorted_Next_Field(FORM *form)
3721{
Steve Kondikae271bc2015-11-15 02:50:53 +01003722 T((T_CALLED("FN_Sorted_Next_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303723 returnCode(_nc_Set_Current_Field(form,
3724 Sorted_Next_Field(form->current)));
3725}
3726
3727/*---------------------------------------------------------------------------
3728| Facility : libnform
3729| Function : static int FN_Sorted_Previous_Field(FORM * form)
3730|
3731| Description : Move to the sorted previous field on the current page
3732| of the form.
3733|
3734| Return Values : E_OK - success
3735| != E_OK - error from subordinate call
3736+--------------------------------------------------------------------------*/
3737static int
3738FN_Sorted_Previous_Field(FORM *form)
3739{
Steve Kondikae271bc2015-11-15 02:50:53 +01003740 T((T_CALLED("FN_Sorted_Previous_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303741 returnCode(_nc_Set_Current_Field(form,
3742 Sorted_Previous_Field(form->current)));
3743}
3744
3745/*---------------------------------------------------------------------------
3746| Facility : libnform
3747| Function : static int FN_Sorted_First_Field(FORM * form)
3748|
3749| Description : Move to the sorted first field on the current page
3750| of the form.
3751|
3752| Return Values : E_OK - success
3753| != E_OK - error from subordinate call
3754+--------------------------------------------------------------------------*/
3755static int
3756FN_Sorted_First_Field(FORM *form)
3757{
Steve Kondikae271bc2015-11-15 02:50:53 +01003758 T((T_CALLED("FN_Sorted_First_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303759 returnCode(_nc_Set_Current_Field(form,
3760 Sorted_Next_Field(form->field[form->page[form->curpage].smax])));
3761}
3762
3763/*---------------------------------------------------------------------------
3764| Facility : libnform
3765| Function : static int FN_Sorted_Last_Field(FORM * form)
3766|
3767| Description : Move to the sorted last field on the current page
3768| of the form.
3769|
3770| Return Values : E_OK - success
3771| != E_OK - error from subordinate call
3772+--------------------------------------------------------------------------*/
3773static int
3774FN_Sorted_Last_Field(FORM *form)
3775{
Steve Kondikae271bc2015-11-15 02:50:53 +01003776 T((T_CALLED("FN_Sorted_Last_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303777 returnCode(_nc_Set_Current_Field(form,
3778 Sorted_Previous_Field(form->field[form->page[form->curpage].smin])));
3779}
3780
3781/*---------------------------------------------------------------------------
3782| Facility : libnform
3783| Function : static int FN_Left_Field(FORM * form)
3784|
3785| Description : Get the field on the left of the current field on the
3786| same line and the same page. Cycles through the line.
3787|
3788| Return Values : E_OK - success
3789| != E_OK - error from subordinate call
3790+--------------------------------------------------------------------------*/
3791static int
3792FN_Left_Field(FORM *form)
3793{
Steve Kondikae271bc2015-11-15 02:50:53 +01003794 T((T_CALLED("FN_Left_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303795 returnCode(_nc_Set_Current_Field(form,
3796 Left_Neighbor_Field(form->current)));
3797}
3798
3799/*---------------------------------------------------------------------------
3800| Facility : libnform
3801| Function : static int FN_Right_Field(FORM * form)
3802|
3803| Description : Get the field on the right of the current field on the
3804| same line and the same page. Cycles through the line.
3805|
3806| Return Values : E_OK - success
3807| != E_OK - error from subordinate call
3808+--------------------------------------------------------------------------*/
3809static int
3810FN_Right_Field(FORM *form)
3811{
Steve Kondikae271bc2015-11-15 02:50:53 +01003812 T((T_CALLED("FN_Right_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303813 returnCode(_nc_Set_Current_Field(form,
3814 Right_Neighbor_Field(form->current)));
3815}
3816
3817/*---------------------------------------------------------------------------
3818| Facility : libnform
3819| Function : static int FN_Up_Field(FORM * form)
3820|
3821| Description : Get the upper neighbor of the current field. This
3822| cycles through the page. See the comments of the
3823| Upper_Neighbor_Field function to understand how
3824| 'upper' is defined.
3825|
3826| Return Values : E_OK - success
3827| != E_OK - error from subordinate call
3828+--------------------------------------------------------------------------*/
3829static int
3830FN_Up_Field(FORM *form)
3831{
Steve Kondikae271bc2015-11-15 02:50:53 +01003832 T((T_CALLED("FN_Up_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303833 returnCode(_nc_Set_Current_Field(form,
3834 Upper_Neighbor_Field(form->current)));
3835}
3836
3837/*---------------------------------------------------------------------------
3838| Facility : libnform
3839| Function : static int FN_Down_Field(FORM * form)
3840|
3841| Description : Get the down neighbor of the current field. This
3842| cycles through the page. See the comments of the
3843| Down_Neighbor_Field function to understand how
3844| 'down' is defined.
3845|
3846| Return Values : E_OK - success
3847| != E_OK - error from subordinate call
3848+--------------------------------------------------------------------------*/
3849static int
3850FN_Down_Field(FORM *form)
3851{
Steve Kondikae271bc2015-11-15 02:50:53 +01003852 T((T_CALLED("FN_Down_Field(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303853 returnCode(_nc_Set_Current_Field(form,
3854 Down_Neighbor_Field(form->current)));
3855}
3856/*----------------------------------------------------------------------------
3857 END of Field Navigation routines
3858 --------------------------------------------------------------------------*/
3859
3860/*----------------------------------------------------------------------------
3861 Helper routines for Page Navigation
3862 --------------------------------------------------------------------------*/
3863
3864/*---------------------------------------------------------------------------
3865| Facility : libnform
3866| Function : int _nc_Set_Form_Page(FORM * form,
3867| int page,
3868| FIELD * field)
3869|
3870| Description : Make the given page number the current page and make
3871| the given field the current field on the page. If
3872| for the field NULL is given, make the first field on
3873| the page the current field. The routine acts only
3874| if the requested page is not the current page.
3875|
3876| Return Values : E_OK - success
3877| != E_OK - error from subordinate call
3878| E_BAD_ARGUMENT - invalid field pointer
3879| E_SYSTEM_ERROR - some severe basic error
3880+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -04003881FORM_EXPORT(int)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303882_nc_Set_Form_Page(FORM *form, int page, FIELD *field)
3883{
3884 int res = E_OK;
3885
3886 if ((form->curpage != page))
3887 {
3888 FIELD *last_field, *field_on_page;
3889
3890 werase(Get_Form_Window(form));
Steve Kondikae271bc2015-11-15 02:50:53 +01003891 form->curpage = (short)page;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303892 last_field = field_on_page = form->field[form->page[page].smin];
3893 do
3894 {
Steve Kondikae271bc2015-11-15 02:50:53 +01003895 if ((unsigned)field_on_page->opts & O_VISIBLE)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303896 if ((res = Display_Field(field_on_page)) != E_OK)
3897 return (res);
3898 field_on_page = field_on_page->snext;
3899 }
3900 while (field_on_page != last_field);
3901
3902 if (field)
3903 res = _nc_Set_Current_Field(form, field);
3904 else
3905 /* N.B.: we don't encapsulate this by Inter_Field_Navigation(),
3906 because this is already executed in a page navigation
3907 context that contains field navigation
3908 */
3909 res = FN_First_Field(form);
3910 }
3911 return (res);
3912}
3913
3914/*---------------------------------------------------------------------------
3915| Facility : libnform
3916| Function : static int Next_Page_Number(const FORM * form)
3917|
3918| Description : Calculate the page number following the current page
3919| number. This cycles if the highest page number is
3920| reached.
3921|
3922| Return Values : The next page number
3923+--------------------------------------------------------------------------*/
3924NCURSES_INLINE static int
3925Next_Page_Number(const FORM *form)
3926{
3927 return (form->curpage + 1) % form->maxpage;
3928}
3929
3930/*---------------------------------------------------------------------------
3931| Facility : libnform
3932| Function : static int Previous_Page_Number(const FORM * form)
3933|
3934| Description : Calculate the page number before the current page
3935| number. This cycles if the first page number is
3936| reached.
3937|
3938| Return Values : The previous page number
3939+--------------------------------------------------------------------------*/
3940NCURSES_INLINE static int
3941Previous_Page_Number(const FORM *form)
3942{
3943 return (form->curpage != 0 ? form->curpage - 1 : form->maxpage - 1);
3944}
3945
3946/*----------------------------------------------------------------------------
3947 Page Navigation routines
3948 --------------------------------------------------------------------------*/
3949
3950/*---------------------------------------------------------------------------
3951| Facility : libnform
3952| Function : static int Page_Navigation(
3953| int (* const fct) (FORM *),
3954| FORM * form)
3955|
3956| Description : Generic behavior for changing a page. This means
3957| that the field is left and a new field is entered.
3958| So the field must be validated and the field init/term
3959| hooks must be called. Because also the page is changed,
micky3879b9f5e72025-07-08 18:04:53 -04003960| the form's init/term hooks must be called also.
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303961|
3962| Return Values : E_OK - success
3963| E_INVALID_FIELD - field is invalid
3964| some other - error from subordinate call
3965+--------------------------------------------------------------------------*/
3966static int
3967Page_Navigation(int (*const fct) (FORM *), FORM *form)
3968{
3969 int res;
3970
3971 if (!_nc_Internal_Validation(form))
3972 res = E_INVALID_FIELD;
3973 else
3974 {
3975 Call_Hook(form, fieldterm);
3976 Call_Hook(form, formterm);
3977 res = fct(form);
3978 Call_Hook(form, forminit);
3979 Call_Hook(form, fieldinit);
3980 }
3981 return res;
3982}
3983
3984/*---------------------------------------------------------------------------
3985| Facility : libnform
3986| Function : static int PN_Next_Page(FORM * form)
3987|
3988| Description : Move to the next page of the form
3989|
3990| Return Values : E_OK - success
3991| != E_OK - error from subordinate call
3992+--------------------------------------------------------------------------*/
3993static int
3994PN_Next_Page(FORM *form)
3995{
Steve Kondikae271bc2015-11-15 02:50:53 +01003996 T((T_CALLED("PN_Next_Page(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05303997 returnCode(_nc_Set_Form_Page(form, Next_Page_Number(form), (FIELD *)0));
3998}
3999
4000/*---------------------------------------------------------------------------
4001| Facility : libnform
4002| Function : static int PN_Previous_Page(FORM * form)
4003|
4004| Description : Move to the previous page of the form
4005|
4006| Return Values : E_OK - success
4007| != E_OK - error from subordinate call
4008+--------------------------------------------------------------------------*/
4009static int
4010PN_Previous_Page(FORM *form)
4011{
Steve Kondikae271bc2015-11-15 02:50:53 +01004012 T((T_CALLED("PN_Previous_Page(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304013 returnCode(_nc_Set_Form_Page(form, Previous_Page_Number(form), (FIELD *)0));
4014}
4015
4016/*---------------------------------------------------------------------------
4017| Facility : libnform
4018| Function : static int PN_First_Page(FORM * form)
4019|
4020| Description : Move to the first page of the form
4021|
4022| Return Values : E_OK - success
4023| != E_OK - error from subordinate call
4024+--------------------------------------------------------------------------*/
4025static int
4026PN_First_Page(FORM *form)
4027{
Steve Kondikae271bc2015-11-15 02:50:53 +01004028 T((T_CALLED("PN_First_Page(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304029 returnCode(_nc_Set_Form_Page(form, 0, (FIELD *)0));
4030}
4031
4032/*---------------------------------------------------------------------------
4033| Facility : libnform
4034| Function : static int PN_Last_Page(FORM * form)
4035|
4036| Description : Move to the last page of the form
4037|
4038| Return Values : E_OK - success
4039| != E_OK - error from subordinate call
4040+--------------------------------------------------------------------------*/
4041static int
4042PN_Last_Page(FORM *form)
4043{
Steve Kondikae271bc2015-11-15 02:50:53 +01004044 T((T_CALLED("PN_Last_Page(%p)"), (void *)form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304045 returnCode(_nc_Set_Form_Page(form, form->maxpage - 1, (FIELD *)0));
4046}
4047
4048/*----------------------------------------------------------------------------
4049 END of Field Navigation routines
4050 --------------------------------------------------------------------------*/
4051
4052/*----------------------------------------------------------------------------
4053 Helper routines for the core form driver.
4054 --------------------------------------------------------------------------*/
4055
Steve Kondikae271bc2015-11-15 02:50:53 +01004056# if USE_WIDEC_SUPPORT
4057/*---------------------------------------------------------------------------
4058| Facility : libnform
4059| Function : static int Data_Entry_w(FORM * form, wchar_t c)
4060|
4061| Description : Enter the wide character c into at the current
4062| position of the current field of the form.
4063|
4064| Return Values : E_OK - success
4065| E_REQUEST_DENIED - driver could not process the request
4066| E_SYSTEM_ERROR -
4067+--------------------------------------------------------------------------*/
4068static int
4069Data_Entry_w(FORM *form, wchar_t c)
4070{
4071 FIELD *field = form->current;
4072 int result = E_REQUEST_DENIED;
4073
4074 T((T_CALLED("Data_Entry(%p,%s)"), (void *)form, _tracechtype((chtype)c)));
4075 if ((Field_Has_Option(field, O_EDIT))
4076#if FIX_FORM_INACTIVE_BUG
4077 && (Field_Has_Option(field, O_ACTIVE))
4078#endif
4079 )
4080 {
4081 wchar_t given[2];
4082 cchar_t temp_ch;
4083
4084 given[0] = c;
micky3879b9f5e72025-07-08 18:04:53 -04004085 given[1] = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +01004086 setcchar(&temp_ch, given, 0, 0, (void *)0);
4087 if ((Field_Has_Option(field, O_BLANK)) &&
4088 First_Position_In_Current_Field(form) &&
4089 !(form->status & _FCHECK_REQUIRED) &&
4090 !(form->status & _WINDOW_MODIFIED))
4091 werase(form->w);
4092
4093 if (form->status & _OVLMODE)
4094 {
4095 wadd_wch(form->w, &temp_ch);
4096 }
4097 else
4098 /* no _OVLMODE */
4099 {
4100 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
4101
4102 if (!(There_Is_Room ||
4103 ((Single_Line_Field(field) && Growable(field)))))
4104 RETURN(E_REQUEST_DENIED);
4105
4106 if (!There_Is_Room && !Field_Grown(field, 1))
4107 RETURN(E_SYSTEM_ERROR);
4108
4109 wins_wch(form->w, &temp_ch);
4110 }
4111
4112 if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
4113 {
4114 bool End_Of_Field = (((field->drows - 1) == form->currow) &&
4115 ((field->dcols - 1) == form->curcol));
4116
4117 form->status |= _WINDOW_MODIFIED;
4118 if (End_Of_Field && !Growable(field) && (Field_Has_Option(field, O_AUTOSKIP)))
4119 result = Inter_Field_Navigation(FN_Next_Field, form);
4120 else
4121 {
4122 if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
4123 result = E_SYSTEM_ERROR;
4124 else
4125 {
4126 /*
4127 * We have just added a byte to the form field. It may have
4128 * been part of a multibyte character. If it was, the
4129 * addch_used field is nonzero and we should not try to move
4130 * to a new column.
4131 */
4132 if (WINDOW_EXT(form->w, addch_used) == 0)
4133 IFN_Next_Character(form);
4134
4135 result = E_OK;
4136 }
4137 }
4138 }
4139 }
4140 RETURN(result);
4141}
4142# endif
4143
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304144/*---------------------------------------------------------------------------
4145| Facility : libnform
4146| Function : static int Data_Entry(FORM * form,int c)
4147|
4148| Description : Enter character c into at the current position of the
4149| current field of the form.
4150|
4151| Return Values : E_OK - success
4152| E_REQUEST_DENIED - driver could not process the request
4153| E_SYSTEM_ERROR -
4154+--------------------------------------------------------------------------*/
4155static int
4156Data_Entry(FORM *form, int c)
4157{
4158 FIELD *field = form->current;
4159 int result = E_REQUEST_DENIED;
4160
Steve Kondikae271bc2015-11-15 02:50:53 +01004161 T((T_CALLED("Data_Entry(%p,%s)"), (void *)form, _tracechtype((chtype)c)));
4162 if ((Field_Has_Option(field, O_EDIT))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304163#if FIX_FORM_INACTIVE_BUG
Steve Kondikae271bc2015-11-15 02:50:53 +01004164 && (Field_Has_Option(field, O_ACTIVE))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304165#endif
4166 )
4167 {
Steve Kondikae271bc2015-11-15 02:50:53 +01004168 if ((Field_Has_Option(field, O_BLANK)) &&
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304169 First_Position_In_Current_Field(form) &&
4170 !(form->status & _FCHECK_REQUIRED) &&
4171 !(form->status & _WINDOW_MODIFIED))
4172 werase(form->w);
4173
4174 if (form->status & _OVLMODE)
4175 {
4176 waddch(form->w, (chtype)c);
4177 }
4178 else
4179 /* no _OVLMODE */
4180 {
4181 bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
4182
4183 if (!(There_Is_Room ||
4184 ((Single_Line_Field(field) && Growable(field)))))
4185 RETURN(E_REQUEST_DENIED);
4186
4187 if (!There_Is_Room && !Field_Grown(field, 1))
4188 RETURN(E_SYSTEM_ERROR);
4189
4190 winsch(form->w, (chtype)c);
4191 }
4192
4193 if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
4194 {
4195 bool End_Of_Field = (((field->drows - 1) == form->currow) &&
4196 ((field->dcols - 1) == form->curcol));
4197
micky3879b9f5e72025-07-08 18:04:53 -04004198 if (Field_Has_Option(field, O_EDGE_INSERT_STAY))
4199 move_after_insert = !!(form->curcol
4200 - form->begincol
4201 - field->cols
4202 + 1);
4203
Steve Kondikae271bc2015-11-15 02:50:53 +01004204 SetStatus(form, _WINDOW_MODIFIED);
4205 if (End_Of_Field && !Growable(field) && (Field_Has_Option(field, O_AUTOSKIP)))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304206 result = Inter_Field_Navigation(FN_Next_Field, form);
4207 else
4208 {
4209 if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
4210 result = E_SYSTEM_ERROR;
4211 else
4212 {
4213#if USE_WIDEC_SUPPORT
4214 /*
4215 * We have just added a byte to the form field. It may have
4216 * been part of a multibyte character. If it was, the
4217 * addch_used field is nonzero and we should not try to move
4218 * to a new column.
4219 */
4220 if (WINDOW_EXT(form->w, addch_used) == 0)
4221 IFN_Next_Character(form);
4222#else
4223 IFN_Next_Character(form);
4224#endif
4225 result = E_OK;
4226 }
4227 }
4228 }
4229 }
4230 RETURN(result);
4231}
4232
4233/* Structure to describe the binding of a request code to a function.
4234 The member keycode codes the request value as well as the generic
4235 routine to use for the request. The code for the generic routine
4236 is coded in the upper 16 Bits while the request code is coded in
4237 the lower 16 bits.
4238
4239 In terms of C++ you might think of a request as a class with a
4240 virtual method "perform". The different types of request are
4241 derived from this base class and overload (or not) the base class
4242 implementation of perform.
4243*/
4244typedef struct
4245{
4246 int keycode; /* must be at least 32 bit: hi:mode, lo: key */
4247 int (*cmd) (FORM *); /* low level driver routine for this key */
4248}
4249Binding_Info;
4250
4251/* You may see this is the class-id of the request type class */
4252#define ID_PN (0x00000000) /* Page navigation */
4253#define ID_FN (0x00010000) /* Inter-Field navigation */
4254#define ID_IFN (0x00020000) /* Intra-Field navigation */
4255#define ID_VSC (0x00030000) /* Vertical Scrolling */
4256#define ID_HSC (0x00040000) /* Horizontal Scrolling */
4257#define ID_FE (0x00050000) /* Field Editing */
4258#define ID_EM (0x00060000) /* Edit Mode */
4259#define ID_FV (0x00070000) /* Field Validation */
4260#define ID_CH (0x00080000) /* Choice */
4261#define ID_Mask (0xffff0000)
4262#define Key_Mask (0x0000ffff)
4263#define ID_Shft (16)
4264
4265/* This array holds all the Binding Infos */
4266/* *INDENT-OFF* */
4267static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =
4268{
4269 { REQ_NEXT_PAGE |ID_PN ,PN_Next_Page},
4270 { REQ_PREV_PAGE |ID_PN ,PN_Previous_Page},
4271 { REQ_FIRST_PAGE |ID_PN ,PN_First_Page},
4272 { REQ_LAST_PAGE |ID_PN ,PN_Last_Page},
4273
4274 { REQ_NEXT_FIELD |ID_FN ,FN_Next_Field},
4275 { REQ_PREV_FIELD |ID_FN ,FN_Previous_Field},
4276 { REQ_FIRST_FIELD |ID_FN ,FN_First_Field},
4277 { REQ_LAST_FIELD |ID_FN ,FN_Last_Field},
4278 { REQ_SNEXT_FIELD |ID_FN ,FN_Sorted_Next_Field},
4279 { REQ_SPREV_FIELD |ID_FN ,FN_Sorted_Previous_Field},
4280 { REQ_SFIRST_FIELD |ID_FN ,FN_Sorted_First_Field},
4281 { REQ_SLAST_FIELD |ID_FN ,FN_Sorted_Last_Field},
4282 { REQ_LEFT_FIELD |ID_FN ,FN_Left_Field},
4283 { REQ_RIGHT_FIELD |ID_FN ,FN_Right_Field},
4284 { REQ_UP_FIELD |ID_FN ,FN_Up_Field},
4285 { REQ_DOWN_FIELD |ID_FN ,FN_Down_Field},
4286
4287 { REQ_NEXT_CHAR |ID_IFN ,IFN_Next_Character},
4288 { REQ_PREV_CHAR |ID_IFN ,IFN_Previous_Character},
4289 { REQ_NEXT_LINE |ID_IFN ,IFN_Next_Line},
4290 { REQ_PREV_LINE |ID_IFN ,IFN_Previous_Line},
4291 { REQ_NEXT_WORD |ID_IFN ,IFN_Next_Word},
4292 { REQ_PREV_WORD |ID_IFN ,IFN_Previous_Word},
4293 { REQ_BEG_FIELD |ID_IFN ,IFN_Beginning_Of_Field},
4294 { REQ_END_FIELD |ID_IFN ,IFN_End_Of_Field},
4295 { REQ_BEG_LINE |ID_IFN ,IFN_Beginning_Of_Line},
4296 { REQ_END_LINE |ID_IFN ,IFN_End_Of_Line},
4297 { REQ_LEFT_CHAR |ID_IFN ,IFN_Left_Character},
4298 { REQ_RIGHT_CHAR |ID_IFN ,IFN_Right_Character},
4299 { REQ_UP_CHAR |ID_IFN ,IFN_Up_Character},
4300 { REQ_DOWN_CHAR |ID_IFN ,IFN_Down_Character},
4301
4302 { REQ_NEW_LINE |ID_FE ,FE_New_Line},
4303 { REQ_INS_CHAR |ID_FE ,FE_Insert_Character},
4304 { REQ_INS_LINE |ID_FE ,FE_Insert_Line},
4305 { REQ_DEL_CHAR |ID_FE ,FE_Delete_Character},
4306 { REQ_DEL_PREV |ID_FE ,FE_Delete_Previous},
4307 { REQ_DEL_LINE |ID_FE ,FE_Delete_Line},
4308 { REQ_DEL_WORD |ID_FE ,FE_Delete_Word},
4309 { REQ_CLR_EOL |ID_FE ,FE_Clear_To_End_Of_Line},
4310 { REQ_CLR_EOF |ID_FE ,FE_Clear_To_End_Of_Field},
4311 { REQ_CLR_FIELD |ID_FE ,FE_Clear_Field},
4312
4313 { REQ_OVL_MODE |ID_EM ,EM_Overlay_Mode},
4314 { REQ_INS_MODE |ID_EM ,EM_Insert_Mode},
4315
4316 { REQ_SCR_FLINE |ID_VSC ,VSC_Scroll_Line_Forward},
4317 { REQ_SCR_BLINE |ID_VSC ,VSC_Scroll_Line_Backward},
4318 { REQ_SCR_FPAGE |ID_VSC ,VSC_Scroll_Page_Forward},
4319 { REQ_SCR_BPAGE |ID_VSC ,VSC_Scroll_Page_Backward},
4320 { REQ_SCR_FHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Forward},
4321 { REQ_SCR_BHPAGE |ID_VSC ,VSC_Scroll_Half_Page_Backward},
4322
4323 { REQ_SCR_FCHAR |ID_HSC ,HSC_Scroll_Char_Forward},
4324 { REQ_SCR_BCHAR |ID_HSC ,HSC_Scroll_Char_Backward},
4325 { REQ_SCR_HFLINE |ID_HSC ,HSC_Horizontal_Line_Forward},
4326 { REQ_SCR_HBLINE |ID_HSC ,HSC_Horizontal_Line_Backward},
4327 { REQ_SCR_HFHALF |ID_HSC ,HSC_Horizontal_Half_Line_Forward},
4328 { REQ_SCR_HBHALF |ID_HSC ,HSC_Horizontal_Half_Line_Backward},
4329
4330 { REQ_VALIDATION |ID_FV ,FV_Validation},
4331
4332 { REQ_NEXT_CHOICE |ID_CH ,CR_Next_Choice},
4333 { REQ_PREV_CHOICE |ID_CH ,CR_Previous_Choice}
4334};
4335/* *INDENT-ON* */
4336
4337/*---------------------------------------------------------------------------
4338| Facility : libnform
4339| Function : int form_driver(FORM * form,int c)
4340|
4341| Description : This is the workhorse of the forms system. It checks
4342| to determine whether the character c is a request or
4343| data. If it is a request, the form driver executes
4344| the request and returns the result. If it is data
4345| (printable character), it enters the data into the
4346| current position in the current field. If it is not
4347| recognized, the form driver assumes it is an application
4348| defined command and returns E_UNKNOWN_COMMAND.
4349| Application defined command should be defined relative
4350| to MAX_FORM_COMMAND, the maximum value of a request.
4351|
4352| Return Values : E_OK - success
4353| E_SYSTEM_ERROR - system error
4354| E_BAD_ARGUMENT - an argument is incorrect
4355| E_NOT_POSTED - form is not posted
4356| E_INVALID_FIELD - field contents are invalid
4357| E_BAD_STATE - called from inside a hook routine
4358| E_REQUEST_DENIED - request failed
4359| E_NOT_CONNECTED - no fields are connected to the form
4360| E_UNKNOWN_COMMAND - command not known
4361+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -04004362FORM_EXPORT(int)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304363form_driver(FORM *form, int c)
4364{
micky3879b9f5e72025-07-08 18:04:53 -04004365 const Binding_Info *BI = (Binding_Info *)0;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304366 int res = E_UNKNOWN_COMMAND;
4367
micky3879b9f5e72025-07-08 18:04:53 -04004368 move_after_insert = TRUE;
4369
Steve Kondikae271bc2015-11-15 02:50:53 +01004370 T((T_CALLED("form_driver(%p,%d)"), (void *)form, c));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304371
4372 if (!form)
4373 RETURN(E_BAD_ARGUMENT);
4374
micky3879b9f5e72025-07-08 18:04:53 -04004375 if (!(form->field) || !(form->current))
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304376 RETURN(E_NOT_CONNECTED);
4377
4378 assert(form->page);
4379
4380 if (c == FIRST_ACTIVE_MAGIC)
4381 {
4382 form->current = _nc_First_Active_Field(form);
4383 RETURN(E_OK);
4384 }
4385
4386 assert(form->current &&
4387 form->current->buf &&
4388 (form->current->form == form)
4389 );
4390
4391 if (form->status & _IN_DRIVER)
4392 RETURN(E_BAD_STATE);
4393
4394 if (!(form->status & _POSTED))
4395 RETURN(E_NOT_POSTED);
4396
4397 if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4398 ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
Steve Kondikae271bc2015-11-15 02:50:53 +01004399 {
4400 TR(TRACE_CALLS, ("form_request %s", form_request_name(c)));
4401 BI = &(bindings[c - MIN_FORM_COMMAND]);
4402 }
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304403
4404 if (BI)
4405 {
4406 typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4407 static const Generic_Method Generic_Methods[] =
4408 {
4409 Page_Navigation, /* overloaded to call field&form hooks */
4410 Inter_Field_Navigation, /* overloaded to call field hooks */
4411 NULL, /* Intra-Field is generic */
4412 Vertical_Scrolling, /* Overloaded to check multi-line */
4413 Horizontal_Scrolling, /* Overloaded to check single-line */
4414 Field_Editing, /* Overloaded to mark modification */
4415 NULL, /* Edit Mode is generic */
4416 NULL, /* Field Validation is generic */
4417 NULL /* Choice Request is generic */
4418 };
4419 size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
micky3879b9f5e72025-07-08 18:04:53 -04004420 size_t method = (size_t)((BI->keycode >> ID_Shft) & 0xffff); /* see ID_Mask */
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304421
4422 if ((method >= nMethods) || !(BI->cmd))
4423 res = E_SYSTEM_ERROR;
4424 else
4425 {
4426 Generic_Method fct = Generic_Methods[method];
4427
4428 if (fct)
Steve Kondikae271bc2015-11-15 02:50:53 +01004429 {
4430 res = fct(BI->cmd, form);
4431 }
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304432 else
Steve Kondikae271bc2015-11-15 02:50:53 +01004433 {
4434 res = (BI->cmd) (form);
4435 }
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304436 }
4437 }
4438#ifdef NCURSES_MOUSE_VERSION
4439 else if (KEY_MOUSE == c)
4440 {
4441 MEVENT event;
Steve Kondikae271bc2015-11-15 02:50:53 +01004442 WINDOW *win = form->win ? form->win : StdScreen(Get_Form_Screen(form));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304443 WINDOW *sub = form->sub ? form->sub : win;
4444
4445 getmouse(&event);
4446 if ((event.bstate & (BUTTON1_CLICKED |
4447 BUTTON1_DOUBLE_CLICKED |
4448 BUTTON1_TRIPLE_CLICKED))
4449 && wenclose(win, event.y, event.x))
4450 { /* we react only if the click was in the userwin, that means
4451 * inside the form display area or at the decoration window.
4452 */
4453 int ry = event.y, rx = event.x; /* screen coordinates */
4454
4455 res = E_REQUEST_DENIED;
4456 if (mouse_trafo(&ry, &rx, FALSE))
4457 { /* rx, ry are now "curses" coordinates */
4458 if (ry < sub->_begy)
4459 { /* we clicked above the display region; this is
4460 * interpreted as "scroll up" request
4461 */
4462 if (event.bstate & BUTTON1_CLICKED)
4463 res = form_driver(form, REQ_PREV_FIELD);
4464 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4465 res = form_driver(form, REQ_PREV_PAGE);
4466 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4467 res = form_driver(form, REQ_FIRST_FIELD);
4468 }
4469 else if (ry > sub->_begy + sub->_maxy)
4470 { /* we clicked below the display region; this is
4471 * interpreted as "scroll down" request
4472 */
4473 if (event.bstate & BUTTON1_CLICKED)
4474 res = form_driver(form, REQ_NEXT_FIELD);
4475 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4476 res = form_driver(form, REQ_NEXT_PAGE);
4477 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4478 res = form_driver(form, REQ_LAST_FIELD);
4479 }
4480 else if (wenclose(sub, event.y, event.x))
4481 { /* Inside the area we try to find the hit item */
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304482 ry = event.y;
4483 rx = event.x;
4484 if (wmouse_trafo(sub, &ry, &rx, FALSE))
4485 {
4486 int min_field = form->page[form->curpage].pmin;
4487 int max_field = form->page[form->curpage].pmax;
micky3879b9f5e72025-07-08 18:04:53 -04004488 int i;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304489
4490 for (i = min_field; i <= max_field; ++i)
4491 {
4492 FIELD *field = form->field[i];
4493
4494 if (Field_Is_Selectable(field)
4495 && Field_encloses(field, ry, rx) == E_OK)
4496 {
4497 res = _nc_Set_Current_Field(form, field);
4498 if (res == E_OK)
4499 res = _nc_Position_Form_Cursor(form);
4500 if (res == E_OK
4501 && (event.bstate & BUTTON1_DOUBLE_CLICKED))
4502 res = E_UNKNOWN_COMMAND;
4503 break;
4504 }
4505 }
4506 }
4507 }
4508 }
4509 }
4510 else
4511 res = E_REQUEST_DENIED;
4512 }
4513#endif /* NCURSES_MOUSE_VERSION */
4514 else if (!(c & (~(int)MAX_REGULAR_CHARACTER)))
4515 {
4516 /*
4517 * If we're using 8-bit characters, iscntrl+isprint cover the whole set.
4518 * But with multibyte characters, there is a third possibility, i.e.,
4519 * parts of characters that build up into printable characters which are
4520 * not considered printable.
4521 *
4522 * FIXME: the wide-character branch should also use Check_Char().
4523 */
4524#if USE_WIDEC_SUPPORT
4525 if (!iscntrl(UChar(c)))
4526#else
4527 if (isprint(UChar(c)) &&
Steve Kondikae271bc2015-11-15 02:50:53 +01004528 Check_Char(form, form->current, form->current->type, c,
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304529 (TypeArgument *)(form->current->arg)))
4530#endif
4531 res = Data_Entry(form, c);
4532 }
4533 _nc_Refresh_Current_Field(form);
4534 RETURN(res);
4535}
4536
Steve Kondikae271bc2015-11-15 02:50:53 +01004537# if USE_WIDEC_SUPPORT
4538/*---------------------------------------------------------------------------
4539| Facility : libnform
4540| Function : int form_driver_w(FORM * form,int type,wchar_t c)
4541|
4542| Description : This is the workhorse of the forms system.
4543|
4544| Input is either a key code (request) or a wide char
4545| returned by e.g. get_wch (). The type must be passed
4546| as well,so that we are able to determine whether the char
4547| is a multibyte char or a request.
4548
4549| If it is a request, the form driver executes
4550| the request and returns the result. If it is data
4551| (printable character), it enters the data into the
4552| current position in the current field. If it is not
4553| recognized, the form driver assumes it is an application
4554| defined command and returns E_UNKNOWN_COMMAND.
4555| Application defined command should be defined relative
4556| to MAX_FORM_COMMAND, the maximum value of a request.
4557|
4558| Return Values : E_OK - success
4559| E_SYSTEM_ERROR - system error
4560| E_BAD_ARGUMENT - an argument is incorrect
4561| E_NOT_POSTED - form is not posted
4562| E_INVALID_FIELD - field contents are invalid
4563| E_BAD_STATE - called from inside a hook routine
4564| E_REQUEST_DENIED - request failed
4565| E_NOT_CONNECTED - no fields are connected to the form
4566| E_UNKNOWN_COMMAND - command not known
4567+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -04004568FORM_EXPORT(int)
Steve Kondikae271bc2015-11-15 02:50:53 +01004569form_driver_w(FORM *form, int type, wchar_t c)
4570{
micky3879b9f5e72025-07-08 18:04:53 -04004571 const Binding_Info *BI = (Binding_Info *)0;
Steve Kondikae271bc2015-11-15 02:50:53 +01004572 int res = E_UNKNOWN_COMMAND;
4573
4574 T((T_CALLED("form_driver(%p,%d)"), (void *)form, (int)c));
4575
4576 if (!form)
4577 RETURN(E_BAD_ARGUMENT);
4578
4579 if (!(form->field))
4580 RETURN(E_NOT_CONNECTED);
4581
4582 assert(form->page);
4583
4584 if (c == (wchar_t)FIRST_ACTIVE_MAGIC)
4585 {
4586 form->current = _nc_First_Active_Field(form);
4587 RETURN(E_OK);
4588 }
4589
4590 assert(form->current &&
4591 form->current->buf &&
4592 (form->current->form == form)
4593 );
4594
4595 if (form->status & _IN_DRIVER)
4596 RETURN(E_BAD_STATE);
4597
4598 if (!(form->status & _POSTED))
4599 RETURN(E_NOT_POSTED);
4600
4601 /* check if this is a keycode or a (wide) char */
4602 if (type == KEY_CODE_YES)
4603 {
4604 if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4605 ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4606 BI = &(bindings[c - MIN_FORM_COMMAND]);
4607 }
4608
4609 if (BI)
4610 {
4611 typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4612 static const Generic_Method Generic_Methods[] =
4613 {
4614 Page_Navigation, /* overloaded to call field&form hooks */
4615 Inter_Field_Navigation, /* overloaded to call field hooks */
4616 NULL, /* Intra-Field is generic */
4617 Vertical_Scrolling, /* Overloaded to check multi-line */
4618 Horizontal_Scrolling, /* Overloaded to check single-line */
4619 Field_Editing, /* Overloaded to mark modification */
4620 NULL, /* Edit Mode is generic */
4621 NULL, /* Field Validation is generic */
4622 NULL /* Choice Request is generic */
4623 };
4624 size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
micky3879b9f5e72025-07-08 18:04:53 -04004625 size_t method = (size_t)(BI->keycode >> ID_Shft) & 0xffff; /* see ID_Mask */
Steve Kondikae271bc2015-11-15 02:50:53 +01004626
4627 if ((method >= nMethods) || !(BI->cmd))
4628 res = E_SYSTEM_ERROR;
4629 else
4630 {
4631 Generic_Method fct = Generic_Methods[method];
4632
4633 if (fct)
4634 res = fct(BI->cmd, form);
4635 else
4636 res = (BI->cmd) (form);
4637 }
4638 }
4639#ifdef NCURSES_MOUSE_VERSION
4640 else if (KEY_MOUSE == c)
4641 {
4642 MEVENT event;
4643 WINDOW *win = form->win ? form->win : StdScreen(Get_Form_Screen(form));
4644 WINDOW *sub = form->sub ? form->sub : win;
4645
4646 getmouse(&event);
4647 if ((event.bstate & (BUTTON1_CLICKED |
4648 BUTTON1_DOUBLE_CLICKED |
4649 BUTTON1_TRIPLE_CLICKED))
4650 && wenclose(win, event.y, event.x))
4651 { /* we react only if the click was in the userwin, that means
4652 * inside the form display area or at the decoration window.
4653 */
4654 int ry = event.y, rx = event.x; /* screen coordinates */
4655
4656 res = E_REQUEST_DENIED;
4657 if (mouse_trafo(&ry, &rx, FALSE))
4658 { /* rx, ry are now "curses" coordinates */
4659 if (ry < sub->_begy)
4660 { /* we clicked above the display region; this is
4661 * interpreted as "scroll up" request
4662 */
4663 if (event.bstate & BUTTON1_CLICKED)
4664 res = form_driver(form, REQ_PREV_FIELD);
4665 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4666 res = form_driver(form, REQ_PREV_PAGE);
4667 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4668 res = form_driver(form, REQ_FIRST_FIELD);
4669 }
4670 else if (ry > sub->_begy + sub->_maxy)
4671 { /* we clicked below the display region; this is
4672 * interpreted as "scroll down" request
4673 */
4674 if (event.bstate & BUTTON1_CLICKED)
4675 res = form_driver(form, REQ_NEXT_FIELD);
4676 else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4677 res = form_driver(form, REQ_NEXT_PAGE);
4678 else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4679 res = form_driver(form, REQ_LAST_FIELD);
4680 }
4681 else if (wenclose(sub, event.y, event.x))
4682 { /* Inside the area we try to find the hit item */
Steve Kondikae271bc2015-11-15 02:50:53 +01004683 ry = event.y;
4684 rx = event.x;
4685 if (wmouse_trafo(sub, &ry, &rx, FALSE))
4686 {
4687 int min_field = form->page[form->curpage].pmin;
4688 int max_field = form->page[form->curpage].pmax;
micky3879b9f5e72025-07-08 18:04:53 -04004689 int i;
Steve Kondikae271bc2015-11-15 02:50:53 +01004690
4691 for (i = min_field; i <= max_field; ++i)
4692 {
4693 FIELD *field = form->field[i];
4694
4695 if (Field_Is_Selectable(field)
4696 && Field_encloses(field, ry, rx) == E_OK)
4697 {
4698 res = _nc_Set_Current_Field(form, field);
4699 if (res == E_OK)
4700 res = _nc_Position_Form_Cursor(form);
4701 if (res == E_OK
4702 && (event.bstate & BUTTON1_DOUBLE_CLICKED))
4703 res = E_UNKNOWN_COMMAND;
4704 break;
4705 }
4706 }
4707 }
4708 }
4709 }
4710 }
4711 else
4712 res = E_REQUEST_DENIED;
4713 }
4714#endif /* NCURSES_MOUSE_VERSION */
4715 else if (type == OK)
4716 {
4717 res = Data_Entry_w(form, c);
4718 }
4719
4720 _nc_Refresh_Current_Field(form);
4721 RETURN(res);
4722}
4723# endif /* USE_WIDEC_SUPPORT */
4724
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304725/*----------------------------------------------------------------------------
4726 Field-Buffer manipulation routines.
4727 The effects of setting a buffer are tightly coupled to the core of the form
4728 driver logic. This is especially true in the case of growable fields.
4729 So I don't separate this into a separate module.
4730 --------------------------------------------------------------------------*/
4731
4732/*---------------------------------------------------------------------------
4733| Facility : libnform
4734| Function : int set_field_buffer(FIELD *field,
4735| int buffer, char *value)
4736|
4737| Description : Set the given buffer of the field to the given value.
4738| Buffer 0 stores the displayed content of the field.
4739| For dynamic fields this may grow the fieldbuffers if
4740| the length of the value exceeds the current buffer
4741| length. For buffer 0 only printable values are allowed.
micky3879b9f5e72025-07-08 18:04:53 -04004742| For static fields, the value must not be zero terminated.
4743| It is copied up to the length of the buffer.
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304744|
4745| Return Values : E_OK - success
4746| E_BAD_ARGUMENT - invalid argument
4747| E_SYSTEM_ERROR - system error
4748+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -04004749FORM_EXPORT(int)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304750set_field_buffer(FIELD *field, int buffer, const char *value)
4751{
4752 FIELD_CELL *p;
4753 int res = E_OK;
Steve Kondikae271bc2015-11-15 02:50:53 +01004754 int i;
4755 int len;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304756
4757#if USE_WIDEC_SUPPORT
4758 FIELD_CELL *widevalue = 0;
4759#endif
4760
Steve Kondikae271bc2015-11-15 02:50:53 +01004761 T((T_CALLED("set_field_buffer(%p,%d,%s)"), (void *)field, buffer, _nc_visbuf(value)));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304762
4763 if (!field || !value || ((buffer < 0) || (buffer > field->nbuf)))
4764 RETURN(E_BAD_ARGUMENT);
4765
4766 len = Buffer_Length(field);
4767
4768 if (Growable(field))
4769 {
4770 /* for a growable field we must assume zero terminated strings, because
4771 somehow we have to detect the length of what should be copied.
4772 */
Steve Kondikae271bc2015-11-15 02:50:53 +01004773 int vlen = (int)strlen(value);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304774
4775 if (vlen > len)
4776 {
4777 if (!Field_Grown(field,
4778 (int)(1 + (vlen - len) / ((field->rows + field->nrow)
4779 * field->cols))))
4780 RETURN(E_SYSTEM_ERROR);
4781
Steve Kondikae271bc2015-11-15 02:50:53 +01004782#if !USE_WIDEC_SUPPORT
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304783 len = vlen;
Steve Kondikae271bc2015-11-15 02:50:53 +01004784#endif
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304785 }
4786 }
4787
4788 p = Address_Of_Nth_Buffer(field, buffer);
4789
4790#if USE_WIDEC_SUPPORT
4791 /*
4792 * Use addstr's logic for converting a string to an array of cchar_t's.
4793 * There should be a better way, but this handles nonspacing characters
4794 * and other special cases that we really do not want to handle here.
4795 */
4796#if NCURSES_EXT_FUNCS
Steve Kondikae271bc2015-11-15 02:50:53 +01004797 if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304798#endif
4799 {
4800 delwin(field->working);
Steve Kondikae271bc2015-11-15 02:50:53 +01004801 field->working = newpad(1, Buffer_Length(field) + 1);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304802 }
Steve Kondikae271bc2015-11-15 02:50:53 +01004803 len = Buffer_Length(field);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304804 wclear(field->working);
Steve Kondikae271bc2015-11-15 02:50:53 +01004805 (void)mvwaddstr(field->working, 0, 0, value);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304806
4807 if ((widevalue = typeCalloc(FIELD_CELL, len + 1)) == 0)
4808 {
4809 RETURN(E_SYSTEM_ERROR);
4810 }
4811 else
4812 {
Steve Kondikae271bc2015-11-15 02:50:53 +01004813 for (i = 0; i < field->drows; ++i)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304814 {
Steve Kondikae271bc2015-11-15 02:50:53 +01004815 (void)mvwin_wchnstr(field->working, 0, (int)i * field->dcols,
4816 widevalue + ((int)i * field->dcols),
4817 field->dcols);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304818 }
4819 for (i = 0; i < len; ++i)
4820 {
4821 if (CharEq(myZEROS, widevalue[i]))
4822 {
4823 while (i < len)
4824 p[i++] = myBLANK;
4825 break;
4826 }
4827 p[i] = widevalue[i];
4828 }
4829 free(widevalue);
4830 }
4831#else
4832 for (i = 0; i < len; ++i)
4833 {
4834 if (value[i] == '\0')
4835 {
4836 while (i < len)
4837 p[i++] = myBLANK;
4838 break;
4839 }
4840 p[i] = value[i];
4841 }
4842#endif
4843
4844 if (buffer == 0)
4845 {
4846 int syncres;
4847
4848 if (((syncres = Synchronize_Field(field)) != E_OK) &&
4849 (res == E_OK))
4850 res = syncres;
4851 if (((syncres = Synchronize_Linked_Fields(field)) != E_OK) &&
4852 (res == E_OK))
4853 res = syncres;
4854 }
4855 RETURN(res);
4856}
4857
4858/*---------------------------------------------------------------------------
4859| Facility : libnform
4860| Function : char *field_buffer(const FIELD *field,int buffer)
4861|
4862| Description : Return the address of the buffer for the field.
4863|
4864| Return Values : Pointer to buffer or NULL if arguments were invalid.
4865+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -04004866FORM_EXPORT(char *)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304867field_buffer(const FIELD *field, int buffer)
4868{
4869 char *result = 0;
4870
Steve Kondikae271bc2015-11-15 02:50:53 +01004871 T((T_CALLED("field_buffer(%p,%d)"), (const void *)field, buffer));
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304872
4873 if (field && (buffer >= 0) && (buffer <= field->nbuf))
4874 {
4875#if USE_WIDEC_SUPPORT
4876 FIELD_CELL *data = Address_Of_Nth_Buffer(field, buffer);
Steve Kondikae271bc2015-11-15 02:50:53 +01004877 size_t need = 0;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304878 int size = Buffer_Length(field);
4879 int n;
4880
4881 /* determine the number of bytes needed to store the expanded string */
4882 for (n = 0; n < size; ++n)
4883 {
Steve Kondikae271bc2015-11-15 02:50:53 +01004884 if (!isWidecExt(data[n]) && data[n].chars[0] != L'\0')
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304885 {
4886 mbstate_t state;
4887 size_t next;
4888
4889 init_mb(state);
4890 next = _nc_wcrtomb(0, data[n].chars[0], &state);
Steve Kondikae271bc2015-11-15 02:50:53 +01004891 if (next > 0)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304892 need += next;
4893 }
4894 }
4895
4896 /* allocate a place to store the expanded string */
4897 if (field->expanded[buffer] != 0)
4898 free(field->expanded[buffer]);
4899 field->expanded[buffer] = typeMalloc(char, need + 1);
4900
Steve Kondikae271bc2015-11-15 02:50:53 +01004901 /*
4902 * Expand the multibyte data.
4903 *
4904 * It may also be multi-column data. In that case, the data for a row
4905 * may be null-padded to align to the dcols/drows layout (or it may
4906 * contain embedded wide-character extensions). Change the null-padding
4907 * to blanks as needed.
4908 */
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304909 if ((result = field->expanded[buffer]) != 0)
4910 {
4911 wclear(field->working);
Steve Kondikae271bc2015-11-15 02:50:53 +01004912 wmove(field->working, 0, 0);
4913 for (n = 0; n < size; ++n)
4914 {
4915 if (!isWidecExt(data[n]) && data[n].chars[0] != L'\0')
4916 wadd_wch(field->working, &data[n]);
4917 }
4918 wmove(field->working, 0, 0);
4919 winnstr(field->working, result, (int)need);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304920 }
4921#else
4922 result = Address_Of_Nth_Buffer(field, buffer);
4923#endif
4924 }
4925 returnPtr(result);
4926}
4927
4928#if USE_WIDEC_SUPPORT
4929
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304930/*---------------------------------------------------------------------------
4931| Convert a multibyte string to a wide-character string. The result must be
4932| freed by the caller.
4933+--------------------------------------------------------------------------*/
micky3879b9f5e72025-07-08 18:04:53 -04004934FORM_EXPORT(wchar_t *)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304935_nc_Widen_String(char *source, int *lengthp)
4936{
4937 wchar_t *result = 0;
4938 wchar_t wch;
4939 size_t given = strlen(source);
4940 size_t tries;
4941 int pass;
4942 int status;
4943
Steve Kondikae271bc2015-11-15 02:50:53 +01004944#ifndef state_unused
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304945 mbstate_t state;
4946#endif
4947
4948 for (pass = 0; pass < 2; ++pass)
4949 {
4950 unsigned need = 0;
4951 size_t passed = 0;
4952
4953 while (passed < given)
4954 {
4955 bool found = FALSE;
4956
4957 for (tries = 1, status = 0; tries <= (given - passed); ++tries)
4958 {
4959 int save = source[passed + tries];
4960
4961 source[passed + tries] = 0;
4962 reset_mbytes(state);
Steve Kondikae271bc2015-11-15 02:50:53 +01004963 status = check_mbytes(wch, source + passed, tries, state);
4964 source[passed + tries] = (char)save;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304965
4966 if (status > 0)
4967 {
4968 found = TRUE;
4969 break;
4970 }
4971 }
4972 if (found)
4973 {
4974 if (pass)
4975 {
4976 result[need] = wch;
4977 }
micky3879b9f5e72025-07-08 18:04:53 -04004978 passed += (size_t)status;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304979 ++need;
4980 }
4981 else
4982 {
4983 if (pass)
4984 {
Steve Kondikae271bc2015-11-15 02:50:53 +01004985 result[need] = (wchar_t)source[passed];
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304986 }
4987 ++need;
4988 ++passed;
4989 }
4990 }
4991
4992 if (!pass)
4993 {
4994 if (!need)
4995 break;
4996 result = typeCalloc(wchar_t, need);
4997
Steve Kondikae271bc2015-11-15 02:50:53 +01004998 *lengthp = (int)need;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05304999 if (result == 0)
5000 break;
5001 }
5002 }
5003
5004 return result;
5005}
5006#endif
5007
5008/* frm_driver.c ends here */